| 1 | import { describe, it, expect } from 'vitest'
|
| 2 | import {
|
| 3 | startOfAppDay,
|
| 4 | previousAppDay,
|
| 5 | parseAppDay,
|
| 6 | appDayWindow,
|
| 7 | appDayPeriod,
|
| 8 | } from '@app/lib/queue/processors/insights/utils'
|
| 9 |
|
| 10 | // America/Sao_Paulo is UTC-3 all year (Brazil dropped daylight saving in 2019),
|
| 11 | // so the app's local day starts at 03:00Z; the trailing 3 is that offset.
|
| 12 | const saoPauloMidnight = (year: number, month: number, day: number) =>
|
| 13 | Date.UTC(year, month - 1, day, 3, 0, 0)
|
| 14 |
|
| 15 | describe('startOfAppDay', () => {
|
| 16 | it('floors a mid-day instant to the start of the local day', () => {
|
| 17 | expect(startOfAppDay(new Date('2026-10-17T12:00:00.000Z')).getTime()).toBe(
|
| 18 | saoPauloMidnight(2026, 10, 17),
|
| 19 | )
|
| 20 | })
|
| 21 |
|
| 22 | it('keeps an instant just before local midnight on the same day', () => {
|
| 23 | expect(
|
| 24 | startOfAppDay(new Date('2026-10-18T02:59:59.999Z')).getTime(),
|
| 25 | ).toBe(saoPauloMidnight(2026, 10, 17))
|
| 26 | })
|
| 27 |
|
| 28 | it('rolls over to the next day at local midnight', () => {
|
| 29 | expect(
|
| 30 | startOfAppDay(new Date('2026-10-18T03:00:00.000Z')).getTime(),
|
| 31 | ).toBe(saoPauloMidnight(2026, 10, 18))
|
| 32 | })
|
| 33 | })
|
| 34 |
|
| 35 | describe('previousAppDay', () => {
|
| 36 | it('returns the start of the day before', () => {
|
| 37 | expect(previousAppDay(new Date('2026-10-17T12:00:00.000Z')).getTime()).toBe(
|
| 38 | saoPauloMidnight(2026, 10, 16),
|
| 39 | )
|
| 40 | })
|
| 41 | })
|
| 42 |
|
| 43 | describe('parseAppDay', () => {
|
| 44 | it('parses a day string at the local midnight', () => {
|
| 45 | expect(parseAppDay('2026-10-17').getTime()).toBe(
|
| 46 | saoPauloMidnight(2026, 10, 17),
|
| 47 | )
|
| 48 | })
|
| 49 | })
|
| 50 |
|
| 51 | describe('appDayWindow', () => {
|
| 52 | it('spans from one local day start to the next', () => {
|
| 53 | const { start, end } = appDayWindow(new Date('2026-10-17T12:00:00.000Z'))
|
| 54 |
|
| 55 | expect(start.getTime()).toBe(saoPauloMidnight(2026, 10, 17))
|
| 56 | expect(end.getTime()).toBe(saoPauloMidnight(2026, 10, 18))
|
| 57 | })
|
| 58 |
|
| 59 | it('rolls the whole window over at local midnight', () => {
|
| 60 | const before = appDayWindow(new Date('2026-10-18T02:59:59.999Z'))
|
| 61 | const after = appDayWindow(new Date('2026-10-18T03:00:00.000Z'))
|
| 62 |
|
| 63 | expect(before.start.getTime()).toBe(saoPauloMidnight(2026, 10, 17))
|
| 64 | expect(before.end.getTime()).toBe(saoPauloMidnight(2026, 10, 18))
|
| 65 | expect(after.start.getTime()).toBe(saoPauloMidnight(2026, 10, 18))
|
| 66 | expect(after.end.getTime()).toBe(saoPauloMidnight(2026, 10, 19))
|
| 67 | })
|
| 68 | })
|
| 69 |
|
| 70 | describe('appDayPeriod', () => {
|
| 71 | it('serializes the window as offset ISO strings', () => {
|
| 72 | expect(appDayPeriod(new Date('2026-10-17T12:00:00.000Z'))).toEqual({
|
| 73 | periodStart: '2026-10-17T00:00:00.000-03:00',
|
| 74 | periodEnd: '2026-10-18T00:00:00.000-03:00',
|
| 75 | })
|
| 76 | })
|
| 77 | })
|