test(notes): verify phase1 transcript→note import against @bytelyst/events schemas
- Add @bytelyst/events dependency for contract validation - Expand ecosystem-phase1 tests from 1 to 13 focused tests: - transcript artifact import from disk - transcript capture event load + missing file graceful - note creation with productId, sourceType, links, tags - note artifact doc for internal persistence - artifact.created event with upstream causation propagation - artifact.linked event chained from artifact.created - provenance lineage preservation (lysnrai→notelett) - NoteArtifactEnvelopeSchema conformance (no contract drift) - ArtifactCreatedEventSchema conformance - ArtifactLinkedEventSchema conformance - disk persistence + index file verification - graceful degradation without capture event - Fix server.test.ts route count (7→8) for ecosystem-phase1 route
This commit is contained in:
parent
9ecc7c6bf5
commit
623d02c32f
@ -17,16 +17,17 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/cosmos": "^4.2.0",
|
"@azure/cosmos": "^4.2.0",
|
||||||
"@bytelyst/auth": "^0.1.0",
|
"@bytelyst/auth": "^0.1.0",
|
||||||
"@bytelyst/config": "^0.1.0",
|
|
||||||
"@bytelyst/cosmos": "^0.1.0",
|
|
||||||
"@bytelyst/datastore": "^0.1.0",
|
|
||||||
"@bytelyst/backend-config": "^0.1.0",
|
"@bytelyst/backend-config": "^0.1.0",
|
||||||
"@bytelyst/backend-flags": "^0.1.0",
|
"@bytelyst/backend-flags": "^0.1.0",
|
||||||
"@bytelyst/backend-telemetry": "^0.1.0",
|
"@bytelyst/backend-telemetry": "^0.1.0",
|
||||||
|
"@bytelyst/config": "^0.1.0",
|
||||||
|
"@bytelyst/cosmos": "^0.1.0",
|
||||||
|
"@bytelyst/datastore": "^0.1.0",
|
||||||
"@bytelyst/errors": "^0.1.0",
|
"@bytelyst/errors": "^0.1.0",
|
||||||
"@bytelyst/field-encrypt": "^0.1.0",
|
"@bytelyst/events": "^0.1.0",
|
||||||
"@bytelyst/fastify-auth": "^0.1.0",
|
"@bytelyst/fastify-auth": "^0.1.0",
|
||||||
"@bytelyst/fastify-core": "^0.1.0",
|
"@bytelyst/fastify-core": "^0.1.0",
|
||||||
|
"@bytelyst/field-encrypt": "^0.1.0",
|
||||||
"@bytelyst/logger": "^0.1.0",
|
"@bytelyst/logger": "^0.1.0",
|
||||||
"fastify": "5.7.4",
|
"fastify": "5.7.4",
|
||||||
"jose": "^6.0.8",
|
"jose": "^6.0.8",
|
||||||
@ -35,10 +36,10 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@bytelyst/testing": "^0.1.0",
|
"@bytelyst/testing": "^0.1.0",
|
||||||
"@types/node": "^22.12.0",
|
"@types/node": "^22.12.0",
|
||||||
|
"eslint": "^9.0.0",
|
||||||
"tsx": "^4.19.2",
|
"tsx": "^4.19.2",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"vitest": "^3.0.5",
|
"typescript-eslint": "^8.0.0",
|
||||||
"eslint": "^9.0.0",
|
"vitest": "^3.0.5"
|
||||||
"typescript-eslint": "^8.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,12 @@
|
|||||||
import { afterEach, describe, expect, it } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||||
import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
import { mkdtemp, mkdir, readFile, rm, 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';
|
||||||
|
import {
|
||||||
|
NoteArtifactEnvelopeSchema,
|
||||||
|
ArtifactCreatedEventSchema,
|
||||||
|
ArtifactLinkedEventSchema,
|
||||||
|
} from '@bytelyst/events';
|
||||||
import {
|
import {
|
||||||
buildPhase1NoteImport,
|
buildPhase1NoteImport,
|
||||||
loadLatestTranscriptArtifact,
|
loadLatestTranscriptArtifact,
|
||||||
@ -9,92 +14,250 @@ import {
|
|||||||
persistPhase1NoteOutputs,
|
persistPhase1NoteOutputs,
|
||||||
} from './ecosystem-phase1.js';
|
} from './ecosystem-phase1.js';
|
||||||
|
|
||||||
|
const TRANSCRIPT_ARTIFACT = {
|
||||||
|
id: 'art_transcript_123',
|
||||||
|
title: 'Standup capture',
|
||||||
|
summary: 'Transcript summary',
|
||||||
|
sourceSurface: 'desktop',
|
||||||
|
createdAt: '2026-04-03T18:15:00.000Z',
|
||||||
|
tags: ['voice'],
|
||||||
|
ownership: { userId: 'user_saravana', orgId: null },
|
||||||
|
provenance: {
|
||||||
|
originProductId: 'lysnrai',
|
||||||
|
originActionId: 'capture_123',
|
||||||
|
sessionId: 'sess_123',
|
||||||
|
runId: null,
|
||||||
|
approvalId: null,
|
||||||
|
correlationId: 'corr_123',
|
||||||
|
lineage: [
|
||||||
|
{
|
||||||
|
stepType: 'captured',
|
||||||
|
productId: 'lysnrai',
|
||||||
|
actorType: 'user' as const,
|
||||||
|
timestamp: '2026-04-03T18:15:00.000Z',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
transcriptText: 'Transcript body',
|
||||||
|
transcriptSource: 'microphone',
|
||||||
|
language: 'en',
|
||||||
|
durationMs: 1000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const TRANSCRIPT_CAPTURE_EVENT = {
|
||||||
|
eventId: 'evt_capture_123',
|
||||||
|
eventName: 'capture.transcript.created' as const,
|
||||||
|
eventVersion: 1 as const,
|
||||||
|
occurredAt: '2026-04-03T18:15:00.000Z',
|
||||||
|
productId: 'lysnrai',
|
||||||
|
sourceSurface: 'desktop',
|
||||||
|
userId: 'user_saravana',
|
||||||
|
orgId: null,
|
||||||
|
sessionId: 'sess_123',
|
||||||
|
runId: null,
|
||||||
|
artifactId: 'art_transcript_123',
|
||||||
|
actor: { actorType: 'user' as const, actorId: 'user_saravana' },
|
||||||
|
trace: {
|
||||||
|
correlationId: 'corr_123',
|
||||||
|
causationId: null,
|
||||||
|
parentEventId: null,
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
artifactId: 'art_transcript_123',
|
||||||
|
durationMs: 1000,
|
||||||
|
language: 'en',
|
||||||
|
transcriptSource: 'microphone' as const,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
describe('ecosystem phase1 note import', () => {
|
describe('ecosystem phase1 note import', () => {
|
||||||
afterEach(() => {
|
let root: string;
|
||||||
delete process.env.BYTELYST_ECOSYSTEM_DIR;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the latest transcript and persists a linked note artifact + events', async () => {
|
beforeEach(async () => {
|
||||||
const root = await mkdtemp(join(tmpdir(), 'notelett-phase1-'));
|
root = await mkdtemp(join(tmpdir(), 'notelett-phase1-'));
|
||||||
process.env.BYTELYST_ECOSYSTEM_DIR = root;
|
process.env.BYTELYST_ECOSYSTEM_DIR = root;
|
||||||
|
|
||||||
const transcriptArtifact = {
|
|
||||||
id: 'art_transcript_123',
|
|
||||||
title: 'Standup capture',
|
|
||||||
summary: 'Transcript summary',
|
|
||||||
sourceSurface: 'desktop',
|
|
||||||
createdAt: '2026-04-03T18:15:00.000Z',
|
|
||||||
tags: ['voice'],
|
|
||||||
ownership: { userId: 'user_saravana', orgId: null },
|
|
||||||
provenance: {
|
|
||||||
originProductId: 'lysnrai',
|
|
||||||
originActionId: 'capture_123',
|
|
||||||
sessionId: 'sess_123',
|
|
||||||
runId: null,
|
|
||||||
approvalId: null,
|
|
||||||
correlationId: 'corr_123',
|
|
||||||
lineage: [
|
|
||||||
{
|
|
||||||
stepType: 'captured',
|
|
||||||
productId: 'lysnrai',
|
|
||||||
actorType: 'user',
|
|
||||||
timestamp: '2026-04-03T18:15:00.000Z',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
payload: {
|
|
||||||
transcriptText: 'Transcript body',
|
|
||||||
transcriptSource: 'microphone',
|
|
||||||
language: 'en',
|
|
||||||
durationMs: 1000,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
await mkdir(join(root, 'indexes'), { recursive: true });
|
await mkdir(join(root, 'indexes'), { recursive: true });
|
||||||
await writeFile(
|
await writeFile(
|
||||||
join(root, 'indexes', 'latest-transcript.json'),
|
join(root, 'indexes', 'latest-transcript.json'),
|
||||||
`${JSON.stringify(transcriptArtifact, null, 2)}\n`,
|
`${JSON.stringify(TRANSCRIPT_ARTIFACT, null, 2)}\n`,
|
||||||
'utf-8'
|
'utf-8'
|
||||||
);
|
);
|
||||||
await writeFile(
|
await writeFile(
|
||||||
join(root, 'indexes', 'latest-transcript-event.json'),
|
join(root, 'indexes', 'latest-transcript-event.json'),
|
||||||
`${JSON.stringify(
|
`${JSON.stringify(TRANSCRIPT_CAPTURE_EVENT, null, 2)}\n`,
|
||||||
{
|
|
||||||
eventId: 'evt_capture_123',
|
|
||||||
eventName: 'capture.transcript.created',
|
|
||||||
eventVersion: 1,
|
|
||||||
occurredAt: '2026-04-03T18:15:00.000Z',
|
|
||||||
productId: 'lysnrai',
|
|
||||||
sourceSurface: 'desktop',
|
|
||||||
userId: 'user_saravana',
|
|
||||||
orgId: null,
|
|
||||||
sessionId: 'sess_123',
|
|
||||||
runId: null,
|
|
||||||
artifactId: 'art_transcript_123',
|
|
||||||
actor: { actorType: 'user', actorId: 'user_saravana' },
|
|
||||||
trace: {
|
|
||||||
correlationId: 'corr_123',
|
|
||||||
causationId: null,
|
|
||||||
parentEventId: null,
|
|
||||||
},
|
|
||||||
payload: {
|
|
||||||
artifactId: 'art_transcript_123',
|
|
||||||
durationMs: 1000,
|
|
||||||
language: 'en',
|
|
||||||
transcriptSource: 'microphone',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)}\n`,
|
|
||||||
'utf-8'
|
'utf-8'
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
delete process.env.BYTELYST_ECOSYSTEM_DIR;
|
||||||
|
await rm(root, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the latest transcript artifact from disk', async () => {
|
||||||
const loaded = await loadLatestTranscriptArtifact(root);
|
const loaded = await loadLatestTranscriptArtifact(root);
|
||||||
const transcriptCaptureEvent = await loadLatestTranscriptCaptureEvent(root);
|
expect(loaded.id).toBe('art_transcript_123');
|
||||||
|
expect(loaded.payload.transcriptText).toBe('Transcript body');
|
||||||
|
expect(loaded.provenance.originProductId).toBe('lysnrai');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the latest transcript capture event from disk', async () => {
|
||||||
|
const event = await loadLatestTranscriptCaptureEvent(root);
|
||||||
|
expect(event).not.toBeNull();
|
||||||
|
expect(event!.eventName).toBe('capture.transcript.created');
|
||||||
|
expect(event!.artifactId).toBe('art_transcript_123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null when transcript capture event file is missing', async () => {
|
||||||
|
await rm(join(root, 'indexes', 'latest-transcript-event.json'));
|
||||||
|
const event = await loadLatestTranscriptCaptureEvent(root);
|
||||||
|
expect(event).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a note linked to the transcript artifact', () => {
|
||||||
|
const generated = buildPhase1NoteImport({
|
||||||
|
transcriptArtifact: TRANSCRIPT_ARTIFACT,
|
||||||
|
transcriptCaptureEvent: TRANSCRIPT_CAPTURE_EVENT,
|
||||||
|
workspaceId: 'ws_phase1',
|
||||||
|
userId: 'user_saravana',
|
||||||
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(generated.note.productId).toBe('notelett');
|
||||||
|
expect(generated.note.sourceType).toBe('ecosystem-transcript');
|
||||||
|
expect(generated.note.sourceUri).toBe('art_transcript_123');
|
||||||
|
expect(generated.note.links).toContain('art_transcript_123');
|
||||||
|
expect(generated.note.tags).toContain('ecosystem');
|
||||||
|
expect(generated.note.tags).toContain('phase1');
|
||||||
|
expect(generated.note.body).toContain('Transcript body');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a note artifact doc for internal persistence', () => {
|
||||||
|
const generated = buildPhase1NoteImport({
|
||||||
|
transcriptArtifact: TRANSCRIPT_ARTIFACT,
|
||||||
|
transcriptCaptureEvent: TRANSCRIPT_CAPTURE_EVENT,
|
||||||
|
workspaceId: 'ws_phase1',
|
||||||
|
userId: 'user_saravana',
|
||||||
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(generated.noteArtifactDoc.productId).toBe('notelett');
|
||||||
|
expect(generated.noteArtifactDoc.artifactType).toBe('summary');
|
||||||
|
expect(generated.noteArtifactDoc.noteId).toBe(generated.note.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits artifact.created event with upstream causation', () => {
|
||||||
|
const generated = buildPhase1NoteImport({
|
||||||
|
transcriptArtifact: TRANSCRIPT_ARTIFACT,
|
||||||
|
transcriptCaptureEvent: TRANSCRIPT_CAPTURE_EVENT,
|
||||||
|
workspaceId: 'ws_phase1',
|
||||||
|
userId: 'user_saravana',
|
||||||
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(generated.createdEvent.eventName).toBe('artifact.created');
|
||||||
|
expect(generated.createdEvent.productId).toBe('notelett');
|
||||||
|
expect(generated.createdEvent.artifactId).toBe(generated.ecosystemNoteArtifact.id);
|
||||||
|
expect(generated.createdEvent.trace.causationId).toBe('evt_capture_123');
|
||||||
|
expect(generated.createdEvent.trace.parentEventId).toBe('evt_capture_123');
|
||||||
|
expect(generated.createdEvent.trace.correlationId).toBe('corr_123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits artifact.linked event chained from artifact.created', () => {
|
||||||
|
const generated = buildPhase1NoteImport({
|
||||||
|
transcriptArtifact: TRANSCRIPT_ARTIFACT,
|
||||||
|
transcriptCaptureEvent: TRANSCRIPT_CAPTURE_EVENT,
|
||||||
|
workspaceId: 'ws_phase1',
|
||||||
|
userId: 'user_saravana',
|
||||||
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(generated.linkedEvent.eventName).toBe('artifact.linked');
|
||||||
|
expect(generated.linkedEvent.trace.causationId).toBe(generated.createdEvent.eventId);
|
||||||
|
expect(generated.linkedEvent.trace.parentEventId).toBe(generated.createdEvent.eventId);
|
||||||
|
expect(generated.linkedEvent.payload).toEqual({
|
||||||
|
sourceArtifactId: generated.ecosystemNoteArtifact.id,
|
||||||
|
targetArtifactId: 'art_transcript_123',
|
||||||
|
relation: 'summarizes',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves provenance lineage from transcript to note', () => {
|
||||||
|
const generated = buildPhase1NoteImport({
|
||||||
|
transcriptArtifact: TRANSCRIPT_ARTIFACT,
|
||||||
|
transcriptCaptureEvent: TRANSCRIPT_CAPTURE_EVENT,
|
||||||
|
workspaceId: 'ws_phase1',
|
||||||
|
userId: 'user_saravana',
|
||||||
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { provenance } = generated.ecosystemNoteArtifact;
|
||||||
|
expect(provenance.originProductId).toBe('lysnrai');
|
||||||
|
expect(provenance.lineage).toHaveLength(2);
|
||||||
|
expect(provenance.lineage[0]).toEqual({
|
||||||
|
stepType: 'captured',
|
||||||
|
productId: 'lysnrai',
|
||||||
|
actorType: 'user',
|
||||||
|
timestamp: '2026-04-03T18:15:00.000Z',
|
||||||
|
});
|
||||||
|
expect(provenance.lineage[1]).toMatchObject({
|
||||||
|
stepType: 'note-created',
|
||||||
|
productId: 'notelett',
|
||||||
|
actorType: 'agent',
|
||||||
|
});
|
||||||
|
expect(generated.ecosystemNoteArtifact.links).toEqual([
|
||||||
|
{ relation: 'summarizes', targetArtifactId: 'art_transcript_123' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('note artifact conforms to @bytelyst/events NoteArtifactEnvelopeSchema', () => {
|
||||||
|
const generated = buildPhase1NoteImport({
|
||||||
|
transcriptArtifact: TRANSCRIPT_ARTIFACT,
|
||||||
|
transcriptCaptureEvent: TRANSCRIPT_CAPTURE_EVENT,
|
||||||
|
workspaceId: 'ws_phase1',
|
||||||
|
userId: 'user_saravana',
|
||||||
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = NoteArtifactEnvelopeSchema.safeParse(generated.ecosystemNoteArtifact);
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('artifact.created event conforms to @bytelyst/events ArtifactCreatedEventSchema', () => {
|
||||||
|
const generated = buildPhase1NoteImport({
|
||||||
|
transcriptArtifact: TRANSCRIPT_ARTIFACT,
|
||||||
|
transcriptCaptureEvent: TRANSCRIPT_CAPTURE_EVENT,
|
||||||
|
workspaceId: 'ws_phase1',
|
||||||
|
userId: 'user_saravana',
|
||||||
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = ArtifactCreatedEventSchema.safeParse(generated.createdEvent);
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('artifact.linked event conforms to @bytelyst/events ArtifactLinkedEventSchema', () => {
|
||||||
|
const generated = buildPhase1NoteImport({
|
||||||
|
transcriptArtifact: TRANSCRIPT_ARTIFACT,
|
||||||
|
transcriptCaptureEvent: TRANSCRIPT_CAPTURE_EVENT,
|
||||||
|
workspaceId: 'ws_phase1',
|
||||||
|
userId: 'user_saravana',
|
||||||
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = ArtifactLinkedEventSchema.safeParse(generated.linkedEvent);
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('persists artifacts and events to disk and populates index files', async () => {
|
||||||
|
const loaded = await loadLatestTranscriptArtifact(root);
|
||||||
|
const captureEvent = await loadLatestTranscriptCaptureEvent(root);
|
||||||
const generated = buildPhase1NoteImport({
|
const generated = buildPhase1NoteImport({
|
||||||
transcriptArtifact: loaded,
|
transcriptArtifact: loaded,
|
||||||
transcriptCaptureEvent,
|
transcriptCaptureEvent: captureEvent,
|
||||||
workspaceId: 'ws_phase1',
|
workspaceId: 'ws_phase1',
|
||||||
userId: 'user_saravana',
|
userId: 'user_saravana',
|
||||||
now: '2026-04-03T18:17:00.000Z',
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
@ -107,36 +270,48 @@ describe('ecosystem phase1 note import', () => {
|
|||||||
root,
|
root,
|
||||||
});
|
});
|
||||||
|
|
||||||
const savedNoteArtifact = JSON.parse(
|
const savedArtifact = JSON.parse(
|
||||||
await readFile(join(root, 'artifacts', 'note', `${generated.ecosystemNoteArtifact.id}.json`), 'utf-8')
|
await readFile(join(root, 'artifacts', 'note', `${generated.ecosystemNoteArtifact.id}.json`), 'utf-8')
|
||||||
);
|
);
|
||||||
|
expect(savedArtifact.artifactType).toBe('note');
|
||||||
|
expect(savedArtifact.payload.noteFormat).toBe('markdown');
|
||||||
|
|
||||||
|
const savedCreatedEvent = JSON.parse(
|
||||||
|
await readFile(join(root, 'events', 'artifact.created', `${generated.createdEvent.eventId}.json`), 'utf-8')
|
||||||
|
);
|
||||||
|
expect(savedCreatedEvent.eventName).toBe('artifact.created');
|
||||||
|
|
||||||
const savedLinkedEvent = JSON.parse(
|
const savedLinkedEvent = JSON.parse(
|
||||||
await readFile(join(root, 'events', 'artifact.linked', `${generated.linkedEvent.eventId}.json`), 'utf-8')
|
await readFile(join(root, 'events', 'artifact.linked', `${generated.linkedEvent.eventId}.json`), 'utf-8')
|
||||||
);
|
);
|
||||||
|
expect(savedLinkedEvent.eventName).toBe('artifact.linked');
|
||||||
expect(generated.note.sourceUri).toBe('art_transcript_123');
|
|
||||||
expect(generated.ecosystemNoteArtifact.links).toEqual([
|
|
||||||
{
|
|
||||||
relation: 'summarizes',
|
|
||||||
targetArtifactId: 'art_transcript_123',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
expect(savedNoteArtifact.payload.noteFormat).toBe('markdown');
|
|
||||||
expect(savedLinkedEvent.payload.targetArtifactId).toBe('art_transcript_123');
|
expect(savedLinkedEvent.payload.targetArtifactId).toBe('art_transcript_123');
|
||||||
expect(savedLinkedEvent.payload.relation).toBe('summarizes');
|
|
||||||
expect(generated.createdEvent.trace.causationId).toBe('evt_capture_123');
|
|
||||||
expect(generated.createdEvent.trace.parentEventId).toBe('evt_capture_123');
|
|
||||||
expect(savedLinkedEvent.trace.causationId).toBe(generated.createdEvent.eventId);
|
|
||||||
|
|
||||||
const savedCreatedEventIndex = JSON.parse(
|
const noteIndex = JSON.parse(await readFile(join(root, 'indexes', 'latest-note.json'), 'utf-8'));
|
||||||
|
expect(noteIndex.id).toBe(generated.ecosystemNoteArtifact.id);
|
||||||
|
|
||||||
|
const createdEventIndex = JSON.parse(
|
||||||
await readFile(join(root, 'indexes', 'latest-note-created-event.json'), 'utf-8')
|
await readFile(join(root, 'indexes', 'latest-note-created-event.json'), 'utf-8')
|
||||||
);
|
);
|
||||||
const savedLinkedEventIndex = JSON.parse(
|
expect(createdEventIndex.eventId).toBe(generated.createdEvent.eventId);
|
||||||
|
|
||||||
|
const linkedEventIndex = JSON.parse(
|
||||||
await readFile(join(root, 'indexes', 'latest-note-linked-event.json'), 'utf-8')
|
await readFile(join(root, 'indexes', 'latest-note-linked-event.json'), 'utf-8')
|
||||||
);
|
);
|
||||||
expect(savedCreatedEventIndex.eventId).toBe(generated.createdEvent.eventId);
|
expect(linkedEventIndex.eventId).toBe(generated.linkedEvent.eventId);
|
||||||
expect(savedLinkedEventIndex.eventId).toBe(generated.linkedEvent.eventId);
|
});
|
||||||
|
|
||||||
await rm(root, { recursive: true, force: true });
|
it('works without upstream capture event (graceful degradation)', () => {
|
||||||
|
const generated = buildPhase1NoteImport({
|
||||||
|
transcriptArtifact: TRANSCRIPT_ARTIFACT,
|
||||||
|
transcriptCaptureEvent: null,
|
||||||
|
workspaceId: 'ws_phase1',
|
||||||
|
userId: 'user_saravana',
|
||||||
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(generated.createdEvent.trace.causationId).toBeNull();
|
||||||
|
expect(generated.createdEvent.trace.parentEventId).toBeNull();
|
||||||
|
expect(generated.note.sourceUri).toBe('art_transcript_123');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -29,6 +29,7 @@ vi.mock('./modules/note-relationships/routes.js', () => ({ noteRelationshipRoute
|
|||||||
vi.mock('./modules/note-tasks/routes.js', () => ({ noteTaskRoutes: vi.fn() }));
|
vi.mock('./modules/note-tasks/routes.js', () => ({ noteTaskRoutes: vi.fn() }));
|
||||||
vi.mock('./modules/saved-views/routes.js', () => ({ savedViewRoutes: vi.fn() }));
|
vi.mock('./modules/saved-views/routes.js', () => ({ savedViewRoutes: vi.fn() }));
|
||||||
vi.mock('./modules/workspaces/routes.js', () => ({ workspaceRoutes: vi.fn() }));
|
vi.mock('./modules/workspaces/routes.js', () => ({ workspaceRoutes: vi.fn() }));
|
||||||
|
vi.mock('./modules/ecosystem-phase1/routes.js', () => ({ ecosystemPhase1Routes: vi.fn() }));
|
||||||
vi.mock('./lib/cosmos-init.js', () => ({ initCosmosIfNeeded: initCosmosIfNeededMock }));
|
vi.mock('./lib/cosmos-init.js', () => ({ initCosmosIfNeeded: initCosmosIfNeededMock }));
|
||||||
vi.mock('./lib/datastore.js', () => ({ initDatastore: initDatastoreMock }));
|
vi.mock('./lib/datastore.js', () => ({ initDatastore: initDatastoreMock }));
|
||||||
vi.mock('./lib/config.js', () => ({
|
vi.mock('./lib/config.js', () => ({
|
||||||
@ -65,7 +66,7 @@ describe('server bootstrap', () => {
|
|||||||
expect(initDatastoreMock).toHaveBeenCalledOnce();
|
expect(initDatastoreMock).toHaveBeenCalledOnce();
|
||||||
expect(createServiceAppMock).toHaveBeenCalledOnce();
|
expect(createServiceAppMock).toHaveBeenCalledOnce();
|
||||||
expect(registerOptionalJwtContextMock).toHaveBeenCalledOnce();
|
expect(registerOptionalJwtContextMock).toHaveBeenCalledOnce();
|
||||||
expect(appMock.register).toHaveBeenCalledTimes(7);
|
expect(appMock.register).toHaveBeenCalledTimes(8);
|
||||||
expect(startServiceMock).toHaveBeenCalledWith(appMock, { port: 4016, host: '0.0.0.0' });
|
expect(startServiceMock).toHaveBeenCalledWith(appMock, { port: 4016, host: '0.0.0.0' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@ -41,6 +41,9 @@ importers:
|
|||||||
'@bytelyst/errors':
|
'@bytelyst/errors':
|
||||||
specifier: ^0.1.0
|
specifier: ^0.1.0
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
'@bytelyst/events':
|
||||||
|
specifier: file:/tmp/bytelyst-events-0.1.0.tgz
|
||||||
|
version: file:../../../../../tmp/bytelyst-events-0.1.0.tgz(zod@3.25.76)
|
||||||
'@bytelyst/fastify-auth':
|
'@bytelyst/fastify-auth':
|
||||||
specifier: ^0.1.0
|
specifier: ^0.1.0
|
||||||
version: 0.1.0(fastify@5.7.4)(jose@6.2.2)
|
version: 0.1.0(fastify@5.7.4)(jose@6.2.2)
|
||||||
@ -969,6 +972,12 @@ packages:
|
|||||||
'@bytelyst/errors@0.1.0':
|
'@bytelyst/errors@0.1.0':
|
||||||
resolution: {integrity: sha512-hE4sHwmQUDGZYDdo3w7VuRdVfuaXgEcG2f0KD0ZLJF+EgfRmDV3IevD1ubPsJIIZxMu8brK8zZOvPohhsMsYdw==, tarball: http://localhost:3300/api/packages/bytelyst/npm/%40bytelyst%2Ferrors/-/0.1.0/errors-0.1.0.tgz}
|
resolution: {integrity: sha512-hE4sHwmQUDGZYDdo3w7VuRdVfuaXgEcG2f0KD0ZLJF+EgfRmDV3IevD1ubPsJIIZxMu8brK8zZOvPohhsMsYdw==, tarball: http://localhost:3300/api/packages/bytelyst/npm/%40bytelyst%2Ferrors/-/0.1.0/errors-0.1.0.tgz}
|
||||||
|
|
||||||
|
'@bytelyst/events@file:../../../../../tmp/bytelyst-events-0.1.0.tgz':
|
||||||
|
resolution: {integrity: sha512-9HmjfrDMmR63UHVbuaruIPEbALWwApcdH/lfeOiF6W+pFpd6FV3yrnLh04gKMzxBOWyGzysH+vrGI7Xnt4PpnQ==, tarball: file:../../../../../tmp/bytelyst-events-0.1.0.tgz}
|
||||||
|
version: 0.1.0
|
||||||
|
peerDependencies:
|
||||||
|
zod: ^3.0.0
|
||||||
|
|
||||||
'@bytelyst/extraction@0.1.0':
|
'@bytelyst/extraction@0.1.0':
|
||||||
resolution: {integrity: sha512-Pzb7T30ig2iZ10hnVM6sjLkKEspVo1j3PgTCERrevBVMbGL7NpBtn/rTwiMVghzj6Q8wDTKwn/q1ZkurzAuF8A==, tarball: http://localhost:3300/api/packages/bytelyst/npm/%40bytelyst%2Fextraction/-/0.1.0/extraction-0.1.0.tgz}
|
resolution: {integrity: sha512-Pzb7T30ig2iZ10hnVM6sjLkKEspVo1j3PgTCERrevBVMbGL7NpBtn/rTwiMVghzj6Q8wDTKwn/q1ZkurzAuF8A==, tarball: http://localhost:3300/api/packages/bytelyst/npm/%40bytelyst%2Fextraction/-/0.1.0/extraction-0.1.0.tgz}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1025,6 +1034,9 @@ packages:
|
|||||||
'@bytelyst/platform-client@0.1.0':
|
'@bytelyst/platform-client@0.1.0':
|
||||||
resolution: {integrity: sha512-KmlxcYp4mFuhdTCj8QT+1QGXtk1ViM1hGqGrO7qMhy+IEnjFHsqMxboArUpR/ZTqWNXa1SrH4w7ws29xtjUO1w==, tarball: http://localhost:3300/api/packages/bytelyst/npm/%40bytelyst%2Fplatform-client/-/0.1.0/platform-client-0.1.0.tgz}
|
resolution: {integrity: sha512-KmlxcYp4mFuhdTCj8QT+1QGXtk1ViM1hGqGrO7qMhy+IEnjFHsqMxboArUpR/ZTqWNXa1SrH4w7ws29xtjUO1w==, tarball: http://localhost:3300/api/packages/bytelyst/npm/%40bytelyst%2Fplatform-client/-/0.1.0/platform-client-0.1.0.tgz}
|
||||||
|
|
||||||
|
'@bytelyst/queue@0.1.0':
|
||||||
|
resolution: {integrity: sha512-G+UrRxn35/CEA7R5bi8hg846I4Mu9/CvLoP3pGLK2XcMbMNGL4nka+Pz1m10yDsPH2s8wr1ML7wdwg/xvbHOAA==, tarball: http://localhost:3300/api/packages/bytelyst/npm/%40bytelyst%2Fqueue/-/0.1.0/queue-0.1.0.tgz}
|
||||||
|
|
||||||
'@bytelyst/react-auth@0.1.1':
|
'@bytelyst/react-auth@0.1.1':
|
||||||
resolution: {integrity: sha512-nXLXh38/nTmIOYKGra0kvzh0AWSU/xhOdVdIZR2ndkhIxi+YqUb6tUoO35hHp+rVeneOm/UhKzOFcmwbvQOndg==, tarball: http://localhost:3300/api/packages/bytelyst/npm/%40bytelyst%2Freact-auth/-/0.1.1/react-auth-0.1.1.tgz}
|
resolution: {integrity: sha512-nXLXh38/nTmIOYKGra0kvzh0AWSU/xhOdVdIZR2ndkhIxi+YqUb6tUoO35hHp+rVeneOm/UhKzOFcmwbvQOndg==, tarball: http://localhost:3300/api/packages/bytelyst/npm/%40bytelyst%2Freact-auth/-/0.1.1/react-auth-0.1.1.tgz}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -7159,6 +7171,11 @@ snapshots:
|
|||||||
|
|
||||||
'@bytelyst/errors@0.1.0': {}
|
'@bytelyst/errors@0.1.0': {}
|
||||||
|
|
||||||
|
'@bytelyst/events@file:../../../../../tmp/bytelyst-events-0.1.0.tgz(zod@3.25.76)':
|
||||||
|
dependencies:
|
||||||
|
'@bytelyst/queue': 0.1.0
|
||||||
|
zod: 3.25.76
|
||||||
|
|
||||||
'@bytelyst/extraction@0.1.0(@bytelyst/api-client@0.1.0)':
|
'@bytelyst/extraction@0.1.0(@bytelyst/api-client@0.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@bytelyst/api-client': 0.1.0
|
'@bytelyst/api-client': 0.1.0
|
||||||
@ -7197,6 +7214,8 @@ snapshots:
|
|||||||
|
|
||||||
'@bytelyst/platform-client@0.1.0': {}
|
'@bytelyst/platform-client@0.1.0': {}
|
||||||
|
|
||||||
|
'@bytelyst/queue@0.1.0': {}
|
||||||
|
|
||||||
'@bytelyst/react-auth@0.1.1(react@19.2.0)':
|
'@bytelyst/react-auth@0.1.1(react@19.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@bytelyst/api-client': 0.1.0
|
'@bytelyst/api-client': 0.1.0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user