sync: 同步 v1.0.4 开源代码并脱敏

This commit is contained in:
MiFaZhan
2026-10-29 14:52:43 +08:00
parent 32e4316ee4
commit 6893e21f07
136 changed files with 9851 additions and 436 deletions
@@ -0,0 +1,138 @@
package upstream
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcfg"
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model"
)
func fullAPNSchema() map[string]map[string]bool {
schema := make(map[string]map[string]bool, len(apnRequiredSchema))
for table, columns := range apnRequiredSchema {
schema[table] = make(map[string]bool, len(columns))
for _, column := range columns {
schema[table][column] = true
}
}
return schema
}
// TestValidateAPNSchemaRequiresAllTablesAndColumns 固化依赖判据:
// 缺表、缺列都必须给出可据此执行迁移的明确原因,而不是笼统的「未就绪」。
func TestValidateAPNSchemaRequiresAllTablesAndColumns(t *testing.T) {
if err := validateAPNSchema(fullAPNSchema()); err != nil {
t.Fatalf("完整结构不应报错:%v", err)
}
missingTable := fullAPNSchema()
delete(missingTable, "data_operation_card_claim")
err := validateAPNSchema(missingTable)
if err == nil || !strings.Contains(err.Error(), "data_operation_card_claim") || !strings.Contains(err.Error(), "缺少数据表") {
t.Fatalf("缺表未被识别:%v", err)
}
missingColumn := fullAPNSchema()
delete(missingColumn["t_apn_operation"], "revision")
err = validateAPNSchema(missingColumn)
if err == nil || !strings.Contains(err.Error(), "t_apn_operation.revision") || !strings.Contains(err.Error(), "缺少字段") {
t.Fatalf("缺列未被识别:%v", err)
}
// 表全部缺失时优先报缺表,避免一次报几十个字段干扰排查。
err = validateAPNSchema(map[string]map[string]bool{})
if err == nil || !strings.Contains(err.Error(), "缺少数据表") {
t.Fatalf("空库应优先报缺表:%v", err)
}
}
// TestAsyncApnSubmitBlockedWhenDependencyNotReady 验证依赖未就绪时在发请求前就拒绝,
// 并把错误标记为「未发送」,避免上游误判为已办理。
func TestAsyncApnSubmitBlockedWhenDependencyNotReady(t *testing.T) {
oldAdapter := g.Cfg().GetAdapter()
defer g.Cfg().SetAdapter(oldAdapter)
oldReady := apnDependencyReady
defer func() { apnDependencyReady = oldReady }()
apnDependencyReady = func(ctx context.Context) error {
return errors.New("缺少数据表 data_operation_card_claim")
}
probes, submits := 0, 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/sim/query/sim-apn-capability" {
probes++
return
}
submits++
}))
defer server.Close()
adapter, err := gcfg.NewAdapterContent(fmt.Sprintf(`{"upstream":{"proxy":{"address":%q,"timeout":"1s"}}}`, strings.TrimPrefix(server.URL, "http://")))
if err != nil {
t.Fatal(err)
}
g.Cfg().SetAdapter(adapter)
_, err = New().SubmitSimApnFunction(context.Background(), &model.SimCardApnChangeInput{
SimKeys: []*model.SimKey{{ChanKey: "test", ICCID: "fake-card"}},
ReqData: model.SimCardApnChangeInputItem{OperationID: "fake-operation"},
})
var notSent interface{ APNRequestNotSent() bool }
if err == nil || !strings.Contains(err.Error(), "依赖未就绪") {
t.Fatalf("依赖未就绪应拒绝提交:%v", err)
}
if !errors.As(err, &notSent) || !notSent.APNRequestNotSent() {
t.Fatalf("依赖拒绝必须标记为未发送:%v", err)
}
if probes != 0 || submits != 0 {
t.Fatalf("依赖未就绪时不应发出任何请求:probes=%d submits=%d", probes, submits)
}
}
// TestReadinessCacheDoesNotCacheCanceledVerification 覆盖审查发现的场景:
// 调用方取消导致的校验失败不能进进程级缓存,否则会误挡后续所有提交。
func TestReadinessCacheDoesNotCacheCanceledVerification(t *testing.T) {
cache := &apnReadinessCache{ttl: time.Minute}
calls := 0
cache.verify = func(ctx context.Context) error {
calls++
if calls == 1 {
return context.Canceled
}
return nil
}
if err := cache.check(context.Background()); !errors.Is(err, context.Canceled) {
t.Fatalf("首次应返回取消错误:%v", err)
}
if err := cache.check(context.Background()); err != nil {
t.Fatalf("取消不应被缓存,第二次应重新校验并放行:%v", err)
}
if calls != 2 {
t.Fatalf("取消后应重新校验,实际调用%d次", calls)
}
}
// TestReadinessCacheReusesResult 保证依赖检查不会每次提交都查库,同时迁移完成后能自动恢复。
func TestReadinessCacheReusesResult(t *testing.T) {
cache := &apnReadinessCache{ttl: time.Minute}
calls := 0
cache.verify = func(ctx context.Context) error {
calls++
return nil
}
for i := 0; i < 3; i++ {
if err := cache.check(context.Background()); err != nil {
t.Fatalf("第%d次检查报错:%v", i+1, err)
}
}
if calls != 1 {
t.Fatalf("缓存期内应只查一次库,实际%d次", calls)
}
}