From e83d804501948cfba7e73ae730841e1d4295b560 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Sun, 11 Oct 2026 07:55:08 +0300 Subject: [PATCH] =?UTF-8?q?test(iot):=20=D0=B1=D0=B5=D0=B7=D0=BE=D0=BF?= =?UTF-8?q?=D0=B0=D1=81=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20enqueue=E2=86=94cl?= =?UTF-8?q?oseAndWait=20=D0=BF=D0=BE=D0=B4=20race=20(=D1=84=D0=B8=D0=BA?= =?UTF-8?q?=D1=81=20=F0=9F=94=B43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Параллельный enqueue + закрытие канала не должен паниковать «send on closed channel». go test -race ./... зелёный. --- internal/service/bridge/sender_test.go | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 internal/service/bridge/sender_test.go diff --git a/internal/service/bridge/sender_test.go b/internal/service/bridge/sender_test.go new file mode 100644 index 0000000..7007403 --- /dev/null +++ b/internal/service/bridge/sender_test.go @@ -0,0 +1,45 @@ +// sender_test.go — тест безопасности enqueue ↔ closeAndWait (фикс 🔴3). +// +// Раньше поздний колбэк paho мог упасть «send on closed channel» при закрытии +// канала во время enqueue. Мьютекс + closed-флаг должны это исключать. +package bridge + +import ( + "io" + "log/slog" + "sync" + "testing" +) + +func TestDispatcherEnqueueCloseNoPanic(t *testing.T) { + log := slog.New(slog.NewTextHandler(io.Discard, nil)) + // client nil — enqueue/closeAndWait его не трогают (только worker'ы, которых не запускаем). + d := newSQSDispatcher(nil, "http://queue", log) + + // Параллельный enqueue, пока основной поток закрывает канал. + var wg sync.WaitGroup + stop := make(chan struct{}) + for i := 0; i < 4; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for { + select { + case <-stop: + return + default: + d.enqueue(TelemetryEnvelope{Namespace: "ns", DeviceID: "d"}) + } + } + }() + } + + d.closeAndWait() + close(stop) + wg.Wait() + + // После закрытия enqueue обязан вернуть false (канал закрыт), а не паниковать. + if d.enqueue(TelemetryEnvelope{Namespace: "ns", DeviceID: "d"}) { + t.Error("enqueue после close должен вернуть false") + } +}