yoonite-saga
Version:
> Orchestration de workflows transactionnels avec gestion de compensation (pattern Saga)
98 lines (91 loc) • 2.9 kB
text/typescript
import { IsInt, IsString, Min } from "class-validator";
import { SagaBuilder } from "./saga-builder";
import { SagaProcessor } from "./saga-processor";
describe("SagaProcessor", () => {
class TestDto {
()
name: string;
()
(1)
count: number;
}
it("should execute a simple workflow and enrich context", async () => {
const builder = new SagaBuilder();
const workflow = builder
.step("init")
.invoke(() => ({ name: "foo", count: 2 }))
.build();
const processor = new SagaProcessor();
processor.add(workflow);
const result = await processor.start();
expect(result.state).toBe("success");
expect(result.context).toEqual({ name: "foo", count: 2 });
expect(result.errors).toHaveLength(0);
});
it("should validate context and fail if invalid", async () => {
const builder = new SagaBuilder();
const workflow = builder
.step("invalid")
.invoke(() => ({ name: 123, count: 0 }))
.validate(TestDto)
.build();
const processor = new SagaProcessor();
processor.add(workflow);
const result = await processor.start();
expect(result.state).toBe("failed");
expect(result.errors.length).toBeGreaterThan(0);
expect(result.errors[0].message).toContain("Validation failed");
});
it("should skip step if condition is false", async () => {
const builder = new SagaBuilder();
const workflow = builder
.step("step1")
.invoke(() => ({ a: 1 }))
.step("step2")
.condition(() => false)
.invoke(() => ({ b: 2 }))
.build();
const processor = new SagaProcessor();
processor.add(workflow);
const result = await processor.start();
expect(result.context).toEqual({ a: 1 });
expect(result.state).toBe("success");
});
it("should execute compensation on error", async () => {
let compensated = false;
const builder = new SagaBuilder();
const workflow = builder
.step("step1")
.invoke(() => ({ a: 1 }))
.withCompensation(() => {
compensated = true;
})
.step("step2")
.invoke(() => {
throw new Error("fail");
})
.build();
const processor = new SagaProcessor();
processor.add(workflow);
const result = await processor.start();
expect(result.state).toBe("failed");
expect(compensated).toBe(true);
});
it("should inject services and use them in invokes", async () => {
const log: string[] = [];
const services = {
logger: { log: (msg: string) => log.push(msg) },
};
const builder = new SagaBuilder();
const workflow = builder
.step("log")
.invoke((ctx, { logger }) => {
logger.log("hello");
})
.build();
const processor = new SagaProcessor(services);
processor.add(workflow);
await processor.start();
expect(log).toContain("hello");
});
});