fd238c1078
В режиме разработки новая страница /admin/emulators с двумя клавишами: - «Эмулятор НРД» (флаг LK.EmulateNSD): заявки ЛК и ручного ввода идут через внутренний имитатор, в НРД ничего не отправляется. Настройки: задержка решения (0–300 с), исход (подтвердить/отклонить/не отвечать), код отказа M2M01–M2M09/18. Сендер перенастраивается в рантайме через потокобезопасный mock.Sender.Reconfigure — без перезапуска. - «Эмулятор МНП QUIK» (флаг QuikMNP.Emulate): шаги цикла без записи в боевую БД МНП. Настройки: задержка шага (0–60 с), точечные отказы по шагам (регистрация/подпись/отказ/зачисление) — эмулированный отказ ведёт себя как боевой: шаг OK=false, состояние не продвигается, повтор на следующем проходе. Настройки хранятся в setup.json (nsd_emu/mnp_emu), обратная совместимость — дефолты. Баннер «Режим эмуляции» показывается и при ручном включении эмуляторов (раньше зависел только от отсутствия ИШ). Пункт меню «Эмуляторы» и две плитки на дашборде — только в dev. Пути занесены в devOnlyPath. Попутно: исправлен race в TestLicenseActivateAndRenew (cleanup восстанавливал DefaultLicensePublicKey без moduleEnforceMu — падал под -race и на чистом HEAD).
100 lines
3.9 KiB
Go
100 lines
3.9 KiB
Go
package lkgateway
|
|
|
|
import (
|
|
"context"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestLKDepoRejectText(t *testing.T) {
|
|
res := depoResult{Ran: true, Recommend: "reject", Secs: []depoSecResult{{
|
|
Security: "RU0009029540",
|
|
Checks: []depoCheck{
|
|
{Name: "Российский выпуск (RU-ISIN)", OK: true, Code: "M2M01"},
|
|
{Name: "Остаток ≥ количеству (98070.04)", OK: false, Code: "QTY", Detail: "на торговом счёте ВТНКЦ00000001: 0 шт., списываем 10 — недостаточно"},
|
|
{Name: "Задолженность по комиссиям", OK: true, Warn: true},
|
|
},
|
|
}}}
|
|
code, short, text := lkDepoRejectText(res)
|
|
if code != "QTY" {
|
|
t.Errorf("код %q, ожидали QTY", code)
|
|
}
|
|
if short != "Отказ по результатам проверки депозитария: "+m2mCodeLabel("QTY") || strings.Contains(short, "Причина") {
|
|
t.Errorf("короткий текст для ЛК: %q", short)
|
|
}
|
|
for _, want := range []string{"Отказ по результатам проверки депозитария", "(QTY)", "Причина: Остаток ≥ количеству (98070.04): на торговом счёте"} {
|
|
if !strings.Contains(text, want) {
|
|
t.Errorf("в тексте нет %q: %s", want, text)
|
|
}
|
|
}
|
|
if strings.Contains(text, "Российский выпуск") || strings.Contains(text, "комиссиям") {
|
|
t.Errorf("пройденные проверки не должны попадать в причину: %s", text)
|
|
}
|
|
}
|
|
|
|
func TestLKEmulateNSD(t *testing.T) {
|
|
rc, err := NewRuntimeConfig(filepath.Join(t.TempDir(), "setup.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s := rc.Snapshot()
|
|
s.LK.ActiveEnv, s.NSD.Profile = "prod", "test3-gost"
|
|
if err := rc.UpdateLK(s.LK); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := rc.UpdateNSD(s.NSD); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if on, why := lkEmulateNSD(rc); !on || !strings.Contains(why, "тестовом контуре НРД") {
|
|
t.Errorf("боевой ЛК + тестовый ИШ: ожидали имитацию, получили %v %q", on, why)
|
|
}
|
|
s.LK.ActiveEnv = "test"
|
|
_ = rc.UpdateLK(s.LK)
|
|
if on, _ := lkEmulateNSD(rc); on {
|
|
t.Error("тестовый ЛК без флага: имитации быть не должно")
|
|
}
|
|
s.LK.EmulateNSD = true
|
|
_ = rc.UpdateLK(s.LK)
|
|
if on, _ := lkEmulateNSD(rc); on {
|
|
t.Error("флаг в настройках без режима разработки не действует")
|
|
}
|
|
devModeForced = true
|
|
defer func() { devModeForced = false }()
|
|
if on, why := lkEmulateNSD(rc); !on || why != "включена в настройках" {
|
|
t.Errorf("флаг в настройках: %v %q", on, why)
|
|
}
|
|
}
|
|
|
|
// applyNSDEmuSettings переносит настройки со страницы «Эмуляторы» в конфиг работающего
|
|
// сендера: исход и код отказа меняются без пересоздания (канал решений тот же).
|
|
func TestApplyNSDEmuSettings(t *testing.T) {
|
|
old := lkEmuSender
|
|
lkEmuSender = newLKEmuSender()
|
|
defer func() { lkEmuSender.Stop(); lkEmuSender = old }()
|
|
rc, err := NewRuntimeConfig(filepath.Join(t.TempDir(), "setup.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := rc.UpdateNSDEmu(NSDEmuSettings{DelaySec: 1, Outcome: "reject", RejectCode: "18"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
applyNSDEmuSettings(rc)
|
|
if _, err := lkEmuSender.Send(context.Background(), sendFailRequest()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
select {
|
|
case d := <-lkEmuSender.Decisions():
|
|
rej := d.Data.Securities[0].TransferDecision.Rejection
|
|
if rej == nil {
|
|
t.Fatal("ожидался отказ (outcome=reject)")
|
|
}
|
|
if len(rej.Codes) != 1 || rej.Codes[0] != "18" {
|
|
t.Errorf("код отказа %v, ожидался 18", rej.Codes)
|
|
}
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("решение не пришло — задержка из настроек не применилась")
|
|
}
|
|
}
|