返回 DeepSeek-Reasonix
content_test.go
根目录 / internal / extension / sidecar / content_test.go
1 package sidecar
2
3 import (
4 "bytes"
5 "context"
6 "crypto/sha256"
7 "encoding/base64"
8 "encoding/hex"
9 "encoding/json"
10 "fmt"
11 "strings"
12 "testing"
13
14 "reasonix/internal/extension/protocol"
15 )
16
17 // readAll pages a content ref through ReadHandler the way an extension would
18 // and returns the reassembled bytes.
19 func readAll(t *testing.T, store *Store, ref string) []byte {
20 t.Helper()
21 var out []byte
22 var offset int64
23 for {
24 params, _ := json.Marshal(protocol.ContentReadParams{ContentRef: ref, Offset: offset})
25 raw, err := store.ReadHandler(context.Background(), params)
26 if err != nil {
27 t.Fatalf("ReadHandler at offset %d: %v", offset, err)
28 }
29 result := raw.(protocol.ContentReadResult)
30 chunk, err := base64.StdEncoding.DecodeString(result.DataBase64)
31 if err != nil {
32 t.Fatalf("chunk base64: %v", err)
33 }
34 out = append(out, chunk...)
35 if result.NextOffset == nil {
36 return out
37 }
38 offset = *result.NextOffset
39 }
40 }
41
42 func TestMaybeExternalizePassthroughBelowThreshold(t *testing.T) {
43 store := NewStore()
44 field, err := MaybeExternalize(store, "/payload", bytes.Repeat([]byte("a"), protocol.ExternalizeFieldBytes))
45 if err != nil {
46 t.Fatalf("MaybeExternalize: %v", err)
47 }
48 if field != nil {
49 t.Fatalf("%d bytes was externalized, want inline passthrough", protocol.ExternalizeFieldBytes)
50 }
51 }
52
53 func TestExternalizeAndChunkedReadRoundTrip(t *testing.T) {
54 store := NewStore()
55 // 700 KiB crosses the threshold and needs three 256 KiB pages.
56 payload := make([]byte, 700<<10)
57 for i := range payload {
58 payload[i] = byte(i % 251)
59 }
60 field, err := MaybeExternalize(store, "/payload", payload)
61 if err != nil {
62 t.Fatalf("MaybeExternalize: %v", err)
63 }
64 if field == nil {
65 t.Fatal("700 KiB payload passed through inline")
66 }
67 sum := sha256.Sum256(payload)
68 if field.SHA256 != hex.EncodeToString(sum[:]) {
69 t.Fatal("descriptor SHA-256 mismatch")
70 }
71 if field.TotalBytes != int64(len(payload)) || field.JSONPointer != "/payload" {
72 t.Fatalf("descriptor = %+v", field)
73 }
74 if got := readAll(t, store, field.ContentRef); !bytes.Equal(got, payload) {
75 t.Fatalf("round trip mismatch: got %d bytes", len(got))
76 }
77 }
78
79 func TestReadHandlerChunkBoundaries(t *testing.T) {
80 store := NewStore()
81 payload := bytes.Repeat([]byte("x"), protocol.ContentRefChunkBytes+1)
82 ref, _, _, err := store.Put(payload)
83 if err != nil {
84 t.Fatalf("Put: %v", err)
85 }
86 read := func(offset int64) protocol.ContentReadResult {
87 params, _ := json.Marshal(protocol.ContentReadParams{ContentRef: ref, Offset: offset})
88 raw, err := store.ReadHandler(context.Background(), params)
89 if err != nil {
90 t.Fatalf("ReadHandler at %d: %v", offset, err)
91 }
92 return raw.(protocol.ContentReadResult)
93 }
94 first := read(0)
95 if first.NextOffset == nil || *first.NextOffset != int64(protocol.ContentRefChunkBytes) {
96 t.Fatalf("first chunk nextOffset = %v", first.NextOffset)
97 }
98 last := read(*first.NextOffset)
99 if last.NextOffset != nil {
100 t.Fatalf("final chunk carried nextOffset %v", *last.NextOffset)
101 }
102 if last.TotalBytes != int64(len(payload)) {
103 t.Fatalf("totalBytes = %d", last.TotalBytes)
104 }
105 }
106
107 func TestReadHandlerRejectsBadOffsetsAndExpiredRefs(t *testing.T) {
108 store := NewStore()
109 ref, _, _, err := store.Put([]byte("hello world"))
110 if err != nil {
111 t.Fatalf("Put: %v", err)
112 }
113 readErr := func(params protocol.ContentReadParams) error {
114 raw, _ := json.Marshal(params)
115 _, err := store.ReadHandler(context.Background(), raw)
116 if err == nil {
117 t.Fatalf("ReadHandler(%+v) succeeded", params)
118 }
119 return err
120 }
121 // Out-of-range offset: the ref exists but the position does not.
122 if reason := protocolReason(t, readErr(protocol.ContentReadParams{ContentRef: ref, Offset: 1 << 20})); reason != protocol.ErrContentRefExpired {
123 t.Fatalf("out-of-range offset reason = %q, want %q", reason, protocol.ErrContentRefExpired)
124 }
125 // Unknown ref.
126 if reason := protocolReason(t, readErr(protocol.ContentReadParams{ContentRef: "content_gone", Offset: 0})); reason != protocol.ErrContentRefExpired {
127 t.Fatalf("expired ref reason = %q, want %q", reason, protocol.ErrContentRefExpired)
128 }
129 // Negative offsets fail strict decode as invalid_params.
130 if reason := protocolReason(t, readErr(protocol.ContentReadParams{ContentRef: ref, Offset: -1})); reason != protocol.ErrInvalidParams {
131 t.Fatalf("negative offset reason = %q, want %q", reason, protocol.ErrInvalidParams)
132 }
133 }
134
135 func TestStoreEnforcesObjectCap(t *testing.T) {
136 store := NewStore()
137 if _, _, _, err := store.Put(make([]byte, protocol.ContentRefObjectBytes+1)); err == nil {
138 t.Fatal("Put accepted an object beyond ContentRefObjectBytes")
139 } else if reason := protocolReason(t, err); reason != protocol.ErrFrameTooLarge {
140 t.Fatalf("reason = %q, want %q", reason, protocol.ErrFrameTooLarge)
141 }
142 if _, _, _, err := store.Put(make([]byte, protocol.ContentRefObjectBytes)); err != nil {
143 t.Fatalf("Put at exactly the cap failed: %v", err)
144 }
145 }
146
147 func TestStoreEvictsOldestBeyondCapacity(t *testing.T) {
148 store := NewStore()
149 refs := make([]string, 0, storeMaxEntries+1)
150 for i := 0; i < storeMaxEntries+1; i++ {
151 ref, _, _, err := store.Put([]byte(fmt.Sprintf("object-%03d", i)))
152 if err != nil {
153 t.Fatalf("Put %d: %v", i, err)
154 }
155 refs = append(refs, ref)
156 }
157 params, _ := json.Marshal(protocol.ContentReadParams{ContentRef: refs[0], Offset: 0})
158 if _, err := store.ReadHandler(context.Background(), params); err == nil {
159 t.Fatal("oldest ref was not evicted beyond the 64-entry cap")
160 }
161 last, _ := json.Marshal(protocol.ContentReadParams{ContentRef: refs[len(refs)-1], Offset: 0})
162 if _, err := store.ReadHandler(context.Background(), last); err != nil {
163 t.Fatalf("newest ref was evicted: %v", err)
164 }
165 if !strings.HasPrefix(refs[0], "content_") {
166 t.Fatalf("ref %q lacks the content_ prefix", refs[0])
167 }
168 }
169
169 lines GO