Files
member-console/internal/server/member_products_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

159 lines
4.5 KiB
Go

package server_test
import (
"context"
"testing"
"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/identity"
"git.coopcloud.tech/wiki-cafe/member-console/internal/organization"
"github.com/google/uuid"
)
// TestMemberTierLabel verifies that a member with an active ladder attachment
// sees their tier name, and a member without one sees an off-plan state.
func TestMemberTierLabel(t *testing.T) {
database := testDB(t)
ctx := context.Background()
tx, err := database.BeginTx(ctx, nil)
if err != nil {
t.Fatal(err)
}
defer tx.Rollback()
bq := billing.New(tx)
eq := entitlements.New(tx)
iq := identity.New(tx)
oq := organization.New(tx)
// Set up a plan product on a ladder
es, err := eq.CreateEntitlementSet(ctx, entitlements.CreateEntitlementSetParams{
Name: "tier-test-set-" + uuid.New().String()[:8],
})
if err != nil {
t.Fatalf("create entitlement set: %v", err)
}
product, err := bq.CreateProduct(ctx, billing.CreateProductParams{
Name: "Standard Plan",
IsActive: true,
IsPublic: true,
EntitlementSetID: uuid.NullUUID{UUID: uuid.MustParse(es.SetID), Valid: true},
LifecycleStatus: "published",
})
if err != nil {
t.Fatalf("create product: %v", err)
}
ladder, err := bq.CreatePlanLadder(ctx, billing.CreatePlanLadderParams{
Name: "Tier Test " + uuid.New().String()[:8],
IsActive: true,
})
if err != nil {
t.Fatalf("create ladder: %v", err)
}
_, err = bq.CreatePlanLadderTier(ctx, billing.CreatePlanLadderTierParams{
PlanLadderID: ladder.PlanLadderID,
ProductID: product.ProductID,
})
if err != nil {
t.Fatalf("create tier: %v", err)
}
// Create org type with default plan, org, person, pool
orgType := "tt-" + uuid.New().String()[:4]
_, err = tx.ExecContext(ctx,
`INSERT INTO core.org_types (org_type, display_name, is_active, default_plan_ladder_id)
VALUES ($1, $2, true, $3)`,
orgType, "Test Type", ladder.PlanLadderID,
)
if err != nil {
t.Fatalf("create org type: %v", err)
}
user, err := iq.CreateUser(ctx, "u-"+uuid.New().String())
if err != nil {
t.Fatalf("create user: %v", err)
}
person, err := iq.CreatePerson(ctx, identity.CreatePersonParams{
UserID: user.UserID,
DisplayName: "Test Member",
PrimaryEmail: "tier-" + uuid.New().String()[:8] + "@example.com",
PrimaryEmailVerified: true,
})
if err != nil {
t.Fatalf("create person: %v", err)
}
org, err := oq.CreateOrganization(ctx, organization.CreateOrganizationParams{
Name: "Tier Test Org",
OrgType: orgType,
OwnerPersonID: person.PersonID,
})
if err != nil {
t.Fatalf("create org: %v", err)
}
pool, err := eq.CreateResourcePool(ctx, entitlements.CreateResourcePoolParams{
OrgID: org.OrgID,
Name: "default",
PoolType: "default",
IsAutoManaged: true,
})
if err != nil {
t.Fatalf("create pool: %v", err)
}
// Scenario A: Pool with active attachment
actor := entitlements.Actor{ActorType: "system", Reason: "test"}
res, err := entitlements.ReapplyDefaultsForPool(ctx, tx, pool.PoolID, actor)
if err != nil {
t.Fatalf("reapply defaults: %v", err)
}
if res.Outcome != "created" || res.ProvisionID == "" {
t.Fatalf("expected a created provision, got outcome=%q provision=%q", res.Outcome, res.ProvisionID)
}
attachments, err := eq.GetActiveAttachmentsByPool(ctx, pool.PoolID)
if err != nil {
t.Fatalf("get attachments: %v", err)
}
if len(attachments) != 1 {
t.Fatalf("expected 1 attachment, got %d", len(attachments))
}
if attachments[0].ProductID != product.ProductID {
t.Fatalf("expected product %s, got %s", product.ProductID, attachments[0].ProductID)
}
// Scenario B: Create another org+pool without attachment
org2, err := oq.CreateOrganization(ctx, organization.CreateOrganizationParams{
Name: "Tier Test Org 2",
OrgType: orgType,
OwnerPersonID: person.PersonID,
})
if err != nil {
t.Fatalf("create org 2: %v", err)
}
pool2, err := eq.CreateResourcePool(ctx, entitlements.CreateResourcePoolParams{
OrgID: org2.OrgID,
Name: "default",
PoolType: "default",
IsAutoManaged: true,
})
if err != nil {
t.Fatalf("create pool 2: %v", err)
}
attachments2, err := eq.GetActiveAttachmentsByPool(ctx, pool2.PoolID)
if err != nil {
t.Fatalf("get attachments 2: %v", err)
}
if len(attachments2) != 0 {
t.Fatalf("expected 0 attachments for pool2, got %d", len(attachments2))
}
}