Skip to content

Commit 37d8430

Browse files
committed
testing the ui
1 parent c7438d8 commit 37d8430

File tree

7 files changed

+270
-4
lines changed

7 files changed

+270
-4
lines changed

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ redoc = ["dep:utoipa-redoc"]
1616
scalar = ["dep:utoipa-scalar"]
1717

1818

19+
[workspace.dependencies]
20+
loco-rs = { git = "https://github.com/loco-rs/loco", default-features = false }
21+
1922
[dependencies]
20-
loco-rs = { git = "https://github.com/loco-rs/loco" }
23+
loco-rs = { workspace = true }
2124
serde = { version = "1", features = ["derive"] }
2225
serde_json = { version = "1" }
2326
async-trait = { version = "0.1" }
@@ -34,5 +37,6 @@ utoipa-redoc = { version = "6.0.0", features = ["axum"], optional = true }
3437
utoipa-scalar = { version = "0.3.0", features = ["axum"], optional = true }
3538

3639
[dev-dependencies]
37-
rstest = { version = "0.21.0" }
40+
loco-rs = { workspace = true, features = ["testing"] }
41+
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
3842
insta = { version = "1.34.0", features = ["redactions", "yaml", "filters"] }

src/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub fn get_openapi_config() -> Option<&'static OpenAPIConfig> {
6464
/// # spec_yaml_url: /api-docs/openapi.yaml
6565
/// ```
6666
#[derive(Debug, Clone, Deserialize, Serialize)]
67-
#[cfg_attr(test, derive(PartialEq))]
67+
#[cfg_attr(test, derive(PartialEq, Eq))]
6868
pub struct OpenAPIConfig {
6969
/// Redoc configuration
7070
/// Example:
@@ -101,7 +101,7 @@ pub struct OpenAPIConfig {
101101

102102
/// `OpenAPI` configuration types
103103
#[derive(Debug, Clone, Deserialize, Serialize)]
104-
#[cfg_attr(test, derive(PartialEq))]
104+
#[cfg_attr(test, derive(PartialEq, Eq))]
105105
pub enum OpenAPIType {
106106
/// Redoc configuration
107107
/// Example:

tests/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#[cfg(feature = "full")]
2+
mod ui;

tests/ui/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod reqeust;

tests/ui/reqeust.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
use async_trait::async_trait;
2+
use loco_openapi::prelude::routes;
3+
use loco_openapi::{
4+
auth::{set_jwt_location, SecurityAddon},
5+
prelude::openapi, // Make sure openapi macro is imported
6+
};
7+
use loco_rs::{
8+
app::{AppContext, Hooks, Initializer},
9+
boot::{create_app, BootResult, StartMode},
10+
config::Config,
11+
controller::AppRoutes,
12+
environment::Environment,
13+
prelude::*,
14+
task::Tasks,
15+
};
16+
use serde::Serialize; // Added import for Album
17+
use serde_json::{json, Value};
18+
use std::collections::BTreeMap;
19+
use utoipa::{OpenApi, ToSchema}; // Added ToSchema
20+
// Define a minimal TestApp
21+
use insta::assert_snapshot;
22+
struct TestApp;
23+
24+
// --- Start: Embedded Album Controller ---
25+
mod album {
26+
use super::*; // Allow using imports from parent module
27+
use axum::debug_handler;
28+
use axum::routing::get;
29+
30+
#[derive(Serialize, Debug, ToSchema)]
31+
pub struct Album {
32+
title: String,
33+
rating: u32,
34+
}
35+
36+
/// Get album
37+
///
38+
/// Returns a title and rating
39+
#[utoipa::path(
40+
get,
41+
path = "/api/album/get_album",
42+
tags = ["album"],
43+
responses(
44+
(status = 200, description = "Album found", body = Album),
45+
),
46+
)]
47+
#[debug_handler]
48+
pub async fn get_album(State(_ctx): State<AppContext>) -> Result<Response> {
49+
format::json(Album {
50+
title: "VH II".to_string(),
51+
rating: 10,
52+
})
53+
}
54+
55+
pub fn routes() -> Routes {
56+
Routes::new()
57+
.prefix("api/album")
58+
.add("/get_album", openapi(get(get_album), routes!(get_album)))
59+
}
60+
}
61+
// --- End: Embedded Album Controller ---
62+
63+
// Helper to create test configuration
64+
fn config_test() -> Config {
65+
let mut config = loco_rs::tests_cfg::config::test_config();
66+
let mut initializers = BTreeMap::new();
67+
let mut openapi_conf = serde_json::Map::new();
68+
69+
// Configure endpoints to match test requests
70+
openapi_conf.insert(
71+
"redoc".to_string(),
72+
json!({
73+
"redoc": {
74+
"url": "/redoc"
75+
}
76+
}),
77+
);
78+
openapi_conf.insert(
79+
"scalar".to_string(),
80+
json!({
81+
"scalar": {
82+
"url": "/scalar"
83+
}
84+
}),
85+
);
86+
openapi_conf.insert(
87+
"swagger".to_string(),
88+
json!({
89+
"swagger": {
90+
"url": "/swagger-ui", // Ensure this matches the test URL
91+
"spec_json_url": "/api-docs/openapi.json" // Required for swagger
92+
}
93+
}),
94+
);
95+
96+
initializers.insert("openapi".to_string(), Value::Object(openapi_conf));
97+
config.initializers = Some(initializers);
98+
config
99+
}
100+
101+
// Implement Hooks for TestApp
102+
#[async_trait]
103+
impl Hooks for TestApp {
104+
fn app_name() -> &'static str {
105+
"loco-openapi-test"
106+
}
107+
fn app_version() -> String {
108+
env!("CARGO_PKG_VERSION").to_string()
109+
}
110+
111+
fn routes(_ctx: &AppContext) -> AppRoutes {
112+
AppRoutes::with_default_routes().add_route(album::routes()) // Add album routes
113+
}
114+
115+
async fn load_config(_environment: &Environment) -> Result<Config> {
116+
Ok(config_test())
117+
}
118+
119+
async fn initializers(_ctx: &AppContext) -> Result<Vec<Box<dyn Initializer>>> {
120+
Ok(vec![Box::new(
121+
loco_openapi::OpenapiInitializerWithSetup::new(
122+
|ctx| {
123+
#[derive(OpenApi)]
124+
#[openapi(
125+
modifiers(&SecurityAddon),
126+
paths(album::get_album), // Add album path to OpenAPI spec
127+
components(schemas(album::Album)), // Add album schema
128+
info(
129+
title = "Loco Demo Test",
130+
description = "Test OpenAPI spec for loco-openapi"
131+
)
132+
)]
133+
struct ApiDoc;
134+
set_jwt_location(ctx.into());
135+
136+
ApiDoc::openapi()
137+
},
138+
None,
139+
),
140+
)])
141+
}
142+
143+
async fn boot(
144+
mode: StartMode,
145+
environment: &Environment,
146+
config: Config,
147+
) -> Result<BootResult> {
148+
// Assuming Migrator is not needed as per previous iteration
149+
create_app::<Self>(mode, environment, config).await
150+
}
151+
152+
async fn connect_workers(_ctx: &AppContext, _queue: &Queue) -> Result<()> {
153+
Ok(())
154+
}
155+
156+
fn register_tasks(_tasks: &mut Tasks) {}
157+
158+
// Removed truncate and seed as they are not part of the Hooks trait
159+
}
160+
161+
// Test for OpenAPI UI Endpoints
162+
#[tokio::test]
163+
async fn test_openapi_ui_endpoints() {
164+
loco_rs::testing::request::request::<TestApp, _, _>(|rq, _ctx| async move {
165+
// Test Redoc endpoint
166+
let res_redoc = rq.get("/redoc").await;
167+
assert_eq!(
168+
res_redoc.status_code(),
169+
200,
170+
"Expected /redoc to return 200 OK: {}",
171+
res_redoc.text()
172+
);
173+
174+
assert_snapshot!("redoc", res_redoc.text());
175+
176+
// Test Scalar endpoint
177+
let res_scalar = rq.get("/scalar").await;
178+
assert_eq!(
179+
res_scalar.status_code(),
180+
200,
181+
"Expected /scalar to return 200 OK: {}",
182+
res_scalar.text()
183+
);
184+
185+
assert_snapshot!("scalar", res_scalar.text());
186+
187+
// Test Swagger UI endpoint (kept commented as per your change)
188+
// let res_swagger = rq.get("/swagger-ui").await;
189+
// assert_eq!(
190+
// res_swagger.status_code(),
191+
// 200,
192+
// "Expected /swagger-ui to return 200 OK: {}",
193+
// res_swagger.text().await // Added .await
194+
// );
195+
196+
// assert_snapshot!("swagger", res_swagger.text());
197+
})
198+
.await;
199+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
source: tests/ui/reqeust.rs
3+
expression: res_redoc.text()
4+
snapshot_kind: text
5+
---
6+
<!DOCTYPE html>
7+
<html>
8+
<head>
9+
<title>Redoc</title>
10+
<meta charset="utf-8" />
11+
<meta name="viewport" content="width=device-width, initial-scale=1" />
12+
<link
13+
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
14+
rel="stylesheet"
15+
/>
16+
17+
<style>
18+
body {
19+
margin: 0;
20+
padding: 0;
21+
}
22+
</style>
23+
</head>
24+
25+
<body>
26+
<div id="redoc-container"></div>
27+
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
28+
<script>
29+
Redoc.init(
30+
{"openapi":"3.1.0","info":{"title":"Loco Demo Test","description":"Test OpenAPI spec for loco-openapi","license":{"name":"MIT OR Apache-2.0","identifier":"MIT OR Apache-2.0"},"version":"0.1.0"},"paths":{"/api/album/get_album":{"get":{"tags":["album"],"summary":"Get album","description":"Returns a title and rating","operationId":"get_album","responses":{"200":{"description":"Album found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Album"}}}}}}}},"components":{"schemas":{"Album":{"type":"object","required":["title","rating"],"properties":{"rating":{"type":"integer","format":"int32","minimum":0},"title":{"type":"string"}}}},"securitySchemes":{"api_key":{"type":"apiKey","in":"header","name":"apikey"},"jwt_token":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}},
31+
{},
32+
document.getElementById("redoc-container")
33+
);
34+
</script>
35+
</body>
36+
</html>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
source: tests/ui/reqeust.rs
3+
expression: res_scalar.text()
4+
snapshot_kind: text
5+
---
6+
<!doctype html>
7+
<html>
8+
<head>
9+
<title>Scalar</title>
10+
<meta charset="utf-8"/>
11+
<meta
12+
name="viewport"
13+
content="width=device-width, initial-scale=1"/>
14+
</head>
15+
<body>
16+
17+
<script
18+
id="api-reference"
19+
type="application/json">
20+
{"openapi":"3.1.0","info":{"title":"Loco Demo Test","description":"Test OpenAPI spec for loco-openapi","license":{"name":"MIT OR Apache-2.0","identifier":"MIT OR Apache-2.0"},"version":"0.1.0"},"paths":{"/api/album/get_album":{"get":{"tags":["album"],"summary":"Get album","description":"Returns a title and rating","operationId":"get_album","responses":{"200":{"description":"Album found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Album"}}}}}}}},"components":{"schemas":{"Album":{"type":"object","required":["title","rating"],"properties":{"rating":{"type":"integer","format":"int32","minimum":0},"title":{"type":"string"}}}},"securitySchemes":{"api_key":{"type":"apiKey","in":"header","name":"apikey"},"jwt_token":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}}
21+
</script>
22+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
23+
</body>
24+
</html>

0 commit comments

Comments
 (0)