Files
michaelschiemer/tests/Unit/Framework/Http/Url/Rfc3986UrlTest.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

238 lines
8.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Http\Url\Rfc3986Url;
use App\Framework\Http\Url\UrlSpec;
describe('Rfc3986Url', function () {
describe('parsing', function () {
it('parses basic HTTP URLs', function () {
$url = Rfc3986Url::parse('https://example.com/path');
expect($url->getScheme())->toBe('https');
expect($url->getHost())->toBe('example.com');
expect($url->getPath())->toBe('/path');
});
it('parses URLs with all components', function () {
$url = Rfc3986Url::parse('https://user:pass@example.com:8080/path?query=1#fragment');
expect($url->getScheme())->toBe('https');
expect($url->getUserInfo())->toBe('user:pass');
expect($url->getHost())->toBe('example.com');
expect($url->getPort())->toBe(8080);
expect($url->getPath())->toBe('/path');
expect($url->getQuery())->toBe('query=1');
expect($url->getFragment())->toBe('fragment');
});
it('handles default ports correctly', function () {
$url = Rfc3986Url::parse('https://example.com/path');
expect($url->getPort())->toBeNull();
});
it('parses relative URLs with base', function () {
$base = Rfc3986Url::parse('https://example.com/base/path');
$url = Rfc3986Url::parse('../other', $base);
expect($url->getPath())->toContain('other');
});
});
describe('spec compliance', function () {
it('reports RFC3986 spec', function () {
$url = Rfc3986Url::parse('https://example.com');
expect($url->getSpec())->toBe(UrlSpec::RFC3986);
expect($url->getSpec()->isRfc3986())->toBeTrue();
expect($url->getSpec()->isWhatwg())->toBeFalse();
});
});
describe('immutable withers', function () {
it('returns new instance with different scheme', function () {
$original = Rfc3986Url::parse('https://example.com');
$modified = $original->withScheme('http');
expect($original->getScheme())->toBe('https');
expect($modified->getScheme())->toBe('http');
expect(spl_object_id($original))->notToBe(spl_object_id($modified));
});
it('returns new instance with different host', function () {
$original = Rfc3986Url::parse('https://example.com');
$modified = $original->withHost('api.example.com');
expect($original->getHost())->toBe('example.com');
expect($modified->getHost())->toBe('api.example.com');
});
it('returns new instance with different port', function () {
$original = Rfc3986Url::parse('https://example.com');
$modified = $original->withPort(8080);
expect($original->getPort())->toBeNull();
expect($modified->getPort())->toBe(8080);
});
it('returns new instance with different path', function () {
$original = Rfc3986Url::parse('https://example.com/old');
$modified = $original->withPath('/new');
expect($original->getPath())->toBe('/old');
expect($modified->getPath())->toBe('/new');
});
it('returns new instance with different query', function () {
$original = Rfc3986Url::parse('https://example.com?old=1');
$modified = $original->withQuery('new=2');
expect($original->getQuery())->toBe('old=1');
expect($modified->getQuery())->toBe('new=2');
});
it('returns new instance with different fragment', function () {
$original = Rfc3986Url::parse('https://example.com#old');
$modified = $original->withFragment('new');
expect($original->getFragment())->toBe('old');
expect($modified->getFragment())->toBe('new');
});
it('returns new instance with user info', function () {
$original = Rfc3986Url::parse('https://example.com');
$modified = $original->withUserInfo('user', 'pass');
expect($original->getUserInfo())->toBe('');
expect($modified->getUserInfo())->toBe('user:pass');
});
it('handles user info without password', function () {
$url = Rfc3986Url::parse('https://example.com');
$modified = $url->withUserInfo('user');
expect($modified->getUserInfo())->toBe('user');
});
});
describe('serialization', function () {
it('converts to string', function () {
$url = Rfc3986Url::parse('https://example.com/path?query=1#fragment');
expect($url->toString())->toBe('https://example.com/path?query=1#fragment');
expect((string) $url)->toBe('https://example.com/path?query=1#fragment');
});
it('converts to ASCII string for IDNA domains', function () {
$url = Rfc3986Url::parse('https://例え.jp/path');
$ascii = $url->toAsciiString();
expect($ascii)->toContain('xn--');
});
});
describe('URL resolution', function () {
it('resolves relative URLs', function () {
$base = Rfc3986Url::parse('https://example.com/base/path');
$resolved = $base->resolve('relative');
expect($resolved->getHost())->toBe('example.com');
expect($resolved->getPath())->toContain('relative');
});
it('resolves absolute URLs', function () {
$base = Rfc3986Url::parse('https://example.com/base');
$resolved = $base->resolve('https://other.com/path');
expect($resolved->getHost())->toBe('other.com');
expect($resolved->getPath())->toBe('/path');
});
});
describe('URL comparison', function () {
it('compares URLs for equality', function () {
$url1 = Rfc3986Url::parse('https://example.com/path');
$url2 = Rfc3986Url::parse('https://example.com/path');
expect($url1->equals($url2))->toBeTrue();
});
it('compares URLs ignoring fragments by default', function () {
$url1 = Rfc3986Url::parse('https://example.com/path#frag1');
$url2 = Rfc3986Url::parse('https://example.com/path#frag2');
expect($url1->equals($url2))->toBeTrue();
expect($url1->equals($url2, includeFragment: true))->toBeFalse();
});
it('detects different URLs', function () {
$url1 = Rfc3986Url::parse('https://example.com/path1');
$url2 = Rfc3986Url::parse('https://example.com/path2');
expect($url1->equals($url2))->toBeFalse();
});
});
describe('edge cases', function () {
it('handles empty path', function () {
$url = Rfc3986Url::parse('https://example.com');
expect($url->getPath())->toBe('');
});
it('handles empty query', function () {
$url = Rfc3986Url::parse('https://example.com/path');
expect($url->getQuery())->toBe('');
});
it('handles empty fragment', function () {
$url = Rfc3986Url::parse('https://example.com/path');
expect($url->getFragment())->toBe('');
});
it('removes port with null', function () {
$url = Rfc3986Url::parse('https://example.com:8080/path');
$modified = $url->withPort(null);
expect($modified->getPort())->toBeNull();
});
it('removes query with empty string', function () {
$url = Rfc3986Url::parse('https://example.com?query=1');
$modified = $url->withQuery('');
expect($modified->getQuery())->toBe('');
});
it('removes fragment with empty string', function () {
$url = Rfc3986Url::parse('https://example.com#fragment');
$modified = $url->withFragment('');
expect($modified->getFragment())->toBe('');
});
});
describe('API client use cases', function () {
it('preserves exact URL structure for API calls', function () {
$url = Rfc3986Url::parse('https://api.example.com/v1/users?filter=active&sort=name');
expect($url->getQuery())->toBe('filter=active&sort=name');
expect($url->toString())->toContain('filter=active&sort=name');
});
it('supports URL signature generation', function () {
$url = Rfc3986Url::parse('https://api.example.com/resource');
$withAuth = $url->withQuery('signature=abc123&timestamp=1234567890');
$canonical = $withAuth->toString();
expect($canonical)->toContain('signature=abc123');
expect($canonical)->toContain('timestamp=1234567890');
});
});
});