- 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 (baseline44161d2areproduced DATA RACE; now green under -race) - goose 00071 duplicate (dialogue merge1da17a28brought 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)
110 lines
2.8 KiB
Go
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)
|
|
}
|
|
}
|