import assert from 'node:assert/strict' import { describe, it } from 'node:test' import { listEventsAcrossCalendarsDefaultBudgetMs, listEventsAcrossCalendarsMaxBudgetMs, resolveListEventsAcrossCalendarsBudgetMs, scanEventsAcrossCalendars, } from './calendar-across.ts' import type { JsonObject } from './types.ts' function calendars( items: Array<{ id: string; summary: string }>, nextPageToken?: string, ): JsonObject { return { items, nextPageToken } } describe('resolveListEventsAcrossCalendarsBudgetMs', () => { it('defaults when the budget is omitted or unusable', () => { assert.equal( resolveListEventsAcrossCalendarsBudgetMs(undefined), listEventsAcrossCalendarsDefaultBudgetMs, ) assert.equal(resolveListEventsAcrossCalendarsBudgetMs(0), 20_000) assert.equal(resolveListEventsAcrossCalendarsBudgetMs(-5), 20_000) assert.equal(resolveListEventsAcrossCalendarsBudgetMs(Number.NaN), 20_000) }) it('caps an explicit budget', () => { assert.equal(resolveListEventsAcrossCalendarsBudgetMs(15_000), 15_000) assert.equal( resolveListEventsAcrossCalendarsBudgetMs(120_000), listEventsAcrossCalendarsMaxBudgetMs, ) }) }) describe('scanEventsAcrossCalendars', () => { it('returns a partial result when the next event page would pass the budget', async () => { let nowMs = 0 const eventCalls: string[] = [] const logs: string[] = [] const result = await scanEventsAcrossCalendars({ params: { account: 'personal', budgetMs: 20_000, showHiddenCalendars: true, timeMin: '2026-09-27T00:00:00Z', }, now: () => nowMs, log: (message) => logs.push(message), listCalendars: async (params) => { assert.equal(params.showHidden, true) assert.equal(params.account, 'personal') return calendars([{ id: 'cal-1', summary: 'Work' }]) }, listEvents: async (params) => { eventCalls.push(params.pageToken ?? 'first') assert.equal(params.calendarId, 'cal-1') assert.equal(params.timeMin, '2026-09-27T00:00:00Z') assert.equal('budgetMs' in params, false) assert.equal('showHiddenCalendars' in params, false) nowMs += 25_000 return { items: [{ id: `event-${eventCalls.length}` }], nextPageToken: 'page-2', } }, }) assert.deepEqual(eventCalls, ['first']) assert.equal(result.truncated, true) assert.equal(result.truncatedReason, 'budget') assert.equal(result.budgetMs, 20_000) assert.equal(result.calendarsScanned, 1) assert.equal(result.items.length, 1) assert.equal(result.items[0]?.calendarSummary, 'Work') assert.match(logs[0] ?? '', /start budgetMs=20000 showHidden=true/) assert.match(logs.at(-1) ?? '', /truncated=true/) }) it('stops before later calendars once the budget is spent', async () => { let nowMs = 0 const scanned: string[] = [] const result = await scanEventsAcrossCalendars({ params: { account: 'personal', budgetMs: 20_000 }, now: () => nowMs, listCalendars: async () => calendars([ { id: 'a', summary: 'A' }, { id: 'b', summary: 'B' }, { id: 'c', summary: 'C' }, ]), listEvents: async (params) => { scanned.push(String(params.calendarId)) nowMs += 10_000 return { items: [{ id: params.calendarId }] } }, }) assert.deepEqual(scanned, ['a', 'b']) assert.equal(result.calendars.length, 3) assert.equal(result.calendarsScanned, 2) assert.equal(result.truncated, true) assert.equal(result.truncatedReason, 'budget') assert.equal(result.items.length, 2) }) it('finishes every calendar inside the budget', async () => { const result = await scanEventsAcrossCalendars({ params: { account: 'personal' }, now: () => 0, listCalendars: async () => calendars([{ id: 'a', summary: 'A' }]), listEvents: async () => ({ items: [{ id: 'event-1' }] }), }) assert.equal(result.budgetMs, listEventsAcrossCalendarsDefaultBudgetMs) assert.equal(result.truncated, false) assert.equal(result.truncatedReason, null) assert.equal(result.calendarsScanned, 1) assert.equal(result.failures.length, 0) }) it('falls back to primary when calendar listing fails and the budget remains', async () => { const scanned: string[] = [] const result = await scanEventsAcrossCalendars({ params: { account: 'personal', budgetMs: 20_000 }, now: () => 0, listCalendars: async () => { throw new Error('calendarList failed') }, listEvents: async (params) => { scanned.push(String(params.calendarId)) return { items: [] } }, }) assert.deepEqual(scanned, ['primary']) assert.deepEqual(result.failures, ['calendarList failed']) assert.equal(result.truncated, false) }) it('records a calendar event failure and continues', async () => { const result = await scanEventsAcrossCalendars({ params: { account: 'personal', budgetMs: 20_000 }, now: () => 0, listCalendars: async () => calendars([ { id: 'bad', summary: 'Hidden' }, { id: 'ok', summary: 'Main' }, ]), listEvents: async (params) => { if (params.calendarId === 'bad') throw new Error('403 insufficient') return { items: [{ id: 'kept' }] } }, }) assert.deepEqual(result.failures, ['Hidden: 403 insufficient']) assert.equal(result.items.length, 1) assert.equal(result.calendarsScanned, 2) assert.equal(result.truncated, false) }) })