diff --git a/workspace-server/internal/handlers/workspace_compute.go b/workspace-server/internal/handlers/workspace_compute.go index 0824a655b..3a105d988 100644 --- a/workspace-server/internal/handlers/workspace_compute.go +++ b/workspace-server/internal/handlers/workspace_compute.go @@ -112,7 +112,12 @@ func workspaceComputeIsZero(compute models.WorkspaceCompute) bool { compute.Display.Mode == "" && compute.Display.Width == 0 && compute.Display.Height == 0 && - compute.Display.Protocol == "" + compute.Display.Protocol == "" && + // A provider- or persistence-only compute is NOT zero — it must + // round-trip so GET returns those fields (canvas provider badge + + // data-persistence selector both read them back). + compute.Provider == "" && + compute.DataPersistence == "" } func workspaceComputeJSON(compute models.WorkspaceCompute) (string, error) { @@ -142,6 +147,17 @@ func workspaceComputeJSON(compute models.WorkspaceCompute) (string, error) { if len(display) > 0 { out["display"] = display } + // Cloud/compute provider + durable-data choice. These were FORWARDED to CP + // at provision time but never serialized back here, so GET /workspaces + // dropped them — the canvas provider badge always showed the default AWS and + // the data-persistence selector always showed "auto". Round-trip them (still + // omit-when-empty, so existing AWS/default rows serialize unchanged). + if compute.Provider != "" { + out["provider"] = compute.Provider + } + if compute.DataPersistence != "" { + out["data_persistence"] = compute.DataPersistence + } b, err := json.Marshal(out) if err != nil { return "", err diff --git a/workspace-server/internal/handlers/workspace_compute_test.go b/workspace-server/internal/handlers/workspace_compute_test.go index a85d55cc1..ba1c38ab1 100644 --- a/workspace-server/internal/handlers/workspace_compute_test.go +++ b/workspace-server/internal/handlers/workspace_compute_test.go @@ -93,6 +93,38 @@ func TestWorkspaceComputeJSON_OmitsEmptyNestedSections(t *testing.T) { } } +// Regression: provider + data_persistence were FORWARDED to CP but dropped from +// the serialized compute, so GET /workspaces never returned them (the canvas +// provider badge always showed AWS, the persistence selector always "auto"). +func TestWorkspaceComputeJSON_RoundTripsProviderAndDataPersistence(t *testing.T) { + got, err := workspaceComputeJSON(models.WorkspaceCompute{ + InstanceType: "t3.medium", + Provider: "gcp", + DataPersistence: "persist", + }) + if err != nil { + t.Fatalf("workspaceComputeJSON returned error: %v", err) + } + if !strings.Contains(got, `"provider":"gcp"`) { + t.Fatalf("workspaceComputeJSON dropped provider: %s", got) + } + if !strings.Contains(got, `"data_persistence":"persist"`) { + t.Fatalf("workspaceComputeJSON dropped data_persistence: %s", got) + } +} + +// A provider-only compute must NOT be treated as zero (else it serializes to +// "{}" and the cloud is lost). +func TestWorkspaceComputeJSON_ProviderOnlyIsNotZero(t *testing.T) { + got, err := workspaceComputeJSON(models.WorkspaceCompute{Provider: "hetzner"}) + if err != nil { + t.Fatalf("workspaceComputeJSON returned error: %v", err) + } + if got == "{}" || !strings.Contains(got, `"provider":"hetzner"`) { + t.Fatalf("provider-only compute serialized as zero: %s", got) + } +} + func TestWorkspaceCreate_WithCompute_PersistsComputeJSON(t *testing.T) { mock := setupTestDB(t) setupTestRedis(t)