67 lines
2.9 KiB
TypeScript
67 lines
2.9 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { buildWorkItems } from "./work-items";
|
|
import type { StatusResponse } from "./types";
|
|
|
|
// Read from test/fixtures/status-contract.json relative to src/
|
|
const status = await Bun.file(
|
|
new URL("../test/fixtures/status-contract.json", import.meta.url).pathname
|
|
).json() as StatusResponse;
|
|
|
|
describe("buildWorkItems contract validation", () => {
|
|
test("satisfies relational properties on real status-contract payload", () => {
|
|
const items = buildWorkItems(status);
|
|
|
|
// 1. The merged issue key is absent from results
|
|
const mergedKey = "octo/widget#4";
|
|
expect(items.some((item) => item.key === mergedKey)).toBe(false);
|
|
|
|
// 2. The superseded older-failed delivery id is absent
|
|
const supersededId = "superseded-failed";
|
|
expect(items.some((item) => item.deliveryId === supersededId)).toBe(false);
|
|
|
|
// 3. The currently-failed issue is present with bucket: "failed"
|
|
const failedItem = items.find((item) => item.key === "octo/widget#2");
|
|
expect(failedItem).toBeDefined();
|
|
expect(failedItem?.bucket).toBe("failed");
|
|
expect(failedItem?.error).toBe("repro diverged");
|
|
|
|
// and sorts before any running card
|
|
const failedIndices = items
|
|
.map((item, idx) => (item.bucket === "failed" ? idx : null))
|
|
.filter((idx): idx is number => idx !== null);
|
|
const runningIndices = items
|
|
.map((item, idx) => (item.bucket === "running" ? idx : null))
|
|
.filter((idx): idx is number => idx !== null);
|
|
|
|
if (failedIndices.length > 0 && runningIndices.length > 0) {
|
|
const maxFailedIndex = Math.max(...failedIndices);
|
|
const minRunningIndex = Math.min(...runningIndices);
|
|
expect(maxFailedIndex).toBeLessThan(minRunningIndex);
|
|
}
|
|
|
|
// 4. The issue-less failed delivery is present with ref: null, bucket: "failed"
|
|
const orphanFailed = items.find((item) => item.deliveryId === "orphan-failed-x");
|
|
expect(orphanFailed).toBeDefined();
|
|
expect(orphanFailed?.ref).toBeNull();
|
|
expect(orphanFailed?.bucket).toBe("failed");
|
|
expect(orphanFailed?.error).toBe("orphan failed error");
|
|
|
|
// 5. The running issue appears exactly once with bucket: "running"
|
|
const runningItems = items.filter((item) => item.key === "octo/widget#1");
|
|
expect(runningItems).toHaveLength(1);
|
|
const runningItem = runningItems[0];
|
|
expect(runningItem.bucket).toBe("running");
|
|
expect(runningItem.live).not.toBeNull();
|
|
expect(runningItem.live?.model).toBe("anthropic/claude-3-5-sonnet");
|
|
expect(runningItem.live?.last_tool).toBe("edit");
|
|
|
|
// 6. Bucket order is non-decreasing by rank (failed < running < queued < active)
|
|
const ranks = { failed: 0, running: 1, queued: 2, active: 3 };
|
|
for (let i = 0; i < items.length - 1; i++) {
|
|
const rankA = ranks[items[i].bucket];
|
|
const rankB = ranks[items[i + 1].bucket];
|
|
expect(rankA).toBeLessThanOrEqual(rankB);
|
|
}
|
|
});
|
|
});
|