"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const AsyncRetry_1 = require("./AsyncRetry");
const _chai_spec_1 = require("./_chai.spec");
describe("AsyncRetry", () => {
    it("returns simple passing thunk", async () => {
        (0, _chai_spec_1.expect)(await (0, AsyncRetry_1.retryOnReject)(() => "hello", 0)).to.eql("hello");
    });
    it("rejects fail/pass thunk", async () => {
        const results = [new Error("boom"), 123];
        return (0, _chai_spec_1.expect)((0, AsyncRetry_1.retryOnReject)(() => {
            const r = results.shift();
            if (r != null && r instanceof Error)
                throw r;
            else
                return r;
        }, 0)).to.be.rejectedWith(/boom/);
    });
    it("accepts fail/pass thunk if retries > 0", async () => {
        const results = [new Error("boom"), 123];
        return (0, _chai_spec_1.expect)((0, AsyncRetry_1.retryOnReject)(() => {
            const r = results.shift();
            if (r != null && r instanceof Error)
                throw r;
            else
                return r;
        }, 1)).to.eventually.eql(123);
    });
    it("succeeds immediately with synchronous success", async () => {
        const result = await (0, AsyncRetry_1.retryOnReject)(() => "success", 3);
        (0, _chai_spec_1.expect)(result).to.eql("success");
    });
    it("succeeds immediately with asynchronous success", async () => {
        const result = await (0, AsyncRetry_1.retryOnReject)(async () => Promise.resolve("async success"), 3);
        (0, _chai_spec_1.expect)(result).to.eql("async success");
    });
    it("retries until success", async () => {
        let attempts = 0;
        const result = await (0, AsyncRetry_1.retryOnReject)(() => {
            attempts++;
            if (attempts < 3) {
                throw new Error("temporary failure");
            }
            return "eventual success";
        }, 3);
        (0, _chai_spec_1.expect)(attempts).to.eql(3);
        (0, _chai_spec_1.expect)(result).to.eql("eventual success");
    });
    it("fails after exhausting retries", async () => {
        let attempts = 0;
        const operation = (0, AsyncRetry_1.retryOnReject)(() => {
            attempts++;
            throw new Error("persistent failure");
        }, 2);
        await (0, _chai_spec_1.expect)(operation).to.be.rejectedWith("persistent failure");
        (0, _chai_spec_1.expect)(attempts).to.eql(3); // Initial + 2 retries
    });
    it("preserves error types", async () => {
        class CustomError extends Error {
            constructor(message) {
                super(message);
                this.name = "CustomError";
            }
        }
        const operation = (0, AsyncRetry_1.retryOnReject)(() => {
            throw new CustomError("custom error");
        }, 1);
        try {
            await operation;
            _chai_spec_1.expect.fail("Should have thrown");
        }
        catch (error) {
            (0, _chai_spec_1.expect)(error).to.be.instanceOf(CustomError);
            (0, _chai_spec_1.expect)(error.message).to.eql("custom error");
        }
    });
    it("handles async errors", async () => {
        let attempts = 0;
        const operation = (0, AsyncRetry_1.retryOnReject)(async () => {
            attempts++;
            return Promise.reject(new Error("async failure"));
        }, 2);
        await (0, _chai_spec_1.expect)(operation).to.be.rejectedWith("async failure");
        (0, _chai_spec_1.expect)(attempts).to.eql(3);
    });
    it("works with zero retries", async () => {
        let attempts = 0;
        const operation = (0, AsyncRetry_1.retryOnReject)(() => {
            attempts++;
            throw new Error("fail");
        }, 0);
        await (0, _chai_spec_1.expect)(operation).to.be.rejectedWith("fail");
        (0, _chai_spec_1.expect)(attempts).to.eql(1); // Only initial attempt
    });
    it("handles mixed sync/async successes", async () => {
        let attempts = 0;
        const result = await (0, AsyncRetry_1.retryOnReject)(async () => {
            attempts++;
            if (attempts === 1)
                return "sync";
            if (attempts === 2)
                return Promise.resolve("async");
            throw new Error("should not reach here");
        }, 2);
        (0, _chai_spec_1.expect)(attempts).to.eql(1);
        (0, _chai_spec_1.expect)(result).to.eql("sync");
    });
    it("handles null/undefined returns", async () => {
        const nullResult = await (0, AsyncRetry_1.retryOnReject)(() => null, 1);
        (0, _chai_spec_1.expect)(nullResult).to.eql(null);
        const undefinedResult = await (0, AsyncRetry_1.retryOnReject)(() => undefined, 1);
        (0, _chai_spec_1.expect)(undefinedResult).to.eql(undefined);
    });
    it("retries with increasing durations", async () => {
        const timestamps = [];
        try {
            await (0, AsyncRetry_1.retryOnReject)(() => {
                timestamps.push(Date.now());
                throw new Error("retry");
            }, 2, 50);
        }
        catch {
            (0, _chai_spec_1.expect)(timestamps).to.have.length(3);
            const [t0, t1, t2] = timestamps;
            // First retry delay should be ~50ms, second should be ~100ms
            const delay1 = t1 - t0;
            const delay2 = t2 - t1;
            (0, _chai_spec_1.expect)(delay1).to.be.gte(45);
            (0, _chai_spec_1.expect)(delay2).to.be.gte(90);
            // Second delay should be roughly double the first
            (0, _chai_spec_1.expect)(delay2).to.be.gte(delay1 * 1.5);
        }
    });
});
//# sourceMappingURL=AsyncRetry.spec.js.map