You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
7.6 KiB
191 lines
7.6 KiB
package console
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"yunyan/pb"
|
|
)
|
|
|
|
// 迁移口径必须与迁移**前**的下发结果逐键一致——错一个键,已发布的客户端就拿不到凭据,
|
|
// 而且只表现为「某个能力鉴权失败」,不报配置错误。下面的数据取自 2026-09-12 线上真实布局:
|
|
// global_config 里 31 个键中只有 4 组因区域而异,其余 27 个是 14 个区域同值复制。
|
|
func TestPlanEnvSvcValues(t *testing.T) {
|
|
const (
|
|
cn = int32(pb.Region_RegionChina)
|
|
us = int32(pb.Region_RegionUSA)
|
|
ea = int32(pb.Region_RegionEastAsia)
|
|
)
|
|
spec := envSvcSpec{
|
|
Id: "stt_azure",
|
|
Fields: []envFieldSpec{
|
|
{EnvKey: "AZURE_SPEECH_KEY", FieldKey: "subscription_key", Encrypted: true},
|
|
{EnvKey: "AZURE_SPEECH_REGION", FieldKey: "region"},
|
|
{EnvKey: "AZURE_SPEECH_ENDPOINT", FieldKey: "endpoint"},
|
|
},
|
|
}
|
|
|
|
t.Run("基础值取中国区,同值区域不写分叉", func(t *testing.T) {
|
|
byKey := map[string]map[int32]string{
|
|
// 真实布局:key 每区不同,region 每区不同,但很多区其实同值
|
|
"AZURE_SPEECH_KEY": {cn: "k-cn", us: "k-us", ea: "k-cn"},
|
|
"AZURE_SPEECH_REGION": {cn: "southeastasia", us: "southeastasia", ea: "eastasia"},
|
|
}
|
|
base, regions := planEnvSvcValues(spec, byKey, nil)
|
|
if base["subscription_key"] != "k-cn" {
|
|
t.Fatalf("基础值应取中国区,实际 %q", base["subscription_key"])
|
|
}
|
|
if base["region"] != "southeastasia" {
|
|
t.Fatalf("基础值应取中国区,实际 %q", base["region"])
|
|
}
|
|
// 美区 key 不同 → 要分叉;美区 region 与中国区同值 → 不该出现在分叉里
|
|
if regions[us]["subscription_key"] != "k-us" {
|
|
t.Fatalf("美区 key 不同,应写分叉,实际 %+v", regions[us])
|
|
}
|
|
if _, ok := regions[us]["region"]; ok {
|
|
t.Fatal("与基础值同值的区域字段不能写分叉——那正是老表 473 行重复的来源")
|
|
}
|
|
// 东亚 key 与中国区同值、region 不同 → 只分叉 region
|
|
if _, ok := regions[ea]["subscription_key"]; ok {
|
|
t.Fatal("东亚 key 与中国区同值,不该写分叉")
|
|
}
|
|
if regions[ea]["region"] != "eastasia" {
|
|
t.Fatalf("东亚 region 不同,应写分叉,实际 %+v", regions[ea])
|
|
}
|
|
})
|
|
|
|
t.Run("global_config 没有该键时回落业务库 config", func(t *testing.T) {
|
|
byKey := map[string]map[int32]string{}
|
|
base, regions := planEnvSvcValues(spec, byKey, map[string]string{
|
|
"AZURE_SPEECH_KEY": "from-app-config",
|
|
})
|
|
if base["subscription_key"] != "from-app-config" {
|
|
t.Fatalf("应回落业务库 config 的值,实际 %q", base["subscription_key"])
|
|
}
|
|
if len(regions) != 0 {
|
|
t.Fatalf("没有区域数据就不该有分叉,实际 %+v", regions)
|
|
}
|
|
})
|
|
|
|
t.Run("中国区缺行时退回区域0,再退回业务库", func(t *testing.T) {
|
|
byKey := map[string]map[int32]string{
|
|
"AZURE_SPEECH_KEY": {0: "k-default", us: "k-us"},
|
|
}
|
|
base, regions := planEnvSvcValues(spec, byKey, map[string]string{"AZURE_SPEECH_KEY": "k-app"})
|
|
if base["subscription_key"] != "k-default" {
|
|
t.Fatalf("中国区缺行应退回区域0,实际 %q", base["subscription_key"])
|
|
}
|
|
if regions[us]["subscription_key"] != "k-us" {
|
|
t.Fatal("与基础值不同的区域仍要分叉")
|
|
}
|
|
})
|
|
|
|
t.Run("区域行是空串按没配处理", func(t *testing.T) {
|
|
byKey := map[string]map[int32]string{
|
|
"AZURE_SPEECH_KEY": {cn: "k-cn", us: " "},
|
|
}
|
|
_, regions := planEnvSvcValues(spec, byKey, nil)
|
|
if _, ok := regions[us]; ok {
|
|
t.Fatal("空值区域不该写分叉——老逻辑下它会下发空串覆盖,那本身就是配置事故")
|
|
}
|
|
})
|
|
|
|
t.Run("整份取不到值的服务不建", func(t *testing.T) {
|
|
base, _ := planEnvSvcValues(spec, nil, nil)
|
|
if hasAnyValue(base) {
|
|
t.Fatalf("一个值都没有时不该判定为可建,实际 %+v", base)
|
|
}
|
|
})
|
|
}
|
|
|
|
// 落点表本身的自检:EnvKey 是发给已发布客户端的键名,全局不能重名——重名时
|
|
// 下发结果取决于服务 id 排序,表现为「凭据时对时不对」。FieldKey 在同一个服务内也不能重名。
|
|
func TestEnvToSvcPlanNoDuplicateKeys(t *testing.T) {
|
|
seenEnv := map[string]string{}
|
|
seenSvc := map[string]bool{}
|
|
for _, spec := range envToSvcPlan {
|
|
if seenSvc[spec.Id] {
|
|
t.Fatalf("服务 id 重复: %s", spec.Id)
|
|
}
|
|
seenSvc[spec.Id] = true
|
|
fields := map[string]bool{}
|
|
for _, f := range spec.Fields {
|
|
if f.EnvKey == "" {
|
|
t.Fatalf("服务 %s 的字段 %s 没有 env_key——那它就不会下发给老客户端", spec.Id, f.FieldKey)
|
|
}
|
|
if owner, ok := seenEnv[f.EnvKey]; ok {
|
|
t.Fatalf("下发键名 %s 被 %s 和 %s 同时占用", f.EnvKey, owner, spec.Id)
|
|
}
|
|
seenEnv[f.EnvKey] = spec.Id
|
|
if fields[f.FieldKey] {
|
|
t.Fatalf("服务 %s 的字段名 %s 重复", spec.Id, f.FieldKey)
|
|
}
|
|
fields[f.FieldKey] = true
|
|
}
|
|
if spec.Categories == "" {
|
|
t.Fatalf("服务 %s 没有类别——第三方服务配置页按类别分组,没类别就只会落进「未分类」", spec.Id)
|
|
}
|
|
}
|
|
// 被明确丢弃的键不能同时又出现在落点表里
|
|
for k := range envKeysDropped {
|
|
if owner, ok := seenEnv[k]; ok {
|
|
t.Fatalf("键 %s 既在丢弃清单里、又被服务 %s 迁移,口径矛盾", k, owner)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 线上存在「填错列」的 config 行(group 里是键名、key 里是值、value 空),
|
|
// 后果是客户端读不到这个键、后台怎么改都没效果。迁移必须按它真正表达的含义读,
|
|
// 否则 ALIBABA_BAILIAN_* 会在归一之后继续丢失。
|
|
func TestNormalizeAppCfgRow(t *testing.T) {
|
|
t.Run("填错列的行按 group 当键名读", func(t *testing.T) {
|
|
k, v := normalizeAppCfgRow(&pb.DBAppConfigItem{
|
|
Group: "ALIBABA_BAILIAN_APP_ID", Key: "mm_982f215733384f79aa5072c3c2bf", Value: "",
|
|
})
|
|
if k != "ALIBABA_BAILIAN_APP_ID" || v != "mm_982f215733384f79aa5072c3c2bf" {
|
|
t.Fatalf("应识别为 (键=group, 值=key),实际 (%q, %q)", k, v)
|
|
}
|
|
})
|
|
t.Run("正常行原样返回", func(t *testing.T) {
|
|
k, v := normalizeAppCfgRow(&pb.DBAppConfigItem{Group: "AI", Key: "AGENT_TYPE", Value: "default"})
|
|
if k != "AGENT_TYPE" || v != "default" {
|
|
t.Fatalf("正常行不该被改写,实际 (%q, %q)", k, v)
|
|
}
|
|
})
|
|
t.Run("group 不是落点表里的键名就不猜", func(t *testing.T) {
|
|
// 值确实为空的普通配置行很常见,绝不能把它的 group 当成键名
|
|
k, v := normalizeAppCfgRow(&pb.DBAppConfigItem{Group: "APP", Key: "APP_AUTHCODE_OPEN", Value: ""})
|
|
if k != "APP_AUTHCODE_OPEN" || v != "" {
|
|
t.Fatalf("不满足识别条件的行必须原样返回,实际 (%q, %q)", k, v)
|
|
}
|
|
})
|
|
t.Run("value 非空时一律原样", func(t *testing.T) {
|
|
k, v := normalizeAppCfgRow(&pb.DBAppConfigItem{
|
|
Group: "ALIBABA_BAILIAN_APP_ID", Key: "x", Value: "real",
|
|
})
|
|
if k != "x" || v != "real" {
|
|
t.Fatalf("value 有值说明这行是正常的,实际 (%q, %q)", k, v)
|
|
}
|
|
})
|
|
}
|
|
|
|
// 多应用部署下的清理判定:这是个 SaaS 平台,一台 console 管多个应用的业务库。
|
|
// 删错一行就等于让某个应用改用别人的凭据,且不报任何错。
|
|
func TestGlobalCovered(t *testing.T) {
|
|
byKey := map[string]map[int32]string{
|
|
"OPENAI_API_KEY": {1: "k-cn", 2: "k-us"},
|
|
"ALIBABA_BAILIAN_APP_ID": {}, // 全局表里没有
|
|
"EMPTY_EVERYWHERE": {1: "", 2: " "}, // 有行但全是空
|
|
}
|
|
if !globalCovered(byKey, "OPENAI_API_KEY") {
|
|
t.Fatal("全局表有非空值就算覆盖——应用自己那行在迁移前就没生效过")
|
|
}
|
|
if globalCovered(byKey, "ALIBABA_BAILIAN_APP_ID") {
|
|
t.Fatal("全局表没有该键,应用自己那行才是生效值,不能当死值删")
|
|
}
|
|
if globalCovered(byKey, "EMPTY_EVERYWHERE") {
|
|
t.Fatal("全是空值不算覆盖")
|
|
}
|
|
if globalCovered(byKey, "NOT_PRESENT") {
|
|
t.Fatal("键根本不存在时不算覆盖")
|
|
}
|
|
}
|
|
|