calendar-source-calendar-patch.test.mjs
121 lines 4.7 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * Tier 1–7 — Source calendar PATCH toggles, policy caps, timeline display filter.
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 parseSourceCalendarPatchBody,
11 patchSourceCalendar,
12 } from '../lib/calendar/source-calendar-patch.mjs';
13 import { readCalendarAgentTierCap } from '../lib/calendar/calendar-policy.mjs';
14 import { importIcsIntoVault } from '../lib/calendar/event-store.mjs';
15 import { buildCalendarTimeline } from '../lib/calendar/timeline.mjs';
16 import { SOURCE_CALENDAR_DEFAULTS } from '../lib/calendar/source-calendar-defaults.mjs';
17
18 const __dirname = path.dirname(fileURLToPath(import.meta.url));
19 const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-calendar-patch');
20
21 describe('parseSourceCalendarPatchBody', () => {
22 it('unit: accepts partial toggle updates', () => {
23 const patch = parseSourceCalendarPatchBody({
24 enabled_for_display: false,
25 user_group: 'work',
26 });
27 assert.equal(patch.enabled_for_display, false);
28 assert.equal(patch.user_group, 'work');
29 });
30
31 it('unit: rejects invalid tier and user_group', () => {
32 assert.throws(() => parseSourceCalendarPatchBody({ agent_context_tier_max: 9 }), /0–4/);
33 assert.throws(() => parseSourceCalendarPatchBody({ user_group: 'family' }), /personal, work/);
34 assert.throws(() => parseSourceCalendarPatchBody({}), /At least one/);
35 });
36 });
37
38 describe('patchSourceCalendar', () => {
39 const dataDir = path.join(tmpRoot, 'data');
40 const vaultPath = path.join(tmpRoot, 'vault');
41 const vaultId = 'default';
42 const ics = fs.readFileSync(path.join(__dirname, 'fixtures', 'calendar', 'simple-utc.ics'), 'utf8');
43 /** @type {string} */
44 let sourceCalendarId;
45
46 beforeEach(() => {
47 fs.rmSync(tmpRoot, { recursive: true, force: true });
48 fs.mkdirSync(dataDir, { recursive: true });
49 fs.mkdirSync(vaultPath, { recursive: true });
50 delete process.env.KNOWTATION_CALENDAR_AGENT_TIER_MAX_CAP;
51 const imported = importIcsIntoVault(dataDir, vaultId, { icsText: ics, displayName: 'Work' });
52 sourceCalendarId = imported.source_calendar_id;
53 });
54
55 afterEach(() => {
56 fs.rmSync(tmpRoot, { recursive: true, force: true });
57 delete process.env.KNOWTATION_CALENDAR_AGENT_TIER_MAX_CAP;
58 });
59
60 it('integration: updates display and agent toggles independently', () => {
61 const result = patchSourceCalendar(dataDir, vaultId, sourceCalendarId, {
62 enabled_for_display: true,
63 enabled_for_agents: true,
64 agent_context_tier_max: 2,
65 user_group: 'personal',
66 });
67 assert.equal(result.source_calendar.enabled_for_display, true);
68 assert.equal(result.source_calendar.enabled_for_agents, true);
69 assert.equal(result.source_calendar.agent_context_tier_max, 2);
70 assert.equal(result.source_calendar.user_group, 'personal');
71 });
72
73 it('integration: timeline hides events when enabled_for_display is false', () => {
74 patchSourceCalendar(dataDir, vaultId, sourceCalendarId, {
75 enabled_for_display: false,
76 });
77 const timeline = buildCalendarTimeline({
78 dataDir,
79 vaultId,
80 vaultPath,
81 from: '2026-06-01',
82 to: '2026-06-30',
83 layers: 'events',
84 });
85 assert.equal(timeline.items.length, 0);
86 });
87
88 it('security: rejects agent_context_tier_max above policy cap', () => {
89 process.env.KNOWTATION_CALENDAR_AGENT_TIER_MAX_CAP = '1';
90 assert.equal(readCalendarAgentTierCap(dataDir), 1);
91 assert.throws(
92 () => patchSourceCalendar(dataDir, vaultId, sourceCalendarId, {
93 agent_context_tier_max: 2,
94 }),
95 (err) => err.code === 'POLICY_CAP_EXCEEDED',
96 );
97 });
98
99 it('data-integrity: new imports keep v0 defaults until patched', () => {
100 const result = patchSourceCalendar(dataDir, vaultId, sourceCalendarId, {
101 user_group: null,
102 });
103 assert.equal(result.source_calendar.enabled_for_display, SOURCE_CALENDAR_DEFAULTS.enabled_for_display);
104 assert.equal(result.source_calendar.enabled_for_agents, SOURCE_CALENDAR_DEFAULTS.enabled_for_agents);
105 assert.equal(result.source_calendar.agent_context_tier_max, SOURCE_CALENDAR_DEFAULTS.agent_context_tier_max);
106 });
107
108 it('stress: repeated patches persist last write', () => {
109 for (let i = 0; i < 20; i += 1) {
110 patchSourceCalendar(dataDir, vaultId, sourceCalendarId, {
111 enabled_for_agents: i % 2 === 0,
112 });
113 }
114 const final = patchSourceCalendar(dataDir, vaultId, sourceCalendarId, {
115 enabled_for_agents: true,
116 agent_context_tier_max: 1,
117 });
118 assert.equal(final.source_calendar.enabled_for_agents, true);
119 assert.equal(final.source_calendar.agent_context_tier_max, 1);
120 });
121 });
File History 1 commit
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago