1
0
Fork 0
mirror of https://gitlab.com/msrd0/gotham-restful.git synced 2025-02-22 20:52:27 +00:00

a whole bunch of tests for the method macros

This commit is contained in:
Dominic 2020-05-08 18:39:11 +02:00
parent 4bf0bd7b09
commit e05f9bb963
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
23 changed files with 473 additions and 28 deletions

View file

@ -122,13 +122,13 @@ pub fn change(attr : TokenStream, item : TokenStream) -> TokenStream
}
#[proc_macro_attribute]
pub fn delete_all(attr : TokenStream, item : TokenStream) -> TokenStream
pub fn remove_all(attr : TokenStream, item : TokenStream) -> TokenStream
{
expand_macro(attr, item, |attr, item| expand_method(Method::RemoveAll, attr, item))
}
#[proc_macro_attribute]
pub fn delete(attr : TokenStream, item : TokenStream) -> TokenStream
pub fn remove(attr : TokenStream, item : TokenStream) -> TokenStream
{
expand_macro(attr, item, |attr, item| expand_method(Method::Remove, attr, item))
}

View file

@ -150,13 +150,18 @@ impl MethodArgumentType
matches!(self, Self::AuthStatus(_) | Self::AuthStatusRef(_))
}
fn quote_ty(&self) -> Option<TokenStream>
fn ty(&self) -> Option<&Type>
{
match self {
Self::MethodArg(ty) | Self::DatabaseConnection(ty) | Self::AuthStatus(ty) | Self::AuthStatusRef(ty) => Some(quote!(#ty)),
Self::MethodArg(ty) | Self::DatabaseConnection(ty) | Self::AuthStatus(ty) | Self::AuthStatusRef(ty) => Some(ty),
_ => None
}
}
fn quote_ty(&self) -> Option<TokenStream>
{
self.ty().map(|ty| quote!(#ty))
}
}
struct MethodArgument
@ -279,6 +284,10 @@ pub fn expand_method(method : Method, mut attrs : AttributeArgs, fun : ItemFn) -
let krate = super::krate();
// parse attributes
if attrs.len() < 1
{
return Err(Error::new(Span::call_site(), "Missing Resource struct. Example: #[read_all(MyResource)]"));
}
let resource_path = match attrs.remove(0) {
NestedMeta::Meta(Meta::Path(path)) => path,
p => return Err(Error::new(p.span(), "Expected name of the Resource struct this method belongs to"))
@ -372,7 +381,7 @@ pub fn expand_method(method : Method, mut attrs : AttributeArgs, fun : ItemFn) -
{
if let Some(arg) = args.iter().find(|arg| (*arg).ty.is_state_ref())
{
return Err(Error::new(arg.span(), "async fn must not take &State as an argument as State is not Sync"));
return Err(Error::new(arg.span(), "async fn must not take &State as an argument as State is not Sync, consider boxing"));
}
block = quote!(#block.await);
}

View file

@ -76,14 +76,14 @@ fn update(id : u64, body : User)
info!("Change User {} to {}", id, body.username);
}
#[delete_all(Users)]
fn delete_all()
#[remove_all(Users)]
fn remove_all()
{
info!("Delete all Users");
}
#[delete(Users)]
fn delete(id : u64)
#[remove(Users)]
fn remove(id : u64)
{
info!("Delete User {}", id);
}

106
tests/async_methods.rs Normal file
View file

@ -0,0 +1,106 @@
#[macro_use] extern crate gotham_derive;
use gotham::{
router::builder::*,
test::TestServer
};
use gotham_restful::*;
use mime::{APPLICATION_JSON, TEXT_PLAIN};
use serde::Deserialize;
mod util { include!("util/mod.rs"); }
use util::{test_get_response, test_post_response, test_put_response, test_delete_response};
#[derive(Resource)]
#[resource(read_all, read, search, create, change_all, change, remove_all, remove)]
struct FooResource;
#[derive(Deserialize)]
#[cfg_attr(feature = "openapi", derive(OpenapiType))]
#[allow(dead_code)]
struct FooBody
{
data : String
}
#[derive(Deserialize, StateData, StaticResponseExtender)]
#[cfg_attr(feature = "openapi", derive(OpenapiType))]
#[allow(dead_code)]
struct FooSearch
{
query : String
}
const READ_ALL_RESPONSE : &[u8] = b"1ARwwSPVyOKpJKrYwqGgECPVWDl1BqajAAj7g7WJ3e";
#[read_all(FooResource)]
async fn read_all() -> Raw<&'static [u8]>
{
Raw::new(READ_ALL_RESPONSE, TEXT_PLAIN)
}
const READ_RESPONSE : &[u8] = b"FEReHoeBKU17X2bBpVAd1iUvktFL43CDu0cFYHdaP9";
#[read(FooResource)]
async fn read(_id : u64) -> Raw<&'static [u8]>
{
Raw::new(READ_RESPONSE, TEXT_PLAIN)
}
const SEARCH_RESPONSE : &[u8] = b"AWqcQUdBRHXKh3at4u79mdupOAfEbnTcx71ogCVF0E";
#[search(FooResource)]
async fn search(_body : FooSearch) -> Raw<&'static [u8]>
{
Raw::new(SEARCH_RESPONSE, TEXT_PLAIN)
}
const CREATE_RESPONSE : &[u8] = b"y6POY7wOMAB0jBRBw0FJT7DOpUNbhmT8KdpQPLkI83";
#[create(FooResource)]
async fn create(_body : FooBody) -> Raw<&'static [u8]>
{
Raw::new(CREATE_RESPONSE, TEXT_PLAIN)
}
const CHANGE_ALL_RESPONSE : &[u8] = b"QlbYg8gHE9OQvvk3yKjXJLTSXlIrg9mcqhfMXJmQkv";
#[change_all(FooResource)]
async fn change_all(_body : FooBody) -> Raw<&'static [u8]>
{
Raw::new(CHANGE_ALL_RESPONSE, TEXT_PLAIN)
}
const CHANGE_RESPONSE : &[u8] = b"qGod55RUXkT1lgPO8h0uVM6l368O2S0GrwENZFFuRu";
#[change(FooResource)]
async fn change(_id : u64, _body : FooBody) -> Raw<&'static [u8]>
{
Raw::new(CHANGE_RESPONSE, TEXT_PLAIN)
}
const REMOVE_ALL_RESPONSE : &[u8] = b"Y36kZ749MRk2Nem4BedJABOZiZWPLOtiwLfJlGTwm5";
#[remove_all(FooResource)]
async fn remove_all() -> Raw<&'static [u8]>
{
Raw::new(REMOVE_ALL_RESPONSE, TEXT_PLAIN)
}
const REMOVE_RESPONSE : &[u8] = b"CwRzBrKErsVZ1N7yeNfjZuUn1MacvgBqk4uPOFfDDq";
#[remove(FooResource)]
async fn remove(_id : u64) -> Raw<&'static [u8]>
{
Raw::new(REMOVE_RESPONSE, TEXT_PLAIN)
}
#[test]
fn async_methods()
{
let server = TestServer::new(build_simple_router(|router| {
router.resource::<FooResource>("foo");
})).unwrap();
test_get_response(&server, "http://localhost/foo", READ_ALL_RESPONSE);
test_get_response(&server, "http://localhost/foo/1", READ_RESPONSE);
test_get_response(&server, "http://localhost/foo/search?query=hello+world", SEARCH_RESPONSE);
test_post_response(&server, "http://localhost/foo", r#"{"data":"hello world"}"#, APPLICATION_JSON, CREATE_RESPONSE);
test_put_response(&server, "http://localhost/foo", r#"{"data":"hello world"}"#, APPLICATION_JSON, CHANGE_ALL_RESPONSE);
test_put_response(&server, "http://localhost/foo/1", r#"{"data":"hello world"}"#, APPLICATION_JSON, CHANGE_RESPONSE);
test_delete_response(&server, "http://localhost/foo", REMOVE_ALL_RESPONSE);
test_delete_response(&server, "http://localhost/foo/1", REMOVE_RESPONSE);
}

View file

@ -1,7 +1,3 @@
mod custom_request_body
{
use gotham::{
hyper::header::CONTENT_TYPE,
router::builder::*,
@ -10,6 +6,7 @@ use gotham::{
use gotham_restful::*;
use mime::TEXT_PLAIN;
const RESPONSE : &[u8] = b"This is the only valid response.";
#[derive(Resource)]
@ -30,7 +27,7 @@ fn create(body : Foo) -> Raw<Vec<u8>> {
#[test]
fn test()
fn custom_request_body()
{
let server = TestServer::new(build_simple_router(|router| {
router.resource::<FooResource>("foo");
@ -44,6 +41,3 @@ fn test()
let body : &[u8] = res.as_ref();
assert_eq!(body, RESPONSE);
}
}

View file

@ -10,6 +10,11 @@ use gotham::{
use gotham_restful::*;
use mime::TEXT_PLAIN;
#[allow(dead_code)]
mod util { include!("util/mod.rs"); }
use util::test_get_response;
const RESPONSE : &[u8] = b"This is the only valid response.";
#[derive(Resource)]
@ -23,13 +28,6 @@ fn read_all() -> Raw<&'static [u8]>
}
fn test_response(server : &TestServer, path : &str)
{
let res = server.client().get(path).perform().unwrap().read_body().unwrap();
let body : &[u8] = res.as_ref();
assert_eq!(body, RESPONSE);
}
#[test]
fn test()
{
@ -51,10 +49,10 @@ fn test()
});
})).unwrap();
test_response(&server, "http://localhost/foo1");
test_response(&server, "http://localhost/bar/foo2");
test_response(&server, "http://localhost/bar/baz/foo3");
test_response(&server, "http://localhost/foo4");
test_get_response(&server, "http://localhost/foo1", RESPONSE);
test_get_response(&server, "http://localhost/bar/foo2", RESPONSE);
test_get_response(&server, "http://localhost/bar/baz/foo3", RESPONSE);
test_get_response(&server, "http://localhost/foo4", RESPONSE);
}

106
tests/sync_methods.rs Normal file
View file

@ -0,0 +1,106 @@
#[macro_use] extern crate gotham_derive;
use gotham::{
router::builder::*,
test::TestServer
};
use gotham_restful::*;
use mime::{APPLICATION_JSON, TEXT_PLAIN};
use serde::Deserialize;
mod util { include!("util/mod.rs"); }
use util::{test_get_response, test_post_response, test_put_response, test_delete_response};
#[derive(Resource)]
#[resource(read_all, read, search, create, change_all, change, remove_all, remove)]
struct FooResource;
#[derive(Deserialize)]
#[cfg_attr(feature = "openapi", derive(OpenapiType))]
#[allow(dead_code)]
struct FooBody
{
data : String
}
#[derive(Deserialize, StateData, StaticResponseExtender)]
#[cfg_attr(feature = "openapi", derive(OpenapiType))]
#[allow(dead_code)]
struct FooSearch
{
query : String
}
const READ_ALL_RESPONSE : &[u8] = b"1ARwwSPVyOKpJKrYwqGgECPVWDl1BqajAAj7g7WJ3e";
#[read_all(FooResource)]
fn read_all() -> Raw<&'static [u8]>
{
Raw::new(READ_ALL_RESPONSE, TEXT_PLAIN)
}
const READ_RESPONSE : &[u8] = b"FEReHoeBKU17X2bBpVAd1iUvktFL43CDu0cFYHdaP9";
#[read(FooResource)]
fn read(_id : u64) -> Raw<&'static [u8]>
{
Raw::new(READ_RESPONSE, TEXT_PLAIN)
}
const SEARCH_RESPONSE : &[u8] = b"AWqcQUdBRHXKh3at4u79mdupOAfEbnTcx71ogCVF0E";
#[search(FooResource)]
fn search(_body : FooSearch) -> Raw<&'static [u8]>
{
Raw::new(SEARCH_RESPONSE, TEXT_PLAIN)
}
const CREATE_RESPONSE : &[u8] = b"y6POY7wOMAB0jBRBw0FJT7DOpUNbhmT8KdpQPLkI83";
#[create(FooResource)]
fn create(_body : FooBody) -> Raw<&'static [u8]>
{
Raw::new(CREATE_RESPONSE, TEXT_PLAIN)
}
const CHANGE_ALL_RESPONSE : &[u8] = b"QlbYg8gHE9OQvvk3yKjXJLTSXlIrg9mcqhfMXJmQkv";
#[change_all(FooResource)]
fn change_all(_body : FooBody) -> Raw<&'static [u8]>
{
Raw::new(CHANGE_ALL_RESPONSE, TEXT_PLAIN)
}
const CHANGE_RESPONSE : &[u8] = b"qGod55RUXkT1lgPO8h0uVM6l368O2S0GrwENZFFuRu";
#[change(FooResource)]
fn change(_id : u64, _body : FooBody) -> Raw<&'static [u8]>
{
Raw::new(CHANGE_RESPONSE, TEXT_PLAIN)
}
const REMOVE_ALL_RESPONSE : &[u8] = b"Y36kZ749MRk2Nem4BedJABOZiZWPLOtiwLfJlGTwm5";
#[remove_all(FooResource)]
fn remove_all() -> Raw<&'static [u8]>
{
Raw::new(REMOVE_ALL_RESPONSE, TEXT_PLAIN)
}
const REMOVE_RESPONSE : &[u8] = b"CwRzBrKErsVZ1N7yeNfjZuUn1MacvgBqk4uPOFfDDq";
#[remove(FooResource)]
fn remove(_id : u64) -> Raw<&'static [u8]>
{
Raw::new(REMOVE_RESPONSE, TEXT_PLAIN)
}
#[test]
fn sync_methods()
{
let server = TestServer::new(build_simple_router(|router| {
router.resource::<FooResource>("foo");
})).unwrap();
test_get_response(&server, "http://localhost/foo", READ_ALL_RESPONSE);
test_get_response(&server, "http://localhost/foo/1", READ_RESPONSE);
test_get_response(&server, "http://localhost/foo/search?query=hello+world", SEARCH_RESPONSE);
test_post_response(&server, "http://localhost/foo", r#"{"data":"hello world"}"#, APPLICATION_JSON, CREATE_RESPONSE);
test_put_response(&server, "http://localhost/foo", r#"{"data":"hello world"}"#, APPLICATION_JSON, CHANGE_ALL_RESPONSE);
test_put_response(&server, "http://localhost/foo/1", r#"{"data":"hello world"}"#, APPLICATION_JSON, CHANGE_RESPONSE);
test_delete_response(&server, "http://localhost/foo", REMOVE_ALL_RESPONSE);
test_delete_response(&server, "http://localhost/foo/1", REMOVE_RESPONSE);
}

View file

@ -8,4 +8,11 @@ fn trybuild_ui()
// always enabled
t.compile_fail("tests/ui/from_body_enum.rs");
t.compile_fail("tests/ui/method_async_state.rs");
t.compile_fail("tests/ui/method_for_unknown_resource.rs");
t.compile_fail("tests/ui/method_no_resource.rs");
t.compile_fail("tests/ui/method_self.rs");
t.compile_fail("tests/ui/method_too_few_args.rs");
t.compile_fail("tests/ui/method_too_many_args.rs");
t.compile_fail("tests/ui/method_unsafe.rs");
}

View file

@ -0,0 +1,15 @@
#[macro_use] extern crate gotham_restful;
use gotham_restful::State;
#[derive(Resource)]
#[resource(read_all)]
struct FooResource;
#[read_all(FooResource)]
async fn read_all(state : &State)
{
}
fn main()
{
}

View file

@ -0,0 +1,21 @@
error: async fn must not take &State as an argument as State is not Sync, consider boxing
--> $DIR/method_async_state.rs:9:19
|
9 | async fn read_all(state : &State)
| ^^^^^
error[E0433]: failed to resolve: use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read_all`
--> $DIR/method_async_state.rs:4:10
|
4 | #[derive(Resource)]
| ^^^^^^^^ use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read_all`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
warning: unused import: `gotham_restful::State`
--> $DIR/method_async_state.rs:2:5
|
2 | use gotham_restful::State;
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default

View file

@ -0,0 +1,10 @@
#[macro_use] extern crate gotham_restful;
#[read_all(UnknownResource)]
fn read_all()
{
}
fn main()
{
}

View file

@ -0,0 +1,5 @@
error[E0412]: cannot find type `UnknownResource` in this scope
--> $DIR/method_for_unknown_resource.rs:3:12
|
3 | #[read_all(UnknownResource)]
| ^^^^^^^^^^^^^^^ not found in this scope

View file

@ -0,0 +1,14 @@
#[macro_use] extern crate gotham_restful;
#[derive(Resource)]
#[resource(read_all)]
struct FooResource;
#[read_all]
fn read_all()
{
}
fn main()
{
}

View file

@ -0,0 +1,15 @@
error: Missing Resource struct. Example: #[read_all(MyResource)]
--> $DIR/method_no_resource.rs:7:1
|
7 | #[read_all]
| ^^^^^^^^^^^
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0433]: failed to resolve: use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read_all`
--> $DIR/method_no_resource.rs:3:10
|
3 | #[derive(Resource)]
| ^^^^^^^^ use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read_all`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

14
tests/ui/method_self.rs Normal file
View file

@ -0,0 +1,14 @@
#[macro_use] extern crate gotham_restful;
#[derive(Resource)]
#[resource(read_all)]
struct FooResource;
#[read_all(FooResource)]
fn read_all(self)
{
}
fn main()
{
}

View file

@ -0,0 +1,13 @@
error: Didn't expect self parameter
--> $DIR/method_self.rs:8:13
|
8 | fn read_all(self)
| ^^^^
error[E0433]: failed to resolve: use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read_all`
--> $DIR/method_self.rs:3:10
|
3 | #[derive(Resource)]
| ^^^^^^^^ use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read_all`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -0,0 +1,14 @@
#[macro_use] extern crate gotham_restful;
#[derive(Resource)]
#[resource(read)]
struct FooResource;
#[read(FooResource)]
fn read()
{
}
fn main()
{
}

View file

@ -0,0 +1,13 @@
error: Too few arguments
--> $DIR/method_too_few_args.rs:8:4
|
8 | fn read()
| ^^^^
error[E0433]: failed to resolve: use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read`
--> $DIR/method_too_few_args.rs:3:10
|
3 | #[derive(Resource)]
| ^^^^^^^^ use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -0,0 +1,14 @@
#[macro_use] extern crate gotham_restful;
#[derive(Resource)]
#[resource(read_all)]
struct FooResource;
#[read_all(FooResource)]
fn read_all(_id : u64)
{
}
fn main()
{
}

View file

@ -0,0 +1,13 @@
error: Too many arguments
--> $DIR/method_too_many_args.rs:8:13
|
8 | fn read_all(_id : u64)
| ^^^
error[E0433]: failed to resolve: use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read_all`
--> $DIR/method_too_many_args.rs:3:10
|
3 | #[derive(Resource)]
| ^^^^^^^^ use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read_all`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

14
tests/ui/method_unsafe.rs Normal file
View file

@ -0,0 +1,14 @@
#[macro_use] extern crate gotham_restful;
#[derive(Resource)]
#[resource(read_all)]
struct FooResource;
#[read_all(FooResource)]
unsafe fn read_all()
{
}
fn main()
{
}

View file

@ -0,0 +1,13 @@
error: Resource methods must not be unsafe
--> $DIR/method_unsafe.rs:8:1
|
8 | unsafe fn read_all()
| ^^^^^^
error[E0433]: failed to resolve: use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read_all`
--> $DIR/method_unsafe.rs:3:10
|
3 | #[derive(Resource)]
| ^^^^^^^^ use of undeclared type or module `_gotham_restful_resource_foo_resource_method_read_all`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

37
tests/util/mod.rs Normal file
View file

@ -0,0 +1,37 @@
use gotham::{
hyper::Body,
test::TestServer
};
use mime::Mime;
pub fn test_get_response(server : &TestServer, path : &str, expected : &[u8])
{
let res = server.client().get(path).perform().unwrap().read_body().unwrap();
let body : &[u8] = res.as_ref();
assert_eq!(body, expected);
}
pub fn test_post_response<B>(server : &TestServer, path : &str, body : B, mime : Mime, expected : &[u8])
where
B : Into<Body>
{
let res = server.client().post(path, body, mime).perform().unwrap().read_body().unwrap();
let body : &[u8] = res.as_ref();
assert_eq!(body, expected);
}
pub fn test_put_response<B>(server : &TestServer, path : &str, body : B, mime : Mime, expected : &[u8])
where
B : Into<Body>
{
let res = server.client().put(path, body, mime).perform().unwrap().read_body().unwrap();
let body : &[u8] = res.as_ref();
assert_eq!(body, expected);
}
pub fn test_delete_response(server : &TestServer, path : &str, expected : &[u8])
{
let res = server.client().delete(path).perform().unwrap().read_body().unwrap();
let body : &[u8] = res.as_ref();
assert_eq!(body, expected);
}