Files
member-console/internal/server/member_products_test.go
T
cgalo5758 16a15560c8 Add reconciled tier removal flow
Preview affected orgs by position source and require keep or migrate for
default-sourced positions. Commit deletion, renumbering, and holder
reconciliation atomically while preserving other-source delivery.
2026-07-12 21:09:43 -05:00

164 lines
4.7 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{
LadderKey: "tier-test-" + uuid.New().String()[:8],
Name: "Tier Test",
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",
Slug: "tier-org-" + uuid.New().String()[:8],
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",
Slug: "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",
Slug: "tier-org-2-" + uuid.New().String()[:8],
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",
Slug: "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))
}
}