"""Le backend doit accepter la mise en page v2 sans relacher sa validation.""" from __future__ import annotations import os from fastapi.testclient import TestClient from app.config import get_settings from tests.conftest import build_map RICH_NODE = { "id": "styled", "type": "standard", "text": "Firewall", "position": {"x": 120, "y": 40}, "parentId": "root", "icon": "server", "iconStyle": {"kind": "builtin", "position": "top", "size": 24, "gap": 12}, "style": { "backgroundColor": "#112233", "textAlign": "center", "verticalAlign": "middle", "lineHeight": 1.6, "fontFamily": "mono", "shadow": "strong", }, "layout": { "widthMode": "fixed", "width": 320, "heightMode": "fixed", "height": 140, "paddingX": 28, "paddingY": 18, "wrap": False, "maxTextWidth": 400, }, "branch": {"style": "straight", "width": 4, "color": "#ff8800"}, } def build_v2_map() -> dict: payload = build_map(node_count=2, name="Infrastructure") payload["version"] = 2 payload["nodes"].append(RICH_NODE) payload["edges"].append({"id": "e-root-styled", "source": "root", "target": "styled"}) payload["settings"]["layoutSpacing"] = {"parentGap": 140, "siblingGap": 80} payload["settings"]["snapToGrid"] = True payload["settings"]["snapGrid"] = 10 payload["settings"]["alignmentGuides"] = False return payload def test_share_accepts_layout_properties(client: TestClient) -> None: response = client.post("/api/shares", json={"title": "v2", "map": build_v2_map()}) assert response.status_code == 201, response.text def test_shared_map_is_returned_unchanged(client: TestClient) -> None: created = client.post("/api/shares", json={"title": "v2", "map": build_v2_map()}) token = created.json()["share_token"] public = client.get("/api/shares/" + token).json() assert public["map_version"] == 2 node = next(item for item in public["map"]["nodes"] if item["id"] == "styled") assert node["layout"] == RICH_NODE["layout"] assert node["style"] == RICH_NODE["style"] assert node["iconStyle"] == RICH_NODE["iconStyle"] assert node["branch"] == RICH_NODE["branch"] assert node["icon"] == "server" assert node["position"] == {"x": 120, "y": 40} settings = public["map"]["settings"] assert settings["layoutSpacing"] == {"parentGap": 140, "siblingGap": 80} assert settings["snapToGrid"] is True assert settings["alignmentGuides"] is False def test_version_1_maps_are_still_accepted(client: TestClient) -> None: response = client.post("/api/shares", json={"title": "v1", "map": build_map()}) assert response.status_code == 201 assert response.json()["share_token"] def test_node_limit_still_applies_to_v2_maps(client: TestClient) -> None: os.environ["MAX_NODES"] = "5" get_settings.cache_clear() try: payload = build_v2_map() for index in range(20): payload["nodes"].append( { "id": f"extra{index}", "type": "standard", "text": "x", "position": {"x": 0, "y": 0}, "parentId": "root", "layout": {"paddingX": 10}, } ) response = client.post("/api/shares", json={"title": "x", "map": payload}) assert response.status_code == 422 finally: os.environ["MAX_NODES"] = "5000" get_settings.cache_clear() def test_unknown_top_level_field_is_still_rejected(client: TestClient) -> None: response = client.post( "/api/shares", json={"title": "x", "map": build_v2_map(), "premium": True} ) assert response.status_code == 422 def test_node_without_id_is_still_rejected(client: TestClient) -> None: payload = build_v2_map() payload["nodes"].append({"text": "sans id", "layout": {"paddingX": 4}}) response = client.post("/api/shares", json={"title": "x", "map": payload}) assert response.status_code == 422