parse(['arg1', 'arg2']); expect($parsed->getAllArguments())->toBeArray(); }); it('parses long options with equals sign', function () { $parser = ArgumentParser::create() ->optionalString('name') ->build(); $parsed = $parser->parse(['--name=John']); expect($parsed->get('name'))->toBe('John'); }); it('parses long options with space', function () { $parser = ArgumentParser::create() ->optionalString('name') ->build(); $parsed = $parser->parse(['--name', 'John']); expect($parsed->get('name'))->toBe('John'); }); it('parses boolean flags', function () { $parser = ArgumentParser::create() ->flag('verbose', 'v') ->build(); $parsed = $parser->parse(['--verbose']); expect($parsed->getBool('verbose'))->toBeTrue(); }); it('parses short options', function () { $parser = ArgumentParser::create() ->addArgument(new ArgumentDefinition('name', ArgumentType::STRING, shortName: 'n')) ->build(); $parsed = $parser->parse(['-n', 'John']); expect($parsed->get('name'))->toBe('John'); }); it('parses combined short flags', function () { $parser = ArgumentParser::create() ->flag('verbose', 'v') ->flag('force', 'f') ->build(); $parsed = $parser->parse(['-vf']); expect($parsed->getBool('verbose'))->toBeTrue(); expect($parsed->getBool('force'))->toBeTrue(); }); it('converts kebab-case to camelCase', function () { $parser = ArgumentParser::create() ->addArgument(new ArgumentDefinition('dryRun', ArgumentType::BOOLEAN)) ->optionalString('outputFile') ->build(); $parsed = $parser->parse(['--dry-run', '--output-file=test.txt']); expect($parsed->getBool('dryRun'))->toBeTrue(); expect($parsed->get('outputFile'))->toBe('test.txt'); }); it('validates required arguments', function () { $parser = ArgumentParser::create() ->requiredString('name') ->build(); expect(fn () => $parser->parse([])) ->toThrow(\InvalidArgumentException::class, "Required argument 'name' is missing"); }); it('applies default values for optional arguments', function () { $parser = ArgumentParser::create() ->optionalString('name', 'Guest') ->build(); $parsed = $parser->parse([]); expect($parsed->get('name'))->toBe('Guest'); }); it('parses integer values', function () { $parser = ArgumentParser::create() ->integer('count') ->build(); $parsed = $parser->parse(['--count=42']); expect($parsed->getInt('count'))->toBe(42); }); it('validates integer values', function () { $parser = ArgumentParser::create() ->integer('count') ->build(); expect(fn () => $parser->parse(['--count=not-a-number'])) ->toThrow(\InvalidArgumentException::class, 'not a valid integer'); }); it('parses float values', function () { $parser = ArgumentParser::create() ->addArgument(new ArgumentDefinition('price', ArgumentType::FLOAT)) ->build(); $parsed = $parser->parse(['--price=12.34']); expect($parsed->getFloat('price'))->toBe(12.34); }); it('parses boolean values', function () { $parser = ArgumentParser::create() ->addArgument(new ArgumentDefinition('active', ArgumentType::BOOLEAN)) ->build(); $parsed = $parser->parse(['--active=true']); expect($parsed->getBool('active'))->toBeTrue(); }); it('parses array values from comma-separated string', function () { $parser = ArgumentParser::create() ->addArgument(new ArgumentDefinition('items', ArgumentType::ARRAY)) ->build(); $parsed = $parser->parse(['--items=item1,item2,item3']); expect($parsed->getArray('items'))->toBe(['item1', 'item2', 'item3']); }); it('validates email addresses', function () { $parser = ArgumentParser::create() ->email('email') ->build(); $parsed = $parser->parse(['--email=user@example.com']); expect($parsed->get('email'))->toBe('user@example.com'); }); it('throws exception for invalid email', function () { $parser = ArgumentParser::create() ->email('email') ->build(); expect(fn () => $parser->parse(['--email=invalid-email'])) ->toThrow(\InvalidArgumentException::class, 'not a valid email address'); }); it('validates URL addresses', function () { $parser = ArgumentParser::create() ->addArgument(new ArgumentDefinition('url', ArgumentType::URL)) ->build(); $parsed = $parser->parse(['--url=https://example.com']); expect($parsed->get('url'))->toBe('https://example.com'); }); it('throws exception for invalid URL', function () { $parser = ArgumentParser::create() ->addArgument(new ArgumentDefinition('url', ArgumentType::URL)) ->build(); expect(fn () => $parser->parse(['--url=not-a-url'])) ->toThrow(\InvalidArgumentException::class, 'not a valid URL'); }); it('validates allowed values', function () { $parser = ArgumentParser::create() ->choice('mode', ['dev', 'prod', 'test']) ->build(); $parsed = $parser->parse(['--mode=dev']); expect($parsed->get('mode'))->toBe('dev'); }); it('throws exception for invalid choice value', function () { $parser = ArgumentParser::create() ->choice('mode', ['dev', 'prod', 'test']) ->build(); expect(fn () => $parser->parse(['--mode=invalid'])) ->toThrow(\InvalidArgumentException::class, "Invalid value 'invalid'"); }); it('handles positional arguments', function () { $parser = ArgumentParser::create() ->addArgument(new ArgumentDefinition('name', ArgumentType::STRING)) ->addArgument(new ArgumentDefinition('age', ArgumentType::INTEGER)) ->build(); $parsed = $parser->parse(['John', '25']); expect($parsed->get('name'))->toBe('John'); expect($parsed->getInt('age'))->toBe(25); }); it('throws exception when required option value is missing', function () { $parser = ArgumentParser::create() ->requiredString('name') ->build(); expect(fn () => $parser->parse(['--name'])) ->toThrow(\InvalidArgumentException::class, "Option '--name' requires a value"); }); it('handles optional boolean flags without value', function () { $parser = ArgumentParser::create() ->flag('verbose', 'v') ->build(); $parsed = $parser->parse(['--verbose']); expect($parsed->getBool('verbose'))->toBeTrue(); }); it('prevents combining short options that require values', function () { $parser = ArgumentParser::create() ->addArgument(new ArgumentDefinition('name', ArgumentType::STRING, shortName: 'n')) ->flag('verbose', 'v') ->build(); expect(fn () => $parser->parse(['-nv'])) ->toThrow(\InvalidArgumentException::class, "requires a value and cannot be combined"); }); it('returns all definitions', function () { $parser = ArgumentParser::create() ->optionalString('name') ->flag('verbose', 'v') ->build(); $definitions = $parser->getDefinitions(); expect($definitions)->toHaveKey('name'); expect($definitions)->toHaveKey('verbose'); }); }); describe('ArgumentParserBuilder', function () { it('creates parser with fluent interface', function () { $parser = ArgumentParser::create() ->requiredString('name') ->build(); expect($parser)->toBeInstanceOf(ArgumentParser::class); }); it('supports all argument definition factory methods', function () { $parser = ArgumentParser::create() ->requiredString('arg1') ->optionalString('opt1') ->flag('flag1', 'f') ->choice('choice1', ['a', 'b', 'c']) ->build(); expect($parser)->toBeInstanceOf(ArgumentParser::class); }); });