a431d1ec95
Add OpenAI-compatible providers, TUI, slash commands, and model switching. Add local file tools plus optional yolo shell execution. Document config, usage, and development workflow.
258 lines
5.9 KiB
Go
258 lines
5.9 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestLoadExpandsEnvAndDefaults(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("AGENTU_TEST_KEY", "sk-test")
|
|
path := filepath.Join(dir, "agentu.yaml")
|
|
if err := os.WriteFile(path, []byte(`
|
|
providers:
|
|
test:
|
|
base_url: https://example.com
|
|
model: test-model
|
|
api_key: ${AGENTU_TEST_KEY}
|
|
agent:
|
|
working_dir: .
|
|
`), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
provider := cfg.Providers[cfg.DefaultProviderName()]
|
|
if provider.APIKey != "sk-test" {
|
|
t.Fatalf("api key = %q", provider.APIKey)
|
|
}
|
|
if providerName := cfg.DefaultProviderName(); providerName != "test" {
|
|
t.Fatalf("default provider = %q", providerName)
|
|
}
|
|
if cfg.Agent.ToolTimeout != 30*time.Second {
|
|
t.Fatalf("tool timeout = %s", cfg.Agent.ToolTimeout)
|
|
}
|
|
if !filepath.IsAbs(cfg.Agent.WorkingDir) {
|
|
t.Fatalf("working dir is not absolute: %s", cfg.Agent.WorkingDir)
|
|
}
|
|
}
|
|
|
|
func TestLoadUsesDefaultConfigPath(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("HOME", dir)
|
|
t.Setenv("AGENTU_TEST_KEY", "sk-test")
|
|
configDir := filepath.Join(dir, ".agentu")
|
|
if err := os.MkdirAll(configDir, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := filepath.Join(configDir, "config.yaml")
|
|
if err := os.WriteFile(path, []byte(`
|
|
providers:
|
|
test:
|
|
base_url: https://example.com
|
|
model: test-model
|
|
api_key: ${AGENTU_TEST_KEY}
|
|
`), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := Load("")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := cfg.DefaultProviderName(); got != "test" {
|
|
t.Fatalf("default provider = %q", got)
|
|
}
|
|
}
|
|
|
|
func TestResolvePathExpandsHome(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("HOME", dir)
|
|
|
|
got, err := ResolvePath("~/.agentu/config.yaml")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := filepath.Join(dir, ".agentu", "config.yaml")
|
|
if got != want {
|
|
t.Fatalf("path = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestLoadDefaultSystemPromptDiscouragesEagerTools(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("AGENTU_TEST_KEY", "sk-test")
|
|
path := filepath.Join(dir, "agentu.yaml")
|
|
if err := os.WriteFile(path, []byte(`
|
|
providers:
|
|
test:
|
|
base_url: https://example.com
|
|
model: test-model
|
|
api_key: ${AGENTU_TEST_KEY}
|
|
`), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, want := range []string{"Answer simple conversation directly", "Use tools only when"} {
|
|
if !strings.Contains(cfg.Agent.SystemPrompt, want) {
|
|
t.Fatalf("system prompt missing %q: %s", want, cfg.Agent.SystemPrompt)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLoadMultiProviderConfig(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("AGENTU_TEST_KEY", "sk-test")
|
|
path := filepath.Join(dir, "agentu.yaml")
|
|
if err := os.WriteFile(path, []byte(`
|
|
providers:
|
|
loveuer:
|
|
base_url: https://example.com
|
|
api_key: ${AGENTU_TEST_KEY}
|
|
model: model-a
|
|
models:
|
|
- model-a
|
|
- model-b
|
|
thinking: xhigh
|
|
thinking_param: thinking
|
|
backup:
|
|
base_url: https://backup.example.com
|
|
api_key: ${AGENTU_TEST_KEY}
|
|
models:
|
|
- model-c
|
|
`), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := cfg.DefaultProviderName(); got != "backup" {
|
|
t.Fatalf("default provider = %q", got)
|
|
}
|
|
provider := cfg.Providers["loveuer"]
|
|
if got := provider.Model; got != "model-a" {
|
|
t.Fatalf("active model = %q", got)
|
|
}
|
|
if got := cfg.Providers["backup"].Model; got != "model-c" {
|
|
t.Fatalf("backup default model = %q", got)
|
|
}
|
|
if !provider.ThinkingConfigured || provider.Thinking != "xhigh" || provider.ThinkingParam != "thinking" {
|
|
t.Fatalf("thinking config = %#v", provider)
|
|
}
|
|
}
|
|
|
|
func TestLoadDefaultProviderNameIsDeterministic(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("AGENTU_TEST_KEY", "sk-test")
|
|
path := filepath.Join(dir, "agentu.yaml")
|
|
if err := os.WriteFile(path, []byte(`
|
|
providers:
|
|
two:
|
|
base_url: https://two.example.com
|
|
api_key: ${AGENTU_TEST_KEY}
|
|
model: model-b
|
|
one:
|
|
base_url: https://one.example.com
|
|
api_key: ${AGENTU_TEST_KEY}
|
|
model: model-a
|
|
`), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := cfg.DefaultProviderName(); got != "one" {
|
|
t.Fatalf("default provider = %q", got)
|
|
}
|
|
}
|
|
|
|
func TestLoadRejectsInvalidThinking(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("AGENTU_TEST_KEY", "sk-test")
|
|
path := filepath.Join(dir, "agentu.yaml")
|
|
if err := os.WriteFile(path, []byte(`
|
|
providers:
|
|
test:
|
|
base_url: https://example.com
|
|
model: test-model
|
|
api_key: ${AGENTU_TEST_KEY}
|
|
thinking: huge
|
|
`), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err := Load(path)
|
|
if err == nil || !strings.Contains(err.Error(), "thinking") {
|
|
t.Fatalf("err = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoadRequiresProviderFields(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "agentu.yaml")
|
|
if err := os.WriteFile(path, []byte(`
|
|
providers:
|
|
test:
|
|
base_url: https://example.com
|
|
`), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err := Load(path)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestLoadRejectsLegacyProviderConfig(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "agentu.yaml")
|
|
if err := os.WriteFile(path, []byte(`
|
|
provider:
|
|
base_url: https://example.com
|
|
model: test-model
|
|
api_key: sk-test
|
|
`), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err := Load(path)
|
|
if err == nil || !strings.Contains(err.Error(), "field provider not found") {
|
|
t.Fatalf("err = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoadRejectsActiveProviderConfig(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "agentu.yaml")
|
|
if err := os.WriteFile(path, []byte(`
|
|
active_provider: loveuer
|
|
providers:
|
|
loveuer:
|
|
base_url: https://example.com
|
|
model: test-model
|
|
api_key: sk-test
|
|
`), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err := Load(path)
|
|
if err == nil || !strings.Contains(err.Error(), "field active_provider not found") {
|
|
t.Fatalf("err = %v", err)
|
|
}
|
|
}
|