feat(admin): dev-страница «Эмуляторы» — переключатели и настройки эмуляции НРД и МНП QUIK

В режиме разработки новая страница /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).
This commit is contained in:
zuevav
2026-10-07 21:34:39 +03:00
parent 773c98d81e
commit fd238c1078
16 changed files with 775 additions and 17 deletions
+22
View File
@@ -29,6 +29,7 @@ type admin struct {
wizard, news, keyWizard, prepare *template.Template
handbook *template.Template
m2mtest, m2mpreview, depoSim, depoTest *template.Template
emulators *template.Template
journal *template.Template
}
@@ -418,6 +419,10 @@ func newAdmin() (*admin, error) {
if err != nil {
return nil, fmt.Errorf("parse admin_depo_test: %w", err)
}
emulators, err := parse("admin_emulators.html")
if err != nil {
return nil, fmt.Errorf("parse admin_emulators: %w", err)
}
journal, err := parse("admin_journal.html")
if err != nil {
return nil, fmt.Errorf("parse admin_journal: %w", err)
@@ -433,6 +438,7 @@ func newAdmin() (*admin, error) {
m2mpreview: m2mpreview,
depoSim: depoSim,
depoTest: depoTest,
emulators: emulators,
journal: journal,
}, nil
}
@@ -478,6 +484,10 @@ type homeData struct {
// LKUrl — адрес тестового ЛК (для первичного теста интеграции).
LKUrl string
// Состояние эмуляторов (dev-плитки дашборда, страница «Эмуляторы»).
NSDEmuOn bool
QuikEmuOn bool
}
// claimsData — данные журнала.
@@ -603,6 +613,8 @@ func (a *admin) renderHome(w http.ResponseWriter, r *http.Request, svc *Service,
News: topNews(visibleNews(rc, rc.Snapshot().News.Items), 3),
LKUrl: rc.Snapshot().LK.PublicURL,
}
data.NSDEmuOn, _ = lkEmulateNSD(rc)
data.QuikEmuOn = rc.Snapshot().Quik.Emulate && devMode(rc)
// Готовность — по тем же шагам, что в мастере настройки (с учётом лицензии).
for _, st := range setupSteps(rc) {
data.TotalCount++
@@ -893,6 +905,16 @@ func nowPage(title, active string) page {
if p.DevMode {
p.MockReason = "Провайдер СКЗИ = stub — подпись не делается, реальный обмен с НРД невозможен"
}
case s.LK.EmulateNSD && p.DevMode:
// ИШ настроен, но заявки ЛК идут через имитатор (страница «Эмуляторы») — баннер честный.
p.IsMockMode = true
p.MockReason = "Эмулятор НРД включён вручную — заявки из личного кабинета в НРД не отправляются"
}
if s.Quik.Emulate && p.DevMode {
if p.MockReason != "" {
p.MockReason += "; "
}
p.MockReason += "эмулятор МНП QUIK включён — в боевую базу МНП ничего не пишется"
}
}
return p