fix(backend): convert ecosystem-phase2 test to Vitest, fix .ts extension + hardcoded productId

- ecosystem-phase2.test.ts used node:test — Vitest discovered it but ran 0 tests
- Converted to Vitest describe/it/expect — now 2 tests actually execute
- Added coverage for missing plan-created-event graceful fallback
- Fixed .ts import extension to .js (ESM convention, tsc build compat)
- Replaced 6 hardcoded 'chronomind' literals with PRODUCT_ID import
This commit is contained in:
saravanakumardb1 2026-04-13 15:51:58 -07:00
parent fcd2ee3ad2
commit 9b398bbd68
2 changed files with 72 additions and 58 deletions

View File

@ -1,5 +1,4 @@
import test from 'node:test'; import { describe, it, expect } from 'vitest';
import assert from 'node:assert/strict';
import { mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises'; import { mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os'; import { tmpdir } from 'node:os';
import { join } from 'node:path'; import { join } from 'node:path';
@ -8,57 +7,71 @@ import {
loadLatestPlanArtifact, loadLatestPlanArtifact,
loadLatestPlanCreatedEvent, loadLatestPlanCreatedEvent,
persistRoutineFromPlan, persistRoutineFromPlan,
} from './ecosystem-phase2.ts'; } from './ecosystem-phase2.js';
test('builds and persists a ChronoMind routine from the latest plan artifact', async () => { describe('ecosystem-phase2', () => {
const root = await mkdtemp(join(tmpdir(), 'chronomind-phase2-')); it('builds and persists a routine from the latest plan artifact', async () => {
await mkdir(join(root, 'indexes'), { recursive: true }); const root = await mkdtemp(join(tmpdir(), 'chronomind-phase2-'));
await writeFile( await mkdir(join(root, 'indexes'), { recursive: true });
join(root, 'indexes', 'latest-plan.json'), await writeFile(
`${JSON.stringify({ join(root, 'indexes', 'latest-plan.json'),
id: 'art_plan_demo', `${JSON.stringify({
title: 'FlowMonk weekly plan for 2026-04-07', id: 'art_plan_demo',
ownership: { userId: 'saravana', orgId: null }, title: 'FlowMonk weekly plan for 2026-04-07',
provenance: { ownership: { userId: 'saravana', orgId: null },
originProductId: 'flowmonk', provenance: {
originActionId: 'plan_export_demo', originProductId: 'flowmonk',
sessionId: 'sess_phase2', originActionId: 'plan_export_demo',
runId: 'run_phase2_plan', sessionId: 'sess_phase2',
approvalId: null, runId: 'run_phase2_plan',
correlationId: 'corr_phase2', approvalId: null,
lineage: [{ stepType: 'plan-exported', productId: 'flowmonk', actorType: 'agent', timestamp: '2026-04-03T12:00:00.000Z' }], correlationId: 'corr_phase2',
}, lineage: [{ stepType: 'plan-exported', productId: 'flowmonk', actorType: 'agent', timestamp: '2026-04-03T12:00:00.000Z' }],
payload: { },
entries: [ payload: {
{ taskTitle: 'Architecture review', durationMinutes: 60 }, entries: [
{ taskTitle: 'API design', durationMinutes: 45 }, { taskTitle: 'Architecture review', durationMinutes: 60 },
], { taskTitle: 'API design', durationMinutes: 45 },
}, ],
}, null, 2)}\n`, },
'utf-8' }, null, 2)}\n`,
); 'utf-8'
await writeFile( );
join(root, 'indexes', 'latest-plan-created-event.json'), await writeFile(
`${JSON.stringify({ eventId: 'evt_plan_created_demo', trace: { correlationId: 'corr_phase2', causationId: null, parentEventId: null } }, null, 2)}\n`, join(root, 'indexes', 'latest-plan-created-event.json'),
'utf-8' `${JSON.stringify({ eventId: 'evt_plan_created_demo', trace: { correlationId: 'corr_phase2', causationId: null, parentEventId: null } }, null, 2)}\n`,
); 'utf-8'
);
const planArtifact = await loadLatestPlanArtifact(root); const planArtifact = await loadLatestPlanArtifact(root);
const planCreatedEvent = await loadLatestPlanCreatedEvent(root); const planCreatedEvent = await loadLatestPlanCreatedEvent(root);
const generated = buildRoutineFromPlan({ const generated = buildRoutineFromPlan({
planArtifact, planArtifact,
planCreatedEvent, planCreatedEvent,
now: '2026-04-03T12:05:00.000Z', now: '2026-04-03T12:05:00.000Z',
});
expect(generated.routine.steps).toHaveLength(2);
expect(generated.routine.steps[0].label).toBe('Architecture review');
expect(generated.routine.steps[1].label).toBe('API design');
expect(generated.routine.totalDurationMinutes).toBe(105);
expect(generated.routine.status).toBe('ready');
expect(generated.routine.isTemplate).toBe(true);
expect(generated.createdEvent.trace.causationId).toBe('evt_plan_created_demo');
expect(generated.artifact.payload.stepCount).toBe(2);
await persistRoutineFromPlan({ ...generated, root });
const linkedEvent = JSON.parse(
await readFile(join(root, 'indexes', 'latest-routine-linked-event.json'), 'utf-8')
) as { payload: { relation: string } };
expect(linkedEvent.payload.relation).toBe('generated-routine');
}); });
assert.equal(generated.routine.steps.length, 2); it('handles missing plan-created-event gracefully', async () => {
assert.equal(generated.createdEvent.trace.causationId, 'evt_plan_created_demo'); const root = await mkdtemp(join(tmpdir(), 'chronomind-phase2-'));
const result = await loadLatestPlanCreatedEvent(root);
await persistRoutineFromPlan({ ...generated, root }); expect(result).toBeNull();
});
const linkedEvent = JSON.parse(
await readFile(join(root, 'indexes', 'latest-routine-linked-event.json'), 'utf-8')
) as { payload: { relation: string } };
assert.equal(linkedEvent.payload.relation, 'generated-routine');
}); });

View File

@ -2,7 +2,8 @@ import { randomUUID } from 'node:crypto';
import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os'; import { homedir } from 'node:os';
import { dirname, join } from 'node:path'; import { dirname, join } from 'node:path';
import type { RoutineDoc, RoutineStep } from '../modules/routines/types.ts'; import type { RoutineDoc, RoutineStep } from '../modules/routines/types.js';
import { PRODUCT_ID } from './product-config.js';
const DEFAULT_PHASE2_ROOT = join(homedir(), '.bytelyst', 'ecosystem', 'phase2'); const DEFAULT_PHASE2_ROOT = join(homedir(), '.bytelyst', 'ecosystem', 'phase2');
@ -45,7 +46,7 @@ type RoutineArtifact = {
id: string; id: string;
artifactType: 'routine'; artifactType: 'routine';
schemaVersion: 1; schemaVersion: 1;
productId: 'chronomind'; productId: typeof PRODUCT_ID;
sourceSurface: 'backend'; sourceSurface: 'backend';
title: string; title: string;
summary: string; summary: string;
@ -79,7 +80,7 @@ type ArtifactEvent = {
eventName: 'artifact.created' | 'artifact.linked'; eventName: 'artifact.created' | 'artifact.linked';
eventVersion: 1; eventVersion: 1;
occurredAt: string; occurredAt: string;
productId: 'chronomind'; productId: typeof PRODUCT_ID;
sourceSurface: 'backend'; sourceSurface: 'backend';
userId: string; userId: string;
orgId: string | null; orgId: string | null;
@ -139,7 +140,7 @@ export function buildRoutineFromPlan(params: {
const routine: RoutineDoc = { const routine: RoutineDoc = {
id: routineId, id: routineId,
userId: params.planArtifact.ownership.userId, userId: params.planArtifact.ownership.userId,
productId: 'chronomind', productId: PRODUCT_ID,
name: `Routine from ${params.planArtifact.title}`, name: `Routine from ${params.planArtifact.title}`,
description: params.planArtifact.title, description: params.planArtifact.title,
steps, steps,
@ -157,7 +158,7 @@ export function buildRoutineFromPlan(params: {
id: artifactId, id: artifactId,
artifactType: 'routine', artifactType: 'routine',
schemaVersion: 1, schemaVersion: 1,
productId: 'chronomind', productId: PRODUCT_ID,
sourceSurface: 'backend', sourceSurface: 'backend',
title: routine.name, title: routine.name,
summary: `${steps.length} routine steps imported from FlowMonk plan`, summary: `${steps.length} routine steps imported from FlowMonk plan`,
@ -206,7 +207,7 @@ export function buildRoutineFromPlan(params: {
eventName: 'artifact.created', eventName: 'artifact.created',
eventVersion: 1, eventVersion: 1,
occurredAt: now, occurredAt: now,
productId: 'chronomind', productId: PRODUCT_ID,
sourceSurface: 'backend', sourceSurface: 'backend',
userId: routine.userId, userId: routine.userId,
orgId: params.planArtifact.ownership.orgId ?? null, orgId: params.planArtifact.ownership.orgId ?? null,
@ -231,7 +232,7 @@ export function buildRoutineFromPlan(params: {
eventName: 'artifact.linked', eventName: 'artifact.linked',
eventVersion: 1, eventVersion: 1,
occurredAt: now, occurredAt: now,
productId: 'chronomind', productId: PRODUCT_ID,
sourceSurface: 'backend', sourceSurface: 'backend',
userId: routine.userId, userId: routine.userId,
orgId: params.planArtifact.ownership.orgId ?? null, orgId: params.planArtifact.ownership.orgId ?? null,