| from __future__ import annotations |
|
|
| import os |
| import sys |
| import shutil |
| from pathlib import Path |
|
|
| import pytest |
| from fastapi.testclient import TestClient |
|
|
| |
| |
| |
| THIS_FILE = Path(__file__).resolve() |
| TESTS_DIR = THIS_FILE.parent |
| MOCK_DIR = TESTS_DIR / "mock" |
|
|
| TEST_IMAGES_DIR = TESTS_DIR / "images" |
| TEST_INPUT_DIR = TEST_IMAGES_DIR / "input" |
| TEST_OUTPUT_DIR = TEST_IMAGES_DIR / "output" |
|
|
| API_DIR = THIS_FILE.parents[1] |
| sys.path.insert(0, str(API_DIR)) |
|
|
| |
| os.environ["IMAGE_BASE_DIR"] = str(TEST_IMAGES_DIR) |
|
|
| import src.main as api_module |
|
|
|
|
| |
| |
| |
| def _reset_test_images_dir() -> None: |
| """Delete api/test/images if it exists, then recreate input/output.""" |
| if TEST_IMAGES_DIR.exists(): |
| shutil.rmtree(TEST_IMAGES_DIR) |
| TEST_INPUT_DIR.mkdir(parents=True, exist_ok=True) |
| TEST_OUTPUT_DIR.mkdir(parents=True, exist_ok=True) |
|
|
|
|
| |
| |
| |
| @pytest.fixture(autouse=True) |
| def _clean_fs_before_each_test(request: pytest.FixtureRequest): |
| """ |
| Runs before EVERY test, EXCEPT the final 'zz' test (so artifacts remain after suite). |
| """ |
| if request.node.name == "test_zz_upload_image_saves_files_in_input_and_output_folders": |
| return |
| _reset_test_images_dir() |
|
|
|
|
| @pytest.fixture |
| def client() -> TestClient: |
| return TestClient(api_module.app) |
|
|
|
|
| |
| |
| |
| def test_hello_world_endpoint(client: TestClient): |
| res = client.get("/") |
| assert res.status_code == 200 |
| assert res.json() == {"Hello": "World"} |
|
|
|
|
| def test_non_image_uploaded_returns_400_and_saves_nothing(client: TestClient): |
| pdf_path = MOCK_DIR / "sample.pdf" |
| assert pdf_path.exists(), f"Missing mock pdf: {pdf_path}" |
|
|
| files = {"image": (pdf_path.name, pdf_path.read_bytes(), "application/pdf")} |
| res = client.post("/upload-image", files=files) |
|
|
| assert res.status_code == 400, res.text |
| assert res.json()["detail"] == "File is not an image." |
| assert list(TEST_INPUT_DIR.glob("*")) == [] |
| assert list(TEST_OUTPUT_DIR.glob("*")) == [] |
|
|
|
|
| def test_invalid_extension_returns_400_and_saves_nothing(client: TestClient): |
| bad_path = MOCK_DIR / "bad.txt" |
| assert bad_path.exists(), f"Missing bad file: {bad_path}" |
|
|
| files = {"image": (bad_path.name, bad_path.read_bytes(), "image/png")} |
| res = client.post("/upload-image", files=files) |
|
|
| assert res.status_code == 400, res.text |
| assert res.json()["detail"] == "Invalid file extension." |
| assert list(TEST_INPUT_DIR.glob("*")) == [] |
| assert list(TEST_OUTPUT_DIR.glob("*")) == [] |
|
|
|
|
| def test_missing_image_returns_422(client: TestClient): |
| res = client.post("/upload-image", files={}) |
| assert res.status_code == 422 |
|
|
|
|
| def test_zz_upload_image_saves_files_in_input_and_output_folders(client: TestClient): |
| """ |
| Real integration run: |
| - start clean |
| - send sample.jpg |
| - verify 1 file in input and 1 in output |
| - leave artifacts so you can visually inspect output image after the suite |
| """ |
| |
| _reset_test_images_dir() |
|
|
| img_path = MOCK_DIR / "sample.jpg" |
| assert img_path.exists(), f"Missing mock image: {img_path}" |
|
|
| files = {"image": (img_path.name, img_path.read_bytes(), "image/jpeg")} |
| res = client.post("/upload-image", files=files) |
|
|
| assert res.status_code == 200, res.text |
|
|
| input_files = list(TEST_INPUT_DIR.glob("*")) |
| output_files = list(TEST_OUTPUT_DIR.glob("*")) |
|
|
| assert len(input_files) == 1, f"Expected 1 input file, found: {input_files}" |
| assert len(output_files) == 1, f"Expected 1 output file, found: {output_files}" |
|
|
| assert input_files[0].name == "sample.jpg" |
| assert output_files[0].name == "sample_processed.png" |
| |