fix: include rendered_at in 304 cache check so re-renders invalidate
CI / test (push) Has been cancelled
CI / test (push) Has been cancelled
After re-cropping an image, the renderer regenerates the .bin and advances the asset's rendered_at, but the device's 304 short-circuit still matched on (image_id, orientation) only — so the device kept serving the old upside-down/stale bytes from its local cache despite the server having freshly-rendered correct ones. Adds device.current_rendered_at, populated whenever a 200 response is served, and tightens the 304 condition to require all three (image id, orientation, rendered_at) to match. The asset lookup now happens before the 304 check so its rendered_at is in scope for the comparison. No firmware change — this is server-side cache logic. Existing devices get null current_rendered_at after the migration; their next poll falls through 304 and re-fetches once, then the cache is in sync. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,7 +66,8 @@ class DeviceImageControllerTest extends AppWebTestCase
|
||||
->setDeviceModel(DeviceModel::V1)
|
||||
->setOrientation(Orientation::Landscape)
|
||||
->setStatus(RenderStatus::Ready)
|
||||
->setFilePath(self::BIN_PATH);
|
||||
->setFilePath(self::BIN_PATH)
|
||||
->setRenderedAt(new \DateTimeImmutable());
|
||||
$this->em()->persist($asset);
|
||||
}
|
||||
|
||||
@@ -166,10 +167,13 @@ class DeviceImageControllerTest extends AppWebTestCase
|
||||
$image = $setup['image'];
|
||||
$imageId = $image->getId();
|
||||
|
||||
$device->setLockedImage($image);
|
||||
// Simulate the device having already received this image at the current
|
||||
// orientation — the 304 path now requires this to match too.
|
||||
// orientation and rendered_at — the 304 path now requires all three
|
||||
// (image id, orientation, rendered_at) to match.
|
||||
$asset = $this->em()->getRepository(RenderedAsset::class)->findOneBy(['image' => $image]);
|
||||
$device->setLockedImage($image);
|
||||
$device->setCurrentImageOrientation(Orientation::Landscape);
|
||||
$device->setCurrentRenderedAt($asset->getRenderedAt());
|
||||
$this->em()->flush();
|
||||
|
||||
$this->client->request('GET', '/api/device/' . self::MAC . '/image', [], [], [
|
||||
@@ -211,6 +215,34 @@ class DeviceImageControllerTest extends AppWebTestCase
|
||||
$this->assertResponseStatusCodeSame(200);
|
||||
}
|
||||
|
||||
public function test_re_render_returns_200_even_when_image_id_and_orientation_match(): void
|
||||
{
|
||||
$setup = $this->createTestSetup();
|
||||
$deviceId = $setup['device']->getId();
|
||||
$imageId = $setup['image']->getId();
|
||||
|
||||
// Seed: device receives the image once, server stores currentRenderedAt.
|
||||
$this->client->request('GET', '/api/device/' . self::MAC . '/image');
|
||||
$this->assertResponseStatusCodeSame(200);
|
||||
|
||||
// Simulate a re-render: the asset's rendered_at advances (e.g. user
|
||||
// re-cropped the image, RenderImageMessageHandler ran again).
|
||||
$this->em()->clear();
|
||||
$asset = $this->em()->getRepository(RenderedAsset::class)->findOneBy([
|
||||
'image' => $this->em()->find(Image::class, $imageId),
|
||||
'orientation' => Orientation::Landscape,
|
||||
]);
|
||||
$asset->setRenderedAt(new \DateTimeImmutable('+1 minute'));
|
||||
$this->em()->flush();
|
||||
|
||||
// Same image id, same orientation — but the bytes have changed, so
|
||||
// the 304 cache must invalidate and the device must re-fetch.
|
||||
$this->client->request('GET', '/api/device/' . self::MAC . '/image', [], [], [
|
||||
'HTTP_X-Current-Image-Id' => (string) $imageId,
|
||||
]);
|
||||
$this->assertResponseStatusCodeSame(200);
|
||||
}
|
||||
|
||||
public function test_poll_advances_current_image(): void
|
||||
{
|
||||
$setup = $this->createTestSetup();
|
||||
|
||||
Reference in New Issue
Block a user