"""Le backend doit accepter le format v4 : cotes de branche et relations.""" from __future__ import annotations from fastapi.testclient import TestClient from tests.conftest import build_map def build_v4_map() -> dict: payload = build_map(node_count=1, name="Infrastructure") payload["version"] = 4 payload["nodes"] = [ { "id": "root", "type": "root", "text": "Infrastructure", "position": {"x": 0, "y": 0}, "parentId": None, "order": 0, }, { "id": "reseau", "type": "standard", "text": "Reseau", "position": {"x": 240, "y": 0}, "parentId": "root", "order": 0, "side": "right", }, { "id": "firewall", "type": "standard", "text": "Firewall", "position": {"x": 460, "y": 0}, "parentId": "reseau", "order": 0, }, { "id": "services", "type": "standard", "text": "Services", "position": {"x": -240, "y": 0}, "parentId": "root", "order": 1, "side": "left", }, { "id": "web", "type": "standard", "text": "Web", "position": {"x": -460, "y": 0}, "parentId": "services", "order": 0, }, ] payload["edges"] = [ {"id": "e-root-reseau", "source": "root", "target": "reseau"}, {"id": "e-reseau-firewall", "source": "reseau", "target": "firewall"}, {"id": "e-root-services", "source": "root", "target": "services"}, {"id": "e-services-web", "source": "services", "target": "web"}, ] payload["relations"] = [ { "id": "r-1", "sourceId": "firewall", "targetId": "web", "label": "protege", "style": "bezier", "color": "#a855f7", "width": 2, "dashed": True, "markerEnd": "arrow", } ] payload["settings"]["layout"] = "mindmap-both" return payload def test_share_accepts_v4_map(client: TestClient) -> None: response = client.post("/api/shares", json={"title": "v4", "map": build_v4_map()}) assert response.status_code == 201, response.text def test_sides_and_relations_are_returned_unchanged(client: TestClient) -> None: created = client.post("/api/shares", json={"title": "v4", "map": build_v4_map()}) token = created.json()["share_token"] public = client.get("/api/shares/" + token).json() assert public["map_version"] == 4 assert public["map"]["settings"]["layout"] == "mindmap-both" sides = {node["id"]: node.get("side") for node in public["map"]["nodes"]} assert sides["reseau"] == "right" assert sides["services"] == "left" # Le cote n'est porte que par les branches de niveau 1. assert sides["firewall"] is None relations = public["map"]["relations"] assert len(relations) == 1 assert relations[0]["sourceId"] == "firewall" assert relations[0]["targetId"] == "web" assert relations[0]["label"] == "protege" assert relations[0]["dashed"] is True def test_relation_does_not_alter_hierarchy(client: TestClient) -> None: """Une relation ne doit jamais devenir un lien parent / enfant.""" created = client.post("/api/shares", json={"title": "v4", "map": build_v4_map()}) public = client.get("/api/shares/" + created.json()["share_token"]).json() parents = {node["id"]: node.get("parentId") for node in public["map"]["nodes"]} assert parents["firewall"] == "reseau" assert parents["web"] == "services" edge_pairs = {(edge["source"], edge["target"]) for edge in public["map"]["edges"]} assert ("firewall", "web") not in edge_pairs def test_self_relation_is_rejected(client: TestClient) -> None: payload = build_v4_map() payload["relations"][0]["targetId"] = payload["relations"][0]["sourceId"] response = client.post("/api/shares", json={"title": "x", "map": payload}) assert response.status_code == 422 def test_relation_without_endpoints_is_rejected(client: TestClient) -> None: payload = build_v4_map() del payload["relations"][0]["targetId"] response = client.post("/api/shares", json={"title": "x", "map": payload}) assert response.status_code == 422 def test_map_without_relations_is_still_accepted(client: TestClient) -> None: payload = build_v4_map() del payload["relations"] response = client.post("/api/shares", json={"title": "x", "map": payload}) assert response.status_code == 201, response.text def test_older_versions_are_still_accepted(client: TestClient) -> None: for version in (1, 2, 3): payload = build_map() payload["version"] = version response = client.post("/api/shares", json={"title": f"v{version}", "map": payload}) assert response.status_code == 201, response.text