# -*- coding: utf-8 -*- # !/usr/bin/env python import sys from django.conf import settings from typing import Union, Callable import contextlib import functools from django.core.urlresolvers import reverse from apilib.monetary import RMB, Permillage from apilib.utils_string import md5 from apilib.utils_json import json_loads from apps.web.constant import AdType, AdScriptType, Const from testcase.library.concurrency import run_concurrently ### Data Fixtures DEALER_ID = '5b87a2497cc02823fcf01108' GROUP_ID = '5b87a2487cc02823fcf01106' PARTNER_GROUP_ID = '5bb28bcb7cc02828e407c4bd' PARTNER_ID = '5bb28bf57cc02828e407c4be' DEVICE_ID = '5b87a2497cc02823fcf01107' AGENT_ID = '5b87a2497cc02823fcf01109' DEFAULT_AGENT_ID ='5b87a2497cc02323fcf01102' MY_USER_ID = '5b87a2497cc02823fcf0110a' MANAGER_ID = '5b87a2497cc02823fcf0110b' DEFAULT_MANAGER_ID = '5b88a2497cc02823fcf0112a' SUPER_MANAGER_ID = '5b87a2497cc02823fcf0110c' DEVICE_TYPE_ID = '5c482f37cdc57a252cb90566' # Dealer Income RECHARGE_RECORD_ID = '5b8fc2177cc0282f04be33db' AD_RECORD_ID = '5b8fc20d7cc0282f04be33da' CARD_RECHARGE_RECORD_ID = '5b8fc1f97cc0282f04be33d8' ON_SALE_ID = '5c372eb7cdc57a32bca62aba' # Dealer Consumption CONSUME_RECORD_ID = '5b8fc2027cc0282f04be33d9' # Ad PAY_AFTER_AD_ID = '5c2d71f5cdc57a223cf8aa15' BANNER_AD_ID = '5cadacaacdc57a1d6c3e55de' MERCHANT_ID = '59a6ab388732d6169b33cde3' BANK_ACCOUNT_CODE = '6217561400008706928' class DEFAULT_WECHAT_CONFIG: appid = 'default_auth_app_id' mchid = 'default_mch_id' secret = 'default_secret' apikey = 'default_apikey' sslCert = '-----BEGIN CERTIFICATE-----MIIEaTCCA9KgAwIBAgIEAa1TIDANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCb25nMREwDwYDVQQHEwhTaGVuemhlbjEQMA4GA1UEChMHVGVuY2VudDEOMAwGA1UECxMFTU1QYXkxLTArBgNVBAMUJOatpuaxieW+ruW8l+aZuumAmuenkeaKgOaciemZkOWFrOWPuDERMA8GA1UEBBMIOTU2MDQ4MDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWH8s3cwbybiAISJBUI6o0iyaRj7VVtFHr3JC5fb0iMxwV4KdnP9qOmtqqiMK+viSk6Hna42DZl2JxtC6Ei2lNdb/B2OxdwlMkk71PbDRG7lvHvdmtc2tuPc6QCOc+D+yNpj860dN8t3eS44WsVrCRwF/a03jDKS2uRVolKqxK+3Jk3J+e8QckPf18dkt/08C6HQQsGBoE6rA4Trbzz2bpXOmLQ5yzfa9d4HiVynN9m6p2yb62mIQkeC+0KvxzinXz9VBhuhcOhCS6sA7Xlub5IppNfwkgUpQZnopTgsqDZe4Ji/SO7NCOxjZW1mAM1NAGoMgO2rrOukn2JwSa8XHBAgMBAAGjggFGMIIBQjAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh0iQ0VTLUNBIEdlbmVyYXRlIENlcnRpZmljYXRlIjAdBgNVHQ4EFgQUYJr4uGXkipVedmdhMcLItKMxibkwgb8GA1UdIwSBtzCBtIAUPgUm9iJitBVbiM1kfrDUYqflhnShgZCkgY0wgYoxCzAJBgNVBAYTAkNOMRIwEAYDVQQIEwlHdWFuZ2RvbmcxETAPBgNVBAcTCFNoZW56aGVuMRAwDgYDVQQKEwdUZW5jZW50MQwwCgYDVQQLEwNXWEcxEzARBgNVBAMTCk1tcGF5bWNoQ0ExHzAdBgkqhkiG9w0BCQEWEG1tcGF5bWNoQHRlbmNlbnSCCQC7VJcrvADoVzAOBgNVHQ8BAf8EBAMCBsAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEASLF+dH6iI6UK62HerRrwN02w/4uoiJle4ij3sEDuh82B3lWyEsy06RZEhxhlX7fSmg7UUUPjUmSVOwT7Ss9eOhpBZjeBgBsLBIeYk1XZq6IVN78bx0soiaQ4TTfWqYdAaQ0XaWB+540ZW3CkKU87XLP0uFDSPulSd68iZ05qTCc=-----END CERTIFICATE-----' sslKey = '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDWH8s3cwbybiAI\nSJBUI6o0iyaRj7VVtFHr3JC5fb0iMxwV4KdnP9qOmtqqiMK+viSk6Hna42DZl2Jx\ntC6Ei2lNdb/B2OxdwlMkk71PbDRG7lvHvdmtc2tuPc6QCOc+D+yNpj860dN8t3eS\n44WsVrCRwF/a03jDKS2uRVolKqxK+3Jk3J+e8QckPf18dkt/08C6HQQsGBoE6rA4\nTrbzz2bpXOmLQ5yzfa9d4HiVynN9m6p2yb62mIQkeC+0KvxzinXz9VBhuhcOhCS6\nsA7Xlub5IppNfwkgUpQZnopTgsqDZe4Ji/SO7NCOxjZW1mAM1NAGoMgO2rrOukn2\nJwSa8XHBAgMBAAECggEAOijuoMQMqgLBjp5vHXuGerklI7p+DaZp7f++C7QckFKD\nmWmo7sGIUt268UEubHLnLye6GXtTTibxZWMEk5nuY0zNZhDOuvTScofyjsUcOfv6\nT/nAS32jO5FsLvFMJi5bGWqDmC1wHVsUDoDgSRxO33f6UUzJa3Ee6EwMHTZHECJW\nuaMQOzddnivs2S17Nk366ckJrPeP5BR4UT+Ulfi4D8kDxx6SLgrqdSHb1ppgpeiW\ncLGRrCIm/DxkAVKi1ATNVrxhnpeBpLa46Q0VUytXvAh2/IQVwGn53T95Uf1QX4B8\nnc3ztHMJ90VfFe0Yg67CPOFfbnpmfwB0tKbVjumvwQKBgQD8HNA+nqcKFyow6YMy\na6XIv/7HM4CWvz37GwZGfdji+kx8nl7eBnX08BeS4bzvsHp4z2SxdHLDQdYbSeQb\naxSCsM8DUbcl7VMDdrsSEsRhsuXaZSD1sGUa/bDLmVNbYZv8lM25mHp/LDgV5Hd/\ndYQYYjLHxws7qlKHW/qwlUCCWQKBgQDZbQaHFBy0VPnBI7THT3EY12q9VnRhqx1S\nMKfrJ0O6RfCiiV7Z9m43SS4VfEeCu6OC8mSDsSTq6n6Goobm64CWUrp+jHb3VtQe\nHoXUXk2uxezcmTZWN2YkUHGrjOsh7AFPJTxNmE+RIegiNhxX7fj6jITy84ZPnvLF\n7+f8VEftqQKBgGWpTMPjf+IOw0Gj9/aN9/UCjr9O4kxnY5vtItOsDZhBbDRYPFL1\ncf1KD4d4JZLVrB0pLCZumZRo6OhqHGR+AnBM0b0550uQ7oeYCglYrf3jgNJXMzo/\nXhXLNM50zmsr6hWo/2gWt1auwxL45MLt5U1b07RBbCMsJElW6rjoBnJJAoGAMYTB\nLdaIyxFnaHOHYrVXHr9BEio0i2H7ZZKrS8U/iZcQPRRoQ9/6LiE+m6oYZ0/w54vp\nFI0tyUrq0iMF8mXEfXgN/XBkT+S4nrt442su6hUt4vgKBgzL+I1Rti58BtfHXI0w\ndMOxq247fj8wqV4LjJSgWuzXtB6RUD9ZaICReTECgYEAykLb5bWQ3VpM/mRNYSfq\noBxbmt4Cjo3P7wD56/Dhxhhwh1bxU/2XuyZQD3IJeUzK7Mk16tUat0O3h8iayiV/\nw9S6iB0DtpQZVl2N3TgiKT8tDHx+MSwZdFKv3hLWK1EKaZ3rmOaq3zUpV7dufijT\nfYl5/FUXNSrW654qwl5MX1o=\n-----END PRIVATE KEY-----\n' manual_withdraw = False class DEFAULT_ALIPAY_CONFIG: appid = 'default_alipay_app_id' public_key_path = './' app_private_key_path = './' class WECHAT_CONFIG: appid = 'test_auth_app_id' mchid = 'test_mch_id' secret = 'test_secret' apikey = 'test_apikey' sslCert = '-----BEGIN' sslKey = '-----BEGIN' manual_withdraw = False class ALIPAY_CONFIG: appid = 'test_alipay_app_id' alipayPublicKey = '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1YUBQoIfVbejOGPPEuQOPb60LBLsNzgrfi92yRUwnDJJO7GeqpEhCLbn8THuX+BQsZmZAHIigVnXqGXNBR+IFv9dl2W/JA9d3Y83+zwNidlPLJSbtFONqBBeTo01en109vesDLLTD0fHjuZzsoPbtXLJFu0YA6uvO5q4rGgjK4bc9fnqJti6tuoS/r/YyV1VnLwL9Gv+LKIUY7cRERkwPBTTsUT4OZ0K/cGhclnnE8VpaHfeov4GK7vaR5VHIerUN5uclg/Nzp8l0GMijF3hg9DfKhvD1Ss7YB8l9PxS9C8inLqBeAO5JQqQmdJH2VwuSsFoZVH7Zh4j1jp42JvuzQIDAQAB\n-----END PUBLIC KEY-----' appPrivateKey = '-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA4VOby1di+q1O5RaEdPndj9BN0OU4GOTTIvlpxY4rxYnJa7Xe\nHtQq+dMPYYw7W5cIeHFw+EoD5Pf+2uSOedNHW4wtfEH5N2EsyaaK2s7izJZxlGXn\nxrJTV0WlQ6rWKXg0FNt1swbtS24IA/LNT+9LjpSUvkWSuQaABZcm3GzuG2BvRgx7\nWh3cnT3JMq4xOkKk9fAsam5Hsxrd2bpzEjreRLa7xynFkXjZhNlzX+CNZnRjP4L1\n5Vq3C4ZeE7YfHSRFTCNPs11Fx3HPMFEe49Xa6r6iayp934C4fax3WkL1vukeQDSr\nZbVMDB1mwMhpmPlKqsp0S53fp/dkOUbScVyqwQIDAQABAoIBACbXQ7bEhIJTl5Lz\n07daTu/4Z0GIEahw4VhZB/uO60qs2rSfb/9+cT4JPNCC3MSH/2TLoT6be5pfiWXw\nGgM0AUvtmPWfZbc/H/pOpAbdvMipckCzZEWi/azAi6FjdHFHkCq8Pcize/oRqH4N\nv4h05mBYil9WHNmaHj1YddFvUYgUP6hYxTcheb4ilyQfw+xwGJryGP/qESZvT11M\n3fIJc6C2brDqch718JD3Cfn/NhdkrAjxxKVP4XkNd8CXqNLYU77gl0JTG2I1Yd06\nd+RtdHYMI6ZrhSTH6TUq4OowAE1rtHOIA1hvK1UPCTxtS9U2g37HNzZXtTaN3hAg\nhFgMlcECgYEA60Bi4lKqjwB3zLypxeby6pCmEhLXbmQhnmdF/hkL+VTwHdXU1i15\nNoQFRJXIKkYXrUWnjsLg+IETM0WWiP6FwxFcnsFnI9DKYeDBKgj5WaKne2Xb82JW\nZfdyi6k+B0ZoTPq6GnoId/HkWpGbTBuv3Ej3q/p8Pb7oQAvw5TTwNGkCgYEA9TMi\nGHnlFhogNy6MBVjLgDlm7cSWKuYiSo9MBjtYDdX2t4TZ3O95le4Y5FMweUZqRmcK\nqPxB491j2lXDGRC/Tuu0ZpjCilrjbm4UOa2q7O6clijhaoHyDj7MFN1kJDZ4Hlu+\ncDAq1A1MPBQynaukpdlvHsFq+SBN90k30odXmJkCgYEA2FaBmPfEeXVItjLZPXFA\n+ozC6+P/0Tir4eu+5glKhaOinQ1DiSKQUZ3e6k+Dn7yNkX54tERqdpzcd04LPTgy\n8kxJDIV2v+0F8DSr7mgKRm/Z7RSizJMmOObxo5WjwTTyPbhk5GxM1WDRSqiUU80y\nUDWZ7cGP94hUNHHGukGQk+ECgYEAl4e7GW8zMN6CbDe61o8SbDKNNoFX4DkKlhNK\ng0tnVl2VM8LJNK1V8V8MSQZUHY5znQCO6PHD8oLd848um63q3GXzvbfH+eLDTHqA\ny0hVKkknwfCPpdeadE1Ap+BNw8dZokFGRmSuAxAYvcQpp6DshoYCEvqrFjxtklXQ\nRq3XZ3kCgYEA0GI6xg1zfyT/U5gjHq9IOOXfZJaKpKocye2DPr0X5O8AXVz56+5j\nnNWLfRm1/0tgsPjo/V1YemzTmGt1KAue7hHdzguE8Fx6hsrBLf6dMQKJvADqm0oB\n/Ui65+R5SS2hZNyJ3BxYn0pK83+6I/IWs7OkSTCEz0mDaOdgaBXRlf8=\n-----END RSA PRIVATE KEY-----' DEALER_FIXTURE = { 'id': DEALER_ID, 'username': '1880188088', 'raw_password': '1234567', 'password': md5('1234567'), 'nickname': u'测试经销商', 'agentId': AGENT_ID, 'withdrawFeeRatio': Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO + Permillage(2), 'defaultDiscountConfig': {} } PARTNER_FIXTURE = { 'id': PARTNER_ID, 'username': '1779292312', 'password': md5('1234567'), 'nickname': u'测试合伙人', 'agentId': AGENT_ID } SOLE_GROUP_FIXTURE = { 'id' : GROUP_ID, 'ownerId': DEALER_ID, 'groupName': u'测试单一经销商设备组', 'address': u'测试地址单一', 'districtId': u'2000272', 'addressType': u'school', } PARTNER_GROUP_FIXTURE = { 'id': PARTNER_GROUP_ID, 'ownerId': DEALER_ID, 'groupName': u'测试有合伙人的组', 'address': u'测试地址合伙', 'districtId': u'2000272', 'addressType': u'workshop', 'partnerDict': [''] } DEFAULT_DEVICE_TYPE_FIXTURE = { 'name': 'test_normal_device_type', 'code': 'test_device_type_code', 'online': True } DEVICE_FIXTURE = { 'id': DEVICE_ID, 'logicalCode': 'logicalCode_via_system_test', 'devNo': '888888888888888', 'devType': DEFAULT_DEVICE_TYPE_FIXTURE, 'groupId': GROUP_ID, 'ownerId': DEALER_ID, 'groupNumber': '1', 'washConfig': { "1" : { "description" : "", "price" : 0.01, "coins" : 1, "time" : 1, "imgList" : [], "unit" : "分钟", "name" : "套餐1" }, "3" : { "description" : "", "price" : 3.0, "coins" : 3, "time" : 15, "imgList" : [], "unit" : "分钟", "name" : "套餐3" }, "2" : { "description" : "", "price" : 2.0, "coins" : 2, "time" : 5, "imgList" : [], "unit" : "分钟", "name" : "套餐2" }, "4" : { "description" : "", "price" : 4.0, "coins" : 4, "time" : 30, "imgList" : [], "unit" : "分钟", "name" : "套餐4" } } } DEVICE_TYPE_FIXTURE = { 'id': DEVICE_TYPE_ID, "name": "洗衣机", "code": "100028", "online": True, "package": [ { "price": 1, "coins": 1, "name": "单脱水", "time": 6 } ], "timeBased": True, "managerId": MANAGER_ID, "agentId": AGENT_ID, } AGENT_FIXTURE = { 'id': AGENT_ID, 'username': '1770177077', 'password': md5('1234567'), 'nickname': u'测试代理商', 'agentSign': 'test_sign', 'managerId': MANAGER_ID, 'customizedAlipayCashflowAllowable': True, 'customizedWechatCashflowAllowable': True } AGENT_NO_CUSTOMIZED_FIXTURE = { 'id': AGENT_ID, 'username': '1770177077', 'password': settings.UNIVERSAL_PASSWORD, 'nickname': u'无自定义测试代理商', 'agentSign': 'test_sign', 'managerId': DEFAULT_MANAGER_ID, 'customizedAlipayCashflowAllowable': False, 'customizedWechatCashflowAllowable': False, 'customizedUserGzhAllowable': False, 'customizedDealerGzhAllowable': False, } DEFAULT_AGENT_FIXTURE = { 'id': DEFAULT_AGENT_ID, 'username': '1778822222', 'password': settings.UNIVERSAL_PASSWORD, 'nickname': u'测试默认代理商', 'agentSign': 'test_sign', 'managerId': DEFAULT_MANAGER_ID, 'customizedAlipayCashflowAllowable': True, 'customizedWechatCashflowAllowable': True, 'customizedUserGzhAllowable': True, } MY_USER_FIXTURE = { 'id': MY_USER_ID, 'openId': 'test_openId', 'promo': {'inhouse_openId': 'test_inhouse_openId'}, 'groupId': GROUP_ID, 'agentId': AGENT_ID, 'nickname': 'test enduser', 'authAppId': WECHAT_CONFIG.appid, 'payAppId': WECHAT_CONFIG.appid, 'balance': RMB(0) } MANAGER_FIXTURE = { 'id': MANAGER_ID, 'nickname': 'test manager', 'username': '1660166066', 'password': md5('1234567'), 'primeAgentId': AGENT_ID } DEFAULT_MANAGER_FIXTURE = { 'id': DEFAULT_MANAGER_ID, 'nickname': 'test default manager', 'username': '1898989999', 'password': md5('1234567'), 'primeAgentId': DEFAULT_AGENT_ID } SUPER_MANAGER_FIXTURE = { 'id': SUPER_MANAGER_ID, 'username': '1660166222', 'password': md5('1234567') } PAY_AFTER_AD_FIXTURE = { 'id': PAY_AFTER_AD_ID, 'name': 'test pay after ad', 'managerId': MANAGER_ID, 'adType': AdType.PAY_AFTER, 'scriptType': AdScriptType.INLINE, 'configs': {'autoRedirect': True}, 'script': "alert('test it')" } BANNER_AD_FIXTURE = { 'id': BANNER_AD_ID, 'name': 'test banner ad', 'link': 'http://www.baidu.com', 'managerId': MANAGER_ID } ON_SALE_FIXTURE = { 'id': '5c372eb7cdc57a32bca62aba', "dealerId" : DEALER_ID, "name" : u"首次使用送金币", "showSite" : 0, "logicalCodeList" : [ DEVICE_FIXTURE['logicalCode'] ], "onsaleType" : u"首次使用送金币", "detailDict" : { "coins" : "10", "duration": "1" }, "desc" : "适用所有设备类型,用户首次扫码的时候,允许客户领金币进行体验,本次活动领过金币的用户,下次无法领取金币。", "img" : "/app/img/marketing/first_give_coins.jpg", "onClickUrl" : "/user/sendCoins", "status" : "start", "showType" : "onlyOne" } ON_SALE_RECORD = { 'id': '5c384d44cdc57a25408ed300' } MERCHANT_FIXTURE = { "id": MERCHANT_ID, "parentBankName": "中国银行", "merchantName": "张某某", "accountCode": BANK_ACCOUNT_CODE, "cardType": "借记卡", "ownerId": DEALER_ID, 'accountType': 'private' } BANK_CARD_FIXTURE = { "id": MERCHANT_ID, "bankName": "中国银行", "branchName": u"光谷支行", "holderName": "张某某", "code":"1026", "cardNo": BANK_ACCOUNT_CODE, "cardType": "借记卡" } class DisposableModel(object): def __init__(self, model, **kwargs): self.model = model self.kwargs = kwargs def __enter__(self): instance = self.model(**self.kwargs).save() self._instance = instance return instance def __exit__(self, exc_type, exc_val, exc_tb): self._instance.delete() def url_fn(view_fn): def url_fn_aux(**kw): query_sign = '?' if kw else '' from six.moves.urllib import parse return reverse(view_fn) + query_sign + parse.urlencode(kw) return url_fn_aux def json_is_the_same(a, b): # type: (Union[str, dict], Union[str, dict])->bool if isinstance(a, basestring) and isinstance(b, basestring): return json_loads(a) == json_loads(b) elif isinstance(a, dict) and isinstance(b, dict): return a == b else: raise TypeError('a and b has to be both str or dict') def is_concurrently_safe(fn): #type: (Callable)->bool num_threads = 10 responses = run_concurrently( fn, num_threads=num_threads) succeeded_response = [ _ for _ in responses if _['result'] == 1] return len(succeeded_response) == 1 and len([ _ for _ in responses if _ not in succeeded_response]) == num_threads - 1 @contextlib.contextmanager def mocked(func, expecting=None, returning=None, raising=None, # specs replacement_logic=None, called=1): """Stub out and mock a function for a yield's duration. borrowed from http://blog.moertel.com/posts/2011-11-07-a-flyweight-mocking-helper-for-python.html >>> def some_func(): return 1 >>> with mocked(some_func, returning=2): >>> ... """ if (returning, raising, replacement_logic).count(None) < 2: raise ValueError("returning, raising, and replacement_logic " "are incompatible with each other") # default logic for implementing mock fn: return or raise per specs def default_logic(*_args, **_kwargs): if raising: raise raising return returning # prepare wrapper to replace mocked function for duration of yield invocations = [0] @functools.wraps(func) def replacement(*args, **kwargs): if expecting is not None: assert expecting == (args, kwargs) # did we get expected args? invocations[0] += 1 return (replacement_logic or default_logic)(*args, **kwargs) # replace mocked function, yield to test, and then check & clean up module = sys.modules.get(func.__module__) setattr(module, func.__name__, replacement) try: yield # give control back to test for a while assert invocations[0] == called # was mock called enough? finally: setattr(module, func.__name__, func) def uncalled(func): """Require that a function not be called for a yield's duration.""" return mocked(func, called=0)