Restructure backend plan to FastAPI's official routers/ + dependencies.py layout
This commit is contained in:
parent
ba7ee21c28
commit
954f7ac367
@ -701,7 +701,7 @@ git commit -m "feat: add MinioClient wrapper for list/get/put/delete object oper
|
|||||||
|
|
||||||
**Interfaces:**
|
**Interfaces:**
|
||||||
- Consumes: `meilisearch.Client` SDK instance (constructor-injected; tests inject a hand-written fake)
|
- Consumes: `meilisearch.Client` SDK instance (constructor-injected; tests inject a hand-written fake)
|
||||||
- Produces: `SearchClient(client, index_name: str)` with `index_document(key: str, path: str, title: str, content: str) -> None`, `delete_document(key: str) -> None`, `search(query: str) -> list[dict]` (each dict has `key`, `title`, `path`, `snippet`) — consumed by `DocumentService` in Task 5 and `routes/search.py` in Task 7.
|
- Produces: `SearchClient(client, index_name: str)` with `index_document(key: str, path: str, title: str, content: str) -> None`, `delete_document(key: str) -> None`, `search(query: str) -> list[dict]` (each dict has `key`, `title`, `path`, `snippet`) — consumed by `DocumentService` in Task 5 and `routers/search.py` in Task 7.
|
||||||
|
|
||||||
- [ ] **Step 1: Write the failing test for `index_document`**
|
- [ ] **Step 1: Write the failing test for `index_document`**
|
||||||
|
|
||||||
@ -901,7 +901,7 @@ git commit -m "feat: add SearchClient wrapper for index/delete/search operations
|
|||||||
|
|
||||||
**Interfaces:**
|
**Interfaces:**
|
||||||
- Consumes: `MinioClient.list_objects/get_object/put_object/delete_object` (Task 3), `SearchClient.index_document/delete_document` (Task 4), `markdown_to_text`/`html_to_text` (Task 2)
|
- Consumes: `MinioClient.list_objects/get_object/put_object/delete_object` (Task 3), `SearchClient.index_document/delete_document` (Task 4), `markdown_to_text`/`html_to_text` (Task 2)
|
||||||
- Produces: `DocumentService(minio_client, search_client)` with `list_documents(path: str) -> dict`, `get_document(key: str) -> dict`, `save_document(key: str, content: bytes, content_type: str) -> dict`, `delete_document(key: str) -> None`, `upload_document(key: str, data: bytes, content_type: str) -> dict`; `DocumentNotFoundError` exception — all consumed by `routes/documents.py` in Task 6.
|
- Produces: `DocumentService(minio_client, search_client)` with `list_documents(path: str) -> dict`, `get_document(key: str) -> dict`, `save_document(key: str, content: bytes, content_type: str) -> dict`, `delete_document(key: str) -> None`, `upload_document(key: str, data: bytes, content_type: str) -> dict`; `DocumentNotFoundError` exception — all consumed by `routers/documents.py` in Task 6.
|
||||||
|
|
||||||
- [ ] **Step 1: Write the failing test for `list_documents`**
|
- [ ] **Step 1: Write the failing test for `list_documents`**
|
||||||
|
|
||||||
@ -1239,26 +1239,27 @@ git commit -m "feat: add DocumentService coordinating MinIO writes and search in
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Task 6: Documents Routes (`routes/documents.py`)
|
### Task 6: Documents Router (`routers/documents.py`)
|
||||||
|
|
||||||
**Files:**
|
**Files:**
|
||||||
- Create: `backend/app/routes/__init__.py`
|
- Create: `backend/app/dependencies.py`
|
||||||
- Create: `backend/app/routes/documents.py`
|
- Create: `backend/app/routers/__init__.py`
|
||||||
- Test: `backend/tests/test_routes_documents.py`
|
- Create: `backend/app/routers/documents.py`
|
||||||
|
- Test: `backend/tests/test_routers_documents.py`
|
||||||
|
|
||||||
**Interfaces:**
|
**Interfaces:**
|
||||||
- Consumes: `DocumentService`, `DocumentNotFoundError` (Task 5), `app.config.settings` (Task 1)
|
- Consumes: `DocumentService`, `DocumentNotFoundError` (Task 5), `app.config.settings` (Task 1)
|
||||||
- Produces: `router` (APIRouter with `GET /api/documents`, `GET /api/documents/{key:path}`, `PUT /api/documents/{key:path}`, `DELETE /api/documents/{key:path}`, `POST /api/upload`), `get_document_service()` dependency provider — both consumed by `main.py` in Task 8 and by route/smoke tests via `app.dependency_overrides`.
|
- Produces: `app.dependencies.get_document_service()` dependency provider (FastAPI's official "Bigger Applications" layout keeps shared dependency providers in a dedicated `dependencies.py`, separate from routers); `router` (APIRouter with `GET /api/documents`, `GET /api/documents/{key:path}`, `PUT /api/documents/{key:path}`, `DELETE /api/documents/{key:path}`, `POST /api/upload`) — both consumed by `main.py` in Task 8 and by route/smoke tests via `app.dependency_overrides`.
|
||||||
|
|
||||||
- [ ] **Step 1: Write the failing test for `GET /api/documents`**
|
- [ ] **Step 1: Write the failing test for `GET /api/documents`**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# backend/tests/test_routes_documents.py
|
# backend/tests/test_routers_documents.py
|
||||||
import pytest
|
import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from app.main import app
|
from app.main import app
|
||||||
from app.routes.documents import get_document_service
|
from app.dependencies import get_document_service
|
||||||
from app.document_service import DocumentNotFoundError
|
from app.document_service import DocumentNotFoundError
|
||||||
|
|
||||||
|
|
||||||
@ -1313,31 +1314,23 @@ def test_list_documents_returns_folders_and_files(client):
|
|||||||
|
|
||||||
- [ ] **Step 2: Run test to verify it fails**
|
- [ ] **Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_documents.py::test_list_documents_returns_folders_and_files -v`
|
Run: `cd backend && pytest tests/test_routers_documents.py::test_list_documents_returns_folders_and_files -v`
|
||||||
Expected: FAIL with `ModuleNotFoundError: No module named 'app.routes'`
|
Expected: FAIL with `ModuleNotFoundError: No module named 'app.routers'`
|
||||||
|
|
||||||
- [ ] **Step 3: Create `backend/app/routes/__init__.py` and write `documents.py` with `GET /api/documents`**
|
- [ ] **Step 3: Create `backend/app/dependencies.py`, `backend/app/routers/__init__.py`, and write `documents.py` with `GET /api/documents`**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# backend/app/routes/__init__.py
|
# backend/app/dependencies.py
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
|
||||||
# backend/app/routes/documents.py
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
|
|
||||||
from minio import Minio
|
|
||||||
import meilisearch
|
import meilisearch
|
||||||
from pydantic import BaseModel
|
from minio import Minio
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.document_service import DocumentNotFoundError, DocumentService
|
from app.document_service import DocumentService
|
||||||
from app.minio_client import MinioClient
|
from app.minio_client import MinioClient
|
||||||
from app.search_client import SearchClient
|
from app.search_client import SearchClient
|
||||||
|
|
||||||
router = APIRouter()
|
|
||||||
|
|
||||||
|
|
||||||
@lru_cache
|
@lru_cache
|
||||||
def get_document_service() -> DocumentService:
|
def get_document_service() -> DocumentService:
|
||||||
@ -1353,6 +1346,27 @@ def get_document_service() -> DocumentService:
|
|||||||
return DocumentService(minio_client, search_client)
|
return DocumentService(minio_client, search_client)
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
|
def get_search_client() -> SearchClient:
|
||||||
|
meili_sdk = meilisearch.Client(settings.meilisearch_host, settings.meilisearch_api_key)
|
||||||
|
return SearchClient(meili_sdk, settings.meilisearch_index)
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# backend/app/routers/__init__.py
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# backend/app/routers/documents.py
|
||||||
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from app.dependencies import get_document_service
|
||||||
|
from app.document_service import DocumentNotFoundError, DocumentService
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
class SaveDocumentRequest(BaseModel):
|
class SaveDocumentRequest(BaseModel):
|
||||||
content: str
|
content: str
|
||||||
contentType: str
|
contentType: str
|
||||||
@ -1365,7 +1379,7 @@ def list_documents(path: str = "", service: DocumentService = Depends(get_docume
|
|||||||
|
|
||||||
- [ ] **Step 4: Run test to verify it passes**
|
- [ ] **Step 4: Run test to verify it passes**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_documents.py::test_list_documents_returns_folders_and_files -v`
|
Run: `cd backend && pytest tests/test_routers_documents.py::test_list_documents_returns_folders_and_files -v`
|
||||||
Expected: PASS
|
Expected: PASS
|
||||||
|
|
||||||
- [ ] **Step 5: Write the failing tests for `GET /api/documents/{key}` (found and 404)**
|
- [ ] **Step 5: Write the failing tests for `GET /api/documents/{key}` (found and 404)**
|
||||||
@ -1384,13 +1398,13 @@ def test_get_document_returns_404_for_missing_key(client):
|
|||||||
|
|
||||||
- [ ] **Step 6: Run tests to verify they fail**
|
- [ ] **Step 6: Run tests to verify they fail**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_documents.py::test_get_document_returns_content tests/test_routes_documents.py::test_get_document_returns_404_for_missing_key -v`
|
Run: `cd backend && pytest tests/test_routers_documents.py::test_get_document_returns_content tests/test_routers_documents.py::test_get_document_returns_404_for_missing_key -v`
|
||||||
Expected: FAIL with `404 Not Found` (route not registered)
|
Expected: FAIL with `404 Not Found` (route not registered)
|
||||||
|
|
||||||
- [ ] **Step 7: Add `GET /api/documents/{key:path}`**
|
- [ ] **Step 7: Add `GET /api/documents/{key:path}`**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# backend/app/routes/documents.py (add below list_documents)
|
# backend/app/routers/documents.py (add below list_documents)
|
||||||
@router.get("/api/documents/{key:path}")
|
@router.get("/api/documents/{key:path}")
|
||||||
def get_document(key: str, service: DocumentService = Depends(get_document_service)):
|
def get_document(key: str, service: DocumentService = Depends(get_document_service)):
|
||||||
try:
|
try:
|
||||||
@ -1401,7 +1415,7 @@ def get_document(key: str, service: DocumentService = Depends(get_document_servi
|
|||||||
|
|
||||||
- [ ] **Step 8: Run tests to verify they pass**
|
- [ ] **Step 8: Run tests to verify they pass**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_documents.py::test_get_document_returns_content tests/test_routes_documents.py::test_get_document_returns_404_for_missing_key -v`
|
Run: `cd backend && pytest tests/test_routers_documents.py::test_get_document_returns_content tests/test_routers_documents.py::test_get_document_returns_404_for_missing_key -v`
|
||||||
Expected: PASS
|
Expected: PASS
|
||||||
|
|
||||||
- [ ] **Step 9: Write the failing test for `PUT /api/documents/{key}`**
|
- [ ] **Step 9: Write the failing test for `PUT /api/documents/{key}`**
|
||||||
@ -1420,13 +1434,13 @@ def test_put_document_saves_content(client):
|
|||||||
|
|
||||||
- [ ] **Step 10: Run test to verify it fails**
|
- [ ] **Step 10: Run test to verify it fails**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_documents.py::test_put_document_saves_content -v`
|
Run: `cd backend && pytest tests/test_routers_documents.py::test_put_document_saves_content -v`
|
||||||
Expected: FAIL with `405 Method Not Allowed`
|
Expected: FAIL with `405 Method Not Allowed`
|
||||||
|
|
||||||
- [ ] **Step 11: Add `PUT /api/documents/{key:path}`**
|
- [ ] **Step 11: Add `PUT /api/documents/{key:path}`**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# backend/app/routes/documents.py (add below get_document)
|
# backend/app/routers/documents.py (add below get_document)
|
||||||
@router.put("/api/documents/{key:path}")
|
@router.put("/api/documents/{key:path}")
|
||||||
def save_document(
|
def save_document(
|
||||||
key: str, body: SaveDocumentRequest, service: DocumentService = Depends(get_document_service)
|
key: str, body: SaveDocumentRequest, service: DocumentService = Depends(get_document_service)
|
||||||
@ -1436,7 +1450,7 @@ def save_document(
|
|||||||
|
|
||||||
- [ ] **Step 12: Run test to verify it passes**
|
- [ ] **Step 12: Run test to verify it passes**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_documents.py::test_put_document_saves_content -v`
|
Run: `cd backend && pytest tests/test_routers_documents.py::test_put_document_saves_content -v`
|
||||||
Expected: PASS
|
Expected: PASS
|
||||||
|
|
||||||
- [ ] **Step 13: Write the failing test for `DELETE /api/documents/{key}`**
|
- [ ] **Step 13: Write the failing test for `DELETE /api/documents/{key}`**
|
||||||
@ -1451,13 +1465,13 @@ def test_delete_document_returns_204(client):
|
|||||||
|
|
||||||
- [ ] **Step 14: Run test to verify it fails**
|
- [ ] **Step 14: Run test to verify it fails**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_documents.py::test_delete_document_returns_204 -v`
|
Run: `cd backend && pytest tests/test_routers_documents.py::test_delete_document_returns_204 -v`
|
||||||
Expected: FAIL with `405 Method Not Allowed`
|
Expected: FAIL with `405 Method Not Allowed`
|
||||||
|
|
||||||
- [ ] **Step 15: Add `DELETE /api/documents/{key:path}`**
|
- [ ] **Step 15: Add `DELETE /api/documents/{key:path}`**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# backend/app/routes/documents.py (add below save_document)
|
# backend/app/routers/documents.py (add below save_document)
|
||||||
@router.delete("/api/documents/{key:path}", status_code=204)
|
@router.delete("/api/documents/{key:path}", status_code=204)
|
||||||
def delete_document(key: str, service: DocumentService = Depends(get_document_service)):
|
def delete_document(key: str, service: DocumentService = Depends(get_document_service)):
|
||||||
service.delete_document(key)
|
service.delete_document(key)
|
||||||
@ -1466,7 +1480,7 @@ def delete_document(key: str, service: DocumentService = Depends(get_document_se
|
|||||||
|
|
||||||
- [ ] **Step 16: Run test to verify it passes**
|
- [ ] **Step 16: Run test to verify it passes**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_documents.py::test_delete_document_returns_204 -v`
|
Run: `cd backend && pytest tests/test_routers_documents.py::test_delete_document_returns_204 -v`
|
||||||
Expected: PASS
|
Expected: PASS
|
||||||
|
|
||||||
- [ ] **Step 17: Write the failing test for `POST /api/upload`**
|
- [ ] **Step 17: Write the failing test for `POST /api/upload`**
|
||||||
@ -1487,13 +1501,13 @@ def test_upload_document_returns_201_with_metadata(client):
|
|||||||
|
|
||||||
- [ ] **Step 18: Run test to verify it fails**
|
- [ ] **Step 18: Run test to verify it fails**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_documents.py::test_upload_document_returns_201_with_metadata -v`
|
Run: `cd backend && pytest tests/test_routers_documents.py::test_upload_document_returns_201_with_metadata -v`
|
||||||
Expected: FAIL with `404 Not Found`
|
Expected: FAIL with `404 Not Found`
|
||||||
|
|
||||||
- [ ] **Step 19: Add `POST /api/upload`**
|
- [ ] **Step 19: Add `POST /api/upload`**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# backend/app/routes/documents.py (add below delete_document)
|
# backend/app/routers/documents.py (add below delete_document)
|
||||||
@router.post("/api/upload", status_code=201)
|
@router.post("/api/upload", status_code=201)
|
||||||
async def upload_document(
|
async def upload_document(
|
||||||
file: UploadFile = File(...),
|
file: UploadFile = File(...),
|
||||||
@ -1508,37 +1522,38 @@ async def upload_document(
|
|||||||
|
|
||||||
- [ ] **Step 20: Run full test file to verify everything passes**
|
- [ ] **Step 20: Run full test file to verify everything passes**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_documents.py -v`
|
Run: `cd backend && pytest tests/test_routers_documents.py -v`
|
||||||
Expected: PASS (all 6 tests) — note: at this point `app.main` does not yet include `documents.router`, so these tests will actually fail with 404s until Task 8 wires the router into `app`. Proceed to Task 8 before considering this task fully green end-to-end; the route functions themselves are complete and unit-correct.
|
Expected: PASS (all 6 tests) — note: at this point `app.main` does not yet include `documents.router`, so these tests will actually fail with 404s until Task 8 wires the router into `app`. Proceed to Task 8 before considering this task fully green end-to-end; the route functions themselves are complete and unit-correct.
|
||||||
|
|
||||||
- [ ] **Step 21: Commit**
|
- [ ] **Step 21: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add backend/app/routes/__init__.py backend/app/routes/documents.py backend/tests/test_routes_documents.py
|
git add backend/app/dependencies.py backend/app/routers/__init__.py backend/app/routers/documents.py backend/tests/test_routers_documents.py
|
||||||
git commit -m "feat: add documents routes for list/get/save/delete/upload"
|
git commit -m "feat: add documents router for list/get/save/delete/upload"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Task 7: Search Route (`routes/search.py`)
|
### Task 7: Search Router (`routers/search.py`)
|
||||||
|
|
||||||
**Files:**
|
**Files:**
|
||||||
- Create: `backend/app/routes/search.py`
|
- Modify: `backend/app/dependencies.py`
|
||||||
- Test: `backend/tests/test_routes_search.py`
|
- Create: `backend/app/routers/search.py`
|
||||||
|
- Test: `backend/tests/test_routers_search.py`
|
||||||
|
|
||||||
**Interfaces:**
|
**Interfaces:**
|
||||||
- Consumes: `SearchClient.search` (Task 4), `app.config.settings` (Task 1)
|
- Consumes: `SearchClient.search` (Task 4), `app.config.settings` (Task 1), `app.dependencies.get_search_client` (Task 6, already defined)
|
||||||
- Produces: `router` (APIRouter with `GET /api/search`), `get_search_client()` dependency provider — both consumed by `main.py` in Task 8 and by route/smoke tests via `app.dependency_overrides`.
|
- Produces: `router` (APIRouter with `GET /api/search`) — consumed by `main.py` in Task 8 and by route/smoke tests via `app.dependency_overrides`.
|
||||||
|
|
||||||
- [ ] **Step 1: Write the failing test for `GET /api/search` with matches**
|
- [ ] **Step 1: Write the failing test for `GET /api/search` with matches**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# backend/tests/test_routes_search.py
|
# backend/tests/test_routers_search.py
|
||||||
import pytest
|
import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from app.main import app
|
from app.main import app
|
||||||
from app.routes.search import get_search_client
|
from app.dependencies import get_search_client
|
||||||
|
|
||||||
|
|
||||||
class FakeSearchClient:
|
class FakeSearchClient:
|
||||||
@ -1579,38 +1594,31 @@ def test_search_returns_matching_results(client):
|
|||||||
|
|
||||||
- [ ] **Step 2: Run test to verify it fails**
|
- [ ] **Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_search.py::test_search_returns_matching_results -v`
|
Run: `cd backend && pytest tests/test_routers_search.py::test_search_returns_matching_results -v`
|
||||||
Expected: FAIL with `ModuleNotFoundError: No module named 'app.routes.search'`
|
Expected: FAIL with `ModuleNotFoundError: No module named 'app.routers.search'`
|
||||||
|
|
||||||
- [ ] **Step 3: Write `search.py` with `GET /api/search`**
|
- [ ] **Step 3: Write `search.py` with `GET /api/search`, reusing `get_search_client` from `dependencies.py`**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# backend/app/routes/search.py
|
# backend/app/routers/search.py
|
||||||
from functools import lru_cache
|
|
||||||
|
|
||||||
import meilisearch
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
from app.config import settings
|
from app.dependencies import get_search_client
|
||||||
from app.search_client import SearchClient
|
from app.search_client import SearchClient
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@lru_cache
|
|
||||||
def get_search_client() -> SearchClient:
|
|
||||||
meili_sdk = meilisearch.Client(settings.meilisearch_host, settings.meilisearch_api_key)
|
|
||||||
return SearchClient(meili_sdk, settings.meilisearch_index)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/search")
|
@router.get("/api/search")
|
||||||
def search(q: str = "", client: SearchClient = Depends(get_search_client)):
|
def search(q: str = "", client: SearchClient = Depends(get_search_client)):
|
||||||
return {"results": client.search(q)}
|
return {"results": client.search(q)}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: `get_search_client` was already added to `backend/app/dependencies.py` in Task 6's Step 3. This task only adds the router that consumes it.
|
||||||
|
|
||||||
- [ ] **Step 4: Run test to verify it passes**
|
- [ ] **Step 4: Run test to verify it passes**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_search.py::test_search_returns_matching_results -v`
|
Run: `cd backend && pytest tests/test_routers_search.py::test_search_returns_matching_results -v`
|
||||||
Expected: PASS — note: this test will actually return 404 until Task 8 wires `search.router` into `app.main`; the route logic itself is complete at this point.
|
Expected: PASS — note: this test will actually return 404 until Task 8 wires `search.router` into `app.main`; the route logic itself is complete at this point.
|
||||||
|
|
||||||
- [ ] **Step 5: Write the failing test for `GET /api/search` with no matches**
|
- [ ] **Step 5: Write the failing test for `GET /api/search` with no matches**
|
||||||
@ -1624,7 +1632,7 @@ def test_search_returns_empty_results_for_no_match(client):
|
|||||||
|
|
||||||
- [ ] **Step 6: Run test (already satisfied by Step 3's implementation once wired in Task 8)**
|
- [ ] **Step 6: Run test (already satisfied by Step 3's implementation once wired in Task 8)**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_search.py::test_search_returns_empty_results_for_no_match -v`
|
Run: `cd backend && pytest tests/test_routers_search.py::test_search_returns_empty_results_for_no_match -v`
|
||||||
Expected: PASS after Task 8 wires the router
|
Expected: PASS after Task 8 wires the router
|
||||||
|
|
||||||
- [ ] **Step 7: Write the failing test asserting the query string is forwarded**
|
- [ ] **Step 7: Write the failing test asserting the query string is forwarded**
|
||||||
@ -1637,13 +1645,13 @@ def test_search_passes_query_string_to_search_client(client):
|
|||||||
|
|
||||||
- [ ] **Step 8: Run test (already satisfied by Step 3's implementation once wired in Task 8)**
|
- [ ] **Step 8: Run test (already satisfied by Step 3's implementation once wired in Task 8)**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/test_routes_search.py::test_search_passes_query_string_to_search_client -v`
|
Run: `cd backend && pytest tests/test_routers_search.py::test_search_passes_query_string_to_search_client -v`
|
||||||
Expected: PASS after Task 8 wires the router
|
Expected: PASS after Task 8 wires the router
|
||||||
|
|
||||||
- [ ] **Step 9: Commit**
|
- [ ] **Step 9: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add backend/app/routes/search.py backend/tests/test_routes_search.py
|
git add backend/app/routers/search.py backend/tests/test_routers_search.py
|
||||||
git commit -m "feat: add search route forwarding queries to SearchClient"
|
git commit -m "feat: add search route forwarding queries to SearchClient"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1656,7 +1664,7 @@ git commit -m "feat: add search route forwarding queries to SearchClient"
|
|||||||
- Edit: `backend/tests/test_main.py`
|
- Edit: `backend/tests/test_main.py`
|
||||||
|
|
||||||
**Interfaces:**
|
**Interfaces:**
|
||||||
- Consumes: `documents.router`, `documents.get_document_service` (Task 6); `search.router`, `search.get_search_client` (Task 7); `DocumentNotFoundError` (Task 5)
|
- Consumes: `documents.router` (Task 6); `search.router` (Task 7); `app.dependencies.get_document_service`, `app.dependencies.get_search_client` (Task 6); `DocumentNotFoundError` (Task 5)
|
||||||
- Produces: fully wired `app.main.app` — the production FastAPI entrypoint served by `uvicorn app.main:app` (per `Dockerfile` from Task 1) and the object the frontend's API client (built separately) will call against.
|
- Produces: fully wired `app.main.app` — the production FastAPI entrypoint served by `uvicorn app.main:app` (per `Dockerfile` from Task 1) and the object the frontend's API client (built separately) will call against.
|
||||||
|
|
||||||
- [ ] **Step 1: Write the failing end-to-end smoke test**
|
- [ ] **Step 1: Write the failing end-to-end smoke test**
|
||||||
@ -1664,8 +1672,7 @@ git commit -m "feat: add search route forwarding queries to SearchClient"
|
|||||||
```python
|
```python
|
||||||
# backend/tests/test_main.py (append to existing file from Task 1)
|
# backend/tests/test_main.py (append to existing file from Task 1)
|
||||||
from app.document_service import DocumentNotFoundError
|
from app.document_service import DocumentNotFoundError
|
||||||
from app.routes.documents import get_document_service
|
from app.dependencies import get_document_service, get_search_client
|
||||||
from app.routes.search import get_search_client
|
|
||||||
|
|
||||||
|
|
||||||
class FakeDocumentService:
|
class FakeDocumentService:
|
||||||
@ -1765,7 +1772,7 @@ from fastapi import FastAPI
|
|||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.routes import documents, search
|
from app.routers import documents, search
|
||||||
|
|
||||||
app = FastAPI(title="DocHub API")
|
app = FastAPI(title="DocHub API")
|
||||||
|
|
||||||
@ -1789,7 +1796,7 @@ def health():
|
|||||||
- [ ] **Step 4: Run the full backend test suite to verify everything passes**
|
- [ ] **Step 4: Run the full backend test suite to verify everything passes**
|
||||||
|
|
||||||
Run: `cd backend && pytest tests/ -v`
|
Run: `cd backend && pytest tests/ -v`
|
||||||
Expected: PASS (all tests across `test_main.py`, `test_text_extract.py`, `test_minio_client.py`, `test_search_client.py`, `test_document_service.py`, `test_routes_documents.py`, `test_routes_search.py`)
|
Expected: PASS (all tests across `test_main.py`, `test_text_extract.py`, `test_minio_client.py`, `test_search_client.py`, `test_document_service.py`, `test_routers_documents.py`, `test_routers_search.py`)
|
||||||
|
|
||||||
- [ ] **Step 5: Commit**
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user