mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
test(connector): Add support for webhook tests (#1863)
Signed-off-by: chikke srujan <121822803+srujanchikke@users.noreply.github.com>
This commit is contained in:
BIN
.github/secrets/connector_auth.toml.gpg
vendored
BIN
.github/secrets/connector_auth.toml.gpg
vendored
Binary file not shown.
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -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",
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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>,
|
||||
|
||||
37
crates/test_utils/tests/connectors/authorizedotnet_wh_ui.rs
Normal file
37
crates/test_utils/tests/connectors/authorizedotnet_wh_ui.rs
Normal 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);
|
||||
}
|
||||
34
crates/test_utils/tests/connectors/bluesnap_wh_ui.rs
Normal file
34
crates/test_utils/tests/connectors/bluesnap_wh_ui.rs
Normal 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);
|
||||
}
|
||||
35
crates/test_utils/tests/connectors/checkout_wh_ui.rs
Normal file
35
crates/test_utils/tests/connectors/checkout_wh_ui.rs
Normal 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);
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
36
crates/test_utils/tests/connectors/stripe_wh_ui.rs
Normal file
36
crates/test_utils/tests/connectors/stripe_wh_ui.rs
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user