]> git.puffer.fish Git - matthieu/nova.git/commitdiff
fix tests and adapt webhook
authorMatthieu <matthieu@developershouse.xyz>
Mon, 18 Oct 2021 06:44:53 +0000 (10:44 +0400)
committerMatthieu <matthieu@developershouse.xyz>
Mon, 18 Oct 2021 06:44:53 +0000 (10:44 +0400)
webhook/BUILD
webhook/Cargo.toml
webhook/cargo/BUILD.bazel
webhook/src/handler/error.rs
webhook/src/handler/handler.rs
webhook/src/handler/mod.rs
webhook/src/handler/signature.rs
webhook/src/handler/tests/handler_integration.rs
webhook/src/handler/types.rs [deleted file]

index 4340909fd956084695d68d75712d5b375cfc94af..82f33058008f599c365151b7a11d3af218235a13 100644 (file)
@@ -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"],
 )
index 7e73c436a119d55bf93cdb5ef5aee62232949d7f..9e6b507023a7823fbd82f90b82b8ed04e89d47e0 100644 (file)
@@ -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"
index 4569cc1026fd3632e9ffbe14e13f9acc36aa8762..3988edc0a9d5a65415d75cc886c6d0a4e63d212a 100644 (file)
@@ -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",
index ccec59fedc402fc1aaae8580dcb6386c9904201a..f998b84aa9011a85e193047e31a5a738b11f21bc 100644 (file)
@@ -1,4 +1,4 @@
-use hyper::{Body, Error, Response, StatusCode};
+use hyper::{Body, Response, StatusCode};
 
 pub struct WebhookError {
     pub code: StatusCode,
index 4af2ba67897e24c7b6bb92617d15fa4c6607244a..74a176267ddbf0ed292fb7083e055815d12b3faa 100644 (file)
@@ -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::<Interaction>(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,
index a437dd540e2b483a27cc591bd61ede38e0614569..20a977a1473fb1d813d60fc8b53d892a082af7d4 100644 (file)
@@ -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;
index c12f9e84f94ef2ed5aa8e8b9a28d442765018427..e4c7d53df8a5d3a5ca5c20058965ea33e5887290 100644 (file)
@@ -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<u8>, 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.
index fd0d67f959757cb71e543aee79202d44d7dc7e38..fea4920d953e83161e24c55091327cfa75f96ad4 100644 (file)
 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<Container<Cli, GenericImage>> = None;
+static mut SETTINGS: Option<Settings<Config>> = 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<Config> = {
-        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 (file)
index 4fc5b68..0000000
+++ /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<Value>,
-}