mod common;
use std::{fmt::Debug, marker::PhantomData};
use common::{Middle, TestGenericWrapper, TestResponse, TestServer};
use derive_builder::Builder;
use httpmock::prelude::*;
use rustified::endpoint::Endpoint;
use rustified_derive::Endpoint;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::json;
use test_log::test;
#[test(tokio::test)]
async fn test_path() {
#[derive(Endpoint)]
#[endpoint(path = "test/path")]
struct Test {}
let t = TestServer::default();
let e = Test {};
let m = t.server.mock(|when, then| {
when.method(GET).path("/test/path");
then.status(200);
});
let r = e.exec(&t.client).await;
m.assert();
assert!(r.is_ok());
}
#[test(tokio::test)]
async fn test_method() {
#[derive(Endpoint)]
#[endpoint(path = "test/path", method = "POST")]
struct Test {}
let t = TestServer::default();
let e = Test {};
let m = t.server.mock(|when, then| {
when.method(POST).path("/test/path");
then.status(200);
});
let r = e.exec(&t.client).await;
m.assert();
assert!(r.is_ok());
}
#[test(tokio::test)]
async fn test_query() {
#[derive(Endpoint)]
#[endpoint(path = "test/path", method = "POST")]
struct Test {
#[endpoint(query)]
pub name: String,
#[endpoint(query)]
pub age: u64,
}
let t = TestServer::default();
let e = Test {
name: "test".to_string(),
age: 30,
};
let m = t.server.mock(|when, then| {
when.method(POST)
.path("/test/path")
.query_param_exists("name")
.query_param_exists("age");
then.status(200);
});
let r = e.exec(&t.client).await;
m.assert();
assert!(r.is_ok());
}
#[test(tokio::test)]
async fn test_path_with_format() {
#[derive(Endpoint)]
#[endpoint(path = "test/path/{self.name}", method = "POST")]
struct Test {
#[endpoint(skip)]
name: String,
}
let t = TestServer::default();
let e = Test {
name: "test".to_string(),
};
let m = t.server.mock(|when, then| {
when.method(POST).path("/test/path/test").body("");
then.status(200);
});
let r = e.exec(&t.client).await;
m.assert();
assert!(r.is_ok());
}
#[test(tokio::test)]
async fn test_data() {
#[derive(Endpoint, Serialize)]
#[endpoint(path = "test/path", method = "POST")]
struct Test {
#[serde(rename = "type")]
ty: String,
}
let t = TestServer::default();
let e = Test {
ty: "test".to_string(),
};
let m = t.server.mock(|when, then| {
when.method(POST)
.path("/test/path")
.json_body(json!({ "type": "test" }));
then.status(200);
});
let r = e.exec(&t.client).await;
m.assert();
assert!(r.is_ok());
}
#[test(tokio::test)]
async fn test_raw_data() {
#[derive(Endpoint)]
#[endpoint(path = "test/path/{self.name}", method = "POST")]
struct Test {
#[endpoint(skip)]
name: String,
#[endpoint(raw)]
data: Vec<u8>,
}
let t = TestServer::default();
let e = Test {
name: "test".to_string(),
data: "somebits".into(),
};
let m = t.server.mock(|when, then| {
when.method(POST)
.path("/test/path/test")
.body_contains("somebits");
then.status(200);
});
let r = e.exec(&t.client).await;
m.assert();
assert!(r.is_ok())
}
#[test(tokio::test)]
async fn test_result() {
#[derive(Endpoint)]
#[endpoint(path = "test/path", response = "TestResponse")]
struct Test {}
#[derive(Deserialize)]
struct TestResponse {
age: u8,
}
let t = TestServer::default();
let e = Test {};
let m = t.server.mock(|when, then| {
when.method(GET).path("/test/path");
then.status(200).json_body(json!({"age": 30}));
});
let r = e.exec(&t.client).await;
m.assert();
assert!(r.is_ok());
assert_eq!(r.unwrap().parse().unwrap().age, 30);
}
#[test(tokio::test)]
async fn test_builder() {
#[derive(Builder, Endpoint)]
#[endpoint(path = "test/path", method = "POST", builder = "true")]
#[builder(setter(into))]
struct Test {
name: String,
}
let t = TestServer::default();
let m = t.server.mock(|when, then| {
when.method(POST).path("/test/path");
then.status(200);
});
let r = Test::builder()
.name("test")
.build()
.unwrap()
.exec(&t.client)
.await;
m.assert();
assert!(r.is_ok());
}
#[test(tokio::test)]
async fn test_mutate() {
#[derive(Endpoint)]
#[endpoint(path = "test/path", response = "TestResponse")]
struct Test {}
let t = TestServer::default();
let e = Test {};
let m = t.server.mock(|when, then| {
when.method(GET)
.path("/test/path")
.header("X-API-Token", "mytoken");
then.status(200).json_body(json!({"result": {"age": 30}}));
});
let r = e.with_middleware(&Middle {}).exec(&t.client).await;
m.assert();
assert!(r.is_ok());
assert_eq!(r.unwrap().parse().unwrap().age, 30);
}
#[test(tokio::test)]
async fn test_wrapper() {
#[derive(Endpoint)]
#[endpoint(path = "test/path", response = "TestResponse")]
struct Test {}
fn strip<T>(wrapper: TestGenericWrapper<T>) -> T {
wrapper.result
}
let t = TestServer::default();
let e = Test {};
let m = t.server.mock(|when, then| {
when.method(GET).path("/test/path");
then.status(200).json_body(json!({"result": {"age": 30}}));
});
let r = e.exec(&t.client).await.unwrap();
let r = r.wrap::<_>().map(strip).unwrap();
m.assert();
assert_eq!(r.age, 30);
}
#[test(tokio::test)]
async fn test_raw_response() {
#[derive(Endpoint)]
#[endpoint(path = "test/path", response = "TestResponse")]
struct Test {}
let resp_data = json!({"result": {"age": 30}});
let t = TestServer::default();
let e = Test {};
let m = t.server.mock(|when, then| {
when.method(GET).path("/test/path");
then.status(200).json_body(json!({"result": {"age": 30}}));
});
let r = e.exec(&t.client).await;
m.assert();
assert!(r.is_ok());
assert_eq!(r.unwrap().raw(), resp_data.to_string().as_bytes());
}
#[allow(dead_code)]
#[test(tokio::test)]
async fn test_generic() {
#[derive(Builder, Debug, Endpoint)]
#[endpoint(path = "test/path/{self.name}", response = "TestResponse<T>")]
#[builder(setter(into))]
struct Test<T: DeserializeOwned + Serialize + Debug + Send + Sync> {
#[endpoint(skip)]
name: String,
#[endpoint(skip)]
#[builder(default = "None", setter(skip))]
data: Option<PhantomData<T>>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct TestData {
age: u8,
}
#[derive(Debug, Serialize, Deserialize)]
struct TestResponse<T> {
data: T,
version: u8,
}
let t = TestServer::default();
let m = t.server.mock(|when, then| {
when.method(GET).path("/test/path/test");
then.status(200)
.json_body(json!({"result": {"data": {"age": 30}, "version": 1}}));
});
let r: TestResponse<TestData> = TestBuilder::default()
.name("test")
.build()
.unwrap()
.with_middleware(&Middle {})
.exec(&t.client)
.await
.unwrap()
.parse()
.unwrap();
m.assert();
assert_eq!(r.data.age, 30);
}
#[test(tokio::test)]
async fn test_complex() {
#[derive(Builder, Default, Endpoint)]
#[endpoint(
path = "test/path/{self.name}",
method = "POST",
response = "TestResponse",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
struct Test {
#[endpoint(skip)]
name: String,
kind: String,
special: Option<bool>,
optional: Option<String>,
}
let t = TestServer::default();
let m = t.server.mock(|when, then| {
when.method(POST)
.path("/test/path/test")
.json_body(json!({ "kind": "test" }));
then.status(200).json_body(json!({"result": {"age": 30}}));
});
let r = Test::builder()
.name("test")
.kind("test")
.build()
.unwrap()
.with_middleware(&Middle {})
.exec(&t.client)
.await;
m.assert();
assert!(r.is_ok());
assert_eq!(r.unwrap().parse().unwrap().age, 30);
}