528 lines
21 KiB
Go
528 lines
21 KiB
Go
package rabbitmq
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
amqp "github.com/rabbitmq/amqp091-go"
|
|
|
|
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/batch"
|
|
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model"
|
|
dailyusageproducer "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/producer/dailyusage"
|
|
monthlyhistoryproducer "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/producer/monthlyusagehistory"
|
|
packagecustomerservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/packagecustomer"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
g.Log().SetWriter(io.Discard)
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
type trafficUsageHandlerStub struct {
|
|
dailyCalls int
|
|
monthlyCalls int
|
|
dailyMessage model.TrafficUsageDailyRetryMessage
|
|
monthMessage model.TrafficUsageMonthlyRetryMessage
|
|
dailyErr error
|
|
monthlyErr error
|
|
}
|
|
|
|
func (s *trafficUsageHandlerStub) RetryDaily(_ context.Context, message model.TrafficUsageDailyRetryMessage) error {
|
|
s.dailyCalls++
|
|
s.dailyMessage = message
|
|
return s.dailyErr
|
|
}
|
|
|
|
func (s *trafficUsageHandlerStub) RetryMonthly(_ context.Context, message model.TrafficUsageMonthlyRetryMessage) error {
|
|
s.monthlyCalls++
|
|
s.monthMessage = message
|
|
return s.monthlyErr
|
|
}
|
|
|
|
type workOrderHandlerStub struct {
|
|
calls int
|
|
message model.WorkOrderAutoCloseMessage
|
|
err error
|
|
}
|
|
|
|
type balanceRechargeHandlerStub struct {
|
|
calls int
|
|
outTradeNo string
|
|
err error
|
|
}
|
|
|
|
func (s *balanceRechargeHandlerStub) CheckRechargeTimeout(_ context.Context, outTradeNo string) error {
|
|
s.calls++
|
|
s.outTradeNo = outTradeNo
|
|
return s.err
|
|
}
|
|
|
|
type batchHandlerStub struct {
|
|
calls int
|
|
message batch.Message
|
|
err error
|
|
}
|
|
|
|
func (s *batchHandlerStub) Handle(_ context.Context, message batch.Message) error {
|
|
s.calls++
|
|
s.message = message
|
|
return s.err
|
|
}
|
|
|
|
type packageCustomerSyncHandlerStub struct {
|
|
calls int
|
|
message model.PackageCustomerSyncMessage
|
|
err error
|
|
}
|
|
|
|
type dailyStartHandlerStub struct {
|
|
calls int
|
|
message model.TrafficUsageDailyStartMessage
|
|
err error
|
|
}
|
|
|
|
func (s *dailyStartHandlerStub) Produce(_ context.Context, message model.TrafficUsageDailyStartMessage) error {
|
|
s.calls++
|
|
s.message = message
|
|
return s.err
|
|
}
|
|
|
|
type monthlyHistoryStartHandlerStub struct {
|
|
calls int
|
|
message model.TrafficUsageMonthlyHistoryStartMessage
|
|
err error
|
|
}
|
|
|
|
func (s *monthlyHistoryStartHandlerStub) Produce(_ context.Context, message model.TrafficUsageMonthlyHistoryStartMessage) error {
|
|
s.calls++
|
|
s.message = message
|
|
return s.err
|
|
}
|
|
|
|
func (s *packageCustomerSyncHandlerStub) SyncDeptPackages(_ context.Context, message model.PackageCustomerSyncMessage) error {
|
|
s.calls++
|
|
s.message = message
|
|
return s.err
|
|
}
|
|
|
|
func (s *workOrderHandlerStub) AutoCloseResolved(_ context.Context, message model.WorkOrderAutoCloseMessage) error {
|
|
s.calls++
|
|
s.message = message
|
|
return s.err
|
|
}
|
|
|
|
func TestHandleTrafficUsageDailyRetry(t *testing.T) {
|
|
stubTrafficPublish(t)
|
|
t.Run("invalid messages are dead lettered", func(t *testing.T) {
|
|
messages := []model.TrafficUsageDailyRetryMessage{
|
|
{},
|
|
{SimKey: &model.SimKey{}, Card: model.TrafficUsageSimCard{Iccid: "card"}},
|
|
{SimKey: &model.SimKey{ICCID: "sim"}},
|
|
}
|
|
for _, message := range messages {
|
|
handler := &trafficUsageHandlerStub{}
|
|
consumers := New(handler, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{})
|
|
if err := consumers.handleTrafficUsageDailyRetry(context.Background(), message); err != nil {
|
|
t.Fatalf("invalid message returned error: %v", err)
|
|
}
|
|
if handler.dailyCalls != 0 {
|
|
t.Fatalf("invalid message called handler %d times", handler.dailyCalls)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("valid message is delegated", func(t *testing.T) {
|
|
expectedErr := errors.New("daily failed")
|
|
expected := model.TrafficUsageDailyRetryMessage{
|
|
SimKey: &model.SimKey{ICCID: "sim"},
|
|
Card: model.TrafficUsageSimCard{Iccid: "sim", PackageCode: "package"},
|
|
QueryDate: "20260714",
|
|
}
|
|
handler := &trafficUsageHandlerStub{dailyErr: expectedErr}
|
|
consumers := New(handler, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{})
|
|
if err := consumers.handleTrafficUsageDailyRetry(context.Background(), expected); err != nil {
|
|
t.Fatalf("expected delegated error, got %v", err)
|
|
}
|
|
if handler.dailyCalls != 1 || !reflect.DeepEqual(handler.dailyMessage, expected) {
|
|
t.Fatalf("daily message was not delegated exactly once: %+v", handler.dailyMessage)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestHandleTrafficUsageMonthlyRetry(t *testing.T) {
|
|
stubTrafficPublish(t)
|
|
t.Run("invalid messages are dead lettered", func(t *testing.T) {
|
|
messages := []model.TrafficUsageMonthlyRetryMessage{
|
|
{},
|
|
{SimKey: &model.SimKey{}, Card: model.TrafficUsageSimCard{Iccid: "card"}},
|
|
{SimKey: &model.SimKey{ICCID: "sim"}},
|
|
}
|
|
for _, message := range messages {
|
|
handler := &trafficUsageHandlerStub{}
|
|
consumers := New(handler, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{})
|
|
if err := consumers.handleTrafficUsageMonthlyRetry(context.Background(), message); err != nil {
|
|
t.Fatalf("invalid message returned error: %v", err)
|
|
}
|
|
if handler.monthlyCalls != 0 {
|
|
t.Fatalf("invalid message called handler %d times", handler.monthlyCalls)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("valid message is delegated", func(t *testing.T) {
|
|
expectedErr := errors.New("monthly failed")
|
|
expected := model.TrafficUsageMonthlyRetryMessage{
|
|
SimKey: &model.SimKey{ICCID: "sim"},
|
|
Card: model.TrafficUsageSimCard{Iccid: "sim", PackageCode: "package"},
|
|
QueryDate: "202607",
|
|
}
|
|
handler := &trafficUsageHandlerStub{monthlyErr: expectedErr}
|
|
consumers := New(handler, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{})
|
|
if err := consumers.handleTrafficUsageMonthlyRetry(context.Background(), expected); err != nil {
|
|
t.Fatalf("expected delegated error, got %v", err)
|
|
}
|
|
if handler.monthlyCalls != 1 || !reflect.DeepEqual(handler.monthMessage, expected) {
|
|
t.Fatalf("monthly message was not delegated exactly once: %+v", handler.monthMessage)
|
|
}
|
|
})
|
|
}
|
|
|
|
func stubTrafficPublish(t *testing.T) {
|
|
t.Helper()
|
|
original, delayed := trafficUsagePublish, trafficUsagePublishDelayed
|
|
t.Cleanup(func() { trafficUsagePublish, trafficUsagePublishDelayed = original, delayed })
|
|
trafficUsagePublish = func(context.Context, string, []byte, amqp.Table) error { return nil }
|
|
trafficUsagePublishDelayed = func(context.Context, string, string, time.Duration, []byte, amqp.Table) error { return nil }
|
|
}
|
|
|
|
func TestHandleWorkOrderAutoClose(t *testing.T) {
|
|
handler := &workOrderHandlerStub{}
|
|
consumers := New(&trafficUsageHandlerStub{}, handler, &balanceRechargeHandlerStub{})
|
|
if err := consumers.handleWorkOrderAutoClose(context.Background(), model.WorkOrderAutoCloseMessage{}); err != nil {
|
|
t.Fatalf("invalid message returned error: %v", err)
|
|
}
|
|
if handler.calls != 0 {
|
|
t.Fatalf("invalid message called handler %d times", handler.calls)
|
|
}
|
|
|
|
expectedErr := errors.New("work order failed")
|
|
expected := model.WorkOrderAutoCloseMessage{WoNo: "WO-1", ResolveTimeTs: 1}
|
|
handler.err = expectedErr
|
|
if err := consumers.handleWorkOrderAutoClose(context.Background(), expected); !errors.Is(err, expectedErr) {
|
|
t.Fatalf("expected delegated error, got %v", err)
|
|
}
|
|
if handler.calls != 1 || !reflect.DeepEqual(handler.message, expected) {
|
|
t.Fatalf("work order message was not delegated exactly once: %+v", handler.message)
|
|
}
|
|
}
|
|
|
|
func TestHandleBalanceRechargeTimeout(t *testing.T) {
|
|
handler := &balanceRechargeHandlerStub{}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, handler)
|
|
if err := consumers.handleBalanceRechargeTimeout(context.Background(), model.BalanceRechargeTimeoutMessage{}); err != nil {
|
|
t.Fatalf("invalid message returned error: %v", err)
|
|
}
|
|
if handler.calls != 0 {
|
|
t.Fatalf("invalid message called handler %d times", handler.calls)
|
|
}
|
|
|
|
message := model.BalanceRechargeTimeoutMessage{OutTradeNo: "BAL-1"}
|
|
if err := consumers.handleBalanceRechargeTimeout(context.Background(), message); err != nil {
|
|
t.Fatalf("valid message returned error: %v", err)
|
|
}
|
|
if handler.calls != 1 || handler.outTradeNo != message.OutTradeNo {
|
|
t.Fatalf("balance recharge message was not delegated exactly once: %+v", handler)
|
|
}
|
|
}
|
|
|
|
func TestHandleBatchExecute(t *testing.T) {
|
|
expectedErr := errors.New("batch failed")
|
|
expected := batch.Message{BatchNo: "BATCH-1", BatchType: batch.Type103}
|
|
handler := &batchHandlerStub{err: expectedErr}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}, handler)
|
|
|
|
if err := consumers.handleBatchExecute(context.Background(), expected); !errors.Is(err, expectedErr) {
|
|
t.Fatalf("expected delegated error, got %v", err)
|
|
}
|
|
if handler.calls != 1 || !reflect.DeepEqual(handler.message, expected) {
|
|
t.Fatalf("batch message was not delegated exactly once: %+v", handler.message)
|
|
}
|
|
}
|
|
|
|
func TestHandlePackageCustomerSync(t *testing.T) {
|
|
t.Run("invalid message is dropped", func(t *testing.T) {
|
|
handler := &packageCustomerSyncHandlerStub{}
|
|
consumers := NewWithPackageCustomerSync(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}, handler)
|
|
if err := consumers.handlePackageCustomerSync(context.Background(), model.PackageCustomerSyncMessage{}); err != nil {
|
|
t.Fatalf("invalid message returned error: %v", err)
|
|
}
|
|
if handler.calls != 0 {
|
|
t.Fatalf("invalid message called handler %d times", handler.calls)
|
|
}
|
|
})
|
|
|
|
t.Run("missing dept is dropped", func(t *testing.T) {
|
|
expected := model.PackageCustomerSyncMessage{TenantId: "000000", DeptId: 164, Trigger: "package_lifecycle", SourceBatchNo: "898604017"}
|
|
handler := &packageCustomerSyncHandlerStub{err: packagecustomerservice.ErrDeptNotFound}
|
|
consumers := NewWithPackageCustomerSync(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}, handler)
|
|
if err := consumers.handlePackageCustomerSync(context.Background(), expected); err != nil {
|
|
t.Fatalf("missing dept returned error: %v", err)
|
|
}
|
|
if handler.calls != 1 || !reflect.DeepEqual(handler.message, expected) {
|
|
t.Fatalf("package customer sync message was not delegated exactly once: %+v", handler.message)
|
|
}
|
|
})
|
|
|
|
t.Run("other errors are retried", func(t *testing.T) {
|
|
expectedErr := errors.New("database failed")
|
|
expected := model.PackageCustomerSyncMessage{TenantId: "000000", DeptId: 117}
|
|
handler := &packageCustomerSyncHandlerStub{err: expectedErr}
|
|
consumers := NewWithPackageCustomerSync(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}, handler)
|
|
if err := consumers.handlePackageCustomerSync(context.Background(), expected); !errors.Is(err, expectedErr) {
|
|
t.Fatalf("expected delegated error, got %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestHandleWorkerDiagnostic(t *testing.T) {
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{})
|
|
|
|
if err := consumers.handleWorkerDiagnostic(context.Background(), model.WorkerDiagnosticMessage{}); err != nil {
|
|
t.Fatalf("invalid message returned error: %v", err)
|
|
}
|
|
|
|
message := model.WorkerDiagnosticMessage{
|
|
TraceID: "worker-test-1",
|
|
SentAt: 1784030000000,
|
|
Message: "worker ping",
|
|
}
|
|
if err := consumers.handleWorkerDiagnostic(context.Background(), message); err != nil {
|
|
t.Fatalf("valid message returned error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWorkerDiagnosticMessageJSONContract(t *testing.T) {
|
|
message := model.WorkerDiagnosticMessage{
|
|
TraceID: "worker-test-1",
|
|
SentAt: 1784030000000,
|
|
Message: "worker ping",
|
|
}
|
|
body, err := json.Marshal(message)
|
|
if err != nil {
|
|
t.Fatalf("marshal diagnostic message: %v", err)
|
|
}
|
|
const expected = `{"traceId":"worker-test-1","sentAt":1784030000000,"message":"worker ping"}`
|
|
if string(body) != expected {
|
|
t.Fatalf("unexpected diagnostic JSON: %s", body)
|
|
}
|
|
}
|
|
|
|
func TestChannelRefreshMessageJSONContract(t *testing.T) {
|
|
message := model.ChannelRefreshMessage{
|
|
Type: "channel_rename",
|
|
OldChanKey: "old_channel",
|
|
NewChanKey: "new_channel",
|
|
}
|
|
body, err := json.Marshal(message)
|
|
if err != nil {
|
|
t.Fatalf("marshal channel rename message: %v", err)
|
|
}
|
|
const expected = `{"type":"channel_rename","oldChanKey":"old_channel","newChanKey":"new_channel"}`
|
|
if string(body) != expected {
|
|
t.Fatalf("unexpected channel rename JSON: %s", body)
|
|
}
|
|
}
|
|
|
|
func TestHandleChannelRefreshDropsInvalidRename(t *testing.T) {
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{})
|
|
for _, message := range []model.ChannelRefreshMessage{
|
|
{Type: "channel_rename"},
|
|
{Type: "channel_rename", OldChanKey: "old_channel"},
|
|
{Type: "channel_rename", NewChanKey: "new_channel"},
|
|
{Type: "channel_rename", OldChanKey: "same", NewChanKey: "same"},
|
|
} {
|
|
if err := consumers.handleChannelRefresh(context.Background(), message); err != nil {
|
|
t.Fatalf("invalid channel rename returned error: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHandleChannelRefreshRenameFailureReturnsError(t *testing.T) {
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{})
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
message := model.ChannelRefreshMessage{
|
|
Type: "channel_rename",
|
|
OldChanKey: "old_channel",
|
|
NewChanKey: "new_channel",
|
|
}
|
|
// ctx 已取消时 renameChannelWithRetry 会在首次重试等待处返回 ctx.Err(),
|
|
// 消费者必须把错误向上抛出,让框架 Nack 重新入队而不是 Ack 丢弃。
|
|
if err := consumers.handleChannelRefresh(ctx, message); err == nil {
|
|
t.Fatalf("expected failed channel rename to return error for Nack+requeue")
|
|
}
|
|
}
|
|
|
|
func TestHandleTrafficUsageDailyStart(t *testing.T) {
|
|
t.Run("invalid message is dropped", func(t *testing.T) {
|
|
for _, message := range []model.TrafficUsageDailyStartMessage{
|
|
{},
|
|
{RunID: "run-1"},
|
|
{QueryDate: "20261009"},
|
|
} {
|
|
handler := &dailyStartHandlerStub{}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithDailyUsageStart(handler)
|
|
if err := consumers.handleTrafficUsageDailyStart(context.Background(), message); err != nil {
|
|
t.Fatalf("invalid message returned error: %v", err)
|
|
}
|
|
if handler.calls != 0 {
|
|
t.Fatalf("invalid message called producer %d times", handler.calls)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("valid message is delegated", func(t *testing.T) {
|
|
expected := model.TrafficUsageDailyStartMessage{
|
|
RunID: "daily-usage-20261009-abc",
|
|
Operation: model.OperationPreviousDayUsageFullSync,
|
|
QueryDate: "20261009",
|
|
TriggeredAt: "2026-10-10T06:00:00+08:00",
|
|
Scope: &model.SyncScope{Channels: []string{"217"}},
|
|
}
|
|
handler := &dailyStartHandlerStub{}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithDailyUsageStart(handler)
|
|
if err := consumers.handleTrafficUsageDailyStart(context.Background(), expected); err != nil {
|
|
t.Fatalf("valid message returned error: %v", err)
|
|
}
|
|
if handler.calls != 1 || !reflect.DeepEqual(handler.message, expected) {
|
|
t.Fatalf("daily start message was not delegated exactly once: %+v", handler.message)
|
|
}
|
|
})
|
|
|
|
t.Run("an already running producer is acked", func(t *testing.T) {
|
|
handler := &dailyStartHandlerStub{err: dailyusageproducer.ErrAlreadyRunning}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithDailyUsageStart(handler)
|
|
if err := consumers.handleTrafficUsageDailyStart(context.Background(), model.TrafficUsageDailyStartMessage{RunID: "r", QueryDate: "20261009"}); err != nil {
|
|
t.Fatalf("ErrAlreadyRunning must be acked, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("a finished run is acked instead of being resurrected", func(t *testing.T) {
|
|
handler := &dailyStartHandlerStub{err: dailyusageproducer.ErrRunFinished}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithDailyUsageStart(handler)
|
|
if err := consumers.handleTrafficUsageDailyStart(context.Background(), model.TrafficUsageDailyStartMessage{RunID: "r", QueryDate: "20261009"}); err != nil {
|
|
t.Fatalf("ErrRunFinished must be acked, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("other errors are surfaced for requeue", func(t *testing.T) {
|
|
expectedErr := errors.New("redis unavailable")
|
|
handler := &dailyStartHandlerStub{err: expectedErr}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithDailyUsageStart(handler)
|
|
if err := consumers.handleTrafficUsageDailyStart(context.Background(), model.TrafficUsageDailyStartMessage{RunID: "r", QueryDate: "20261009"}); !errors.Is(err, expectedErr) {
|
|
t.Fatalf("expected the producer error to propagate, got %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestDailyUsageStartConsumerIsOptIn pins the assembly contract: without an
|
|
// explicit WithDailyUsageStart the initializer list must be unchanged, so a
|
|
// disabled daily chain cannot alter existing deployments.
|
|
func TestDailyUsageStartConsumerIsOptIn(t *testing.T) {
|
|
base := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{})
|
|
withDaily := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithDailyUsageStart(&dailyStartHandlerStub{})
|
|
if got, want := len(withDaily.initializers(false)), len(base.initializers(false))+1; got != want {
|
|
t.Fatalf("initializers = %d, want %d", got, want)
|
|
}
|
|
}
|
|
|
|
func TestHandleTrafficUsageMonthlyHistoryStart(t *testing.T) {
|
|
t.Run("invalid message is dropped", func(t *testing.T) {
|
|
for _, message := range []model.TrafficUsageMonthlyHistoryStartMessage{
|
|
{},
|
|
{RunID: "run-1"},
|
|
{QueryMonth: "202609"},
|
|
} {
|
|
handler := &monthlyHistoryStartHandlerStub{}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithMonthlyHistoryStart(handler)
|
|
if err := consumers.handleTrafficUsageMonthlyHistoryStart(context.Background(), message); err != nil {
|
|
t.Fatalf("invalid message returned error: %v", err)
|
|
}
|
|
if handler.calls != 0 {
|
|
t.Fatalf("invalid message called producer %d times", handler.calls)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("valid message is delegated", func(t *testing.T) {
|
|
expected := model.TrafficUsageMonthlyHistoryStartMessage{
|
|
RunID: "monthly-history-202609-abc",
|
|
Operation: model.OperationPreviousMonthUsageFullSync,
|
|
QueryMonth: "202609",
|
|
TriggeredAt: "2026-10-01T06:00:00+08:00",
|
|
Scope: &model.SyncScope{Channels: []string{"217"}},
|
|
}
|
|
handler := &monthlyHistoryStartHandlerStub{}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithMonthlyHistoryStart(handler)
|
|
if err := consumers.handleTrafficUsageMonthlyHistoryStart(context.Background(), expected); err != nil {
|
|
t.Fatalf("valid message returned error: %v", err)
|
|
}
|
|
if handler.calls != 1 || !reflect.DeepEqual(handler.message, expected) {
|
|
t.Fatalf("monthly history start message was not delegated exactly once: %+v", handler.message)
|
|
}
|
|
})
|
|
|
|
t.Run("an already running producer is acked", func(t *testing.T) {
|
|
handler := &monthlyHistoryStartHandlerStub{err: monthlyhistoryproducer.ErrAlreadyRunning}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithMonthlyHistoryStart(handler)
|
|
if err := consumers.handleTrafficUsageMonthlyHistoryStart(context.Background(), model.TrafficUsageMonthlyHistoryStartMessage{RunID: "r", QueryMonth: "202609"}); err != nil {
|
|
t.Fatalf("ErrAlreadyRunning must be acked, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("a finished run is acked instead of being resurrected", func(t *testing.T) {
|
|
handler := &monthlyHistoryStartHandlerStub{err: monthlyhistoryproducer.ErrRunFinished}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithMonthlyHistoryStart(handler)
|
|
if err := consumers.handleTrafficUsageMonthlyHistoryStart(context.Background(), model.TrafficUsageMonthlyHistoryStartMessage{RunID: "r", QueryMonth: "202609"}); err != nil {
|
|
t.Fatalf("ErrRunFinished must be acked, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("other errors are surfaced for requeue", func(t *testing.T) {
|
|
expectedErr := errors.New("redis unavailable")
|
|
handler := &monthlyHistoryStartHandlerStub{err: expectedErr}
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithMonthlyHistoryStart(handler)
|
|
if err := consumers.handleTrafficUsageMonthlyHistoryStart(context.Background(), model.TrafficUsageMonthlyHistoryStartMessage{RunID: "r", QueryMonth: "202609"}); !errors.Is(err, expectedErr) {
|
|
t.Fatalf("expected the producer error to propagate, got %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestMonthlyHistoryStartConsumerIsOptIn mirrors the daily chain: a disabled
|
|
// monthly-history chain must not add an initializer.
|
|
func TestMonthlyHistoryStartConsumerIsOptIn(t *testing.T) {
|
|
base := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{})
|
|
withHistory := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{}).WithMonthlyHistoryStart(&monthlyHistoryStartHandlerStub{})
|
|
if got, want := len(withHistory.initializers(false)), len(base.initializers(false))+1; got != want {
|
|
t.Fatalf("initializers = %d, want %d", got, want)
|
|
}
|
|
}
|
|
|
|
func TestConsumerInitializersRespectWorkerDiagnosticSwitch(t *testing.T) {
|
|
consumers := New(&trafficUsageHandlerStub{}, &workOrderHandlerStub{}, &balanceRechargeHandlerStub{})
|
|
// 手动同步上游结果(initDataReviewQuery)随业务消费者常驻,无处理器时自跳过。
|
|
if got := len(consumers.initializers(false)); got != 7 {
|
|
t.Fatalf("diagnostic disabled: expected 7 business consumers, got %d", got)
|
|
}
|
|
if got := len(consumers.initializers(true)); got != 8 {
|
|
t.Fatalf("diagnostic enabled: expected 8 consumers, got %d", got)
|
|
}
|
|
}
|