UNPKG

@radcliffetech/symbolos-core

Version:

Core symbolic simulation and execution engine for Symbolos

301 lines (300 loc) 10.9 kB
import { createWorldFromFrame, makeNewWorld, runWorldPipeline } from './world-simulator'; import { describe, expect, it } from 'vitest'; import { forkWorld } from './world-simulator'; it('forks a world and retains aonst userArgs: Prtifacts and context', () => { const original = makeNewWorld('fire-spread'); const obj = { id: 'x', type: 'Thing', createdAt: new Date().toISOString(), status: 'active' }; original.artifacts.set('x', obj); const forked = forkWorld(original, { firebreak: true }); expect(forked.runId).not.toBe(original.runId); expect(forked.pipelineId).toBe('fire-spread'); expect(forked.artifacts.has('x')).toBe(true); expect(forked.context.forkedFromRunId).toBe(original.runId); expect(forked.context.userArgs.params.firebreak).toBe(true); }); describe('world-simulator', () => { it('creates a new world with correct structure', () => { const world = makeNewWorld('test-pipeline'); expect(world.tick).toBe(0); expect(world.pipelineId).toBe('test-pipeline'); expect(world.artifacts).toBeInstanceOf(Map); }); it('creates a world from frame with correct artifacts', () => { const mockFrame = { id: 'frame-1', tick: 3, step: 2, runId: 'abc', type: 'SymbolicWorldFrame', pipelineId: 'mock-pipeline', createdAt: new Date().toISOString(), status: 'active', members: [{ id: 'x', type: 'MockObject', label: 'X', createdAt: new Date().toISOString() }], }; const world = createWorldFromFrame({ frame: mockFrame, pipelineId: mockFrame.pipelineId, runId: mockFrame.runId, }); expect(world.tick).toBe(3); expect(world.artifacts.get('x')).toBeDefined(); expect(world.pipelineId).toBe('mock-pipeline'); }); it('runs a pipeline and stores output in artifacts', async () => { const world = makeNewWorld('test'); const userArgs = { type: 'PipelineArgs', id: 'args-1', createdAt: new Date().toISOString(), status: 'valid', pipelineId: 'test-pipeline', runId: world.runId, params: {}, }; const steps = [ { id: 'step-1', purpose: 'test-step', functor: { id: 'mock-functor', name: 'MockFunctor', method: 'mock', inputType: 'PipelineArgs', outputType: 'MockResult', apply: async () => ({ id: 'obj-1', type: 'MockResult', createdAt: new Date().toISOString(), }), describeProvenance: () => ({ source: 'test' }), }, }, ]; const result = await runWorldPipeline({ world, userArgs, steps, config: { verbose: false }, }); expect(result.tick).toBeGreaterThan(0); expect(result.artifacts.has('obj-1')).toBe(true); }); }); it('stores step output in context under expected key', async () => { const world = makeNewWorld('test'); const userArgs = { type: 'PipelineArgs', id: 'args-2', status: 'valid', runId: world.runId, pipelineId: 'test-pipeline', createdAt: new Date().toISOString(), params: {}, }; const steps = [ { id: 'step-1', purpose: 'store-context-test', functor: { id: 'mock-store', name: 'MockStoreFunctor', method: 'mock-store', inputType: 'PipelineArgs', outputType: 'MockResult', apply: async () => ({ id: 'mock-result', type: 'MockResult', createdAt: new Date().toISOString(), }), describeProvenance: () => ({ source: 'test' }), }, storeOutputAs: 'Frame_t0', tickAdvance: false, }, ]; const result = await runWorldPipeline({ world, userArgs, steps, config: { verbose: false }, }); expect(result.context.Frame_t0).toBeDefined(); expect(result.context.Frame_t0).toMatchObject({ id: 'mock-result' }); }); it('initializes tick, step, and artifacts correctly from frame', () => { const mockFrame = { id: 'frame-x', tick: 7, step: 3, runId: 'run-x', pipelineId: 'pipeline-x', type: 'SymbolicWorldFrame', status: 'active', createdAt: new Date().toISOString(), members: [ { id: 'alpha', type: 'TestObject', createdAt: new Date().toISOString() }, { id: 'beta', type: 'TestObject', createdAt: new Date().toISOString() }, ], }; const world = createWorldFromFrame({ frame: mockFrame, pipelineId: mockFrame.pipelineId, runId: mockFrame.runId, }); expect(world.tick).toBe(7); expect(world.step).toBe(3); expect(world.artifacts.size).toBe(2); expect(world.artifacts.get('alpha')?.type).toBe('TestObject'); expect(world.pipelineId).toBe('pipeline-x'); }); it('forks a world from a frame and retains pipelineId and forkedFromRunId', () => { const mockFrame = { id: 'frame-a', tick: 5, step: 4, runId: 'original-run', pipelineId: 'mock-pipeline', type: 'SymbolicWorldFrame', status: 'active', createdAt: new Date().toISOString(), members: [ { id: 'a', type: 'Thing', createdAt: new Date().toISOString() }, { id: 'b', type: 'Thing', createdAt: new Date().toISOString() }, ], }; const baseWorld = createWorldFromFrame({ frame: mockFrame, pipelineId: mockFrame.pipelineId, runId: mockFrame.runId, }); const forked = forkWorld(baseWorld, { firebreak: true }); expect(forked.pipelineId).toBe('mock-pipeline'); expect(forked.context.forkedFromRunId).toBe('original-run'); expect(forked.artifacts.has('a')).toBe(true); expect(forked.artifacts.has('b')).toBe(true); }); it('runs a different pipeline after forking and generates a distinct PipelineRun', async () => { const baseFrame = { id: 'base-frame', tick: 2, step: 2, runId: 'base-run', pipelineId: 'starter-pipeline', type: 'SymbolicWorldFrame', status: 'active', createdAt: new Date().toISOString(), members: [{ id: 'x', type: 'BaseObject', createdAt: new Date().toISOString() }], }; const baseWorld = createWorldFromFrame({ frame: baseFrame, pipelineId: baseFrame.pipelineId, runId: baseFrame.runId, }); const forked = forkWorld(baseWorld, { alt: true }); const userArgs = { type: 'PipelineArgs', id: 'args-fork', status: 'valid', createdAt: new Date().toISOString(), runId: forked.runId, pipelineId: 'forked-pipeline', params: { alt: true }, }; const steps = [ { id: 'alt-step', purpose: 'altered path', functor: { id: 'alt-fn', name: 'AltFunctor', method: 'alt-method', inputType: 'PipelineArgs', outputType: 'AlteredResult', apply: async () => ({ id: 'alt-result', type: 'AlteredResult', createdAt: new Date().toISOString(), }), describeProvenance: () => ({ source: 'fork-test' }), }, storeOutputAs: 'AlteredOutput', }, ]; const result = await runWorldPipeline({ world: forked, userArgs, steps, config: { verbose: false }, }); const pipelineRuns = Array.from(result.artifacts.values()).filter((o) => o.type === 'PipelineRun'); expect(pipelineRuns.length).toBe(1); expect(pipelineRuns[0].pipelineId).toBe('starter-pipeline'); expect(pipelineRuns[0].label).toMatch(/Forked Run/); }); it('creates multiple forks from a base world and runs separate pipelines', async () => { const baseFrame = { id: 'frame-base', tick: 1, step: 1, runId: 'original-run', pipelineId: 'base-pipeline', type: 'SymbolicWorldFrame', status: 'active', createdAt: new Date().toISOString(), members: [ { id: 'a', type: 'Symbol', createdAt: new Date().toISOString() }, { id: 'b', type: 'Symbol', createdAt: new Date().toISOString() }, ], }; const baseWorld = createWorldFromFrame({ frame: baseFrame, pipelineId: baseFrame.pipelineId, runId: baseFrame.runId, }); const results = await Promise.all(['fork-A', 'fork-B', 'fork-C'].map(async (label, index) => { const forked = forkWorld(baseWorld, { branch: label }); const userArgs = { type: 'PipelineArgs', id: `args-${label}`, status: 'valid', createdAt: new Date().toISOString(), runId: forked.runId, pipelineId: `branch-${index + 1}`, params: { branch: label }, }; const steps = [ { id: `step-${label}`, purpose: `test fork ${label}`, functor: { id: `functor-${label}`, name: `Functor ${label}`, method: `method-${label}`, inputType: 'PipelineArgs', outputType: 'BranchResult', apply: async () => ({ id: `result-${label}`, type: 'BranchResult', createdAt: new Date().toISOString(), }), describeProvenance: () => ({ source: `fork-${label}` }), }, storeOutputAs: `BranchOutput_${label}`, }, ]; const result = await runWorldPipeline({ world: forked, userArgs, steps, config: { verbose: false }, }); return result; })); expect(results.length).toBe(3); results.forEach((world, i) => { const branchResult = world.artifacts.get(`result-fork-${String.fromCharCode(65 + i)}`); expect(branchResult).toBeDefined(); expect(world.pipelineId).toBe('base-pipeline'); expect(world.context.forkedFromRunId).toBe('original-run'); }); });