import { parseEvaluationArgs, type EvaluationSuite } from '../cli/argument-parser'; describe('argument-parser', () => { describe('suite options', () => { it('accepts all valid suite options', () => { const validSuites: EvaluationSuite[] = [ 'llm-judge', 'pairwise', 'programmatic', 'similarity', ]; for (const suite of validSuites) { const args = parseEvaluationArgs(['--suite', suite]); expect(args.suite).toBe(suite); } }); }); it('parses numeric flags like --max-examples and --concurrency', () => { const args = parseEvaluationArgs([ '--suite', 'pairwise', '--backend', 'langsmith', '--max-examples', '5', '--concurrency', '3', ]); expect(args.maxExamples).toBe(5); expect(args.concurrency).toBe(3); }); it('supports inline --max-examples= syntax', () => { const args = parseEvaluationArgs(['--max-examples=7']); expect(args.maxExamples).toBe(7); }); it('parses filters for pairwise suite', () => { const args = parseEvaluationArgs([ '--suite', 'pairwise', '--backend', 'langsmith', '--filter', 'do:Slack', '--filter', 'technique:content_generation', ]); expect(args.filters).toEqual({ doSearch: 'Slack', technique: 'content_generation', }); }); it('accepts prompt values that start with "-"', () => { const args = parseEvaluationArgs(['--prompt', '-starts-with-dash']); expect(args.prompt).toBe('-starts-with-dash'); }); it('rejects conflicting backend/local when --langsmith is set', () => { expect(() => parseEvaluationArgs(['--langsmith', '--backend', 'local'])).toThrow( 'Cannot combine `--langsmith` with `--backend local`', ); }); it('treats --langsmith as backend=langsmith', () => { const args = parseEvaluationArgs(['--langsmith']); expect(args.backend).toBe('langsmith'); }); it('rejects do/dont filters for non-pairwise suite', () => { expect(() => parseEvaluationArgs(['--suite', 'llm-judge', '--filter', 'do:Slack'])).toThrow( 'only supported for `--suite pairwise`', ); }); it('rejects malformed filters', () => { expect(() => parseEvaluationArgs(['--suite', 'pairwise', '--backend', 'langsmith', '--filter', 'nope']), ).toThrow('Invalid `--filter` format'); }); describe('--webhook-url', () => { it('parses valid HTTPS webhook URL', () => { const args = parseEvaluationArgs(['--webhook-url', 'https://example.com/webhook']); expect(args.webhookUrl).toBe('https://example.com/webhook'); }); it('parses webhook URL with inline = syntax', () => { const args = parseEvaluationArgs(['--webhook-url=https://api.example.com/hook']); expect(args.webhookUrl).toBe('https://api.example.com/hook'); }); it('rejects invalid URL format', () => { expect(() => parseEvaluationArgs(['--webhook-url', 'not-a-url'])).toThrow(); }); it('rejects non-URL strings', () => { expect(() => parseEvaluationArgs(['--webhook-url', 'just-some-text'])).toThrow(); }); it('allows webhook URL to be undefined when not provided', () => { const args = parseEvaluationArgs([]); expect(args.webhookUrl).toBeUndefined(); }); it('parses webhook URL with path and query params', () => { const args = parseEvaluationArgs([ '--webhook-url', 'https://hooks.example.com/api/v1/notify?token=abc123', ]); expect(args.webhookUrl).toBe('https://hooks.example.com/api/v1/notify?token=abc123'); }); }); it('accepts binary-checks suite', () => { const args = parseEvaluationArgs(['--suite', 'binary-checks']); expect(args.suite).toBe('binary-checks'); }); it('parses --checks flag as comma-separated list', () => { const args = parseEvaluationArgs([ '--suite', 'binary-checks', '--checks', 'has_nodes,has_trigger,all_nodes_connected', ]); expect(args.checks).toEqual(['has_nodes', 'has_trigger', 'all_nodes_connected']); }); it('trims whitespace in --checks values', () => { const args = parseEvaluationArgs([ '--suite', 'binary-checks', '--checks', ' has_nodes , has_trigger ', ]); expect(args.checks).toEqual(['has_nodes', 'has_trigger']); }); it('throws when --checks is used with non-binary-checks suite', () => { expect(() => parseEvaluationArgs(['--suite', 'llm-judge', '--checks', 'has_nodes'])).toThrow( '`--checks` is only supported for `--suite binary-checks`', ); }); describe('--webhook-secret', () => { it('parses valid webhook secret', () => { const args = parseEvaluationArgs(['--webhook-secret', 'my-secure-secret-key-1234567890']); expect(args.webhookSecret).toBe('my-secure-secret-key-1234567890'); }); it('parses webhook secret with inline = syntax', () => { const args = parseEvaluationArgs(['--webhook-secret=another-secret-key-12345678']); expect(args.webhookSecret).toBe('another-secret-key-12345678'); }); it('rejects secret shorter than 16 characters', () => { expect(() => parseEvaluationArgs(['--webhook-secret', 'short'])).toThrow(); }); it('allows webhook secret to be undefined when not provided', () => { const args = parseEvaluationArgs([]); expect(args.webhookSecret).toBeUndefined(); }); it('can be combined with webhook URL', () => { const args = parseEvaluationArgs([ '--webhook-url', 'https://example.com/webhook', '--webhook-secret', 'my-secure-secret-key-1234567890', ]); expect(args.webhookUrl).toBe('https://example.com/webhook'); expect(args.webhookSecret).toBe('my-secure-secret-key-1234567890'); }); }); });