Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
b5dbc6a
feat: add .dockerignore to reduce build context size
codcreater1 Jul 2, 2026
c0fd62b
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 2, 2026
58cc3fb
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 2, 2026
a7a0d8c
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 2, 2026
5662ffd
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 2, 2026
fc424cf
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 2, 2026
4feb80f
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 2, 2026
01225e0
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 2, 2026
cdb378b
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 2, 2026
b779c47
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 2, 2026
1165274
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 7, 2026
0d3b748
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 14, 2026
1445cee
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 14, 2026
b66b82a
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 16, 2026
7cdb654
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 16, 2026
1127d6d
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 16, 2026
9526e90
Merge branch 'main' of https://github.com/codcreater1/agentic-interns…
codcreater1 Jul 16, 2026
8eba3d6
fix: stop deleting the signed contract when it is downloaded
codcreater1 Jul 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions backend/app/routers/pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import logging

from fastapi import (
APIRouter, BackgroundTasks, Depends, File,
APIRouter, Depends, File,
Form, HTTPException, UploadFile, status,
)
from fastapi.responses import FileResponse
Expand Down Expand Up @@ -154,7 +154,6 @@ async def sign_pdf(
async def download_signed(
task_id: str,
token: str,
background: BackgroundTasks,
ss: StorageService = Depends(get_storage_service),
) -> FileResponse:

Expand All @@ -180,11 +179,11 @@ async def download_signed(
if not path.is_file():
_raise_http(SignedPdfNotReadyError())

background.add_task(
ss.remove_task,
task_id,
)

# The task dir is deliberately NOT removed after download. A signed
# internship agreement is a lasting record: the dashboard previews it, the
# coordinator may re-download it, and it is emailed to the candidate. The
# delete-after-download behaviour here came from this service's one-shot
# PDF-signing origins and destroyed contracts on first download.
return FileResponse(
path=str(path),
media_type="application/pdf",
Expand Down
44 changes: 44 additions & 0 deletions backend/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,50 @@ def test_missing_application_returns_404():
assert client.get("/applications/9999").status_code == 404


def _upload_and_sign() -> str:
"""Upload a blank PDF, sign it, and return the download URL."""
doc = fitz.open()
doc.new_page()
pdf_buf = io.BytesIO()
doc.save(pdf_buf)
pdf_buf.seek(0)

upload = client.post(
"/pdf/upload",
files={"file": ("sample.pdf", pdf_buf, "application/pdf")},
)
assert upload.status_code == 201
task_id = upload.json()["task_id"]

img = Image.new("RGBA", (50, 20), (255, 0, 0, 255))
img_buf = io.BytesIO()
img.save(img_buf, format="PNG")
img_buf.seek(0)

signed = client.post(
"/pdf/sign",
data={"task_id": task_id, "page": 0, "x": 10, "y": 10, "w": 50, "h": 20},
files={"image": ("sig.png", img_buf, "image/png")},
)
assert signed.status_code == 200
return signed.json()["download_url"]


def test_signed_contract_survives_download():
"""Regression test: /pdf/download used to schedule remove_task() as a
background task, wiping the whole task directory the first time the file was
fetched. The dashboard opens that link automatically right after signing, so
a signed internship agreement — which is previewed, re-downloaded and
emailed to the candidate — was destroyed on first download. Downloading
twice must keep working.
"""
download_url = _upload_and_sign()

assert client.get(download_url).status_code == 200
# The file must still be there for the preview / email / re-download paths.
assert client.get(download_url).status_code == 200


def test_sign_pdf_download_url_is_reachable():
"""Regression test: /pdf/sign previously returned a download_url prefixed
with settings.api_prefix ("/api/v1/pdf/download/..."), but routers are
Expand Down
Loading