forked from coder/claudecode.nvim
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmock.lua
More file actions
287 lines (238 loc) · 7.09 KB
/
mock.lua
File metadata and controls
287 lines (238 loc) · 7.09 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
---@brief [[
--- Mock WebSocket server implementation for testing.
--- This module provides a minimal implementation of the WebSocket server
--- functionality, suitable for testing or when real WebSocket connections
--- are not available or needed.
---@brief ]]
local M = {}
local tools = require("opencode.tools.init")
--- Mock server state
M.state = {
server = nil,
port = nil,
handlers = {},
messages = {}, -- Store messages for testing
}
---Find an available port in the given range
---@param min number The minimum port number
---@param max number The maximum port number
---@return number port The selected port
function M.find_available_port(min, max)
-- For mock implementation, just return the minimum port
-- In a real implementation, this would scan for available ports in the range
return min
end
---Start the WebSocket server
---@param config table Configuration options
---@return boolean success Whether the server started successfully
---@return number|string port_or_error The port number or error message
function M.start(config)
if M.state.server then
-- Already running
return false, "Server already running"
end
-- Find an available port
local port = M.find_available_port(config.port_range.min, config.port_range.max)
if not port then
return false, "No available ports found"
end
-- Store the port in state
M.state.port = port
-- Create mock server object
M.state.server = {
port = port,
clients = {},
on_message = function() end,
on_connect = function() end,
on_disconnect = function() end,
}
-- Register message handlers
M.register_handlers()
return true, port
end
---Stop the WebSocket server
---@return boolean success Whether the server stopped successfully
---@return string|nil error Error message if failed
function M.stop()
if not M.state.server then
-- Not running
return false, "Server not running"
end
-- Reset state
M.state.server = nil
M.state.port = nil
M.state.messages = {}
return true
end
--- Register message handlers
function M.register_handlers()
-- Default handlers
M.state.handlers = {
["mcp.connect"] = function(client, params)
-- Handle connection handshake
-- Parameters not used in this mock implementation
return { result = { message = "Connection established" } }
end,
["mcp.tool.invoke"] = function(client, params)
-- Handle tool invocation by dispatching to tools implementation
return tools.handle_invoke(client, params)
end,
}
end
---Add a client to the server
---@param client_id string A unique client identifier
---@return table client The client object
function M.add_client(client_id)
assert(type(client_id) == "string", "Expected client_id to be a string")
if not M.state.server then
error("Server not running")
end
assert(type(M.state.server.clients) == "table", "Expected mock server.clients to be a table")
local client = {
id = client_id,
connected = true,
messages = {},
}
M.state.server.clients[client_id] = client
return client
end
---Remove a client from the server
---@param client_id string The client identifier
---@return boolean success Whether removal was successful
function M.remove_client(client_id)
assert(type(client_id) == "string", "Expected client_id to be a string")
if not M.state.server or type(M.state.server.clients) ~= "table" then
return false
end
if not M.state.server.clients[client_id] then
return false
end
M.state.server.clients[client_id] = nil
return true
end
---Send a message to a client
---@param client table|string The client object or ID
---@param method string The method name
---@param params table The parameters to send
---@return boolean success Whether sending was successful
function M.send(client, method, params)
local client_obj
if type(client) == "string" then
if not M.state.server or type(M.state.server.clients) ~= "table" then
return false
end
client_obj = M.state.server.clients[client]
else
client_obj = client
end
if not client_obj then
return false
end
local message = {
jsonrpc = "2.0",
method = method,
params = params,
}
-- Store for testing
table.insert(client_obj.messages, message)
table.insert(M.state.messages, {
client = client_obj.id,
direction = "outbound",
message = message,
})
return true
end
---Send a response to a client
---@param client table|string The client object or ID
---@param id string The message ID
---@param result table|nil The result data
---@param error table|nil The error data
---@return boolean success Whether sending was successful
function M.send_response(client, id, result, error)
local client_obj
if type(client) == "string" then
if not M.state.server or type(M.state.server.clients) ~= "table" then
return false
end
client_obj = M.state.server.clients[client]
else
client_obj = client
end
if not client_obj then
return false
end
local response = {
jsonrpc = "2.0",
id = id,
}
if error then
response.error = error
else
response.result = result
end
-- Store for testing
table.insert(client_obj.messages, response)
table.insert(M.state.messages, {
client = client_obj.id,
direction = "outbound",
message = response,
})
return true
end
---Broadcast a message to all connected clients
---@param method string The method name
---@param params table The parameters to send
---@return boolean success Whether broadcasting was successful
function M.broadcast(method, params)
if not M.state.server or type(M.state.server.clients) ~= "table" then
return false
end
local success = true
for client_id, _ in pairs(M.state.server.clients) do
local send_success = M.send(client_id, method, params)
success = success and send_success
end
return success
end
---Simulate receiving a message from a client
---@param client_id string The client ID
---@param message table The message to process
---@return table|nil response The response if any
function M.simulate_message(client_id, message)
assert(type(client_id) == "string", "Expected client_id to be a string")
if not M.state.server or type(M.state.server.clients) ~= "table" then
return nil
end
local client = M.state.server.clients[client_id]
if not client then
return nil
end
-- Store the message
table.insert(M.state.messages, {
client = client_id,
direction = "inbound",
message = message,
})
-- Process the message
if message.method and M.state.handlers[message.method] then
local handler = M.state.handlers[message.method]
local response = handler(client, message.params)
if message.id and response then
-- If the message had an ID, this is a request and needs a response
M.send_response(client, message.id, response.result, response.error)
return response
end
end
return nil
end
---Clear test messages
function M.clear_messages()
M.state.messages = {}
if not M.state.server or type(M.state.server.clients) ~= "table" then
return
end
for _, client in pairs(M.state.server.clients) do
client.messages = {}
end
end
return M