toBeInstanceOf(WhatwgUrl::class); expect($url->getSpec())->toBe(UrlSpec::WHATWG); }); it('selects RFC3986 for non-http schemes', function () { $url = UrlFactory::parse('ftp://example.com/file'); expect($url)->toBeInstanceOf(Rfc3986Url::class); expect($url->getSpec())->toBe(UrlSpec::RFC3986); }); it('selects WHATWG for file:// URLs', function () { $url = UrlFactory::parse('file:///path/to/file'); expect($url)->toBeInstanceOf(WhatwgUrl::class); }); it('selects WHATWG for WebSocket URLs', function () { $url = UrlFactory::parse('ws://example.com/socket'); expect($url)->toBeInstanceOf(WhatwgUrl::class); }); }); describe('use case factory methods', function () { it('creates RFC3986 URL for API client', function () { $url = UrlFactory::forApiClient('https://api.example.com/users'); expect($url)->toBeInstanceOf(Rfc3986Url::class); expect($url->getHost())->toBe('api.example.com'); expect($url->getPath())->toBe('/users'); }); it('creates RFC3986 URL for cURL request', function () { $url = UrlFactory::forCurlRequest('https://example.com/endpoint'); expect($url)->toBeInstanceOf(Rfc3986Url::class); }); it('creates RFC3986 URL for signature generation', function () { $url = UrlFactory::forSignature('https://api.example.com/resource'); expect($url)->toBeInstanceOf(Rfc3986Url::class); }); it('creates RFC3986 URL for canonical URLs', function () { $url = UrlFactory::forCanonical('https://example.com/page'); expect($url)->toBeInstanceOf(Rfc3986Url::class); }); it('creates WHATWG URL for browser redirect', function () { $url = UrlFactory::forBrowserRedirect('https://example.com/redirect'); expect($url)->toBeInstanceOf(WhatwgUrl::class); }); it('creates WHATWG URL for deep link', function () { $url = UrlFactory::forDeepLink('myapp://open/profile'); expect($url)->toBeInstanceOf(WhatwgUrl::class); }); it('creates WHATWG URL for form action', function () { $url = UrlFactory::forFormAction('https://example.com/submit'); expect($url)->toBeInstanceOf(WhatwgUrl::class); }); it('creates WHATWG URL for client-side JavaScript', function () { $url = UrlFactory::forClientSide('https://example.com/api/data'); expect($url)->toBeInstanceOf(WhatwgUrl::class); }); }); describe('use case automatic selection', function () { it('creates RFC3986 for API_CLIENT use case', function () { $url = UrlFactory::forUseCase( UrlUseCase::API_CLIENT, 'https://api.example.com/users' ); expect($url)->toBeInstanceOf(Rfc3986Url::class); }); it('creates RFC3986 for CURL_REQUEST use case', function () { $url = UrlFactory::forUseCase( UrlUseCase::CURL_REQUEST, 'https://example.com/file' ); expect($url)->toBeInstanceOf(Rfc3986Url::class); }); it('creates RFC3986 for SIGNATURE_GENERATION use case', function () { $url = UrlFactory::forUseCase( UrlUseCase::SIGNATURE_GENERATION, 'https://api.example.com/sign' ); expect($url)->toBeInstanceOf(Rfc3986Url::class); }); it('creates RFC3986 for CANONICAL_URL use case', function () { $url = UrlFactory::forUseCase( UrlUseCase::CANONICAL_URL, 'https://example.com/page' ); expect($url)->toBeInstanceOf(Rfc3986Url::class); }); it('creates WHATWG for BROWSER_REDIRECT use case', function () { $url = UrlFactory::forUseCase( UrlUseCase::BROWSER_REDIRECT, 'https://example.com/redirect' ); expect($url)->toBeInstanceOf(WhatwgUrl::class); }); it('creates WHATWG for DEEP_LINK use case', function () { $url = UrlFactory::forUseCase( UrlUseCase::DEEP_LINK, 'myapp://open' ); expect($url)->toBeInstanceOf(WhatwgUrl::class); }); it('creates WHATWG for HTML_FORM_ACTION use case', function () { $url = UrlFactory::forUseCase( UrlUseCase::HTML_FORM_ACTION, 'https://example.com/submit' ); expect($url)->toBeInstanceOf(WhatwgUrl::class); }); it('creates WHATWG for CLIENT_SIDE_URL use case', function () { $url = UrlFactory::forUseCase( UrlUseCase::CLIENT_SIDE_URL, 'https://example.com/api' ); expect($url)->toBeInstanceOf(WhatwgUrl::class); }); }); describe('spec conversion', function () { it('converts RFC3986 to WHATWG', function () { $rfc = UrlFactory::forApiClient('https://example.com/path'); $whatwg = UrlFactory::convert($rfc, UrlSpec::WHATWG); expect($whatwg)->toBeInstanceOf(WhatwgUrl::class); expect($whatwg->getHost())->toBe('example.com'); expect($whatwg->getPath())->toBe('/path'); }); it('converts WHATWG to RFC3986', function () { $whatwg = UrlFactory::forBrowserRedirect('https://example.com/path'); $rfc = UrlFactory::convert($whatwg, UrlSpec::RFC3986); expect($rfc)->toBeInstanceOf(Rfc3986Url::class); expect($rfc->getHost())->toBe('example.com'); expect($rfc->getPath())->toBe('/path'); }); it('returns same instance if already correct spec', function () { $rfc = UrlFactory::forApiClient('https://example.com'); $converted = UrlFactory::convert($rfc, UrlSpec::RFC3986); expect($converted)->toBe($rfc); }); }); describe('integration scenarios', function () { it('creates API client URL with authentication', function () { $url = UrlFactory::forApiClient('https://api.example.com/users'); $withAuth = $url->withUserInfo('api_key', 'secret'); expect($withAuth->getUserInfo())->toBe('api_key:secret'); expect($withAuth->toString())->toContain('api_key:secret'); }); it('creates redirect URL with query parameters', function () { $url = UrlFactory::forBrowserRedirect('https://example.com/login'); $withParams = $url->withQuery('return_url=/dashboard&status=success'); expect($withParams->getQuery())->toContain('return_url'); }); it('creates canonical URL without query and fragment', function () { $url = UrlFactory::forCanonical('https://example.com/page?utm=123#section'); $canonical = $url->withQuery('')->withFragment(''); expect($canonical->toString())->toBe('https://example.com/page'); }); it('creates signature-safe URL', function () { $url = UrlFactory::forSignature('https://api.example.com/resource'); $withSig = $url->withQuery('timestamp=1234567890&signature=abc123'); $urlString = $withSig->toString(); expect($urlString)->toContain('timestamp=1234567890'); expect($urlString)->toContain('signature=abc123'); }); it('creates deep link with custom data', function () { $url = UrlFactory::forDeepLink('myapp://open/profile'); $withData = $url->withQuery('user_id=123&action=view'); expect($withData->getScheme())->toBe('myapp'); expect($withData->getQuery())->toContain('user_id=123'); }); }); describe('real-world scenarios', function () { it('handles OAuth URL construction', function () { $authUrl = UrlFactory::forApiClient('https://oauth.example.com/authorize'); $withParams = $authUrl->withQuery('client_id=123&redirect_uri=https://app.com&scope=read'); expect($withParams->getQuery())->toContain('client_id=123'); expect($withParams->getQuery())->toContain('redirect_uri'); }); it('handles REST API pagination', function () { $apiUrl = UrlFactory::forApiClient('https://api.example.com/users'); $page2 = $apiUrl->withQuery('page=2&per_page=50'); expect($page2->getQuery())->toBe('page=2&per_page=50'); }); it('handles mobile deep link with fallback', function () { $deepLink = UrlFactory::forDeepLink('myapp://open/article/123'); $fallback = UrlFactory::forBrowserRedirect('https://example.com/article/123'); expect($deepLink->getScheme())->toBe('myapp'); expect($fallback->getScheme())->toBe('https'); }); it('handles form submission with CSRF token', function () { $formAction = UrlFactory::forFormAction('https://example.com/submit'); $withToken = $formAction->withQuery('csrf_token=abc123'); expect($withToken->getQuery())->toContain('csrf_token'); }); }); });