From c982b85e2f1d3a0e0502b91698a3e2c887b9fda7 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 18 Jun 2026 15:42:54 +0200 Subject: [PATCH 1/2] acc: preserve serialized_dashboard formatting in testserver The lakeview testserver fake round-tripped serialized_dashboard through json.Unmarshal/Marshal, collapsing whitespace and reordering keys. The real backend stores and returns the serialized dashboard verbatim, modulo the documented pageType injection and dataset catalog/schema overrides. Preserve the caller's original formatting when no mutation is needed, and use indented marshaling otherwise to match cloud's pretty-printing. This lets the dashboards/generate_inplace acceptance test run locally; flip its Local flag and re-baseline serialized_dashboard formatting across the dashboard test family. Co-authored-by: Isaac --- .../detect-change/out.plan.direct.json | 4 ++-- .../dashboards/generate_inplace/out.test.toml | 2 +- .../dashboards/generate_inplace/test.toml | 2 +- .../dashboards/simple/out.plan.direct.json | 9 +-------- .../resources/dashboards/simple/output.txt | 2 +- .../simple_outside_bundle_root/output.txt | 2 +- .../dashboards/simple_syncroot/output.txt | 2 +- libs/testserver/dashboards.go | 16 +++++++++++++++- 8 files changed, 23 insertions(+), 16 deletions(-) diff --git a/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json b/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json index b113b73d33a..635fdd0f193 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json @@ -26,7 +26,7 @@ "parent_path": "/Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/resources", "path": "/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/resources/test-dashboard-[UNIQUE_NAME].lvdash.json", "published": true, - "serialized_dashboard": "{}\n", + "serialized_dashboard": "{}", "update_time": "[TIMESTAMP]", "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" }, @@ -41,7 +41,7 @@ "reason": "etag_based", "old": "{\n \"pages\": [\n {\n \"displayName\": \"New Page\",\n \"layout\": [\n {\n \"position\": {\n \"height\": 2,\n \"width\": 6,\n \"x\": 0,\n \"y\": 0\n },\n \"widget\": {\n \"name\": \"82eb9107\",\n \"textbox_spec\": \"# I'm a title\"\n }\n },\n {\n \"position\": {\n \"height\": 2,\n \"width\": 6,\n \"x\": 0,\n \"y\": 2\n },\n \"widget\": {\n \"name\": \"ffa6de4f\",\n \"textbox_spec\": \"Text\"\n }\n }\n ],\n \"name\": \"fdd21a3c\"\n }\n ]\n}\n", "new": "{\n \"pages\": [\n {\n \"displayName\": \"New Page\",\n \"layout\": [\n {\n \"position\": {\n \"height\": 2,\n \"width\": 6,\n \"x\": 0,\n \"y\": 0\n },\n \"widget\": {\n \"name\": \"82eb9107\",\n \"textbox_spec\": \"# I'm a title\"\n }\n },\n {\n \"position\": {\n \"height\": 2,\n \"width\": 6,\n \"x\": 0,\n \"y\": 2\n },\n \"widget\": {\n \"name\": \"ffa6de4f\",\n \"textbox_spec\": \"Text\"\n }\n }\n ],\n \"name\": \"fdd21a3c\"\n }\n ]\n}\n", - "remote": "{}\n" + "remote": "{}" } } } diff --git a/acceptance/bundle/resources/dashboards/generate_inplace/out.test.toml b/acceptance/bundle/resources/dashboards/generate_inplace/out.test.toml index bbcef543fa6..7edd52865f7 100644 --- a/acceptance/bundle/resources/dashboards/generate_inplace/out.test.toml +++ b/acceptance/bundle/resources/dashboards/generate_inplace/out.test.toml @@ -1,4 +1,4 @@ -Local = false +Local = true Cloud = true RequiresWarehouse = true RunsOnDbr = true diff --git a/acceptance/bundle/resources/dashboards/generate_inplace/test.toml b/acceptance/bundle/resources/dashboards/generate_inplace/test.toml index 731f8ff31b5..58053ece188 100644 --- a/acceptance/bundle/resources/dashboards/generate_inplace/test.toml +++ b/acceptance/bundle/resources/dashboards/generate_inplace/test.toml @@ -1,5 +1,5 @@ Cloud = true -Local = false +Local = true RecordRequests = false RunsOnDbr = true diff --git a/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json b/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json index f0e8fb6f047..3a5fe0e2eb8 100644 --- a/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json @@ -16,7 +16,7 @@ "parent_path": "/Workspace/Users/[USERNAME]", "path": "/Users/[USERNAME]/test bundle-deploy-dashboard [UUID].lvdash.json", "published": true, - "serialized_dashboard": "{}\n", + "serialized_dashboard": "{ }\n", "update_time": "[TIMESTAMP]", "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" }, @@ -26,13 +26,6 @@ "reason": "custom", "old": "[ETAG]", "remote": "[ETAG]" - }, - "serialized_dashboard": { - "action": "skip", - "reason": "etag_based", - "old": "{ }\n", - "new": "{ }\n", - "remote": "{}\n" } } } diff --git a/acceptance/bundle/resources/dashboards/simple/output.txt b/acceptance/bundle/resources/dashboards/simple/output.txt index 7464a2e50f5..422aca2a79e 100644 --- a/acceptance/bundle/resources/dashboards/simple/output.txt +++ b/acceptance/bundle/resources/dashboards/simple/output.txt @@ -10,7 +10,7 @@ Deployment complete! "lifecycle_state": "ACTIVE", "parent_path": "/Users/[USERNAME]", "path": "/Users/[USERNAME]/test bundle-deploy-dashboard [UUID].lvdash.json", - "serialized_dashboard": "{}\n" + "serialized_dashboard": "{ }\n" } >>> [CLI] bundle plan -o json diff --git a/acceptance/bundle/resources/dashboards/simple_outside_bundle_root/output.txt b/acceptance/bundle/resources/dashboards/simple_outside_bundle_root/output.txt index 6e16fb6ef0b..9b773af8b9f 100644 --- a/acceptance/bundle/resources/dashboards/simple_outside_bundle_root/output.txt +++ b/acceptance/bundle/resources/dashboards/simple_outside_bundle_root/output.txt @@ -10,7 +10,7 @@ Deployment complete! "lifecycle_state": "ACTIVE", "parent_path": "/Users/[USERNAME]", "path": "/Users/[USERNAME]/test bundle-deploy-dashboard [UUID].lvdash.json", - "serialized_dashboard": "{}\n" + "serialized_dashboard": "{ }\n" } >>> [CLI] bundle destroy --auto-approve diff --git a/acceptance/bundle/resources/dashboards/simple_syncroot/output.txt b/acceptance/bundle/resources/dashboards/simple_syncroot/output.txt index 8bb6baeca54..a51090b98af 100644 --- a/acceptance/bundle/resources/dashboards/simple_syncroot/output.txt +++ b/acceptance/bundle/resources/dashboards/simple_syncroot/output.txt @@ -10,7 +10,7 @@ Deployment complete! "lifecycle_state": "ACTIVE", "parent_path": "/Users/[USERNAME]", "path": "/Users/[USERNAME]/test bundle-deploy-dashboard [UUID].lvdash.json", - "serialized_dashboard": "{}\n" + "serialized_dashboard": "{ }\n" } >>> [CLI] bundle destroy --auto-approve diff --git a/libs/testserver/dashboards.go b/libs/testserver/dashboards.go index 10cc03a477c..9eef81562cb 100644 --- a/libs/testserver/dashboards.go +++ b/libs/testserver/dashboards.go @@ -32,11 +32,18 @@ func transformSerializedDashboard(serializedDashboard, datasetCatalog, datasetSc return serializedDashboard } + // The real backend stores and returns the serialized dashboard verbatim, + // modulo the mutations applied below. Track whether any mutation actually + // happens so we can preserve the caller's original formatting (whitespace + // and key order) when it doesn't, matching cloud behavior. + mutated := false + // Add pageType to each page in the pages array (as of June 2025, this is an undocumented Lakeview API behaviour) if pages, ok := dashboardContent["pages"].([]any); ok { for _, page := range pages { if pageMap, ok := page.(map[string]any); ok { pageMap["pageType"] = "PAGE_TYPE_CANVAS" + mutated = true } } } @@ -47,15 +54,22 @@ func transformSerializedDashboard(serializedDashboard, datasetCatalog, datasetSc if datasetMap, ok := dataset.(map[string]any); ok { if datasetCatalog != "" { datasetMap["catalog"] = datasetCatalog + mutated = true } if datasetSchema != "" { datasetMap["schema"] = datasetSchema + mutated = true } } } } - updatedContent, err := json.Marshal(dashboardContent) + if !mutated { + return serializedDashboard + } + + // Pretty-print with 2-space indentation to match cloud's formatting. + updatedContent, err := json.MarshalIndent(dashboardContent, "", " ") if err != nil { return serializedDashboard } From f88ea67f63850e35ddd51598e934280c1b4ad567 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 18 Jun 2026 18:50:02 +0200 Subject: [PATCH 2/2] acc: keep mutation path compact; fix missed verbatim goldens The previous commit pretty-printed the serialized dashboard whenever it was mutated (pageType/dataset injection), which re-baselined goldens in local-only tests (bundle/migrate/dashboards, bundle/generate/dashboard-inplace) for a formatting cloud is never checked against. Keep the mutation path compact (its original behavior) and only return the dashboard verbatim when nothing is mutated. This narrows the change to the verbatim case and regenerates the one remaining missed golden (bundle/generate/dashboard-inplace). Co-authored-by: Isaac --- acceptance/bundle/generate/dashboard-inplace/output.txt | 2 +- libs/testserver/dashboards.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/acceptance/bundle/generate/dashboard-inplace/output.txt b/acceptance/bundle/generate/dashboard-inplace/output.txt index 90d054197aa..5b8f9dfbdb4 100644 --- a/acceptance/bundle/generate/dashboard-inplace/output.txt +++ b/acceptance/bundle/generate/dashboard-inplace/output.txt @@ -19,7 +19,7 @@ Deployment complete! "lifecycle_state": "ACTIVE", "parent_path": "/Users/[USERNAME]/.bundle/dashboard update inplace/default/resources", "path": "/Users/[USERNAME]/.bundle/dashboard update inplace/default/resources/test dashboard.lvdash.json", - "serialized_dashboard": "{\"a\":\"b\"}\n", + "serialized_dashboard": "{\"a\":\"b\"}", "update_time": "[TIMESTAMP]", "warehouse_id": "" } diff --git a/libs/testserver/dashboards.go b/libs/testserver/dashboards.go index 9eef81562cb..673973236bd 100644 --- a/libs/testserver/dashboards.go +++ b/libs/testserver/dashboards.go @@ -68,8 +68,7 @@ func transformSerializedDashboard(serializedDashboard, datasetCatalog, datasetSc return serializedDashboard } - // Pretty-print with 2-space indentation to match cloud's formatting. - updatedContent, err := json.MarshalIndent(dashboardContent, "", " ") + updatedContent, err := json.Marshal(dashboardContent) if err != nil { return serializedDashboard }