Files
glpi-ai-agent/internal/config/config_test.go
T
groot a10225d74c
release-tag / release-image (push) Successful in 1m41s
AI-Label hinzugefügt
2026-07-29 14:53:12 +02:00

195 lines
5.9 KiB
Go

package config
import (
"testing"
"time"
)
func validConfig() Config {
return Config{
GLPIURL: "https://glpi.internal.example",
GLPIClientID: "client-id",
GLPIClientSecret: "real-secret-value",
GLPIUsername: "svc-agent",
GLPIPassword: "real-password-value",
GLPIAllowedStatusIDs: []int64{1},
WebAllowAnonymous: true,
Workers: 1,
QueueSize: 1,
CategoryConfidence: .9,
ReplyConfidence: .9,
KnowledgeMinScore: .8,
KnowledgeAllowedSources: []string{"internal-kb"},
KnowledgeAutoReplySources: []string{"internal-kb"},
CommunicationLanguage: "de-DE",
CommunicationStyle: "formal",
OllamaTimeout: time.Minute,
OllamaNumPredict: 256,
OllamaKeepAlive: 10 * time.Minute,
OllamaThink: false,
OllamaMaxConcurrent: 1,
}
}
func TestValidateRejectsPlainHTTPByDefault(t *testing.T) {
c := validConfig()
c.GLPIURL = "http://glpi.internal.example"
if err := c.Validate(); err == nil {
t.Fatal("expected plain HTTP to be rejected")
}
c.GLPIAllowInsecureHTTP = true
if err := c.Validate(); err != nil {
t.Fatalf("explicit insecure override should validate: %v", err)
}
}
func TestValidateRejectsPlaceholderSecrets(t *testing.T) {
c := validConfig()
c.GLPIClientSecret = "CHANGE_ME"
if err := c.Validate(); err == nil {
t.Fatal("expected placeholder secret to be rejected")
}
}
func TestValidateRequiresAllowedStatus(t *testing.T) {
c := validConfig()
c.GLPIAllowedStatusIDs = nil
if err := c.Validate(); err == nil {
t.Fatal("expected empty status whitelist to be rejected")
}
}
func TestValidateRejectsAutoReplySourceOutsideAllowlist(t *testing.T) {
c := validConfig()
c.KnowledgeAutoReplySources = []string{"vendor-docs"}
if err := c.Validate(); err == nil {
t.Fatal("expected auto-reply source outside allowlist to be rejected")
}
}
func TestValidateRejectsUnknownCommunicationStyle(t *testing.T) {
c := validConfig()
c.CommunicationStyle = "super-friendly"
if err := c.Validate(); err == nil {
t.Fatal("expected unsupported communication style to be rejected")
}
}
func TestValidateRequiresMajorIncidentFilter(t *testing.T) {
c := validConfig()
c.ContextEnabled = true
c.ContextTimeout = 1
c.MajorIncidentsEnabled = true
c.GLPIMajorIncidentLimit = 20
if err := c.Validate(); err == nil {
t.Fatal("expected major incident filter to be required")
}
}
func TestValidateUptimeKumaMetricsRequiresAPIKey(t *testing.T) {
c := validConfig()
c.ContextEnabled = true
c.ContextTimeout = 1
c.UptimeKumaEnabled = true
c.UptimeKumaURL = "https://uptime.internal.example"
c.UptimeKumaMode = "metrics"
c.UptimeKumaMaxIssues = 20
if err := c.Validate(); err == nil {
t.Fatal("expected Uptime Kuma API key to be required in metrics mode")
}
c.UptimeKumaAPIKey = "test-api-key"
if err := c.Validate(); err != nil {
t.Fatalf("expected metrics mode with API key to validate: %v", err)
}
}
func TestValidateUserDeviceFilterRequiresUserPlaceholder(t *testing.T) {
c := validConfig()
c.ContextEnabled = true
c.ContextTimeout = 1
c.UserDeviceContextEnabled = true
c.GLPIUserDevicePaths = []string{"/Assets/Computer"}
c.GLPIUserDeviceFilterTemplate = "user.id==42"
c.GLPIUserDeviceLimit = 20
if err := c.Validate(); err == nil {
t.Fatal("expected user device filter template to require {{user_id}}")
}
}
func TestKnowledgeWebEditRequiresAuthentication(t *testing.T) {
c := validConfig()
c.KnowledgeWebEditEnabled = true
c.WebAllowAnonymous = true
if err := c.Validate(); err == nil {
t.Fatal("expected anonymous KB editing to be rejected")
}
}
func TestValidateGLPIKBRequiresAllowedSource(t *testing.T) {
c := validConfig()
c.GLPIKBEnabled = true
c.GLPIKBPath = "auto"
c.GLPIKBLimit = 100
c.GLPIKBSyncInterval = 10 * time.Minute
c.GLPIKBSource = "glpi-kb"
if err := c.Validate(); err == nil {
t.Fatal("expected GLPI KB source outside allowlist to be rejected")
}
c.KnowledgeAllowedSources = []string{"internal-kb", "glpi-kb"}
if err := c.Validate(); err != nil {
t.Fatalf("expected GLPI KB config to validate: %v", err)
}
}
func TestValidateGLPIKBAutoReplyRequiresCategoryWhitelist(t *testing.T) {
c := validConfig()
c.GLPIKBEnabled = true
c.GLPIKBPath = "auto"
c.GLPIKBLimit = 100
c.GLPIKBSyncInterval = 10 * time.Minute
c.GLPIKBSource = "glpi-kb"
c.KnowledgeAllowedSources = []string{"internal-kb", "glpi-kb"}
c.KnowledgeAutoReplySources = []string{"internal-kb", "glpi-kb"}
c.GLPIKBAutoReply = true
if err := c.Validate(); err == nil {
t.Fatal("expected auto reply category whitelist to be required")
}
c.GLPIKBAutoReplyCategoryIDs = []int64{3}
if err := c.Validate(); err != nil {
t.Fatalf("expected explicit category whitelist to validate: %v", err)
}
}
func TestValidateRejectsInvalidKnowledgeScoringConfig(t *testing.T) {
c := validConfig()
c.KnowledgeSemanticWeight = 1.2
if err := c.Validate(); err == nil {
t.Fatal("expected knowledge weight > 1 to be rejected")
}
c = validConfig()
c.KnowledgeChunkWords = 100
c.KnowledgeChunkOverlapWords = 100
if err := c.Validate(); err == nil {
t.Fatal("expected overlap >= chunk size to be rejected")
}
}
func TestValidateKnowledgeCategoryModeTrimsWhitespace(t *testing.T) {
c := validConfig()
c.KnowledgeCategoryMode = " UNSCOPED "
if err := c.Validate(); err != nil {
t.Fatalf("expected whitespace/case normalized category mode to validate: %v", err)
}
}
func TestEnvNormalizedLowerDefaultsBlankOrWhitespace(t *testing.T) {
t.Setenv("KNOWLEDGE_CATEGORY_MODE", " ")
if got := envNormalizedLower("KNOWLEDGE_CATEGORY_MODE", "unscoped"); got != "unscoped" {
t.Fatalf("expected unscoped default, got %q", got)
}
t.Setenv("KNOWLEDGE_CATEGORY_MODE", " STRICT ")
if got := envNormalizedLower("KNOWLEDGE_CATEGORY_MODE", "unscoped"); got != "strict" {
t.Fatalf("expected strict, got %q", got)
}
}