__init__.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. import json
  2. import random
  3. import re
  4. import sys
  5. import time
  6. from datetime import datetime
  7. from math import inf
  8. from time import perf_counter
  9. from typing import Dict, Callable
  10. from uuid import uuid4
  11. import requests
  12. import connection
  13. import model
  14. import test.do_some_requests.current_websocket
  15. from debug import debug
  16. from game import random_ownable_name, DB_NAME, CURRENCY_NAME
  17. from test import failed_requests
  18. from util import round_to_n
  19. MRO_AMOUNT = 1e9
  20. DEFAULT_PW = 'pw'
  21. PORT = connection.PORT
  22. HOST = 'http://127.0.0.1' + ':' + str(PORT)
  23. # HOST = 'http://koljastrohm-games.com' + ':' + str(PORT)
  24. JSON_HEADERS = {'Content-type': 'application/json'}
  25. EXIT_ON_FAILED_REQUEST = True
  26. response_collection: Dict[str, Dict] = {}
  27. default_request_method: Callable[[str, Dict], Dict]
  28. def receive_answer(token):
  29. """Waits until the server sends an answer that contains the desired request_token.
  30. All intermediate requests are also collected for later use, or, if they contain no token, they are just printed out.
  31. """
  32. if token in response_collection:
  33. json_content = response_collection[token]
  34. del response_collection[token]
  35. return json_content
  36. json_content = {}
  37. while 'request_token' not in json_content or json_content['request_token'] != token:
  38. if 'request_token' in json_content:
  39. response_collection[json_content['request_token']] = json_content
  40. received = test.do_some_requests.current_websocket.current_websocket.recv_data_frame()[1].data
  41. content = received.decode('utf-8')
  42. formatted_content = re.sub(r'{([^}]*?):(.*?)}', r'\n{\g<1>:\g<2>}', content)
  43. print('Received through websocket: ' + formatted_content)
  44. json_content = json.loads(content)
  45. return json_content
  46. def websocket_request(route: str, data: Dict) -> Dict:
  47. original_data = data
  48. if not test.do_some_requests.current_websocket.current_websocket.connected:
  49. ws_host = HOST.replace('http://', 'ws://')
  50. test.do_some_requests.current_websocket.current_websocket.connect(ws_host + '/websocket')
  51. token = str(uuid4())
  52. data = json.dumps({'route': route, 'body': data, 'request_token': token})
  53. print('Sending to websocket:', str(data).replace('{', '\n{')[1:])
  54. test.do_some_requests.current_websocket.current_websocket.send(data, opcode=2)
  55. json_content = receive_answer(token)
  56. print()
  57. status_code = json_content['http_status_code']
  58. if status_code == 200:
  59. pass
  60. elif status_code == 451: # Copyright problems, likely a bug in the upload filter
  61. # Try again
  62. return websocket_request(route, original_data)
  63. else:
  64. if EXIT_ON_FAILED_REQUEST:
  65. if not test.do_some_requests.current_websocket.current_websocket.connected:
  66. test.do_some_requests.current_websocket.current_websocket.close()
  67. sys.exit(status_code)
  68. failed_requests.append((route, status_code))
  69. return json_content['body']
  70. def http_request(route: str, data: Dict) -> Dict:
  71. original_data = data
  72. data = json.dumps(data)
  73. print('Sending to /' + route + ':', str(data).replace('{', '\n{')[1:])
  74. r = requests.post(HOST + '/json/' + route, data=data,
  75. headers=JSON_HEADERS)
  76. content = r.content.decode()
  77. print('Request returned: ' + content.replace('{', '\n{'))
  78. print()
  79. if r.status_code == 200:
  80. pass
  81. elif r.status_code == 451:
  82. return http_request(route, original_data)
  83. else:
  84. if EXIT_ON_FAILED_REQUEST:
  85. sys.exit(r.status_code)
  86. failed_requests.append((route, r.status_code))
  87. return json.loads(content)
  88. default_request_method: Callable[[str, Dict], Dict] = http_request
  89. # default_request_method = websocket_request
  90. def random_time():
  91. start = random.randrange(140)
  92. start = 1561960800 + start * 21600 # somewhere in july or at the beginning of august 2019
  93. return {
  94. 'dt_start': start,
  95. 'dt_end': start + random.choice([1800, 3600, 7200, 86400]),
  96. }
  97. def run_tests():
  98. if debug:
  99. print('You are currently in debug mode.')
  100. print('Host:', str(HOST))
  101. usernames = [f'user{datetime.now().timestamp()}',
  102. f'user{datetime.now().timestamp()}+1']
  103. banks = usernames[:1]
  104. session_ids = {}
  105. message = {}
  106. route = 'news'
  107. default_request_method(route, message)
  108. message = {}
  109. route = 'leaderboard'
  110. default_request_method(route, message)
  111. message = {}
  112. route = 'tradables'
  113. default_request_method(route, message)
  114. for username in usernames:
  115. message = {'username': username, 'password': DEFAULT_PW}
  116. route = 'register'
  117. default_request_method(route, message)
  118. message = {'username': username, 'password': DEFAULT_PW}
  119. route = 'login'
  120. session_ids[username] = default_request_method(route, message)['session_id']
  121. message = {'session_id': session_ids[username]}
  122. route = 'logout'
  123. default_request_method(route, message)
  124. message = {'username': username, 'password': DEFAULT_PW}
  125. route = 'login'
  126. session_ids[username] = default_request_method(route, message)['session_id']
  127. message = {'session_id': session_ids[username]}
  128. route = 'depot'
  129. default_request_method(route, message)
  130. message = {'session_id': session_ids[username]}
  131. route = 'orders'
  132. default_request_method(route, message)
  133. message = {'session_id': session_ids[username], "ownable": "\u20adollar"}
  134. route = 'orders_on'
  135. default_request_method(route, message)
  136. for password in ['pw2', DEFAULT_PW]:
  137. message = {'session_id': session_ids[username], 'password': password}
  138. route = 'change_password'
  139. default_request_method(route, message)
  140. message = {'username': username, 'password': password}
  141. route = 'login'
  142. session_ids[username] = default_request_method(route, message)['session_id']
  143. for limit in [0, 5, 10, 20, 50]:
  144. message = {'session_id': session_ids[username], 'limit': limit}
  145. route = 'trades'
  146. data = default_request_method(route, message)['data']
  147. assert len(data) <= limit
  148. for username in banks:
  149. message = {'session_id': session_ids[username], 'amount': 5.5e6}
  150. route = 'take_out_personal_loan'
  151. default_request_method(route, message)
  152. message = {'session_id': session_ids[username]}
  153. route = 'buy_banking_license'
  154. default_request_method(route, message)
  155. message = {'session_id': session_ids[username],
  156. 'coupon': 0.05,
  157. 'name': random_ownable_name(),
  158. 'run_time': 43200}
  159. route = 'issue_bond'
  160. default_request_method(route, message)
  161. message = {'issuer': username}
  162. route = 'credits'
  163. default_request_method(route, message)
  164. message = {'issuer': username, 'only_next_mro_qualified': True}
  165. route = 'credits'
  166. default_request_method(route, message)
  167. message = {'issuer': username, 'only_next_mro_qualified': False}
  168. route = 'credits'
  169. default_request_method(route, message)
  170. my_mro_name = random_ownable_name()
  171. message = {'session_id': session_ids[username],
  172. 'coupon': 'next_mro',
  173. 'name': my_mro_name,
  174. 'run_time': 'next_mro'}
  175. route = 'issue_bond'
  176. default_request_method(route, message)
  177. message = {'session_id': session_ids[username],
  178. 'buy': False,
  179. 'ownable': my_mro_name,
  180. 'amount': MRO_AMOUNT,
  181. 'limit': 1,
  182. 'stop_loss': False,
  183. 'time_until_expiration': 43200}
  184. route = 'order'
  185. default_request_method(route, message)
  186. message = {'session_id': session_ids[username]}
  187. route = 'orders'
  188. orders_before_tt = default_request_method(route, message)['data']
  189. assert len(orders_before_tt) == 1
  190. message = {'issuer': username}
  191. route = 'credits'
  192. default_request_method(route, message)
  193. message = {'issuer': username, 'only_next_mro_qualified': True}
  194. route = 'credits'
  195. default_request_method(route, message)
  196. message = {'issuer': username, 'only_next_mro_qualified': False}
  197. route = 'credits'
  198. default_request_method(route, message)
  199. message = {'session_id': session_ids[username]}
  200. route = 'depot'
  201. depot_before_tt = default_request_method(route, message)
  202. message = {}
  203. route = 'tender_calendar'
  204. tender_calendar = default_request_method(route, message)['data']
  205. next_mro_dt = inf
  206. for row in tender_calendar:
  207. if row[0] > time.time():
  208. next_mro_dt = min(next_mro_dt, row[0])
  209. assert next_mro_dt < inf
  210. model.connect(DB_NAME)
  211. assert next_mro_dt - time.time() - 10 > 0, 'Random test fail, this can happen'
  212. model.time_travel(next_mro_dt - time.time() - 10) # 10 seconds before MRO
  213. model.current_connection.commit()
  214. model.cleanup()
  215. message = {'session_id': session_ids[username]}
  216. route = 'depot'
  217. depot_before_mro = default_request_method(route, message)
  218. message = {'session_id': session_ids[username]}
  219. route = 'orders'
  220. orders_before_mro = default_request_method(route, message)['data']
  221. assert len(orders_before_mro) == 1
  222. # some money was spent as interest
  223. assert depot_before_tt['own_wealth'] > depot_before_mro['own_wealth']
  224. assert depot_before_tt['minimum_reserve'] == depot_before_mro['minimum_reserve'] == 0
  225. assert depot_before_tt['banking_license'] and depot_before_mro['banking_license']
  226. for row1 in depot_before_tt['data']:
  227. for row2 in depot_before_mro['data']:
  228. if row1[0] == row2[0]:
  229. if row1[0] == CURRENCY_NAME:
  230. assert row1[1] > row2[1]
  231. else:
  232. assert row1[1] == row2[1]
  233. model.connect(DB_NAME)
  234. model.time_travel(20) # 10 seconds after MRO
  235. model.current_connection.commit()
  236. model.cleanup()
  237. message = {'session_id': session_ids[username]}
  238. route = 'orders'
  239. orders_after_mro = default_request_method(route, message)['data']
  240. assert len(orders_after_mro) == 0
  241. message = {'session_id': session_ids[username]}
  242. route = 'depot'
  243. depot_after_mro = default_request_method(route, message)
  244. # some money was spent as interest and some MROs were sold
  245. assert depot_before_mro['own_wealth'] >= depot_after_mro['own_wealth'] # can be equal since interest rates are only subtracted once per time interval
  246. assert depot_after_mro['minimum_reserve'] == max(0., MRO_AMOUNT * 0.01 - 100000)
  247. assert depot_after_mro['banking_license']
  248. for row1 in depot_before_mro['data']:
  249. for row2 in depot_after_mro['data']:
  250. if row1[0] == row2[0]:
  251. if row1[0] == CURRENCY_NAME:
  252. assert row1[1] < row2[1] < row1[1] + MRO_AMOUNT
  253. else:
  254. assert row1[1] == row2[1]
  255. message = {}
  256. route = 'credits'
  257. default_request_method(route, message)
  258. message = {'only_next_mro_qualified': True}
  259. route = 'credits'
  260. default_request_method(route, message)
  261. message = {'only_next_mro_qualified': False}
  262. route = 'credits'
  263. default_request_method(route, message)
  264. for session_id in session_ids.values():
  265. message = {'session_id': session_id}
  266. route = 'logout'
  267. default_request_method(route, message)
  268. def main():
  269. global default_request_method
  270. for m in [
  271. test.do_some_requests.websocket_request,
  272. # test.do_some_requests.http_request
  273. ]:
  274. # print('Removing existing database (if exists)...', end='')
  275. # try:
  276. # os.remove(DB_NAME + '.db')
  277. # except PermissionError:
  278. # print('Could not recreate database')
  279. # sys.exit(-1)
  280. # except FileNotFoundError:
  281. # pass
  282. # print('done')
  283. default_request_method = m
  284. start = perf_counter()
  285. run_tests()
  286. print()
  287. print('Failed requests:', failed_requests)
  288. print('Total time:' + str(round_to_n(perf_counter() - start, 4)) + 's,')
  289. if test.do_some_requests.current_websocket.current_websocket.connected:
  290. test.do_some_requests.current_websocket.current_websocket.close()
  291. sys.exit(len(failed_requests))
  292. if __name__ == '__main__':
  293. main()