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),属既有缺陷,本仓未引入亦未修复。
This commit is contained in:
MiFaZhan
2026-10-22 16:37:56 +08:00
commit 2bec257289
361 changed files with 71524 additions and 0 deletions
@@ -0,0 +1,249 @@
package monthlyusage
import (
"context"
"testing"
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model"
)
func newScopeProducer(t *testing.T, cards []Card) (*Producer, *producerRedis) {
t.Helper()
store := testStore(t)
scanner := scannerFor(cards, cards[len(cards)-1].ID)
for i := range scanner.channels {
for _, c := range cards {
if c.ChanKey == scanner.channels[i].ChanKey {
scanner.channels[i].TempKey = c.TempKey
scanner.channels[i].SyncEnabled = c.SyncEnabled
break
}
}
}
p, err := New(scanner, store, Config{PageSize: 100})
if err != nil {
t.Fatal(err)
}
return p, &producerRedis{enqueueBatches: map[string]int{}}
}
func scopedMessage(runID string, channels ...string) model.TrafficUsageMonthlyStartMessage {
return model.TrafficUsageMonthlyStartMessage{
RunID: runID,
Operation: "current_month_usage_full_sync",
QueryMonth: "202608",
Scope: &model.SyncScope{Channels: channels},
}
}
func TestProduceScopeEnqueuesOnlySelectedChannels(t *testing.T) {
producer, redis := newScopeProducer(t, []Card{
{ID: 1, ChanKey: "217", TempKey: "sim_yd_v5", Operator: 1},
{ID: 2, ChanKey: "173", TempKey: "sim_yd_v5", Operator: 1},
{ID: 3, ChanKey: "217", TempKey: "sim_yd_v5", Operator: 1},
{ID: 4, ChanKey: "189", TempKey: "sim_yd_v5", Operator: 1},
})
if err := producer.Produce(context.Background(), scopedMessage("scoped", "217")); err != nil {
t.Fatal(err)
}
redis.enqueueBatches = map[string]int{}
for _, ch := range []string{"217", "173", "189", "ghost-channel", "a", "b", "mobile-a", "mobile-b"} {
key, _ := producer.store.RoundActiveKey("scoped", 1, ch)
n, _ := producer.store.SizeKey(context.Background(), key)
if n > 0 {
redis.enqueueBatches["flow:"+ch] = int(n)
}
}
redis.mu.Lock()
defer redis.mu.Unlock()
if len(redis.enqueueBatches) != 1 {
t.Fatalf("enqueue batches = %+v, want only the 217 channel", redis.enqueueBatches)
}
if redis.enqueueBatches["flow:217"] != 2 {
t.Fatalf("enqueue batches = %+v, want 2 tasks on flow:217", redis.enqueueBatches)
}
}
func TestProduceScopeMultipleChannels(t *testing.T) {
producer, redis := newScopeProducer(t, []Card{
{ID: 1, ChanKey: "217", TempKey: "sim_yd_v5", Operator: 1},
{ID: 2, ChanKey: "173", TempKey: "sim_yd_v5", Operator: 1},
{ID: 3, ChanKey: "189", TempKey: "sim_yd_v5", Operator: 1},
})
if err := producer.Produce(context.Background(), scopedMessage("scoped", "189", "217")); err != nil {
t.Fatal(err)
}
redis.enqueueBatches = map[string]int{}
for _, ch := range []string{"217", "173", "189", "ghost-channel", "a", "b", "mobile-a", "mobile-b"} {
key, _ := producer.store.RoundActiveKey("scoped", 1, ch)
n, _ := producer.store.SizeKey(context.Background(), key)
if n > 0 {
redis.enqueueBatches["flow:"+ch] = int(n)
}
}
redis.mu.Lock()
defer redis.mu.Unlock()
if len(redis.enqueueBatches) != 2 {
t.Fatalf("enqueue batches = %+v, want 217 and 189", redis.enqueueBatches)
}
if _, ok := redis.enqueueBatches["flow:173"]; ok {
t.Fatalf("channel 173 was enqueued despite not being selected: %+v", redis.enqueueBatches)
}
}
func TestProduceFullScopeKeepsEveryChannel(t *testing.T) {
producer, redis := newScopeProducer(t, []Card{
{ID: 1, ChanKey: "217", TempKey: "sim_yd_v5", Operator: 1},
{ID: 2, ChanKey: "173", TempKey: "sim_yd_v5", Operator: 1},
})
if err := producer.Produce(context.Background(), model.TrafficUsageMonthlyStartMessage{
RunID: "full",
Operation: "current_month_usage_full_sync",
QueryMonth: "202608",
}); err != nil {
t.Fatal(err)
}
redis.enqueueBatches = map[string]int{}
for _, ch := range []string{"217", "173", "189", "ghost-channel", "a", "b", "mobile-a", "mobile-b"} {
key, _ := producer.store.RoundActiveKey("full", 1, ch)
n, _ := producer.store.SizeKey(context.Background(), key)
if n > 0 {
redis.enqueueBatches["flow:"+ch] = int(n)
}
}
redis.mu.Lock()
defer redis.mu.Unlock()
if len(redis.enqueueBatches) != 2 {
t.Fatalf("enqueue batches = %+v, want both channels for a full sync", redis.enqueueBatches)
}
}
func TestProduceScopeCombinesWithSyncDisabledGuard(t *testing.T) {
producer, redis := newScopeProducer(t, []Card{
{ID: 1, ChanKey: "217", TempKey: "sim_yd_v5", Operator: 1},
// Selected but excluded by sync_enabled: scope must not override it.
{ID: 2, ChanKey: "173", TempKey: "sim_yd_v5", SyncEnabled: true, Operator: 1},
})
if err := producer.Produce(context.Background(), scopedMessage("scoped", "173")); err != nil {
t.Fatal(err)
}
redis.enqueueBatches = map[string]int{}
for _, ch := range []string{"217", "173", "189", "ghost-channel", "a", "b", "mobile-a", "mobile-b"} {
key, _ := producer.store.RoundActiveKey("scoped", 1, ch)
n, _ := producer.store.SizeKey(context.Background(), key)
if n > 0 {
redis.enqueueBatches["flow:"+ch] = int(n)
}
}
redis.mu.Lock()
defer redis.mu.Unlock()
if len(redis.enqueueBatches) != 0 {
t.Fatalf("enqueue batches = %+v, want none (selected channel is sync-disabled)", redis.enqueueBatches)
}
}
func TestProduceScopeSelectingUnknownChannelEnqueuesNothing(t *testing.T) {
producer, redis := newScopeProducer(t, []Card{
{ID: 1, ChanKey: "217", TempKey: "sim_yd_v5", Operator: 1},
})
if err := producer.Produce(context.Background(), scopedMessage("scoped", "does-not-exist")); err != nil {
t.Fatal(err)
}
redis.enqueueBatches = map[string]int{}
for _, ch := range []string{"217", "173", "189", "ghost-channel", "a", "b", "mobile-a", "mobile-b"} {
key, _ := producer.store.RoundActiveKey("scoped", 1, ch)
n, _ := producer.store.SizeKey(context.Background(), key)
if n > 0 {
redis.enqueueBatches["flow:"+ch] = int(n)
}
}
redis.mu.Lock()
defer redis.mu.Unlock()
if len(redis.enqueueBatches) != 0 {
t.Fatalf("enqueue batches = %+v, want none", redis.enqueueBatches)
}
}
func TestProduceRejectsInvalidScope(t *testing.T) {
producer, redis := newScopeProducer(t, []Card{
{ID: 1, ChanKey: "217", TempKey: "sim_yd_v5", Operator: 1},
})
if err := producer.Produce(context.Background(), scopedMessage("bad", "invalid/chan")); err == nil {
t.Fatal("expected invalid chanKey to be rejected")
}
redis.enqueueBatches = map[string]int{}
for _, ch := range []string{"217", "173", "189", "ghost-channel", "a", "b", "mobile-a", "mobile-b"} {
key, _ := producer.store.RoundActiveKey("scoped", 1, ch)
n, _ := producer.store.SizeKey(context.Background(), key)
if n > 0 {
redis.enqueueBatches["flow:"+ch] = int(n)
}
}
redis.mu.Lock()
defer redis.mu.Unlock()
if len(redis.enqueueBatches) != 0 {
t.Fatalf("rejected scope must not enqueue: %+v", redis.enqueueBatches)
}
}
func TestProduceScopeDeduplicatesChannels(t *testing.T) {
producer, redis := newScopeProducer(t, []Card{
{ID: 1, ChanKey: "217", TempKey: "sim_yd_v5", Operator: 1},
})
if err := producer.Produce(context.Background(), scopedMessage("scoped", "217", "217")); err != nil {
t.Fatal(err)
}
redis.enqueueBatches = map[string]int{}
for _, ch := range []string{"217", "173", "189", "ghost-channel", "a", "b", "mobile-a", "mobile-b"} {
key, _ := producer.store.RoundActiveKey("scoped", 1, ch)
n, _ := producer.store.SizeKey(context.Background(), key)
if n > 0 {
redis.enqueueBatches["flow:"+ch] = int(n)
}
}
redis.mu.Lock()
defer redis.mu.Unlock()
if redis.enqueueBatches["flow:217"] != 1 {
t.Fatalf("enqueue batches = %+v, want 1 task despite a duplicated chanKey", redis.enqueueBatches)
}
}
func TestScopeDigestIsOrderIndependent(t *testing.T) {
first := &model.SyncScope{Channels: []string{"173", "217"}}
second := &model.SyncScope{Channels: []string{"217", "173"}}
// Normalization sorts the channels, so both requests produce one digest.
normalizedFirst, err := model.NormalizeScope(first)
if err != nil {
t.Fatal(err)
}
normalizedSecond, err := model.NormalizeScope(second)
if err != nil {
t.Fatal(err)
}
if scopeDigest(normalizedFirst) != scopeDigest(normalizedSecond) {
t.Fatalf("digest depends on channel order: %s vs %s", scopeDigest(normalizedFirst), scopeDigest(normalizedSecond))
}
if scopeDigest(nil) != "" {
t.Fatalf("full sync must have an empty digest")
}
}
func TestRunAlreadyFinishedTreatsMissingRunAsFirstDelivery(t *testing.T) {
producer, _ := newScopeProducer(t, []Card{
{ID: 1, ChanKey: "217", TempKey: "sim_yd_v5", Operator: 1},
})
// The fake Redis returns no metadata, which must not be treated as finished.
finished, err := producer.runAlreadyFinished(context.Background(), "fresh")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if finished {
t.Fatal("a run without stored metadata must not be reported as finished")
}
}