['https://dl4k-a.test', 'https://dl4k-b.test'], ]); } public function test_fhd_uses_4k_domains_and_hentai_1080p_prefix(): void { Http::fake([ 'https://dl4k-a.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 12345]), 'https://dl4k-b.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 12345]), ]); $result = app(CdnPathValidator::class)->validateDownload('FHD', '2026/Title/E01.mkv'); $this->assertTrue($result['valid']); $this->assertSame(12345, $result['size']); $this->assertSame([ 'https://dl4k-a.test' => ['hentai-1080p/2026/Title/E01.mkv' => true], 'https://dl4k-b.test' => ['hentai-1080p/2026/Title/E01.mkv' => true], ], $result['mirrors']); Http::assertSent(fn ($request) => str_contains($request->url(), 'https://dl4k-a.test/check/')); } public function test_uhd_uses_download_domain_4k_and_hentai_prefix(): void { Http::fake([ 'https://dl4k-a.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 777]), 'https://dl4k-b.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 777]), ]); $result = app(CdnPathValidator::class)->validateDownload('UHD', '2026/Title/E01.mkv'); $this->assertTrue($result['valid']); $this->assertArrayHasKey('hentai/2026/Title/E01.mkv', $result['mirrors']['https://dl4k-a.test']); $this->assertArrayNotHasKey('hentai-1080p/2026/Title/E01.mkv', $result['mirrors']['https://dl4k-a.test']); } public function test_missing_on_one_mirror_is_invalid(): void { Http::fake([ 'https://dl4k-a.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 12345]), 'https://dl4k-b.test/*' => Http::response(['valid' => false, 'error' => 'File not found!'], 404), ]); $result = app(CdnPathValidator::class)->validateDownload('FHD', '2026/Title/E01.mkv'); $this->assertFalse($result['valid']); $this->assertNull($result['size']); $this->assertTrue($result['mirrors']['https://dl4k-a.test']['hentai-1080p/2026/Title/E01.mkv']); $this->assertFalse($result['mirrors']['https://dl4k-b.test']['hentai-1080p/2026/Title/E01.mkv']); } public function test_connection_error_is_invalid(): void { Http::fake(function ($request) { if (str_starts_with($request->url(), 'https://dl4k-a.test/')) { return Http::response(['valid' => true, 'type' => 'file', 'size' => 12345]); } throw new ConnectionException('Connection timed out', 0); }); $result = app(CdnPathValidator::class)->validateDownload('FHD', '2026/Title/E01.mkv'); $this->assertFalse($result['valid']); $this->assertNull($result['size']); $this->assertFalse($result['mirrors']['https://dl4k-b.test']['hentai-1080p/2026/Title/E01.mkv']); } public function test_stream_checks_base_directory_and_first_manifest(): void { Http::fake([ 'https://dl4k-a.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 1024]), 'https://dl4k-b.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 1024]), ]); $result = app(CdnPathValidator::class)->validateStream('2026/Title', 3); $this->assertTrue($result['valid']); $this->assertArrayHasKey('hentai-stream/2026/Title', $result['mirrors']['https://dl4k-a.test']); $this->assertArrayHasKey('hentai-stream/2026/Title/E01/720/manifest.mpd', $result['mirrors']['https://dl4k-a.test']); Http::assertSentCount(4); } public function test_stream_can_target_specific_episode_manifest(): void { Http::fake([ 'https://dl4k-a.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 1024]), 'https://dl4k-b.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 1024]), ]); $result = app(CdnPathValidator::class)->validateStream('2026/Title', 1, false, 2); $this->assertTrue($result['valid']); $this->assertArrayHasKey('hentai-stream/2026/Title', $result['mirrors']['https://dl4k-a.test']); $this->assertArrayHasKey('hentai-stream/2026/Title/E02/720/manifest.mpd', $result['mirrors']['https://dl4k-a.test']); $this->assertArrayHasKey('hentai-stream/2026/Title/E02/720/manifest.mpd', $result['mirrors']['https://dl4k-b.test']); $this->assertArrayNotHasKey('hentai-stream/2026/Title/E01/720/manifest.mpd', $result['mirrors']['https://dl4k-a.test']); $this->assertArrayNotHasKey('hentai-stream/2026/Title/E01/720/manifest.mpd', $result['mirrors']['https://dl4k-b.test']); $this->assertSame(4, count(Http::recorded())); } public function test_results_are_cached_for_short_period(): void { Http::fake([ 'https://dl4k-a.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 12345]), 'https://dl4k-b.test/*' => Http::response(['valid' => true, 'type' => 'file', 'size' => 12345]), ]); $validator = app(CdnPathValidator::class); $first = $validator->validateDownload('FHD', '2026/Title/E01.mkv'); $second = $validator->validateDownload('FHD', '2026/Title/E01.mkv'); $this->assertTrue($first['valid']); $this->assertTrue($second['valid']); Http::assertSentCount(2); } public function test_fresh_check_bypasses_cache(): void { $valid = true; Http::fake(function ($request) use (&$valid) { return Http::response( $valid ? ['valid' => true, 'type' => 'file', 'size' => 12345] : ['valid' => false, 'error' => 'File not found!'], $valid ? 200 : 404 ); }); $validator = app(CdnPathValidator::class); $cached = $validator->validateDownload('FHD', '2026/Title/E01.mkv'); $this->assertTrue($cached['valid']); $this->assertCount(2, Http::recorded()); // The CDN result changes within the cache window, but a non-fresh // check still serves the cached valid result. $valid = false; $stale = $validator->validateDownload('FHD', '2026/Title/E01.mkv'); $this->assertTrue($stale['valid']); $this->assertCount(2, Http::recorded()); // A fresh check bypasses the cache and overwrites the stored result. $fresh = $validator->validateDownload('FHD', '2026/Title/E01.mkv', true); $this->assertFalse($fresh['valid']); $this->assertCount(4, Http::recorded()); // The fresh (invalid) result is now what subsequent cached checks serve. $after = $validator->validateDownload('FHD', '2026/Title/E01.mkv'); $this->assertFalse($after['valid']); $this->assertCount(4, Http::recorded()); } }