package console import ( "context" "encoding/json" "testing" "time" "github.com/carlosframework/platform/internal/domains" "github.com/carlosframework/platform/internal/release" ) // An account minted with an owner in its manifest is materialized WITH that // owner as a member. This is the whole point of naming an owner at creation: // an account whose owner is written nowhere is an account nobody can open, // which is the failure this feature exists to end. func TestMaterializeHonoursTheManifestOwner(t *testing.T) { ctx := context.Background() rel := release.NewDirStore(t.TempDir()) st := openTestStore(t) now := time.Now() id, sqid, err := domains.MintAccount(ctx, rel, "Kass", "paul@keymail.dev", now) if err != nil { t.Fatal(err) } body, _, err := rel.Get(ctx, domains.AccountManifestKey(sqid)) if err != nil { t.Fatal(err) } var m domains.AccountManifest if err := json.Unmarshal(body, &m); err != nil { t.Fatal(err) } if m.Owner != "paul@keymail.dev" { t.Fatalf("manifest owner = %q, want paul@keymail.dev", m.Owner) } n, err := MaterializeAccounts(ctx, rel, st) if err != nil || n != 1 { t.Fatalf("MaterializeAccounts = %d %v, want 1", n, err) } role, err := st.Role(id, "paul@keymail.dev") if err != nil || role != RoleOwner { t.Fatalf("role after materializing = %q %v, want owner", role, err) } // Re-materializing changes nothing — no membership is re-created, no role // is reset. This runs on every console start. if err := st.AddMember(id, "paul@keymail.dev", RoleMember, now); err == nil { t.Fatal("demoting the last owner should have been refused") } if n, err := MaterializeAccounts(ctx, rel, st); err != nil || n != 0 { t.Fatalf("second MaterializeAccounts = %d %v, want 0", n, err) } } // A manifest with no owner still materializes — every account minted before // this existed has none — and still creates no membership out of thin air. func TestMaterializeWithoutAnOwnerCreatesNoMembership(t *testing.T) { ctx := context.Background() rel := release.NewDirStore(t.TempDir()) st := openTestStore(t) id, _, err := domains.MintAccount(ctx, rel, "Legacy", "", time.Now()) if err != nil { t.Fatal(err) } if _, err := MaterializeAccounts(ctx, rel, st); err != nil { t.Fatal(err) } members, err := st.Members(id) if err != nil { t.Fatal(err) } if len(members) != 0 { t.Fatalf("members = %v, want none", members) } } // A manifest owner that the sign-in form would refuse is not a membership // anybody could ever claim, so it is skipped rather than written. func TestMaterializeRefusesAnUnusableOwner(t *testing.T) { ctx := context.Background() rel := release.NewDirStore(t.TempDir()) st := openTestStore(t) // MintAccount validates the owner, so a manifest this broken can only // arrive by hand-editing or corruption — which is exactly the case // materialization has to survive. Write it directly. id, sqid, err := domains.MintAccount(ctx, rel, "Typo", "", time.Now()) if err != nil { t.Fatal(err) } raw, err := json.Marshal(domains.AccountManifest{ ID: id, Sqid: sqid, Name: "Typo", Owner: "not-an-address", CreatedAt: time.Now(), }) if err != nil { t.Fatal(err) } body, etag, err := rel.Get(ctx, AccountManifestKey(sqid)) if err != nil { t.Fatal(err) } _ = body if _, err := rel.Put(ctx, AccountManifestKey(sqid), raw, etag); err != nil { t.Fatal(err) } if _, err := MaterializeAccounts(ctx, rel, st); err != nil { t.Fatal(err) } if members, err := st.Members(id); err != nil || len(members) != 0 { t.Fatalf("members = %v %v, want none", members, err) } } // The console's own mint stamps the owner into the bucket too, so a second // box materializing the same account knows who it belongs to. func TestConsoleMintStampsTheOwner(t *testing.T) { fx := newConsoleFixture(t) token := cliToken(t, fx, "paul@keymail.dev", "code-owner") _, out := postJSON(t, fx.ts.URL+"/auth/cli/accounts", map[string]string{"name": "kass"}, token) sqid := str(t, out, "sqid") body, _, err := fx.rel.Get(context.Background(), AccountManifestKey(sqid)) if err != nil { t.Fatal(err) } var m domains.AccountManifest if err := json.Unmarshal(body, &m); err != nil { t.Fatal(err) } if m.Owner != "paul@keymail.dev" { t.Fatalf("manifest owner = %q, want paul@keymail.dev", m.Owner) } }