| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { SCOPE_RESOURCES, permissionsOf, scopesOfPermissions } from "@g1t/contracts/scopes"; |
| 5 | |
| 6 | import { |
| 7 | changesTo, |
| 8 | currentTokensPath, |
| 9 | expiryChoices, |
| 10 | keptOutOfAll, |
| 11 | levelLabel, |
| 12 | lifetimeFromForm, |
| 13 | permissionChips, |
| 14 | permissionGroups, |
| 15 | policyNote, |
| 16 | reachSummary, |
| 17 | resourcesFor, |
| 18 | statusBadge, |
| 19 | tokenFromForm, |
| 20 | } from "./access-tokens.ts"; |
| 21 | |
| 22 | function form(fields: Record<string, string | string[]>) { |
| 23 | return { |
| 24 | get: (name: string) => { |
| 25 | const value = fields[name]; |
| 26 | return Array.isArray(value) ? (value[0] ?? null) : (value ?? null); |
| 27 | }, |
| 28 | getAll: (name: string) => { |
| 29 | const value = fields[name]; |
| 30 | return value === undefined ? [] : Array.isArray(value) ? value : [value]; |
| 31 | }, |
| 32 | }; |
| 33 | } |
| 34 | |
| 35 | const policy = { |
| 36 | allowTokensForAllWorkspaces: true, |
| 37 | allowTokensForThisWorkspace: true, |
| 38 | requireApproval: true, |
| 39 | maxLifetimeDays: null, |
| 40 | forbidNoExpiry: false, |
| 41 | }; |
| 42 | |
| 43 | test("a token made for one workspace reads its reach, repositories and permissions", () => { |
| 44 | const parsed = tokenFromForm( |
| 45 | form({ |
| 46 | name: "release bot", |
| 47 | workspace: "Acme", |
| 48 | expires: "30", |
| 49 | repository_selection: "selected", |
| 50 | repo: ["web", "acme/api", "web"], |
| 51 | "perm.code": "write", |
| 52 | "perm.issues": "none", |
| 53 | "perm.workflow_files": "write", |
| 54 | "perm.packages": "delete", |
| 55 | }), |
| 56 | ); |
| 57 | assert.ok(parsed.ok); |
| 58 | assert.equal(parsed.value.workspace, "acme"); |
| 59 | assert.equal(parsed.value.ttlSeconds, 30 * 86_400); |
| 60 | assert.deepEqual(parsed.value.repositories, ["web", "acme/api"]); |
| 61 | assert.deepEqual(parsed.value.permissions, { code: "write", workflow_files: "write", packages: "delete" }); |
| 62 | }); |
| 63 | |
| 64 | test("a token reaches every workspace, or none, and may never expire", () => { |
| 65 | const all = tokenFromForm(form({ name: "laptop", workspace: "*", expires: "never", "perm.repo": "read", repository_selection: "selected" })); |
| 66 | assert.ok(all.ok); |
| 67 | assert.equal(all.value.workspace, null); |
| 68 | assert.equal(all.value.repositorySelection, "all", "only a token for one workspace selects repositories"); |
| 69 | assert.equal(all.value.ttlSeconds, null); |
| 70 | const none = tokenFromForm(form({ name: "inbox", workspace: "-", expires: "7", "perm.notifications": "write" })); |
| 71 | assert.ok(none.ok); |
| 72 | assert.equal(none.value.workspace, null); |
| 73 | assert.equal(none.value.repositorySelection, "public"); |
| 74 | }); |
| 75 | |
| 76 | test("a workspace's own token holds nothing about a person, and reaches all or selected repositories", () => { |
| 77 | const parsed = tokenFromForm( |
| 78 | form({ name: "deploy", expires: "90", repository_selection: "public", "perm.code": "write", "perm.notifications": "write" }), |
| 79 | { workspaceOwned: true, owner: "acme" }, |
| 80 | ); |
| 81 | assert.ok(parsed.ok); |
| 82 | assert.equal(parsed.value.owner, "acme"); |
| 83 | assert.equal(parsed.value.repositorySelection, "all"); |
| 84 | assert.deepEqual(parsed.value.permissions, { code: "write" }); |
| 85 | assert.ok(resourcesFor(true).every((row) => row.group !== "account")); |
| 86 | assert.equal(resourcesFor(false).length, SCOPE_RESOURCES.length); |
| 87 | assert.ok(permissionGroups(true).every((group) => group.group !== "account")); |
| 88 | }); |
| 89 | |
| 90 | test("editing reads neither the expiry nor needs a name", () => { |
| 91 | const parsed = tokenFromForm(form({ expires: "9999", "perm.issues": "read" }), { editing: true }); |
| 92 | assert.ok(parsed.ok); |
| 93 | assert.equal(parsed.value.name, ""); |
| 94 | }); |
| 95 | |
| 96 | test("mistakes are named", () => { |
| 97 | const error = (fields: Record<string, string | string[]>) => (tokenFromForm(form(fields)) as { ok: false; error: string }).error; |
| 98 | assert.match(error({ workspace: "acme" }), /Name/); |
| 99 | assert.match(error({ name: "x", expires: "400", "perm.repo": "read" }), /366/); |
| 100 | assert.match(error({ name: "x", workspace: "acme", repository_selection: "selected", "perm.repo": "read" }), /repository/); |
| 101 | assert.match(error({ name: "x", "perm.workflow_files": "read" }), /Workflow files/); |
| 102 | assert.match(error({ name: "x", "perm.issues": "admin" }), /Issues/); |
| 103 | assert.match(error({ name: "x" }), /at least one permission/); |
| 104 | }); |
| 105 | |
| 106 | test("permissions are scopes read per resource", () => { |
| 107 | assert.deepEqual(permissionsOf(["repo:read", "repo:admin", "issues:write", "bogus"]), { repo: "admin", issues: "write" }); |
| 108 | assert.deepEqual(scopesOfPermissions({ issues: "write", repo: "read" }), ["repo:read", "issues:write"]); |
| 109 | assert.equal(Object.keys(permissionsOf(null)).length, SCOPE_RESOURCES.length, "full access is every resource"); |
| 110 | assert.equal(levelLabel("workflow_files", "write"), "Write"); |
| 111 | assert.equal(levelLabel("issues", "write"), "Read and write"); |
| 112 | assert.equal(levelLabel("packages", "delete"), "Read, write and delete"); |
| 113 | }); |
| 114 | |
| 115 | test("lifetimes follow the rules of every workspace a token reaches", () => { |
| 116 | assert.deepEqual(expiryChoices([null]), { days: [7, 30, 60, 90, 180, 366], never: true }); |
| 117 | assert.deepEqual(expiryChoices([{ ...policy, maxLifetimeDays: 90 }]), { days: [7, 30, 60, 90], never: false }); |
| 118 | assert.deepEqual(expiryChoices([policy, { ...policy, maxLifetimeDays: 45 }]), { days: [7, 30, 45], never: false }); |
| 119 | assert.equal(expiryChoices([{ ...policy, forbidNoExpiry: true }]).never, false); |
| 120 | assert.deepEqual(lifetimeFromForm(""), { ok: true, value: null }); |
| 121 | assert.deepEqual(lifetimeFromForm("90"), { ok: true, value: 90 }); |
| 122 | assert.equal(lifetimeFromForm("0").ok, false); |
| 123 | }); |
| 124 | |
| 125 | test("lists read a token in a line", () => { |
| 126 | const token = { |
| 127 | scopes: ["repo:read", "code:write", "issues:read"], |
| 128 | workspace: "acme", |
| 129 | repositorySelection: "selected" as const, |
| 130 | repositories: ["acme/web", "acme/api"], |
| 131 | }; |
| 132 | assert.equal(reachSummary(token), "acme · 2 repositories"); |
| 133 | assert.equal(reachSummary({ workspace: null, repositorySelection: "all" }), "All your workspaces"); |
| 134 | assert.equal(reachSummary({ workspace: null, repositorySelection: "public" }), "Your account and public repositories"); |
| 135 | assert.equal(reachSummary({ workspaceOwned: true, repositorySelection: "all" }), "All repositories"); |
| 136 | assert.deepEqual( |
| 137 | permissionChips(token).map((chip) => chip.label), |
| 138 | ["Repositories: read", "Code: write", "Issues: read"], |
| 139 | ); |
| 140 | assert.ok(permissionChips({ scopes: ["repo:admin"] })[0]!.dangerous); |
| 141 | assert.deepEqual(statusBadge("pending"), { label: "Pending approval", tone: "warn" }); |
| 142 | assert.equal(statusBadge("active"), null); |
| 143 | assert.match(policyNote("acme", policy, false) ?? "", /must approve/); |
| 144 | assert.equal(policyNote("acme", policy, true), null, "owners' own tokens never wait"); |
| 145 | assert.match(policyNote("acme", { ...policy, allowTokensForThisWorkspace: false }, true) ?? "", /does not allow/); |
| 146 | assert.deepEqual(keptOutOfAll([{ slug: "acme", policy: { ...policy, allowTokensForAllWorkspaces: false } }, { slug: "globex", policy }]), ["acme"]); |
| 147 | }); |
| 148 | |
| 149 | test("old addresses of the token settings go to the current pages", () => { |
| 150 | assert.equal(currentTokensPath("/settings/tokens", new URLSearchParams("tab=classic")), "/settings/tokens"); |
| 151 | assert.equal(currentTokensPath("/settings/tokens", new URLSearchParams("tab=classic&edit=tok_01ABC")), "/settings/tokens/tok_01ABC"); |
| 152 | assert.equal(currentTokensPath("/acme/-/personal-access-tokens", new URLSearchParams("kind=fine_grained")), "/acme/-/personal-access-tokens"); |
| 153 | assert.equal(currentTokensPath("/settings/tokens", new URLSearchParams("")), null); |
| 154 | }); |
| 155 | |
| 156 | test("saving a token sends only what changed", () => { |
| 157 | const token = { |
| 158 | id: "tok_1", name: "ci", createdAt: "", lastUsedAt: null, createdBy: null, legacy: false, expiresAt: null, |
| 159 | scopes: ["repo:read", "repo:write", "code:read", "code:write"], |
| 160 | workspace: "acme", repositorySelection: "selected" as const, repositories: ["acme/web"], description: null, |
| 161 | }; |
| 162 | const same = { owner: null, name: "", description: null, ttlSeconds: null, workspace: "acme", repositorySelection: "selected" as const, repositories: ["Acme/Web"], permissions: { repo: "write" as const, code: "write" as const } }; |
| 163 | assert.deepEqual(changesTo(token, same), {}, "the same permissions and repositories ask nothing again"); |
| 164 | assert.deepEqual(changesTo(token, { ...same, permissions: { repo: "write", code: "read" } }), { permissions: { repo: "write", code: "read" } }); |
| 165 | assert.deepEqual(changesTo(token, { ...same, repositories: ["acme/web", "acme/api"] }), { repositorySelection: "selected", repositories: ["acme/web", "acme/api"] }); |
| 166 | assert.deepEqual(changesTo(token, { ...same, repositorySelection: "all", repositories: [] }), { repositorySelection: "all" }); |
| 167 | assert.deepEqual(changesTo(token, { ...same, name: "release", description: "ships" }), { name: "release", description: "ships" }); |
| 168 | assert.deepEqual(changesTo(token, { ...same, website: true }), { website: true }); |
| 169 | assert.deepEqual(changesTo({ ...token, website: true }, { ...same, website: false }), { website: false }); |
| 170 | assert.deepEqual(changesTo({ ...token, website: true }, { ...same, website: true }), {}); |
| 171 | }); |
| 172 | |
| 173 | test("using the website is off unless checked, and never on a workspace's token", () => { |
| 174 | const base = { name: "e2e", workspace: "*", expires: "7", "perm.repo": "read" }; |
| 175 | const off = tokenFromForm(form(base)); |
| 176 | assert.ok(off.ok); |
| 177 | assert.equal(off.value.website, false); |
| 178 | const on = tokenFromForm(form({ ...base, website: "on" })); |
| 179 | assert.ok(on.ok); |
| 180 | assert.equal(on.value.website, true); |
| 181 | const workspace = tokenFromForm(form({ ...base, website: "on" }), { workspaceOwned: true, owner: "acme" }); |
| 182 | assert.ok(workspace.ok); |
| 183 | assert.equal(workspace.value.website, false); |
| 184 | }); |