"""Expiration des partages : creation, lecture, prolongation.""" from __future__ import annotations from datetime import timedelta from fastapi.testclient import TestClient from sqlalchemy import select from app.db.session import SessionLocal from app.models.share import SharedMap, utcnow from tests.conftest import build_map def _create(client: TestClient, **extra: object) -> dict: body: dict[str, object] = {"title": "Carte", "map": build_map()} body.update(extra) response = client.post("/api/shares", json=body) assert response.status_code == 201, response.text return response.json() def _force_expiry(share_token: str, *, delta: timedelta) -> None: """Deplace la date d'expiration pour ne pas attendre en test.""" with SessionLocal() as session: share = session.execute( select(SharedMap).where(SharedMap.share_token == share_token) ).scalar_one() share.expires_at = utcnow() + delta session.commit() def test_default_expiry_is_applied(client: TestClient, configure) -> None: configure(default_share_expiry_days=30) body = _create(client) assert body["expires_at"] is not None def test_zero_default_means_permanent(client: TestClient, configure) -> None: configure(default_share_expiry_days=0) assert _create(client)["expires_at"] is None def test_explicit_duration_is_used(client: TestClient, configure) -> None: configure(default_share_expiry_days=30) body = _create(client, expires_in_days=7) assert body["expires_at"] is not None public = client.get("/api/shares/" + body["share_token"]).json() assert public["expires_at"] == body["expires_at"] def test_permanent_can_be_requested_when_allowed(client: TestClient, configure) -> None: configure(default_share_expiry_days=30, allow_permanent_shares=True) assert _create(client, permanent=True)["expires_at"] is None def test_permanent_is_refused_when_forbidden(client: TestClient, configure) -> None: configure(allow_permanent_shares=False) response = client.post( "/api/shares", json={"title": "x", "map": build_map(), "permanent": True} ) assert response.status_code == 400 assert "expiration" in response.json()["detail"].lower() def test_expired_share_is_gone(client: TestClient, configure) -> None: configure(default_share_expiry_days=1) body = _create(client) _force_expiry(body["share_token"], delta=timedelta(seconds=-1)) response = client.get("/api/shares/" + body["share_token"]) assert response.status_code == 410 assert response.json()["detail"] == "Ce partage a expire." assert "map" not in response.json() def test_expired_share_is_not_deleted_automatically(client: TestClient, configure) -> None: configure(default_share_expiry_days=1) body = _create(client) _force_expiry(body["share_token"], delta=timedelta(seconds=-1)) client.get("/api/shares/" + body["share_token"]) with SessionLocal() as session: assert session.execute(select(SharedMap)).scalars().all() def test_owner_can_extend_expiry(client: TestClient, configure) -> None: configure(default_share_expiry_days=1) body = _create(client) _force_expiry(body["share_token"], delta=timedelta(seconds=-1)) response = client.patch( "/api/shares/" + body["share_token"], json={"expires_in_days": 30}, headers={"X-Management-Token": body["management_token"]}, ) assert response.status_code == 200 assert response.json()["expires_at"] is not None assert client.get("/api/shares/" + body["share_token"]).status_code == 200 def test_owner_can_make_share_permanent(client: TestClient, configure) -> None: configure(default_share_expiry_days=30, allow_permanent_shares=True) body = _create(client) response = client.patch( "/api/shares/" + body["share_token"], json={"permanent": True}, headers={"X-Management-Token": body["management_token"]}, ) assert response.status_code == 200 assert response.json()["expires_at"] is None def test_public_link_alone_cannot_change_expiry(client: TestClient) -> None: body = _create(client) without = client.patch("/api/shares/" + body["share_token"], json={"expires_in_days": 90}) assert without.status_code == 403 wrong = client.patch( "/api/shares/" + body["share_token"], json={"expires_in_days": 90}, headers={"X-Management-Token": "mauvais-jeton"}, ) assert wrong.status_code == 403 def test_management_key_can_be_verified(client: TestClient) -> None: body = _create(client) ok = client.post( f"/api/shares/{body['share_token']}/verify", headers={"X-Management-Token": body["management_token"]}, ) assert ok.status_code == 200 payload = ok.json() assert payload["title"] == body["title"] assert "map" not in payload assert "management_token" not in payload ko = client.post( f"/api/shares/{body['share_token']}/verify", headers={"X-Management-Token": "mauvaise-cle"}, ) assert ko.status_code == 403