Files
member-console/internal/systemtenant/systemtenant_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

178 lines
5.9 KiB
Go

package systemtenant
import (
"context"
"database/sql"
"os"
"testing"
_ "github.com/jackc/pgx/v5/stdlib"
"git.coopcloud.tech/wiki-cafe/member-console/internal/db"
"git.coopcloud.tech/wiki-cafe/member-console/internal/identity"
"git.coopcloud.tech/wiki-cafe/member-console/internal/organization"
)
// newTestDB opens the test DB and runs core's migrations only (identity +
// organization, plus billing + entitlements, all part of the single core
// schema) — not the full internal/migrate.Sources() integration-registry
// set. internal/migrate imports internal/integrations, whose registered
// integrations (e.g. internal/integrations/fedwiki) call this package's
// Ensure from their WorkflowProvider Startup hook to re-derive the system
// workspace ID, so importing internal/migrate here would cycle back
// through this very package. This test only exercises core tables, so
// db.BaseSources() is sufficient.
func newTestDB(t *testing.T) *sql.DB {
t.Helper()
dsn := os.Getenv("TEST_DATABASE_URL")
if dsn == "" {
t.Skip("TEST_DATABASE_URL not set")
}
database, err := sql.Open("pgx", dsn)
if err != nil {
t.Fatalf("open db: %v", err)
}
t.Cleanup(func() { database.Close() })
sources := db.BaseSources()
if err := db.RunMigrations(database, sources); err != nil {
t.Fatalf("migrations: %v", err)
}
return database
}
// TestEnsureCreatesAndIsIdempotent: a fresh DB gets the full chain created and a
// valid workspace ID returned; a second call returns the same ID and creates no
// duplicate workspace. Also asserts the NULL default-ladder invariant.
func TestEnsureCreatesAndIsIdempotent(t *testing.T) {
database := newTestDB(t)
ctx := context.Background()
// The shared test-stack DB may already hold a live system tenant (an app
// boot creates one). The assertions below are therefore relative: Ensure
// must not add a workspace beyond whatever the first call establishes.
wsID, err := Ensure(ctx, database)
if err != nil {
t.Fatalf("Ensure (first): %v", err)
}
if wsID == "" {
t.Fatal("Ensure returned an empty workspace ID")
}
orgQ := organization.New(database)
org, err := orgQ.GetSystemOrganization(ctx)
if err != nil {
t.Fatalf("system org missing after Ensure: %v", err)
}
if org.OrgType != OrgType {
t.Fatalf("system org_type = %q, want %q", org.OrgType, OrgType)
}
ot, err := orgQ.GetOrgType(ctx, OrgType)
if err != nil {
t.Fatalf("system org_type row missing: %v", err)
}
if ot.DefaultPlanLadderID.Valid {
t.Fatal("system org_type must have NULL default_plan_ladder_id (no default grant)")
}
wssAfterFirst, err := orgQ.GetWorkspacesByOrgID(ctx, org.OrgID)
if err != nil {
t.Fatalf("list system workspaces: %v", err)
}
if len(wssAfterFirst) == 0 {
t.Fatal("expected at least 1 system workspace after Ensure")
}
wsID2, err := Ensure(ctx, database)
if err != nil {
t.Fatalf("Ensure (second): %v", err)
}
if wsID2 != wsID {
t.Fatalf("Ensure not idempotent: second call returned %q, want %q", wsID2, wsID)
}
wss, err := orgQ.GetWorkspacesByOrgID(ctx, org.OrgID)
if err != nil {
t.Fatalf("list system workspaces: %v", err)
}
if len(wss) != len(wssAfterFirst) {
t.Fatalf("repeat Ensure changed the workspace count: %d -> %d", len(wssAfterFirst), len(wss))
}
}
// TestEnsureAdoptsExistingWorkspace: when the system org already has a
// differently-named workspace (the removed ad-hoc seed used "reconcile"),
// Ensure adopts it rather than creating a second one (design D3).
func TestEnsureAdoptsExistingWorkspace(t *testing.T) {
database := newTestDB(t)
ctx := context.Background()
idQ := identity.New(database)
orgQ := organization.New(database)
// The adoption scenario needs to own the system tenant's workspace set:
// against a database where the system org already has a workspace (any
// app boot or a prior Ensure-based test creates one), seeding the legacy
// workspace makes two and the adoption assertion is meaningless. Only a
// fresh database can exercise this path.
if org, err := orgQ.GetSystemOrganization(ctx); err == nil {
if wss, err := orgQ.GetWorkspacesByOrgID(ctx, org.OrgID); err == nil && len(wss) > 0 {
t.Skip("system tenant already has a workspace — the legacy-adoption scenario needs a fresh database")
}
}
// Seed a system tenant whose workspace carries a different name.
if err := idQ.EnsureSystemUser(ctx, userOIDCSubject); err != nil {
t.Fatalf("seed user: %v", err)
}
user, err := idQ.GetUserByOIDCSubject(ctx, userOIDCSubject)
if err != nil {
t.Fatalf("get seeded user: %v", err)
}
if err := idQ.EnsureSystemPerson(ctx, identity.EnsureSystemPersonParams{
UserID: user.UserID, DisplayName: personName, PrimaryEmail: personEmail,
}); err != nil {
t.Fatalf("seed person: %v", err)
}
person, err := idQ.GetPersonByUserID(ctx, user.UserID)
if err != nil {
t.Fatalf("get seeded person: %v", err)
}
if err := orgQ.EnsureSystemOrgType(ctx, organization.EnsureSystemOrgTypeParams{
OrgType: OrgType, DisplayName: orgName,
}); err != nil {
t.Fatalf("seed org_type: %v", err)
}
if err := orgQ.EnsureSystemOrganization(ctx, organization.EnsureSystemOrganizationParams{
Name: orgName, OrgType: OrgType, OwnerPersonID: person.PersonID,
}); err != nil {
t.Fatalf("seed org: %v", err)
}
org, err := orgQ.GetSystemOrganization(ctx)
if err != nil {
t.Fatalf("get seeded org: %v", err)
}
legacy, err := orgQ.CreateWorkspace(ctx, organization.CreateWorkspaceParams{
OrgID: org.OrgID, Name: "Legacy"})
if err != nil {
t.Fatalf("seed legacy workspace: %v", err)
}
wsID, err := Ensure(ctx, database)
if err != nil {
t.Fatalf("Ensure: %v", err)
}
if wsID != legacy.WorkspaceID {
t.Fatalf("Ensure did not adopt the existing workspace: got %q, want %q", wsID, legacy.WorkspaceID)
}
wss, err := orgQ.GetWorkspacesByOrgID(ctx, org.OrgID)
if err != nil {
t.Fatalf("list system workspaces: %v", err)
}
if len(wss) != 1 {
t.Fatalf("expected exactly 1 (adopted) system workspace, got %d", len(wss))
}
}