M8-T12: scope energy contracts

This commit is contained in:
2026-08-23 21:22:06 +02:00
parent a9458394f2
commit b812d5ac46
13 changed files with 533 additions and 47 deletions
+67
View File
@@ -55,6 +55,7 @@ from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session
from app.models.energy import EnergyContract, EnergyContractVersion
from app.services.contracts import activate_contract
# ---------------------------------------------------------------------------
# Shared helpers
@@ -567,6 +568,72 @@ def test_deactivate_contract(contracts_client):
assert resp.json()["active"] is False
def test_scope_defaults_filtering_and_kind_mismatch(contracts_client):
"""Old clients default to electricity; a supplied incompatible scope is rejected."""
client, engine = contracts_client
_login(client)
created = client.post(
"/api/energy/contracts",
json=_manual_payload(),
headers={"X-CSRF-Token": _CSRF},
)
assert created.status_code == 201
assert created.json()["scope"] == "electricity"
assert client.get("/api/energy/contracts").json()["total"] == 1
assert client.get("/api/energy/contracts?scope=thermal").json()["items"] == []
mismatch = client.post(
"/api/energy/contracts",
json=_manual_payload(scope="thermal"),
headers={"X-CSRF-Token": _CSRF},
)
assert mismatch.status_code == 422
with Session(engine) as session:
assert len(session.execute(select(EnergyContract)).scalars().all()) == 1
def test_activation_is_scope_local_and_transaction_rollback_is_safe(contracts_client):
"""A thermal activation neither deactivates electricity nor survives rollback."""
client, engine = contracts_client
_login(client)
electricity = client.post(
"/api/energy/contracts",
json=_manual_payload(name="Electricity"),
headers={"X-CSRF-Token": _CSRF},
).json()
client.patch(
f"/api/energy/contracts/{electricity['id']}",
json={"active": True},
headers={"X-CSRF-Token": _CSRF},
)
now = datetime.now(UTC)
with Session(engine) as session:
thermal = EnergyContract(
name="Future thermal", kind="district_heating", scope="thermal", active=False,
currency="EUR", created_at=now, updated_at=now,
)
session.add(thermal)
session.commit()
thermal_id = thermal.id
with Session(engine) as session:
thermal = session.get(EnergyContract, thermal_id)
assert thermal is not None
activate_contract(session, thermal)
session.rollback() # Simulate a later write failure in this transaction.
with Session(engine) as session:
rows = {row.scope: row for row in session.execute(select(EnergyContract)).scalars()}
assert rows["electricity"].active is True
assert rows["thermal"].active is False
activate_contract(session, rows["thermal"])
session.commit()
with Session(engine) as session:
active = session.execute(select(EnergyContract).where(EnergyContract.active.is_(True))).scalars().all()
assert {row.scope for row in active} == {"electricity", "thermal"}
# ---------------------------------------------------------------------------
# POST /api/energy/contracts/{id}/versions
# ---------------------------------------------------------------------------