Files

100 lines
3.4 KiB
Go

package imeiqueue
import (
"context"
"encoding/json"
"errors"
"reflect"
"testing"
"time"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
)
func stubImeiTrigger(t *testing.T) {
t.Helper()
var (
now = imeiNow
runID = imeiRunID
enabled = imeiRabbitEnabled
publish = imeiPublish
)
t.Cleanup(func() {
imeiNow, imeiRunID, imeiRabbitEnabled, imeiPublish = now, runID, enabled, publish
})
imeiNow = func() time.Time { return time.Date(2026, 10, 13, 4, 0, 0, 0, time.FixedZone("UTC+8", 8*60*60)) }
imeiRunID = func() string { return "test-run" }
imeiRabbitEnabled = func(context.Context) bool { return true }
}
func TestImeiTriggerPublishesOneRabbitMQStartWithScope(t *testing.T) {
stubImeiTrigger(t)
if consts.QueueImeiQueueStart != "iotlcm.imei.queue.start" {
t.Fatal("IMEI start queue does not match Worker")
}
calls := 0
var messages []model.ImeiQueueStartMessage
imeiPublish = func(ctx context.Context, queue string, data any) error {
calls++
if ctx == nil || queue != consts.QueueImeiQueueStart {
t.Errorf("queue=%q, context=%v", queue, ctx)
}
messages = append(messages, data.(model.ImeiQueueStartMessage))
return nil
}
for _, scope := range []*model.SyncScope{nil, {Channels: []string{}}, {Channels: []string{"b", "a", "b"}, TriggerSource: model.TriggerSourceManual}} {
runID, err := New().StartImeiRunScoped(nil, scope)
if err != nil || runID != "imei-20261013040000-test-run" {
t.Fatalf("run=%q, error=%v", runID, err)
}
}
if calls != 3 || messages[0].Scope != nil || messages[1].Scope != nil {
t.Fatalf("calls=%d, messages=%+v", calls, messages)
}
message := messages[2]
if message.Operation != "imei_full_sync" || message.TriggeredAt != "2026-10-13T04:00:00+08:00" || !reflect.DeepEqual(message.Scope.Channels, []string{"a", "b"}) || message.Scope.TriggerSource != model.TriggerSourceManual {
t.Fatalf("message=%+v", message)
}
for i, message := range messages {
data, err := json.Marshal(message)
if err != nil {
t.Fatal(err)
}
var body map[string]any
if err := json.Unmarshal(data, &body); err != nil {
t.Fatal(err)
}
if body["runId"] != message.RunID || body["operation"] != "imei_full_sync" || body["triggeredAt"] != message.TriggeredAt {
t.Fatalf("Worker JSON contract: %s", data)
}
if _, present := body["scope"]; present != (i == 2) {
t.Fatalf("full/scoped message compatibility: %s", data)
}
}
}
func TestImeiTriggerRejectsInvalidScopeDisabledMQAndPublishFailure(t *testing.T) {
stubImeiTrigger(t)
calls := 0
publishErr := errors.New("broker unavailable")
imeiPublish = func(context.Context, string, any) error { calls++; return publishErr }
if _, err := New().StartImeiRunScoped(context.Background(), &model.SyncScope{Channels: []string{"bad/channel"}}); err == nil {
t.Fatal("invalid scope accepted")
}
imeiRabbitEnabled = func(context.Context) bool { return false }
if _, err := New().StartImeiRun(context.Background()); err == nil {
t.Fatal("disabled RabbitMQ accepted")
}
if calls != 0 {
t.Fatal("invalid scope or disabled RabbitMQ invoked publisher")
}
imeiRabbitEnabled = func(context.Context) bool { return true }
if runID, err := New().StartImeiRun(context.Background()); runID != "" || !errors.Is(err, publishErr) {
t.Fatalf("run=%q, error=%v", runID, err)
}
if calls != 1 {
t.Fatalf("failed publish retried unexpectedly: %d", calls)
}
}