spikard 0.12.0 → 0.15.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/Steepfile +6 -0
- data/ext/spikard_rb/extconf.rb +1 -2
- data/ext/spikard_rb/{Cargo.lock → native/Cargo.lock} +897 -451
- data/ext/spikard_rb/native/Cargo.toml +24 -0
- data/ext/spikard_rb/src/lib.rs +5366 -3
- data/lib/spikard/native.rb +86 -0
- data/lib/spikard/version.rb +6 -1
- data/lib/spikard.rb +8 -45
- data/lib/spikard_rb.so +0 -0
- data/sig/types.rbs +427 -0
- metadata +14 -242
- data/LICENSE +0 -1
- data/README.md +0 -267
- data/ext/spikard_rb/Cargo.toml +0 -17
- data/lib/spikard/app.rb +0 -428
- data/lib/spikard/background.rb +0 -58
- data/lib/spikard/config.rb +0 -506
- data/lib/spikard/converters.rb +0 -13
- data/lib/spikard/grpc.rb +0 -182
- data/lib/spikard/handler_wrapper.rb +0 -113
- data/lib/spikard/provide.rb +0 -214
- data/lib/spikard/response.rb +0 -173
- data/lib/spikard/schema.rb +0 -243
- data/lib/spikard/sse.rb +0 -111
- data/lib/spikard/streaming_response.rb +0 -44
- data/lib/spikard/testing.rb +0 -432
- data/lib/spikard/upload_file.rb +0 -131
- data/lib/spikard/websocket.rb +0 -59
- data/sig/spikard.rbs +0 -719
- data/vendor/crates/spikard-bindings-shared/Cargo.toml +0 -80
- data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +0 -132
- data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +0 -905
- data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +0 -210
- data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +0 -252
- data/vendor/crates/spikard-bindings-shared/src/error_response.rs +0 -404
- data/vendor/crates/spikard-bindings-shared/src/grpc_metadata.rs +0 -199
- data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +0 -252
- data/vendor/crates/spikard-bindings-shared/src/json_conversion.rs +0 -829
- data/vendor/crates/spikard-bindings-shared/src/lazy_cache.rs +0 -587
- data/vendor/crates/spikard-bindings-shared/src/lib.rs +0 -33
- data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +0 -298
- data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +0 -594
- data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +0 -743
- data/vendor/crates/spikard-bindings-shared/src/response_interpreter.rs +0 -944
- data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +0 -260
- data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +0 -369
- data/vendor/crates/spikard-bindings-shared/tests/config_extractor_behavior.rs +0 -192
- data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +0 -383
- data/vendor/crates/spikard-bindings-shared/tests/full_coverage.rs +0 -459
- data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +0 -280
- data/vendor/crates/spikard-bindings-shared/tests/integration_tests.rs +0 -669
- data/vendor/crates/spikard-core/Cargo.toml +0 -60
- data/vendor/crates/spikard-core/src/bindings/mod.rs +0 -3
- data/vendor/crates/spikard-core/src/bindings/response.rs +0 -130
- data/vendor/crates/spikard-core/src/debug.rs +0 -127
- data/vendor/crates/spikard-core/src/di/container.rs +0 -702
- data/vendor/crates/spikard-core/src/di/dependency.rs +0 -273
- data/vendor/crates/spikard-core/src/di/error.rs +0 -118
- data/vendor/crates/spikard-core/src/di/factory.rs +0 -538
- data/vendor/crates/spikard-core/src/di/graph.rs +0 -507
- data/vendor/crates/spikard-core/src/di/mod.rs +0 -192
- data/vendor/crates/spikard-core/src/di/resolved.rs +0 -428
- data/vendor/crates/spikard-core/src/di/value.rs +0 -282
- data/vendor/crates/spikard-core/src/errors.rs +0 -72
- data/vendor/crates/spikard-core/src/http.rs +0 -492
- data/vendor/crates/spikard-core/src/lib.rs +0 -29
- data/vendor/crates/spikard-core/src/lifecycle.rs +0 -1273
- data/vendor/crates/spikard-core/src/metadata.rs +0 -378
- data/vendor/crates/spikard-core/src/parameters.rs +0 -2546
- data/vendor/crates/spikard-core/src/problem.rs +0 -358
- data/vendor/crates/spikard-core/src/request_data.rs +0 -1146
- data/vendor/crates/spikard-core/src/router.rs +0 -530
- data/vendor/crates/spikard-core/src/schema_registry.rs +0 -197
- data/vendor/crates/spikard-core/src/type_hints.rs +0 -311
- data/vendor/crates/spikard-core/src/validation/error_mapper.rs +0 -710
- data/vendor/crates/spikard-core/src/validation/mod.rs +0 -470
- data/vendor/crates/spikard-core/tests/bindings_response_tests.rs +0 -136
- data/vendor/crates/spikard-core/tests/di_dependency_defaults.rs +0 -37
- data/vendor/crates/spikard-core/tests/error_mapper.rs +0 -761
- data/vendor/crates/spikard-core/tests/parameters_edge_cases.rs +0 -106
- data/vendor/crates/spikard-core/tests/parameters_full.rs +0 -701
- data/vendor/crates/spikard-core/tests/parameters_schema_and_formats.rs +0 -301
- data/vendor/crates/spikard-core/tests/request_data_roundtrip.rs +0 -67
- data/vendor/crates/spikard-core/tests/validation_coverage.rs +0 -250
- data/vendor/crates/spikard-core/tests/validation_error_paths.rs +0 -45
- data/vendor/crates/spikard-http/Cargo.toml +0 -87
- data/vendor/crates/spikard-http/examples/sse-notifications.rs +0 -148
- data/vendor/crates/spikard-http/examples/websocket-chat.rs +0 -92
- data/vendor/crates/spikard-http/src/auth.rs +0 -301
- data/vendor/crates/spikard-http/src/background.rs +0 -1860
- data/vendor/crates/spikard-http/src/bindings/mod.rs +0 -3
- data/vendor/crates/spikard-http/src/bindings/response.rs +0 -1
- data/vendor/crates/spikard-http/src/body_metadata.rs +0 -8
- data/vendor/crates/spikard-http/src/cors.rs +0 -1026
- data/vendor/crates/spikard-http/src/debug.rs +0 -128
- data/vendor/crates/spikard-http/src/di_handler.rs +0 -1672
- data/vendor/crates/spikard-http/src/grpc/framing.rs +0 -469
- data/vendor/crates/spikard-http/src/grpc/handler.rs +0 -1122
- data/vendor/crates/spikard-http/src/grpc/mod.rs +0 -434
- data/vendor/crates/spikard-http/src/grpc/service.rs +0 -622
- data/vendor/crates/spikard-http/src/grpc/streaming.rs +0 -319
- data/vendor/crates/spikard-http/src/handler_response.rs +0 -901
- data/vendor/crates/spikard-http/src/handler_trait.rs +0 -1015
- data/vendor/crates/spikard-http/src/handler_trait_tests.rs +0 -290
- data/vendor/crates/spikard-http/src/jsonrpc/http_handler.rs +0 -502
- data/vendor/crates/spikard-http/src/jsonrpc/method_registry.rs +0 -648
- data/vendor/crates/spikard-http/src/jsonrpc/mod.rs +0 -58
- data/vendor/crates/spikard-http/src/jsonrpc/protocol.rs +0 -1207
- data/vendor/crates/spikard-http/src/jsonrpc/router.rs +0 -2262
- data/vendor/crates/spikard-http/src/lib.rs +0 -548
- data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +0 -230
- data/vendor/crates/spikard-http/src/lifecycle.rs +0 -1193
- data/vendor/crates/spikard-http/src/middleware/mod.rs +0 -560
- data/vendor/crates/spikard-http/src/middleware/multipart.rs +0 -912
- data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +0 -513
- data/vendor/crates/spikard-http/src/middleware/validation.rs +0 -768
- data/vendor/crates/spikard-http/src/openapi/mod.rs +0 -309
- data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +0 -535
- data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +0 -1363
- data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +0 -667
- data/vendor/crates/spikard-http/src/query_parser.rs +0 -793
- data/vendor/crates/spikard-http/src/response.rs +0 -720
- data/vendor/crates/spikard-http/src/server/fast_router.rs +0 -186
- data/vendor/crates/spikard-http/src/server/grpc_routing.rs +0 -858
- data/vendor/crates/spikard-http/src/server/handler.rs +0 -1661
- data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +0 -253
- data/vendor/crates/spikard-http/src/server/mod.rs +0 -1649
- data/vendor/crates/spikard-http/src/server/request_extraction.rs +0 -871
- data/vendor/crates/spikard-http/src/server/routing_factory.rs +0 -618
- data/vendor/crates/spikard-http/src/sse.rs +0 -1409
- data/vendor/crates/spikard-http/src/testing/form.rs +0 -52
- data/vendor/crates/spikard-http/src/testing/multipart.rs +0 -64
- data/vendor/crates/spikard-http/src/testing/test_client.rs +0 -787
- data/vendor/crates/spikard-http/src/testing.rs +0 -617
- data/vendor/crates/spikard-http/src/websocket.rs +0 -1477
- data/vendor/crates/spikard-http/tests/auth_integration.rs +0 -645
- data/vendor/crates/spikard-http/tests/background_behavior.rs +0 -832
- data/vendor/crates/spikard-http/tests/common/grpc_helpers.rs +0 -1012
- data/vendor/crates/spikard-http/tests/common/handlers.rs +0 -309
- data/vendor/crates/spikard-http/tests/common/mod.rs +0 -33
- data/vendor/crates/spikard-http/tests/common/test_builders.rs +0 -628
- data/vendor/crates/spikard-http/tests/di_handler_error_responses.rs +0 -162
- data/vendor/crates/spikard-http/tests/di_integration.rs +0 -192
- data/vendor/crates/spikard-http/tests/doc_snippets.rs +0 -5
- data/vendor/crates/spikard-http/tests/grpc_bidirectional_streaming.rs +0 -430
- data/vendor/crates/spikard-http/tests/grpc_client_streaming.rs +0 -738
- data/vendor/crates/spikard-http/tests/grpc_error_handling_test.rs +0 -652
- data/vendor/crates/spikard-http/tests/grpc_integration_test.rs +0 -334
- data/vendor/crates/spikard-http/tests/grpc_metadata_test.rs +0 -532
- data/vendor/crates/spikard-http/tests/grpc_server_integration.rs +0 -495
- data/vendor/crates/spikard-http/tests/grpc_server_streaming.rs +0 -974
- data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +0 -1093
- data/vendor/crates/spikard-http/tests/middleware_stack_integration.rs +0 -389
- data/vendor/crates/spikard-http/tests/multipart_behavior.rs +0 -656
- data/vendor/crates/spikard-http/tests/request_extraction_full.rs +0 -513
- data/vendor/crates/spikard-http/tests/server_auth_middleware_behavior.rs +0 -328
- data/vendor/crates/spikard-http/tests/server_config_builder.rs +0 -314
- data/vendor/crates/spikard-http/tests/server_configured_router_behavior.rs +0 -200
- data/vendor/crates/spikard-http/tests/server_cors_preflight.rs +0 -83
- data/vendor/crates/spikard-http/tests/server_handler_wrappers.rs +0 -464
- data/vendor/crates/spikard-http/tests/server_method_router_additional_behavior.rs +0 -286
- data/vendor/crates/spikard-http/tests/server_method_router_coverage.rs +0 -118
- data/vendor/crates/spikard-http/tests/server_middleware_behavior.rs +0 -99
- data/vendor/crates/spikard-http/tests/server_middleware_branches.rs +0 -204
- data/vendor/crates/spikard-http/tests/server_openapi_jsonrpc_static.rs +0 -421
- data/vendor/crates/spikard-http/tests/server_router_behavior.rs +0 -121
- data/vendor/crates/spikard-http/tests/sse_behavior.rs +0 -620
- data/vendor/crates/spikard-http/tests/sse_full_behavior.rs +0 -584
- data/vendor/crates/spikard-http/tests/sse_handler_behavior.rs +0 -130
- data/vendor/crates/spikard-http/tests/test_client_requests.rs +0 -167
- data/vendor/crates/spikard-http/tests/testing_helpers.rs +0 -87
- data/vendor/crates/spikard-http/tests/testing_module_coverage.rs +0 -155
- data/vendor/crates/spikard-http/tests/urlencoded_content_type.rs +0 -82
- data/vendor/crates/spikard-http/tests/websocket_behavior.rs +0 -663
- data/vendor/crates/spikard-http/tests/websocket_full_behavior.rs +0 -440
- data/vendor/crates/spikard-http/tests/websocket_integration.rs +0 -150
- data/vendor/crates/spikard-rb/Cargo.toml +0 -68
- data/vendor/crates/spikard-rb/build.rs +0 -200
- data/vendor/crates/spikard-rb/src/background.rs +0 -63
- data/vendor/crates/spikard-rb/src/config/mod.rs +0 -5
- data/vendor/crates/spikard-rb/src/config/server_config.rs +0 -401
- data/vendor/crates/spikard-rb/src/conversion.rs +0 -688
- data/vendor/crates/spikard-rb/src/di/builder.rs +0 -100
- data/vendor/crates/spikard-rb/src/di/mod.rs +0 -375
- data/vendor/crates/spikard-rb/src/grpc/handler.rs +0 -834
- data/vendor/crates/spikard-rb/src/grpc/mod.rs +0 -13
- data/vendor/crates/spikard-rb/src/gvl.rs +0 -80
- data/vendor/crates/spikard-rb/src/handler.rs +0 -699
- data/vendor/crates/spikard-rb/src/integration/mod.rs +0 -3
- data/vendor/crates/spikard-rb/src/lib.rs +0 -2264
- data/vendor/crates/spikard-rb/src/lifecycle.rs +0 -303
- data/vendor/crates/spikard-rb/src/metadata/mod.rs +0 -5
- data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +0 -507
- data/vendor/crates/spikard-rb/src/request.rs +0 -439
- data/vendor/crates/spikard-rb/src/runtime/mod.rs +0 -5
- data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +0 -344
- data/vendor/crates/spikard-rb/src/server.rs +0 -307
- data/vendor/crates/spikard-rb/src/sse.rs +0 -231
- data/vendor/crates/spikard-rb/src/testing/client.rs +0 -698
- data/vendor/crates/spikard-rb/src/testing/mod.rs +0 -7
- data/vendor/crates/spikard-rb/src/testing/sse.rs +0 -108
- data/vendor/crates/spikard-rb/src/testing/websocket.rs +0 -573
- data/vendor/crates/spikard-rb/src/websocket.rs +0 -475
- data/vendor/crates/spikard-rb-macros/Cargo.toml +0 -25
- data/vendor/crates/spikard-rb-macros/src/lib.rs +0 -51
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
use axum::body::Body;
|
|
2
|
-
use axum::http::{Request, StatusCode};
|
|
3
|
-
use axum::response::IntoResponse;
|
|
4
|
-
use axum::{Router, routing::any};
|
|
5
|
-
use spikard_http::testing::{MultipartFilePart, TestClient};
|
|
6
|
-
|
|
7
|
-
async fn echo(req: Request<Body>) -> axum::response::Response {
|
|
8
|
-
let method = req.method().to_string();
|
|
9
|
-
let uri = req.uri().to_string();
|
|
10
|
-
let headers = req
|
|
11
|
-
.headers()
|
|
12
|
-
.iter()
|
|
13
|
-
.fold(serde_json::Map::new(), |mut map, (key, value)| {
|
|
14
|
-
map.insert(
|
|
15
|
-
key.to_string(),
|
|
16
|
-
serde_json::Value::String(value.to_str().unwrap_or("").to_string()),
|
|
17
|
-
);
|
|
18
|
-
map
|
|
19
|
-
});
|
|
20
|
-
let bytes = axum::body::to_bytes(req.into_body(), usize::MAX).await.unwrap();
|
|
21
|
-
let body_text = String::from_utf8_lossy(&bytes).to_string();
|
|
22
|
-
|
|
23
|
-
let payload = serde_json::json!({
|
|
24
|
-
"method": method,
|
|
25
|
-
"uri": uri,
|
|
26
|
-
"headers": headers,
|
|
27
|
-
"body": body_text,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
(StatusCode::OK, axum::Json(payload)).into_response()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
#[tokio::test]
|
|
34
|
-
async fn test_client_sends_query_headers_and_bodies() {
|
|
35
|
-
let app = Router::new().route("/{*path}", any(echo));
|
|
36
|
-
let client = TestClient::from_router(app).expect("client");
|
|
37
|
-
|
|
38
|
-
let snapshot = client
|
|
39
|
-
.get(
|
|
40
|
-
"/items",
|
|
41
|
-
Some(vec![("q".to_string(), "a b".to_string())]),
|
|
42
|
-
Some(vec![("x-test".to_string(), "1".to_string())]),
|
|
43
|
-
)
|
|
44
|
-
.await
|
|
45
|
-
.expect("get");
|
|
46
|
-
assert_eq!(snapshot.status, 200);
|
|
47
|
-
let json = snapshot.json().expect("json");
|
|
48
|
-
assert_eq!(json["method"], "GET");
|
|
49
|
-
assert!(json["uri"].as_str().unwrap().contains("/items?q=a%20b"));
|
|
50
|
-
assert_eq!(json["headers"]["x-test"], "1");
|
|
51
|
-
|
|
52
|
-
let snapshot = client
|
|
53
|
-
.post(
|
|
54
|
-
"/json",
|
|
55
|
-
Some(serde_json::json!({"hello":"world"})),
|
|
56
|
-
None,
|
|
57
|
-
None,
|
|
58
|
-
None,
|
|
59
|
-
None,
|
|
60
|
-
)
|
|
61
|
-
.await
|
|
62
|
-
.expect("post");
|
|
63
|
-
let json = snapshot.json().expect("json");
|
|
64
|
-
assert_eq!(json["method"], "POST");
|
|
65
|
-
assert!(json["body"].as_str().unwrap().contains("\"hello\":\"world\""));
|
|
66
|
-
|
|
67
|
-
let snapshot = client
|
|
68
|
-
.post(
|
|
69
|
-
"/form",
|
|
70
|
-
None,
|
|
71
|
-
Some(vec![("a".to_string(), "b".to_string())]),
|
|
72
|
-
None,
|
|
73
|
-
None,
|
|
74
|
-
None,
|
|
75
|
-
)
|
|
76
|
-
.await
|
|
77
|
-
.expect("post");
|
|
78
|
-
let json = snapshot.json().expect("json");
|
|
79
|
-
let body = json["body"].as_str().unwrap();
|
|
80
|
-
assert!(body.contains('a'));
|
|
81
|
-
assert!(body.contains('b'));
|
|
82
|
-
|
|
83
|
-
let snapshot = client
|
|
84
|
-
.post(
|
|
85
|
-
"/multipart",
|
|
86
|
-
None,
|
|
87
|
-
None,
|
|
88
|
-
Some((
|
|
89
|
-
vec![("field".to_string(), "value".to_string())],
|
|
90
|
-
vec![MultipartFilePart {
|
|
91
|
-
field_name: "file".to_string(),
|
|
92
|
-
filename: "hello.txt".to_string(),
|
|
93
|
-
content_type: Some("text/plain".to_string()),
|
|
94
|
-
content: b"hello".to_vec(),
|
|
95
|
-
}],
|
|
96
|
-
)),
|
|
97
|
-
None,
|
|
98
|
-
None,
|
|
99
|
-
)
|
|
100
|
-
.await
|
|
101
|
-
.expect("post");
|
|
102
|
-
let json = snapshot.json().expect("json");
|
|
103
|
-
assert!(
|
|
104
|
-
json["headers"]["content-type"]
|
|
105
|
-
.as_str()
|
|
106
|
-
.unwrap()
|
|
107
|
-
.contains("multipart/form-data")
|
|
108
|
-
);
|
|
109
|
-
assert!(json["body"].as_str().unwrap().contains("hello"));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
#[tokio::test]
|
|
113
|
-
async fn test_client_supports_other_http_methods_and_query_merging() {
|
|
114
|
-
let app = Router::new().route("/{*path}", any(echo));
|
|
115
|
-
let client = TestClient::from_router(app).expect("client");
|
|
116
|
-
|
|
117
|
-
let snapshot = client
|
|
118
|
-
.put(
|
|
119
|
-
"/put",
|
|
120
|
-
Some(serde_json::json!({"name":"spikard"})),
|
|
121
|
-
None,
|
|
122
|
-
Some(vec![("x-test".to_string(), "2".to_string())]),
|
|
123
|
-
)
|
|
124
|
-
.await
|
|
125
|
-
.expect("put");
|
|
126
|
-
let json = snapshot.json().expect("json");
|
|
127
|
-
assert_eq!(json["method"], "PUT");
|
|
128
|
-
assert_eq!(json["headers"]["x-test"], "2");
|
|
129
|
-
assert!(json["body"].as_str().unwrap().contains("\"name\":\"spikard\""));
|
|
130
|
-
|
|
131
|
-
let snapshot = client.delete("/delete", None, None).await.expect("delete");
|
|
132
|
-
assert_eq!(snapshot.json().expect("json")["method"], "DELETE");
|
|
133
|
-
|
|
134
|
-
let snapshot = client.options("/options", None, None).await.expect("options");
|
|
135
|
-
assert_eq!(snapshot.json().expect("json")["method"], "OPTIONS");
|
|
136
|
-
|
|
137
|
-
let snapshot = client.head("/head", None, None).await.expect("head");
|
|
138
|
-
assert_eq!(snapshot.status, 200);
|
|
139
|
-
assert!(snapshot.body.is_empty());
|
|
140
|
-
|
|
141
|
-
let snapshot = client.trace("/trace", None, None).await.expect("trace");
|
|
142
|
-
assert_eq!(snapshot.json().expect("json")["method"], "TRACE");
|
|
143
|
-
|
|
144
|
-
let snapshot = client
|
|
145
|
-
.get("/items?x=1", Some(vec![("y".to_string(), "2".to_string())]), None)
|
|
146
|
-
.await
|
|
147
|
-
.expect("get");
|
|
148
|
-
assert!(
|
|
149
|
-
snapshot.json().expect("json")["uri"]
|
|
150
|
-
.as_str()
|
|
151
|
-
.unwrap()
|
|
152
|
-
.contains("x=1&y=2")
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
#[tokio::test]
|
|
157
|
-
async fn test_client_rejects_invalid_header_names() {
|
|
158
|
-
let app = Router::new().route("/{*path}", any(echo));
|
|
159
|
-
let client = TestClient::from_router(app).expect("client");
|
|
160
|
-
|
|
161
|
-
let error = client
|
|
162
|
-
.get("/items", None, Some(vec![("bad\n".to_string(), "1".to_string())]))
|
|
163
|
-
.await
|
|
164
|
-
.expect_err("invalid header");
|
|
165
|
-
let message = error.to_string();
|
|
166
|
-
assert!(message.contains("Invalid header name"));
|
|
167
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
use axum::http::HeaderValue;
|
|
2
|
-
use axum::{Router, response::IntoResponse, routing::get};
|
|
3
|
-
use brotli::CompressorWriter;
|
|
4
|
-
use flate2::Compression;
|
|
5
|
-
use flate2::write::GzEncoder;
|
|
6
|
-
use spikard_http::testing::{MultipartFilePart, build_multipart_body, encode_urlencoded_body, snapshot_response};
|
|
7
|
-
use std::io::Write;
|
|
8
|
-
|
|
9
|
-
#[test]
|
|
10
|
-
fn urlencoded_encoding_handles_scalars_and_objects() {
|
|
11
|
-
let s = serde_json::Value::String("a=b&c=d".to_string());
|
|
12
|
-
assert_eq!(encode_urlencoded_body(&s).unwrap(), b"a=b&c=d".to_vec());
|
|
13
|
-
|
|
14
|
-
let mut obj = serde_json::Map::new();
|
|
15
|
-
obj.insert("name".to_string(), serde_json::Value::String("Alice".to_string()));
|
|
16
|
-
obj.insert("tags".to_string(), serde_json::json!(["a", "b"]));
|
|
17
|
-
let value = serde_json::Value::Object(obj);
|
|
18
|
-
let encoded = String::from_utf8(encode_urlencoded_body(&value).unwrap()).unwrap();
|
|
19
|
-
assert!(encoded.contains("name=Alice"));
|
|
20
|
-
assert!(encoded.contains("tags"));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
#[test]
|
|
24
|
-
fn multipart_body_contains_fields_and_files() {
|
|
25
|
-
let (body, boundary) = build_multipart_body(
|
|
26
|
-
&[("field".to_string(), "value".to_string())],
|
|
27
|
-
&[MultipartFilePart {
|
|
28
|
-
field_name: "file".to_string(),
|
|
29
|
-
filename: "hello.txt".to_string(),
|
|
30
|
-
content_type: Some("text/plain".to_string()),
|
|
31
|
-
content: b"hello".to_vec(),
|
|
32
|
-
}],
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
let body_str = String::from_utf8_lossy(&body);
|
|
36
|
-
assert!(body_str.contains(&format!("--{boundary}")));
|
|
37
|
-
assert!(body_str.contains("name=\"field\""));
|
|
38
|
-
assert!(body_str.contains("value"));
|
|
39
|
-
assert!(body_str.contains("name=\"file\"; filename=\"hello.txt\""));
|
|
40
|
-
assert!(body_str.contains("Content-Type: text/plain"));
|
|
41
|
-
assert!(body_str.contains("hello"));
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
#[tokio::test]
|
|
45
|
-
async fn snapshot_response_decodes_gzip_body() {
|
|
46
|
-
let app = Router::new().route(
|
|
47
|
-
"/gzip",
|
|
48
|
-
get(|| async move {
|
|
49
|
-
let raw = b"hello gzip".to_vec();
|
|
50
|
-
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
|
|
51
|
-
encoder.write_all(&raw).unwrap();
|
|
52
|
-
let compressed = encoder.finish().unwrap();
|
|
53
|
-
|
|
54
|
-
([("content-encoding", HeaderValue::from_static("gzip"))], compressed).into_response()
|
|
55
|
-
}),
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
let server = axum_test::TestServer::new(app);
|
|
59
|
-
let response = server.get("/gzip").await;
|
|
60
|
-
|
|
61
|
-
let snapshot = snapshot_response(response).await.expect("snapshot failed");
|
|
62
|
-
assert_eq!(snapshot.status, 200);
|
|
63
|
-
assert_eq!(snapshot.text().unwrap(), "hello gzip");
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
#[tokio::test]
|
|
67
|
-
async fn snapshot_response_decodes_brotli_body() {
|
|
68
|
-
let app = Router::new().route(
|
|
69
|
-
"/br",
|
|
70
|
-
get(|| async move {
|
|
71
|
-
let raw = b"hello br".to_vec();
|
|
72
|
-
let mut writer = CompressorWriter::new(Vec::new(), 4096, 6, 22);
|
|
73
|
-
writer.write_all(&raw).unwrap();
|
|
74
|
-
writer.flush().unwrap();
|
|
75
|
-
let compressed = writer.into_inner();
|
|
76
|
-
|
|
77
|
-
([("content-encoding", HeaderValue::from_static("br"))], compressed).into_response()
|
|
78
|
-
}),
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
let server = axum_test::TestServer::new(app);
|
|
82
|
-
let response = server.get("/br").await;
|
|
83
|
-
|
|
84
|
-
let snapshot = snapshot_response(response).await.expect("snapshot failed");
|
|
85
|
-
assert_eq!(snapshot.status, 200);
|
|
86
|
-
assert_eq!(snapshot.text().unwrap(), "hello br");
|
|
87
|
-
}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
use axum::body::Body;
|
|
2
|
-
use axum::http::{HeaderValue, Request, StatusCode};
|
|
3
|
-
use axum::response::IntoResponse;
|
|
4
|
-
use axum::routing::get;
|
|
5
|
-
use flate2::Compression;
|
|
6
|
-
use flate2::write::GzEncoder;
|
|
7
|
-
use spikard_http::testing::{SnapshotError, WebSocketMessage, call_test_server, connect_websocket, snapshot_response};
|
|
8
|
-
use std::io::Write;
|
|
9
|
-
|
|
10
|
-
#[tokio::test]
|
|
11
|
-
async fn call_test_server_preserves_method_headers_query_and_body() {
|
|
12
|
-
let app = axum::Router::new().route(
|
|
13
|
-
"/echo",
|
|
14
|
-
get(|req: Request<Body>| async move {
|
|
15
|
-
let method = req.method().to_string();
|
|
16
|
-
let uri = req.uri().to_string();
|
|
17
|
-
let header = req
|
|
18
|
-
.headers()
|
|
19
|
-
.get("x-test")
|
|
20
|
-
.and_then(|v| v.to_str().ok())
|
|
21
|
-
.map_or_else(|| "<missing>".to_string(), str::to_string);
|
|
22
|
-
let bytes = axum::body::to_bytes(req.into_body(), usize::MAX).await.unwrap();
|
|
23
|
-
(StatusCode::OK, format!("{method} {uri} {header} {}", bytes.len())).into_response()
|
|
24
|
-
}),
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
let server = axum_test::TestServer::new(app);
|
|
28
|
-
let request = Request::builder()
|
|
29
|
-
.method("GET")
|
|
30
|
-
.uri("/echo?q=1")
|
|
31
|
-
.header("x-test", "1")
|
|
32
|
-
.body(Body::from("abc"))
|
|
33
|
-
.expect("request");
|
|
34
|
-
|
|
35
|
-
let response = call_test_server(&server, request).await;
|
|
36
|
-
assert_eq!(response.status_code(), StatusCode::OK);
|
|
37
|
-
let text = response.text();
|
|
38
|
-
assert!(text.contains("GET"));
|
|
39
|
-
assert!(text.contains("/echo"));
|
|
40
|
-
assert!(text.contains("q=1"));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
#[tokio::test]
|
|
44
|
-
async fn snapshot_response_reports_invalid_headers_and_decompression_errors() {
|
|
45
|
-
let bad_header = HeaderValue::from_bytes(b"\xFF").expect("header value");
|
|
46
|
-
let app = axum::Router::new()
|
|
47
|
-
.route(
|
|
48
|
-
"/bad-header",
|
|
49
|
-
get(move || async move {
|
|
50
|
-
(
|
|
51
|
-
StatusCode::OK,
|
|
52
|
-
[(axum::http::header::HeaderName::from_static("x-bad"), bad_header.clone())],
|
|
53
|
-
"ok",
|
|
54
|
-
)
|
|
55
|
-
}),
|
|
56
|
-
)
|
|
57
|
-
.route(
|
|
58
|
-
"/bad-gzip",
|
|
59
|
-
get(|| async move {
|
|
60
|
-
(
|
|
61
|
-
StatusCode::OK,
|
|
62
|
-
[(axum::http::header::CONTENT_ENCODING, "gzip")],
|
|
63
|
-
vec![0_u8, 1, 2, 3],
|
|
64
|
-
)
|
|
65
|
-
}),
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
let server = axum_test::TestServer::new(app);
|
|
69
|
-
|
|
70
|
-
let err = snapshot_response(server.get("/bad-header").await)
|
|
71
|
-
.await
|
|
72
|
-
.expect_err("invalid header");
|
|
73
|
-
assert!(matches!(err, SnapshotError::InvalidHeader(_)));
|
|
74
|
-
|
|
75
|
-
let err = snapshot_response(server.get("/bad-gzip").await)
|
|
76
|
-
.await
|
|
77
|
-
.expect_err("bad gzip");
|
|
78
|
-
assert!(matches!(err, SnapshotError::Decompression(_)));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
#[tokio::test]
|
|
82
|
-
async fn websocket_testing_wrappers_roundtrip_and_message_helpers() {
|
|
83
|
-
let app = axum::Router::new().route(
|
|
84
|
-
"/ws",
|
|
85
|
-
get(|ws: axum::extract::ws::WebSocketUpgrade| async move {
|
|
86
|
-
ws.on_upgrade(|mut socket| async move {
|
|
87
|
-
while let Some(msg) = socket.recv().await {
|
|
88
|
-
match msg {
|
|
89
|
-
Ok(axum::extract::ws::Message::Text(text)) => {
|
|
90
|
-
let _ = socket.send(axum::extract::ws::Message::Text(text)).await;
|
|
91
|
-
}
|
|
92
|
-
Ok(axum::extract::ws::Message::Binary(data)) => {
|
|
93
|
-
let _ = socket.send(axum::extract::ws::Message::Binary(data)).await;
|
|
94
|
-
}
|
|
95
|
-
Ok(axum::extract::ws::Message::Ping(data)) => {
|
|
96
|
-
let _ = socket.send(axum::extract::ws::Message::Pong(data)).await;
|
|
97
|
-
}
|
|
98
|
-
Ok(axum::extract::ws::Message::Close(_)) | Err(_) => break,
|
|
99
|
-
Ok(axum::extract::ws::Message::Pong(_)) => {}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
}),
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
let server = axum_test::TestServer::new_with_config(
|
|
107
|
-
app,
|
|
108
|
-
axum_test::TestServerConfig {
|
|
109
|
-
transport: Some(axum_test::Transport::HttpRandomPort),
|
|
110
|
-
..axum_test::TestServerConfig::default()
|
|
111
|
-
},
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
let mut ws = connect_websocket(&server, "/ws").await;
|
|
115
|
-
|
|
116
|
-
ws.send_text("hi").await;
|
|
117
|
-
let msg = ws.receive_message().await;
|
|
118
|
-
assert_eq!(msg.as_text(), Some("hi"));
|
|
119
|
-
assert!(msg.as_json().is_err());
|
|
120
|
-
|
|
121
|
-
ws.send_message(axum_test::WsMessage::Binary(bytes::Bytes::from_static(b"bin")))
|
|
122
|
-
.await;
|
|
123
|
-
let msg = ws.receive_message().await;
|
|
124
|
-
assert_eq!(msg.as_binary().expect("binary"), b"bin");
|
|
125
|
-
assert!(msg.as_json().is_err());
|
|
126
|
-
|
|
127
|
-
ws.send_message(axum_test::WsMessage::Ping(bytes::Bytes::from_static(b"ping")))
|
|
128
|
-
.await;
|
|
129
|
-
let msg = ws.receive_message().await;
|
|
130
|
-
assert!(matches!(msg, WebSocketMessage::Pong(_)));
|
|
131
|
-
|
|
132
|
-
ws.close().await;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
#[tokio::test]
|
|
136
|
-
async fn snapshot_response_decodes_gzip_body() {
|
|
137
|
-
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
|
|
138
|
-
encoder.write_all(b"hello gzip").expect("write");
|
|
139
|
-
let gzipped = encoder.finish().expect("finish");
|
|
140
|
-
|
|
141
|
-
let app = axum::Router::new().route(
|
|
142
|
-
"/gzip",
|
|
143
|
-
get(move || async move {
|
|
144
|
-
(
|
|
145
|
-
StatusCode::OK,
|
|
146
|
-
[(axum::http::header::CONTENT_ENCODING, "gzip")],
|
|
147
|
-
gzipped.clone(),
|
|
148
|
-
)
|
|
149
|
-
}),
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
let server = axum_test::TestServer::new(app);
|
|
153
|
-
let snapshot = snapshot_response(server.get("/gzip").await).await.expect("snapshot");
|
|
154
|
-
assert_eq!(snapshot.text().expect("text"), "hello gzip");
|
|
155
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
#![allow(clippy::pedantic, clippy::nursery, clippy::all)]
|
|
2
|
-
//! Integration coverage for validate_content_type_middleware with urlencoded bodies.
|
|
3
|
-
|
|
4
|
-
use axum::Router;
|
|
5
|
-
use axum::extract::Extension;
|
|
6
|
-
use axum::http::{HeaderMap, StatusCode};
|
|
7
|
-
use axum::middleware;
|
|
8
|
-
use axum::routing::post;
|
|
9
|
-
use spikard_http::middleware::PreReadBody;
|
|
10
|
-
use spikard_http::middleware::{RouteInfo, validate_content_type_middleware};
|
|
11
|
-
|
|
12
|
-
/// Build a router with the content-type middleware and route configuration.
|
|
13
|
-
fn build_router(route_info: RouteInfo) -> Router {
|
|
14
|
-
Router::new()
|
|
15
|
-
.route(
|
|
16
|
-
"/forms",
|
|
17
|
-
post(
|
|
18
|
-
|headers: HeaderMap, Extension(pre_read): Extension<PreReadBody>| async move {
|
|
19
|
-
let content_type = headers
|
|
20
|
-
.get(axum::http::header::CONTENT_TYPE)
|
|
21
|
-
.and_then(|h| h.to_str().ok())
|
|
22
|
-
.unwrap_or_default()
|
|
23
|
-
.to_string();
|
|
24
|
-
let body_str = String::from_utf8(pre_read.0.to_vec()).unwrap();
|
|
25
|
-
(
|
|
26
|
-
StatusCode::OK,
|
|
27
|
-
axum::Json(serde_json::json!({ "content_type": content_type, "body": body_str })),
|
|
28
|
-
)
|
|
29
|
-
},
|
|
30
|
-
),
|
|
31
|
-
)
|
|
32
|
-
.layer(middleware::from_fn_with_state(
|
|
33
|
-
route_info,
|
|
34
|
-
validate_content_type_middleware,
|
|
35
|
-
))
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
#[tokio::test]
|
|
39
|
-
async fn urlencoded_body_is_transformed_to_json() {
|
|
40
|
-
let app = build_router(RouteInfo {
|
|
41
|
-
expects_json_body: true,
|
|
42
|
-
});
|
|
43
|
-
let server = axum_test::TestServer::new(app);
|
|
44
|
-
|
|
45
|
-
let response = server
|
|
46
|
-
.post("/forms")
|
|
47
|
-
.text("name=alice&active=true&count=3&empty=")
|
|
48
|
-
.content_type("application/x-www-form-urlencoded")
|
|
49
|
-
.await;
|
|
50
|
-
|
|
51
|
-
assert_eq!(response.status_code(), StatusCode::OK);
|
|
52
|
-
let payload: serde_json::Value = response.json();
|
|
53
|
-
assert_eq!(payload["content_type"], "application/json");
|
|
54
|
-
|
|
55
|
-
let body_json: serde_json::Value =
|
|
56
|
-
serde_json::from_str(payload["body"].as_str().expect("body string")).expect("valid json");
|
|
57
|
-
assert_eq!(body_json["name"], "alice");
|
|
58
|
-
assert_eq!(body_json["active"], true);
|
|
59
|
-
assert_eq!(body_json["count"], 3);
|
|
60
|
-
assert_eq!(body_json["empty"], "");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
#[tokio::test]
|
|
64
|
-
async fn invalid_charset_on_json_returns_unsupported_media_type() {
|
|
65
|
-
let app = build_router(RouteInfo {
|
|
66
|
-
expects_json_body: true,
|
|
67
|
-
});
|
|
68
|
-
let server = axum_test::TestServer::new(app);
|
|
69
|
-
|
|
70
|
-
let response = server
|
|
71
|
-
.post("/forms")
|
|
72
|
-
.text("{\"name\":\"alice\"}")
|
|
73
|
-
.content_type("application/json; charset=utf-16")
|
|
74
|
-
.await;
|
|
75
|
-
|
|
76
|
-
assert_eq!(response.status_code(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
|
|
77
|
-
let body: serde_json::Value = response.json();
|
|
78
|
-
assert_eq!(
|
|
79
|
-
body["type"],
|
|
80
|
-
serde_json::Value::String("https://spikard.dev/errors/unsupported-charset".to_string())
|
|
81
|
-
);
|
|
82
|
-
}
|