All checks were successful
CI / test (push) Successful in 43s
CI / build-and-package (amd64, darwin) (push) Successful in 40s
CI / build-and-package (amd64, linux) (push) Successful in 39s
CI / build-and-package (amd64, windows) (push) Successful in 41s
CI / build-and-package (arm64, darwin) (push) Successful in 42s
CI / build-and-package (arm64, linux) (push) Successful in 41s
195 lines
4.6 KiB
Go
195 lines
4.6 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func writeCfg(t *testing.T, content string) string {
|
|
t.Helper()
|
|
f, err := os.CreateTemp(t.TempDir(), "cfg*.yaml")
|
|
if err != nil {
|
|
t.Fatalf("temp: %v", err)
|
|
}
|
|
if _, err := f.WriteString(content); err != nil {
|
|
t.Fatalf("write: %v", err)
|
|
}
|
|
f.Close()
|
|
return f.Name()
|
|
}
|
|
|
|
func TestLoad_FullConfig(t *testing.T) {
|
|
t.Setenv("TG_TOKEN", "tok123")
|
|
|
|
yaml := `
|
|
telegram:
|
|
token: "${TG_TOKEN}"
|
|
chat_id: "123"
|
|
|
|
opencode:
|
|
bin: "my-opencode"
|
|
hard_timeout: "10m"
|
|
`
|
|
path := writeCfg(t, yaml)
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
if cfg.Telegram.Token != "tok123" {
|
|
t.Errorf("tg token = %q", cfg.Telegram.Token)
|
|
}
|
|
if cfg.Telegram.ChatID != "123" {
|
|
t.Errorf("tg chat_id = %q", cfg.Telegram.ChatID)
|
|
}
|
|
if cfg.OpenCode.Bin != "my-opencode" {
|
|
t.Errorf("opencode bin = %q", cfg.OpenCode.Bin)
|
|
}
|
|
if cfg.OpenCode.HardTimeout.Duration() != 10*time.Minute {
|
|
t.Errorf("hard timeout = %v", cfg.OpenCode.HardTimeout)
|
|
}
|
|
}
|
|
|
|
func TestLoad_Defaults(t *testing.T) {
|
|
t.Setenv("TG_TOKEN", "tok")
|
|
t.Setenv("TG_CHAT_ID", "42")
|
|
|
|
yaml := `
|
|
telegram:
|
|
token: "${TG_TOKEN}"
|
|
chat_id: "${TG_CHAT_ID}"
|
|
`
|
|
path := writeCfg(t, yaml)
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
// default opencode.bin
|
|
if cfg.OpenCode.Bin != "opencode" {
|
|
t.Errorf("bin = %q, want opencode", cfg.OpenCode.Bin)
|
|
}
|
|
if cfg.OpenCode.HardTimeout.Duration() != 20*time.Minute {
|
|
t.Errorf("hard timeout = %v", cfg.OpenCode.HardTimeout)
|
|
}
|
|
if cfg.OpenCode.IdleTimeout.Duration() != 2*time.Minute {
|
|
t.Errorf("idle timeout = %v", cfg.OpenCode.IdleTimeout)
|
|
}
|
|
if cfg.Paths.Worktree != "./worktrees" {
|
|
t.Errorf("worktree = %q, want ./worktrees", cfg.Paths.Worktree)
|
|
}
|
|
}
|
|
|
|
func TestLoad_OpenCodeConfigDir(t *testing.T) {
|
|
t.Setenv("TG_TOKEN", "tok")
|
|
t.Setenv("TG_CHAT_ID", "42")
|
|
|
|
// без config_dir — пусто
|
|
yaml := `telegram:
|
|
token: "${TG_TOKEN}"
|
|
chat_id: "${TG_CHAT_ID}"
|
|
`
|
|
cfg, err := Load(writeCfg(t, yaml))
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
if cfg.OpenCode.ConfigDir != "" {
|
|
t.Errorf("config_dir = %q, want пусто", cfg.OpenCode.ConfigDir)
|
|
}
|
|
|
|
// через env OPENCODE_CONFIG_DIR
|
|
t.Setenv("OPENCODE_CONFIG_DIR", "/opt/agents")
|
|
cfg, err = Load(writeCfg(t, yaml))
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
if cfg.OpenCode.ConfigDir != "/opt/agents" {
|
|
t.Errorf("config_dir = %q, want /opt/agents", cfg.OpenCode.ConfigDir)
|
|
}
|
|
}
|
|
|
|
func TestLoad_EnvOverride(t *testing.T) {
|
|
t.Setenv("TG_TOKEN", "tok")
|
|
t.Setenv("TG_CHAT_ID", "42")
|
|
// env override для opencode.timeout
|
|
t.Setenv("OPENCODE_CONFIG", "/custom/opencode.json")
|
|
|
|
yaml := `telegram:
|
|
token: "${TG_TOKEN}"
|
|
chat_id: "${TG_CHAT_ID}"
|
|
`
|
|
path := writeCfg(t, yaml)
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
if cfg.OpenCode.Config != "/custom/opencode.json" {
|
|
t.Errorf("opencode config = %q", cfg.OpenCode.Config)
|
|
}
|
|
}
|
|
|
|
func TestLoad_MissingEnvVarUsesDefault(t *testing.T) {
|
|
t.Setenv("TG_TOKEN", "tok")
|
|
t.Setenv("TG_CHAT_ID", "42")
|
|
|
|
yaml := `telegram:
|
|
token: "${TG_TOKEN}"
|
|
chat_id: "${TG_CHAT_ID}"
|
|
opencode:
|
|
db_path: "${OPENCODE_DB:-/default/db.sqlite}"
|
|
`
|
|
path := writeCfg(t, yaml)
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
if cfg.OpenCode.DBPath != "/default/db.sqlite" {
|
|
t.Errorf("db_path = %q, want /default/db.sqlite", cfg.OpenCode.DBPath)
|
|
}
|
|
}
|
|
|
|
func TestLoad_FileNotFound(t *testing.T) {
|
|
_, err := Load("/nonexistent/config.yaml")
|
|
if err == nil || !strings.Contains(err.Error(), "file not found") {
|
|
t.Fatalf("ожидалась ошибка file not found, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoad_MissingRequired(t *testing.T) {
|
|
// пустой конфиг — все обязательные поля отсутствуют
|
|
t.Setenv("TG_TOKEN", "")
|
|
t.Setenv("TG_CHAT_ID", "")
|
|
|
|
yaml := `telegram:
|
|
token: ""
|
|
chat_id: ""
|
|
`
|
|
path := writeCfg(t, yaml)
|
|
_, err := Load(path)
|
|
if err == nil {
|
|
t.Fatal("ожидалась ошибка валидации")
|
|
}
|
|
if !strings.Contains(err.Error(), "telegram.token") {
|
|
t.Errorf("ожидалось telegram.token в ошибке, got %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "telegram.chat_id") {
|
|
t.Errorf("ожидалось telegram.chat_id в ошибке, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoad_InvalidDuration(t *testing.T) {
|
|
t.Setenv("TG_TOKEN", "x")
|
|
t.Setenv("TG_CHAT_ID", "x")
|
|
|
|
yaml := `telegram:
|
|
token: "x"
|
|
chat_id: "x"
|
|
opencode:
|
|
hard_timeout: "не-длительность"
|
|
`
|
|
path := writeCfg(t, yaml)
|
|
_, err := Load(path)
|
|
if err == nil || !strings.Contains(err.Error(), "parse duration") {
|
|
t.Fatalf("ожидалась ошибка парсинга duration, got %v", err)
|
|
}
|
|
} |