返回 DeepSeek-Reasonix
compaction_heartbeat_test.go
根目录 / internal / agent / compaction_heartbeat_test.go
1 package agent
2
3 import (
4 "errors"
5 "io"
6 "net/http"
7 "reflect"
8 "testing"
9 "testing/synctest"
10 "time"
11
12 "reasonix/internal/event"
13 "reasonix/internal/provider"
14 "reasonix/internal/provider/openai"
15 )
16
17 func TestCompactionDeadlineSurvivesSSEHeartbeats(t *testing.T) {
18 synctest.Test(t, func(t *testing.T) {
19 closed := make(chan struct{})
20 requests, heartbeats := 0, 0
21 client := &http.Client{Transport: accountingRoundTripFunc(func(req *http.Request) (*http.Response, error) {
22 requests++
23 reader, writer := io.Pipe()
24 go func() {
25 defer close(closed)
26 defer writer.Close()
27 tick := time.NewTicker(10 * time.Second)
28 defer tick.Stop()
29 for {
30 select {
31 case <-req.Context().Done():
32 return
33 case <-tick.C:
34 if _, err := io.WriteString(writer, ": heartbeat\n\n"); err != nil {
35 return
36 }
37 heartbeats++
38 }
39 }
40 }()
41 return &http.Response{StatusCode: http.StatusOK, Header: http.Header{"Content-Type": {"text/event-stream"}}, Body: reader}, nil
42 })}
43 p, err := openai.New(provider.Config{Name: "fixture", BaseURL: "https://example.invalid/v1", Model: "fixture", APIKey: "fixture", HTTPClient: client})
44 if err != nil {
45 t.Fatal(err)
46 }
47 sess := foldableSessionOverForce(6)
48 original := sess.Snapshot()
49 a := New(p, nil, sess, Options{ContextWindow: 5000, CompactRatio: .5}, event.Discard)
50 started := time.Now()
51 err = a.CompactNow(t.Context(), "")
52 <-closed
53 if !errors.Is(err, errSummaryBudget) || time.Since(started) != compactionBudget {
54 t.Fatalf("elapsed=%s err=%v", time.Since(started), err)
55 }
56 if requests != 1 || heartbeats < 20 {
57 t.Fatalf("requests=%d heartbeats=%d", requests, heartbeats)
58 }
59 if a.currentProjectionVersion() != 0 || !reflect.DeepEqual(original, sess.Snapshot()) {
60 t.Fatal("heartbeat timeout changed the conversation")
61 }
62 })
63 }
64
64 lines GO