Files
plane/apps/live/tests/services/pdf-export/effect-utils.test.ts
sriram veeraghanta 02d0ee3e0f chore: add copyright (#8584)
* feat: adding new copyright info on all files

* chore: adding CI
2026-01-27 13:54:22 +05:30

156 lines
5.0 KiB
TypeScript

/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
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");
});
});
});