-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmemory_test.go
More file actions
290 lines (247 loc) · 7.52 KB
/
memory_test.go
File metadata and controls
290 lines (247 loc) · 7.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
package syndicate
import (
"sync"
"testing"
)
// TestSimpleMemory_AddAndGet verifica que al agregar mensajes se recuperan correctamente.
func TestSimpleMemory_AddAndGet(t *testing.T) {
mem := NewSimpleMemory()
msg1 := Message{
Role: RoleUser,
Content: "Mensaje 1",
}
msg2 := Message{
Role: RoleAssistant,
Content: "Mensaje 2",
}
mem.Add(msg1)
mem.Add(msg2)
messages := mem.Get()
if len(messages) != 2 {
t.Fatalf("Se esperaban 2 mensajes, se obtuvieron %d", len(messages))
}
if messages[0].Content != "Mensaje 1" {
t.Errorf("El primer mensaje debía ser 'Mensaje 1', se obtuvo '%s'", messages[0].Content)
}
if messages[1].Content != "Mensaje 2" {
t.Errorf("El segundo mensaje debía ser 'Mensaje 2', se obtuvo '%s'", messages[1].Content)
}
}
// TestSimpleMemory_GetReturnsCopy verifica que Get retorne una copia de los mensajes.
func TestSimpleMemory_GetReturnsCopy(t *testing.T) {
mem := NewSimpleMemory()
origMsg := Message{Role: RoleUser, Content: "Original"}
mem.Add(origMsg)
retrieved := mem.Get()
if len(retrieved) != 1 {
t.Fatalf("Se esperaba 1 mensaje, se obtuvieron %d", len(retrieved))
}
// Se modifica la copia y se verifica que el original no cambie.
retrieved[0].Content = "Modificado"
fresh := mem.Get()
if fresh[0].Content != "Original" {
t.Errorf("Get no devolvió una copia; se modificó el mensaje original a '%s'", fresh[0].Content)
}
}
// TestSimpleMemory_Concurrency prueba que las operaciones de Add sean seguras en concurrencia.
func TestSimpleMemory_Concurrency(t *testing.T) {
mem := NewSimpleMemory()
const iterations = 1000
var wg sync.WaitGroup
for i := 0; i < iterations; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
mem.Add(Message{Role: RoleUser, Content: "Mensaje concurrente"})
}(i)
}
wg.Wait()
messages := mem.Get()
if len(messages) != iterations {
t.Errorf("Se esperaban %d mensajes tras operaciones concurrentes, se obtuvieron %d", iterations, len(messages))
}
}
// TestNewMemory_WithFunctionalOptions verifica que NewMemory funcione con functional options.
func TestNewMemory_WithFunctionalOptions(t *testing.T) {
var storedMessages []Message
var mutex sync.Mutex
mem, err := NewMemory(
WithAddHandler(func(message Message) {
mutex.Lock()
defer mutex.Unlock()
storedMessages = append(storedMessages, message)
}),
WithGetHandler(func() []Message {
mutex.Lock()
defer mutex.Unlock()
// Retornar copia para simular comportamiento thread-safe
copyMessages := make([]Message, len(storedMessages))
copy(copyMessages, storedMessages)
return copyMessages
}),
)
if err != nil {
t.Fatalf("Error inesperado al crear memoria: %v", err)
}
// Agregar mensajes
msg1 := Message{Role: RoleUser, Content: "Test 1"}
msg2 := Message{Role: RoleAssistant, Content: "Test 2"}
mem.Add(msg1)
mem.Add(msg2)
// Verificar que se almacenaron correctamente
messages := mem.Get()
if len(messages) != 2 {
t.Fatalf("Se esperaban 2 mensajes, se obtuvieron %d", len(messages))
}
if messages[0].Content != "Test 1" {
t.Errorf("El primer mensaje debía ser 'Test 1', se obtuvo '%s'", messages[0].Content)
}
if messages[1].Content != "Test 2" {
t.Errorf("El segundo mensaje debía ser 'Test 2', se obtuvo '%s'", messages[1].Content)
}
}
// TestNewMemory_MissingAddHandler verifica que NewMemory falle sin WithAddHandler.
func TestNewMemory_MissingAddHandler(t *testing.T) {
_, err := NewMemory(
WithGetHandler(func() []Message {
return []Message{}
}),
)
if err == nil {
t.Error("Se esperaba error al faltar WithAddHandler")
}
if err != nil && err.Error() != "WithAddHandler is required when creating custom memory" {
t.Errorf("Error inesperado: %v", err)
}
}
// TestNewMemory_MissingGetHandler verifica que NewMemory falle sin WithGetHandler.
func TestNewMemory_MissingGetHandler(t *testing.T) {
_, err := NewMemory(
WithAddHandler(func(message Message) {}),
)
if err == nil {
t.Error("Se esperaba error al faltar WithGetHandler")
}
if err != nil && err.Error() != "WithGetHandler is required when creating custom memory" {
t.Errorf("Error inesperado: %v", err)
}
}
// TestNewMemory_NilAddHandler verifica que WithAddHandler falle con función nil.
func TestNewMemory_NilAddHandler(t *testing.T) {
_, err := NewMemory(
WithAddHandler(nil),
WithGetHandler(func() []Message {
return []Message{}
}),
)
if err == nil {
t.Error("Se esperaba error al pasar nil a WithAddHandler")
}
if err != nil && err.Error() != "failed to apply memory option: addFunc cannot be nil" {
t.Errorf("Error inesperado: %v", err)
}
}
// TestNewMemory_NilGetHandler verifica que WithGetHandler falle con función nil.
func TestNewMemory_NilGetHandler(t *testing.T) {
_, err := NewMemory(
WithAddHandler(func(message Message) {}),
WithGetHandler(nil),
)
if err == nil {
t.Error("Se esperaba error al pasar nil a WithGetHandler")
}
if err != nil && err.Error() != "failed to apply memory option: getFunc cannot be nil" {
t.Errorf("Error inesperado: %v", err)
}
}
// TestNewMemory_EmptyOptions verifica que NewMemory falle sin opciones.
func TestNewMemory_EmptyOptions(t *testing.T) {
_, err := NewMemory()
if err == nil {
t.Error("Se esperaba error al crear memoria sin opciones")
}
}
// TestNewMemory_CustomLogic verifica que se pueda implementar lógica personalizada.
func TestNewMemory_CustomLogic(t *testing.T) {
// Simulamos una memoria que solo guarda los últimos 3 mensajes
var messages []Message
maxSize := 3
mem, err := NewMemory(
WithAddHandler(func(message Message) {
messages = append(messages, message)
if len(messages) > maxSize {
messages = messages[1:] // Remover el primer mensaje
}
}),
WithGetHandler(func() []Message {
copyMessages := make([]Message, len(messages))
copy(copyMessages, messages)
return copyMessages
}),
)
if err != nil {
t.Fatalf("Error inesperado: %v", err)
}
// Agregar más mensajes que el límite
for i := 0; i < 5; i++ {
mem.Add(Message{
Role: RoleUser,
Content: "Mensaje " + string(rune('1'+i)),
})
}
// Verificar que solo se mantuvieron los últimos 3
retrieved := mem.Get()
if len(retrieved) != maxSize {
t.Errorf("Se esperaban %d mensajes, se obtuvieron %d", maxSize, len(retrieved))
}
// Verificar que son los mensajes correctos (3, 4, 5)
expectedContents := []string{"Mensaje 3", "Mensaje 4", "Mensaje 5"}
for i, msg := range retrieved {
if msg.Content != expectedContents[i] {
t.Errorf("Mensaje %d: se esperaba '%s', se obtuvo '%s'",
i, expectedContents[i], msg.Content)
}
}
}
// TestNewMemory_Concurrency verifica que una memoria personalizada pueda ser thread-safe.
func TestNewMemory_Concurrency(t *testing.T) {
var messages []Message
var mutex sync.RWMutex
mem, err := NewMemory(
WithAddHandler(func(message Message) {
mutex.Lock()
defer mutex.Unlock()
messages = append(messages, message)
}),
WithGetHandler(func() []Message {
mutex.RLock()
defer mutex.RUnlock()
copyMessages := make([]Message, len(messages))
copy(copyMessages, messages)
return copyMessages
}),
)
if err != nil {
t.Fatalf("Error inesperado: %v", err)
}
const iterations = 100
var wg sync.WaitGroup
// Ejecutar operaciones Add concurrentes
for i := 0; i < iterations; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
mem.Add(Message{
Role: RoleUser,
Content: "Mensaje concurrente",
})
}(i)
}
wg.Wait()
// Verificar que todos los mensajes se agregaron
finalMessages := mem.Get()
if len(finalMessages) != iterations {
t.Errorf("Se esperaban %d mensajes, se obtuvieron %d",
iterations, len(finalMessages))
}
}