fix(workspace): round-trip compute.provider (+ data_persistence) in GET #2410

Merged
core-devops merged 1 commits from fix/compute-serialize-provider into main 2026-06-07 21:20:20 +00:00
2 changed files with 49 additions and 1 deletions
@@ -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
@@ -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)