dir = sys_get_temp_dir() . '/weerscherm-radar-' . uniqid(); $this->file = $this->dir . '/radar.gif'; } protected function tearDown(): void { array_map('unlink', glob($this->dir . '/*') ?: []); @rmdir($this->dir); } private function service(FakeHttp $http, int $ttl = 240): RadarService { return new RadarService( new BuienradarClient($http), $this->file, $ttl, static fn(): int => self::NOW, ); } /** Legt een cachebestand neer met een gegeven leeftijd in seconden. */ private function seedCache(string $bytes, int $ageSeconds): void { mkdir($this->dir, 0700, true); file_put_contents($this->file, $bytes); touch($this->file, self::NOW - $ageSeconds); } public function testFetchesAndCachesWhenNothingIsStored(): void { $http = (new FakeHttp())->queue(200, self::GIF); $result = $this->service($http)->image(); self::assertSame(['bytes' => self::GIF, 'stale' => false, 'age' => 0], $result); self::assertStringEqualsFile($this->file, self::GIF); self::assertSame([], glob($this->dir . '/*.tmp.*') ?: [], 'geen tijdelijke bestanden achtergelaten'); } public function testFreshCacheIsServedWithoutFetching(): void { $this->seedCache(self::OLD_GIF, 100); $http = new FakeHttp(); // geen response gepland: een fetch zou de test laten falen $result = $this->service($http)->image(); self::assertSame(self::OLD_GIF, $result['bytes']); self::assertFalse($result['stale']); self::assertSame(100, $result['age']); self::assertSame([], $http->requests); } public function testExpiredCacheIsRefetched(): void { $this->seedCache(self::OLD_GIF, 300); $http = (new FakeHttp())->queue(200, self::GIF); $result = $this->service($http)->image(); self::assertSame(self::GIF, $result['bytes']); self::assertFalse($result['stale']); self::assertCount(1, $http->requests); self::assertStringEqualsFile($this->file, self::GIF); } public function testUpstreamFailureFallsBackToTheCachedImageMarkedStale(): void { $this->seedCache(self::OLD_GIF, 3600); $http = (new FakeHttp())->queue(500, ''); $result = $this->service($http)->image(); self::assertSame(self::OLD_GIF, $result['bytes'], 'ongeacht de leeftijd: liever een oud beeld dan niets'); self::assertTrue($result['stale']); self::assertSame(3600, $result['age']); } public function testUpstreamFailureWithoutCacheThrows(): void { $http = (new FakeHttp())->queue(500, ''); $this->expectException(UpstreamException::class); $this->service($http)->image(); } public function testEmptyCacheFileIsIgnored(): void { $this->seedCache('', 10); $http = (new FakeHttp())->queue(200, self::GIF); $result = $this->service($http)->image(); self::assertSame(self::GIF, $result['bytes']); self::assertCount(1, $http->requests, 'een leeg cachebestand telt niet als geldige cache'); } }