package cabana_test import ( "context" "encoding/json" "net/http" "strconv" "sync" "testing" "git.golem15.com/golem15/summercms/modules/cabana" "git.golem15.com/golem15/summercms/modules/pact" ) // TestAccountTokenRevokeEvent pins plan 07's token-specific revocation // (B-WR-04): revoking one g15_ token from My account fires exactly one // revoked subject event for the admin, after the commit, whose TokenID is // the revoked row's id, and no event for the admin as a whole (their other // tokens and OAuth sessions stay). Postgres only: under -short nothing runs. func TestAccountTokenRevokeEvent(t *testing.T) { if testing.Short() { return } t.Run("one revoked event with the token id", func(t *testing.T) { env := newScreensEnv(t) var mu sync.Mutex var got []pact.MCPSubjectChanged env.app.Events.Listen("test.revoke", func(_ context.Context, ev *pact.MCPSubjectChanged) error { mu.Lock() got = append(got, *ev) mu.Unlock() return nil }) admin, jwt := env.admin(t, env.unique("revoke"), true, "") ids := make([]uint, 2) for i := range ids { out := env.expect(t, http.StatusCreated, http.MethodPost, "/account/tokens", jwt, map[string]any{ "name": env.unique("agent"), "expires": "90d", "scopes": []string{"*"}, }) raw, _ := json.Marshal(out["data"]) var created cabana.AdminAPITokenCreated if err := json.Unmarshal(raw, &created); err != nil || created.ID == 0 { t.Fatalf("created token = %s (%v)", raw, err) } ids[i] = created.ID } mu.Lock() got = nil mu.Unlock() env.expect(t, http.StatusOK, http.MethodDelete, "/account/tokens/"+strconv.FormatUint(uint64(ids[0]), 10), jwt, map[string]any{}) mu.Lock() defer mu.Unlock() if len(got) != 1 { t.Fatalf("revoking one token fired %d events, want 1: %+v", len(got), got) } ev := got[0] if ev.Kind != pact.MCPSubjectRevoked || ev.Guard != "backend" || ev.UserID != admin.ID || ev.TokenID != ids[0] || ev.ClientID != "" { t.Fatalf("event = %+v, want revoked backend %d with token %d", ev, admin.ID, ids[0]) } }) }