返回 DeepSeek-Reasonix
session_listing_repair_test.go
根目录 / internal / agent / session_listing_repair_test.go
1 package agent
2
3 import (
4 "context"
5 "errors"
6 "path/filepath"
7 "strings"
8 "testing"
9 "time"
10
11 "reasonix/internal/provider"
12 "reasonix/internal/store"
13 )
14
15 func TestRepairSessionListingProjectionHealsRecoveryLedgerOnce(t *testing.T) {
16 path := filepath.Join(t.TempDir(), "legacy-recovery.jsonl")
17 msgs := []provider.Message{
18 {Role: provider.RoleUser, Content: "question"},
19 {Role: provider.RoleAssistant, Content: "answer"},
20 }
21 writeSessionFile(t, path, msgs)
22 wrong := strings.Repeat("0", 64)
23 if err := SaveBranchMeta(path, BranchMeta{
24 ID: BranchID(path), Recovered: true, Revision: 7,
25 ContentDigest: wrong, RecoveryDigest: wrong, SchemaVersion: 1,
26 }); err != nil {
27 t.Fatal(err)
28 }
29
30 result, err := RepairSessionListingProjection(context.Background(), path)
31 if err != nil {
32 t.Fatal(err)
33 }
34 if result.Status != SessionListingRepairApplied || !result.LedgerRepaired || result.Preview != "question" || result.Turns != 1 {
35 t.Fatalf("repair result = %+v", result)
36 }
37 digest, err := ContentDigestForMessages(msgs)
38 if err != nil {
39 t.Fatal(err)
40 }
41 meta, ok, err := LoadBranchMeta(path)
42 if err != nil || !ok {
43 t.Fatalf("LoadBranchMeta ok=%v err=%v", ok, err)
44 }
45 if meta.Revision != 8 || meta.ContentDigest != digest || meta.RecoveryDigest != digest ||
46 meta.ListingRevision != 8 || meta.ListingContentDigest != digest || meta.Preview != "question" || meta.Turns != 1 {
47 t.Fatalf("healed meta = %+v", meta)
48 }
49 idx, err := LoadSessionDisplayIndex(store.SessionDisplayIndex(path))
50 if err != nil || idx.Revision != 8 || idx.ContentDigest != digest || !idx.ListingPreviewKnown || idx.ListingPreview != "question" {
51 t.Fatalf("display index = %+v err=%v", idx, err)
52 }
53
54 result, err = RepairSessionListingProjection(context.Background(), path)
55 if err != nil || result.Status != SessionListingRepairAlreadyCurrent || result.LedgerRepaired {
56 t.Fatalf("second repair = %+v err=%v", result, err)
57 }
58 }
59
60 func TestRepairSessionListingProjectionYieldsToForegroundSave(t *testing.T) {
61 path := filepath.Join(t.TempDir(), "busy.jsonl")
62 writeSessionFile(t, path, []provider.Message{{Role: provider.RoleUser, Content: "question"}})
63 unlock := lockSessionSavePath(path)
64 defer unlock()
65 started := time.Now()
66 _, err := RepairSessionListingProjection(context.Background(), path)
67 if !errors.Is(err, ErrSessionListingRepairBusy) {
68 t.Fatalf("repair err = %v, want busy", err)
69 }
70 if elapsed := time.Since(started); elapsed > 100*time.Millisecond {
71 t.Fatalf("busy repair waited %v", elapsed)
72 }
73 }
74
75 func TestRepairSessionListingProjectionHonorsCancellationDuringLargeDecode(t *testing.T) {
76 path := filepath.Join(t.TempDir(), "large.jsonl")
77 writeSessionFile(t, path, []provider.Message{{
78 Role: provider.RoleUser, Content: "large", Images: []string{strings.Repeat("a", 49<<20)},
79 }})
80 ctx, cancel := context.WithCancel(context.Background())
81 time.AfterFunc(time.Millisecond, cancel)
82 started := time.Now()
83 _, err := RepairSessionListingProjection(ctx, path)
84 if !errors.Is(err, context.Canceled) {
85 t.Fatalf("repair err = %v, want context cancellation", err)
86 }
87 if elapsed := time.Since(started); elapsed > 2*time.Second {
88 t.Fatalf("cancellation took %v", elapsed)
89 }
90 }
91
91 lines GO