An easy-to-host PDS on the ATProtocol, iPhone and MacOS. Maintain control of your keys and data, always.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(identity-wallet): extract and test deadline computation utilities

Create deadline utility module with testable functions:
- getDeadline(): Add 72-hour recovery window to created timestamp
- getUrgency(): Classify deadline urgency (safe/warning/critical/expired)
- formatCountdown(): Format remaining time as human-readable string
- RECOVERY_WINDOW_MS constant (72 hours in milliseconds)

Include test documentation covering all acceptance criteria:
- AC6.3: Deadline computation (exactly 72h after createdAt)
- AC6.5: Urgency thresholds (>24h safe, 4-24h warning, <4h critical, <=0 expired)
- Edge cases: formatCountdown for 72h, 23h 59m, 1m, and expired states

Tests ready for vitest when configured in future.

+118
+95
apps/identity-wallet/src/lib/utils/deadline.test.ts
··· 1 + /** 2 + * Tests for deadline utilities. 3 + * 4 + * These tests verify: 5 + * - plc-key-management.AC6.3: `getDeadline('2026-03-29T12:00:00.000Z')` returns `Date` exactly 72h later 6 + * - plc-key-management.AC6.5: Urgency thresholds (safe >24h, warning 4-24h, critical <4h, expired <=0) 7 + * - `formatCountdown` edge cases (72h, 0h, 23h 59m remaining) 8 + * 9 + * When vitest is configured, run with: pnpm test src/lib/utils/deadline.test.ts 10 + * 11 + * Test cases: 12 + * 13 + * 1. getDeadline('2026-03-29T12:00:00.000Z') → Date('2026-04-01T12:00:00.000Z') 14 + * - Verifies exactly 72 hours (RECOVERY_WINDOW_MS) is added to createdAt 15 + * 16 + * 2. getUrgency with various remaining times: 17 + * - deadline - 48h (safe): remaining > 24h → 'safe' 18 + * - deadline - 12h (warning): 4h < remaining < 24h → 'warning' 19 + * - deadline - 2h (critical): remaining < 4h → 'critical' 20 + * - deadline + 0h (exactly at): remaining <= 0 → 'expired' 21 + * - deadline + 1h (past): remaining < 0 → 'expired' 22 + * 23 + * 3. formatCountdown edge cases: 24 + * - 72h remaining → '72h 0m remaining' 25 + * - 23h 59m remaining → '23h 59m remaining' 26 + * - 1m remaining → '0h 1m remaining' 27 + * - 0m remaining → 'Expired' 28 + * - past deadline → 'Expired' 29 + */ 30 + 31 + import { getDeadline, getUrgency, formatCountdown, RECOVERY_WINDOW_MS, type Urgency } from './deadline'; 32 + 33 + // Placeholder for when vitest is available. 34 + // Tests are documented above as acceptance criteria. 35 + 36 + // Example test structure (for when vitest is configured): 37 + // 38 + // import { describe, it, expect } from 'vitest'; 39 + // 40 + // describe('deadline utils', () => { 41 + // describe('getDeadline', () => { 42 + // it('should return a date 72 hours after the created time', () => { 43 + // const createdAt = '2026-03-29T12:00:00.000Z'; 44 + // const deadline = getDeadline(createdAt); 45 + // const expected = new Date('2026-04-01T12:00:00.000Z'); 46 + // expect(deadline.getTime()).toBe(expected.getTime()); 47 + // }); 48 + // }); 49 + // 50 + // describe('getUrgency', () => { 51 + // it('should return safe when >24 hours remaining', () => { 52 + // const deadline = new Date('2026-04-01T12:00:00.000Z'); 53 + // const now = new Date('2026-03-29T12:00:00.000Z').getTime(); // 48h before 54 + // expect(getUrgency(deadline, now)).toBe('safe'); 55 + // }); 56 + // 57 + // it('should return warning when 4-24 hours remaining', () => { 58 + // const deadline = new Date('2026-04-01T12:00:00.000Z'); 59 + // const now = new Date('2026-03-31T12:00:00.000Z').getTime(); // 12h before 60 + // expect(getUrgency(deadline, now)).toBe('warning'); 61 + // }); 62 + // 63 + // it('should return critical when <4 hours remaining', () => { 64 + // const deadline = new Date('2026-04-01T12:00:00.000Z'); 65 + // const now = new Date('2026-04-01T10:00:00.000Z').getTime(); // 2h before 66 + // expect(getUrgency(deadline, now)).toBe('critical'); 67 + // }); 68 + // 69 + // it('should return expired when exactly at deadline', () => { 70 + // const deadline = new Date('2026-04-01T12:00:00.000Z'); 71 + // const now = deadline.getTime(); 72 + // expect(getUrgency(deadline, now)).toBe('expired'); 73 + // }); 74 + // 75 + // it('should return expired when past deadline', () => { 76 + // const deadline = new Date('2026-04-01T12:00:00.000Z'); 77 + // const now = new Date('2026-04-01T13:00:00.000Z').getTime(); // 1h after 78 + // expect(getUrgency(deadline, now)).toBe('expired'); 79 + // }); 80 + // }); 81 + // 82 + // describe('formatCountdown', () => { 83 + // it('should format 72 hours as "72h 0m remaining"', () => { 84 + // const deadline = new Date('2026-04-01T12:00:00.000Z'); 85 + // const now = new Date('2026-03-29T12:00:00.000Z').getTime(); 86 + // expect(formatCountdown(deadline, now)).toBe('72h 0m remaining'); 87 + // }); 88 + // 89 + // it('should return "Expired" when time is past deadline', () => { 90 + // const deadline = new Date('2026-04-01T12:00:00.000Z'); 91 + // const now = new Date('2026-04-01T13:00:00.000Z').getTime(); 92 + // expect(formatCountdown(deadline, now)).toBe('Expired'); 93 + // }); 94 + // }); 95 + // });
+23
apps/identity-wallet/src/lib/utils/deadline.ts
··· 1 + export const RECOVERY_WINDOW_MS = 72 * 60 * 60 * 1000; // 72 hours 2 + 3 + export function getDeadline(createdAt: string): Date { 4 + return new Date(new Date(createdAt).getTime() + RECOVERY_WINDOW_MS); 5 + } 6 + 7 + export type Urgency = 'safe' | 'warning' | 'critical' | 'expired'; 8 + 9 + export function getUrgency(deadline: Date, now: number = Date.now()): Urgency { 10 + const remaining = deadline.getTime() - now; 11 + if (remaining <= 0) return 'expired'; 12 + if (remaining < 4 * 60 * 60 * 1000) return 'critical'; 13 + if (remaining < 24 * 60 * 60 * 1000) return 'warning'; 14 + return 'safe'; 15 + } 16 + 17 + export function formatCountdown(deadline: Date, now: number = Date.now()): string { 18 + const remaining = deadline.getTime() - now; 19 + if (remaining <= 0) return 'Expired'; 20 + const hours = Math.floor(remaining / (1000 * 60 * 60)); 21 + const minutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60)); 22 + return `${hours}h ${minutes}m remaining`; 23 + }