test(dispatcher): Handle 入口级集成测试 —— 钉死核心编排链 + harness 治理栈协同

此前测试都直打 runGraph/evaluate,绕过真正的任务入口 Handle()——熔断、输入护栏门控、
状态机流转、token 预算、异步评测/用量这些治理逻辑的协同从未在入口级被覆盖。

新增 handle_test.go 6 例(全 fake 依赖,可断言各出口):
- HappyPath:running→done + token 流出/收尾 + 异步评测落库(ok)
- ToolFeedsAgent:图执行→工具→agent 全程经 Handle,工具被调、产出注入
- CircuitBreakerOpen:熔断开 → 快速 failed、不执行图
- BudgetExceeded:meta token_budget=3 → failed + 用量回写带 Exceeded
- GuardrailBlocks:灰区 + LLM 分类器命中 → rejected、不进 running
- EmptyTaskDropped:空 id 直接丢弃、无状态回写

配套 fakeStatus/fakeUsageSink;fakeEvalSink 加锁(Handle 异步评测在 goroutine 写)。
go test -race ./internal/eino 干净,四模块全绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-06-26 14:10:53 +08:00
parent f926f6fd41
commit 32f50cd983
3 changed files with 251 additions and 6 deletions
@@ -3,6 +3,7 @@ package eino
import (
"context"
"strings"
"sync"
"testing"
"github.com/sundynix/sundynix-dispatcher/internal/harness"
@@ -10,10 +11,25 @@ import (
"github.com/sundynix/sundynix-shared/contract"
)
// fakeEvalSink 捕获回流的评测事件,供断言纠偏终值。
type fakeEvalSink struct{ ev *contract.EvalEvent }
// fakeEvalSink 捕获回流的评测事件,供断言纠偏终值。并发安全(Handle 异步评测会在 goroutine 写)。
type fakeEvalSink struct {
mu sync.Mutex
ev *contract.EvalEvent
}
func (f *fakeEvalSink) PublishEval(ev *contract.EvalEvent) error { f.ev = ev; return nil }
func (f *fakeEvalSink) PublishEval(ev *contract.EvalEvent) error {
f.mu.Lock()
f.ev = ev
f.mu.Unlock()
return nil
}
// get 并发安全地取最近一次评测事件。
func (f *fakeEvalSink) get() *contract.EvalEvent {
f.mu.Lock()
defer f.mu.Unlock()
return f.ev
}
// orchForEval 组一个仅评测/纠偏所需依赖的编排器(生成器 gen + 评审 judge + 评测出口 sink)。
func orchForEval(gen *fakeLLM, judge func(ctx context.Context, sys, user string) (string, error), sink *fakeEvalSink) *Orchestrator {