tsikol 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +22 -0
- data/CONTRIBUTING.md +84 -0
- data/LICENSE +21 -0
- data/README.md +579 -0
- data/Rakefile +12 -0
- data/docs/README.md +69 -0
- data/docs/api/middleware.md +721 -0
- data/docs/api/prompt.md +858 -0
- data/docs/api/resource.md +651 -0
- data/docs/api/server.md +509 -0
- data/docs/api/test-helpers.md +591 -0
- data/docs/api/tool.md +527 -0
- data/docs/cookbook/authentication.md +651 -0
- data/docs/cookbook/caching.md +877 -0
- data/docs/cookbook/dynamic-tools.md +970 -0
- data/docs/cookbook/error-handling.md +887 -0
- data/docs/cookbook/logging.md +1044 -0
- data/docs/cookbook/rate-limiting.md +717 -0
- data/docs/examples/code-assistant.md +922 -0
- data/docs/examples/complete-server.md +726 -0
- data/docs/examples/database-manager.md +1198 -0
- data/docs/examples/devops-tools.md +1382 -0
- data/docs/examples/echo-server.md +501 -0
- data/docs/examples/weather-service.md +822 -0
- data/docs/guides/completion.md +472 -0
- data/docs/guides/getting-started.md +462 -0
- data/docs/guides/middleware.md +823 -0
- data/docs/guides/project-structure.md +434 -0
- data/docs/guides/prompts.md +920 -0
- data/docs/guides/resources.md +720 -0
- data/docs/guides/sampling.md +804 -0
- data/docs/guides/testing.md +863 -0
- data/docs/guides/tools.md +627 -0
- data/examples/README.md +92 -0
- data/examples/advanced_features.rb +129 -0
- data/examples/basic-migrated/app/prompts/weather_chat.rb +44 -0
- data/examples/basic-migrated/app/resources/weather_alerts.rb +18 -0
- data/examples/basic-migrated/app/tools/get_current_weather.rb +34 -0
- data/examples/basic-migrated/app/tools/get_forecast.rb +30 -0
- data/examples/basic-migrated/app/tools/get_weather_by_coords.rb +48 -0
- data/examples/basic-migrated/server.rb +25 -0
- data/examples/basic.rb +73 -0
- data/examples/full_featured.rb +175 -0
- data/examples/middleware_example.rb +112 -0
- data/examples/sampling_example.rb +104 -0
- data/examples/weather-service/app/prompts/weather/chat.rb +90 -0
- data/examples/weather-service/app/resources/weather/alerts.rb +59 -0
- data/examples/weather-service/app/tools/weather/get_current.rb +82 -0
- data/examples/weather-service/app/tools/weather/get_forecast.rb +90 -0
- data/examples/weather-service/server.rb +28 -0
- data/exe/tsikol +6 -0
- data/lib/tsikol/cli/templates/Gemfile.erb +10 -0
- data/lib/tsikol/cli/templates/README.md.erb +38 -0
- data/lib/tsikol/cli/templates/gitignore.erb +49 -0
- data/lib/tsikol/cli/templates/prompt.rb.erb +53 -0
- data/lib/tsikol/cli/templates/resource.rb.erb +29 -0
- data/lib/tsikol/cli/templates/server.rb.erb +24 -0
- data/lib/tsikol/cli/templates/tool.rb.erb +60 -0
- data/lib/tsikol/cli.rb +203 -0
- data/lib/tsikol/error_handler.rb +141 -0
- data/lib/tsikol/health.rb +198 -0
- data/lib/tsikol/http_transport.rb +72 -0
- data/lib/tsikol/lifecycle.rb +149 -0
- data/lib/tsikol/middleware.rb +168 -0
- data/lib/tsikol/prompt.rb +101 -0
- data/lib/tsikol/resource.rb +53 -0
- data/lib/tsikol/router.rb +190 -0
- data/lib/tsikol/server.rb +660 -0
- data/lib/tsikol/stdio_transport.rb +108 -0
- data/lib/tsikol/test_helpers.rb +261 -0
- data/lib/tsikol/tool.rb +111 -0
- data/lib/tsikol/version.rb +5 -0
- data/lib/tsikol.rb +72 -0
- metadata +219 -0
@@ -0,0 +1,591 @@
|
|
1
|
+
# Test Helpers API Reference
|
2
|
+
|
3
|
+
Tsikol provides comprehensive test helpers for testing MCP servers, tools, resources, and prompts.
|
4
|
+
|
5
|
+
## Module: Tsikol::TestHelpers
|
6
|
+
|
7
|
+
### Including Test Helpers
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'minitest/autorun'
|
11
|
+
require 'tsikol/test_helpers'
|
12
|
+
|
13
|
+
class MyTest < Minitest::Test
|
14
|
+
include Tsikol::TestHelpers::Assertions
|
15
|
+
|
16
|
+
def setup
|
17
|
+
@server = Tsikol::Server.new(name: "test")
|
18
|
+
@client = Tsikol::TestHelpers::TestClient.new(@server)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
## Class: Tsikol::TestHelpers::TestClient
|
24
|
+
|
25
|
+
A test client that simulates MCP protocol interactions without network communication.
|
26
|
+
|
27
|
+
### Constructor
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
client = Tsikol::TestHelpers::TestClient.new(server, options = {})
|
31
|
+
```
|
32
|
+
|
33
|
+
#### Parameters
|
34
|
+
|
35
|
+
- `server`: The Tsikol::Server instance to test
|
36
|
+
- `options` (Hash): Configuration options
|
37
|
+
- `:client_id` - Client identifier for the session
|
38
|
+
- `:strict_mode` - Enable strict protocol validation
|
39
|
+
|
40
|
+
### Connection Methods
|
41
|
+
|
42
|
+
#### `#initialize_connection`
|
43
|
+
|
44
|
+
Initialize the MCP connection and retrieve capabilities.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
response = @client.initialize_connection
|
48
|
+
|
49
|
+
# Response includes server capabilities
|
50
|
+
response[:result][:capabilities]
|
51
|
+
# => {
|
52
|
+
# tools: { listChanged: true },
|
53
|
+
# resources: { subscribe: true, listChanged: true },
|
54
|
+
# prompts: { listChanged: true },
|
55
|
+
# completion: { providers: [...] },
|
56
|
+
# sampling: {}
|
57
|
+
# }
|
58
|
+
```
|
59
|
+
|
60
|
+
### Tool Methods
|
61
|
+
|
62
|
+
#### `#list_tools`
|
63
|
+
|
64
|
+
List all available tools.
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
response = @client.list_tools
|
68
|
+
|
69
|
+
response[:result][:tools]
|
70
|
+
# => [
|
71
|
+
# {
|
72
|
+
# name: "file_manager",
|
73
|
+
# description: "Manage files",
|
74
|
+
# inputSchema: { ... }
|
75
|
+
# }
|
76
|
+
# ]
|
77
|
+
```
|
78
|
+
|
79
|
+
#### `#call_tool(name, arguments)`
|
80
|
+
|
81
|
+
Call a tool with arguments.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
response = @client.call_tool("calculator", {
|
85
|
+
"operation" => "add",
|
86
|
+
"a" => 5,
|
87
|
+
"b" => 3
|
88
|
+
})
|
89
|
+
|
90
|
+
response[:result][:content]
|
91
|
+
# => [{ type: "text", text: "8" }]
|
92
|
+
```
|
93
|
+
|
94
|
+
### Resource Methods
|
95
|
+
|
96
|
+
#### `#list_resources`
|
97
|
+
|
98
|
+
List all available resources.
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
response = @client.list_resources
|
102
|
+
|
103
|
+
response[:result][:resources]
|
104
|
+
# => [
|
105
|
+
# {
|
106
|
+
# uri: "system/status",
|
107
|
+
# name: "System Status",
|
108
|
+
# description: "Current system status",
|
109
|
+
# mimeType: "application/json"
|
110
|
+
# }
|
111
|
+
# ]
|
112
|
+
```
|
113
|
+
|
114
|
+
#### `#read_resource(uri)`
|
115
|
+
|
116
|
+
Read a resource by URI.
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
response = @client.read_resource("system/status")
|
120
|
+
|
121
|
+
response[:result][:contents]
|
122
|
+
# => [
|
123
|
+
# {
|
124
|
+
# uri: "system/status",
|
125
|
+
# mimeType: "application/json",
|
126
|
+
# text: '{"status":"healthy"}'
|
127
|
+
# }
|
128
|
+
# ]
|
129
|
+
```
|
130
|
+
|
131
|
+
### Prompt Methods
|
132
|
+
|
133
|
+
#### `#list_prompts`
|
134
|
+
|
135
|
+
List all available prompts.
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
response = @client.list_prompts
|
139
|
+
|
140
|
+
response[:result][:prompts]
|
141
|
+
# => [
|
142
|
+
# {
|
143
|
+
# name: "code_assistant",
|
144
|
+
# description: "AI code assistant",
|
145
|
+
# arguments: [...]
|
146
|
+
# }
|
147
|
+
# ]
|
148
|
+
```
|
149
|
+
|
150
|
+
#### `#get_prompt(name, arguments)`
|
151
|
+
|
152
|
+
Get a prompt with arguments.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
response = @client.get_prompt("translator", {
|
156
|
+
"text" => "Hello",
|
157
|
+
"target_language" => "spanish"
|
158
|
+
})
|
159
|
+
|
160
|
+
response[:result][:messages]
|
161
|
+
# => [
|
162
|
+
# { role: "system", content: { type: "text", text: "..." } },
|
163
|
+
# { role: "user", content: { type: "text", text: "..." } }
|
164
|
+
# ]
|
165
|
+
```
|
166
|
+
|
167
|
+
### Completion Methods
|
168
|
+
|
169
|
+
#### `#complete(ref, argument)`
|
170
|
+
|
171
|
+
Get completion suggestions.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
# Tool parameter completion
|
175
|
+
response = @client.complete(
|
176
|
+
{ type: "ref/tool", name: "file_manager" },
|
177
|
+
{ name: "path", value: "/usr/lo" }
|
178
|
+
)
|
179
|
+
|
180
|
+
response[:result][:completion][:values]
|
181
|
+
# => ["/usr/local", "/usr/local/bin", "/usr/local/lib"]
|
182
|
+
|
183
|
+
# Prompt argument completion
|
184
|
+
response = @client.complete(
|
185
|
+
{ type: "ref/prompt", name: "code_review" },
|
186
|
+
{ name: "language", value: "jav" }
|
187
|
+
)
|
188
|
+
|
189
|
+
response[:result][:completion][:values]
|
190
|
+
# => ["java", "javascript"]
|
191
|
+
```
|
192
|
+
|
193
|
+
### Sampling Methods
|
194
|
+
|
195
|
+
#### `#sample_text(messages, options = {})`
|
196
|
+
|
197
|
+
Request text generation (if server has sampling enabled).
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
response = @client.sample_text(
|
201
|
+
[
|
202
|
+
{ role: "user", content: { type: "text", text: "Hello!" } }
|
203
|
+
],
|
204
|
+
temperature: 0.7,
|
205
|
+
max_tokens: 100
|
206
|
+
)
|
207
|
+
|
208
|
+
response[:result][:text]
|
209
|
+
# => "Hello! How can I help you today?"
|
210
|
+
```
|
211
|
+
|
212
|
+
### Notification Methods
|
213
|
+
|
214
|
+
#### `#send_notification(method, params = {})`
|
215
|
+
|
216
|
+
Send a notification (no response expected).
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
@client.send_notification("notifications/cancelled", {
|
220
|
+
requestId: "123",
|
221
|
+
reason: "User cancelled"
|
222
|
+
})
|
223
|
+
```
|
224
|
+
|
225
|
+
### Logging Methods
|
226
|
+
|
227
|
+
#### `#send_log(level, message, data = nil)`
|
228
|
+
|
229
|
+
Send a log message (if server has logging enabled).
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
@client.send_log(:info, "Test started", { test_name: "example" })
|
233
|
+
@client.send_log(:error, "Test failed", { error: "Timeout" })
|
234
|
+
```
|
235
|
+
|
236
|
+
## Module: Tsikol::TestHelpers::Assertions
|
237
|
+
|
238
|
+
Custom assertions for testing MCP responses.
|
239
|
+
|
240
|
+
### Response Assertions
|
241
|
+
|
242
|
+
#### `assert_successful_response(response)`
|
243
|
+
|
244
|
+
Assert that a response succeeded.
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
response = @client.call_tool("echo", { "message" => "Hello" })
|
248
|
+
assert_successful_response(response)
|
249
|
+
```
|
250
|
+
|
251
|
+
#### `assert_error_response(response, expected_code = nil)`
|
252
|
+
|
253
|
+
Assert that a response contains an error.
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
response = @client.call_tool("invalid_tool", {})
|
257
|
+
assert_error_response(response, -32601) # Method not found
|
258
|
+
```
|
259
|
+
|
260
|
+
#### `assert_response_contains(response, expected)`
|
261
|
+
|
262
|
+
Assert that response contains expected content.
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
response = @client.read_resource("config")
|
266
|
+
assert_response_contains(response, "database_url")
|
267
|
+
```
|
268
|
+
|
269
|
+
### Content Assertions
|
270
|
+
|
271
|
+
#### `assert_tool_result(response, expected)`
|
272
|
+
|
273
|
+
Assert tool result matches expected value.
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
response = @client.call_tool("calculator", {
|
277
|
+
"operation" => "multiply",
|
278
|
+
"a" => 6,
|
279
|
+
"b" => 7
|
280
|
+
})
|
281
|
+
assert_tool_result(response, "42")
|
282
|
+
```
|
283
|
+
|
284
|
+
#### `assert_resource_content(response, expected)`
|
285
|
+
|
286
|
+
Assert resource content matches expected value.
|
287
|
+
|
288
|
+
```ruby
|
289
|
+
response = @client.read_resource("version")
|
290
|
+
assert_resource_content(response, "1.0.0")
|
291
|
+
```
|
292
|
+
|
293
|
+
#### `assert_prompt_messages(response, expected_count)`
|
294
|
+
|
295
|
+
Assert prompt returns expected number of messages.
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
response = @client.get_prompt("assistant", { "task" => "help" })
|
299
|
+
assert_prompt_messages(response, 2) # System + user message
|
300
|
+
```
|
301
|
+
|
302
|
+
### Validation Assertions
|
303
|
+
|
304
|
+
#### `assert_valid_tool_schema(tool)`
|
305
|
+
|
306
|
+
Assert tool has valid parameter schema.
|
307
|
+
|
308
|
+
```ruby
|
309
|
+
response = @client.list_tools
|
310
|
+
tool = response[:result][:tools].first
|
311
|
+
assert_valid_tool_schema(tool)
|
312
|
+
```
|
313
|
+
|
314
|
+
#### `assert_valid_prompt_arguments(prompt)`
|
315
|
+
|
316
|
+
Assert prompt has valid argument definitions.
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
response = @client.list_prompts
|
320
|
+
prompt = response[:result][:prompts].first
|
321
|
+
assert_valid_prompt_arguments(prompt)
|
322
|
+
```
|
323
|
+
|
324
|
+
## Test Helpers Utilities
|
325
|
+
|
326
|
+
### Mock Server
|
327
|
+
|
328
|
+
Create a mock server for testing:
|
329
|
+
|
330
|
+
```ruby
|
331
|
+
class MockServer < Tsikol::TestHelpers::MockServer
|
332
|
+
def initialize
|
333
|
+
super(name: "mock-server")
|
334
|
+
|
335
|
+
# Configure mock responses
|
336
|
+
mock_tool_response("echo", ->(args) { args["message"] })
|
337
|
+
mock_resource_response("time", -> { Time.now.iso8601 })
|
338
|
+
mock_prompt_response("simple", ->(args) {
|
339
|
+
[{ role: "user", content: { type: "text", text: args["text"] } }]
|
340
|
+
})
|
341
|
+
end
|
342
|
+
end
|
343
|
+
```
|
344
|
+
|
345
|
+
### Test Data Generators
|
346
|
+
|
347
|
+
Generate test data for various scenarios:
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
module TestData
|
351
|
+
extend Tsikol::TestHelpers::Generators
|
352
|
+
|
353
|
+
# Generate valid JSON-RPC request
|
354
|
+
def self.request(method, params = {})
|
355
|
+
{
|
356
|
+
jsonrpc: "2.0",
|
357
|
+
id: random_id,
|
358
|
+
method: method,
|
359
|
+
params: params
|
360
|
+
}
|
361
|
+
end
|
362
|
+
|
363
|
+
# Generate tool parameters
|
364
|
+
def self.tool_params
|
365
|
+
{
|
366
|
+
"string_param" => random_string,
|
367
|
+
"number_param" => random_number,
|
368
|
+
"boolean_param" => random_boolean,
|
369
|
+
"array_param" => random_array,
|
370
|
+
"object_param" => random_object
|
371
|
+
}
|
372
|
+
end
|
373
|
+
end
|
374
|
+
```
|
375
|
+
|
376
|
+
### Performance Testing
|
377
|
+
|
378
|
+
Test performance and response times:
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
class PerformanceTest < Minitest::Test
|
382
|
+
include Tsikol::TestHelpers::Performance
|
383
|
+
|
384
|
+
def test_tool_performance
|
385
|
+
assert_response_time(max_ms: 100) do
|
386
|
+
@client.call_tool("quick_tool", { "data" => "test" })
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
def test_resource_caching
|
391
|
+
# First call
|
392
|
+
time1 = measure_time do
|
393
|
+
@client.read_resource("expensive/data")
|
394
|
+
end
|
395
|
+
|
396
|
+
# Cached call should be faster
|
397
|
+
time2 = measure_time do
|
398
|
+
@client.read_resource("expensive/data")
|
399
|
+
end
|
400
|
+
|
401
|
+
assert time2 < time1 / 10, "Cache should improve performance"
|
402
|
+
end
|
403
|
+
|
404
|
+
def test_concurrent_requests
|
405
|
+
assert_handles_concurrent_requests(count: 100) do |i|
|
406
|
+
@client.call_tool("echo", { "message" => "Request #{i}" })
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
```
|
411
|
+
|
412
|
+
## Complete Test Example
|
413
|
+
|
414
|
+
```ruby
|
415
|
+
require 'minitest/autorun'
|
416
|
+
require 'tsikol/test_helpers'
|
417
|
+
|
418
|
+
class ComprehensiveTest < Minitest::Test
|
419
|
+
include Tsikol::TestHelpers::Assertions
|
420
|
+
|
421
|
+
def setup
|
422
|
+
# Create server with all features
|
423
|
+
@server = Tsikol::Server.new(name: "test-server")
|
424
|
+
@server.logging true
|
425
|
+
@server.completion true
|
426
|
+
@server.sampling true
|
427
|
+
|
428
|
+
# Add middleware
|
429
|
+
@server.use Tsikol::LoggingMiddleware, level: :debug
|
430
|
+
@server.use Tsikol::ErrorHandlingMiddleware
|
431
|
+
|
432
|
+
# Register components
|
433
|
+
register_test_components
|
434
|
+
|
435
|
+
# Create test client
|
436
|
+
@client = Tsikol::TestHelpers::TestClient.new(@server)
|
437
|
+
@client.initialize_connection
|
438
|
+
end
|
439
|
+
|
440
|
+
def teardown
|
441
|
+
# Cleanup
|
442
|
+
cleanup_test_files
|
443
|
+
end
|
444
|
+
|
445
|
+
def test_complete_workflow
|
446
|
+
# 1. List available tools
|
447
|
+
response = @client.list_tools
|
448
|
+
assert_successful_response(response)
|
449
|
+
|
450
|
+
tools = response[:result][:tools]
|
451
|
+
assert tools.any? { |t| t[:name] == "file_manager" }
|
452
|
+
|
453
|
+
# 2. Create a test file using tool
|
454
|
+
response = @client.call_tool("file_manager", {
|
455
|
+
"operation" => "write",
|
456
|
+
"path" => "test.txt",
|
457
|
+
"content" => "Hello, World!"
|
458
|
+
})
|
459
|
+
assert_successful_response(response)
|
460
|
+
|
461
|
+
# 3. Read the file back
|
462
|
+
response = @client.call_tool("file_manager", {
|
463
|
+
"operation" => "read",
|
464
|
+
"path" => "test.txt"
|
465
|
+
})
|
466
|
+
assert_tool_result(response, "Hello, World!")
|
467
|
+
|
468
|
+
# 4. Check system status resource
|
469
|
+
response = @client.read_resource("system/status")
|
470
|
+
assert_successful_response(response)
|
471
|
+
|
472
|
+
status = JSON.parse(response[:result][:contents][0][:text])
|
473
|
+
assert_equal "healthy", status["status"]
|
474
|
+
|
475
|
+
# 5. Get AI prompt
|
476
|
+
response = @client.get_prompt("assistant", {
|
477
|
+
"task" => "Review the file test.txt"
|
478
|
+
})
|
479
|
+
assert_successful_response(response)
|
480
|
+
assert_prompt_messages(response, 2)
|
481
|
+
|
482
|
+
# 6. Test completion
|
483
|
+
response = @client.complete(
|
484
|
+
{ type: "ref/tool", name: "file_manager" },
|
485
|
+
{ name: "operation", value: "rea" }
|
486
|
+
)
|
487
|
+
assert_successful_response(response)
|
488
|
+
assert response[:result][:completion][:values].include?("read")
|
489
|
+
|
490
|
+
# 7. Test error handling
|
491
|
+
response = @client.call_tool("file_manager", {
|
492
|
+
"operation" => "read",
|
493
|
+
"path" => "nonexistent.txt"
|
494
|
+
})
|
495
|
+
assert_error_response(response)
|
496
|
+
assert_match /not found/i, response[:error][:message]
|
497
|
+
end
|
498
|
+
|
499
|
+
def test_concurrent_operations
|
500
|
+
threads = 10.times.map do |i|
|
501
|
+
Thread.new do
|
502
|
+
# Each thread performs operations
|
503
|
+
response = @client.call_tool("echo", {
|
504
|
+
"message" => "Thread #{i}"
|
505
|
+
})
|
506
|
+
assert_successful_response(response)
|
507
|
+
assert_tool_result(response, "Thread #{i}")
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
threads.each(&:join)
|
512
|
+
end
|
513
|
+
|
514
|
+
private
|
515
|
+
|
516
|
+
def register_test_components
|
517
|
+
# Register test tool
|
518
|
+
@server.tool "echo" do |message:|
|
519
|
+
message
|
520
|
+
end
|
521
|
+
|
522
|
+
# Register file manager tool
|
523
|
+
@server.register_tool_instance(TestFileManager.new)
|
524
|
+
|
525
|
+
# Register test resource
|
526
|
+
@server.resource "system/status" do
|
527
|
+
{ status: "healthy", timestamp: Time.now.iso8601 }.to_json
|
528
|
+
end
|
529
|
+
|
530
|
+
# Register test prompt
|
531
|
+
@server.prompt "assistant" do |task:|
|
532
|
+
[
|
533
|
+
{ role: "system", content: { type: "text", text: "You are helpful." } },
|
534
|
+
{ role: "user", content: { type: "text", text: task } }
|
535
|
+
]
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
def cleanup_test_files
|
540
|
+
File.delete("test.txt") if File.exist?("test.txt")
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
class TestFileManager < Tsikol::Tool
|
545
|
+
description "Test file manager"
|
546
|
+
|
547
|
+
parameter :operation do
|
548
|
+
type :string
|
549
|
+
required
|
550
|
+
enum ["read", "write"]
|
551
|
+
end
|
552
|
+
|
553
|
+
parameter :path do
|
554
|
+
type :string
|
555
|
+
required
|
556
|
+
end
|
557
|
+
|
558
|
+
parameter :content do
|
559
|
+
type :string
|
560
|
+
optional
|
561
|
+
end
|
562
|
+
|
563
|
+
def execute(operation:, path:, content: nil)
|
564
|
+
case operation
|
565
|
+
when "read"
|
566
|
+
raise Tsikol::ValidationError, "File not found" unless File.exist?(path)
|
567
|
+
File.read(path)
|
568
|
+
when "write"
|
569
|
+
File.write(path, content)
|
570
|
+
"File written successfully"
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
```
|
575
|
+
|
576
|
+
## Testing Best Practices
|
577
|
+
|
578
|
+
1. **Isolation**: Test components in isolation before integration
|
579
|
+
2. **Coverage**: Test success cases, error cases, and edge cases
|
580
|
+
3. **Assertions**: Use specific assertions for better error messages
|
581
|
+
4. **Cleanup**: Always clean up test data and resources
|
582
|
+
5. **Concurrency**: Test thread safety with concurrent requests
|
583
|
+
6. **Performance**: Set reasonable performance expectations
|
584
|
+
7. **Mocking**: Mock external dependencies for reliable tests
|
585
|
+
|
586
|
+
## See Also
|
587
|
+
|
588
|
+
- [Testing Guide](../guides/testing.md)
|
589
|
+
- [Server API](server.md)
|
590
|
+
- [Tool API](tool.md)
|
591
|
+
- [Error Handling](../cookbook/error-handling.md)
|