feat(cms): add recovery history and operational reliability tools
This commit is contained in:
1 parent
89526af344
commit
1ef405be0a
92 files changed
+6384
-267
No files matched your search
@@ -100,3 +100,104 @@ it("loads bounded history for the requesting owner", async () => {
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result.every((job) => job.userId === 4)).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps history pages stable while an older job changes", async () => {
|
||||
const s = await store();
|
||||
const older = { ...job(), createdAt: "2026-01-01T00:00:00.000Z" };
|
||||
const newer = { ...job(), createdAt: "2026-01-02T00:00:00.000Z" };
|
||||
await s.create(older);
|
||||
await s.create(newer);
|
||||
const first = await s.page({ userId: 4, limit: 1 });
|
||||
expect(first.jobs.map((j) => j.id)).toEqual([newer.id]);
|
||||
await s.save({ ...older, state: "completed" });
|
||||
const second = await s.page({
|
||||
userId: 4,
|
||||
limit: 1,
|
||||
before: first.nextCursor ?? undefined,
|
||||
});
|
||||
expect(second.jobs.map((j) => j.id)).toEqual([older.id]);
|
||||
expect(second.nextCursor).toBeNull();
|
||||
});
|
||||
it("creates a single retry across concurrent requests and excludes successful items", async () => {
|
||||
const s = await store();
|
||||
const original = {
|
||||
...job(),
|
||||
state: "completed" as const,
|
||||
items: [
|
||||
{
|
||||
id: 1,
|
||||
classname: "chair",
|
||||
name: "Chair",
|
||||
description: "",
|
||||
type: "flooritem",
|
||||
revision: 1,
|
||||
category: "",
|
||||
state: "failed" as const,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
classname: "lamp",
|
||||
name: "Lamp",
|
||||
description: "",
|
||||
type: "flooritem",
|
||||
revision: 1,
|
||||
category: "",
|
||||
state: "done" as const,
|
||||
},
|
||||
],
|
||||
};
|
||||
await s.create(original);
|
||||
const [a, b] = await Promise.all([
|
||||
s.retry(original.id, 4),
|
||||
s.retry(original.id, 4),
|
||||
]);
|
||||
expect(a?.id).toBe(b?.id);
|
||||
expect(a?.items.map((i) => i.classname)).toEqual(["chair"]);
|
||||
expect(await s.list()).toHaveLength(2);
|
||||
expect(await s.retry(original.id, 99)).toBeNull();
|
||||
});
|
||||
|
||||
it("does not retry active jobs or uncertain outcomes", async () => {
|
||||
const s = await store();
|
||||
const original = {
|
||||
...job(),
|
||||
items: [
|
||||
{
|
||||
id: 1,
|
||||
classname: "chair",
|
||||
name: "Chair",
|
||||
description: "",
|
||||
type: "flooritem",
|
||||
revision: 1,
|
||||
category: "",
|
||||
state: "interrupted" as const,
|
||||
},
|
||||
],
|
||||
};
|
||||
await s.create(original);
|
||||
expect(await s.retry(original.id, 4)).toBeNull();
|
||||
await s.save({ ...original, state: "interrupted" });
|
||||
expect(await s.retry(original.id, 4)).toBeNull();
|
||||
expect(await s.list()).toHaveLength(1);
|
||||
});
|
||||
it("paginates timestamp ties without duplicates and excludes other owners", async () => {
|
||||
const s = await store();
|
||||
const createdAt = "2026-01-01T00:00:00.000Z";
|
||||
const own = [
|
||||
{ ...job(), createdAt },
|
||||
{ ...job(), createdAt },
|
||||
{ ...job(), createdAt },
|
||||
];
|
||||
for (const entry of [...own, { ...job(), createdAt, userId: 99 }])
|
||||
await s.create(entry);
|
||||
const first = await s.page({ userId: 4, limit: 2 });
|
||||
const second = await s.page({
|
||||
userId: 4,
|
||||
limit: 2,
|
||||
before: first.nextCursor ?? undefined,
|
||||
});
|
||||
expect(new Set([...first.jobs, ...second.jobs].map((j) => j.id))).toEqual(
|
||||
new Set(own.map((j) => j.id)),
|
||||
);
|
||||
expect(first.jobs.length + second.jobs.length).toBe(3);
|
||||
});
|
||||
Reference in new issue
Block a user