forked from ChelseaKR/constituent-reconciler
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconftest.py
More file actions
148 lines (114 loc) · 5.1 KB
/
Copy pathconftest.py
File metadata and controls
148 lines (114 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
"""Shared fixtures and fakes for the test suite.
``make_pdf`` (in ``constituent_reconciler.testing``) generates a minimal but
valid PDF-1.4 file whose text content pdfplumber can extract. It uses only the
Python standard library, with no dependency on reportlab, fpdf2, or any
PDF-creation library. It lives in the package rather than here so that
``eval/fixtures/extraction/make_fixtures.py`` can regenerate the committed
labeled extraction fixtures from the same generator.
``FakeAirtableTransport``, ``FakeCivicrmTransport``,
``FakeSalesforceTransport``, and ``FakeWebhookTransport`` are queued-response
transports for the network
connectors. The connector unit tests and the conformance suite share them so
every test observes requests the same way.
"""
from __future__ import annotations
import json
from pathlib import Path
import pytest
from constituent_reconciler.testing import make_pdf
class FakeAirtableTransport:
"""Returns queued responses and records every Airtable PATCH."""
def __init__(self, responses: list[tuple[int, bytes]]) -> None:
self._responses = responses
self.calls: list[tuple[str, dict[str, str], bytes]] = []
def patch(self, url: str, *, headers: dict[str, str], body: bytes) -> tuple[int, bytes]:
self.calls.append((url, headers, body))
return self._responses.pop(0)
class FakeCivicrmTransport:
"""Returns queued responses and records every request for inspection."""
def __init__(self, responses: list[tuple[int, dict[str, object]]]) -> None:
self._responses = responses
self.calls: list[tuple[str, dict[str, str], bytes]] = []
def post(self, url: str, *, headers: dict[str, str], body: bytes) -> tuple[int, bytes]:
self.calls.append((url, headers, body))
status, payload = self._responses.pop(0)
return status, json.dumps(payload).encode("utf-8")
class FakeSalesforceTransport:
"""Returns queued responses and records every request for inspection."""
def __init__(self, responses: list[tuple[int, dict[str, object] | None]]) -> None:
self._responses = responses
self.calls: list[tuple[str, str, dict[str, str], bytes | None]] = []
def send(
self, method: str, url: str, *, headers: dict[str, str], body: bytes | None
) -> tuple[int, bytes]:
self.calls.append((method, url, headers, body))
status, payload = self._responses.pop(0)
raw = b"" if payload is None else json.dumps(payload).encode("utf-8")
return status, raw
class FakeWebhookTransport:
"""Returns queued responses and records every request for inspection."""
def __init__(self, responses: list[tuple[int, bytes]]) -> None:
self._responses = responses
self.calls: list[tuple[str, dict[str, str], bytes]] = []
def post(self, url: str, *, headers: dict[str, str], body: bytes) -> tuple[int, bytes]:
self.calls.append((url, headers, body))
return self._responses.pop(0)
@pytest.fixture()
def intake_pdf(tmp_path: Path) -> Path:
"""A single-page intake form PDF with all five canonical fields present."""
pdf_bytes = make_pdf(
[
"Intake Form",
"First Name: Alice",
"Last Name: Walker",
"DOB: 1970-05-12",
"Email: alice@example.org",
"Phone: 555-123-4567",
]
)
path = tmp_path / "intake-form.pdf"
path.write_bytes(pdf_bytes)
return path
@pytest.fixture()
def low_confidence_pdf(tmp_path: Path) -> Path:
"""A PDF whose page has too little text to reach full confidence."""
pdf_bytes = make_pdf(["Hi"])
path = tmp_path / "low-confidence.pdf"
path.write_bytes(pdf_bytes)
return path
@pytest.fixture()
def scanned_pdf(tmp_path: Path) -> Path:
"""A single-page PDF with no text operators at all -- i.e. no text layer.
Stands in for an image-only scan: pdfplumber's ``extract_text()`` returns
"" for it just as it would for a real scanned page with no OCR baked in by
the scanner, which is exactly the condition the OCR backend (``extract/
ocr.py``) watches for. The page still rasterizes fine via pdfplumber's
renderer, so `page.to_image()` works; tests supply the Tesseract output
rather than depending on OCR actually reading anything from a blank page.
"""
pdf_bytes = make_pdf([])
path = tmp_path / "scanned-form.pdf"
path.write_bytes(pdf_bytes)
return path
@pytest.fixture()
def intake_pdf_folder(tmp_path: Path) -> Path:
"""A folder with one intake-form PDF and one CSV, for folder-ingest tests."""
folder = tmp_path / "intake-docs"
folder.mkdir()
pdf_bytes = make_pdf(
[
"Intake Form",
"First Name: Alice",
"Last Name: Walker",
"DOB: 1970-05-12",
"Email: alice@example.org",
"Phone: 555-123-4567",
]
)
(folder / "form-001.pdf").write_bytes(pdf_bytes)
csv_content = (
"id,first,last,dob,email,phone,consent\n"
"X001,Bob,Smith,1985-07-04,bob@example.org,555-987-6543,granted\n"
)
(folder / "batch-001.csv").write_text(csv_content, encoding="utf-8")
return folder