Files
Bridge-and-Join-s/internal/lkgateway/notifyadmin_test.go
T
zuevav d23ddf6e0c Оповещения: группа «Администратор системы», блок «Кому писать» открыт; копии: адрес хранилища как в проводнике
Администратору (адрес из «Почты») — сбои (ИШ, связь с НРД, службы bj, DEPO) и обновления (новый выпуск bj,
ИШ, лицензия, сертификаты); отделам — переводы и поручения. Существующие установки: администратор получает
всё, что получал. Новые оповещения: «Доступно обновление bj» (раз на выпуск), «Служба … не работает /
снова работает» (модули и служба ключей Валидаты). Резервные копии: \\сервер\папка или сервер + общая
папка по полям → //сервер/папка; понятная причина отказа доступа.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
2026-10-06 22:44:35 +03:00

139 lines
6.3 KiB
Go

package lkgateway
import (
"path/filepath"
"strings"
"testing"
)
// Сбои и обновления — отдельные типы (по умолчанию — администратору системы), деловые — отделам.
func TestAlertKindAdmin(t *testing.T) {
cases := map[string]string{
"ИШ НРД недоступен": "infra",
"Перезапуск ИШ НРД не помог (попытка 1 из 3)": "infra",
"НРД не отвечает шлюзу": "infra",
"Компонент не отвечает: poll-incoming": "infra",
"Служба «Подпись» не работает": "infra",
"DEPO: вход логином bj_user не удался": "infra",
"Доступно обновление bj 2026.10.06.24": "updates",
"ИШ обновлён до 107.0.930": "updates",
"Лицензия: сервер лицензий отказал в продлении": "updates",
"Поддержка bj заканчивается через 10 дней": "updates",
"Сертификат ключа организации истекает через 5": "updates",
"НРД: депо-поручение не исполнено в срок 5 минут": "orders",
"QUIK МНП: не удалось зачислить бумаги": "quik",
"Перевод не доставлен в НРД": "system",
"DEPO: джоба не обработала перевод ab12": "system",
}
for title, want := range cases {
if got := alertKind(title); got != want {
t.Errorf("%q: %s, ожидалось %s", title, got, want)
}
}
}
// Существующая установка: администратор (адрес из «Почты») получает всё, что получал раньше; выключенные
// уведомления о переводах — пустой маршрут. Новая установка — маршруты по умолчанию.
func TestNotifyRoutesMigration(t *testing.T) {
var old Settings
old.Alerts.SMTP.To = "it@example.ru"
old.Incoming.Emails = "depo@example.ru"
migrateNotifyRoutes(&old)
r := old.Incoming.Routes
for _, k := range []string{"orders", "quik", "infra", "updates", "system"} {
if !old.Incoming.RouteHas(k, "admin") {
t.Errorf("%s: администратор должен остаться получателем (%q)", k, r[k])
}
}
if r["incoming"] != "" || old.Incoming.RouteHas("incoming", "depo") || !old.Incoming.Enabled || old.Incoming.RoutesV != 2 {
t.Errorf("выключенные уведомления о переводах: %+v", old.Incoming)
}
if !old.Incoming.RouteHas("infra", "back") || !old.Incoming.RouteHas("orders", "depo") {
t.Errorf("сбои — тем же отделам, что «прочие» раньше: %v", r)
}
if got := notifyRecipients(old, "infra"); got != "it@example.ru" {
t.Errorf("сбои: %q", got)
}
migrateNotifyRoutes(&old) // повторно — без изменений
if old.Incoming.Routes["orders"] != r["orders"] || strings.Count(old.Incoming.Routes["orders"], "admin") != 1 {
t.Errorf("повторная миграция: %v", old.Incoming.Routes)
}
var fresh Settings
migrateNotifyRoutes(&fresh)
fresh.Alerts.SMTP.To = "it@example.ru"
fresh.Incoming.Emails, fresh.Incoming.EmailsBack = "depo@example.ru", "back@example.ru"
if fresh.Incoming.Routes != nil || !fresh.Incoming.Enabled {
t.Fatalf("новая установка: %+v", fresh.Incoming)
}
want := map[string]string{
"incoming": "depo@example.ru, back@example.ru",
"orders": "depo@example.ru, back@example.ru",
"infra": "it@example.ru",
"updates": "it@example.ru",
"system": "it@example.ru, back@example.ru",
}
for k, w := range want {
if got := notifyRecipients(fresh, k); got != w {
t.Errorf("%s: %q, ожидалось %q", k, got, w)
}
}
}
// Служба пишет администратору, только пролежав две проверки подряд, один раз; поднялась — второе письмо.
func TestUnitsWatchTick(t *testing.T) {
rc, err := NewRuntimeConfig(filepath.Join(t.TempDir(), "setup.json"))
if err != nil {
t.Fatal(err)
}
unitsWatch.Lock()
unitsWatch.down, unitsWatch.alerted = map[string]int{}, map[string]bool{}
unitsWatch.Unlock()
down := []watchedUnit{{Key: "bj-npr", Title: "Риск-монитор", Unit: "bj-npr", State: "failed/failed"}}
up := []watchedUnit{{Key: "bj-npr", Title: "Риск-монитор", Unit: "bj-npr", OK: true}}
count := func(sub string) int {
n := 0
for _, it := range rc.Snapshot().News.Items {
if strings.Contains(it.Title, sub) {
n++
}
}
return n
}
unitsWatchTick(rc, down)
if count("не работает") != 0 {
t.Fatal("после одной проверки письма быть не должно")
}
unitsWatchTick(rc, down)
unitsWatchTick(rc, down)
if count("не работает") != 1 {
t.Fatalf("оповещений о сбое: %d", count("не работает"))
}
unitsWatchTick(rc, up)
if count("снова работает") != 1 {
t.Fatalf("оповещений о восстановлении: %d", count("снова работает"))
}
}
// Адрес хранилища копий — как его вводят люди: в проводнике \\сервер\папка, или сервер и общая папка по разным полям.
func TestSMBLocation(t *testing.T) {
cases := []struct{ share, dir, wantShare, wantDir string }{
{`\\10.10.10.11\`, "backup_test", "//10.10.10.11/backup_test", ""},
{`\\10.10.10.11\backup_test`, "", "//10.10.10.11/backup_test", ""},
{`\\nas\backup\bj\copies`, "x", "//nas/backup", "bj/copies/x"},
{"//nas/backup", "bj-backup", "//nas/backup", "bj-backup"},
{"smb://nas/backup/", "", "//nas/backup", ""},
{"nas", "", "//nas", ""},
{"", "bj", "", "bj"},
}
for _, c := range cases {
s, d := smbLocation(c.share, c.dir)
if s != c.wantShare || d != c.wantDir {
t.Errorf("%q + %q → %q %q, ожидалось %q %q", c.share, c.dir, s, d, c.wantShare, c.wantDir)
}
}
if smbShareOK("//nas") || !smbShareOK("//nas/backup") || smbWin("//nas/backup") != `\\nas\backup` {
t.Error("smbShareOK/smbWin")
}
}