- Introduced a new contract for script returns, requiring an object with `output`, `context`, and `skipRemaining` fields. - Refactored existing scripts to align with the new return structure, ensuring compatibility with the updated context management. - Added utility functions for normalizing context and step results, improving the handling of script execution context. - Updated various scripts (e.g., fetch-binary, fetch-html, fetch-http) to return structured output and context, enhancing data flow and usability. - Improved error handling for script returns, ensuring clearer feedback when invalid structures are returned. This update significantly enhances the flexibility and clarity of script interactions within workflows.
106 lines
3.8 KiB
JavaScript
106 lines
3.8 KiB
JavaScript
import { migrate, db } from "../db.js";
|
|
import { createKvApi, kvDelete, kvSet } from "../kv-store.js";
|
|
import {
|
|
createFingerprintApi,
|
|
hashFingerprint,
|
|
} from "../script-fingerprint.js";
|
|
import { runScriptSource } from "../script-sandbox.js";
|
|
import { log } from "../logger.js";
|
|
|
|
await migrate();
|
|
|
|
const ns = "test/fingerprint-smoke";
|
|
const api = createKvApi(ns);
|
|
const fp = createFingerprintApi(api);
|
|
|
|
for (const key of ["a", "b", "cas", "age", "legacy", "script-key"]) {
|
|
await kvDelete(ns, key);
|
|
}
|
|
|
|
const h1 = hashFingerprint({ b: 1, a: 2 });
|
|
const h2 = hashFingerprint({ a: 2, b: 1 });
|
|
if (h1 !== h2) throw new Error("hash should ignore key order");
|
|
|
|
const bufHash = hashFingerprint(Buffer.from("hello"));
|
|
const sameLen = hashFingerprint(Buffer.from("world"));
|
|
if (bufHash !== sameLen) {
|
|
throw new Error("buffers of equal length should hash as { $bytes: length }");
|
|
}
|
|
const otherLen = hashFingerprint(Buffer.from("hi"));
|
|
if (bufHash === otherLen) throw new Error("different byte lengths should hash differently");
|
|
|
|
const first = await fp.check("a", { item: "one" });
|
|
if (!first.changed || first.previous !== null) {
|
|
throw new Error(`first check should be changed: ${JSON.stringify(first)}`);
|
|
}
|
|
|
|
const remembered = await fp.remember("a", first.hash);
|
|
if (remembered.hash !== first.hash || typeof remembered.at !== "string") {
|
|
throw new Error(`remember failed: ${JSON.stringify(remembered)}`);
|
|
}
|
|
|
|
const second = await fp.check("a", { item: "one" });
|
|
if (second.changed) throw new Error("second check should be unchanged");
|
|
if (second.previous !== first.hash) throw new Error("previous hash mismatch");
|
|
if (second.previousAt !== remembered.at) throw new Error("previousAt should be kept");
|
|
|
|
const claimNew = await fp.claim("b", "hello");
|
|
if (!claimNew.changed) throw new Error("first claim should be changed");
|
|
const storedAt = claimNew.at;
|
|
|
|
const claimSame = await fp.claim("b", "hello");
|
|
if (claimSame.changed) throw new Error("unchanged claim should return changed: false");
|
|
if (claimSame.at !== storedAt) throw new Error("unchanged claim should keep original at");
|
|
|
|
const oldAt = new Date(Date.now() - 8 * 24 * 60 * 60 * 1000).toISOString();
|
|
const oldHash = hashFingerprint("aged");
|
|
await kvSet(ns, "age", { hash: oldHash, at: oldAt });
|
|
|
|
const agedNoMax = await fp.check("age", "aged");
|
|
if (agedNoMax.changed) {
|
|
throw new Error("omit maxAge: matching hash should skip even if old");
|
|
}
|
|
|
|
const agedExpired = await fp.claim("age", "aged", { maxAge: "7d" });
|
|
if (!agedExpired.changed || !agedExpired.expired) {
|
|
throw new Error(`maxAge should expire old hash: ${JSON.stringify(agedExpired)}`);
|
|
}
|
|
if (agedExpired.at === oldAt) throw new Error("expired claim should write a new at");
|
|
|
|
const fresh = await fp.check("age", "aged", { maxAge: "7d" });
|
|
if (fresh.changed || fresh.expired) {
|
|
throw new Error(`freshly claimed hash should not expire: ${JSON.stringify(fresh)}`);
|
|
}
|
|
|
|
await kvSet(ns, "legacy", oldHash);
|
|
const legacyNoMax = await fp.check("legacy", "aged");
|
|
if (legacyNoMax.changed) {
|
|
throw new Error("legacy bare hash should still match without maxAge");
|
|
}
|
|
const legacyExpired = await fp.check("legacy", "aged", { maxAge: "1h" });
|
|
if (!legacyExpired.changed || !legacyExpired.expired) {
|
|
throw new Error(`legacy hash with maxAge should expire: ${JSON.stringify(legacyExpired)}`);
|
|
}
|
|
|
|
const script = `
|
|
export default async function (ctx) {
|
|
const result = await $fingerprint.claim("script-key", ctx.data.n);
|
|
return result;
|
|
}
|
|
`;
|
|
|
|
const scriptOut = await runScriptSource(
|
|
"fingerprint-smoke.js",
|
|
script,
|
|
{ data: { n: 1 }, context: {} },
|
|
{ log, workflowName: ns },
|
|
);
|
|
if (!scriptOut.changed) throw new Error("sandbox $fingerprint.claim should be new");
|
|
|
|
for (const key of ["a", "b", "cas", "age", "legacy", "script-key"]) {
|
|
await kvDelete(ns, key);
|
|
}
|
|
|
|
console.log("fingerprint smoke test passed");
|
|
await db.destroy();
|