Load agent/cursor env overrides in config loader; pass root .env plus environment-specific env files in env-service; align compose env_file mounts. Co-authored-by: Cursor <cursoragent@cursor.com>
147 lines
4.7 KiB
Go
147 lines
4.7 KiB
Go
package config_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"chat-agent-system/internal/config"
|
|
)
|
|
|
|
func TestConfigLoadDefaultsAndFile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
envPath := filepath.Join(tmpDir, ".env")
|
|
envContent := `ENV=dev
|
|
DATA_DIR=./data/test_data
|
|
AGENT_BACKEND=pi-agent
|
|
AGENT_TIMEOUT=15m
|
|
KB_DB_PORT=55432
|
|
`
|
|
if err := os.WriteFile(envPath, []byte(envContent), 0o644); err != nil {
|
|
t.Fatalf("failed to write test env: %v", err)
|
|
}
|
|
|
|
cfg, err := config.Load(envPath)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error loading config: %v", err)
|
|
}
|
|
|
|
if cfg.Env != "dev" {
|
|
t.Errorf("expected Env 'dev', got %q", cfg.Env)
|
|
}
|
|
if cfg.DataDir != "./data/test_data" {
|
|
t.Errorf("expected DataDir './data/test_data', got %q", cfg.DataDir)
|
|
}
|
|
if cfg.AgentBackend != "pi-agent" {
|
|
t.Errorf("expected AgentBackend 'pi-agent', got %q", cfg.AgentBackend)
|
|
}
|
|
if cfg.AgentTimeout != 15*time.Minute {
|
|
t.Errorf("expected AgentTimeout 15m, got %v", cfg.AgentTimeout)
|
|
}
|
|
if cfg.KbDbPort != 55432 {
|
|
t.Errorf("expected KbDbPort 55432, got %d", cfg.KbDbPort)
|
|
}
|
|
}
|
|
|
|
func TestConfigLoadDSNOverrides(t *testing.T) {
|
|
t.Setenv("APP_DB_DSN", "postgres://app_user:secret_pass@system-db:5432/agent_system_dev?sslmode=disable")
|
|
t.Setenv("POSTGRES_SHARED_KB_DSN", "postgres://kb_user:kb_pass@shuimu-kb-shared:5432/regulations?sslmode=disable")
|
|
t.Setenv("HTTP_ADDR", ":9090")
|
|
t.Setenv("PI_AGENT_RUNNER_URL", "http://pi-agent:18865")
|
|
|
|
cfg, err := config.Load("/dev/null")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error loading config: %v", err)
|
|
}
|
|
|
|
if cfg.ContextDbHost != "system-db" || cfg.ContextDbPort != 5432 || cfg.ContextDbUser != "app_user" || cfg.ContextDbName != "agent_system_dev" {
|
|
t.Fatalf("unexpected context db config: %+v", cfg)
|
|
}
|
|
if cfg.KbDbHost != "shuimu-kb-shared" || cfg.KbDbPort != 5432 || cfg.KbDbUser != "kb_user" || cfg.KbDbName != "regulations" {
|
|
t.Fatalf("unexpected kb db config: %+v", cfg)
|
|
}
|
|
if cfg.HTTPPort != 9090 || cfg.HTTPHost != "0.0.0.0" {
|
|
t.Fatalf("expected HTTP 0.0.0.0:9090, got %s:%d", cfg.HTTPHost, cfg.HTTPPort)
|
|
}
|
|
if cfg.PiAgentRunnerURL != "http://pi-agent:18865" {
|
|
t.Fatalf("expected pi-agent runner url override, got %q", cfg.PiAgentRunnerURL)
|
|
}
|
|
}
|
|
|
|
func TestConfigLoadRuntimeEnvironmentOverrides(t *testing.T) {
|
|
t.Setenv("ENV", "dev")
|
|
t.Setenv("AGENT_TIMEOUT", "47m")
|
|
t.Setenv("CURSOR_API_KEY", "cursor-test-key")
|
|
t.Setenv("CURSOR_MODEL", "cursor-test-model")
|
|
t.Setenv("CURSOR_BIN", "/test/cursor")
|
|
t.Setenv("CURSOR_BRIDGE_BIN", "/test/cursor-bridge.mjs")
|
|
t.Setenv("PI_AGENT_BRIDGE_BIN", "/test/pi-bridge.mjs")
|
|
t.Setenv("EMBEDDING_MODEL", "embedding-test-model")
|
|
t.Setenv("RERANKER_MODEL", "reranker-test-model")
|
|
|
|
cfg, err := config.Load("/dev/null")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error loading config: %v", err)
|
|
}
|
|
if cfg.AgentTimeout != 47*time.Minute {
|
|
t.Fatalf("expected environment AgentTimeout 47m, got %v", cfg.AgentTimeout)
|
|
}
|
|
if cfg.CursorAPIKey != "cursor-test-key" || cfg.CursorModel != "cursor-test-model" || cfg.CursorBin != "/test/cursor" || cfg.CursorBridgeBin != "/test/cursor-bridge.mjs" {
|
|
t.Fatalf("cursor environment overrides were not applied")
|
|
}
|
|
if cfg.PiAgentBridgeBin != "/test/pi-bridge.mjs" {
|
|
t.Fatalf("pi bridge environment override was not applied")
|
|
}
|
|
if cfg.EmbeddingModel != "embedding-test-model" || cfg.RerankerModel != "reranker-test-model" {
|
|
t.Fatalf("retrieval model environment overrides were not applied")
|
|
}
|
|
}
|
|
|
|
func TestConfigLoadCORSDefaultsForDev(t *testing.T) {
|
|
cfg, err := config.Load("/dev/null")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(cfg.CORSAllowedOrigins) == 0 {
|
|
t.Fatal("expected default CORS origins for dev")
|
|
}
|
|
}
|
|
|
|
func TestConfigLoadRejectsAllowInMemoryInMain(t *testing.T) {
|
|
t.Setenv("ENV", "main")
|
|
t.Setenv("KB_DB_PASSWORD", "kb-prod-secret-xyz")
|
|
t.Setenv("CONTEXT_DB_PASSWORD", "ctx-prod-secret-xyz")
|
|
t.Setenv("ADMIN_PASSWORD", "admin-prod-secret-xyz")
|
|
t.Setenv("ALLOW_IN_MEMORY", "true")
|
|
|
|
_, err := config.Load("/dev/null")
|
|
if err == nil {
|
|
t.Fatal("expected config load to fail when ALLOW_IN_MEMORY=true in main")
|
|
}
|
|
}
|
|
|
|
func TestConfigLoadRejectsWeakSecretsOutsideDev(t *testing.T) {
|
|
t.Setenv("ENV", "main")
|
|
t.Setenv("KB_DB_PASSWORD", "")
|
|
t.Setenv("CONTEXT_DB_PASSWORD", "")
|
|
t.Setenv("ADMIN_PASSWORD", "")
|
|
|
|
_, err := config.Load("/dev/null")
|
|
if err == nil {
|
|
t.Fatal("expected config load to fail for missing secrets in main env")
|
|
}
|
|
}
|
|
|
|
func TestConfigLoadRejectsProductionDatabasePlaceholder(t *testing.T) {
|
|
t.Setenv("ENV", "prod")
|
|
t.Setenv("KB_DB_PASSWORD", "kb-prod-secret-xyz")
|
|
t.Setenv("CONTEXT_DB_PASSWORD", "prod_highly_secure_db_pass")
|
|
t.Setenv("ADMIN_PASSWORD", "admin-prod-secret-xyz")
|
|
|
|
_, err := config.Load("/dev/null")
|
|
if err == nil {
|
|
t.Fatal("expected config load to reject the documented production database placeholder")
|
|
}
|
|
}
|