// 單元測試:validateContract // Requirements: 1.1, 1.2, 11.5 import { describe, it, expect } from 'vitest'; import { validateContract } from '../src/actions/validateContract'; const VALID_CONTRACT = { canonical_id: 'validate_json', display_name: 'JSON 格式驗證器', category: 'logic', version: 'v1', wasi_target: 'preview1', stability: 'floating', runtime_compat: ['cf-workers', 'wazero'], constraints: { max_size_kb: 2048, max_cold_start_ms: 50, no_network_syscall: true, io_model: 'stdin_stdout_json', }, input_schema: { type: 'object', required: ['json_string'] }, output_schema: { type: 'object', properties: { valid: { type: 'boolean' } } }, gherkin_tests: [ { scenario: 'happy path', given: '{"json_string":"{}"}', then_contains: '{"valid":true}' }, { scenario: 'error path', given: '{"json_string":"bad"}', then_contains: '{"valid":false' }, ], }; describe('validateContract', () => { it('完整合約通過驗證', () => { const result = validateContract(VALID_CONTRACT); expect(result.valid).toBe(true); expect(result.missing_fields).toHaveLength(0); expect(result.errors).toHaveLength(0); }); it('缺少 canonical_id 時回傳 missing_fields', () => { const { canonical_id: _, ...rest } = VALID_CONTRACT; const result = validateContract(rest); expect(result.valid).toBe(false); expect(result.missing_fields).toContain('canonical_id'); }); it('缺少 version 時回傳 missing_fields', () => { const { version: _, ...rest } = VALID_CONTRACT; const result = validateContract(rest); expect(result.valid).toBe(false); expect(result.missing_fields).toContain('version'); }); it('缺少 constraints.io_model 時驗證失敗', () => { const contract = { ...VALID_CONTRACT, constraints: { max_size_kb: 2048, max_cold_start_ms: 50, no_network_syscall: true, // io_model 缺失 }, }; const result = validateContract(contract); expect(result.valid).toBe(false); // io_model 缺失時可能在 missing_fields 或 errors 中 const allIssues = [...result.missing_fields, ...result.errors]; expect(allIssues.some(f => f.includes('io_model'))).toBe(true); }); it('gherkin_tests 少於 2 個時驗證失敗', () => { const contract = { ...VALID_CONTRACT, gherkin_tests: [ { scenario: 'only one', given: '{}', then_contains: '{}' }, ], }; const result = validateContract(contract); expect(result.valid).toBe(false); }); it('category 不在允許集合時驗證失敗', () => { const contract = { ...VALID_CONTRACT, category: 'invalid_category' }; const result = validateContract(contract); expect(result.valid).toBe(false); }); it('wasi_target 不是 preview1 時驗證失敗', () => { const contract = { ...VALID_CONTRACT, wasi_target: 'preview2' }; const result = validateContract(contract); expect(result.valid).toBe(false); }); it('version 格式不符時驗證失敗', () => { const contract = { ...VALID_CONTRACT, version: '1.0.0' }; const result = validateContract(contract); expect(result.valid).toBe(false); }); it('canonical_id 含大寫時驗證失敗', () => { const contract = { ...VALID_CONTRACT, canonical_id: 'ValidateJson' }; const result = validateContract(contract); expect(result.valid).toBe(false); }); it('空物件回傳所有必填欄位', () => { const result = validateContract({}); expect(result.valid).toBe(false); expect(result.missing_fields.length).toBeGreaterThan(0); }); it('null 輸入回傳驗證失敗', () => { const result = validateContract(null); expect(result.valid).toBe(false); }); });