Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions CubeOps/internal/handler/sdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,33 @@ func TestSDK_GetSandbox_Success(t *testing.T) {
}
}

func TestSDK_GetSandbox_UnknownStateIsNotReportedAsRunning(t *testing.T) {
cm := &fakeCM{
getSandbox: func(_ context.Context, _, _ string) (json.RawMessage, error) {
return raw(`{
"ret": {"ret_code": 0},
"data": [{
"sandbox_id": "sb-unknown", "status": 3,
"containers": [], "annotations": {}, "labels": {}
}]
}`), nil
},
}
r := newSDKRouter(t, cm)

w := httptestRecorder(t, r, "GET", "/api/v1/sdk/sandboxes/sb-unknown")
if w.Code != http.StatusOK {
t.Fatalf("status = %d, want 200; body=%s", w.Code, w.Body.String())
}
var detail map[string]interface{}
if err := json.Unmarshal(w.Body.Bytes(), &detail); err != nil {
t.Fatalf("unmarshal: %v body=%s", err, w.Body.String())
}
if detail["state"] != "unknown" {
t.Errorf("state = %v, want unknown (CubeMaster status 3)", detail["state"])
}
}

func TestSDK_GetSandbox_NotFoundInCM(t *testing.T) {
cm := &fakeCM{
getSandbox: func(_ context.Context, _, _ string) (json.RawMessage, error) {
Expand Down
14 changes: 7 additions & 7 deletions CubeOps/internal/translator/translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,17 @@ func SnakeToCamel(s string) string {

// SandboxStateFromInt converts CubeMaster integer status to frontend state string.
// CubeMaster: 0=created, 1=running, 2=exited/stopped, 3=unknown, 4=pausing, 5=paused
// Frontend enum: "running" | "paused" | "pausing"
// Frontend enum: "running" | "paused" | "pausing" | "unknown"
func SandboxStateFromInt(s int) string {
switch s {
case 1:
return "running"
case 4:
return "pausing"
case 5:
return "paused"
default:
return "running"
return "unknown"
Comment thread
luzhixing12345 marked this conversation as resolved.
Comment thread
luzhixing12345 marked this conversation as resolved.
}
}

Expand All @@ -164,24 +166,22 @@ func SandboxStateFromRaw(raw json.RawMessage) string {
return "paused"
case "pausing":
return "pausing"
case "1":
return "running"
case "2":
case "running", "1":
return "running"
case "4":
return "pausing"
case "5":
return "paused"
default:
return "running"
return "unknown"
}
}
// Try int.
var n int
if json.Unmarshal(raw, &n) == nil {
return SandboxStateFromInt(n)
}
return "running"
return "unknown"
}

// ParseMemoryMB converts "2048Mi" → 2048, "2048MB" → 2048, "2G" → 2048.
Expand Down
72 changes: 72 additions & 0 deletions CubeOps/internal/translator/translator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2026 Tencent Inc.
// SPDX-License-Identifier: Apache-2.0

package translator

import (
"encoding/json"
"testing"
)

func TestSandboxStateFromInt(t *testing.T) {
tests := []struct {
status int
want string
}{
{0, "unknown"},
{1, "running"},
{2, "unknown"},
{3, "unknown"},
{4, "pausing"},
{5, "paused"},
{99, "unknown"},
}
for _, test := range tests {
if got := SandboxStateFromInt(test.status); got != test.want {
t.Errorf("SandboxStateFromInt(%d) = %q, want %q", test.status, got, test.want)
}
}
}

func TestSandboxStateFromRaw(t *testing.T) {
tests := []struct {
raw json.RawMessage
want string
}{
{json.RawMessage(`1`), "running"},
{json.RawMessage(`"running"`), "running"},
{json.RawMessage(`4`), "pausing"},
{json.RawMessage(`"pausing"`), "pausing"},
{json.RawMessage(`5`), "paused"},
{json.RawMessage(`"pause"`), "paused"},
{json.RawMessage(`0`), "unknown"},
{json.RawMessage(`2`), "unknown"},
Comment thread
luzhixing12345 marked this conversation as resolved.
{json.RawMessage(`3`), "unknown"},
{json.RawMessage(`"3"`), "unknown"},
{json.RawMessage(`"UNKNOWN"`), "unknown"},
{json.RawMessage(`99`), "unknown"},
{json.RawMessage(`"invalid"`), "unknown"},
{json.RawMessage(`null`), "unknown"},
{json.RawMessage(`{`), "unknown"},
{nil, "unknown"},
}
for _, test := range tests {
if got := SandboxStateFromRaw(test.raw); got != test.want {
t.Errorf("SandboxStateFromRaw(%s) = %q, want %q", test.raw, got, test.want)
}
}
}

func TestTransformSandboxListPreservesUnknownState(t *testing.T) {
raw := json.RawMessage(`{
"ret":{"ret_code":0},
"data":[{"sandbox_id":"sb-unknown","status":3,"annotations":{},"labels":{}}]
}`)
items, ok := TransformSandboxList(raw).([]map[string]interface{})
if !ok || len(items) != 1 {
t.Fatalf("TransformSandboxList() = %#v, want one sandbox", TransformSandboxList(raw))
}
if got := items[0]["state"]; got != "unknown" {
t.Errorf("state = %v, want unknown", got)
}
}
3 changes: 2 additions & 1 deletion openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1860,11 +1860,12 @@ components:
- pause
SandboxState:
type: string
description: State of the sandbox (running | paused)
description: State of the sandbox (running | paused | pausing | unknown)
enum:
- running
- paused
- pausing
- unknown
SandboxVolumeMount:
type: object
description: Volume mount inside the sandbox.
Expand Down
4 changes: 2 additions & 2 deletions web/src/api/generated/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,10 +467,10 @@ export interface components {
logs: components["schemas"]["SandboxLogEntry"][];
};
/**
* @description State of the sandbox (running | paused)
* @description State of the sandbox (running | paused | pausing | unknown)
* @enum {string}
*/
SandboxState: "running" | "paused" | "pausing";
SandboxState: "running" | "paused" | "pausing" | "unknown";
/** @description Volume mount inside the sandbox. */
SandboxVolumeMount: {
name: string;
Expand Down
Loading