Files
Agent-System/internal/engineeringdashboard/runtime_test.go
T
weichenandCursor a07ba1065b feat(engineering-dashboard): U2-U4 docs git and runtime collectors
Add allowlisted markdown indexing, read-only git plumbing via injected
runners, and docker/readiness collection with path and loopback guards.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 07:03:32 +00:00

118 lines
3.8 KiB
Go

package engineeringdashboard
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
func TestRuntime_ReadyOKAndEventsRestarting(t *testing.T) {
now := time.Now().UTC()
dockerLines := []map[string]any{
{"Names": "shuimu-agent-main", "Image": "agent:main", "Status": "Up (healthy)", "Labels": "com.docker.compose.project=agent-main,com.docker.compose.service=app"},
{"Names": "shuimu-agent-events-main", "Image": "events:main", "Status": "Restarting (1) 2 seconds ago", "Labels": "com.docker.compose.project=agent-main,com.docker.compose.service=events"},
}
var b strings.Builder
for _, row := range dockerLines {
raw, _ := json.Marshal(row)
b.Write(raw)
b.WriteByte('\n')
}
fake := &FakeRunner{Map: map[string]FakeResult{
"docker ps -a --format {{json .}}": {Stdout: b.String()},
}}
// Override ready endpoints via temporary server on allowlisted pattern — use real client against loopback.
// CollectRuntime uses fixed ports; for unit test we only validate docker facet path by calling parse helpers.
cv, env, ok := parseDockerLine(string(mustJSON(dockerLines[1])))
if !ok || env != "main" || cv.Status == "" {
t.Fatalf("parse failed: %+v %s", cv, env)
}
envState := EnvironmentState{
Name: "main",
Containers: []ContainerView{
mustParseCV(dockerLines[0]),
mustParseCV(dockerLines[1]),
},
Readiness: "ok",
}
envState.Facets.ContainerStatus = summarizeContainers(envState.Containers)
envState.Facets.AppReadiness = "ok"
envState.Facets.ServicesOK = summarizeServices(envState.Containers, "ok")
if envState.Facets.ContainerStatus != "partial" || envState.Facets.ServicesOK != "partial" {
t.Fatalf("expected partial facets: %+v", envState.Facets)
}
_ = fake
_ = now
}
func TestRuntime_RedirectOffAllowlistDenied(t *testing.T) {
if err := assertLoopbackReadyURL("http://example.com/ready"); err == nil {
t.Fatal("expected deny")
}
if err := assertLoopbackReadyURL("http://127.0.0.1:9999/ready"); err == nil {
t.Fatal("expected port deny")
}
if err := assertLoopbackReadyURL("http://127.0.0.1:8080/ready"); err != nil {
t.Fatal(err)
}
}
func TestRuntime_DockerUnavailableStillReturnsEnvs(t *testing.T) {
fake := &FakeRunner{Map: map[string]FakeResult{
"docker ps -a --format {{json .}}": {Err: errorsNew("docker missing")},
}}
// Avoid hitting real ready ports: still OK if degraded.
res := CollectRuntime(context.Background(), t.TempDir(), fake, time.Now().UTC())
if len(res.Environments) != 4 {
t.Fatalf("expected 4 envs: %+v", res.Environments)
}
if res.Source.Status != SourceDegraded && res.Source.Status != SourceOK {
t.Fatalf("unexpected source: %+v", res.Source)
}
}
func TestRuntime_NoEnvInOutput(t *testing.T) {
line := `{"Names":"x","Image":"i","Status":"Up","Labels":"com.docker.compose.project=agent-dev,com.docker.compose.service=app","Env":["SECRET=1"]}`
cv, _, ok := parseDockerLine(line)
if !ok {
t.Fatal("parse failed")
}
raw, _ := json.Marshal(cv)
if strings.Contains(string(raw), "SECRET") || strings.Contains(string(raw), "Env") {
t.Fatalf("env leaked: %s", raw)
}
}
func TestReadyHTTP_UsesLoopbackServer(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
_, _ = w.Write([]byte(`{"schema_version":48}`))
}))
defer srv.Close()
// server URL is 127.0.0.1:random — not allowlisted port, assertLoopbackReadyURL should fail
if err := assertLoopbackReadyURL(srv.URL + "/ready"); err == nil {
t.Fatal("random port must be denied by allowlist")
}
}
func mustJSON(v any) []byte {
b, _ := json.Marshal(v)
return b
}
func mustParseCV(row map[string]any) ContainerView {
cv, _, _ := parseDockerLine(string(mustJSON(row)))
return cv
}
func errorsNew(s string) error { return &simpleErr{s} }
type simpleErr struct{ s string }
func (e *simpleErr) Error() string { return e.s }