Add synthetic dry-run sync regression test
This commit is contained in:
parent
6e7c092484
commit
6d323393a3
1 changed files with 84 additions and 0 deletions
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { resolve } from "node:path";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { Issue } from "@paperclipai/shared";
|
import type { Issue } from "@paperclipai/shared";
|
||||||
import { ATTACHMENT_NOTE } from "../src/constants.js";
|
import { ATTACHMENT_NOTE } from "../src/constants.js";
|
||||||
|
|
@ -76,6 +78,11 @@ function buildIssue(overrides: Partial<Issue> = {}): Issue {
|
||||||
} as Issue;
|
} as Issue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readExampleIssue(name: string): Issue {
|
||||||
|
const filePath = resolve(import.meta.dirname, "..", "examples", name);
|
||||||
|
return JSON.parse(readFileSync(filePath, "utf8")) as Issue;
|
||||||
|
}
|
||||||
|
|
||||||
describe("paperclip issue sync", () => {
|
describe("paperclip issue sync", () => {
|
||||||
it("selects issues by the configured sync label", () => {
|
it("selects issues by the configured sync label", () => {
|
||||||
const issue = buildIssue();
|
const issue = buildIssue();
|
||||||
|
|
@ -325,4 +332,81 @@ describe("paperclip issue sync", () => {
|
||||||
expect(retryComplete).toHaveBeenCalledOnce();
|
expect(retryComplete).toHaveBeenCalledOnce();
|
||||||
expect(retryQueueManualReview).not.toHaveBeenCalled();
|
expect(retryQueueManualReview).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reproduces the synthetic dry-run payload and reuses the stored mapping on retry", async () => {
|
||||||
|
const issue = readExampleIssue("first-dry-run-paperclip-issue.json");
|
||||||
|
const payload = buildForgejoIssuePayload(issue);
|
||||||
|
const createdRemoteIssue = {
|
||||||
|
id: 401,
|
||||||
|
number: 77,
|
||||||
|
url: "https://forgejo.example/acme/repo/issues/77",
|
||||||
|
apiUrl: "https://forgejo.example/api/v1/repos/acme/repo/issues/77"
|
||||||
|
};
|
||||||
|
const createRemoteIssue = vi.fn(async () => createdRemoteIssue);
|
||||||
|
const complete = vi.fn(async () => undefined);
|
||||||
|
|
||||||
|
expect(payload.title).toBe("[PRIA-DRY-1] Synthetic dry-run: reproduce outbound issue creation");
|
||||||
|
expect(payload.body).toContain("A controlled dry-run payload for the first Forgejo sync rehearsal.");
|
||||||
|
expect(payload.body).toContain(ATTACHMENT_NOTE);
|
||||||
|
expect(payload.body).toContain("trace.log | text/plain | 2.0 KiB");
|
||||||
|
expect(payload.body).toContain("<!-- paperclip-sync:company-dry-run:issue-dry-run-1 -->");
|
||||||
|
|
||||||
|
const firstResult = await syncIssueToForgejo(
|
||||||
|
{
|
||||||
|
config: { get: async () => ({ forgejoOwner: "acme", forgejoRepo: "repo" }) },
|
||||||
|
activity: { log: vi.fn(async () => undefined) }
|
||||||
|
} as never,
|
||||||
|
issue,
|
||||||
|
{
|
||||||
|
reserve: async () => ({ kind: "reserved" as const }),
|
||||||
|
createRemoteIssue,
|
||||||
|
recordRemote: vi.fn(async () => undefined),
|
||||||
|
complete,
|
||||||
|
fail: vi.fn(async () => undefined),
|
||||||
|
failAfterRemote: vi.fn(async () => undefined),
|
||||||
|
queueManualReview: vi.fn(async () => undefined)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const retryResult = await syncIssueToForgejo(
|
||||||
|
{
|
||||||
|
config: { get: async () => ({ forgejoOwner: "acme", forgejoRepo: "repo" }) },
|
||||||
|
activity: { log: vi.fn(async () => undefined) }
|
||||||
|
} as never,
|
||||||
|
issue,
|
||||||
|
{
|
||||||
|
reserve: async () => ({
|
||||||
|
kind: "existing" as const,
|
||||||
|
mapping: {
|
||||||
|
companyId: "company-dry-run",
|
||||||
|
paperclipIssueId: "issue-dry-run-1",
|
||||||
|
repoOwner: "acme",
|
||||||
|
repoName: "repo",
|
||||||
|
dedupeKey: "paperclip-issue:company-dry-run:issue-dry-run-1",
|
||||||
|
sourceTitle: payload.title,
|
||||||
|
sourceBody: payload.body,
|
||||||
|
attachmentMetadata: [],
|
||||||
|
manualReviewRequired: false,
|
||||||
|
reviewReasonCode: null,
|
||||||
|
forgejoIssueId: createdRemoteIssue.id,
|
||||||
|
forgejoIssueNumber: createdRemoteIssue.number,
|
||||||
|
forgejoIssueUrl: createdRemoteIssue.url,
|
||||||
|
forgejoApiUrl: createdRemoteIssue.apiUrl,
|
||||||
|
syncStatus: "synced",
|
||||||
|
lastError: null
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
createRemoteIssue,
|
||||||
|
complete,
|
||||||
|
fail: vi.fn(async () => undefined),
|
||||||
|
failAfterRemote: vi.fn(async () => undefined),
|
||||||
|
queueManualReview: vi.fn(async () => undefined)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(firstResult).toBe("created");
|
||||||
|
expect(retryResult).toBe("existing");
|
||||||
|
expect(createRemoteIssue).toHaveBeenCalledTimes(1);
|
||||||
|
expect(complete).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue