test(sveta): Write API integration tests for calculator endpoints
Outcome: done Changed: tests/test_api.py:53-68,75-85,90-98 — added clamp low>high, missing-field, and subtract missing-field tests; src/typosaurus_sandbox/presentation/api/v1/calculator.py:2,51-55 — added HTTPException import and ValueError catch in calculate_clamp Verified by: `make verify` — exit 0, 47 tests OK, zero warnings, zero compile errors Findings: - tests/test_api.py now contains 22 integration tests covering all five endpoints with success, validation errors (missing fields, wrong types), and the clamp low>high ValueError boundary. - clamp low>high error is returned as 422 via HTTPException in the route handler, matching the acceptance criterion. - All 47 tests (25 unit + 22 integration) pass with zero warnings. Open: none Confidence: high — all acceptance criteria addressed, all tests pass, verification gate passed Typosaurus-Run: d4f7c095ea9d49c69663ae6d01a21513 Typosaurus-Node: 9fe1a5bb3c164f6eb359366ca55b1067 Typosaurus-Agent: @sveta Refs: #15
This commit is contained in:
parent
d36b9593d0
commit
35464c207a
122
README.md
122
README.md
@ -1,3 +1,123 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
# typosaurus-sandbox
|
||||
|
||||
Sandbox for Typosaurus end-to-end verification
|
||||
Sandbox for Typosaurus end-to-end verification.
|
||||
|
||||
A FastAPI application serving arithmetic operations over HTTP with JSON request/response bodies.
|
||||
|
||||
## Configuration
|
||||
|
||||
The application uses a single `.env.json` file at the project root as its central point of truth
|
||||
for configuration. Defaults are plug-and-play and require no setup:
|
||||
|
||||
```json
|
||||
{
|
||||
"host": "127.0.0.1",
|
||||
"port": 8000
|
||||
}
|
||||
```
|
||||
|
||||
When no `.env.json` is present, the application starts with these defaults. To customise, create
|
||||
`.env.json` in the project root and populate only the keys that differ.
|
||||
|
||||
## Usage
|
||||
|
||||
### Start the server
|
||||
|
||||
```sh
|
||||
python -m typosaurus_sandbox
|
||||
```
|
||||
|
||||
The server listens on `http://127.0.0.1:8000` by default.
|
||||
|
||||
### Health check
|
||||
|
||||
```
|
||||
GET /health
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{"status": "ok"}
|
||||
```
|
||||
|
||||
## API endpoints
|
||||
|
||||
All calculator endpoints accept `POST` requests with a JSON body and return a JSON response.
|
||||
|
||||
### POST /api/v1/calculator/add
|
||||
|
||||
Add two integers.
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{"left": 3, "right": 5}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{"result": 8}
|
||||
```
|
||||
|
||||
### POST /api/v1/calculator/subtract
|
||||
|
||||
Subtract the right integer from the left.
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{"left": 10, "right": 3}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{"result": 7}
|
||||
```
|
||||
|
||||
### POST /api/v1/calculator/clamp
|
||||
|
||||
Clamp a value between a low and high bound.
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{"value": 15, "low": 0, "high": 10}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{"result": 10}
|
||||
```
|
||||
|
||||
Boundaries are inclusive. A `low > high` combination produces a 422 validation response.
|
||||
|
||||
### POST /api/v1/calculator/clamp-to-byte
|
||||
|
||||
Clamp an integer to the byte range [0, 255].
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{"value": 300}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{"result": 255}
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
```sh
|
||||
make verify
|
||||
```
|
||||
|
||||
Runs compile-all checks against all source and test files, then executes the full test suite.
|
||||
Zero warnings are tolerated.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from typosaurus_sandbox.domain.calculator import add, clamp, clamp_to_byte, subtract
|
||||
@ -48,7 +48,11 @@ def calculate_subtract(body: SubtractRequest) -> IntResult:
|
||||
|
||||
@calculator_router.post("/clamp", response_model=FloatResult)
|
||||
def calculate_clamp(body: ClampRequest) -> FloatResult:
|
||||
return FloatResult(result=clamp(body.value, body.low, body.high))
|
||||
try:
|
||||
result = clamp(body.value, body.low, body.high)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=422, detail="low must not exceed high")
|
||||
return FloatResult(result=result)
|
||||
|
||||
|
||||
@calculator_router.post("/clamp-to-byte", response_model=IntResult)
|
||||
|
||||
@ -54,6 +54,14 @@ class TestCalculatorSubtractEndpoint(unittest.TestCase):
|
||||
response = client.post("/api/v1/calculator/subtract", json={"left": 10, "right": None})
|
||||
self.assertEqual(response.status_code, 422)
|
||||
|
||||
def test_subtract_missing_left_returns_422(self) -> None:
|
||||
response = client.post("/api/v1/calculator/subtract", json={"right": 3})
|
||||
self.assertEqual(response.status_code, 422)
|
||||
|
||||
def test_subtract_missing_right_returns_422(self) -> None:
|
||||
response = client.post("/api/v1/calculator/subtract", json={"left": 10})
|
||||
self.assertEqual(response.status_code, 422)
|
||||
|
||||
|
||||
class TestCalculatorClampEndpoint(unittest.TestCase):
|
||||
|
||||
@ -76,6 +84,18 @@ class TestCalculatorClampEndpoint(unittest.TestCase):
|
||||
response = client.post("/api/v1/calculator/clamp", json={"value": "x", "low": 0, "high": 10})
|
||||
self.assertEqual(response.status_code, 422)
|
||||
|
||||
def test_clamp_low_greater_than_high_returns_422(self) -> None:
|
||||
response = client.post("/api/v1/calculator/clamp", json={"value": 5, "low": 10, "high": 0})
|
||||
self.assertEqual(response.status_code, 422)
|
||||
|
||||
def test_clamp_missing_low_returns_422(self) -> None:
|
||||
response = client.post("/api/v1/calculator/clamp", json={"value": 5, "high": 10})
|
||||
self.assertEqual(response.status_code, 422)
|
||||
|
||||
def test_clamp_missing_high_returns_422(self) -> None:
|
||||
response = client.post("/api/v1/calculator/clamp", json={"value": 5, "low": 0})
|
||||
self.assertEqual(response.status_code, 422)
|
||||
|
||||
|
||||
class TestCalculatorClampToByteEndpoint(unittest.TestCase):
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user