fix: pdf export (#8564)
* feat: pdf export * fix: tests * fix: tests --------- Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
parent
20e266c9bb
commit
b31c0195bc
23 changed files with 4287 additions and 62 deletions
149
apps/live/tests/services/pdf-export/effect-utils.test.ts
Normal file
149
apps/live/tests/services/pdf-export/effect-utils.test.ts
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
import { describe, it, expect, assert } from "vitest";
|
||||
import { Effect, Duration, Either } from "effect";
|
||||
import { withTimeoutAndRetry, recoverWithDefault, tryAsync } from "@/services/pdf-export/effect-utils";
|
||||
import { PdfTimeoutError } from "@/schema/pdf-export";
|
||||
|
||||
describe("effect-utils", () => {
|
||||
describe("withTimeoutAndRetry", () => {
|
||||
it("should succeed when effect completes within timeout", async () => {
|
||||
const effect = Effect.succeed("success");
|
||||
const wrapped = withTimeoutAndRetry("test-operation")(effect);
|
||||
|
||||
const result = await Effect.runPromise(wrapped);
|
||||
expect(result).toBe("success");
|
||||
});
|
||||
|
||||
it("should fail with PdfTimeoutError when effect exceeds timeout", async () => {
|
||||
const slowEffect = Effect.gen(function* () {
|
||||
yield* Effect.sleep(Duration.millis(500));
|
||||
return "success";
|
||||
});
|
||||
|
||||
const wrapped = withTimeoutAndRetry("test-operation", {
|
||||
timeoutMs: 50,
|
||||
maxRetries: 0,
|
||||
})(slowEffect);
|
||||
|
||||
const result = await Effect.runPromise(Effect.either(wrapped));
|
||||
|
||||
assert(Either.isLeft(result), "Expected Left but got Right");
|
||||
expect(result.left).toBeInstanceOf(PdfTimeoutError);
|
||||
expect((result.left as PdfTimeoutError).operation).toBe("test-operation");
|
||||
});
|
||||
|
||||
it("should retry on failure up to maxRetries times", async () => {
|
||||
const attemptCounter = { count: 0 };
|
||||
|
||||
const flakyEffect = Effect.gen(function* () {
|
||||
attemptCounter.count++;
|
||||
if (attemptCounter.count < 3) {
|
||||
return yield* Effect.fail(new Error("transient failure"));
|
||||
}
|
||||
return "success";
|
||||
});
|
||||
|
||||
const wrapped = withTimeoutAndRetry("test-operation", {
|
||||
timeoutMs: 5000,
|
||||
maxRetries: 3,
|
||||
})(flakyEffect);
|
||||
|
||||
const result = await Effect.runPromise(wrapped);
|
||||
|
||||
expect(result).toBe("success");
|
||||
expect(attemptCounter.count).toBe(3);
|
||||
});
|
||||
|
||||
it("should fail after exhausting retries", async () => {
|
||||
const effect = Effect.fail(new Error("permanent failure"));
|
||||
|
||||
const wrapped = withTimeoutAndRetry("test-operation", {
|
||||
timeoutMs: 5000,
|
||||
maxRetries: 2,
|
||||
})(effect);
|
||||
|
||||
const result = await Effect.runPromise(Effect.either(wrapped));
|
||||
|
||||
expect(result._tag).toBe("Left");
|
||||
});
|
||||
});
|
||||
|
||||
describe("recoverWithDefault", () => {
|
||||
it("should return success value when effect succeeds", async () => {
|
||||
const effect = Effect.succeed("success");
|
||||
const wrapped = recoverWithDefault("fallback")(effect);
|
||||
|
||||
const result = await Effect.runPromise(wrapped);
|
||||
expect(result).toBe("success");
|
||||
});
|
||||
|
||||
it("should return fallback value when effect fails", async () => {
|
||||
const effect = Effect.fail(new Error("failure"));
|
||||
const wrapped = recoverWithDefault("fallback")(effect);
|
||||
|
||||
const result = await Effect.runPromise(wrapped);
|
||||
expect(result).toBe("fallback");
|
||||
});
|
||||
|
||||
it("should log warning when recovering from error", async () => {
|
||||
const logs: string[] = [];
|
||||
|
||||
const effect = Effect.fail(new Error("test error")).pipe(
|
||||
recoverWithDefault("fallback"),
|
||||
Effect.tap(() => Effect.sync(() => logs.push("after recovery")))
|
||||
);
|
||||
|
||||
const result = await Effect.runPromise(effect);
|
||||
|
||||
expect(result).toBe("fallback");
|
||||
expect(logs).toContain("after recovery");
|
||||
});
|
||||
|
||||
it("should work with complex fallback objects", async () => {
|
||||
const fallback = { items: [], count: 0, metadata: { version: 1 } };
|
||||
|
||||
const effect = Effect.fail(new Error("failure"));
|
||||
const wrapped = recoverWithDefault(fallback)(effect);
|
||||
|
||||
const result = await Effect.runPromise(wrapped);
|
||||
expect(result).toEqual(fallback);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tryAsync", () => {
|
||||
it("should wrap successful promise", async () => {
|
||||
const effect = tryAsync(
|
||||
() => Promise.resolve("success"),
|
||||
(err) => new Error(`wrapped: ${err}`)
|
||||
);
|
||||
|
||||
const result = await Effect.runPromise(effect);
|
||||
expect(result).toBe("success");
|
||||
});
|
||||
|
||||
it("should wrap rejected promise with custom error", async () => {
|
||||
const effect = tryAsync(
|
||||
() => Promise.reject(new Error("original")),
|
||||
(err) => new Error(`wrapped: ${(err as Error).message}`)
|
||||
);
|
||||
|
||||
const result = await Effect.runPromise(Effect.either(effect));
|
||||
|
||||
assert(Either.isLeft(result), "Expected Left but got Right");
|
||||
expect(result.left.message).toBe("wrapped: original");
|
||||
});
|
||||
|
||||
it("should handle synchronous throws", async () => {
|
||||
const effect = tryAsync(
|
||||
() => {
|
||||
throw new Error("sync error");
|
||||
},
|
||||
(err) => new Error(`caught: ${(err as Error).message}`)
|
||||
);
|
||||
|
||||
const result = await Effect.runPromise(Effect.either(effect));
|
||||
|
||||
assert(Either.isLeft(result), "Expected Left but got Right");
|
||||
expect(result.left.message).toBe("caught: sync error");
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue