import assert from 'node:assert/strict'
import test from 'node:test'
import {
DEFAULT_SOURCES,
calloutsFromSections,
deliveryPreview,
failedSection,
isDryRun,
notifyBody,
reportMarkdown,
resolveDate,
resolveOutput,
resolveSources,
resolveTimezone,
skippedSection,
} from './format.ts'
import { aqiCategory, weatherLabel } from './open-meteo.ts'
import { homeSection } from './sources/home.ts'
test('resolveSources defaults to calendar and weather', () => {
assert.deepEqual(resolveSources({}), DEFAULT_SOURCES)
})
test('resolveSources accepts optional inbox, health, and home', () => {
assert.deepEqual(resolveSources({ sources: ['calendar', 'weather', 'inbox', 'health', 'home'] }), [
'calendar',
'weather',
'inbox',
'health',
'home',
])
})
test('resolveSources rejects unknown sources', () => {
assert.throws(
() => resolveSources({ sources: ['discord'] as never }),
/Unknown source "discord"/,
)
})
test('resolveOutput accepts email or notify and rejects others', () => {
assert.equal(resolveOutput({}), 'email')
assert.equal(resolveOutput({ output: 'notify' }), 'notify')
assert.throws(() => resolveOutput({ output: 'discord' as never }), /Unknown output/)
})
test('isDryRun is opt-in', () => {
assert.equal(isDryRun({}), false)
assert.equal(isDryRun({ dryRun: true }), true)
})
test('resolveDate uses an explicit YYYY-MM-DD', () => {
assert.equal(resolveDate({ date: '2026-08-22' }, 'UTC'), '2026-08-22')
})
test('resolveTimezone rejects invalid zones', () => {
assert.equal(resolveTimezone({}), 'UTC')
assert.throws(() => resolveTimezone({ timezone: 'Not/AZone' }), /RangeError|invalid/i)
})
test('report markdown and notify stay Discord-free', () => {
const report = {
title: 'Saturday, August 22 · Denver, CO',
date: '2026-08-22',
callouts: [{ title: 'US AQI 140', detail: 'Unhealthy for sensitive groups', severity: 'warning' as const }],
sections: [
{
id: 'weather' as const,
title: 'Weather',
status: 'ok' as const,
summary: 'Clear',
items: [{ title: 'Clear sky, 28° / 14°C', detail: 'Current 22°C' }],
},
],
}
const markdown = reportMarkdown(report)
const notify = notifyBody(report)
assert.match(markdown, /Needs attention/)
assert.match(notify, /US AQI 140/)
assert.doesNotMatch(markdown, /discord/i)
assert.doesNotMatch(notify, /discord/i)
assert.doesNotMatch(deliveryPreview({ ...report, markdown, notifyText: notify } as never, 'email').text, /discord/i)
})
test('notify preview uses a short subject', () => {
const report = {
date: '2026-08-22',
title: 'Saturday, August 22',
callouts: [] as Array<{ title: string }>,
sections: [],
markdown: '# Saturday, August 22\n',
notifyText: 'all clear',
}
const preview = deliveryPreview(report as never, 'notify')
assert.equal(preview.subject, 'Briefing all clear · 2026-08-22')
assert.equal(preview.text, 'all clear')
})
test('failed and skipped sections keep source ids', () => {
const failed = failedSection('inbox', new Error('missing gmail.readonly'))
assert.equal(failed.id, 'inbox')
assert.equal(failed.status, 'error')
const skipped = skippedSection('home', 'Pass home.items')
assert.equal(skipped.status, 'skipped')
})
test('callouts only include warning and critical items', () => {
const callouts = calloutsFromSections([
{
id: 'health',
title: 'Health',
status: 'warning',
items: [
{ title: 'ok', severity: 'good' },
{ title: 'watch', severity: 'warning' },
{ title: 'bad', severity: 'critical' },
],
},
])
assert.deepEqual(callouts.map((item) => item.title), ['watch', 'bad'])
})
test('home section uses caller items and skips Discord-style defaults', async () => {
const skipped = await homeSection({})
assert.equal(skipped.status, 'skipped')
assert.match(skipped.summary || '', /home\.items/)
assert.doesNotMatch(skipped.summary || '', /post to discord/i)
const section = await homeSection({
home: { items: [{ title: 'Thermostat 70°', detail: 'Heat' }] },
})
assert.equal(section.status, 'ok')
assert.equal(section.items[0]?.title, 'Thermostat 70°')
})
test('Open-Meteo helpers map codes and AQI bands', () => {
assert.equal(weatherLabel(0), 'Clear sky')
assert.equal(weatherLabel(99), 'Thunderstorm with heavy hail')
assert.equal(aqiCategory(12), 'Good')
assert.equal(aqiCategory(120), 'Unhealthy for sensitive groups')
assert.equal(aqiCategory(250), 'Very unhealthy')
})