Files
ratatoskr-go/internal/config/config_test.go
2026-08-14 22:59:23 +05:00

211 lines
4.9 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")
t.Setenv("GITEA_URL", "http://gitea.home")
t.Setenv("GITEA_TOKEN", "gtok")
t.Setenv("GITEA_USER", "kamelion")
yaml := `
telegram:
token: "${TG_TOKEN}"
chat_id: "123"
gitea:
url: "${GITEA_URL}"
token: "${GITEA_TOKEN}"
user: "${GITEA_USER}"
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.Gitea.URL != "http://gitea.home" {
t.Errorf("gitea url = %q", cfg.Gitea.URL)
}
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")
t.Setenv("GITEA_URL", "http://x")
t.Setenv("GITEA_TOKEN", "secret")
t.Setenv("GITEA_USER", "u")
yaml := `
telegram:
token: "${TG_TOKEN}"
chat_id: "${TG_CHAT_ID}"
gitea:
url: "${GITEA_URL}"
token: "${GITEA_TOKEN}"
user: "${GITEA_USER}"
`
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 != "/opt/data/ratatoskr/worktrees" {
t.Errorf("worktree = %q", cfg.Paths.Worktree)
}
}
func TestLoad_EnvOverride(t *testing.T) {
t.Setenv("TG_TOKEN", "tok")
t.Setenv("TG_CHAT_ID", "42")
t.Setenv("GITEA_URL", "http://x")
t.Setenv("GITEA_TOKEN", "secret")
t.Setenv("GITEA_USER", "u")
// env override для opencode.timeout
t.Setenv("OPENCODE_CONFIG", "/custom/opencode.json")
yaml := `telegram:
token: "${TG_TOKEN}"
chat_id: "${TG_CHAT_ID}"
gitea:
url: "${GITEA_URL}"
token: "${GITEA_TOKEN}"
user: "${GITEA_USER}"
`
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")
t.Setenv("GITEA_URL", "http://x")
t.Setenv("GITEA_TOKEN", "secret")
t.Setenv("GITEA_USER", "u")
yaml := `telegram:
token: "${TG_TOKEN}"
chat_id: "${TG_CHAT_ID}"
gitea:
url: "${GITEA_URL}"
token: "${GITEA_TOKEN}"
user: "${GITEA_USER}"
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("GITEA_URL", "") // не ставим
t.Setenv("GITEA_TOKEN", "")
t.Setenv("TG_TOKEN", "")
t.Setenv("TG_CHAT_ID", "")
t.Setenv("GITEA_USER", "")
yaml := `telegram:
token: ""
chat_id: ""
gitea:
url: ""
token: ""
user: ""
`
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)
}
}
func TestLoad_InvalidDuration(t *testing.T) {
t.Setenv("TG_TOKEN", "x")
t.Setenv("TG_CHAT_ID", "x")
t.Setenv("GITEA_URL", "x")
t.Setenv("GITEA_TOKEN", "x")
t.Setenv("GITEA_USER", "x")
yaml := `telegram:
token: "x"
chat_id: "x"
gitea:
url: "x"
token: "x"
user: "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)
}
}