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.
164 lines
4.7 KiB
Go
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))
|
|
}
|
|
}
|