feat(atlassian): implement milestone M2 - jira command surface
This commit is contained in:
321
skills/atlassian/shared/scripts/tests/jira.test.ts
Normal file
321
skills/atlassian/shared/scripts/tests/jira.test.ts
Normal file
@@ -0,0 +1,321 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { markdownToAdf } from "../src/adf.js";
|
||||
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("jira-search emits normalized results and uses pagination inputs", async () => {
|
||||
const calls: Array<{ url: string; init: RequestInit | undefined }> = [];
|
||||
|
||||
const fetchImpl: typeof fetch = async (input, init) => {
|
||||
const url = typeof input === "string" ? input : input.toString();
|
||||
calls.push({ url, init });
|
||||
|
||||
return jsonResponse({
|
||||
startAt: 10,
|
||||
maxResults: 2,
|
||||
total: 25,
|
||||
issues: [
|
||||
{
|
||||
key: "ENG-1",
|
||||
fields: {
|
||||
summary: "Add Jira search command",
|
||||
issuetype: { name: "Story" },
|
||||
status: { name: "In Progress" },
|
||||
assignee: { displayName: "Ada Lovelace" },
|
||||
created: "2026-03-01T00:00:00.000Z",
|
||||
updated: "2026-03-02T00:00:00.000Z",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
const result = await runCli({
|
||||
args: ["jira-search", "--jql", "project = ENG", "--max-results", "2", "--start-at", "10"],
|
||||
env: baseEnv,
|
||||
fetchImpl,
|
||||
});
|
||||
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(calls[0]?.url, "https://example.atlassian.net/rest/api/3/search");
|
||||
assert.equal(calls[0]?.init?.method, "POST");
|
||||
assert.match(String(calls[0]?.init?.headers), /Authorization/);
|
||||
assert.deepEqual(JSON.parse(String(calls[0]?.init?.body)), {
|
||||
jql: "project = ENG",
|
||||
maxResults: 2,
|
||||
startAt: 10,
|
||||
fields: ["summary", "issuetype", "status", "assignee", "created", "updated"],
|
||||
});
|
||||
|
||||
assert.deepEqual(JSON.parse(result.stdout), {
|
||||
ok: true,
|
||||
data: {
|
||||
issues: [
|
||||
{
|
||||
key: "ENG-1",
|
||||
summary: "Add Jira search command",
|
||||
issueType: "Story",
|
||||
status: "In Progress",
|
||||
assignee: "Ada Lovelace",
|
||||
created: "2026-03-01T00:00:00.000Z",
|
||||
updated: "2026-03-02T00:00:00.000Z",
|
||||
url: "https://example.atlassian.net/browse/ENG-1",
|
||||
},
|
||||
],
|
||||
startAt: 10,
|
||||
maxResults: 2,
|
||||
total: 25,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("jira-get returns normalized fields plus the raw Jira payload", async () => {
|
||||
const rawIssue = {
|
||||
key: "ENG-42",
|
||||
fields: {
|
||||
summary: "Ship v1",
|
||||
issuetype: { name: "Task" },
|
||||
status: { name: "Done" },
|
||||
assignee: { displayName: "Grace Hopper" },
|
||||
created: "2026-03-03T00:00:00.000Z",
|
||||
updated: "2026-03-04T00:00:00.000Z",
|
||||
},
|
||||
};
|
||||
|
||||
const fetchImpl: typeof fetch = async () => jsonResponse(rawIssue);
|
||||
|
||||
const result = await runCli({
|
||||
args: ["jira-get", "--issue", "ENG-42"],
|
||||
env: baseEnv,
|
||||
fetchImpl,
|
||||
});
|
||||
|
||||
assert.deepEqual(JSON.parse(result.stdout), {
|
||||
ok: true,
|
||||
data: {
|
||||
issue: {
|
||||
key: "ENG-42",
|
||||
summary: "Ship v1",
|
||||
issueType: "Task",
|
||||
status: "Done",
|
||||
assignee: "Grace Hopper",
|
||||
created: "2026-03-03T00:00:00.000Z",
|
||||
updated: "2026-03-04T00:00:00.000Z",
|
||||
url: "https://example.atlassian.net/browse/ENG-42",
|
||||
},
|
||||
},
|
||||
raw: rawIssue,
|
||||
});
|
||||
});
|
||||
|
||||
test("markdownToAdf converts headings, paragraphs, and bullet lists", () => {
|
||||
assert.deepEqual(markdownToAdf("# Summary\n\nBuild the Jira skill.\n\n- Search\n- Comment"), {
|
||||
type: "doc",
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: "heading",
|
||||
attrs: { level: 1 },
|
||||
content: [{ type: "text", text: "Summary" }],
|
||||
},
|
||||
{
|
||||
type: "paragraph",
|
||||
content: [{ type: "text", text: "Build the Jira skill." }],
|
||||
},
|
||||
{
|
||||
type: "bulletList",
|
||||
content: [
|
||||
{
|
||||
type: "listItem",
|
||||
content: [
|
||||
{
|
||||
type: "paragraph",
|
||||
content: [{ type: "text", text: "Search" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "listItem",
|
||||
content: [
|
||||
{
|
||||
type: "paragraph",
|
||||
content: [{ type: "text", text: "Comment" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test("jira-create dry-run emits an ADF request body without calling Jira", async () => {
|
||||
const workspace = createTempWorkspace();
|
||||
|
||||
try {
|
||||
workspace.write("description.md", "# New story\n\n- one\n- two");
|
||||
let called = false;
|
||||
|
||||
const result = await runCli({
|
||||
args: [
|
||||
"jira-create",
|
||||
"--type",
|
||||
"Story",
|
||||
"--summary",
|
||||
"Create the Atlassian skill",
|
||||
"--description-file",
|
||||
"description.md",
|
||||
"--dry-run",
|
||||
],
|
||||
cwd: workspace.cwd,
|
||||
env: {
|
||||
...baseEnv,
|
||||
ATLASSIAN_DEFAULT_PROJECT: "ENG",
|
||||
},
|
||||
fetchImpl: async () => {
|
||||
called = true;
|
||||
return jsonResponse({});
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(called, false);
|
||||
assert.deepEqual(JSON.parse(result.stdout), {
|
||||
ok: true,
|
||||
dryRun: true,
|
||||
data: {
|
||||
method: "POST",
|
||||
url: "https://example.atlassian.net/rest/api/3/issue",
|
||||
body: {
|
||||
fields: {
|
||||
project: { key: "ENG" },
|
||||
issuetype: { name: "Story" },
|
||||
summary: "Create the Atlassian skill",
|
||||
description: markdownToAdf("# New story\n\n- one\n- two"),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
workspace.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test("jira-update, jira-comment, and jira-transition dry-runs build the expected Jira requests", async () => {
|
||||
const workspace = createTempWorkspace();
|
||||
|
||||
try {
|
||||
workspace.write("issue.md", "Updated description");
|
||||
workspace.write("comment.md", "Comment body");
|
||||
|
||||
const update = await runCli({
|
||||
args: [
|
||||
"jira-update",
|
||||
"--issue",
|
||||
"ENG-9",
|
||||
"--summary",
|
||||
"Updated summary",
|
||||
"--description-file",
|
||||
"issue.md",
|
||||
"--dry-run",
|
||||
],
|
||||
cwd: workspace.cwd,
|
||||
env: baseEnv,
|
||||
});
|
||||
|
||||
const comment = await runCli({
|
||||
args: ["jira-comment", "--issue", "ENG-9", "--body-file", "comment.md", "--dry-run"],
|
||||
cwd: workspace.cwd,
|
||||
env: baseEnv,
|
||||
});
|
||||
|
||||
const transition = await runCli({
|
||||
args: ["jira-transition", "--issue", "ENG-9", "--transition", "31", "--dry-run"],
|
||||
cwd: workspace.cwd,
|
||||
env: baseEnv,
|
||||
});
|
||||
|
||||
assert.deepEqual(JSON.parse(update.stdout), {
|
||||
ok: true,
|
||||
dryRun: true,
|
||||
data: {
|
||||
method: "PUT",
|
||||
url: "https://example.atlassian.net/rest/api/3/issue/ENG-9",
|
||||
body: {
|
||||
fields: {
|
||||
summary: "Updated summary",
|
||||
description: markdownToAdf("Updated description"),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(JSON.parse(comment.stdout), {
|
||||
ok: true,
|
||||
dryRun: true,
|
||||
data: {
|
||||
method: "POST",
|
||||
url: "https://example.atlassian.net/rest/api/3/issue/ENG-9/comment",
|
||||
body: {
|
||||
body: markdownToAdf("Comment body"),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(JSON.parse(transition.stdout), {
|
||||
ok: true,
|
||||
dryRun: true,
|
||||
data: {
|
||||
method: "POST",
|
||||
url: "https://example.atlassian.net/rest/api/3/issue/ENG-9/transitions",
|
||||
body: {
|
||||
transition: {
|
||||
id: "31",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
workspace.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test("jira-transitions returns normalized transition options", async () => {
|
||||
const fetchImpl: typeof fetch = async () =>
|
||||
jsonResponse({
|
||||
transitions: [
|
||||
{
|
||||
id: "21",
|
||||
name: "Start Progress",
|
||||
to: { name: "In Progress" },
|
||||
hasScreen: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = await runCli({
|
||||
args: ["jira-transitions", "--issue", "ENG-9"],
|
||||
env: baseEnv,
|
||||
fetchImpl,
|
||||
});
|
||||
|
||||
assert.deepEqual(JSON.parse(result.stdout), {
|
||||
ok: true,
|
||||
data: {
|
||||
transitions: [
|
||||
{
|
||||
id: "21",
|
||||
name: "Start Progress",
|
||||
toStatus: "In Progress",
|
||||
hasScreen: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user