mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 11:30:15 +00:00
Add ability for users to describe what kind of agent profile they need in natural language, and have AI generate a suggestion with name, description, config values, and rationale. - Add ProfileSuggestionHandler with schema-aware prompting - Add SuggestProfileModal component with example prompts - Update AgentProfilesPanel with suggest button and description field - Streamline ValidConfigKeys to only agent-supported settings - Update profile validation tests for simplified schema
409 lines
9.7 KiB
Go
409 lines
9.7 KiB
Go
package models
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestProfileValidator_ValidateStringType(t *testing.T) {
|
|
validator := NewProfileValidator()
|
|
|
|
tests := []struct {
|
|
name string
|
|
config AgentConfigMap
|
|
wantErr bool
|
|
errKey string
|
|
}{
|
|
{
|
|
name: "valid string",
|
|
config: AgentConfigMap{"report_ip": "192.168.1.100"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid empty string",
|
|
config: AgentConfigMap{"report_ip": ""},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "invalid string type",
|
|
config: AgentConfigMap{"report_ip": 123},
|
|
wantErr: true,
|
|
errKey: "report_ip",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := validator.Validate(tt.config)
|
|
if tt.wantErr && result.Valid {
|
|
t.Errorf("expected validation to fail, but it passed")
|
|
}
|
|
if !tt.wantErr && !result.Valid {
|
|
t.Errorf("expected validation to pass, but it failed: %v", result.Errors)
|
|
}
|
|
if tt.wantErr && len(result.Errors) > 0 {
|
|
found := false
|
|
for _, err := range result.Errors {
|
|
if err.Key == tt.errKey {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected error for key %s, but got: %v", tt.errKey, result.Errors)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProfileValidator_ValidateBoolType(t *testing.T) {
|
|
validator := NewProfileValidator()
|
|
|
|
tests := []struct {
|
|
name string
|
|
config AgentConfigMap
|
|
wantErr bool
|
|
errKey string
|
|
}{
|
|
{
|
|
name: "valid bool true",
|
|
config: AgentConfigMap{"enable_docker": true},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid bool false",
|
|
config: AgentConfigMap{"enable_docker": false},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "invalid bool type - string",
|
|
config: AgentConfigMap{"enable_docker": "true"},
|
|
wantErr: true,
|
|
errKey: "enable_docker",
|
|
},
|
|
{
|
|
name: "invalid bool type - int",
|
|
config: AgentConfigMap{"enable_docker": 1},
|
|
wantErr: true,
|
|
errKey: "enable_docker",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := validator.Validate(tt.config)
|
|
if tt.wantErr && result.Valid {
|
|
t.Errorf("expected validation to fail, but it passed")
|
|
}
|
|
if !tt.wantErr && !result.Valid {
|
|
t.Errorf("expected validation to pass, but it failed: %v", result.Errors)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Note: Int type validation tests removed - no schema keys currently use ConfigTypeInt.
|
|
// The validation logic exists in validateValue() and can be tested if int keys are added.
|
|
|
|
// Note: Float type validation tests removed - no schema keys currently use ConfigTypeFloat.
|
|
// The validation logic exists in validateValue() and can be tested if float keys are added.
|
|
|
|
func TestProfileValidator_ValidateDurationType(t *testing.T) {
|
|
validator := NewProfileValidator()
|
|
|
|
tests := []struct {
|
|
name string
|
|
config AgentConfigMap
|
|
wantErr bool
|
|
errKey string
|
|
}{
|
|
{
|
|
name: "valid duration seconds",
|
|
config: AgentConfigMap{"interval": "30s"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid duration minutes",
|
|
config: AgentConfigMap{"interval": "5m"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid duration hours",
|
|
config: AgentConfigMap{"interval": "1h"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid duration complex",
|
|
config: AgentConfigMap{"interval": "1h30m45s"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "invalid duration format",
|
|
config: AgentConfigMap{"interval": "30"},
|
|
wantErr: true,
|
|
errKey: "interval",
|
|
},
|
|
{
|
|
name: "invalid duration - not a string",
|
|
config: AgentConfigMap{"interval": 30},
|
|
wantErr: true,
|
|
errKey: "interval",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := validator.Validate(tt.config)
|
|
if tt.wantErr && result.Valid {
|
|
t.Errorf("expected validation to fail, but it passed")
|
|
}
|
|
if !tt.wantErr && !result.Valid {
|
|
t.Errorf("expected validation to pass, but it failed: %v", result.Errors)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProfileValidator_ValidateEnumType(t *testing.T) {
|
|
validator := NewProfileValidator()
|
|
|
|
tests := []struct {
|
|
name string
|
|
config AgentConfigMap
|
|
wantErr bool
|
|
errKey string
|
|
}{
|
|
{
|
|
name: "valid enum debug",
|
|
config: AgentConfigMap{"log_level": "debug"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid enum info",
|
|
config: AgentConfigMap{"log_level": "info"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid enum warn",
|
|
config: AgentConfigMap{"log_level": "warn"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid enum error",
|
|
config: AgentConfigMap{"log_level": "error"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "invalid enum value",
|
|
config: AgentConfigMap{"log_level": "trace"},
|
|
wantErr: true,
|
|
errKey: "log_level",
|
|
},
|
|
{
|
|
name: "invalid enum type",
|
|
config: AgentConfigMap{"log_level": 1},
|
|
wantErr: true,
|
|
errKey: "log_level",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := validator.Validate(tt.config)
|
|
if tt.wantErr && result.Valid {
|
|
t.Errorf("expected validation to fail, but it passed")
|
|
}
|
|
if !tt.wantErr && !result.Valid {
|
|
t.Errorf("expected validation to pass, but it failed: %v", result.Errors)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProfileValidator_UnknownKeysWarning(t *testing.T) {
|
|
validator := NewProfileValidator()
|
|
|
|
config := AgentConfigMap{
|
|
"interval": "30s",
|
|
"unknown_key": "some_value",
|
|
}
|
|
|
|
result := validator.Validate(config)
|
|
|
|
// Should still be valid (warnings don't fail validation)
|
|
if !result.Valid {
|
|
t.Errorf("expected validation to pass with warnings, but it failed: %v", result.Errors)
|
|
}
|
|
|
|
// Should have a warning for unknown key
|
|
if len(result.Warnings) == 0 {
|
|
t.Errorf("expected a warning for unknown key, but got none")
|
|
}
|
|
|
|
found := false
|
|
for _, w := range result.Warnings {
|
|
if w.Key == "unknown_key" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected warning for 'unknown_key', got: %v", result.Warnings)
|
|
}
|
|
}
|
|
|
|
func TestProfileValidator_ComplexConfig(t *testing.T) {
|
|
validator := NewProfileValidator()
|
|
|
|
config := AgentConfigMap{
|
|
"interval": "30s",
|
|
"enable_host": true,
|
|
"enable_docker": true,
|
|
"enable_kubernetes": false,
|
|
"enable_proxmox": true,
|
|
"proxmox_type": "pve",
|
|
"docker_runtime": "auto",
|
|
"log_level": "info",
|
|
"disable_auto_update": false,
|
|
"disable_docker_update_checks": false,
|
|
"report_ip": "192.168.1.100",
|
|
}
|
|
|
|
result := validator.Validate(config)
|
|
|
|
if !result.Valid {
|
|
t.Errorf("expected complex config to be valid, but got errors: %v", result.Errors)
|
|
}
|
|
}
|
|
|
|
func TestProfileValidator_NullValue(t *testing.T) {
|
|
validator := NewProfileValidator()
|
|
|
|
config := AgentConfigMap{
|
|
"interval": nil,
|
|
}
|
|
|
|
result := validator.Validate(config)
|
|
|
|
// Null values should be valid for non-required keys
|
|
if !result.Valid {
|
|
t.Errorf("expected null value to be valid for non-required key, but got errors: %v", result.Errors)
|
|
}
|
|
}
|
|
|
|
func TestGetConfigKeyDefinitions(t *testing.T) {
|
|
defs := GetConfigKeyDefinitions()
|
|
|
|
if len(defs) == 0 {
|
|
t.Error("expected config key definitions to be non-empty")
|
|
}
|
|
|
|
// Check some known keys exist (keys actually applied by the agent)
|
|
expectedKeys := []string{"interval", "enable_docker", "log_level", "enable_host", "docker_runtime"}
|
|
for _, key := range expectedKeys {
|
|
found := false
|
|
for _, def := range defs {
|
|
if def.Key == key {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected key %s to be in definitions", key)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetConfigKeyDefinition(t *testing.T) {
|
|
def, found := GetConfigKeyDefinition("interval")
|
|
if !found {
|
|
t.Error("expected to find 'interval' definition")
|
|
}
|
|
if def.Type != ConfigTypeDuration {
|
|
t.Errorf("expected 'interval' type to be Duration, got %s", def.Type)
|
|
}
|
|
|
|
_, found = GetConfigKeyDefinition("nonexistent_key")
|
|
if found {
|
|
t.Error("expected not to find 'nonexistent_key' definition")
|
|
}
|
|
}
|
|
|
|
func TestAgentProfile_MergedConfig(t *testing.T) {
|
|
parentProfile := AgentProfile{
|
|
ID: "parent-1",
|
|
Name: "Parent Profile",
|
|
Config: AgentConfigMap{
|
|
"interval": "30s",
|
|
"enable_docker": true,
|
|
"log_level": "info",
|
|
},
|
|
}
|
|
|
|
childProfile := AgentProfile{
|
|
ID: "child-1",
|
|
Name: "Child Profile",
|
|
ParentID: "parent-1",
|
|
Config: AgentConfigMap{
|
|
"interval": "10s", // Override parent
|
|
"log_level": "debug", // Override parent
|
|
},
|
|
}
|
|
|
|
profiles := []AgentProfile{parentProfile, childProfile}
|
|
|
|
merged := childProfile.MergedConfig(profiles)
|
|
|
|
// Child's interval should override parent's
|
|
if merged["interval"] != "10s" {
|
|
t.Errorf("expected interval to be '10s', got %v", merged["interval"])
|
|
}
|
|
|
|
// Child's log_level should override parent's
|
|
if merged["log_level"] != "debug" {
|
|
t.Errorf("expected log_level to be 'debug', got %v", merged["log_level"])
|
|
}
|
|
|
|
// Parent's enable_docker should be inherited
|
|
if merged["enable_docker"] != true {
|
|
t.Errorf("expected enable_docker to be true, got %v", merged["enable_docker"])
|
|
}
|
|
}
|
|
|
|
func TestAgentProfile_MergedConfigNoParent(t *testing.T) {
|
|
profile := AgentProfile{
|
|
ID: "profile-1",
|
|
Name: "Standalone Profile",
|
|
Config: AgentConfigMap{
|
|
"interval": "30s",
|
|
},
|
|
}
|
|
|
|
profiles := []AgentProfile{profile}
|
|
|
|
merged := profile.MergedConfig(profiles)
|
|
|
|
if merged["interval"] != "30s" {
|
|
t.Errorf("expected interval to be '30s', got %v", merged["interval"])
|
|
}
|
|
}
|
|
|
|
func TestAgentProfile_MergedConfigParentNotFound(t *testing.T) {
|
|
profile := AgentProfile{
|
|
ID: "child-1",
|
|
Name: "Child Profile",
|
|
ParentID: "nonexistent-parent",
|
|
Config: AgentConfigMap{
|
|
"interval": "10s",
|
|
},
|
|
}
|
|
|
|
profiles := []AgentProfile{profile}
|
|
|
|
// Should return just the child's config if parent not found
|
|
merged := profile.MergedConfig(profiles)
|
|
|
|
if merged["interval"] != "10s" {
|
|
t.Errorf("expected interval to be '10s', got %v", merged["interval"])
|
|
}
|
|
}
|