|
11 | 11 | aac_visualization_to_declarative, |
12 | 12 | declarative_dataset_to_aac, |
13 | 13 | declarative_metric_to_aac, |
| 14 | + declarative_visualization_to_aac, |
14 | 15 | detect_yaml_format, |
15 | 16 | load_aac_workspace_from_disk, |
16 | 17 | store_aac_workspace_to_disk, |
@@ -176,6 +177,38 @@ def test_dataset_declarative_to_aac(self) -> None: |
176 | 177 | assert result["json"]["id"] == "orders" |
177 | 178 | assert isinstance(result["content"], str) |
178 | 179 |
|
| 180 | + def test_visualization_declarative_to_aac(self) -> None: |
| 181 | + """Test declarative → AAC for visualizations (round-trip from fixture).""" |
| 182 | + content = yaml.safe_load((_FIXTURES_DIR / "visualisations" / "ratings.yaml").read_text()) |
| 183 | + declarative = aac_visualization_to_declarative(content) |
| 184 | + result = declarative_visualization_to_aac(declarative) |
| 185 | + assert result["json"]["id"] == "71bdc379-384a-4eac-9627-364ea847d977" |
| 186 | + assert isinstance(result["content"], str) |
| 187 | + assert "Ratings" in result["content"] |
| 188 | + |
| 189 | + def test_visualization_declarative_to_aac_inline(self) -> None: |
| 190 | + """Test declarative → AAC for a visualization built from inline data.""" |
| 191 | + aac_input = { |
| 192 | + "type": "table", |
| 193 | + "id": "my_table", |
| 194 | + "title": "My Table", |
| 195 | + "query": { |
| 196 | + "fields": { |
| 197 | + "m1": { |
| 198 | + "title": "Sum of Amount", |
| 199 | + "aggregation": "SUM", |
| 200 | + "using": "fact/amount", |
| 201 | + }, |
| 202 | + }, |
| 203 | + }, |
| 204 | + "metrics": [{"field": "m1", "format": "#,##0"}], |
| 205 | + } |
| 206 | + declarative = aac_visualization_to_declarative(aac_input) |
| 207 | + result = declarative_visualization_to_aac(declarative) |
| 208 | + assert result["json"]["id"] == "my_table" |
| 209 | + assert isinstance(result["content"], str) |
| 210 | + assert "my_table" in result["content"] |
| 211 | + |
179 | 212 |
|
180 | 213 | # --------------------------------------------------------------------------- |
181 | 214 | # Format detection tests |
@@ -263,6 +296,68 @@ def test_store_and_reload_metrics(self, tmp_path: Path) -> None: |
263 | 296 | reloaded_dict = reloaded.to_dict(camel_case=True) |
264 | 297 | assert len(reloaded_dict.get("analytics", {}).get("metrics", [])) == 2 |
265 | 298 |
|
| 299 | + def test_store_and_reload_visualizations(self, tmp_path: Path) -> None: |
| 300 | + """Store visualization AAC files via store_aac_workspace_to_disk, then reload.""" |
| 301 | + aac_vis = { |
| 302 | + "type": "table", |
| 303 | + "id": "test_vis", |
| 304 | + "title": "Test Visualization", |
| 305 | + "query": { |
| 306 | + "fields": { |
| 307 | + "m1": { |
| 308 | + "title": "Sum of Amount", |
| 309 | + "aggregation": "SUM", |
| 310 | + "using": "fact/amount", |
| 311 | + }, |
| 312 | + }, |
| 313 | + }, |
| 314 | + "metrics": [{"field": "m1", "format": "#,##0"}], |
| 315 | + } |
| 316 | + vis_declarative = [aac_visualization_to_declarative(aac_vis)] |
| 317 | + |
| 318 | + model_dict = {"analytics": {"visualizationObjects": vis_declarative}} |
| 319 | + from gooddata_sdk.catalog.workspace.declarative_model.workspace.workspace import ( |
| 320 | + CatalogDeclarativeWorkspaceModel, |
| 321 | + ) |
| 322 | + |
| 323 | + model = CatalogDeclarativeWorkspaceModel.from_dict(model_dict) |
| 324 | + store_aac_workspace_to_disk(model, tmp_path) |
| 325 | + |
| 326 | + # Verify files were created |
| 327 | + vis_files = list((tmp_path / "visualisations").glob("*.yaml")) |
| 328 | + assert len(vis_files) == 1 |
| 329 | + |
| 330 | + # Reload |
| 331 | + reloaded = load_aac_workspace_from_disk(tmp_path) |
| 332 | + reloaded_dict = reloaded.to_dict(camel_case=True) |
| 333 | + assert len(reloaded_dict.get("analytics", {}).get("visualizationObjects", [])) == 1 |
| 334 | + |
| 335 | + def test_store_and_reload_from_fixtures(self, tmp_path: Path) -> None: |
| 336 | + """Load fixtures, store to disk, reload — full round-trip.""" |
| 337 | + import shutil |
| 338 | + import tempfile |
| 339 | + |
| 340 | + with tempfile.TemporaryDirectory() as tmp: |
| 341 | + fixture_path = Path(tmp) |
| 342 | + for subdir in ("datasets", "metrics", "visualisations"): |
| 343 | + src = _FIXTURES_DIR / subdir |
| 344 | + if src.exists(): |
| 345 | + shutil.copytree(src, fixture_path / subdir) |
| 346 | + |
| 347 | + model = load_aac_workspace_from_disk(fixture_path) |
| 348 | + |
| 349 | + store_aac_workspace_to_disk(model, tmp_path) |
| 350 | + |
| 351 | + # Verify visualization files exist |
| 352 | + vis_files = list((tmp_path / "visualisations").glob("*.yaml")) |
| 353 | + assert len(vis_files) == 2 |
| 354 | + |
| 355 | + # Reload and verify counts match |
| 356 | + reloaded = load_aac_workspace_from_disk(tmp_path) |
| 357 | + reloaded_dict = reloaded.to_dict(camel_case=True) |
| 358 | + assert len(reloaded_dict.get("analytics", {}).get("visualizationObjects", [])) == 2 |
| 359 | + assert len(reloaded_dict.get("analytics", {}).get("metrics", [])) == 1 |
| 360 | + |
266 | 361 | def test_load_ignores_non_workspace_dirs(self, tmp_path: Path) -> None: |
267 | 362 | """Ensure load skips declarative non-workspace directories.""" |
268 | 363 | # Create AAC file |
|
0 commit comments