Add tests
This commit is contained in:
377
internal/config/config_test.go
Normal file
377
internal/config/config_test.go
Normal file
@@ -0,0 +1,377 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
configYAML string
|
||||
envVars map[string]string
|
||||
expectError bool
|
||||
validate func(t *testing.T, cfg *Config)
|
||||
}{
|
||||
{
|
||||
name: "basic config with all fields",
|
||||
configYAML: `
|
||||
server:
|
||||
address: ":8080"
|
||||
providers:
|
||||
openai:
|
||||
type: openai
|
||||
api_key: sk-test-key
|
||||
anthropic:
|
||||
type: anthropic
|
||||
api_key: sk-ant-key
|
||||
models:
|
||||
- name: gpt-4
|
||||
provider: openai
|
||||
provider_model_id: gpt-4-turbo
|
||||
- name: claude-3
|
||||
provider: anthropic
|
||||
provider_model_id: claude-3-sonnet-20240229
|
||||
auth:
|
||||
enabled: true
|
||||
issuer: https://accounts.google.com
|
||||
audience: my-client-id
|
||||
conversations:
|
||||
store: memory
|
||||
ttl: 1h
|
||||
`,
|
||||
validate: func(t *testing.T, cfg *Config) {
|
||||
assert.Equal(t, ":8080", cfg.Server.Address)
|
||||
assert.Len(t, cfg.Providers, 2)
|
||||
assert.Equal(t, "openai", cfg.Providers["openai"].Type)
|
||||
assert.Equal(t, "sk-test-key", cfg.Providers["openai"].APIKey)
|
||||
assert.Len(t, cfg.Models, 2)
|
||||
assert.Equal(t, "gpt-4", cfg.Models[0].Name)
|
||||
assert.True(t, cfg.Auth.Enabled)
|
||||
assert.Equal(t, "memory", cfg.Conversations.Store)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "config with environment variables",
|
||||
configYAML: `
|
||||
server:
|
||||
address: ":8080"
|
||||
providers:
|
||||
openai:
|
||||
type: openai
|
||||
api_key: ${OPENAI_API_KEY}
|
||||
models:
|
||||
- name: gpt-4
|
||||
provider: openai
|
||||
provider_model_id: gpt-4
|
||||
`,
|
||||
envVars: map[string]string{
|
||||
"OPENAI_API_KEY": "sk-from-env",
|
||||
},
|
||||
validate: func(t *testing.T, cfg *Config) {
|
||||
assert.Equal(t, "sk-from-env", cfg.Providers["openai"].APIKey)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minimal config",
|
||||
configYAML: `
|
||||
server:
|
||||
address: ":8080"
|
||||
providers:
|
||||
openai:
|
||||
type: openai
|
||||
api_key: test-key
|
||||
models:
|
||||
- name: gpt-4
|
||||
provider: openai
|
||||
`,
|
||||
validate: func(t *testing.T, cfg *Config) {
|
||||
assert.Equal(t, ":8080", cfg.Server.Address)
|
||||
assert.Len(t, cfg.Providers, 1)
|
||||
assert.Len(t, cfg.Models, 1)
|
||||
assert.False(t, cfg.Auth.Enabled)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "azure openai provider",
|
||||
configYAML: `
|
||||
server:
|
||||
address: ":8080"
|
||||
providers:
|
||||
azure:
|
||||
type: azure_openai
|
||||
api_key: azure-key
|
||||
endpoint: https://my-resource.openai.azure.com
|
||||
api_version: "2024-02-15-preview"
|
||||
models:
|
||||
- name: gpt-4-azure
|
||||
provider: azure
|
||||
provider_model_id: gpt-4-deployment
|
||||
`,
|
||||
validate: func(t *testing.T, cfg *Config) {
|
||||
assert.Equal(t, "azure_openai", cfg.Providers["azure"].Type)
|
||||
assert.Equal(t, "azure-key", cfg.Providers["azure"].APIKey)
|
||||
assert.Equal(t, "https://my-resource.openai.azure.com", cfg.Providers["azure"].Endpoint)
|
||||
assert.Equal(t, "2024-02-15-preview", cfg.Providers["azure"].APIVersion)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vertex ai provider",
|
||||
configYAML: `
|
||||
server:
|
||||
address: ":8080"
|
||||
providers:
|
||||
vertex:
|
||||
type: vertex_ai
|
||||
project: my-gcp-project
|
||||
location: us-central1
|
||||
models:
|
||||
- name: gemini-pro
|
||||
provider: vertex
|
||||
provider_model_id: gemini-1.5-pro
|
||||
`,
|
||||
validate: func(t *testing.T, cfg *Config) {
|
||||
assert.Equal(t, "vertex_ai", cfg.Providers["vertex"].Type)
|
||||
assert.Equal(t, "my-gcp-project", cfg.Providers["vertex"].Project)
|
||||
assert.Equal(t, "us-central1", cfg.Providers["vertex"].Location)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sql conversation store",
|
||||
configYAML: `
|
||||
server:
|
||||
address: ":8080"
|
||||
providers:
|
||||
openai:
|
||||
type: openai
|
||||
api_key: test-key
|
||||
models:
|
||||
- name: gpt-4
|
||||
provider: openai
|
||||
conversations:
|
||||
store: sql
|
||||
driver: sqlite3
|
||||
dsn: conversations.db
|
||||
ttl: 2h
|
||||
`,
|
||||
validate: func(t *testing.T, cfg *Config) {
|
||||
assert.Equal(t, "sql", cfg.Conversations.Store)
|
||||
assert.Equal(t, "sqlite3", cfg.Conversations.Driver)
|
||||
assert.Equal(t, "conversations.db", cfg.Conversations.DSN)
|
||||
assert.Equal(t, "2h", cfg.Conversations.TTL)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "redis conversation store",
|
||||
configYAML: `
|
||||
server:
|
||||
address: ":8080"
|
||||
providers:
|
||||
openai:
|
||||
type: openai
|
||||
api_key: test-key
|
||||
models:
|
||||
- name: gpt-4
|
||||
provider: openai
|
||||
conversations:
|
||||
store: redis
|
||||
dsn: redis://localhost:6379/0
|
||||
ttl: 30m
|
||||
`,
|
||||
validate: func(t *testing.T, cfg *Config) {
|
||||
assert.Equal(t, "redis", cfg.Conversations.Store)
|
||||
assert.Equal(t, "redis://localhost:6379/0", cfg.Conversations.DSN)
|
||||
assert.Equal(t, "30m", cfg.Conversations.TTL)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid model references unknown provider",
|
||||
configYAML: `
|
||||
server:
|
||||
address: ":8080"
|
||||
providers:
|
||||
openai:
|
||||
type: openai
|
||||
api_key: test-key
|
||||
models:
|
||||
- name: gpt-4
|
||||
provider: unknown_provider
|
||||
`,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid YAML",
|
||||
configYAML: `invalid: yaml: content: [unclosed`,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "multiple models same provider",
|
||||
configYAML: `
|
||||
server:
|
||||
address: ":8080"
|
||||
providers:
|
||||
openai:
|
||||
type: openai
|
||||
api_key: test-key
|
||||
models:
|
||||
- name: gpt-4
|
||||
provider: openai
|
||||
provider_model_id: gpt-4-turbo
|
||||
- name: gpt-3.5
|
||||
provider: openai
|
||||
provider_model_id: gpt-3.5-turbo
|
||||
- name: gpt-4-mini
|
||||
provider: openai
|
||||
provider_model_id: gpt-4o-mini
|
||||
`,
|
||||
validate: func(t *testing.T, cfg *Config) {
|
||||
assert.Len(t, cfg.Models, 3)
|
||||
for _, model := range cfg.Models {
|
||||
assert.Equal(t, "openai", model.Provider)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create temporary config file
|
||||
tmpDir := t.TempDir()
|
||||
configPath := filepath.Join(tmpDir, "config.yaml")
|
||||
err := os.WriteFile(configPath, []byte(tt.configYAML), 0644)
|
||||
require.NoError(t, err, "failed to write test config file")
|
||||
|
||||
// Set environment variables
|
||||
for key, value := range tt.envVars {
|
||||
t.Setenv(key, value)
|
||||
}
|
||||
|
||||
// Load config
|
||||
cfg, err := Load(configPath)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err, "expected an error")
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err, "unexpected error loading config")
|
||||
require.NotNil(t, cfg, "config should not be nil")
|
||||
|
||||
if tt.validate != nil {
|
||||
tt.validate(t, cfg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadNonExistentFile(t *testing.T) {
|
||||
_, err := Load("/nonexistent/config.yaml")
|
||||
assert.Error(t, err, "should error on nonexistent file")
|
||||
}
|
||||
|
||||
func TestConfigValidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config Config
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "valid config",
|
||||
config: Config{
|
||||
Providers: map[string]ProviderEntry{
|
||||
"openai": {Type: "openai"},
|
||||
},
|
||||
Models: []ModelEntry{
|
||||
{Name: "gpt-4", Provider: "openai"},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "model references unknown provider",
|
||||
config: Config{
|
||||
Providers: map[string]ProviderEntry{
|
||||
"openai": {Type: "openai"},
|
||||
},
|
||||
Models: []ModelEntry{
|
||||
{Name: "gpt-4", Provider: "unknown"},
|
||||
},
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "no models",
|
||||
config: Config{
|
||||
Providers: map[string]ProviderEntry{
|
||||
"openai": {Type: "openai"},
|
||||
},
|
||||
Models: []ModelEntry{},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "multiple models multiple providers",
|
||||
config: Config{
|
||||
Providers: map[string]ProviderEntry{
|
||||
"openai": {Type: "openai"},
|
||||
"anthropic": {Type: "anthropic"},
|
||||
},
|
||||
Models: []ModelEntry{
|
||||
{Name: "gpt-4", Provider: "openai"},
|
||||
{Name: "claude-3", Provider: "anthropic"},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.config.validate()
|
||||
if tt.expectError {
|
||||
assert.Error(t, err, "expected validation error")
|
||||
} else {
|
||||
assert.NoError(t, err, "unexpected validation error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvironmentVariableExpansion(t *testing.T) {
|
||||
configYAML := `
|
||||
server:
|
||||
address: "${SERVER_ADDRESS}"
|
||||
providers:
|
||||
openai:
|
||||
type: openai
|
||||
api_key: ${OPENAI_KEY}
|
||||
anthropic:
|
||||
type: anthropic
|
||||
api_key: ${ANTHROPIC_KEY:-default-key}
|
||||
models:
|
||||
- name: gpt-4
|
||||
provider: openai
|
||||
`
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
configPath := filepath.Join(tmpDir, "config.yaml")
|
||||
err := os.WriteFile(configPath, []byte(configYAML), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set only some env vars to test defaults
|
||||
t.Setenv("SERVER_ADDRESS", ":9090")
|
||||
t.Setenv("OPENAI_KEY", "sk-from-env")
|
||||
// Don't set ANTHROPIC_KEY to test default value
|
||||
|
||||
cfg, err := Load(configPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, ":9090", cfg.Server.Address)
|
||||
assert.Equal(t, "sk-from-env", cfg.Providers["openai"].APIKey)
|
||||
// Note: Go's os.Expand doesn't support default values like ${VAR:-default}
|
||||
// This is just documenting current behavior
|
||||
}
|
||||
Reference in New Issue
Block a user