Files
ratatoskr-go/internal/app/app_test.go
Hermes 8fcafcdffd
All checks were successful
CI / test (push) Successful in 42s
CI / build-and-package (amd64, darwin) (push) Successful in 39s
CI / build-and-package (amd64, linux) (push) Successful in 42s
CI / build-and-package (amd64, windows) (push) Successful in 41s
CI / build-and-package (arm64, darwin) (push) Successful in 48s
CI / build-and-package (arm64, linux) (push) Successful in 47s
feat(update): владелец пакета вшивается в бинарь
- -X main.updateOwner (из vars.TC_GITEA_OWNER / Make UPDATE_OWNER), фоллбэк на update.owner в конфиге.
- update.owner больше не обязателен: гейты в startAutoCheck и handleUpdateCmd требуют только update.base_url.
- Обновлены main, app.New (сигнатура), CI, Makefile, тесты.
2026-08-16 12:33:17 +05:00

170 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
)
// TestNew проверяет, что composition root собирается без ошибок.
// Использует :memory: (? или file-based с t.TempDir).
func TestNew(t *testing.T) {
tmp := t.TempDir()
dbPath := filepath.Join(tmp, "test.db")
// Настраиваем минимальные env для обязательных полей
t.Setenv("TG_TOKEN", "test:token")
t.Setenv("TG_CHAT_ID", "12345")
t.Setenv("RATATOSKR_DB", dbPath)
// Загружаем без config-файла (дефолты + env)
a, err := New("", "dev", "", "")
if err != nil {
t.Fatalf("New() err = %v", err)
}
if a.Store == nil {
t.Fatal("Store не создан")
}
if a.Router == nil {
t.Fatal("Router не создан")
}
if a.CoreCtx == nil {
t.Fatal("Core не создан")
}
if a.Worker == nil {
t.Fatal("Worker не создан")
}
if a.tg == nil {
t.Fatal("Telegram-канал не создан")
}
t.Logf("app OK: db=%s", a.Config.Paths.DB)
}
// TestNew_MissingToken проверяет ошибку при пустом токене.
func TestNew_MissingToken(t *testing.T) {
tmp := t.TempDir()
dbPath := filepath.Join(tmp, "test.db")
t.Setenv("TG_TOKEN", "")
t.Setenv("TG_CHAT_ID", "12345")
t.Setenv("RATATOSKR_DB", dbPath)
_, err := New("", "dev", "", "")
if err == nil {
t.Fatal("expected error for missing token")
}
t.Logf("got expected error: %v", err)
}
// TestNew_BadDB проверяет ошибку при пути к БД, который нельзя создать.
func TestNew_BadDB(t *testing.T) {
tmp := t.TempDir()
dbPath := filepath.Join(tmp, "nonexistent", "test.db") // родителя нет
t.Setenv("TG_TOKEN", "test:token")
t.Setenv("TG_CHAT_ID", "12345")
t.Setenv("RATATOSKR_DB", dbPath)
_, err := New("", "dev", "", "")
if err == nil {
t.Fatal("expected error for invalid db path")
}
if !strings.Contains(err.Error(), "db open error") {
t.Errorf("expected 'db open error' in message, got %v", err)
}
}
// TestNew_CtxCancel проверяет, что Run завершается на отмене контекста.
func TestNew_RunCtxCancel(t *testing.T) {
tmp := t.TempDir()
dbPath := filepath.Join(tmp, "test-runcancel.db")
t.Setenv("TG_TOKEN", "test:token")
t.Setenv("TG_CHAT_ID", "12345")
t.Setenv("RATATOSKR_DB", dbPath)
a, err := New("", "dev", "", "")
if err != nil {
t.Fatalf("New() err = %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
cancel() // сразу отменяем
err = a.Run(ctx)
if err != nil {
t.Fatalf("Run() expected nil, got %v", err)
}
t.Log("Run OK on cancelled context")
}
// TestMain — дёргает fatalf, нормально что os.Exit не тестируется.
// Просто тест, что main.go компилируется.
func TestMain(t *testing.T) {
// main() не вызываем — он бы os.Exit
_, err := os.Stat("../../cmd/ratatoskr/main.go")
if err != nil {
t.Fatalf("main.go not found: %v", err)
}
t.Log("main.go exists")
}
// TestNew_UpdateWiring проверяет: 1) update.base_url берётся из update-блока
// (не git.base_url); 2) приоритет токена: вшитый updateToken > update.token;
// 3) приоритет владельца: вшитый updateOwner > update.owner.
func TestNew_UpdateWiring(t *testing.T) {
tmp := t.TempDir()
dbPath := filepath.Join(tmp, "test.db")
configPath := filepath.Join(tmp, "config.yaml")
content := strings.Join([]string{
"telegram:",
" token: \"test:token\"",
" chat_id: \"12345\"",
"git:",
" base_url: \"http://local-git/\"",
" token: \"git-token\"",
"update:",
" enabled: true",
" base_url: \"https://hub.example.com\"",
" token: \"cfg-update-token\"",
" owner: \"cfg-owner\"",
"paths:",
" db: \"" + dbPath + "\"",
"", // пустая строка в конце
}, "\n")
if err := os.WriteFile(configPath, []byte(content), 0o600); err != nil {
t.Fatalf("write config: %v", err)
}
// 1) base_url из update-блока, а НЕ git
a, err := New(configPath, "dev", "", "")
if err != nil {
t.Fatalf("New() err = %v", err)
}
if a.Updater == nil {
t.Fatal("Updater не создан")
}
if want := "https://hub.example.com"; a.Updater.BaseURL != want {
t.Errorf("BaseURL = %q, want %q (должен быть update.base_url, не git.base_url)", a.Updater.BaseURL, want)
}
// без вшитого токена и владельца берём их из конфига
if a.Updater.Token != "cfg-update-token" {
t.Errorf("Token = %q, want cfg-update-token (при пустом вшитом)", a.Updater.Token)
}
if a.Updater.Owner != "cfg-owner" {
t.Errorf("Owner = %q, want cfg-owner (при пустом вшитом)", a.Updater.Owner)
}
// 2) вшитый updateToken перекрывает конфиг
a2, err := New(configPath, "dev", "embedded-update-token", "embedded-owner")
if err != nil {
t.Fatalf("New() err = %v", err)
}
if a2.Updater.Token != "embedded-update-token" {
t.Errorf("Token = %q, want embedded-update-token (вшитый приоритетнее)", a2.Updater.Token)
}
// 3) вшитый updateOwner перекрывает конфиг
if a2.Updater.Owner != "embedded-owner" {
t.Errorf("Owner = %q, want embedded-owner (вшитый приоритетнее)", a2.Updater.Owner)
}
}