Add comprehensive Claude Code monitoring and realtime streaming to the K8s dashboard. Includes API endpoints for health, stats, summary, inventory, and live event streaming. Frontend provides overview, usage, inventory, debug, and live feed views.
90 lines
2.4 KiB
Go
90 lines
2.4 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/will/k8s-agent-dashboard/internal/claude"
|
|
)
|
|
|
|
// Integration-style smoke test for the Claude endpoints.
|
|
//
|
|
// This does NOT start a server process; it wires chi routes directly and calls
|
|
// them via httptest.
|
|
func TestClaudeRoutes_Smoke(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
|
|
// Minimal filesystem layout expected by endpoints.
|
|
mustMkdirAll(t, filepath.Join(tmp, "agents"))
|
|
mustMkdirAll(t, filepath.Join(tmp, "skills"))
|
|
mustMkdirAll(t, filepath.Join(tmp, "commands"))
|
|
mustMkdirAll(t, filepath.Join(tmp, "state"))
|
|
|
|
// Minimal stats-cache.json required by /stats, /summary, /debug/files.
|
|
// Keep it tiny and deterministic.
|
|
statsCache := `{
|
|
"totalSessions": 1,
|
|
"totalMessages": 1,
|
|
"modelUsage": {
|
|
"claude-test": {
|
|
"inputTokens": 1,
|
|
"outputTokens": 1,
|
|
"cacheReadInputTokens": 0,
|
|
"cacheCreationInputTokens": 0
|
|
}
|
|
}
|
|
}`
|
|
if err := os.WriteFile(filepath.Join(tmp, "stats-cache.json"), []byte(statsCache), 0o600); err != nil {
|
|
t.Fatalf("write stats-cache.json: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(tmp, "history.jsonl"), []byte("{}\n"), 0o600); err != nil {
|
|
t.Fatalf("write history.jsonl: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(tmp, "state", "component-registry.json"), []byte("{}"), 0o600); err != nil {
|
|
t.Fatalf("write state/component-registry.json: %v", err)
|
|
}
|
|
|
|
loader := claude.NewLoader(tmp)
|
|
|
|
r := chi.NewRouter()
|
|
// Mirror the /api/claude routes from cmd/server/main.go.
|
|
r.Route("/api", func(r chi.Router) {
|
|
r.Route("/claude", func(r chi.Router) {
|
|
r.Get("/health", GetClaudeHealth(loader))
|
|
r.Get("/stats", GetClaudeStats(loader))
|
|
r.Get("/summary", GetClaudeSummary(loader))
|
|
r.Get("/inventory", GetClaudeInventory(loader))
|
|
r.Get("/debug/files", GetClaudeDebugFiles(loader))
|
|
})
|
|
})
|
|
|
|
for _, path := range []string{
|
|
"/api/claude/health",
|
|
"/api/claude/stats",
|
|
"/api/claude/inventory",
|
|
"/api/claude/debug/files",
|
|
"/api/claude/summary",
|
|
} {
|
|
path := path
|
|
|
|
req := httptest.NewRequest(http.MethodGet, path, nil)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("GET %s status=%d body=%s", path, w.Code, w.Body.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func mustMkdirAll(t *testing.T, p string) {
|
|
t.Helper()
|
|
if err := os.MkdirAll(p, 0o755); err != nil {
|
|
t.Fatalf("mkdir %s: %v", p, err)
|
|
}
|
|
}
|