返回 DeepSeek-Reasonix
context_read_test.go
根目录 / desktop / context_read_test.go
1 package main
2
3 import (
4 "path/filepath"
5 "sync"
6 "testing"
7
8 "reasonix/internal/agent"
9 "reasonix/internal/control"
10 "reasonix/internal/event"
11 "reasonix/internal/tool"
12 )
13
14 type delayedContextController struct {
15 control.SessionAPI
16 once sync.Once
17 entered, release chan struct{}
18 }
19
20 func TestContextUsageForTabReadsRotatedSessionWithoutMutatingTelemetry(t *testing.T) {
21 dir := t.TempDir()
22 rotated, stale := filepath.Join(dir, "rotated.jsonl"), filepath.Join(dir, "stale.jsonl")
23 ag := agent.New(usageProvider{}, tool.NewRegistry(), agent.NewSession("system"), agent.Options{}, event.Discard)
24 tab := &WorkspaceTab{ID: "tab", Ctrl: newFixtureController(t, control.Options{
25 Executor: ag, Sink: event.Discard, SessionDir: dir, SessionPath: rotated,
26 })}
27 // A legacy /new can rotate before the event writer re-keys telemetry.
28 tab.syncTelemetryToSession(stale)
29 tab.recordUsage(costedUsageEvent())
30 app := &App{tabs: map[string]*WorkspaceTab{"tab": tab}}
31 info := app.ContextUsageForTab("tab")
32 if info.SessionCost != 0 || info.SessionTokens != 0 {
33 t.Fatalf("rotated session read inherited previous counters: %+v", info)
34 }
35 if got := tab.telemetrySnapshot().Usage.RequestCount; got != 1 {
36 t.Fatalf("read changed live telemetry: %d, want original 1", got)
37 }
38 // The event/lifecycle writer owns the re-key even when no overview is open.
39 tab.syncTelemetryToSession(rotated)
40 if got := tab.telemetrySnapshot().Usage.RequestCount; got != 0 {
41 t.Fatalf("writer did not reset rotated session telemetry: %d", got)
42 }
43 }
44
45 func (c *delayedContextController) SessionPath() string {
46 path := c.SessionAPI.SessionPath()
47 c.once.Do(func() { close(c.entered); <-c.release })
48 return path
49 }
50
51 func TestContextReadsCannotRestorePreviousSessionTelemetry(t *testing.T) {
52 for _, panel := range []bool{false, true} {
53 for _, replace := range []bool{false, true} {
54 name := "usage"
55 if panel {
56 name = "panel"
57 }
58 if replace {
59 name += "/replacement"
60 } else {
61 name += "/rotation"
62 }
63 t.Run(name, func(t *testing.T) {
64 dir := t.TempDir()
65 oldPath, newPath := filepath.Join(dir, "old.jsonl"), filepath.Join(dir, "new.jsonl")
66 makeController := func(path string) *control.Controller {
67 ag := agent.New(usageProvider{}, tool.NewRegistry(), agent.NewSession("system"), agent.Options{}, event.Discard)
68 return newFixtureController(t, control.Options{Executor: ag, Sink: event.Discard, SessionDir: dir, SessionPath: path})
69 }
70 original := makeController(oldPath)
71 old := &delayedContextController{SessionAPI: original, entered: make(chan struct{}), release: make(chan struct{})}
72 tab := &WorkspaceTab{ID: "tab", Ctrl: old}
73 tab.syncTelemetryToSession(oldPath)
74 tab.recordUsage(costedUsageEvent())
75 if err := saveTelemetry(oldPath+".telemetry.json", tab.telemetrySnapshot()); err != nil {
76 t.Fatal(err)
77 }
78 app := &App{tabs: map[string]*WorkspaceTab{"tab": tab}}
79 done := make(chan int, 1)
80 go func() {
81 if panel {
82 done <- app.ContextPanel("tab").TotalTokens
83 } else {
84 done <- app.ContextUsageForTab("tab").SessionTokens
85 }
86 }()
87 <-old.entered
88 if replace {
89 next := makeController(newPath)
90 app.mu.Lock()
91 tab.Ctrl = next
92 tab.SessionGeneration++
93 app.mu.Unlock()
94 } else {
95 original.SetSessionPath(newPath)
96 }
97 tab.resetTelemetry(newPath)
98 close(old.release)
99 if got := <-done; got != 0 {
100 t.Fatalf("stale response contains %d previous-session tokens", got)
101 }
102 if got := tab.telemetrySnapshot().Usage; got.TotalTokens != 0 || got.SessionCost != 0 || got.RequestCount != 0 {
103 t.Fatalf("old read overwrote new telemetry: %+v", got)
104 }
105 tab.telemMu.Lock()
106 key := tab.telemetrySessionKey
107 tab.telemMu.Unlock()
108 if key != sessionRuntimeKey(newPath) {
109 t.Fatalf("telemetry owner = %q", key)
110 }
111 })
112 }
113 }
114 }
115
115 lines GO