sync: 同步 v1.0.4 开源代码并脱敏
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
package upstreamapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/database/gredis"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/model"
|
||||
)
|
||||
|
||||
type packChanKeyRedis struct {
|
||||
gredis.Adapter
|
||||
gredis.IGroupString
|
||||
values map[string]string
|
||||
mgetCalls int
|
||||
setKeys []string
|
||||
}
|
||||
|
||||
func (r *packChanKeyRedis) GroupGeneric() gredis.IGroupGeneric { return nil }
|
||||
func (r *packChanKeyRedis) GroupHash() gredis.IGroupHash { return nil }
|
||||
func (r *packChanKeyRedis) GroupList() gredis.IGroupList { return nil }
|
||||
func (r *packChanKeyRedis) GroupPubSub() gredis.IGroupPubSub { return nil }
|
||||
func (r *packChanKeyRedis) GroupScript() gredis.IGroupScript { return nil }
|
||||
func (r *packChanKeyRedis) GroupSet() gredis.IGroupSet { return nil }
|
||||
func (r *packChanKeyRedis) GroupSortedSet() gredis.IGroupSortedSet { return nil }
|
||||
func (r *packChanKeyRedis) GroupString() gredis.IGroupString { return r }
|
||||
|
||||
func (r *packChanKeyRedis) MGet(_ context.Context, keys ...string) (map[string]*gvar.Var, error) {
|
||||
r.mgetCalls++
|
||||
values := make(map[string]*gvar.Var, len(keys))
|
||||
for _, key := range keys {
|
||||
values[key] = gvar.New(r.values[key])
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func (r *packChanKeyRedis) Set(_ context.Context, key string, value any, _ ...gredis.SetOption) (*gvar.Var, error) {
|
||||
r.values[key] = gvar.New(value).String()
|
||||
r.setKeys = append(r.setKeys, key)
|
||||
return gvar.New("OK"), nil
|
||||
}
|
||||
|
||||
func TestPackChanKeyPreservesIdentifiersAcrossLookupBranches(t *testing.T) {
|
||||
// 子进程隔离 GoFrame 缓存的默认 DB/Redis 实例,所有查询只使用临时 SQLite 和内存适配器。
|
||||
const childEnv = "PACK_CHAN_KEY_TEST_CHILD"
|
||||
if os.Getenv(childEnv) != "1" {
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
command := exec.CommandContext(t.Context(), executable, "-test.run=^TestPackChanKeyPreservesIdentifiersAcrossLookupBranches$", "-test.v")
|
||||
command.Env = append(os.Environ(), childEnv+"=1")
|
||||
output, err := command.CombinedOutput()
|
||||
t.Logf("%s", output)
|
||||
if err != nil {
|
||||
t.Fatalf("隔离回归失败:%v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
cache := &packChanKeyRedis{}
|
||||
gredis.RegisterAdapterFunc(func(*gredis.Config) gredis.Adapter { return cache })
|
||||
gredis.SetConfig(&gredis.Config{Address: "in-memory-test"})
|
||||
if err := gdb.SetConfigGroup("default", gdb.ConfigGroup{{
|
||||
Type: "sqlite", Name: filepath.Join(t.TempDir(), "pack-chan-key.db"),
|
||||
}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db := g.DB()
|
||||
t.Cleanup(func() { _ = db.Close(ctx) })
|
||||
if _, err := db.Exec(ctx, "CREATE TABLE t_sim_card_info (iccid TEXT PRIMARY KEY, chan_key TEXT)"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
api := NewUpstreamAPI()
|
||||
for _, branch := range []string{"redis", "database", "unmatched"} {
|
||||
t.Run(branch, func(t *testing.T) {
|
||||
cache.values = make(map[string]string)
|
||||
cache.mgetCalls = 0
|
||||
cache.setKeys = nil
|
||||
if _, err := db.Exec(ctx, "DELETE FROM t_sim_card_info"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
first := &model.SimKey{ICCID: "iccid-duplicate", MSISDN: "msisdn-first", IMSI: "imsi-first"}
|
||||
second := &model.SimKey{ICCID: first.ICCID, MSISDN: "msisdn-second", IMSI: "imsi-second"}
|
||||
other := &model.SimKey{ICCID: "iccid-other", MSISDN: "msisdn-other", IMSI: "imsi-other"}
|
||||
inputs := []*model.SimKey{first, second, first, other}
|
||||
wantChannel := ""
|
||||
if branch != "unmatched" {
|
||||
wantChannel = branch + "-channel"
|
||||
for _, key := range []*model.SimKey{first, other} {
|
||||
if branch == "redis" {
|
||||
cache.values["sim:chan:iccid:"+key.ICCID] = wantChannel
|
||||
} else if _, err := db.Exec(ctx, "INSERT INTO t_sim_card_info (iccid, chan_key) VALUES (?, ?)", key.ICCID, wantChannel); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
before := make([]model.SimKey, len(inputs))
|
||||
for i, key := range inputs {
|
||||
before[i] = *key
|
||||
}
|
||||
got, err := api.packChanKey(ctx, inputs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got) != 1 || len(got[wantChannel]) != len(inputs) {
|
||||
t.Fatalf("分组数量错误:%+v", got)
|
||||
}
|
||||
assertPackedSimKeys(t, got[wantChannel], inputs, before, wantChannel)
|
||||
if cache.mgetCalls != 1 {
|
||||
t.Fatalf("MGet 调用次数=%d,期望1", cache.mgetCalls)
|
||||
}
|
||||
wantSets := 0
|
||||
if branch == "database" {
|
||||
wantSets = 2
|
||||
}
|
||||
if len(cache.setKeys) != wantSets {
|
||||
t.Fatalf("缓存回填数量=%d,期望%d", len(cache.setKeys), wantSets)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("mixed_with_explicit_channel", func(t *testing.T) {
|
||||
cache.values = map[string]string{"sim:chan:iccid:redis-card": "redis-channel"}
|
||||
if _, err := db.Exec(ctx, "DELETE FROM t_sim_card_info"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Exec(ctx, "INSERT INTO t_sim_card_info (iccid, chan_key) VALUES ('database-card', 'database-channel')"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
explicit := &model.SimKey{ChanKey: "explicit-channel", ICCID: "redis-card", MSISDN: "msisdn-explicit", IMSI: "imsi-explicit"}
|
||||
inputs := []*model.SimKey{explicit}
|
||||
wanted := map[string][]*model.SimKey{}
|
||||
for _, tc := range []struct{ iccid, channel string }{
|
||||
{"redis-card", "redis-channel"}, {"database-card", "database-channel"}, {"missing-card", ""},
|
||||
} {
|
||||
first := &model.SimKey{ICCID: tc.iccid, MSISDN: tc.iccid + "-msisdn-first", IMSI: tc.iccid + "-imsi-first"}
|
||||
second := &model.SimKey{ICCID: tc.iccid, MSISDN: tc.iccid + "-msisdn-second", IMSI: tc.iccid + "-imsi-second"}
|
||||
inputs = append(inputs, first, second)
|
||||
wanted[tc.channel] = []*model.SimKey{first, second}
|
||||
}
|
||||
before := make(map[*model.SimKey]model.SimKey, len(inputs))
|
||||
for _, key := range inputs {
|
||||
before[key] = *key
|
||||
}
|
||||
got, err := api.packChanKey(ctx, inputs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got) != 4 || len(got[explicit.ChanKey]) != 1 || got[explicit.ChanKey][0] != explicit {
|
||||
t.Fatalf("显式通道分组被改变:%+v", got)
|
||||
}
|
||||
for channel, originals := range wanted {
|
||||
assertPackedSimKeys(t, got[channel], originals, []model.SimKey{before[originals[0]], before[originals[1]]}, channel)
|
||||
}
|
||||
if *explicit != before[explicit] {
|
||||
t.Fatal("显式通道原始输入被修改")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func assertPackedSimKeys(t *testing.T, got, originals []*model.SimKey, before []model.SimKey, channel string) {
|
||||
t.Helper()
|
||||
if len(got) != len(originals) {
|
||||
t.Fatalf("输出数量=%d,期望%d", len(got), len(originals))
|
||||
}
|
||||
want := make(map[model.SimKey]int, len(originals))
|
||||
for i, original := range originals {
|
||||
if *original != before[i] {
|
||||
t.Fatalf("原始输入被修改:%+v", original)
|
||||
}
|
||||
key := before[i]
|
||||
key.ChanKey = channel
|
||||
want[key]++
|
||||
}
|
||||
seen := make(map[*model.SimKey]bool, len(got))
|
||||
for _, key := range got {
|
||||
if key == nil || seen[key] {
|
||||
t.Fatal("输出为空或复用了同一指针")
|
||||
}
|
||||
seen[key] = true
|
||||
for _, original := range originals {
|
||||
if key == original {
|
||||
t.Fatal("输出复用了原始输入指针")
|
||||
}
|
||||
}
|
||||
if want[*key] == 0 {
|
||||
t.Fatalf("卡标识丢失或被另一条重复ICCID输入覆盖:%+v", key)
|
||||
}
|
||||
want[*key]--
|
||||
}
|
||||
for _, remaining := range want {
|
||||
if remaining != 0 {
|
||||
t.Fatal("输出未保留每条原始输入")
|
||||
}
|
||||
}
|
||||
if len(got) > 1 {
|
||||
other := *got[1]
|
||||
got[0].MSISDN = "changed-output"
|
||||
got[0].IMSI = "changed-output"
|
||||
if *got[1] != other {
|
||||
t.Fatal("修改一条输出影响了另一条输出")
|
||||
}
|
||||
}
|
||||
for i, original := range originals {
|
||||
if *original != before[i] {
|
||||
t.Fatalf("修改输出影响了原始输入:%+v", original)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackChanKeyEmptyInput(t *testing.T) {
|
||||
for _, inputs := range [][]*model.SimKey{nil, {}} {
|
||||
got, err := NewUpstreamAPI().packChanKey(context.Background(), inputs)
|
||||
if err != nil || got == nil || len(got) != 0 {
|
||||
t.Fatalf("空输入结果=%+v,错误=%v", got, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user