Minor updates from line drifts

This commit is contained in:
stumpylog committed 2026-08-18 09:35:43 -07:00
1 parent 04a703029c
commit decaaf2f1b
10 files changed
+53 -51

No files matched your search

@@ -16,7 +16,7 @@
- `page_count:>5`, `asn:<10`, `page_count:>=5`, `asn:[1 TO 10]`, `tag_id:1,2,3` parse OK (comparison operators produce correct `RangeQuery`).
- `asn:[1 TO]` / `asn:[TO 10]` are a **Syntax Error** (open numeric ranges unsupported; only open _date_ ranges work via sentinels).
- `scan()` only tokenizes fields in `KNOWN_FIELDS`; unknown `foobar:hello` stays a `Passthrough` and only fails at `parse_query` -> detected by the backstop, not proactively.
- `difflib.get_close_matches("corespondent", pool)` -> `["correspondent"]`; `has_tags`/`http`/`12` -> `[]` (bare message).
- `difflib.get_close_matches("correspondent", pool)` -> `["correspondent"]`; `has_tags`/`http`/`12` -> `[]` (bare message).
- `tantivy.Schema` exposes no field-name list, so the drift guard is parse-based.
## File Structure
@@ -74,7 +74,7 @@ class TestErrorClasses:
assert str(err) == "Unknown search field 'has_tags'."
def test_unknown_field_message_with_suggestion(self):
err = UnknownFieldError("corespondent", suggestion="correspondent")
err = UnknownFieldError("correspondent", suggestion="correspondent")
assert err.suggestion == "correspondent"
assert str(err) == (
"Unknown search field 'corespondent'. Did you mean 'correspondent'?"
@@ -350,9 +350,9 @@ from documents.search._translate import map_tantivy_error
class TestMapTantivyError:
def test_unknown_field_maps_with_suggestion(self):
exc = ValueError("Field does not exist: 'corespondent'")
mapped = map_tantivy_error(exc, "corespondent:foo")
mapped = map_tantivy_error(exc, "correspondent:foo")
assert isinstance(mapped, UnknownFieldError)
assert mapped.field == "corespondent"
assert mapped.field == "correspondent"
assert mapped.suggestion == "correspondent"
def test_unknown_field_maps_without_suggestion(self):
@@ -481,7 +481,7 @@ git commit -m "feat(search): map tantivy parse errors to user-safe messages"
**Files:**
- Modify: `src/documents/search/_query.py` (import `map_tantivy_error`; add `_parse_query_friendly`; use it at lines 231-235 and 253-259)
- Modify: `src/documents/search/_query.py` (import `map_tantivy_error`; add `_parse_query_friendly`; use it at lines 216-220 and 238-244)
- Test: `src/documents/tests/search/test_error_shapes.py`
- [ ] **Step 1: Write the failing test**
@@ -506,7 +506,7 @@ class TestBackstopViaParseUserQuery:
def test_unknown_field_suggestion(self, index: tantivy.Index):
with pytest.raises(UnknownFieldError) as exc_info:
parse_user_query(index, "corespondent:bob", UTC)
parse_user_query(index, "correspondent:bob", UTC)
assert exc_info.value.suggestion == "correspondent"
def test_legacy_backend_field_is_unknown(self, index: tantivy.Index):
@@ -555,13 +555,13 @@ Expected: FAIL — unknown-field/syntax cases currently raise the bare Tantivy `
- [ ] **Step 3: Write minimal implementation**
In `src/documents/search/_query.py`, add the import alongside the existing translate imports (after line 20):
In `src/documents/search/_query.py`, add the import alongside the existing translate imports (after line 13):
```python
from documents.search._translate import map_tantivy_error
```
Add a module-level helper (place it just above `parse_user_query`, before line 191):
Add a module-level helper (place it just above `parse_user_query`, before line 176):
```python
def _parse_query_friendly(
@@ -584,7 +584,7 @@ def _parse_query_friendly(
raise
```
In `parse_user_query`, replace the exact-query parse (lines 231-235):
In `parse_user_query`, replace the exact-query parse (lines 216-220):
```python
exact = _parse_query_friendly(
@@ -596,7 +596,7 @@ In `parse_user_query`, replace the exact-query parse (lines 231-235):
)
```
and the fuzzy parse (lines 253-259):
and the fuzzy parse (lines 238-244):
```python
fuzzy = _parse_query_friendly(
@@ -610,7 +610,7 @@ and the fuzzy parse (lines 253-259):
)
```
(`SearchQueryError` is already imported in `_query.py` at line 19.)
(`SearchQueryError` is already imported in `_query.py` at line 12.)
- [ ] **Step 4: Run test to verify it passes**
@@ -712,7 +712,7 @@ Expected: PASS. (These assert current truth; they guard against future drift. If
- [ ] **Step 4: Write the failing view-level test**
In `src/documents/tests/test_api_search.py`, locate `test_search_added_invalid_date` (around line 723) and add this test directly after it, inside the same `TestDocumentSearchApi` class (mirrors that test's structure):
In `src/documents/tests/test_api_search.py`, locate `test_search_added_invalid_date` (around line 765) and add this test directly after it, inside the same `TestDocumentSearchApi` class (mirrors that test's structure):
```python
def test_search_unknown_field_returns_400(self) -> None:
@@ -95,7 +95,7 @@ class TestBulkEditPermissionMatrix:
Notes:
- Mock the underlying `bulk_edit.<fn>` (patch `documents.views.bulk_edit.<fn>`) so the operations don't actually run — this test is purely about the permission gate returning 200 vs 403.
- A superuser short-circuits to allowed (`views.py:2697`); include one superuser row to pin that.
- A superuser short-circuits to allowed (`views.py:2833`); include one superuser row to pin that.
- This is verbose by design; the matrix is the security contract. Prefer one parametrized test over hand-written methods.
- **Cover the six moved single-action endpoints too (REQUIRED — C2).** `/api/documents/rotate/`, `/merge/`, `/delete/`, `/reprocess/`, `/edit_pdf/`, `/remove_password/` run the **same** `_has_document_permissions` gate via `_execute_document_action`, and that path is rewritten in Task 3 (C1). Add a parallel parametrized test that POSTs to each (their request bodies are the dedicated serializers' fields — e.g. `{"documents": [...], "degrees": 90}` for rotate — **not** a `method`+`parameters` envelope). The existing `test_api_bulk_edit.py` already covers these endpoints' permission gates (`test_rotate_insufficient_permissions:1320`, `test_merge_and_delete_insufficient_permissions:1381`, `test_edit_pdf_insufficient_permissions:1635`, `test_remove_password_insufficient_permissions:1719`), so this is hardening rather than the sole net — but make the moved-endpoint matrix explicit here so the `_execute_document_action` rewrite is guarded by a parametrized characterization, not scattered one-offs.
- **`edit_pdf` test docs need a `page_count` (M3).** `clean_parameters` for `edit_pdf` bounds-checks `op["page"]` against `Document.page_count` (`serialisers.py:2052-2059`); this test mocks execution but **not** validation, so an `edit_pdf` row with `page: 1` needs its target doc created with `page_count >= 1`, else it fails with a 400 (out-of-bounds) instead of the expected 200/403.
@@ -103,7 +103,7 @@ Notes:
- [ ] **Step 2: Run it against CURRENT code — it must PASS**
Run: `bash /c/Users/tholmes/Documents/Coding/paperless/vmtest.sh "src/documents/tests/test_bulk_operations.py -v"`
Expected: PASS. If any row is red, the spec matrix is misread — reconcile against `views.py:2713-2760` before writing any production code.
Expected: PASS. If any row is red, the spec matrix is misread — reconcile against `views.py:2843-2906` before writing any production code.
- [ ] **Step 3: Commit**
@@ -519,7 +519,7 @@ git commit -m "Feature: consume_file owns and cleans the staged work_root"
**Files:**
- Modify: `src/documents/management/commands/document_consumer.py:340-353`
- Modify: `src/documents/management/commands/document_consumer.py:344-357`
- Test: `src/documents/tests/test_management_consumer.py:99-103`
- [ ] **Step 1: Repoint the consumer test fixture**
@@ -548,7 +548,7 @@ Expected: FAIL — the folder site still calls `consume_file.apply_async`, not t
- [ ] **Step 3: Migrate the folder enqueue site**
In `src/documents/management/commands/document_consumer.py`, add `from documents import ingest` at the top, and replace the enqueue block (lines ~340-353):
In `src/documents/management/commands/document_consumer.py`, add `from documents import ingest` at the top, and replace the enqueue block (lines ~344-357):
```python
# Queue for consumption
@@ -594,7 +594,7 @@ rewrite and the site migration land together.
**Files:**
- Modify: `src/documents/tests/utils.py:242-274`
- Modify: `src/documents/views.py:3149-3192` (PostDocumentView) and `:1917-1976` (update_version)
- Modify: `src/documents/views.py:3275-3338` (PostDocumentView) and `:2036-2098` (update_version)
- Modify: `src/documents/tests/test_api_document_versions.py` (patch target)
- [ ] **Step 1: Rewrite `ConsumeTaskMixin` to patch the seam**
@@ -643,7 +643,7 @@ Expected: FAIL — `PostDocumentView` still calls `consume_file.apply_async`, so
- [ ] **Step 3: Migrate `PostDocumentView.post`**
In `src/documents/views.py`, ensure `from documents import ingest` is imported,
then replace the staging + dispatch (lines ~3149-3192) with a `stage_document`
then replace the staging + dispatch (lines ~3275-3338) with a `stage_document`
block:
```python
@@ -687,7 +687,7 @@ The old `SCRATCH_DIR.mkdir` + `mkdtemp` + `write_bytes` + the explicit
- [ ] **Step 4: Migrate `update_version`**
In `src/documents/views.py` `update_version` (lines ~1917-1976), replace its
In `src/documents/views.py` `update_version` (lines ~2036-2098), replace its
`mkdtemp`/`write`/`consume_file.apply_async` with the same pattern, preserving its
specific fields (`root_document_id`, `version_label`, `actor_id`):
@@ -732,7 +732,7 @@ git commit -m "Refactor: route API/WebUI/version ingest through the staging seam
**Files:**
- Modify: `src/paperless_mail/mail.py` (`_handle_message` ~716-760, `_process_attachments` ~861-908, `_process_eml` ~952-1006)
- Modify: `src/paperless_mail/mail.py` (`_handle_message` ~790-835, `_process_attachments` ~893-1024, `_process_eml` ~1026-1097)
- Test: `src/paperless_mail/tests/test_mail.py`, `test_mail_nfc.py` (verify, likely no change)
- [ ] **Step 1: Wrap the message's staging in an `ExitStack`**
@@ -774,7 +774,7 @@ work_root for the message.)
- [ ] **Step 2: Stage each attachment via the stack**
Replace the attachment staging (`mail.py:861-908`) inside `_process_attachments`:
Replace the attachment staging (`mail.py:893-1024`) inside `_process_attachments`:
```python
staged = staging_stack.enter_context(
@@ -804,7 +804,7 @@ Replace the attachment staging (`mail.py:861-908`) inside `_process_attachments`
The old `SCRATCH_DIR.mkdir` + `mkdtemp` + `write_bytes` + `consume_file.s(...).set(...)`
are gone; `stage_document` handles the temp dir and `build_consume_signature` the
header. Do the analogous replacement in `_process_eml` (`mail.py:952-1006`),
header. Do the analogous replacement in `_process_eml` (`mail.py:1026-1097`),
staging the `.eml` bytes and building the signature the same way.
- [ ] **Step 3: Run the mail suites**
@@ -885,14 +885,14 @@ folder source needs it (its loose file in `CONSUMPTION_DIR` is removed on succes
**Files:**
- Modify: `src/documents/consumer.py:417-422`
- Modify: `src/documents/consumer.py:408-414`
- Test: `src/documents/tests/test_consumer.py`
- [ ] **Step 1: Use the handed-in working dir instead of a second `TemporaryDirectory`**
`ConsumerPlugin` already receives the task's working dir as `self.base_tmp_dir`
(the `tmp_dir` arg from `tasks.py:227-233`). Replace its own
`tempfile.TemporaryDirectory(...)` (`consumer.py:417`) with a subfolder of that
`tempfile.TemporaryDirectory(...)` (`consumer.py:408`) with a subfolder of that
handed-in dir:
```python
@@ -389,7 +389,7 @@ git commit -m "Add HybridRetriever: lexical fallback for archive AI chat retriev
**Files:**
- Modify: `src/paperless_ai/chat.py:95-112`
- Modify: `src/paperless_ai/chat.py:149-177`
- Modify: `src/paperless_ai/tests/test_chat.py`
**Interfaces:**
@@ -436,7 +436,7 @@ Expected: FAIL — `HybridRetriever` is never constructed yet (`chat.py` still b
- [ ] **Step 3: Wire `HybridRetriever` into `_stream_chat_with_documents`**
In `src/paperless_ai/chat.py`, inside `_stream_chat_with_documents` (currently lines 95-112), add the `HybridRetriever` import alongside the other lazy llama-index imports and replace the retriever construction:
In `src/paperless_ai/chat.py`, inside `_stream_chat_with_documents` (currently lines 149-177), add the `HybridRetriever` import alongside the other lazy llama-index imports and replace the retriever construction:
```python
from llama_index.core.prompts import PromptTemplate