refactor: fix unit and documentation tests (#4754)

This commit is contained in:
Sanchith Hegde
2024-07-05 23:37:39 +05:30
committed by GitHub
parent ae2a34e02c
commit 648cecb204
17 changed files with 179 additions and 155 deletions

11
.github/nextest.toml vendored Normal file
View File

@ -0,0 +1,11 @@
[profile.default]
status-level = "slow"
final-status-level = "leak"
failure-output = "final"
fail-fast = false
[profile.ci]
retries = 2
status-level = "all"
failure-output = "final"
fail-fast = false

View File

@ -33,8 +33,8 @@ env:
RUSTUP_MAX_RETRIES: 10
# Don't emit giant backtraces in the CI logs.
RUST_BACKTRACE: short
# Use cargo's sparse index protocol
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
# Use `sccache` for caching compilation artifacts
RUSTC_WRAPPER: sccache
jobs:
formatting:
@ -88,14 +88,7 @@ jobs:
check-msrv:
name: Check compilation on MSRV toolchain
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os:
- ubuntu-latest
# - macos-latest
# - windows-latest
runs-on: hyperswitch-runners
steps:
- name: Checkout repository
@ -122,14 +115,17 @@ jobs:
with:
toolchain: "${{ env.rust_version }}"
- uses: Swatinem/rust-cache@v2.7.0
- name: Install sccache
uses: taiki-e/install-action@v2.33.28
with:
save-if: false
tool: sccache
checksum: true
- name: Install cargo-hack
uses: baptiste0928/cargo-install@v2.2.0
uses: taiki-e/install-action@v2.33.28
with:
crate: cargo-hack
tool: cargo-hack
checksum: true
- name: Deny warnings
shell: bash
@ -210,14 +206,7 @@ jobs:
test:
name: Run tests on stable toolchain
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
# - macos-latest
# - windows-latest
runs-on: hyperswitch-runners
steps:
- name: Generate a token
@ -256,19 +245,23 @@ jobs:
toolchain: stable 2 weeks ago
components: clippy
- name: Install cargo-hack
uses: baptiste0928/cargo-install@v2.2.0
- name: Install sccache
uses: taiki-e/install-action@v2.33.28
with:
crate: cargo-hack
tool: sccache
checksum: true
- name: Install cargo-hack
uses: taiki-e/install-action@v2.33.28
with:
tool: cargo-hack
checksum: true
# - name: Install cargo-nextest
# uses: baptiste0928/cargo-install@v2.2.0
# uses: taiki-e/install-action@v2.33.28
# with:
# crate: cargo-nextest
- uses: Swatinem/rust-cache@v2.7.0
with:
save-if: false
# tool: cargo-nextest
# checksum: true
# - name: Setup Embark Studios lint rules
# shell: bash

View File

@ -39,14 +39,7 @@ jobs:
check-msrv:
name: Check compilation on MSRV toolchain
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os:
- ubuntu-latest
# - macos-latest
# - windows-latest
runs-on: ubuntu-latest
steps:
- name: Checkout repository
@ -115,14 +108,7 @@ jobs:
test:
name: Run tests on stable toolchain
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
# - macos-latest
# - windows-latest
runs-on: ubuntu-latest
steps:
- name: Checkout repository

3
Cargo.lock generated
View File

@ -6030,7 +6030,9 @@ dependencies = [
name = "router_derive"
version = "0.1.0"
dependencies = [
"common_utils",
"diesel",
"error-stack",
"indexmap 2.2.6",
"proc-macro2",
"quote",
@ -6038,6 +6040,7 @@ dependencies = [
"serde_json",
"strum 0.26.2",
"syn 2.0.57",
"utoipa",
]
[[package]]

View File

@ -314,6 +314,7 @@ mod tests {
& payment_method /= voucher
& payment_method /= gift_card
& payment_method /= card_redirect
& payment_method /= real_time_payment
}
}
}

View File

@ -163,7 +163,8 @@ pub trait FromEncoded: Sized {
/// # Example
///
/// ```rust
/// # use your_module::{FromEncoded, masking::Secret, Vec};
/// use external_services::hashicorp_vault::core::FromEncoded;
/// use masking::Secret;
/// let secret_instance = Secret::<String>::from_encoded("encoded_secret_string".to_string());
/// let vec_instance = Vec::<u8>::from_encoded("68656c6c6f".to_string());
/// ```

View File

@ -22,7 +22,7 @@ pub struct PaymentsAuthorizeData {
pub payment_method_data: PaymentMethodData,
/// total amount (original_amount + surcharge_amount + tax_on_surcharge_amount)
/// If connector supports separate field for surcharge amount, consider using below functions defined on `PaymentsAuthorizeData` to fetch original amount and surcharge amount separately
/// ```
/// ```text
/// get_original_amount()
/// get_surcharge_amount()
/// get_tax_on_surcharge_amount()

View File

@ -15,26 +15,43 @@ This solution has such advantages over alternatives:
## How to use
To convert non-secret variable into secret use `new()`. Sample:
To convert a non-secret variable into a secret, use `Secret::new()`:
```rust
expiry_year: ccard.map(|x| Secret::new(x.card_exp_year.to_string())),
// output: "expiry_year: *** alloc::string::String ***"
use masking::Secret;
let card_number: Secret<String> = Secret::new(String::from("1234 5678 9012 3456"));
assert_eq!(format!("{:?}", card_number), "*** alloc::string::String ***");
```
To get value from secret use `expose()`. Sample:
To get a reference to the inner value from the secret, use `peek()`:
```rust
last4_digits: Some(card_number.expose())
use masking::{PeekInterface, Secret};
let card_number: Secret<String> = Secret::new(String::from("1234 5678 9012 3456"));
let last4_digits: &str = card_number.peek();
```
Most fields are under `Option`. To simplify dealing with `Option`, use `expose_option()`. Sample:
To get the owned inner value from the secret, use `expose()`:
```rust
card_info.push_str(
&card_detail
.card_holder_name
.expose_option()
.unwrap_or_default(),
);
use masking::{ExposeInterface, Secret};
let card_number: Secret<String> = Secret::new(String::from("1234 5678 9012 3456"));
let last4_digits: String = card_number.expose();
```
For fields that are `Option<T>`, you can use `expose_option()`:
```rust
use masking::{ExposeOptionInterface, Secret};
let card_number: Option<Secret<String>> = Some(Secret::new(String::from("1234 5678 9012 3456")));
let card_number_str: String = card_number.expose_option().unwrap_or_default();
assert_eq!(format!("{}", card_number_str), "1234 5678 9012 3456");
let card_number: Option<Secret<String>> = None;
let card_number_str: String = card_number.expose_option().unwrap_or_default();
assert_eq!(format!("{}", card_number_str), "");
```

View File

@ -1001,15 +1001,10 @@ mod hashmap_deserialization_test {
fn test_payment_method_and_payment_method_types() {
use diesel_models::enums::{PaymentMethod, PaymentMethodType};
let input_map: HashMap<String, String> = serde_json::json!({
"bank_transfer": "ach,bacs",
"wallet": "paypal,venmo",
})
.as_object()
.unwrap()
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
let input_map: HashMap<String, String> = HashMap::from([
("bank_transfer".to_string(), "ach,bacs".to_string()),
("wallet".to_string(), "paypal,venmo".to_string()),
]);
let deserializer: MapDeserializer<
'_,
std::collections::hash_map::IntoIter<String, String>,
@ -1035,15 +1030,10 @@ mod hashmap_deserialization_test {
fn test_payment_method_and_payment_method_types_with_spaces() {
use diesel_models::enums::{PaymentMethod, PaymentMethodType};
let input_map: HashMap<String, String> = serde_json::json!({
" bank_transfer ": " ach , bacs ",
"wallet ": " paypal , pix , venmo ",
})
.as_object()
.unwrap()
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
let input_map: HashMap<String, String> = HashMap::from([
(" bank_transfer ".to_string(), " ach , bacs ".to_string()),
("wallet ".to_string(), " paypal , pix , venmo ".to_string()),
]);
let deserializer: MapDeserializer<
'_,
std::collections::hash_map::IntoIter<String, String>,
@ -1073,15 +1063,10 @@ mod hashmap_deserialization_test {
fn test_payment_method_deserializer_error() {
use diesel_models::enums::{PaymentMethod, PaymentMethodType};
let input_map: HashMap<String, String> = serde_json::json!({
"unknown": "ach,bacs",
"wallet": "paypal,unknown",
})
.as_object()
.unwrap()
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
let input_map: HashMap<String, String> = HashMap::from([
("unknown".to_string(), "ach,bacs".to_string()),
("wallet".to_string(), "paypal,unknown".to_string()),
]);
let deserializer: MapDeserializer<
'_,
std::collections::hash_map::IntoIter<String, String>,

View File

@ -118,8 +118,8 @@ pub struct ThreeDSRequestor {
/// External IP address (i.e., the device public IP address) used by the 3DS Requestor App when it connects to the
/// 3DS Requestor environment. The value length is maximum 45 characters. Accepted values are:
///
/// IPv4 address is represented in the dotted decimal f. Refer to RFC 791.
/// IPv6 address. Refer to RFC 4291.
/// - IPv4 address is represented in the dotted decimal f. Refer to RFC 791.
/// - IPv6 address. Refer to RFC 4291.
///
/// This field is required when deviceChannel = 01 (APP) and unless market or regional mandate restricts sending
/// this information.
@ -1454,17 +1454,17 @@ pub struct Sdk {
/// The Split-SDK Server:
/// Creates a JSON object of the following data as the JWS payload to be signed:
///
/// SDK Reference Number -> Identifies the vendor and version of the 3DS SDK that is utilised for a specific
/// - SDK Reference Number -> Identifies the vendor and version of the 3DS SDK that is utilised for a specific
/// transaction. The value is assigned by EMVCo when the Letter of Approval of the
/// specific 3DS SDK is issued. The field is limited to 32 characters.
/// SDK Signature Timestamp -> Date and time indicating when the 3DS SDK generated the Split-SDK Server Signed
/// - SDK Signature Timestamp -> Date and time indicating when the 3DS SDK generated the Split-SDK Server Signed
/// Content converted into UTC. The value is limited to 14 characters. Accepted
/// format: YYYYMMDDHHMMSS.
/// SDK Transaction ID -> Universally unique transaction identifier assigned by the 3DS SDK to identify a
/// - SDK Transaction ID -> Universally unique transaction identifier assigned by the 3DS SDK to identify a
/// single transaction. The field is limited to 36 characters and it shall be in a
/// canonical format as defined in IETF RFC 4122. This may utilize any of the specified
/// versions as long as the output meets specific requirements.
/// Split-SDK Server ID -> DS assigned Split-SDK Server identifier. Each DS can provide a unique ID to each
/// - Split-SDK Server ID -> DS assigned Split-SDK Server identifier. Each DS can provide a unique ID to each
/// Split-SDK Server on an individual basis. The field is limited to 32 characters.
/// Any individual DS may impose specific formatting and character requirements on the
/// contents of this field.
@ -1473,8 +1473,8 @@ pub struct Sdk {
/// Serialization. The parameter values for this version of the specification and to be included in the JWS
/// header are:
///
/// "alg": PS2567 or ES256
/// "x5c": X.5C v3: Cert (PbSDK) and chaining certificates if present
/// - `alg`: PS2567 or ES256
/// - `x5c`: X.5C v3: Cert (PbSDK) and chaining certificates if present
///
/// All other parameters: optional
///

View File

@ -3098,7 +3098,7 @@ mod tests {
use super::*;
#[test]
fn test_authenticate_client_secret_fulfillment_time_not_expired() {
fn test_authenticate_client_secret_session_not_expired() {
let payment_intent = PaymentIntent {
payment_id: "23".to_string(),
merchant_id: "22".to_string(),
@ -3160,7 +3160,9 @@ mod tests {
}
#[test]
fn test_authenticate_client_secret_fulfillment_time_expired() {
fn test_authenticate_client_secret_session_expired() {
let created_at =
common_utils::date_time::now().saturating_sub(time::Duration::seconds(20 * 60));
let payment_intent = PaymentIntent {
payment_id: "23".to_string(),
merchant_id: "22".to_string(),
@ -3177,7 +3179,7 @@ mod tests {
billing_address_id: None,
statement_descriptor_name: None,
statement_descriptor_suffix: None,
created_at: common_utils::date_time::now().saturating_sub(time::Duration::seconds(20)),
created_at,
modified_at: common_utils::date_time::now(),
fingerprint_id: None,
last_synced: None,
@ -3206,8 +3208,7 @@ mod tests {
incremental_authorization_allowed: None,
authorization_count: None,
session_expiry: Some(
common_utils::date_time::now()
.saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY)),
created_at.saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY)),
),
request_external_three_ds_authentication: None,
charges: None,

View File

@ -754,7 +754,7 @@ mod tests {
.unwrap();
assert!(updated_event.is_webhook_notified);
assert_eq!(updated_event.primary_object_id, "primary_object_tet");
assert_eq!(updated_event.primary_object_id, payment_id);
assert_eq!(updated_event.event_id, event_id);
}
}

View File

@ -99,20 +99,28 @@ mod tests {
types::{self, storage::enums},
};
#[actix_rt::test]
#[ignore]
async fn test_payment_attempt_insert() {
async fn create_single_connection_test_transaction_pool() -> routes::AppState {
// Set pool size to 1 and minimum idle connection size to 0
std::env::set_var("ROUTER__MASTER_DATABASE__POOL_SIZE", "1");
std::env::set_var("ROUTER__MASTER_DATABASE__MIN_IDLE", "0");
std::env::set_var("ROUTER__REPLICA_DATABASE__POOL_SIZE", "1");
std::env::set_var("ROUTER__REPLICA_DATABASE__MIN_IDLE", "0");
let conf = Settings::new().expect("invalid settings");
let tx: oneshot::Sender<()> = oneshot::channel().0;
let api_client = Box::new(services::MockApiClient);
let state = Box::pin(routes::AppState::with_storage(
Box::pin(routes::AppState::with_storage(
conf,
StorageImpl::PostgresqlTest,
tx,
api_client,
))
.await;
.await
}
#[tokio::test]
async fn test_payment_attempt_insert() {
let state = create_single_connection_test_transaction_pool().await;
let payment_id = Uuid::new_v4().to_string();
let current_time = common_utils::date_time::now();
let connector = types::Connector::DummyConnector1.to_string();
@ -137,22 +145,11 @@ mod tests {
assert_eq!(response.payment_id, payment_id.clone());
}
#[actix_rt::test]
#[tokio::test]
/// Example of unit test
/// Kind of test: state-based testing
async fn test_find_payment_attempt() {
use crate::configs::settings::Settings;
let conf = Settings::new().expect("invalid settings");
let tx: oneshot::Sender<()> = oneshot::channel().0;
let api_client = Box::new(services::MockApiClient);
let state = Box::pin(routes::AppState::with_storage(
conf,
StorageImpl::PostgresqlTest,
tx,
api_client,
))
.await;
let state = create_single_connection_test_transaction_pool().await;
let current_time = common_utils::date_time::now();
let payment_id = Uuid::new_v4().to_string();
let attempt_id = Uuid::new_v4().to_string();
@ -192,23 +189,12 @@ mod tests {
assert_eq!(response.payment_id, payment_id);
}
#[actix_rt::test]
#[tokio::test]
/// Example of unit test
/// Kind of test: state-based testing
async fn test_payment_attempt_mandate_field() {
use crate::configs::settings::Settings;
let conf = Settings::new().expect("invalid settings");
let state = create_single_connection_test_transaction_pool().await;
let uuid = Uuid::new_v4().to_string();
let tx: oneshot::Sender<()> = oneshot::channel().0;
let api_client = Box::new(services::MockApiClient);
let state = Box::pin(routes::AppState::with_storage(
conf,
StorageImpl::PostgresqlTest,
tx,
api_client,
))
.await;
let current_time = common_utils::date_time::now();
let connector = types::Connector::DummyConnector1.to_string();

View File

@ -20,5 +20,9 @@ strum = { version = "0.26.2", features = ["derive"] }
[dev-dependencies]
diesel = { version = "2.1.5", features = ["postgres"] }
error-stack = "0.4.1"
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.115"
utoipa = "4.2.0"
common_utils = { version = "0.1.0", path = "../common_utils" }

View File

@ -63,7 +63,7 @@ pub fn debug_as_display_derive(input: proc_macro::TokenStream) -> proc_macro::To
/// // yourself if required.
/// #[derive(strum::Display, strum::EnumString)]
/// #[derive(Debug)]
/// #[diesel_enum(storage_type = "pg_enum")]
/// #[diesel_enum(storage_type = "db_enum")]
/// enum Color {
/// Red,
/// Green,
@ -153,14 +153,16 @@ pub fn diesel_enum(
/// # Example
/// ```
/// use router_derive::Setter;
///
/// #[derive(Setter)]
/// struct Test {
/// test:u32
/// }
/// ```
/// The above Example will expand to
/// ```
/// ```rust, ignore
/// impl Test {
/// fn set_test(&mut self,val:u32)->&mut Self {
/// fn set_test(&mut self, val: u32) -> &mut Self {
/// self.test = val;
/// self
/// }
@ -381,7 +383,7 @@ pub fn api_error_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
/// - update_tracker
///
/// ## Example
/// ```
/// ```rust, ignore
/// use router_derive::Operation;
///
/// #[derive(Operation)]
@ -470,12 +472,14 @@ pub fn operation_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
/// Generates different schemas with the ability to mark few fields as mandatory for certain schema
/// Usage
/// ```
/// use router_derive::PolymorphicSchema;
///
/// #[derive(PolymorphicSchema)]
/// #[generate_schemas(PaymentsCreateRequest, PaymentsConfirmRequest)]
/// struct PaymentsRequest {
/// #[mandatory_in(PaymentsCreateRequest)]
/// #[mandatory_in(PaymentsCreateRequest = u64)]
/// amount: Option<u64>,
/// #[mandatory_in(PaymentsCreateRequest)]
/// #[mandatory_in(PaymentsCreateRequest = String)]
/// currency: Option<String>,
/// payment_method: String,
/// }
@ -520,6 +524,17 @@ pub fn polymorphic_schema(input: proc_macro::TokenStream) -> proc_macro::TokenSt
/// Implements the `Validate` trait to check if the config variable is present
/// Usage
/// ```
/// use router_derive::ConfigValidate;
///
/// #[derive(ConfigValidate)]
/// struct ConnectorParams {
/// base_url: String,
/// }
///
/// enum ApplicationError {
/// InvalidConfigurationValueError(String),
/// }
///
/// #[derive(ConfigValidate)]
/// struct Connectors {
/// pub stripe: ConnectorParams,
@ -529,7 +544,7 @@ pub fn polymorphic_schema(input: proc_macro::TokenStream) -> proc_macro::TokenSt
///
/// This will call the `validate()` function for all the fields in the struct
///
/// ```
/// ```rust, ignore
/// impl Connectors {
/// fn validate(&self) -> Result<(), ApplicationError> {
/// self.stripe.validate()?;
@ -549,9 +564,26 @@ pub fn validate_config(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
/// Generates the function to get the value out of enum variant
/// Usage
/// ```
/// use router_derive::TryGetEnumVariant;
///
/// impl std::fmt::Display for RedisError {
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
/// match self {
/// Self::UnknownResult => write!(f, "Unknown result")
/// }
/// }
/// }
///
/// impl std::error::Error for RedisError {}
///
/// #[derive(Debug)]
/// enum RedisError {
/// UnknownResult
/// }
///
/// #[derive(TryGetEnumVariant)]
/// #[error(RedisError(UnknownResult))]
/// enum Result {
/// #[error(RedisError::UnknownResult)]
/// enum RedisResult {
/// Set(String),
/// Get(i32)
/// }
@ -559,8 +591,8 @@ pub fn validate_config(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
///
/// This will generate the function to get `String` and `i32` out of the variants
///
/// ```
/// impl Result {
/// ```rust, ignore
/// impl RedisResult {
/// fn try_into_get(&self)-> Result<i32, RedisError> {
/// match self {
/// Self::Get(a) => Ok(a),
@ -575,6 +607,7 @@ pub fn validate_config(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
/// }
/// }
/// }
/// ```
#[proc_macro_derive(TryGetEnumVariant, attributes(error))]
pub fn try_get_enum_variant(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);

View File

@ -5,6 +5,9 @@ Environment of payment router: logger, basic config, its environment awareness.
## Example
```rust
use router_env::logger;
use tracing::{self, instrument};
#[instrument]
pub fn sample() -> () {
logger::log!(

View File

@ -152,7 +152,7 @@ where
///
/// ## Example
/// ```rust
/// let formatting_layer = router_env::FormattingLayer::new(router_env::service_name!(),std::io::stdout, CompactFormatter);
/// let formatting_layer = router_env::FormattingLayer::new("my_service", std::io::stdout, serde_json::ser::CompactFormatter);
/// ```
///
pub fn new(service: &str, dst_writer: W, formatter: F) -> Self {