70bf21e064
Deploy (to testing) and Test Playground Preview Worker / Deploy Playground Preview Worker (testing) (push) Has been skipped
Deploy Workers Shared Staging / Deploy Workers Shared Staging (push) Failing after 0s
Prerelease / build (push) Has been skipped
Handle Changesets / Handle Changesets (push) Has been cancelled
Semgrep OSS scan / semgrep-oss (push) Has been cancelled
175 lines
5.2 KiB
TypeScript
175 lines
5.2 KiB
TypeScript
import { introspectWorkflow } from "cloudflare:test";
|
|
import { env, exports } from "cloudflare:workers";
|
|
import { describe, it } from "vitest";
|
|
|
|
const STATUS_COMPLETE = "complete";
|
|
const STEP_NAME = "AI content scan";
|
|
const mockResult = { violationScore: 0 };
|
|
|
|
// This example implicitly disposes the Workflow instance
|
|
it("workflow should be able to reach the end and be successful", async ({
|
|
expect,
|
|
}) => {
|
|
// CONFIG with `await using` to ensure Workflow instances cleanup:
|
|
await using introspector = await introspectWorkflow(env.MODERATOR);
|
|
await introspector.modifyAll(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.mockStepResult({ name: STEP_NAME }, mockResult);
|
|
});
|
|
|
|
await exports.default.fetch(`https://mock-worker.local/moderate`);
|
|
|
|
const instances = await introspector.get();
|
|
expect(instances.length).toBe(1);
|
|
|
|
// ASSERTIONS:
|
|
const instance = instances[0];
|
|
expect(await instance.waitForStepResult({ name: STEP_NAME })).toEqual(
|
|
mockResult
|
|
);
|
|
await expect(instance.waitForStatus(STATUS_COMPLETE)).resolves.not.toThrow();
|
|
|
|
const output = await instance.getOutput();
|
|
expect(output).toEqual({ status: "auto_approved" });
|
|
|
|
// DISPOSE: ensured by `await using`
|
|
});
|
|
|
|
// This example explicitly disposes the Workflow instances
|
|
it("workflow batch should be able to reach the end and be successful", async ({
|
|
expect,
|
|
}) => {
|
|
// CONFIG:
|
|
let introspector = await introspectWorkflow(env.MODERATOR);
|
|
try {
|
|
await introspector.modifyAll(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.mockStepResult({ name: STEP_NAME }, mockResult);
|
|
});
|
|
|
|
await exports.default.fetch(`https://mock-worker.local/moderate-batch`);
|
|
|
|
const instances = await introspector.get();
|
|
expect(instances.length).toBe(3);
|
|
|
|
// ASSERTIONS:
|
|
for (const instance of instances) {
|
|
expect(await instance.waitForStepResult({ name: STEP_NAME })).toEqual(
|
|
mockResult
|
|
);
|
|
await expect(
|
|
instance.waitForStatus(STATUS_COMPLETE)
|
|
).resolves.not.toThrow();
|
|
|
|
const output = await instance.getOutput();
|
|
expect(output).toEqual({ status: "auto_approved" });
|
|
}
|
|
} finally {
|
|
// DISPOSE:
|
|
// Workflow introspector should be disposed the end of each test, if no `await using` dyntax is used
|
|
// Also disposes all intercepted instances
|
|
await introspector.dispose();
|
|
}
|
|
});
|
|
|
|
describe("workflow instance lifecycle methods", () => {
|
|
it("should terminate a workflow instance", async ({ expect }) => {
|
|
// CONFIG:
|
|
await using introspector = await introspectWorkflow(env.MODERATOR);
|
|
await introspector.modifyAll(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.mockStepResult({ name: STEP_NAME }, { violationScore: 50 });
|
|
});
|
|
|
|
const res = await exports.default.fetch(
|
|
"https://mock-worker.local/moderate"
|
|
);
|
|
const data = (await res.json()) as { id: string; details: unknown };
|
|
|
|
const instances = await introspector.get();
|
|
expect(instances.length).toBe(1);
|
|
const instance = instances[0];
|
|
|
|
expect(await instance.waitForStepResult({ name: STEP_NAME })).toEqual({
|
|
violationScore: 50,
|
|
});
|
|
|
|
const handle = await env.MODERATOR.get(data.id);
|
|
await handle.terminate();
|
|
|
|
// ASSERTIONS:
|
|
await expect(instance.waitForStatus("terminated")).resolves.not.toThrow();
|
|
|
|
// DISPOSE: ensured by `await using`
|
|
});
|
|
|
|
it("should restart a workflow instance", async ({ expect }) => {
|
|
// CONFIG:
|
|
await using introspector = await introspectWorkflow(env.MODERATOR);
|
|
await introspector.modifyAll(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.mockStepResult({ name: STEP_NAME }, { violationScore: 50 });
|
|
await m.mockEvent({
|
|
type: "moderation-decision",
|
|
payload: { moderatorAction: "approve" },
|
|
});
|
|
});
|
|
|
|
const res = await exports.default.fetch(
|
|
"https://mock-worker.local/moderate"
|
|
);
|
|
const data = (await res.json()) as { id: string; details: unknown };
|
|
|
|
const instances = await introspector.get();
|
|
expect(instances.length).toBe(1);
|
|
const instance = instances[0];
|
|
|
|
expect(await instance.waitForStepResult({ name: STEP_NAME })).toEqual({
|
|
violationScore: 50,
|
|
});
|
|
|
|
const handle = await env.MODERATOR.get(data.id);
|
|
await handle.restart();
|
|
|
|
// Mocks survive instace restart, so the restarted workflow re-runs
|
|
// with the same config
|
|
await expect(
|
|
instance.waitForStatus(STATUS_COMPLETE)
|
|
).resolves.not.toThrow();
|
|
|
|
// DISPOSE: ensured by `await using`
|
|
});
|
|
|
|
// TODO(vaish): add restart-from-step test here once @cloudflare/workers-types ships restart options
|
|
|
|
it("should pause a workflow instance", async ({ expect }) => {
|
|
// CONFIG:
|
|
await using introspector = await introspectWorkflow(env.MODERATOR);
|
|
await introspector.modifyAll(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.mockStepResult({ name: STEP_NAME }, { violationScore: 50 });
|
|
});
|
|
|
|
const res = await exports.default.fetch(
|
|
"https://mock-worker.local/moderate"
|
|
);
|
|
const data = (await res.json()) as { id: string; details: unknown };
|
|
|
|
const instances = await introspector.get();
|
|
expect(instances.length).toBe(1);
|
|
const instance = instances[0];
|
|
|
|
expect(await instance.waitForStepResult({ name: STEP_NAME })).toEqual({
|
|
violationScore: 50,
|
|
});
|
|
|
|
const handle = await env.MODERATOR.get(data.id);
|
|
await handle.pause();
|
|
|
|
// ASSERTIONS:
|
|
await expect(instance.waitForStatus("paused")).resolves.not.toThrow();
|
|
|
|
// DISPOSE: ensured by `await using`
|
|
});
|
|
});
|