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
218 lines
5.8 KiB
TypeScript
218 lines
5.8 KiB
TypeScript
import { introspectWorkflowInstance } from "cloudflare:test";
|
|
import { env } from "cloudflare:workers";
|
|
import { it } from "vitest";
|
|
|
|
const STATUS_COMPLETE = "complete";
|
|
const STATUS_ERROR = "errored";
|
|
const STEP_NAME = "AI content scan";
|
|
|
|
// This example implicitly disposes the Workflow instance
|
|
it("should mock a non-violation score and complete", async ({ expect }) => {
|
|
const instanceId = crypto.randomUUID();
|
|
const mockResult = { violationScore: 0 };
|
|
|
|
// CONFIG with `await using` to ensure Workflow instances cleanup:
|
|
await using instance = await introspectWorkflowInstance(
|
|
env.MODERATOR,
|
|
instanceId
|
|
);
|
|
await instance.modify(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.mockStepResult({ name: STEP_NAME }, mockResult);
|
|
});
|
|
|
|
await env.MODERATOR.create({
|
|
id: instanceId,
|
|
});
|
|
|
|
// ASSERTIONS:
|
|
expect(await instance.waitForStepResult({ name: STEP_NAME })).toEqual(
|
|
mockResult
|
|
);
|
|
expect(
|
|
await instance.waitForStepResult({ name: "auto approve content" })
|
|
).toEqual({ status: "auto_approved" });
|
|
|
|
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 instance
|
|
it("should mock the violation score calculation to fail 2 times and then complete", async ({
|
|
expect,
|
|
}) => {
|
|
const instanceId = crypto.randomUUID();
|
|
const mockResult = { violationScore: 0 };
|
|
|
|
// CONFIG:
|
|
const instance = await introspectWorkflowInstance(env.MODERATOR, instanceId);
|
|
|
|
try {
|
|
await instance.modify(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.mockStepError(
|
|
{ name: STEP_NAME },
|
|
new Error("Something went wrong!"),
|
|
2
|
|
);
|
|
await m.mockStepResult({ name: STEP_NAME }, mockResult);
|
|
});
|
|
|
|
await env.MODERATOR.create({
|
|
id: instanceId,
|
|
});
|
|
|
|
// ASSERTIONS:
|
|
expect(await instance.waitForStepResult({ name: STEP_NAME })).toEqual(
|
|
mockResult
|
|
);
|
|
expect(
|
|
await instance.waitForStepResult({ name: "auto approve content" })
|
|
).toEqual({ status: "auto_approved" });
|
|
|
|
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 instance.dispose();
|
|
}
|
|
});
|
|
|
|
it("should mock a violation score and complete", async ({ expect }) => {
|
|
const instanceId = crypto.randomUUID();
|
|
const mockResult = { violationScore: 99 };
|
|
|
|
await using instance = await introspectWorkflowInstance(
|
|
env.MODERATOR,
|
|
instanceId
|
|
);
|
|
await instance.modify(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.mockStepResult({ name: STEP_NAME }, mockResult);
|
|
});
|
|
|
|
await env.MODERATOR.create({
|
|
id: instanceId,
|
|
});
|
|
|
|
expect(await instance.waitForStepResult({ name: STEP_NAME })).toEqual(
|
|
mockResult
|
|
);
|
|
expect(
|
|
await instance.waitForStepResult({ name: "auto reject content" })
|
|
).toEqual({ status: "auto_rejected" });
|
|
|
|
await expect(instance.waitForStatus(STATUS_COMPLETE)).resolves.not.toThrow();
|
|
|
|
const output = await instance.getOutput();
|
|
expect(output).toEqual({ status: "auto_rejected" });
|
|
});
|
|
|
|
it("should be reviewed, accepted and complete", async ({ expect }) => {
|
|
const instanceId = crypto.randomUUID();
|
|
const mockResult = { violationScore: 50 };
|
|
|
|
await using instance = await introspectWorkflowInstance(
|
|
env.MODERATOR,
|
|
instanceId
|
|
);
|
|
await instance.modify(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.mockStepResult({ name: STEP_NAME }, mockResult);
|
|
await m.mockEvent({
|
|
type: "moderation-decision",
|
|
payload: { moderatorAction: "approve" },
|
|
});
|
|
});
|
|
|
|
await env.MODERATOR.create({
|
|
id: instanceId,
|
|
});
|
|
|
|
expect(await instance.waitForStepResult({ name: STEP_NAME })).toEqual(
|
|
mockResult
|
|
);
|
|
expect(
|
|
await instance.waitForStepResult({ name: "apply moderator decision" })
|
|
).toEqual({ status: "moderated", decision: "approve" });
|
|
|
|
await expect(instance.waitForStatus(STATUS_COMPLETE)).resolves.not.toThrow();
|
|
|
|
const output = await instance.getOutput();
|
|
expect(output).toEqual({ decision: "approve", status: "moderated" });
|
|
});
|
|
|
|
it("should disable retry delays when mocking step errors", async ({
|
|
expect,
|
|
}) => {
|
|
const instanceId = crypto.randomUUID();
|
|
const mockResult = { violationScore: 0 };
|
|
|
|
await using instance = await introspectWorkflowInstance(
|
|
env.MODERATOR,
|
|
instanceId
|
|
);
|
|
await instance.modify(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.disableRetryDelays();
|
|
await m.mockStepError(
|
|
{ name: STEP_NAME },
|
|
new Error("Transient failure"),
|
|
2
|
|
);
|
|
await m.mockStepResult({ name: STEP_NAME }, mockResult);
|
|
});
|
|
|
|
await env.MODERATOR.create({
|
|
id: instanceId,
|
|
});
|
|
|
|
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" });
|
|
});
|
|
|
|
it("should force human review to timeout and error", async ({ expect }) => {
|
|
const instanceId = crypto.randomUUID();
|
|
const mockResult = { violationScore: 50 };
|
|
|
|
await using instance = await introspectWorkflowInstance(
|
|
env.MODERATOR,
|
|
instanceId
|
|
);
|
|
await instance.modify(async (m) => {
|
|
await m.disableSleeps();
|
|
await m.mockStepResult({ name: STEP_NAME }, mockResult);
|
|
await m.forceEventTimeout({ name: "human review" });
|
|
});
|
|
|
|
await env.MODERATOR.create({
|
|
id: instanceId,
|
|
});
|
|
|
|
expect(await instance.waitForStepResult({ name: STEP_NAME })).toEqual(
|
|
mockResult
|
|
);
|
|
|
|
await expect(instance.waitForStatus(STATUS_ERROR)).resolves.not.toThrow();
|
|
|
|
const error = await instance.getError();
|
|
expect(error.name).toEqual("Error");
|
|
expect(error.message).toContain("Execution timed out");
|
|
});
|