"""Tests for Excalidraw file support in ObsiGate.""" import json EXCALIDRAW_SKELETON = { "type": "excalidraw", "version": 2, "elements": [], "appState": {"viewBackgroundColor": "#ffffff"}, "files": {}, } EXCALIDRAW_WITH_CONTENT = { "type": "excalidraw", "version": 2, "elements": [ { "id": "rect-1", "type": "rectangle", "x": 100, "y": 200, "width": 300, "height": 150, "angle": 0, "strokeColor": "#1e1e1e", "backgroundColor": "#ffec99", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], "roundness": {"type": 3}, "boundElements": None, "updated": 1, "link": None, "locked": False, }, { "id": "text-1", "type": "text", "x": 150, "y": 260, "width": 200, "height": 30, "angle": 0, "strokeColor": "#1e1e1e", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], "roundness": None, "boundElements": None, "updated": 1, "link": None, "locked": False, "text": "Hello Excalidraw", "fontSize": 20, "fontFamily": 1, "textAlign": "center", "verticalAlign": "middle", "containerId": "rect-1", "originalText": "Hello Excalidraw", "autoResize": True, "lineHeight": 1.25, }, ], "appState": {"viewBackgroundColor": "#ffffff", "name": "My Diagram"}, "files": {}, } class TestExcalidrawDetection: """Test that the /api/file endpoint correctly detects .excalidraw files.""" VAULT = "TestVault" def test_valid_excalidraw_file(self, client): """A valid .excalidraw file should return is_excalidraw: true.""" path = "diagram.excalidraw" resp = client.post( f"/api/file/{self.VAULT}", json={"path": path, "content": json.dumps(EXCALIDRAW_WITH_CONTENT)}, ) assert resp.status_code == 200, resp.text resp = client.get(f"/api/file/{self.VAULT}", params={"path": path}) assert resp.status_code == 200, resp.text data = resp.json() assert data["is_excalidraw"] is True assert "excalidraw_data" in data assert len(data["excalidraw_data"]["elements"]) == 2 assert data["excalidraw_data"]["elements"][0]["type"] == "rectangle" assert data["title"] == "My Diagram" def test_empty_excalidraw_creation(self, client): """Creating a .excalidraw with empty content injects skeleton JSON.""" path = "empty.excalidraw" resp = client.post( f"/api/file/{self.VAULT}", json={"path": path, "content": ""}, ) assert resp.status_code == 200, resp.text resp = client.get(f"/api/file/{self.VAULT}", params={"path": path}) assert resp.status_code == 200, resp.text data = resp.json() assert data["is_excalidraw"] is True assert data["excalidraw_data"]["elements"] == [] assert data["excalidraw_data"]["appState"]["viewBackgroundColor"] == "#ffffff" def test_invalid_json_excalidraw_fallback(self, client): """A .excalidraw with invalid JSON should show as text, not Excalidraw.""" path = "corrupt.excalidraw" resp = client.post( f"/api/file/{self.VAULT}", json={"path": path, "content": "this is not valid json!!!"}, ) assert resp.status_code == 200, resp.text resp = client.get(f"/api/file/{self.VAULT}", params={"path": path}) assert resp.status_code == 200, resp.text data = resp.json() assert not data.get("is_excalidraw") def test_json_without_excalidraw_type(self, client): """A .excalidraw with valid JSON but wrong type falls through.""" path = "not-excalidraw.excalidraw" resp = client.post( f"/api/file/{self.VAULT}", json={"path": path, "content": '{"type":"something-else","data":[1,2,3]}'}, ) assert resp.status_code == 200, resp.text resp = client.get(f"/api/file/{self.VAULT}", params={"path": path}) assert resp.status_code == 200, resp.text data = resp.json() assert not data.get("is_excalidraw") def test_excalidraw_in_tree(self, client): """A .excalidraw file appears in the file tree.""" path = "tree-diagram.excalidraw" resp = client.post( f"/api/file/{self.VAULT}", json={"path": path, "content": json.dumps(EXCALIDRAW_SKELETON)}, ) assert resp.status_code == 200, resp.text resp = client.get(f"/api/browse/{self.VAULT}", params={"path": ""}) assert resp.status_code == 200, resp.text items = resp.json()["items"] names = [i["name"] for i in items] assert "tree-diagram.excalidraw" in names, f"Not found in: {names}" def test_excalidraw_save_roundtrip(self, client): """Create, edit (PUT), and re-read an .excalidraw file.""" path = "roundtrip.excalidraw" client.post( f"/api/file/{self.VAULT}", json={"path": path, "content": ""}, ) modified = dict(EXCALIDRAW_SKELETON) modified["elements"] = [{ "id": "ellipse-1", "type": "ellipse", "x": 50, "y": 50, "width": 100, "height": 80, "angle": 0, "strokeColor": "#1e1e1e", "backgroundColor": "#a5d8ff", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], "roundness": {"type": 2}, "boundElements": None, "updated": 1, "link": None, "locked": False, }] resp = client.put( f"/api/file/{self.VAULT}/save", params={"path": path, "backup": "false"}, json={"content": json.dumps(modified)}, ) assert resp.status_code == 200, resp.text resp = client.get(f"/api/file/{self.VAULT}", params={"path": path}) assert resp.status_code == 200, resp.text data = resp.json() assert data["is_excalidraw"] is True assert len(data["excalidraw_data"]["elements"]) == 1 assert data["excalidraw_data"]["elements"][0]["type"] == "ellipse"