From 65fc86d2de2570087ed7648d900c30f85dfa164b Mon Sep 17 00:00:00 2001 From: Arijit429 Date: Mon, 13 Apr 2026 19:04:55 +0530 Subject: [PATCH 1/2] test: add edge case coverage for template file routes --- tests/test_template_file_routes.py | 71 ++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/test_template_file_routes.py diff --git a/tests/test_template_file_routes.py b/tests/test_template_file_routes.py new file mode 100644 index 0000000..7bae8c8 --- /dev/null +++ b/tests/test_template_file_routes.py @@ -0,0 +1,71 @@ +from fastapi.testclient import TestClient +from api.main import app + +client = TestClient(app) + + +def test_upload_template_pdf(): + pdf_content = b"%PDF-1.4 test pdf content" + + files = { + "file": ("sample_test.pdf", pdf_content, "application/pdf") + } + + data = { + "directory": "src/inputs" + } + + response = client.post( + "/templates/upload", + files=files, + data=data + ) + + assert response.status_code == 200 + + response_data = response.json() + + assert "filename" in response_data + assert "pdf_path" in response_data + assert response_data["filename"].endswith(".pdf") + + +def test_preview_template_pdf(): + response = client.get( + "/templates/preview", + params={"path": "src/inputs/file.pdf"} + ) + + assert response.status_code == 200 + assert response.headers["content-type"] == "application/pdf" + +def test_upload_non_pdf_should_fail(): + files = { + "file": ("sample.txt", b"not a pdf", "text/plain") + } + + response = client.post( + "/templates/upload", + files=files, + data={"directory": "src/inputs"} + ) + + assert response.status_code == 400 + + +def test_preview_missing_file_should_fail(): + response = client.get( + "/templates/preview", + params={"path": "src/inputs/does_not_exist.pdf"} + ) + + assert response.status_code == 404 + + +def test_preview_outside_project_should_fail(): + response = client.get( + "/templates/preview", + params={"path": "/etc/passwd"} + ) + + assert response.status_code == 400 From 51521a5680b179d1b444c278c709b9182d09bf71 Mon Sep 17 00:00:00 2001 From: Arijit429 Date: Tue, 14 Apr 2026 00:22:03 +0530 Subject: [PATCH 2/2] feat(frontend): prevent duplicate submit requests --- frontend/app.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/frontend/app.js b/frontend/app.js index 2f6de80..5a3104d 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -32,6 +32,8 @@ const elements = { let templates = loadTemplates(); let activeObjectUrl = null; let selectedTemplateFile = null; +let isTemplateSubmitting = false; +let isFillSubmitting = false; initialize(); @@ -246,6 +248,14 @@ function normalizeFields(rawFields) { async function handleTemplateSubmit(event) { event.preventDefault(); + + if (isTemplateSubmitting) { + setStatus(elements.templateFormMessage, "Request already in progress...", "info"); + return; + } + + isTemplateSubmitting = true; + clearJson(elements.templateFormResponse); setStatus(elements.templateFormMessage, ""); @@ -259,11 +269,13 @@ async function handleTemplateSubmit(event) { "Name, PDF file, and template directory are required.", "error" ); + isTemplateSubmitting = false; return; } if (normalized.error) { setStatus(elements.templateFormMessage, normalized.error, "error"); + isTemplateSubmitting = false; return; } @@ -280,6 +292,7 @@ async function handleTemplateSubmit(event) { }; setStatus(elements.templateFormMessage, "Creating template...", "info"); + const response = await fetch(`${API_BASE_URL}/templates/create`, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -287,12 +300,14 @@ async function handleTemplateSubmit(event) { }); const body = await parseJsonResponse(response); + if (!response.ok) { throw new Error(extractErrorMessage(body, response.status)); } upsertTemplate(body); await refreshTemplatesFromApi(); + elements.fillTemplateId.value = String(body.id || ""); elements.serverPdfPath.value = body.pdf_path || ""; @@ -301,12 +316,16 @@ async function handleTemplateSubmit(event) { `Template created (id: ${body.id}). PDF saved at ${upload.pdf_path}.`, "success" ); + showJson(elements.templateFormResponse, body); } catch (error) { setStatus(elements.templateFormMessage, error.message, "error"); + } finally { + isTemplateSubmitting = false; } } + async function uploadTemplatePdf(file, directory) { const formData = new FormData(); formData.append("file", file, file.name); @@ -327,6 +346,14 @@ async function uploadTemplatePdf(file, directory) { async function handleFillSubmit(event) { event.preventDefault(); + + if (isFillSubmitting) { + setStatus(elements.fillFormMessage, "Request already in progress...", "info"); + return; + } + + isFillSubmitting = true; + clearJson(elements.fillFormResponse); setStatus(elements.fillFormMessage, ""); @@ -335,11 +362,13 @@ async function handleFillSubmit(event) { if (!Number.isInteger(templateId) || templateId < 1) { setStatus(elements.fillFormMessage, "Template ID must be a positive integer.", "error"); + isFillSubmitting = false; return; } if (!inputText) { setStatus(elements.fillFormMessage, "Input text is required.", "error"); + isFillSubmitting = false; return; } @@ -350,6 +379,7 @@ async function handleFillSubmit(event) { try { setStatus(elements.fillFormMessage, "Submitting form fill request...", "info"); + const response = await fetch(`${API_BASE_URL}/forms/fill`, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -357,6 +387,7 @@ async function handleFillSubmit(event) { }); const body = await parseJsonResponse(response); + if (!response.ok) { throw new Error(extractErrorMessage(body, response.status)); } @@ -372,9 +403,12 @@ async function handleFillSubmit(event) { `Form filled (submission id: ${body.id}).`, "success" ); + showJson(elements.fillFormResponse, body); } catch (error) { setStatus(elements.fillFormMessage, error.message, "error"); + } finally { + isFillSubmitting = false; } }