9737c787f9
Инфраструктура M2M (живой обмен с НРД через ИШ): - обработка M2MTransferResponse: ERROR(M2Mxx) → заявка Отклонена, сохранение ответа; INFO → ждём Decision; идемпотентность поллера - fallback-корреляция ответов с нулевым GUID (M2M14/M2M17) по FIFO - сырой XML ответа НРД в карточке заявки (для пересылки в ТП) - тестовый пакет роботу приведён к эталону m2m_robot_samples (CostInfo=Yes, 4 бумаги, IsolationStatus, DocumentSeries=сценарий); override паспорта - редирект из теста сразу в карточку заявки Мастер установки ключа Валидаты на флешку (admin/setup/keywizard): - пошаговый: загрузка .7z+пароль → выбор флешки → запись → справочник сертификатов (CRL) → перезапуск+проверка ИШ → готово - привилегированный воркер (bj-keymedia) в host-namespace через файл-обмен, bj-server остаётся в песочнице - сохранение структуры профиля архива (spr<N>), перечисление съёмных USB Прочее: - пакет-доказательство для ТП НРД + форма регистрации участника M2M - эталонные образцы робота (DOC/m2m_robot_samples) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
86 lines
3.9 KiB
Go
86 lines
3.9 KiB
Go
package m2mcore_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2m"
|
|
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2mcore"
|
|
)
|
|
|
|
// makeResponse строит M2MTransferResponse сервиса МОСТ с заданным статусом и
|
|
// одним кодом/текстом (как в реальном ответе НРД, напр. M2M14).
|
|
func makeResponse(guid m2m.UUID, status m2m.StatusCode, code, text string) *m2m.M2MTransferResponse {
|
|
return &m2m.M2MTransferResponse{
|
|
GUID: guid,
|
|
StatusCode: status,
|
|
Responses: []m2m.Response{{Code: code, Text: text}},
|
|
}
|
|
}
|
|
|
|
// TestReceiveServiceResponseError — ERROR (отказ сервиса, напр. M2M14)
|
|
// переводит сделку в Rejected и сохраняет ответ для отображения в карточке.
|
|
func TestReceiveServiceResponseError(t *testing.T) {
|
|
d := newTestDeal(t)
|
|
resp := makeResponse(d.GUID, m2m.StatusError, "M2M14",
|
|
"Код ЭДО НРД отправителя отсутствует в справочнике участников M2M")
|
|
|
|
if err := d.ReceiveServiceResponse(context.Background(), resp, nil); err != nil {
|
|
t.Fatalf("ReceiveServiceResponse(ERROR): %v", err)
|
|
}
|
|
if d.State != m2mcore.StateRejected {
|
|
t.Errorf("состояние %s, ожидалось %s", d.State, m2mcore.StateRejected)
|
|
}
|
|
if d.Response == nil || d.Response.StatusCode != m2m.StatusError {
|
|
t.Errorf("ответ НРД не сохранён в сделке: %+v", d.Response)
|
|
}
|
|
if len(d.Response.Responses) == 0 || d.Response.Responses[0].Code != "M2M14" {
|
|
t.Errorf("код ответа не сохранён: %+v", d.Response)
|
|
}
|
|
}
|
|
|
|
// TestReceiveServiceResponseInfo — INFO (принято в обработку) сохраняет ответ,
|
|
// но НЕ меняет состояние: ждём M2MTransferDecision контрагента.
|
|
func TestReceiveServiceResponseInfo(t *testing.T) {
|
|
d := newTestDeal(t)
|
|
before := d.State
|
|
resp := makeResponse(d.GUID, m2m.StatusInfo, "01", "Запрос на перевод принят в обработку")
|
|
|
|
if err := d.ReceiveServiceResponse(context.Background(), resp, nil); err != nil {
|
|
t.Fatalf("ReceiveServiceResponse(INFO): %v", err)
|
|
}
|
|
if d.State != before {
|
|
t.Errorf("состояние изменилось на %s, ожидалось без изменений (%s)", d.State, before)
|
|
}
|
|
if d.Response == nil || d.Response.StatusCode != m2m.StatusInfo {
|
|
t.Errorf("ответ НРД не сохранён: %+v", d.Response)
|
|
}
|
|
}
|
|
|
|
// TestReceiveServiceResponseTerminalIdempotent — повторный ERROR на уже
|
|
// терминальной (Rejected) сделке не валит FSM, ответ обновляется.
|
|
func TestReceiveServiceResponseTerminalIdempotent(t *testing.T) {
|
|
d := newTestDeal(t)
|
|
first := makeResponse(d.GUID, m2m.StatusError, "M2M14", "первый отказ")
|
|
if err := d.ReceiveServiceResponse(context.Background(), first, nil); err != nil {
|
|
t.Fatalf("первый ERROR: %v", err)
|
|
}
|
|
// Поллер ИШ может прислать тот же пакет повторно — не должно паниковать
|
|
// и не должно ломать состояние недопустимым переходом Rejected→Rejected.
|
|
second := makeResponse(d.GUID, m2m.StatusError, "M2M14", "повторный отказ")
|
|
if err := d.ReceiveServiceResponse(context.Background(), second, nil); err != nil {
|
|
t.Fatalf("повторный ERROR на терминальной сделке: %v", err)
|
|
}
|
|
if d.State != m2mcore.StateRejected {
|
|
t.Errorf("состояние %s, ожидалось %s", d.State, m2mcore.StateRejected)
|
|
}
|
|
}
|
|
|
|
// TestReceiveServiceResponseNil — защита от nil.
|
|
func TestReceiveServiceResponseNil(t *testing.T) {
|
|
d := newTestDeal(t)
|
|
if err := d.ReceiveServiceResponse(context.Background(), nil, nil); err == nil {
|
|
t.Error("ожидалась ошибка на nil-ответе")
|
|
}
|
|
}
|