Files
glpi-neuroforge-mega/services/agent/internal/web/control_graph_test.go
jbergner a6bc71fb3a
Some checks failed
release-tag / Resolve release metadata (push) Successful in 30s
release-tag / Build knowledge (push) Failing after 4m51s
release-tag / Build control (push) Failing after 5m0s
release-tag / Build agent (push) Failing after 5m0s
release-tag / Build agent-data-init (push) Failing after 5m5s
release-tag / Build neuroforge-worker (push) Failing after 5m7s
release-tag / Build neuroforge (push) Failing after 5m9s
Init
2026-08-26 18:34:41 +02:00

79 lines
3.2 KiB
Go

package web
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/example/glpi-ai-agent/internal/config"
"github.com/example/glpi-ai-agent/internal/learning"
"github.com/example/glpi-ai-agent/internal/model"
)
func TestControlReadAuthIsScopedBearerOnly(t *testing.T) {
s := &Server{cfg: config.Config{ControlReadToken: "01234567890123456789012345678901"}}
h := s.controlReadAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) }))
for _, tc := range []struct {
name, auth string
want int
}{
{"missing", "", http.StatusUnauthorized},
{"wrong", "Bearer no", http.StatusUnauthorized},
{"valid", "Bearer 01234567890123456789012345678901", http.StatusNoContent},
} {
t.Run(tc.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/control/runs", nil)
if tc.auth != "" {
req.Header.Set("Authorization", tc.auth)
}
rr := httptest.NewRecorder()
h.ServeHTTP(rr, req)
if rr.Code != tc.want {
t.Fatalf("status=%d want=%d", rr.Code, tc.want)
}
})
}
}
func TestBuildRunGraphContainsEvidencePoliciesAndOutcome(t *testing.T) {
r := model.RunRecord{RunID: "run-1", TicketID: 42, TicketName: "VPN geht nicht", Outcome: "processed", KnowledgeID: "KB-1", ReplyProposed: true, ReplyProposedText: "VPN neu verbinden", AIReplyConfidence: .94,
ReplyKnowledgeCandidates: []model.KnowledgeCandidateAudit{{ID: "KB-1", Title: "VPN", Source: "internal-kb", Score: .91, RetrievalRank: 1, AutoReply: true}},
ValidatedOutcomeCandidates: []model.ValidatedOutcomeEvidence{{MemoryID: "m-old", OutcomeID: "o-old", Decision: "accepted", Text: "Adapter reset", Similarity: .82, Source: "glpi.outcome.accepted"}},
ReplyChecks: []model.RuleCheck{{Code: "evidence", Label: "Evidence ausreichend", Status: "pass", Blocking: true}},
}
outs := []learning.TicketOutcome{{ID: "o1", RunID: "run-1", TicketID: 42, Decision: "corrected", ConfirmedReply: "VPN Adapter neu starten", NeuroForgeID: "m1", SyncStatus: "learned"}}
g := buildRunGraph(r, outs)
kinds := map[string]bool{}
for _, n := range g.Nodes {
kinds[n.Kind] = true
}
for _, want := range []string{"ticket", "run", "knowledge", "validated_outcome", "policy_check", "reply", "human_outcome", "memory"} {
if !kinds[want] {
t.Fatalf("missing node kind %q in %#v", want, kinds)
}
}
edges := map[string]bool{}
for _, e := range g.Edges {
edges[e.Kind] = true
}
for _, want := range []string{"retrieved", "experience_evidence", "checked", "proposed_reply", "validated_by", "learned_as"} {
if !edges[want] {
t.Fatalf("missing edge kind %q in %#v", want, edges)
}
}
}
func TestBuildLearningGraphPreservesSupersession(t *testing.T) {
items := []learning.TicketOutcome{{ID: "new", RunID: "r", TicketID: 7, Decision: "corrected", ConfirmedReply: "new", SupersedesID: "old", SyncStatus: "learned"}, {ID: "old", RunID: "r", TicketID: 7, Decision: "accepted", ConfirmedReply: "old", SyncStatus: "learned"}}
g := buildLearningGraph(items)
found := false
for _, e := range g.Edges {
if e.Kind == "supersedes" && e.From == "outcome:new" && e.To == "outcome:old" {
found = true
}
}
if !found {
t.Fatalf("supersession edge missing: %+v", g.Edges)
}
}