fix(storage): #106.5 — serve_blob probes S3 sub-backend, not dual disk-OR-S3 exists
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
באג-אינטראקציה שהתגלה לפני ה-flip ל-dual: DualBackend.exists() מחזיר True אם הקובץ על **הדיסק או** ב-S3. serve_blob בדק backend.exists() ואז הנפיק presigned — כך שתחת dual, קובץ שקיים-רק-בדיסק (mirror שנכשל / מחוץ לסט-ההגירה) היה מקבל redirect ל-presigned-URL שמחזיר 404 מ-MinIO, במקום fallback-לדיסק. תיקון: serve_blob בודק קיום ב-**S3 ספציפית** — `s3 = getattr(backend, "s3", backend)` (DualBackend.s3, או ה-S3Backend עצמו תחת s3) — כך שקובץ disk-only נופל ל-FileResponse אמיתי. תואם-לאחור ל-filesystem/s3 (getattr מחזיר את ה-backend עצמו). invariants: INV-STG6 (presigned רק כשהאובייקט באמת ב-S3) · INV-G10 (אפס שינוי תחת filesystem). tests: 6 (2 חדשות — dual מ-S3-sub-backend present→redirect / absent→disk-fallback). py_compile OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -78,3 +78,34 @@ def test_s3_missing_no_disk_404(monkeypatch):
|
||||
with pytest.raises(HTTPException) as ei:
|
||||
_serve(monkeypatch, "s3", False, DATA_DIR / "audit" / "_nope.txt")
|
||||
assert ei.value.status_code == 404
|
||||
|
||||
|
||||
class _FakeDual:
|
||||
"""DualBackend stand-in: generic exists() is disk-OR-S3 (always True here),
|
||||
but the .s3 sub-backend is S3-only. The fix must probe .s3, not exists()."""
|
||||
name = "dual"
|
||||
|
||||
def __init__(self, s3_has: bool) -> None:
|
||||
self.s3 = _FakeBackend("s3", s3_has)
|
||||
|
||||
async def exists(self, key, *, bucket) -> bool: # noqa: ANN001 — disk-or-S3 → True
|
||||
return True
|
||||
|
||||
async def presign_get(self, key, *, bucket, download_name=None) -> str: # noqa: ANN001
|
||||
return f"https://s3.example/WRONG/{key}" # must NOT be used (proves .s3 probed)
|
||||
|
||||
|
||||
def test_dual_probes_s3_subbackend_not_generic_exists(blob, monkeypatch):
|
||||
"""Regression (DualBackend.exists is disk-OR-S3): a file on disk but NOT in
|
||||
S3 must fall back to a disk FileResponse, never a presigned URL that 404s."""
|
||||
monkeypatch.setattr(storage, "get_storage", lambda: _FakeDual(s3_has=False))
|
||||
r = asyncio.new_event_loop().run_until_complete(
|
||||
app.serve_blob(str(blob), media_type="text/plain", filename="x.txt"))
|
||||
assert isinstance(r, FileResponse) # disk fallback, NOT a (broken) redirect
|
||||
|
||||
|
||||
def test_dual_in_s3_uses_s3_subbackend(blob, monkeypatch):
|
||||
monkeypatch.setattr(storage, "get_storage", lambda: _FakeDual(s3_has=True))
|
||||
r = asyncio.new_event_loop().run_until_complete(
|
||||
app.serve_blob(str(blob), media_type="text/plain", filename="x.txt"))
|
||||
assert isinstance(r, RedirectResponse)
|
||||
|
||||
Reference in New Issue
Block a user