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) } }