# -*- coding: utf-8 -*- # !/usr/bin/env python import simplejson as json from django.conf import settings from django.core.urlresolvers import reverse from hypothesis import given, strategies as st from pytest_mock import MockFixture from typing import Dict, Union from apilib.monetary import RMB from apilib.utils_datetime import generate_timestamp_ex from apps.web.agent.define import AGENT_INCOME_TYPE from apps.web.agent.models import Agent from apps.web.common.models import WithdrawRecord from apps.web.common.transaction import WITHDRAW_PAY_TYPE, WithdrawStatus from apps.web.core.payment.wechat import WechatWithdrawGateway from apps.web.dealer.define import DEALER_INCOME_TYPE from apps.web.dealer.models import Dealer from apps.web.dealer.urls import urlpatterns from library.wechatbase.exceptions import WeChatPayException, WechatNetworkException from testcase.unit.base import RequestTestClient from testcase.unit.common import url_fn, BANK_ACCOUNT_CODE from testcase.unit.responses import WECHAT_WITHDRAW_ERROR, BANK_WITHDRAW_SUCCEEDED, BANK_WITHDRAW_ERROR ViewName = str Url = str urls = {} # type: Dict[ViewName, Url] for _ in urlpatterns: try: if reverse(_.callback).startswith('/dealer'): urls[_.callback.func_name] = reverse(_.callback) except: pass # def test_views_are_protected(mocker, client, dealer_client): # from apps.web.core.messages.sms import GenericSMSProvider # from apps.web.core.auth.wechat import WechatAuthBridge # # mocker.patch.object(GenericSMSProvider, 'get', return_value = (True, '')) # mocker.patch.object(GenericSMSProvider, 'verify', return_value = (True, '')) # # mocker.patch.object(WechatAuthBridge, 'get_user_info', return_value = {}) # # excluded = [ # urls['verifyForgetCode'], # urls['getOwnerAgents'], # urls['dealerRegister'], # urls['getCheckCode'], # urls['login'], # urls['getDealerRegisterSMSCode'], # urls['getSubAccountCheckCode'], # urls['getDealerListByAgent'], # urls['getDealerDetailList'], # urls['verifySubAccountForgetCode'], # urls['payGateway'], # urls['getJoinerDetailList'], # urls['getJoinRecordList'], # urls['getCardPwd'], # urls['setCardPwd'], # urls['setCardMode'], # urls['getCardMode'], # urls['updateDealerOrderAddr'], # urls['sendCoinsForCard'], # urls['watchLogin'], # urls['getUserCardTicketDetail'], # urls['dealerWithdraw'], # urls['saveAutoWithdrawConfig'], # urls['getDeviceFunctionForIC'], # urls['addEditItemType'], # urls['getDevicePort'], # urls['getDeviceFunction'], # urls['exportBusinessStats'], # urls['saveEntityCard'], # urls['swapCardNo'], # urls['deviceOfflineTrend'], # urls['setElecFeeConf'], # urls['adjustUserVirtualCardQuota'], # urls['deleteBattery'], # urls['getDeviceStatistics'], # urls['deviceOfflineTrend'], # urls['addEditSubAccount'], # urls['handlerKeepingOrder'], # urls['setSelfRechargeCardPrice'], # urls['createEmptyCardTicket'], # urls['selfRechargeCardRecords'], # urls['exportGroupStatistics'], # urls['addEditCell'], # urls['subAccountRegisterCode'], # urls['updateExchangeOrder'], # urls['getDashboard'], # urls['getDeviceCells'], # urls['remarkUserVirtual'], # urls['saveAccountPermission'], # urls['exportIncomeOrderList'], # urls['setDeviceFunctionByKey'], # urls['messageRead'], # urls['paymentOrderRecords'], # urls['getDeviceCellsFromDB'], # urls['updateInfo'], # urls['getUserCardRecord'], # urls['toggleOnsale'], # urls['deleteOnsale'], # urls['getPeakValueTrendByDevice'], # urls['exportIncomeAggregate'], # urls['getGroupStatistics'], # urls['exportConsumptionOrderList'], # urls['getAccountPermissionById'], # urls['getFeatureList'], # urls['deleteSubAccount'], # urls['getCheckCodeForNewTel'], # urls['equipmentList'], # urls['getUserIdentifyList'], # urls['addGoodsForStock'], # urls['setDeviceFunction'], # urls['adjustUserVirtualCardTime'], # urls['updateStockQuantity'], # urls['bindVirtualCardToRechargeIDCard'], # urls['asyncBatterySnByEnter'], # urls['exportAPIOrderList'], # urls['saveDeviceElcPrice'], # urls['freezeCard'], # urls['deleteElcPriceMod'], # urls['deleteAddress'], # urls['getDeviceList'], # urls['adjustUserVirtualState'], # urls['addGoodsToDeviceCell'], # urls['finishedOrder'], # urls['withdrawEntry'], # urls['asyncBatterySnByDevice'], # urls['createJoinOrder'], # urls['editDefaultJoiner'], # urls['updateStockQuantityForGoods'], # urls['saveElcPriceMod'], # urls['deleteCell'], # urls['getConsumptionOrderList'], # urls['getAPIOrderList'], # urls['getIncomeOrderList'], # urls['delUserActiveInfo'], # urls['handleAlarm'], # urls['deleteItemStock'], # urls['getOnPointsOrderList'], # urls['getOrderStatistics'], # urls['getOrderTrendByGroup'], # urls['getDefaultJoiner'], # urls['setDeviceFunctionParam'], # urls['getWalletWithdrawInfo'], # urls['getJoinerDetailList'], # urls['getJoinRecordList'], # urls['getDealerDetailList'], # urls['getDealerListByAgent'], # urls['editDefaultJoiner'], # urls['getDefaultJoiner'], # urls['getJoinerDetailList'], # urls['getSignalTrendByDevice'], # urls['unlockCell'], # urls['getDeviceFunctionByKey'], # urls['unbindVirtualCardToRechargeIDCard'], # urls['quitJoin'], # urls['ActivateUser'], # urls['getUserCardTicketList'], # urls['delUserVirtualCard'], # urls['editCard'] # ] # # for url in (_ for _ in urls.values() if _ not in excluded): # assert client.get(url).status_code in (401, 405), '%s is not protected' % (url,) # dealer_client._re_login() # assert dealer_client.get(url).status_code in (200, 405, 302), '%s cannot be viewed with dealer client' % (url,) # ## ## Withdraw related ## @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), amount = st.one_of(st.floats(min_value = 10, max_value = 1000), st.integers(min_value = 10, max_value = 1000))) def test_dealer_incr_fund(dealer, source_key, income_type, amount): # type:(Dealer, str, str, Union[int, float])->None """ :return: """ added = RMB(amount) left = dealer.sub_balance(income_type, source_key) + added dealer.incr_fund(income_type, source_key, added) assert dealer.reload().sub_balance(income_type, source_key) == left ## ## Withdraw related ## @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), amount = st.one_of(st.floats(min_value = 10, max_value = 1000), st.integers(min_value = 10, max_value = 1000))) def test_dealer_decr_fund(dealer, source_key, income_type, amount): # type:(Dealer, str, str, Union[int, float])->None """ :return: """ sub = RMB(amount) left = dealer.sub_balance(income_type, source_key) - sub dealer.decr_fund(income_type, source_key, sub) assert dealer.reload().sub_balance(income_type, source_key) == left ## ## Withdraw related ## @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), amount = st.one_of(st.floats(min_value = 10, max_value = 1000), st.integers(min_value = 10, max_value = 1000))) def test_dealer_set_balance(dealer, source_key, income_type, amount): # type:(Dealer, str, str, Union[int, float])->None """ :return: """ money = RMB(amount) dealer.set_balance(income_type, source_key, money) assert dealer.reload().sub_balance(income_type, source_key) == money @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), amount = st.one_of(st.floats(min_value = 10, max_value = 1000), st.integers(min_value = 10, max_value = 1000)), initial_amount = st.one_of(st.floats(min_value = 1000, max_value = 10000), st.integers(min_value = 1000, max_value = 10000))) def test_dealer_recover_freeze_balance(dealer, source_key, income_type, amount, initial_amount): # type:(Dealer, str, str, Union[int, float], Union[int, float])->None initial = RMB(initial_amount) dealer.set_balance(income_type, source_key, initial) dealer.reload() assert dealer.sub_balance(income_type, source_key) == initial withdrawed = RMB(amount) transaction_id = str(generate_timestamp_ex()) pre_frozenBalance = dealer.sub_frozen_balance(income_type, source_key) pre_balance = dealer.sub_balance(income_type, source_key) dealer.freeze_balance(income_type, withdrawed, source_key, transaction_id, True) dealer.reload() assert dealer.sub_frozen_balance(income_type, source_key) - withdrawed == pre_frozenBalance assert dealer.sub_balance(income_type, source_key) + withdrawed == pre_balance dealer.freeze_balance(income_type, withdrawed, source_key, transaction_id, True) dealer.reload() assert dealer.sub_frozen_balance(income_type, source_key) - withdrawed == pre_frozenBalance assert dealer.sub_balance(income_type, source_key) + withdrawed == pre_balance exists = False for item in dealer.inhandWithdrawList: if transaction_id == item['transaction_id']: exists = True break assert exists # test inverse dealer.recover_frozen_balance(income_type, withdrawed, source_key, transaction_id) dealer.reload() assert dealer.sub_frozen_balance(income_type, source_key) == pre_frozenBalance assert dealer.sub_balance(income_type, source_key) == pre_balance assert dealer.sub_balance(income_type, source_key) == initial exists = False for item in dealer.inhandWithdrawList: if transaction_id == item['transaction_id']: exists = True break assert not exists dealer.recover_frozen_balance(income_type, withdrawed, source_key, transaction_id) dealer.reload() assert dealer.sub_frozen_balance(income_type, source_key) == pre_frozenBalance assert dealer.sub_balance(income_type, source_key) == pre_balance assert dealer.sub_balance(income_type, source_key) == initial @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), amount = st.one_of(st.floats(min_value = 10, max_value = 1000), st.integers(min_value = 10, max_value = 1000)), initial_amount = st.one_of(st.floats(min_value = 1000, max_value = 10000), st.integers(min_value = 1000, max_value = 10000))) def test_dealer_clear_freeze_balance(dealer, source_key, income_type, amount, initial_amount): # type:(Dealer, str, str, Union[int, float], Union[int, float])->None initial = RMB(initial_amount) dealer.set_balance(income_type, source_key, initial) dealer.reload() assert dealer.sub_balance(income_type, source_key) == initial withdrawed = RMB(amount) transaction_id = str(generate_timestamp_ex()) pre_frozenBalance = dealer.sub_frozen_balance(income_type, source_key) pre_balance = dealer.sub_balance(income_type, source_key) dealer.freeze_balance(income_type, withdrawed, source_key, transaction_id, True) dealer.reload() assert dealer.sub_frozen_balance(income_type, source_key) - withdrawed == pre_frozenBalance assert dealer.sub_balance(income_type, source_key) + withdrawed == pre_balance dealer.freeze_balance(income_type, withdrawed, source_key, transaction_id, True) dealer.reload() assert dealer.sub_frozen_balance(income_type, source_key) - withdrawed == pre_frozenBalance assert dealer.sub_balance(income_type, source_key) + withdrawed == pre_balance exists = False for item in dealer.inhandWithdrawList: if transaction_id == item['transaction_id']: exists = True break assert exists dealer.clear_frozen_balance(transaction_id) dealer.reload() assert dealer.sub_frozen_balance(income_type, source_key) == pre_frozenBalance assert dealer.sub_balance(income_type, source_key) + withdrawed == pre_balance exists = False for item in dealer.inhandWithdrawList: if transaction_id == item['transaction_id']: exists = True break assert not exists dealer.clear_frozen_balance(transaction_id) dealer.reload() assert dealer.sub_frozen_balance(income_type, source_key) == pre_frozenBalance assert dealer.sub_balance(income_type, source_key) + withdrawed == pre_balance exists = False for item in dealer.inhandWithdrawList: if transaction_id == item['transaction_id']: exists = True break assert not exists # @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), # amount = st.one_of(st.floats(min_value = 10, max_value = 1000), st.integers(min_value = 10, max_value = 1000))) # def test_dealer_withdraw_with_wechat(mocker, dealer, agent, income_type, source_key, dealer_client, amount): # # type: (MockFixture, Dealer, Agent, str, str, RequestTestClient, float)->None # money = RMB(amount) # # pre_balance = dealer.sub_balance(income_type, source_key) # assert dealer.incr_fund(income_type, source_key, money), 'balance inc failed' # # dealer.reload() # # desired_service_fee = money * (dealer.withdrawFeeRatio / Const.WITHDRAW_FEE_UNIT) # desired_actual_pay = money - desired_service_fee # # if dealer.withdrawFeeRatio > Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO: # desired_agent_balance = agent.sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) + money * ( # (dealer.withdrawFeeRatio - Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO) / Const.WITHDRAW_FEE_UNIT) # else: # desired_agent_balance = agent.sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) # # from apps.web.dealer.views import dealerWithdraw # url = url_fn(dealerWithdraw) # payload = { # 'code': '1234', # 'payType': WITHDRAW_PAY_TYPE.WECHAT, # 'amount': str(money), # 'sourceType': income_type, # 'sourceId': source_key # } # # from apps.web.core.payment.wechat import WechatPaymentGateway # mocker.patch.object(WechatPaymentGateway, 'withdraw_via_changes', return_value = WECHAT_WITHDRAW_SUCCEEDED) # # response = dealer_client.post_json(url, data = payload) # # assert response # # dealer.reload() # # assert pre_balance == dealer.sub_balance(income_type, source_key) # # json_response = json.loads(response.content) # assert json_response['result'] == 1 # assert json_response['description'] == u'提现成功' # # withdraw_record_id = json_response['payload']['paymentId'] # # record = WithdrawRecord.objects(id = str(withdraw_record_id)).first() # type: WithdrawRecord # assert record # assert record.serviceFee == desired_service_fee # assert record.actualPay == desired_actual_pay # assert record.amount == money # assert record.ownerId == str(dealer.id) # assert record.payType == WITHDRAW_PAY_TYPE.WECHAT # assert record.balance == (pre_balance + money) # assert record.withdrawGatewayKey == source_key # assert record.status == WithdrawStatus.SUCCEEDED # # assert agent.reload().sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) == desired_agent_balance # # @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), # amount = st.one_of(st.floats(min_value = 0.01, max_value = settings.WITHDRAW_MINIMUM - 0.01), # st.integers(min_value = 1, max_value = settings.WITHDRAW_MINIMUM - 1))) # def test_dealer_withdraw_with_wechat_less_min(mocker, dealer, agent, income_type, source_key, dealer_client, amount): # # type: (MockFixture, Dealer, Agent, str, str, RequestTestClient, float)->None # money = RMB(amount) # # pre_balance = dealer.sub_balance(income_type, source_key) # assert dealer.incr_fund(income_type, source_key, money), 'balance inc failed' # # added_balance = dealer.reload().sub_balance(income_type, source_key) # # from apps.web.dealer.views import dealerWithdraw # url = url_fn(dealerWithdraw) # payload = { # 'code': '1234', # 'payType': WITHDRAW_PAY_TYPE.WECHAT, # 'amount': str(money), # 'sourceType': income_type, # 'sourceId': source_key # } # # response = dealer_client.post_json(url, data = payload) # # assert response # # json_response = json.loads(response.content) # assert json_response['result'] == 0 # assert json_response['description'] == u"提现金额不能少于%s元" % (settings.WITHDRAW_MINIMUM,) # # assert added_balance == dealer.reload().sub_balance(income_type, source_key) # # # @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), # amount = st.one_of( # st.floats(min_value = settings.WITHDRAW_MAXIMUM + 0.01, max_value = settings.WITHDRAW_MAXIMUM + 10), # st.integers(min_value = settings.WITHDRAW_MAXIMUM + 1, max_value = settings.WITHDRAW_MAXIMUM + 10))) # def test_dealer_withdraw_with_wechat_more_max(mocker, dealer, agent, income_type, source_key, dealer_client, amount): # # type: (MockFixture, Dealer, Agent, str, str, RequestTestClient, float)->None # money = RMB(amount) # # pre_balance = dealer.sub_balance(income_type, source_key) # assert dealer.incr_fund(income_type, source_key, money), 'balance inc failed' # # added_balance = dealer.reload().sub_balance(income_type, source_key) # # from apps.web.dealer.views import dealerWithdraw # url = url_fn(dealerWithdraw) # payload = { # 'code': '1234', # 'payType': WITHDRAW_PAY_TYPE.WECHAT, # 'amount': str(money), # 'sourceType': income_type, # 'sourceId': source_key # } # # response = dealer_client.post_json(url, data = payload) # # assert response # # json_response = json.loads(response.content) # assert json_response['result'] == 0 # assert json_response['description'] == u"单次提现金额不得大于%s元" % (settings.WITHDRAW_MAXIMUM,) # # assert added_balance == dealer.reload().sub_balance(income_type, source_key) # # # @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME])) # def test_dealer_withdraw_with_wechat_less_balance(mocker, dealer, agent, income_type, source_key, dealer_client): # # type: (MockFixture, Dealer, Agent, str, str, RequestTestClient)->None # withdrawed = RMB(20) # # dealer.set_balance(income_type, source_key, RMB(10)) # pre_balance = dealer.reload().sub_balance(income_type, source_key) # # assert pre_balance == RMB(10) # # from apps.web.dealer.views import dealerWithdraw # url = url_fn(dealerWithdraw) # payload = { # 'code': '1234', # 'payType': WITHDRAW_PAY_TYPE.WECHAT, # 'amount': str(withdrawed), # 'sourceType': income_type, # 'sourceId': source_key # } # # response = dealer_client.post_json(url, data = payload) # # assert response # # json_response = json.loads(response.content) # assert json_response['result'] == 0 # assert json_response['description'] == u"余额不足" # # assert pre_balance == dealer.reload().sub_balance(income_type, source_key) # # # @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME])) # def test_dealer_withdraw_with_wechat_manual(mocker, dealer, agent, income_type, dealer_client): # # type: (MockFixture, Dealer, Agent, str, RequestTestClient)->None # # agent.payAppWechat.manual_withdraw = True # agent.payAppWechat.save() # # gateway = WechatWithdrawGateway(agent.payAppWechat) # type: WechatWithdrawGateway # # withdrawed = RMB(20) # # pre_balance = dealer.sub_balance(income_type, gateway.source_key) # assert dealer.incr_fund(income_type, gateway.source_key, withdrawed), 'balance inc failed' # # added_balance = dealer.reload().sub_balance(income_type, gateway.source_key) # # from apps.web.dealer.views import dealerWithdraw # url = url_fn(dealerWithdraw) # payload = { # 'code': '1234', # 'payType': WITHDRAW_PAY_TYPE.WECHAT, # 'amount': str(withdrawed), # 'sourceType': income_type, # 'sourceId': gateway.gateway_key # } # # response = dealer_client.post_json(url, data = payload) # # assert response # # json_response = json.loads(response.content) # assert json_response['result'] == 0 # assert json_response['description'] == u"暂时不支持提现到微信" # # assert added_balance == dealer.reload().sub_balance(income_type, gateway.source_key) # # @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), test_id = st.integers(min_value = 0, max_value = len(WECHAT_WITHDRAW_ERROR) - 1)) def test_dealer_withdraw_via_wechat_error(mocker, dealer, agent, income_type, source_key, dealer_client, test_id): # type: (MockFixture, Dealer, Agent, str, str, RequestTestClient, int)->None """ 测试提现到微信失败的场景 """ desired_result = WECHAT_WITHDRAW_ERROR[test_id]['result'] if 'remarks' in WECHAT_WITHDRAW_ERROR[test_id]: desired_remarks = WECHAT_WITHDRAW_ERROR[test_id]['remarks'] else: desired_remarks = u'{err_code_des}({err_code})'.format( err_code = WECHAT_WITHDRAW_ERROR[test_id]['err_code'], err_code_des = WECHAT_WITHDRAW_ERROR[test_id][ 'err_code_des']) if 'show_message' in WECHAT_WITHDRAW_ERROR[test_id]: desired_show_message = WECHAT_WITHDRAW_ERROR[test_id]['show_message'] else: desired_show_message = WECHAT_WITHDRAW_ERROR[test_id]['err_code_des'] withdrawed = RMB(20) after_withdraw_balance = dealer.sub_balance(income_type, source_key) assert dealer.incr_fund(income_type, source_key, withdrawed), 'balance inc failed' before_withdraw_balance = dealer.reload().sub_balance(income_type, source_key) desired_service_fee = withdrawed * (dealer.withdrawFeeRatio.as_ratio) desired_actual_pay = withdrawed - desired_service_fee before_agent_balance = agent.sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) from apps.web.dealer.views import dealerWithdraw url = url_fn(dealerWithdraw) payload = { 'code': '1234', 'payType': WITHDRAW_PAY_TYPE.WECHAT, 'amount': str(withdrawed), 'sourceType': income_type, 'sourceId': source_key, 'openId': 'testOpenId' } from apps.web.core.payment.wechat import WechatWithdrawGateway if WECHAT_WITHDRAW_ERROR[test_id]['return_code'] == 'FAIL': mocker.patch.object(WechatWithdrawGateway, 'withdraw_via_changes', side_effect = WechatNetworkException( errCode = WECHAT_WITHDRAW_ERROR[test_id]['return_code'], errMsg = WECHAT_WITHDRAW_ERROR[test_id]['return_msg'])) else: mocker.patch.object(WechatWithdrawGateway, 'withdraw_via_changes', side_effect = WeChatPayException( errCode = WECHAT_WITHDRAW_ERROR[test_id]['err_code'], errMsg = WECHAT_WITHDRAW_ERROR[test_id]['err_code_des'])) response = dealer_client.post_json(url, data = payload) assert response json_response = json.loads(response.content) assert json_response['result'] == desired_result assert json_response['description'] == desired_show_message dealer.reload() if WECHAT_WITHDRAW_ERROR[test_id]['refund']: assert before_withdraw_balance == dealer.sub_balance(income_type, source_key) else: assert after_withdraw_balance == dealer.sub_balance(income_type, source_key) withdraw_record_id = json_response['payload']['paymentId'] record = WithdrawRecord.objects(id = str(withdraw_record_id)).first() # type: WithdrawRecord assert record assert record.serviceFee == desired_service_fee assert record.actualPay == desired_actual_pay assert record.amount == withdrawed assert record.ownerId == str(dealer.id) assert record.payType == WITHDRAW_PAY_TYPE.WECHAT assert record.balance == before_withdraw_balance assert record.withdrawSourceKey == source_key if WECHAT_WITHDRAW_ERROR[test_id]['refund']: assert record.status == WithdrawStatus.CLOSED else: assert record.status == WithdrawStatus.FAILED assert agent.reload().sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) == before_agent_balance @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), amount = st.one_of(st.floats(min_value = 20, max_value = 1000), st.integers(min_value = 20, max_value = 1000))) def test_dealer_withdraw_with_bank(mocker, dealer, agent, income_type, source_key, dealer_client, amount): # type: (MockFixture, Dealer, Agent, str, str, RequestTestClient, float)->None withdrawed = RMB(amount) after_withdraw_balance = dealer.sub_balance(income_type, source_key) assert dealer.incr_fund(income_type, source_key, withdrawed), 'balance inc failed' before_withdraw_balance = dealer.reload().sub_balance(income_type, source_key) desired_service_fee = withdrawed * (dealer.withdrawFeeRatio.as_ratio) desired_actual_pay = withdrawed - desired_service_fee desired_agent_balance = agent.sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) from apps.web.dealer.views import dealerWithdraw url = url_fn(dealerWithdraw) payload = { 'code': '1234', 'payType': WITHDRAW_PAY_TYPE.BANK, 'amount': str(withdrawed), 'sourceType': income_type, 'sourceId': source_key, 'bankAccount': BANK_ACCOUNT_CODE, 'openId': 'test' } from apps.web.common.models import Banks mocker.patch.object(Banks, 'get_wechat_bank_code', return_value = '1026') from apps.web.core.payment.wechat import WechatWithdrawGateway mocker.patch.object(WechatWithdrawGateway, 'withdraw_via_bank', return_value = BANK_WITHDRAW_SUCCEEDED) response = dealer_client.post_json(url, data = payload) assert response assert after_withdraw_balance == dealer.reload().sub_balance(income_type, source_key) json_response = json.loads(response.content) assert json_response['result'] == 1 assert json_response['description'] == u'提现申请已经受理' withdraw_record_id = json_response['payload']['paymentId'] record = WithdrawRecord.objects(id = str(withdraw_record_id)).first() # type: WithdrawRecord assert record assert record.serviceFee == desired_service_fee assert record.actualPay == desired_actual_pay assert record.amount == withdrawed assert record.ownerId == str(dealer.id) assert record.payType == WITHDRAW_PAY_TYPE.BANK assert record.balance == before_withdraw_balance assert record.withdrawSourceKey == source_key assert record.status == WithdrawStatus.PROCESSING assert record.payAgentId == str(agent.id) assert record.remarks == u'提现申请已经受理' assert agent.reload().sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) == desired_agent_balance @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), amount = st.one_of(st.floats(min_value = 0.01, max_value = settings.WITHDRAW_MINIMUM - 0.01), st.integers(min_value = 1, max_value = settings.WITHDRAW_MINIMUM - 1))) def test_dealer_withdraw_with_bank_less_min(mocker, dealer, agent, income_type, source_key, dealer_client, amount): # type: (MockFixture, Dealer, Agent, str, str, RequestTestClient, float)->None money = RMB(amount) pre_balance = dealer.sub_balance(income_type, source_key) assert dealer.incr_fund(income_type, source_key, money), 'balance inc failed' added_balance = dealer.reload().sub_balance(income_type, source_key) from apps.web.dealer.views import dealerWithdraw url = url_fn(dealerWithdraw) payload = { 'code': '1234', 'payType': WITHDRAW_PAY_TYPE.BANK, 'amount': str(money), 'sourceType': income_type, 'sourceId': source_key, 'bankAccount': BANK_ACCOUNT_CODE, 'openId': 'test' } response = dealer_client.post_json(url, data = payload) assert response json_response = json.loads(response.content) assert json_response['result'] == 0 assert json_response['description'] == u"提现金额不能少于%s元" % (settings.WITHDRAW_MINIMUM,) assert added_balance == dealer.reload().sub_balance(income_type, source_key) @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), amount = st.one_of( st.floats(min_value = settings.WITHDRAW_MAXIMUM + 0.01, max_value = settings.WITHDRAW_MAXIMUM + 10), st.integers(min_value = settings.WITHDRAW_MAXIMUM + 1, max_value = settings.WITHDRAW_MAXIMUM + 10))) def test_dealer_withdraw_with_bank_more_max(mocker, dealer, agent, income_type, source_key, dealer_client, amount): # type: (MockFixture, Dealer, Agent, str, str, RequestTestClient, float)->None money = RMB(amount) pre_balance = dealer.sub_balance(income_type, source_key) assert dealer.incr_fund(income_type, source_key, money), 'balance inc failed' added_balance = dealer.reload().sub_balance(income_type, source_key) from apps.web.dealer.views import dealerWithdraw url = url_fn(dealerWithdraw) payload = { 'code': '1234', 'payType': WITHDRAW_PAY_TYPE.BANK, 'amount': str(money), 'sourceType': income_type, 'sourceId': source_key, 'bankAccount': BANK_ACCOUNT_CODE, 'openId': 'test' } response = dealer_client.post_json(url, data = payload) assert response json_response = json.loads(response.content) assert json_response['result'] == 0 assert json_response['description'] == u"单次提现金额不得大于%s元" % (settings.WITHDRAW_MAXIMUM,) assert added_balance == dealer.reload().sub_balance(income_type, source_key) # @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME])) def test_dealer_withdraw_with_bank_less_balance(mocker, dealer, agent, income_type, source_key, dealer_client): # type: (MockFixture, Dealer, Agent, str, str, RequestTestClient)->None withdrawed = RMB(20) dealer.set_balance(income_type, source_key, RMB(10)) pre_balance = dealer.reload().sub_balance(income_type, source_key) assert pre_balance == RMB(10) from apps.web.dealer.views import dealerWithdraw url = url_fn(dealerWithdraw) payload = { 'code': '1234', 'payType': WITHDRAW_PAY_TYPE.BANK, 'amount': str(withdrawed), 'sourceType': income_type, 'sourceId': source_key, 'bankAccount': BANK_ACCOUNT_CODE, 'openId': 'test' } response = dealer_client.post_json(url, data = payload) assert response json_response = json.loads(response.content) assert json_response['result'] == 0 assert json_response['description'] == u"余额不足" assert pre_balance == dealer.reload().sub_balance(income_type, source_key) @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), manual_type = st.integers(min_value = 1, max_value = 2)) def test_dealer_withdraw_with_bank_manual(mocker, dealer, agent, income_type, dealer_client, manual_type): # type: (MockFixture, Dealer, Agent, str, RequestTestClient, int)->None agent.payAppWechat.occupant = agent agent.payAppWechat.manual_withdraw = True agent.payAppWechat.save() gateway = WechatWithdrawGateway(agent.payAppWechat) source_key = gateway.gateway_key print repr(gateway) withdrawed = RMB(20) after_withdraw_balance = dealer.sub_balance(income_type, source_key) assert dealer.incr_fund(income_type, source_key, withdrawed), 'balance inc failed' before_withdraw_balance = dealer.reload().sub_balance(income_type, source_key) desired_service_fee = withdrawed * (dealer.withdrawFeeRatio.as_ratio) desired_actual_pay = withdrawed - desired_service_fee desired_agent_balance = agent.sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) from apps.web.dealer.views import dealerWithdraw url = url_fn(dealerWithdraw) payload = { 'code': '1234', 'payType': WITHDRAW_PAY_TYPE.BANK, 'amount': str(withdrawed), 'sourceType': income_type, 'sourceId': source_key, 'bankAccount': BANK_ACCOUNT_CODE, 'openId': 'test' } response = dealer_client.post_json(url, data = payload) assert response json_response = json.loads(response.content) assert json_response['result'] == 1 assert json_response['description'] == u"提现申请已经受理" assert after_withdraw_balance == dealer.reload().sub_balance(income_type, source_key) withdraw_record_id = json_response['payload']['paymentId'] record = WithdrawRecord.objects(id = str(withdraw_record_id)).first() # type: WithdrawRecord assert record assert record.serviceFee == desired_service_fee assert record.actualPay == desired_actual_pay assert record.amount == withdrawed assert record.ownerId == str(dealer.id) assert record.payType == WITHDRAW_PAY_TYPE.BANK assert record.balance == before_withdraw_balance assert record.withdrawSourceKey == source_key assert record.status == WithdrawStatus.PROCESSING assert record.payAgentId == str(agent.id) assert record.remarks == u'提现申请已经受理' assert agent.reload().sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) == desired_agent_balance @given(income_type = st.sampled_from([DEALER_INCOME_TYPE.DEVICE_INCOME, DEALER_INCOME_TYPE.AD_INCOME]), test_id = st.integers(min_value = 0, max_value = len(BANK_WITHDRAW_ERROR) - 1)) def test_dealer_withdraw_via_bank_error(mocker, dealer, agent, income_type, source_key, dealer_client, test_id): # type: (MockFixture, Dealer, Agent, str, str, RequestTestClient, int)->None """ 测试提现到微信失败的场景 """ desired_result = BANK_WITHDRAW_ERROR[test_id]['result'] if 'remarks' in BANK_WITHDRAW_ERROR[test_id]: desired_remarks = BANK_WITHDRAW_ERROR[test_id]['remarks'] else: desired_remarks = u'{err_code_des}({err_code})'.format( err_code = BANK_WITHDRAW_ERROR[test_id]['err_code'], err_code_des = BANK_WITHDRAW_ERROR[test_id][ 'err_code_des']) if 'show_message' in BANK_WITHDRAW_ERROR[test_id]: desired_show_message = BANK_WITHDRAW_ERROR[test_id]['show_message'] else: desired_show_message = BANK_WITHDRAW_ERROR[test_id]['err_code_des'] withdrawed = RMB(20) after_withdraw_balance = dealer.sub_balance(income_type, source_key) assert dealer.incr_fund(income_type, source_key, withdrawed), 'balance inc failed' before_withdraw_balance = dealer.reload().sub_balance(income_type, source_key) desired_service_fee = withdrawed * (dealer.withdrawFeeRatio.as_ratio) desired_actual_pay = withdrawed - desired_service_fee before_agent_balance = agent.sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) from apps.web.dealer.views import dealerWithdraw url = url_fn(dealerWithdraw) payload = { 'code': '1234', 'payType': WITHDRAW_PAY_TYPE.BANK, 'amount': str(withdrawed), 'sourceType': income_type, 'sourceId': source_key, 'bankAccount': BANK_ACCOUNT_CODE, 'openId': 'test' } from apps.web.common.models import Banks mocker.patch.object(Banks, 'get_wechat_bank_code', return_value = '1026') if BANK_WITHDRAW_ERROR[test_id]['return_code'] == 'FAIL': mocker.patch.object(WechatWithdrawGateway, 'withdraw_via_bank', side_effect = WechatNetworkException( errCode = BANK_WITHDRAW_ERROR[test_id]['return_code'], errMsg = BANK_WITHDRAW_ERROR[test_id].get('return_msg', ''))) else: mocker.patch.object(WechatWithdrawGateway, 'withdraw_via_bank', side_effect = WeChatPayException( errCode = BANK_WITHDRAW_ERROR[test_id]['err_code'], errMsg = BANK_WITHDRAW_ERROR[test_id]['err_code_des'])) response = dealer_client.post_json(url, data = payload) assert response json_response = json.loads(response.content) assert json_response['result'] == desired_result assert json_response['description'] == desired_show_message dealer.reload() if BANK_WITHDRAW_ERROR[test_id]['refund']: assert before_withdraw_balance == dealer.sub_balance(income_type, source_key) else: assert after_withdraw_balance == dealer.sub_balance(income_type, source_key) withdraw_record_id = json_response['payload']['paymentId'] record = WithdrawRecord.objects(id = str(withdraw_record_id)).first() # type: WithdrawRecord assert record assert record.serviceFee == desired_service_fee assert record.actualPay == desired_actual_pay assert record.amount == withdrawed assert record.ownerId == str(dealer.id) assert record.payType == WITHDRAW_PAY_TYPE.BANK assert record.balance == before_withdraw_balance assert record.withdrawSourceKey == source_key if BANK_WITHDRAW_ERROR[test_id]['refund']: assert record.status == WithdrawStatus.CLOSED else: assert record.status == WithdrawStatus.FAILED assert agent.reload().sub_balance(AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, source_key) == before_agent_balance # # def test_getIncomeList(dealer_client): # # type: (RequestTestClient)->None # url = urls['getIncomeList'] # # from apps.web.dealer.proxy import DealerIncomeProxy # # proxies = DealerIncomeProxy() # # today_date = datetime.datetime.now().strftime(Const.DATE_FMT) # # response_schema = Schema( # { # 'total': int, # 'adShow': bool, # 'totalAmount': str, # 'dataList': list, # 'aggregate': dict # }) # # # test different sources conform to response schema # response = dealer_client.get(url, data = { # 'pageIndex': 1, # 'pageSize': 10, # 'searchKey': '', # 'startTime': today_date, # 'endTime': today_date, # 'source': '', # 'groupId': '', # 'logicalCode': '', # 'aggregateBy': 'month' # }) # # assert response_schema(json.loads(response.content)['payload']) # # # def test_getIncomeAggregate(dealer_client): # # type: (RequestTestClient)->None # # url = urls['getIncomeAggregate'] # # # def test_getIncomeDetail(dealer_client): # # type: (RequestTestClient)->None # # url = urls['getIncomeDetail'] # # # def test_getConsumptionList(dealer_client): # # type: (RequestTestClient)->None # # url = urls['getConsumptionList'] # # # def test_getConsumptionAggregate(dealer_client): # # type: (RequestTestClient)->None # # url = urls['getConsumptionAggregate'] # # # # def test_groupIncomeData(dealer_client): # url = urls['groupIncomeData'] # # # def test_dealerRegister(client, agent): # from faker import Factory # fake = Factory.create() # from faker.providers import internet # # fake.add_provider(internet) # # from apps.web.dealer.views import dealerRegister # from testcase.providers.dealer import DealerDataProvider # # fake.add_provider(DealerDataProvider) # # url = url_fn(dealerRegister) # # payload = {} # # assert client.post_json(url, payload).json()['result'] == 0 # # payload = { # 'code': '1234', # 'username': fake.username(), # 'password': fake.name(), # 'nickname': fake.name(), # 'agentId': str(agent.id) # } # # assert client.post_json(url, payload).json()['result'] == 1 # # from apps.web.dealer.models import Dealer # Dealer.objects(username = payload['username']).delete() # # # def test_getFeatureList(dealer_client, dealer): # from apps.web.dealer.views import getFeatureList # # from apps.web.common.models import Feature # # dealer.update(set__features = []) # # url = url_fn(getFeatureList) # # assert dealer_client.post_json(url).json()['payload'] == {} # # with DisposableModel(model = Feature, name = 'test', key = 'test', role = "dealer") as model: # dealer.update(set__features = [model.key]) # assert dealer_client.post_json(url, data = {'list': ['test']}).json()['payload'] == {'test': True} # dealer.update(set__features = []) # # def test_dealer_withdraw_service(mocker, dealer, agent_no_customized, default_agent, gateway_key): # from apps.web.core.payment.wechat import WechatPaymentGateway # mocker.patch.object(WechatPaymentGateway, 'withdraw_via_changes', return_value = WECHAT_WITHDRAW_SUCCEEDED) # # for income_type in DEALER_INCOME_TYPE.choices(): # dealer.set_balance(income_type, gateway_key, RMB(20)) # dealer.reload() # # for __gateway_key, balance_field in dealer.balance_dict(income_type).iteritems(): # balance = balance_field.balance # if balance > RMB(10): # withdraw_service = DealerWithdrawService(payee = dealer, # income_type = income_type, # code = None, amount = balance, # pay_type = WITHDRAW_PAY_TYPE.WECHAT, # bank_card_no = None) # result = withdraw_service.execute(gateway_key = __gateway_key, recurrent = True) # # print '%s' % result # # # for income_type in DEALER_INCOME_TYPE.choices(): # dealer.set_balance(income_type, gateway_key, RMB(30)) # dealer.reload() # # for __gateway_key, balance_field in dealer.balance_dict(income_type).iteritems(): # balance = balance_field.balance # if balance > RMB(10): # withdraw_service = DealerWithdrawService(payee = dealer, # income_type = income_type, # code = None, amount = balance, # pay_type = WITHDRAW_PAY_TYPE.WECHAT, # bank_card_no = None) # result = withdraw_service.execute(gateway_key = __gateway_key, recurrent = False) # # print '%s' % result # # default_agent.payAppWechat.manual_withdraw = True # default_agent.payAppWechat.save() # default_agent.payAppWechat.reload() # # for income_type in DEALER_INCOME_TYPE.choices(): # dealer.set_balance(income_type, gateway_key, RMB(20)) # dealer.reload() # # for __gateway_key, balance_field in dealer.balance_dict(income_type).iteritems(): # balance = balance_field.balance # if balance > RMB(10): # withdraw_service = DealerWithdrawService(payee = dealer, # income_type = income_type, # code = None, amount = balance, # pay_type = WITHDRAW_PAY_TYPE.WECHAT, # bank_card_no = None) # result = withdraw_service.execute(gateway_key = __gateway_key, recurrent = True) # # print '%s' % result # # #