/** * Unit tests for the Jira / Confluence / GitLab REST helpers. * The lib modules are pure (no pi API), so we inject a mocked `fetchImpl`. * Run with: node --experimental-strip-types test/api.test.ts */ process.env.PI_KIT_AUDIT_OFF = "1"; process.env.JIRA_TOKEN = "t-jira"; process.env.CONFLUENCE_TOKEN = "t-conf"; process.env.GITLAB_TOKEN = "t-glab"; import { buildUrl, requireToken, ApiError } from "../extensions/lib/atlassian.ts"; import { jiraBaseUrl, issueGet } from "../extensions/lib/jira.ts"; import { gitlabBaseUrl, projectId } from "../extensions/lib/gitlab.ts"; import { confluenceBaseUrl } from "../extensions/lib/confluence.ts"; let fails = 0; function check(name: string, cond: boolean) { console.log(`${cond ? "PASS" : "FAIL"} ${name}`); if (!cond) fails++; } function okFetch(jsonBody: unknown): typeof fetch { return (async () => ({ ok: true, status: 200, json: async () => jsonBody, })) as any; } async function main() { // --- atlassian helpers --- check("buildUrl: appends query, drops empties", buildUrl("https://x/", "/a", { q: "1", e: undefined }) === "https://x/a?q=1"); check("buildUrl: no query when none", buildUrl("https://x", "/a") === "https://x/a"); check("requireToken: returns value", requireToken("JIRA_TOKEN", "h") === "t-jira"); check("requireToken: throws on missing", (() => { try { requireToken("NO_SUCH_VAR_ZZZ", "h"); return false; } catch (e) { return e instanceof ApiError; } })()); // --- config resolution (needs a real company.json present) --- check("jiraBaseUrl resolves", typeof jiraBaseUrl() === "string" && jiraBaseUrl() !== ""); check("gitlabBaseUrl ends with api/v4", gitlabBaseUrl().endsWith("/api/v4")); check("confluenceBaseUrl resolves", typeof confluenceBaseUrl() === "string" && confluenceBaseUrl() !== ""); // --- gitlab project id encoding --- check("projectId encodes namespaces", projectId("grp/repo") === "grp%2Frepo"); check("projectId passes through numeric id", projectId("42") === "42"); // --- issueGet through a mocked fetch (field mapping) --- const issueUrl = await issueGetByKey("PROJ-123"); check("issueGet hits /rest/api/2/issue/", issueUrl.includes("/rest/api/2/issue/")); console.log(fails === 0 ? "\nALL PASS" : `\n${fails} FAILED`); process.exit(fails === 0 ? 0 : 1); } async function issueGetByKey(key: string): Promise { // Capture the URL by injecting a mocked fetch into the issueGet call path. // We temporarily swap global.fetch. const realFetch = globalThis.fetch; let captured = ""; globalThis.fetch = (async (input: any) => { captured = String(input); return { ok: true, status: 200, json: async () => ({ key, fields: { summary: "s", status: { name: "Done" } } }) }; }) as any; try { await issueGet(key); } finally { globalThis.fetch = realFetch; } return captured; } main();