diff --git a/backend/app/gateway/routers/uploads.py b/backend/app/gateway/routers/uploads.py index c43c820..d808a34 100644 --- a/backend/app/gateway/routers/uploads.py +++ b/backend/app/gateway/routers/uploads.py @@ -187,7 +187,10 @@ async def delete_uploaded_file(thread_id: str, filename: str) -> dict: raise HTTPException(status_code=403, detail="Access denied") try: - file_path.unlink() + if file_path.suffix.lower() in CONVERTIBLE_EXTENSIONS: + companion_markdown = file_path.with_suffix(".md") + companion_markdown.unlink(missing_ok=True) + file_path.unlink(missing_ok=True) logger.info(f"Deleted file: {filename}") return {"success": True, "message": f"Deleted {filename}"} except Exception as e: diff --git a/backend/tests/test_uploads_router.py b/backend/tests/test_uploads_router.py index 1e43ce5..649d0d3 100644 --- a/backend/tests/test_uploads_router.py +++ b/backend/tests/test_uploads_router.py @@ -96,3 +96,17 @@ def test_upload_files_rejects_dotdot_and_dot_filenames(tmp_path): # Only the safely normalised file should exist assert [f.name for f in thread_uploads_dir.iterdir()] == ["passwd"] + + +def test_delete_uploaded_file_removes_generated_markdown_companion(tmp_path): + thread_uploads_dir = tmp_path / "uploads" + thread_uploads_dir.mkdir(parents=True) + (thread_uploads_dir / "report.pdf").write_bytes(b"pdf-bytes") + (thread_uploads_dir / "report.md").write_text("converted", encoding="utf-8") + + with patch.object(uploads, "get_uploads_dir", return_value=thread_uploads_dir): + result = asyncio.run(uploads.delete_uploaded_file("thread-aio", "report.pdf")) + + assert result == {"success": True, "message": "Deleted report.pdf"} + assert not (thread_uploads_dir / "report.pdf").exists() + assert not (thread_uploads_dir / "report.md").exists()