diff --git a/src/handlers/day0.rs b/src/handlers/day0.rs index 416606f..26295f9 100644 --- a/src/handlers/day0.rs +++ b/src/handlers/day0.rs @@ -6,10 +6,37 @@ pub fn router() -> axum::Router { .route("/-1/error", get(internal_server_error)) } -pub async fn internal_server_error() -> impl IntoResponse { +async fn internal_server_error() -> impl IntoResponse { StatusCode::INTERNAL_SERVER_ERROR } async fn hello_world() -> impl IntoResponse { "Hello, World!" } + +#[cfg(test)] +mod tests { + use super::*; + use axum::http::StatusCode; + use axum_test_helper::TestClient; + + #[tokio::test] + async fn hello_world() { + let app = router(); + + let client = TestClient::new(app); + let res = client.get("/").send().await; + assert_eq!(res.status(), StatusCode::OK); + assert_eq!(res.text().await, "Hello, World!"); + } + + #[tokio::test] + async fn internal_server_error() { + let app = router(); + + let client = TestClient::new(app); + let res = client.get("/-1/error").send().await; + assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!(res.text().await, ""); + } +} diff --git a/src/handlers/day1.rs b/src/handlers/day1.rs index 6006e53..7fc556e 100644 --- a/src/handlers/day1.rs +++ b/src/handlers/day1.rs @@ -5,7 +5,7 @@ pub fn router() -> axum::Router { axum::Router::new().route("/1/*ids", get(packet_ids)) } -pub async fn packet_ids(Path(ids): Path) -> impl IntoResponse { +async fn packet_ids(Path(ids): Path) -> impl IntoResponse { let packet_ids: Vec = ids .split('/') // TODO: How to handle this gracefully? @@ -23,3 +23,32 @@ pub async fn packet_ids(Path(ids): Path) -> impl IntoResponse { let result = packet_ids.iter().fold(0, |acc, prev| acc ^ prev).pow(3); format!("{result}").into_response() } + +#[cfg(test)] +mod tests { + use super::*; + use axum::http::StatusCode; + use axum_test_helper::TestClient; + + #[tokio::test] + async fn num1_xor_num2_pow_3() { + let app = router(); + + let client = TestClient::new(app); + let res = client.get("/1/3/5").send().await; + assert_eq!(res.status(), StatusCode::OK); + let expected = ((3 ^ 5) as i32).pow(3); + assert_eq!(res.text().await, format!("{expected}")); + } + + #[tokio::test] + async fn one_packet_ids() { + let app = router(); + + let client = TestClient::new(app); + let res = client.get("/1/10").send().await; + assert_eq!(res.status(), StatusCode::OK); + let expected = 1000; + assert_eq!(res.text().await, format!("{expected}")); + } +} diff --git a/src/handlers/day4.rs b/src/handlers/day4.rs index 016a570..0cab53c 100644 --- a/src/handlers/day4.rs +++ b/src/handlers/day4.rs @@ -12,6 +12,7 @@ struct Reindeer { name: String, strength: i32, } + async fn sum_strength(Json(reindeers): Json>) -> impl IntoResponse { reindeers .iter() @@ -20,24 +21,24 @@ async fn sum_strength(Json(reindeers): Json>) -> impl IntoResponse } #[derive(Debug, Serialize, Deserialize)] -pub struct ContestReindeer { - pub name: String, - pub strength: i32, - pub speed: f32, - pub height: u32, - pub antler_width: u32, - pub snow_magic_power: i64, - pub favorite_food: String, +struct ContestReindeer { + name: String, + strength: i32, + speed: f32, + height: u32, + antler_width: u32, + snow_magic_power: i64, + favorite_food: String, #[serde(alias = "cAnD13s_3ATeN-yesT3rdAy")] - pub candies: i32, + candies: i32, } #[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)] -pub struct ContestResult { - pub fastest: String, - pub tallest: String, - pub magician: String, - pub consumer: String, +struct ContestResult { + fastest: String, + tallest: String, + magician: String, + consumer: String, } async fn contest(Json(reindeers): Json>) -> impl IntoResponse { let fastest = reindeers.iter().max_by(|a, b| a.strength.cmp(&b.strength)); @@ -67,3 +68,93 @@ async fn contest(Json(reindeers): Json>) -> impl IntoRespon _ => (StatusCode::BAD_REQUEST, "Invalid contest").into_response(), } } + +#[cfg(test)] +mod tests { + use super::*; + use axum::http::StatusCode; + use axum_test_helper::TestClient; + use serde_json::json; + + #[tokio::test] + async fn sum_of_strength() { + let app = router(); + let client = TestClient::new(app); + let res = client + .post("/4/strength") + .body( + json!([ + { "name": "Dasher", "strength": 5 }, + { "name": "Dancer", "strength": 6 }, + { "name": "Prancer", "strength": 4 }, + { "name": "Vixen", "strength": 7 } + ]) + .to_string(), + ) + .header("Content-Type", "application/json") + .send() + .await; + assert_eq!(res.status(), StatusCode::OK); + let expected = "22"; + assert_eq!(res.text().await, format!("{expected}")); + } + + #[tokio::test] + async fn valid_contest() { + let app = router(); + + let client = TestClient::new(app); + let res = client + .post("/4/contest") + .body( + json!([ + { + "name": "Dasher", + "strength": 5, + "speed": 50.4, + "height": 80, + "antler_width": 36, + "snow_magic_power": 9001, + "favorite_food": "hay", + "cAnD13s_3ATeN-yesT3rdAy": 2 + }, + { + "name": "Dancer", + "strength": 6, + "speed": 48.2, + "height": 65, + "antler_width": 37, + "snow_magic_power": 4004, + "favorite_food": "grass", + "cAnD13s_3ATeN-yesT3rdAy": 5 + } + ]) + .to_string(), + ) + .header("Content-Type", "application/json") + .send() + .await; + assert_eq!(res.status(), StatusCode::OK); + let expected = ContestResult { + fastest: "Speeding past the finish line with a strength of 6 is Dancer".to_string(), + tallest: "Dasher is standing tall with his 36 cm wide antlers".to_string(), + magician: "Dasher could blast you away with a snow magic power of 9001".to_string(), + consumer: "Dancer ate lots of candies, but also some grass".to_string(), + }; + assert_eq!(res.json::().await, expected); + } + + #[tokio::test] + async fn invalid_contest() { + let app = router(); + + let client = TestClient::new(app); + let res = client + .post("/4/contest") + .body(json!([]).to_string()) + .header("Content-Type", "application/json") + .send() + .await; + assert_eq!(res.status(), StatusCode::BAD_REQUEST); + } +} diff --git a/src/handlers/day6.rs b/src/handlers/day6.rs index fbd0547..ad3a6ff 100644 --- a/src/handlers/day6.rs +++ b/src/handlers/day6.rs @@ -6,12 +6,12 @@ pub fn router() -> axum::Router { } #[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)] -pub struct ElfOnShelfResult { - pub elf: u32, +struct ElfOnShelfResult { + elf: u32, #[serde(alias = "elf on a shelf")] - pub elf_on_shelf: u32, + elf_on_shelf: u32, #[serde(alias = "shelf with no elf on it")] - pub shelf_with_no_elf: u32, + shelf_with_no_elf: u32, } async fn elf_on_shelf(elf_text: String) -> impl IntoResponse { @@ -24,3 +24,52 @@ async fn elf_on_shelf(elf_text: String) -> impl IntoResponse { shelf_with_no_elf: shelf - elf_on_shelf, }) } + +#[cfg(test)] +mod tests { + use super::*; + use axum::http::StatusCode; + use axum_test_helper::TestClient; + + #[tokio::test] + async fn elf() { + let app = router(); + + let client = TestClient::new(app); + let res = client + .post("/6") + .body( + "The mischievous elf peeked out from behind the toy workshop, + and another elf joined in the festive dance. + Look, there is also an elf on that shelf!", + ) + .header("Content-Type", "text/plain") + .send() + .await; + assert_eq!(res.status(), StatusCode::OK); + let expected = ElfOnShelfResult { + elf: 4, + ..Default::default() + }; + assert_eq!(res.json::().await.elf, expected.elf); + } + #[tokio::test] + async fn elf_on_shelf() { + let app = router(); + + let client = TestClient::new(app); + let res = client + .post("/6") + .body("there is an elf on a shelf on an elf. there is also another shelf in Belfast.") + .header("Content-Type", "text/plain") + .send() + .await; + assert_eq!(res.status(), StatusCode::OK); + let expected = ElfOnShelfResult { + elf: 5, + shelf_with_no_elf: 1, + elf_on_shelf: 1, + }; + assert_eq!(res.json::().await, expected); + } +} diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 6ef9264..e199ede 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -3,9 +3,6 @@ mod day1; mod day4; mod day6; -pub use day4::{ContestReindeer, ContestResult}; -pub use day6::ElfOnShelfResult; - pub fn router() -> axum::Router { axum::Router::new() .nest("/", day0::router()) diff --git a/src/main.rs b/src/main.rs index 1be3714..359284d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,206 +10,3 @@ fn app() -> Router { async fn main() -> shuttle_axum::ShuttleAxum { Ok(app().into()) } - -#[cfg(test)] -mod tests { - use super::*; - use axum::http::StatusCode; - use axum_test_helper::TestClient; - use serde_json::json; - - #[tokio::test] - async fn hello_world() { - let app = app(); - - let client = TestClient::new(app); - let res = client.get("/").send().await; - assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await, "Hello, World!"); - } - - #[tokio::test] - async fn internal_server_error() { - let app = app(); - - let client = TestClient::new(app); - let res = client.get("/-1/error").send().await; - assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); - assert_eq!(res.text().await, ""); - } - - #[tokio::test] - async fn num1_xor_num2_pow_3() { - let app = app(); - - let client = TestClient::new(app); - let res = client.get("/1/3/5").send().await; - assert_eq!(res.status(), StatusCode::OK); - let expected = ((3 ^ 5) as i32).pow(3); - assert_eq!(res.text().await, format!("{expected}")); - } - - #[tokio::test] - async fn one_packet_ids() { - let app = app(); - - let client = TestClient::new(app); - let res = client.get("/1/10").send().await; - assert_eq!(res.status(), StatusCode::OK); - let expected = 1000; - assert_eq!(res.text().await, format!("{expected}")); - } - - #[tokio::test] - async fn multi_packet_ids() { - let app = app(); - - let client = TestClient::new(app); - let res = client.get("/1/4/5/8/10").send().await; - assert_eq!(res.status(), StatusCode::OK); - let expected = 27; - assert_eq!(res.text().await, format!("{expected}")); - } - - #[tokio::test] - async fn multi_packet_ids_more_than_20_ids() { - let app = app(); - - let client = TestClient::new(app); - let res = client - .get("/1/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21") - .send() - .await; - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - let expected = "packet ids must be between 1 and 20 inclusive packets in a sled"; - assert_eq!(res.text().await, format!("{expected}")); - } - - #[tokio::test] - async fn sum_of_strength() { - let app = app(); - - let client = TestClient::new(app); - let res = client - .post("/4/strength") - .body( - json!([ - { "name": "Dasher", "strength": 5 }, - { "name": "Dancer", "strength": 6 }, - { "name": "Prancer", "strength": 4 }, - { "name": "Vixen", "strength": 7 } - ]) - .to_string(), - ) - .header("Content-Type", "application/json") - .send() - .await; - assert_eq!(res.status(), StatusCode::OK); - let expected = "22"; - assert_eq!(res.text().await, format!("{expected}")); - } - - #[tokio::test] - async fn valid_contest() { - let app = app(); - - let client = TestClient::new(app); - let res = client - .post("/4/contest") - .body( - json!([ - { - "name": "Dasher", - "strength": 5, - "speed": 50.4, - "height": 80, - "antler_width": 36, - "snow_magic_power": 9001, - "favorite_food": "hay", - "cAnD13s_3ATeN-yesT3rdAy": 2 - }, - { - "name": "Dancer", - "strength": 6, - "speed": 48.2, - "height": 65, - "antler_width": 37, - "snow_magic_power": 4004, - "favorite_food": "grass", - "cAnD13s_3ATeN-yesT3rdAy": 5 - } - ]) - .to_string(), - ) - .header("Content-Type", "application/json") - .send() - .await; - assert_eq!(res.status(), StatusCode::OK); - let expected = handlers::ContestResult { - fastest: "Speeding past the finish line with a strength of 6 is Dancer".to_string(), - tallest: "Dasher is standing tall with his 36 cm wide antlers".to_string(), - magician: "Dasher could blast you away with a snow magic power of 9001".to_string(), - consumer: "Dancer ate lots of candies, but also some grass".to_string(), - }; - assert_eq!(res.json::().await, expected); - } - - #[tokio::test] - async fn invalid_contest() { - let app = app(); - - let client = TestClient::new(app); - let res = client - .post("/4/contest") - .body(json!([]).to_string()) - .header("Content-Type", "application/json") - .send() - .await; - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - } - - #[tokio::test] - async fn elf() { - let app = app(); - - let client = TestClient::new(app); - let res = client - .post("/6") - .body( - "The mischievous elf peeked out from behind the toy workshop, - and another elf joined in the festive dance. - Look, there is also an elf on that shelf!", - ) - .header("Content-Type", "text/plain") - .send() - .await; - assert_eq!(res.status(), StatusCode::OK); - let expected = handlers::ElfOnShelfResult { - elf: 4, - ..Default::default() - }; - assert_eq!( - res.json::().await.elf, - expected.elf - ); - } - #[tokio::test] - async fn elf_on_shelf() { - let app = app(); - - let client = TestClient::new(app); - let res = client - .post("/6") - .body("there is an elf on a shelf on an elf. there is also another shelf in Belfast.") - .header("Content-Type", "text/plain") - .send() - .await; - assert_eq!(res.status(), StatusCode::OK); - let expected = handlers::ElfOnShelfResult { - elf: 5, - shelf_with_no_elf: 1, - elf_on_shelf: 1, - }; - assert_eq!(res.json::().await, expected); - } -}