-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest-rag-errors.lua
More file actions
369 lines (321 loc) · 9.75 KB
/
Copy pathtest-rag-errors.lua
File metadata and controls
369 lines (321 loc) · 9.75 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
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
-- Recommended profile: rag-dev
-- Run with: llmspell -p rag-dev run test-rag-errors.lua
-- RAG testing with debug output
-- Test: RAG Error Handling Validation Test
-- Purpose: Tests error conditions, boundary cases, and recovery
-- Prerequisites: OPENAI_API_KEY environment variable for embeddings
-- Expected Output: Error handling test results and validation
-- Tags: test, rag, error-handling, validation
--
-- HOW TO RUN:
-- 1. With builtin RAG development profile (recommended):
-- ./target/debug/llmspell -p rag-dev run examples/script-users/tests/test-rag-errors.lua
--
-- 2. With custom RAG configuration:
-- ./target/debug/llmspell -c path/to/rag-config.toml run examples/script-users/tests/test-rag-errors.lua
--
-- 3. Debug mode:
-- ./target/debug/llmspell --debug -p rag-dev run examples/script-users/tests/test-rag-errors.lua
--
-- Prerequisites:
-- • LLMSpell installed and built
-- • OPENAI_API_KEY environment variable (for text-embedding-ada-002)
-- • Network connectivity for embedding API calls
--
-- EXPECTED OUTPUT:
-- • 10+ error handling tests
-- • Parameter validation tests (missing fields, invalid types, boundary conditions)
-- • Recovery after errors validation
-- • Concurrent error handling tests
-- • Resource exhaustion simulation
-- • Exit code 0 on success, 1 on failure
print("=== RAG Error Handling Validation ===")
print("")
local test_count = 0
local pass_count = 0
local fail_count = 0
-- Helper function to run a test
local function test(name, fn)
test_count = test_count + 1
print(string.format("Test %d: %s", test_count, name))
local ok, err = pcall(fn)
if ok then
pass_count = pass_count + 1
print(" ✓ PASS")
else
fail_count = fail_count + 1
print(" ✗ FAIL: " .. tostring(err))
end
print("")
end
-- Helper to test that something fails
local function should_fail(name, fn, expected_error)
test_count = test_count + 1
print(string.format("Test %d: %s (should fail)", test_count, name))
local ok, err = pcall(fn)
if ok then
fail_count = fail_count + 1
print(" ✗ FAIL: Expected error but succeeded")
else
pass_count = pass_count + 1
if expected_error and not string.find(tostring(err), expected_error) then
print(" ✓ PASS (failed as expected, but different error)")
print(" Expected: " .. expected_error)
print(" Got: " .. tostring(err))
else
print(" ✓ PASS (failed as expected)")
end
end
print("")
end
-- Check RAG availability
if not RAG then
error("RAG not available - ensure config has rag.enabled=true")
end
print("Testing error handling scenarios...")
print("")
-- Test 1: Missing required fields
should_fail("Ingest with missing content", function()
RAG.ingest({
metadata = { test = true }
-- Missing content field
})
end)
should_fail("Ingest with nil content", function()
RAG.ingest({
content = nil,
metadata = { test = true }
})
end)
should_fail("Ingest with empty content", function()
RAG.ingest({
content = "",
metadata = { test = true }
})
end)
-- Test 2: Search parameter validation
should_fail("Search with missing query", function()
RAG.search({
top_k = 5
-- Missing query field
})
end)
should_fail("Search with negative top_k", function()
RAG.search({
query = "test",
top_k = -1
})
end)
should_fail("Search with zero top_k", function()
RAG.search({
query = "test",
top_k = 0
})
end)
should_fail("Search with huge top_k", function()
RAG.search({
query = "test",
top_k = 999999999
})
end)
-- Test 3: Invalid data types
should_fail("Ingest with number content", function()
RAG.ingest({
content = 12345,
metadata = { test = true }
})
end)
should_fail("Ingest with table content", function()
RAG.ingest({
content = { "this", "is", "wrong" },
metadata = { test = true }
})
end)
should_fail("Search with number query", function()
RAG.search({
query = 42,
top_k = 5
})
end)
should_fail("Search with string top_k", function()
RAG.search({
query = "test",
top_k = "five"
})
end)
-- Test 4: Multi-tenant errors (if enabled)
test("Multi-tenant validation", function()
if not RAG.config or not RAG.config.multi_tenant then
print(" Skipping - multi-tenant not enabled")
return
end
-- Try to access another tenant's data
RAG.ingest({
content = "Secret tenant data",
tenant_id = "tenant_a"
})
local results = RAG.search({
query = "secret",
tenant_id = "tenant_b"
})
-- Should not find the other tenant's data
assert(#results == 0, "Tenant isolation violated!")
end)
-- Test 5: Recovery after errors
test("Recovery after error", function()
-- Cause an error
pcall(function()
RAG.ingest({ content = nil })
end)
-- System should still work
local id = RAG.ingest({
content = "Recovery test document",
metadata = { recovery = true }
})
assert(id ~= nil, "Failed to ingest after error")
local results = RAG.search({
query = "recovery",
top_k = 5
})
assert(results ~= nil, "Search failed after error")
end)
-- Test 6: Boundary conditions
test("Boundary conditions", function()
-- Very short content
local id1 = RAG.ingest({
content = "x",
metadata = { boundary = "short" }
})
assert(id1 ~= nil, "Failed on very short content")
-- Very long content
local long_content = string.rep("This is a test. ", 10000)
local id2 = RAG.ingest({
content = long_content,
metadata = { boundary = "long" }
})
assert(id2 ~= nil, "Failed on very long content")
-- Unicode content
local id3 = RAG.ingest({
content = "Testing unicode: 你好世界 🚀 émojis",
metadata = { boundary = "unicode" }
})
assert(id3 ~= nil, "Failed on unicode content")
-- Special characters
local id4 = RAG.ingest({
content = "Special chars: !@#$%^&*()_+-=[]{}|;:',.<>?/`~",
metadata = { boundary = "special" }
})
assert(id4 ~= nil, "Failed on special characters")
end)
-- Test 7: Metadata validation
test("Metadata handling", function()
-- No metadata (should work)
local id1 = RAG.ingest({
content = "Document without metadata"
})
assert(id1 ~= nil, "Failed with no metadata")
-- Empty metadata (should work)
local id2 = RAG.ingest({
content = "Document with empty metadata",
metadata = {}
})
assert(id2 ~= nil, "Failed with empty metadata")
-- Complex metadata
local id3 = RAG.ingest({
content = "Document with complex metadata",
metadata = {
string_field = "value",
number_field = 42,
boolean_field = true,
nested = {
field = "nested value"
},
array = {1, 2, 3}
}
})
assert(id3 ~= nil, "Failed with complex metadata")
end)
-- Test 8: Concurrent error handling
test("Concurrent operations with errors", function()
local success_count = 0
local error_count = 0
for i = 1, 20 do
if i % 3 == 0 then
-- Intentionally cause an error
local ok = pcall(function()
RAG.ingest({ content = nil })
end)
if not ok then
error_count = error_count + 1
end
else
-- Normal operation
local ok = pcall(function()
RAG.ingest({
content = "Concurrent doc " .. i
})
end)
if ok then
success_count = success_count + 1
else
error_count = error_count + 1
end
end
end
print(string.format(" Success: %d, Errors: %d", success_count, error_count))
assert(success_count > 0, "No operations succeeded")
end)
-- Test 9: Invalid filter conditions
should_fail("Search with invalid filter type", function()
RAG.search({
query = "test",
top_k = 5,
metadata_filter = "not a table" -- Should be a table
})
end)
-- Test 10: Resource exhaustion simulation
test("Graceful degradation under load", function()
-- Try to ingest many documents rapidly
local ingested = 0
local failed = 0
for i = 1, 1000 do
local ok = pcall(function()
RAG.ingest({
content = string.rep("Load test document ", 100),
metadata = { load_test = i }
})
end)
if ok then
ingested = ingested + 1
else
failed = failed + 1
-- System might reject due to rate limiting or resources
-- This is acceptable behavior
end
-- Break if we're getting too many failures
if failed > 100 then
break
end
end
print(string.format(" Ingested: %d, Failed: %d", ingested, failed))
-- System should still be responsive
local results = RAG.search({
query = "load test",
top_k = 5
})
assert(results ~= nil, "System unresponsive after load")
end)
-- Summary
print(string.rep("=", 50))
print("ERROR HANDLING VALIDATION RESULTS")
print(string.rep("=", 50))
print(string.format("Total tests: %d", test_count))
print(string.format("Passed: %d", pass_count))
print(string.format("Failed: %d", fail_count))
print("")
if fail_count == 0 then
print("✅ All error handling tests passed!")
else
print(string.format("⚠ %d test(s) failed", fail_count))
os.exit(1)
end