Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 11 additions & 3 deletions src/example/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,16 @@ def create_app(settings: AppSettings | None = None) -> FastAPI:
note_repo, tag_repo, comment_repo, db_executor = _build_repositories(cfg)
app.state.db_executor = db_executor

# OpenAPI parity with NENE2 PHP: /examples/notes, /examples/tags (nene2-js client paths).
app.include_router(
make_note_router(
ListNotesUseCase(note_repo),
GetNoteUseCase(note_repo),
CreateNoteUseCase(note_repo),
UpdateNoteUseCase(note_repo),
DeleteNoteUseCase(note_repo),
)
),
prefix="/examples",
)

app.include_router(
Expand All @@ -182,7 +184,8 @@ def create_app(settings: AppSettings | None = None) -> FastAPI:
CreateTagUseCase(tag_repo),
UpdateTagUseCase(tag_repo),
DeleteTagUseCase(tag_repo),
)
),
prefix="/examples",
)

app.include_router(
Expand All @@ -192,9 +195,14 @@ def create_app(settings: AppSettings | None = None) -> FastAPI:
CreateCommentUseCase(comment_repo, note_repo),
UpdateCommentUseCase(comment_repo),
DeleteCommentUseCase(comment_repo),
)
),
prefix="/examples",
)

@app.get("/examples/ping", tags=["system"], summary="Example ping")
async def example_ping() -> JSONResponse:
return JSONResponse({"message": "pong", "status": "ok"})

db_health = DatabaseHealthCheck(db_executor) if db_executor else None

@app.get("/health", tags=["system"], summary="Health check")
Expand Down
53 changes: 28 additions & 25 deletions tests/example/comment/test_comment_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def _make_client() -> Generator[TestClient, None, None]:

def test_list_comments_empty() -> None:
with _make_client() as client:
note = client.post("/notes", json={"title": "Test Note", "body": "body"}).json()
response = client.get(f"/notes/{note['id']}/comments")
note = client.post("/examples/notes", json={"title": "Test Note", "body": "body"}).json()
response = client.get(f"/examples/notes/{note['id']}/comments")
assert response.status_code == 200
body = response.json()
assert body["total"] == 0
Expand All @@ -31,82 +31,85 @@ def test_list_comments_empty() -> None:

def test_create_and_list_comments() -> None:
with _make_client() as client:
note = client.post("/notes", json={"title": "Test Note", "body": "body"}).json()
note = client.post("/examples/notes", json={"title": "Test Note", "body": "body"}).json()
note_id = note["id"]
create_response = client.post(f"/notes/{note_id}/comments", json={"body": "first comment"})
comments = f"/examples/notes/{note_id}/comments"
create_response = client.post(comments, json={"body": "first comment"})
assert create_response.status_code == 201
data = create_response.json()
assert data["note_id"] == note_id
assert data["body"] == "first comment"

list_response = client.get(f"/notes/{note_id}/comments")
list_response = client.get(f"/examples/notes/{note_id}/comments")
assert list_response.json()["total"] == 1


def test_get_comment() -> None:
with _make_client() as client:
note = client.post("/notes", json={"title": "Test Note", "body": "body"}).json()
note = client.post("/examples/notes", json={"title": "Test Note", "body": "body"}).json()
note_id = note["id"]
created = client.post(f"/notes/{note_id}/comments", json={"body": "get me"}).json()
response = client.get(f"/notes/{note_id}/comments/{created['id']}")
created = client.post(f"/examples/notes/{note_id}/comments", json={"body": "get me"}).json()
response = client.get(f"/examples/notes/{note_id}/comments/{created['id']}")
assert response.status_code == 200
assert response.json()["body"] == "get me"


def test_get_comment_not_found() -> None:
with _make_client() as client:
note = client.post("/notes", json={"title": "Test Note", "body": "body"}).json()
response = client.get(f"/notes/{note['id']}/comments/9999")
note = client.post("/examples/notes", json={"title": "Test Note", "body": "body"}).json()
response = client.get(f"/examples/notes/{note['id']}/comments/9999")
assert response.status_code == 404


def test_update_comment() -> None:
with _make_client() as client:
note = client.post("/notes", json={"title": "Test Note", "body": "body"}).json()
note = client.post("/examples/notes", json={"title": "Test Note", "body": "body"}).json()
note_id = note["id"]
created = client.post(f"/notes/{note_id}/comments", json={"body": "original"}).json()
url = f"/notes/{note_id}/comments/{created['id']}"
comments = f"/examples/notes/{note_id}/comments"
created = client.post(comments, json={"body": "original"}).json()
url = f"/examples/notes/{note_id}/comments/{created['id']}"
response = client.put(url, json={"body": "updated"})
assert response.status_code == 200
assert response.json()["body"] == "updated"


def test_delete_comment() -> None:
with _make_client() as client:
note = client.post("/notes", json={"title": "Test Note", "body": "body"}).json()
note = client.post("/examples/notes", json={"title": "Test Note", "body": "body"}).json()
note_id = note["id"]
created = client.post(f"/notes/{note_id}/comments", json={"body": "to delete"}).json()
delete_response = client.delete(f"/notes/{note_id}/comments/{created['id']}")
comments = f"/examples/notes/{note_id}/comments"
created = client.post(comments, json={"body": "to delete"}).json()
delete_response = client.delete(f"{comments}/{created['id']}")
assert delete_response.status_code == 204
get_response = client.get(f"/notes/{note_id}/comments/{created['id']}")
get_response = client.get(f"/examples/notes/{note_id}/comments/{created['id']}")
assert get_response.status_code == 404


def test_create_comment_empty_body_returns_422() -> None:
with _make_client() as client:
note = client.post("/notes", json={"title": "Test Note", "body": "body"}).json()
response = client.post(f"/notes/{note['id']}/comments", json={"body": " "})
note = client.post("/examples/notes", json={"title": "Test Note", "body": "body"}).json()
response = client.post(f"/examples/notes/{note['id']}/comments", json={"body": " "})
assert response.status_code == 422


def test_create_comment_for_nonexistent_note_returns_404() -> None:
with _make_client() as client:
response = client.post("/notes/9999/comments", json={"body": "orphan"})
response = client.post("/examples/notes/9999/comments", json={"body": "orphan"})
assert response.status_code == 404


def test_get_comment_from_wrong_note_returns_404() -> None:
with _make_client() as client:
note1 = client.post("/notes", json={"title": "Note 1", "body": "b"}).json()
note2 = client.post("/notes", json={"title": "Note 2", "body": "b"}).json()
note1 = client.post("/examples/notes", json={"title": "Note 1", "body": "b"}).json()
note2 = client.post("/examples/notes", json={"title": "Note 2", "body": "b"}).json()
comment = client.post(
f"/notes/{note1['id']}/comments", json={"body": "belongs to note1"}
f"/examples/notes/{note1['id']}/comments", json={"body": "belongs to note1"}
).json()
response = client.get(f"/notes/{note2['id']}/comments/{comment['id']}")
response = client.get(f"/examples/notes/{note2['id']}/comments/{comment['id']}")
assert response.status_code == 404


def test_list_comments_for_nonexistent_note_returns_404() -> None:
with _make_client() as client:
response = client.get("/notes/9999/comments")
response = client.get("/examples/notes/9999/comments")
assert response.status_code == 404
28 changes: 14 additions & 14 deletions tests/example/note/test_list_notes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


def test_list_notes_empty(client: TestClient) -> None:
r = client.get("/notes")
r = client.get("/examples/notes")
assert r.status_code == 200
body = r.json()
assert body["items"] == []
Expand All @@ -15,61 +15,61 @@ def test_list_notes_empty(client: TestClient) -> None:


def test_create_and_get_note(client: TestClient) -> None:
r = client.post("/notes", json={"title": "Hello", "body": "World"})
r = client.post("/examples/notes", json={"title": "Hello", "body": "World"})
assert r.status_code == 201
note_id = r.json()["id"]

r2 = client.get(f"/notes/{note_id}")
r2 = client.get(f"/examples/notes/{note_id}")
assert r2.status_code == 200
assert r2.json()["title"] == "Hello"


def test_create_note_empty_title_returns_422(client: TestClient) -> None:
r = client.post("/notes", json={"title": "", "body": "b"})
r = client.post("/examples/notes", json={"title": "", "body": "b"})
assert r.status_code == 422
assert r.json()["errors"][0]["field"] == "title"


def test_get_nonexistent_note_returns_404(client: TestClient) -> None:
r = client.get("/notes/9999")
r = client.get("/examples/notes/9999")
assert r.status_code == 404


def test_update_note_returns_200(client: TestClient) -> None:
r = client.post("/notes", json={"title": "Old", "body": "Old body"})
r = client.post("/examples/notes", json={"title": "Old", "body": "Old body"})
note_id = r.json()["id"]

r2 = client.put(f"/notes/{note_id}", json={"title": "New", "body": "New body"})
r2 = client.put(f"/examples/notes/{note_id}", json={"title": "New", "body": "New body"})
assert r2.status_code == 200
assert r2.json()["title"] == "New"
assert r2.json()["body"] == "New body"


def test_update_nonexistent_note_returns_404(client: TestClient) -> None:
r = client.put("/notes/9999", json={"title": "T", "body": "B"})
r = client.put("/examples/notes/9999", json={"title": "T", "body": "B"})
assert r.status_code == 404


def test_update_note_empty_title_returns_422(client: TestClient) -> None:
r = client.post("/notes", json={"title": "T", "body": "B"})
r = client.post("/examples/notes", json={"title": "T", "body": "B"})
note_id = r.json()["id"]
r2 = client.put(f"/notes/{note_id}", json={"title": "", "body": "B"})
r2 = client.put(f"/examples/notes/{note_id}", json={"title": "", "body": "B"})
assert r2.status_code == 422


def test_delete_note_returns_204(client: TestClient) -> None:
r = client.post("/notes", json={"title": "T", "body": "B"})
r = client.post("/examples/notes", json={"title": "T", "body": "B"})
note_id = r.json()["id"]

r2 = client.delete(f"/notes/{note_id}")
r2 = client.delete(f"/examples/notes/{note_id}")
assert r2.status_code == 204

r3 = client.get(f"/notes/{note_id}")
r3 = client.get(f"/examples/notes/{note_id}")
assert r3.status_code == 404


def test_delete_nonexistent_note_returns_404(client: TestClient) -> None:
r = client.delete("/notes/9999")
r = client.delete("/examples/notes/9999")
assert r.status_code == 404


Expand Down
32 changes: 16 additions & 16 deletions tests/example/tag/test_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,76 +4,76 @@


def test_list_tags_empty(client: TestClient) -> None:
r = client.get("/tags")
r = client.get("/examples/tags")
assert r.status_code == 200
body = r.json()
assert body["items"] == []
assert body["total"] == 0


def test_create_and_get_tag(client: TestClient) -> None:
r = client.post("/tags", json={"name": "python"})
r = client.post("/examples/tags", json={"name": "python"})
assert r.status_code == 201
tag_id = r.json()["id"]

r2 = client.get(f"/tags/{tag_id}")
r2 = client.get(f"/examples/tags/{tag_id}")
assert r2.status_code == 200
assert r2.json()["name"] == "python"


def test_create_tag_empty_name_returns_422(client: TestClient) -> None:
r = client.post("/tags", json={"name": ""})
r = client.post("/examples/tags", json={"name": ""})
assert r.status_code == 422
assert r.json()["errors"][0]["field"] == "name"


def test_get_nonexistent_tag_returns_404(client: TestClient) -> None:
r = client.get("/tags/9999")
r = client.get("/examples/tags/9999")
assert r.status_code == 404


def test_update_tag_returns_200(client: TestClient) -> None:
r = client.post("/tags", json={"name": "old"})
r = client.post("/examples/tags", json={"name": "old"})
tag_id = r.json()["id"]

r2 = client.put(f"/tags/{tag_id}", json={"name": "new"})
r2 = client.put(f"/examples/tags/{tag_id}", json={"name": "new"})
assert r2.status_code == 200
assert r2.json()["name"] == "new"


def test_update_nonexistent_tag_returns_404(client: TestClient) -> None:
r = client.put("/tags/9999", json={"name": "x"})
r = client.put("/examples/tags/9999", json={"name": "x"})
assert r.status_code == 404


def test_update_tag_empty_name_returns_422(client: TestClient) -> None:
r = client.post("/tags", json={"name": "t"})
r = client.post("/examples/tags", json={"name": "t"})
tag_id = r.json()["id"]
r2 = client.put(f"/tags/{tag_id}", json={"name": ""})
r2 = client.put(f"/examples/tags/{tag_id}", json={"name": ""})
assert r2.status_code == 422


def test_delete_tag_returns_204(client: TestClient) -> None:
r = client.post("/tags", json={"name": "temp"})
r = client.post("/examples/tags", json={"name": "temp"})
tag_id = r.json()["id"]

r2 = client.delete(f"/tags/{tag_id}")
r2 = client.delete(f"/examples/tags/{tag_id}")
assert r2.status_code == 204

r3 = client.get(f"/tags/{tag_id}")
r3 = client.get(f"/examples/tags/{tag_id}")
assert r3.status_code == 404


def test_delete_nonexistent_tag_returns_404(client: TestClient) -> None:
r = client.delete("/tags/9999")
r = client.delete("/examples/tags/9999")
assert r.status_code == 404


def test_list_tags_pagination(client: TestClient) -> None:
for name in ["a", "b", "c"]:
client.post("/tags", json={"name": name})
client.post("/examples/tags", json={"name": name})

r = client.get("/tags?limit=2&offset=0")
r = client.get("/examples/tags?limit=2&offset=0")
assert r.status_code == 200
body = r.json()
assert len(body["items"]) == 2
Expand Down
3 changes: 2 additions & 1 deletion tests/scripts/test_export_openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ def test_main_yaml_is_valid_openapi(

content = yaml.safe_load((tmp_path / "docs" / "openapi.yaml").read_text())
assert content["openapi"].startswith("3.")
assert "/notes" in content["paths"]
assert "/examples/notes" in content["paths"]
assert "/examples/ping" in content["paths"]


def test_main_prints_output_path(
Expand Down
Loading