33import pytest
44from unittest .mock import MagicMock
55
6- from mpesakit .services .b2b import B2BService
6+ from mpesakit .services .b2b import B2BService , AsyncB2BService
77from mpesakit .business_buy_goods import (
88 BusinessBuyGoodsResponse ,
99)
1313from mpesakit .b2b_express_checkout import (
1414 B2BExpressCheckoutResponse ,
1515)
16- from mpesakit .auth import TokenManager
17- from mpesakit .http_client import HttpClient
16+ from mpesakit .auth import TokenManager , AsyncTokenManager
17+ from mpesakit .http_client import HttpClient , AsyncHttpClient
18+ from unittest .mock import AsyncMock
1819
1920
2021@pytest .fixture
@@ -30,6 +31,18 @@ def mock_http_client():
3031 """Mock HttpClient to simulate HTTP requests."""
3132 return MagicMock (spec = HttpClient )
3233
34+ @pytest .fixture
35+ def mock_async_token_manager ():
36+ """Mock AsyncTokenManager to return a fixed token."""
37+ mock = AsyncMock (spec = AsyncTokenManager )
38+ mock .get_token .return_value = "async_test_token"
39+ return mock
40+
41+
42+ @pytest .fixture
43+ def mock_async_http_client ():
44+ """Mock AsyncHttpClient to simulate async HTTP requests."""
45+ return AsyncMock (spec = AsyncHttpClient )
3346
3447@pytest .fixture
3548def b2b_service (mock_http_client , mock_token_manager ):
@@ -156,3 +169,149 @@ def test_b2b_service_initializes_services_correctly(
156169 if hasattr (service , "buygoods_service" ):
157170 assert service .buygoods_service .http_client is mock_http_client
158171 assert service .buygoods_service .token_manager is mock_token_manager
172+ @pytest .fixture
173+ def mock_async_http_client ():
174+ """Mock async HttpClient to simulate async HTTP requests."""
175+ client = MagicMock (spec = HttpClient )
176+ client .post = AsyncMock ()
177+ return client
178+
179+
180+ @pytest .fixture
181+ def async_b2b_service (mock_async_http_client , mock_async_token_manager ):
182+ """Fixture to create an AsyncB2BService instance with mocked dependencies."""
183+ return AsyncB2BService (
184+ http_client = mock_async_http_client ,
185+ token_manager = mock_async_token_manager
186+ )
187+
188+
189+ @pytest .mark .asyncio
190+ async def test_async_express_checkout_calls_ussd_push (
191+ async_b2b_service , mock_async_http_client
192+ ):
193+ """Test that async express_checkout calls the B2BExpressCheckout service."""
194+ response_data = {"code" : "0" , "status" : "USSD Initiated Successfully" }
195+ mock_async_http_client .post .return_value = response_data
196+
197+ resp = await async_b2b_service .express_checkout (
198+ primary_short_code = "123456" ,
199+ receiver_short_code = "654321" ,
200+ amount = 100 ,
201+ payment_ref = "Invoice123" ,
202+ callback_url = "http://example.com/result" ,
203+ partner_name = "VendorName" ,
204+ request_ref_id = "550e8400-e29b-41d4-a716-446655440000" ,
205+ )
206+
207+ assert isinstance (resp , B2BExpressCheckoutResponse )
208+ assert resp .code == "0"
209+ assert resp .status == "USSD Initiated Successfully"
210+
211+
212+ @pytest .mark .asyncio
213+ async def test_async_paybill_calls_paybill (async_b2b_service , mock_async_http_client ):
214+ """Test that async paybill calls the BusinessPayBill service."""
215+ response_data = {
216+ "OriginatorConversationID" : "5118-111210482-1" ,
217+ "ConversationID" : "AG_20230420_2010759fd5662ef6d054" ,
218+ "ResponseCode" : "0" ,
219+ "ResponseDescription" : "Accept the service request successfully." ,
220+ }
221+ mock_async_http_client .post .return_value = response_data
222+
223+ resp = await async_b2b_service .paybill (
224+ initiator = "apiuser" ,
225+ security_credential = "secure" ,
226+ amount = 200 ,
227+ party_a = 111111 ,
228+ party_b = 222222 ,
229+ account_reference = "Ref001" ,
230+ requester = "254700000000" ,
231+ remarks = "Test" ,
232+ queue_timeout_url = "http://timeout.url" ,
233+ result_url = "http://result.url" ,
234+ )
235+
236+ assert isinstance (resp , BusinessPayBillResponse )
237+ assert resp .is_successful () is True
238+ assert resp .ResponseDescription == "Accept the service request successfully."
239+
240+
241+ @pytest .mark .asyncio
242+ async def test_async_buygoods_calls_buy_goods (
243+ async_b2b_service , mock_async_http_client
244+ ):
245+ """Test that async buygoods calls the BusinessBuyGoods service."""
246+ response_data = {
247+ "OriginatorConversationID" : "5118-111210482-1" ,
248+ "ConversationID" : "AG_20230420_2010759fd5662ef6d054" ,
249+ "ResponseCode" : "0" ,
250+ "ResponseDescription" : "Accept the service request successfully." ,
251+ }
252+ mock_async_http_client .post .return_value = response_data
253+
254+ resp = await async_b2b_service .buygoods (
255+ initiator = "apiuser" ,
256+ security_credential = "secure" ,
257+ amount = 300 ,
258+ party_a = 333333 ,
259+ party_b = 444444 ,
260+ account_reference = "Ref002" ,
261+ requester = "254711111111" ,
262+ remarks = "BuyGoods" ,
263+ queue_timeout_url = "http://timeout.url" ,
264+ result_url = "http://result.url" ,
265+ occassion = "Occasion" ,
266+ )
267+
268+ assert isinstance (resp , BusinessBuyGoodsResponse )
269+ assert resp .is_successful () is True
270+ assert resp .ResponseDescription == "Accept the service request successfully."
271+
272+
273+ @pytest .mark .asyncio
274+ async def test_async_express_checkout_filters_kwargs (
275+ async_b2b_service , mock_async_http_client
276+ ):
277+ """Test that async express_checkout filters out unexpected kwargs."""
278+ response_data = {"code" : "0" , "status" : "USSD Initiated Successfully" }
279+ mock_async_http_client .post .return_value = response_data
280+
281+ resp = await async_b2b_service .express_checkout (
282+ primary_short_code = "123456" ,
283+ receiver_short_code = "654321" ,
284+ amount = 100 ,
285+ payment_ref = "Invoice123" ,
286+ callback_url = "http://example.com/result" ,
287+ partner_name = "VendorName" ,
288+ request_ref_id = "550e8400-e29b-41d4-a716-446655440000" ,
289+ unexpected_field = "should be ignored" ,
290+ )
291+
292+ assert isinstance (resp , B2BExpressCheckoutResponse )
293+ assert resp .is_successful () is True
294+ assert resp .status == "USSD Initiated Successfully"
295+
296+
297+ def test_async_b2b_service_initializes_services_correctly (
298+ mock_async_http_client , mock_async_token_manager
299+ ):
300+ """Test AsyncB2BService initializes dependencies with correct arguments."""
301+ service = AsyncB2BService (
302+ http_client = mock_async_http_client ,
303+ token_manager = mock_async_token_manager ,
304+ )
305+
306+ assert service .http_client is mock_async_http_client
307+ assert service .token_manager is mock_async_token_manager
308+
309+ if hasattr (service , "express_checkout_service" ):
310+ assert service .express_checkout_service .http_client is mock_async_http_client
311+ assert service .express_checkout_service .token_manager is mock_async_token_manager
312+ if hasattr (service , "paybill_service" ):
313+ assert service .paybill_service .http_client is mock_async_http_client
314+ assert service .paybill_service .token_manager is mock_async_token_manager
315+ if hasattr (service , "buygoods_service" ):
316+ assert service .buygoods_service .http_client is mock_async_http_client
317+ assert service .buygoods_service .token_manager is mock_async_token_manager
0 commit comments