Files
iotlcm/internal/library/rabbitmq/rabbitmq_test.go
T
MiFaZhan 2bec257289 chore: 初始开源发布
iotlcm-worker-svr:异步执行中枢(RabbitMQ 消费者 + 全量同步编排)。

- Go module:iotlcm-worker-svr → git.5iot.com/iotlcm-public/iotlcm-worker-svr(214 文件)
- 凭据与内部标识脱敏:MySQL/Redis 凭据、私网与生产 IP、内网域名、
  库名 xiujie_admin → iotlcm、哨兵名占位
- 159 处真实手机号 → 合法 11 位编造号(保留号段与后 4 位,维持互异,
  不破坏「校验 MSISDN 格式」等测试断言)
- 移除内部资产:CI 触发器、Jenkins 触发测试文档、预演门禁与镜像
  digest 记录
- Dockerfile 改用公共基础镜像(golang:1.25-alpine → alpine:3.23),
  移除写死的 git 凭据
- README 重写(架构/全量同步模型/配置/已知失败),补 Apache-2.0
  LICENSE,LF 行尾 + .gitattributes

已知:3 个测试包在源仓同一提交上同样失败(client/upstream、
service/allocate、service/inbound),属既有缺陷,本仓未引入亦未修复。
2026-10-22 16:37:56 +08:00

152 lines
3.2 KiB
Go

package rabbitmq
import (
"context"
"reflect"
"sync"
"testing"
)
type fakeChannel struct {
mu sync.Mutex
cancelCalls int
closeCalls int
closed bool
}
func (f *fakeChannel) Cancel(string, bool) error {
f.mu.Lock()
defer f.mu.Unlock()
f.cancelCalls++
return nil
}
func (f *fakeChannel) Close() error {
f.mu.Lock()
defer f.mu.Unlock()
f.closeCalls++
f.closed = true
return nil
}
func (f *fakeChannel) IsClosed() bool {
f.mu.Lock()
defer f.mu.Unlock()
return f.closed
}
func TestCloseIsIdempotent(t *testing.T) {
resetRabbitMQState(t)
channel := &fakeChannel{}
done := make(chan struct{})
close(done)
registerConsumer(&managedConsumer{
channel: channel,
tag: "test-consumer",
done: done,
})
if err := Close(context.Background()); err != nil {
t.Fatalf("first Close() error = %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
cancel()
if err := Close(ctx); err != nil {
t.Fatalf("second Close() error = %v", err)
}
channel.mu.Lock()
defer channel.mu.Unlock()
if channel.cancelCalls != 1 {
t.Fatalf("Cancel() calls = %d, want 1", channel.cancelCalls)
}
if channel.closeCalls != 1 {
t.Fatalf("Close() calls = %d, want 1", channel.closeCalls)
}
}
func TestCloseSkipsAlreadyClosedChannel(t *testing.T) {
resetRabbitMQState(t)
channel := &fakeChannel{closed: true}
done := make(chan struct{})
close(done)
registerConsumer(&managedConsumer{
channel: channel,
tag: "closed-consumer",
done: done,
})
if err := Close(context.Background()); err != nil {
t.Fatalf("Close() error = %v", err)
}
channel.mu.Lock()
defer channel.mu.Unlock()
if channel.cancelCalls != 0 || channel.closeCalls != 0 {
t.Fatalf("closed channel calls: Cancel=%d Close=%d, want 0", channel.cancelCalls, channel.closeCalls)
}
}
func TestCloseHonorsContextWhileWaiting(t *testing.T) {
resetRabbitMQState(t)
channel := &fakeChannel{}
registerConsumer(&managedConsumer{
channel: channel,
tag: "blocked-consumer",
done: make(chan struct{}),
})
ctx, cancel := context.WithCancel(context.Background())
cancel()
if err := Close(ctx); err == nil {
t.Fatal("Close() error = nil, want context cancellation error")
}
channel.mu.Lock()
defer channel.mu.Unlock()
if channel.closeCalls != 1 {
t.Fatalf("Close() calls = %d, want forced cleanup", channel.closeCalls)
}
}
func TestDeliveryMetadataContext(t *testing.T) {
expected := DeliveryMetadata{
DeliveryTag: 42,
Redelivered: true,
Consumer: "batch-consumer",
Queue: "iotlcm.batch.execute",
}
ctx := WithDeliveryMetadata(context.Background(), expected)
actual, ok := DeliveryMetadataFromContext(ctx)
if !ok {
t.Fatal("delivery metadata not found in context")
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("delivery metadata = %+v, want %+v", actual, expected)
}
if _, ok = DeliveryMetadataFromContext(context.Background()); ok {
t.Fatal("unexpected delivery metadata in empty context")
}
}
func resetRabbitMQState(t *testing.T) {
t.Helper()
consumerMu.Lock()
consumers = nil
consumerMu.Unlock()
connMu.Lock()
conn = nil
connMu.Unlock()
t.Cleanup(func() {
consumerMu.Lock()
consumers = nil
consumerMu.Unlock()
connMu.Lock()
conn = nil
connMu.Unlock()
})
}