diff --git a/src/endpoints/transaction_splits.rs b/src/endpoints/transaction_splits.rs index 8370a6e..40806c6 100644 --- a/src/endpoints/transaction_splits.rs +++ b/src/endpoints/transaction_splits.rs @@ -4,9 +4,9 @@ //! across their payout account, and one or more subaccounts. use crate::{ - get_request, post_request, put_request, CreateTransactionSplitBody, Error, PaystackResult, - ResponseWithoutData, SubaccountBody, TransactionSplitListResponse, TransactionSplitResponse, - UpdateTransactionSplitBody, + get_request, post_request, put_request, CreateTransactionSplitBody, DeleteSubAccountBody, + Error, PaystackResult, ResponseWithoutData, SubaccountBody, TransactionSplitListResponse, + TransactionSplitResponse, UpdateTransactionSplitBody, }; use reqwest::StatusCode; @@ -95,7 +95,7 @@ impl<'a> TransactionSplitEndpoints<'a> { &self, split_id: &str, ) -> PaystackResult { - let url = format!("{}/split{}", BASE_URL, split_id); + let url = format!("{}/split/{}", BASE_URL, split_id); match get_request(self.api_key, &url, None).await { Ok(response) => match response.status() { @@ -177,7 +177,7 @@ impl<'a> TransactionSplitEndpoints<'a> { pub async fn remove_subaccount_from_transaction_split( &self, split_id: &str, - subaccount: &str, + subaccount: DeleteSubAccountBody, ) -> PaystackResult { let url = format!("{}/split/{}/subaccount/remove", BASE_URL, split_id); diff --git a/src/models/subaccounts.rs b/src/models/subaccounts.rs index b04595f..2b952f1 100644 --- a/src/models/subaccounts.rs +++ b/src/models/subaccounts.rs @@ -133,3 +133,10 @@ pub struct FetchSubaccountResponse { /// Fetch Subaccount response data. pub data: SubaccountsResponseData, } + +/// This struct is used to create the body for deleting a subaccount on your integration. +#[derive(Debug, Deserialize, Serialize)] +pub struct DeleteSubAccountBody { + /// This is the sub account code + pub subaccount: String, +} diff --git a/tests/api/main.rs b/tests/api/main.rs index 895bcf1..56e4397 100644 --- a/tests/api/main.rs +++ b/tests/api/main.rs @@ -1,5 +1,5 @@ pub mod charge; pub mod helpers; +mod subaccount; pub mod transaction; pub mod transaction_split; -mod subaccount; diff --git a/tests/api/transaction_split.rs b/tests/api/transaction_split.rs index e5dd943..b0a0d10 100644 --- a/tests/api/transaction_split.rs +++ b/tests/api/transaction_split.rs @@ -3,78 +3,62 @@ use fake::{ Fake, }; use paystack::{ - CreateSubaccountBodyBuilder, CreateTransactionSplitBodyBuilder, Currency, SubaccountBodyBuilder, + CreateSubaccountBodyBuilder, CreateTransactionSplitBody, CreateTransactionSplitBodyBuilder, + Currency, DeleteSubAccountBody, PaystackClient, SubaccountBody, SubaccountBodyBuilder, + UpdateTransactionSplitBodyBuilder, }; use crate::helpers::{get_bank_account_number_and_code, get_paystack_client}; -#[tokio::test] -async fn create_transaction_split_passes_with_valid_data() { - // Arrange - let client = get_paystack_client(); - - let txn_split_name: String = FirstName().fake(); +async fn create_subaccount_body( + client: &PaystackClient<'_>, + percentage_charge: f32, + share: f32, +) -> SubaccountBody { let (account_number, bank_code, _bank_name) = get_bank_account_number_and_code(); - // Create first subaccount - let first_business_name: String = CompanyName().fake(); - let first_description: String = Sentence(5..10).fake(); + let business_name: String = CompanyName().fake(); + let description: String = Sentence(5..10).fake(); let body = CreateSubaccountBodyBuilder::default() - .business_name(first_business_name) + .business_name(business_name) .settlement_bank(bank_code.clone()) .account_number(account_number.clone()) - .percentage_charge(18.2) - .description(first_description) + .percentage_charge(percentage_charge) + .description(description) .build() .unwrap(); - let first_subaccount = client + let subaccount = client .subaccount .create_subaccount(body) .await .expect("Unable to Create a subaccount"); - // Create second subaccount - let second_business_name: String = CompanyName().fake(); - let second_description: String = Sentence(5..10).fake(); - - let body = CreateSubaccountBodyBuilder::default() - .business_name(second_business_name) - .settlement_bank(bank_code.clone()) - .account_number(account_number.clone()) - .percentage_charge(10.0) - .description(second_description) + SubaccountBodyBuilder::default() + .share(share) + .subaccount(subaccount.data.subaccount_code) .build() - .unwrap(); + .unwrap() +} - let second_subaccount = client - .subaccount - .create_subaccount(body) - .await - .expect("Unable to create a subaccount"); +async fn build_transaction_split( + client: &PaystackClient<'_>, +) -> (String, CreateTransactionSplitBody) { + let txn_split_name: String = FirstName().fake(); - // Create subaccount bodies - let first_subaccount_body = SubaccountBodyBuilder::default() - .share(90.0) - .subaccount(first_subaccount.data.subaccount_code) - // .subaccount_code("ACCT_xv2cusld7thdw7r".to_string()) - .build() - .unwrap(); + // Create first subaccount body + let first_subaccount_body = create_subaccount_body(client, 18.2, 80.0).await; - let second_subaccount_body = SubaccountBodyBuilder::default() - .share(10.0) - .subaccount(second_subaccount.data.subaccount_code) - .build() - .unwrap(); + // Create second subaccount body + let second_subaccount_body = create_subaccount_body(client, 10.0, 10.0).await; // Create transaction split body - let split_body = CreateTransactionSplitBodyBuilder::default() - .name(txn_split_name) + let body = CreateTransactionSplitBodyBuilder::default() + .name(txn_split_name.clone()) .split_type(paystack::SplitType::Percentage) .currency(paystack::Currency::NGN) .bearer_type(paystack::BearerType::Subaccount) - // .bearer_subaccount("ACCT_xv2cusld7thdw7r") .subaccounts(vec![ first_subaccount_body.clone(), second_subaccount_body.clone(), @@ -83,7 +67,14 @@ async fn create_transaction_split_passes_with_valid_data() { .build() .unwrap(); - println!("{:#?}", split_body); + (txn_split_name, body) +} + +#[tokio::test] +async fn create_transaction_split_passes_with_valid_data() { + // Arrange + let client = get_paystack_client(); + let (_, split_body) = build_transaction_split(&client).await; // Act let res = client @@ -92,8 +83,6 @@ async fn create_transaction_split_passes_with_valid_data() { .await .expect("Failed to create transaction split"); - // dbg!(res); - // Assert assert!(res.status); assert_eq!(res.message, "Split created"); @@ -101,28 +90,320 @@ async fn create_transaction_split_passes_with_valid_data() { } #[tokio::test] -async fn create_transaction_split_fails_with_invalid_data() {} +async fn create_transaction_split_fails_with_invalid_data() { + //Arrange + let client = get_paystack_client(); + let split_name: String = FirstName().fake(); + let body = CreateTransactionSplitBodyBuilder::default() + .name(split_name) + .split_type(paystack::SplitType::Flat) + .currency(paystack::Currency::EMPTY) + .subaccounts(vec![]) + .bearer_type(paystack::BearerType::Subaccount) + .bearer_subaccount("non_existent_subaccount".to_string()) + .build() + .unwrap(); + + //Act + let res = client + .transaction_split + .create_transaction_split(body) + .await; + + if let Err(err) = res { + assert_eq!(err.to_string(), "Request failed - Status Code: 400 Bad Request Body: {\"status\":false,\"message\":\"At least one subaccount is required\"}".to_string()); + } else { + assert!(false); + } +} #[tokio::test] -async fn list_transaction_splits_in_the_integration() {} +async fn list_transaction_splits_in_the_integration() { + // Arrange + let client = get_paystack_client(); + let (split_name, split_body) = build_transaction_split(&client).await; + + // Act + // Create transaction split + client + .transaction_split + .create_transaction_split(split_body) + .await + .expect("Failed to create transaction split"); + + // Fetch the splits + let res = client + .transaction_split + .list_transaction_splits(Some(&split_name), None) + .await; + + // Assert + if let Ok(data) = res { + assert!(data.status); + assert_eq!(data.message, "Split retrieved".to_string()); + assert_eq!(data.data.len(), 1); + + let transaction_split = data.data.first().unwrap(); + assert_eq!( + transaction_split.split_type, + paystack::SplitType::Percentage.to_string() + ); + } else { + assert!(false); + } +} #[tokio::test] -async fn fetch_a_transaction_split_in_the_integration() {} +async fn fetch_a_transaction_split_in_the_integration() { + //Arrange + let client = get_paystack_client(); + let (_, split_body) = build_transaction_split(&client).await; + + // Act + let transaction_split = client + .transaction_split + .create_transaction_split(split_body) + .await + .expect("Failed to create transaction split"); + + let res = client + .transaction_split + .fetch_transaction_split(&transaction_split.data.id.to_string()) + .await + .unwrap(); + + // Assert + assert!(res.status); + assert_eq!( + res.data.total_subaccounts as usize, + res.data.subaccounts.len() + ); + assert_eq!(res.message, "Split retrieved".to_string()); +} #[tokio::test] -async fn update_a_transaction_split_passes_with_valid_data() {} +async fn update_a_transaction_split_passes_with_valid_data() { + //Arrange + let client = get_paystack_client(); + let (_, split_body) = build_transaction_split(&client).await; + + // Act + let transaction_split = client + .transaction_split + .create_transaction_split(split_body) + .await + .expect("Failed to create transaction split"); + + let new_subaccount_body = create_subaccount_body(&client, 44.3, 30.0).await; + let new_split_name: String = FirstName().fake(); + + // create update split body + let update_split_body = UpdateTransactionSplitBodyBuilder::default() + .active(false) + .bearer_type(Some(paystack::BearerType::Account)) + .bearer_subaccount(Some(new_subaccount_body)) + .name(new_split_name.clone()) + .build() + .unwrap(); + + // Act + let split_id = transaction_split.data.id.to_string(); + let res = client + .transaction_split + .update_transaction_split(&split_id, update_split_body) + .await; + + // Assert + if let Ok(data) = res { + assert!(data.status); + assert_eq!(data.message, "Split group updated".to_string()); + assert!(!data.data.active.unwrap()); + assert_eq!(data.data.name, new_split_name); + } else { + assert!(false); + } +} #[tokio::test] -async fn update_a_transaction_split_fails_with_invalid_data() {} +async fn update_a_transaction_split_fails_with_invalid_data() { + //Arrange + let client = get_paystack_client(); + let (_, split_body) = build_transaction_split(&client).await; + + // Act + let transaction_split = client + .transaction_split + .create_transaction_split(split_body) + .await + .expect("Failed to create transaction split"); + + // create update split body + let update_split_body = UpdateTransactionSplitBodyBuilder::default() + .active(true) + .bearer_type(Some(paystack::BearerType::Subaccount)) + .bearer_subaccount(None) + .name("".to_string()) + .build() + .unwrap(); + + // Act + let split_id = transaction_split.data.id.to_string(); + let res = client + .transaction_split + .update_transaction_split(&split_id, update_split_body) + .await; + + // Assert + if let Err(err) = res { + assert!(err.to_string().contains("Bearer subaccount is required")); + } else { + assert!(false); + } +} #[tokio::test] -async fn add_a_transaction_split_subaccount_passes_with_valid_data() {} +async fn add_a_transaction_split_subaccount_passes_with_valid_data() { + // Arrange + let client = get_paystack_client(); + let (_, split_body) = build_transaction_split(&client).await; + + // Act + let transaction_split = client + .transaction_split + .create_transaction_split(split_body) + .await + .expect("Failed to create transaction split"); + + let new_subaccount_body = create_subaccount_body(&client, 2.8, 4.0).await; + + let split_id = transaction_split.data.id.to_string(); + let res = client + .transaction_split + .add_or_update_subaccount_split(&split_id, new_subaccount_body.clone()) + .await + .unwrap(); + + // Assert + assert!(res.status); + assert_eq!(res.message, "Subaccount added"); + assert_eq!(res.data.subaccounts.len(), 3); +} #[tokio::test] -async fn add_a_transaction_split_subaccount_fails_with_invalid_data() {} +async fn add_a_transaction_split_subaccount_fails_with_invalid_data() { + // Arrange + let client = get_paystack_client(); + let (_, split_body) = build_transaction_split(&client).await; + + // Act + let transaction_split = client + .transaction_split + .create_transaction_split(split_body) + .await + .expect("Failed to create transaction split"); + + let new_subaccount_body = create_subaccount_body(&client, 55.0, 120.0).await; + + let split_id = transaction_split.data.id.to_string(); + let res = client + .transaction_split + .add_or_update_subaccount_split(&split_id, new_subaccount_body.clone()) + .await; + + // Assert + if let Err(err) = res { + assert!(err.to_string().contains("Shares cannot exceed 100%")); + } else { + assert!(false); + }; +} #[tokio::test] -async fn remove_a_subaccount_from_a_transaction_split_passes_with_valid_data() {} +async fn remove_a_subaccount_from_a_transaction_split_passes_with_valid_data() { + // Arrange + let client = get_paystack_client(); + let (_, split_body) = build_transaction_split(&client).await; + + // Act + let transaction_split = client + .transaction_split + .create_transaction_split(split_body) + .await + .expect("Failed to create transaction split"); + let split_id = transaction_split.data.id.to_string(); + + // Validate the number of subaccounts attached + assert_eq!(transaction_split.data.subaccounts.len(), 2); + + let subaccount_data = transaction_split.data.subaccounts.first().unwrap(); + let code = &subaccount_data.subaccount.subaccount_code; + // Remove subaccount + let res = client + .transaction_split + .remove_subaccount_from_transaction_split( + &split_id, + DeleteSubAccountBody { + subaccount: code.to_string(), + }, + ) + .await + .unwrap(); + + // Assert + assert!(res.status); + assert_eq!(res.message, "Subaccount removed"); + + // Revalidate number of subaccounts attached + let transaction_split = client + .transaction_split + .fetch_transaction_split(&split_id) + .await + .unwrap(); + + // Assert + assert!(transaction_split.status); + assert_eq!(transaction_split.data.total_subaccounts, 1); + let remaining_subaccount = transaction_split.data.subaccounts.first().unwrap(); + assert_ne!( + remaining_subaccount.subaccount.subaccount_code, + subaccount_data.subaccount.subaccount_code + ); +} #[tokio::test] -async fn remove_a_subaccount_from_a_transaction_split_fails_with_invalid_data() {} +async fn remove_a_subaccount_from_a_transaction_split_fails_with_invalid_data() { + // Arrange + let client = get_paystack_client(); + let (_, split_body) = build_transaction_split(&client).await; + + // Act + let transaction_split = client + .transaction_split + .create_transaction_split(split_body) + .await + .expect("Failed to create transaction split"); + let split_id = transaction_split.data.id.to_string(); + + // Validate the number of subaccounts attached + assert_eq!(transaction_split.data.subaccounts.len(), 2); + + // Remove subaccount + let res = client + .transaction_split + .remove_subaccount_from_transaction_split( + &split_id, + DeleteSubAccountBody { + subaccount: "".to_string(), + }, + ) + .await; + + // Assert + if let Err(err) = res { + assert!(err + .to_string() + .contains("Please specify subaccount to be removed")) + } else { + assert!(false) + } +}