You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 line
6.1KB

  1. import asyncio
  2. import itertools
  3. from pico import start_server, send_json, recv_json
  4. import websockets
  5. class Script:
  6. def __init__(self):
  7. self.events = []
  8. def __add__(self, event):
  9. self.events.append(('send', event))
  10. return self
  11. def __sub__(self, event):
  12. self.events.append(('recv', event))
  13. return self
  14. async def _make_client(path, timeout, script):
  15. state = {}
  16. error = False
  17. await asyncio.sleep(timeout)
  18. async with websockets.connect(path) as websocket:
  19. for kind, message in script.events:
  20. if kind == 'recv':
  21. A, B = message, await recv_json(websocket)
  22. B.pop('ts')
  23. state.update(message)
  24. if A != B:
  25. error = True
  26. print('-', A)
  27. print('+', B)
  28. print()
  29. elif kind == 'send':
  30. await send_json(websocket, **message)
  31. while True:
  32. if state['users'][0] == state['username']:
  33. break
  34. message = await recv_json(websocket)
  35. message.pop('ts')
  36. error = True
  37. print('+', message)
  38. print()
  39. state.update(message)
  40. if error:
  41. print('error')
  42. exit()
  43. async def _test(*clients):
  44. try:
  45. gather = asyncio.gather(
  46. start_server('localhost', 8642, 'TestServer'),
  47. *clients,
  48. )
  49. server, *_ = await asyncio.wait_for(gather, timeout=2)
  50. server.close()
  51. except asyncio.TimeoutError:
  52. return
  53. def test_happy_path():
  54. client = _make_client('ws://localhost:8642/', 0.1, Script()
  55. + {'action': 'login', 'username': 'TestUser'}
  56. - {'kind': 'update', 'username': 'TestUser'}
  57. - {'kind': 'update', 'users': ['TestUser'], 'info': 'TestUser joined'}
  58. + {'action': 'post', 'text': 'Hello World!'}
  59. - {'kind': 'post', 'source': 'TestUser', 'text': 'Hello World!'}
  60. )
  61. return _test(client)
  62. def test_post_before_login():
  63. client = _make_client('ws://localhost:8642/', 0.1, Script()
  64. + {}
  65. - {'kind': 'error', 'info': 'Message without action is invalid'}
  66. + {'action': 'post', 'text': ''}
  67. - {'kind': 'error', 'info': 'Not logged in'}
  68. + {'action': 'login', 'username': ''}
  69. - {'kind': 'error', 'info': 'Invalid username'}
  70. + {'action': 'login', 'username': 'Joe'}
  71. - {'kind': 'update', 'username': 'Joe'}
  72. - {'kind': 'update', 'users': ['Joe'], 'info': 'Joe joined'}
  73. )
  74. return _test(client)
  75. def test_interact():
  76. client1 = _make_client('ws://localhost:8642/', 0.10, Script()
  77. + {'action': 'login', 'username': 'Alice'}
  78. - {'kind': 'update', 'username': 'Alice'}
  79. - {'kind': 'update', 'users': ['Alice'], 'info': 'Alice joined'}
  80. - {'kind': 'update', 'users': ['Alice', 'Bob'], 'info': 'Bob joined'}
  81. + {'action': 'post', 'text': 'Hey Bob!'}
  82. - {'kind': 'post', 'source': 'Alice', 'text': 'Hey Bob!'}
  83. - {'kind': 'post', 'source': 'Bob', 'text': 'Howdy!'}
  84. )
  85. client2 = _make_client('ws://localhost:8642/', 0.11, Script()
  86. + {'action': 'login', 'username': 'Bob'}
  87. - {'kind': 'update', 'username': 'Bob'}
  88. - {'kind': 'update', 'users': ['Alice', 'Bob'], 'info': 'Bob joined'}
  89. - {'kind': 'post', 'source': 'Alice', 'text': 'Hey Bob!'}
  90. + {'action': 'post', 'text': 'Howdy!'}
  91. - {'kind': 'post', 'source': 'Bob', 'text': 'Howdy!'}
  92. + {'action': 'post', 'text': ''}
  93. - {'kind': 'update', 'users': ['Bob'], 'info': 'Alice left'}
  94. )
  95. return _test(client1, client2)
  96. def test_party():
  97. client1 = _make_client('ws://localhost:8642/', 0.10, Script()
  98. + {'action': 'login', 'username': 'Norman'}
  99. - {'kind': 'update', 'username': 'Norman'}
  100. - {'kind': 'update', 'users': ['Norman'], 'info': 'Norman joined'}
  101. - {'kind': 'update', 'users': ['Norman', 'Ray'], 'info': 'Ray joined'}
  102. - {'kind': 'update', 'users': ['Norman', 'Ray', 'Emma'], 'info': 'Emma joined'}
  103. + {'action': 'post', 'text': 'なに?'}
  104. - {'kind': 'post', 'source': 'Norman', 'text': 'なに?'}
  105. )
  106. client2 = _make_client('ws://localhost:8642/', 0.11, Script()
  107. + {'action': 'login', 'username': 'Ray'}
  108. - {'kind': 'update', 'username': 'Ray'}
  109. - {'kind': 'update', 'users': ['Norman', 'Ray'], 'info': 'Ray joined'}
  110. - {'kind': 'update', 'users': ['Norman', 'Ray', 'Emma'], 'info': 'Emma joined'}
  111. - {'kind': 'post', 'source': 'Norman', 'text': 'なに?'}
  112. - {'kind': 'update', 'users': ['Ray', 'Emma'], 'info': 'Norman left'}
  113. )
  114. client3 = _make_client('ws://localhost:8642/', 0.12, Script()
  115. + {'action': 'login', 'username': 'Emma'}
  116. - {'kind': 'update', 'username': 'Emma'}
  117. - {'kind': 'update', 'users': ['Norman', 'Ray', 'Emma'], 'info': 'Emma joined'}
  118. - {'kind': 'post', 'source': 'Norman', 'text': 'なに?'}
  119. - {'kind': 'update', 'users': ['Ray', 'Emma'], 'info': 'Norman left'}
  120. - {'kind': 'update', 'users': ['Emma'], 'info': 'Ray left'}
  121. )
  122. return _test(client1, client2, client3)
  123. def test_rooms():
  124. client1 = _make_client('ws://localhost:8642/A', 0.10, Script()
  125. + {'action': 'login', 'username': 'Dandy'}
  126. - {'kind': 'update', 'username': 'Dandy'}
  127. - {'kind': 'update', 'users': ['Dandy'], 'info': 'Dandy joined'}
  128. + {'action': 'post', 'text': 'Hi'}
  129. - {'kind': 'post', 'source': 'Dandy', 'text': 'Hi'}
  130. )
  131. client2 = _make_client('ws://localhost:8642/B', 0.10, Script()
  132. + {'action': 'login', 'username': 'Dandy'}
  133. - {'kind': 'update', 'username': 'Dandy'}
  134. - {'kind': 'update', 'users': ['Dandy'], 'info': 'Dandy joined'}
  135. + {'action': 'post', 'text': 'Howdy'}
  136. - {'kind': 'post', 'source': 'Dandy', 'text': 'Howdy'}
  137. )
  138. return _test(client1, client2)
  139. if __name__ == '__main__':
  140. loop = asyncio.get_event_loop()
  141. for fn_name, fn in list(locals().items()):
  142. if fn_name.startswith('test_'):
  143. loop.run_until_complete(fn())