Skip to content

Commit 4fd1999

Browse files
committed
Add testcase
1 parent 8cc8dc9 commit 4fd1999

File tree

1 file changed

+123
-14
lines changed
  • crates/vespera_macro/src

1 file changed

+123
-14
lines changed

crates/vespera_macro/src/lib.rs

Lines changed: 123 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,26 @@ fn validate_route_fn(item_fn: &syn::ItemFn) -> Result<(), syn::Error> {
4242
Ok(())
4343
}
4444

45+
/// Process route attribute - extracted for testability
46+
fn process_route_attribute(
47+
attr: proc_macro2::TokenStream,
48+
item: proc_macro2::TokenStream,
49+
) -> syn::Result<proc_macro2::TokenStream> {
50+
syn::parse2::<args::RouteArgs>(attr)?;
51+
let item_fn: syn::ItemFn = syn::parse2(item.clone()).map_err(|e| {
52+
syn::Error::new(e.span(), "route attribute can only be applied to functions")
53+
})?;
54+
validate_route_fn(&item_fn)?;
55+
Ok(item)
56+
}
57+
4558
/// route attribute macro
4659
#[proc_macro_attribute]
4760
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
48-
if let Err(e) = syn::parse::<args::RouteArgs>(attr) {
49-
return e.to_compile_error().into();
50-
}
51-
let item_fn = match syn::parse::<syn::ItemFn>(item.clone()) {
52-
Ok(f) => f,
53-
Err(e) => {
54-
return syn::Error::new(e.span(), "route attribute can only be applied to functions")
55-
.to_compile_error()
56-
.into();
57-
}
58-
};
59-
if let Err(e) = validate_route_fn(&item_fn) {
60-
return e.to_compile_error().into();
61+
match process_route_attribute(attr.into(), item.into()) {
62+
Ok(tokens) => tokens.into(),
63+
Err(e) => e.to_compile_error().into(),
6164
}
62-
item
6365
}
6466

6567
// Schema Storage global variable
@@ -2428,4 +2430,111 @@ pub fn get_users() -> String {
24282430
assert_eq!(input.name.to_string(), "MyApp");
24292431
assert_eq!(input.dir.unwrap().value(), "api");
24302432
}
2433+
2434+
// ========== Tests for process_route_attribute ==========
2435+
2436+
#[test]
2437+
fn test_process_route_attribute_valid() {
2438+
let attr = quote::quote!(get);
2439+
let item = quote::quote!(
2440+
pub async fn handler() -> String {
2441+
"ok".to_string()
2442+
}
2443+
);
2444+
let result = process_route_attribute(attr, item.clone());
2445+
assert!(result.is_ok());
2446+
// Should return the original item unchanged
2447+
assert_eq!(result.unwrap().to_string(), item.to_string());
2448+
}
2449+
2450+
#[test]
2451+
fn test_process_route_attribute_invalid_attr() {
2452+
let attr = quote::quote!(invalid_method);
2453+
let item = quote::quote!(
2454+
pub async fn handler() -> String {
2455+
"ok".to_string()
2456+
}
2457+
);
2458+
let result = process_route_attribute(attr, item);
2459+
assert!(result.is_err());
2460+
}
2461+
2462+
#[test]
2463+
fn test_process_route_attribute_not_function() {
2464+
let attr = quote::quote!(get);
2465+
let item = quote::quote!(
2466+
struct NotAFunction;
2467+
);
2468+
let result = process_route_attribute(attr, item);
2469+
assert!(result.is_err());
2470+
let err = result.unwrap_err().to_string();
2471+
assert!(err.contains("can only be applied to functions"));
2472+
}
2473+
2474+
#[test]
2475+
fn test_process_route_attribute_not_public() {
2476+
let attr = quote::quote!(get);
2477+
let item = quote::quote!(
2478+
async fn private_handler() -> String {
2479+
"ok".to_string()
2480+
}
2481+
);
2482+
let result = process_route_attribute(attr, item);
2483+
assert!(result.is_err());
2484+
let err = result.unwrap_err().to_string();
2485+
assert!(err.contains("must be public"));
2486+
}
2487+
2488+
#[test]
2489+
fn test_process_route_attribute_not_async() {
2490+
let attr = quote::quote!(get);
2491+
let item = quote::quote!(
2492+
pub fn sync_handler() -> String {
2493+
"ok".to_string()
2494+
}
2495+
);
2496+
let result = process_route_attribute(attr, item);
2497+
assert!(result.is_err());
2498+
let err = result.unwrap_err().to_string();
2499+
assert!(err.contains("must be async"));
2500+
}
2501+
2502+
#[test]
2503+
fn test_process_route_attribute_with_path() {
2504+
let attr = quote::quote!(get, path = "/users/{id}");
2505+
let item = quote::quote!(
2506+
pub async fn get_user() -> String {
2507+
"user".to_string()
2508+
}
2509+
);
2510+
let result = process_route_attribute(attr, item);
2511+
assert!(result.is_ok());
2512+
}
2513+
2514+
#[test]
2515+
fn test_process_route_attribute_with_tags() {
2516+
let attr = quote::quote!(post, tags = ["users", "admin"]);
2517+
let item = quote::quote!(
2518+
pub async fn create_user() -> String {
2519+
"created".to_string()
2520+
}
2521+
);
2522+
let result = process_route_attribute(attr, item);
2523+
assert!(result.is_ok());
2524+
}
2525+
2526+
#[test]
2527+
fn test_process_route_attribute_all_methods() {
2528+
let methods = ["get", "post", "put", "patch", "delete", "head", "options"];
2529+
for method in methods {
2530+
let attr: proc_macro2::TokenStream = method.parse().unwrap();
2531+
let item = quote::quote!(
2532+
pub async fn handler() -> String {
2533+
"ok".to_string()
2534+
}
2535+
);
2536+
let result = process_route_attribute(attr, item);
2537+
assert!(result.is_ok(), "Method {} should be valid", method);
2538+
}
2539+
}
24312540
}

0 commit comments

Comments
 (0)