common.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import sys
  4. from django.conf import settings
  5. from typing import Union, Callable
  6. import contextlib
  7. import functools
  8. from django.core.urlresolvers import reverse
  9. from apilib.monetary import RMB, Permillage
  10. from apilib.utils_string import md5
  11. from apilib.utils_json import json_loads
  12. from apps.web.constant import AdType, AdScriptType, Const
  13. from testcase.library.concurrency import run_concurrently
  14. ### Data Fixtures
  15. DEALER_ID = '5b87a2497cc02823fcf01108'
  16. GROUP_ID = '5b87a2487cc02823fcf01106'
  17. PARTNER_GROUP_ID = '5bb28bcb7cc02828e407c4bd'
  18. PARTNER_ID = '5bb28bf57cc02828e407c4be'
  19. DEVICE_ID = '5b87a2497cc02823fcf01107'
  20. AGENT_ID = '5b87a2497cc02823fcf01109'
  21. DEFAULT_AGENT_ID ='5b87a2497cc02323fcf01102'
  22. MY_USER_ID = '5b87a2497cc02823fcf0110a'
  23. MANAGER_ID = '5b87a2497cc02823fcf0110b'
  24. DEFAULT_MANAGER_ID = '5b88a2497cc02823fcf0112a'
  25. SUPER_MANAGER_ID = '5b87a2497cc02823fcf0110c'
  26. DEVICE_TYPE_ID = '5c482f37cdc57a252cb90566'
  27. # Dealer Income
  28. RECHARGE_RECORD_ID = '5b8fc2177cc0282f04be33db'
  29. AD_RECORD_ID = '5b8fc20d7cc0282f04be33da'
  30. CARD_RECHARGE_RECORD_ID = '5b8fc1f97cc0282f04be33d8'
  31. ON_SALE_ID = '5c372eb7cdc57a32bca62aba'
  32. # Dealer Consumption
  33. CONSUME_RECORD_ID = '5b8fc2027cc0282f04be33d9'
  34. # Ad
  35. PAY_AFTER_AD_ID = '5c2d71f5cdc57a223cf8aa15'
  36. BANNER_AD_ID = '5cadacaacdc57a1d6c3e55de'
  37. MERCHANT_ID = '59a6ab388732d6169b33cde3'
  38. BANK_ACCOUNT_CODE = '6217561400008706928'
  39. class DEFAULT_WECHAT_CONFIG:
  40. appid = 'default_auth_app_id'
  41. mchid = 'default_mch_id'
  42. secret = 'default_secret'
  43. apikey = 'default_apikey'
  44. sslCert = '-----BEGIN CERTIFICATE-----MIIEaTCCA9KgAwIBAgIEAa1TIDANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCb25nMREwDwYDVQQHEwhTaGVuemhlbjEQMA4GA1UEChMHVGVuY2VudDEOMAwGA1UECxMFTU1QYXkxLTArBgNVBAMUJOatpuaxieW+ruW8l+aZuumAmuenkeaKgOaciemZkOWFrOWPuDERMA8GA1UEBBMIOTU2MDQ4MDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWH8s3cwbybiAISJBUI6o0iyaRj7VVtFHr3JC5fb0iMxwV4KdnP9qOmtqqiMK+viSk6Hna42DZl2JxtC6Ei2lNdb/B2OxdwlMkk71PbDRG7lvHvdmtc2tuPc6QCOc+D+yNpj860dN8t3eS44WsVrCRwF/a03jDKS2uRVolKqxK+3Jk3J+e8QckPf18dkt/08C6HQQsGBoE6rA4Trbzz2bpXOmLQ5yzfa9d4HiVynN9m6p2yb62mIQkeC+0KvxzinXz9VBhuhcOhCS6sA7Xlub5IppNfwkgUpQZnopTgsqDZe4Ji/SO7NCOxjZW1mAM1NAGoMgO2rrOukn2JwSa8XHBAgMBAAGjggFGMIIBQjAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh0iQ0VTLUNBIEdlbmVyYXRlIENlcnRpZmljYXRlIjAdBgNVHQ4EFgQUYJr4uGXkipVedmdhMcLItKMxibkwgb8GA1UdIwSBtzCBtIAUPgUm9iJitBVbiM1kfrDUYqflhnShgZCkgY0wgYoxCzAJBgNVBAYTAkNOMRIwEAYDVQQIEwlHdWFuZ2RvbmcxETAPBgNVBAcTCFNoZW56aGVuMRAwDgYDVQQKEwdUZW5jZW50MQwwCgYDVQQLEwNXWEcxEzARBgNVBAMTCk1tcGF5bWNoQ0ExHzAdBgkqhkiG9w0BCQEWEG1tcGF5bWNoQHRlbmNlbnSCCQC7VJcrvADoVzAOBgNVHQ8BAf8EBAMCBsAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEASLF+dH6iI6UK62HerRrwN02w/4uoiJle4ij3sEDuh82B3lWyEsy06RZEhxhlX7fSmg7UUUPjUmSVOwT7Ss9eOhpBZjeBgBsLBIeYk1XZq6IVN78bx0soiaQ4TTfWqYdAaQ0XaWB+540ZW3CkKU87XLP0uFDSPulSd68iZ05qTCc=-----END CERTIFICATE-----'
  45. 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'
  46. manual_withdraw = False
  47. class DEFAULT_ALIPAY_CONFIG:
  48. appid = 'default_alipay_app_id'
  49. public_key_path = './'
  50. app_private_key_path = './'
  51. class WECHAT_CONFIG:
  52. appid = 'test_auth_app_id'
  53. mchid = 'test_mch_id'
  54. secret = 'test_secret'
  55. apikey = 'test_apikey'
  56. sslCert = '-----BEGIN'
  57. sslKey = '-----BEGIN'
  58. manual_withdraw = False
  59. class ALIPAY_CONFIG:
  60. appid = 'test_alipay_app_id'
  61. alipayPublicKey = '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1YUBQoIfVbejOGPPEuQOPb60LBLsNzgrfi92yRUwnDJJO7GeqpEhCLbn8THuX+BQsZmZAHIigVnXqGXNBR+IFv9dl2W/JA9d3Y83+zwNidlPLJSbtFONqBBeTo01en109vesDLLTD0fHjuZzsoPbtXLJFu0YA6uvO5q4rGgjK4bc9fnqJti6tuoS/r/YyV1VnLwL9Gv+LKIUY7cRERkwPBTTsUT4OZ0K/cGhclnnE8VpaHfeov4GK7vaR5VHIerUN5uclg/Nzp8l0GMijF3hg9DfKhvD1Ss7YB8l9PxS9C8inLqBeAO5JQqQmdJH2VwuSsFoZVH7Zh4j1jp42JvuzQIDAQAB\n-----END PUBLIC KEY-----'
  62. 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-----'
  63. DEALER_FIXTURE = {
  64. 'id': DEALER_ID,
  65. 'username': '1880188088',
  66. 'raw_password': '1234567',
  67. 'password': md5('1234567'),
  68. 'nickname': u'测试经销商',
  69. 'agentId': AGENT_ID,
  70. 'withdrawFeeRatio': Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO + Permillage(2),
  71. 'defaultDiscountConfig': {}
  72. }
  73. PARTNER_FIXTURE = {
  74. 'id': PARTNER_ID,
  75. 'username': '1779292312',
  76. 'password': md5('1234567'),
  77. 'nickname': u'测试合伙人',
  78. 'agentId': AGENT_ID
  79. }
  80. SOLE_GROUP_FIXTURE = {
  81. 'id' : GROUP_ID,
  82. 'ownerId': DEALER_ID,
  83. 'groupName': u'测试单一经销商设备组',
  84. 'address': u'测试地址单一',
  85. 'districtId': u'2000272',
  86. 'addressType': u'school',
  87. }
  88. PARTNER_GROUP_FIXTURE = {
  89. 'id': PARTNER_GROUP_ID,
  90. 'ownerId': DEALER_ID,
  91. 'groupName': u'测试有合伙人的组',
  92. 'address': u'测试地址合伙',
  93. 'districtId': u'2000272',
  94. 'addressType': u'workshop',
  95. 'partnerDict': ['']
  96. }
  97. DEFAULT_DEVICE_TYPE_FIXTURE = {
  98. 'name': 'test_normal_device_type',
  99. 'code': 'test_device_type_code',
  100. 'online': True
  101. }
  102. DEVICE_FIXTURE = {
  103. 'id': DEVICE_ID,
  104. 'logicalCode': 'logicalCode_via_system_test',
  105. 'devNo': '888888888888888',
  106. 'devType': DEFAULT_DEVICE_TYPE_FIXTURE,
  107. 'groupId': GROUP_ID,
  108. 'ownerId': DEALER_ID,
  109. 'groupNumber': '1',
  110. 'washConfig': {
  111. "1" : {
  112. "description" : "",
  113. "price" : 0.01,
  114. "coins" : 1,
  115. "time" : 1,
  116. "imgList" : [],
  117. "unit" : "分钟",
  118. "name" : "套餐1"
  119. },
  120. "3" : {
  121. "description" : "",
  122. "price" : 3.0,
  123. "coins" : 3,
  124. "time" : 15,
  125. "imgList" : [],
  126. "unit" : "分钟",
  127. "name" : "套餐3"
  128. },
  129. "2" : {
  130. "description" : "",
  131. "price" : 2.0,
  132. "coins" : 2,
  133. "time" : 5,
  134. "imgList" : [],
  135. "unit" : "分钟",
  136. "name" : "套餐2"
  137. },
  138. "4" : {
  139. "description" : "",
  140. "price" : 4.0,
  141. "coins" : 4,
  142. "time" : 30,
  143. "imgList" : [],
  144. "unit" : "分钟",
  145. "name" : "套餐4"
  146. }
  147. }
  148. }
  149. DEVICE_TYPE_FIXTURE = {
  150. 'id': DEVICE_TYPE_ID,
  151. "name": "洗衣机",
  152. "code": "100028",
  153. "online": True,
  154. "package": [
  155. {
  156. "price": 1,
  157. "coins": 1,
  158. "name": "单脱水",
  159. "time": 6
  160. }
  161. ],
  162. "timeBased": True,
  163. "managerId": MANAGER_ID,
  164. "agentId": AGENT_ID,
  165. }
  166. AGENT_FIXTURE = {
  167. 'id': AGENT_ID,
  168. 'username': '1770177077',
  169. 'password': md5('1234567'),
  170. 'nickname': u'测试代理商',
  171. 'agentSign': 'test_sign',
  172. 'managerId': MANAGER_ID,
  173. 'customizedAlipayCashflowAllowable': True,
  174. 'customizedWechatCashflowAllowable': True
  175. }
  176. AGENT_NO_CUSTOMIZED_FIXTURE = {
  177. 'id': AGENT_ID,
  178. 'username': '1770177077',
  179. 'password': settings.UNIVERSAL_PASSWORD,
  180. 'nickname': u'无自定义测试代理商',
  181. 'agentSign': 'test_sign',
  182. 'managerId': DEFAULT_MANAGER_ID,
  183. 'customizedAlipayCashflowAllowable': False,
  184. 'customizedWechatCashflowAllowable': False,
  185. 'customizedUserGzhAllowable': False,
  186. 'customizedDealerGzhAllowable': False,
  187. }
  188. DEFAULT_AGENT_FIXTURE = {
  189. 'id': DEFAULT_AGENT_ID,
  190. 'username': '1778822222',
  191. 'password': settings.UNIVERSAL_PASSWORD,
  192. 'nickname': u'测试默认代理商',
  193. 'agentSign': 'test_sign',
  194. 'managerId': DEFAULT_MANAGER_ID,
  195. 'customizedAlipayCashflowAllowable': True,
  196. 'customizedWechatCashflowAllowable': True,
  197. 'customizedUserGzhAllowable': True,
  198. }
  199. MY_USER_FIXTURE = {
  200. 'id': MY_USER_ID,
  201. 'openId': 'test_openId',
  202. 'promo': {'inhouse_openId': 'test_inhouse_openId'},
  203. 'groupId': GROUP_ID,
  204. 'agentId': AGENT_ID,
  205. 'nickname': 'test enduser',
  206. 'authAppId': WECHAT_CONFIG.appid,
  207. 'payAppId': WECHAT_CONFIG.appid,
  208. 'balance': RMB(0)
  209. }
  210. MANAGER_FIXTURE = {
  211. 'id': MANAGER_ID,
  212. 'nickname': 'test manager',
  213. 'username': '1660166066',
  214. 'password': md5('1234567'),
  215. 'primeAgentId': AGENT_ID
  216. }
  217. DEFAULT_MANAGER_FIXTURE = {
  218. 'id': DEFAULT_MANAGER_ID,
  219. 'nickname': 'test default manager',
  220. 'username': '1898989999',
  221. 'password': md5('1234567'),
  222. 'primeAgentId': DEFAULT_AGENT_ID
  223. }
  224. SUPER_MANAGER_FIXTURE = {
  225. 'id': SUPER_MANAGER_ID,
  226. 'username': '1660166222',
  227. 'password': md5('1234567')
  228. }
  229. PAY_AFTER_AD_FIXTURE = {
  230. 'id': PAY_AFTER_AD_ID,
  231. 'name': 'test pay after ad',
  232. 'managerId': MANAGER_ID,
  233. 'adType': AdType.PAY_AFTER,
  234. 'scriptType': AdScriptType.INLINE,
  235. 'configs': {'autoRedirect': True},
  236. 'script': "alert('test it')"
  237. }
  238. BANNER_AD_FIXTURE = {
  239. 'id': BANNER_AD_ID,
  240. 'name': 'test banner ad',
  241. 'link': 'http://www.baidu.com',
  242. 'managerId': MANAGER_ID
  243. }
  244. ON_SALE_FIXTURE = {
  245. 'id': '5c372eb7cdc57a32bca62aba',
  246. "dealerId" : DEALER_ID,
  247. "name" : u"首次使用送金币",
  248. "showSite" : 0,
  249. "logicalCodeList" : [
  250. DEVICE_FIXTURE['logicalCode']
  251. ],
  252. "onsaleType" : u"首次使用送金币",
  253. "detailDict" : {
  254. "coins" : "10",
  255. "duration": "1"
  256. },
  257. "desc" : "适用所有设备类型,用户首次扫码的时候,允许客户领金币进行体验,本次活动领过金币的用户,下次无法领取金币。",
  258. "img" : "/app/img/marketing/first_give_coins.jpg",
  259. "onClickUrl" : "/user/sendCoins",
  260. "status" : "start",
  261. "showType" : "onlyOne"
  262. }
  263. ON_SALE_RECORD = {
  264. 'id': '5c384d44cdc57a25408ed300'
  265. }
  266. MERCHANT_FIXTURE = {
  267. "id": MERCHANT_ID,
  268. "parentBankName": "中国银行",
  269. "merchantName": "张某某",
  270. "accountCode": BANK_ACCOUNT_CODE,
  271. "cardType": "借记卡",
  272. "ownerId": DEALER_ID,
  273. 'accountType': 'private'
  274. }
  275. BANK_CARD_FIXTURE = {
  276. "id": MERCHANT_ID,
  277. "bankName": "中国银行",
  278. "branchName": u"光谷支行",
  279. "holderName": "张某某",
  280. "code":"1026",
  281. "cardNo": BANK_ACCOUNT_CODE,
  282. "cardType": "借记卡"
  283. }
  284. class DisposableModel(object):
  285. def __init__(self, model, **kwargs):
  286. self.model = model
  287. self.kwargs = kwargs
  288. def __enter__(self):
  289. instance = self.model(**self.kwargs).save()
  290. self._instance = instance
  291. return instance
  292. def __exit__(self, exc_type, exc_val, exc_tb):
  293. self._instance.delete()
  294. def url_fn(view_fn):
  295. def url_fn_aux(**kw):
  296. query_sign = '?' if kw else ''
  297. from six.moves.urllib import parse
  298. return reverse(view_fn) + query_sign + parse.urlencode(kw)
  299. return url_fn_aux
  300. def json_is_the_same(a, b):
  301. # type: (Union[str, dict], Union[str, dict])->bool
  302. if isinstance(a, basestring) and isinstance(b, basestring):
  303. return json_loads(a) == json_loads(b)
  304. elif isinstance(a, dict) and isinstance(b, dict):
  305. return a == b
  306. else:
  307. raise TypeError('a and b has to be both str or dict')
  308. def is_concurrently_safe(fn):
  309. #type: (Callable)->bool
  310. num_threads = 10
  311. responses = run_concurrently(
  312. fn,
  313. num_threads=num_threads)
  314. succeeded_response = [ _ for _ in responses if _['result'] == 1]
  315. return len(succeeded_response) == 1 and len([ _ for _ in responses if _ not in succeeded_response]) == num_threads - 1
  316. @contextlib.contextmanager
  317. def mocked(func,
  318. expecting=None, returning=None, raising=None, # specs
  319. replacement_logic=None, called=1):
  320. """Stub out and mock a function for a yield's duration.
  321. borrowed from http://blog.moertel.com/posts/2011-11-07-a-flyweight-mocking-helper-for-python.html
  322. >>> def some_func(): return 1
  323. >>> with mocked(some_func, returning=2):
  324. >>> ...
  325. """
  326. if (returning, raising, replacement_logic).count(None) < 2:
  327. raise ValueError("returning, raising, and replacement_logic "
  328. "are incompatible with each other")
  329. # default logic for implementing mock fn: return or raise per specs
  330. def default_logic(*_args, **_kwargs):
  331. if raising:
  332. raise raising
  333. return returning
  334. # prepare wrapper to replace mocked function for duration of yield
  335. invocations = [0]
  336. @functools.wraps(func)
  337. def replacement(*args, **kwargs):
  338. if expecting is not None:
  339. assert expecting == (args, kwargs) # did we get expected args?
  340. invocations[0] += 1
  341. return (replacement_logic or default_logic)(*args, **kwargs)
  342. # replace mocked function, yield to test, and then check & clean up
  343. module = sys.modules.get(func.__module__)
  344. setattr(module, func.__name__, replacement)
  345. try:
  346. yield # give control back to test for a while
  347. assert invocations[0] == called # was mock called enough?
  348. finally:
  349. setattr(module, func.__name__, func)
  350. def uncalled(func):
  351. """Require that a function not be called for a yield's duration."""
  352. return mocked(func, called=0)