package auditlog import ( "path/filepath" "testing" "time" ) func newTestStore(t *testing.T) *Store { t.Helper() s, err := New(filepath.Join(t.TempDir(), "audit.db")) if err != nil { t.Fatal(err) } return s } // eventually polls List until at least want entries are persisted (writes are // async) or it times out. func eventually(t *testing.T, s *Store, want int) []Entry { t.Helper() deadline := time.Now().Add(time.Second) for { got, err := s.List(100) if err != nil { t.Fatal(err) } if len(got) >= want { return got } if time.Now().After(deadline) { t.Fatalf("timed out waiting for %d entries, have %d", want, len(got)) } time.Sleep(5 * time.Millisecond) } } func TestRecordAndList(t *testing.T) { s := newTestStore(t) s.Record("alice", "POST", "/api/users", "users", 200) s.Record("bob", "DELETE", "/api/users/x", "users", 403) got := eventually(t, s, 2) // Newest first. if got[0].Username != "bob" || got[0].Status != 403 { t.Errorf("newest entry wrong: %+v", got[0]) } if got[1].Username != "alice" || got[1].Method != "POST" { t.Errorf("oldest entry wrong: %+v", got[1]) } if got[0].Time == "" { t.Error("time not populated") } } func TestListLimit(t *testing.T) { s := newTestStore(t) for range 5 { s.Record("u", "POST", "/api/x", "system", 200) } eventually(t, s, 5) got, err := s.List(3) if err != nil { t.Fatal(err) } if len(got) != 3 { t.Errorf("limit not honored: got %d, want 3", len(got)) } } func TestCloseDrains(t *testing.T) { path := filepath.Join(t.TempDir(), "audit.db") s, err := New(path) if err != nil { t.Fatal(err) } for range 50 { s.Record("u", "POST", "/api/x", "system", 200) } // Close must flush everything still buffered before returning. if err := s.Close(); err != nil { t.Fatal(err) } // Reopen and confirm all 50 made it to disk. s2, err := New(path) if err != nil { t.Fatal(err) } got, err := s2.List(100) if err != nil { t.Fatal(err) } if len(got) != 50 { t.Errorf("Close did not drain: got %d entries, want 50", len(got)) } } func TestListEmpty(t *testing.T) { got, err := newTestStore(t).List(10) if err != nil { t.Fatal(err) } if len(got) != 0 { t.Errorf("fresh store should be empty, got %d", len(got)) } }