- internal/agent: observe-mode loop — periodic scan + SSE job-event pull (events only pull the next scan earlier, debounced 5s; job.finished feeds exit_burst). Provider chain: primary (LLM) → echo fallback on failure, recorded as degraded. VerifyEvidence firewall runs on every LLM answer. Observe-only by construction: no write path reachable. - internal/declog: JSONL decision log (scenario, trigger, provider, model, latency, tokens, findings, raw output, state snapshot + digest, degraded flag), size-rotated. - internal/runbook: incident-pattern seed (embedded YAML) from the cluster's real debugging history (bug-1494 stale config, IPC ABI skew, bug-1513 rejoin crashes, bug-1521 reason race, first-fit bias, dispatch-fail repend family), rendered into the provider prompt. - cmd: advisor run (signal-handled observe loop) and advisor once (single round, JSON report to stdout, no decision log). - internal/config tests; full suite CGO_ENABLED=0 go test ./... clean. Co-Authored-By: Claude Code <noreply@anthropic.com>
70 lines
1.9 KiB
Go
70 lines
1.9 KiB
Go
package config
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func setenvs(t *testing.T, kv map[string]string) {
|
|
t.Helper()
|
|
for _, k := range []string{"XCE_API_KEY", "XCE_API_URL", "ADVISOR_LLM_BASE_URL", "ADVISOR_LLM_MODEL",
|
|
"ADVISOR_INTERVAL", "ADVISOR_LLM_TIMEOUT", "ADVISOR_LOG", "ADVISOR_RUNBOOK"} {
|
|
t.Setenv(k, "") // clear first
|
|
}
|
|
for k, v := range kv {
|
|
t.Setenv(k, v)
|
|
}
|
|
}
|
|
|
|
func TestFromEnv_Minimal(t *testing.T) {
|
|
setenvs(t, map[string]string{"XCE_API_KEY": "xce_ak_x"})
|
|
c, err := FromEnv()
|
|
if err != nil {
|
|
t.Fatalf("FromEnv: %v", err)
|
|
}
|
|
if c.APIURL != defaultAPIURL {
|
|
t.Errorf("default APIURL = %q", c.APIURL)
|
|
}
|
|
if c.LLMEnabled() {
|
|
t.Errorf("LLM should be disabled by default")
|
|
}
|
|
if c.Interval != defaultInterval {
|
|
t.Errorf("default interval = %v", c.Interval)
|
|
}
|
|
}
|
|
|
|
func TestFromEnv_MissingKey(t *testing.T) {
|
|
setenvs(t, nil)
|
|
if _, err := FromEnv(); err == nil {
|
|
t.Fatal("missing XCE_API_KEY must error")
|
|
}
|
|
}
|
|
|
|
func TestFromEnv_LLMRequiresModel(t *testing.T) {
|
|
setenvs(t, map[string]string{"XCE_API_KEY": "k", "ADVISOR_LLM_BASE_URL": "http://llm"})
|
|
if _, err := FromEnv(); err == nil {
|
|
t.Fatal("BASE_URL without MODEL must error")
|
|
}
|
|
setenvs(t, map[string]string{
|
|
"XCE_API_KEY": "k", "ADVISOR_LLM_BASE_URL": "http://llm", "ADVISOR_LLM_MODEL": "glm-4-flash",
|
|
})
|
|
c, err := FromEnv()
|
|
if err != nil || !c.LLMEnabled() || c.LLMModel != "glm-4-flash" {
|
|
t.Fatalf("LLM config failed: %v %+v", err, c)
|
|
}
|
|
}
|
|
|
|
func TestFromEnv_Durations(t *testing.T) {
|
|
setenvs(t, map[string]string{"XCE_API_KEY": "k", "ADVISOR_INTERVAL": "45s", "ADVISOR_LLM_TIMEOUT": "10s"})
|
|
c, err := FromEnv()
|
|
if err != nil {
|
|
t.Fatalf("FromEnv: %v", err)
|
|
}
|
|
if c.Interval != 45*time.Second || c.LLMTimeout != 10*time.Second {
|
|
t.Errorf("durations = %v %v", c.Interval, c.LLMTimeout)
|
|
}
|
|
setenvs(t, map[string]string{"XCE_API_KEY": "k", "ADVISOR_INTERVAL": "bogus"})
|
|
if _, err := FromEnv(); err == nil {
|
|
t.Fatal("bad interval must error")
|
|
}
|
|
}
|