package server import ( "encoding/json" "io" "net/http" "net/http/httptest" "strings" "testing" "chat-agent-system/internal/bizdev" "chat-agent-system/internal/config" "chat-agent-system/internal/taskrun" ) func TestTaskRunResponseMarshalUsesSnakeCase(t *testing.T) { raw, err := json.Marshal(taskRunResponse{ RunRecord: taskrun.RunRecord{ RunID: "run_snake", Status: "failed", ErrorMessage: "dial tcp connection refused", TaskKey: "user_requirements", WorkspaceID: "ws_1", }, Resumable: false, }) if err != nil { t.Fatal(err) } var body map[string]any if err := json.Unmarshal(raw, &body); err != nil { t.Fatal(err) } for _, key := range []string{"run_id", "status", "error_message", "workspace_id", "task_key", "resumable"} { if _, ok := body[key]; !ok { t.Fatalf("missing %q in %s", key, raw) } } for _, pascal := range []string{"Status", "ErrorMessage", "RunID", "WorkspaceID"} { if _, ok := body[pascal]; ok { t.Fatalf("pascal key %q leaked into %s", pascal, raw) } } if body["status"] != "failed" || body["error_message"] != "dial tcp connection refused" { t.Fatalf("payload=%s", raw) } } func TestGetTaskRunJSONUsesSnakeCase(t *testing.T) { srv := newTestServer(t) ts := httptest.NewServer(srv.Routes()) t.Cleanup(ts.Close) runID := "run_snake_http" rec := &taskrun.RunRecord{ RunID: runID, SessionID: "sess-snake", Status: "failed", ErrorMessage: "dial tcp connection refused", TaskKey: "intake", UserID: 1, } if _, err := srv.taskRuns().CreateAndStart( t.Context(), runID, rec, bizdev.TaskRunRequest{TaskKey: "intake", ProjectName: "snake-json"}, bizdev.StageRunRequest{}, "", ); err != nil { t.Fatalf("CreateAndStart: %v", err) } token := testBearerToken(t, ts.URL, &config.Config{ AdminUsername: "admin", AdminPassword: "admin-test-pass", }) req, err := http.NewRequest(http.MethodGet, ts.URL+"/api/v1/tasks/runs/"+runID, nil) if err != nil { t.Fatal(err) } req.Header.Set("Authorization", "Bearer "+token) resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() payload, _ := io.ReadAll(resp.Body) if resp.StatusCode != http.StatusOK { t.Fatalf("GET status=%d body=%s", resp.StatusCode, payload) } var body map[string]any if err := json.Unmarshal(payload, &body); err != nil { t.Fatalf("decode: %v body=%s", err, payload) } status, _ := body["status"].(string) if status == "" { t.Fatalf("status key missing or empty (pascal leak?): %s", payload) } if _, ok := body["Status"]; ok || strings.Contains(string(payload), `"ErrorMessage"`) { t.Fatalf("pascal keys in GET body: %s", payload) } if _, ok := body["error_message"]; !ok { t.Fatalf("missing error_message: %s", payload) } if _, ok := body["run_id"]; !ok { t.Fatalf("missing run_id: %s", payload) } }