Files
ai-coding-skills/skills/atlassian/shared/scripts/tests/confluence.test.ts

352 lines
8.6 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { createTempWorkspace, jsonResponse, runCli } from "./helpers.js";
const baseEnv = {
ATLASSIAN_BASE_URL: "https://example.atlassian.net",
ATLASSIAN_EMAIL: "dev@example.com",
ATLASSIAN_API_TOKEN: "secret-token",
};
test("conf-search uses CQL search and normalizes page results", async () => {
const calls: Array<{ url: string; init: RequestInit | undefined }> = [];
const fetchImpl: typeof fetch = async (input, init) => {
calls.push({ url: typeof input === "string" ? input : input.toString(), init });
return jsonResponse({
results: [
{
content: {
id: "123",
type: "page",
title: "Runbook",
_links: { webui: "/spaces/OPS/pages/123/Runbook" },
},
excerpt: "Operational runbook",
},
],
start: 5,
limit: 1,
size: 1,
totalSize: 7,
});
};
const result = await runCli({
args: ["conf-search", "--query", "title ~ \"Runbook\"", "--max-results", "1", "--start-at", "5"],
env: baseEnv,
fetchImpl,
});
assert.equal(calls.length, 1);
assert.equal(
calls[0]?.url,
"https://example.atlassian.net/wiki/rest/api/search?cql=title+%7E+%22Runbook%22&limit=1&start=5",
);
assert.equal(calls[0]?.init?.method, "GET");
assert.deepEqual(JSON.parse(result.stdout), {
ok: true,
data: {
pages: [
{
id: "123",
title: "Runbook",
type: "page",
excerpt: "Operational runbook",
url: "https://example.atlassian.net/spaces/OPS/pages/123/Runbook",
},
],
startAt: 5,
maxResults: 1,
total: 7,
},
});
});
test("conf-get returns normalized page details plus raw payload", async () => {
const rawPage = {
id: "123",
status: "current",
title: "Runbook",
spaceId: "OPS",
version: { number: 4 },
body: {
storage: {
value: "<p>Runbook</p>",
representation: "storage",
},
},
_links: {
webui: "/spaces/OPS/pages/123/Runbook",
},
};
const result = await runCli({
args: ["conf-get", "--page", "123"],
env: baseEnv,
fetchImpl: async () => jsonResponse(rawPage),
});
assert.deepEqual(JSON.parse(result.stdout), {
ok: true,
data: {
page: {
id: "123",
title: "Runbook",
type: "page",
status: "current",
spaceId: "OPS",
version: 4,
body: "<p>Runbook</p>",
url: "https://example.atlassian.net/spaces/OPS/pages/123/Runbook",
},
},
raw: rawPage,
});
});
test("conf-children returns normalized direct children with pagination", async () => {
const result = await runCli({
args: ["conf-children", "--page", "123", "--max-results", "2", "--start-at", "1"],
env: baseEnv,
fetchImpl: async (input) => {
const url = typeof input === "string" ? input : input.toString();
assert.equal(
url,
"https://example.atlassian.net/wiki/api/v2/pages/123/direct-children?limit=2&cursor=1",
);
return jsonResponse({
results: [
{
id: "124",
title: "Child page",
type: "page",
status: "current",
spaceId: "OPS",
_links: { webui: "/spaces/OPS/pages/124/Child+page" },
},
],
});
},
});
assert.deepEqual(JSON.parse(result.stdout), {
ok: true,
data: {
pages: [
{
id: "124",
title: "Child page",
type: "page",
status: "current",
spaceId: "OPS",
url: "https://example.atlassian.net/spaces/OPS/pages/124/Child+page",
},
],
nextCursor: null,
},
});
});
test("conf-create dry-run emits a storage-format request body", async () => {
const workspace = createTempWorkspace();
try {
workspace.write("page.storage.html", "<p>Runbook</p>");
const result = await runCli({
args: [
"conf-create",
"--title",
"Runbook",
"--body-file",
"page.storage.html",
"--dry-run",
],
cwd: workspace.cwd,
env: {
...baseEnv,
ATLASSIAN_DEFAULT_SPACE: "OPS",
},
});
assert.deepEqual(JSON.parse(result.stdout), {
ok: true,
dryRun: true,
data: {
method: "POST",
url: "https://example.atlassian.net/wiki/api/v2/pages",
body: {
spaceId: "OPS",
title: "Runbook",
status: "current",
body: {
representation: "storage",
value: "<p>Runbook</p>",
},
},
},
});
} finally {
workspace.cleanup();
}
});
test("conf-update fetches the current page version and increments it for dry-run", async () => {
const workspace = createTempWorkspace();
const calls: Array<{ url: string; init: RequestInit | undefined }> = [];
try {
workspace.write("page.storage.html", "<p>Updated</p>");
const result = await runCli({
args: [
"conf-update",
"--page",
"123",
"--title",
"Runbook",
"--body-file",
"page.storage.html",
"--dry-run",
],
cwd: workspace.cwd,
env: baseEnv,
fetchImpl: async (input, init) => {
calls.push({ url: typeof input === "string" ? input : input.toString(), init });
return jsonResponse({
id: "123",
spaceId: "OPS",
status: "current",
title: "Runbook",
version: { number: 4 },
body: {
storage: {
value: "<p>Old</p>",
representation: "storage",
},
},
});
},
});
assert.equal(calls.length, 1);
assert.equal(
calls[0]?.url,
"https://example.atlassian.net/wiki/api/v2/pages/123?body-format=storage",
);
assert.deepEqual(JSON.parse(result.stdout), {
ok: true,
dryRun: true,
data: {
method: "PUT",
url: "https://example.atlassian.net/wiki/api/v2/pages/123",
body: {
id: "123",
status: "current",
title: "Runbook",
spaceId: "OPS",
version: {
number: 5,
},
body: {
representation: "storage",
value: "<p>Updated</p>",
},
},
},
});
} finally {
workspace.cleanup();
}
});
test("conf-comment dry-run targets footer comments", async () => {
const workspace = createTempWorkspace();
try {
workspace.write("comment.storage.html", "<p>Looks good</p>");
const result = await runCli({
args: ["conf-comment", "--page", "123", "--body-file", "comment.storage.html", "--dry-run"],
cwd: workspace.cwd,
env: baseEnv,
});
assert.deepEqual(JSON.parse(result.stdout), {
ok: true,
dryRun: true,
data: {
method: "POST",
url: "https://example.atlassian.net/wiki/api/v2/footer-comments",
body: {
pageId: "123",
body: {
representation: "storage",
value: "<p>Looks good</p>",
},
},
},
});
} finally {
workspace.cleanup();
}
});
test("conf-update surfaces version conflicts clearly", async () => {
const workspace = createTempWorkspace();
try {
workspace.write("page.storage.html", "<p>Updated</p>");
await assert.rejects(
runCli({
args: [
"conf-update",
"--page",
"123",
"--title",
"Runbook",
"--body-file",
"page.storage.html",
],
cwd: workspace.cwd,
env: baseEnv,
fetchImpl: async (input, init) => {
const url = typeof input === "string" ? input : input.toString();
if (url.endsWith("?body-format=storage")) {
return jsonResponse({
id: "123",
spaceId: "OPS",
status: "current",
title: "Runbook",
version: { number: 4 },
body: {
storage: {
value: "<p>Old</p>",
representation: "storage",
},
},
});
}
assert.equal(init?.method, "PUT");
return new Response(JSON.stringify({ message: "Conflict" }), {
status: 409,
statusText: "Conflict",
headers: { "content-type": "application/json" },
});
},
}),
/Confluence update conflict: page 123 was updated by someone else/,
);
} finally {
workspace.cleanup();
}
});