scripts/garden-inbox.test.mjs
scripts/garden-inbox.test.mjsBrowse 3 files
1,861 tokens
6,736 bytes
Token encoding: o200k_base
Snapshot 5b913e7
← Back to SKILL.md
1import assert from "node:assert/strict";2import test from "node:test";3 4import {5 acceptedCandidates,6 archiveTargetBody,7 classify,8 confirmationBody,9 decodeJwtPayload,10 fetchMineInboxRows,11 normalizeApiBase,12} from "./garden-inbox.mjs";13 14const now = new Date("2026-07-29T00:00:00.000Z");15 16function issue(overrides = {}) {17 return {18 id: "issue-1",19 identifier: "PAP-1",20 title: "Example",21 status: "done",22 updatedAt: "2025-01-01T00:00:00.000Z",23 blockedBy: [],24 ...overrides,25 };26}27 28function workspace(overrides = {}) {29 return {30 gone: false,31 error: null,32 status: "active",33 branchCommitAt: "2025-01-01T00:00:00.000Z",34 readiness: {35 git: { isMergedIntoBase: false, aheadCount: 0 },36 linkedIssues: [{ isTerminal: true }],37 },38 ...overrides,39 };40}41 42test("classifies each archive and keep condition into one bucket", () => {43 const merged = workspace({44 readiness: {45 git: { isMergedIntoBase: true, aheadCount: 0 },46 linkedIssues: [{ isTerminal: true }],47 },48 });49 assert.equal(classify(issue(), merged, 60, now).bucket, "A");50 assert.equal(classify(issue(), workspace(), 60, now).bucket, "B");51 assert.equal(classify(issue(), workspace({52 readiness: {53 git: { isMergedIntoBase: false, aheadCount: 2 },54 linkedIssues: [{ isTerminal: true }],55 },56 }), 60, now).bucket, "C");57 assert.equal(classify(issue({ status: "in_progress" }), workspace(), 60, now).bucket, "D");58});59 60test("keeps candidates when workspace safety inspection fails", () => {61 const result = classify(issue(), workspace({ error: "503 Service Unavailable", readiness: null }), 60, now);62 assert.equal(result.bucket, "D");63 assert.equal(result.reason.code, "workspace_inspection_failed");64});65 66test("returns only accepted options from the originating scan", () => {67 const candidate = { issueId: "issue-1", bucket: "A" };68 const scan = { scanId: "scan-1" };69 const interaction = {70 id: "interaction-1",71 kind: "request_checkbox_confirmation",72 idempotencyKey: "garden-inbox:scan-1:1:1",73 status: "accepted",74 payload: { options: [{ id: "issue-1" }] },75 result: { outcome: "accepted", selectedOptionIds: ["issue-1"] },76 };77 assert.deepEqual(acceptedCandidates(interaction, scan, new Map([[candidate.issueId, candidate]])), [candidate]);78});79 80test("unselected candidates start unchecked and are labelled as previously declined", () => {81 const scan = { scanId: "scan-1", staleDays: 60 };82 const candidates = [83 { issueId: "issue-1", identifier: "PAP-1", title: "Kept before", bucket: "B", lastActivityAt: "2026-05-01T00:00:00.000Z", reason: { message: "Stale." } },84 { issueId: "issue-2", identifier: "PAP-2", title: "New candidate", bucket: "B", lastActivityAt: "2026-05-01T00:00:00.000Z", reason: { message: "Stale." } },85 ];86 const body = confirmationBody(scan, candidates, 0, 1, new Set(["issue-1"]));87 assert.deepEqual(body.payload.defaultSelectedOptionIds, ["issue-2"]);88 assert.match(body.payload.options[0].description, /Declined in a previous pass; starts unchecked\./);89 assert.doesNotMatch(body.payload.options[1].description, /Declined in a previous pass/);90 assert.notEqual(body.idempotencyKey, "garden-inbox:scan-1:1:1");91 assert.equal(92 body.idempotencyKey,93 confirmationBody(scan, candidates, 0, 1, new Set(["issue-1"])).idempotencyKey,94 );95 assert.notEqual(96 body.idempotencyKey,97 confirmationBody(scan, candidates, 0, 1, new Set(["issue-2"])).idempotencyKey,98 );99 assert.equal(100 confirmationBody(scan, candidates, 0, 1).idempotencyKey,101 "garden-inbox:scan-1:1:1",102 );103 assert.equal(104 confirmationBody(scan, [candidates[1]], 1, 2, new Set(["issue-1"])).idempotencyKey,105 "garden-inbox:scan-1:2:2",106 );107});108 109test("preserves an overridden scan user for archive and undo requests", () => {110 assert.deepEqual(archiveTargetBody({ userId: "target-user" }), { userId: "target-user" });111});112 113test("rejects selected ids that were not offered", () => {114 const scan = { scanId: "scan-1" };115 const interaction = {116 id: "interaction-1",117 kind: "request_checkbox_confirmation",118 idempotencyKey: "garden-inbox:scan-1:1:1",119 status: "accepted",120 payload: { options: [{ id: "issue-1" }] },121 result: { outcome: "accepted", selectedOptionIds: ["issue-2"] },122 };123 assert.throws(124 () => acceptedCandidates(interaction, scan, new Map()),125 /was not an option in the interaction/,126 );127});128 129test("decodes the responsible user and normalizes API URLs locally", () => {130 const payload = Buffer.from(JSON.stringify({ responsible_user_id: "user-1" })).toString("base64url");131 assert.equal(decodeJwtPayload(`header.${payload}.signature`).responsible_user_id, "user-1");132 assert.equal(normalizeApiBase("https://paperclip.example/api/"), "https://paperclip.example");133});134 135test("scans Mine once per status and merges rows by issue id", async (t) => {136 const requestedStatuses = [];137 t.mock.method(globalThis, "fetch", async (url) => {138 const parsed = new URL(url);139 const status = parsed.searchParams.get("status");140 requestedStatuses.push(status);141 const rows = status === "todo"142 ? [issue({ id: "shared", status: "todo" })]143 : status === "done"144 ? [issue({ id: "shared", status: "done" }), issue({ id: "done-only" })]145 : [];146 return new Response(JSON.stringify(rows), { status: 200 });147 });148 149 const result = await fetchMineInboxRows({150 apiBase: "https://paperclip.example",151 apiKey: "secret",152 }, "user-1");153 154 assert.deepEqual(requestedStatuses, ["backlog", "todo", "in_progress", "in_review", "blocked", "done"]);155 assert.equal(result.rows.length, 2);156 assert.equal(result.rows.find((row) => row.id === "shared").status, "done");157 assert.equal(result.coverage.duplicateCount, 1);158 assert.equal(result.coverage.complete, true);159 assert.deepEqual(result.coverage.statusCounts, {160 backlog: 0,161 todo: 1,162 in_progress: 0,163 in_review: 0,164 blocked: 0,165 done: 2,166 });167});168 169test("warns when an individual status query reaches the Mine endpoint cap", async (t) => {170 t.mock.method(globalThis, "fetch", async (url) => {171 const status = new URL(url).searchParams.get("status");172 const rows = status === "done"173 ? Array.from({ length: 500 }, (_, index) => issue({ id: `done-${index}` }))174 : [];175 return new Response(JSON.stringify(rows), { status: 200 });176 });177 178 const result = await fetchMineInboxRows({179 apiBase: "https://paperclip.example",180 apiKey: "secret",181 }, "user-1");182 183 assert.equal(result.rows.length, 500);184 assert.equal(result.coverage.complete, false);185 assert.deepEqual(result.coverage.cappedStatuses, ["done"]);186 assert.equal(result.coverage.queryCap, 500);187});188