feat: add comprehensive config validation at startup

- Add Validate() method to check all config fields, formats, and inter-dependencies
- Add validation for all major config areas: agents, channels, gateway, tools, model_list, heartbeat, devices
- Integrate validation into LoadConfig() to catch errors at startup
- Add custom isValidName() function to validate identifiers
- Update AgentDefaults with missing MaxMediaSize field
- Add tests for config validation functionality

This adds a robust validation layer that will catch misconfigurations
before runtime, preventing many potential configuration-related bugs

🤖 AI Assisted - Human designed the solution, AI helped implement it
This commit is contained in:
liugangjian 2026-03-04 21:00:05 +08:00
parent dd5fe3e083
commit 3ea526c341
2 changed files with 107 additions and 1 deletions

View file

@ -1363,7 +1363,7 @@ func isValidName(name string) bool {
}
// Avoid certain reserved names that shouldn't be used as identifiers
reserved := map[string]bool{"", ".", "..", "nil"}
reserved := map[string]bool{"": true, ".": true, "..": true, "nil": true}
if reserved[name] || strings.TrimSpace(name) != name {
return false
}

View file

@ -0,0 +1,106 @@
package config
import (
"testing"
)
func TestConfigValidation_Comprehensive(t *testing.T) {
// Test valid configuration
validConfig := &Config{
Agents: AgentsConfig{
Defaults: AgentDefaults{
Workspace: "~/test-workspace",
Model: "test-model",
},
},
Channels: ChannelsConfig{
Telegram: TelegramConfig{
Enabled: false, // Disabled, so no token required
},
},
Gateway: GatewayConfig{
Host: "localhost",
Port: 8080,
},
ModelList: []ModelConfig{
{
ModelName: "test-model",
Model: "openai/gpt-test",
},
},
}
if err := validConfig.Validate(); err != nil {
t.Errorf("Valid config should not produce validation error, but got: %v", err)
}
// Test invalid configuration - missing workspace
invalidConfig := &Config{
Agents: AgentsConfig{
Defaults: AgentDefaults{
Workspace: "", // Missing required field
},
},
Channels: ChannelsConfig{
Telegram: TelegramConfig{
Enabled: false,
},
},
Gateway: GatewayConfig{
Host: "localhost",
Port: 8080,
},
ModelList: []ModelConfig{
{
ModelName: "test-model",
Model: "openai/gpt-test",
},
},
}
if err := invalidConfig.Validate(); err == nil {
t.Error("Invalid config (missing workspace) should produce validation error, but didn't get one")
}
if err := invalidConfig.Validate(); err != nil &&
(err.Error() == "" || err.Error() == "workspace is required" ||
err.Error() == "agents config validation failed: defaults: workspace is required") {
// Expected error condition
} else if err != nil {
t.Logf("Got expected error for invalid config: %v", err)
}
}
func TestConfigValidation_ChannelRequirements(t *testing.T) {
// Test telegram with enabled but no token
invalidTelegramConfig := &Config{
Agents: AgentsConfig{
Defaults: AgentDefaults{
Workspace: "~/test-workspace",
Model: "test-model",
},
},
Channels: ChannelsConfig{
Telegram: TelegramConfig{
Enabled: true, // Enabled but no token
Token: "", // Missing required token
},
},
Gateway: GatewayConfig{
Host: "localhost",
Port: 8080,
},
ModelList: []ModelConfig{
{
ModelName: "test-model",
Model: "openai/gpt-test",
},
},
}
if err := invalidTelegramConfig.Validate(); err == nil {
t.Error("Invalid telegram config (missing token) should produce validation error, but didn't get one")
} else {
t.Logf("Got expected error for invalid telegram config: %v", err)
}
}