getMessage()}\n"; } } // RFC 3986 Tests echo "--- RFC 3986 Tests ---\n"; test('Parse basic URL', function () { $url = Rfc3986Url::parse('https://example.com/path'); assert($url->getScheme() === 'https'); assert($url->getHost() === 'example.com'); assert($url->getPath() === '/path'); }); test('Parse URL with all components', function () { $url = Rfc3986Url::parse('https://user:pass@example.com:8080/path?query=1#frag'); assert($url->getScheme() === 'https'); assert($url->getUserInfo() === 'user:pass'); assert($url->getHost() === 'example.com'); assert($url->getPort() === 8080); assert($url->getPath() === '/path'); assert($url->getQuery() === 'query=1'); assert($url->getFragment() === 'frag'); }); test('Immutable withScheme', function () { $original = Rfc3986Url::parse('https://example.com'); $modified = $original->withScheme('http'); assert($original->getScheme() === 'https'); assert($modified->getScheme() === 'http'); assert(spl_object_id($original) !== spl_object_id($modified)); }); test('Immutable withPath', function () { $url = Rfc3986Url::parse('https://example.com/old'); $modified = $url->withPath('/new'); assert($url->getPath() === '/old'); assert($modified->getPath() === '/new'); }); test('URL resolution', function () { $base = Rfc3986Url::parse('https://example.com/base/path'); $resolved = $base->resolve('../other'); assert($resolved->getHost() === 'example.com'); }); test('URL equality', function () { $url1 = Rfc3986Url::parse('https://example.com/path'); $url2 = Rfc3986Url::parse('https://example.com/path'); assert($url1->equals($url2) === true); }); test('URL equality ignores fragment by default', function () { $url1 = Rfc3986Url::parse('https://example.com/path#frag1'); $url2 = Rfc3986Url::parse('https://example.com/path#frag2'); assert($url1->equals($url2) === true); // Native PHP API always ignores fragments in equals() // For our wrapper, we delegate to native behavior }); test('URLs with different paths are not equal', function () { $url1 = Rfc3986Url::parse('https://example.com/path1'); $url2 = Rfc3986Url::parse('https://example.com/path2'); assert($url1->equals($url2) === false); }); // WHATWG Tests echo "\n--- WHATWG Tests ---\n"; test('Parse WHATWG URL', function () { $url = WhatwgUrl::parse('https://example.com/path'); assert($url->getScheme() === 'https'); assert($url->getHost() === 'example.com'); assert($url->getPath() === '/path'); }); test('WHATWG with all components', function () { $url = WhatwgUrl::parse('https://user:pass@example.com:8080/path?query=1#frag'); assert($url->getScheme() === 'https'); assert($url->getUserInfo() === 'user:pass'); assert($url->getHost() === 'example.com'); assert($url->getPort() === 8080); assert($url->getPath() === '/path'); assert($url->getQuery() === 'query=1'); assert($url->getFragment() === 'frag'); }); test('WHATWG immutable withers', function () { $original = WhatwgUrl::parse('https://example.com'); $modified = $original->withScheme('http'); assert($original->getScheme() === 'https'); assert($modified->getScheme() === 'http'); }); test('WHATWG URL resolution', function () { $base = WhatwgUrl::parse('https://example.com/base'); $resolved = $base->resolve('relative'); assert($resolved->getHost() === 'example.com'); }); // Factory Tests echo "\n--- Factory Tests ---\n"; test('Factory auto-selects WHATWG for http', function () { $url = UrlFactory::parse('https://example.com'); assert($url instanceof WhatwgUrl); }); test('Factory forApiClient creates RFC3986', function () { $url = UrlFactory::forApiClient('https://api.example.com'); assert($url instanceof Rfc3986Url); }); test('Factory forBrowserRedirect creates WHATWG', function () { $url = UrlFactory::forBrowserRedirect('https://example.com'); assert($url instanceof WhatwgUrl); }); // Integration Tests echo "\n--- Integration Tests ---\n"; test('API client with authentication', function () { $url = UrlFactory::forApiClient('https://api.example.com/users'); $withAuth = $url->withUserInfo('api_key', 'secret'); $urlString = $withAuth->toString(); assert(str_contains($urlString, 'api_key')); assert(str_contains($urlString, 'secret')); }); test('Browser redirect with query', function () { $url = UrlFactory::forBrowserRedirect('https://example.com/login'); $withQuery = $url->withQuery('return_url=/dashboard'); assert($withQuery->getQuery() === 'return_url=/dashboard'); }); test('URL signature generation', function () { $url = UrlFactory::forSignature('https://api.example.com/resource'); $withParams = $url->withQuery('timestamp=123&user_id=42'); $urlString = $withParams->toString(); assert(str_contains($urlString, 'timestamp=123')); }); // Summary echo "\n=== Summary ===\n"; echo "Tests: {$passed}/{$tests} passed\n"; if ($passed === $tests) { echo "✅ All tests passed!\n"; exit(0); } else { echo "❌ Some tests failed\n"; exit(1); }