chore: add ion package, codesight wiki, work plans, ascli config
New @boocode/ion package (v0.0.1) for inference optimization network. .codesight/ wiki artifacts for codebase documentation. .omo/ work plans for openspec cleanup and enhanced file panel.
This commit is contained in:
132
packages/ion/src/engine/__tests__/output-ref.test.ts
Normal file
132
packages/ion/src/engine/__tests__/output-ref.test.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
resolveNodeOutputField,
|
||||
declaredFieldsFromSchema,
|
||||
OutputRefError,
|
||||
} from '../output-ref.js';
|
||||
|
||||
describe('resolveNodeOutputField', () => {
|
||||
describe('with declared schema match', () => {
|
||||
it('returns value when field exists in output and schema', () => {
|
||||
const declaredFields = new Set(['name', 'status']);
|
||||
const output = { name: 'test-result', status: 'completed' };
|
||||
const result = resolveNodeOutputField(output, 'node-1', 'name', declaredFields);
|
||||
expect(result).toEqual({ kind: 'value', value: 'test-result' });
|
||||
});
|
||||
|
||||
it('returns JSON-serialized value for non-string fields', () => {
|
||||
const declaredFields = new Set(['count']);
|
||||
const output = { count: 42 };
|
||||
const result = resolveNodeOutputField(output, 'node-1', 'count', declaredFields);
|
||||
expect(result).toEqual({ kind: 'value', value: '42' });
|
||||
});
|
||||
|
||||
it('returns JSON-serialized value for object fields', () => {
|
||||
const declaredFields = new Set(['data']);
|
||||
const output = { data: { key: 'val' } };
|
||||
const result = resolveNodeOutputField(output, 'node-1', 'data', declaredFields);
|
||||
expect(result).toEqual({ kind: 'value', value: '{"key":"val"}' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('with schemaless JSON output', () => {
|
||||
it('returns value when field exists in output without schema', () => {
|
||||
const output = { dynamic_field: 'hello' };
|
||||
const result = resolveNodeOutputField(output, 'node-1', 'dynamic_field');
|
||||
expect(result).toEqual({ kind: 'value', value: 'hello' });
|
||||
});
|
||||
|
||||
it('returns JSON-serialized number without schema', () => {
|
||||
const output = { score: 99 };
|
||||
const result = resolveNodeOutputField(output, 'node-1', 'score');
|
||||
expect(result).toEqual({ kind: 'value', value: '99' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('missing optional field', () => {
|
||||
it('returns empty when field is declared in schema but missing from output', () => {
|
||||
const declaredFields = new Set(['name', 'optional_field']);
|
||||
const output = { name: 'test' };
|
||||
const result = resolveNodeOutputField(output, 'node-1', 'optional_field', declaredFields);
|
||||
expect(result).toEqual({ kind: 'empty', value: '' });
|
||||
});
|
||||
|
||||
it('returns empty when field exists but value is null', () => {
|
||||
const declaredFields = new Set(['name']);
|
||||
const output = { name: null };
|
||||
const result = resolveNodeOutputField(output, 'node-1', 'name', declaredFields);
|
||||
expect(result).toEqual({ kind: 'empty', value: '' });
|
||||
});
|
||||
|
||||
it('returns empty when field exists but value is undefined', () => {
|
||||
const declaredFields = new Set(['name']);
|
||||
const output = { name: undefined };
|
||||
const result = resolveNodeOutputField(output, 'node-1', 'name', declaredFields);
|
||||
expect(result).toEqual({ kind: 'empty', value: '' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('missing required field', () => {
|
||||
it('throws OutputRefError when field is not declared and not in output', () => {
|
||||
const output = { name: 'test' };
|
||||
expect(() => resolveNodeOutputField(output, 'node-1', 'nonexistent')).toThrow(OutputRefError);
|
||||
});
|
||||
|
||||
it('throws OutputRefError with nodeId and field info', () => {
|
||||
const output = { name: 'test' };
|
||||
try {
|
||||
resolveNodeOutputField(output, 'my-node', 'missing_field');
|
||||
expect.unreachable('Should have thrown');
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(OutputRefError);
|
||||
if (err instanceof OutputRefError) {
|
||||
expect(err.nodeId).toBe('my-node');
|
||||
expect(err.field).toBe('missing_field');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('includes available fields in error message', () => {
|
||||
const output = { name: 'test', status: 'ok' };
|
||||
try {
|
||||
resolveNodeOutputField(output, 'node-1', 'nonexistent');
|
||||
expect.unreachable('Should have thrown');
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(OutputRefError);
|
||||
if (err instanceof OutputRefError) {
|
||||
expect(err.message).toContain('name');
|
||||
expect(err.message).toContain('status');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('declaredFieldsFromSchema', () => {
|
||||
it('extracts fields from a valid JSON Schema object', () => {
|
||||
const schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
count: { type: 'number' },
|
||||
},
|
||||
};
|
||||
const fields = declaredFieldsFromSchema(schema);
|
||||
expect(fields).toEqual(new Set(['name', 'count']));
|
||||
});
|
||||
|
||||
it('returns empty set for undefined schema', () => {
|
||||
const fields = declaredFieldsFromSchema(undefined);
|
||||
expect(fields).toEqual(new Set());
|
||||
});
|
||||
|
||||
it('returns empty set for string schema', () => {
|
||||
const fields = declaredFieldsFromSchema('just a string description');
|
||||
expect(fields).toEqual(new Set());
|
||||
});
|
||||
|
||||
it('returns empty set for schema without properties', () => {
|
||||
const fields = declaredFieldsFromSchema({ type: 'object' });
|
||||
expect(fields).toEqual(new Set());
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user