Files
Bridge-and-Join-s/internal/lkgateway/claimnotsent_render_test.go
T
fontvielle c476cfd7a3 ЛК-карточка и M2M: операторский путь заявки, баннеры «в НРД не отправлено», дубль-поручение, idempotency, фикс DEPO-алертов
- Карточка заявки: операторский «Путь заявки» (claimTimelineOperator) короткими
  понятными вехами + сворачиваемый технический журнал (buildClaimTimeline);
  QUIK показан пошагово (register/sign/deny/deposit) с верными временами, отказ
  регистрации больше не выглядит успехом (quikActionFailTitles).
- Баннеры: «в НРД не отправлено», когда пакет сформирован, но не ушёл (нет
  submitted_to_nsd и не Fail); заголовок карточки и бейдж учитывают это; красный
  баннер QUIK при отказе, строгий «нет бумаг» только при реальном шорт/недостатке.
- НРД-ответ «Поручение с исходящим номером … уже существует» теперь REG_DUP
  (предупреждение-дубль, не красный «Отказ в регистрации»); не влияет на od.Rejected.
- Idempotency: submitguard.go (inflightSubmit) + защита точек подачи от
  двойного срабатывания (manual + auto-loop, двойной клик): submitOutgoingFor,
  submitIncomingOrderFor, QuikOutRegister (ранний возврат при registered/signed).
- DEPO-алерты: водяной знак depoFailAlertedID засеивается текущим MAX(ID) на
  старте процесса (Hlth03) — после рестарта bj больше не поднимает повторно
  алерты про уже известные ошибки обработки событий BJ_*.
- Тесты: claimnotsent_render_test.go (хелперы + рендер карточки).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XG3zNMMf2LMoDpXo22mwMX
2026-10-15 17:47:28 +03:00

101 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package lkgateway
import (
"bytes"
"strings"
"testing"
"time"
)
// buildClaimDataFromView повторяет вычисления renderClaim для теста.
func buildClaimDataFromView(v ClaimView, quik []*QuikLink, fail *SendFailure) claimData {
qfISIN, qfReply := quikRegisterFailure(quik)
return claimData{
page: page{Title: "Заявка"},
Claim: v, Quik: quik, Fail: fail,
SentToNSD: claimSentToNSD(v), QuikFailISIN: qfISIN, QuikFailReply: qfReply,
QuikNoSec: quikReplyIsNoSecurities(qfReply),
}
}
func TestClaimHelpers(t *testing.T) {
sent := ClaimView{Stages: []StageView{{State: "draft"}, {State: "validated"}, {State: "submitted_to_nsd"}, {State: "awaiting_decision"}}}
if !claimSentToNSD(sent) {
t.Fatal("submitted_to_nsd должна считаться отправкой в НРД")
}
notSent := ClaimView{Stages: []StageView{{State: "draft"}, {State: "validated"}, {State: "rejected"}}}
if claimSentToNSD(notSent) {
t.Fatal("validated→rejected без submitted_to_nsd — в НРД не отправлялось")
}
for _, r := range []string{"Данный инструмент запрещен для операции шорт", "Недостаточно ценных бумаг", "короткая позиция"} {
if !quikReplyIsNoSecurities(r) {
t.Fatalf("ответ %q должен распознаваться как нехватка бумаг", r)
}
}
if quikReplyIsNoSecurities("Неизвестный депозитарий получателя") {
t.Fatal("ошибка про депозитарий — не нехватка бумаг")
}
links := []*QuikLink{{GUID: "g", ISIN: "RU0009029540", State: "error",
Err: "МНП отверг регистрацию: Данный инструмент запрещен для операции шорт",
Steps: []QuikStep{{Action: "register", Reply: "Данный инструмент запрещен для операции шорт", OK: false, At: time.Now()}}}}
isin, reply := quikRegisterFailure(links)
if isin != "RU0009029540" || !strings.Contains(reply, "шорт") {
t.Fatalf("quikRegisterFailure вернул %q / %q", isin, reply)
}
}
// Отклонена до отправки + QUIK сообщил о нехватке бумаг (как e3607d2a).
func TestClaimRenderNotSentNoSec(t *testing.T) {
a, err := newAdmin()
if err != nil {
t.Fatal(err)
}
v := ClaimView{ID: "e3607d2a-2608-441b-b49a-e99ef3e4edd1", Status: "rejected",
RequestRawXML: "<M2MTransferRequest/>",
Stages: []StageView{{State: "draft"}, {State: "validated"}, {State: "rejected"}}}
quik := []*QuikLink{{GUID: "67f09cec", ISIN: "RU0009029540", State: "error",
Err: "МНП отверг регистрацию: Данный инструмент запрещен для операции шорт",
Steps: []QuikStep{{Action: "register", Reply: "Данный инструмент запрещен для операции шорт", OK: false, At: time.Now()}}}}
var b bytes.Buffer
if err := a.claim.ExecuteTemplate(&b, "layout", buildClaimDataFromView(v, quik, nil)); err != nil {
t.Fatal(err)
}
s := b.String()
for _, want := range []string{
"В НРД ничего не отправлено", // баннер отмены до отправки
"Сформированный пакет (M2MTransferRequest)", // заголовок карточки
"в НРД не отправлен", // бейдж
"нет (недостаточно) бумаг", // карточка QUIK: нехватка
"Данный инструмент запрещен для операции шорт", // дословный ответ QUIK
} {
if !strings.Contains(s, want) {
t.Fatalf("нет %q", want)
}
}
if strings.Contains(s, "Отправленный пакет (M2MTransferRequest)") {
t.Fatal("для неотправленной заявки не должно быть заголовка «Отправленный пакет»")
}
}
// Нормально отправленная заявка — старый заголовок сохраняется, баннеров нет.
func TestClaimRenderSentOK(t *testing.T) {
a, err := newAdmin()
if err != nil {
t.Fatal(err)
}
v := ClaimView{ID: "11111111-2222-3333-4444-555555555555", Status: "awaiting_decision",
RequestRawXML: "<M2MTransferRequest/>",
Stages: []StageView{{State: "draft"}, {State: "validated"}, {State: "submitted_to_nsd"}, {State: "awaiting_decision"}}}
var b bytes.Buffer
if err := a.claim.ExecuteTemplate(&b, "layout", buildClaimDataFromView(v, nil, nil)); err != nil {
t.Fatal(err)
}
s := b.String()
if !strings.Contains(s, "Отправленный пакет (M2MTransferRequest)") {
t.Fatal("отправленная заявка должна показывать «Отправленный пакет»")
}
if strings.Contains(s, "В НРД ничего не отправлено") || strings.Contains(s, "в НРД не отправлен") {
t.Fatal("для отправленной заявки баннеры «не отправлено» показывать нельзя")
}
}