This commit is contained in:
j3d1 2026-08-24 15:57:17 +02:00
parent 8d96bc97c4
commit ed04d98bf1
54 changed files with 661 additions and 1214 deletions

View file

@ -113,12 +113,7 @@ class FilesTestCase(FilesTestMixin, ToolshedTestCase):
self.assertEqual(countdir(DefaultStorage(), ''), 3)
def test_file_upload_reclaims_stale_orphan_at_canonical_path(self):
# Reproduces a real incident: a File row gets deleted without its underlying stored
# bytes being removed (e.g. a bug in some cleanup call site), leaving an orphan sitting
# at the exact path hash_upload() would compute for that content. A later upload of the
# same content must land back on that canonical path - not get silently suffixed by
# Django's default collision-avoidance, which would make it unreachable to every part
# of the app that derives a file's URL purely from its hash.
# Regression test for a stale orphan at the canonical hash path. See docs/implementation.md#stale-orphan-cleanup-at-the-canonical-hash-path.
expected_path = f"{self.f['hash4'][:2]}/{self.f['hash4'][2:4]}/{self.f['hash4'][4:6]}/{self.f['hash4'][6:]}"
default_storage.save(expected_path, ContentFile(self.f['test_content4']))
self.assertTrue(default_storage.exists(expected_path))
@ -231,11 +226,8 @@ class ThumbnailUrlTestCase(FilesTestMixin, UserTestMixin, InventoryTestMixin, To
self.prepare_properties()
self.prepare_inventory()
# Each test method gets its own distinct image content (and therefore its own content
# hash / thumbnail cache path) - InMemoryStorage isn't reset between test methods within
# a run, so sharing one fixed image across methods risks one test's cached (or, in
# test_thumbnail_served_from_cache_on_second_request's case, deliberately corrupted)
# thumbnail leaking into another test's assertions.
# Each test uses a distinct seeded image (own hash/cache path) since InMemoryStorage
# isn't reset between test methods, so a shared image risks one test's cached thumbnail leaking into another's assertions.
seed = zlib.crc32(self._testMethodName.encode()) % 256
buffer = io.BytesIO()
Image.new('RGB', (800, 600), (seed, 255 - seed, 128)).save(buffer, 'PNG')
@ -254,8 +246,7 @@ class ThumbnailUrlTestCase(FilesTestMixin, UserTestMixin, InventoryTestMixin, To
return os.path.join('thumbnails', str(size), h[:2], h[2:4], h[4:6], h[6:] + '.jpg')
def test_thumbnail_sizes_available(self):
# Documents the fixed size allow-list this test suite exercises against - update both
# if files/media_urls.py's THUMBNAIL_SIZES ever changes.
# Fixed size allow-list this suite exercises - update both if media_urls.py's THUMBNAIL_SIZES changes.
self.assertEqual(THUMBNAIL_SIZES, (32, 64, 256))
@override_settings(SERVE_X_ACCEL_REDIRECT=False)
@ -274,10 +265,7 @@ class ThumbnailUrlTestCase(FilesTestMixin, UserTestMixin, InventoryTestMixin, To
@override_settings(SERVE_X_ACCEL_REDIRECT=False)
def test_thumbnail_flattens_transparency_instead_of_going_black(self):
# Reproduces a real incident: an 'LA' (grayscale + alpha) source whose fully-transparent
# region has zeroed-out luminance underneath, as many image tools produce. Converting
# straight to RGB (dropping alpha without compositing) reveals that zeroed data - the
# whole thumbnail comes out solid black even though the visible (opaque) content isn't.
# Regression test for an 'LA' source with zeroed transparent-region luminance. See docs/implementation.md#rgba-flattening-avoids-revealing-black-under-transparent-pixels.
half_transparent = Image.new('LA', (200, 200))
pixels = half_transparent.load()
for x in range(200):
@ -310,8 +298,7 @@ class ThumbnailUrlTestCase(FilesTestMixin, UserTestMixin, InventoryTestMixin, To
with default_storage.open(rel_path, 'rb') as f:
cached_bytes = f.read()
# Overwrite the cached file with a marker so a correct implementation must serve this
# exact content back rather than regenerating it from the original.
# Overwrites the cache with a marker so a correct implementation must serve it back, not regenerate.
default_storage.delete(rel_path)
default_storage.save(rel_path, ContentFile(cached_bytes + b'MARKER'))
@ -333,8 +320,7 @@ class ThumbnailUrlTestCase(FilesTestMixin, UserTestMixin, InventoryTestMixin, To
self.assertEqual(reply.status_code, 403)
def test_thumbnail_not_friend(self):
# local_user1/local_user2 are friends in these fixtures (see prepare_inventory) - the
# denied case needs a stranger to that friendship instead.
# local_user1/local_user2 are friends here (see prepare_inventory), so the denied case needs a stranger instead.
reply = client.get(self._thumb_url(64), self.f['ext_user1'])
self.assertEqual(reply.status_code, 404)
self.assertFalse(default_storage.exists(self._thumb_rel_path(64)))