package lkgateway import ( "testing" "time" ) // Строчные события (договорённость с Fancy 18.09.2026): пакет исполняется по бумагам, // состояние объекта складывается из состояний строк. func TestOrderWatchLinesPartial(t *testing.T) { now := time.Now() w := &OrderWatch{GUID: "G1", Lines: map[string]*OrderLineWatch{ "cd-sber": {Ref: "R1", ISIN: "RU0009029540", MS010At: now}, // исполнено "cd-tech": {Ref: "R2", ISIN: "RU000A106YF0", MS010At: now, Rejected: true}, // отказ "cd-gaz": {Ref: "R3", ISIN: "RU0007661625"}, // ждём }} lines := []orderLine{{ContrDID: "cd-sber", Ref: "R1"}, {ContrDID: "cd-tech", Ref: "R2"}, {ContrDID: "cd-gaz", Ref: "R3"}} if allLinesMS010(w, lines) { t.Error("не все строки получили MS010 — объект не должен считаться закрытым") } total, executed, rejected, expired := w.linesSummary() if total != 3 || executed != 1 || rejected != 1 || expired != 0 { t.Errorf("сводка строк: total=%d executed=%d rejected=%d expired=%d", total, executed, rejected, expired) } if got := w.StatusRu(); got != "исполнено частично: 1 из 3 бумаг, отвергнуто 1, ожидаем MS010 по 1" { t.Errorf("частичный статус: %q", got) } if w.Kind() != "warn" { t.Errorf("частичное исполнение должно быть warn, got %q", w.Kind()) } // все строки закрыты → объект закрыт w.Lines["cd-gaz"].MS010At = now if !allLinesMS010(w, lines) { t.Error("все строки с MS010 — объект должен быть закрыт") } // строка, истёкшая по сроку, тоже финальна if !(&OrderLineWatch{ExpiredAt: now}).lineDone() || (&OrderLineWatch{}).lineDone() || (*OrderLineWatch)(nil).lineDone() { t.Error("lineDone: истёкшая строка финальна, пустая/nil — нет") } } // Строки входящего 16/1: новые записи хранят все contr_d_id с REFERENCE; старые — только первый. func TestIncomingOrderLines(t *testing.T) { it := &IncomingTransfer{OrderLines: []IncomingOrderLine{ {ContrDID: "c1", ISIN: "RU0009029540", Ref: "R1"}, {ContrDID: "c2", ISIN: "RU000A106YF0", Ref: "R2"}, }} got := incomingOrderLines(it) if len(got) != 2 || got[0].ContrDID != "c1" || got[0].Ref != "R1" || got[1].Ref != "R2" { t.Errorf("строки нового формата: %+v", got) } legacy := &IncomingTransfer{OrderContrDID: "c-old"} if got := incomingOrderLines(legacy); len(got) != 1 || got[0].ContrDID != "c-old" || got[0].Ref != "" { t.Errorf("старая запись: одна строка без REFERENCE, got %+v", got) } if incomingOrderLines(&IncomingTransfer{}) != nil || incomingOrderLines(nil) != nil { t.Error("без поручений строк нет") } } // Строки исходящего 16 без репозитория: contr_d_id/ISIN из черновиков, REFERENCE пустой. func TestOutgoingLineRefsNoRepo(t *testing.T) { od := &OutgoingDeal{GUID: "G2", Secs: []OutgoingSec{ {ISIN: "ru0009029540", ContrDID: "cd1"}, {ISIN: "RU000A106YF0", ContrDID: ""}, // без номера поручения — не подавалась {ISIN: "RU0007661625", ContrDID: "cd3"}, }} got := outgoingLineRefs(t.Context(), nil, od) if len(got) != 2 || got[0].ContrDID != "cd1" || got[0].ISIN != "RU0009029540" || got[1].ContrDID != "cd3" { t.Errorf("строки исходящего: %+v", got) } if outgoingLineRefs(t.Context(), nil, nil) != nil { t.Error("nil-сделка → нет строк") } }