Skip to content

Commit

Permalink
Convert nh2.mock.expect_connect to a context manager.
Browse files Browse the repository at this point in the history
Assert that the expected connection has been established before the context manager ends, similar to:

  mock_server = await nh2.mock.expect_connect(…)
  conn = await nh2.connection.Connection(…)
  assert conn.mock_server is mock_server

Note that sometimes tests only imply a connection, they don't explicitly set one themselves, as in:

  async def opaque_workflow():
      conn = await nh2.connection.Connection('example.com', 443)

  async with nh2.anyio_util.create_task_group() as tg:
      async with nh2.mock.expect_connect('example.com', 443) as mock_server:
          future = tg.start_soon(opaque_workflow)

so expect_connect needs to allow the async system to actually schedule and run backgrounded tasks (which for now is done with just an `anyio.sleep(.01)`).

Explicitly reset nh2.mock.expect_connect's cache between runs.

See #1.
  • Loading branch information
nmlorg committed Nov 2, 2024
1 parent d48f9df commit b448de9
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 11 deletions.
1 change: 1 addition & 0 deletions nh2/_pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
@pytest.fixture(autouse=True)
def _connection_mock(monkeypatch):
monkeypatch.setattr('nh2.connection.Connection', nh2.mock.MockConnection)
monkeypatch.setattr('nh2.mock._servers', {}) # Don't let a failing test poison other tests.
7 changes: 6 additions & 1 deletion nh2/mock.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Utilities to control nh2 during tests."""

import contextlib
import textwrap

import anyio
Expand All @@ -13,6 +14,7 @@
_servers = {}


@contextlib.asynccontextmanager
async def expect_connect(host, port, *, live=False):
"""Prepare for an upcoming attempt to connect to host:port."""

Expand All @@ -22,7 +24,10 @@ async def expect_connect(host, port, *, live=False):
else:
server = await MockServer(host, port)
_servers[host, port] = server
return server
yield server
if (host, port) in _servers:
await anyio.sleep(.01)
assert (host, port) not in _servers


class MockConnection(nh2.connection.Connection):
Expand Down
12 changes: 6 additions & 6 deletions nh2/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
async def test_simple():
"""Basic functionality."""

await nh2.mock.expect_connect('httpbin.org', 443, live=True)
conn = await nh2.connection.Connection('httpbin.org', 443)
async with nh2.mock.expect_connect('httpbin.org', 443, live=True):
conn = await nh2.connection.Connection('httpbin.org', 443)
try:
assert not conn.streams
live_request = await conn.request('GET', '/get?a=b')
Expand All @@ -41,8 +41,8 @@ async def test_simple():
async def test_concurrent_send():
"""Verify the Connection can handle multiple concurrent sends."""

await nh2.mock.expect_connect('httpbin.org', 443, live=True)
conn = await nh2.connection.Connection('httpbin.org', 443)
async with nh2.mock.expect_connect('httpbin.org', 443, live=True):
conn = await nh2.connection.Connection('httpbin.org', 443)
try:
async with anyio.create_task_group() as tg:
tg.start_soon(conn.request, 'GET', '/get?a=1')
Expand All @@ -54,8 +54,8 @@ async def test_concurrent_send():
async def test_concurrent_wait():
"""Verify the interaction between two concurrent LiveRequest.wait()s."""

await nh2.mock.expect_connect('httpbin.org', 443, live=True)
conn = await nh2.connection.Connection('httpbin.org', 443)
async with nh2.mock.expect_connect('httpbin.org', 443, live=True):
conn = await nh2.connection.Connection('httpbin.org', 443)
try:
res1 = res2 = None

Expand Down
8 changes: 4 additions & 4 deletions nh2/test_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
async def test_simple():
"""Manually run a complete connection and request."""

mock_server = await nh2.mock.expect_connect('example.com', 443)
conn = await nh2.connection.Connection('example.com', 443)
async with nh2.mock.expect_connect('example.com', 443) as mock_server:
conn = await nh2.connection.Connection('example.com', 443)
# On connect, client and server each send settings, but client does not block to receive them.
assert mock_server.get_client_events() == ''
assert await mock_server.read() == """
Expand Down Expand Up @@ -105,8 +105,8 @@ async def opaque_workflow():
return 'finished'

async with nh2.anyio_util.create_task_group() as tg:
mock_server = await nh2.mock.expect_connect('example.com', 443)
future = tg.start_soon(opaque_workflow)
async with nh2.mock.expect_connect('example.com', 443) as mock_server:
future = tg.start_soon(opaque_workflow)

assert await mock_server.read() == """
- [RemoteSettingsChanged header_table_size=4096 enable_push=1 initial_window_size=65535 max_frame_size=16384 enable_connect_protocol=0 max_concurrent_streams=100 max_header_list_size=65536]
Expand Down

0 comments on commit b448de9

Please sign in to comment.