Files
member-console/internal/server/operator_enrollment_extend_guard_test.go
T
cgalo5758 ad7a219adf Enforce schema and boot invariants
Enforce 10j's verified gaps (schema-hardening change):

- Migration 00010: partial unique indexes for one default pool and one
  primary assignment per workspace, plus CHECKs pinning
  pool/provider/subscription vocabularies and provider lifecycle
  timestamps.
- Workspace creation shares a transactional provisioning function;
  extension validates its target pool; last-tier deletion of a defaulted
  ladder is guarded; signup completes plan-less on a broken ladder.
- Boot asserts integration slug parity and validates declared config
  enums; Stripe invoice amounts are range-checked; domain cancellation
  runs a final evidence probe; rule authoring is additive-only.
2026-08-22 18:02:46 -05:00

161 lines
6.4 KiB
Go

package server_test
// Tests for ExtendGrant's pool-ownership and multi-pool refusal guards
// (schema-hardening tasks 2.2 + 2.5, design D3): before conferring, the
// handler now refuses an organization that resolves to more than one active
// resource pool (mirroring IssueGrant's finding-#32 guard), and verifies the
// URL pool actually belongs to the URL org and is its default pool. Reuses
// the otc*/otcHarness fixtures from operator_org_type_change_test.go (same
// package).
import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"git.coopcloud.tech/wiki-cafe/member-console/internal/entitlements"
)
// postOrgPool invokes an ExtendGrant-shaped handler with both {orgID} and
// {poolID} path values set, using the harness's authenticated-operator
// context. Returns status, body, and the HX-Trigger header (success/error
// toasts fire there, not in the body -- docs/operator-ux-conventions.md §3).
func (h *otcHarness) postOrgPool(handler http.HandlerFunc, orgID, poolID string, form url.Values) (int, string, string) {
h.t.Helper()
req := httptest.NewRequestWithContext(h.ctx, http.MethodPost, "/", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.SetPathValue("orgID", orgID)
req.SetPathValue("poolID", poolID)
rec := httptest.NewRecorder()
handler(rec, req)
return rec.Code, rec.Body.String(), rec.Header().Get("HX-Trigger")
}
// A pool that belongs to a different organization than the URL's org is
// refused: nothing is conferred or superseded (plan-enrollment-administration
// spec, "Extension against a pool the org does not own is refused").
func TestExtendGrant_RefusesPoolFromAnotherOrg(t *testing.T) {
database := testDB(t)
f := newOtcFixture(t, database)
h := newOtcHarness(t, database, f.operatorID)
orgA, poolA := otcOrg(t, database, f, "Extend Org A", true)
otcConferDefaultA(t, database, f, poolA)
_, poolB := otcOrg(t, database, f, "Extend Org B", true)
otcConferDefaultA(t, database, f, poolB)
beforeA := otcScalar(t, database, `SELECT count(*) FROM core.grants WHERE granted_to_org_id = $1`, orgA)
// orgA's URL, but poolB's pool (belongs to a different org).
code, body, _ := h.postOrgPool(h.handler.ExtendGrant, orgA, poolB, url.Values{"reason": {"cross-pool test"}})
if code != http.StatusOK {
t.Fatalf("status=%d body=%s", code, body)
}
if !strings.Contains(body, "does not belong to this organization") {
t.Errorf("refusal copy missing; body:\n%s", body)
}
afterA := otcScalar(t, database, `SELECT count(*) FROM core.grants WHERE granted_to_org_id = $1`, orgA)
if afterA != beforeA {
t.Errorf("grants for orgA changed: %d -> %d, want no writes on refusal", beforeA, afterA)
}
}
// A pool that is the URL org's ONLY pool but isn't its default pool is
// refused too -- the ownership/default-type check, isolated from the
// multi-pool guard (single pool here, so that guard passes clean).
func TestExtendGrant_RefusesNonDefaultPool(t *testing.T) {
database := testDB(t)
f := newOtcFixture(t, database)
h := newOtcHarness(t, database, f.operatorID)
org, _ := otcOrg(t, database, f, "Extend NonDefault Org", false) // no pool yet
entQ := entitlements.New(database)
shared, err := entQ.CreateResourcePool(context.Background(), entitlements.CreateResourcePoolParams{
OrgID: org, Name: "Shared", Slug: "shared-pool", PoolType: "shared", IsAutoManaged: false,
})
if err != nil {
t.Fatalf("create shared pool: %v", err)
}
before := otcScalar(t, database, `SELECT count(*) FROM core.grants WHERE granted_to_org_id = $1`, org)
code, body, _ := h.postOrgPool(h.handler.ExtendGrant, org, shared.PoolID, url.Values{"reason": {"non-default test"}})
if code != http.StatusOK {
t.Fatalf("status=%d body=%s", code, body)
}
if !strings.Contains(body, "does not belong to this organization") {
t.Errorf("non-default-pool refusal copy missing; body:\n%s", body)
}
after := otcScalar(t, database, `SELECT count(*) FROM core.grants WHERE granted_to_org_id = $1`, org)
if after != before {
t.Errorf("grants changed: %d -> %d, want no writes on refusal", before, after)
}
}
// An organization with more than one active resource pool is refused
// wholesale, mirroring IssueGrant's finding-#32 guard
// (plan-enrollment-administration spec, "Multi-pool organization is
// refused").
func TestExtendGrant_RefusesMultiPoolOrg(t *testing.T) {
database := testDB(t)
f := newOtcFixture(t, database)
h := newOtcHarness(t, database, f.operatorID)
org, pool := otcOrg(t, database, f, "Extend MultiPool Org", true)
otcConferDefaultA(t, database, f, pool)
entQ := entitlements.New(database)
if _, err := entQ.CreateResourcePool(context.Background(), entitlements.CreateResourcePoolParams{
OrgID: org, Name: "Second Pool", Slug: "second-pool", PoolType: "shared", IsAutoManaged: false,
}); err != nil {
t.Fatalf("create second pool: %v", err)
}
before := otcScalar(t, database, `SELECT count(*) FROM core.grants WHERE granted_to_org_id = $1`, org)
code, body, _ := h.postOrgPool(h.handler.ExtendGrant, org, pool, url.Values{"reason": {"multi-pool test"}})
if code != http.StatusOK {
t.Fatalf("status=%d body=%s", code, body)
}
if !strings.Contains(body, "more than one resource pool") {
t.Errorf("multi-pool refusal copy missing; body:\n%s", body)
}
after := otcScalar(t, database, `SELECT count(*) FROM core.grants WHERE granted_to_org_id = $1`, org)
if after != before {
t.Errorf("grants changed: %d -> %d, want no writes on refusal", before, after)
}
}
// Control: a normal single-default-pool org's extension is not blocked by
// the new guards -- it proceeds to conferral as before.
func TestExtendGrant_SucceedsForSinglePoolOrg(t *testing.T) {
database := testDB(t)
f := newOtcFixture(t, database)
h := newOtcHarness(t, database, f.operatorID)
org, pool := otcOrg(t, database, f, "Extend Control Org", true)
otcConferDefaultA(t, database, f, pool)
before := otcScalar(t, database, `SELECT count(*) FROM core.grants WHERE granted_to_org_id = $1`, org)
code, body, trigger := h.postOrgPool(h.handler.ExtendGrant, org, pool, url.Values{"reason": {"control test"}})
if code != http.StatusOK {
t.Fatalf("status=%d body=%s", code, body)
}
if !strings.Contains(trigger, "extended") {
t.Errorf("expected an extension-succeeded toast; HX-Trigger=%q", trigger)
}
after := otcScalar(t, database, `SELECT count(*) FROM core.grants WHERE granted_to_org_id = $1`, org)
if after != before+1 {
t.Errorf("grants for org: %d -> %d, want exactly one new grant", before, after)
}
}