Skip to content

Commit

Permalink
Run jupyter tests separately with proper dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielSchiavini committed Feb 20, 2024
1 parent 9307ce9 commit ce96097
Show file tree
Hide file tree
Showing 15 changed files with 61 additions and 102 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ jobs:
--cov-fail-under=70
tests/unitary/
jupyter:
name: "integration tests (jupyter)"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"

- name: Install Requirements
run: |
pip install -r dev-requirements.txt
pip install .[jupyter-testing]
- name: Run Jupyter tests
# run separately to clarify its dependency on dependencies
run: pytest -n auto tests/integration/jupyter/

anvil:
name: "integration tests (anvil)"
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies = [

[project.optional-dependencies]
forking-recommended = ["ujson"]
jupyter-testing = ["jupyter_server", "ipython", "nest_asyncio"]

[build-system]
requires = ["setuptools", "wheel"]
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import json
import re
from asyncio import get_event_loop
Expand All @@ -9,6 +10,8 @@
from eth_account import Account

import boa
from boa.integrations.jupyter import BrowserRPC, BrowserSigner
from boa.rpc import RPCError


@pytest.fixture()
Expand All @@ -21,7 +24,7 @@ def mocked_token(token):


@pytest.fixture()
def env(browser, account, mock_fork, mock_callback):
def env(account, mock_fork, mock_callback):
mock_callback("eth_requestAccounts", [account.address])
boa.set_browser_env(account)
return boa.env
Expand Down Expand Up @@ -64,7 +67,7 @@ def find_response(mock_calls, func_to_body_dict):


@pytest.fixture()
def mock_callback(mocked_token, browser, display_mock):
def mock_callback(mocked_token, display_mock):
"""Returns a function that allows mocking the result of the frontend callback."""

with mock.patch("boa.integrations.jupyter.browser.get_event_loop") as mock_get_loop:
Expand Down Expand Up @@ -110,17 +113,9 @@ def mock_fork(mock_callback):


@pytest.fixture()
def browser():
# Import the browser module after the mocks have been set up
from boa.integrations.jupyter import browser

return browser


@pytest.fixture()
def display_mock(browser):
yield browser.display
browser.display.reset_mock()
def display_mock():
with patch("boa.integrations.jupyter.browser.display") as display_mock:
yield display_mock


@pytest.fixture()
Expand All @@ -129,17 +124,16 @@ def account():


@pytest.fixture()
def colab_eval_mock(browser):
colab_eval_mock = MagicMock()
with patch.object(browser, "colab_eval_js", colab_eval_mock):
def colab_eval_mock():
with patch("boa.integrations.jupyter.browser.colab_eval_js") as colab_eval_mock:
yield colab_eval_mock


def test_nest_applied(browser):
browser.nest_asyncio.apply.assert_called()
def test_nest_applied():
assert hasattr(asyncio, "_nest_patched")


def test_browser_sign_typed_data(browser, display_mock, mock_callback, env):
def test_browser_sign_typed_data(display_mock, mock_callback, env):
signature = env.generate_address()
mock_callback("eth_signTypedData_v4", signature)
data = env.signer.sign_typed_data(
Expand All @@ -148,32 +142,28 @@ def test_browser_sign_typed_data(browser, display_mock, mock_callback, env):
assert data == signature


def test_browser_rpc_inject_js(mocked_token, display_mock, browser, mock_callback):
browser.BrowserRPC()
def test_browser_rpc_inject_js(mocked_token, display_mock, mock_callback):
BrowserRPC()
(((js1,), _),) = display_mock.call_args_list
assert "window._titanoboa = " in js1.data


def test_browser_signer_colab(colab_eval_mock, mocked_token, browser, display_mock):
def test_browser_signer_colab(colab_eval_mock, mocked_token, display_mock):
address = boa.env.generate_address()
colab_eval_mock.return_value = json.dumps({"data": [address]})
signer = browser.BrowserSigner()
signer = BrowserSigner()
assert signer.address == address
colab_eval_mock.assert_called_once()
(js,), _ = colab_eval_mock.call_args
assert f'rpc("{mocked_token}", "eth_requestAccounts", [])' in js
display_mock.assert_called_once()


def test_browser_loads_signer(
token, browser, display_mock, mock_callback, account, mock_fork
):
def test_browser_loads_signer(token, display_mock, mock_callback, account, mock_fork):
mock_callback("eth_requestAccounts", [account.address])
boa.set_browser_env()
assert boa.env.eoa == account.address
assert (
type(boa.env._accounts[boa.env.eoa]).__name__ == browser.BrowserSigner.__name__
)
assert type(boa.env._accounts[boa.env.eoa]).__name__ == BrowserSigner.__name__


def test_browser_chain_id(token, env, display_mock, mock_callback):
Expand All @@ -190,9 +180,7 @@ def test_browser_chain_id(token, env, display_mock, mock_callback):
)


def test_browser_rpc(
token, browser, display_mock, mock_callback, account, mock_fork, env
):
def test_browser_rpc(token, display_mock, mock_callback, account, mock_fork, env):
mock_callback("eth_gasPrice", "0x123")
assert env.get_gas_price() == 291

Expand All @@ -201,37 +189,33 @@ def test_browser_rpc(
assert f'rpc("{token}", "eth_gasPrice", [])' in js.data


def test_browser_rpc_error(
token, browser, display_mock, mock_callback, account, mock_fork, env
):
def test_browser_rpc_error(token, display_mock, mock_callback, account, mock_fork, env):
rpc_error = {"code": -32000, "message": "Reverted"}
mock_callback(
"eth_gasPrice", error={"message": "error", "info": {"error": rpc_error}}
)
with pytest.raises(browser.RPCError) as exc_info:
with pytest.raises(RPCError) as exc_info:
env.get_gas_price()
assert str(exc_info.value) == "-32000: Reverted"


def test_browser_rpc_server_error(
token, browser, display_mock, mock_callback, account, mock_fork, env
token, display_mock, mock_callback, account, mock_fork, env
):
error = {
"code": "UNKNOWN_ERROR",
"error": {"code": -32603, "message": "server error"},
}
mock_callback("eth_gasPrice", error=error)
with pytest.raises(browser.RPCError) as exc_info:
with pytest.raises(RPCError) as exc_info:
env.get_gas_price()
assert str(exc_info.value) == "-32603: server error"


def test_browser_js_error(
token, browser, display_mock, mock_callback, account, mock_fork
):
def test_browser_js_error(token, display_mock, mock_callback, account, mock_fork):
mock_callback(
"eth_requestAccounts", error={"message": "custom message", "stack": ""}
)
with pytest.raises(browser.RPCError) as exc_info:
browser.BrowserSigner()
with pytest.raises(RPCError) as exc_info:
BrowserSigner()
assert str(exc_info.value) == "CALLBACK_ERROR: custom message"
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import pytest

from boa.integrations.jupyter.handlers import CallbackHandler, setup_handlers


@pytest.fixture()
def server_app_mock():
Expand All @@ -11,32 +13,24 @@ def server_app_mock():


@pytest.fixture()
def handlers():
from boa.integrations.jupyter import handlers

return handlers


@pytest.fixture()
def callback_handler(handlers):
handler = handlers.CallbackHandler()
handler.request = MagicMock()
def callback_handler(server_app_mock):
request_mock = MagicMock()
handler = CallbackHandler(server_app_mock.web_app, request_mock)
handler.current_user = MagicMock()
handler.set_status = MagicMock()
handler.finish = MagicMock()
return handler


def test_setup_handlers(handlers, server_app_mock):
handlers.setup_handlers(server_app_mock)
def test_setup_handlers(server_app_mock):
setup_handlers(server_app_mock)
server_app_mock.web_app.add_handlers.assert_called_once()
_, kwargs = server_app_mock.web_app.add_handlers.call_args
assert kwargs == {
"host_pattern": ".*$",
"host_handlers": [
(
"/base_url/titanoboa_jupyterlab/callback/(titanoboa_jupyterlab_[0-9a-fA-F]{64})$",
handlers.CallbackHandler,
CallbackHandler,
)
],
}
Expand All @@ -45,15 +39,15 @@ def test_setup_handlers(handlers, server_app_mock):
def test_no_body(callback_handler, token):
callback_handler.request.body = None
callback_handler.post(token)
callback_handler.set_status.assert_called_once_with(400)
assert callback_handler.get_status() == 400
callback_handler.finish.assert_called_once_with(
{"error": "Request body is required"}
)


def test_invalid_token(callback_handler, token):
callback_handler.post(token)
callback_handler.set_status.assert_called_once_with(404)
assert callback_handler.get_status() == 404
callback_handler.finish.assert_called_once_with(
{"error": "Invalid token: " + token}
)
Expand All @@ -62,7 +56,7 @@ def test_invalid_token(callback_handler, token):
def test_value_error(callback_handler, token, shared_memory, shared_memory_length):
callback_handler.request.body = b"0" * shared_memory_length # no space for the \0
callback_handler.post(token)
callback_handler.set_status.assert_called_once_with(413)
assert callback_handler.get_status() == 413
callback_handler.finish.assert_called_once_with(
{"error": "Request body has 51201 bytes, but only 51200 are allowed"}
)
Expand All @@ -71,5 +65,5 @@ def test_value_error(callback_handler, token, shared_memory, shared_memory_lengt
def test_success(callback_handler, token, shared_memory):
callback_handler.request.body = b"body"
callback_handler.post(token)
callback_handler.set_status.assert_called_once_with(204)
assert callback_handler.get_status() == 204
callback_handler.finish.assert_called_once_with()
12 changes: 0 additions & 12 deletions tests/unitary/jupyter/IPython/display.py

This file was deleted.

Empty file.
Empty file.
2 changes: 0 additions & 2 deletions tests/unitary/jupyter/jupyter_server/base/handlers.py

This file was deleted.

7 changes: 0 additions & 7 deletions tests/unitary/jupyter/jupyter_server/serverapp.py

This file was deleted.

7 changes: 0 additions & 7 deletions tests/unitary/jupyter/jupyter_server/utils.py

This file was deleted.

6 changes: 0 additions & 6 deletions tests/unitary/jupyter/nest_asyncio/__init__.py

This file was deleted.

Empty file.
7 changes: 0 additions & 7 deletions tests/unitary/jupyter/tornado/web.py

This file was deleted.

0 comments on commit ce96097

Please sign in to comment.