package httpapi import ( "context" "encoding/json" "io" "knowledge-governance/internal/qaopsorg" "net/http" "net/http/httptest" "strings" "testing" ) type orgSyncSpy struct { calls int key, actor string replay bool err error } func (s *orgSyncSpy) GetSyncStatus(context.Context) (qaopsorg.SyncStatus, error) { s.calls++ return qaopsorg.SyncStatus{Configured: true, WorkerReady: true}, s.err } func (s *orgSyncSpy) EnqueueSync(_ context.Context, key, actor string) (qaopsorg.SyncReceipt, bool, error) { s.calls++ s.key = key s.actor = actor return qaopsorg.SyncReceipt{JobID: "fixture-job"}, s.replay, s.err } func TestQAOrganizationSyncRequiresAdminIntentAndDurableOperation(t *testing.T) { spy := &orgSyncSpy{} auth := newFakeAuthStore() auth.sessions["sync-admin"] = "usr-boss" auth.sessions["sync-viewer"] = "usr-eye" handler, err := NewHandler(Config{Store: &fakeJobStore{}, QAOrgSync: spy, QAOrgSyncEnabled: true, Auth: &AuthConfig{Store: auth}}) if err != nil { t.Fatal(err) } for _, tc := range []struct { method, token, key, action, origin, body string status int }{ {"GET", "", "", "", "", "", 401}, {"GET", "sync-viewer", "", "", "", "", 403}, {"GET", "sync-admin", "", "", "", "", 200}, {"POST", "sync-admin", "op-one", "", "", "{}", 403}, {"POST", "sync-admin", "op-one", "qa-organization", "https://evil.invalid", "{}", 403}, {"POST", "sync-admin", "", "qa-organization", "", "{}", 400}, {"POST", "sync-admin", " op-one ", "qa-organization", "", "{}", 400}, {"POST", "sync-admin", "op-one", "qa-organization", "", `null`, 400}, {"POST", "sync-admin", "op-one", "qa-organization", "", `{"tenant":"forged"}`, 400}, {"POST", "sync-admin", "op-one", "qa-organization", "", "{} {}", 400}, {"POST", "sync-admin", "op-one", "qa-organization", "", "{}", 202}, } { req := httptest.NewRequest(tc.method, "/api/v1/qa-ops/organization/sync", strings.NewReader(tc.body)) if tc.token != "" { req.AddCookie(&http.Cookie{Name: sessionCookieName, Value: tc.token}) } req.Header.Set("Idempotency-Key", tc.key) req.Header.Set("X-Governance-Action", tc.action) req.Header.Set("Origin", tc.origin) res := httptest.NewRecorder() handler.ServeHTTP(res, req) if res.Code != tc.status { t.Fatalf("%+v got %d: %s", tc, res.Code, res.Body.String()) } } if spy.calls != 2 || spy.key != "op-one" || spy.actor == "" { t.Fatalf("invalid request reached store: %+v", spy) } spy.replay = true req := httptest.NewRequest("POST", "/api/v1/qa-ops/organization/sync", strings.NewReader("{}")) req.AddCookie(&http.Cookie{Name: sessionCookieName, Value: "sync-admin"}) req.Header.Set("Idempotency-Key", "op-one") req.Header.Set("X-Governance-Action", "qa-organization") res := httptest.NewRecorder() handler.ServeHTTP(res, req) var body map[string]any if json.Unmarshal(res.Body.Bytes(), &body) != nil || body["operation"] != "replayed" { t.Fatal("operation replay not exposed") } disabled, _ := NewHandler(Config{Store: &fakeJobStore{}, QAOrgSync: spy, Auth: &AuthConfig{Store: auth}}) req.Body = io.NopCloser(strings.NewReader("{}")) res = httptest.NewRecorder() disabled.ServeHTTP(res, req) if res.Code != 503 { t.Fatal("disabled sync enqueued") } }