135 lines
3.8 KiB
Go
135 lines
3.8 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/oklog/ulid/v2"
|
|
)
|
|
|
|
func newAuditTestStore(t *testing.T) (*Store, string) {
|
|
t.Helper()
|
|
st, err := Open(context.Background(), filepath.Join(t.TempDir(), "rm.db"))
|
|
if err != nil {
|
|
t.Fatalf("open: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = st.Close() })
|
|
uid := ulid.Make().String()
|
|
if err := st.CreateUser(context.Background(), User{
|
|
ID: uid, Username: "alice", PasswordHash: "x",
|
|
Role: RoleOperator, CreatedAt: time.Now().UTC(),
|
|
}); err != nil {
|
|
t.Fatalf("create user: %v", err)
|
|
}
|
|
return st, uid
|
|
}
|
|
|
|
func appendAudit(t *testing.T, st *Store, uid, actor, action, targetKind, targetID string, ts time.Time) {
|
|
t.Helper()
|
|
var u, tk, ti *string
|
|
if uid != "" {
|
|
u = &uid
|
|
}
|
|
if targetKind != "" {
|
|
tk = &targetKind
|
|
}
|
|
if targetID != "" {
|
|
ti = &targetID
|
|
}
|
|
if err := st.AppendAudit(context.Background(), AuditEntry{
|
|
ID: ulid.Make().String(), UserID: u, Actor: actor, Action: action,
|
|
TargetKind: tk, TargetID: ti, TS: ts, Payload: json.RawMessage(`{}`),
|
|
}); err != nil {
|
|
t.Fatalf("append: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestListAuditFiltersAndOrdering(t *testing.T) {
|
|
t.Parallel()
|
|
st, uid := newAuditTestStore(t)
|
|
t0 := time.Now().UTC()
|
|
|
|
appendAudit(t, st, uid, "user", "host.enrolled", "host", "h1", t0.Add(-3*time.Hour))
|
|
appendAudit(t, st, uid, "user", "alert.acknowledge", "alert", "a1", t0.Add(-2*time.Hour))
|
|
appendAudit(t, st, uid, "user", "alert.resolve", "alert", "a1", t0.Add(-time.Hour))
|
|
appendAudit(t, st, "", "system", "host.auto_init", "host", "h1", t0.Add(-30*time.Minute))
|
|
|
|
all, err := st.ListAudit(context.Background(), AuditFilter{})
|
|
if err != nil {
|
|
t.Fatalf("list: %v", err)
|
|
}
|
|
if len(all) != 4 {
|
|
t.Fatalf("len: got %d want 4", len(all))
|
|
}
|
|
// Ordered ts DESC — most recent first.
|
|
if all[0].Action != "host.auto_init" || all[3].Action != "host.enrolled" {
|
|
t.Errorf("ordering: got %s ... %s", all[0].Action, all[3].Action)
|
|
}
|
|
|
|
// Action prefix filter: alert.* → 2 rows.
|
|
got, err := st.ListAudit(context.Background(), AuditFilter{ActionLike: "alert."})
|
|
if err != nil {
|
|
t.Fatalf("filter alert.: %v", err)
|
|
}
|
|
if len(got) != 2 {
|
|
t.Errorf("alert.* filter: got %d want 2", len(got))
|
|
}
|
|
|
|
// User filter excludes system rows.
|
|
got, _ = st.ListAudit(context.Background(), AuditFilter{UserID: uid})
|
|
if len(got) != 3 {
|
|
t.Errorf("user filter: got %d want 3", len(got))
|
|
}
|
|
|
|
// Actor=system isolates the auto_init.
|
|
got, _ = st.ListAudit(context.Background(), AuditFilter{Actor: "system"})
|
|
if len(got) != 1 || got[0].Action != "host.auto_init" {
|
|
t.Errorf("actor=system: got %+v", got)
|
|
}
|
|
|
|
// Target kind filter.
|
|
got, _ = st.ListAudit(context.Background(), AuditFilter{TargetKind: "alert"})
|
|
if len(got) != 2 {
|
|
t.Errorf("target_kind=alert: got %d want 2", len(got))
|
|
}
|
|
|
|
// Time range: last 90m → resolve + auto_init.
|
|
got, _ = st.ListAudit(context.Background(), AuditFilter{Since: t0.Add(-90 * time.Minute)})
|
|
if len(got) != 2 {
|
|
t.Errorf("since 90m: got %d want 2", len(got))
|
|
}
|
|
|
|
// Limit clamps result count.
|
|
got, _ = st.ListAudit(context.Background(), AuditFilter{Limit: 2})
|
|
if len(got) != 2 {
|
|
t.Errorf("limit: got %d want 2", len(got))
|
|
}
|
|
}
|
|
|
|
func TestDistinctAuditActions(t *testing.T) {
|
|
t.Parallel()
|
|
st, uid := newAuditTestStore(t)
|
|
t0 := time.Now().UTC()
|
|
|
|
appendAudit(t, st, uid, "user", "host.enrolled", "host", "h1", t0)
|
|
appendAudit(t, st, uid, "user", "host.enrolled", "host", "h2", t0)
|
|
appendAudit(t, st, uid, "user", "alert.acknowledge", "alert", "a1", t0)
|
|
|
|
got, err := st.DistinctAuditActions(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("distinct: %v", err)
|
|
}
|
|
want := []string{"alert.acknowledge", "host.enrolled"}
|
|
if len(got) != len(want) {
|
|
t.Fatalf("got %v want %v", got, want)
|
|
}
|
|
for i := range want {
|
|
if got[i] != want[i] {
|
|
t.Errorf("[%d]: got %q want %q", i, got[i], want[i])
|
|
}
|
|
}
|
|
}
|