package main import ( "context" "testing" "time" "git.yeij.top/AskaEth/Cyrene_v2/internal/bus" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/history" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/orchestration" ) // 本文件锁住 M32 的接线本身:**组合根真的把回填订阅者挂上了那条总线**。 // // 为什么单独测这一步:这次缺陷的形状就是「所有零件都在、没有一根线」。回填订阅者 // 写对了、总线也发得对,只要 `wireFrontend` 里少掉那一行调用,主人在界面上看到的 // 仍然是「没有下文」—— 而且没有任何测试会红。因此这里断言的是**装配后的行为**: // 组合根接线 → 真 bus 上发布编排层载荷 → 父会话真的多出一条系统注记行。 // waitForBackfillRow 轮询历史,直到父会话出现一条消息或到期。 func waitForBackfillRow(t *testing.T, st *history.Store, sessionID string) []history.Message { t.Helper() deadline := time.Now().Add(3 * time.Second) for time.Now().Before(deadline) { rows, err := st.Recent(context.Background(), sessionID, 10) if err != nil { t.Fatalf("read recent: %v", err) } if len(rows) > 0 { return rows } time.Sleep(5 * time.Millisecond) } t.Fatal("the parent session never received the backfilled note: the composition root did not wire the subscriber") return nil } // TestWireSubagentBackfillPersistsThroughBus 是 ④ 的正面证据:装配 → 发布 → 落行。 func TestWireSubagentBackfillPersistsThroughBus(t *testing.T) { b := bus.New() t.Cleanup(func() { _ = b.Close() }) a := &app{bus: b, history: newTestHistory(t), log: testLog()} wireSubagentBackfill(a, nil) payload := &orchestration.SubagentResultEvent{ SessionID: history.MainSessionID, SpawnResult: &orchestration.SpawnResult{ Status: "done", Summary: "子代理汇总: 共 1 个, 1 成功", Agents: []orchestration.SpawnAgentResult{{ ID: "sub-a1", Goal: "调研 X", Role: "cyrene", Status: "done", Summary: "结论:X 可行", }}, }, } if err := b.Publish(context.Background(), orchestration.SubagentResultSubject, payload); err != nil { t.Fatalf("publish: %v", err) } rows := waitForBackfillRow(t, a.history, history.MainSessionID) if len(rows) != 1 { t.Fatalf("parent session has %d messages, want exactly the one system note: %+v", len(rows), rows) } row := rows[0] if row.Role != history.RoleSystem { t.Fatalf("backfilled row role = %q, want %q (a sub-agent conclusion is not her own words)", row.Role, history.RoleSystem) } if row.TurnID != "" || row.PersonaID != "" { t.Fatalf("backfilled row = %+v, want no turn_id and no persona_id", row) } } // TestWireSubagentBackfillWithoutHistoryWarns 锁住降级口径:没有历史存储时不装配、 // 不 panic,而是留一条可见的告警(绝不假装回填成立)。 func TestWireSubagentBackfillWithoutHistoryWarns(t *testing.T) { a := &app{bus: bus.New(), log: testLog()} wireSubagentBackfill(a, nil) // 没有 history:只允许告警 if err := a.bus.Publish(context.Background(), orchestration.SubagentResultSubject, &orchestration.SubagentResultEvent{SessionID: history.MainSessionID}); err != nil { t.Fatalf("publish on an unwired root: %v", err) } }