主人 2026-10-03 拍板「注释一律中文」。本次先把本会话动过的文件就地翻; 不做全仓一次性翻译,存量按包分批排期(见 工作小结.md 待办)。 范围:ws_frame_scope.go / ws_frame_scope_test.go / broadcast.go / ws_hub_turn.go / ws_hub_message_event.go / session_read.go / server.go / ws_hub_stream_scope_test.go / history_write_broadcast_test.go / mood_test.go / extra_test.go 含中文的整行注释 18 → 382。 校验(判据 = "只改注释、零代码字节改动"): - `git diff -U0 | 排除 +++/--- | 排除注释行/空行/行尾注释` → 为空 - 独立实现:两侧剥掉注释后逐行比对代码文本 → 11/11 文件全等,不一致数 0 - 行尾注释 7 行逐条列出,代码列与原文逐字节相同(gofmt 未要求重排) - gofmt -l 干净;全树 go build/vet 绿;go test ./... = 58 包 ok / 0 FAIL; linux/amd64 + darwin/arm64 交叉编译绿 语义未删减:部分折行英文长句合并为更少行,故整行注释数 438 → 399。
103 lines
3.5 KiB
Go
103 lines
3.5 KiB
Go
package gateway
|
||
|
||
import (
|
||
"context"
|
||
"io"
|
||
"log/slog"
|
||
"testing"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/history"
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
|
||
)
|
||
|
||
// moodRunner 是一个 TurnRunner,以固定结果结算一轮,使测试能断言
|
||
// 被持久化的正是 loop 报告的心情。
|
||
type moodRunner struct {
|
||
mood string
|
||
}
|
||
|
||
func (m moodRunner) Run(_ context.Context, _ string, _ []model.Message, _ loop.SessionEnv) (*loop.Result, error) {
|
||
return &loop.Result{Text: "在的呀♪", Steps: 1, StopReason: "stop", Mood: m.mood}, nil
|
||
}
|
||
|
||
// TestOrchestratorPersistsTurnMood 在 Web 路径上端到端锁住 mood 列:
|
||
// loop 轮次边界钩子报告的心情落到持久化的助手消息上
|
||
// (design/16 §3.3 `mode/mood/thinking`),因此 DTO、`message_persisted`
|
||
// 与历史回放携带真实数据,而不是永远为空的心情。用户消息保持空
|
||
// 心情(design/16 §3.3 把心情分配给助手回复)。
|
||
func TestOrchestratorPersistsTurnMood(t *testing.T) {
|
||
t.Parallel()
|
||
hist := &fakeHistory{}
|
||
o := NewOrchestrator(OrchestratorOptions{
|
||
Run: moodRunner{mood: "happy"}.Run,
|
||
Log: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
||
History: hist,
|
||
})
|
||
|
||
sr, res := o.SubmitSync(context.Background(), "web", []model.Message{userMsg("谢谢你")}, loop.OwnerEnv())
|
||
if !sr.Accepted || res == nil {
|
||
t.Fatalf("submit = %+v res=%v, want accepted with a result", sr, res)
|
||
}
|
||
recs := hist.records()
|
||
if len(recs) != 2 {
|
||
t.Fatalf("appends = %d, want 2 (user + assistant)", len(recs))
|
||
}
|
||
if recs[1].Role != history.RoleAssistant || recs[1].Mood != "happy" {
|
||
t.Fatalf("assistant record = %+v, want Mood happy", recs[1])
|
||
}
|
||
wire := toHistoryMessage(recs[1])
|
||
if wire.Mood != "happy" {
|
||
t.Fatalf("wire mood = %q, want happy (message_persisted payload)", wire.Mood)
|
||
}
|
||
if recs[0].Mood != "" {
|
||
t.Fatalf("user message mood = %q, want empty", recs[0].Mood)
|
||
}
|
||
}
|
||
|
||
// TestOrchestratorPersistsEmptyMoodWhenUntracked 锁住诚实的降级:没有
|
||
// 情绪跟踪时(未接钩子,因此 Result.Mood 为空)存储的心情保持为空,
|
||
// 而不是被编造。
|
||
func TestOrchestratorPersistsEmptyMoodWhenUntracked(t *testing.T) {
|
||
t.Parallel()
|
||
hist := &fakeHistory{}
|
||
o := NewOrchestrator(OrchestratorOptions{
|
||
Run: moodRunner{mood: ""}.Run,
|
||
Log: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
||
History: hist,
|
||
})
|
||
if _, res := o.SubmitSync(context.Background(), "web", []model.Message{userMsg("你好")}, loop.OwnerEnv()); res == nil {
|
||
t.Fatal("sync result is nil")
|
||
}
|
||
recs := hist.records()
|
||
if len(recs) != 2 {
|
||
t.Fatalf("appends = %d, want 2", len(recs))
|
||
}
|
||
if recs[1].Mood != "" {
|
||
t.Fatalf("assistant mood = %q, want empty", recs[1].Mood)
|
||
}
|
||
}
|
||
|
||
// TestServerBroadcastMood 锁住前端 mood 处理器读取的 `mood` 帧
|
||
// (design/07 §3):类型是 `mood`,data.mood 是标签,角色 id / 好感度
|
||
// 作为额外细节一同携带。
|
||
func TestServerBroadcastMood(t *testing.T) {
|
||
t.Parallel()
|
||
env := newEnv(t, nil, false)
|
||
c := newOwnerConn(env.srv.hub, 32)
|
||
env.srv.hub.register(c)
|
||
|
||
env.srv.BroadcastMood("cyrene", "happy", 60)
|
||
ev := readFakeFrame(t, c)
|
||
if ev.Type != "mood" {
|
||
t.Fatalf("frame type = %q, want mood (frame=%+v)", ev.Type, ev)
|
||
}
|
||
data, ok := ev.Data.(map[string]any)
|
||
if !ok || data["mood"] != "happy" || data["personaId"] != "cyrene" {
|
||
t.Fatalf("frame data = %+v, want {mood:happy,personaId:cyrene}", ev.Data)
|
||
}
|
||
if got, ok := data["affection"].(float64); !ok || got != 60 {
|
||
t.Fatalf("frame affection = %v, want 60", data["affection"])
|
||
}
|
||
}
|