@@ -84,104 +84,21 @@ def test_list_templates_ordering(self, db):
8484class TestTemplateEndpoints :
8585
8686 def test_list_templates_empty (self , client ):
87+ """Contract registry list is empty until a template is registered."""
8788 resp = client .get (f"{ API_PREFIX } /templates" )
8889 assert resp .status_code == 200
8990 assert resp .json () == []
9091
91- def test_create_template (self , client , mock_controller ):
92- payload = {
93- "name" : "Fire Report" ,
94- "pdf_path" : "src/inputs/fire_report.pdf" ,
95- "fields" : {
96- "Name" : "string" ,
97- "Date" : "string" ,
98- "Location" : "string" ,
99- },
100- }
101- resp = client .post (f"{ API_PREFIX } /templates/create" , json = payload )
102- assert resp .status_code == 200
103-
104- data = resp .json ()
105- assert data ["id" ] is not None
106- assert data ["name" ] == "Fire Report"
107- assert data ["fields" ]["Location" ] == "string"
108- # Plain create just persists the row; commonforms only runs via
109- # the separate /make-fillable endpoint.
110- mock_controller ["template_ctrl" ].create_template .assert_not_called ()
111-
112- def test_create_then_list (self , client , mock_controller ):
113- """Creating a template should make it appear in the list."""
114- client .post (f"{ API_PREFIX } /templates/create" , json = {
115- "name" : "T1" ,
116- "pdf_path" : "a.pdf" ,
117- "fields" : {"f" : "string" },
118- })
119- resp = client .get (f"{ API_PREFIX } /templates" )
120- assert resp .status_code == 200
121- assert len (resp .json ()) == 1
122- assert resp .json ()[0 ]["name" ] == "T1"
123-
124- def test_upload_pdf (self , client , pdf_upload , tmp_path , monkeypatch ):
125- """Upload a valid PDF file."""
126- # Point the upload directory inside tmp_path (which is inside the project
127- # for the path-safety check — we monkeypatch the check).
128- monkeypatch .setattr (
129- "app.api.routes.templates.PROJECT_ROOT" ,
130- tmp_path ,
131- )
132- resp = client .post (
133- f"{ API_PREFIX } /templates/upload" ,
134- files = [pdf_upload ],
135- data = {"directory" : str (tmp_path )},
136- )
137- assert resp .status_code == 200
138- data = resp .json ()
139- assert data ["filename" ] == "test_form.pdf"
140- assert data ["pdf_path" ].endswith (".pdf" )
141-
142- def test_upload_non_pdf_rejected (self , client ):
143- import io
144- bad_file = ("file" , ("notes.txt" , io .BytesIO (b"hello" ), "text/plain" ))
145- resp = client .post (f"{ API_PREFIX } /templates/upload" , files = [bad_file ])
146- assert resp .status_code == 400
147- assert "PDF" in resp .json ()["detail" ]
148-
149- def test_preview_missing_file (self , client ):
150- resp = client .get (f"{ API_PREFIX } /templates/preview" , params = {"path" : "src/inputs/nonexistent.pdf" })
151- assert resp .status_code == 404
152-
153- def test_directory_traversal_blocked (self , client ):
154- import io
155- pdf = ("file" , ("evil.pdf" , io .BytesIO (b"%PDF-1.4" ), "application/pdf" ))
156- resp = client .post (
157- f"{ API_PREFIX } /templates/upload" ,
158- files = [pdf ],
159- data = {"directory" : "/etc" },
160- )
161- assert resp .status_code == 400
162- assert "inside the project" in resp .json ()["detail" ]
163-
16492
16593# ═══════════════════════════════════════════════════════════════════════════
166- # Form fill endpoints
94+ # Form fill endpoints (legacy pipeline — templates seeded directly in the DB
95+ # since the /templates/create endpoint was removed in the contract migration)
16796# ═══════════════════════════════════════════════════════════════════════════
16897
16998class TestFormEndpoints :
17099
171- def _seed_template (self , client , mock_controller ):
172- """Helper: create a template and return its ID."""
173- resp = client .post (f"{ API_PREFIX } /templates/create" , json = {
174- "name" : "Employee Form" ,
175- "pdf_path" : "src/inputs/employee.pdf" ,
176- "fields" : {
177- "Employee's name" : "string" ,
178- "Employee's email" : "string" ,
179- },
180- })
181- return resp .json ()["id" ]
182-
183- def test_fill_form_success (self , client , mock_controller ):
184- tpl_id = self ._seed_template (client , mock_controller )
100+ def test_fill_form_success (self , client , mock_controller , seed_template ):
101+ tpl_id = seed_template ()
185102
186103 resp = client .post (f"{ API_PREFIX } /forms/fill" , json = {
187104 "template_id" : tpl_id ,
@@ -202,8 +119,8 @@ def test_fill_form_missing_template(self, client, mock_controller):
202119 })
203120 assert resp .status_code == 404
204121
205- def test_fill_form_template_file_not_found (self , client , mock_controller ):
206- tpl_id = self . _seed_template ( client , mock_controller )
122+ def test_fill_form_template_file_not_found (self , client , mock_controller , seed_template ):
123+ tpl_id = seed_template ( )
207124 mock_controller ["form_ctrl" ].fill_form .side_effect = FileNotFoundError ("PDF template not found" )
208125
209126 resp = client .post (f"{ API_PREFIX } /forms/fill" , json = {
@@ -283,9 +200,9 @@ def boom(*a, **k):
283200 assert resp .status_code == 200
284201 assert resp .json ()["models" ] == ["qwen2.5:1.5b" ]
285202
286- def test_fill_form_passes_model_override (self , client , mock_controller ):
203+ def test_fill_form_passes_model_override (self , client , mock_controller , seed_template ):
287204 """A `model` in the request reaches Controller.fill_form but isn't persisted."""
288- tpl_id = self . _seed_template ( client , mock_controller )
205+ tpl_id = seed_template ( )
289206 resp = client .post (f"{ API_PREFIX } /forms/fill" , json = {
290207 "template_id" : tpl_id ,
291208 "input_text" : "John Doe" ,
@@ -316,45 +233,27 @@ def fake_post(*args, **kwargs):
316233
317234class TestE2EPipeline :
318235 """
319- Full pipeline: upload PDF → create template → fill form → verify DB state.
320- This is the critical path that the product depends on.
236+ Legacy fill pipeline: seed template → fill form → verify DB state.
237+ Template registration via API was removed in the contract migration, so the
238+ template is seeded directly in the DB.
321239 """
322240
323- def test_full_flow (self , client , mock_controller , pdf_upload , tmp_path , monkeypatch , db ):
324- # -- Step 1: Upload a PDF --
325- monkeypatch .setattr ("app.api.routes.templates.PROJECT_ROOT" , tmp_path )
326- upload_resp = client .post (
327- f"{ API_PREFIX } /templates/upload" ,
328- files = [pdf_upload ],
329- data = {"directory" : str (tmp_path )},
330- )
331- assert upload_resp .status_code == 200
332- uploaded_path = upload_resp .json ()["pdf_path" ]
333- assert uploaded_path .endswith (".pdf" )
334-
335- # -- Step 2: Create a template from the uploaded PDF --
336- create_resp = client .post (f"{ API_PREFIX } /templates/create" , json = {
337- "name" : "Incident Report" ,
338- "pdf_path" : uploaded_path ,
339- "fields" : {
241+ def test_full_flow (self , client , mock_controller , seed_template , db ):
242+ # -- Step 1: Seed a template --
243+ template_id = seed_template (
244+ name = "Incident Report" ,
245+ pdf_path = "src/inputs/incident.pdf" ,
246+ fields = {
340247 "Officer name" : "string" ,
341248 "Badge number" : "string" ,
342249 "Incident date" : "string" ,
343250 "Location" : "string" ,
344251 "Description" : "string" ,
345252 },
346- })
347- assert create_resp .status_code == 200
348- template_id = create_resp .json ()["id" ]
253+ )
349254 assert template_id is not None
350255
351- # -- Step 3: Verify template appears in list --
352- list_resp = client .get (f"{ API_PREFIX } /templates" )
353- assert list_resp .status_code == 200
354- templates = list_resp .json ()
355- assert any (t ["id" ] == template_id for t in templates )
356-
357- # -- Step 4: Fill the form --
256+ # -- Step 2: Fill the form --
358257 fill_resp = client .post (f"{ API_PREFIX } /forms/fill" , json = {
359258 "template_id" : template_id ,
360259 "input_text" : (
0 commit comments