From: Matthieu Date: Mon, 18 Oct 2021 06:44:53 +0000 (+0400) Subject: fix tests and adapt webhook X-Git-Tag: v0.1~55^2~2 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=beacbc6c79c2543bf5ed7386554e3ac6b2d1b9a0;p=matthieu%2Fnova.git fix tests and adapt webhook --- diff --git a/webhook/BUILD b/webhook/BUILD index 4340909..82f3305 100644 --- a/webhook/BUILD +++ b/webhook/BUILD @@ -1,5 +1,5 @@ load("@rules_rust//rust:rust.bzl", "rust_binary", "rust_test") -load("//cargo:crates.bzl", "all_crate_deps") +load("//cargo:crates.bzl", "all_crate_deps", "crate_deps") load("@io_bazel_rules_docker//rust:image.bzl", "rust_image") test_suite(name = "tests") @@ -7,6 +7,7 @@ test_suite(name = "tests") rust_binary( name = "webhook", srcs = glob(["src/**"]), + proc_macro_deps = crate_deps(["ctor"]), visibility = ["//visibility:public"], deps = all_crate_deps() + ["//common/rust:common"], ) diff --git a/webhook/Cargo.toml b/webhook/Cargo.toml index 7e73c43..9e6b507 100644 --- a/webhook/Cargo.toml +++ b/webhook/Cargo.toml @@ -13,6 +13,7 @@ hex = "0.4.3" serde_json = { version = "1.0" } libc = "0.2.101" lazy_static = "1.4.0" +ctor = "0.1.21" [[bin]] name = "webhook" diff --git a/webhook/cargo/BUILD.bazel b/webhook/cargo/BUILD.bazel index 4569cc1..3988edc 100644 --- a/webhook/cargo/BUILD.bazel +++ b/webhook/cargo/BUILD.bazel @@ -12,6 +12,15 @@ licenses([ ]) # Aliased targets +alias( + name = "ctor", + actual = "@raze__ctor__0_1_21//:ctor", + tags = [ + "cargo-raze", + "manual", + ], +) + alias( name = "hex", actual = "@raze__hex__0_4_3//:hex", diff --git a/webhook/src/handler/error.rs b/webhook/src/handler/error.rs index ccec59f..f998b84 100644 --- a/webhook/src/handler/error.rs +++ b/webhook/src/handler/error.rs @@ -1,4 +1,4 @@ -use hyper::{Body, Error, Response, StatusCode}; +use hyper::{Body, Response, StatusCode}; pub struct WebhookError { pub code: StatusCode, diff --git a/webhook/src/handler/handler.rs b/webhook/src/handler/handler.rs index 4af2ba6..74a1762 100644 --- a/webhook/src/handler/handler.rs +++ b/webhook/src/handler/handler.rs @@ -1,8 +1,10 @@ use super::error::WebhookError; -use super::{signature::validate_signature, types::Interaction}; +use super::signature::validate_signature; use crate::config::Config; -use common::log::{debug, error, info}; +use common::discord_models::slash_commands::{Interaction, InteractionRequestType}; +use common::log::{debug, error}; use common::nats_crate::Connection; +use common::payloads::CacheData; use hyper::{ body::{to_bytes, Bytes}, service::Service, @@ -79,15 +81,12 @@ impl HandlerService { let utf8 = from_utf8(&data); match utf8 { Ok(data) => match serde_json::from_str::(data) { - Ok(value) => { - if value.t == 1 { - info!("sending pong"); - // a ping must be responded with another ping - return Ok(Response::builder() - .header("Content-Type", "application/json") - .body(serde_json::to_string(&Ping { t: 1 }).unwrap().into()) - .unwrap()); - } else { + Ok(value) => match value.type_ { + InteractionRequestType::Ping => Ok(Response::builder() + .header("Content-Type", "application/json") + .body(serde_json::to_string(&Ping { t: 1 }).unwrap().into()) + .unwrap()), + _ => { debug!("calling nats"); // this should hopefully not fail ? let payload = @@ -96,8 +95,9 @@ impl HandlerService { node_id: "".to_string(), span: None, }, - operation: "".to_string(), - data: value, + data: CacheData::InteractionCreate { + interaction: Box::new(value), + }, }) .unwrap(); @@ -120,7 +120,7 @@ impl HandlerService { } } } - } + }, Err(_) => Err(WebhookError::new( StatusCode::BAD_REQUEST, diff --git a/webhook/src/handler/mod.rs b/webhook/src/handler/mod.rs index a437dd5..20a977a 100644 --- a/webhook/src/handler/mod.rs +++ b/webhook/src/handler/mod.rs @@ -1,8 +1,7 @@ +mod error; +mod handler; pub mod make_service; mod signature; -mod handler; -mod types; -mod error; #[cfg(test)] -pub mod tests; \ No newline at end of file +pub mod tests; diff --git a/webhook/src/handler/signature.rs b/webhook/src/handler/signature.rs index c12f9e8..e4c7d53 100644 --- a/webhook/src/handler/signature.rs +++ b/webhook/src/handler/signature.rs @@ -8,16 +8,16 @@ lazy_static::lazy_static! { &["signature"] ).unwrap(); - static ref SIGNATURE_BODY_COUNTER: Counter = register_counter!(opts!( - "nova_webhook_", - "", + static ref SIGNATURE_COUNTER: Counter = register_counter!(opts!( + "nova_webhook_signatures_verify", + "number of signatures verification issued by the service", labels! {"handler" => "webhook_main"} )).unwrap(); } /// Checks the signature of a given data using the hex signature and the public key. pub fn validate_signature(hex_public_key: &str, data: &Vec, hex_signature: &str) -> bool { - SIGNATURE_BODY_COUNTER.inc(); + SIGNATURE_COUNTER.inc(); let timer = SIGNATURE_TIME_HISTOGRAM.with_label_values(&["webhook_main"]).start_timer(); // First, we need to check if the signature & private key is valid base64. diff --git a/webhook/src/handler/tests/handler_integration.rs b/webhook/src/handler/tests/handler_integration.rs index fd0d67f..fea4920 100644 --- a/webhook/src/handler/tests/handler_integration.rs +++ b/webhook/src/handler/tests/handler_integration.rs @@ -1,106 +1,103 @@ use std::time::Duration; - use crate::{ config::Config, handler::tests::utils::{generate_keypair, sign_message}, start, }; -use common::{config::test_init, nats_crate::Connection, testcontainers::{Image, images::generic::WaitFor}}; +use common::{ + config::test_init, + nats_crate::Connection, + testcontainers::{images::generic::WaitFor, Image}, +}; use common::{ config::Settings, log::info, testcontainers::{clients::Cli, images::generic::GenericImage, Container, Docker}, }; -use hyper::{Body, Method, Request}; -use lazy_static::{__Deref, lazy_static}; +use hyper::{Body, Method, Request, StatusCode}; +use lazy_static::{lazy_static}; use serde_json::json; +use ctor; -#[cfg(all(unix, target_arch = "x86_64"))] const fn nats_image<'a>() -> &'a str { + #[cfg(all(unix, target_arch = "x86_64"))] return "amd64/nats"; -} - -#[cfg(all(unix, target_arch = "aarch64"))] -const fn nats_image<'a>() -> &'a str { + #[cfg(all(unix, target_arch = "aarch64"))] return "arm64v8/nats"; -} - -#[cfg(all(target_arch = "x86_64", target_os = "windows"))] -const fn nats_image<'a>() -> &'a str { + #[cfg(all(target_arch = "x86_64", target_os = "windows"))] return "winamd64/nats"; } +static mut NATS: Option> = None; +static mut SETTINGS: Option> = None; + lazy_static! { + static ref KEYPAIR: (String, [u8; 64]) = generate_keypair(); static ref DOCKER: Cli = Cli::default(); +} - static ref NATS_CONTAINER: Container<'static, Cli, GenericImage> = { - test_init(); - - let image: GenericImage = GenericImage::new(nats_image()) - .with_wait_for(WaitFor::message_on_stderr("Server is ready")); - - let container = DOCKER.run(image); - container.start(); - container.image().wait_until_ready(&container); - container.get_host_port(4222).unwrap(); - container - }; - - - static ref KEYPAIR: (String, [u8; 64]) = { - generate_keypair() - }; - - static ref SETTINGS: Settings = { - let port = NATS_CONTAINER.get_host_port(4222).unwrap(); - common::config::Settings { - config: crate::config::Config { - server: crate::config::ServerSettings { - port: 5003, - address: "0.0.0.0".to_string(), - }, - discord: crate::config::Discord { - public_key: KEYPAIR.0.clone(), - client_id: 0, - }, - }, - redis: common::redis::RedisConfiguration { - url: "".to_string(), +#[ctor::ctor] +unsafe fn init() { + test_init(); + let image = GenericImage::new(nats_image()) + .with_wait_for(WaitFor::message_on_stderr("Server is ready")); + + let container = DOCKER.run(image); + container.start(); + container.image().wait_until_ready(&container); + container.get_host_port(4222).unwrap(); + + let port = container.get_host_port(4222).unwrap(); + NATS = Some(container); + SETTINGS = Some(common::config::Settings { + config: crate::config::Config { + server: crate::config::ServerSettings { + port: 5003, + address: "0.0.0.0".to_string(), }, - monitoring: common::monitoring::MonitoringConfiguration { - enabled: false, - address: None, - port: None, + discord: crate::config::Discord { + public_key: KEYPAIR.0.clone(), + client_id: 0, }, - nats: common::nats::NatsConfiguration { - client_cert: None, - root_cert: None, - jetstream_api_prefix: None, - max_reconnects: None, - reconnect_buffer_size: None, - tls: None, - client_name: None, - tls_required: None, - host: format!("localhost:{}", port), - }, - } - }; - - static ref TASK: () = { - std::thread::spawn(|| { - let r = tokio::runtime::Runtime::new().unwrap(); - r.spawn(async { start(SETTINGS.clone()).await }); - loop {} - }); - std::thread::sleep(Duration::from_secs(1)); - }; + }, + redis: common::redis::RedisConfiguration { + url: "".to_string(), + }, + monitoring: common::monitoring::MonitoringConfiguration { + enabled: false, + address: None, + port: None, + }, + nats: common::nats::NatsConfiguration { + client_cert: None, + root_cert: None, + jetstream_api_prefix: None, + max_reconnects: None, + reconnect_buffer_size: None, + tls: None, + client_name: None, + tls_required: None, + host: format!("localhost:{}", port), + }, + }); + let settings = (&mut SETTINGS).as_ref().unwrap(); + + std::thread::spawn(move || { + let runtime = tokio::runtime::Runtime::new().unwrap(); + runtime.block_on(start(settings.clone())); + }); + std::thread::sleep(Duration::from_secs(3)); +} + +#[ctor::dtor] +unsafe fn destroy() { + let nats = (&mut NATS).as_ref().unwrap(); + nats.stop(); } #[tokio::test] async fn respond_to_pings() { - let _ = NATS_CONTAINER.deref(); - let _ = TASK.deref(); - let ping = json!({ "type": 1 }).to_string(); + let ping = json!({ "type": 1, "id": "0", "application_id": "0", "token": "random token", "version": 1 }).to_string(); let timestamp = "my datetime :)"; let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat(); let signature = sign_message(signature_data, KEYPAIR.1); @@ -115,14 +112,12 @@ async fn respond_to_pings() { let client = hyper::client::Client::new(); let result = client.request(req).await.unwrap(); - assert!(result.status() == 200); + assert_eq!(result.status(), StatusCode::OK); } #[tokio::test] async fn deny_invalid_signatures() { - let _ = NATS_CONTAINER.deref(); - let _ = TASK.deref(); - let ping = json!({ "type": 1 }).to_string(); + let ping = json!({ "type": 1, "id": "0", "application_id": "0", "token": "random token", "version": 1 }).to_string(); let timestamp = "my datetime :)"; let req = Request::builder() @@ -134,14 +129,12 @@ async fn deny_invalid_signatures() { .expect("request builder"); let client = hyper::client::Client::new(); let result = client.request(req).await.unwrap(); - assert!(result.status() == 401); + assert_eq!(result.status(), StatusCode::UNAUTHORIZED); } #[tokio::test] async fn response_500_when_no_nats_response() { - let _ = NATS_CONTAINER.deref(); - let _ = TASK.deref(); - let ping = json!({ "type": 0 }).to_string(); + let ping = json!({ "type": 2, "id": "0", "application_id": "0", "token": "random token", "version": 1 }).to_string(); let timestamp = "my datetime :)"; let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat(); let signature = sign_message(signature_data, KEYPAIR.1); @@ -157,16 +150,17 @@ async fn response_500_when_no_nats_response() { let client = hyper::client::Client::new(); let result = client.request(req).await.unwrap(); - assert!(result.status() == 500); + assert_eq!(result.status(), StatusCode::INTERNAL_SERVER_ERROR); } #[tokio::test] async fn respond_from_nats_response() { - let _ = NATS_CONTAINER.deref(); - let _ = TASK.deref(); - let nats: Connection = SETTINGS.clone().nats.into(); + let nats: Connection; + unsafe { + nats = SETTINGS.clone().unwrap().nats.into(); + } let sub = nats.subscribe("nova.cache.dispatch.interaction").unwrap(); - let ping = json!({ "type": 0 }).to_string(); + let ping = json!({ "type": 2, "id": "0", "application_id": "0", "token": "random token", "version": 1 }).to_string(); let timestamp = "my datetime :)"; let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat(); let signature = sign_message(signature_data, KEYPAIR.1); @@ -186,13 +180,11 @@ async fn respond_from_nats_response() { .expect("request builder"); let client = hyper::client::Client::new(); let result = client.request(req).await.unwrap(); - assert!(result.status() == 200); + assert_eq!(result.status(), StatusCode::OK); } #[tokio::test] async fn response_400_when_invalid_json_body() { - let _ = NATS_CONTAINER.deref(); - let _ = TASK.deref(); let ping = "{".to_string(); let timestamp = "my datetime :)"; let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat(); @@ -207,13 +199,11 @@ async fn response_400_when_invalid_json_body() { .expect("request builder"); let client = hyper::client::Client::new(); let result = client.request(req).await.unwrap(); - assert!(result.status() == 400); + assert_eq!(result.status(), StatusCode::BAD_REQUEST); } #[tokio::test] async fn response_400_when_invalid_utf8_body() { - let _ = NATS_CONTAINER.deref(); - let _ = TASK.deref(); // invalid 2 octet sequence let ping = vec![0xc3, 0x28]; @@ -230,5 +220,5 @@ async fn response_400_when_invalid_utf8_body() { .expect("request builder"); let client = hyper::client::Client::new(); let result = client.request(req).await.unwrap(); - assert!(result.status() == 400); + assert_eq!(result.status(), StatusCode::BAD_REQUEST); } diff --git a/webhook/src/handler/types.rs b/webhook/src/handler/types.rs deleted file mode 100644 index 4fc5b68..0000000 --- a/webhook/src/handler/types.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::{Serialize, Deserialize}; -use serde_json::Value; - -#[derive(Debug, Deserialize, Clone, Default, Serialize)] -pub struct Interaction { - #[serde(rename = "type")] - pub t: i16, - pub data: Option, -}