Initial push
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
"""
|
||||
Tests for RN Praxis 1
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import re
|
||||
import socket
|
||||
import time
|
||||
from http.client import HTTPConnection
|
||||
|
||||
import pytest
|
||||
|
||||
from util import KillOnExit, randbytes
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def webserver(request):
|
||||
"""
|
||||
Return a callable function that spawns a webserver with the given arguments.
|
||||
"""
|
||||
def runner(*args, **kwargs):
|
||||
"""Spawn a webserver with the given arguments."""
|
||||
return KillOnExit([request.config.getoption('executable'), *args], **kwargs)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def empty_context(*args, **kwargs):
|
||||
yield # No server initialization here, just an empty context that never fails
|
||||
|
||||
if request.config.getoption('debug_own'):
|
||||
return empty_context
|
||||
|
||||
return runner
|
||||
|
||||
|
||||
def test_execute(webserver, port):
|
||||
"""
|
||||
Test server is executable
|
||||
"""
|
||||
|
||||
with webserver('127.0.0.1', f'{port}'):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.timeout(1)
|
||||
def test_listen(webserver, port):
|
||||
"""
|
||||
Test server is listening on port (2.1)
|
||||
"""
|
||||
|
||||
with webserver(
|
||||
'127.0.0.1', f'{port}'
|
||||
), socket.create_connection(
|
||||
('localhost', port)
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.timeout(1)
|
||||
def test_reply(webserver, port):
|
||||
"""
|
||||
Test the server is sending a reply (2.2)
|
||||
"""
|
||||
|
||||
with webserver(
|
||||
'127.0.0.1', f'{port}'
|
||||
), socket.create_connection(
|
||||
('localhost', port)
|
||||
) as conn:
|
||||
conn.send('Request\r\n\r\n'.encode())
|
||||
reply = conn.recv(1024)
|
||||
assert len(reply) > 0
|
||||
|
||||
|
||||
@pytest.mark.timeout(2)
|
||||
def test_packets(webserver, port):
|
||||
"""
|
||||
Test HTTP delimiter for packet end (2.3)
|
||||
"""
|
||||
|
||||
with webserver(
|
||||
'127.0.0.1', f'{port}'
|
||||
), socket.create_connection(
|
||||
('localhost', port)
|
||||
) as conn:
|
||||
conn.send('GET / HTTP/1.1\r\n\r\n'.encode())
|
||||
time.sleep(.5)
|
||||
conn.send('GET / HTTP/1.1\r\na: b\r\n'.encode())
|
||||
time.sleep(.5)
|
||||
conn.send('\r\n'.encode())
|
||||
time.sleep(.5) # Attempt to gracefully handle all kinds of multi-packet replies...
|
||||
replies = conn.recv(1024).split(b'\r\n\r\n')
|
||||
assert replies[0] and replies[1] and not replies[2]
|
||||
|
||||
|
||||
@pytest.mark.timeout(1)
|
||||
def test_httpreply(webserver, port):
|
||||
"""
|
||||
Test reply is syntactically correct HTTP packet (2.4)
|
||||
"""
|
||||
|
||||
with webserver(
|
||||
'127.0.0.1', f'{port}'
|
||||
), socket.create_connection(
|
||||
('localhost', port)
|
||||
) as conn:
|
||||
conn.send('Request\r\n\r\n'.encode())
|
||||
time.sleep(.5) # Attempt to gracefully handle all kinds of multi-packet replies...
|
||||
reply = conn.recv(1024)
|
||||
assert re.search(br'HTTP/1.[01] 400.*\r\n(.*\r\n)*\r\n', reply)
|
||||
|
||||
|
||||
@pytest.mark.timeout(1)
|
||||
def test_httpreplies(webserver, port):
|
||||
"""
|
||||
Test reply is semantically correct HTTP packet (2.5)
|
||||
"""
|
||||
|
||||
with webserver(
|
||||
'127.0.0.1', f'{port}'
|
||||
), contextlib.closing(
|
||||
HTTPConnection('localhost', port)
|
||||
) as conn:
|
||||
conn.connect()
|
||||
|
||||
# there is no HEAD command in HTTP :-)
|
||||
conn.request('HEAD', '/index.html')
|
||||
reply = conn.getresponse()
|
||||
reply.read()
|
||||
assert reply.status == 501, "HEAD did not reply with '501'"
|
||||
|
||||
conn.request('GET', '/index.html')
|
||||
reply = conn.getresponse()
|
||||
assert reply.status == 404
|
||||
|
||||
|
||||
@pytest.mark.timeout(1)
|
||||
def test_static_content(webserver, port):
|
||||
"""
|
||||
Test static content can be accessed (2.6)
|
||||
"""
|
||||
|
||||
with webserver(
|
||||
'127.0.0.1', f'{port}'
|
||||
), contextlib.closing(
|
||||
HTTPConnection('localhost', port)
|
||||
) as conn:
|
||||
conn.connect()
|
||||
|
||||
for path, content in {
|
||||
'/static/foo': b'Foo',
|
||||
'/static/bar': b'Bar',
|
||||
'/static/baz': b'Baz',
|
||||
}.items():
|
||||
conn.request('GET', path)
|
||||
reply = conn.getresponse()
|
||||
payload = reply.read()
|
||||
assert reply.status == 200
|
||||
assert payload == content
|
||||
|
||||
for path in [
|
||||
'/static/other',
|
||||
'/static/anything',
|
||||
'/static/else'
|
||||
]:
|
||||
conn.request('GET', path)
|
||||
reply = conn.getresponse()
|
||||
reply.length = 0 # Convince `http.client` to handle no-content 404s properly
|
||||
reply.read()
|
||||
assert reply.status == 404
|
||||
|
||||
|
||||
@pytest.mark.timeout(1)
|
||||
def test_dynamic_content(webserver, port):
|
||||
"""
|
||||
Test dynamic storage of data (key,value) works (2.7)
|
||||
"""
|
||||
|
||||
with webserver(
|
||||
'127.0.0.1', f'{port}'
|
||||
), contextlib.closing(
|
||||
HTTPConnection('localhost', port)
|
||||
) as conn:
|
||||
conn.connect()
|
||||
|
||||
path = f'/dynamic/{randbytes(8).hex()}'
|
||||
content = randbytes(32).hex().encode()
|
||||
|
||||
conn.request('GET', path)
|
||||
response = conn.getresponse()
|
||||
payload = response.read()
|
||||
assert response.status == 404, f"'{path}' should be missing, but GET was not answered with '404'"
|
||||
|
||||
conn.request('PUT', path, content)
|
||||
response = conn.getresponse()
|
||||
payload = response.read()
|
||||
assert response.status in {200, 201, 202, 204}, f"Creation of '{path}' did not yield '201'"
|
||||
|
||||
conn.request('GET', path)
|
||||
response = conn.getresponse()
|
||||
payload = response.read()
|
||||
assert response.status == 200
|
||||
assert payload == content, f"Content of '{path}' does not match what was passed"
|
||||
|
||||
conn.request('DELETE', path)
|
||||
response = conn.getresponse()
|
||||
response.read()
|
||||
assert response.status in {200, 202, 204}, f"Deletion of '{path}' did not succeed"
|
||||
|
||||
conn.request('GET', path)
|
||||
response = conn.getresponse()
|
||||
response.read()
|
||||
assert response.status == 404, f"'{path}' should be missing"
|
||||
Reference in New Issue
Block a user