fix(dispatcher): Branch else 兜底 + 选路留痕(T4.E)
- branchNode 识别 default/else 边:主选择(true/false 或边序)未命中任何下游时, 走显式 default 边而非悄悄落 END - trace 明确区分「走 default / 未匹配收口结束(END) / 正常选路」, 杜绝静默 fall-through 被误当 bug - compose 层原有 len(chosen)==0 → END 收口保留(无 default 时的安全兜底) - 3 断言单测:default 命中 / 条件真走 true / 无 default 未命中仍返回空 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -108,6 +108,33 @@ func TestBranchNode_OrderFallback(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBranchNode_DefaultFallback(t *testing.T) {
|
||||
o := &Orchestrator{}
|
||||
tr := &execTracer{}
|
||||
byID := map[string]dsl.Node{"A": {ID: "A"}, "D": {ID: "D"}}
|
||||
// 只有 true 边 + default 边,无 false 边。
|
||||
outs := []dsl.Edge{
|
||||
{Source: "br", Target: "A", SourceHandle: "true"},
|
||||
{Source: "br", Target: "D", SourceHandle: "default"},
|
||||
}
|
||||
n := dsl.Node{ID: "br", Kind: "branch", Config: map[string]any{"condition": "1>2"}}
|
||||
// 条件假 → true 未命中 → 走 default(D),而非悄悄落 END。
|
||||
if got := o.branchNode(n, &board{}, outs, byID, tr); len(got) != 1 || got[0] != "D" {
|
||||
t.Errorf("条件假且无 false 边应走 default(D), got %v", got)
|
||||
}
|
||||
// 条件真 → 正常走 true(A),default 不介入。
|
||||
n.Config["condition"] = "2>1"
|
||||
if got := o.branchNode(n, &board{}, outs, byID, tr); len(got) != 1 || got[0] != "A" {
|
||||
t.Errorf("条件真应走 true(A), got %v", got)
|
||||
}
|
||||
// 无 default 边时,未命中仍返回空(兜底不凭空造边,交上层收口 END)。
|
||||
noDef := []dsl.Edge{{Source: "br", Target: "A", SourceHandle: "true"}}
|
||||
n.Config["condition"] = "1>2"
|
||||
if got := o.branchNode(n, &board{}, noDef, byID, tr); len(got) != 0 {
|
||||
t.Errorf("无 default 且未命中应为空, got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmallHelpers(t *testing.T) {
|
||||
cfg := map[string]any{"s": " hi ", "n": 7, "b": true, "nil": nil}
|
||||
if cstr(cfg, "s") != "hi" {
|
||||
|
||||
Reference in New Issue
Block a user