chore: 初始开源发布
iotlcm-upstream-chan:运营商上游通道适配服务(统一 /sim/* 协议)。 - 内联原私有依赖:yunze-public → internal/pub/(10 包 18 文件), yunze-pb/pb → internal/pb/(9 文件);Consul 注册改用官方 gogf/gf/contrib/registry/consul/v2(API 兼容) - Go module:yz-svr → git.5iot.com/iotlcm-public/iotlcm-upstream-chan - 修复 mingqian 包测试:实现重构后 `adapter.operations` 字段已移除, 测试断言按当前实现语义重写(不改产品行为) - 凭据与内部标识脱敏:MySQL/Redis/Consul 凭据、私网与生产 IP、 内网域名、真实手机号、注释里的真实 token/transid - 移除内部资产:CI 触发器、生产通道种子 SQL、真实凭据测试资源文档 - Dockerfile 改用公共基础镜像,移除写死的 registry 凭据 - 补 Apache-2.0 LICENSE,重写 README,LF 行尾 + .gitattributes
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
package upstreamapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/logic/channelconfig"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/service"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type normalGateTestChannel struct {
|
||||
model.SimCardChannelI
|
||||
}
|
||||
|
||||
type normalGateTestTemplate struct {
|
||||
calls int
|
||||
lastConfig *model.SimChannelConfigItemCache
|
||||
}
|
||||
|
||||
func (f *normalGateTestTemplate) NewTemp(_ context.Context, cfg *model.SimChannelConfigItemCache) model.SimCardChannelI {
|
||||
f.calls++
|
||||
f.lastConfig = cfg
|
||||
return &normalGateTestChannel{}
|
||||
}
|
||||
|
||||
// Exercise real ORM decoding and config versioning as well as both creation paths.
|
||||
// All SQL runs against a temporary SQLite file, never a configured environment DB.
|
||||
func TestChannelNormalCreationGate(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
if err := gdb.SetConfigGroup("default", gdb.ConfigGroup{{
|
||||
Type: "sqlite", Name: filepath.Join(t.TempDir(), "channels.db"),
|
||||
}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db := g.DB()
|
||||
t.Cleanup(func() { _ = db.Close(ctx) })
|
||||
execSQL := func(query string, args ...any) {
|
||||
t.Helper()
|
||||
if _, err := db.Exec(ctx, query, args...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
execSQL("CREATE TABLE t_sim_channels (id INTEGER PRIMARY KEY, chan_key TEXT, temp_key TEXT, chan_name TEXT, is_normal INTEGER NULL)")
|
||||
execSQL("CREATE TABLE t_sim_channel_template (id INTEGER PRIMARY KEY, temp_key TEXT, temp_model TEXT)")
|
||||
execSQL("INSERT INTO t_sim_channel_template (id, temp_key, temp_model) VALUES (1, 'gate_test', 'direct')")
|
||||
|
||||
oldConfig := service.ChannelConfig()
|
||||
t.Cleanup(func() { service.RegisterChannelConfig(oldConfig) })
|
||||
|
||||
for _, path := range []string{"loop", "refresh"} {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
value any
|
||||
eligible bool
|
||||
}{
|
||||
{"yes_is_zero", 0, true},
|
||||
{"no_is_one", 1, false},
|
||||
{"null", nil, false},
|
||||
{"unexpected", 2, false},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
execSQL("DELETE FROM t_sim_channels")
|
||||
execSQL("INSERT INTO t_sim_channels (id, chan_key, temp_key, is_normal) VALUES (1, 'gate_channel', 'gate_test', ?)", tc.value)
|
||||
config := channelconfig.NewChannelConfig()
|
||||
service.RegisterChannelConfig(config)
|
||||
api := NewUpstreamAPI()
|
||||
factory := &normalGateTestTemplate{}
|
||||
api.RegisterTemp("gate_test", factory)
|
||||
reload := func() {
|
||||
t.Helper()
|
||||
var err error
|
||||
if path == "loop" {
|
||||
err = config.LoadAllChannelConfig(ctx)
|
||||
if err == nil {
|
||||
err = api.loop(ctx)
|
||||
}
|
||||
} else {
|
||||
err = api.RefreshChannel(ctx, "gate_channel")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("refresh must succeed: %v", err)
|
||||
}
|
||||
}
|
||||
reload()
|
||||
cfg, ok := config.GetChannel("gate_channel")
|
||||
if !ok || cfg.CanCreateInstance() != tc.eligible {
|
||||
t.Fatalf("wrong eligibility after ORM scan: %+v", cfg)
|
||||
}
|
||||
if tc.value == nil && cfg.IsNormal != nil {
|
||||
t.Fatalf("SQL NULL decoded as non-nil: %v", *cfg.IsNormal)
|
||||
}
|
||||
if cfg.Channel.ChanKey != "gate_channel" {
|
||||
t.Fatal("embedded channel fields were not scanned")
|
||||
}
|
||||
_, exists := api.GetChan("gate_channel")
|
||||
if exists != tc.eligible || (factory.calls == 1) != tc.eligible {
|
||||
t.Fatalf("instance exists=%v factory calls=%d eligible=%v", exists, factory.calls, tc.eligible)
|
||||
}
|
||||
initialVersion := cfg.Version
|
||||
|
||||
// Only is_normal changes: the configuration cache must notice it.
|
||||
execSQL("UPDATE t_sim_channels SET is_normal=0")
|
||||
reload()
|
||||
current, ok := api.GetChan("gate_channel")
|
||||
if !ok || current.Instance == nil {
|
||||
t.Fatal("eligible channel was not instantiated")
|
||||
}
|
||||
cfg, _ = config.GetChannel("gate_channel")
|
||||
if !tc.eligible && cfg.Version == initialVersion {
|
||||
t.Fatal("changing only is_normal did not change config version")
|
||||
}
|
||||
oldCalls := factory.calls
|
||||
|
||||
// Becoming ineligible must remove the old runtime instance.
|
||||
execSQL("UPDATE t_sim_channels SET is_normal=1, chan_name='changed while disabled', temp_key='missing_template'")
|
||||
reload()
|
||||
if _, exists := api.GetChan("gate_channel"); exists || factory.calls != oldCalls {
|
||||
t.Fatal("ineligible channel retained or rebuilt its runtime instance")
|
||||
}
|
||||
cfg, _ = config.GetChannel("gate_channel")
|
||||
if cfg.CanCreateInstance() {
|
||||
t.Fatal("ineligible config must remain ineligible")
|
||||
}
|
||||
// Repeat the disabled refresh, as a retry would.
|
||||
reload()
|
||||
if factory.calls != oldCalls {
|
||||
t.Fatal("retry created an ineligible instance")
|
||||
}
|
||||
|
||||
// A process restart cannot recreate the retained disabled instance.
|
||||
restarted := NewUpstreamAPI()
|
||||
restarted.RegisterTemp("gate_test", factory)
|
||||
if err := restarted.loop(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := restarted.GetChan("gate_channel"); ok {
|
||||
t.Fatal("restart recreated disabled instance")
|
||||
}
|
||||
|
||||
execSQL("UPDATE t_sim_channels SET is_normal=0, temp_key='gate_test'")
|
||||
reload()
|
||||
restored, _ := api.GetChan("gate_channel")
|
||||
if restored == current || factory.calls != oldCalls+1 ||
|
||||
factory.lastConfig.Channel.ChanName != "changed while disabled" {
|
||||
t.Fatal("restored channel did not rebuild using the latest configuration")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user