180 lines
5.6 KiB
Python
180 lines
5.6 KiB
Python
import os
|
|||
|
|
import time
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
import requests
|
||
|
|
|
||
|
|
HTTP_CONNECT_TIMEOUT_S = 1
|
||
|
|
HTTP_READ_TIMEOUT_S = 5
|
||
|
|
HTTP_READY_TIMEOUT_S = 20
|
||
|
|
HTTP_RETRY_INTERVAL_S = 0.5
|
||
|
|
HTTP_TIMEOUT = (HTTP_CONNECT_TIMEOUT_S, HTTP_READ_TIMEOUT_S)
|
||
|
|
MESSAGE_TIMEOUT_S = 10
|
||
|
|
MESSAGE_POLL_INTERVAL_S = 0.05
|
||
|
|
|
||
|
|
|
||
|
|
def wait_for_http(url):
|
||
|
|
deadline = time.monotonic() + HTTP_READY_TIMEOUT_S
|
||
|
|
last_exception = None
|
||
|
|
while True:
|
||
|
|
remaining = deadline - time.monotonic()
|
||
|
|
if remaining <= 0:
|
||
|
|
pytest.fail(
|
||
|
|
f'timed out waiting for HTTP endpoint {url}: {last_exception}',
|
||
|
|
pytrace=False,
|
||
|
|
)
|
||
|
|
readiness_timeout = (
|
||
|
|
min(HTTP_CONNECT_TIMEOUT_S, remaining),
|
||
|
|
min(HTTP_READ_TIMEOUT_S, remaining),
|
||
|
|
)
|
||
|
|
try:
|
||
|
|
response = requests.get(url, timeout=readiness_timeout)
|
||
|
|
response.close()
|
||
|
|
return
|
||
|
|
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as exc:
|
||
|
|
last_exception = exc
|
||
|
|
|
||
|
|
remaining = deadline - time.monotonic()
|
||
|
|
if remaining <= 0:
|
||
|
|
pytest.fail(
|
||
|
|
f'timed out waiting for HTTP endpoint {url}: {last_exception}',
|
||
|
|
pytrace=False,
|
||
|
|
)
|
||
|
|
time.sleep(min(HTTP_RETRY_INTERVAL_S, remaining))
|
||
|
|
|
||
|
|
|
||
|
|
def post_json(url, path, timeout=HTTP_TIMEOUT, **kwargs):
|
||
|
|
endpoint = url + path
|
||
|
|
try:
|
||
|
|
response = requests.post(endpoint, timeout=timeout, **kwargs)
|
||
|
|
except requests.exceptions.Timeout as exc:
|
||
|
|
pytest.fail(f'timed out waiting for HTTP response from {endpoint}: {exc}', pytrace=False)
|
||
|
|
except requests.exceptions.ConnectionError as exc:
|
||
|
|
pytest.fail(f'could not connect to HTTP endpoint {endpoint}: {exc}', pytrace=False)
|
||
|
|
|
||
|
|
try:
|
||
|
|
assert response.status_code == 200, response.text
|
||
|
|
return response.json()
|
||
|
|
finally:
|
||
|
|
response.close()
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture(scope='session')
|
||
|
|
def client1_ready_url():
|
||
|
|
url = 'http://' + os.environ.get('MESSENGER_TEST_CLIENT1_ADDR', '127.0.0.1:8080')
|
||
|
|
wait_for_http(url)
|
||
|
|
return url
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture(scope='session')
|
||
|
|
def client2_ready_url():
|
||
|
|
url = 'http://' + os.environ.get('MESSENGER_TEST_CLIENT2_ADDR', '127.0.0.1:8081')
|
||
|
|
wait_for_http(url)
|
||
|
|
return url
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture
|
||
|
|
def client1_url(client1_ready_url):
|
||
|
|
url = client1_ready_url
|
||
|
|
get_messages(url) # we need to flush pending messages before and after each tests
|
||
|
|
yield url
|
||
|
|
get_messages(url)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture
|
||
|
|
def client2_url(client2_ready_url):
|
||
|
|
url = client2_ready_url
|
||
|
|
get_messages(url)
|
||
|
|
yield url
|
||
|
|
get_messages(url)
|
||
|
|
|
||
|
|
|
||
|
|
def send_message(url, mes):
|
||
|
|
return post_json(url, '/sendMessage', json=mes)
|
||
|
|
|
||
|
|
|
||
|
|
def get_messages(url, **kwargs):
|
||
|
|
return post_json(url, '/getAndFlushMessages', **kwargs)
|
||
|
|
|
||
|
|
|
||
|
|
def wait_for_messages(url, expected):
|
||
|
|
deadline = time.monotonic() + MESSAGE_TIMEOUT_S
|
||
|
|
messages = []
|
||
|
|
while len(messages) < len(expected):
|
||
|
|
remaining = deadline - time.monotonic()
|
||
|
|
if remaining <= 0:
|
||
|
|
pytest.fail(
|
||
|
|
f'timed out waiting for messages from {url}: '
|
||
|
|
f'expected {expected!r}, received {messages!r}',
|
||
|
|
pytrace=False,
|
||
|
|
)
|
||
|
|
# Divide the remaining budget between connecting and reading the response.
|
||
|
|
timeout = (
|
||
|
|
min(HTTP_CONNECT_TIMEOUT_S, remaining / 2),
|
||
|
|
min(HTTP_READ_TIMEOUT_S, remaining / 2),
|
||
|
|
)
|
||
|
|
batch = get_messages(url, timeout=timeout)
|
||
|
|
assert isinstance(batch, list), f'expected a message array, received {batch!r}'
|
||
|
|
messages.extend(batch)
|
||
|
|
assert messages == expected[:len(messages)], \
|
||
|
|
f'expected {expected!r}, received {messages!r}'
|
||
|
|
remaining = deadline - time.monotonic()
|
||
|
|
if remaining <= 0:
|
||
|
|
pytest.fail(f'timed out waiting for messages from {url}', pytrace=False)
|
||
|
|
if len(messages) < len(expected):
|
||
|
|
time.sleep(min(MESSAGE_POLL_INTERVAL_S, remaining))
|
||
|
|
return messages
|
||
|
|
|
||
|
|
|
||
|
|
def test_single_client_single_message(client1_url, client2_url):
|
||
|
|
mes = {
|
||
|
|
'author': 'TestSingleClient',
|
||
|
|
'text': 'This is test text'
|
||
|
|
}
|
||
|
|
resp = send_message(client1_url, mes)
|
||
|
|
mes['sendTime'] = resp['sendTime']
|
||
|
|
assert wait_for_messages(client1_url, [mes]) == [mes]
|
||
|
|
# Drain both subscriptions before the next test sends more messages.
|
||
|
|
assert wait_for_messages(client2_url, [mes]) == [mes]
|
||
|
|
|
||
|
|
|
||
|
|
def test_single_client_multiple_messages(client1_url, client2_url):
|
||
|
|
mes = [{
|
||
|
|
'author': 'TestSingleClient1',
|
||
|
|
'text': 'This is test text'
|
||
|
|
}, {
|
||
|
|
'author': 'TestSingleClient2',
|
||
|
|
'text': 'This is test text'
|
||
|
|
}]
|
||
|
|
for m in mes:
|
||
|
|
resp = send_message(client1_url, m)
|
||
|
|
m['sendTime'] = resp['sendTime']
|
||
|
|
assert wait_for_messages(client1_url, mes) == mes
|
||
|
|
assert wait_for_messages(client2_url, mes) == mes
|
||
|
|
|
||
|
|
|
||
|
|
def test_two_clients_multiple_messages(client1_url, client2_url):
|
||
|
|
client1_name = 'TestMultiClient1'
|
||
|
|
client2_name = 'TestMultiClient2'
|
||
|
|
mes = [{
|
||
|
|
'author': client1_name,
|
||
|
|
'text': 'This is test text #1'
|
||
|
|
}, {
|
||
|
|
'author': client1_name,
|
||
|
|
'text': 'This is test text #2'
|
||
|
|
}, {
|
||
|
|
'author': client2_name,
|
||
|
|
'text': 'This is test text #3'
|
||
|
|
}, {
|
||
|
|
'author': client2_name,
|
||
|
|
'text': 'This is test text #4'
|
||
|
|
}]
|
||
|
|
times = set()
|
||
|
|
for m in mes:
|
||
|
|
resp = send_message(client1_url if m['author'] == client1_name else client2_url, m)
|
||
|
|
m['sendTime'] = resp['sendTime']
|
||
|
|
times.add(m['sendTime'])
|
||
|
|
assert len(times) == len(mes)
|
||
|
|
assert wait_for_messages(client1_url, mes) == mes
|
||
|
|
assert wait_for_messages(client2_url, mes) == mes
|