Skip to content

Commit 8dec59a

Browse files
committed
reallife tests
1 parent 8d61993 commit 8dec59a

File tree

23 files changed

+528
-8
lines changed

23 files changed

+528
-8
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
shell: bash
4444
run: |
4545
cargo install cargo-tarpaulin
46-
cargo tarpaulin --out Xml --output-dir .
46+
cargo tarpaulin --workspace --all-targets --out Xml --output-dir .
4747
ls -la cobertura.xml
4848
4949
# Upload coverage to Codecov

examples/custom_schema/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ rocket_okapi = { path = "../../rocket-okapi", features = [
1111
"rapidoc",
1212
] }
1313
serde = { workspace = true, features = ["derive"] }
14-
serde_json = { workspace = true }
1514
schemars = "1.1.0"
15+
16+
serde_json = "1.0"

examples/custom_schema/src/main.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ fn custom_openapi_spec() -> OpenApi {
135135
#[cfg(test)]
136136
mod tests {
137137
use super::*;
138+
use rocket::http::Status;
139+
use rocket::local::asynchronous::Client;
138140
use rocket_okapi::openapi_get_spec;
141+
use serde_json::Value;
139142

140143
#[test]
141144
fn custom_spec_contains_home_path() {
@@ -150,4 +153,46 @@ mod tests {
150153
let spec = rocket_okapi::openapi_get_spec![settings: post::create_post, post::get_post];
151154
assert!(spec.paths.keys().any(|k| k.contains("/")));
152155
}
156+
157+
async fn fetch_openapi_spec(client: &Client, path: &str) -> Value {
158+
let response = client.get(path).dispatch().await;
159+
assert_eq!(response.status(), Status::Ok);
160+
let body = response.into_string().await.expect("body string");
161+
serde_json::from_str(&body).expect("valid json")
162+
}
163+
164+
#[rocket::async_test]
165+
async fn server_provides_openapi_json_v1_and_routes_match() {
166+
// Start the server using the factory from this example
167+
let rocket = create_server();
168+
let client = Client::tracked(rocket).await.expect("valid client");
169+
170+
// Request the generated OpenAPI JSON that the example mounts to /v1/openapi.json
171+
let spec = fetch_openapi_spec(&client, "/v1/openapi.json").await;
172+
173+
// Quick sanity checks
174+
assert!(spec["openapi"].is_string());
175+
assert!(spec["paths"].is_object());
176+
177+
// Helper: checks that every documented path is present in Rocket's mounted routes
178+
let paths = spec["paths"].as_object().unwrap();
179+
for path in paths.keys() {
180+
// If the path is from an external custom spec, it's valid in OpenAPI but not a Rocket route
181+
if path.starts_with("/external") {
182+
continue;
183+
}
184+
// Convert OpenAPI style path (e.g. /user/{id}) to Rocket style (/user/<id>)
185+
let rocket_style = path.replace('{', "<").replace('}', ">");
186+
let rocket_style_alt = rocket_style.replace('>', "..>");
187+
let found = client.rocket().routes().any(|r| {
188+
r.uri.to_string().contains(&rocket_style)
189+
|| r.uri.to_string().contains(&rocket_style_alt)
190+
});
191+
assert!(
192+
found,
193+
"OpenApi path '{}' not found among Rocket routes",
194+
path
195+
);
196+
}
197+
}
153198
}

examples/dyn_templates/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ rocket_okapi = { path = "../../rocket-okapi", features = [
1515
serde = { workspace = true, features = ["derive"] }
1616
rocket_dyn_templates = { workspace = true, features = ["handlebars"] }
1717
handlebars = "5.0.0"
18+
19+
[dev-dependencies]
20+
serde_json = "1.0"

examples/dyn_templates/src/main.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,48 @@ async fn main() {
5050
#[cfg(test)]
5151
mod tests {
5252
use super::*;
53+
use rocket::http::Status;
54+
use rocket::local::asynchronous::Client;
5355
use rocket_okapi::openapi_get_spec;
56+
use serde_json::Value;
5457

5558
#[test]
5659
fn spec_contains_page_route() {
5760
let spec = openapi_get_spec![get_page];
5861
assert!(spec.paths.keys().any(|k| k.contains("/page")));
5962
}
63+
64+
async fn fetch_openapi_spec(client: &Client, path: &str) -> Value {
65+
let response = client.get(path).dispatch().await;
66+
assert_eq!(response.status(), Status::Ok);
67+
let body = response.into_string().await.expect("body string");
68+
serde_json::from_str(&body).expect("valid json")
69+
}
70+
71+
#[rocket::async_test]
72+
async fn server_provides_openapi_json_and_matches_routes() {
73+
let rocket = rocket::build()
74+
.mount("/", openapi_get_routes![get_page])
75+
.attach(Template::fairing());
76+
let client = Client::tracked(rocket).await.expect("client");
77+
let spec = fetch_openapi_spec(&client, "/openapi.json").await;
78+
assert!(spec["paths"]
79+
.as_object()
80+
.unwrap()
81+
.keys()
82+
.any(|k| k.contains("/page")));
83+
for path in spec["paths"].as_object().unwrap().keys() {
84+
let rocket_style = path.replace('{', "<").replace('}', ">");
85+
let rocket_style_alt = rocket_style.replace('>', "..>");
86+
let found = client.rocket().routes().any(|r| {
87+
r.uri.to_string().contains(&rocket_style)
88+
|| r.uri.to_string().contains(&rocket_style_alt)
89+
});
90+
assert!(
91+
found,
92+
"OpenApi path '{}' not found among Rocket routes",
93+
path
94+
);
95+
}
96+
}
6097
}

examples/json-web-api/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ rocket_okapi = { path = "../../rocket-okapi", features = [
1212
] }
1313
serde = { workspace = true, features = ["derive"] }
1414
schemars = "1.1.0"
15+
16+
[dev-dependencies]
17+
serde_json = "1.0"

examples/json-web-api/src/main.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,10 @@ async fn main() {
139139
#[cfg(test)]
140140
mod tests {
141141
use super::*;
142+
use rocket::http::Status;
143+
use rocket::local::asynchronous::Client;
142144
use rocket_okapi::openapi_get_spec;
145+
use serde_json::Value;
143146

144147
#[test]
145148
fn generated_spec_contains_user_routes() {
@@ -155,4 +158,87 @@ mod tests {
155158
// The `hidden` endpoint is marked skip
156159
assert!(!spec.paths.keys().any(|k| k.contains("/hidden")));
157160
}
161+
162+
async fn fetch_openapi_spec(client: &Client, path: &str) -> Value {
163+
let response = client.get(path).dispatch().await;
164+
assert_eq!(response.status(), Status::Ok);
165+
let body = response.into_string().await.expect("body string");
166+
serde_json::from_str(&body).expect("valid json")
167+
}
168+
169+
#[rocket::async_test]
170+
async fn server_openapi_contains_and_matches_routes() {
171+
// Rebuild the Rocket instance similarly to `main` so the example's routes are mounted
172+
let rocket = rocket::build()
173+
.mount(
174+
"/",
175+
openapi_get_routes![
176+
get_all_users,
177+
get_user,
178+
get_user_by_name,
179+
create_user,
180+
hidden,
181+
create_post_by_query,
182+
],
183+
)
184+
.mount(
185+
"/swagger-ui/",
186+
make_swagger_ui(&SwaggerUIConfig {
187+
url: "../openapi.json".to_owned(),
188+
..Default::default()
189+
}),
190+
)
191+
.mount(
192+
"/rapidoc/",
193+
make_rapidoc(&RapiDocConfig {
194+
general: GeneralConfig {
195+
spec_urls: vec![UrlObject::new("General", "../openapi.json")],
196+
..Default::default()
197+
},
198+
hide_show: HideShowConfig {
199+
allow_spec_url_load: false,
200+
allow_spec_file_load: false,
201+
..Default::default()
202+
},
203+
..Default::default()
204+
}),
205+
);
206+
207+
let client = Client::tracked(rocket).await.expect("client");
208+
let spec = fetch_openapi_spec(&client, "/openapi.json").await;
209+
210+
// The spec should include the user related route paths
211+
assert!(spec["paths"]
212+
.as_object()
213+
.unwrap()
214+
.keys()
215+
.any(|k| k.contains("/user")));
216+
assert!(spec["paths"]
217+
.as_object()
218+
.unwrap()
219+
.keys()
220+
.any(|k| k.contains("post_by_query")));
221+
// And the `hidden` route should be omitted
222+
assert!(!spec["paths"]
223+
.as_object()
224+
.unwrap()
225+
.keys()
226+
.any(|k| k.contains("/hidden")));
227+
228+
// Cross-check: every documented path should match a Rocket route
229+
let paths = spec["paths"].as_object().unwrap();
230+
for path in paths.keys() {
231+
let rocket_style = path.replace('{', "<").replace('}', ">");
232+
let rocket_style_alt = rocket_style.replace('>', "..>");
233+
let found = client.rocket().routes().any(|r| {
234+
r.uri.to_string().contains(&rocket_style)
235+
|| r.uri.to_string().contains(&rocket_style_alt)
236+
});
237+
assert!(
238+
found,
239+
"OpenApi path '{}' not found among Rocket routes",
240+
path
241+
);
242+
}
243+
}
158244
}

examples/nested/Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ name = "nested"
33
version = "0.1.0"
44
authors = [
55
"Maxime Borges <[email protected]>",
6-
"Ralph Bisschops <ralph.bisschops[email protected]>",
6+
"Ralph Bisschops <ralph.bissops[email protected]>",
77
]
88
edition = "2021"
99

1010
[dependencies]
1111
rocket = { workspace = true }
1212
rocket_okapi = { path = "../../rocket-okapi", features = ["rapidoc"] }
1313
serde = { workspace = true, features = ["derive"] }
14-
serde_json = { workspace = true }
14+
serde_json = "1.0"
1515
schemars = "1.1.0"
16+
17+
[dev-dependencies]
18+
serde_json = "1.0"

examples/nested/src/main.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,45 @@ fn custom_openapi_spec() -> OpenApi {
125125
#[cfg(test)]
126126
mod tests {
127127
use super::*;
128-
use rocket_okapi::openapi_get_spec;
128+
use rocket::http::Status;
129+
use rocket::local::asynchronous::Client;
129130
use rocket_okapi::settings::OpenApiSettings;
131+
use serde_json::Value;
130132

131133
#[test]
132134
fn nested_api_spec_contains_paths() {
133135
let settings = OpenApiSettings::default();
134136
let (_routes, spec) = api::get_routes_and_docs(&settings);
135137
assert!(spec.paths.keys().any(|k| k.contains("/")));
136138
}
139+
140+
async fn fetch_openapi_spec(client: &Client, path: &str) -> Value {
141+
let response = client.get(path).dispatch().await;
142+
assert_eq!(response.status(), Status::Ok);
143+
let body = response.into_string().await.expect("body string");
144+
serde_json::from_str(&body).expect("valid json")
145+
}
146+
147+
#[rocket::async_test]
148+
async fn server_provides_v1_openapi_and_matches_routes() {
149+
let rocket = create_server();
150+
let client = Client::tracked(rocket).await.expect("client");
151+
let spec = fetch_openapi_spec(&client, "/v1/openapi.json").await;
152+
assert!(spec["paths"].is_object());
153+
for path in spec["paths"].as_object().unwrap().keys() {
154+
if path.starts_with("/external") {
155+
continue;
156+
}
157+
let rocket_style = path.replace('{', "<").replace('}', ">");
158+
let found = client
159+
.rocket()
160+
.routes()
161+
.any(|r| r.uri.to_string().contains(&rocket_style));
162+
assert!(
163+
found,
164+
"OpenApi path '{}' not found among Rocket routes",
165+
path
166+
);
167+
}
168+
}
137169
}

examples/openapi_attributes/Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,12 @@ edition = "2021"
66

77
[dependencies]
88
rocket = { workspace = true }
9-
rocket_okapi = { path = "../../rocket-okapi", features = ["swagger", "rapidoc"] }
9+
rocket_okapi = { path = "../../rocket-okapi", features = [
10+
"swagger",
11+
"rapidoc",
12+
] }
1013
serde = { workspace = true }
14+
schemars = "1.1.0"
15+
16+
[dev-dependencies]
17+
serde_json = "1.0"

0 commit comments

Comments
 (0)