challange: 9
This commit is contained in:
parent
ef5b3820e2
commit
c8f3af6cf9
10
Cargo.toml
10
Cargo.toml
@ -5,14 +5,18 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.7.4", features = ["query"] }
|
||||
cargo-manifest = "0.17.0"
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
serde_json = "1.0.133"
|
||||
serde_yml = "0.0.12"
|
||||
shuttle-axum = "0.49.0"
|
||||
shuttle-runtime = "0.49.0"
|
||||
tokio = "1.28.2"
|
||||
toml = "0.8.19"
|
||||
cargo-manifest = { version = "0.17.0", optional = true }
|
||||
serde_yml = { version = "0.0.12", optional = true }
|
||||
toml = { version = "0.8.19", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
axum-test = "16.4.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
task1-9 = ["cargo-manifest", "serde_yml", "toml"]
|
||||
|
358
src/lib.rs
358
src/lib.rs
@ -1,14 +1,23 @@
|
||||
mod routes;
|
||||
|
||||
use axum::routing::post;
|
||||
pub use routes::hello_world;
|
||||
use routes::{hello_bird, ipv4_dest, ipv4_key, ipv6_dest, ipv6_key, manifest, minus_one};
|
||||
#[cfg(feature = "task1-9")]
|
||||
use routes::{
|
||||
hello_bird, hello_world, ipv4_dest, ipv4_key, ipv6_dest, ipv6_key, manifest, milk, minus_one,
|
||||
refill, MilkFactory,
|
||||
};
|
||||
|
||||
pub fn router() -> axum::Router {
|
||||
#[cfg(feature = "task1-9")]
|
||||
use axum::routing::get;
|
||||
#[cfg(feature = "task1-9")]
|
||||
use axum::routing::post;
|
||||
use axum::Router;
|
||||
|
||||
Router::new()
|
||||
#[cfg(feature = "task1-9")]
|
||||
let milk_factory = MilkFactory::new();
|
||||
|
||||
#[cfg(feature = "task1-9")]
|
||||
return Router::new()
|
||||
.route("/hello_world", get(hello_world))
|
||||
.route("/-1/seek", get(minus_one))
|
||||
.route("/2/dest", get(ipv4_dest))
|
||||
@ -16,338 +25,11 @@ pub fn router() -> axum::Router {
|
||||
.route("/2/v6/dest", get(ipv6_dest))
|
||||
.route("/2/v6/key", get(ipv6_key))
|
||||
.route("/5/manifest", post(manifest))
|
||||
.route("/", get(hello_bird))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use axum::http::StatusCode;
|
||||
use axum_test::TestServer;
|
||||
|
||||
fn test_server() -> TestServer {
|
||||
TestServer::new(router()).unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_hello_world() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/hello_world").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Hello, world!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_hello_bird() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Hello, bird!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_minus_one() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/-1/seek").await;
|
||||
response.assert_header("location", "https://www.youtube.com/watch?v=9Gc4QTqslN4");
|
||||
response.assert_status(StatusCode::FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ipv4_dest() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/2/dest?from=10.0.0.0&key=1.2.3.255").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("11.2.3.255");
|
||||
|
||||
let response = server.get("/2/dest?from=invalid-from&key=1.2.3.255").await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server.get("/2/dest?from=10.0.0.0&key=invalid-key").await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server
|
||||
.get("/2/dest?from=128.128.33.0&key=255.0.255.33")
|
||||
.await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("127.128.32.33");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ipv4_key() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/2/key?from=10.0.0.0&to=11.2.3.255").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("1.2.3.255");
|
||||
|
||||
let response = server.get("/2/key?from=invalid-from&to=1.2.3.255").await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server.get("/2/key?from=10.0.0.0&to=invalid-to").await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server
|
||||
.get("/2/key?from=128.128.33.0&to=127.128.32.33")
|
||||
.await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("255.0.255.33");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ipv6_dest() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/2/v6/dest?from=fe80::1&key=5:6:7::3333").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("fe85:6:7::3332");
|
||||
|
||||
let response = server
|
||||
.get("/2/v6/dest?from=invalid-from&key=5:6:7::3333")
|
||||
.await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server.get("/2/v6/dest?from=fe80::1&key=invalid-key").await;
|
||||
response.assert_status_bad_request();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ipv6_key() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server
|
||||
.get("/2/v6/key?from=aaaa::aaaa&to=5555:ffff:c:0:0:c:1234:5555")
|
||||
.await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("ffff:ffff:c::c:1234:ffff");
|
||||
|
||||
let response = server
|
||||
.get("/2/v6/dest?from=invalid-from&to=5:6:7::3333")
|
||||
.await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server.get("/2/v6/dest?from=fe80::1&to=invalid-to").await;
|
||||
response.assert_status_bad_request();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_manifest_1() {
|
||||
let server = test_server();
|
||||
|
||||
let want = "Toy car: 2\nLego brick: 230";
|
||||
|
||||
let payload = r#"[package]
|
||||
name = "not-a-gift-order"
|
||||
authors = ["Not Santa"]
|
||||
keywords = ["Christmas 2024"]
|
||||
|
||||
[[package.metadata.orders]]
|
||||
item = "Toy car"
|
||||
quantity = 2
|
||||
|
||||
[[package.metadata.orders]]
|
||||
item = "Lego brick"
|
||||
quantity = 230"#;
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("application/toml")
|
||||
.await;
|
||||
|
||||
response.assert_status_ok();
|
||||
response.assert_text(want);
|
||||
|
||||
let payload = r#"[package]
|
||||
name = "coal-in-a-bowl"
|
||||
authors = ["H4CK3R_13E7"]
|
||||
keywords = ["Christmas 2024"]
|
||||
|
||||
[[package.metadata.orders]]
|
||||
item = "Coal"
|
||||
quantity = "Hahaha get rekt""#;
|
||||
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("application/toml")
|
||||
.await;
|
||||
response.assert_status(StatusCode::NO_CONTENT);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_manifest_2() {
|
||||
let server = test_server();
|
||||
|
||||
let payload = r#"[package]
|
||||
name = false
|
||||
authors = ["Not Santa"]
|
||||
keywords = ["Christmas 2024"]"#;
|
||||
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("application/toml")
|
||||
.await;
|
||||
response.assert_status(StatusCode::BAD_REQUEST);
|
||||
|
||||
let payload = r#"[package]
|
||||
name = "not-a-gift-order"
|
||||
authors = ["Not Santa"]
|
||||
keywords = ["Christmas 2024"]
|
||||
|
||||
[profile.release]
|
||||
incremental = "stonks""#;
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("application/toml")
|
||||
.await;
|
||||
response.assert_status(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_manifest_3() {
|
||||
let server = test_server();
|
||||
|
||||
let want = "Toy car: 2\nLego brick: 230";
|
||||
|
||||
let payload = r#"[package]
|
||||
name = "not-a-gift-order"
|
||||
authors = ["Not Santa"]
|
||||
keywords = ["Christmas 2024"]
|
||||
|
||||
[[package.metadata.orders]]
|
||||
item = "Toy car"
|
||||
quantity = 2
|
||||
|
||||
[[package.metadata.orders]]
|
||||
item = "Lego brick"
|
||||
quantity = 230"#;
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("application/toml")
|
||||
.await;
|
||||
|
||||
response.assert_status_ok();
|
||||
response.assert_text(want);
|
||||
|
||||
let payload = r#"[package]
|
||||
name = "not-a-gift-order"
|
||||
authors = ["Not Santa"]
|
||||
|
||||
[[package.metadata.orders]]
|
||||
item = "Toy car"
|
||||
quantity = 2
|
||||
|
||||
[[package.metadata.orders]]
|
||||
item = "Lego brick"
|
||||
quantity = 230"#;
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("application/toml")
|
||||
.await;
|
||||
|
||||
response.assert_status(StatusCode::BAD_REQUEST);
|
||||
response.assert_text("Magic keyword not provided");
|
||||
|
||||
let payload = r#"[package]
|
||||
name = "grass"
|
||||
authors = ["A vegan cow"]
|
||||
keywords = ["Moooooo"]"#;
|
||||
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("application/toml")
|
||||
.await;
|
||||
response.assert_status(StatusCode::BAD_REQUEST);
|
||||
response.assert_text("Magic keyword not provided");
|
||||
|
||||
let payload = r#"[package]
|
||||
name = "grass"
|
||||
authors = ["A vegan cow"]
|
||||
keywords = ["Christmas 2024"]"#;
|
||||
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("application/toml")
|
||||
.await;
|
||||
response.assert_status(StatusCode::NO_CONTENT);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_manifest_4() {
|
||||
let server = test_server();
|
||||
|
||||
let payload = r#"[package]
|
||||
name = "grass"
|
||||
authors = ["A vegan cow"]
|
||||
keywords = ["Christmas 2024"]"#;
|
||||
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("text/html")
|
||||
.await;
|
||||
response.assert_status(StatusCode::UNSUPPORTED_MEDIA_TYPE);
|
||||
|
||||
let want = "Toy train: 5";
|
||||
|
||||
let payload = r#"package:
|
||||
name: big-chungus-sleigh
|
||||
version: "2.0.24"
|
||||
metadata:
|
||||
orders:
|
||||
- item: "Toy train"
|
||||
quantity: 5
|
||||
rust-version: "1.69"
|
||||
keywords:
|
||||
- "Christmas 2024""#;
|
||||
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("application/yaml")
|
||||
.await;
|
||||
|
||||
response.assert_status_ok();
|
||||
response.assert_text(want);
|
||||
|
||||
let want = "Toy train: 5";
|
||||
|
||||
let payload = r#"{
|
||||
"package": {
|
||||
"name": "big-chungus-sleigh",
|
||||
"version": "2.0.24",
|
||||
"metadata": {
|
||||
"orders": [
|
||||
{
|
||||
"item": "Toy train",
|
||||
"quantity": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
"rust-version": "1.69",
|
||||
"keywords": [
|
||||
"Christmas 2024"
|
||||
]
|
||||
}
|
||||
}"#;
|
||||
|
||||
let response = server
|
||||
.post("/5/manifest")
|
||||
.text(payload)
|
||||
.content_type("application/json")
|
||||
.await;
|
||||
|
||||
response.assert_status_ok();
|
||||
response.assert_text(want);
|
||||
}
|
||||
.route("/9/milk", post(milk))
|
||||
.route("/9/refill", post(refill))
|
||||
.with_state(milk_factory)
|
||||
.route("/", get(hello_bird));
|
||||
|
||||
// #[cfg(feature="task12")]
|
||||
Router::new()
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
#![cfg(feature = "task1-9")]
|
||||
mod hello_bird;
|
||||
mod hello_world;
|
||||
mod minus_one;
|
||||
mod task_five;
|
||||
mod task_nine;
|
||||
mod task_two;
|
||||
|
||||
pub use hello_bird::hello_bird;
|
||||
@ -12,3 +14,5 @@ pub use task_two::ipv4_dest;
|
||||
pub use task_two::ipv4_key;
|
||||
pub use task_two::ipv6_dest;
|
||||
pub use task_two::ipv6_key;
|
||||
|
||||
pub use task_nine::{milk, refill, MilkFactory};
|
||||
|
47
src/routes/task_nine/milk.rs
Normal file
47
src/routes/task_nine/milk.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use axum::{
|
||||
extract::State,
|
||||
http::{header::CONTENT_TYPE, HeaderMap, StatusCode},
|
||||
response::IntoResponse,
|
||||
};
|
||||
|
||||
use super::{MilkFactory, Unit};
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
pub async fn refill(State(milk_factory): State<MilkFactory>) -> impl IntoResponse {
|
||||
milk_factory.magic_refill();
|
||||
String::new().into_response()
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
pub async fn milk(
|
||||
State(milk_factory): State<MilkFactory>,
|
||||
headers: HeaderMap,
|
||||
payload: String,
|
||||
) -> Result<String, impl IntoResponse> {
|
||||
headers.get(CONTENT_TYPE).map_or_else(
|
||||
|| match milk_factory.withdraw() {
|
||||
Ok(message) => Ok(message.to_string()),
|
||||
Err(message) => Err(message.into_response()),
|
||||
},
|
||||
|content_type| match content_type.to_str().unwrap() {
|
||||
"application/json" => Unit::from_json(&payload).map_or_else(
|
||||
|_| {
|
||||
let _ = milk_factory.withdraw();
|
||||
Err((StatusCode::BAD_REQUEST, "").into_response())
|
||||
},
|
||||
|unit| match unit {
|
||||
Unit::Liters(_) | Unit::Gallons(_) | Unit::Litres(_) | Unit::Pints(_) => {
|
||||
match milk_factory.withdraw() {
|
||||
Ok(_) => Ok(unit.json().unwrap()),
|
||||
Err(message) => Err(message.into_response()),
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
_ => match milk_factory.withdraw() {
|
||||
Ok(message) => Ok(message.to_string()),
|
||||
Err(message) => Err(message.into_response()),
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
153
src/routes/task_nine/milk_factory.rs
Normal file
153
src/routes/task_nine/milk_factory.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
sync::{atomic::AtomicU32, Arc},
|
||||
thread,
|
||||
};
|
||||
|
||||
use axum::{http::StatusCode, response::IntoResponse};
|
||||
use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MilkFactory {
|
||||
stock: Arc<AtomicU32>,
|
||||
}
|
||||
|
||||
impl MilkFactory {
|
||||
pub fn new() -> Self {
|
||||
let mf = Self {
|
||||
stock: Arc::new(AtomicU32::new(5)),
|
||||
};
|
||||
mf.run();
|
||||
mf
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
thread::spawn({
|
||||
let stock = self.stock.clone();
|
||||
move || {
|
||||
let mut start = std::time::Instant::now();
|
||||
loop {
|
||||
if start.elapsed().as_millis() >= 990 {
|
||||
let current_stock = stock.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if current_stock < 5 {
|
||||
stock.store(current_stock + 1, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
start = std::time::Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn magic_refill(&self) {
|
||||
self.stock.store(5, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn withdraw(&self) -> Result<MilkMessage, MilkMessage> {
|
||||
let current_stock = self.stock.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if current_stock < 1 {
|
||||
return Err(MilkMessage::NoMilkAvailable);
|
||||
}
|
||||
self.stock
|
||||
.store(current_stock - 1, std::sync::atomic::Ordering::Relaxed);
|
||||
Ok(MilkMessage::Withdraw)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum MilkMessage {
|
||||
Withdraw,
|
||||
Refill,
|
||||
NoMilkAvailable,
|
||||
WithdrawUnit(Unit),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Copy, Clone, PartialEq)]
|
||||
pub enum Unit {
|
||||
#[serde(rename(deserialize = "liters", serialize = "gallons"))]
|
||||
Liters(f32),
|
||||
#[serde(rename(deserialize = "gallons", serialize = "liters"))]
|
||||
Gallons(f32),
|
||||
#[serde(rename(deserialize = "litres", serialize = "pints"))]
|
||||
Litres(f32),
|
||||
#[serde(rename(deserialize = "pints", serialize = "litres"))]
|
||||
Pints(f32),
|
||||
}
|
||||
|
||||
impl Unit {
|
||||
pub fn from_json(json: &str) -> Result<Self, serde_json::error::Error> {
|
||||
serde_json::from_str(json)
|
||||
}
|
||||
|
||||
pub fn json(self) -> Result<String, serde_json::error::Error> {
|
||||
serde_json::to_string(&self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Unit {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("Unit", 1)?;
|
||||
match self {
|
||||
Self::Liters(value) => {
|
||||
state.serialize_field::<f32>("gallons", &(*value * 0.264_172_06))?;
|
||||
}
|
||||
Self::Gallons(value) => {
|
||||
state.serialize_field::<f32>("liters", &(*value * 3.785_412))?;
|
||||
}
|
||||
Self::Litres(value) => {
|
||||
state.serialize_field::<f32>("pints", &(*value * 1.759_754))?;
|
||||
}
|
||||
Self::Pints(value) => {
|
||||
state.serialize_field::<f32>("litres", &(*value * 0.568_261))?;
|
||||
}
|
||||
}
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unit() {
|
||||
let unit = Unit::Liters(5.0);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&unit).unwrap(),
|
||||
r#"{"gallons":1.3208603}"#
|
||||
);
|
||||
|
||||
let unit = Unit::from_json(serde_json::json!({"liters": 5.0}).to_string().as_str()).unwrap();
|
||||
assert_eq!(unit, Unit::Liters(5.0));
|
||||
assert_eq!(unit.json().unwrap(), r#"{"gallons":1.3208603}"#);
|
||||
|
||||
let unit = Unit::from_json(
|
||||
serde_json::json!({"gallons": -2.000_000_000_000_001})
|
||||
.to_string()
|
||||
.as_str(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(unit, Unit::Gallons(-2.000_000_000_000_001));
|
||||
assert_eq!(unit.json().unwrap(), r#"{"liters":-7.570824}"#);
|
||||
}
|
||||
|
||||
impl Display for MilkMessage {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Withdraw => writeln!(f, "Milk withdrawn"),
|
||||
Self::Refill => writeln!(f, "Refilled milk"),
|
||||
Self::NoMilkAvailable => writeln!(f, "No milk available"),
|
||||
Self::WithdrawUnit(u) => write!(f, "{}", u.json().unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for MilkMessage {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
match self {
|
||||
Self::Withdraw | Self::WithdrawUnit(_) => (StatusCode::OK, self.to_string()),
|
||||
Self::Refill => (StatusCode::OK, String::new()),
|
||||
Self::NoMilkAvailable => (StatusCode::TOO_MANY_REQUESTS, self.to_string()),
|
||||
}
|
||||
.into_response()
|
||||
}
|
||||
}
|
6
src/routes/task_nine/mod.rs
Normal file
6
src/routes/task_nine/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
mod milk;
|
||||
mod milk_factory;
|
||||
|
||||
pub use milk::{milk, refill};
|
||||
|
||||
pub use milk_factory::{MilkFactory, Unit};
|
38
tests/minus_one/main.rs
Normal file
38
tests/minus_one/main.rs
Normal file
@ -0,0 +1,38 @@
|
||||
#[cfg(all(test, feature = "task1-9"))]
|
||||
mod minus_one {
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use axum_test::TestServer;
|
||||
use itsscb_shuttlings_cch24::router;
|
||||
|
||||
fn test_server() -> TestServer {
|
||||
TestServer::new(router()).unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_hello_world() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/hello_world").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Hello, world!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_hello_bird() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Hello, bird!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_minus_one() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/-1/seek").await;
|
||||
response.assert_header("location", "https://www.youtube.com/watch?v=9Gc4QTqslN4");
|
||||
response.assert_status(StatusCode::FOUND);
|
||||
}
|
||||
}
|
121
tests/task_five/main.rs
Normal file
121
tests/task_five/main.rs
Normal file
@ -0,0 +1,121 @@
|
||||
#[cfg(all(test, feature = "task1-9"))]
|
||||
mod task_five {
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use axum_test::TestServer;
|
||||
use itsscb_shuttlings_cch24::router;
|
||||
|
||||
fn test_server() -> TestServer {
|
||||
TestServer::new(router()).unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_milk() {
|
||||
let sever = test_server();
|
||||
|
||||
let response = sever.post("/9/milk").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Milk withdrawn\n");
|
||||
let response = sever.post("/9/milk").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Milk withdrawn\n");
|
||||
let response = sever.post("/9/milk").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Milk withdrawn\n");
|
||||
let response = sever.post("/9/milk").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Milk withdrawn\n");
|
||||
let response = sever.post("/9/milk").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Milk withdrawn\n");
|
||||
let response = sever.post("/9/milk").await;
|
||||
response.assert_status(StatusCode::TOO_MANY_REQUESTS);
|
||||
|
||||
let sever = test_server();
|
||||
|
||||
for i in 0..=10 {
|
||||
let response = sever.post("/9/milk").await;
|
||||
match i {
|
||||
0..=4 | 6 | 8 | 9 => {
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Milk withdrawn\n");
|
||||
}
|
||||
5 | 7 | 10 => {
|
||||
response.assert_status(StatusCode::TOO_MANY_REQUESTS);
|
||||
response.assert_text("No milk available\n");
|
||||
match i {
|
||||
5 => thread::sleep(Duration::from_secs(1)),
|
||||
7 => thread::sleep(Duration::from_secs(2)),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
response.assert_status(StatusCode::SERVICE_UNAVAILABLE);
|
||||
response.assert_text("No milk available\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let sever = test_server();
|
||||
|
||||
let response = sever
|
||||
.post("/9/milk")
|
||||
.text(r#"{"liters":5}"#)
|
||||
.content_type("application/json")
|
||||
.await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text(r#"{"gallons":1.3208603}"#);
|
||||
|
||||
let sever = test_server();
|
||||
|
||||
let response = sever
|
||||
.post("/9/milk")
|
||||
.text(r#"{"gallons":5}"#)
|
||||
.content_type("application/json")
|
||||
.await;
|
||||
|
||||
response.assert_status_ok();
|
||||
response.assert_text(r#"{"liters":18.927061}"#);
|
||||
|
||||
let sever = test_server();
|
||||
|
||||
let response = sever
|
||||
.post("/9/milk")
|
||||
.text(r#"{"liters":1, "gallons":5}"#)
|
||||
.content_type("application/json")
|
||||
.await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = sever
|
||||
.post("/9/milk")
|
||||
.text(r#"{"litres":2}"#)
|
||||
.content_type("application/json")
|
||||
.await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text(r#"{"pints":3.519508}"#);
|
||||
|
||||
let sever = test_server();
|
||||
|
||||
for i in 0..=11 {
|
||||
let response = sever.post("/9/milk").await;
|
||||
match i {
|
||||
0..=4 | 6..=10 => {
|
||||
response.assert_status_ok();
|
||||
response.assert_text("Milk withdrawn\n");
|
||||
}
|
||||
5 | 11 => {
|
||||
response.assert_status(StatusCode::TOO_MANY_REQUESTS);
|
||||
response.assert_text("No milk available\n");
|
||||
let response = sever.post("/9/refill").await;
|
||||
response.assert_status_ok();
|
||||
}
|
||||
_ => {
|
||||
response.assert_status(StatusCode::SERVICE_UNAVAILABLE);
|
||||
response.assert_text("No milk available\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
87
tests/task_two/main.rs
Normal file
87
tests/task_two/main.rs
Normal file
@ -0,0 +1,87 @@
|
||||
#[cfg(all(test, feature = "task1-9"))]
|
||||
mod task_two {
|
||||
use axum_test::TestServer;
|
||||
use itsscb_shuttlings_cch24::router;
|
||||
|
||||
fn test_server() -> TestServer {
|
||||
TestServer::new(router()).unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ipv4_dest() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/2/dest?from=10.0.0.0&key=1.2.3.255").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("11.2.3.255");
|
||||
|
||||
let response = server.get("/2/dest?from=invalid-from&key=1.2.3.255").await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server.get("/2/dest?from=10.0.0.0&key=invalid-key").await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server
|
||||
.get("/2/dest?from=128.128.33.0&key=255.0.255.33")
|
||||
.await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("127.128.32.33");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ipv4_key() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/2/key?from=10.0.0.0&to=11.2.3.255").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("1.2.3.255");
|
||||
|
||||
let response = server.get("/2/key?from=invalid-from&to=1.2.3.255").await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server.get("/2/key?from=10.0.0.0&to=invalid-to").await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server
|
||||
.get("/2/key?from=128.128.33.0&to=127.128.32.33")
|
||||
.await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("255.0.255.33");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ipv6_dest() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server.get("/2/v6/dest?from=fe80::1&key=5:6:7::3333").await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("fe85:6:7::3332");
|
||||
|
||||
let response = server
|
||||
.get("/2/v6/dest?from=invalid-from&key=5:6:7::3333")
|
||||
.await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server.get("/2/v6/dest?from=fe80::1&key=invalid-key").await;
|
||||
response.assert_status_bad_request();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ipv6_key() {
|
||||
let server = test_server();
|
||||
|
||||
let response = server
|
||||
.get("/2/v6/key?from=aaaa::aaaa&to=5555:ffff:c:0:0:c:1234:5555")
|
||||
.await;
|
||||
response.assert_status_ok();
|
||||
response.assert_text("ffff:ffff:c::c:1234:ffff");
|
||||
|
||||
let response = server
|
||||
.get("/2/v6/dest?from=invalid-from&to=5:6:7::3333")
|
||||
.await;
|
||||
response.assert_status_bad_request();
|
||||
|
||||
let response = server.get("/2/v6/dest?from=fe80::1&to=invalid-to").await;
|
||||
response.assert_status_bad_request();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user