Skip to content
← Public packages

@kody/google

Call Gmail, Calendar, Tasks, Drive, Docs, Sheets, People, YouTube, and Analytics through saved Google OAuth.

src/calendar-across.test.ts

166 lines · 5.3 KB · TypeScript
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',
				integration: 'google-work',
				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')
				assert.equal(params.integration, 'google-work')
				return calendars([{ id: 'cal-1', summary: 'Work' }])
			},
			listEvents: async (params) => {
				eventCalls.push(params.pageToken ?? 'first')
				assert.equal(params.calendarId, 'cal-1')
				assert.equal(params.integration, 'google-work')
				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)
	})
})