Files
glpi-ai-agent/internal/glpikb/sync_test.go
T
groot 31fa84f243
release-tag / release-image (push) Successful in 1m34s
Bugfix GLPI-KB-Category
2026-08-05 15:33:56 +02:00

100 lines
4.0 KiB
Go

package glpikb
import (
"context"
"path/filepath"
"strings"
"testing"
"time"
"github.com/example/glpi-ai-agent/internal/config"
"github.com/example/glpi-ai-agent/internal/metrics"
"github.com/example/glpi-ai-agent/internal/model"
)
type fakeSource struct{}
func (fakeSource) DiscoverKnowledgeBasePath(context.Context, string) (string, error) {
return "/Knowledge/KnowbaseItem", nil
}
func (fakeSource) ListKnowledgeBaseItems(context.Context, string, int, string) ([]model.GLPIKnowledgeItem, error) {
return []model.GLPIKnowledgeItem{{ID: 12, Title: "Benutzerkonto gesperrt", Content: "<p>Bitte entsperren.</p>", CategoryIDs: []int64{9}, ModifiedAt: "now"}}, nil
}
func (fakeSource) GetCategories(context.Context) ([]model.Category, error) {
return []model.Category{{ID: 2, Name: "Active Directory", KnowbaseCategoryID: 9}}, nil
}
type fakeStore struct{ docs []model.KnowledgeDoc }
func (f *fakeStore) ReplaceExternalSource(_ context.Context, _ string, d []model.KnowledgeDoc) error {
f.docs = append([]model.KnowledgeDoc(nil), d...)
return nil
}
func (f *fakeStore) Count() int { return len(f.docs) }
func TestSyncMapsGLPIKBAndAutoReplyWhitelist(t *testing.T) {
cfg := config.Config{DataDir: t.TempDir(), GLPIKBEnabled: true, GLPIKBPath: "auto", GLPIKBLimit: 50, GLPIKBSyncInterval: time.Minute, GLPIKBSource: "glpi-kb", GLPIKBAutoReply: true, GLPIKBAutoReplyCategoryIDs: []int64{9}, CommunicationLanguage: "de-DE", CommunicationStyle: "formal", GLPITimeout: time.Second}
st := &fakeStore{}
m := metrics.New()
s := New(cfg, fakeSource{}, st, m)
if err := s.Sync(context.Background()); err != nil {
t.Fatal(err)
}
if len(st.docs) != 1 {
t.Fatalf("docs=%d", len(st.docs))
}
d := st.docs[0]
if d.ID != "GLPI-KB-12" || !d.AutoReply || len(d.Categories) != 1 || d.Categories[0] != 2 || d.Text != "Bitte entsperren." || d.AnswerHTML != "<p>Bitte entsperren.</p>" {
t.Fatalf("unexpected doc: %+v", d)
}
if d.AutoReplyDecision != "glpi_kb_auto_reply_approved" || d.AutoReplyDetail == "" {
t.Fatalf("expected auditable approval reason, got decision=%q detail=%q", d.AutoReplyDecision, d.AutoReplyDetail)
}
if _, err := filepath.Abs(cfg.DataDir); err != nil {
t.Fatal(err)
}
}
func TestSyncAllowsAutoReplyByMappedITILCategory(t *testing.T) {
cfg := config.Config{
DataDir: t.TempDir(), GLPIKBEnabled: true, GLPIKBPath: "auto", GLPIKBLimit: 50,
GLPIKBSyncInterval: time.Minute, GLPIKBSource: "glpi-kb", GLPIKBAutoReply: true,
GLPIKBAutoReplyITILCategoryIDs: []int64{2}, CommunicationLanguage: "de-DE",
CommunicationStyle: "formal", GLPITimeout: time.Second,
}
st := &fakeStore{}
s := New(cfg, fakeSource{}, st, metrics.New())
if err := s.Sync(context.Background()); err != nil {
t.Fatal(err)
}
if len(st.docs) != 1 || !st.docs[0].AutoReply {
t.Fatalf("mapped ITIL category should approve auto reply: %+v", st.docs)
}
if got := st.docs[0].AutoReplyDetail; !strings.Contains(got, "ITIL-/Ticketkategorie") {
t.Fatalf("approval detail should name ITIL category path, got %q", got)
}
}
func TestSyncExplainsBlockedAutoReply(t *testing.T) {
cfg := config.Config{
DataDir: t.TempDir(), GLPIKBEnabled: true, GLPIKBPath: "auto", GLPIKBLimit: 50,
GLPIKBSyncInterval: time.Minute, GLPIKBSource: "glpi-kb", GLPIKBAutoReply: true,
GLPIKBAutoReplyCategoryIDs: []int64{99}, CommunicationLanguage: "de-DE",
CommunicationStyle: "formal", GLPITimeout: time.Second,
}
st := &fakeStore{}
s := New(cfg, fakeSource{}, st, metrics.New())
if err := s.Sync(context.Background()); err != nil {
t.Fatal(err)
}
if len(st.docs) != 1 || st.docs[0].AutoReply {
t.Fatalf("non-matching whitelist must keep auto reply disabled: %+v", st.docs)
}
if got := st.docs[0].AutoReplyDecision; got != "glpi_kb_category_not_whitelisted" {
t.Fatalf("decision=%q", got)
}
if got := st.docs[0].AutoReplyDetail; !strings.Contains(got, "GLPI-KB-Kategorien: [9]") || !strings.Contains(got, "freigegebene GLPI-KB-Kategorien: [99]") {
t.Fatalf("detail should expose source and configured IDs, got %q", got)
}
}