munin/internal/stats/stats_test.go
2026-10-19 10:13:56 +02:00

124 lines
3.9 KiB
Go

package stats
import (
"context"
"os"
"path/filepath"
"testing"
"time"
"munin/internal/store"
"munin/internal/workspace"
)
// testEnv legt eine Kontroll-Datenbank mit zwei Workspaces an, je einem
// Projekt darin.
func testEnv(t *testing.T) (*store.ControlStore, *workspace.Manager, []store.Workspace) {
t.Helper()
ctx := context.Background()
dir := t.TempDir()
wsDir := filepath.Join(dir, "workspaces")
if err := os.MkdirAll(wsDir, 0750); err != nil {
t.Fatal(err)
}
backend := store.NewSQLite(filepath.Join(dir, "control.db"), wsDir)
control, err := backend.OpenControl()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { control.Close() })
manager := workspace.NewManager(backend)
t.Cleanup(func() { manager.Close() })
var list []store.Workspace
for _, name := range []string{"Vertrieb", "Entwicklung"} {
ws, err := control.CreateWorkspace(ctx, name)
if err != nil {
t.Fatal(err)
}
opened, err := manager.Get(ws.ID)
if err != nil {
t.Fatal(err)
}
if _, err := opened.Store.CreateProject(ctx, ws.ID, "allgemein", "Allgemein "+name, ""); err != nil {
t.Fatal(err)
}
list = append(list, ws)
}
return control, manager, list
}
// TestCollect zählt über alle Workspaces zusammen, füllt jeden Tag des
// Zeitraums und trennt aktive von ruhigen Projekten.
func TestCollect(t *testing.T) {
control, manager, list := testEnv(t)
ctx := context.Background()
// Im Workspace "Vertrieb" schreibt ein Agent einen Eintrag.
opened, _ := manager.Get(list[0].ID)
projects, _ := opened.Store.ListProjects(ctx, list[0].ID)
if _, err := opened.Store.PutEntry(ctx, store.EntryWrite{
ProjectID: projects[0].ID, Slug: "notiz", Title: "Notiz", Content: "x", Source: store.SourceAgent,
}); err != nil {
t.Fatal(err)
}
opened.Store.LogAudit(ctx, store.AuditEvent{
ProjectID: projects[0].ID, Actor: "agent", Action: store.ActionEntryWrite, Source: store.SourceMCP,
})
snapshot := NewCollector(control, manager).Current(ctx, time.Now())
if len(snapshot.Errors) > 0 {
t.Fatalf("Fehler: %v", snapshot.Errors)
}
if len(snapshot.Workspaces) != 2 || snapshot.Workspaces[0].Name != "Entwicklung" {
t.Errorf("Workspaces %+v, erwartet zwei, nach Name sortiert", snapshot.Workspaces)
}
if snapshot.Total.Projects != 2 || snapshot.Total.Entries != 1 {
t.Errorf("Summe %+v, erwartet 2 Projekte und 1 Eintrag", snapshot.Total)
}
if snapshot.DiskUsage.Data <= 0 {
t.Error("keine Belegung gezählt")
}
if snapshot.SchemaVersion == "" {
t.Error("Schemastand fehlt")
}
if len(snapshot.Days) != Days {
t.Fatalf("%d Tage, erwartet %d", len(snapshot.Days), Days)
}
today := snapshot.Days[len(snapshot.Days)-1]
if today.AgentWrites != 1 || today.Date.Format("2006-01-02") != time.Now().UTC().Format("2006-01-02") {
t.Errorf("letzter Tag %+v, erwartet heute mit einem Schreibvorgang", today)
}
if len(snapshot.Projects) != 1 || snapshot.Projects[0].Workspace != "Vertrieb" {
t.Errorf("aktive Projekte %+v, erwartet nur das im Vertrieb", snapshot.Projects)
}
if len(snapshot.Idle) != 1 || snapshot.Idle[0].Workspace != "Entwicklung" {
t.Errorf("ruhige Projekte %+v, erwartet nur das in der Entwicklung", snapshot.Idle)
}
}
// TestMinInterval: Innerhalb von MinInterval kommt der gemerkte Stand
// zurück, danach ein neuer — der sich die Wartezahl des vorigen merkt.
func TestMinInterval(t *testing.T) {
control, manager, _ := testEnv(t)
ctx := context.Background()
s := NewCollector(control, manager)
now := time.Now()
first := s.Current(ctx, now)
if second := s.Current(ctx, now.Add(time.Minute)); !second.CollectedAt.Equal(first.CollectedAt) {
t.Error("nach einer Minute neu gezählt, erwartet den gemerkten Stand")
}
third := s.Current(ctx, now.Add(MinInterval+time.Second))
if third.CollectedAt.Equal(first.CollectedAt) {
t.Fatal("nach MindestAbstand nicht neu gezählt")
}
if !third.PrevCollectedAt.Equal(first.CollectedAt) {
t.Errorf("VorherErhoben = %v, erwartet den ersten Stand %v", third.PrevCollectedAt, first.CollectedAt)
}
}