Delete Document & Delete Source buttons now delete the underlying files

This commit is contained in:
Jim Lancaster
2026-08-04 18:34:35 -05:00
parent 271633d1d5
commit d9f5fbb1a4
7 changed files with 141 additions and 5 deletions
+36 -1
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
from datetime import UTC
from datetime import datetime
from pathlib import Path
from uuid import uuid4
import pytest
@@ -87,8 +88,9 @@ async def test_delete_document_blocks_when_dependencies_exist(default_session_fa
@pytest.mark.asyncio
async def test_delete_document_succeeds_when_unlinked(default_session_factory):
async def test_delete_document_succeeds_when_unlinked(default_session_factory, tmp_path):
service = DocumentService(session_factory=default_session_factory)
service.settings.upload_dir = tmp_path
document = await service.create_document(
Document(
@@ -98,12 +100,45 @@ async def test_delete_document_succeeds_when_unlinked(default_session_factory):
)
)
document_dir = service.settings.upload_dir / "documents" / str(document.id)
document_dir.mkdir(parents=True, exist_ok=True)
(document_dir / "leftover.txt").write_text("orphan", encoding="utf-8")
await service.delete_document(document)
assert not document_dir.exists()
with pytest.raises(DocumentError):
await service.read_document_detail(document.id)
@pytest.mark.asyncio
async def test_delete_document_removes_populated_storage_tree(default_session_factory, tmp_path):
service = DocumentService(session_factory=default_session_factory)
service.settings.upload_dir = tmp_path
document = await service.create_document(
Document(
id=uuid4(),
name="tree-delete",
document_type="memo",
)
)
document_dir = service.settings.upload_dir / "documents" / str(document.id)
(document_dir / "page-1.jpg").parent.mkdir(parents=True, exist_ok=True)
(document_dir / "page-1.jpg").write_bytes(b"one")
(document_dir / "page-2.jpg").write_bytes(b"two")
(document_dir / "nested" / "manifest.json").parent.mkdir(parents=True, exist_ok=True)
(document_dir / "nested" / "manifest.json").write_text('{"ok": true}', encoding="utf-8")
assert document_dir.exists()
await service.delete_document(document)
assert not document_dir.exists()
@pytest.mark.asyncio
async def test_read_person_detail_loads_document_links(default_session_factory):
service = DocumentService(session_factory=default_session_factory)
+16 -4
View File
@@ -93,10 +93,11 @@ class TestTranscriptionServiceRevisionUpsert:
assert revisions[0].revised_text == "Revision v2"
@pytest.mark.asyncio
async def test_delete_source_from_job_context_removes_source_and_single_link(self, default_session_factory):
async def test_delete_source_from_job_context_removes_source_and_single_link(self, default_session_factory, tmp_path):
documents = DocumentService(session_factory=default_session_factory)
jobs = JobService(session_factory=default_session_factory)
transcriptions = TranscriptionService(session_factory=default_session_factory)
transcriptions.settings.upload_dir = tmp_path
document = Document(id=uuid4(), name="delete-source-success")
await documents.create_document(document=document)
@@ -104,12 +105,16 @@ class TestTranscriptionServiceRevisionUpsert:
job = Job(document_id=document.id, status=JobStatus.QUEUED)
await jobs.create_job(job=job)
stored_path = tmp_path / "documents" / str(document.id) / "delete.jpg"
stored_path.parent.mkdir(parents=True, exist_ok=True)
stored_path.write_bytes(b"data")
source = Source(
document_id=document.id,
page_number=1,
upload_name="delete.jpg",
filename="delete.jpg",
file_path="uploads/delete.jpg",
file_path=str(stored_path),
)
async with transcriptions._session_scope() as session:
session.add(source)
@@ -122,6 +127,7 @@ class TestTranscriptionServiceRevisionUpsert:
with pytest.raises(TranscriptionNotFoundError):
await transcriptions.read_source(source.id)
assert not stored_path.exists()
@pytest.mark.asyncio
async def test_delete_source_from_job_context_blocks_when_other_job_links_exist(self, default_session_factory):
@@ -156,19 +162,24 @@ class TestTranscriptionServiceRevisionUpsert:
await transcriptions.delete_source_from_job_context(job_id=job_one.id, source_id=source.id)
@pytest.mark.asyncio
async def test_delete_unlinked_source_succeeds(self, default_session_factory):
async def test_delete_unlinked_source_succeeds(self, default_session_factory, tmp_path):
documents = DocumentService(session_factory=default_session_factory)
transcriptions = TranscriptionService(session_factory=default_session_factory)
transcriptions.settings.upload_dir = tmp_path
document = Document(id=uuid4(), name="delete-unlinked-source")
await documents.create_document(document=document)
stored_path = tmp_path / "documents" / str(document.id) / "orphan.jpg"
stored_path.parent.mkdir(parents=True, exist_ok=True)
stored_path.write_bytes(b"data")
source = Source(
document_id=document.id,
page_number=1,
upload_name="orphan.jpg",
filename="orphan.jpg",
file_path="uploads/orphan.jpg",
file_path=str(stored_path),
)
await transcriptions.create_source(source=source)
@@ -176,6 +187,7 @@ class TestTranscriptionServiceRevisionUpsert:
with pytest.raises(TranscriptionNotFoundError):
await transcriptions.read_source(source.id)
assert not stored_path.exists()
@pytest.mark.asyncio
async def test_delete_unlinked_source_blocks_when_linked(self, default_session_factory):
+17
View File
@@ -105,6 +105,23 @@ class TestSourcesPageRendering:
assert "Sources for Job" in response.text
assert "Back to Job" in response.text
assert "job-page.png" in response.text
assert "Job Source Status" in response.text
def test_sources_page_job_context_shows_job_source_status_and_error_detail(self, app_client, seed_job):
_, client = app_client
job_id = seed_job(
filename="job-failed-page.png",
status=JobStatus.FAILED,
transcription_text=None,
error_detail="Provider timed out",
)
response = client.get(f"/ui/sources?job_id={job_id}")
assert response.status_code == 200
assert "job-failed-page.png" in response.text
assert "failed" in response.text.lower()
assert "Provider timed out" in response.text
def test_source_detail_page_renders_preview_and_revision_box(self, app_client, seed_job):
_, client = app_client