pub mod args;
use base64::{engine::general_purpose, Engine};
use reqwest::{
multipart::{Form, Part},
redirect::Policy,
StatusCode,
};
use tokio::sync::mpsc::Sender;
use tracing::info;
use uuid::Uuid;
pub const SUPPORTED_CHALLENGES: &[i32] = &[-1, 1, 4, 6, 7, 8, 11, 12];
pub const SUBMISSION_TIMEOUT: u64 = 60;
#[derive(Debug)]
pub enum SubmissionState {
Waiting,
Running,
Done,
Error,
}
impl std::fmt::Display for SubmissionState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Debug)]
pub enum SubmissionUpdate {
State(SubmissionState),
TaskCompleted(bool, i32),
LogLine(String),
Save,
}
impl From<SubmissionState> for SubmissionUpdate {
fn from(value: SubmissionState) -> Self {
Self::State(value)
}
}
impl From<(bool, i32)> for SubmissionUpdate {
fn from((b, i): (bool, i32)) -> Self {
Self::TaskCompleted(b, i)
}
}
impl From<String> for SubmissionUpdate {
fn from(value: String) -> Self {
Self::LogLine(value)
}
}
pub async fn run(url: String, id: Uuid, number: i32, tx: Sender<SubmissionUpdate>) {
info!(%id, %url, %number, "Starting submission");
tx.send(SubmissionState::Running.into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
tokio::select! {
_ = validate(url.as_str(), number, tx.clone()) => (),
_ = tokio::time::sleep(tokio::time::Duration::from_secs(SUBMISSION_TIMEOUT)) => {
info!(%id, %url, %number, "Submission timed out");
tx.send("Timed out".to_owned().into()).await.unwrap();
tx.send(SubmissionState::Done.into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
},
};
info!(%id, %url, %number, "Completed submission");
}
type TaskTest = (i32, i32);
type ValidateResult = std::result::Result<(), TaskTest>;
pub async fn validate(url: &str, number: i32, tx: Sender<SubmissionUpdate>) {
if !SUPPORTED_CHALLENGES.contains(&number) {
tx.send(
format!("Validating Challenge {number} is not supported yet! Check for updates.")
.into(),
)
.await
.unwrap();
return;
}
let txc = tx.clone();
if let Err((task, test)) = match number {
-1 => validate_minus1(url, txc).await,
1 => validate_1(url, txc).await,
4 => validate_4(url, txc).await,
6 => validate_6(url, txc).await,
7 => validate_7(url, txc).await,
8 => validate_8(url, txc).await,
11 => validate_11(url, txc).await,
12 => validate_12(url, txc).await,
_ => unreachable!(),
} {
info!(%url, %number, %task, %test, "Submission failed");
tx.send(format!("Task {task}: test #{test} failed 🟥").into())
.await
.unwrap();
}
tx.send(SubmissionState::Done.into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
}
pub fn make_url(pn: Option<&str>) -> String {
match pn {
Some(pn) => format!("https://{pn}.shuttleapp.rs"),
None => "http://localhost:8000".to_owned(),
}
}
fn new_client() -> reqwest::Client {
reqwest::ClientBuilder::new()
.http1_only()
.connect_timeout(tokio::time::Duration::from_secs(3))
.redirect(Policy::limited(3))
.referer(false)
.timeout(tokio::time::Duration::from_secs(60))
.build()
.unwrap()
}
async fn validate_minus1(base_url: &str, tx: Sender<SubmissionUpdate>) -> ValidateResult {
let client = new_client();
let mut test: TaskTest;
test = (1, 1);
let url = &format!("{}/", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
if res.status() != StatusCode::OK {
return Err(test);
}
tx.send((true, 0).into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
test = (2, 1);
let url = &format!("{}/-1/error", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
if res.status() != StatusCode::INTERNAL_SERVER_ERROR {
return Err(test);
}
tx.send((false, 0).into()).await.unwrap();
Ok(())
}
async fn validate_1(base_url: &str, tx: Sender<SubmissionUpdate>) -> ValidateResult {
let client = new_client();
let mut test: TaskTest;
test = (1, 1);
let url = &format!("{}/1/2/3", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "1" {
return Err(test);
}
test = (1, 2);
let url = &format!("{}/1/12/16", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "21952" {
return Err(test);
}
tx.send((true, 0).into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
test = (2, 1);
let url = &format!("{}/1/3/5/7/9", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "512" {
return Err(test);
}
test = (2, 2);
let url = &format!("{}/1/0/0/0", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "0" {
return Err(test);
}
test = (2, 3);
let url = &format!("{}/1/-3/1", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "-64" {
return Err(test);
}
test = (2, 4);
let url = &format!("{}/1/3/5/7/9/2/13/12/16/18", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "729" {
return Err(test);
}
tx.send((false, 100).into()).await.unwrap();
Ok(())
}
async fn validate_4(base_url: &str, tx: Sender<SubmissionUpdate>) -> ValidateResult {
let client = new_client();
let mut test: TaskTest;
test = (1, 1);
let url = &format!("{}/4/strength", base_url);
let res = client
.post(url)
.json(&serde_json::json!([
{
"name": "Zeus",
"strength": 8
},
{
"name": "Oner",
"strength": 6
},
{
"name": "Faker",
"strength": 7
},
{
"name": "Gumayusi",
"strength": 6
},
{
"name": "Keria",
"strength": 6
}
]))
.send()
.await
.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "33" {
return Err(test);
}
tx.send((true, 0).into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
test = (2, 1);
let url = &format!("{}/4/contest", base_url);
let res = client
.post(url)
.json(&serde_json::json!([
{
"name": "Zeus",
"strength": 8,
"speed": 51.2,
"height": 81,
"antler_width": 31,
"snow_magic_power": 311,
"favorite_food": "pizza",
"cAnD13s_3ATeN-yesT3rdAy": 4
},
{
"name": "Oner",
"strength": 6,
"speed": 41.3,
"height": 51,
"antler_width": 30,
"snow_magic_power": 321,
"favorite_food": "burger",
"cAnD13s_3ATeN-yesT3rdAy": 1
},
{
"name": "Faker",
"strength": 7,
"speed": 50,
"height": 50,
"antler_width": 37,
"snow_magic_power": 6667,
"favorite_food": "broccoli",
"cAnD13s_3ATeN-yesT3rdAy": 1
},
{
"name": "Gumayusi",
"strength": 6,
"speed": 60.1,
"height": 50,
"antler_width": 34,
"snow_magic_power": 2323,
"favorite_food": "pizza",
"cAnD13s_3ATeN-yesT3rdAy": 1
},
{
"name": "Keria",
"strength": 6,
"speed": 48.2,
"height": 65,
"antler_width": 33,
"snow_magic_power": 5014,
"favorite_food": "wok",
"cAnD13s_3ATeN-yesT3rdAy": 5
}
]))
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json
!= serde_json::json!({
"fastest":"Speeding past the finish line with a strength of 6 is Gumayusi",
"tallest":"Zeus is standing tall with his 31 cm wide antlers",
"magician":"Faker could blast you away with a snow magic power of 6667",
"consumer":"Keria ate lots of candies, but also some wok"
})
{
return Err(test);
}
tx.send((false, 150).into()).await.unwrap();
Ok(())
}
async fn validate_6(base_url: &str, tx: Sender<SubmissionUpdate>) -> ValidateResult {
let client = new_client();
let mut test: TaskTest;
let url = &format!("{}/6", base_url);
test = (1, 1);
let res = client
.post(url)
.body("elf elf elf")
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json["elf"] != serde_json::Value::Number(3.into()) {
return Err(test);
}
test = (1, 2);
let res = client
.post(url)
.body("In the quirky town of Elf stood an enchanting shop named 'The Elf & Shelf.' Managed by Wally, a mischievous elf with a knack for crafting exquisite shelves, the shop was a bustling hub of elf after elf who wanter to see their dear elf in Belfast.")
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json["elf"] != serde_json::Value::Number(6.into()) {
return Err(test);
}
tx.send((true, 0).into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
test = (2, 1);
let res = client
.post(url)
.body("elf elf elf on a shelf")
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json
!= serde_json::json!({
"elf":4,
"elf on a shelf":1,
"shelf with no elf on it":0
})
{
return Err(test);
}
test = (2, 2);
let res = client
.post(url)
.body("In Belfast I heard an elf on a shelf on a shelf on a ")
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json
!= serde_json::json!({
"elf":4,
"elf on a shelf":2,
"shelf with no elf on it":0
})
{
return Err(test);
}
test = (2, 3);
let res = client
.post(url)
.body("Somewhere in Belfast under a shelf store but above the shelf realm there's an elf on a shelf on a shelf on a shelf on a elf on a shelf on a shelf on a shelf on a shelf on a elf on a elf on a elf on a shelf on a ")
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json
!= serde_json::json!({
"elf":16,
"elf on a shelf":8,
"shelf with no elf on it":2
})
{
return Err(test);
}
tx.send((false, 200).into()).await.unwrap();
Ok(())
}
async fn validate_7(base_url: &str, tx: Sender<SubmissionUpdate>) -> ValidateResult {
let client = new_client();
let mut test: TaskTest;
test = (1, 1);
let url = &format!("{}/7/decode", base_url);
let data = serde_json::json!({
"recipe": {
"flour": 4,
"sugar": 3,
"butter": 3,
"baking powder": 1,
"raisins": 50
},
});
let b64 = general_purpose::STANDARD.encode(serde_json::to_vec(&data).unwrap());
let res = client
.get(url)
.header("Cookie", format!("recipe={b64}"))
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json != data {
return Err(test);
}
test = (1, 2);
let data = serde_json::json!({
"recipe": {
"peanuts": 26,
"dough": 37,
"extra salt": 1,
"raisins": 50
},
});
let b64 = general_purpose::STANDARD.encode(serde_json::to_vec(&data).unwrap());
let res = client
.get(url)
.header("Cookie", format!("recipe={b64}"))
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json != data {
return Err(test);
}
tx.send((true, 0).into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
let url = &format!("{}/7/bake", base_url);
let test_bake = |test: (i32, i32), i: serde_json::Value, o: serde_json::Value| async move {
let client = new_client();
let b64 = general_purpose::STANDARD.encode(serde_json::to_vec(&i).unwrap());
let res = client
.get(url)
.header("Cookie", format!("recipe={b64}"))
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json != o {
return Err(test);
}
Ok(())
};
test = (2, 1);
test_bake(
test,
serde_json::json!({
"recipe": {
"flour": 35,
"sugar": 56,
"butter": 3,
"baking powder": 1001,
"chocolate chips": 55
},
"pantry": {
"flour": 4045,
"sugar": 9606,
"butter": 99, "baking powder": 8655432,
"chocolate chips": 4587
}
}),
serde_json::json!({
"cookies": 33,
"pantry": {
"flour": 2890,
"sugar": 7758,
"butter": 0,
"baking powder": 8622399,
"chocolate chips": 2772
}
}),
)
.await?;
test = (2, 2);
test_bake(
test,
serde_json::json!({
"recipe": {
"flour": 35,
"sugar": 56,
"butter": 3,
"baking powder": 1001,
"chocolate chips": 55
},
"pantry": {
"flour": 4045,
"sugar": 7606,
"butter": 100,
"baking powder": 865543211516164409i64,
"chocolate chips": 4587
}
}),
serde_json::json!({
"cookies": 33,
"pantry": {
"flour": 2890,
"sugar": 5758,
"butter": 1,
"baking powder": 865543211516131376i64,
"chocolate chips": 2772
}
}),
)
.await?;
tx.send((false, 120).into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
test = (3, 1);
test_bake(
test,
serde_json::json!({
"recipe": {
"chicken": 1,
},
"pantry": {
"chicken": 0,
}
}),
serde_json::json!({
"cookies": 0,
"pantry": {
"chicken": 0,
}
}),
)
.await?;
test = (3, 2);
test_bake(
test,
serde_json::json!({
"recipe": {
"cocoa bean": 1,
"chicken": 0,
},
"pantry": {
"cocoa bean": 5,
"corn": 5,
"cucumber": 0,
}
}),
serde_json::json!({
"cookies": 5,
"pantry": {
"cocoa bean": 0,
"corn": 5,
"cucumber": 0,
}
}),
)
.await?;
test = (3, 3);
test_bake(
test,
serde_json::json!({
"recipe": {
"cocoa bean": 1,
"chicken": 0,
},
"pantry": {
"cocoa bean": 5,
"chicken": 0,
}
}),
serde_json::json!({
"cookies": 5,
"pantry": {
"cocoa bean": 0,
"chicken": 0,
}
}),
)
.await?;
test = (3, 4);
test_bake(
test,
serde_json::json!({
"recipe": {
"cocoa bean": 1,
"chicken": 0,
},
"pantry": {
"cocoa bean": 5,
}
}),
serde_json::json!({
"cookies": 5,
"pantry": {
"cocoa bean": 0,
}
}),
)
.await?;
tx.send((false, 100).into()).await.unwrap();
Ok(())
}
async fn validate_8(base_url: &str, tx: Sender<SubmissionUpdate>) -> ValidateResult {
let client = new_client();
let mut test: TaskTest;
let tol = 0.001f64;
test = (1, 1);
let url = &format!("{}/8/weight/225", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
let num: f64 = text.parse().map_err(|_| test)?;
if !(num.is_finite() && (num - 16f64).abs() < tol) {
return Err(test);
}
test = (1, 2);
let url = &format!("{}/8/weight/393", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
let num: f64 = text.parse().map_err(|_| test)?;
if !(num.is_finite() && (num - 5.2f64).abs() < tol) {
return Err(test);
}
test = (1, 3);
let url = &format!("{}/8/weight/92", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
let num: f64 = text.parse().map_err(|_| test)?;
if !(num.is_finite() && (num - 0.1f64).abs() < tol) {
return Err(test);
}
tx.send((true, 0).into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
test = (2, 1);
let url = &format!("{}/8/drop/383", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
let num: f64 = text.parse().map_err(|_| test)?;
if !(num.is_finite() && (num - 13316.953480432378f64).abs() < tol) {
return Err(test);
}
test = (2, 2);
let url = &format!("{}/8/drop/16", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
let num: f64 = text.parse().map_err(|_| test)?;
if !(num.is_finite() && (num - 25.23212238397714f64).abs() < tol) {
return Err(test);
}
test = (2, 3);
let url = &format!("{}/8/drop/143", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
let num: f64 = text.parse().map_err(|_| test)?;
if !(num.is_finite() && (num - 6448.2090536830465f64).abs() < tol) {
return Err(test);
}
tx.send((false, 160).into()).await.unwrap();
Ok(())
}
async fn validate_11(base_url: &str, tx: Sender<SubmissionUpdate>) -> ValidateResult {
let client = new_client();
let mut test: TaskTest;
test = (1, 1);
let url = &format!("{}/11/assets/decoration.png", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
if res.status() != StatusCode::OK {}
let headers = res.headers();
if !headers
.get("content-type")
.is_some_and(|v| v == "image/png")
{
return Err(test);
}
if !headers.get("content-length").is_some_and(|v| v == "787297") {
return Err(test);
}
let bytes = res.bytes().await.map_err(|_| test)?;
const EXPECTED: &[u8] = include_bytes!("../assets/decoration.png");
if bytes.to_vec().as_slice() != EXPECTED {
return Err(test);
}
tx.send((true, 0).into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
test = (2, 1);
let url = &format!("{}/11/red_pixels", base_url);
let form = Form::new().part(
"image",
Part::bytes(include_bytes!("../assets/decoration2.png").as_slice())
.file_name("decoration2.png")
.mime_str("image/png")
.unwrap(),
);
let res = client
.post(url)
.multipart(form)
.send()
.await
.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "152107" {
return Err(test);
}
test = (2, 2);
let form = Form::new().part(
"image",
Part::bytes(include_bytes!("../assets/decoration3.png").as_slice())
.file_name("decoration3.png")
.mime_str("image/png")
.unwrap(),
);
let res = client
.post(url)
.multipart(form.into())
.send()
.await
.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "40263" {
return Err(test);
}
test = (2, 3);
let form = Form::new().part(
"image",
Part::bytes(include_bytes!("../assets/decoration4.png").as_slice())
.file_name("decoration4.png")
.mime_str("image/png")
.unwrap(),
);
let res = client
.post(url)
.multipart(form.into())
.send()
.await
.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "86869" {
return Err(test);
}
tx.send((false, 200).into()).await.unwrap();
Ok(())
}
async fn validate_12(base_url: &str, tx: Sender<SubmissionUpdate>) -> ValidateResult {
let client = new_client();
let mut test: TaskTest;
test = (1, 1);
let url = &format!("{}/12/save/cch23", base_url);
let res = client.post(url).send().await.map_err(|_| test)?;
if res.status() != StatusCode::OK {
return Err(test);
}
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
let url = &format!("{}/12/load/cch23", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "2" {
return Err(test);
}
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
let url = &format!("{}/12/load/cch23", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "4" {
return Err(test);
}
test = (1, 2);
let url = &format!("{}/12/save/alpha", base_url);
let res = client.post(url).send().await.map_err(|_| test)?;
if res.status() != StatusCode::OK {
return Err(test);
}
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
let url = &format!("{}/12/save/omega", base_url);
let res = client.post(url).send().await.map_err(|_| test)?;
if res.status() != StatusCode::OK {
return Err(test);
}
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
let url = &format!("{}/12/load/alpha", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "4" {
return Err(test);
}
let url = &format!("{}/12/save/alpha", base_url);
let res = client.post(url).send().await.map_err(|_| test)?;
if res.status() != StatusCode::OK {
return Err(test);
}
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
let url = &format!("{}/12/load/omega", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "3" {
return Err(test);
}
let url = &format!("{}/12/load/alpha", base_url);
let res = client.get(url).send().await.map_err(|_| test)?;
let text = res.text().await.map_err(|_| test)?;
if text != "1" {
return Err(test);
}
tx.send((true, 0).into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
test = (2, 1);
let url = &format!("{}/12/ulids", base_url);
let res = client
.post(url)
.json(&serde_json::json!([
"01BJQ0E1C3Z56ABCD0E11HYX4M",
"01BJQ0E1C3Z56ABCD0E11HYX5N",
"01BJQ0E1C3Z56ABCD0E11HYX6Q",
"01BJQ0E1C3Z56ABCD0E11HYX7R",
"01BJQ0E1C3Z56ABCD0E11HYX8P"
]))
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json
!= serde_json::json!([
"015cae07-0583-f94c-a5b1-a070431f7516",
"015cae07-0583-f94c-a5b1-a070431f74f8",
"015cae07-0583-f94c-a5b1-a070431f74d7",
"015cae07-0583-f94c-a5b1-a070431f74b5",
"015cae07-0583-f94c-a5b1-a070431f7494"
])
{
return Err(test);
}
test = (2, 2);
let res = client
.post(url)
.json(&serde_json::json!([]))
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json != serde_json::json!([]) {
return Err(test);
}
tx.send((false, 100).into()).await.unwrap();
tx.send(SubmissionUpdate::Save).await.unwrap();
test = (3, 1);
let ids = serde_json::json!([
"00WEGGF0G0J5HEYXS3D7RWZGV8",
"76EP4G39R8JD1N8AQNYDVJBRCF",
"018CJ7KMG0051CDCS3B7BFJ3AK",
"00Y986KPG0AMGB78RD45E9109K",
"010451HTG0NYWMPWCEXG6AJ8F2",
"01HH9SJEG0KY16H81S3N1BMXM4",
"01HH9SJEG0P9M22Z9VGHH9C8CX",
"017F8YY0G0NQA16HHC2QT5JD6X",
"03QCPC7P003V1NND3B3QJW72QJ"
]);
let url = &format!("{}/12/ulids/5", base_url);
let res = client.post(url).json(&ids).send().await.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json
!= serde_json::json!({
"christmas eve": 3,
"weekday": 1,
"in the future": 2,
"LSB is 1": 5
})
{
return Err(test);
}
test = (3, 2);
let url = &format!("{}/12/ulids/0", base_url);
let res = client.post(url).json(&ids).send().await.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json
!= serde_json::json!({
"christmas eve": 3,
"weekday": 0,
"in the future": 2,
"LSB is 1": 5
})
{
return Err(test);
}
test = (3, 3);
let url = &format!("{}/12/ulids/2", base_url);
let res = client
.post(url)
.json(&serde_json::json!(["04BJK8N300BAMR9SQQWPWHVYKZ"]))
.send()
.await
.map_err(|_| test)?;
let json = res.json::<serde_json::Value>().await.map_err(|_| test)?;
if json
!= serde_json::json!({
"christmas eve": 1,
"weekday": 1,
"in the future": 1,
"LSB is 1": 1
})
{
return Err(test);
}
tx.send((false, 200).into()).await.unwrap();
Ok(())
}