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()); }); });