diff --git a/Cargo.lock b/Cargo.lock index 6ebad2e..c00e80e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,6 +47,27 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -161,6 +182,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.5" @@ -279,6 +306,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb7bdea464ae038f09197b82430b921c53619fc8d2bcaf7b151013b3ca008017" +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "cookie" version = "0.18.0" @@ -368,6 +404,25 @@ dependencies = [ "typenum", ] +[[package]] +name = "deadpool" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" +dependencies = [ + "async-trait", + "deadpool-runtime", + "num_cpus", + "retain_mut", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49" + [[package]] name = "deranged" version = "0.3.9" @@ -477,6 +532,21 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fastrand" version = "2.0.1" @@ -668,6 +738,21 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.29" @@ -691,6 +776,12 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + [[package]] name = "futures-util" version = "0.3.29" @@ -742,6 +833,17 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.11" @@ -750,7 +852,7 @@ checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -854,6 +956,27 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-types" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" +dependencies = [ + "anyhow", + "async-channel", + "base64 0.13.1", + "futures-lite", + "http", + "infer", + "pin-project-lite", + "rand 0.7.3", + "serde", + "serde_json", + "serde_qs", + "serde_urlencoded", + "url", +] + [[package]] name = "httparse" version = "1.8.0" @@ -994,6 +1117,12 @@ dependencies = [ "serde", ] +[[package]] +name = "infer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" + [[package]] name = "inlinable_string" version = "0.1.15" @@ -1020,6 +1149,15 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "intl-memoizer" version = "0.5.1" @@ -1208,7 +1346,7 @@ checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -1512,7 +1650,7 @@ dependencies = [ "opentelemetry", "ordered-float", "percent-encoding", - "rand", + "rand 0.8.5", "serde_json", "thiserror", "tokio", @@ -1566,9 +1704,12 @@ dependencies = [ "serde_json", "serde_yaml", "sha256", + "test-log", "tokio", "tracing", + "tracing-subscriber", "unic-langid", + "wiremock", ] [[package]] @@ -1581,6 +1722,12 @@ dependencies = [ "serde", ] +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot" version = "0.12.1" @@ -1714,7 +1861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -1843,6 +1990,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -1850,8 +2010,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -1861,7 +2031,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -1870,7 +2049,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.11", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -1961,7 +2149,7 @@ version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ - "base64", + "base64 0.21.5", "bytes", "encoding_rs", "futures-core", @@ -1993,6 +2181,12 @@ dependencies = [ "winreg", ] +[[package]] +name = "retain_mut" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" + [[package]] name = "rocket" version = "0.5.0" @@ -2014,7 +2208,7 @@ dependencies = [ "num_cpus", "parking_lot", "pin-project-lite", - "rand", + "rand 0.8.5", "ref-cast", "rocket_codegen", "rocket_http", @@ -2224,6 +2418,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_qs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + [[package]] name = "serde_spanned" version = "0.6.4" @@ -2437,7 +2642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", - "fastrand", + "fastrand 2.0.1", "redox_syscall 0.4.1", "rustix", "windows-sys", @@ -2457,7 +2662,7 @@ dependencies = [ "percent-encoding", "pest", "pest_derive", - "rand", + "rand 0.8.5", "regex", "serde", "serde_json", @@ -2465,6 +2670,17 @@ dependencies = [ "unic-segment", ] +[[package]] +name = "test-log" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f66edd6b6cd810743c0c71e1d085e92b01ce6a72782032e3f794c8284fe4bcdd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "thiserror" version = "1.0.50" @@ -2665,7 +2881,7 @@ checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ "async-trait", "axum", - "base64", + "base64 0.21.5", "bytes", "futures-core", "futures-util", @@ -2696,7 +2912,7 @@ dependencies = [ "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand", + "rand 0.8.5", "slab", "tokio", "tokio-util", @@ -2977,6 +3193,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -2991,7 +3208,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ - "getrandom", + "getrandom 0.2.11", ] [[package]] @@ -3012,6 +3229,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + [[package]] name = "walkdir" version = "2.4.0" @@ -3031,6 +3254,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3257,6 +3486,28 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "wiremock" +version = "0.5.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a3a53eaf34f390dd30d7b1b078287dd05df2aa2e21a589ccb80f5c7253c2e9" +dependencies = [ + "assert-json-diff", + "async-trait", + "base64 0.21.5", + "deadpool", + "futures", + "futures-timer", + "http-types", + "hyper", + "log", + "once_cell", + "regex", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 9137ebd..90cb557 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,6 @@ tracing = "0.1.40" [dev-dependencies] serde_json = "1.0.108" +wiremock = "0.5.22" +test-log = { version = "0.2.13", features = ["trace"], default-features = false} +tracing-subscriber = { version = "0.3.18", features = ["env-filter", "fmt"] } diff --git a/src/lib.rs b/src/lib.rs index 60fa4da..9be6a60 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ use nanobyte_tera::{hosts::static_filter, l18n::translate_filter, date::{calculate_age, get_year}, string::insert_space_every, gravatar::gravatar_link}; use ovlach_tera::entity::lang_entity; -use rocket::{*, fairing::AdHoc}; +use rocket::{*, fairing::{AdHoc, Fairing}}; use rocket_dyn_templates::Template; use ::serde::{Deserialize, Serialize}; use tools::tera::advanced_filter; @@ -35,6 +35,22 @@ pub struct DefaultPerson { default_person_name: String, } +pub fn template_fairing() -> impl Fairing { + Template::try_custom(|engines| { + engines.tera.register_filter("static", static_filter); + engines.tera.register_filter("translate", translate_filter); + engines.tera.register_filter("calculate_age", calculate_age); + engines.tera.register_filter("insert_space_every", insert_space_every); + engines.tera.register_filter("gravatar_link", gravatar_link); + // filters specific to API + engines.tera.register_filter("lang_entity", lang_entity); + engines.tera.register_filter("format_date", get_year); // deprecated + engines.tera.register_filter("get_year", get_year); + engines.tera.register_filter("advanced_filter", advanced_filter); + Ok(()) + }) +} + pub fn rocket_builder() -> Rocket { let rocket = rocket::build(); @@ -43,19 +59,7 @@ pub fn rocket_builder() -> Rocket { //let config: PresentationConfig = figment.extract().expect("config"); rocket.attach( - Template::try_custom(|engines| { - engines.tera.register_filter("static", static_filter); - engines.tera.register_filter("translate", translate_filter); - engines.tera.register_filter("calculate_age", calculate_age); - engines.tera.register_filter("insert_space_every", insert_space_every); - engines.tera.register_filter("gravatar_link", gravatar_link); - // filters specific to API - engines.tera.register_filter("lang_entity", lang_entity); - engines.tera.register_filter("format_date", get_year); // deprecated - engines.tera.register_filter("get_year", get_year); - engines.tera.register_filter("advanced_filter", advanced_filter); - Ok(()) - }) + template_fairing() ).attach( AdHoc::config::() ).attach( diff --git a/src/routes/root.rs b/src/routes/root.rs index 73a6b65..75d9688 100644 --- a/src/routes/root.rs +++ b/src/routes/root.rs @@ -48,3 +48,104 @@ pub fn index_without_lang() -> Redirect { // Default language is czech (TODO: config) Redirect::to(format!("{}/{}", "", "cs")) } + +#[cfg(test)] +mod test { + use std::collections::HashMap; + use crate::template_fairing; + + use super::*; + + use chrono::NaiveDate; + use nanobyte_opentelemetry::rocket::TracingFairing; + use ovlach_data::cv::data::{CV, SocalLinks, Person}; + use rocket::local::asynchronous::Client; + use rocket::{routes, Build}; + use serde_json::json; + use wiremock::{MockServer, Mock, ResponseTemplate}; + use wiremock::matchers::{method, path}; + + + fn create_cv() -> CV { + let mut about = HashMap::::new(); + about.insert("cs".to_string(), "czech".to_string()); + CV { + person: Person { + name: "TEST@NAME".to_string(), + email: "TEST@EMAIL".to_string(), + surname: "TEST@SURNAME".to_string(), + birthday: NaiveDate::from_ymd_opt(1980, 1, 1).unwrap(), + phone: 800017002, + adress: None, + social: SocalLinks { + facebook: None, + linkedin: None, + github: None, + instagram: None, + mastodon: None, + }, + about, + }, + languages: vec![], + education: vec![], + jobs: vec![], + skills: vec![], + } + } + + fn build_rocket(port: u16) -> rocket::Rocket { + let data_source = CVBackendConfig{ + cv_backend_path: format!("http://localhost:{}", port), + }; + + let presentation_config = PresentationConfig { + static_route: format!("http://localhost:{}", port), + }; + + let default_person = DefaultPerson { + default_person_name: "foo".to_string(), + }; + + let frontend_svc = FrontendSVCConfig { + contact_service: "http://localhost:8080".to_string(), + pdf_service: "http://localhost:8080".to_string(), + }; + + rocket::build() + .mount("/", routes![index]) + .attach(TracingFairing::ignite()) + .attach(template_fairing()) + .manage(presentation_config) + .manage(default_person) + .manage(data_source) + .manage(frontend_svc) + } + + #[test_log::test(tokio::test)] + async fn main() { + // Start a background HTTP server on a random local port + let mock_server = MockServer::start().await; + + // Arrange the behaviour of the MockServer adding a Mock: + // when it receives a GET request on '/hello' it will respond with a 200. + Mock::given(method("GET")) + .and(path("/api/v1/cv/foo")) + .respond_with(ResponseTemplate::new(200).set_body_json(json!(create_cv()))) + // Mounting the mock on the mock server - it's now effective! + .mount(&mock_server) + .await; + + let rocket = build_rocket(mock_server.address().port()); + let client = Client::tracked(rocket).await.unwrap(); + let response = client + .get("/cs").dispatch().await; + + assert_eq!(response.status(), Status::Ok); + assert!(response.body().is_some()); + assert_eq!(response.content_type(), Some("text/html".parse().unwrap())); + let body = response.into_string().await.unwrap(); + assert!(body.contains("TEST@NAME")); + assert!(body.contains("TEST@EMAIL")); + assert!(body.contains("TEST@SURNAME")) + } +} diff --git a/src/services/cv.rs b/src/services/cv.rs index 3a5a015..3a74252 100644 --- a/src/services/cv.rs +++ b/src/services/cv.rs @@ -16,7 +16,7 @@ impl From for FetchError { #[instrument] pub async fn fetch_cv_data_from_backend(backend_host: &String, person_name: &String, client: &Client) -> Result { - let url = format!("{}/{}/{}", backend_host, "/api/v1/cv", person_name); + let url = format!("{}/{}/{}", backend_host, "api/v1/cv", person_name); debug!("Fetching CV data from backend: {}", url); let resp = client .get(url).send()