diff --git a/Cargo.lock b/Cargo.lock index acdbb5ce7a3..e5c6ce10106 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,6 +99,17 @@ version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7" +[[package]] +name = "assert-json-diff" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4259cbe96513d2f1073027a259fc2ca917feb3026a5a8d984e3628e490255cc0" +dependencies = [ + "extend", + "serde", + "serde_json", +] + [[package]] name = "async-compression" version = "0.3.6" @@ -300,6 +311,7 @@ dependencies = [ "lettre", "license-exprs", "log", + "mockito", "oauth2", "parking_lot", "rand", @@ -441,6 +453,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "colored" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +dependencies = [ + "atty", + "lazy_static", + "winapi 0.3.9", +] + [[package]] name = "comrak" version = "0.9.0" @@ -752,6 +775,12 @@ dependencies = [ "migrations_macros", ] +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + [[package]] name = "digest" version = "0.8.1" @@ -822,6 +851,18 @@ dependencies = [ "termcolor", ] +[[package]] +name = "extend" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47da3a72ec598d9c8937a7ebca8962a5c7a1f28444e38c2b33c771ba3f55f05" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "failure" version = "0.1.8" @@ -1694,6 +1735,24 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "mockito" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36a0eb7e686b49b02c1cb87c14b8e2a05de0d36c6eee0293653d0a875906d499" +dependencies = [ + "assert-json-diff", + "colored", + "difference", + "httparse", + "lazy_static", + "log", + "rand", + "regex", + "serde_json", + "serde_urlencoded", +] + [[package]] name = "native-tls" version = "0.2.6" diff --git a/Cargo.toml b/Cargo.toml index cadaa2294c9..eb53f0db270 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ conduit-test = "0.9.0-alpha.3" diesel_migrations = { version = "1.3.0", features = ["postgres"] } hyper-tls = "0.4" lazy_static = "1.0" +mockito = "0.28" tokio = { version = "0.2", default-features = false, features = ["stream"]} tower-service = "0.3.0" diff --git a/src/admin/on_call.rs b/src/admin/on_call.rs index bc180b32bf1..47cbceaeebf 100644 --- a/src/admin/on_call.rs +++ b/src/admin/on_call.rs @@ -25,11 +25,17 @@ impl Event { /// If the variant is `Trigger`, this will page whoever is on call /// (potentially waking them up at 3 AM). pub fn send(self) -> Result<()> { + #[cfg(not(test))] + let base_url = "https://events.pagerduty.com"; + #[cfg(test)] + let base_url = mockito::server_url(); + let api_token = dotenv::var("PAGERDUTY_API_TOKEN")?; let service_key = dotenv::var("PAGERDUTY_INTEGRATION_KEY")?; + let url = format!("{}/generic/2010-04-15/create_event.json", base_url); let response = Client::new() - .post("https://events.pagerduty.com/generic/2010-04-15/create_event.json") + .post(&url) .header(header::ACCEPT, "application/vnd.pagerduty+json;version=2") .header(header::AUTHORIZATION, format!("Token token={}", api_token)) .json(&FullEvent { @@ -66,3 +72,75 @@ struct InvalidEvent { message: String, errors: Vec, } + +#[cfg(test)] +mod tests { + use super::Event; + use mockito::{mock, Matcher}; + use std::env; + + #[test] + fn test_send() { + // set environment variables for this test + env::set_var("PAGERDUTY_API_TOKEN", "secret123"); + env::set_var("PAGERDUTY_INTEGRATION_KEY", "crates-io-service-key"); + + // setup the pagerduty API endpoint mock + let response_body = json!({ + "description": "possible spam attack underway", + "event_type": "trigger", + "incident_key": "spam_attack", + "service_key": "crates-io-service-key" + }); + + let mock = mock("POST", "/generic/2010-04-15/create_event.json") + .match_header("Accept", "application/vnd.pagerduty+json;version=2") + .match_header("Authorization", "Token token=secret123") + .match_header("Content-Type", "application/json") + .match_body(Matcher::Json(response_body)) + .with_status(200) + .create(); + + // create and send the event + let event = Event::Trigger { + incident_key: Some("spam_attack".into()), + description: "possible spam attack underway".into(), + }; + + let result = event.send(); + + // check that the mock endpoint was triggered + mock.assert(); + assert_ok!(result); + } + + #[test] + fn test_send_with_400_error() { + // set environment variables for this test + env::set_var("PAGERDUTY_API_TOKEN", "secret123"); + env::set_var("PAGERDUTY_INTEGRATION_KEY", "crates-io-service-key"); + + // setup the pagerduty API endpoint mock + let request_body = json!({ + "message": "oops", + "errors": ["something", "went", "wrong"], + }); + + let mock = mock("POST", "/generic/2010-04-15/create_event.json") + .with_status(400) + .with_body(request_body.to_string()) + .create(); + + // create and send the event + let event = Event::Trigger { + incident_key: Some("spam_attack".into()), + description: "possible spam attack underway".into(), + }; + + let result = event.send(); + + // check that the mock endpoint was triggered + mock.assert(); + assert_err!(result); + } +}