package server_test import ( "net/http" "testing" "gitea.meghsakha.com/platform/tenant-registry/internal/store" ) func TestAppendAndListAudit(t *testing.T) { eachStore(t, func(t *testing.T, h *testHarness) { // Take a snapshot of the audit count beforehand (the seed acme tenant // + any /v1/tenants POST in earlier subtests already emit events). resp, body := h.do("GET", "/v1/audit?limit=500", nil) if resp.StatusCode != 200 { t.Fatalf("baseline list status = %d", resp.StatusCode) } baseline := decode[struct { Items []store.AuditEvent `json:"items"` }](t, body) before := len(baseline.Items) // Append three events. for i := 0; i < 3; i++ { resp, body := h.do("POST", "/v1/audit", map[string]any{ "tenant_id": h.tenant.ID, "action": "test.event", "actor_id": "u1", "actor_name": "Test User", "metadata": map[string]any{"i": i}, }) if resp.StatusCode != http.StatusCreated { t.Fatalf("append %d status = %d, body=%s", i, resp.StatusCode, body) } } // List again, expect baseline + 3 resp, body = h.do("GET", "/v1/audit?limit=500", nil) if resp.StatusCode != 200 { t.Fatalf("list status = %d", resp.StatusCode) } after := decode[struct { Items []store.AuditEvent `json:"items"` }](t, body) if len(after.Items) != before+3 { t.Errorf("expected before+3=%d events, got %d", before+3, len(after.Items)) } // Filter by action: only our test.event rows. resp, body = h.do("GET", "/v1/audit?action=test.event", nil) if resp.StatusCode != 200 { t.Fatalf("filter status = %d", resp.StatusCode) } filtered := decode[struct { Items []store.AuditEvent `json:"items"` }](t, body) if len(filtered.Items) != 3 { t.Errorf("expected 3 filtered events, got %d", len(filtered.Items)) } for _, ev := range filtered.Items { if ev.Action != "test.event" { t.Errorf("filter leaked %q", ev.Action) } } }) } func TestAppendAudit_actionRequired(t *testing.T) { eachStore(t, func(t *testing.T, h *testHarness) { resp, _ := h.do("POST", "/v1/audit", map[string]any{ "tenant_id": h.tenant.ID, }) if resp.StatusCode != http.StatusBadRequest { t.Fatalf("status = %d", resp.StatusCode) } }) } func TestListAudit_invalidParams(t *testing.T) { eachStore(t, func(t *testing.T, h *testHarness) { cases := []string{ "/v1/audit?since=notatime", "/v1/audit?until=notatime", "/v1/audit?limit=0", "/v1/audit?limit=10000", "/v1/audit?cursor=abc", } for _, p := range cases { resp, _ := h.do("GET", p, nil) if resp.StatusCode != http.StatusBadRequest { t.Errorf("%s: status = %d, want 400", p, resp.StatusCode) } } }) } func TestAuditAutoEmittedOnTenantCreate(t *testing.T) { eachStore(t, func(t *testing.T, h *testHarness) { _, body := h.do("POST", "/v1/tenants", map[string]any{ "slug": "audit-target", "name": "Audit Target", }) freshWrap := decode[struct { Tenant *store.Tenant `json:"tenant"` }](t, body) fresh := freshWrap.Tenant resp, body := h.do("GET", "/v1/audit?action=tenant.created&tenant_id="+fresh.ID, nil) if resp.StatusCode != 200 { t.Fatalf("status = %d", resp.StatusCode) } events := decode[struct { Items []store.AuditEvent `json:"items"` }](t, body) if len(events.Items) != 1 || events.Items[0].TargetID != fresh.ID { t.Errorf("expected exactly one tenant.created event for %s, got %d items", fresh.ID, len(events.Items)) } }) }