49 lines
2.9 KiB
JavaScript
49 lines
2.9 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import crypto from "node:crypto";
|
|
import fs from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import { submitIdempotentServerJobResult } from "../../../tools/web/server-job-idempotency.mjs";
|
|
|
|
const h = (value) => crypto.createHash("sha256").update(value).digest("hex");
|
|
const identity = { requestId: "retry-1", projectId: "project-1", baseRevision: 4, sourceSha256: h("source"), settingsSha256: h("settings"), buildSha256: h("build") };
|
|
|
|
test("M13-04J reuses the exact verified result for the same request", async () => {
|
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), "m13-04j-reuse-"));
|
|
const options = { receiptDirectory: path.join(root, "receipts"), outputDirectory: path.join(root, "outputs") };
|
|
try {
|
|
const first = await submitIdempotentServerJobResult(identity, new Uint8Array([1, 2]), options);
|
|
const second = await submitIdempotentServerJobResult(identity, new Uint8Array([1, 2]), options);
|
|
assert.equal(first.reused, false);
|
|
assert.equal(second.reused, true);
|
|
assert.equal(first.outputSha256, second.outputSha256);
|
|
assert.equal((await fs.readdir(options.receiptDirectory)).length, 1);
|
|
} finally { await fs.rm(root, { recursive: true, force: true }); }
|
|
});
|
|
|
|
test("M13-04J rejects conflicting output/identity and isolates different requests", async () => {
|
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), "m13-04j-conflict-"));
|
|
const options = { receiptDirectory: path.join(root, "receipts"), outputDirectory: path.join(root, "outputs") };
|
|
try {
|
|
await submitIdempotentServerJobResult(identity, new Uint8Array([3]), options);
|
|
await assert.rejects(submitIdempotentServerJobResult(identity, new Uint8Array([4]), options), /SERVER_JOB_IDEMPOTENCY_CONFLICT/);
|
|
await assert.rejects(submitIdempotentServerJobResult({ ...identity, settingsSha256: h("changed") }, new Uint8Array([3]), options), /SERVER_JOB_IDEMPOTENCY_CONFLICT/);
|
|
const other = await submitIdempotentServerJobResult({ ...identity, requestId: "retry-2" }, new Uint8Array([4]), options);
|
|
assert.equal(other.reused, false);
|
|
assert.equal((await fs.readdir(options.receiptDirectory)).length, 2);
|
|
} finally { await fs.rm(root, { recursive: true, force: true }); }
|
|
});
|
|
|
|
test("M13-04J serializes concurrent duplicate requests", async () => {
|
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), "m13-04j-concurrent-"));
|
|
const options = { receiptDirectory: path.join(root, "receipts"), outputDirectory: path.join(root, "outputs") };
|
|
try {
|
|
const results = await Promise.all([
|
|
submitIdempotentServerJobResult({ ...identity, requestId: "retry-3" }, new Uint8Array([8]), options),
|
|
submitIdempotentServerJobResult({ ...identity, requestId: "retry-3" }, new Uint8Array([8]), options),
|
|
]);
|
|
assert.deepEqual(results.map((result) => result.reused).sort(), [false, true]);
|
|
} finally { await fs.rm(root, { recursive: true, force: true }); }
|
|
});
|