返回 DeepSeek-Reasonix
turn_events_head_test.go
根目录 / internal / control / turn_events_head_test.go
1 package control
2
3 import (
4 "path/filepath"
5 "slices"
6 "testing"
7 "time"
8
9 "reasonix/internal/agent"
10 "reasonix/internal/event"
11 "reasonix/internal/provider"
12 "reasonix/internal/tool"
13 )
14
15 // TestTurnDoneStampsHeadReferenceForSchemaTwo pins that the terminal turn
16 // envelope names the head and leaf message the turn ended on, so projection
17 // acks and orphan repair can identify the transcript position without a
18 // revision/digest pair.
19 func TestTurnDoneStampsHeadReferenceForSchemaTwo(t *testing.T) {
20 dir := t.TempDir()
21 path := filepath.Join(dir, "session.jsonl")
22 reply := [][]provider.Chunk{{{Type: provider.ChunkText, Text: "ok"}, {Type: provider.ChunkDone}}}
23 exec := agent.New(&recordingProvider{streams: reply}, tool.NewRegistry(), agent.NewSession("SYS"), agent.Options{}, event.Discard)
24 done := make(chan event.Event, 4)
25 c := newOwnedTestController(t, Options{Runner: exec, Executor: exec, SystemPrompt: "SYS", SessionDir: dir, SessionPath: path, Label: "test",
26 Sink: event.FuncSink(func(e event.Event) {
27 if e.Kind == event.TurnDone {
28 done <- e
29 }
30 })})
31 t.Cleanup(c.Close)
32 // The first turn's save creates the schema-2 log; the second turn ends on it.
33 for _, input := range []string{"first", "second"} {
34 c.Submit(input)
35 select {
36 case <-done:
37 case <-time.After(5 * time.Second):
38 t.Fatalf("turn %q did not finish", input)
39 }
40 }
41 records, err := c.TurnEventsAfter(0)
42 if err != nil {
43 t.Fatalf("TurnEventsAfter: %v", err)
44 }
45 var terminal int
46 for i, record := range slices.Backward(records) {
47 if record.Kind == "turn_done" {
48 terminal = i
49 break
50 }
51 }
52 ref, ok := exec.Session().Head()
53 if !ok {
54 t.Fatal("session must be schema 2 after two saved turns")
55 }
56 got := records[terminal]
57 if got.HeadID != ref.HeadID || got.LeafMessageID == "" || got.LeafMessageID != exec.Session().LeafID() {
58 t.Fatalf("terminal envelope head=%q leaf=%q, want head %q leaf %q", got.HeadID, got.LeafMessageID, ref.HeadID, exec.Session().LeafID())
59 }
60 view, err := c.TurnEventReplay(0)
61 if err != nil || view.HeadID != ref.HeadID || view.LeafMessageID != got.LeafMessageID {
62 t.Fatalf("replay view = %+v err=%v", view, err)
63 }
64 }
65
65 lines GO