chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
package imeiqueue
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rabbitmq"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/uuid32"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
const fullSyncOperation = "imei_full_sync"
|
||||
|
||||
type sImeiQueue struct{}
|
||||
|
||||
var (
|
||||
imeiNow = time.Now
|
||||
imeiRunID = uuid32.S
|
||||
imeiRabbitEnabled = rabbitmq.Enabled
|
||||
imeiPublish = rabbitmq.Publish
|
||||
)
|
||||
|
||||
func init() { service.RegisterImeiQueue(New()) }
|
||||
|
||||
func New() *sImeiQueue { return &sImeiQueue{} }
|
||||
|
||||
func (s *sImeiQueue) StartImeiRun(ctx context.Context) (string, error) {
|
||||
return s.StartImeiRunScoped(ctx, nil)
|
||||
}
|
||||
|
||||
// StartImeiRunScoped starts an IMEI run restricted to scope.Channels. A nil
|
||||
// scope, or one whose channels normalize to empty, starts a full sync.
|
||||
//
|
||||
// The runId prefix keeps IMEI runs distinguishable from status runs in logs and
|
||||
// in the Redis key space (each chain has its own prefix anyway).
|
||||
func (s *sImeiQueue) StartImeiRunScoped(ctx context.Context, scope *model.SyncScope) (string, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
normalized, err := model.NormalizeScope(scope)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !imeiRabbitEnabled(ctx) {
|
||||
return "", gerror.New("imei queue start skipped: rabbitmq is disabled")
|
||||
}
|
||||
now := imeiNow()
|
||||
message := model.ImeiQueueStartMessage{
|
||||
RunID: fmt.Sprintf("imei-%s-%s", now.Format("20060102150405"), imeiRunID()),
|
||||
Operation: fullSyncOperation,
|
||||
TriggeredAt: now.Format(time.RFC3339),
|
||||
Scope: normalized,
|
||||
}
|
||||
if err := imeiPublish(ctx, consts.QueueImeiQueueStart, message); err != nil {
|
||||
return "", gerror.Wrap(err, "publish imei queue start")
|
||||
}
|
||||
g.Log().Infof(ctx, "event=imei_queue_trigger status=published runId=%s scopeChannels=%d", message.RunID, normalized.ChannelCount())
|
||||
return message.RunID, nil
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user