From d455a106de42c8e7ede33e37d545b49d85c9fa46 Mon Sep 17 00:00:00 2001 From: s6pa1rta3n-lab Date: Mon, 28 Sep 2026 04:13:55 -0400 Subject: [PATCH] test: add unit tests for src/utils/formatRemaining.ts (#1973) --- src/utils/__tests__/formatRemaining.test.ts | 143 ++++++++++++++++++++ src/utils/formatRemaining.ts | 48 +++++++ 2 files changed, 191 insertions(+) create mode 100644 src/utils/__tests__/formatRemaining.test.ts create mode 100644 src/utils/formatRemaining.ts diff --git a/src/utils/__tests__/formatRemaining.test.ts b/src/utils/__tests__/formatRemaining.test.ts new file mode 100644 index 00000000..b1e8e5d4 --- /dev/null +++ b/src/utils/__tests__/formatRemaining.test.ts @@ -0,0 +1,143 @@ +import { describe, expect, it } from 'vitest'; +import { formatRemaining } from '../formatRemaining'; + +const ONE_SECOND_MS = 1000; +const ONE_MINUTE_MS = 60 * ONE_SECOND_MS; +const ONE_HOUR_MS = 60 * ONE_MINUTE_MS; +const ONE_DAY_MS = 24 * ONE_HOUR_MS; + +describe('formatRemaining', () => { + describe('matured case (diffMs <= 0)', () => { + it('returns matured status when current time equals maturity time', () => { + const baseTime = 1_700_000_000_000; + expect(formatRemaining(baseTime, baseTime)).toEqual({ + text: 'Matured', + status: 'matured', + }); + }); + + it('returns matured status when maturity time is in the past', () => { + const current = 1_700_000_000_000; + const maturity = current - 5 * ONE_MINUTE_MS; + expect(formatRemaining(maturity, current)).toEqual({ + text: 'Matured', + status: 'matured', + }); + }); + + it('uses Date.now by default when maturity timestamp is in the past', () => { + const pastTime = Date.now() - 10_000; + expect(formatRemaining(pastTime)).toEqual({ + text: 'Matured', + status: 'matured', + }); + }); + }); + + describe('threshold boundaries', () => { + const current = 1_700_000_000_000; + + it('returns critical status when remaining time is exactly one day', () => { + const maturity = current + ONE_DAY_MS; + expect(formatRemaining(maturity, current)).toEqual({ + text: '1d 0h 0m', + status: 'critical', + }); + }); + + it('returns critical status when remaining time is less than one day', () => { + const maturity = current + ONE_DAY_MS - 1; + expect(formatRemaining(maturity, current)).toEqual({ + text: '23h 59m', + status: 'critical', + }); + }); + + it('returns critical status for small remaining durations under one hour', () => { + const maturity = current + 30 * ONE_MINUTE_MS; + expect(formatRemaining(maturity, current)).toEqual({ + text: '30m', + status: 'critical', + }); + }); + + it('returns warning status when remaining time is slightly greater than one day', () => { + const maturity = current + ONE_DAY_MS + 1; + expect(formatRemaining(maturity, current)).toEqual({ + text: '1d 0h 0m', + status: 'warning', + }); + }); + + it('returns warning status when remaining time is between one and seven days', () => { + const maturity = current + 3 * ONE_DAY_MS + 4 * ONE_HOUR_MS + 15 * ONE_MINUTE_MS; + expect(formatRemaining(maturity, current)).toEqual({ + text: '3d 4h 15m', + status: 'warning', + }); + }); + + it('returns warning status when remaining time is exactly seven days', () => { + const maturity = current + 7 * ONE_DAY_MS; + expect(formatRemaining(maturity, current)).toEqual({ + text: '7d 0h 0m', + status: 'warning', + }); + }); + + it('returns healthy status when remaining time is strictly greater than seven days', () => { + const maturity = current + 7 * ONE_DAY_MS + 1; + expect(formatRemaining(maturity, current)).toEqual({ + text: '7d 0h 0m', + status: 'healthy', + }); + }); + + it('returns healthy status for long remaining durations', () => { + const maturity = current + 14 * ONE_DAY_MS + 2 * ONE_HOUR_MS + 5 * ONE_MINUTE_MS; + expect(formatRemaining(maturity, current)).toEqual({ + text: '14d 2h 5m', + status: 'healthy', + }); + }); + }); + + describe('formatting branches (lines 33-35)', () => { + const current = 1_700_000_000_000; + + it('includes days prefix and hours when days > 0 and hours > 0', () => { + const maturity = current + 4 * ONE_DAY_MS + 6 * ONE_HOUR_MS + 20 * ONE_MINUTE_MS; + const result = formatRemaining(maturity, current); + expect(result.text).toBe('4d 6h 20m'); + expect(result.status).toBe('warning'); + }); + + it('includes 0h when days > 0 and hours === 0', () => { + const maturity = current + 2 * ONE_DAY_MS + 45 * ONE_MINUTE_MS; + const result = formatRemaining(maturity, current); + expect(result.text).toBe('2d 0h 45m'); + expect(result.status).toBe('warning'); + }); + + it('omits days and includes hours when days === 0 and hours > 0', () => { + const maturity = current + 5 * ONE_HOUR_MS + 12 * ONE_MINUTE_MS; + const result = formatRemaining(maturity, current); + expect(result.text).toBe('5h 12m'); + expect(result.status).toBe('critical'); + }); + + it('omits days and hours when days === 0 and hours === 0', () => { + const maturity = current + 42 * ONE_MINUTE_MS; + const result = formatRemaining(maturity, current); + expect(result.text).toBe('42m'); + expect(result.status).toBe('critical'); + }); + + it('formats 0m when diff is less than one minute', () => { + const maturity = current + 45 * ONE_SECOND_MS; + const result = formatRemaining(maturity, current); + expect(result.text).toBe('0m'); + expect(result.status).toBe('critical'); + }); + }); +}); diff --git a/src/utils/formatRemaining.ts b/src/utils/formatRemaining.ts new file mode 100644 index 00000000..b04a3810 --- /dev/null +++ b/src/utils/formatRemaining.ts @@ -0,0 +1,48 @@ +export type MaturityStatus = 'healthy' | 'warning' | 'critical' | 'matured'; + +export interface RemainingMaturity { + text: string; + status: MaturityStatus; +} + +/** + * Calculates human-readable remaining time until maturity and determines urgency status. + * + * @param maturityTimestamp - Target maturity epoch timestamp in milliseconds. + * @param currentTimestamp - Current epoch timestamp in milliseconds, defaulting to Date.now(). + * @returns Object containing formatted remaining time string and urgency status. + */ +export const formatRemaining = ( + maturityTimestamp: number, + currentTimestamp: number = Date.now(), +): RemainingMaturity => { + const diffMs = maturityTimestamp - currentTimestamp; + + if (diffMs <= 0) { + return { text: 'Matured', status: 'matured' }; + } + + const days = Math.floor(diffMs / (1000 * 60 * 60 * 24)); + const hours = Math.floor((diffMs / (1000 * 60 * 60)) % 24); + const minutes = Math.floor((diffMs / (1000 * 60)) % 60); + + let status: MaturityStatus = 'healthy'; + const oneDayMs = 24 * 60 * 60 * 1000; + const sevenDaysMs = 7 * oneDayMs; + + if (diffMs <= oneDayMs) { + status = 'critical'; + } else if (diffMs <= sevenDaysMs) { + status = 'warning'; + } + + const timeParts: string[] = []; + if (days > 0) timeParts.push(`${days}d`); + if (hours > 0 || days > 0) timeParts.push(`${hours}h`); + timeParts.push(`${minutes}m`); + + return { + text: timeParts.join(' '), + status, + }; +};