fix(storage): thumb_derived_import claims JPEG sidecars too

The filter was strip_suffix(".webp"), but persist_rendered writes
{hash}.{format.ext()} — so any client not advertising WebP leaves
{hash}.jpg on disk. Correct only while the derived tier was WebP-only;
once variant carried the format (20261022000000) a JPEG sidecar became
ordinary content, and leaving it unclaimed would keep .thumbnails/
permanently non-empty — the very signal step 10e gates on. The migration
could never finish.

Both codecs are now claimed and the format comes from the file's own
extension, so a .jpg imports AS JPEG. Deriving the variant and
content_type from it rather than hardcoding WebP is the point: a
mislabelled row would serve the wrong codec to whoever the read path
then matched it for.

ThumbnailFormat::ALL exists so the claim list and the write path cannot
drift — adding a format without teaching the import about it would
strand that codec silently.

The `ext-` rejection now carries real weight. Previously .jpg was
rejected wholesale, so the two jobs could not overlap by construction;
now they share an extension and only the prefix separates them. Both
directions stay under test.

Caught by the cross-job assertion, which counts every real sidecar being
claimed exactly once — the fixture gained a .jpg and the total moved 3
to 4, which is the test noticing rather than a test to update.
This commit is contained in:
Edouard Vanbelle
2026-08-27 19:06:09 +02:00
parent f4e4b518a9
commit 12158ccf59
3 changed files with 78 additions and 31 deletions
+6
View File
@@ -77,6 +77,12 @@ pub enum ThumbnailFormat {
}
impl ThumbnailFormat {
/// Every format, for callers that must handle all of them — notably
/// `thumb_derived_import`, which claims one sidecar extension per format
/// and would silently strand a codec if this list and the write path
/// drifted apart.
pub const ALL: [ThumbnailFormat; 2] = [ThumbnailFormat::Webp, ThumbnailFormat::Jpeg];
/// Stable name, byte-identical to the derived `Debug` output (see
/// [`ThumbnailSize::as_str`] — same ETag-stability contract).
pub fn as_str(self) -> &'static str {