From 3c2ac03e0732e46824d088c1b291d47edcba259c Mon Sep 17 00:00:00 2001 From: FORGE Date: Fri, 10 Apr 2026 19:26:28 +0000 Subject: [PATCH 1/3] feat: Set Up API Server Entry Point Run: a491d202-f3ca-478c-8c69-a99ca22d4447 Task: 65c68bcc-5ef3-4337-845c-74b5d49dd138 Agent: builder --- Dockerfile | 12 +++++++++ RUNNING.md | 60 ++++++++++++++++++++++++++++++++++++--------- app/__init__.py | 0 app/main.py | 20 +++++++++++++++ docker-compose.yml | 9 +++++++ requirements.txt | 10 +++----- tests/test_hello.py | 27 ++++++++++++++++++++ 7 files changed, 121 insertions(+), 17 deletions(-) create mode 100644 Dockerfile create mode 100644 app/__init__.py create mode 100644 app/main.py create mode 100644 docker-compose.yml create mode 100644 tests/test_hello.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..527264e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.11-slim + +WORKDIR /code + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 8000 + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/RUNNING.md b/RUNNING.md index 77896cf..74117c2 100644 --- a/RUNNING.md +++ b/RUNNING.md @@ -1,33 +1,71 @@ -# Running the Todo API +# Running the Application + +## TEAM_BRIEF +stack: Python/FastAPI +test_runner: pytest tests/ +lint_tool: ruff check . +coverage_tool: pytest-cov +coverage_threshold: 70 +coverage_applies: true ## Prerequisites -- Python 3.10 or later +- Docker and Docker Compose installed +- (Optional) Python 3.11+ for running locally without Docker + +## Running with Docker -## Install dependencies +### Start the server ```bash -pip install fastapi uvicorn pydantic +docker compose up --build ``` -For running the test suite you will also need: +The API will be available at `http://localhost:8000`. + +### Verify the endpoint ```bash -pip install httpx pytest +curl http://localhost:8000/hello ``` -## Start the server +Expected response: + +```json +{"message": "hello"} +``` + +### Stop the server ```bash -uvicorn main:app --reload --host 0.0.0.0 --port 8000 +docker compose down ``` -The API will be available at . +## Running Tests + +### With Docker -Interactive docs are served at . +```bash +docker compose run --rm api pytest tests/ +``` -## Run the tests +### Locally (without Docker) ```bash +pip install -r requirements.txt pytest tests/ ``` + +## Running with Coverage + +```bash +pip install pytest-cov +pytest tests/ --cov=app --cov-report=term-missing +``` + +## Local Development (without Docker) + +```bash +pip install -r requirements.txt +uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload +``` diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..40cd178 --- /dev/null +++ b/app/main.py @@ -0,0 +1,20 @@ +"""FastAPI application entry point. + +Initialises the FastAPI app and defines the GET /hello endpoint. +""" + +from __future__ import annotations + +from fastapi import FastAPI + +app = FastAPI( + title="Hello API", + description="A minimal API server with a /hello endpoint.", + version="0.1.0", +) + + +@app.get("/hello", tags=["hello"]) +async def hello() -> dict: + """Return a simple JSON greeting.""" + return {"message": "hello"} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..977b558 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3.9" + +services: + api: + build: . + ports: + - "8000:8000" + environment: + - PYTHONUNBUFFERED=1 diff --git a/requirements.txt b/requirements.txt index 5a6ebf7..ecbcde8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,4 @@ -fastapi>=0.100.0 -uvicorn>=0.23.0 -pydantic>=2.0.0 -pytest>=7.0.0 -pytest-timeout>=2.1.0 -httpx>=0.24.0 +fastapi>=0.110.0,<1.0.0 +uvicorn>=0.29.0,<1.0.0 +pytest>=8.0.0,<9.0.0 +httpx>=0.27.0,<1.0.0 diff --git a/tests/test_hello.py b/tests/test_hello.py new file mode 100644 index 0000000..efab44a --- /dev/null +++ b/tests/test_hello.py @@ -0,0 +1,27 @@ +"""Tests for the GET /hello endpoint.""" + +from __future__ import annotations + +from fastapi.testclient import TestClient + +from app.main import app + +client = TestClient(app) + + +def test_get_hello_returns_200() -> None: + """GET /hello should return HTTP 200.""" + response = client.get("/hello") + assert response.status_code == 200 + + +def test_get_hello_returns_correct_json() -> None: + """GET /hello should return JSON body {'message': 'hello'}.""" + response = client.get("/hello") + assert response.json() == {"message": "hello"} + + +def test_get_hello_content_type() -> None: + """GET /hello should return application/json content type.""" + response = client.get("/hello") + assert "application/json" in response.headers["content-type"] From b93dfe10f0ead19757bf66f2272a979c2f79365e Mon Sep 17 00:00:00 2001 From: FORGE Date: Fri, 10 Apr 2026 19:26:58 +0000 Subject: [PATCH 2/3] feat: Implement /hello GET Endpoint Run: a491d202-f3ca-478c-8c69-a99ca22d4447 Task: 1682b787-be98-4e2e-91f8-47e19c4f6cdd Agent: builder --- Dockerfile | 2 +- RUNNING.md | 34 +++++++++++++--------------------- conftest.py | 5 +++++ docker-compose.yml | 6 ++++-- requirements.txt | 2 +- tests/test_hello.py | 18 +++++++++++++++--- 6 files changed, 39 insertions(+), 28 deletions(-) diff --git a/Dockerfile b/Dockerfile index 527264e..94825db 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-slim -WORKDIR /code +WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt diff --git a/RUNNING.md b/RUNNING.md index 74117c2..89f2c19 100644 --- a/RUNNING.md +++ b/RUNNING.md @@ -1,4 +1,4 @@ -# Running the Application +# Running the Hello API ## TEAM_BRIEF stack: Python/FastAPI @@ -10,20 +10,19 @@ coverage_applies: true ## Prerequisites -- Docker and Docker Compose installed -- (Optional) Python 3.11+ for running locally without Docker +- Docker and Docker Compose installed on your machine. -## Running with Docker +## Start the Server -### Start the server +Build and start the application container: ```bash docker compose up --build ``` -The API will be available at `http://localhost:8000`. +The API will be available at **http://localhost:8000**. -### Verify the endpoint +Verify it is running: ```bash curl http://localhost:8000/hello @@ -35,37 +34,30 @@ Expected response: {"message": "hello"} ``` -### Stop the server +## Stop the Server ```bash docker compose down ``` -## Running Tests +## Run Tests -### With Docker +Run the test suite inside the container: ```bash -docker compose run --rm api pytest tests/ +docker compose run --rm app pytest tests/ ``` -### Locally (without Docker) +Or, if you have Python 3.11+ and the dependencies installed locally: ```bash pip install -r requirements.txt pytest tests/ ``` -## Running with Coverage - -```bash -pip install pytest-cov -pytest tests/ --cov=app --cov-report=term-missing -``` - -## Local Development (without Docker) +## Run Without Docker ```bash pip install -r requirements.txt -uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload +uvicorn app.main:app --host 0.0.0.0 --port 8000 ``` diff --git a/conftest.py b/conftest.py index 2cb6af4..e21aa40 100644 --- a/conftest.py +++ b/conftest.py @@ -7,6 +7,11 @@ import sys from pathlib import Path +# Ensure the project root is on sys.path so that 'app' package is importable. +_PROJECT_ROOT = str(Path(__file__).resolve().parent) +if _PROJECT_ROOT not in sys.path: + sys.path.insert(0, _PROJECT_ROOT) + def pytest_addoption(parser): """Register --timeout so pytest doesn't choke when the plugin is absent.""" diff --git a/docker-compose.yml b/docker-compose.yml index 977b558..4ae249c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,10 @@ version: "3.9" services: - api: - build: . + app: + build: + context: . + dockerfile: Dockerfile ports: - "8000:8000" environment: diff --git a/requirements.txt b/requirements.txt index ecbcde8..fd9da7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ fastapi>=0.110.0,<1.0.0 uvicorn>=0.29.0,<1.0.0 -pytest>=8.0.0,<9.0.0 httpx>=0.27.0,<1.0.0 +pytest>=8.0.0,<9.0.0 diff --git a/tests/test_hello.py b/tests/test_hello.py index efab44a..5c87eef 100644 --- a/tests/test_hello.py +++ b/tests/test_hello.py @@ -9,14 +9,14 @@ client = TestClient(app) -def test_get_hello_returns_200() -> None: +def test_get_hello_status_code() -> None: """GET /hello should return HTTP 200.""" response = client.get("/hello") assert response.status_code == 200 -def test_get_hello_returns_correct_json() -> None: - """GET /hello should return JSON body {'message': 'hello'}.""" +def test_get_hello_json_body() -> None: + """GET /hello should return JSON {'message': 'hello'}.""" response = client.get("/hello") assert response.json() == {"message": "hello"} @@ -25,3 +25,15 @@ def test_get_hello_content_type() -> None: """GET /hello should return application/json content type.""" response = client.get("/hello") assert "application/json" in response.headers["content-type"] + + +def test_post_hello_not_allowed() -> None: + """POST /hello should return 405 Method Not Allowed.""" + response = client.post("/hello") + assert response.status_code == 405 + + +def test_nonexistent_route_returns_404() -> None: + """Requesting a non-existent path should return 404.""" + response = client.get("/nonexistent") + assert response.status_code == 404 From 388e8e6519c03a21a46993c74f5bb1e82367b236 Mon Sep 17 00:00:00 2001 From: FORGE Date: Fri, 10 Apr 2026 19:27:33 +0000 Subject: [PATCH 3/3] feat: Write Complete Test Suite Run: a491d202-f3ca-478c-8c69-a99ca22d4447 Task: f840cd06-8c8c-4236-b295-e31c84ecf3a6 Agent: builder --- RUNNING.md | 66 ++++++++++++++++++++++++++++++++++----------- docker-compose.yml | 9 ++++--- requirements.txt | 2 +- tests/test_hello.py | 11 +++++--- 4 files changed, 63 insertions(+), 25 deletions(-) diff --git a/RUNNING.md b/RUNNING.md index 89f2c19..69b9fa8 100644 --- a/RUNNING.md +++ b/RUNNING.md @@ -10,19 +10,22 @@ coverage_applies: true ## Prerequisites -- Docker and Docker Compose installed on your machine. +- Docker and Docker Compose installed, **or** +- Python 3.11+ installed locally -## Start the Server +--- -Build and start the application container: +## Docker-based Setup (Recommended) + +### Build and Start the Server ```bash -docker compose up --build +docker-compose up --build ``` -The API will be available at **http://localhost:8000**. +The API will be available at `http://localhost:8000`. -Verify it is running: +### Verify the /hello Endpoint ```bash curl http://localhost:8000/hello @@ -34,30 +37,61 @@ Expected response: {"message": "hello"} ``` -## Stop the Server +### Run Tests inside Docker ```bash -docker compose down +docker-compose run --rm api pytest tests/ -v ``` -## Run Tests - -Run the test suite inside the container: +### Stop the Server ```bash -docker compose run --rm app pytest tests/ +docker-compose down ``` -Or, if you have Python 3.11+ and the dependencies installed locally: +--- + +## Local Setup (without Docker) + +### Install Dependencies ```bash pip install -r requirements.txt -pytest tests/ ``` -## Run Without Docker +### Start the Server ```bash -pip install -r requirements.txt uvicorn app.main:app --host 0.0.0.0 --port 8000 ``` + +### Run Tests + +```bash +pytest tests/ -v +``` + +--- + +## Project Structure + +``` +. +├── app/ +│ ├── __init__.py +│ └── main.py # FastAPI application with GET /hello +├── tests/ +│ ├── __init__.py +│ └── test_hello.py # Pytest tests for the /hello endpoint +├── conftest.py # Root pytest configuration +├── requirements.txt # Python dependencies +├── Dockerfile # Container image definition +├── docker-compose.yml # Docker Compose orchestration +└── RUNNING.md # This file +``` + +## API Endpoints + +| Method | Path | Description | Response | +|--------|----------|-----------------------------------|-----------------------| +| GET | `/hello` | Returns a JSON greeting message | `{"message": "hello"}` | diff --git a/docker-compose.yml b/docker-compose.yml index 4ae249c..6011f7d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,12 @@ version: "3.9" services: - app: - build: - context: . - dockerfile: Dockerfile + api: + build: . ports: - "8000:8000" + volumes: + - .:/app environment: - PYTHONUNBUFFERED=1 + command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload diff --git a/requirements.txt b/requirements.txt index fd9da7a..ecbcde8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ fastapi>=0.110.0,<1.0.0 uvicorn>=0.29.0,<1.0.0 -httpx>=0.27.0,<1.0.0 pytest>=8.0.0,<9.0.0 +httpx>=0.27.0,<1.0.0 diff --git a/tests/test_hello.py b/tests/test_hello.py index 5c87eef..9e89c38 100644 --- a/tests/test_hello.py +++ b/tests/test_hello.py @@ -1,4 +1,7 @@ -"""Tests for the GET /hello endpoint.""" +"""Tests for the GET /hello endpoint. + +Verifies that the endpoint returns HTTP 200 and the expected JSON body. +""" from __future__ import annotations @@ -28,12 +31,12 @@ def test_get_hello_content_type() -> None: def test_post_hello_not_allowed() -> None: - """POST /hello should return 405 Method Not Allowed.""" + """POST /hello should return HTTP 405 Method Not Allowed.""" response = client.post("/hello") assert response.status_code == 405 -def test_nonexistent_route_returns_404() -> None: - """Requesting a non-existent path should return 404.""" +def test_nonexistent_endpoint_returns_404() -> None: + """Requesting a non-existent path should return HTTP 404.""" response = client.get("/nonexistent") assert response.status_code == 404