spikard 0.8.2 → 0.10.1
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/README.md +19 -10
- data/ext/spikard_rb/Cargo.lock +234 -162
- data/ext/spikard_rb/Cargo.toml +3 -3
- data/ext/spikard_rb/extconf.rb +4 -3
- data/lib/spikard/config.rb +88 -12
- data/lib/spikard/testing.rb +3 -1
- data/lib/spikard/version.rb +1 -1
- data/lib/spikard.rb +11 -0
- data/vendor/crates/spikard-bindings-shared/Cargo.toml +11 -6
- data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +8 -8
- data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +63 -25
- data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +20 -4
- data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +10 -4
- data/vendor/crates/spikard-bindings-shared/src/error_response.rs +25 -22
- data/vendor/crates/spikard-bindings-shared/src/grpc_metadata.rs +14 -12
- data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +24 -10
- data/vendor/crates/spikard-bindings-shared/src/json_conversion.rs +829 -0
- data/vendor/crates/spikard-bindings-shared/src/lazy_cache.rs +587 -0
- data/vendor/crates/spikard-bindings-shared/src/lib.rs +7 -0
- data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +17 -11
- data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +51 -73
- data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +442 -4
- data/vendor/crates/spikard-bindings-shared/src/response_interpreter.rs +944 -0
- data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +22 -10
- data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +28 -10
- data/vendor/crates/spikard-bindings-shared/tests/config_extractor_behavior.rs +3 -2
- data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +13 -13
- data/vendor/crates/spikard-bindings-shared/tests/{comprehensive_coverage.rs → full_coverage.rs} +10 -5
- data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +14 -14
- data/vendor/crates/spikard-bindings-shared/tests/integration_tests.rs +669 -0
- data/vendor/crates/spikard-core/Cargo.toml +11 -3
- data/vendor/crates/spikard-core/src/bindings/response.rs +6 -9
- data/vendor/crates/spikard-core/src/debug.rs +2 -2
- data/vendor/crates/spikard-core/src/di/container.rs +2 -2
- data/vendor/crates/spikard-core/src/di/error.rs +1 -1
- data/vendor/crates/spikard-core/src/di/factory.rs +9 -5
- data/vendor/crates/spikard-core/src/di/graph.rs +1 -0
- data/vendor/crates/spikard-core/src/di/resolved.rs +25 -2
- data/vendor/crates/spikard-core/src/di/value.rs +2 -1
- data/vendor/crates/spikard-core/src/errors.rs +3 -0
- data/vendor/crates/spikard-core/src/http.rs +94 -18
- data/vendor/crates/spikard-core/src/lifecycle.rs +85 -61
- data/vendor/crates/spikard-core/src/parameters.rs +75 -54
- data/vendor/crates/spikard-core/src/problem.rs +19 -5
- data/vendor/crates/spikard-core/src/request_data.rs +16 -24
- data/vendor/crates/spikard-core/src/router.rs +26 -6
- data/vendor/crates/spikard-core/src/schema_registry.rs +25 -11
- data/vendor/crates/spikard-core/src/type_hints.rs +14 -7
- data/vendor/crates/spikard-core/src/validation/error_mapper.rs +30 -16
- data/vendor/crates/spikard-core/src/validation/mod.rs +46 -33
- data/vendor/crates/spikard-core/tests/di_dependency_defaults.rs +1 -1
- data/vendor/crates/spikard-core/tests/error_mapper.rs +2 -2
- data/vendor/crates/spikard-core/tests/parameters_edge_cases.rs +1 -1
- data/vendor/crates/spikard-core/tests/parameters_full.rs +1 -1
- data/vendor/crates/spikard-core/tests/parameters_schema_and_formats.rs +1 -1
- data/vendor/crates/spikard-core/tests/validation_coverage.rs +4 -4
- data/vendor/crates/spikard-http/Cargo.toml +11 -2
- data/vendor/crates/spikard-http/src/cors.rs +32 -11
- data/vendor/crates/spikard-http/src/di_handler.rs +12 -8
- data/vendor/crates/spikard-http/src/grpc/framing.rs +469 -0
- data/vendor/crates/spikard-http/src/grpc/handler.rs +887 -25
- data/vendor/crates/spikard-http/src/grpc/mod.rs +114 -22
- data/vendor/crates/spikard-http/src/grpc/service.rs +232 -2
- data/vendor/crates/spikard-http/src/grpc/streaming.rs +80 -2
- data/vendor/crates/spikard-http/src/handler_trait.rs +204 -27
- data/vendor/crates/spikard-http/src/handler_trait_tests.rs +15 -15
- data/vendor/crates/spikard-http/src/jsonrpc/http_handler.rs +2 -2
- data/vendor/crates/spikard-http/src/jsonrpc/router.rs +2 -2
- data/vendor/crates/spikard-http/src/lib.rs +1 -1
- data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +2 -2
- data/vendor/crates/spikard-http/src/lifecycle.rs +4 -4
- data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +2 -0
- data/vendor/crates/spikard-http/src/server/fast_router.rs +186 -0
- data/vendor/crates/spikard-http/src/server/grpc_routing.rs +324 -23
- data/vendor/crates/spikard-http/src/server/handler.rs +33 -22
- data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +21 -2
- data/vendor/crates/spikard-http/src/server/mod.rs +125 -20
- data/vendor/crates/spikard-http/src/server/request_extraction.rs +126 -44
- data/vendor/crates/spikard-http/src/server/routing_factory.rs +80 -69
- data/vendor/crates/spikard-http/tests/common/handlers.rs +2 -2
- data/vendor/crates/spikard-http/tests/common/test_builders.rs +12 -12
- data/vendor/crates/spikard-http/tests/di_handler_error_responses.rs +2 -2
- data/vendor/crates/spikard-http/tests/di_integration.rs +6 -6
- data/vendor/crates/spikard-http/tests/grpc_bidirectional_streaming.rs +430 -0
- data/vendor/crates/spikard-http/tests/grpc_client_streaming.rs +738 -0
- data/vendor/crates/spikard-http/tests/grpc_integration_test.rs +13 -9
- data/vendor/crates/spikard-http/tests/grpc_server_streaming.rs +974 -0
- data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +2 -2
- data/vendor/crates/spikard-http/tests/request_extraction_full.rs +4 -4
- data/vendor/crates/spikard-http/tests/server_config_builder.rs +2 -2
- data/vendor/crates/spikard-http/tests/server_cors_preflight.rs +1 -0
- data/vendor/crates/spikard-http/tests/server_openapi_jsonrpc_static.rs +140 -0
- data/vendor/crates/spikard-rb/Cargo.toml +11 -1
- data/vendor/crates/spikard-rb/build.rs +1 -0
- data/vendor/crates/spikard-rb/src/conversion.rs +138 -4
- data/vendor/crates/spikard-rb/src/grpc/handler.rs +706 -229
- data/vendor/crates/spikard-rb/src/grpc/mod.rs +6 -2
- data/vendor/crates/spikard-rb/src/gvl.rs +2 -2
- data/vendor/crates/spikard-rb/src/handler.rs +169 -91
- data/vendor/crates/spikard-rb/src/lib.rs +502 -62
- data/vendor/crates/spikard-rb/src/lifecycle.rs +31 -3
- data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +108 -43
- data/vendor/crates/spikard-rb/src/request.rs +117 -20
- data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +52 -25
- data/vendor/crates/spikard-rb/src/server.rs +23 -14
- data/vendor/crates/spikard-rb/src/testing/client.rs +5 -4
- data/vendor/crates/spikard-rb/src/testing/sse.rs +1 -36
- data/vendor/crates/spikard-rb/src/testing/websocket.rs +3 -38
- data/vendor/crates/spikard-rb/src/websocket.rs +32 -23
- data/vendor/crates/spikard-rb-macros/Cargo.toml +9 -1
- data/vendor/crates/spikard-rb-macros/src/lib.rs +4 -5
- metadata +14 -4
- data/vendor/bundle/ruby/3.4.0/gems/diff-lcs-1.6.2/mise.toml +0 -5
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.10.0/build/buildkitd.toml +0 -2
|
@@ -23,13 +23,15 @@ impl TestClientConfig {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/// Set the timeout in milliseconds
|
|
26
|
-
|
|
26
|
+
#[must_use]
|
|
27
|
+
pub const fn with_timeout(mut self, timeout_ms: u64) -> Self {
|
|
27
28
|
self.timeout_ms = timeout_ms;
|
|
28
29
|
self
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
/// Set whether to follow redirects
|
|
32
|
-
|
|
33
|
+
#[must_use]
|
|
34
|
+
pub const fn with_follow_redirects(mut self, follow_redirects: bool) -> Self {
|
|
33
35
|
self.follow_redirects = follow_redirects;
|
|
34
36
|
self
|
|
35
37
|
}
|
|
@@ -60,7 +62,13 @@ pub struct TestResponseMetadata {
|
|
|
60
62
|
|
|
61
63
|
impl TestResponseMetadata {
|
|
62
64
|
/// Create a new test response metadata
|
|
63
|
-
|
|
65
|
+
#[must_use]
|
|
66
|
+
pub const fn new(
|
|
67
|
+
status_code: u16,
|
|
68
|
+
headers: HashMap<String, String>,
|
|
69
|
+
body_size: usize,
|
|
70
|
+
response_time_ms: u64,
|
|
71
|
+
) -> Self {
|
|
64
72
|
Self {
|
|
65
73
|
status_code,
|
|
66
74
|
headers,
|
|
@@ -70,6 +78,7 @@ impl TestResponseMetadata {
|
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
/// Get a header value by name (case-insensitive)
|
|
81
|
+
#[must_use]
|
|
73
82
|
pub fn get_header(&self, name: &str) -> Option<&String> {
|
|
74
83
|
let lower_name = name.to_lowercase();
|
|
75
84
|
self.headers
|
|
@@ -79,17 +88,20 @@ impl TestResponseMetadata {
|
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
/// Check if response was successful (2xx status code)
|
|
82
|
-
|
|
91
|
+
#[must_use]
|
|
92
|
+
pub const fn is_success(&self) -> bool {
|
|
83
93
|
self.status_code >= 200 && self.status_code < 300
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
/// Check if response was a client error (4xx status code)
|
|
87
|
-
|
|
97
|
+
#[must_use]
|
|
98
|
+
pub const fn is_client_error(&self) -> bool {
|
|
88
99
|
self.status_code >= 400 && self.status_code < 500
|
|
89
100
|
}
|
|
90
101
|
|
|
91
102
|
/// Check if response was a server error (5xx status code)
|
|
92
|
-
|
|
103
|
+
#[must_use]
|
|
104
|
+
pub const fn is_server_error(&self) -> bool {
|
|
93
105
|
self.status_code >= 500 && self.status_code < 600
|
|
94
106
|
}
|
|
95
107
|
}
|
|
@@ -194,7 +206,7 @@ mod tests {
|
|
|
194
206
|
let metadata_204 = TestResponseMetadata::new(204, headers.clone(), 0, 50);
|
|
195
207
|
let metadata_299 = TestResponseMetadata::new(299, headers.clone(), 100, 50);
|
|
196
208
|
let metadata_300 = TestResponseMetadata::new(300, headers.clone(), 100, 50);
|
|
197
|
-
let metadata_400 = TestResponseMetadata::new(400, headers
|
|
209
|
+
let metadata_400 = TestResponseMetadata::new(400, headers, 100, 50);
|
|
198
210
|
|
|
199
211
|
assert!(metadata_200.is_success());
|
|
200
212
|
assert!(metadata_201.is_success());
|
|
@@ -211,7 +223,7 @@ mod tests {
|
|
|
211
223
|
let metadata_400 = TestResponseMetadata::new(400, headers.clone(), 100, 50);
|
|
212
224
|
let metadata_404 = TestResponseMetadata::new(404, headers.clone(), 100, 50);
|
|
213
225
|
let metadata_499 = TestResponseMetadata::new(499, headers.clone(), 100, 50);
|
|
214
|
-
let metadata_500 = TestResponseMetadata::new(500, headers
|
|
226
|
+
let metadata_500 = TestResponseMetadata::new(500, headers, 100, 50);
|
|
215
227
|
|
|
216
228
|
assert!(!metadata_399.is_client_error());
|
|
217
229
|
assert!(metadata_400.is_client_error());
|
|
@@ -227,7 +239,7 @@ mod tests {
|
|
|
227
239
|
let metadata_500 = TestResponseMetadata::new(500, headers.clone(), 100, 50);
|
|
228
240
|
let metadata_502 = TestResponseMetadata::new(502, headers.clone(), 100, 50);
|
|
229
241
|
let metadata_599 = TestResponseMetadata::new(599, headers.clone(), 100, 50);
|
|
230
|
-
let metadata_600 = TestResponseMetadata::new(600, headers
|
|
242
|
+
let metadata_600 = TestResponseMetadata::new(600, headers, 100, 50);
|
|
231
243
|
|
|
232
244
|
assert!(!metadata_499.is_server_error());
|
|
233
245
|
assert!(metadata_500.is_server_error());
|
|
@@ -240,7 +252,7 @@ mod tests {
|
|
|
240
252
|
fn test_response_metadata_debug() {
|
|
241
253
|
let headers = HashMap::new();
|
|
242
254
|
let metadata = TestResponseMetadata::new(200, headers, 100, 50);
|
|
243
|
-
let debug_str = format!("{:?}"
|
|
255
|
+
let debug_str = format!("{metadata:?}");
|
|
244
256
|
assert!(debug_str.contains("200"));
|
|
245
257
|
assert!(debug_str.contains("100"));
|
|
246
258
|
assert!(debug_str.contains("50"));
|
|
@@ -7,29 +7,37 @@ pub struct HeaderValidator;
|
|
|
7
7
|
|
|
8
8
|
impl HeaderValidator {
|
|
9
9
|
/// Validate that required headers are present
|
|
10
|
+
///
|
|
11
|
+
/// # Errors
|
|
12
|
+
///
|
|
13
|
+
/// Returns an error if required headers are missing.
|
|
10
14
|
pub fn validate_required(headers: &[(String, String)], required: &[&str]) -> Result<(), String> {
|
|
11
15
|
let header_names: std::collections::HashSet<_> = headers.iter().map(|(k, _)| k.to_lowercase()).collect();
|
|
12
16
|
|
|
13
17
|
for req in required {
|
|
14
18
|
if !header_names.contains(&req.to_lowercase()) {
|
|
15
|
-
return Err(format!("Missing required header: {}"
|
|
19
|
+
return Err(format!("Missing required header: {req}"));
|
|
16
20
|
}
|
|
17
21
|
}
|
|
18
22
|
Ok(())
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
/// Validate header format
|
|
26
|
+
///
|
|
27
|
+
/// # Errors
|
|
28
|
+
///
|
|
29
|
+
/// Returns an error if the header format is invalid.
|
|
22
30
|
pub fn validate_format(key: &str, value: &str, format: HeaderFormat) -> Result<(), String> {
|
|
23
31
|
match format {
|
|
24
32
|
HeaderFormat::Bearer => {
|
|
25
33
|
if !value.starts_with("Bearer ") {
|
|
26
|
-
return Err(format!("{}: must start with 'Bearer '"
|
|
34
|
+
return Err(format!("{key}: must start with 'Bearer '"));
|
|
27
35
|
}
|
|
28
36
|
Ok(())
|
|
29
37
|
}
|
|
30
38
|
HeaderFormat::Json => {
|
|
31
39
|
if !value.starts_with("application/json") {
|
|
32
|
-
return Err(format!("{}: must be 'application/json'"
|
|
40
|
+
return Err(format!("{key}: must be 'application/json'"));
|
|
33
41
|
}
|
|
34
42
|
Ok(())
|
|
35
43
|
}
|
|
@@ -38,6 +46,7 @@ impl HeaderValidator {
|
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
/// Header validation formats
|
|
49
|
+
#[derive(Copy, Clone)]
|
|
41
50
|
pub enum HeaderFormat {
|
|
42
51
|
/// Bearer token format
|
|
43
52
|
Bearer,
|
|
@@ -50,6 +59,10 @@ pub struct BodyValidator;
|
|
|
50
59
|
|
|
51
60
|
impl BodyValidator {
|
|
52
61
|
/// Validate that required fields are present in a JSON object
|
|
62
|
+
///
|
|
63
|
+
/// # Errors
|
|
64
|
+
///
|
|
65
|
+
/// Returns an error if required fields are missing.
|
|
53
66
|
pub fn validate_required_fields(body: &Value, required: &[&str]) -> Result<(), String> {
|
|
54
67
|
let obj = body
|
|
55
68
|
.as_object()
|
|
@@ -57,44 +70,48 @@ impl BodyValidator {
|
|
|
57
70
|
|
|
58
71
|
for field in required {
|
|
59
72
|
if !obj.contains_key(*field) {
|
|
60
|
-
return Err(format!("Missing required field: {}"
|
|
73
|
+
return Err(format!("Missing required field: {field}"));
|
|
61
74
|
}
|
|
62
75
|
}
|
|
63
76
|
Ok(())
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
/// Validate field type
|
|
80
|
+
///
|
|
81
|
+
/// # Errors
|
|
82
|
+
///
|
|
83
|
+
/// Returns an error if the field type doesn't match.
|
|
67
84
|
pub fn validate_field_type(body: &Value, field: &str, expected_type: FieldType) -> Result<(), String> {
|
|
68
85
|
let obj = body
|
|
69
86
|
.as_object()
|
|
70
87
|
.ok_or_else(|| "Body must be a JSON object".to_string())?;
|
|
71
88
|
|
|
72
|
-
let value = obj.get(field).ok_or_else(|| format!("Field not found: {}"
|
|
89
|
+
let value = obj.get(field).ok_or_else(|| format!("Field not found: {field}"))?;
|
|
73
90
|
|
|
74
91
|
match expected_type {
|
|
75
92
|
FieldType::String => {
|
|
76
93
|
if !value.is_string() {
|
|
77
|
-
return Err(format!("{}: expected string"
|
|
94
|
+
return Err(format!("{field}: expected string"));
|
|
78
95
|
}
|
|
79
96
|
}
|
|
80
97
|
FieldType::Number => {
|
|
81
98
|
if !value.is_number() {
|
|
82
|
-
return Err(format!("{}: expected number"
|
|
99
|
+
return Err(format!("{field}: expected number"));
|
|
83
100
|
}
|
|
84
101
|
}
|
|
85
102
|
FieldType::Boolean => {
|
|
86
103
|
if !value.is_boolean() {
|
|
87
|
-
return Err(format!("{}: expected boolean"
|
|
104
|
+
return Err(format!("{field}: expected boolean"));
|
|
88
105
|
}
|
|
89
106
|
}
|
|
90
107
|
FieldType::Object => {
|
|
91
108
|
if !value.is_object() {
|
|
92
|
-
return Err(format!("{}: expected object"
|
|
109
|
+
return Err(format!("{field}: expected object"));
|
|
93
110
|
}
|
|
94
111
|
}
|
|
95
112
|
FieldType::Array => {
|
|
96
113
|
if !value.is_array() {
|
|
97
|
-
return Err(format!("{}: expected array"
|
|
114
|
+
return Err(format!("{field}: expected array"));
|
|
98
115
|
}
|
|
99
116
|
}
|
|
100
117
|
}
|
|
@@ -103,6 +120,7 @@ impl BodyValidator {
|
|
|
103
120
|
}
|
|
104
121
|
|
|
105
122
|
/// Field types for validation
|
|
123
|
+
#[derive(Copy, Clone)]
|
|
106
124
|
pub enum FieldType {
|
|
107
125
|
String,
|
|
108
126
|
Number,
|
|
@@ -7,6 +7,7 @@ struct JsonSource {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
impl JsonSource {
|
|
10
|
+
#[allow(clippy::missing_const_for_fn)]
|
|
10
11
|
fn new(value: serde_json::Value) -> Self {
|
|
11
12
|
Self { value }
|
|
12
13
|
}
|
|
@@ -44,7 +45,7 @@ impl ConfigSource for JsonSource {
|
|
|
44
45
|
|
|
45
46
|
fn get_nested(&self, key: &str) -> Option<Box<dyn ConfigSource + '_>> {
|
|
46
47
|
let nested = self.get(key)?.as_object()?;
|
|
47
|
-
Some(Box::new(
|
|
48
|
+
Some(Box::new(Self {
|
|
48
49
|
value: serde_json::Value::Object(nested.clone()),
|
|
49
50
|
}))
|
|
50
51
|
}
|
|
@@ -60,7 +61,7 @@ impl ConfigSource for JsonSource {
|
|
|
60
61
|
fn get_array_element(&self, key: &str, index: usize) -> Option<Box<dyn ConfigSource + '_>> {
|
|
61
62
|
let array = self.get(key)?.as_array()?;
|
|
62
63
|
let element = array.get(index)?.as_object()?;
|
|
63
|
-
Some(Box::new(
|
|
64
|
+
Some(Box::new(Self {
|
|
64
65
|
value: serde_json::Value::Object(element.clone()),
|
|
65
66
|
}))
|
|
66
67
|
}
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
//!
|
|
3
3
|
//! These tests cover serialization edge cases and ensure fallback
|
|
4
4
|
//! behavior works correctly when serialization fails.
|
|
5
|
+
#![allow(
|
|
6
|
+
clippy::redundant_clone,
|
|
7
|
+
clippy::uninlined_format_args,
|
|
8
|
+
clippy::doc_markdown,
|
|
9
|
+
reason = "Edge case tests for error handling"
|
|
10
|
+
)]
|
|
5
11
|
|
|
6
12
|
use axum::http::StatusCode;
|
|
7
13
|
use pretty_assertions::assert_eq;
|
|
@@ -99,7 +105,7 @@ fn test_from_structured_error_with_details() {
|
|
|
99
105
|
json!({"field": "username", "reason": "already_exists"}),
|
|
100
106
|
);
|
|
101
107
|
|
|
102
|
-
let (status, body) = ErrorResponseBuilder::from_structured_error(error);
|
|
108
|
+
let (status, body) = ErrorResponseBuilder::from_structured_error(&error);
|
|
103
109
|
assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
|
|
104
110
|
let parsed: Value = serde_json::from_str(&body).unwrap();
|
|
105
111
|
assert_eq!(parsed["code"], "custom_error");
|
|
@@ -201,23 +207,17 @@ fn test_all_convenience_methods_return_valid_json() {
|
|
|
201
207
|
];
|
|
202
208
|
|
|
203
209
|
for (status, body) in test_cases {
|
|
204
|
-
let parsed: Value =
|
|
205
|
-
.unwrap_or_else(|_| panic!("Failed to parse JSON for status {}: {}"
|
|
210
|
+
let parsed: Value =
|
|
211
|
+
serde_json::from_str(&body).unwrap_or_else(|_| panic!("Failed to parse JSON for status {status}: {body}"));
|
|
206
212
|
|
|
207
213
|
assert!(
|
|
208
214
|
parsed.get("error").is_some(),
|
|
209
|
-
"Missing 'error' field for status {}"
|
|
210
|
-
status
|
|
211
|
-
);
|
|
212
|
-
assert!(
|
|
213
|
-
parsed.get("code").is_some(),
|
|
214
|
-
"Missing 'code' field for status {}",
|
|
215
|
-
status
|
|
215
|
+
"Missing 'error' field for status {status}"
|
|
216
216
|
);
|
|
217
|
+
assert!(parsed.get("code").is_some(), "Missing 'code' field for status {status}");
|
|
217
218
|
assert!(
|
|
218
219
|
parsed.get("details").is_some(),
|
|
219
|
-
"Missing 'details' field for status {}"
|
|
220
|
-
status
|
|
220
|
+
"Missing 'details' field for status {status}"
|
|
221
221
|
);
|
|
222
222
|
}
|
|
223
223
|
}
|
|
@@ -336,7 +336,7 @@ fn test_error_message_types() {
|
|
|
336
336
|
let (_, body) = ErrorResponseBuilder::bad_request("test");
|
|
337
337
|
assert!(body.contains("test"));
|
|
338
338
|
|
|
339
|
-
let (_, body) = ErrorResponseBuilder::bad_request(format!("Error: {}", 123));
|
|
339
|
+
let (_, body) = ErrorResponseBuilder::bad_request(format!("Error: {0}", 123));
|
|
340
340
|
assert!(body.contains("Error: 123"));
|
|
341
341
|
}
|
|
342
342
|
|
data/vendor/crates/spikard-bindings-shared/tests/{comprehensive_coverage.rs → full_coverage.rs}
RENAMED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
//!
|
|
3
3
|
//! This test file ensures full code coverage across all modules in the crate,
|
|
4
4
|
//! testing edge cases, error paths, and integration scenarios.
|
|
5
|
+
#![allow(
|
|
6
|
+
clippy::doc_markdown,
|
|
7
|
+
clippy::items_after_statements,
|
|
8
|
+
clippy::uninlined_format_args,
|
|
9
|
+
clippy::redundant_clone,
|
|
10
|
+
reason = "Integration test with many coverage scenarios"
|
|
11
|
+
)]
|
|
5
12
|
|
|
6
13
|
use axum::http::{Request, StatusCode};
|
|
7
14
|
use pretty_assertions::assert_eq;
|
|
@@ -201,12 +208,10 @@ fn test_lifecycle_hook_types() {
|
|
|
201
208
|
}
|
|
202
209
|
|
|
203
210
|
let continue_result = HookResult::Continue;
|
|
204
|
-
|
|
205
|
-
assert!(matches!(cloned_continue, HookResult::Continue));
|
|
211
|
+
assert!(matches!(continue_result.clone(), HookResult::Continue));
|
|
206
212
|
|
|
207
213
|
let short_circuit = HookResult::ShortCircuit(json!({"status": "error"}));
|
|
208
|
-
|
|
209
|
-
assert!(matches!(cloned_short, HookResult::ShortCircuit(_)));
|
|
214
|
+
assert!(matches!(short_circuit.clone(), HookResult::ShortCircuit(_)));
|
|
210
215
|
}
|
|
211
216
|
|
|
212
217
|
#[test]
|
|
@@ -413,7 +418,7 @@ fn test_conversion_traits_comprehensive() {
|
|
|
413
418
|
fn from_any(value: &(dyn std::any::Any + Send + Sync)) -> Result<Self, Self::Error> {
|
|
414
419
|
value
|
|
415
420
|
.downcast_ref::<i32>()
|
|
416
|
-
.map(|&v|
|
|
421
|
+
.map(|&v| Self { value: v })
|
|
417
422
|
.ok_or_else(|| "Type mismatch".to_string())
|
|
418
423
|
}
|
|
419
424
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Integration tests for handler_base module
|
|
1
|
+
//! Integration tests for `handler_base` module
|
|
2
2
|
//!
|
|
3
3
|
//! These tests cover the validation paths and error handling that aren't
|
|
4
4
|
//! covered by unit tests in the module itself.
|
|
@@ -41,7 +41,7 @@ impl LanguageHandler for MockHandler {
|
|
|
41
41
|
if should_fail {
|
|
42
42
|
Err(HandlerError::Execution("Handler failed".to_string()))
|
|
43
43
|
} else {
|
|
44
|
-
Ok(format!("output:{}"
|
|
44
|
+
Ok(format!("output:{input}"))
|
|
45
45
|
}
|
|
46
46
|
})
|
|
47
47
|
}
|
|
@@ -81,10 +81,10 @@ async fn test_handler_executor_with_validation_error() {
|
|
|
81
81
|
let request = Request::builder().body(Body::empty()).unwrap();
|
|
82
82
|
let request_data = RequestData {
|
|
83
83
|
path_params: Arc::new(HashMap::new()),
|
|
84
|
-
query_params: json!({}),
|
|
84
|
+
query_params: Arc::new(json!({})),
|
|
85
85
|
validated_params: None,
|
|
86
86
|
raw_query_params: Arc::new(HashMap::new()),
|
|
87
|
-
body: json!({"username": "john"}),
|
|
87
|
+
body: Arc::new(json!({"username": "john"})),
|
|
88
88
|
raw_body: None,
|
|
89
89
|
headers: Arc::new(HashMap::new()),
|
|
90
90
|
cookies: Arc::new(HashMap::new()),
|
|
@@ -110,10 +110,10 @@ async fn test_handler_executor_prepare_failure() {
|
|
|
110
110
|
let request = Request::builder().body(Body::empty()).unwrap();
|
|
111
111
|
let request_data = RequestData {
|
|
112
112
|
path_params: Arc::new(HashMap::new()),
|
|
113
|
-
query_params: json!({}),
|
|
113
|
+
query_params: Arc::new(json!({})),
|
|
114
114
|
validated_params: None,
|
|
115
115
|
raw_query_params: Arc::new(HashMap::new()),
|
|
116
|
-
body: json!({}),
|
|
116
|
+
body: Arc::new(json!({})),
|
|
117
117
|
raw_body: None,
|
|
118
118
|
headers: Arc::new(HashMap::new()),
|
|
119
119
|
cookies: Arc::new(HashMap::new()),
|
|
@@ -139,10 +139,10 @@ async fn test_handler_executor_invoke_failure() {
|
|
|
139
139
|
let request = Request::builder().body(Body::empty()).unwrap();
|
|
140
140
|
let request_data = RequestData {
|
|
141
141
|
path_params: Arc::new(HashMap::new()),
|
|
142
|
-
query_params: json!({}),
|
|
142
|
+
query_params: Arc::new(json!({})),
|
|
143
143
|
validated_params: None,
|
|
144
144
|
raw_query_params: Arc::new(HashMap::new()),
|
|
145
|
-
body: json!({}),
|
|
145
|
+
body: Arc::new(json!({})),
|
|
146
146
|
raw_body: None,
|
|
147
147
|
headers: Arc::new(HashMap::new()),
|
|
148
148
|
cookies: Arc::new(HashMap::new()),
|
|
@@ -168,10 +168,10 @@ async fn test_handler_executor_interpret_failure() {
|
|
|
168
168
|
let request = Request::builder().body(Body::empty()).unwrap();
|
|
169
169
|
let request_data = RequestData {
|
|
170
170
|
path_params: Arc::new(HashMap::new()),
|
|
171
|
-
query_params: json!({}),
|
|
171
|
+
query_params: Arc::new(json!({})),
|
|
172
172
|
validated_params: None,
|
|
173
173
|
raw_query_params: Arc::new(HashMap::new()),
|
|
174
|
-
body: json!({}),
|
|
174
|
+
body: Arc::new(json!({})),
|
|
175
175
|
raw_body: None,
|
|
176
176
|
headers: Arc::new(HashMap::new()),
|
|
177
177
|
cookies: Arc::new(HashMap::new()),
|
|
@@ -210,10 +210,10 @@ async fn test_handler_executor_with_request_validator() {
|
|
|
210
210
|
|
|
211
211
|
let request_data = RequestData {
|
|
212
212
|
path_params: Arc::new(HashMap::new()),
|
|
213
|
-
query_params: json!({}),
|
|
213
|
+
query_params: Arc::new(json!({})),
|
|
214
214
|
validated_params: None,
|
|
215
215
|
raw_query_params: Arc::new(HashMap::new()),
|
|
216
|
-
body: json!({"name": "test"}),
|
|
216
|
+
body: Arc::new(json!({"name": "test"})),
|
|
217
217
|
raw_body: None,
|
|
218
218
|
headers: Arc::new(headers),
|
|
219
219
|
cookies: Arc::new(HashMap::new()),
|
|
@@ -263,10 +263,10 @@ async fn test_handler_executor_builder_pattern() {
|
|
|
263
263
|
let request = Request::builder().body(Body::empty()).unwrap();
|
|
264
264
|
let request_data = RequestData {
|
|
265
265
|
path_params: Arc::new(HashMap::new()),
|
|
266
|
-
query_params: json!({}),
|
|
266
|
+
query_params: Arc::new(json!({})),
|
|
267
267
|
validated_params: None,
|
|
268
268
|
raw_query_params: Arc::new(HashMap::new()),
|
|
269
|
-
body: json!({"email": "test@example.com"}),
|
|
269
|
+
body: Arc::new(json!({"email": "test@example.com"})),
|
|
270
270
|
raw_body: None,
|
|
271
271
|
headers: Arc::new(HashMap::new()),
|
|
272
272
|
cookies: Arc::new(HashMap::new()),
|