#![feature(custom_attribute, param_attrs)] #![allow(unused_attributes)] use interfacer_http::{ http::{header::CONTENT_TYPE, header::COOKIE, Request, Response}, http_service, mime, mock::{Client, Error}, url::Url, ContentInto, ToContent, }; use serde_derive::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct User { name: String, age: i32, } const MOCK_BASE_URL: &str = "https://mock.rs"; const DEFAULT_COOKIE: &str = "cookie=cookie"; #[rustfmt::skip] #[http_service] trait UserService { type Error; #[options] async fn ping(&self) -> Result, Self::Error>; #[get("/api/user/{id}")] #[expect(200, mime::APPLICATION_JSON)] async fn get_user(&self, id: u64) -> Result, Self::Error>; #[get("/api/user?age_max={age_max}")] #[expect(200, mime::APPLICATION_JSON)] async fn get_users(&self, age_max: u8) -> Result>, Self::Error>; #[put("/api/user/{id}", mime::APPLICATION_JSON)] #[expect(200, "application/json")] async fn put_user( &self, id: u64, #[body] user: &User, #[header(COOKIE)] cookie: &str, ) -> Result, Self::Error>; #[post("/api/user", mime::APPLICATION_WWW_FORM_URLENCODED)] #[expect(201, mime::APPLICATION_MSGPACK)] async fn post_user( &self, #[body] user: &User, #[header(COOKIE)] cookie: &str, ) -> Result, Self::Error>; } async fn ping_handler(req: Request>) -> Result>, Error> { assert_eq!(Url::parse(MOCK_BASE_URL)?.join("/")?.as_str(), req.uri()); assert_eq!("OPTIONS", req.method()); Ok(Response::builder().status(200).body(Vec::new())?) } #[tokio::test] async fn test_ping() -> Result<(), Error> { let service = Client::new(MOCK_BASE_URL.parse()?, ping_handler); let resp = service.ping().await?; assert_eq!(200, resp.status()); Ok(()) } async fn get_user_handler(req: Request>) -> Result>, Error> { assert_eq!( Url::parse(MOCK_BASE_URL)?.join("/api/user/0")?.as_str(), req.uri() ); assert_eq!("GET", req.method()); Ok(Response::builder() .status(200) .header(CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) .body( User { name: "hexi".to_string(), age: 20, } .to_content(&mime::APPLICATION_JSON)?, )?) } #[tokio::test] async fn test_get_user() -> Result<(), Error> { let service = Client::new(MOCK_BASE_URL.parse()?, get_user_handler); let resp = service.get_user(0).await?; assert_eq!(200, resp.status()); assert_eq!( &User { name: "hexi".to_string(), age: 20, }, resp.body() ); Ok(()) } async fn get_users_handler(req: Request>) -> Result>, Error> { assert_eq!( Url::parse(MOCK_BASE_URL)? .join("/api/user?age_max=40")? .as_str(), req.uri() ); assert_eq!("GET", req.method()); Ok(Response::builder() .status(200) .header(CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) .body( vec![ User { name: "hexi".to_string(), age: 20, }, User { name: "boss".to_string(), age: 35, }, ] .to_content(&mime::APPLICATION_JSON)?, )?) } #[tokio::test] async fn test_get_users() -> Result<(), Error> { let service = Client::new(MOCK_BASE_URL.parse()?, get_users_handler); let resp = service.get_users(40).await?; assert_eq!(200, resp.status()); assert_eq!( &vec![ User { name: "hexi".to_string(), age: 20, }, User { name: "boss".to_string(), age: 35, }, ], resp.body() ); Ok(()) } async fn put_user_handler(req: Request>) -> Result>, Error> { assert_eq!( Url::parse(MOCK_BASE_URL)?.join("/api/user/0")?.as_str(), req.uri() ); assert_eq!("PUT", req.method()); assert_eq!( mime::APPLICATION_JSON, req.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap() ); assert_eq!( DEFAULT_COOKIE, req.headers().get(COOKIE).unwrap().to_str().unwrap() ); let user: User = req.into_body().content_into(&mime::APPLICATION_JSON)?; Ok(Response::builder() .status(200) .header(CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) .body(user.to_content(&mime::APPLICATION_JSON)?)?) } #[tokio::test] async fn test_put_user() -> Result<(), Error> { let service = Client::new(MOCK_BASE_URL.parse()?, put_user_handler); let user = User { name: "hexi".to_string(), age: 20, }; let resp = service.put_user(0, &user, DEFAULT_COOKIE).await?; assert_eq!(200, resp.status()); assert_eq!(&user, resp.body()); Ok(()) } async fn post_user_handler(req: Request>) -> Result>, Error> { assert_eq!( Url::parse(MOCK_BASE_URL)?.join("/api/user")?.as_str(), req.uri() ); assert_eq!("POST", req.method()); assert_eq!( mime::APPLICATION_WWW_FORM_URLENCODED, req.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap() ); assert_eq!( DEFAULT_COOKIE, req.headers().get(COOKIE).unwrap().to_str().unwrap() ); let user: User = req .into_body() .content_into(&mime::APPLICATION_WWW_FORM_URLENCODED)?; Ok(Response::builder() .status(201) .header(CONTENT_TYPE, mime::APPLICATION_MSGPACK.as_ref()) .body(user.to_content(&mime::APPLICATION_MSGPACK)?)?) } #[tokio::test] async fn test_post_user() -> Result<(), Error> { let service = Client::new(MOCK_BASE_URL.parse()?, post_user_handler); let user = User { name: "hexi".to_string(), age: 20, }; let resp = service.post_user(&user, DEFAULT_COOKIE).await?; assert_eq!(201, resp.status()); assert_eq!(&user, resp.body()); Ok(()) }