task-loop-store-unit.test.mjs
117 lines 3.6 KB
Raw
sha256:b5f647cb9c409f563d4671fe3fc05ddea01fabfed9b41fc11cb923588e1c1baf mirror: GitHub Phase A durable MCP OAuth (#270) Human minor ⚠ breaking 10 days ago
1 /**
2 * Tier 1 — UNIT: Task loop store validation, regexes, projections.
3 *
4 * @see docs/TASK-LOOP-STORE-CONTRACT-2G-c.md §6
5 */
6 import { describe, it } from 'node:test';
7 import assert from 'node:assert/strict';
8 import fs from 'node:fs';
9 import path from 'node:path';
10 import { getRepoRoot } from '../lib/repo-root.mjs';
11 import {
12 LOOP_ID_RE,
13 OCCURRENCE_KEY_RE,
14 GRAPH_ID_RE,
15 validateRecurrence,
16 validateTaskLoopRecord,
17 validateOrchestratorGraphRecord,
18 taskLoopSummaryForClient,
19 } from '../lib/task/task-loop-store.mjs';
20
21 const loopStarterDir = path.join(getRepoRoot(), 'task-loops/starter');
22 const graphStarterDir = path.join(getRepoRoot(), 'orchestrator-graphs/starter');
23
24 const VALID_LOOP = JSON.parse(
25 fs.readFileSync(path.join(loopStarterDir, 'loop_school_trip.json'), 'utf8'),
26 );
27 const VALID_GRAPH = JSON.parse(
28 fs.readFileSync(path.join(graphStarterDir, 'graph_school_trip.json'), 'utf8'),
29 );
30
31 describe('Task loop store — id regexes', () => {
32 it('LOOP_ID_RE, OCCURRENCE_KEY_RE, GRAPH_ID_RE accept canonical ids', () => {
33 assert.ok(LOOP_ID_RE.test('loop_school_trip'));
34 assert.ok(OCCURRENCE_KEY_RE.test('2026-W25'));
35 assert.ok(GRAPH_ID_RE.test('graph_school_trip'));
36 assert.ok(!LOOP_ID_RE.test('loop'));
37 assert.ok(!GRAPH_ID_RE.test('not_graph_x'));
38 });
39 });
40
41 describe('Task loop store — recurrence validation', () => {
42 it('accepts cron, interval, manual, on_wake v0 subset', () => {
43 assert.equal(
44 validateRecurrence({ kind: 'manual' }).ok,
45 true,
46 );
47 assert.equal(
48 validateRecurrence({ kind: 'on_wake', source_loop_ref: 'loop_school_trip' }).ok,
49 true,
50 );
51 assert.equal(
52 validateRecurrence({
53 kind: 'cron',
54 expression: '0 9 * * 1',
55 anchor_tz: 'America/Los_Angeles',
56 }).ok,
57 true,
58 );
59 assert.equal(
60 validateRecurrence({
61 kind: 'interval',
62 every: 2,
63 unit: 'week',
64 anchor_at: '2026-06-01T09:00:00Z',
65 }).ok,
66 true,
67 );
68 });
69
70 it('rejects invalid recurrence shapes', () => {
71 assert.equal(validateRecurrence({ kind: 'rrule' }).ok, false);
72 assert.equal(validateRecurrence({ kind: 'on_wake' }).ok, false);
73 });
74
75 it('accepts rrule recurrence when series timezone is provided', () => {
76 const result = validateRecurrence(
77 {
78 kind: 'rrule',
79 rrule: 'FREQ=WEEKLY;BYDAY=MO',
80 dtstart: '2026-06-02T16:00:00Z',
81 anchor_tz: 'America/Los_Angeles',
82 },
83 'America/Los_Angeles',
84 );
85 assert.equal(result.ok, true);
86 });
87 });
88
89 describe('Task loop store — record validation', () => {
90 it('validateTaskLoopRecord accepts school-trip starter bundle', () => {
91 const result = validateTaskLoopRecord(VALID_LOOP);
92 assert.equal(result.ok, true);
93 if (result.ok) {
94 assert.equal(result.loop.loop_id, 'loop_school_trip');
95 assert.equal(result.loop.boundary_policy, 'observe_only');
96 }
97 });
98
99 it('validateOrchestratorGraphRecord accepts graph_school_trip', () => {
100 const result = validateOrchestratorGraphRecord(VALID_GRAPH);
101 assert.equal(result.ok, true);
102 if (result.ok) {
103 assert.equal(result.graph.nodes.length, 5);
104 assert.equal(result.graph.composition_tier, 'personal_safe');
105 }
106 });
107
108 it('taskLoopSummaryForClient keeps summary fields only', () => {
109 const validated = validateTaskLoopRecord(VALID_LOOP);
110 assert.equal(validated.ok, true);
111 if (!validated.ok) return;
112 const summary = taskLoopSummaryForClient(validated.loop);
113 assert.equal(summary.loop_id, 'loop_school_trip');
114 assert.equal(summary.recurrence_kind, 'manual');
115 assert.equal('memory_links' in summary, false);
116 });
117 });
File History 1 commit
sha256:b5f647cb9c409f563d4671fe3fc05ddea01fabfed9b41fc11cb923588e1c1baf mirror: GitHub Phase A durable MCP OAuth (#270) Human minor 10 days ago