Оповещения: группа «Администратор системы», блок «Кому писать» открыт; копии: адрес хранилища как в проводнике
Администратору (адрес из «Почты») — сбои (ИШ, связь с НРД, службы bj, DEPO) и обновления (новый выпуск bj, ИШ, лицензия, сертификаты); отделам — переводы и поручения. Существующие установки: администратор получает всё, что получал. Новые оповещения: «Доступно обновление bj» (раз на выпуск), «Служба … не работает / снова работает» (модули и служба ключей Валидаты). Резервные копии: \\сервер\папка или сервер + общая папка по полям → //сервер/папка; понятная причина отказа доступа. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user