diff --git a/document-parser/tests/test_chunking.py b/document-parser/tests/test_chunking.py index b429d35..52e84c8 100644 --- a/document-parser/tests/test_chunking.py +++ b/document-parser/tests/test_chunking.py @@ -9,9 +9,9 @@ from unittest.mock import AsyncMock, MagicMock import pytest from fastapi.testclient import TestClient -from api.schemas import ChunkingOptionsRequest, ChunkResponse, RechunkRequest +from api.schemas import ChunkBboxResponse, ChunkingOptionsRequest, ChunkResponse, RechunkRequest from domain.models import AnalysisJob, AnalysisStatus -from domain.value_objects import ChunkingOptions, ChunkResult +from domain.value_objects import ChunkBbox, ChunkingOptions, ChunkResult from main import app # --------------------------------------------------------------------------- @@ -60,7 +60,52 @@ class TestChunkResult: def test_serializable(self): chunk = ChunkResult(text="x", headings=["h1"], source_page=1, token_count=10) data = asdict(chunk) - assert data == {"text": "x", "headings": ["h1"], "source_page": 1, "token_count": 10} + assert data == { + "text": "x", + "headings": ["h1"], + "source_page": 1, + "token_count": 10, + "bboxes": [], + } + + +class TestChunkBbox: + def test_construction(self): + bbox = ChunkBbox(page=1, bbox=[10.0, 20.0, 100.0, 80.0]) + assert bbox.page == 1 + assert bbox.bbox == [10.0, 20.0, 100.0, 80.0] + + def test_serializable(self): + bbox = ChunkBbox(page=2, bbox=[0.0, 0.0, 50.0, 50.0]) + data = asdict(bbox) + assert data == {"page": 2, "bbox": [0.0, 0.0, 50.0, 50.0]} + + def test_chunk_result_with_bboxes(self): + chunk = ChunkResult( + text="content", + bboxes=[ + ChunkBbox(page=1, bbox=[10, 20, 100, 80]), + ChunkBbox(page=2, bbox=[50, 50, 150, 250]), + ], + ) + assert len(chunk.bboxes) == 2 + assert chunk.bboxes[0].page == 1 + + +class TestChunkBboxResponse: + def test_serializes(self): + resp = ChunkBboxResponse(page=1, bbox=[10.0, 20.0, 100.0, 80.0]) + data = resp.model_dump(by_alias=True) + assert data == {"page": 1, "bbox": [10.0, 20.0, 100.0, 80.0]} + + def test_chunk_response_with_bboxes(self): + resp = ChunkResponse( + text="hello", + bboxes=[ChunkBboxResponse(page=1, bbox=[10, 20, 100, 80])], + ) + data = resp.model_dump(by_alias=True) + assert len(data["bboxes"]) == 1 + assert data["bboxes"][0]["page"] == 1 # --------------------------------------------------------------------------- @@ -282,6 +327,28 @@ class TestRechunkEndpoint: ) assert resp.status_code == 400 + def test_rechunk_returns_bboxes(self, client, mock_analysis_service): + mock_analysis_service.rechunk = AsyncMock( + return_value=[ + ChunkResult( + text="chunk1", + source_page=1, + token_count=10, + bboxes=[ChunkBbox(page=1, bbox=[10, 20, 100, 80])], + ), + ] + ) + + resp = client.post( + "/api/analyses/j1/rechunk", + json={"chunkingOptions": {"chunker_type": "hybrid"}}, + ) + assert resp.status_code == 200 + data = resp.json() + assert len(data[0]["bboxes"]) == 1 + assert data[0]["bboxes"][0]["page"] == 1 + assert data[0]["bboxes"][0]["bbox"] == [10, 20, 100, 80] + def test_rechunk_invalid_chunker_type(self, client, mock_analysis_service): resp = client.post( "/api/analyses/j1/rechunk", diff --git a/frontend/src/features/chunking/store.test.ts b/frontend/src/features/chunking/store.test.ts index 80b3fa3..17fd7de 100644 --- a/frontend/src/features/chunking/store.test.ts +++ b/frontend/src/features/chunking/store.test.ts @@ -21,8 +21,14 @@ describe('analysis store — chunking', () => { it('currentChunks parses chunksJson from current analysis', () => { const store = useAnalysisStore() const chunks = [ - { text: 'chunk1', headings: ['H1'], sourcePage: 1, tokenCount: 10 }, - { text: 'chunk2', headings: [], sourcePage: 2, tokenCount: 20 }, + { + text: 'chunk1', + headings: ['H1'], + sourcePage: 1, + tokenCount: 10, + bboxes: [{ page: 1, bbox: [10, 20, 100, 80] }], + }, + { text: 'chunk2', headings: [], sourcePage: 2, tokenCount: 20, bboxes: [] }, ] store.currentAnalysis = { id: 'j1', @@ -64,7 +70,7 @@ describe('analysis store — chunking', () => { it('rechunk calls API and refreshes analysis', async () => { const store = useAnalysisStore() - const chunks = [{ text: 'c1', headings: [], sourcePage: 1, tokenCount: 5 }] + const chunks = [{ text: 'c1', headings: [], sourcePage: 1, tokenCount: 5, bboxes: [] }] vi.mocked(api.rechunkAnalysis).mockResolvedValue(chunks) vi.mocked(api.fetchAnalysis).mockResolvedValue({ id: 'j1',