From: Kyle Fuller Date: Sat, 1 Apr 2023 11:13:13 +0000 (+0100) Subject: refactor: split tests into multiple files X-Git-Url: http://git.99rst.org/?a=commitdiff_plain;h=b97d4426130c7962860800d4a6d70e765bc3c7cf;p=znc-palaver.git refactor: split tests into multiple files --- diff --git a/.gitignore b/.gitignore index 1a36729..d892798 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ palaver.so test-regex +__pycache__/ diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..f56dd3c --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,14 @@ +from test.utils import setUp, tearDown + +import pytest +import pytest_asyncio + +# All test coroutines will be treated as marked. +pytestmark = pytest.mark.asyncio + + +@pytest_asyncio.fixture +async def znc(): + proc, reader, writer = await setUp() + yield (reader, writer) + await tearDown(proc) diff --git a/test/test_palaver.py b/test/test_palaver.py index d6994d1..494134e 100644 --- a/test/test_palaver.py +++ b/test/test_palaver.py @@ -1,135 +1,36 @@ import asyncio import json -import os -from typing import Dict, Tuple, Optional +from test.utils import ( + assert_user_agent, + get_znc_version, + read_headers, + read_push_request, + requires_znc_version, + setUp, + tearDown, +) import pytest import pytest_asyncio -from semantic_version import Version - # All test coroutines will be treated as marked. pytestmark = pytest.mark.asyncio -async def get_znc_version(): - proc = await asyncio.create_subprocess_shell( - 'znc --version', - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE) - - stdout, stderr = await proc.communicate() - return stdout.decode('utf-8').split()[1] - - -async def requires_znc_version(znc_version): - actual_version = await get_znc_version() - - if Version(znc_version) > Version(actual_version): - pytest.skip('ZNC >= {} is required for this test, found {}'.format(znc_version, version)) - - -async def read_headers(reader) -> Dict[str, str]: - headers = {} - - while True: - line = await reader.readline() - if line == b'\r\n': - return headers - - name, _, value = line.decode('utf-8').strip().partition(': ') - # while HTTP allows the multiple headers wiht the same name, - # we're keeping implementation simple by not handling that - # case (because we don't use it). - assert name not in headers - - headers[name] = value - - -async def read_push_request(reader) -> Tuple[Dict[str, str], bytes]: - request_line = await reader.readline() - assert request_line == b'POST /push HTTP/1.1\r\n' - - headers = await read_headers(reader) - assert headers['Host'] == '127.0.0.1' - assert headers['Connection'] == 'close' - assert headers['Content-Type'] == 'application/json' - await assert_user_agent(headers['User-Agent']) - - assert 'Content-Length' in headers - body = await reader.read(int(headers['Content-Length'])) - return headers, body - - -async def assert_user_agent(user_agent): - products = user_agent.split(' ') - assert len(products) == 2 - - product1, product1_version = products[0].split('/') - assert product1 == 'znc-palaver' - assert Version(product1_version).major >= 1 - - product2, product2_version = products[1].split('/') - assert product2 == 'znc' - assert product2_version == await get_znc_version() - - -async def setUp(): - running_as_root = os.getuid() == 0 - allow_root = ' --allow-root' if running_as_root else '' - - proc = await asyncio.create_subprocess_shell(f'znc -d test/fixtures --foreground --debug{allow_root}') - await asyncio.sleep(31 if running_as_root else 1) - - (reader, writer) = await asyncio.open_connection('localhost', 6698) - writer.write(b'CAP LS 302\r\n') - - line = await reader.readline() - writer.write(b'CAP REQ :palaverapp.com\r\n') - - line = await reader.readline() - assert line == b':irc.znc.in CAP unknown-nick ACK :palaverapp.com\r\n' - - writer.write(b'CAP END\r\n') - writer.write(b'PASS admin\r\n') - writer.write(b'USER admin admin admin\r\n') - writer.write(b'NICK admin\r\n') - await writer.drain() - - line = await reader.readline() - assert line == b':irc.znc.in 001 admin :Welcome to ZNC\r\n' - - return (proc, reader, writer) - - -async def tearDown(proc): - await asyncio.sleep(0.2) - - proc.kill() - await proc.wait() - - config = 'test/fixtures/moddata/palaver/palaver.conf' - if os.path.exists(config): - os.remove(config) - - -@pytest_asyncio.fixture -async def znc(): - proc, reader, writer = await setUp() - yield (reader, writer) - await tearDown(proc) - - async def test_registering_device(znc): reader, writer = znc - writer.write(b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n') + writer.write( + b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n' + ) await writer.drain() line = await reader.readline() assert line == b'PALAVER REQ *\r\n' - writer.write(b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n') + writer.write( + b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n' + ) writer.write(b'PALAVER SET PUSH-TOKEN 605b64f5addc408fcfa7ff0685e2d065fdecb127\r\n') writer.write(b'PALAVER SET PUSH-ENDPOINT https://api.palaverapp.com/1/push\r\n') writer.write(b'PALAVER ADD MENTION-KEYWORD cocode\r\n') @@ -159,7 +60,10 @@ async def test_loading_module_new_cap(): assert line == b':irc.znc.in CAP admin NEW :palaverapp.com\r\n' line = await reader.readline() - assert line == b':*status!znc@znc.in PRIVMSG admin :Loaded module palaver: [test/fixtures/modules/palaver.so]\r\n' + assert ( + line + == b':*status!znc@znc.in PRIVMSG admin :Loaded module palaver: [test/fixtures/modules/palaver.so]\r\n' + ) await tearDown(proc) @@ -191,7 +95,7 @@ async def test_receiving_notification(znc): 'badge': 1, 'message': 'Test notification', 'sender': 'palaver', - 'network': 'b758eaab1a4611a310642a6e8419fbff' + 'network': 'b758eaab1a4611a310642a6e8419fbff', } writer.write(b'HTTP/1.1 204 No Content\r\n\r\n') @@ -205,13 +109,17 @@ async def test_receiving_notification(znc): addr = server.sockets[0].getsockname() url = f'http://{addr[0]}:{addr[1]}/push' - writer.write(b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n') + writer.write( + b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n' + ) await writer.drain() line = await reader.readline() assert line == b'PALAVER REQ *\r\n' - writer.write(b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n') + writer.write( + b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n' + ) writer.write(f'PALAVER SET PUSH-ENDPOINT {url}\r\n'.encode('utf-8')) writer.write(b'PALAVER END\r\n') await writer.drain() @@ -234,7 +142,7 @@ async def test_receiving_notification_with_push_token(znc): 'badge': 1, 'message': 'Test notification', 'sender': 'palaver', - 'network': 'b758eaab1a4611a310642a6e8419fbff' + 'network': 'b758eaab1a4611a310642a6e8419fbff', } writer.write(b'HTTP/1.1 204 No Content\r\n\r\n') @@ -249,13 +157,17 @@ async def test_receiving_notification_with_push_token(znc): url = f'http://{addr[0]}:{addr[1]}/push' reader, writer = znc - writer.write(b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n') + writer.write( + b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n' + ) await writer.drain() line = await reader.readline() assert line == b'PALAVER REQ *\r\n' - writer.write(b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n') + writer.write( + b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n' + ) writer.write(f'PALAVER SET PUSH-ENDPOINT {url}\r\n'.encode('utf-8')) writer.write(f'PALAVER SET PUSH-TOKEN abcdefg\r\n'.encode('utf-8')) writer.write(b'PALAVER END\r\n') @@ -281,7 +193,7 @@ async def test_receiving_notification_with_retry_on_rate_limit(znc): 'badge': 1, 'message': 'Test notification', 'sender': 'palaver', - 'network': 'b758eaab1a4611a310642a6e8419fbff' + 'network': 'b758eaab1a4611a310642a6e8419fbff', } if not hasattr(connected, 'requests'): @@ -302,13 +214,17 @@ async def test_receiving_notification_with_retry_on_rate_limit(znc): addr = server.sockets[0].getsockname() url = f'http://{addr[0]}:{addr[1]}/push' - writer.write(b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n') + writer.write( + b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n' + ) await writer.drain() line = await reader.readline() assert line == b'PALAVER REQ *\r\n' - writer.write(b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n') + writer.write( + b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n' + ) writer.write(f'PALAVER SET PUSH-ENDPOINT {url}\r\n'.encode('utf-8')) writer.write(f'PALAVER SET PUSH-TOKEN abcdefg\r\n'.encode('utf-8')) writer.write(b'PALAVER END\r\n') @@ -318,7 +234,10 @@ async def test_receiving_notification_with_retry_on_rate_limit(znc): await writer.drain() line = await reader.readline() - assert line == b':*palaver!znc@znc.in PRIVMSG admin :Notification sent to 1 clients.\r\n' + assert ( + line + == b':*palaver!znc@znc.in PRIVMSG admin :Notification sent to 1 clients.\r\n' + ) await asyncio.sleep(2.2) server.close() @@ -337,7 +256,7 @@ async def test_receiving_notification_with_retry_on_server_error(znc): 'badge': 1, 'message': 'Test notification', 'sender': 'palaver', - 'network': 'b758eaab1a4611a310642a6e8419fbff' + 'network': 'b758eaab1a4611a310642a6e8419fbff', } if not hasattr(connected, 'requests'): @@ -358,13 +277,17 @@ async def test_receiving_notification_with_retry_on_server_error(znc): addr = server.sockets[0].getsockname() url = f'http://{addr[0]}:{addr[1]}/push' - writer.write(b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n') + writer.write( + b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n' + ) await writer.drain() line = await reader.readline() assert line == b'PALAVER REQ *\r\n' - writer.write(b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n') + writer.write( + b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n' + ) writer.write(f'PALAVER SET PUSH-ENDPOINT {url}\r\n'.encode('utf-8')) writer.write(f'PALAVER SET PUSH-TOKEN abcdefg\r\n'.encode('utf-8')) writer.write(b'PALAVER END\r\n') @@ -374,7 +297,10 @@ async def test_receiving_notification_with_retry_on_server_error(znc): await writer.drain() line = await reader.readline() - assert line == b':*palaver!znc@znc.in PRIVMSG admin :Notification sent to 1 clients.\r\n' + assert ( + line + == b':*palaver!znc@znc.in PRIVMSG admin :Notification sent to 1 clients.\r\n' + ) await asyncio.sleep(2.2) server.close() @@ -398,7 +324,7 @@ async def test_receiving_notification_with_retry_on_disconnect(znc): 'badge': 1, 'message': 'Test notification', 'sender': 'palaver', - 'network': 'b758eaab1a4611a310642a6e8419fbff' + 'network': 'b758eaab1a4611a310642a6e8419fbff', } connected.requests += 1 @@ -415,13 +341,17 @@ async def test_receiving_notification_with_retry_on_disconnect(znc): addr = server.sockets[0].getsockname() url = f'http://{addr[0]}:{addr[1]}/push' - writer.write(b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n') + writer.write( + b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n' + ) await writer.drain() line = await reader.readline() assert line == b'PALAVER REQ *\r\n' - writer.write(b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n') + writer.write( + b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n' + ) writer.write(f'PALAVER SET PUSH-ENDPOINT {url}\r\n'.encode('utf-8')) writer.write(f'PALAVER SET PUSH-TOKEN abcdefg\r\n'.encode('utf-8')) writer.write(b'PALAVER END\r\n') @@ -431,7 +361,10 @@ async def test_receiving_notification_with_retry_on_disconnect(znc): await writer.drain() line = await reader.readline() - assert line == b':*palaver!znc@znc.in PRIVMSG admin :Notification sent to 1 clients.\r\n' + assert ( + line + == b':*palaver!znc@znc.in PRIVMSG admin :Notification sent to 1 clients.\r\n' + ) await asyncio.sleep(2.2) server.close() diff --git a/test/utils.py b/test/utils.py new file mode 100644 index 0000000..09168a0 --- /dev/null +++ b/test/utils.py @@ -0,0 +1,110 @@ +import asyncio +import os +from typing import Dict, Optional, Tuple + +import pytest +from semantic_version import Version + + +async def setUp(): + running_as_root = os.getuid() == 0 + allow_root = ' --allow-root' if running_as_root else '' + + proc = await asyncio.create_subprocess_shell( + f'znc -d test/fixtures --foreground --debug{allow_root}' + ) + await asyncio.sleep(31 if running_as_root else 1) + + (reader, writer) = await asyncio.open_connection('localhost', 6698) + writer.write(b'CAP LS 302\r\n') + + line = await reader.readline() + writer.write(b'CAP REQ :palaverapp.com\r\n') + + line = await reader.readline() + assert line == b':irc.znc.in CAP unknown-nick ACK :palaverapp.com\r\n' + + writer.write(b'CAP END\r\n') + writer.write(b'PASS admin\r\n') + writer.write(b'USER admin admin admin\r\n') + writer.write(b'NICK admin\r\n') + await writer.drain() + + line = await reader.readline() + assert line == b':irc.znc.in 001 admin :Welcome to ZNC\r\n' + + return (proc, reader, writer) + + +async def tearDown(proc): + await asyncio.sleep(0.2) + + proc.kill() + await proc.wait() + + config = 'test/fixtures/moddata/palaver/palaver.conf' + if os.path.exists(config): + os.remove(config) + + +async def get_znc_version(): + proc = await asyncio.create_subprocess_shell( + 'znc --version', stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + + stdout, stderr = await proc.communicate() + return stdout.decode('utf-8').split()[1] + + +async def requires_znc_version(znc_version): + actual_version = await get_znc_version() + + if Version(znc_version) > Version(actual_version): + pytest.skip( + 'ZNC >= {} is required for this test, found {}'.format(znc_version, version) + ) + + +async def read_headers(reader) -> Dict[str, str]: + headers = {} + + while True: + line = await reader.readline() + if line == b'\r\n': + return headers + + name, _, value = line.decode('utf-8').strip().partition(': ') + # while HTTP allows the multiple headers wiht the same name, + # we're keeping implementation simple by not handling that + # case (because we don't use it). + assert name not in headers + + headers[name] = value + + +async def read_push_request(reader) -> Tuple[Dict[str, str], bytes]: + request_line = await reader.readline() + assert request_line == b'POST /push HTTP/1.1\r\n' + + headers = await read_headers(reader) + assert headers['Host'] == '127.0.0.1' + assert headers['Connection'] == 'close' + assert headers['Content-Type'] == 'application/json' + await assert_user_agent(headers['User-Agent']) + + assert 'Content-Length' in headers + body = await reader.read(int(headers['Content-Length'])) + return headers, body + + +async def assert_user_agent(user_agent): + products = user_agent.split(' ') + assert len(products) == 2 + + product1, product1_version = products[0].split('/') + assert product1 == 'znc-palaver' + assert Version(product1_version).major >= 1 + + product2, product2_version = products[1].split('/') + assert product2 == 'znc' + assert product2_version == await get_znc_version()