trmnlc/src/sources/donetick.test.ts
Kate Meerburg 2e34246d14 Add today-view plugin with Tana, ICS, and Donetick data sources
Implements an e-ink daily dashboard plugin ("today") with four sections:
date/focus/success header, agenda timeline, chores checklist, and
due/overdue task lists.

Data sources:
- Focus & success text: Tana daily note (src/sources/tana.ts)
- Due/overdue tasks: Tana task search (src/sources/tana.ts)
- Agenda events: ICS calendar feeds (src/sources/ics.ts)
- Chores: Donetick API (src/sources/donetick.ts)

All sources fetch in parallel and fall back gracefully on error.
Tests use mock HTTP servers with synthetic data — no real services needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 23:18:13 +02:00

63 lines
2.3 KiB
TypeScript

import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
import type { Server } from 'bun';
import { fetchDonetickChores } from './donetick';
const today = new Date().toISOString().slice(0, 10);
const tomorrow = new Date(Date.now() + 86_400_000).toISOString().slice(0, 10);
const yesterday = new Date(Date.now() - 86_400_000).toISOString().slice(0, 10);
const CHORES = [
{ id: 1, name: 'Make the bed', assignedTo: 1, nextDueDate: `${tomorrow}T00:00:00Z`, isActive: true },
{ id: 2, name: 'Water the plants', assignedTo: 1, nextDueDate: `${today}T00:00:00Z`, isActive: true },
{ id: 3, name: 'Walk the dog', assignedTo: 2, nextDueDate: `${today}T00:00:00Z`, isActive: true },
{ id: 4, name: 'Inactive chore', assignedTo: 1, nextDueDate: `${today}T00:00:00Z`, isActive: false },
{ id: 5, name: 'Shared chore', assignedTo: 2, nextDueDate: `${yesterday}T00:00:00Z`, isActive: true, assignees: [{ userId: 1 }, { userId: 2 }] },
];
let server: Server;
let baseUrl: string;
beforeAll(() => {
server = Bun.serve({
port: 0,
hostname: '127.0.0.1',
routes: {
'/eapi/v1/chore': (req) => {
if (req.headers.get('secretkey') !== 'test-token')
return new Response('Unauthorized', { status: 401 });
return Response.json(CHORES);
},
},
});
baseUrl = `http://127.0.0.1:${server.port}`;
});
afterAll(() => {
server.stop();
});
describe('donetick source', () => {
test('fetches chores for the configured user', async () => {
const chores = await fetchDonetickChores({
url: baseUrl,
token: 'test-token',
user_id: 1,
});
// Should include: Water the plants (due today), Shared chore (overdue, in assignees)
// Should exclude: Make the bed (done, nextDueDate=tomorrow), Walk the dog (different user), Inactive chore
expect(chores).toHaveLength(2);
expect(chores.map((c) => c.text)).toContain('Water the plants');
expect(chores.map((c) => c.text)).toContain('Shared chore');
expect(chores.map((c) => c.text)).not.toContain('Make the bed');
expect(chores.map((c) => c.text)).not.toContain('Walk the dog');
expect(chores.map((c) => c.text)).not.toContain('Inactive chore');
});
test('throws on auth failure', async () => {
await expect(
fetchDonetickChores({ url: baseUrl, token: 'wrong', user_id: 1 })
).rejects.toThrow('401');
});
});