1
0
Fork 0
mirror of https://gitlab.com/msrd0/gotham-restful.git synced 2025-02-23 04:52:28 +00:00

improve test coverage for the result types

This commit is contained in:
Dominic 2020-05-03 18:48:55 +02:00
parent 0d95ca4abb
commit 101e94b900
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
6 changed files with 171 additions and 84 deletions

View file

@ -6,7 +6,7 @@ use crate::{
}; };
use super::SECURITY_NAME; use super::SECURITY_NAME;
use indexmap::IndexMap; use indexmap::IndexMap;
use mime::{Mime, STAR_STAR}; use mime::Mime;
use openapiv3::{ use openapiv3::{
MediaType, Operation, Parameter, ParameterData, ParameterSchemaOrContent, ReferenceOr, MediaType, Operation, Parameter, ParameterData, ParameterSchemaOrContent, ReferenceOr,
ReferenceOr::Item, RequestBody as OARequestBody, Response, Responses, Schema, SchemaKind, ReferenceOr::Item, RequestBody as OARequestBody, Response, Responses, Schema, SchemaKind,
@ -148,7 +148,7 @@ impl<'a> OperationDescription<'a>
let (operation_id, default_status, accepted_types, schema, params, body_schema, supported_types, requires_auth) = ( let (operation_id, default_status, accepted_types, schema, params, body_schema, supported_types, requires_auth) = (
self.operation_id, self.default_status, self.accepted_types, self.schema, self.params, self.body_schema, self.supported_types, self.requires_auth); self.operation_id, self.default_status, self.accepted_types, self.schema, self.params, self.body_schema, self.supported_types, self.requires_auth);
let content = Self::schema_to_content(accepted_types.unwrap_or_else(|| vec![STAR_STAR]), schema); let content = Self::schema_to_content(accepted_types.or_all_types(), schema);
let mut responses : IndexMap<StatusCode, ReferenceOr<Response>> = IndexMap::new(); let mut responses : IndexMap<StatusCode, ReferenceOr<Response>> = IndexMap::new();
responses.insert(StatusCode::Code(default_status.as_u16()), Item(Response { responses.insert(StatusCode::Code(default_status.as_u16()), Item(Response {
@ -159,7 +159,7 @@ impl<'a> OperationDescription<'a>
let request_body = body_schema.map(|schema| Item(OARequestBody { let request_body = body_schema.map(|schema| Item(OARequestBody {
description: None, description: None,
content: Self::schema_to_content(supported_types.unwrap_or_else(|| vec![STAR_STAR]), schema), content: Self::schema_to_content(supported_types.or_all_types(), schema),
required: true required: true
})); }));
@ -199,7 +199,7 @@ mod test
{ {
let types = NoContent::accepted_types(); let types = NoContent::accepted_types();
let schema = <NoContent as OpenapiType>::schema(); let schema = <NoContent as OpenapiType>::schema();
let content = OperationDescription::schema_to_content(types.unwrap_or_else(|| vec![STAR_STAR]), Item(schema.into_schema())); let content = OperationDescription::schema_to_content(types.or_all_types(), Item(schema.into_schema()));
assert!(content.is_empty()); assert!(content.is_empty());
} }
@ -208,7 +208,7 @@ mod test
{ {
let types = Raw::<&str>::accepted_types(); let types = Raw::<&str>::accepted_types();
let schema = <Raw<&str> as OpenapiType>::schema(); let schema = <Raw<&str> as OpenapiType>::schema();
let content = OperationDescription::schema_to_content(types.unwrap_or_else(|| vec![STAR_STAR]), Item(schema.into_schema())); let content = OperationDescription::schema_to_content(types.or_all_types(), Item(schema.into_schema()));
assert_eq!(content.len(), 1); assert_eq!(content.len(), 1);
let json = serde_json::to_string(&content.values().nth(0).unwrap()).unwrap(); let json = serde_json::to_string(&content.values().nth(0).unwrap()).unwrap();
assert_eq!(json, r#"{"schema":{"type":"string","format":"binary"}}"#); assert_eq!(json, r#"{"schema":{"type":"string","format":"binary"}}"#);

View file

@ -2,7 +2,7 @@ use crate::Response;
#[cfg(feature = "openapi")] #[cfg(feature = "openapi")]
use crate::OpenapiSchema; use crate::OpenapiSchema;
use futures_util::future::FutureExt; use futures_util::future::FutureExt;
use mime::Mime; use mime::{Mime, STAR_STAR};
use serde::Serialize; use serde::Serialize;
use std::{ use std::{
error::Error, error::Error,
@ -26,6 +26,21 @@ pub use result::IntoResponseError;
mod success; mod success;
pub use success::Success; pub use success::Success;
pub(crate) trait OrAllTypes
{
fn or_all_types(self) -> Vec<Mime>;
}
impl OrAllTypes for Option<Vec<Mime>>
{
fn or_all_types(self) -> Vec<Mime>
{
self.unwrap_or_else(|| vec![STAR_STAR])
}
}
/// A trait provided to convert a resource's result to json. /// A trait provided to convert a resource's result to json.
pub trait ResourceResult pub trait ResourceResult
{ {
@ -145,13 +160,11 @@ where
mod test mod test
{ {
use super::*; use super::*;
use crate::{OpenapiType, StatusCode};
use futures_executor::block_on; use futures_executor::block_on;
use mime::{APPLICATION_JSON, TEXT_PLAIN};
use thiserror::Error; use thiserror::Error;
#[derive(Debug, Default, Deserialize, Serialize)] #[derive(Debug, Default, Deserialize, Serialize)]
#[cfg_attr(feature = "openapi", derive(OpenapiType))] #[cfg_attr(feature = "openapi", derive(crate::OpenapiType))]
struct Msg struct Msg
{ {
msg : String msg : String
@ -162,63 +175,16 @@ mod test
struct MsgError; struct MsgError;
#[test] #[test]
fn resource_result_ok() fn result_from_future()
{ {
let ok : Result<Msg, MsgError> = Ok(Msg::default()); let nc = NoContent::default();
let res = block_on(ok.into_response()).expect("didn't expect error response"); let res = block_on(nc.into_response()).unwrap();
assert_eq!(res.status, StatusCode::OK);
assert_eq!(res.mime, Some(APPLICATION_JSON));
assert_eq!(res.full_body().unwrap(), r#"{"msg":""}"#.as_bytes());
}
#[test] let fut_nc = async move { NoContent::default() }.boxed();
fn resource_result_err() let fut_res = block_on(fut_nc.into_response()).unwrap();
{
let err : Result<Msg, MsgError> = Err(MsgError::default());
let res = block_on(err.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::INTERNAL_SERVER_ERROR);
assert_eq!(res.mime, Some(APPLICATION_JSON));
assert_eq!(res.full_body().unwrap(), format!(r#"{{"error":true,"message":"{}"}}"#, MsgError::default()).as_bytes());
}
#[test] assert_eq!(res.status, fut_res.status);
fn success_always_successfull() assert_eq!(res.mime, fut_res.mime);
{ assert_eq!(res.full_body().unwrap(), fut_res.full_body().unwrap());
let success : Success<Msg> = Msg::default().into();
let res = block_on(success.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::OK);
assert_eq!(res.mime, Some(APPLICATION_JSON));
assert_eq!(res.full_body().unwrap(), r#"{"msg":""}"#.as_bytes());
}
#[test]
fn no_content_has_empty_response()
{
let no_content = NoContent::default();
let res = block_on(no_content.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::NO_CONTENT);
assert_eq!(res.mime, None);
assert_eq!(res.full_body().unwrap(), &[] as &[u8]);
}
#[test]
fn no_content_result()
{
let no_content : Result<NoContent, MsgError> = Ok(NoContent::default());
let res = block_on(no_content.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::NO_CONTENT);
assert_eq!(res.mime, None);
assert_eq!(res.full_body().unwrap(), &[] as &[u8]);
}
#[test]
fn raw_response()
{
let msg = "Test";
let raw = Raw::new(msg, TEXT_PLAIN);
let res = block_on(raw.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::OK);
assert_eq!(res.mime, Some(TEXT_PLAIN));
assert_eq!(res.full_body().unwrap(), msg.as_bytes());
} }
} }

View file

@ -104,3 +104,37 @@ where
NoContent::default_status() NoContent::default_status()
} }
} }
#[cfg(test)]
mod test
{
use super::*;
use futures_executor::block_on;
use gotham::hyper::StatusCode;
use thiserror::Error;
#[derive(Debug, Default, Error)]
#[error("An Error")]
struct MsgError;
#[test]
fn no_content_has_empty_response()
{
let no_content = NoContent::default();
let res = block_on(no_content.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::NO_CONTENT);
assert_eq!(res.mime, None);
assert_eq!(res.full_body().unwrap(), &[] as &[u8]);
}
#[test]
fn no_content_result()
{
let no_content : Result<NoContent, MsgError> = Ok(NoContent::default());
let res = block_on(no_content.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::NO_CONTENT);
assert_eq!(res.mime, None);
assert_eq!(res.full_body().unwrap(), &[] as &[u8]);
}
}

View file

@ -10,10 +10,11 @@ use mime::Mime;
use openapiv3::{SchemaKind, StringFormat, StringType, Type, VariantOrUnknownOrEmpty}; use openapiv3::{SchemaKind, StringFormat, StringType, Type, VariantOrUnknownOrEmpty};
use serde_json::error::Error as SerdeJsonError; use serde_json::error::Error as SerdeJsonError;
use std::{ use std::{
fmt::{Debug, Display}, fmt::Display,
pin::Pin pin::Pin
}; };
#[derive(Debug)]
pub struct Raw<T> pub struct Raw<T>
{ {
pub raw : T, pub raw : T,
@ -39,13 +40,6 @@ impl<T : Clone> Clone for Raw<T>
} }
} }
impl<T : Debug> Debug for Raw<T>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Raw({:?}, {:?})", self.raw, self.mime)
}
}
impl<T : Into<Body>> ResourceResult for Raw<T> impl<T : Into<Body>> ResourceResult for Raw<T>
where where
Self : Send Self : Send
@ -88,3 +82,23 @@ where
<Raw<T> as ResourceResult>::schema() <Raw<T> as ResourceResult>::schema()
} }
} }
#[cfg(test)]
mod test
{
use super::*;
use futures_executor::block_on;
use mime::TEXT_PLAIN;
#[test]
fn raw_response()
{
let msg = "Test";
let raw = Raw::new(msg, TEXT_PLAIN);
let res = block_on(raw.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::OK);
assert_eq!(res.mime, Some(TEXT_PLAIN));
assert_eq!(res.full_body().unwrap(), msg.as_bytes());
}
}

View file

@ -57,3 +57,50 @@ where
R::schema() R::schema()
} }
} }
#[cfg(test)]
mod test
{
use super::*;
use crate::result::OrAllTypes;
use futures_executor::block_on;
use thiserror::Error;
#[derive(Debug, Default, Deserialize, Serialize)]
#[cfg_attr(feature = "openapi", derive(crate::OpenapiType))]
struct Msg
{
msg : String
}
#[derive(Debug, Default, Error)]
#[error("An Error")]
struct MsgError;
#[test]
fn result_ok()
{
let ok : Result<Msg, MsgError> = Ok(Msg::default());
let res = block_on(ok.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::OK);
assert_eq!(res.mime, Some(APPLICATION_JSON));
assert_eq!(res.full_body().unwrap(), r#"{"msg":""}"#.as_bytes());
}
#[test]
fn result_err()
{
let err : Result<Msg, MsgError> = Err(MsgError::default());
let res = block_on(err.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::INTERNAL_SERVER_ERROR);
assert_eq!(res.mime, Some(APPLICATION_JSON));
assert_eq!(res.full_body().unwrap(), format!(r#"{{"error":true,"message":"{}"}}"#, MsgError::default()).as_bytes());
}
#[test]
fn success_accepts_json()
{
assert!(<Result<Msg, MsgError>>::accepted_types().or_all_types().contains(&APPLICATION_JSON))
}
}

View file

@ -41,6 +41,7 @@ fn read_all(_state: &mut State) -> Success<MyResponse> {
# } # }
``` ```
*/ */
#[derive(Debug)]
pub struct Success<T>(T); pub struct Success<T>(T);
impl<T> AsMut<T> for Success<T> impl<T> AsMut<T> for Success<T>
@ -97,13 +98,6 @@ impl<T : Copy> Copy for Success<T>
{ {
} }
impl<T : Debug> Debug for Success<T>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Success({:?})", self.0)
}
}
impl<T : Default> Default for Success<T> impl<T : Default> Default for Success<T>
{ {
fn default() -> Self fn default() -> Self
@ -134,3 +128,35 @@ where
T::schema() T::schema()
} }
} }
#[cfg(test)]
mod test
{
use super::*;
use crate::result::OrAllTypes;
use futures_executor::block_on;
#[derive(Debug, Default, Serialize)]
#[cfg_attr(feature = "openapi", derive(crate::OpenapiType))]
struct Msg
{
msg : String
}
#[test]
fn success_always_successfull()
{
let success : Success<Msg> = Msg::default().into();
let res = block_on(success.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::OK);
assert_eq!(res.mime, Some(APPLICATION_JSON));
assert_eq!(res.full_body().unwrap(), r#"{"msg":""}"#.as_bytes());
}
#[test]
fn success_accepts_json()
{
assert!(<Success<Msg>>::accepted_types().or_all_types().contains(&APPLICATION_JSON))
}
}