diff --git a/tests/funding-deadline-validator.test.ts b/tests/funding-deadline-validator.test.ts index 6ddf482..b4c8afc 100644 --- a/tests/funding-deadline-validator.test.ts +++ b/tests/funding-deadline-validator.test.ts @@ -114,6 +114,52 @@ describe("validateFundingDeadline", () => { expect(validateFundingDeadline(iso, NOW)).toBeNull(); }); + describe("timezone normalization", () => { + const originalTz = process.env.TZ; + + beforeEach(() => { + process.env.TZ = "America/New_York"; + }); + + afterEach(() => { + process.env.TZ = originalTz; + }); + + it("evaluates equivalent instants identically regardless of string format", () => { + const utcString = "2026-03-03T12:00:00.000Z"; + const offsetString = "2026-03-03T14:00:00.000+02:00"; + expect(validateFundingDeadline(utcString, NOW)).toBeNull(); + expect(validateFundingDeadline(offsetString, NOW)).toBeNull(); + }); + + it("rejects equivalent past deadlines across timezones", () => { + const utcString = "2026-03-01T11:00:00.000Z"; + const offsetString = "2026-03-01T06:00:00.000-05:00"; + expect(validateFundingDeadline(utcString, NOW)).toMatchObject({ + code: "DUE_DATE_IN_PAST", + }); + expect(validateFundingDeadline(offsetString, NOW)).toMatchObject({ + code: "DUE_DATE_IN_PAST", + }); + }); + + it("enforces the exact boundary consistently across timezones", () => { + const utcBoundary = "2026-03-02T12:00:00.000Z"; + const offsetBoundary = "2026-03-02T17:30:00.000+05:30"; + expect(validateFundingDeadline(utcBoundary, NOW)).toBeNull(); + expect(validateFundingDeadline(offsetBoundary, NOW)).toBeNull(); + + const utcTooSoon = "2026-03-02T11:59:59.999Z"; + const offsetTooSoon = "2026-03-02T17:29:59.999+05:30"; + expect(validateFundingDeadline(utcTooSoon, NOW)).toMatchObject({ + code: "DUE_DATE_TOO_SOON", + }); + expect(validateFundingDeadline(offsetTooSoon, NOW)).toMatchObject({ + code: "DUE_DATE_TOO_SOON", + }); + }); + }); + describe("clock source", () => { afterEach(() => { jest.useRealTimers(); diff --git a/tests/investor-return.test.ts b/tests/investor-return.test.ts index b8f30ca..b0c5fa9 100644 --- a/tests/investor-return.test.ts +++ b/tests/investor-return.test.ts @@ -5,11 +5,26 @@ describe("computeInvestorReturn", () => { expect(computeInvestorReturn(2000n, 4000n, 4400n)).toBe(2200n); }); - it("floors the result when division is not exact, without rounding up", () => { + it("floors the result when division is not exact (repeating fractional ratios), without rounding up", () => { // (1000 * 1000) / 3000 = 333.33... -> floors to 333 expect(computeInvestorReturn(1000n, 3000n, 1000n)).toBe(333n); }); + it("handles repeating fractional ratios correctly in edge cases", () => { + // e.g., 2/3 of 1000 = 666 + expect(computeInvestorReturn(2000n, 3000n, 1000n)).toBe(666n); + }); + + it("returns 0 when yield (settledProceeds) is zero", () => { + expect(computeInvestorReturn(1000n, 5000n, 0n)).toBe(0n); + }); + + it("handles maximum supported yield without overflow and exact division", () => { + // 64-bit signed integer max + const int64Max = 9223372036854775807n; + expect(computeInvestorReturn(1000n, 2000n, int64Max)).toBe(int64Max / 2n); + }); + it("returns the full settled proceeds when investedAmount equals totalFunded", () => { expect(computeInvestorReturn(5000n, 5000n, 7500n)).toBe(7500n); }); @@ -27,4 +42,9 @@ describe("computeInvestorReturn", () => { expect(() => computeInvestorReturn(-1n, 1000n, 1000n)).toThrow(RangeError); expect(() => computeInvestorReturn(1n, 1000n, -1000n)).toThrow(RangeError); }); + + it("asserts integer output at the service boundary (bigint return type)", () => { + const result = computeInvestorReturn(1000n, 3000n, 1000n); + expect(typeof result).toBe("bigint"); + }); });