calendar-timeline-unit.test.mjs
136 lines 4.2 KB
Raw
sha256:94ec65bd2b200240ac785a97cf14c5db066832bd608a24d6a9c151f17b918b02 feat(calendar): hosted bridge/gateway route parity and time… Human minor ⚠ breaking 21 hours ago
1 /**
2 * Tier 1–2 — UNIT/INTEGRATION: timeline merge and range parsing.
3 */
4 import { describe, it, beforeEach, afterEach } from 'node:test';
5 import assert from 'node:assert/strict';
6 import fs from 'node:fs';
7 import path from 'node:path';
8 import { fileURLToPath } from 'node:url';
9 import {
10 parseTimelineRange,
11 parseTimelineLayers,
12 buildCalendarTimeline,
13 noteCalendarDayKey,
14 } from '../lib/calendar/timeline.mjs';
15 import { importIcsIntoVault } from '../lib/calendar/event-store.mjs';
16 import { writeNote } from '../lib/write.mjs';
17
18 const __dirname = path.dirname(fileURLToPath(import.meta.url));
19 const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-calendar-timeline');
20
21 describe('parseTimelineRange', () => {
22 it('accepts YYYY-MM-DD boundaries', () => {
23 const r = parseTimelineRange('2026-06-01', '2026-06-30');
24 assert.equal(r.fromDate, '2026-06-01');
25 assert.equal(r.toDate, '2026-06-30');
26 assert.equal(r.fromIso, '2026-06-01T00:00:00.000Z');
27 });
28
29 it('rejects inverted ranges', () => {
30 assert.throws(() => parseTimelineRange('2026-06-30', '2026-06-01'), /before/);
31 });
32 });
33
34 describe('parseTimelineLayers', () => {
35 it('defaults to notes and events', () => {
36 assert.deepEqual(parseTimelineLayers(undefined), ['notes', 'events']);
37 });
38
39 it('supports notes-only preset', () => {
40 assert.deepEqual(parseTimelineLayers('notes'), ['notes']);
41 });
42 });
43
44 describe('buildCalendarTimeline', () => {
45 const dataDir = path.join(tmpRoot, 'data');
46 const vaultPath = path.join(tmpRoot, 'vault');
47 const vaultId = 'default';
48
49 beforeEach(() => {
50 fs.rmSync(tmpRoot, { recursive: true, force: true });
51 fs.mkdirSync(dataDir, { recursive: true });
52 fs.mkdirSync(vaultPath, { recursive: true });
53 writeNote(vaultPath, 'notes/june-note.md', {
54 body: 'Biology reading',
55 frontmatter: { title: 'Bio', date: '2026-06-18' },
56 });
57 const ics = fs.readFileSync(path.join(__dirname, 'fixtures', 'calendar', 'simple-utc.ics'), 'utf8');
58 importIcsIntoVault(dataDir, vaultId, { icsText: ics, displayName: 'Work' });
59 });
60
61 afterEach(() => {
62 fs.rmSync(tmpRoot, { recursive: true, force: true });
63 });
64
65 it('merges note and event layers sorted by sort_at', () => {
66 const timeline = buildCalendarTimeline({
67 dataDir,
68 vaultId,
69 vaultPath,
70 from: '2026-06-01',
71 to: '2026-06-30',
72 layers: 'notes,events',
73 });
74 assert.equal(timeline.schema, 'knowtation.calendar_timeline/v0');
75 assert.equal(timeline.items.length, 2);
76 const kinds = timeline.items.map((i) => i.kind);
77 assert.deepEqual(kinds.sort(), ['event', 'note']);
78 });
79
80 it('notes-only layer excludes external events', () => {
81 const timeline = buildCalendarTimeline({
82 dataDir,
83 vaultId,
84 vaultPath,
85 from: '2026-06-01',
86 to: '2026-06-30',
87 layers: 'notes',
88 });
89 assert.ok(timeline.items.every((i) => i.kind === 'note'));
90 });
91
92 it('accepts pre-fetched note records for hosted bridge merge', () => {
93 const timeline = buildCalendarTimeline({
94 dataDir,
95 vaultId,
96 noteRecords: [
97 {
98 path: 'notes/june-note.md',
99 frontmatter: { title: 'Bio', date: '2026-06-18' },
100 date: '2026-06-18',
101 project: null,
102 tags: ['biology'],
103 },
104 ],
105 from: '2026-06-01',
106 to: '2026-06-30',
107 layers: 'notes',
108 });
109 assert.equal(timeline.items.length, 1);
110 assert.equal(timeline.items[0].kind, 'note');
111 assert.equal(timeline.items[0].path, 'notes/june-note.md');
112 });
113
114 it('hides events when enabled_for_display is false', () => {
115 const storePath = path.join(dataDir, 'hub_calendar_store.json');
116 const store = JSON.parse(fs.readFileSync(storePath, 'utf8'));
117 store.vaults.default.source_calendars[0].enabled_for_display = false;
118 fs.writeFileSync(storePath, JSON.stringify(store));
119
120 const timeline = buildCalendarTimeline({
121 dataDir,
122 vaultId,
123 vaultPath,
124 from: '2026-06-01',
125 to: '2026-06-30',
126 layers: 'events',
127 });
128 assert.equal(timeline.items.length, 0);
129 });
130 });
131
132 describe('noteCalendarDayKey', () => {
133 it('uses frontmatter date when present', () => {
134 assert.equal(noteCalendarDayKey({ frontmatter: { date: '2026-06-18' } }), '2026-06-18');
135 });
136 });
File History 1 commit
sha256:94ec65bd2b200240ac785a97cf14c5db066832bd608a24d6a9c151f17b918b02 feat(calendar): hosted bridge/gateway route parity and time… Human minor 21 hours ago