From 86a5be776721aae420d9060f7028cdf7c3860688 Mon Sep 17 00:00:00 2001 From: Michele Pangrazzi Date: Fri, 10 Jan 2025 16:14:24 +0100 Subject: [PATCH] Add missing tests for some endpoints before route refactoring (#52) * add tests for status endpoint * add missing tests for deploy endpoints * add test for draw endpoint * Refactor status endpoint * Test refactoring using utilty methods as fixtures for calling API endpoints --- src/hayhooks/server/handlers/status.py | 11 +++++- src/hayhooks/server/pipelines/registry.py | 3 ++ tests/conftest.py | 34 +++++++++++++++++ tests/test_it_deploy.py | 41 +++++++++++++++++--- tests/test_it_draw.py | 23 +++++++++++ tests/test_it_handling_deploy_exceptions.py | 4 +- tests/test_it_status.py | 42 +++++++++++++++++++++ 7 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 tests/conftest.py create mode 100644 tests/test_it_draw.py create mode 100644 tests/test_it_status.py diff --git a/src/hayhooks/server/handlers/status.py b/src/hayhooks/server/handlers/status.py index 93f304e..c59f6d7 100644 --- a/src/hayhooks/server/handlers/status.py +++ b/src/hayhooks/server/handlers/status.py @@ -3,7 +3,14 @@ from hayhooks.server.pipelines import registry -@app.get("/status") -async def status(): +@app.get("/status", tags=["status"]) +async def status_all(): pipelines = registry.get_names() return {"status": "Up!", "pipelines": pipelines} + + +@app.get("/status/{pipeline_name}", tags=["status"]) +async def status(pipeline_name: str): + if pipeline_name not in registry.get_names(): + raise HTTPException(status_code=404, detail=f"Pipeline '{pipeline_name}' not found") + return {"status": "Up!", "pipeline": pipeline_name} diff --git a/src/hayhooks/server/pipelines/registry.py b/src/hayhooks/server/pipelines/registry.py index f3dc270..9a58fae 100644 --- a/src/hayhooks/server/pipelines/registry.py +++ b/src/hayhooks/server/pipelines/registry.py @@ -31,5 +31,8 @@ def get(self, name: str) -> Optional[Pipeline]: def get_names(self) -> list[str]: return list(self._pipelines.keys()) + def clear(self): + self._pipelines.clear() + registry = _PipelineRegistry() diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..e71d4ab --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,34 @@ +import pytest +from fastapi.testclient import TestClient + + +@pytest.fixture +def deploy_pipeline(): + def _deploy_pipeline(client: TestClient, pipeline_name: str, pipeline_source_code: str): + deploy_response = client.post("/deploy", json={"name": pipeline_name, "source_code": pipeline_source_code}) + return deploy_response + return _deploy_pipeline + + +@pytest.fixture +def undeploy_pipeline(): + def _undeploy_pipeline(client: TestClient, pipeline_name: str): + undeploy_response = client.post(f"/undeploy/{pipeline_name}") + return undeploy_response + return _undeploy_pipeline + + +@pytest.fixture +def draw_pipeline(): + def _draw_pipeline(client: TestClient, pipeline_name: str): + draw_response = client.get(f"/draw/{pipeline_name}") + return draw_response + return _draw_pipeline + + +@pytest.fixture +def status_pipeline(): + def _status_pipeline(client: TestClient, pipeline_name: str): + status_response = client.get(f"/status/{pipeline_name}") + return status_response + return _status_pipeline diff --git a/tests/test_it_deploy.py b/tests/test_it_deploy.py index 0721e1e..d980c22 100644 --- a/tests/test_it_deploy.py +++ b/tests/test_it_deploy.py @@ -2,23 +2,52 @@ from fastapi.testclient import TestClient from hayhooks.server import app from pathlib import Path +from hayhooks.server.pipelines.registry import registry client = TestClient(app) + +@pytest.fixture(autouse=True) +def clear_registry(): + registry.clear() + + # Load pipeline definitions from test_files test_files = Path(__file__).parent / "test_files" / "working_pipelines" pipeline_data = [{"name": file.stem, "source_code": file.read_text()} for file in test_files.glob("*.yml")] @pytest.mark.parametrize("pipeline_data", pipeline_data) -def test_deploy_pipeline_def(pipeline_data: dict): - deploy_response = client.post( - "/deploy", json={"name": pipeline_data["name"], "source_code": pipeline_data["source_code"]} - ) +def test_deploy_pipeline_def(deploy_pipeline, status_pipeline, pipeline_data: dict): + deploy_response = deploy_pipeline(client, pipeline_data["name"], pipeline_data["source_code"]) assert deploy_response.status_code == 200 - status_response = client.get("/status") - assert pipeline_data["name"] in status_response.json()["pipelines"] + status_response = status_pipeline(client, pipeline_data["name"]) + assert pipeline_data["name"] in status_response.json()["pipeline"] docs_response = client.get("/docs") assert docs_response.status_code == 200 + + +def test_undeploy_pipeline_def(deploy_pipeline, undeploy_pipeline, status_pipeline): + pipeline_file = Path(__file__).parent / "test_files" / "working_pipelines/test_pipeline_01.yml" + pipeline_data = {"name": pipeline_file.stem, "source_code": pipeline_file.read_text()} + + deploy_response = deploy_pipeline(client, pipeline_data["name"], pipeline_data["source_code"]) + assert deploy_response.status_code == 200 + + undeploy_response = undeploy_pipeline(client, pipeline_data["name"]) + assert undeploy_response.status_code == 200 + + status_response = status_pipeline(client, pipeline_data["name"]) + assert status_response.status_code == 404 + + +def test_undeploy_non_existent_pipeline(undeploy_pipeline): + undeploy_response = undeploy_pipeline(client, "non_existent_pipeline") + assert undeploy_response.status_code == 404 + + +def test_undeploy_no_pipelines(undeploy_pipeline): + undeploy_response = undeploy_pipeline(client, "") + assert undeploy_response.status_code == 404 diff --git a/tests/test_it_draw.py b/tests/test_it_draw.py new file mode 100644 index 0000000..fb71776 --- /dev/null +++ b/tests/test_it_draw.py @@ -0,0 +1,23 @@ +from fastapi.testclient import TestClient +from hayhooks.server import app +from pathlib import Path + +client = TestClient(app) + + +def test_draw_pipeline(deploy_pipeline, draw_pipeline): + pipeline_file = Path(__file__).parent / "test_files" / "working_pipelines/test_pipeline_01.yml" + pipeline_data = {"name": pipeline_file.stem, "source_code": pipeline_file.read_text()} + + deploy_pipeline(client, pipeline_data["name"], pipeline_data["source_code"]) + + draw_response = draw_pipeline(client, pipeline_data["name"]) + assert draw_response.status_code == 200 + + assert draw_response.headers["Content-Type"] == "image/png" + assert len(draw_response.content) > 0 + + +def test_draw_non_existent_pipeline(draw_pipeline): + draw_response = draw_pipeline(client, "non_existent_pipeline") + assert draw_response.status_code == 404 diff --git a/tests/test_it_handling_deploy_exceptions.py b/tests/test_it_handling_deploy_exceptions.py index 9e9a8b6..46b2766 100644 --- a/tests/test_it_handling_deploy_exceptions.py +++ b/tests/test_it_handling_deploy_exceptions.py @@ -5,10 +5,10 @@ client = TestClient(app) -def test_gracefully_handle_deploy_exception(): +def test_gracefully_handle_deploy_exception(deploy_pipeline): pipeline_name = "broken_rag_pipeline" pipeline_def = (Path(__file__).parent / "test_files" / "broken_rag_pipeline.yml").read_text() - deploy_response = client.post("/deploy", json={"name": pipeline_name, "source_code": pipeline_def}) + deploy_response = deploy_pipeline(client, pipeline_name, pipeline_def) assert deploy_response.status_code == 500 assert "Couldn't deserialize component 'llm'" in deploy_response.json()["detail"] diff --git a/tests/test_it_status.py b/tests/test_it_status.py new file mode 100644 index 0000000..e34eda1 --- /dev/null +++ b/tests/test_it_status.py @@ -0,0 +1,42 @@ +import pytest +from fastapi.testclient import TestClient +from hayhooks.server import app +from hayhooks.server.pipelines import registry +from pathlib import Path + +client = TestClient(app) + +@pytest.fixture(autouse=True) +def clear_registry(): + registry.clear() + + +def test_status_all_pipelines(status_pipeline): + status_response = status_pipeline(client, "") + assert status_response.status_code == 200 + assert "pipelines" in status_response.json() + + +def test_status_single_pipeline(deploy_pipeline, status_pipeline): + pipeline_file = Path(__file__).parent / "test_files" / "working_pipelines/test_pipeline_01.yml" + pipeline_data = {"name": pipeline_file.stem, "source_code": pipeline_file.read_text()} + + deploy_response = deploy_pipeline(client, pipeline_data["name"], pipeline_data["source_code"]) + assert deploy_response.status_code == 200 + + status_response = status_pipeline(client, pipeline_data["name"]) + assert status_response.status_code == 200 + assert status_response.json()["pipeline"] == pipeline_data["name"] + + +def test_status_non_existent_pipeline(status_pipeline): + status_response = status_pipeline(client, "non_existent_pipeline") + assert status_response.status_code == 404 + assert status_response.json()["detail"] == f"Pipeline 'non_existent_pipeline' not found" + + +def test_status_no_pipelines(status_pipeline): + status_response = status_pipeline(client, "") + assert status_response.status_code == 200 + assert "pipelines" in status_response.json() + assert len(status_response.json()["pipelines"]) == 0