Skip to content

Commit

Permalink
Add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
itsyaasir committed Nov 18, 2023
1 parent 9bad181 commit f0fa2d7
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
45 changes: 45 additions & 0 deletions src/services/express_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ pub struct MpesaExpressRequest<'mpesa> {
pub timestamp: DateTime<Local>,
/// This is the transaction type that is used to identify the transaction
/// when sending the request to M-PESA
///
/// The TransactionType for Mpesa Express is either
/// `CommandId::BusinessBuyGoods` or
/// `CommandId::CustomerPayBillOnline`
pub transaction_type: CommandId,
/// This is the Amount transacted normally a numeric value
pub amount: f64,
Expand Down Expand Up @@ -99,20 +103,45 @@ pub struct MpesaExpressResponse {
pub struct MpesaExpress<'mpesa, Env: ApiEnvironment> {
#[builder(pattern = "immutable")]
client: &'mpesa Mpesa<Env>,
/// This is the organization's shortcode (Paybill or Buygoods - A 5 to
/// 6-digit account number) used to identify an organization and receive
/// the transaction.
#[builder(setter(into))]
business_short_code: &'mpesa str,
/// This is the transaction type that is used to identify the transaction
/// when sending the request to M-PESA
///
/// The TransactionType for Mpesa Express is either
/// `CommandId::BusinessBuyGoods` or
/// `CommandId::CustomerPayBillOnline`
transaction_type: CommandId,
/// This is the Amount transacted normally a numeric value
#[builder(setter(into))]
amount: f64,
/// The phone number sending money.
party_a: &'mpesa str,
/// The organization that receives the funds
party_b: &'mpesa str,
/// The Mobile Number to receive the STK Pin Prompt.
phone_number: &'mpesa str,
/// A CallBack URL is a valid secure URL that is used to receive
/// notifications from M-Pesa API.
/// It is the endpoint to which the results will be sent by M-Pesa API.
#[builder(try_setter, setter(into))]
callback_url: Url,
/// Account Reference: This is an Alpha-Numeric parameter that is defined
/// by your system as an Identifier of the transaction for
/// CustomerPayBillOnline
#[builder(setter(into))]
account_ref: &'mpesa str,
/// This is any additional information/comment that can be sent along with
/// the request from your system
#[builder(setter(into, strip_option), default)]
transaction_desc: Option<&'mpesa str>,
/// This is the password used for encrypting the request sent:
/// The password for encrypting the request is obtained by base64 encoding
/// BusinessShortCode, Passkey and Timestamp.
/// The timestamp format is YYYYMMDDHHmmss
#[builder(setter(into))]
pass_key: &'mpesa str,
}
Expand Down Expand Up @@ -169,6 +198,22 @@ impl<'mpesa, Env: ApiEnvironment> MpesaExpress<'mpesa, Env> {
MpesaExpressBuilder::default().client(client)
}

/// Encodes the password for the request
/// The password for encrypting the request is obtained by base64 encoding
/// BusinessShortCode, Passkey and Timestamp.
/// The timestamp format is YYYYMMDDHHmmss
pub fn encode_password(business_short_code: &str, pass_key: Option<&'mpesa str>) -> String {
base64::encode_block(
format!(
"{}{}{}",
business_short_code,
pass_key.unwrap_or(DEFAULT_PASSKEY),
chrono::Local::now()
)
.as_bytes(),
)
}

/// Creates a new `MpesaExpress` from a `MpesaExpressRequest`
pub fn from_request(
client: &'mpesa Mpesa<Env>,
Expand Down
54 changes: 54 additions & 0 deletions tests/mpesa-rust/stk_push_test.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use mpesa::services::{MpesaExpress, MpesaExpressRequest};
use mpesa::CommandId;
use serde_json::json;
use wiremock::matchers::{method, path};
use wiremock::{Mock, ResponseTemplate};

use crate::get_mpesa_client;
use crate::helpers::TestEnvironment;

#[tokio::test]
async fn stk_push_success() {
Expand Down Expand Up @@ -86,3 +89,54 @@ async fn stk_push_only_accepts_specific_tx_type() {
"Invalid transaction type. Expected BusinessBuyGoods or CustomerPayBillOnline"
);
}

#[tokio::test]
async fn express_request_test_using_struct_initialization() {
let (client, server) = get_mpesa_client!();

let sample_response_body = json!({
"MerchantRequestID": "16813-1590513-1",
"CheckoutRequestID": "ws_CO_DMZ_12321_23423476",
"ResponseDescription": "Accept the service request successfully.",
"ResponseCode": "0",
"CustomerMessage": "Success. Request accepted for processing"
});

let password = MpesaExpress::<TestEnvironment>::encode_password("174379", None);

let request = MpesaExpressRequest {
business_short_code: "174379",
transaction_type: CommandId::BusinessBuyGoods,
amount: 500.0,
party_a: "254708374149",
party_b: "174379",
phone_number: "254708374149",
password,
timestamp: chrono::Local::now(),
call_back_url: "https://test.example.com/api".try_into().unwrap(),
account_reference: "test",
transaction_desc: None,
};

Mock::given(method("POST"))
.and(path("/mpesa/stkpush/v1/processrequest"))
.respond_with(ResponseTemplate::new(200).set_body_json(sample_response_body))
.expect(1)
.mount(&server)
.await;

let request = MpesaExpress::from_request(&client, request, None);

let response = request.send().await.unwrap();

assert_eq!(response.merchant_request_id, "16813-1590513-1");
assert_eq!(response.checkout_request_id, "ws_CO_DMZ_12321_23423476");
assert_eq!(
response.response_description,
"Accept the service request successfully."
);
assert_eq!(
response.customer_message,
"Success. Request accepted for processing"
);
}

0 comments on commit f0fa2d7

Please sign in to comment.