Files
Bridge-and-Join-s/internal/lkgateway/nsdhealth_test.go
T

188 lines
6.2 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"
"context"
"path/filepath"
"strings"
"testing"
"time"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2mcore"
)
func nsdTestRC(t *testing.T) *RuntimeConfig {
t.Helper()
rc, err := NewRuntimeConfig(filepath.Join(t.TempDir(), "setup.json"))
if err != nil {
t.Fatal(err)
}
n := rc.Snapshot().NSD
n.IGWBaseURL = "http://localhost:8090"
if err := rc.UpdateNSD(n); err != nil {
t.Fatal(err)
}
return rc
}
func nsdTestReset(t *testing.T) {
t.Helper()
old := sendFailFile
sendFailFile = filepath.Join(t.TempDir(), "sf.json")
sendFails.loaded = false
oldHist := nsdHistoryFile
nsdHistoryFile = filepath.Join(t.TempDir(), "h.json")
t.Cleanup(func() {
sendFailFile, sendFails.loaded, nsdHistoryFile = old, false, oldHist
igwWatch.mu.Lock()
igwWatch.Checked, igwWatch.Up = false, false
igwWatch.mu.Unlock()
nsdWatch.Lock()
nsdWatch.DownSince, nsdWatch.LastPollAt, nsdWatch.LastPollOK = time.Time{}, time.Time{}, time.Time{}
nsdWatch.Unlock()
nsdH.Lock()
nsdH.ProbeSentAt, nsdH.ProbeReplyAt, nsdH.ProbeErr, nsdH.history, nsdH.historyLoaded = time.Time{}, time.Time{}, "", nil, false
nsdH.Unlock()
})
}
func segState(h NSDHealth, key string) string {
for _, s := range h.Segs {
if s.Key == key {
return s.State
}
}
return ""
}
// Зелёный — только при свежих подтверждениях; сбой опросов и молчание на проверку — красный.
func TestNSDHealthStates(t *testing.T) {
nsdTestReset(t)
rc := nsdTestRC(t)
now := time.Now()
igwWatch.mu.Lock()
igwWatch.Checked, igwWatch.Up = true, true
igwWatch.mu.Unlock()
nsdWatch.Lock()
nsdWatch.LastPollAt, nsdWatch.LastPollOK = now.Add(-10*time.Second), now.Add(-10*time.Second)
nsdWatch.Unlock()
nsdH.Lock()
nsdH.ProbeSentAt, nsdH.ProbeReplyAt = now.Add(-5*time.Minute), now.Add(-4*time.Minute)
nsdH.Unlock()
h := nsdHealthNow(rc, now)
if h.State != segOK || !nsdLinkOK(h) || nsdClientNotice(h) != "" || h.Ago == "" {
t.Fatalf("всё работает: %+v", h)
}
nsdWatch.Lock()
nsdWatch.DownSince, nsdWatch.LastErr = now.Add(-5*time.Minute), "#404"
nsdWatch.Unlock()
h = nsdHealthNow(rc, now)
if segState(h, "nsd") != segErr || h.State != segErr || nsdLinkOK(h) || nsdClientNotice(h) == "" {
t.Fatalf("НРД отвечает ошибками 5 мин: %+v", h)
}
nsdWatch.Lock()
nsdWatch.DownSince = time.Time{}
nsdWatch.LastPollOK = now.Add(-20 * time.Minute) // тишина — не «работает»
nsdWatch.Unlock()
if h = nsdHealthNow(rc, now); segState(h, "nsd") != segWarn {
t.Fatalf("давно нет опросов: %+v", h)
}
nsdWatch.Lock()
nsdWatch.LastPollOK = now
nsdWatch.Unlock()
nsdH.Lock()
nsdH.ProbeSentAt = now.Add(-12 * time.Minute)
nsdH.ProbeReplyAt = now.Add(-25 * time.Minute)
nsdH.Unlock()
if h = nsdHealthNow(rc, now); segState(h, "m2m") != segErr {
t.Fatalf("НРД молчит на проверку 12 мин: %+v", h)
}
}
// Автоповтор: НРД снова отвечает — недоставленный перевод уходит сам; старше суток — не трогаем.
func TestAutoResend(t *testing.T) {
nsdTestReset(t)
oldHook, oldQ := sentHook, quikSendHook
defer func() { sentHook, quikSendHook = oldHook, oldQ }()
sentHook, quikSendHook = nil, nil
ctx := context.Background()
snd := &flakySender{fail: true}
svc := NewService(Config{Repository: m2mcore.NewMemoryRepository(), Sender: snd})
s := &Server{svc: svc}
req := sendFailRequest()
if _, err := svc.SendBuiltRequest(ctx, req, "inv", nil); err == nil {
t.Fatal("ожидалась ошибка доставки")
}
guid := string(req.Header.GUID)
s.autoResend(ctx) // последняя попытка только что — ждём 5 мин
if sendFailGet(guid) == nil {
t.Fatal("рано повторять")
}
sendFails.Lock()
f := sendFails.m[guid]
f.At = time.Now().Add(-6 * time.Minute)
sendFails.m[guid] = f
sendFails.Unlock()
snd.fail = false
s.autoResend(ctx)
if sendFailGet(guid) != nil {
t.Fatal("перевод должен уйти автоматически")
}
d, _ := svc.Repo().GetByGUID(ctx, req.Header.GUID)
if d.State != m2mcore.StateAwaitingDecision {
t.Fatalf("после автоповтора: %s", d.State)
}
// Старше суток — автоповтор останавливается.
sendFails.Lock()
sendFails.m["old"] = SendFailure{DealID: "x", At: time.Now().Add(-26 * time.Hour), FirstAt: time.Now().Add(-26 * time.Hour), Attempts: 9}
sendFails.Unlock()
s.autoResend(ctx)
if f := sendFailGet("old"); f == nil || !f.GaveUp {
t.Fatalf("старше суток: %+v", f)
}
}
func TestNSDStripAndStatusRender(t *testing.T) {
nsdTestReset(t)
now := time.Now()
nsdH.Lock()
nsdH.historyLoaded = true
nsdH.Unlock()
for i := 30; i >= 0; i-- {
st := segOK
if i < 5 {
st = segErr
}
nsdHistoryAdd(NSDHealth{Configured: true, Segs: []NSDSeg{{State: segOK}, {State: st}, {State: segOK}, {State: segOK}}}, now.Add(-time.Duration(i)*time.Minute))
}
rows := nsdStrip([]string{"Шлюз", "НРД", "M2M", "Отправка"}, now)
if len(rows) != 4 || len(rows[1].Cells) != 144 || rows[1].Cells[143].State != segErr || rows[0].Bad != "сбоев не было" || !strings.HasPrefix(rows[1].Bad, "сбой") {
t.Fatalf("полоса: %+v / %q", rows[1].Cells[143], rows[1].Bad)
}
a, err := newAdmin()
if err != nil {
t.Fatal(err)
}
pg := page{Title: "Статус"}
pg.NSDLink = NSDHealth{Configured: true, State: segErr, Segs: []NSDSeg{{Key: "igw", Title: "Шлюз", State: segOK, Text: "отвечает"}, {Key: "nsd", Title: "НРД", State: segErr, Text: "отвечает шлюзу ошибками"}}}
var b bytes.Buffer
data := statusData{page: pg, Strip: rows, Fails: []SendFailView{{GUID: "d7a182d4-4f79", DealID: "7f1c5cfb", Text: "контур НРД не принял пакет", FirstAt: now, Attempts: 2}}}
if err := a.status.ExecuteTemplate(&b, "layout", data); err != nil {
t.Fatal(err)
}
for _, want := range []string{"Связь с НРД", "nsd-cells", "Не доставлены в НРД", "Проверить связь сейчас", "id=\"nsd-chain\"", "отправится сам"} {
if !strings.Contains(b.String(), want) {
t.Fatalf("нет %q", want)
}
}
}