new ArgumentDefinition('name', ArgumentType::STRING), ]; $parsed = new ParsedArguments( ['name' => 'John'], [], $definitions ); expect($parsed->get('name'))->toBe('John'); }); it('gets option values', function () { $definitions = [ 'verbose' => new ArgumentDefinition('verbose', ArgumentType::BOOLEAN), ]; $parsed = new ParsedArguments( [], ['verbose' => true], $definitions ); expect($parsed->get('verbose'))->toBeTrue(); }); it('returns default value when argument is missing', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING, default: 'Guest'), ]; $parsed = new ParsedArguments([], [], $definitions); expect($parsed->get('name'))->toBe('Guest'); }); it('returns null when argument is missing and no default', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING), ]; $parsed = new ParsedArguments([], [], $definitions); expect($parsed->get('name'))->toBeNull(); }); it('requires argument and throws exception when missing', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING, required: true), ]; $parsed = new ParsedArguments([], [], $definitions); expect(fn () => $parsed->require('name')) ->toThrow(\InvalidArgumentException::class, "Required argument 'name' is missing"); }); it('requires argument and returns value when present', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING, required: true), ]; $parsed = new ParsedArguments(['name' => 'John'], [], $definitions); expect($parsed->require('name'))->toBe('John'); }); it('gets typed values', function () { $definitions = [ 'count' => new ArgumentDefinition('count', ArgumentType::INTEGER), 'active' => new ArgumentDefinition('active', ArgumentType::BOOLEAN), ]; $parsed = new ParsedArguments( ['count' => '42', 'active' => 'true'], [], $definitions ); expect($parsed->getTyped('count'))->toBe(42); expect($parsed->getTyped('active'))->toBeTrue(); }); it('gets string value', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING), ]; $parsed = new ParsedArguments(['name' => 'John'], [], $definitions); expect($parsed->getString('name'))->toBe('John'); expect($parsed->getString('name'))->toBeString(); }); it('gets integer value', function () { $definitions = [ 'count' => new ArgumentDefinition('count', ArgumentType::INTEGER), ]; $parsed = new ParsedArguments(['count' => '42'], [], $definitions); expect($parsed->getInt('count'))->toBe(42); expect($parsed->getInt('count'))->toBeInt(); }); it('throws exception for invalid integer', function () { $definitions = [ 'count' => new ArgumentDefinition('count', ArgumentType::INTEGER), ]; $parsed = new ParsedArguments(['count' => 'not-a-number'], [], $definitions); expect(fn () => $parsed->getInt('count')) ->toThrow(\InvalidArgumentException::class, 'not a valid integer'); }); it('gets float value', function () { $definitions = [ 'price' => new ArgumentDefinition('price', ArgumentType::FLOAT), ]; $parsed = new ParsedArguments(['price' => '12.34'], [], $definitions); expect($parsed->getFloat('price'))->toBe(12.34); expect($parsed->getFloat('price'))->toBeFloat(); }); it('throws exception for invalid float', function () { $definitions = [ 'price' => new ArgumentDefinition('price', ArgumentType::FLOAT), ]; $parsed = new ParsedArguments(['price' => 'not-a-number'], [], $definitions); expect(fn () => $parsed->getFloat('price')) ->toThrow(\InvalidArgumentException::class, 'not a valid number'); }); it('gets boolean value', function () { $definitions = [ 'active' => new ArgumentDefinition('active', ArgumentType::BOOLEAN), ]; $parsed = new ParsedArguments(['active' => 'true'], [], $definitions); expect($parsed->getBool('active'))->toBeTrue(); }); it('parses various boolean string values', function () { $definitions = [ 'flag' => new ArgumentDefinition('flag', ArgumentType::BOOLEAN), ]; $trueValues = ['true', '1', 'yes', 'on']; foreach ($trueValues as $value) { $parsed = new ParsedArguments(['flag' => $value], [], $definitions); expect($parsed->getBool('flag'))->toBeTrue(); } }); it('gets array value', function () { $definitions = [ 'items' => new ArgumentDefinition('items', ArgumentType::ARRAY), ]; $parsed = new ParsedArguments(['items' => 'item1,item2,item3'], [], $definitions); expect($parsed->getArray('items'))->toBe(['item1', 'item2', 'item3']); }); it('handles array value that is already an array', function () { $definitions = [ 'items' => new ArgumentDefinition('items', ArgumentType::ARRAY), ]; $parsed = new ParsedArguments(['items' => ['item1', 'item2']], [], $definitions); expect($parsed->getArray('items'))->toBe(['item1', 'item2']); }); it('gets email value object', function () { $definitions = [ 'email' => new ArgumentDefinition('email', ArgumentType::EMAIL), ]; $parsed = new ParsedArguments(['email' => 'user@example.com'], [], $definitions); $email = $parsed->getEmail('email'); expect($email)->toBeInstanceOf(\App\Framework\Core\ValueObjects\EmailAddress::class); expect($email->toString())->toBe('user@example.com'); }); it('throws exception for invalid email', function () { $definitions = [ 'email' => new ArgumentDefinition('email', ArgumentType::EMAIL), ]; $parsed = new ParsedArguments(['email' => 'invalid-email'], [], $definitions); expect(fn () => $parsed->getEmail('email')) ->toThrow(\InvalidArgumentException::class, 'not a valid email address'); }); it('gets URL value object', function () { $definitions = [ 'url' => new ArgumentDefinition('url', ArgumentType::URL), ]; $parsed = new ParsedArguments(['url' => 'https://example.com'], [], $definitions); $url = $parsed->getUrl('url'); expect($url)->toBeInstanceOf(\App\Framework\Http\Url\Url::class); }); it('throws exception for invalid URL', function () { $definitions = [ 'url' => new ArgumentDefinition('url', ArgumentType::URL), ]; $parsed = new ParsedArguments(['url' => 'not-a-url'], [], $definitions); expect(fn () => $parsed->getUrl('url')) ->toThrow(\InvalidArgumentException::class, 'not a valid URL'); }); it('checks if argument exists', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING), ]; $parsed = new ParsedArguments(['name' => 'John'], [], $definitions); expect($parsed->has('name'))->toBeTrue(); expect($parsed->has('missing'))->toBeFalse(); }); it('checks if argument has value', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING), 'empty' => new ArgumentDefinition('empty', ArgumentType::STRING), ]; $parsed = new ParsedArguments( ['name' => 'John', 'empty' => ''], [], $definitions ); expect($parsed->hasValue('name'))->toBeTrue(); expect($parsed->hasValue('empty'))->toBeFalse(); expect($parsed->hasValue('missing'))->toBeFalse(); }); it('validates required arguments', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING, required: true), 'optional' => new ArgumentDefinition('optional', ArgumentType::STRING), ]; $parsed = new ParsedArguments(['name' => 'John'], [], $definitions); // Should not throw $parsed->validate(); expect(true)->toBeTrue(); }); it('throws exception when required argument is missing', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING, required: true), ]; $parsed = new ParsedArguments([], [], $definitions); expect(fn () => $parsed->validate()) ->toThrow(\InvalidArgumentException::class, "Required argument 'name' is missing"); }); it('validates allowed values', function () { $definitions = [ 'mode' => new ArgumentDefinition('mode', ArgumentType::STRING, allowedValues: ['dev', 'prod', 'test']), ]; $parsed = new ParsedArguments(['mode' => 'dev'], [], $definitions); // Should not throw $parsed->validate(); expect(true)->toBeTrue(); }); it('throws exception for invalid allowed value', function () { $definitions = [ 'mode' => new ArgumentDefinition('mode', ArgumentType::STRING, allowedValues: ['dev', 'prod', 'test']), ]; $parsed = new ParsedArguments(['mode' => 'invalid'], [], $definitions); expect(fn () => $parsed->validate()) ->toThrow(\InvalidArgumentException::class, "Invalid value 'invalid'"); }); it('returns all arguments', function () { $parsed = new ParsedArguments( ['arg1' => 'value1', 'arg2' => 'value2'], [], [] ); $all = $parsed->getAllArguments(); expect($all)->toHaveKey('arg1'); expect($all)->toHaveKey('arg2'); expect($all['arg1'])->toBe('value1'); }); it('returns all options', function () { $parsed = new ParsedArguments( [], ['opt1' => 'value1', 'opt2' => 'value2'], [] ); $all = $parsed->getAllOptions(); expect($all)->toHaveKey('opt1'); expect($all)->toHaveKey('opt2'); expect($all['opt1'])->toBe('value1'); }); it('returns all definitions', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING), 'count' => new ArgumentDefinition('count', ArgumentType::INTEGER), ]; $parsed = new ParsedArguments([], [], $definitions); $all = $parsed->getDefinitions(); expect($all)->toHaveKey('name'); expect($all)->toHaveKey('count'); }); it('handles empty string as missing value', function () { $definitions = [ 'name' => new ArgumentDefinition('name', ArgumentType::STRING, required: true), ]; $parsed = new ParsedArguments(['name' => ''], [], $definitions); expect(fn () => $parsed->validate()) ->toThrow(\InvalidArgumentException::class, "Required argument 'name' is missing"); }); it('handles boolean false correctly', function () { $definitions = [ 'active' => new ArgumentDefinition('active', ArgumentType::BOOLEAN), ]; $parsed = new ParsedArguments(['active' => false], [], $definitions); expect($parsed->getBool('active'))->toBeFalse(); expect($parsed->hasValue('active'))->toBeTrue(); }); it('handles empty array correctly', function () { $definitions = [ 'items' => new ArgumentDefinition('items', ArgumentType::ARRAY), ]; $parsed = new ParsedArguments(['items' => []], [], $definitions); expect($parsed->getArray('items'))->toBe([]); // Empty array is considered a value (it's not null or empty string) expect($parsed->hasValue('items'))->toBeTrue(); }); });