Files
iotlcm/worker-svr/internal/worker/imei_config_test.go
T

158 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package worker
import (
"testing"
"github.com/gogf/gf/v2/os/gcfg"
)
// loadImeiTestConfig loads the real shipped config file.
//
// A plain g.Cfg() lookup is not enough here: the test binary does not resolve
// `config_default.yaml` (GoFrame searches for a file literally named
// `config`), so every MustGet would silently fall back to the default argument
// and a typo in the YAML key would go undetected. Loading the adapter
// explicitly makes the assertions below test the file, not the defaults.
func loadImeiTestConfig(t *testing.T) *gcfg.Config {
t.Helper()
adapter, err := gcfg.NewAdapterFile("config_default.yaml")
if err != nil {
t.Fatalf("load config_default.yaml: %v", err)
}
// Resolve relative to the package directory (internal/worker) up to the
// repository root, where config/ lives.
adapter.AddPath("../../config")
return gcfg.NewWithAdapter(adapter)
}
// TestImeiConfigFileResolves 验证 imeiFullSync 配置段真实存在于发布配置中,
// 且与状态链路结构对齐。键名拼错或段落缺失会让链路静默使用零值。
func TestImeiConfigFileResolves(t *testing.T) {
config := loadImeiTestConfig(t)
// enabled 必须存在且为 false:未完成预演验证前不得默认开启。
enabled, err := config.Get(nil, "imeiFullSync.enabled")
if err != nil {
t.Fatalf("读取 imeiFullSync.enabled 失败(配置段可能缺失): %v", err)
}
if enabled.Bool() {
t.Fatal("imeiFullSync.enabled 必须为 false(预演验证后再开)")
}
prefix, err := config.Get(nil, "imeiFullSync.redisKeyPrefix")
if err != nil || prefix.String() != "imei:" {
t.Fatalf("redisKeyPrefix = %q (err=%v), want imei:", prefix.String(), err)
}
// 三条链路的前缀必须互不相同,否则 Run 命名空间会串场。
for _, other := range []struct {
key string
want string
}{
{"trafficUsageFullSync.redisKeyPrefix", "flow:"},
{"simStatusQueueSync.redisKeyPrefix", "sim-status:"},
} {
value, err := config.Get(nil, other.key)
if err != nil {
t.Fatalf("读取 %s 失败: %v", other.key, err)
}
if value.String() == prefix.String() {
t.Fatalf("imei 前缀 %q 与 %s (%q) 重复", prefix.String(), other.key, value.String())
}
}
// 关键数值项逐条断言(对齐状态链路的键名)。
checks := []struct {
key string
want string
}{
{"imeiFullSync.producer.pageSize", "1000"},
{"imeiFullSync.producer.runLockKey", "imei:producer:active"},
{"imeiFullSync.producer.runLockTTL", "5m"},
{"imeiFullSync.consumer.lockKey", "imei:consumer:active"},
{"imeiFullSync.consumer.claimBatch", "100"},
{"imeiFullSync.consumer.mobileConcurrency", "100"},
{"imeiFullSync.consumer.telecomConcurrency", "8"},
{"imeiFullSync.consumer.networkRetries", "2"},
{"imeiFullSync.consumer.chanClientTimeout", "30s"},
{"imeiFullSync.retry.maxRounds", "5"},
{"imeiFullSync.retry.failureRetention", "720h"},
// 失败池告警阀值:漏配会静默退化为默认 50000,但键名拼错时
// 我们希望测试直接报错,而不是让链路跑在一个意外阀值上。
{"imeiFullSync.consumer.failurePoolAlertThreshold", "50000"},
}
for _, check := range checks {
value, err := config.Get(nil, check.key)
if err != nil {
t.Fatalf("读取 %s 失败: %v", check.key, err)
}
if value.String() != check.want {
t.Fatalf("%s = %q, want %q", check.key, value.String(), check.want)
}
}
// consumer.enabled 与 producer 同为 false:链路默认不跑。
consumerEnabled, err := config.Get(nil, "imeiFullSync.consumer.enabled")
if err != nil {
t.Fatalf("读取 consumer.enabled 失败: %v", err)
}
if consumerEnabled.Bool() {
t.Fatal("imeiFullSync.consumer.enabled 必须为 false")
}
}
// TestImeiTemplateQPSInConfigFile 模板限速必须覆盖生产实际存在的移动族模板,
// 否则会退回通用 1.5 QPS(255,976 张卡将耗时数天)。
func TestImeiTemplateQPSInConfigFile(t *testing.T) {
config := loadImeiTestConfig(t)
for _, tempKey := range []string{
"sim_yd_v5",
"sim_yd_mock_v5",
"sim_onelink_tengyu_v1",
"sim_onelink_xuzhou_mobile_v1",
"sim_onelink_lelian_v1",
"sim_onelink_beixin_v1",
} {
value, err := config.Get(nil, "imeiFullSync.consumer.templateQPS."+tempKey)
if err != nil {
t.Fatalf("templateQPS 缺少 %s(会退回通用 1.5 QPS): %v", tempKey, err)
}
if value.Float64() != 100 {
t.Fatalf("templateQPS.%s = %v, want 100", tempKey, value.Float64())
}
}
// 电信额度与状态/流量链路共享,必须压低。
telecom, err := config.Get(nil, "imeiFullSync.consumer.templateQPS.sim_dx_v1")
if err != nil {
t.Fatalf("templateQPS 缺少 sim_dx_v1: %v", err)
}
if telecom.Float64() != 5 {
t.Fatalf("templateQPS.sim_dx_v1 = %v, want 5(电信额度共享,必须压低)", telecom.Float64())
}
}
// TestFailurePoolAlertConfiguredForEveryChain 三条全量同步链路都要有失败池
// 告警阀值。漏配某一条时该链路会退化为「无告警」,而这正是内存问题最需要
// 被看见的地方,所以这里显式要求三条都在配置文件里出现。
func TestFailurePoolAlertConfiguredForEveryChain(t *testing.T) {
config := loadImeiTestConfig(t)
chains := []string{
"trafficUsageFullSync",
"simStatusQueueSync",
"imeiFullSync",
}
for _, chain := range chains {
key := chain + ".consumer.failurePoolAlertThreshold"
value, err := config.Get(nil, key)
if err != nil {
t.Fatalf("%s 未配置(该链路将无失败池告警): %v", key, err)
}
if value.Int64() <= 0 {
t.Fatalf("%s = %d,应为正数(0 表示关闭告警)", key, value.Int64())
}
}
}