spikard 0.8.1 → 0.8.2
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 +4 -4
- data/ext/spikard_rb/Cargo.toml +1 -1
- data/lib/spikard/grpc.rb +5 -5
- data/lib/spikard/version.rb +1 -1
- data/vendor/crates/spikard-bindings-shared/Cargo.toml +1 -1
- data/vendor/crates/spikard-bindings-shared/src/grpc_metadata.rs +3 -3
- data/vendor/crates/spikard-core/Cargo.toml +1 -1
- data/vendor/crates/spikard-core/src/metadata.rs +3 -14
- data/vendor/crates/spikard-http/Cargo.toml +1 -1
- data/vendor/crates/spikard-http/src/grpc/mod.rs +1 -1
- data/vendor/crates/spikard-http/src/grpc/service.rs +11 -11
- data/vendor/crates/spikard-http/src/grpc/streaming.rs +5 -1
- data/vendor/crates/spikard-http/src/server/grpc_routing.rs +59 -20
- data/vendor/crates/spikard-http/src/server/routing_factory.rs +179 -201
- data/vendor/crates/spikard-http/tests/common/grpc_helpers.rs +49 -60
- data/vendor/crates/spikard-http/tests/common/handlers.rs +5 -5
- data/vendor/crates/spikard-http/tests/common/mod.rs +7 -8
- data/vendor/crates/spikard-http/tests/common/test_builders.rs +14 -19
- data/vendor/crates/spikard-http/tests/grpc_error_handling_test.rs +68 -69
- data/vendor/crates/spikard-http/tests/grpc_integration_test.rs +1 -3
- data/vendor/crates/spikard-http/tests/grpc_metadata_test.rs +98 -84
- data/vendor/crates/spikard-http/tests/grpc_server_integration.rs +76 -57
- data/vendor/crates/spikard-rb/Cargo.toml +1 -1
- data/vendor/crates/spikard-rb/src/grpc/handler.rs +30 -25
- data/vendor/crates/spikard-rb/src/lib.rs +1 -2
- data/vendor/crates/spikard-rb-macros/Cargo.toml +1 -1
- metadata +1 -1
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
//! ```
|
|
41
41
|
|
|
42
42
|
use bytes::Bytes;
|
|
43
|
-
use serde_json::{
|
|
43
|
+
use serde_json::{Value, json};
|
|
44
44
|
use spikard_http::grpc::{GrpcHandler, GrpcHandlerResult, GrpcRequestData, GrpcResponseData};
|
|
45
45
|
use std::future::Future;
|
|
46
46
|
use std::pin::Pin;
|
|
47
47
|
use std::sync::Arc;
|
|
48
|
-
use tonic::metadata::MetadataMap;
|
|
49
48
|
use tonic::Code;
|
|
49
|
+
use tonic::metadata::MetadataMap;
|
|
50
50
|
|
|
51
51
|
/// Test server for gRPC integration testing
|
|
52
52
|
///
|
|
@@ -82,7 +82,7 @@ impl GrpcTestServer {
|
|
|
82
82
|
///
|
|
83
83
|
/// # Arguments
|
|
84
84
|
///
|
|
85
|
-
/// * `url` - The base URL for the server (e.g.,
|
|
85
|
+
/// * `url` - The base URL for the server (e.g., <http://localhost:8080>)
|
|
86
86
|
pub fn with_url(url: impl Into<String>) -> Self {
|
|
87
87
|
Self {
|
|
88
88
|
handlers: Arc::new(std::sync::Mutex::new(Vec::new())),
|
|
@@ -102,7 +102,7 @@ impl GrpcTestServer {
|
|
|
102
102
|
/// let mut server = GrpcTestServer::new();
|
|
103
103
|
/// server.register_service(Arc::new(MyHandler));
|
|
104
104
|
/// ```
|
|
105
|
-
pub fn register_service(&
|
|
105
|
+
pub fn register_service(&self, handler: Arc<dyn GrpcHandler>) {
|
|
106
106
|
let mut handlers = self.handlers.lock().unwrap();
|
|
107
107
|
handlers.push(handler);
|
|
108
108
|
}
|
|
@@ -127,10 +127,7 @@ impl GrpcTestServer {
|
|
|
127
127
|
/// * `service_name` - The fully qualified service name to look up
|
|
128
128
|
pub fn get_handler(&self, service_name: &str) -> Option<Arc<dyn GrpcHandler>> {
|
|
129
129
|
let handlers = self.handlers.lock().unwrap();
|
|
130
|
-
handlers
|
|
131
|
-
.iter()
|
|
132
|
-
.find(|h| h.service_name() == service_name)
|
|
133
|
-
.cloned()
|
|
130
|
+
handlers.iter().find(|h| h.service_name() == service_name).cloned()
|
|
134
131
|
}
|
|
135
132
|
|
|
136
133
|
/// Check if a service is registered
|
|
@@ -170,7 +167,7 @@ pub struct GrpcTestClient;
|
|
|
170
167
|
|
|
171
168
|
impl GrpcTestClient {
|
|
172
169
|
/// Create a new test client
|
|
173
|
-
pub fn new() -> Self {
|
|
170
|
+
pub const fn new() -> Self {
|
|
174
171
|
Self
|
|
175
172
|
}
|
|
176
173
|
}
|
|
@@ -182,7 +179,7 @@ impl Default for GrpcTestClient {
|
|
|
182
179
|
}
|
|
183
180
|
|
|
184
181
|
/// Factory function to create a gRPC test client
|
|
185
|
-
pub fn create_grpc_test_client() -> GrpcTestClient {
|
|
182
|
+
pub const fn create_grpc_test_client() -> GrpcTestClient {
|
|
186
183
|
GrpcTestClient::new()
|
|
187
184
|
}
|
|
188
185
|
|
|
@@ -196,7 +193,7 @@ pub fn create_grpc_test_client() -> GrpcTestClient {
|
|
|
196
193
|
///
|
|
197
194
|
/// * `server` - The test server instance
|
|
198
195
|
/// * `service` - Fully qualified service name (e.g., "mypackage.UserService")
|
|
199
|
-
/// * `method` - Method name (e.g.,
|
|
196
|
+
/// * `method` - Method name (e.g., `GetUser`)
|
|
200
197
|
/// * `payload` - Serialized protobuf message bytes
|
|
201
198
|
/// * `metadata` - gRPC metadata (headers) to include in the request
|
|
202
199
|
///
|
|
@@ -227,12 +224,12 @@ pub async fn send_unary_request(
|
|
|
227
224
|
payload: Bytes,
|
|
228
225
|
metadata: MetadataMap,
|
|
229
226
|
) -> Result<GrpcResponseData, Box<dyn std::error::Error>> {
|
|
230
|
-
let handler = server
|
|
231
|
-
|
|
232
|
-
.ok_or_else(|| Box::new(std::io::Error::new(
|
|
227
|
+
let handler = server.get_handler(service).ok_or_else(|| {
|
|
228
|
+
Box::new(std::io::Error::new(
|
|
233
229
|
std::io::ErrorKind::NotFound,
|
|
234
|
-
format!("Service not found: {}"
|
|
235
|
-
)) as Box<dyn std::error::Error>
|
|
230
|
+
format!("Service not found: {service}"),
|
|
231
|
+
)) as Box<dyn std::error::Error>
|
|
232
|
+
})?;
|
|
236
233
|
|
|
237
234
|
let request = GrpcRequestData {
|
|
238
235
|
service_name: service.to_string(),
|
|
@@ -264,14 +261,12 @@ pub async fn send_unary_request(
|
|
|
264
261
|
/// let response = send_unary_request(...).await?;
|
|
265
262
|
/// assert_grpc_response(response, json!({"id": 1, "name": "Alice"}));
|
|
266
263
|
/// ```
|
|
267
|
-
pub fn assert_grpc_response(response: GrpcResponseData, expected: Value) {
|
|
268
|
-
let actual = serde_json::from_slice::<Value>(&response.payload)
|
|
269
|
-
.expect("Failed to parse response payload as JSON");
|
|
264
|
+
pub fn assert_grpc_response(response: &GrpcResponseData, expected: &Value) {
|
|
265
|
+
let actual = serde_json::from_slice::<Value>(&response.payload).expect("Failed to parse response payload as JSON");
|
|
270
266
|
|
|
271
267
|
assert_eq!(
|
|
272
|
-
actual, expected,
|
|
273
|
-
"Response payload mismatch.\nExpected: {}\nActual: {}",
|
|
274
|
-
expected, actual
|
|
268
|
+
actual, *expected,
|
|
269
|
+
"Response payload mismatch.\nExpected: {expected}\nActual: {actual}",
|
|
275
270
|
);
|
|
276
271
|
}
|
|
277
272
|
|
|
@@ -286,7 +281,7 @@ pub fn assert_grpc_response(response: GrpcResponseData, expected: Value) {
|
|
|
286
281
|
/// # Arguments
|
|
287
282
|
///
|
|
288
283
|
/// * `result` - The gRPC handler result
|
|
289
|
-
/// * `expected_status` - The expected tonic::Code
|
|
284
|
+
/// * `expected_status` - The expected `tonic::Code`
|
|
290
285
|
///
|
|
291
286
|
/// # Example
|
|
292
287
|
///
|
|
@@ -307,10 +302,7 @@ pub fn assert_grpc_status(result: &GrpcHandlerResult, expected_status: Code) {
|
|
|
307
302
|
);
|
|
308
303
|
}
|
|
309
304
|
Ok(_) => {
|
|
310
|
-
panic!(
|
|
311
|
-
"Expected error status {:?} but got success response",
|
|
312
|
-
expected_status
|
|
313
|
-
);
|
|
305
|
+
panic!("Expected error status {expected_status:?} but got success response");
|
|
314
306
|
}
|
|
315
307
|
}
|
|
316
308
|
}
|
|
@@ -363,15 +355,13 @@ impl ProtobufMessageBuilder {
|
|
|
363
355
|
|
|
364
356
|
/// Add a string field to the message
|
|
365
357
|
pub fn add_string_field(&mut self, name: &str, value: &str) -> &mut Self {
|
|
366
|
-
self.fields
|
|
367
|
-
.insert(name.to_string(), Value::String(value.to_string()));
|
|
358
|
+
self.fields.insert(name.to_string(), Value::String(value.to_string()));
|
|
368
359
|
self
|
|
369
360
|
}
|
|
370
361
|
|
|
371
362
|
/// Add an integer field to the message
|
|
372
363
|
pub fn add_int_field(&mut self, name: &str, value: i64) -> &mut Self {
|
|
373
|
-
self.fields
|
|
374
|
-
.insert(name.to_string(), json!(value));
|
|
364
|
+
self.fields.insert(name.to_string(), json!(value));
|
|
375
365
|
self
|
|
376
366
|
}
|
|
377
367
|
|
|
@@ -446,11 +436,11 @@ pub fn create_test_metadata() -> MetadataMap {
|
|
|
446
436
|
|
|
447
437
|
/// Create test metadata with custom headers
|
|
448
438
|
///
|
|
449
|
-
/// Allows building metadata from a HashMap of key-value pairs.
|
|
439
|
+
/// Allows building metadata from a `HashMap` of key-value pairs.
|
|
450
440
|
///
|
|
451
441
|
/// # Arguments
|
|
452
442
|
///
|
|
453
|
-
/// * `headers` - HashMap of header names to values (String-based)
|
|
443
|
+
/// * `headers` - `HashMap` of header names to values (String-based)
|
|
454
444
|
///
|
|
455
445
|
/// # Example
|
|
456
446
|
///
|
|
@@ -475,7 +465,7 @@ pub fn create_test_metadata_with_headers(
|
|
|
475
465
|
Ok(metadata)
|
|
476
466
|
}
|
|
477
467
|
|
|
478
|
-
/// Add authentication metadata to an existing MetadataMap
|
|
468
|
+
/// Add authentication metadata to an existing `MetadataMap`
|
|
479
469
|
///
|
|
480
470
|
/// Adds a standard Bearer token authorization header.
|
|
481
471
|
///
|
|
@@ -491,12 +481,12 @@ pub fn create_test_metadata_with_headers(
|
|
|
491
481
|
/// add_auth_metadata(&mut metadata, "secret_token_123");
|
|
492
482
|
/// ```
|
|
493
483
|
pub fn add_auth_metadata(metadata: &mut MetadataMap, token: &str) -> Result<(), Box<dyn std::error::Error>> {
|
|
494
|
-
let auth_value = format!("Bearer {}"
|
|
484
|
+
let auth_value = format!("Bearer {token}");
|
|
495
485
|
metadata.insert("authorization", auth_value.parse()?);
|
|
496
486
|
Ok(())
|
|
497
487
|
}
|
|
498
488
|
|
|
499
|
-
/// Add custom metadata header to an existing MetadataMap
|
|
489
|
+
/// Add custom metadata header to an existing `MetadataMap`
|
|
500
490
|
///
|
|
501
491
|
/// # Arguments
|
|
502
492
|
///
|
|
@@ -545,8 +535,8 @@ impl MockGrpcHandler {
|
|
|
545
535
|
}
|
|
546
536
|
|
|
547
537
|
/// Create a mock handler that returns JSON
|
|
548
|
-
pub fn with_json(service_name: &'static str, json: Value) -> Self {
|
|
549
|
-
let bytes = serde_json::to_vec(
|
|
538
|
+
pub fn with_json(service_name: &'static str, json: &Value) -> Self {
|
|
539
|
+
let bytes = serde_json::to_vec(json).unwrap_or_default();
|
|
550
540
|
Self::new(service_name, Bytes::from(bytes))
|
|
551
541
|
}
|
|
552
542
|
}
|
|
@@ -643,6 +633,22 @@ impl GrpcHandler for EchoMockHandler {
|
|
|
643
633
|
mod tests {
|
|
644
634
|
use super::*;
|
|
645
635
|
|
|
636
|
+
// Mock handler for testing gRPC functionality
|
|
637
|
+
struct TestHandler;
|
|
638
|
+
impl GrpcHandler for TestHandler {
|
|
639
|
+
fn call(&self, _request: GrpcRequestData) -> Pin<Box<dyn Future<Output = GrpcHandlerResult> + Send>> {
|
|
640
|
+
Box::pin(async {
|
|
641
|
+
Ok(GrpcResponseData {
|
|
642
|
+
payload: serde_json::to_vec(&json!({"result": "success"})).unwrap().into(),
|
|
643
|
+
metadata: MetadataMap::new(),
|
|
644
|
+
})
|
|
645
|
+
})
|
|
646
|
+
}
|
|
647
|
+
fn service_name(&self) -> &'static str {
|
|
648
|
+
"test.TestService"
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
646
652
|
#[test]
|
|
647
653
|
fn test_grpc_test_server_new() {
|
|
648
654
|
let server = GrpcTestServer::new();
|
|
@@ -659,7 +665,7 @@ mod tests {
|
|
|
659
665
|
|
|
660
666
|
#[test]
|
|
661
667
|
fn test_grpc_test_server_register_service() {
|
|
662
|
-
let
|
|
668
|
+
let server = GrpcTestServer::new();
|
|
663
669
|
let handler = Arc::new(MockGrpcHandler::new("test.Service", "response"));
|
|
664
670
|
|
|
665
671
|
server.register_service(handler);
|
|
@@ -670,7 +676,7 @@ mod tests {
|
|
|
670
676
|
|
|
671
677
|
#[test]
|
|
672
678
|
fn test_grpc_test_server_register_multiple_services() {
|
|
673
|
-
let
|
|
679
|
+
let server = GrpcTestServer::new();
|
|
674
680
|
let handler1 = Arc::new(MockGrpcHandler::new("service1", "response1"));
|
|
675
681
|
let handler2 = Arc::new(MockGrpcHandler::new("service2", "response2"));
|
|
676
682
|
|
|
@@ -706,7 +712,7 @@ mod tests {
|
|
|
706
712
|
#[tokio::test]
|
|
707
713
|
async fn test_mock_grpc_handler_with_json() {
|
|
708
714
|
let json_response = json!({"id": 1, "name": "Alice"});
|
|
709
|
-
let handler = MockGrpcHandler::with_json("test.UserService", json_response
|
|
715
|
+
let handler = MockGrpcHandler::with_json("test.UserService", &json_response);
|
|
710
716
|
|
|
711
717
|
let request = GrpcRequestData {
|
|
712
718
|
service_name: "test.UserService".to_string(),
|
|
@@ -908,7 +914,7 @@ mod tests {
|
|
|
908
914
|
};
|
|
909
915
|
|
|
910
916
|
let expected = json!({"id": 1, "name": "Alice"});
|
|
911
|
-
assert_grpc_response(response, expected);
|
|
917
|
+
assert_grpc_response(&response, &expected);
|
|
912
918
|
}
|
|
913
919
|
|
|
914
920
|
#[test]
|
|
@@ -945,25 +951,9 @@ mod tests {
|
|
|
945
951
|
|
|
946
952
|
#[tokio::test]
|
|
947
953
|
async fn test_send_unary_request_with_mock_handler() {
|
|
948
|
-
let
|
|
954
|
+
let server = GrpcTestServer::new();
|
|
949
955
|
let _response_payload = json!({"result": "success"});
|
|
950
956
|
|
|
951
|
-
// Create a custom handler that reports the correct service name
|
|
952
|
-
struct TestHandler;
|
|
953
|
-
impl GrpcHandler for TestHandler {
|
|
954
|
-
fn call(&self, _request: GrpcRequestData) -> Pin<Box<dyn Future<Output = GrpcHandlerResult> + Send>> {
|
|
955
|
-
Box::pin(async {
|
|
956
|
-
Ok(GrpcResponseData {
|
|
957
|
-
payload: serde_json::to_vec(&json!({"result": "success"})).unwrap().into(),
|
|
958
|
-
metadata: MetadataMap::new(),
|
|
959
|
-
})
|
|
960
|
-
})
|
|
961
|
-
}
|
|
962
|
-
fn service_name(&self) -> &'static str {
|
|
963
|
-
"test.TestService"
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
|
|
967
957
|
server.register_service(Arc::new(TestHandler));
|
|
968
958
|
|
|
969
959
|
let message = ProtobufMessageBuilder::new()
|
|
@@ -1016,7 +1006,6 @@ mod tests {
|
|
|
1016
1006
|
#[test]
|
|
1017
1007
|
fn test_grpc_test_client_creation() {
|
|
1018
1008
|
let _client = create_grpc_test_client();
|
|
1019
|
-
let _default_client = GrpcTestClient::default();
|
|
1020
1009
|
let _new_client = GrpcTestClient::new();
|
|
1021
1010
|
// Just ensure it can be created without panicking
|
|
1022
1011
|
}
|
|
@@ -141,12 +141,12 @@ pub struct JsonHandler {
|
|
|
141
141
|
|
|
142
142
|
impl JsonHandler {
|
|
143
143
|
/// Create a new JSON handler with given status code and body
|
|
144
|
-
pub fn new(status_code: StatusCode, body: serde_json::Value) -> Self {
|
|
144
|
+
pub const fn new(status_code: StatusCode, body: serde_json::Value) -> Self {
|
|
145
145
|
Self { status_code, body }
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
/// Create a JSON handler with 200 OK status
|
|
149
|
-
pub fn ok(body: serde_json::Value) -> Self {
|
|
149
|
+
pub const fn ok(body: serde_json::Value) -> Self {
|
|
150
150
|
Self {
|
|
151
151
|
status_code: StatusCode::OK,
|
|
152
152
|
body,
|
|
@@ -154,7 +154,7 @@ impl JsonHandler {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
/// Create a JSON handler with 201 Created status
|
|
157
|
-
pub fn created(body: serde_json::Value) -> Self {
|
|
157
|
+
pub const fn created(body: serde_json::Value) -> Self {
|
|
158
158
|
Self {
|
|
159
159
|
status_code: StatusCode::CREATED,
|
|
160
160
|
body,
|
|
@@ -162,7 +162,7 @@ impl JsonHandler {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/// Create a JSON handler with 400 Bad Request status
|
|
165
|
-
pub fn bad_request(body: serde_json::Value) -> Self {
|
|
165
|
+
pub const fn bad_request(body: serde_json::Value) -> Self {
|
|
166
166
|
Self {
|
|
167
167
|
status_code: StatusCode::BAD_REQUEST,
|
|
168
168
|
body,
|
|
@@ -170,7 +170,7 @@ impl JsonHandler {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
/// Create a JSON handler with 500 Internal Server Error status
|
|
173
|
-
pub fn server_error(body: serde_json::Value) -> Self {
|
|
173
|
+
pub const fn server_error(body: serde_json::Value) -> Self {
|
|
174
174
|
Self {
|
|
175
175
|
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
|
176
176
|
body,
|
|
@@ -17,18 +17,17 @@
|
|
|
17
17
|
|
|
18
18
|
#![allow(dead_code)]
|
|
19
19
|
|
|
20
|
+
pub mod grpc_helpers;
|
|
20
21
|
pub mod handlers;
|
|
21
22
|
pub mod test_builders;
|
|
22
|
-
pub mod grpc_helpers;
|
|
23
23
|
|
|
24
|
+
#[allow(unused_imports)]
|
|
25
|
+
pub use grpc_helpers::{
|
|
26
|
+
EchoMockHandler, ErrorMockHandler, GrpcTestClient, GrpcTestServer, MockGrpcHandler, ProtobufMessageBuilder,
|
|
27
|
+
add_auth_metadata, add_metadata_header, assert_grpc_response, assert_grpc_status, create_grpc_test_client,
|
|
28
|
+
create_test_metadata, create_test_metadata_with_headers, send_unary_request,
|
|
29
|
+
};
|
|
24
30
|
#[allow(unused_imports)]
|
|
25
31
|
pub use handlers::{EchoHandler, ErrorHandler, JsonHandler, PanicHandler, SuccessHandler};
|
|
26
32
|
#[allow(unused_imports)]
|
|
27
33
|
pub use test_builders::{HandlerBuilder, RequestBuilder, assert_status, load_fixture, parse_json_body};
|
|
28
|
-
#[allow(unused_imports)]
|
|
29
|
-
pub use grpc_helpers::{
|
|
30
|
-
GrpcTestServer, GrpcTestClient, create_grpc_test_client, send_unary_request,
|
|
31
|
-
assert_grpc_response, assert_grpc_status, ProtobufMessageBuilder, create_test_metadata,
|
|
32
|
-
add_auth_metadata, add_metadata_header, create_test_metadata_with_headers,
|
|
33
|
-
MockGrpcHandler, ErrorMockHandler, EchoMockHandler,
|
|
34
|
-
};
|
|
@@ -87,7 +87,7 @@ impl HandlerBuilder {
|
|
|
87
87
|
/// Add a delay to the handler response for testing timeouts
|
|
88
88
|
///
|
|
89
89
|
/// Useful for simulating slow handlers and testing timeout middleware.
|
|
90
|
-
pub fn delay(mut self, duration: Duration) -> Self {
|
|
90
|
+
pub const fn delay(mut self, duration: Duration) -> Self {
|
|
91
91
|
self.delay = Some(duration);
|
|
92
92
|
self
|
|
93
93
|
}
|
|
@@ -95,7 +95,7 @@ impl HandlerBuilder {
|
|
|
95
95
|
/// Configure the handler to panic when called
|
|
96
96
|
///
|
|
97
97
|
/// Useful for testing panic recovery and error handling middleware.
|
|
98
|
-
pub fn panics(mut self) -> Self {
|
|
98
|
+
pub const fn panics(mut self) -> Self {
|
|
99
99
|
self.should_panic = true;
|
|
100
100
|
self
|
|
101
101
|
}
|
|
@@ -119,7 +119,7 @@ impl Default for HandlerBuilder {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
/// Internal handler implementation constructed by HandlerBuilder
|
|
122
|
+
/// Internal handler implementation constructed by `HandlerBuilder`
|
|
123
123
|
struct ConfiguredHandler {
|
|
124
124
|
status: StatusCode,
|
|
125
125
|
body: Value,
|
|
@@ -139,9 +139,7 @@ impl Handler for ConfiguredHandler {
|
|
|
139
139
|
let should_panic = self.should_panic;
|
|
140
140
|
|
|
141
141
|
Box::pin(async move {
|
|
142
|
-
|
|
143
|
-
panic!("Handler configured to panic");
|
|
144
|
-
}
|
|
142
|
+
assert!(!should_panic, "Handler configured to panic");
|
|
145
143
|
|
|
146
144
|
if let Some(duration) = delay {
|
|
147
145
|
sleep(duration).await;
|
|
@@ -160,7 +158,7 @@ impl Handler for ConfiguredHandler {
|
|
|
160
158
|
|
|
161
159
|
/// Fluent builder for constructing test HTTP requests
|
|
162
160
|
///
|
|
163
|
-
/// Provides a fluent API for building both hyper Request objects and RequestData
|
|
161
|
+
/// Provides a fluent API for building both hyper `Request` objects and `RequestData`
|
|
164
162
|
/// structures needed for handler testing. Handles typical test scenarios without
|
|
165
163
|
/// requiring manual construction of all components.
|
|
166
164
|
///
|
|
@@ -215,7 +213,7 @@ impl RequestBuilder {
|
|
|
215
213
|
self
|
|
216
214
|
}
|
|
217
215
|
|
|
218
|
-
/// Add or replace headers from a HashMap
|
|
216
|
+
/// Add or replace headers from a `HashMap`
|
|
219
217
|
///
|
|
220
218
|
/// Values are stored as-is; no normalization is performed.
|
|
221
219
|
pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
|
|
@@ -229,7 +227,7 @@ impl RequestBuilder {
|
|
|
229
227
|
self
|
|
230
228
|
}
|
|
231
229
|
|
|
232
|
-
/// Add or replace cookies from a HashMap
|
|
230
|
+
/// Add or replace cookies from a `HashMap`
|
|
233
231
|
pub fn cookies(mut self, cookies: HashMap<String, String>) -> Self {
|
|
234
232
|
self.cookies = cookies;
|
|
235
233
|
self
|
|
@@ -249,9 +247,9 @@ impl RequestBuilder {
|
|
|
249
247
|
self
|
|
250
248
|
}
|
|
251
249
|
|
|
252
|
-
/// Set query parameters as a HashMap of name to values
|
|
250
|
+
/// Set query parameters as a `HashMap` of name to values
|
|
253
251
|
///
|
|
254
|
-
/// Values are stored as Vec<String
|
|
252
|
+
/// Values are stored as `Vec<String>` to support multi-valued parameters.
|
|
255
253
|
pub fn query_params(mut self, params: HashMap<String, Vec<String>>) -> Self {
|
|
256
254
|
self.query_params = params;
|
|
257
255
|
self
|
|
@@ -259,16 +257,13 @@ impl RequestBuilder {
|
|
|
259
257
|
|
|
260
258
|
/// Add a single query parameter
|
|
261
259
|
pub fn query_param(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
|
|
262
|
-
self.query_params
|
|
263
|
-
.entry(name.into())
|
|
264
|
-
.or_insert_with(Vec::new)
|
|
265
|
-
.push(value.into());
|
|
260
|
+
self.query_params.entry(name.into()).or_default().push(value.into());
|
|
266
261
|
self
|
|
267
262
|
}
|
|
268
263
|
|
|
269
|
-
/// Build the request into (Request<Body>, RequestData) tuple
|
|
264
|
+
/// Build the request into `(Request<Body>, RequestData)` tuple
|
|
270
265
|
///
|
|
271
|
-
/// The Request can be passed directly to handler.call()
|
|
266
|
+
/// The `Request` can be passed directly to `handler.call()`. `RequestData` contains
|
|
272
267
|
/// all extracted request information (params, body, headers, etc.).
|
|
273
268
|
pub fn build(self) -> (Request<Body>, RequestData) {
|
|
274
269
|
let body = if self.body.is_null() {
|
|
@@ -327,11 +322,11 @@ fn build_query_json(raw_params: &HashMap<String, Vec<String>>) -> Value {
|
|
|
327
322
|
Value::Object(map)
|
|
328
323
|
}
|
|
329
324
|
|
|
330
|
-
/// Load a JSON fixture from the testing_data directory
|
|
325
|
+
/// Load a JSON fixture from the `testing_data` directory
|
|
331
326
|
///
|
|
332
327
|
/// # Arguments
|
|
333
328
|
///
|
|
334
|
-
/// * `relative_path` - Path relative to project root, e.g., "testing_data/headers/01_user_agent_default.json"
|
|
329
|
+
/// * `relative_path` - Path relative to project root, e.g., `"testing_data/headers/01_user_agent_default.json"`
|
|
335
330
|
///
|
|
336
331
|
/// # Example
|
|
337
332
|
///
|