diff --git a/internal/store/audit.go b/internal/store/audit.go index d0cc3e5..b08bc30 100644 --- a/internal/store/audit.go +++ b/internal/store/audit.go @@ -74,6 +74,25 @@ func readSummaries(path string) ([]SessionSummary, error) { return out, sc.Err() } +// RecentSessions returns up to n most-recent summaries from the audit chain in +// oldest-first order. A missing/empty chain or n <= 0 yields (nil, nil). +func RecentSessions(path string, n int) ([]SessionSummary, error) { + if n <= 0 { + return nil, nil + } + all, err := readSummaries(path) + if err != nil { + return nil, err + } + if len(all) == 0 { + return nil, nil + } + if len(all) > n { + all = all[len(all)-n:] + } + return all, nil +} + // AppendSession links s onto the chain (genesis prev_hash = 64 zeros), assigns // the next contiguous Seq, computes its Hash, and appends one JSON line with an // fsync. diff --git a/internal/store/audit_test.go b/internal/store/audit_test.go index a3a08c5..566122b 100644 --- a/internal/store/audit_test.go +++ b/internal/store/audit_test.go @@ -75,3 +75,45 @@ func TestTamperDetected(t *testing.T) { t.Fatalf("tamper on line 2 should be reported, got %v", err) } } + +func TestRecentSessionsReturnsLastNOldestFirst(t *testing.T) { + path := auditFixture(t) + for _, id := range []string{"a", "b", "c"} { + if err := AppendSession(path, sampleSummary("session-"+id)); err != nil { + t.Fatalf("append %s: %v", id, err) + } + } + got, err := RecentSessions(path, 2) + if err != nil { + t.Fatalf("recent: %v", err) + } + if len(got) != 2 { + t.Fatalf("want 2 summaries, got %d", len(got)) + } + if got[0].SessionID != "session-b" || got[1].SessionID != "session-c" { + t.Fatalf("want [b c] oldest-first, got [%s %s]", got[0].SessionID, got[1].SessionID) + } +} + +func TestRecentSessionsFewerThanN(t *testing.T) { + path := auditFixture(t) + _ = AppendSession(path, sampleSummary("session-a")) + got, err := RecentSessions(path, 5) + if err != nil { + t.Fatalf("recent: %v", err) + } + if len(got) != 1 || got[0].SessionID != "session-a" { + t.Fatalf("want [a], got %+v", got) + } +} + +func TestRecentSessionsMissingOrZero(t *testing.T) { + if got, err := RecentSessions(auditFixture(t), 5); err != nil || got != nil { + t.Fatalf("missing chain: want (nil,nil), got (%+v,%v)", got, err) + } + path := auditFixture(t) + _ = AppendSession(path, sampleSummary("session-a")) + if got, err := RecentSessions(path, 0); err != nil || got != nil { + t.Fatalf("n=0: want (nil,nil), got (%+v,%v)", got, err) + } +}