Files
Agent-System/internal/server/task_run_json_test.go
T
weichen a9c9a9182e refactor(server): remove 24 mirrored Runtime fields via nil-safe accessors; fix Shutdown-vs-background-loop DATA RACE and goose 00071 duplicate version
- Server no longer mirrors app.Runtime services (gov/svc/pwSvc/...): 574 call sites now use accessors in runtime_accessors.go
- backgroundLoop (mutex+sync.Once) stops sweeper/publisher/reminder/wsGC goroutines race-free (baseline 44161d2a reproduced DATA RACE; now green under -race)
- goose 00071 duplicate (dialogue merge 1da17a28 brought two 00071): context-conflict-adjudications renumbered to 00072; dev DB applied only through 00070 so zero data migration
- task_finalize tests build minimal app.Runtime; test injection via serverTestOverrides + setOverride* (explicit-nil markers for fail-closed cases)
2026-09-25 13:17:22 +00:00

110 lines
2.8 KiB
Go

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)
}
}