Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b4583f305 | |||
| fc8346fcda | |||
| 40d91bc52b | |||
| 2ac342240b |
41
openapi_first/templates/crud_app/data.py
Normal file
41
openapi_first/templates/crud_app/data.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
"""
|
||||||
|
In-memory mock data store for CRUD example.
|
||||||
|
|
||||||
|
This module intentionally avoids persistence and concurrency guarantees.
|
||||||
|
It is suitable for demos, tests, and scaffolding only.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
_items: Dict[int, dict] = {
|
||||||
|
1: {"id": 1, "name": "Apple", "price": 0.5},
|
||||||
|
2: {"id": 2, "name": "Banana", "price": 0.3},
|
||||||
|
}
|
||||||
|
|
||||||
|
_next_id = 3
|
||||||
|
|
||||||
|
|
||||||
|
def list_items():
|
||||||
|
return list(_items.values())
|
||||||
|
|
||||||
|
|
||||||
|
def get_item(item_id: int):
|
||||||
|
return _items[item_id]
|
||||||
|
|
||||||
|
|
||||||
|
def create_item(payload: dict):
|
||||||
|
global _next_id
|
||||||
|
item = {"id": _next_id, **payload}
|
||||||
|
_items[_next_id] = item
|
||||||
|
_next_id += 1
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def update_item(item_id: int, payload: dict):
|
||||||
|
item = {"id": item_id, **payload}
|
||||||
|
_items[item_id] = item
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def delete_item(item_id: int):
|
||||||
|
del _items[item_id]
|
||||||
8
openapi_first/templates/crud_app/main.py
Normal file
8
openapi_first/templates/crud_app/main.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from openapi_first.app import OpenAPIFirstApp
|
||||||
|
import routes
|
||||||
|
|
||||||
|
app = OpenAPIFirstApp(
|
||||||
|
openapi_path="openapi.yaml",
|
||||||
|
routes_module=routes,
|
||||||
|
title="CRUD Example Service",
|
||||||
|
)
|
||||||
115
openapi_first/templates/crud_app/openapi.yaml
Normal file
115
openapi_first/templates/crud_app/openapi.yaml
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: CRUD Example Service
|
||||||
|
version: "1.0.0"
|
||||||
|
description: Minimal OpenAPI-first CRUD service with in-memory mock data.
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/items:
|
||||||
|
get:
|
||||||
|
operationId: list_items
|
||||||
|
summary: List all items
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: List of items
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/Item"
|
||||||
|
|
||||||
|
post:
|
||||||
|
operationId: create_item
|
||||||
|
summary: Create a new item
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ItemCreate"
|
||||||
|
responses:
|
||||||
|
"201":
|
||||||
|
description: Item created
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Item"
|
||||||
|
|
||||||
|
/items/{item_id}:
|
||||||
|
get:
|
||||||
|
operationId: get_item
|
||||||
|
summary: Get item by ID
|
||||||
|
parameters:
|
||||||
|
- name: item_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Item found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Item"
|
||||||
|
|
||||||
|
put:
|
||||||
|
operationId: update_item
|
||||||
|
summary: Update an item
|
||||||
|
parameters:
|
||||||
|
- name: item_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ItemCreate"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Item updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Item"
|
||||||
|
|
||||||
|
delete:
|
||||||
|
operationId: delete_item
|
||||||
|
summary: Delete an item
|
||||||
|
parameters:
|
||||||
|
- name: item_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: Item deleted
|
||||||
|
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Item:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
price:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
|
required: [id, name, price]
|
||||||
|
|
||||||
|
ItemCreate:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
price:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
|
required: [name, price]
|
||||||
50
openapi_first/templates/crud_app/routes.py
Normal file
50
openapi_first/templates/crud_app/routes.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
CRUD route handlers bound via OpenAPI operationId.
|
||||||
|
|
||||||
|
These handlers explicitly control HTTP status codes to ensure
|
||||||
|
runtime behavior matches the OpenAPI contract.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fastapi import Response, HTTPException
|
||||||
|
|
||||||
|
from data import (
|
||||||
|
list_items as _list_items,
|
||||||
|
get_item as _get_item,
|
||||||
|
create_item as _create_item,
|
||||||
|
update_item as _update_item,
|
||||||
|
delete_item as _delete_item,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def list_items():
|
||||||
|
return _list_items()
|
||||||
|
|
||||||
|
|
||||||
|
def get_item(item_id: int):
|
||||||
|
try:
|
||||||
|
return _get_item(item_id)
|
||||||
|
except KeyError:
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
|
||||||
|
def create_item(payload: dict, response: Response):
|
||||||
|
item = _create_item(payload)
|
||||||
|
response.status_code = 201
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def update_item(item_id: int, payload: dict):
|
||||||
|
try:
|
||||||
|
return _update_item(item_id, payload)
|
||||||
|
except KeyError:
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
|
||||||
|
def delete_item(item_id: int, response: Response):
|
||||||
|
try:
|
||||||
|
_delete_item(item_id)
|
||||||
|
except KeyError:
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
response.status_code = 204
|
||||||
|
return None
|
||||||
97
openapi_first/templates/crud_app/test_crud_app.py
Normal file
97
openapi_first/templates/crud_app/test_crud_app.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
"""
|
||||||
|
End-to-end tests for the OpenAPI-first CRUD example app.
|
||||||
|
|
||||||
|
These tests validate that all CRUD operations behave correctly
|
||||||
|
against the in-memory mock data store.
|
||||||
|
|
||||||
|
The tests assume:
|
||||||
|
- OpenAPI-first route binding
|
||||||
|
- In-memory storage (no persistence guarantees)
|
||||||
|
- Deterministic behavior in a single process
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from main import app
|
||||||
|
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_items_initial():
|
||||||
|
"""Initial items should be present."""
|
||||||
|
response = client.get("/items")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
assert isinstance(data, list)
|
||||||
|
assert len(data) >= 2
|
||||||
|
|
||||||
|
ids = {item["id"] for item in data}
|
||||||
|
assert 1 in ids
|
||||||
|
assert 2 in ids
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_item():
|
||||||
|
"""Existing item should be retrievable by ID."""
|
||||||
|
response = client.get("/items/1")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
item = response.json()
|
||||||
|
assert item["id"] == 1
|
||||||
|
assert "name" in item
|
||||||
|
assert "price" in item
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_item():
|
||||||
|
"""Creating a new item should return the created entity."""
|
||||||
|
payload = {
|
||||||
|
"name": "Orange",
|
||||||
|
"price": 0.8,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = client.post("/items", json=payload)
|
||||||
|
assert response.status_code == 201
|
||||||
|
|
||||||
|
item = response.json()
|
||||||
|
assert "id" in item
|
||||||
|
assert item["name"] == payload["name"]
|
||||||
|
assert item["price"] == payload["price"]
|
||||||
|
|
||||||
|
# Verify it appears in list
|
||||||
|
list_response = client.get("/items")
|
||||||
|
ids = {i["id"] for i in list_response.json()}
|
||||||
|
assert item["id"] in ids
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_item():
|
||||||
|
"""Updating an item should replace its values."""
|
||||||
|
payload = {
|
||||||
|
"name": "Green Apple",
|
||||||
|
"price": 0.6,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = client.put("/items/1", json=payload)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
item = response.json()
|
||||||
|
assert item["id"] == 1
|
||||||
|
assert item["name"] == payload["name"]
|
||||||
|
assert item["price"] == payload["price"]
|
||||||
|
|
||||||
|
# Verify persisted update
|
||||||
|
get_response = client.get("/items/1")
|
||||||
|
updated = get_response.json()
|
||||||
|
assert updated["name"] == payload["name"]
|
||||||
|
assert updated["price"] == payload["price"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_item():
|
||||||
|
"""Deleting an item should remove it from the store."""
|
||||||
|
response = client.delete("/items/2")
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
# Verify deletion
|
||||||
|
list_response = client.get("/items")
|
||||||
|
ids = {item["id"] for item in list_response.json()}
|
||||||
|
assert 2 not in ids
|
||||||
44
openapi_first/templates/model_app/data.py
Normal file
44
openapi_first/templates/model_app/data.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
"""
|
||||||
|
In-memory data store using Pydantic models.
|
||||||
|
|
||||||
|
This module is NOT thread-safe and is intended for demos and scaffolds only.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from models import Item, ItemCreate
|
||||||
|
|
||||||
|
_items: Dict[int, Item] = {
|
||||||
|
1: Item(id=1, name="Apple", price=0.5),
|
||||||
|
2: Item(id=2, name="Banana", price=0.3),
|
||||||
|
}
|
||||||
|
|
||||||
|
_next_id = 3
|
||||||
|
|
||||||
|
|
||||||
|
def list_items() -> list[Item]:
|
||||||
|
return list(_items.values())
|
||||||
|
|
||||||
|
|
||||||
|
def get_item(item_id: int) -> Item:
|
||||||
|
return _items[item_id]
|
||||||
|
|
||||||
|
|
||||||
|
def create_item(payload: ItemCreate) -> Item:
|
||||||
|
global _next_id
|
||||||
|
item = Item(id=_next_id, **payload.model_dump())
|
||||||
|
_items[_next_id] = item
|
||||||
|
_next_id += 1
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def update_item(item_id: int, payload: ItemCreate) -> Item:
|
||||||
|
if item_id not in _items:
|
||||||
|
raise KeyError(item_id)
|
||||||
|
item = Item(id=item_id, **payload.model_dump())
|
||||||
|
_items[item_id] = item
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def delete_item(item_id: int) -> None:
|
||||||
|
del _items[item_id]
|
||||||
8
openapi_first/templates/model_app/main.py
Normal file
8
openapi_first/templates/model_app/main.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from openapi_first.app import OpenAPIFirstApp
|
||||||
|
import routes
|
||||||
|
|
||||||
|
app = OpenAPIFirstApp(
|
||||||
|
openapi_path="openapi.yaml",
|
||||||
|
routes_module=routes,
|
||||||
|
title="Model CRUD Example Service",
|
||||||
|
)
|
||||||
18
openapi_first/templates/model_app/models.py
Normal file
18
openapi_first/templates/model_app/models.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"""
|
||||||
|
Pydantic domain models for the CRUD example.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class ItemBase(BaseModel):
|
||||||
|
name: str
|
||||||
|
price: float
|
||||||
|
|
||||||
|
|
||||||
|
class ItemCreate(ItemBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Item(ItemBase):
|
||||||
|
id: int
|
||||||
116
openapi_first/templates/model_app/openapi.yaml
Normal file
116
openapi_first/templates/model_app/openapi.yaml
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: Model CRUD Example Service
|
||||||
|
version: "1.0.0"
|
||||||
|
description: OpenAPI-first CRUD service with Pydantic models.
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/items:
|
||||||
|
get:
|
||||||
|
operationId: list_items
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: List of items
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/Item"
|
||||||
|
|
||||||
|
post:
|
||||||
|
operationId: create_item
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ItemCreate"
|
||||||
|
responses:
|
||||||
|
"201":
|
||||||
|
description: Item created
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Item"
|
||||||
|
|
||||||
|
/items/{item_id}:
|
||||||
|
get:
|
||||||
|
operationId: get_item
|
||||||
|
parameters:
|
||||||
|
- name: item_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Item found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Item"
|
||||||
|
"404":
|
||||||
|
description: Item not found
|
||||||
|
|
||||||
|
put:
|
||||||
|
operationId: update_item
|
||||||
|
parameters:
|
||||||
|
- name: item_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ItemCreate"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Item updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Item"
|
||||||
|
"404":
|
||||||
|
description: Item not found
|
||||||
|
|
||||||
|
delete:
|
||||||
|
operationId: delete_item
|
||||||
|
parameters:
|
||||||
|
- name: item_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: Item deleted
|
||||||
|
"404":
|
||||||
|
description: Item not found
|
||||||
|
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Item:
|
||||||
|
type: object
|
||||||
|
required: [id, name, price]
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
price:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
|
|
||||||
|
ItemCreate:
|
||||||
|
type: object
|
||||||
|
required: [name, price]
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
price:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
48
openapi_first/templates/model_app/routes.py
Normal file
48
openapi_first/templates/model_app/routes.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
"""
|
||||||
|
CRUD route handlers bound via OpenAPI operationId.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fastapi import Response, HTTPException
|
||||||
|
|
||||||
|
from models import ItemCreate
|
||||||
|
from data import (
|
||||||
|
list_items as _list_items,
|
||||||
|
get_item as _get_item,
|
||||||
|
create_item as _create_item,
|
||||||
|
update_item as _update_item,
|
||||||
|
delete_item as _delete_item,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def list_items():
|
||||||
|
return _list_items()
|
||||||
|
|
||||||
|
|
||||||
|
def get_item(item_id: int):
|
||||||
|
try:
|
||||||
|
return _get_item(item_id)
|
||||||
|
except KeyError:
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
|
||||||
|
def create_item(payload: ItemCreate, response: Response):
|
||||||
|
item = _create_item(payload)
|
||||||
|
response.status_code = 201
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def update_item(item_id: int, payload: ItemCreate):
|
||||||
|
try:
|
||||||
|
return _update_item(item_id, payload)
|
||||||
|
except KeyError:
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
|
||||||
|
def delete_item(item_id: int, response: Response):
|
||||||
|
try:
|
||||||
|
_delete_item(item_id)
|
||||||
|
except KeyError:
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
response.status_code = 204
|
||||||
|
return None
|
||||||
98
openapi_first/templates/model_app/test_model_app.py
Normal file
98
openapi_first/templates/model_app/test_model_app.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
"""
|
||||||
|
End-to-end tests for the OpenAPI-first model CRUD example app.
|
||||||
|
|
||||||
|
These tests validate that all CRUD operations behave correctly
|
||||||
|
against the in-memory mock data store using Pydantic models.
|
||||||
|
|
||||||
|
The tests assume:
|
||||||
|
- OpenAPI-first route binding
|
||||||
|
- Pydantic model validation
|
||||||
|
- In-memory storage (no persistence guarantees)
|
||||||
|
- Deterministic behavior in a single process
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from main import app
|
||||||
|
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_items_initial():
|
||||||
|
"""Initial items should be present."""
|
||||||
|
response = client.get("/items")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
assert isinstance(data, list)
|
||||||
|
assert len(data) >= 2
|
||||||
|
|
||||||
|
ids = {item["id"] for item in data}
|
||||||
|
assert 1 in ids
|
||||||
|
assert 2 in ids
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_item():
|
||||||
|
"""Existing item should be retrievable by ID."""
|
||||||
|
response = client.get("/items/1")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
item = response.json()
|
||||||
|
assert item["id"] == 1
|
||||||
|
assert isinstance(item["name"], str)
|
||||||
|
assert isinstance(item["price"], float)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_item():
|
||||||
|
"""Creating a new item should return the created entity."""
|
||||||
|
payload = {
|
||||||
|
"name": "Orange",
|
||||||
|
"price": 0.8,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = client.post("/items", json=payload)
|
||||||
|
assert response.status_code == 201
|
||||||
|
|
||||||
|
item = response.json()
|
||||||
|
assert "id" in item
|
||||||
|
assert item["name"] == payload["name"]
|
||||||
|
assert item["price"] == payload["price"]
|
||||||
|
|
||||||
|
# Verify it appears in list
|
||||||
|
list_response = client.get("/items")
|
||||||
|
ids = {i["id"] for i in list_response.json()}
|
||||||
|
assert item["id"] in ids
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_item():
|
||||||
|
"""Updating an item should replace its values."""
|
||||||
|
payload = {
|
||||||
|
"name": "Green Apple",
|
||||||
|
"price": 0.6,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = client.put("/items/1", json=payload)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
item = response.json()
|
||||||
|
assert item["id"] == 1
|
||||||
|
assert item["name"] == payload["name"]
|
||||||
|
assert item["price"] == payload["price"]
|
||||||
|
|
||||||
|
# Verify persisted update
|
||||||
|
get_response = client.get("/items/1")
|
||||||
|
updated = get_response.json()
|
||||||
|
assert updated["name"] == payload["name"]
|
||||||
|
assert updated["price"] == payload["price"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_item():
|
||||||
|
"""Deleting an item should remove it from the store."""
|
||||||
|
response = client.delete("/items/2")
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
# Verify deletion
|
||||||
|
list_response = client.get("/items")
|
||||||
|
ids = {item["id"] for item in list_response.json()}
|
||||||
|
assert 2 not in ids
|
||||||
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "openapi-first"
|
name = "openapi-first"
|
||||||
version = "0.0.1"
|
version = "0.0.2"
|
||||||
description = "Strict OpenAPI-first application bootstrap for FastAPI."
|
description = "Strict OpenAPI-first application bootstrap for FastAPI."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ fastapi==0.128.0,
|
|||||||
openapi-spec-validator==0.7.2,
|
openapi-spec-validator==0.7.2,
|
||||||
pyyaml==6.0.3,
|
pyyaml==6.0.3,
|
||||||
uvicorn==0.40.0
|
uvicorn==0.40.0
|
||||||
|
pydantic==2.12.5
|
||||||
|
|
||||||
# Test Packages
|
# Test Packages
|
||||||
pytest==7.4.0
|
pytest==7.4.0
|
||||||
|
|||||||
Reference in New Issue
Block a user