Files
Bridge-and-Join-s/internal/nsdadapter/igw/client_test.go
T

238 lines
7.7 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 igw_test
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter/igw"
)
// TestSendPackageHappyPath — отправка ZIP, ИШ возвращает id.
// Сценарий по DOC/instr-ish-rest-api.pdf раздел 2.5.1.
func TestSendPackageHappyPath(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/api/package/status/123" {
_ = json.NewEncoder(w).Encode(map[string]any{"id": 123, "status": "SENT"})
return
}
if r.URL.Path != "/api/package/CH1/file" {
t.Errorf("неожиданный путь %q", r.URL.Path)
}
// ИШ ждёт multipart/form-data: поля Type (FILE|ARCHIVE) + File (binary).
if err := r.ParseMultipartForm(10 << 20); err != nil {
t.Fatalf("parse multipart: %v", err)
}
if got := r.FormValue("Type"); got != "ARCHIVE" {
t.Errorf("Type = %q, ожидалось ARCHIVE", got)
}
f, _, err := r.FormFile("File")
if err != nil {
t.Fatalf("form file: %v", err)
}
defer f.Close()
b, _ := io.ReadAll(f)
if len(b) == 0 {
t.Errorf("File пустой")
}
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(map[string]any{"id": 123})
}))
defer srv.Close()
c := igw.NewClient(srv.URL, igw.WithRetry(0, time.Millisecond))
id, err := c.SendPackage(context.Background(), "CH1", "#M2MTR", []byte("PK\x03\x04zipbody"))
if err != nil {
t.Fatal(err)
}
if id != "123" {
t.Errorf("id = %q, ожидалось 123", id)
}
}
func TestSendPackageRetryOn500(t *testing.T) {
calls := 0
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
calls++
if calls < 2 {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(map[string]any{"id": 999})
}))
defer srv.Close()
c := igw.NewClient(srv.URL, igw.WithRetry(3, time.Millisecond))
id, err := c.SendPackage(context.Background(), "CH1", "#M2MTR", []byte("x"))
if err != nil {
t.Fatal(err)
}
if id != "999" {
t.Errorf("id = %q, ожидалось 999", id)
}
if calls < 2 {
t.Errorf("ожидалось хотя бы 2 попытки, получено %d", calls)
}
}
func TestSendPackage4xxNoRetry(t *testing.T) {
calls := 0
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
calls++
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(`{"error":"bad"}`))
}))
defer srv.Close()
c := igw.NewClient(srv.URL, igw.WithRetry(3, time.Millisecond))
_, err := c.SendPackage(context.Background(), "CH1", "#M2MTR", []byte("x"))
if err == nil {
t.Fatal("ожидалась ошибка на 400")
}
if calls != 1 {
t.Errorf("4xx не должен ретраиться, попыток = %d", calls)
}
}
// TestGetStatus — формат ответа по разделу 2.5.2 инструкции:
// {id: 123, name: "#M2MTR...zip", status: SENT|NEW|ERROR, error: "..."}.
func TestGetStatus(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/package/status/123" {
t.Errorf("неожиданный путь %q", r.URL.Path)
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"id":123,"name":"#M2MTR20260320140624.zip","status":"SENT"}`))
}))
defer srv.Close()
c := igw.NewClient(srv.URL, igw.WithRetry(0, time.Millisecond))
st, err := c.GetStatus(context.Background(), "123")
if err != nil {
t.Fatal(err)
}
if st.Status != "SENT" {
t.Errorf("status = %q, ожидалось SENT", st.Status)
}
if !strings.Contains(st.Name, "M2MTR") {
t.Errorf("name = %q, ожидалось содержать M2MTR", st.Name)
}
}
// TestListIncoming — формат ответа по разделу 2.6: массив пакетов с полями
// channel/id/name/type/state/files/signs.
func TestListIncoming(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
if q.Get("channel") != "CH1" {
t.Errorf("channel = %q, ожидалось CH1", q.Get("channel"))
}
if q.Get("type") != "M2MTD" {
t.Errorf("type = %q, ожидалось M2MTD", q.Get("type"))
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`[{
"channel":"CH1",
"id":22423,
"name":"#M2MTD20260320140624.ZIP",
"type":"M2MTD",
"state":"RECEIVED",
"files":[{"id":30112,"name":"M2MTD20260320140624.XML"}],
"signs":[{"serial":"40:50:14","subject":"INN=007702165310,CN=НРД","status":"VALID"}]
}]`))
}))
defer srv.Close()
c := igw.NewClient(srv.URL, igw.WithRetry(0, time.Millisecond))
pkgs, err := c.ListIncoming(context.Background(), igw.ListFilter{
Channel: "CH1",
Type: "M2MTD",
})
if err != nil {
t.Fatal(err)
}
if len(pkgs) != 1 {
t.Fatalf("ожидался 1 пакет, получено %d", len(pkgs))
}
p := pkgs[0]
if p.ID != 22423 {
t.Errorf("ID = %d, ожидалось 22423", p.ID)
}
if p.State != "RECEIVED" {
t.Errorf("State = %q, ожидалось RECEIVED", p.State)
}
if len(p.Signs) != 1 || p.Signs[0].Status != "VALID" {
t.Errorf("Signs неверные: %+v", p.Signs)
}
}
// TestGetPackage — скачивание содержимого. ИШ может возвращать либо чистый
// ZIP, либо JSON с base64-полем. Тестируем оба случая.
func TestGetPackageRawZIP(t *testing.T) {
zipBytes := []byte("PK\x03\x04zip-content-here")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/package/22423" {
t.Errorf("неожиданный путь %q", r.URL.Path)
}
w.Header().Set("Content-Type", "application/zip")
_, _ = w.Write(zipBytes)
}))
defer srv.Close()
c := igw.NewClient(srv.URL, igw.WithRetry(0, time.Millisecond))
body, err := c.GetPackage(context.Background(), 22423)
if err != nil {
t.Fatal(err)
}
if string(body) != string(zipBytes) {
t.Errorf("body = %q, ожидалось %q", body, zipBytes)
}
}
func TestGetPackageBase64InJSON(t *testing.T) {
zipBytes := []byte("PK\x03\x04zip-from-base64")
encoded := base64.StdEncoding.EncodeToString(zipBytes)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"file":"` + encoded + `"}`))
}))
defer srv.Close()
c := igw.NewClient(srv.URL, igw.WithRetry(0, time.Millisecond))
body, err := c.GetPackage(context.Background(), 1)
if err != nil {
t.Fatal(err)
}
if string(body) != string(zipBytes) {
t.Errorf("decoded = %q, ожидалось %q", body, zipBytes)
}
}
// Пакет принят ИШ, но в НРД не ушёл (контур недоступен) — отправка должна вернуть ошибку.
func TestSendPackageNotDelivered(t *testing.T) {
polls := 0
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/api/package/status/") {
polls++
st := map[string]any{"id": 187, "status": "NEW"}
if polls > 1 {
st = map[string]any{"id": 187, "status": "ERROR", "error": "ERRORXML_UPLOAD_FAILED"}
}
_ = json.NewEncoder(w).Encode(st)
return
}
_ = json.NewEncoder(w).Encode(map[string]any{"id": 187})
}))
defer srv.Close()
c := igw.NewClient(srv.URL, igw.WithRetry(0, time.Millisecond))
_, err := c.SendPackage(context.Background(), "CH1", "#M2MTR", []byte("PK"))
var nd *igw.NotDeliveredError
if !errors.As(err, &nd) || nd.PackageID != "187" || nd.Code != "ERRORXML_UPLOAD_FAILED" {
t.Fatalf("ожидалась ошибка доставки, получено %v", err)
}
}