de41aea00c
Полный клиент Интеграционного шлюза НРД в internal/nsdadapter/igw/:
client.go — REST endpoint'ы по свежей спецификации НРД:
- POST /api/package/{channel}/file — отправка ZIP (Type=archive, File=base64)
возвращает id пакета (поддерживаются варианты id|package_id|ID)
- GET /api/package/status/{id} — статус NEW|SENT|ERROR (с error-полем)
- GET /api/package?channel=&type=M2MTD|M2MER&date=&id=&count=&excludeErrors=
— список входящих от НРД, с files[] и signs[] (ИШ сам проверяет ЭП и
выдаёт VALID|INVALID)
- GET /api/package/{id} — скачать ZIP (raw или base64-в-JSON, авто-детект
по сигнатуре PK\x03\x04)
- Ретраи только на 5xx/сетевые ошибки (4xx — сразу ошибка)
- HTTP-клиент через options, кастомный таймаут, ретраи
pack.go — упаковщик/распаковщик ZIP по разделу 2.3 инструкции:
- PackRequest(req, docName) — M2MTransferRequest→ZIP с config.xml
- PackXML(xml, docName, packageType) — для эталонных сообщений
- UnpackPackage(zip) → {DocXML, WinfXML, Signature, Filenames}
- ParseDecision / ParseResponse через nsdxml.Unmarshal
Покрыто тестами (10/10 PASS):
- send happy path с проверкой формата JSON-body
- retry на 5xx, без ретраев на 4xx
- GetStatus с числовым id
- ListIncoming как массив (новый формат) и как {items:[]} (старый)
- GetPackage raw ZIP + GetPackage с base64-в-JSON
- упаковка/распаковка: 2 файла в ZIP, имена, содержимое config.xml
- распаковка с .sgn и winf.xml
cmd/bj-server/main.go — NSD-poller адаптирован под новый API
(client.ListIncoming(ctx, ListFilter{}) вместо позиционных параметров;
поля Package.ID/Name/Type/State вместо PackageID/PackageType).
Скачана и положена в DOC/ свежая спецификация (798 KB, 15 стр):
DOC/instr-ish-rest-api.pdf — это исходный документ для нашей реализации.
REPORT.md обновлён:
- общая готовность 65% → 70%
- готовность к роботу 80% → 85%
- добавлен раздел про REST-клиент ИШ
- блокер #6 — отсутствие «Руководства по установке ИШ»
115 lines
3.4 KiB
Go
115 lines
3.4 KiB
Go
package igw_test
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter/igw"
|
|
)
|
|
|
|
// TestPackXML_StructureMatchesSpec — после упаковки в ZIP должны быть
|
|
// ровно два файла: doc.xml + config.xml. Config содержит <name> и
|
|
// <package>. Это структура из раздела 2.3 инструкции НРД.
|
|
func TestPackXML_StructureMatchesSpec(t *testing.T) {
|
|
xmlBody := []byte(`<?xml version="1.0" encoding="windows-1251"?><rt:M2MTransferRequest/>`)
|
|
zipBytes, err := igw.PackXML(xmlBody, "M2MTransferRequest.xml", "#M2MTR")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(r.File) != 2 {
|
|
t.Fatalf("в ZIP должно быть 2 файла, получено %d", len(r.File))
|
|
}
|
|
got := map[string][]byte{}
|
|
for _, f := range r.File {
|
|
rc, _ := f.Open()
|
|
b, _ := io.ReadAll(rc)
|
|
rc.Close()
|
|
got[f.Name] = b
|
|
}
|
|
if _, ok := got["M2MTransferRequest.xml"]; !ok {
|
|
t.Errorf("в ZIP нет M2MTransferRequest.xml. Файлы: %v", keys(got))
|
|
}
|
|
cfg, ok := got["config.xml"]
|
|
if !ok {
|
|
t.Fatalf("в ZIP нет config.xml. Файлы: %v", keys(got))
|
|
}
|
|
cfgStr := string(cfg)
|
|
if !strings.Contains(cfgStr, "<name>M2MTransferRequest.xml</name>") {
|
|
t.Errorf("config.xml не содержит правильное <name>: %s", cfgStr)
|
|
}
|
|
if !strings.Contains(cfgStr, "<package>#M2MTR</package>") {
|
|
t.Errorf("config.xml не содержит правильное <package>: %s", cfgStr)
|
|
}
|
|
}
|
|
|
|
func TestPackXML_RejectsBadPackageType(t *testing.T) {
|
|
_, err := igw.PackXML([]byte("<x/>"), "doc.xml", "M2MTR")
|
|
if err == nil {
|
|
t.Fatal("ожидалась ошибка для packageType без #")
|
|
}
|
|
}
|
|
|
|
// TestUnpackPackage_FindsXMLAndWinf — распаковка эмулирует входящий ZIP
|
|
// от ИШ: M2MTD.xml + winf.xml + .sgn. Проверяем что UnpackPackage
|
|
// корректно раскладывает по полям.
|
|
func TestUnpackPackage_FindsXMLAndWinf(t *testing.T) {
|
|
docBody := []byte("<dn:M2MTransferDecision/>")
|
|
winfBody := []byte("<winf/>")
|
|
sgnBody := []byte("BINARY-SIGN-BLOB")
|
|
|
|
var buf bytes.Buffer
|
|
w := zip.NewWriter(&buf)
|
|
must := func(name string, data []byte) {
|
|
fw, _ := w.Create(name)
|
|
_, _ = fw.Write(data)
|
|
}
|
|
must("M2MTD20260320140624.XML", docBody)
|
|
must("winf.xml", winfBody)
|
|
must("M2MTD20260320140624.XML.sgn", sgnBody)
|
|
_ = w.Close()
|
|
|
|
pkg, err := igw.UnpackPackage(buf.Bytes())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(pkg.DocXML) != string(docBody) {
|
|
t.Errorf("DocXML mismatch")
|
|
}
|
|
if string(pkg.WinfXML) != string(winfBody) {
|
|
t.Errorf("WinfXML mismatch")
|
|
}
|
|
if string(pkg.Signature) != string(sgnBody) {
|
|
t.Errorf("Signature mismatch")
|
|
}
|
|
if len(pkg.Filenames) != 3 {
|
|
t.Errorf("ожидалось 3 файла в Filenames, получено %d", len(pkg.Filenames))
|
|
}
|
|
}
|
|
|
|
func TestUnpackPackage_EmptyXML(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
w := zip.NewWriter(&buf)
|
|
fw, _ := w.Create("winf.xml")
|
|
_, _ = fw.Write([]byte("<winf/>"))
|
|
_ = w.Close()
|
|
_, err := igw.UnpackPackage(buf.Bytes())
|
|
if err == nil {
|
|
t.Fatal("ожидалась ошибка когда в ZIP нет основного .xml")
|
|
}
|
|
}
|
|
|
|
func keys(m map[string][]byte) []string {
|
|
out := make([]string, 0, len(m))
|
|
for k := range m {
|
|
out = append(out, k)
|
|
}
|
|
return out
|
|
}
|