yoonite-saga
Version:
> Orchestration de workflows transactionnels avec gestion de compensation (pattern Saga)
107 lines • 4.51 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
const class_validator_1 = require("class-validator");
const saga_builder_1 = require("./saga-builder");
const saga_processor_1 = require("./saga-processor");
describe("SagaProcessor", () => {
class TestDto {
}
__decorate([
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], TestDto.prototype, "name", void 0);
__decorate([
(0, class_validator_1.IsInt)(),
(0, class_validator_1.Min)(1),
__metadata("design:type", Number)
], TestDto.prototype, "count", void 0);
it("should execute a simple workflow and enrich context", async () => {
const builder = new saga_builder_1.SagaBuilder();
const workflow = builder
.step("init")
.invoke(() => ({ name: "foo", count: 2 }))
.build();
const processor = new saga_processor_1.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 saga_builder_1.SagaBuilder();
const workflow = builder
.step("invalid")
.invoke(() => ({ name: 123, count: 0 }))
.validate(TestDto)
.build();
const processor = new saga_processor_1.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 saga_builder_1.SagaBuilder();
const workflow = builder
.step("step1")
.invoke(() => ({ a: 1 }))
.step("step2")
.condition(() => false)
.invoke(() => ({ b: 2 }))
.build();
const processor = new saga_processor_1.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 saga_builder_1.SagaBuilder();
const workflow = builder
.step("step1")
.invoke(() => ({ a: 1 }))
.withCompensation(() => {
compensated = true;
})
.step("step2")
.invoke(() => {
throw new Error("fail");
})
.build();
const processor = new saga_processor_1.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 = [];
const services = {
logger: { log: (msg) => log.push(msg) },
};
const builder = new saga_builder_1.SagaBuilder();
const workflow = builder
.step("log")
.invoke((ctx, { logger }) => {
logger.log("hello");
})
.build();
const processor = new saga_processor_1.SagaProcessor(services);
processor.add(workflow);
await processor.start();
expect(log).toContain("hello");
});
});
//# sourceMappingURL=saga-processor.spec.js.map