feat(backend): CRUD jardins + tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 21:20:02 +01:00
parent 26b5a3dbdf
commit aa379aa1b4
9 changed files with 188 additions and 0 deletions

View File

@@ -27,6 +27,17 @@ app.add_middleware(
allow_headers=["*"],
)
from app.routers import gardens, varieties, plantings, tasks, settings, media
app.include_router(gardens.router, prefix="/api")
app.include_router(varieties.router, prefix="/api")
app.include_router(plantings.router, prefix="/api")
app.include_router(tasks.router, prefix="/api")
app.include_router(settings.router, prefix="/api")
app.include_router(media.router, prefix="/api")
# Note: le mount StaticFiles sera ajouté ici dans Task 6
@app.get("/api/health")
def health():

View File

@@ -0,0 +1,82 @@
from datetime import datetime
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlmodel import Session, select
from app.database import get_session
from app.models.garden import Garden, GardenCell, GardenImage, Measurement
router = APIRouter(tags=["jardins"])
@router.get("/gardens", response_model=List[Garden])
def list_gardens(session: Session = Depends(get_session)):
return session.exec(select(Garden)).all()
@router.post("/gardens", response_model=Garden, status_code=status.HTTP_201_CREATED)
def create_garden(garden: Garden, session: Session = Depends(get_session)):
session.add(garden)
session.commit()
session.refresh(garden)
return garden
@router.get("/gardens/{id}", response_model=Garden)
def get_garden(id: int, session: Session = Depends(get_session)):
g = session.get(Garden, id)
if not g:
raise HTTPException(status_code=404, detail="Jardin introuvable")
return g
@router.put("/gardens/{id}", response_model=Garden)
def update_garden(id: int, data: Garden, session: Session = Depends(get_session)):
g = session.get(Garden, id)
if not g:
raise HTTPException(status_code=404, detail="Jardin introuvable")
for k, v in data.model_dump(exclude_unset=True, exclude={"id", "created_at"}).items():
setattr(g, k, v)
g.updated_at = datetime.utcnow()
session.add(g)
session.commit()
session.refresh(g)
return g
@router.delete("/gardens/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_garden(id: int, session: Session = Depends(get_session)):
g = session.get(Garden, id)
if not g:
raise HTTPException(status_code=404, detail="Jardin introuvable")
session.delete(g)
session.commit()
@router.get("/gardens/{id}/cells", response_model=List[GardenCell])
def list_cells(id: int, session: Session = Depends(get_session)):
return session.exec(select(GardenCell).where(GardenCell.garden_id == id)).all()
@router.post("/gardens/{id}/cells", response_model=GardenCell, status_code=status.HTTP_201_CREATED)
def create_cell(id: int, cell: GardenCell, session: Session = Depends(get_session)):
cell.garden_id = id
session.add(cell)
session.commit()
session.refresh(cell)
return cell
@router.get("/gardens/{id}/measurements", response_model=List[Measurement])
def list_measurements(id: int, session: Session = Depends(get_session)):
return session.exec(select(Measurement).where(Measurement.garden_id == id)).all()
@router.post("/gardens/{id}/measurements", response_model=Measurement, status_code=status.HTTP_201_CREATED)
def create_measurement(id: int, m: Measurement, session: Session = Depends(get_session)):
m.garden_id = id
session.add(m)
session.commit()
session.refresh(m)
return m

View File

@@ -0,0 +1,2 @@
from fastapi import APIRouter
router = APIRouter()

View File

@@ -0,0 +1,2 @@
from fastapi import APIRouter
router = APIRouter()

View File

@@ -0,0 +1,2 @@
from fastapi import APIRouter
router = APIRouter()

View File

@@ -0,0 +1,2 @@
from fastapi import APIRouter
router = APIRouter()

View File

@@ -0,0 +1,2 @@
from fastapi import APIRouter
router = APIRouter()

31
backend/tests/conftest.py Normal file
View File

@@ -0,0 +1,31 @@
import pytest
from fastapi.testclient import TestClient
from sqlmodel import SQLModel, create_engine, Session
from sqlmodel.pool import StaticPool
import app.models # noqa — force l'enregistrement des modèles
from app.main import app
from app.database import get_session
@pytest.fixture(name="session")
def session_fixture():
engine = create_engine(
"sqlite://",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
yield session
@pytest.fixture(name="client")
def client_fixture(session: Session):
def get_session_override():
yield session
app.dependency_overrides[get_session] = get_session_override
client = TestClient(app)
yield client
app.dependency_overrides.clear()

View File

@@ -0,0 +1,54 @@
def test_health(client):
r = client.get("/api/health")
assert r.status_code == 200
def test_create_garden(client):
r = client.post("/api/gardens", json={"nom": "Mon potager", "type": "plein_air"})
assert r.status_code == 201
data = r.json()
assert data["nom"] == "Mon potager"
assert data["id"] is not None
def test_list_gardens(client):
client.post("/api/gardens", json={"nom": "Jardin 1", "type": "plein_air"})
client.post("/api/gardens", json={"nom": "Jardin 2", "type": "serre"})
r = client.get("/api/gardens")
assert r.status_code == 200
assert len(r.json()) == 2
def test_get_garden(client):
r = client.post("/api/gardens", json={"nom": "Potager", "type": "plein_air"})
id = r.json()["id"]
r2 = client.get(f"/api/gardens/{id}")
assert r2.status_code == 200
assert r2.json()["nom"] == "Potager"
def test_update_garden(client):
r = client.post("/api/gardens", json={"nom": "Vieux nom", "type": "plein_air"})
id = r.json()["id"]
r2 = client.put(f"/api/gardens/{id}", json={"nom": "Nouveau nom", "type": "serre"})
assert r2.status_code == 200
assert r2.json()["nom"] == "Nouveau nom"
def test_delete_garden(client):
r = client.post("/api/gardens", json={"nom": "À supprimer", "type": "plein_air"})
id = r.json()["id"]
r2 = client.delete(f"/api/gardens/{id}")
assert r2.status_code == 204
r3 = client.get(f"/api/gardens/{id}")
assert r3.status_code == 404
def test_create_measurement(client):
r = client.post("/api/gardens", json={"nom": "Potager", "type": "plein_air"})
id = r.json()["id"]
r2 = client.post(
f"/api/gardens/{id}/measurements",
json={"temp_air": 22.5, "humidite_air": 60.0},
)
assert r2.status_code == 201