diff --git a/.changepacks/changepack_log_HkIOQGN5zIEAUT5Au3KyO.json b/.changepacks/changepack_log_HkIOQGN5zIEAUT5Au3KyO.json new file mode 100644 index 0000000..a4c8e36 --- /dev/null +++ b/.changepacks/changepack_log_HkIOQGN5zIEAUT5Au3KyO.json @@ -0,0 +1 @@ +{"changes":{"Cargo.toml":"Patch"},"note":"Optimize","date":"2026-02-17T19:54:11.651834500Z"} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 50c6ecb..e6e1db4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3199,7 +3199,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vespera" -version = "0.1.34" +version = "0.1.33" dependencies = [ "axum", "axum-extra", @@ -3215,7 +3215,7 @@ dependencies = [ [[package]] name = "vespera_core" -version = "0.1.34" +version = "0.1.33" dependencies = [ "rstest", "serde", @@ -3224,7 +3224,7 @@ dependencies = [ [[package]] name = "vespera_macro" -version = "0.1.34" +version = "0.1.33" dependencies = [ "insta", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 58b5dbd..d255670 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,15 +3,15 @@ resolver = "2" members = ["crates/*", "examples/*"] [workspace.package] -version = "0.1.34" +version = "0.1.33" edition = "2024" license = "Apache-2.0" repository = "https://github.com/dev-five-git/vespera" readme = "README.md" [workspace.dependencies] -vespera_core = { path = "crates/vespera_core", version = "0.1.34" } -vespera_macro = { path = "crates/vespera_macro", version = "0.1.34" } +vespera_core = { path = "crates/vespera_core", version = "0.1.33" } +vespera_macro = { path = "crates/vespera_macro", version = "0.1.33" } [workspace.lints.clippy] all = { level = "warn", priority = -1 } diff --git a/crates/vespera_macro/src/lib.rs b/crates/vespera_macro/src/lib.rs index 2af084a..ece5fba 100644 --- a/crates/vespera_macro/src/lib.rs +++ b/crates/vespera_macro/src/lib.rs @@ -283,12 +283,7 @@ pub fn export_app(input: TokenStream) -> TokenStream { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") else { - return syn::Error::new( - proc_macro2::Span::call_site(), - "export_app! macro: CARGO_MANIFEST_DIR is not set. This macro must be used within a cargo build.", - ) - .to_compile_error() - .into(); + return syn::Error::new(proc_macro2::Span::call_site(), "export_app! macro: CARGO_MANIFEST_DIR is not set. This macro must be used within a cargo build.").to_compile_error().into(); }; match process_export_app(&name, &folder_name, &schema_storage, &manifest_dir) { diff --git a/crates/vespera_macro/src/openapi_generator.rs b/crates/vespera_macro/src/openapi_generator.rs index 6f0910e..ef6abd2 100644 --- a/crates/vespera_macro/src/openapi_generator.rs +++ b/crates/vespera_macro/src/openapi_generator.rs @@ -1346,6 +1346,98 @@ pub fn get_user() -> User { assert!(value.is_none()); } + #[test] + fn test_build_path_items_unknown_http_method() { + // Test lines 131-134: route with unknown HTTP method is skipped + let temp_dir = TempDir::new().expect("Failed to create temp dir"); + + let route_content = r#" +pub fn get_users() -> String { + "users".to_string() +} +"#; + let route_file = create_temp_file(&temp_dir, "users.rs", route_content); + + let mut metadata = CollectedMetadata::new(); + metadata.routes.push(RouteMetadata { + method: "INVALID".to_string(), + path: "/users".to_string(), + function_name: "get_users".to_string(), + module_path: "test::users".to_string(), + file_path: route_file.to_string_lossy().to_string(), + signature: "fn get_users() -> String".to_string(), + error_status: None, + tags: None, + description: None, + }); + + let doc = generate_openapi_doc_with_metadata(None, None, None, &metadata); + + // Route with unknown HTTP method should be skipped entirely + assert!( + doc.paths.is_empty(), + "Route with unknown HTTP method should be skipped" + ); + } + + #[test] + fn test_build_path_items_unknown_method_skipped_valid_kept() { + // Test that unknown methods are skipped while valid routes are kept + let temp_dir = TempDir::new().expect("Failed to create temp dir"); + + let route_content = r#" +pub fn get_users() -> String { + "users".to_string() +} + +pub fn create_users() -> String { + "created".to_string() +} +"#; + let route_file = create_temp_file(&temp_dir, "users.rs", route_content); + let file_path = route_file.to_string_lossy().to_string(); + + let mut metadata = CollectedMetadata::new(); + // Invalid method route + metadata.routes.push(RouteMetadata { + method: "CONNECT".to_string(), + path: "/users".to_string(), + function_name: "get_users".to_string(), + module_path: "test::users".to_string(), + file_path: file_path.clone(), + signature: "fn get_users() -> String".to_string(), + error_status: None, + tags: None, + description: None, + }); + // Valid method route + metadata.routes.push(RouteMetadata { + method: "POST".to_string(), + path: "/users".to_string(), + function_name: "create_users".to_string(), + module_path: "test::users".to_string(), + file_path, + signature: "fn create_users() -> String".to_string(), + error_status: None, + tags: None, + description: None, + }); + + let doc = generate_openapi_doc_with_metadata(None, None, None, &metadata); + + // Only the valid POST route should appear + assert_eq!(doc.paths.len(), 1); + let path_item = doc.paths.get("/users").unwrap(); + assert!( + path_item.post.is_some(), + "Valid POST route should be present" + ); + assert!( + path_item.get.is_none(), + "Invalid method route should be skipped" + ); + } + #[test] fn test_generate_openapi_with_unparseable_definition() { // Test line 42: syn::parse_str fails with invalid Rust syntax diff --git a/crates/vespera_macro/src/router_codegen.rs b/crates/vespera_macro/src/router_codegen.rs index 65d1b1f..9370fee 100644 --- a/crates/vespera_macro/src/router_codegen.rs +++ b/crates/vespera_macro/src/router_codegen.rs @@ -36,10 +36,9 @@ use proc_macro2::Span; use quote::quote; use syn::{ - bracketed, + LitStr, bracketed, parse::{Parse, ParseStream}, punctuated::Punctuated, - LitStr, }; use vespera_core::{openapi::Server, route::HttpMethod}; @@ -1262,6 +1261,87 @@ pub fn get_users() -> String { assert!(result.is_err()); } + #[test] + fn test_generate_router_code_unknown_http_method() { + // Test lines 337-340: route with unknown HTTP method is skipped in router codegen + let mut metadata = CollectedMetadata { + routes: Vec::new(), + structs: Vec::new(), + }; + metadata.routes.push(crate::metadata::RouteMetadata { + method: "INVALID".to_string(), + path: "/users".to_string(), + function_name: "get_users".to_string(), + module_path: "routes::users".to_string(), + file_path: "dummy.rs".to_string(), + signature: "fn get_users() -> String".to_string(), + error_status: None, + tags: None, + description: None, + }); + + let result = generate_router_code(&metadata, None, None, &[]); + let code = result.to_string(); + + // Router should be generated but without any route calls + assert!( + code.contains("Router") && code.contains("new"), + "Code should contain Router::new(), got: {code}" + ); + assert!( + !code.contains(". route ("), + "Route with unknown HTTP method should be skipped, got: {code}" + ); + } + + #[test] + fn test_generate_router_code_unknown_method_skipped_valid_kept() { + // Test that unknown methods are skipped while valid routes are still generated + let temp_dir = TempDir::new().expect("Failed to create temp dir"); + let folder_name = "routes"; + + create_temp_file( + &temp_dir, + "users.rs", + r#" +#[route(get)] +pub fn get_users() -> String { + "users".to_string() +} +"#, + ); + + let mut metadata = collect_metadata(temp_dir.path(), folder_name).unwrap(); + // Inject an additional route with invalid method + metadata.routes.push(crate::metadata::RouteMetadata { + method: "CONNECT".to_string(), + path: "/invalid".to_string(), + function_name: "connect_handler".to_string(), + module_path: "routes::invalid".to_string(), + file_path: "dummy.rs".to_string(), + signature: "fn connect_handler() -> String".to_string(), + error_status: None, + tags: None, + description: None, + }); + + let result = generate_router_code(&metadata, None, None, &[]); + let code = result.to_string(); + + // Valid route should be present + assert!( + code.contains("get_users"), + "Valid route should be present, got: {code}" + ); + // Invalid route should be skipped + assert!( + !code.contains("connect_handler"), + "Invalid method route should be skipped, got: {code}" + ); + + drop(temp_dir); + } + #[test] fn test_auto_router_input_parse_invalid_token() { // Test line 149: neither ident nor string literal triggers lookahead error diff --git a/crates/vespera_macro/src/schema_impl.rs b/crates/vespera_macro/src/schema_impl.rs index 99ecc7a..9dff737 100644 --- a/crates/vespera_macro/src/schema_impl.rs +++ b/crates/vespera_macro/src/schema_impl.rs @@ -190,7 +190,6 @@ mod tests { assert_eq!(metadata.name, "Container"); } - #[test] fn test_extract_schema_name_attr_non_name_meta_key() { // #[schema(other = "foo")] — has schema attr but no "name" key