From a6af9585bb28e36095e907252854eecd5de52048 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Fri, 30 Jan 2026 07:12:56 +0000 Subject: [PATCH] Add test files: fixtures, generators, spec parser, validation --- test/validation.spec.ts | 169 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 test/validation.spec.ts diff --git a/test/validation.spec.ts b/test/validation.spec.ts new file mode 100644 index 0000000..914178d --- /dev/null +++ b/test/validation.spec.ts @@ -0,0 +1,169 @@ +import { validateSpec } from '../src/validators/schema.js'; + +describe('Spec Validation', () => { + describe('valid specs', () => { + it('should validate a minimal spec', () => { + const spec = { + name: 'test', + version: '1.0.0', + description: 'A test CLI', + commands: [{ name: 'test', description: 'Test command' }], + }; + + const result = validateSpec(spec); + + expect(result.success).toBe(true); + expect((result as { success: true; data: unknown }).data).toBeDefined(); + }); + + it('should validate a full spec with all options', () => { + const spec = { + name: 'my-cli', + version: '1.0.0', + description: 'A full featured CLI', + author: 'John Doe', + license: 'MIT', + bin: 'my-cli', + globalOptions: [ + { + name: 'verbose', + short: 'v', + description: 'Verbose output', + type: 'boolean' as const, + default: false, + }, + ], + commands: [ + { + name: 'start', + description: 'Start the service', + aliases: ['run'], + options: [ + { + name: 'port', + description: 'Port number', + type: 'number' as const, + default: 8080, + }, + ], + arguments: [ + { + name: 'service', + description: 'Service name', + required: true, + }, + ], + }, + ], + examples: [ + { + description: 'Start service', + command: 'my-cli start api', + output: 'Started api', + }, + ], + }; + + const result = validateSpec(spec); + + expect(result.success).toBe(true); + }); + }); + + describe('invalid specs', () => { + it('should fail on empty name', () => { + const spec = { + name: '', + version: '1.0.0', + description: 'test', + commands: [{ name: 'test', description: 'test' }], + }; + + const result = validateSpec(spec); + + expect(result.success).toBe(false); + expect((result as { success: false; errors: string[] }).errors?.some((e) => e.includes('name'))).toBe(true); + }); + + it('should fail on invalid version format', () => { + const spec = { + name: 'test', + version: 'invalid', + description: 'test', + commands: [{ name: 'test', description: 'test' }], + }; + + const result = validateSpec(spec); + + expect(result.success).toBe(false); + expect((result as { success: false; errors: string[] }).errors?.some((e) => e.includes('version'))).toBe(true); + }); + + it('should fail on empty description', () => { + const spec = { + name: 'test', + version: '1.0.0', + description: '', + commands: [{ name: 'test', description: 'test' }], + }; + + const result = validateSpec(spec); + + expect(result.success).toBe(false); + }); + + it('should fail on invalid option type', () => { + const spec = { + name: 'test', + version: '1.0.0', + description: 'test', + commands: [{ name: 'test', description: 'test' }], + globalOptions: [ + { + name: 'opt', + description: 'An option', + type: 'invalid-type' as const, + }, + ], + }; + + const result = validateSpec(spec); + + expect(result.success).toBe(false); + expect((result as { success: false; errors: string[] }).errors?.some((e) => e.includes('type'))).toBe(true); + }); + }); + + describe('nested commands', () => { + it('should validate nested subcommands', () => { + const spec = { + name: 'test', + version: '1.0.0', + description: 'test', + commands: [ + { + name: 'parent', + description: 'Parent command', + subcommands: [ + { + name: 'child', + description: 'Child command', + options: [ + { + name: 'flag', + description: 'A flag', + type: 'boolean' as const, + }, + ], + }, + ], + }, + ], + }; + + const result = validateSpec(spec); + + expect(result.success).toBe(true); + }); + }); +});