test(connector): Add support for webhook tests (#1863)

Signed-off-by: chikke srujan <121822803+srujanchikke@users.noreply.github.com>
This commit is contained in:
chikke srujan
2023-08-08 12:36:45 +05:30
committed by GitHub
parent c2f471efcb
commit 7b2c419ce5
10 changed files with 253 additions and 12 deletions

Binary file not shown.

2
Cargo.lock generated
View File

@ -4749,10 +4749,12 @@ dependencies = [
"api_models",
"async-trait",
"awc",
"base64 0.21.2",
"clap",
"derive_deref",
"masking",
"rand 0.8.5",
"reqwest",
"serde",
"serde_json",
"serde_path_to_error",

View File

@ -15,6 +15,7 @@ payouts = []
[dependencies]
async-trait = "0.1.68"
actix-web = "4.3.1"
base64 = "0.21.2"
clap = { version = "4.3.2", default-features = false, features = ["std", "derive", "help", "usage"] }
serde = { version = "1.0.163", features = ["derive"] }
serde_json = "1.0.96"
@ -26,6 +27,7 @@ actix-http = "3.3.1"
awc = { version = "3.1.1", features = ["rustls"] }
derive_deref = "1.1.1"
rand = "0.8.5"
reqwest = { version = "0.11.18", features = ["native-tls"] }
thirtyfour = "0.31.0"
time = { version = "0.3.21", features = ["macros"] }
tokio = "1.28.2"

View File

@ -269,6 +269,9 @@ impl From<MultiAuthKey> for ConnectorAuthType {
pub struct AutomationConfigs {
pub hs_base_url: Option<String>,
pub hs_api_key: Option<String>,
pub hs_api_keys: Option<String>,
pub hs_webhook_url: Option<String>,
pub hs_test_env: Option<String>,
pub hs_test_browser: Option<String>,
pub chrome_profile_path: Option<String>,
pub firefox_profile_path: Option<String>,

View File

@ -0,0 +1,37 @@
use rand::Rng;
use serial_test::serial;
use thirtyfour::{prelude::*, WebDriver};
use crate::{selenium::*, tester};
struct AuthorizedotnetSeleniumTest;
impl SeleniumTest for AuthorizedotnetSeleniumTest {
fn get_connector_name(&self) -> String {
"authorizedotnet".to_string()
}
}
async fn should_make_webhook(web_driver: WebDriver) -> Result<(), WebDriverError> {
let conn = AuthorizedotnetSeleniumTest {};
let amount = rand::thread_rng().gen_range(50..1000); //This connector detects it as fradulent payment if the same amount is used for multiple payments so random amount is passed for testing(
conn.make_webhook_test(
web_driver,
&format!("{CHEKOUT_BASE_URL}/saved/227?amount={amount}"),
vec![
Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
Event::Assert(Assert::IsPresent("status")),
Event::Assert(Assert::IsPresent("processing")),
],
10,
"processing",
)
.await?;
Ok(())
}
#[test]
#[serial]
fn should_make_webhook_test() {
tester!(should_make_webhook);
}

View File

@ -0,0 +1,34 @@
use serial_test::serial;
use thirtyfour::{prelude::*, WebDriver};
use crate::{selenium::*, tester};
struct BluesnapSeleniumTest;
impl SeleniumTest for BluesnapSeleniumTest {
fn get_connector_name(&self) -> String {
"bluesnap".to_string()
}
}
async fn should_make_webhook(web_driver: WebDriver) -> Result<(), WebDriverError> {
let conn = BluesnapSeleniumTest {};
conn.make_webhook_test(
web_driver,
&format!("{CHEKOUT_BASE_URL}/saved/199"),
vec![
Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
Event::Assert(Assert::IsPresent("succeeded")),
],
5,
"succeeded",
)
.await?;
Ok(())
}
#[test]
#[serial]
fn should_make_webhook_test() {
tester!(should_make_webhook);
}

View File

@ -0,0 +1,35 @@
use serial_test::serial;
use thirtyfour::{prelude::*, WebDriver};
use crate::{selenium::*, tester};
struct CheckoutSeleniumTest;
impl SeleniumTest for CheckoutSeleniumTest {
fn get_connector_name(&self) -> String {
"checkout".to_string()
}
}
async fn should_make_webhook(web_driver: WebDriver) -> Result<(), WebDriverError> {
let conn = CheckoutSeleniumTest {};
conn.make_webhook_test(
web_driver,
&format!("{CHEKOUT_BASE_URL}/saved/18"),
vec![
Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
Event::Assert(Assert::IsPresent("status")),
Event::Assert(Assert::IsPresent("succeeded")),
],
5,
"succeeded",
)
.await?;
Ok(())
}
#[test]
#[serial]
fn should_make_webhook_test() {
tester!(should_make_webhook);
}

View File

@ -8,9 +8,12 @@ mod aci_ui;
mod adyen_uk_ui;
mod airwallex_ui;
mod authorizedotnet_ui;
mod authorizedotnet_wh_ui;
mod bambora_ui;
mod bluesnap_ui;
mod bluesnap_wh_ui;
mod checkout_ui;
mod checkout_wh_ui;
mod globalpay_ui;
mod mollie_ui;
mod multisafepay_ui;
@ -22,6 +25,7 @@ mod payu_ui;
mod selenium;
mod shift4_ui;
mod stripe_ui;
mod stripe_wh_ui;
mod trustpay_3ds_ui;
mod worldline_ui;
mod zen_ui;

View File

@ -14,6 +14,8 @@ use std::{
};
use async_trait::async_trait;
use base64::Engine;
use serde::{Deserialize, Serialize};
use serde_json::json;
use test_utils::connector_auth;
use thirtyfour::{components::SelectElement, prelude::*, WebDriver};
@ -247,23 +249,18 @@ pub trait SeleniumTest {
let saved_tests =
serde_json::to_string(&self.get_saved_testcases()).unwrap();
let conf = serde_json::to_string(&self.get_configs()).unwrap();
let hs_base_url = self
.get_configs()
.automation_configs
.unwrap()
let configs = self.get_configs().automation_configs.unwrap();
let hs_base_url = configs
.hs_base_url
.unwrap_or_else(|| "http://localhost:8080".to_string());
let configs_url = self
.get_configs()
.automation_configs
.unwrap()
.configs_url
.unwrap();
let configs_url = configs.configs_url.unwrap();
let hs_api_keys = configs.hs_api_keys.unwrap();
let test_env = configs.hs_test_env.unwrap();
let script = &[
format!("localStorage.configs='{configs_url}'").as_str(),
"localStorage.current_env='local'",
format!("localStorage.current_env='{test_env}'").as_str(),
"localStorage.hs_api_key=''",
"localStorage.hs_api_keys=''",
format!("localStorage.hs_api_keys='{hs_api_keys}'").as_str(),
format!("localStorage.base_url='{hs_base_url}'").as_str(),
format!("localStorage.hs_api_configs='{conf}'").as_str(),
format!("localStorage.saved_payments=JSON.stringify({saved_tests})")
@ -485,6 +482,60 @@ pub trait SeleniumTest {
affirm_actions.extend(actions);
self.complete_actions(&driver, affirm_actions).await
}
async fn make_webhook_test(
&self,
web_driver: WebDriver,
payment_url: &str,
actions: Vec<Event<'_>>,
webhook_retry_time: u64,
webhook_status: &str,
) -> Result<(), WebDriverError> {
self.complete_actions(
&web_driver,
vec![Event::Trigger(Trigger::Goto(payment_url))],
)
.await?;
self.complete_actions(&web_driver, actions).await?; //additional actions needs to make a payment
self.complete_actions(
&web_driver,
vec![Event::Trigger(Trigger::Goto(&format!(
"{CHEKOUT_BASE_URL}/events"
)))],
)
.await?;
let element = web_driver.query(By::Css("h2.last-payment")).first().await?;
let payment_id = element.text().await?;
let retries = 3; // no of retry times
for _i in 0..retries {
let configs = self.get_configs().automation_configs.unwrap();
let outgoing_webhook_url = configs.hs_webhook_url.unwrap().to_string();
let client = reqwest::Client::new();
let response = client.get(outgoing_webhook_url).send().await.unwrap(); // get events from outgoing webhook endpoint
let body_text = response.text().await.unwrap();
let data: WebhookResponse = serde_json::from_str(&body_text).unwrap();
let last_three_events = &data.data[data.data.len().saturating_sub(3)..]; // Get the last three elements if available
for last_event in last_three_events {
let last_event_body = &last_event.step.request.body;
let decoded_bytes = base64::engine::general_purpose::STANDARD //decode the encoded outgoing webhook event
.decode(last_event_body)
.unwrap();
let decoded_str = String::from_utf8(decoded_bytes).unwrap();
let webhook_response: HsWebhookResponse =
serde_json::from_str(&decoded_str).unwrap();
if payment_id == webhook_response.content.object.payment_id
&& webhook_status == webhook_response.content.object.status
{
return Ok(());
}
}
self.complete_actions(
&web_driver,
vec![Event::Trigger(Trigger::Sleep(webhook_retry_time))],
)
.await?;
}
Err(WebDriverError::CustomError("Webhook Not Found".to_string()))
}
async fn make_paypal_payment(
&self,
web_driver: WebDriver,
@ -827,3 +878,40 @@ pub fn handle_test_error(
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WebhookResponse {
data: Vec<WebhookResponseData>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WebhookResponseData {
step: WebhookRequestData,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WebhookRequestData {
request: WebhookRequest,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WebhookRequest {
body: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct HsWebhookResponse {
content: HsWebhookContent,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct HsWebhookContent {
object: HsWebhookObject,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct HsWebhookObject {
payment_id: String,
status: String,
connector: String,
}

View File

@ -0,0 +1,36 @@
use serial_test::serial;
use thirtyfour::{prelude::*, WebDriver};
use crate::{selenium::*, tester};
struct StripeSeleniumTest;
impl SeleniumTest for StripeSeleniumTest {
fn get_connector_name(&self) -> String {
"stripe".to_string()
}
}
async fn should_make_webhook(web_driver: WebDriver) -> Result<(), WebDriverError> {
let conn = StripeSeleniumTest {};
conn.make_webhook_test(
web_driver,
&format!("{CHEKOUT_BASE_URL}/saved/16"),
vec![
Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
Event::Assert(Assert::IsPresent("status")),
Event::Assert(Assert::IsPresent("succeeded")),
],
7,
"succeeded",
)
.await?;
Ok(())
}
#[test]
#[serial]
fn should_make_webhook_test() {
tester!(should_make_webhook);
}