/** * Copyright since 2025 Mifos Initiative * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { deriveKycDocumentRenewal, kycDocumentNeedsAttention, KYC_DOCUMENT_EXPIRY_WARNING_DAYS, KYC_DOCUMENT_RENEWAL_YEARS } from './kyc-document-renewal.util'; const TODAY = new Date(2026, 7, 7); // 07 Aug 2026 const at = (y: number, m: number, d: number) => new Date(y, m - 1, d); const check = (issueDate: Date | null, expiryDate: Date | null = null, today: Date = TODAY) => deriveKycDocumentRenewal({ issueDate, expiryDate, today }); describe('deriveKycDocumentRenewal', () => { describe('the five-year rule', () => { it('says nothing about a document with neither date captured', () => { const result = check(null, null); expect(result.state).toBe('UNKNOWN'); expect(result.dueOn).toBeNull(); expect(result.daysRemaining).toBeNull(); }); it('leaves a recently issued document alone', () => { const result = check(at(2025, 1, 15)); expect(result.state).toBe('OK'); expect(result.reason).toBe('AGE'); }); it('falls due exactly five years after issue', () => { const result = check(at(2021, 8, 7)); expect(result.dueOn).toEqual(at(2026, 8, 7)); expect(result.daysRemaining).toBe(0); // Due today is not yet past — the branch still has the day to act. expect(result.state).toBe('OK'); }); it('becomes DUE the day after the fifth anniversary', () => { const result = check(at(2021, 8, 6)); expect(result.state).toBe('DUE'); expect(result.reason).toBe('AGE'); }); it('is DUE, not EXPIRED, once past five years — the document is still valid, just stale', () => { const result = check(at(2020, 3, 10)); expect(result.state).toBe('DUE'); expect(result.reason).toBe('AGE'); expect(result.daysRemaining).toBeLessThan(0); }); it('does not warn ahead of the five-year mark — a document one day short is entirely fine', () => { // Due tomorrow on age alone, and still OK: only expiry gets an advance warning. const result = check(at(2021, 8, 8)); expect(result.daysRemaining).toBe(1); expect(result.state).toBe('OK'); }); }); describe('an explicit expiry date', () => { it('warns seven days before expiry', () => { const result = check(at(2025, 6, 1), at(2026, 8, 14)); expect(result.reason).toBe('EXPIRY'); expect(result.daysRemaining).toBe(7); expect(result.state).toBe('EXPIRING_SOON'); }); it('is still OK eight days out', () => { expect(check(at(2025, 6, 1), at(2026, 8, 15)).state).toBe('OK'); }); it('warns on the day of expiry itself', () => { const result = check(at(2025, 6, 1), at(2026, 8, 7)); expect(result.daysRemaining).toBe(0); expect(result.state).toBe('EXPIRING_SOON'); }); it('marks a passed expiry as EXPIRED — no longer a valid OVD', () => { const result = check(at(2024, 1, 1), at(2026, 1, 1)); expect(result.state).toBe('EXPIRED'); expect(result.reason).toBe('EXPIRY'); }); it('governs when it falls before the five-year mark', () => { // Issued last year, so the age clock is years away — expiry is what matters. const result = check(at(2025, 6, 1), at(2026, 10, 1)); expect(result.reason).toBe('EXPIRY'); expect(result.dueOn).toEqual(at(2026, 10, 1)); }); it('yields to the five-year rule when it falls later', () => { // A passport issued in 2019 expiring in 2029 is stale long before it expires. const result = check(at(2019, 5, 20), at(2029, 5, 20)); expect(result.reason).toBe('AGE'); expect(result.dueOn).toEqual(at(2024, 5, 20)); expect(result.state).toBe('DUE'); }); it('is judged on expiry alone when no issue date was captured', () => { const result = check(null, at(2027, 1, 1)); expect(result.reason).toBe('EXPIRY'); expect(result.state).toBe('OK'); }); }); describe('leap-year issue dates', () => { it('pins a 29 February issue to 28 February, not 1 March', () => { // 2020-02-29 + 5 years has no real anniversary; rolling into March would push the // document into the wrong month. const result = check(at(2020, 2, 29)); expect(result.dueOn).toEqual(at(2025, 2, 28)); }); }); describe('kycDocumentNeedsAttention', () => { it('flags only the states staff must act on', () => { expect(kycDocumentNeedsAttention('UNKNOWN')).toBe(false); expect(kycDocumentNeedsAttention('OK')).toBe(false); expect(kycDocumentNeedsAttention('EXPIRING_SOON')).toBe(true); expect(kycDocumentNeedsAttention('EXPIRED')).toBe(true); expect(kycDocumentNeedsAttention('DUE')).toBe(true); }); }); it('uses a five-year age window and a seven-day expiry warning', () => { expect(KYC_DOCUMENT_RENEWAL_YEARS).toBe(5); expect(KYC_DOCUMENT_EXPIRY_WARNING_DAYS).toBe(7); }); });