package user import ( "encoding/json" "testing" "yunyan/comm" "yunyan/pb" ) // 规则选取口径:应用自己配了就整套用应用的(不与全局混),只留本区域+不限区域的、启用的, // 并按「优先级降序 → id 升序」排好。混合两套作用域会让全局兜底规则压过应用的精确规则。 func TestV3CallTranslateRuleSelection(t *testing.T) { rows := []*comm.CallTranslateRule{ {Id: 1, AppName: "", Region: comm.RegionAny, Priority: 900, Enable: true}, // 全局:应用有规则时整套不用 {Id: 2, AppName: "ym", Region: int32(pb.Region_RegionChina), Priority: 100, Enable: true}, // 本区域 {Id: 3, AppName: "ym", Region: comm.RegionAny, Priority: 100, Enable: true}, // 不限区域,同优先级 → 排 id 2 之后 {Id: 4, AppName: "ym", Region: int32(pb.Region_RegionUSA), Priority: 500, Enable: true}, // 别的区域 {Id: 5, AppName: "ym", Region: int32(pb.Region_RegionChina), Priority: 999, Enable: false}, // 已停用 {Id: 6, AppName: "ym", Region: int32(pb.Region_RegionChina), Priority: 200, Enable: true}, // 优先级最高的有效规则 } got := selectCallTranslateRules(rows, "ym", pb.Region_RegionChina) want := []uint64{6, 2, 3} if len(got) != len(want) { t.Fatalf("选中规则数不对:want %v,got %d 条", want, len(got)) } for i, id := range want { if got[i].Id != id { t.Fatalf("规则顺序不对:want %v,got[%d]=%d", want, i, got[i].Id) } } } // 应用一条规则都没配时回退全局默认那套,否则新接入的应用通话翻译直接不可用。 func TestV3CallTranslateFallsBackToGlobal(t *testing.T) { rows := []*comm.CallTranslateRule{ {Id: 1, AppName: "", Region: comm.RegionAny, Priority: 10, Enable: true}, {Id: 2, AppName: "other", Region: comm.RegionAny, Priority: 99, Enable: true}, } got := selectCallTranslateRules(rows, "ym", pb.Region_RegionChina) if len(got) != 1 || got[0].Id != 1 { t.Fatalf("应回退到全局规则 #1,got %v", got) } } // 全局作用域(app_name=”)自己请求时不该把别的应用的规则也算进来。 func TestV3CallTranslateGlobalScopeExcludesAppRows(t *testing.T) { rows := []*comm.CallTranslateRule{ {Id: 1, AppName: "", Region: comm.RegionAny, Priority: 10, Enable: true}, {Id: 2, AppName: "ym", Region: comm.RegionAny, Priority: 99, Enable: true}, } got := selectCallTranslateRules(rows, "", pb.Region_RegionChina) if len(got) != 1 || got[0].Id != 1 { t.Fatalf("全局作用域只该拿到全局规则 #1,got %v", got) } } // appconfig 的 call_translate 得是「拿了就能选路」的一份:规则字段齐、agent 带着服务编排。 // 客户端只调 appconfig 就要完成通话翻译选路,缺一层就得多一次往返。 func TestV3AppConfigCallTranslateContract(t *testing.T) { resp := &pb.UserGetAppConfigV3Resp{CallTranslate: &pb.CallTranslateItem{ Rules: []*pb.CallTranslateRuleItem{{ Id: 7, Name: "中日互译", SrcLangs: "zh-CN", DstLangs: "ja-JP", Bidir: true, AgentId: "trans-abc", Priority: 100, }}, Agents: []*pb.AgentItemV3{{ Id: "trans-abc", Name: "三段式翻译", Type: comm.AgentTypeTranslate, SttSvcId: "svc_stt", MtSvcId: "svc_mt", TtsSvcId: "svc_tts", }}, }} buf, err := json.Marshal(resp) if err != nil { t.Fatal(err) } var got struct { CallTranslate struct { Rules []map[string]any `json:"rules"` Agents []map[string]any `json:"agents"` } `json:"call_translate"` } if err = json.Unmarshal(buf, &got); err != nil { t.Fatal(err) } if len(got.CallTranslate.Rules) != 1 || len(got.CallTranslate.Agents) != 1 { t.Fatalf("call_translate 解析失败:%s", buf) } for _, k := range []string{"id", "name", "src_langs", "dst_langs", "bidir", "agent_id", "priority"} { if _, ok := got.CallTranslate.Rules[0][k]; !ok { t.Fatalf("规则缺字段 %s:%s", k, buf) } } // 翻译 agent 的服务槽位:三段式看 stt/mt/tts,端到端看 ast——少一个客户端就不知道调哪个服务。 for _, k := range []string{"type", "stt_svc_id", "mt_svc_id", "tts_svc_id", "ast_svc_id", "voice_map"} { if _, ok := got.CallTranslate.Agents[0][k]; !ok { t.Fatalf("翻译 agent 缺字段 %s:%s", k, buf) } } if got.CallTranslate.Agents[0]["type"] != comm.AgentTypeTranslate { t.Fatalf("翻译 agent 的 type 没下发对:%s", buf) } }