diff --git a/app/AbstractUseStaleRequest.php b/app/AbstractUseStaleRequest.php index 82697c3..3d6b9d1 100644 --- a/app/AbstractUseStaleRequest.php +++ b/app/AbstractUseStaleRequest.php @@ -43,9 +43,10 @@ protected function responseFromCache(): ?Response protected function writeResponseToCache(): void { - if ($this->shouldWriteResponseToCache()) { - Cache::tags($this->getCacheTags())->put($this->refreshCacheKey(), 'refresh after', $this->refreshAfter()); + if (!$this->shouldWriteResponseToCache()) { + return; } + Cache::tags($this->getCacheTags())->put($this->refreshCacheKey(), 'refresh after', $this->refreshAfter()); parent::writeResponseToCache(); } diff --git a/tests/Feature/AbstractUseStaleRequestTest.php b/tests/Feature/AbstractUseStaleRequestTest.php index 71b25cf..5ab15d3 100644 --- a/tests/Feature/AbstractUseStaleRequestTest.php +++ b/tests/Feature/AbstractUseStaleRequestTest.php @@ -121,6 +121,63 @@ public function testRefreshOnNextRequest(): void self::assertTrue($request->needsRefresh(), 'But we want to refresh opportunistically'); } + public function testCacheHitDoesNotWriteRefreshMarker(): void + { + $request = new ConcreteUseStaleRequest('thing'); + + self::mockRequestCachedResponse($request, 'cached body'); + Cache::tags($request->getCacheTags())->put( + $request->refreshCacheKey(), + 'sentinel', + Carbon::now()->addMinutes(15), + ); + + self::assertSame('cached body', $request->sync()); + + self::assertSame( + 'sentinel', + Cache::tags($request->getCacheTags())->get($request->refreshCacheKey()), + 'Refresh marker should be untouched on a cache hit — writeResponseToCache must early-return.', + ); + } + + public function testFreshFetchWritesRefreshMarker(): void + { + $this->mockGuzzleWithTapper()->addMatchBody('GET', '/test/', 'fresh'); + $request = new ConcreteUseStaleRequest('thing'); + + self::assertNull( + Cache::tags($request->getCacheTags())->get($request->refreshCacheKey()), + 'Marker should not exist before first fetch.', + ); + + self::assertSame('fresh', $request->sync()); + + self::assertSame( + 'refresh after', + Cache::tags($request->getCacheTags())->get($request->refreshCacheKey()), + 'A successful live fetch must write the refresh marker.', + ); + } + + public function testWriteCacheDisabledSkipsRefreshMarker(): void + { + $this->mockGuzzleWithTapper()->addMatchBody('GET', '/test/', 'fresh'); + $request = new ConcreteUseStaleRequest('thing'); + $request->setWriteCache(false); + + self::assertSame('fresh', $request->sync()); + + self::assertNull( + Cache::tags($request->getCacheTags())->get($request->refreshCacheKey()), + 'setWriteCache(false) must prevent the refresh marker write.', + ); + self::assertFalse( + $request->canBeFulfilledByCache(), + 'setWriteCache(false) must also prevent the response from being cached.', + ); + } + public function testCacheBehaviorUnderHeavyLoad(): void { Queue::fake();