Skip to content
103 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import {
5 accountWentSummary,
6 confirmsUsername,
7 deletedWithAccount,
8 soleOwnerNote,
9 soleWorkspacesRefusal,
10 staffDeleteProblem,
11 staffDeletionRefusal,
12} from "./deleted-accounts.ts";
13
14const deletion = {
15 username: "ada",
16 workspaces: 2,
17 tokens: 1,
18 ssh_keys: 0,
19 applications: 0,
20 repositories: 0,
21 sole_owner_of: [],
22 protected: false,
23};
24
25const sole = (slug: string) => ({ slug, name: slug, members: 1, billing: null, protected: false });
26
27const went = { workspaces: 2, teams: 1, repositories: 3, tokens: 1, sshKeys: 2, staff: null, reason: null, deletedWorkspaces: [] };
28
29test("what went reads as one line", () => {
30 assert.equal(accountWentSummary(went), "2 workspaces, 1 team, 3 repositories, 1 token, 2 SSH keys");
31});
32
33test("only a protected account is refused outright", () => {
34 assert.equal(staffDeletionRefusal(deletion), null);
35 const owner = { ...deletion, sole_owner_of: [sole("acme")] };
36 assert.equal(staffDeletionRefusal(owner), null);
37 assert.equal(
38 staffDeletionRefusal({ ...owner, username: "g1t", protected: true }),
39 "g1t is protected and can never be deleted.",
40 );
41});
42
43test("workspaces it owns alone go with it, said up front", () => {
44 assert.equal(soleOwnerNote(deletion), null);
45 assert.match(soleOwnerNote({ ...deletion, sole_owner_of: [sole("ada")] })!, /^ada is the only owner of 1 workspace\. Deleting the account deletes it first/);
46 assert.match(soleOwnerNote({ ...deletion, sole_owner_of: [sole("a"), sole("b")] })!, /of 2 workspaces\. Deleting the account deletes them first/);
47 assert.equal(soleWorkspacesRefusal({ ...deletion, sole_owner_of: [sole("ada")] }), null);
48});
49
50test("a protected workspace or billing that cannot settle stops it, naming which", () => {
51 const owner = {
52 ...deletion,
53 sole_owner_of: [
54 sole("ada"),
55 { ...sole("flagon-io"), protected: true },
56 { ...sole("ada-labs"), billing: "ada-labs has an unpaid invoice. Pay it from the workspace's Billing page first." },
57 ],
58 };
59 assert.equal(
60 soleWorkspacesRefusal(owner),
61 "ada cannot be deleted with its workspaces yet. flagon-io is protected and can never be deleted. ada-labs has an unpaid invoice. Pay it from the workspace's Billing page first.",
62 );
63});
64
65test("staff confirm with the username", () => {
66 assert.ok(confirmsUsername("ada", " ADA "));
67 assert.ok(!confirmsUsername("ada", ""));
68 assert.ok(!confirmsUsername("ada", "grace"));
69});
70
71test("the form needs a reason, the username, and the box ticked when workspaces go too", () => {
72 const form = { username: "ada", reason: "Retired test account", confirm: "ada", withWorkspaces: false, acknowledged: false };
73 assert.equal(staffDeleteProblem(form), null);
74 assert.equal(staffDeleteProblem({ ...form, reason: "" }), "Say why the account is being deleted.");
75 assert.equal(staffDeleteProblem({ ...form, confirm: "grace" }), "Type ada to confirm.");
76 assert.equal(
77 staffDeleteProblem({ ...form, withWorkspaces: true }),
78 "Tick the box to say the workspaces it alone owns are deleted too.",
79 );
80 assert.equal(staffDeleteProblem({ ...form, withWorkspaces: true, acknowledged: true }), null);
81});
82
83test("workspaces deleted with the account are matched to their deletions", () => {
84 const waiting = {
85 workspaceId: "wsp_1",
86 slug: "ada",
87 name: "Ada",
88 deletedAt: "2026-10-08T00:00:00.000Z",
89 deletedBy: "staff@g1t.sh",
90 purgeAfter: "2026-11-07T00:00:00.000Z",
91 went: { repositories: 1, projects: 0, members: 1, billing: null, protected: false },
92 restorable: true,
93 };
94 const gone = deletedWithAccount(
95 { ...went, deletedWorkspaces: [{ workspaceId: "wsp_1", slug: "ada" }, { workspaceId: "wsp_2", slug: "ada-labs" }] },
96 [waiting],
97 );
98 assert.deepEqual(gone, [
99 { workspaceId: "wsp_1", slug: "ada", deleted: waiting },
100 { workspaceId: "wsp_2", slug: "ada-labs", deleted: null },
101 ]);
102 assert.deepEqual(deletedWithAccount(went, [waiting]), []);
103});