Files
backup-master/internal/agent/collect_test.go
T
mrleo1nidandClaude Opus 5.5 97c7051021
CI / test (push) Successful in 1m36s
Release / release (push) Successful in 1m33s
Restore: перевод бэкапов на восстановленный сервер
Раньше восстановленная копия получала конфиг и таймер агента со старым токеном
(через категории etc и units), и бэкапы шли с двух серверов одновременно.

- конфиг и юниты агента больше не бэкапятся
- в конце restore спрашивает, сделать ли сервер основным: сервер бэкапов
  выпускает новый токен (POST /api/v1/restore/takeover), restore ставит
  агента; токен старого сервера отозван — его агент получает отказ
- --takeover / --no-takeover; без них — вопрос (с --yes — да)
- уведомление «Сервер восстановлен» в ntfy/Telegram

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 15:29:34 +03:00

97 lines
3.2 KiB
Go

package agent
import (
"os"
"path/filepath"
"slices"
"testing"
"gitea.hsrv.site/mrleo1nid/backup-master/internal/archive"
"gitea.hsrv.site/mrleo1nid/backup-master/internal/catalog"
)
func TestExpandPaths(t *testing.T) {
dir := t.TempDir()
for _, n := range []string{"ipcheck.dat", "geoip.dat", "x.txt"} {
if err := os.WriteFile(filepath.Join(dir, n), nil, 0o644); err != nil {
t.Fatal(err)
}
}
got := expandPaths([]string{filepath.Join(dir, "*.dat"), "/etc/x-ui", filepath.Join(dir, "*.none")})
want := []string{filepath.Join(dir, "geoip.dat"), filepath.Join(dir, "ipcheck.dat"), "/etc/x-ui"}
if !slices.Equal(got, want) {
t.Fatalf("expandPaths = %v, want %v", got, want)
}
}
// Свои geo-файлы попадают в бэкап, штатные из релиза 3x-ui — нет.
func TestXUIGeoExcludes(t *testing.T) {
cat, err := catalog.Default()
if err != nil {
t.Fatal(err)
}
s := cat.Find("xui")
skip := archive.NewMatcher(cat.Exclude, s.Exclude)
cases := map[string]bool{
"/usr/local/x-ui/bin/ipcheck.dat": false,
"/usr/local/x-ui/bin/discord-geosite.dat": false,
"/usr/local/x-ui/bin/geoip.dat": true,
"/usr/local/x-ui/bin/geosite_RU.dat": true,
"/usr/local/x-ui/bin/geoip_IR.dat": true,
"/usr/local/x-ui/bin/ipcheck.dat.bak": true,
"/etc/x-ui/x-ui.db": true,
}
for p, want := range cases {
if got := skip.Match(p); got != want {
t.Errorf("exclude(%s) = %v, want %v", p, got, want)
}
}
f2b := cat.Find("fail2ban")
if f2b == nil || !slices.Contains(f2b.Paths, "/etc/fail2ban/jail.d") || !slices.Contains(f2b.Paths, "/etc/fail2ban/filter.d/3x-ipl.conf") {
t.Fatalf("категория fail2ban: %+v", f2b)
}
}
func TestUnitExecPaths(t *testing.T) {
f := filepath.Join(t.TempDir(), "x.service")
unit := "[Service]\nExecStartPre=-/opt/app/prepare.sh --x\nExecStart=/usr/local/bin/app run\nExecReload=/bin/kill -HUP $MAINPID\nEnvironment=A=/tmp/x\n"
if err := os.WriteFile(f, []byte(unit), 0o644); err != nil {
t.Fatal(err)
}
got := unitExecPaths(f)
want := []string{"/opt/app/prepare.sh", "/usr/local/bin/app", "/bin/kill"}
if !slices.Equal(got, want) {
t.Fatalf("unitExecPaths = %v, want %v", got, want)
}
}
func TestEtcCategoryExcludes(t *testing.T) {
cat, err := catalog.Default()
if err != nil {
t.Fatal(err)
}
s := cat.Find("etc")
if s == nil || !s.ModifiedConffiles {
t.Fatal("нет категории etc")
}
skip := archive.NewMatcher(cat.Exclude, s.Exclude)
for p, want := range map[string]bool{
"/etc/netplan/50-cloud-init.yaml": true,
"/etc/apt/sources.list.d/ubuntu.sources": true,
"/etc/systemd/system/my.service": true,
"/etc/fstab": true,
"/etc/wireguard/wg0.conf": false,
"/etc/nginx/sites-available/site": false,
"/etc/security/limits.conf": false,
"/etc/systemd/journald.conf": false,
"/etc/backup-agent/agent.yaml": true,
} {
if got := skip.Match(p); got != want {
t.Errorf("etc exclude(%s) = %v, want %v", p, got, want)
}
}
if cat.Find("units") == nil || cat.Find("ssh") == nil || cat.Find("docker") == nil {
t.Fatal("нет категорий units/ssh/docker")
}
}