Module: MockServer
- Defined in:
- lib/mockserver/llm.rb,
lib/mockserver/mcp.rb,
lib/mockserver/rspec.rb,
lib/mockserver/client.rb,
lib/mockserver/errors.rb,
lib/mockserver/models.rb,
lib/mockserver/version.rb,
lib/mockserver/binary_launcher.rb,
lib/mockserver/websocket_client.rb,
lib/mockserver/forward_chain_expectation.rb
Defined Under Namespace
Modules: CrossProtocolTrigger, LLM, MCP, RSpec, ResponseMode Classes: AfterAction, BinaryLauncher, BinaryResponse, Body, CallbackError, Client, ConnectionError, ConnectionOptions, CrossProtocolScenario, CrudExpectationsDefinition, Delay, DelayDistribution, DnsRecord, DnsResponse, Error, Expectation, ExpectationId, ExpectationStep, ForwardChainExpectation, GraphQLBody, GrpcBidiResponse, GrpcBidiRule, GrpcStreamMessage, GrpcStreamResponse, HttpChaosProfile, HttpClassCallback, HttpError, HttpForward, HttpObjectCallback, HttpOverrideForwardedRequest, HttpRequest, HttpRequestAndHttpResponse, HttpResponse, HttpSseResponse, HttpTemplate, HttpWebSocketResponse, JsonRpcBody, KeyToMultiValue, LoadProfile, LoadScenario, LoadStage, LoadStep, OpenAPIDefinition, OpenAPIExpectation, Ports, ScenarioHandle, ScenarioState, SocketAddress, SseEvent, TimeToLive, Times, Verification, VerificationError, VerificationSequence, VerificationTimes, WebSocketClient, WebSocketError, WebSocketMessage
Constant Summary collapse
- FIELD_MAP =
Explicit mapping from Ruby snake_case field names to the camelCase keys expected by the MockServer JSON protocol.
{ 'status_code' => 'statusCode', 'reason_phrase' => 'reasonPhrase', 'keep_alive' => 'keepAlive', 'respond_before_body' => 'respondBeforeBody', 'query_string_parameters' => 'queryStringParameters', 'path_parameters' => 'pathParameters', 'socket_address' => 'socketAddress', 'time_unit' => 'timeUnit', 'time_to_live' => 'timeToLive', 'remaining_times' => 'remainingTimes', 'close_socket' => 'closeSocket', 'close_socket_delay' => 'closeSocketDelay', 'suppress_content_length_header' => 'suppressContentLengthHeader', 'content_length_header_override' => 'contentLengthHeaderOverride', 'suppress_connection_header' => 'suppressConnectionHeader', 'keep_alive_override' => 'keepAliveOverride', 'connection_options' => 'connectionOptions', 'callback_class' => 'callbackClass', 'client_id' => 'clientId', 'response_callback' => 'responseCallback', 'drop_connection' => 'dropConnection', 'response_bytes' => 'responseBytes', 'stream_error' => 'streamError', 'http_request' => 'httpRequest', 'http_response' => 'httpResponse', 'http_response_template' => 'httpResponseTemplate', 'http_response_class_callback' => 'httpResponseClassCallback', 'http_response_object_callback' => 'httpResponseObjectCallback', 'http_forward' => 'httpForward', 'http_forward_template' => 'httpForwardTemplate', 'http_forward_class_callback' => 'httpForwardClassCallback', 'http_forward_object_callback' => 'httpForwardObjectCallback', 'http_override_forwarded_request' => 'httpOverrideForwardedRequest', 'http_error' => 'httpError', 'http_sse_response' => 'httpSseResponse', 'http_websocket_response' => 'httpWebSocketResponse', 'template_type' => 'templateType', 'template_file' => 'templateFile', 'file_path' => 'filePath', 'base64_bytes' => 'base64Bytes', 'not_body' => 'not', 'content_type' => 'contentType', 'at_least' => 'atLeast', 'at_most' => 'atMost', 'expectation_id' => 'expectationId', 'expectation_ids' => 'expectationIds', 'http_requests' => 'httpRequests', 'spec_url_or_payload' => 'specUrlOrPayload', 'operations_and_responses' => 'operationsAndResponses', 'operation_id' => 'operationId', 'request_modifier' => 'requestModifier', 'response_modifier' => 'responseModifier', 'maximum_number_of_request_to_return_in_verification_failure' => 'maximumNumberOfRequestToReturnInVerificationFailure', 'base_path' => 'basePath', 'id_field' => 'idField', 'id_strategy' => 'idStrategy', 'initial_data' => 'initialData', 'error_status' => 'errorStatus', 'error_probability' => 'errorProbability', 'drop_connection_probability' => 'dropConnectionProbability', 'retry_after' => 'retryAfter', 'succeed_first' => 'succeedFirst', 'fail_request_count' => 'failRequestCount', 'outage_after_millis' => 'outageAfterMillis', 'outage_duration_millis' => 'outageDurationMillis', 'truncate_body_at_fraction' => 'truncateBodyAtFraction', 'malformed_body' => 'malformedBody', 'slow_response_chunk_size' => 'slowResponseChunkSize', 'slow_response_chunk_delay' => 'slowResponseChunkDelay', 'quota_name' => 'quotaName', 'quota_limit' => 'quotaLimit', 'quota_window_millis' => 'quotaWindowMillis', 'quota_error_status' => 'quotaErrorStatus', 'degradation_ramp_millis' => 'degradationRampMillis', 'http_class_callback' => 'httpClassCallback', 'http_object_callback' => 'httpObjectCallback', 'failure_policy' => 'failurePolicy', 'http_responses' => 'httpResponses', 'response_mode' => 'responseMode', 'response_weights' => 'responseWeights', 'switch_after' => 'switchAfter', 'cross_protocol_scenarios' => 'crossProtocolScenarios', 'scenario_name' => 'scenarioName', 'scenario_state' => 'scenarioState', 'new_scenario_state' => 'newScenarioState', 'match_pattern' => 'matchPattern', 'target_state' => 'targetState' }.freeze
- REVERSE_FIELD_MAP =
FIELD_MAP.invert.freeze
- BODY_TYPES =
Known Body type strings used to distinguish Body objects from plain hashes during deserialization.
Set.new(%w[ STRING JSON REGEX XML BINARY JSON_SCHEMA JSON_PATH XPATH XML_SCHEMA JSON_RPC GRAPHQL FILE ]).freeze
- RequestDefinition =
Alias matching the Python client
HttpRequest- VERSION =
'7.2.0'- WEB_SOCKET_CORRELATION_ID_HEADER_NAME =
'WebSocketCorrelationId'- BREAKPOINT_ID_HEADER_NAME =
'X-MockServer-BreakpointId'- CLIENT_REGISTRATION_ID_HEADER =
'X-CLIENT-REGISTRATION-ID'- WEBSOCKET_PATH =
'/_mockserver_callback_websocket'- TYPE_HTTP_REQUEST =
'org.mockserver.model.HttpRequest'- TYPE_HTTP_RESPONSE =
'org.mockserver.model.HttpResponse'- TYPE_HTTP_REQUEST_AND_RESPONSE =
'org.mockserver.model.HttpRequestAndHttpResponse'- TYPE_CLIENT_ID_DTO =
'org.mockserver.serialization.model.WebSocketClientIdDTO'- TYPE_ERROR_DTO =
'org.mockserver.serialization.model.WebSocketErrorDTO'- TYPE_PAUSED_STREAM_FRAME_DTO =
'org.mockserver.serialization.model.PausedStreamFrameDTO'- TYPE_STREAM_FRAME_DECISION_DTO =
'org.mockserver.serialization.model.StreamFrameDecisionDTO'- MAX_RECONNECT_ATTEMPTS =
3- REGISTRATION_TIMEOUT =
10
Class Method Summary collapse
- .add_correlation_id_header(message, correlation_id) ⇒ Object
- .build_error_message(error_msg, correlation_id) ⇒ Object
- .build_ws_message(type_name, value_dict) ⇒ Object
- .clean_context_path(context_path) ⇒ Object private
-
.coerce_class_callback(value) ⇒ Object
private
Coerce a class-callback value into an HttpClassCallback.
- .deserialize_body(data) ⇒ Object private
- .deserialize_cookies(data) ⇒ Object private
- .deserialize_key_multi_values(data) ⇒ Object private
-
.encode_path_segment(value) ⇒ Object
private
Percent-encode a single URL path segment (e.g. a scenario name), encoding spaces as %20 (not ) so the segment is safe inside /mockserver/scenario/name+.
- .extract_breakpoint_id(request) ⇒ Object
- .extract_correlation_id(request) ⇒ Object
- .from_camel(camel_str) ⇒ Object private
- .serialize_body(body) ⇒ Object private
-
.serialize_cookies(items) ⇒ Object
private
Unlike headers / query parameters, MockServer represents cookies as a single-value => value object map, not a [values] array.
- .serialize_key_multi_values(items) ⇒ Object private
- .serialize_value(value) ⇒ Object private
- .strip_none(hash) ⇒ Object private
- .to_camel(snake_str) ⇒ Object private
Class Method Details
.add_correlation_id_header(message, correlation_id) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/mockserver/websocket_client.rb', line 49 def self.add_correlation_id_header(, correlation_id) .headers ||= [] .headers.each do |header| if header.name == WEB_SOCKET_CORRELATION_ID_HEADER_NAME header.values = [correlation_id] return end end .headers << KeyToMultiValue.new( name: WEB_SOCKET_CORRELATION_ID_HEADER_NAME, values: [correlation_id] ) end |
.build_error_message(error_msg, correlation_id) ⇒ Object
71 72 73 74 75 76 77 78 79 |
# File 'lib/mockserver/websocket_client.rb', line 71 def self.(error_msg, correlation_id) JSON.generate({ 'type' => TYPE_ERROR_DTO, 'value' => JSON.generate({ 'message' => error_msg, 'webSocketCorrelationId' => correlation_id }) }) end |
.build_ws_message(type_name, value_dict) ⇒ Object
64 65 66 67 68 69 |
# File 'lib/mockserver/websocket_client.rb', line 64 def self.(type_name, value_dict) JSON.generate({ 'type' => type_name, 'value' => JSON.generate(value_dict) }) end |
.clean_context_path(context_path) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
82 83 84 85 86 87 |
# File 'lib/mockserver/websocket_client.rb', line 82 def self.clean_context_path(context_path) return '' if context_path.nil? || context_path.empty? return "/#{context_path}" unless context_path.start_with?('/') context_path end |
.coerce_class_callback(value) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Coerce a class-callback value into an HttpClassCallback. Accepts:
* +nil+ -> +nil+
* a +String+ -> +HttpClassCallback.new(callback_class: <string>)+
* an HttpClassCallback -> returned unchanged
Any other type raises a TypeError. This lets the Expectation setters and the fluent builder accept either a fully-qualified class-name String or a pre-built HttpClassCallback (carrying delay / primary).
141 142 143 144 145 146 147 148 |
# File 'lib/mockserver/models.rb', line 141 def self.coerce_class_callback(value) return nil if value.nil? return HttpClassCallback.new(callback_class: value) if value.is_a?(String) return value if value.is_a?(HttpClassCallback) raise TypeError, "Expected a class-name String or HttpClassCallback, got #{value.class.name}" end |
.deserialize_body(data) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/mockserver/models.rb', line 182 def self.deserialize_body(data) return nil if data.nil? return data if data.is_a?(String) if data.is_a?(Hash) if data['type'] == 'JSON_RPC' return JsonRpcBody.from_hash(data) end if data['type'] == 'GRAPHQL' return GraphQLBody.from_hash(data) end return Body.from_hash(data) if BODY_TYPES.include?(data['type']) return data end data end |
.deserialize_cookies(data) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
238 239 240 241 242 243 244 245 246 247 |
# File 'lib/mockserver/models.rb', line 238 def self.(data) return nil if data.nil? if data.is_a?(Hash) return data.map { |k, v| KeyToMultiValue.new(name: k, values: v.is_a?(Array) ? v : [v]) } end # tolerate the legacy array form on read deserialize_key_multi_values(data) end |
.deserialize_key_multi_values(data) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/mockserver/models.rb', line 208 def self.deserialize_key_multi_values(data) return nil if data.nil? if data.is_a?(Hash) return data.map { |k, v| KeyToMultiValue.new(name: k, values: v.is_a?(Array) ? v : [v]) } end data.map do |item| if item.is_a?(Hash) KeyToMultiValue.from_hash(item) elsif item.is_a?(String) KeyToMultiValue.new(name: item, values: []) else KeyToMultiValue.from_hash(item) end end end |
.encode_path_segment(value) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Percent-encode a single URL path segment (e.g. a scenario name), encoding spaces as %20 (not ) so the segment is safe inside /mockserver/scenario/name+.
153 154 155 |
# File 'lib/mockserver/models.rb', line 153 def self.encode_path_segment(value) ERB::Util.url_encode(value.to_s) end |
.extract_breakpoint_id(request) ⇒ Object
38 39 40 41 42 43 44 45 46 47 |
# File 'lib/mockserver/websocket_client.rb', line 38 def self.extract_breakpoint_id(request) return nil if request.headers.nil? request.headers.each do |header| if header.name == BREAKPOINT_ID_HEADER_NAME return header.values.first if header.values && !header.values.empty? end end nil end |
.extract_correlation_id(request) ⇒ Object
27 28 29 30 31 32 33 34 35 36 |
# File 'lib/mockserver/websocket_client.rb', line 27 def self.extract_correlation_id(request) return nil if request.headers.nil? request.headers.each do |header| if header.name == WEB_SOCKET_CORRELATION_ID_HEADER_NAME return header.values.first if header.values && !header.values.empty? end end nil end |
.from_camel(camel_str) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
122 123 124 125 126 |
# File 'lib/mockserver/models.rb', line 122 def self.from_camel(camel_str) return REVERSE_FIELD_MAP[camel_str] if REVERSE_FIELD_MAP.key?(camel_str) camel_str.gsub(/([A-Z])/) { "_#{$1.downcase}" } end |
.serialize_body(body) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
170 171 172 173 174 175 176 177 178 179 |
# File 'lib/mockserver/models.rb', line 170 def self.serialize_body(body) return nil if body.nil? return body if body.is_a?(String) return body if body.is_a?(Hash) return body.to_h if body.is_a?(Body) return body.to_h if body.is_a?(JsonRpcBody) return body.to_h if body.is_a?(GraphQLBody) body end |
.serialize_cookies(items) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Unlike headers / query parameters, MockServer represents cookies as a single-value => value object map, not a [values] array.
229 230 231 232 233 234 235 |
# File 'lib/mockserver/models.rb', line 229 def self.(items) return nil if items.nil? items.each_with_object({}) do |item, map| map[item.name] = item.values.is_a?(Array) ? item.values.first : item.values end end |
.serialize_key_multi_values(items) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
201 202 203 204 205 |
# File 'lib/mockserver/models.rb', line 201 def self.serialize_key_multi_values(items) return nil if items.nil? items.map(&:to_h) end |
.serialize_value(value) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
158 159 160 161 162 163 164 165 166 167 |
# File 'lib/mockserver/models.rb', line 158 def self.serialize_value(value) case value when ->(v) { v.respond_to?(:to_h) && v.class.ancestors.any? { |a| a.to_s.start_with?('MockServer::') } } value.to_h when Array value.map { |item| serialize_value(item) } else value end end |
.strip_none(hash) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
129 130 131 |
# File 'lib/mockserver/models.rb', line 129 def self.strip_none(hash) hash.reject { |_k, v| v.nil? } end |
.to_camel(snake_str) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
114 115 116 117 118 119 |
# File 'lib/mockserver/models.rb', line 114 def self.to_camel(snake_str) return FIELD_MAP[snake_str] if FIELD_MAP.key?(snake_str) parts = snake_str.split('_') parts[0] + parts[1..].map(&:capitalize).join end |