Files
member-console/internal/entitlements/conferral_proof_test.go
T
cgalo5758 dd3962990b Adopt entity keys and add invoice numbers
Replace the entity slugs on organizations, workspaces, resource pools,
and
plan ladders with nullable `key` columns and add keys to products,
prices,
and entitlement sets. Rename `providers.slug` to `provider` and add
partial
unique indexes for system and org role names.

Assign invoice numbers per billing account from a gapless transactional
counter; Stripe's number moves to the invoice mapping as an external
reference.

Seeds, fixtures, and the operator lookup address rows by key, and the
returning-login resync no longer blanks a display name when the IdP
sends
no `name` claim.
2026-08-29 20:12:04 -05:00

960 lines
38 KiB
Go

package entitlements_test
// Doc 41 Appendix A proof scenarios (design D10, tasks 6.2) as integration
// tests over the Go conferral wrappers. Each test runs in a rolled-back
// transaction against the TEST_DATABASE_URL database (see testDB in
// materialize_test.go) and builds its own isolated fixture, so runs are
// deterministic and leave no rows behind.
//
// Also here: the confer timestamp regression test (zero ActivatedAt inside a
// long transaction must succeed; explicit future activation must be refused)
// and the vacancy-guarded baseline restoration tests over
// ReapplyDefaultsIfVacant (plan-enrollment-administration delta scenarios
// "Revoking the plan-holding grant restores the baseline" and "Restoration
// guard never supersedes another source").
import (
"context"
"database/sql"
"encoding/json"
"strings"
"testing"
"time"
"git.coopcloud.tech/wiki-cafe/member-console/internal/billing"
"git.coopcloud.tech/wiki-cafe/member-console/internal/entitlements"
"git.coopcloud.tech/wiki-cafe/member-console/internal/organization"
"github.com/google/uuid"
)
// --- Local fixture helpers -------------------------------------------------
// proofLadder creates a plan ladder and appends the given products as tiers.
// CreatePlanLadderTier self-assigns rank MAX+1, so products land at ranks
// 0..N-1 in argument order.
func proofLadder(t *testing.T, ctx context.Context, tx *sql.Tx, key string, productIDs ...string) string {
t.Helper()
bq := billing.New(tx)
ladder, err := bq.CreatePlanLadder(ctx, billing.CreatePlanLadderParams{
Name: "Doc41 " + key,
IsActive: true,
})
if err != nil {
t.Fatalf("create ladder %s: %v", key, err)
}
for _, pid := range productIDs {
if _, err := bq.CreatePlanLadderTier(ctx, billing.CreatePlanLadderTierParams{
PlanLadderID: ladder.PlanLadderID,
ProductID: pid,
}); err != nil {
t.Fatalf("create tier on %s for %s: %v", key, pid, err)
}
}
return ladder.PlanLadderID
}
// proofGrant records a manual operator grant decree for the product.
func proofGrant(t *testing.T, ctx context.Context, tx *sql.Tx, to testOrg, productID, reason string) entitlements.Grant {
t.Helper()
q := entitlements.New(tx)
g, err := q.CreateGrant(ctx, entitlements.CreateGrantParams{
ProductID: productID,
GrantedToOrgID: uuid.NullUUID{UUID: uuid.MustParse(to.org.OrgID), Valid: true},
GrantedByPersonID: uuid.NullUUID{UUID: uuid.MustParse(to.person.PersonID), Valid: true},
GrantReason: reason,
Quantity: 1,
ValidFrom: time.Now(),
})
if err != nil {
t.Fatalf("create grant for %s: %v", productID, err)
}
return g
}
// proofConfer confers the grant onto the org's pool, expecting the given
// outcome, and returns the provision id. The fixture person is the operator
// actor (chk_pool_provision_transitions_operator_actor requires an actor_id
// for operator-typed transitions).
func proofConfer(t *testing.T, ctx context.Context, tx *sql.Tx, to testOrg, productID string, grant entitlements.Grant, wantOutcome string) string {
t.Helper()
q := entitlements.New(tx)
provisionID, outcome, err := q.Confer(ctx, entitlements.ConferParams{
PoolID: to.pool.PoolID,
ProductID: productID,
GrantID: uuid.NullUUID{UUID: uuid.MustParse(grant.GrantID), Valid: true},
Quantity: 1,
ActorType: "operator",
ActorID: uuid.NullUUID{UUID: uuid.MustParse(to.person.PersonID), Valid: true},
})
if err != nil {
t.Fatalf("confer %s: %v", productID, err)
}
if outcome != wantOutcome {
t.Fatalf("confer %s outcome = %q, want %q", productID, outcome, wantOutcome)
}
return provisionID
}
// proofSubscription creates a billing account for the org and a bare core
// subscription row, returning the subscription id. Subscriptions are a
// writer-writable source table; only their enactment is enclosed.
func proofSubscription(t *testing.T, ctx context.Context, tx *sql.Tx, orgID string) uuid.UUID {
t.Helper()
bq := billing.New(tx)
account, err := bq.CreateBillingAccount(ctx, billing.CreateBillingAccountParams{
OrgID: orgID,
Name: "proof-" + uuid.New().String()[:8],
Status: "active",
Metadata: json.RawMessage("{}"),
})
if err != nil {
t.Fatalf("create billing account: %v", err)
}
var subID uuid.UUID
if err := tx.QueryRowContext(ctx,
`INSERT INTO core.subscriptions (billing_account_id, status) VALUES ($1, 'active')
RETURNING subscription_id`, account.BillingAccountID,
).Scan(&subID); err != nil {
t.Fatalf("create subscription: %v", err)
}
return subID
}
func proofCount(t *testing.T, ctx context.Context, tx *sql.Tx, query string, args ...any) int {
t.Helper()
var n int
if err := tx.QueryRowContext(ctx, query, args...).Scan(&n); err != nil {
t.Fatalf("count query %q: %v", query, err)
}
return n
}
func proofActiveJunctions(t *testing.T, ctx context.Context, tx *sql.Tx, provisionID string) int {
return proofCount(t, ctx, tx,
`SELECT count(*) FROM core.pool_provision_ladders WHERE provision_id = $1 AND status = 'active'`,
provisionID)
}
func proofTransitions(t *testing.T, ctx context.Context, tx *sql.Tx, provisionID string) int {
return proofCount(t, ctx, tx,
`SELECT count(*) FROM core.pool_provision_transitions WHERE provision_id = $1`,
provisionID)
}
func proofProvisionStatus(t *testing.T, ctx context.Context, tx *sql.Tx, provisionID string) string {
t.Helper()
var s string
if err := tx.QueryRowContext(ctx,
`SELECT status FROM core.pool_provisions WHERE provision_id = $1`, provisionID,
).Scan(&s); err != nil {
t.Fatalf("provision status: %v", err)
}
return s
}
func proofBegin(t *testing.T) (context.Context, *sql.Tx) {
t.Helper()
database := testDB(t)
ctx := context.Background()
tx, err := database.BeginTx(ctx, nil)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = tx.Rollback() })
return ctx, tx
}
// proofSavepoint runs fn inside a savepoint and rolls back to it, so a
// statement expected to fail doesn't abort the enclosing test transaction.
func proofSavepoint(t *testing.T, ctx context.Context, tx *sql.Tx, fn func()) {
t.Helper()
if _, err := tx.ExecContext(ctx, "SAVEPOINT proof_probe"); err != nil {
t.Fatalf("savepoint: %v", err)
}
fn()
if _, err := tx.ExecContext(ctx, "ROLLBACK TO SAVEPOINT proof_probe"); err != nil {
t.Fatalf("rollback to savepoint: %v", err)
}
}
// --- Appendix A scenarios ----------------------------------------------------
// Scenario 1 — Addon granted, then revoked, over an active plan (finding #25).
// end_conferral resolves by source: revoking the off-ladder addon grant ends
// only the addon's provision and can never touch the plan's ladder position.
// The second revoke is refused at the decree level (RevokeGrant matches only
// status='active'), and a raw end_conferral replay is the quiet zero-live-
// target success, not an error.
func TestConferralProof_AddonRevokeReplay(t *testing.T) {
ctx, tx := proofBegin(t)
q := entitlements.New(tx)
to := setupTestOrg(t, ctx, tx)
plan := createTestProduct(t, ctx, tx, "p1-plan", 5, false)
proofLadder(t, ctx, tx, "p1", plan.productID)
planGrant := proofGrant(t, ctx, tx, to, plan.productID, "manual")
planProvision := proofConfer(t, ctx, tx, to, plan.productID, planGrant, "created")
addon := createTestProduct(t, ctx, tx, "p1-addon", 2, false)
addonGrant := proofGrant(t, ctx, tx, to, addon.productID, "promotional")
addonProvision := proofConfer(t, ctx, tx, to, addon.productID, addonGrant, "created")
// Revoke the addon: decree first, then end_conferral by source.
if _, err := q.RevokeGrant(ctx, entitlements.RevokeGrantParams{
GrantID: addonGrant.GrantID,
RevokedByPersonID: uuid.NullUUID{UUID: uuid.MustParse(to.person.PersonID), Valid: true},
RevocationReason: sql.NullString{String: "proof-1", Valid: true},
}); err != nil {
t.Fatalf("revoke addon decree: %v", err)
}
ended, err := q.EndConferral(ctx, entitlements.EndConferralParams{
GrantID: uuid.NullUUID{UUID: uuid.MustParse(addonGrant.GrantID), Valid: true},
ActorType: "operator",
})
if err != nil {
t.Fatalf("end addon conferral: %v", err)
}
if len(ended) != 1 || ended[0] != addonProvision {
t.Fatalf("ended = %v, want exactly [%s]", ended, addonProvision)
}
// The plan position is untouched.
if s := proofProvisionStatus(t, ctx, tx, planProvision); s != "active" {
t.Errorf("plan provision status = %q, want active", s)
}
if n := proofActiveJunctions(t, ctx, tx, planProvision); n != 1 {
t.Errorf("plan active junction rows = %d, want 1", n)
}
// The addon occupied no rung, so no position transition exists nor could
// be fabricated (transition_requires_occupancy).
if n := proofTransitions(t, ctx, tx, addonProvision); n != 0 {
t.Errorf("addon transitions = %d, want 0", n)
}
// Second revoke: refused at the decree level.
if _, err := q.RevokeGrant(ctx, entitlements.RevokeGrantParams{
GrantID: addonGrant.GrantID,
RevokedByPersonID: uuid.NullUUID{UUID: uuid.MustParse(to.person.PersonID), Valid: true},
RevocationReason: sql.NullString{String: "proof-1-again", Valid: true},
}); err != sql.ErrNoRows {
t.Errorf("second revoke decree err = %v, want sql.ErrNoRows (decree-level refusal)", err)
}
// A raw source-arc replay is success with zero effect, never an error.
replay, err := q.EndConferral(ctx, entitlements.EndConferralParams{
GrantID: uuid.NullUUID{UUID: uuid.MustParse(addonGrant.GrantID), Valid: true},
ActorType: "operator",
})
if err != nil {
t.Fatalf("end_conferral replay: %v", err)
}
if len(replay) != 0 {
t.Errorf("replay ended %v, want empty set", replay)
}
}
// Scenario 2 — Plan granted to an org that already has it: the second decree
// is recorded (the grant is the ledger entry) and confer no-ops, appending no
// provision, junction, or transition row.
func TestConferralProof_SamePlanRegrantNoop(t *testing.T) {
ctx, tx := proofBegin(t)
to := setupTestOrg(t, ctx, tx)
plan := createTestProduct(t, ctx, tx, "p2-plan", 5, false)
proofLadder(t, ctx, tx, "p2", plan.productID)
g1 := proofGrant(t, ctx, tx, to, plan.productID, "manual")
provision := proofConfer(t, ctx, tx, to, plan.productID, g1, "created")
g2 := proofGrant(t, ctx, tx, to, plan.productID, "manual")
noopProvision := proofConfer(t, ctx, tx, to, plan.productID, g2, "noop")
if noopProvision != provision {
t.Errorf("noop returned provision %s, want incumbent %s", noopProvision, provision)
}
// Both decrees stand as ledger rows.
if n := proofCount(t, ctx, tx,
`SELECT count(*) FROM core.grants WHERE product_id = $1 AND granted_to_org_id = $2`,
plan.productID, to.org.OrgID); n != 2 {
t.Errorf("grant rows = %d, want 2 (both decrees recorded)", n)
}
// No duplicated machinery.
if n := proofCount(t, ctx, tx,
`SELECT count(*) FROM core.pool_provisions WHERE pool_id = $1`, to.pool.PoolID); n != 1 {
t.Errorf("provisions = %d, want 1", n)
}
if n := proofActiveJunctions(t, ctx, tx, provision); n != 1 {
t.Errorf("junction rows = %d, want 1", n)
}
if n := proofTransitions(t, ctx, tx, provision); n != 1 {
t.Errorf("transitions = %d, want 1 (initiate only)", n)
}
}
// Scenario 3 — The bare-entitlement-set grant form is structurally
// impossible: grants.product_id is NOT NULL and grants.entitlement_set_id no
// longer exists.
func TestConferralProof_BareSetGrantUnrepresentable(t *testing.T) {
ctx, tx := proofBegin(t)
to := setupTestOrg(t, ctx, tx)
set := createTestProduct(t, ctx, tx, "p3-set", 1, false)
// A grant without a product is a NOT NULL violation.
proofSavepoint(t, ctx, tx, func() {
_, err := tx.ExecContext(ctx, `
INSERT INTO core.grants (product_id, granted_to_org_id, granted_by_person_id, grant_reason)
VALUES (NULL, $1, $2, 'manual')`,
to.org.OrgID, to.person.PersonID)
if err == nil {
t.Fatal("expected NOT NULL violation inserting grant without product_id, got nil")
}
if !strings.Contains(err.Error(), "product_id") {
t.Errorf("expected product_id not-null violation, got: %v", err)
}
})
// The bare-set column itself no longer exists.
proofSavepoint(t, ctx, tx, func() {
_, err := tx.ExecContext(ctx, `
INSERT INTO core.grants (product_id, entitlement_set_id, granted_to_org_id, granted_by_person_id, grant_reason)
VALUES ($1, $2, $3, $4, 'manual')`,
set.productID, set.setID, to.org.OrgID, to.person.PersonID)
if err == nil {
t.Fatal("expected undefined-column error naming entitlement_set_id, got nil")
}
if !strings.Contains(err.Error(), "entitlement_set_id") {
t.Errorf("expected entitlement_set_id undefined-column error, got: %v", err)
}
})
}
// Scenario 4 — Tier rows AND a non-recurring price co-occur legally (a
// lifetime-license tier): product_shape reports the dimensions separately and
// confer grants the ladder position.
func TestConferralProof_LadderTierWithOneTimePrice(t *testing.T) {
ctx, tx := proofBegin(t)
to := setupTestOrg(t, ctx, tx)
bq := billing.New(tx)
lifetime := createTestProduct(t, ctx, tx, "p4-lifetime", 5, false)
if _, err := bq.CreatePrice(ctx, billing.CreatePriceParams{
ProductID: lifetime.productID,
Currency: "usd",
UnitAmount: 19900,
// RecurringInterval left NULL: a one-time price.
}); err != nil {
t.Fatalf("create one-time price: %v", err)
}
proofLadder(t, ctx, tx, "p4", lifetime.productID)
var ladderCount int
var billingShape string
if err := tx.QueryRowContext(ctx,
`SELECT ladder_count, billing_shape FROM core.product_shape WHERE product_id = $1`,
lifetime.productID,
).Scan(&ladderCount, &billingShape); err != nil {
t.Fatalf("read product_shape: %v", err)
}
if ladderCount != 1 || billingShape != "one_time" {
t.Errorf("product_shape = (ladder_count=%d, billing_shape=%q), want (1, one_time)", ladderCount, billingShape)
}
grant := proofGrant(t, ctx, tx, to, lifetime.productID, "manual")
provision := proofConfer(t, ctx, tx, to, lifetime.productID, grant, "created")
if n := proofActiveJunctions(t, ctx, tx, provision); n != 1 {
t.Errorf("junction rows = %d, want 1 (position conferred despite one-time price)", n)
}
}
// Scenario 5 — Prepaid credit pack, as far as the schema supports today: a
// one-time price with usage_type NULL is the legal shape (consumption lives
// in the set's per-unit rule and quantity), and the
// usage_pricing_requires_recurrence CHECK forbids putting a usage flavor on a
// non-recurring price.
func TestConferralProof_PrepaidCreditPackShape(t *testing.T) {
ctx, tx := proofBegin(t)
q := entitlements.New(tx)
to := setupTestOrg(t, ctx, tx)
bq := billing.New(tx)
// Per-unit set rule: 1 site per purchased unit — the credit flavor.
pack := createTestProduct(t, ctx, tx, "p5-pack", 1, true)
if _, err := bq.CreatePrice(ctx, billing.CreatePriceParams{
ProductID: pack.productID,
Currency: "usd",
UnitAmount: 500,
}); err != nil {
t.Fatalf("create one-time pack price: %v", err)
}
// A usage-typed non-recurring price is structurally rejected.
proofSavepoint(t, ctx, tx, func() {
_, err := tx.ExecContext(ctx, `
INSERT INTO core.prices (product_id, currency, unit_amount, usage_type)
VALUES ($1, 'usd', 500, 'metered')`, pack.productID)
if err == nil {
t.Fatal("expected usage_pricing_requires_recurrence violation, got nil")
}
if !strings.Contains(err.Error(), "usage_pricing_requires_recurrence") {
t.Errorf("expected chk_prices_usage_pricing_requires_recurrence, got: %v", err)
}
})
// Conferring the pack with quantity scales the per-unit entitlement.
grant, err := q.CreateGrant(ctx, entitlements.CreateGrantParams{
ProductID: pack.productID,
GrantedToOrgID: uuid.NullUUID{UUID: uuid.MustParse(to.org.OrgID), Valid: true},
GrantedByPersonID: uuid.NullUUID{UUID: uuid.MustParse(to.person.PersonID), Valid: true},
GrantReason: "manual",
Quantity: 5,
ValidFrom: time.Now(),
})
if err != nil {
t.Fatalf("create pack grant: %v", err)
}
if _, _, err := q.Confer(ctx, entitlements.ConferParams{
PoolID: to.pool.PoolID,
ProductID: pack.productID,
GrantID: uuid.NullUUID{UUID: uuid.MustParse(grant.GrantID), Valid: true},
Quantity: 5,
ActorType: "operator",
}); err != nil {
t.Fatalf("confer pack: %v", err)
}
if err := entitlements.MaterializePoolEntitlements(ctx, q, to.pool.PoolID); err != nil {
t.Fatalf("materialize: %v", err)
}
if limit := getEntitlementLimit(t, ctx, q, to.pool.PoolID); limit != 5 {
t.Errorf("pack limit = %d, want 5 (1 per unit x qty 5)", limit)
}
}
// Scenario 6 — A product tiered into two ladders is conferred in ONE call:
// one provision, one junction row per ladder, one initiate transition per
// rung.
func TestConferralProof_TwoLadderConferral(t *testing.T) {
ctx, tx := proofBegin(t)
to := setupTestOrg(t, ctx, tx)
dual := createTestProduct(t, ctx, tx, "p6-dual", 5, false)
ladder1 := proofLadder(t, ctx, tx, "p6-a", dual.productID)
ladder2 := proofLadder(t, ctx, tx, "p6-b", dual.productID)
grant := proofGrant(t, ctx, tx, to, dual.productID, "manual")
provision := proofConfer(t, ctx, tx, to, dual.productID, grant, "created")
if n := proofCount(t, ctx, tx,
`SELECT count(*) FROM core.pool_provisions WHERE pool_id = $1`, to.pool.PoolID); n != 1 {
t.Errorf("provisions = %d, want 1", n)
}
if n := proofActiveJunctions(t, ctx, tx, provision); n != 2 {
t.Errorf("active junction rows = %d, want 2 (one per ladder)", n)
}
for _, ladder := range []string{ladder1, ladder2} {
if n := proofCount(t, ctx, tx,
`SELECT count(*) FROM core.pool_provision_ladders
WHERE provision_id = $1 AND plan_ladder_id = $2 AND status = 'active'`,
provision, ladder); n != 1 {
t.Errorf("junction rows on ladder %s = %d, want 1", ladder, n)
}
}
if n := proofTransitions(t, ctx, tx, provision); n != 2 {
t.Errorf("transitions = %d, want 2 (initiate per rung)", n)
}
}
// Scenario 7 — A cooperative with zero ladders: confer runs its off-ladder
// path — one provision, no junction, no transition, no ladder machinery
// consulted.
func TestConferralProof_NoLadderCooperative(t *testing.T) {
ctx, tx := proofBegin(t)
to := setupTestOrg(t, ctx, tx)
product := createTestProduct(t, ctx, tx, "p7-flat", 3, false)
grant := proofGrant(t, ctx, tx, to, product.productID, "manual")
provision := proofConfer(t, ctx, tx, to, product.productID, grant, "created")
if s := proofProvisionStatus(t, ctx, tx, provision); s != "active" {
t.Errorf("provision status = %q, want active", s)
}
if n := proofCount(t, ctx, tx,
`SELECT count(*) FROM core.pool_provision_ladders WHERE provision_id = $1`, provision); n != 0 {
t.Errorf("junction rows = %d, want 0", n)
}
if n := proofTransitions(t, ctx, tx, provision); n != 0 {
t.Errorf("transitions = %d, want 0", n)
}
}
// Scenario 8 — Label/structure disagreement is unrepresentable by
// dissolution: display_category claims nothing, so 'addon' on a laddered
// product changes no conferral behavior — the position is conferred exactly
// as with a NULL label.
func TestConferralProof_DisplayCategoryInert(t *testing.T) {
ctx, tx := proofBegin(t)
to := setupTestOrg(t, ctx, tx)
labeled := createTestProduct(t, ctx, tx, "p8-labeled", 5, false)
if _, err := tx.ExecContext(ctx,
`UPDATE core.products SET display_category = 'addon' WHERE product_id = $1`,
labeled.productID); err != nil {
t.Fatalf("set display_category: %v", err)
}
proofLadder(t, ctx, tx, "p8", labeled.productID)
grant := proofGrant(t, ctx, tx, to, labeled.productID, "manual")
provision := proofConfer(t, ctx, tx, to, labeled.productID, grant, "created")
if n := proofActiveJunctions(t, ctx, tx, provision); n != 1 {
t.Errorf("junction rows = %d, want 1 (label did not change position behavior)", n)
}
if n := proofTransitions(t, ctx, tx, provision); n != 1 {
t.Errorf("transitions = %d, want 1 (initiate recorded as for any tier product)", n)
}
}
// Scenario 9 — One authoritative read path per consumer: conferral reads
// product_conferral_shapes (a draft product refuses conferral), diagnostics
// reads product_shape (mixed billing reported honestly in its own column).
func TestConferralProof_OneAuthoritativeSurface(t *testing.T) {
ctx, tx := proofBegin(t)
q := entitlements.New(tx)
to := setupTestOrg(t, ctx, tx)
bq := billing.New(tx)
// Draft product: representable in the catalog, refused by confer.
set, err := q.CreateEntitlementSet(ctx, entitlements.CreateEntitlementSetParams{
Name: "p9-draft-set-" + uuid.New().String()[:8], IsActive: true,
})
if err != nil {
t.Fatalf("create draft set: %v", err)
}
var draftID string
if err := tx.QueryRowContext(ctx,
`INSERT INTO core.products (name, is_active, is_public, lifecycle_status, entitlement_set_id)
VALUES ('p9-draft', TRUE, FALSE, 'draft', $1) RETURNING product_id`, set.SetID,
).Scan(&draftID); err != nil {
t.Fatalf("create draft product: %v", err)
}
draftGrant := proofGrant(t, ctx, tx, to, draftID, "manual")
proofSavepoint(t, ctx, tx, func() {
_, _, err := q.Confer(ctx, entitlements.ConferParams{
PoolID: to.pool.PoolID,
ProductID: draftID,
GrantID: uuid.NullUUID{UUID: uuid.MustParse(draftGrant.GrantID), Valid: true},
Quantity: 1,
ActorType: "operator",
})
if err == nil {
t.Fatal("expected confer to refuse a draft product, got nil")
}
if !strings.Contains(err.Error(), "draft") {
t.Errorf("expected draft refusal, got: %v", err)
}
})
// Mixed billing: recurring + one-time active prices on one product is
// reported as 'mixed' in billing_shape, hidden behind no precedence rule.
mixed := createTestProduct(t, ctx, tx, "p9-mixed", 1, false)
if _, err := bq.CreatePrice(ctx, billing.CreatePriceParams{
ProductID: mixed.productID, Currency: "usd", UnitAmount: 1000,
RecurringInterval: sql.NullString{String: "month", Valid: true},
}); err != nil {
t.Fatalf("create recurring price: %v", err)
}
if _, err := bq.CreatePrice(ctx, billing.CreatePriceParams{
ProductID: mixed.productID, Currency: "usd", UnitAmount: 9900,
}); err != nil {
t.Fatalf("create one-time price: %v", err)
}
var billingShape string
if err := tx.QueryRowContext(ctx,
`SELECT billing_shape FROM core.product_shape WHERE product_id = $1`,
mixed.productID).Scan(&billingShape); err != nil {
t.Fatalf("read product_shape: %v", err)
}
if billingShape != "mixed" {
t.Errorf("billing_shape = %q, want mixed", billingShape)
}
// The conferral surface reports the structural shape independently.
var ladderCount int
if err := tx.QueryRowContext(ctx,
`SELECT DISTINCT ladder_count FROM core.product_conferral_shapes WHERE product_id = $1`,
mixed.productID).Scan(&ladderCount); err != nil {
t.Fatalf("read product_conferral_shapes: %v", err)
}
if ladderCount != 0 {
t.Errorf("conferral ladder_count = %d, want 0 (no tiers)", ladderCount)
}
}
// Scenario 10 — grant_reason is a closed classification: out-of-domain values
// cannot reach the column, and 'default' is structurally system-authored (the
// biconditional CHECK, in both directions).
func TestConferralProof_GrantReasonDomain(t *testing.T) {
ctx, tx := proofBegin(t)
to := setupTestOrg(t, ctx, tx)
product := createTestProduct(t, ctx, tx, "p10", 1, false)
insertGrant := func(reason string, byPerson bool) error {
grantedBy := sql.NullString{}
if byPerson {
grantedBy = sql.NullString{String: to.person.PersonID, Valid: true}
}
_, err := tx.ExecContext(ctx, `
INSERT INTO core.grants (product_id, granted_to_org_id, granted_by_person_id, grant_reason)
VALUES ($1, $2, $3, $4)`,
product.productID, to.org.OrgID, grantedBy, reason)
return err
}
expectViolation := func(reason string, byPerson bool, wantConstraint string) {
t.Helper()
proofSavepoint(t, ctx, tx, func() {
if err := insertGrant(reason, byPerson); err == nil {
t.Errorf("expected %s violation for reason %q, got nil", wantConstraint, reason)
} else if !strings.Contains(err.Error(), wantConstraint) {
t.Errorf("expected %s for reason %q, got: %v", wantConstraint, reason, err)
}
})
}
// The retired value 'trial' is outside the domain; free text cannot
// reach the reason column.
expectViolation("trial", true, "chk_grants_reason_domain")
expectViolation("special favor", true, "chk_grants_reason_domain")
// The biconditional, in both directions: operator-typed 'default' and a
// system-authored grant claiming anything else.
expectViolation("default", true, "chk_grants_default_iff_system_authored")
expectViolation("manual", false, "chk_grants_default_iff_system_authored")
// The legal shapes on both sides of the biconditional still insert.
if err := insertGrant("evaluation", true); err != nil {
t.Errorf("operator 'evaluation' grant should insert: %v", err)
}
if err := insertGrant("default", false); err != nil {
t.Errorf("system-authored 'default' grant should insert: %v", err)
}
}
// --- Confer timestamp regression ---------------------------------------------
// Regression for the transaction-timestamp bug: a Confer with zero
// ActivatedAt marshals as SQL NULL (database-resolved "now"), so it must
// succeed inside a transaction that has already done prior work — the
// Go-sampled wall clock is always ahead of the frozen transaction_timestamp
// and used to be rejected as "in the future". An explicitly future
// activation must still be refused.
func TestConfer_TimestampSemantics(t *testing.T) {
ctx, tx := proofBegin(t)
q := entitlements.New(tx)
// The fixture itself is the "prior statements in this transaction" load.
to := setupTestOrg(t, ctx, tx)
product := createTestProduct(t, ctx, tx, "reg-ts", 1, false)
grant := proofGrant(t, ctx, tx, to, product.productID, "manual")
// Explicit future activation is refused (probed first, inside a
// savepoint, so the raise doesn't abort the test transaction).
proofSavepoint(t, ctx, tx, func() {
_, _, err := q.Confer(ctx, entitlements.ConferParams{
PoolID: to.pool.PoolID,
ProductID: product.productID,
GrantID: uuid.NullUUID{UUID: uuid.MustParse(grant.GrantID), Valid: true},
Quantity: 1,
ActorType: "operator",
ActivatedAt: time.Now().Add(time.Hour),
})
if err == nil {
t.Fatal("expected future-activation refusal, got nil")
}
if !strings.Contains(err.Error(), "future") {
t.Errorf("expected future-activation error, got: %v", err)
}
})
if _, outcome, err := q.Confer(ctx, entitlements.ConferParams{
PoolID: to.pool.PoolID,
ProductID: product.productID,
GrantID: uuid.NullUUID{UUID: uuid.MustParse(grant.GrantID), Valid: true},
Quantity: 1,
ActorType: "operator",
// ActivatedAt zero: NULL-means-now.
}); err != nil {
t.Fatalf("confer with zero ActivatedAt inside an active tx: %v", err)
} else if outcome != "created" {
t.Fatalf("outcome = %q, want created", outcome)
}
}
// --- Vacancy-guarded baseline restoration ------------------------------------
// defaultLadderFixture wires an org whose org type (mutated inside the
// rolled-back tx) configures a default ladder with a rank-0 baseline product
// and a rank-1 plan product.
type defaultLadderFixture struct {
to testOrg
ladderID string
baseline testProduct
plan testProduct
}
func setupDefaultLadderFixture(t *testing.T, ctx context.Context, tx *sql.Tx) defaultLadderFixture {
t.Helper()
to := setupTestOrg(t, ctx, tx)
baseline := createTestProduct(t, ctx, tx, "restore-base", 1, false)
plan := createTestProduct(t, ctx, tx, "restore-plan", 5, false)
ladderID := proofLadder(t, ctx, tx, "restore", baseline.productID, plan.productID)
oq := organization.New(tx)
if _, err := oq.UpdateOrgTypeDefaultPlanLadder(ctx, organization.UpdateOrgTypeDefaultPlanLadderParams{
OrgType: "personal",
DefaultPlanLadderID: uuid.NullUUID{UUID: uuid.MustParse(ladderID), Valid: true},
}); err != nil {
t.Fatalf("set org-type default ladder: %v", err)
}
return defaultLadderFixture{to: to, ladderID: ladderID, baseline: baseline, plan: plan}
}
// Revoking the plan-holding grant leaves the default ladder vacant, so the
// guarded restoration confers a system-authored default decree returning the
// pool to the rank-0 tier.
func TestReapplyDefaultsIfVacant_RestoresVacantDefaultLadder(t *testing.T) {
ctx, tx := proofBegin(t)
q := entitlements.New(tx)
f := setupDefaultLadderFixture(t, ctx, tx)
grant := proofGrant(t, ctx, tx, f.to, f.plan.productID, "manual")
proofConfer(t, ctx, tx, f.to, f.plan.productID, grant, "created")
// Decree-first revoke, then end by source: the ladder is left vacant.
if _, err := q.RevokeGrant(ctx, entitlements.RevokeGrantParams{
GrantID: grant.GrantID,
RevokedByPersonID: uuid.NullUUID{UUID: uuid.MustParse(f.to.person.PersonID), Valid: true},
RevocationReason: sql.NullString{String: "restore-test", Valid: true},
}); err != nil {
t.Fatalf("revoke decree: %v", err)
}
if _, err := q.EndConferral(ctx, entitlements.EndConferralParams{
GrantID: uuid.NullUUID{UUID: uuid.MustParse(grant.GrantID), Valid: true},
ActorType: "operator",
ActorID: uuid.NullUUID{UUID: uuid.MustParse(f.to.person.PersonID), Valid: true},
}); err != nil {
t.Fatalf("end conferral: %v", err)
}
res, err := entitlements.ReapplyDefaultsIfVacant(ctx, tx, f.to.pool.PoolID, entitlements.Actor{
ActorType: "system", Reason: "restore-test",
})
if err != nil {
t.Fatalf("ReapplyDefaultsIfVacant: %v", err)
}
if res.Outcome != "created" {
t.Fatalf("restoration outcome = %q, want created", res.Outcome)
}
// The pool is back on the rank-0 baseline, delivered by a system-authored
// default decree.
if n := proofCount(t, ctx, tx, `
SELECT count(*) FROM core.pool_provision_ladders l
JOIN core.pool_provisions p ON p.provision_id = l.provision_id
WHERE l.pool_id = $1 AND l.plan_ladder_id = $2 AND l.status = 'active'
AND p.product_id = $3`,
f.to.pool.PoolID, f.ladderID, f.baseline.productID); n != 1 {
t.Errorf("active rank-0 baseline attachments = %d, want 1", n)
}
if n := proofCount(t, ctx, tx, `
SELECT count(*) FROM core.grants
WHERE granted_to_org_id = $1 AND grant_reason = 'default' AND granted_by_person_id IS NULL`,
f.to.org.OrgID); n != 1 {
t.Errorf("system-authored default decrees = %d, want 1", n)
}
}
// Ending an off-ladder grant while a subscription-sourced provision holds the
// default ladder finds the ladder occupied: the guard does nothing and never
// supersedes the other source.
func TestReapplyDefaultsIfVacant_NeverSupersedesAnotherSource(t *testing.T) {
ctx, tx := proofBegin(t)
q := entitlements.New(tx)
f := setupDefaultLadderFixture(t, ctx, tx)
// A subscription holds the pool's position on the default ladder.
subID := proofSubscription(t, ctx, tx, f.to.org.OrgID)
subProvision, outcome, err := q.Confer(ctx, entitlements.ConferParams{
PoolID: f.to.pool.PoolID,
ProductID: f.plan.productID,
SubscriptionID: uuid.NullUUID{UUID: subID, Valid: true},
Quantity: 1,
ActorType: "webhook",
})
if err != nil || outcome != "created" {
t.Fatalf("confer subscription plan: outcome=%q err=%v", outcome, err)
}
// An unrelated off-ladder grant is issued and then revoked.
addon := createTestProduct(t, ctx, tx, "restore-addon", 2, false)
addonGrant := proofGrant(t, ctx, tx, f.to, addon.productID, "promotional")
proofConfer(t, ctx, tx, f.to, addon.productID, addonGrant, "created")
if _, err := q.RevokeGrant(ctx, entitlements.RevokeGrantParams{
GrantID: addonGrant.GrantID,
RevokedByPersonID: uuid.NullUUID{UUID: uuid.MustParse(f.to.person.PersonID), Valid: true},
RevocationReason: sql.NullString{String: "guard-test", Valid: true},
}); err != nil {
t.Fatalf("revoke addon decree: %v", err)
}
if _, err := q.EndConferral(ctx, entitlements.EndConferralParams{
GrantID: uuid.NullUUID{UUID: uuid.MustParse(addonGrant.GrantID), Valid: true},
ActorType: "operator",
}); err != nil {
t.Fatalf("end addon conferral: %v", err)
}
res, err := entitlements.ReapplyDefaultsIfVacant(ctx, tx, f.to.pool.PoolID, entitlements.Actor{
ActorType: "system", Reason: "guard-test",
})
if err != nil {
t.Fatalf("ReapplyDefaultsIfVacant: %v", err)
}
if !res.AlreadyAtTier || res.Outcome != "noop" {
t.Fatalf("guard result = %+v, want noop (ladder occupied by subscription)", res)
}
// The subscription's position is untouched and no default decree exists.
if s := proofProvisionStatus(t, ctx, tx, subProvision); s != "active" {
t.Errorf("subscription provision status = %q, want active", s)
}
if n := proofCount(t, ctx, tx, `
SELECT count(*) FROM core.grants
WHERE granted_to_org_id = $1 AND grant_reason = 'default'`,
f.to.org.OrgID); n != 0 {
t.Errorf("default decrees = %d, want 0 (guard must not restore)", n)
}
}
// A pool whose only live plan position sits on a NON-default ladder (e.g. a
// grandfathered legacy grant) is occupied under the floor guard: ending an
// unrelated off-ladder grant restores nothing, and the pool never accretes
// the current default alongside its existing plan (org-type-default-change-flow
// delta scenarios for plan-enrollment-administration).
func TestReapplyDefaultsIfVacant_DefersToPlanOnNonDefaultLadder(t *testing.T) {
ctx, tx := proofBegin(t)
q := entitlements.New(tx)
f := setupDefaultLadderFixture(t, ctx, tx)
// The pool's plan lives on a second ladder that is not the org-type default.
legacyPlan := createTestProduct(t, ctx, tx, "floor-legacy", 3, false)
proofLadder(t, ctx, tx, "floor-legacy", legacyPlan.productID)
legacyGrant := proofGrant(t, ctx, tx, f.to, legacyPlan.productID, "legacy")
legacyProvision := proofConfer(t, ctx, tx, f.to, legacyPlan.productID, legacyGrant, "created")
// An unrelated off-ladder grant is issued and then revoked.
addon := createTestProduct(t, ctx, tx, "floor-addon", 2, false)
addonGrant := proofGrant(t, ctx, tx, f.to, addon.productID, "promotional")
proofConfer(t, ctx, tx, f.to, addon.productID, addonGrant, "created")
if _, err := q.RevokeGrant(ctx, entitlements.RevokeGrantParams{
GrantID: addonGrant.GrantID,
RevokedByPersonID: uuid.NullUUID{UUID: uuid.MustParse(f.to.person.PersonID), Valid: true},
RevocationReason: sql.NullString{String: "floor-guard-test", Valid: true},
}); err != nil {
t.Fatalf("revoke addon decree: %v", err)
}
if _, err := q.EndConferral(ctx, entitlements.EndConferralParams{
GrantID: uuid.NullUUID{UUID: uuid.MustParse(addonGrant.GrantID), Valid: true},
ActorType: "operator",
ActorID: uuid.NullUUID{UUID: uuid.MustParse(f.to.person.PersonID), Valid: true},
}); err != nil {
t.Fatalf("end addon conferral: %v", err)
}
res, err := entitlements.ReapplyDefaultsIfVacant(ctx, tx, f.to.pool.PoolID, entitlements.Actor{
ActorType: "system", Reason: "floor-guard-test",
})
if err != nil {
t.Fatalf("ReapplyDefaultsIfVacant: %v", err)
}
if !res.AlreadyAtTier || res.Outcome != "noop" {
t.Fatalf("guard result = %+v, want noop (pool occupied on a non-default ladder)", res)
}
// The legacy position is untouched, and the default ladder gained nothing:
// no union of old plan + current default.
if s := proofProvisionStatus(t, ctx, tx, legacyProvision); s != "active" {
t.Errorf("legacy provision status = %q, want active", s)
}
if n := proofCount(t, ctx, tx, `
SELECT count(*) FROM core.pool_provision_ladders
WHERE pool_id = $1 AND plan_ladder_id = $2`,
f.to.pool.PoolID, f.ladderID); n != 0 {
t.Errorf("default-ladder attachments = %d, want 0 (floor guard must not layer the default)", n)
}
if n := proofCount(t, ctx, tx, `
SELECT count(*) FROM core.grants
WHERE granted_to_org_id = $1 AND grant_reason = 'default'`,
f.to.org.OrgID); n != 0 {
t.Errorf("default decrees = %d, want 0 (guard must not restore)", n)
}
}
// The grandfather sunset, end to end: an org whose plan is held by a legacy
// grant on a NON-default ladder has that grant revoked — the floor guard then
// finds the pool with no live plan and converges it onto the CURRENT default,
// on a different ladder than the one the clause preserved
// (org-type-default-change-flow: "Grandfather clause can be sunset").
func TestReapplyDefaultsIfVacant_GrandfatherSunsetConvergesToCurrentDefault(t *testing.T) {
ctx, tx := proofBegin(t)
q := entitlements.New(tx)
f := setupDefaultLadderFixture(t, ctx, tx) // current default: ladder A, rank-0 = f.baseline
// Grandfathered state: a legacy grant holds the pool's only plan, on a
// second ladder that is not the org-type default.
legacyPlan := createTestProduct(t, ctx, tx, "sunset-legacy", 3, false)
proofLadder(t, ctx, tx, "sunset-legacy", legacyPlan.productID)
legacyGrant := proofGrant(t, ctx, tx, f.to, legacyPlan.productID, "legacy")
legacyProvision := proofConfer(t, ctx, tx, f.to, legacyPlan.productID, legacyGrant, "created")
// Sunset: the operator revokes the legacy grant (decree-first), its
// conferral ends by source, and the guarded restoration runs — the pool
// is left with no live plan, so the current default is conferred.
if _, err := q.RevokeGrant(ctx, entitlements.RevokeGrantParams{
GrantID: legacyGrant.GrantID,
RevokedByPersonID: uuid.NullUUID{UUID: uuid.MustParse(f.to.person.PersonID), Valid: true},
RevocationReason: sql.NullString{String: "sunset-test", Valid: true},
}); err != nil {
t.Fatalf("revoke legacy decree: %v", err)
}
if _, err := q.EndConferral(ctx, entitlements.EndConferralParams{
GrantID: uuid.NullUUID{UUID: uuid.MustParse(legacyGrant.GrantID), Valid: true},
ActorType: "operator",
ActorID: uuid.NullUUID{UUID: uuid.MustParse(f.to.person.PersonID), Valid: true},
}); err != nil {
t.Fatalf("end legacy conferral: %v", err)
}
res, err := entitlements.ReapplyDefaultsIfVacant(ctx, tx, f.to.pool.PoolID, entitlements.Actor{
ActorType: "system", Reason: "sunset-test",
})
if err != nil {
t.Fatalf("ReapplyDefaultsIfVacant: %v", err)
}
if res.Outcome != "created" {
t.Fatalf("restoration outcome = %q, want created (clause ended, pool converges)", res.Outcome)
}
// The legacy position is gone and the pool sits on the current default's
// rank-0 — not on the ladder the clause used to preserve.
if s := proofProvisionStatus(t, ctx, tx, legacyProvision); s != "ended" {
t.Errorf("legacy provision status = %q, want ended", s)
}
if n := proofCount(t, ctx, tx, `
SELECT count(*) FROM core.pool_provision_ladders l
JOIN core.pool_provisions p ON p.provision_id = l.provision_id
WHERE l.pool_id = $1 AND l.plan_ladder_id = $2 AND l.status = 'active'
AND p.product_id = $3`,
f.to.pool.PoolID, f.ladderID, f.baseline.productID); n != 1 {
t.Errorf("current-default attachments = %d, want 1 (converged)", n)
}
if n := proofCount(t, ctx, tx, `
SELECT count(*) FROM core.grants
WHERE granted_to_org_id = $1 AND grant_reason = 'default' AND granted_by_person_id IS NULL`,
f.to.org.OrgID); n != 1 {
t.Errorf("system default decrees = %d, want 1", n)
}
}