2018-11-04 19:23:11 +00:00
|
|
|
"""
|
|
|
|
This file is part of nucypher.
|
|
|
|
|
|
|
|
nucypher is free software: you can redistribute it and/or modify
|
2019-03-05 02:50:11 +00:00
|
|
|
it under the terms of the GNU Affero General Public License as published by
|
2018-11-04 19:23:11 +00:00
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
nucypher is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2019-03-05 02:50:11 +00:00
|
|
|
GNU Affero General Public License for more details.
|
2018-11-04 19:23:11 +00:00
|
|
|
|
2019-03-05 02:50:11 +00:00
|
|
|
You should have received a copy of the GNU Affero General Public License
|
2018-11-04 19:23:11 +00:00
|
|
|
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
"""
|
2020-12-10 00:49:29 +00:00
|
|
|
|
2020-08-10 23:59:13 +00:00
|
|
|
from collections import defaultdict
|
2018-11-03 21:48:11 +00:00
|
|
|
|
2021-01-03 00:52:18 +00:00
|
|
|
import lmdb
|
2018-11-09 15:25:20 +00:00
|
|
|
import pytest
|
|
|
|
|
2019-02-26 04:46:03 +00:00
|
|
|
from nucypher.characters.control.emitters import WebEmitter
|
2019-08-31 04:05:57 +00:00
|
|
|
from nucypher.crypto.powers import TransactingPower
|
2020-12-11 18:29:12 +00:00
|
|
|
from nucypher.network.nodes import Learner
|
2020-04-04 00:39:00 +00:00
|
|
|
from nucypher.network.trackers import AvailabilityTracker
|
2019-07-18 03:50:11 +00:00
|
|
|
from nucypher.utilities.logging import GlobalLoggerSettings
|
2020-05-13 18:24:36 +00:00
|
|
|
from tests.constants import INSECURE_DEVELOPMENT_PASSWORD
|
2021-01-03 00:52:18 +00:00
|
|
|
from tests.mock.datastore import mock_lmdb_open
|
2019-12-17 02:21:49 +00:00
|
|
|
|
2019-02-22 05:38:54 +00:00
|
|
|
# Crash on server error by default
|
2019-06-12 18:14:07 +00:00
|
|
|
WebEmitter._crash_on_error_default = True
|
2019-02-26 04:48:31 +00:00
|
|
|
|
2020-05-13 17:33:39 +00:00
|
|
|
# Dont re-lock account in background while making commitments
|
2019-08-31 16:52:56 +00:00
|
|
|
LOCK_FUNCTION = TransactingPower.lock_account
|
|
|
|
TransactingPower.lock_account = lambda *a, **k: True
|
2019-08-31 04:05:57 +00:00
|
|
|
|
2020-03-31 17:48:25 +00:00
|
|
|
# Prevent halting the reactor via health checks during tests
|
2020-04-04 00:39:00 +00:00
|
|
|
AvailabilityTracker._halt_reactor = lambda *a, **kw: True
|
2020-03-31 17:48:25 +00:00
|
|
|
|
2020-08-10 23:59:13 +00:00
|
|
|
# Global test character cache
|
|
|
|
global_mutable_where_everybody = defaultdict(list)
|
|
|
|
|
2020-08-27 21:46:15 +00:00
|
|
|
Learner._DEBUG_MODE = False
|
2020-08-27 20:59:51 +00:00
|
|
|
|
2020-12-11 18:29:12 +00:00
|
|
|
|
2019-02-26 04:48:31 +00:00
|
|
|
@pytest.fixture(autouse=True, scope='session')
|
|
|
|
def __very_pretty_and_insecure_scrypt_do_not_use():
|
|
|
|
"""
|
|
|
|
# WARNING: DO NOT USE THIS CODE ANYWHERE #
|
|
|
|
|
|
|
|
Mocks Scrypt derivation function for the duration of
|
|
|
|
the test session in order to improve test performance.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Capture Scrypt derivation method
|
|
|
|
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
|
|
|
|
original_derivation_function = Scrypt.derive
|
|
|
|
|
|
|
|
# One-Time Insecure Password
|
2019-03-04 22:10:12 +00:00
|
|
|
insecure_password = bytes(INSECURE_DEVELOPMENT_PASSWORD, encoding='utf8')
|
2019-02-26 04:48:31 +00:00
|
|
|
|
|
|
|
# Patch Method
|
|
|
|
def __insecure_derive(*args, **kwargs):
|
|
|
|
"""Temporarily replaces Scrypt.derive for mocking"""
|
|
|
|
return insecure_password
|
|
|
|
|
|
|
|
# Disable Scrypt KDF
|
|
|
|
Scrypt.derive = __insecure_derive
|
|
|
|
yield
|
|
|
|
# Re-Enable Scrypt KDF
|
|
|
|
Scrypt.derive = original_derivation_function
|
|
|
|
|
2019-02-22 05:38:54 +00:00
|
|
|
|
2021-01-03 00:52:18 +00:00
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def monkeysession():
|
2020-05-17 20:06:41 +00:00
|
|
|
from _pytest.monkeypatch import MonkeyPatch
|
|
|
|
mpatch = MonkeyPatch()
|
|
|
|
yield mpatch
|
|
|
|
mpatch.undo()
|
|
|
|
|
|
|
|
|
2018-11-20 04:29:01 +00:00
|
|
|
#
|
2018-11-09 15:25:20 +00:00
|
|
|
# Pytest configuration
|
2018-11-20 04:29:01 +00:00
|
|
|
#
|
|
|
|
|
2018-11-09 15:25:20 +00:00
|
|
|
pytest_plugins = [
|
2018-12-07 02:19:57 +00:00
|
|
|
'tests.fixtures', # Includes external fixtures module
|
2018-11-09 15:25:20 +00:00
|
|
|
]
|
2018-06-04 18:00:08 +00:00
|
|
|
|
|
|
|
|
2018-05-07 02:11:20 +00:00
|
|
|
def pytest_addoption(parser):
|
2019-09-30 21:05:07 +00:00
|
|
|
parser.addoption("--run-nightly",
|
|
|
|
action="store_true",
|
|
|
|
default=False,
|
|
|
|
help="run tests even if they are marked as nightly")
|
|
|
|
|
2020-08-27 20:59:51 +00:00
|
|
|
# class SetLearnerDebugMode((argparse.Action)):
|
|
|
|
# def __call__(self, *args, **kwargs):
|
|
|
|
# from nucypher.network.nodes import Learner
|
|
|
|
# Learner._DEBUG_MODE = True
|
|
|
|
|
|
|
|
# parser.addoption("--track-character-lifecycles",
|
|
|
|
# action=SetLearnerDebugMode,
|
|
|
|
# default=False,
|
|
|
|
# help="Track characters in a global... mutable... where everybody...")
|
2020-08-23 16:05:42 +00:00
|
|
|
|
2019-09-30 21:05:07 +00:00
|
|
|
|
|
|
|
def pytest_configure(config):
|
|
|
|
message = "{0}: mark test as {0} to run (skipped by default, use '{1}' to include these tests)"
|
|
|
|
config.addinivalue_line("markers", message.format("nightly", "--run-nightly"))
|
2018-05-07 02:11:20 +00:00
|
|
|
|
2018-06-04 18:00:08 +00:00
|
|
|
|
2018-05-07 02:11:20 +00:00
|
|
|
def pytest_collection_modifyitems(config, items):
|
2019-05-31 20:41:56 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# Handle slow tests marker
|
|
|
|
#
|
|
|
|
|
2019-09-30 21:05:07 +00:00
|
|
|
option_markers = {
|
|
|
|
"--run-nightly": "nightly"
|
|
|
|
}
|
|
|
|
|
|
|
|
for option, marker in option_markers.items():
|
|
|
|
option_is_set = config.getoption(option)
|
|
|
|
if option_is_set:
|
|
|
|
continue
|
2019-05-31 20:41:56 +00:00
|
|
|
|
2019-09-30 21:05:07 +00:00
|
|
|
skip_reason = pytest.mark.skip(reason=f"need {option} option to run tests marked with '@pytest.mark.{marker}'")
|
2018-11-17 19:27:53 +00:00
|
|
|
for item in items:
|
2019-09-30 21:05:07 +00:00
|
|
|
if marker in item.keywords:
|
|
|
|
item.add_marker(skip_reason)
|
2019-05-31 20:41:56 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# Handle Log Level
|
|
|
|
#
|
|
|
|
|
2018-11-17 19:27:53 +00:00
|
|
|
log_level_name = config.getoption("--log-level", "info", skip=True)
|
2019-05-31 20:41:56 +00:00
|
|
|
|
2019-11-07 08:31:02 +00:00
|
|
|
GlobalLoggerSettings.stop_sentry_logging()
|
2019-07-18 03:50:11 +00:00
|
|
|
GlobalLoggerSettings.set_log_level(log_level_name)
|
|
|
|
GlobalLoggerSettings.start_text_file_logging()
|
|
|
|
GlobalLoggerSettings.start_json_file_logging()
|
2020-08-07 20:47:30 +00:00
|
|
|
|
|
|
|
|
2020-12-12 00:41:24 +00:00
|
|
|
# global_mutable_where_everybody = defaultdict(list) # TODO: cleanup
|
2020-08-07 20:47:30 +00:00
|
|
|
|
|
|
|
@pytest.fixture(scope='module', autouse=True)
|
|
|
|
def check_character_state_after_test(request):
|
2020-08-24 14:07:40 +00:00
|
|
|
from nucypher.network.nodes import Learner
|
2020-08-07 20:47:30 +00:00
|
|
|
yield
|
2020-08-24 14:07:40 +00:00
|
|
|
if Learner._DEBUG_MODE:
|
|
|
|
gmwe = global_mutable_where_everybody
|
|
|
|
module_name = request.module.__name__
|
|
|
|
|
|
|
|
test_learners = global_mutable_where_everybody.get(module_name, [])
|
|
|
|
# Those match the module name exactly; maybe there are some that we got by frame.
|
|
|
|
for maybe_frame, learners in global_mutable_where_everybody.items():
|
|
|
|
if f"{module_name}.py" in maybe_frame:
|
|
|
|
test_learners.extend(learners)
|
|
|
|
|
|
|
|
crashed = [learner for learner in test_learners if learner._crashed]
|
|
|
|
|
|
|
|
if any(crashed):
|
|
|
|
failure_message = ""
|
|
|
|
for learner in crashed:
|
|
|
|
failure_message += learner._crashed.getBriefTraceback()
|
|
|
|
pytest.fail(f"Some learners crashed:{failure_message}")
|
|
|
|
|
|
|
|
still_running = [learner for learner in test_learners if learner._learning_task.running]
|
|
|
|
|
|
|
|
if any(still_running):
|
|
|
|
offending_tests = set()
|
|
|
|
for learner in still_running:
|
|
|
|
offending_tests.add(learner._FOR_TEST)
|
|
|
|
try: # TODO: Deal with stop vs disenchant. Currently stop is only for Ursula.
|
|
|
|
learner.stop()
|
2020-09-22 05:21:07 +00:00
|
|
|
learner._finalize()
|
2020-08-24 14:07:40 +00:00
|
|
|
except AttributeError:
|
|
|
|
learner.disenchant()
|
|
|
|
pytest.fail(f"Learners remaining: {still_running}. Offending tests: {offending_tests} ")
|
2020-08-27 20:59:51 +00:00
|
|
|
|
|
|
|
still_tracking = [learner for learner in test_learners if hasattr(learner, 'work_tracker') and learner.work_tracker._tracking_task.running]
|
|
|
|
for tracker in still_tracking:
|
|
|
|
tracker.work_tracker.stop()
|
2020-12-10 00:49:29 +00:00
|
|
|
|
|
|
|
|
2020-12-12 01:16:14 +00:00
|
|
|
|
2021-01-03 00:52:18 +00:00
|
|
|
@pytest.fixture(scope='session', autouse=True)
|
|
|
|
def mock_datastore(monkeysession):
|
|
|
|
monkeysession.setattr(lmdb, 'open', mock_lmdb_open)
|
|
|
|
yield
|
2020-12-12 01:16:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
def mock_get_external_ip_from_url_source(session_mocker):
|
|
|
|
"""Prevent tests from making a call to third party external networks"""
|
|
|
|
target = 'nucypher.utilities.networking.get_external_ip_from_centralized_source'
|
|
|
|
session_mocker.patch(target, return_value=None)
|