| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { DANGEROUS_SCOPES, OAUTH_DEFAULT_SCOPES, SCOPES, SCOPE_GROUPS } from "@g1t/contracts/scopes"; |
| 5 | |
| 6 | import { |
| 7 | accessSummary, |
| 8 | consentedScopes, |
| 9 | describeExpiry, |
| 10 | everyScope, |
| 11 | expiryTtl, |
| 12 | grantFromForm, |
| 13 | impliedBy, |
| 14 | matchingPreset, |
| 15 | normalizeScopes, |
| 16 | requestedScopes, |
| 17 | scopesFromForm, |
| 18 | } from "./token-scopes.ts"; |
| 19 | |
| 20 | function form(fields: Record<string, string | string[]>) { |
| 21 | return { |
| 22 | get: (name: string) => { |
| 23 | const value = fields[name]; |
| 24 | return Array.isArray(value) ? (value[0] ?? null) : (value ?? null); |
| 25 | }, |
| 26 | getAll: (name: string) => { |
| 27 | const value = fields[name]; |
| 28 | return value === undefined ? [] : Array.isArray(value) ? value : [value]; |
| 29 | }, |
| 30 | }; |
| 31 | } |
| 32 | |
| 33 | test("every scope is on the checklist exactly once, admin ones under Dangerous", () => { |
| 34 | const listed = [...SCOPE_GROUPS.flatMap((group) => group.scopes), ...DANGEROUS_SCOPES]; |
| 35 | assert.deepEqual([...listed].sort(), SCOPES.map((row) => row.scope).sort()); |
| 36 | assert.equal(new Set(listed).size, listed.length); |
| 37 | assert.ok(DANGEROUS_SCOPES.every((scope) => scope.endsWith(":admin"))); |
| 38 | }); |
| 39 | |
| 40 | test("ticked boxes store the highest level of each resource", () => { |
| 41 | const parsed = scopesFromForm( |
| 42 | form({ scope: ["issues:read", "issues:write", "code:read", "agents:run", "bogus:read"] }), |
| 43 | ); |
| 44 | assert.deepEqual(parsed, { ok: true, value: ["code:read", "issues:write", "agents:run"] }); |
| 45 | assert.deepEqual(normalizeScopes(["repo:read", "repo:admin", "repo:write"]), ["repo:admin"]); |
| 46 | }); |
| 47 | |
| 48 | test("full access is null, and nothing ticked is refused", () => { |
| 49 | assert.deepEqual(scopesFromForm(form({ preset: "full", scope: "issues:read" })), { ok: true, value: null }); |
| 50 | assert.equal(scopesFromForm(form({ preset: "full" }), { allowFull: false }).ok, false); |
| 51 | assert.equal(scopesFromForm(form({ preset: "custom" })).ok, false); |
| 52 | assert.deepEqual(grantFromForm(form({ scope: "memory:read" })), { ok: true, value: { scopes: ["memory:read"] } }); |
| 53 | }); |
| 54 | |
| 55 | test("a higher level ticks the lower ones of its resource only", () => { |
| 56 | assert.equal(impliedBy(["issues:write"], "issues:read"), "issues:write"); |
| 57 | assert.equal(impliedBy(["repo:admin"], "repo:write"), "repo:admin"); |
| 58 | assert.equal(impliedBy(["issues:write"], "issues:write"), null); |
| 59 | assert.equal(impliedBy(["issues:write"], "pull_requests:read"), null); |
| 60 | assert.equal(impliedBy(["code:read"], "code:write"), null); |
| 61 | }); |
| 62 | |
| 63 | test("full access ticks the top level of everything", () => { |
| 64 | const all = everyScope(); |
| 65 | assert.ok(all.includes("repo:admin")); |
| 66 | assert.ok(all.includes("agents:run")); |
| 67 | assert.ok(all.includes("code:write")); |
| 68 | assert.ok(!all.includes("code:read")); |
| 69 | }); |
| 70 | |
| 71 | test("presets are recognised however their scopes are written", () => { |
| 72 | assert.equal(matchingPreset(null), "full"); |
| 73 | assert.equal(matchingPreset(["repo:read", "code:write", "workflows:write"]), "ci"); |
| 74 | assert.equal(matchingPreset([...OAUTH_DEFAULT_SCOPES]), "agent"); |
| 75 | assert.equal(matchingPreset(["issues:read"]), null); |
| 76 | }); |
| 77 | |
| 78 | test("a token's access reads plainly", () => { |
| 79 | assert.equal(accessSummary({ scopes: null, legacy: true }), "Legacy · full access"); |
| 80 | assert.equal(accessSummary({ scopes: null, legacy: false }), "Full access"); |
| 81 | assert.equal(accessSummary({ scopes: ["code:read", "code:write", "workflows:write", "repo:read"], legacy: false }), "CI"); |
| 82 | assert.equal(accessSummary({ scopes: ["issues:read", "issues:write", "memory:read"], legacy: false }), "2 scopes"); |
| 83 | assert.equal(accessSummary({ scopes: [], legacy: false }), "No scopes"); |
| 84 | }); |
| 85 | |
| 86 | test("an application asking for nothing usable gets the default set, never admin", () => { |
| 87 | assert.deepEqual(requestedScopes(null), OAUTH_DEFAULT_SCOPES); |
| 88 | assert.deepEqual(requestedScopes("openid profile *"), OAUTH_DEFAULT_SCOPES); |
| 89 | assert.ok(!requestedScopes("").some((scope) => scope.endsWith(":admin"))); |
| 90 | assert.deepEqual(requestedScopes("issues:write nonsense repo:read"), ["repo:read", "issues:write"]); |
| 91 | }); |
| 92 | |
| 93 | test("consent keeps only what was asked for", () => { |
| 94 | const requested = requestedScopes("repo:read issues:read issues:write"); |
| 95 | assert.deepEqual(consentedScopes(form({ scope: ["issues:write", "repo:admin", "secrets:admin"] }), requested), ["issues:write"]); |
| 96 | assert.deepEqual(consentedScopes(form({ scope: ["issues:read", "repo:read"] }), requested), ["repo:read", "issues:read"]); |
| 97 | assert.deepEqual(consentedScopes(form({}), requested), []); |
| 98 | }); |
| 99 | |
| 100 | test("expiry choices and words", () => { |
| 101 | assert.equal(expiryTtl("7"), 7 * 86_400); |
| 102 | assert.equal(expiryTtl("never"), undefined); |
| 103 | assert.equal(expiryTtl("13"), 90 * 86_400); |
| 104 | assert.equal(expiryTtl(null), 90 * 86_400); |
| 105 | const now = Date.parse("2026-10-05T00:00:00Z"); |
| 106 | assert.equal(describeExpiry(null, now), "No expiry"); |
| 107 | assert.equal(describeExpiry("2026-10-04T00:00:00Z", now), "Expired"); |
| 108 | assert.equal(describeExpiry("2026-10-12T00:00:00Z", now), "Expires in 7 days"); |
| 109 | assert.equal(describeExpiry("2026-10-05T01:00:00Z", now), "Expires in 1 hour"); |
| 110 | assert.equal(describeExpiry("2027-01-03T00:00:00Z", now), "Expires in 3 months"); |
| 111 | }); |