Files
doormilxpress_astryx/tests/lib/doormileTimestamp.test.js
2026-08-27 13:40:48 +05:30

134 lines
5.2 KiB
JavaScript

import dayjs from 'dayjs';
import { parseDoormileTimestamp, formatDoormileTimestamp, doormileDay } from '@/lib/doormileTimestamp';
/**
* Doormile timestamps are IST wall-clock in naive Postgres columns, and some
* responses carry a FALSE trailing `Z` (a Go+pgx marshalling footgun). Parsed
* naively, dayjs reads that Z as a real UTC instant and adds +5:30 to digits
* that were already correct.
*
* These tests protect the one property the whole app depends on: whatever zone
* marker a timestamp arrives with, the rendered and bucketed result is the raw
* wall-clock digits — asserted without depending on the machine's own timezone.
*/
describe('doormileTimestamp', () => {
describe('parseDoormileTimestamp', () => {
it('should ignore a false trailing Z rather than shifting the clock by +5:30', () => {
const parsed = parseDoormileTimestamp('2026-08-27T14:30:00Z');
expect(parsed.isValid()).toBe(true);
expect(parsed.hour()).toBe(14);
expect(parsed.minute()).toBe(30);
expect(parsed.date()).toBe(27);
});
it('should strip a +05:30 offset and keep the same wall-clock digits', () => {
const parsed = parseDoormileTimestamp('2026-08-27T14:30:00+05:30');
expect(parsed.hour()).toBe(14);
expect(parsed.minute()).toBe(30);
});
it('should strip a colon-less offset such as +0530', () => {
expect(parseDoormileTimestamp('2026-08-27T14:30:00+0530').hour()).toBe(14);
});
it('should strip a negative offset too', () => {
expect(parseDoormileTimestamp('2026-08-27T14:30:00-04:00').hour()).toBe(14);
});
it('should parse an already-naive timestamp unchanged', () => {
const parsed = parseDoormileTimestamp('2026-08-27T14:30:00');
expect(parsed.hour()).toBe(14);
expect(parsed.date()).toBe(27);
});
it('should produce the identical instant for the naive and the false-Z form', () => {
const naive = parseDoormileTimestamp('2026-08-27T23:45:00');
const falseZ = parseDoormileTimestamp('2026-08-27T23:45:00Z');
expect(falseZ.valueOf()).toBe(naive.valueOf());
});
it.each([null, undefined, '', 0, false])(
'should return an invalid dayjs (not throw) for the blank value %p',
(raw) => {
expect(parseDoormileTimestamp(raw).isValid()).toBe(false);
}
);
it('should return an invalid dayjs for unparseable text', () => {
expect(parseDoormileTimestamp('not-a-timestamp').isValid()).toBe(false);
});
it('should accept a bare date with no time component', () => {
const parsed = parseDoormileTimestamp('2026-08-27');
expect(parsed.isValid()).toBe(true);
expect(parsed.format('YYYY-MM-DD')).toBe('2026-08-27');
});
it('should accept a Date instance without throwing', () => {
expect(parseDoormileTimestamp(new Date('2026-08-27T10:00:00Z')).isValid()).toBe(true);
});
});
describe('formatDoormileTimestamp', () => {
it('should render the default operator-facing format', () => {
expect(formatDoormileTimestamp('2026-08-27T14:30:00Z')).toBe('27 Aug 2026, 02:30 PM');
});
it('should honour a caller-supplied format', () => {
expect(formatDoormileTimestamp('2026-08-27T14:30:00Z', 'YYYY-MM-DD HH:mm')).toBe('2026-08-27 14:30');
});
it('should return the em-dash fallback for an unusable timestamp', () => {
expect(formatDoormileTimestamp(null)).toBe('—');
expect(formatDoormileTimestamp('garbage')).toBe('—');
});
it('should return a caller-supplied fallback instead of the em dash', () => {
expect(formatDoormileTimestamp(undefined, 'DD MMM', 'Not set')).toBe('Not set');
});
});
describe('doormileDay', () => {
it('should return the calendar day the wall-clock reading falls on', () => {
expect(doormileDay('2026-08-27T14:30:00Z')).toBe('2026-08-27');
});
it('should keep a late-evening row on its own day rather than rolling it forward', () => {
expect(doormileDay('2026-08-27T23:45:00Z')).toBe('2026-08-27');
});
it('should keep an early-morning row on its own day', () => {
expect(doormileDay('2026-08-27T00:15:00Z')).toBe('2026-08-27');
});
it('should return undefined — not a string — for an unusable timestamp', () => {
// Callers compare this against a selected date; a formatted "Invalid Date"
// would silently never match.
expect(doormileDay(null)).toBeUndefined();
expect(doormileDay('garbage')).toBeUndefined();
});
it('should agree across every zone-marker spelling of the same reading', () => {
const forms = [
'2026-08-27T23:45:00',
'2026-08-27T23:45:00Z',
'2026-08-27T23:45:00+05:30',
'2026-08-27T23:45:00+0000'
];
const days = forms.map(doormileDay);
expect(new Set(days).size).toBe(1);
expect(days[0]).toBe('2026-08-27');
});
it('should differ from a bare dayjs() parse whenever the runner is not on UTC', () => {
// Guards the regression directly: bare dayjs() on a Z-suffixed IST reading
// lands on a different day in any non-UTC zone.
const raw = '2026-08-27T23:45:00Z';
expect(doormileDay(raw)).toBe('2026-08-27');
if (dayjs(raw).utcOffset() > 15) {
expect(dayjs(raw).format('YYYY-MM-DD')).toBe('2026-08-28');
}
});
});
});