def testStorageAndHydraulicExports(self, project: _Project, request: _pt.FixtureRequest): helper = _Helper(project) helper.setup() # The following line is required otherwise QT will crash application = _qtw.QApplication([]) def quitApplication(): application.quit() request.addfinalizer(quitApplication) projectFolderPath = helper.actualProjectFolderPath self._exportHydraulic(projectFolderPath, _format="mfs") mfsDdckRelativePath = f"{project.projectName}_mfs.dck" helper.ensureFilesAreEqual(mfsDdckRelativePath, shallReplaceRandomizedFlowRates=True) self._exportHydraulic(projectFolderPath, _format="ddck") hydraulicDdckRelativePath = "ddck/hydraulic/hydraulic.ddck" helper.ensureFilesAreEqual(hydraulicDdckRelativePath, shallReplaceRandomizedFlowRates=False) storageTankNames = self._exportStorageTanksAndGetNames(projectFolderPath) for storageTankName in storageTankNames: ddckFileRelativePath = f"ddck/{storageTankName}/{storageTankName}.ddck" helper.ensureFilesAreEqual(ddckFileRelativePath, shallReplaceRandomizedFlowRates=False) ddcxFileRelativePath = f"ddck/{storageTankName}/{storageTankName}.ddcx" helper.ensureFilesAreEqual(ddcxFileRelativePath, shallReplaceRandomizedFlowRates=False)
def bokeh_server(request: pytest.FixtureRequest, log_file: IO[str]) -> str: bokeh_port: int = request.config.option.bokeh_port cmd = ["python", "-m", "bokeh", "serve"] argv = [f"--port={bokeh_port}"] bokeh_server_url = f"http://localhost:{bokeh_port}" env = os.environ.copy() env['BOKEH_MINIFIED'] = 'false' try: proc = subprocess.Popen(cmd + argv, env=env, stdout=log_file, stderr=log_file) except OSError: write(f"Failed to run: {' '.join(cmd + argv)}") sys.exit(1) else: # Add in the clean-up code def stop_bokeh_server() -> None: write("Shutting down bokeh-server ...") proc.kill() request.addfinalizer(stop_bokeh_server) def wait_until(func: Callable[[], Any], timeout: float = 5.0, interval: float = 0.01) -> bool: start = time.time() while True: if func(): return True if time.time() - start > timeout: return False time.sleep(interval) def wait_for_bokeh_server() -> bool: def helper() -> Any: if proc.returncode is not None: return True try: # type: ignore[unreachable] # XXX: typeshed bug, proc.returncode: int return requests.get(bokeh_server_url) except ConnectionError: return False return wait_until(helper) if not wait_for_bokeh_server(): write(f"Timeout when running: {' '.join(cmd + argv)}") sys.exit(1) if proc.returncode is not None: write(f"bokeh server exited with code {proc.returncode}") sys.exit(1) return bokeh_server_url # type: ignore[unreachable] # XXX: typeshed bug, proc.returncode: int
def remote_repos_path(user_path: pathlib.Path, request: pytest.FixtureRequest) -> pathlib.Path: """System's remote (file-based) repos to clone andpush to. Emphemeral directory.""" dir = user_path / "remote_repos" dir.mkdir(exist_ok=True) def clean() -> None: shutil.rmtree(dir) request.addfinalizer(clean) return dir
def projects_path(user_path: pathlib.Path, request: pytest.FixtureRequest) -> pathlib.Path: """User's local checkouts and clones. Emphemeral directory.""" dir = user_path / "projects" dir.mkdir(exist_ok=True) def clean() -> None: shutil.rmtree(dir) request.addfinalizer(clean) return dir
def screenshoter(browser: WebDriver, request: pytest.FixtureRequest): class Screenshoter: def save(self): if SCREENSHOTS_FOLDER is None: return assert browser.save_screenshot( f'{SCREENSHOTS_FOLDER}/{time.time_ns()}_{request.node.name}.png' ) instance = Screenshoter() request.addfinalizer(lambda: instance.save()) return instance
def test_file_path_and_url(request: pytest.FixtureRequest, file_server: SimpleWebServer) -> Tuple[str, str]: filename = request.function.__name__ + '.html' file_obj = request.fspath.dirpath().join(filename) file_path = file_obj.strpath url = file_path.replace('\\', '/') # Windows-proof def tear_down() -> None: if file_obj.isfile(): file_obj.remove() request.addfinalizer(tear_down) return file_path, file_server.where_is(url)
def redis_proc_fixture(request: FixtureRequest, tmp_path_factory: TempPathFactory): """ Fixture for pytest-redis. #. Get configs. #. Run redis process. #. Stop redis process after tests. :param request: fixture request object :param tmpdir_factory: :rtype: pytest_redis.executors.TCPExecutor :returns: tcp executor """ config = get_config(request) redis_exec = executable or config["exec"] rdbcompression = config[ "compression"] if compression is None else compression rdbchecksum = config["rdbchecksum"] if checksum is None else checksum if datadir: redis_datadir = Path(datadir) elif config["datadir"]: redis_datadir = Path(config["datadir"]) else: redis_datadir = tmp_path_factory.mktemp( f"pytest-redis-{request.fixturename}") redis_executor = RedisExecutor( executable=redis_exec, databases=db_count or config["db_count"], redis_timeout=timeout or config["timeout"], loglevel=loglevel or config["loglevel"], rdbcompression=rdbcompression, rdbchecksum=rdbchecksum, syslog_enabled=syslog or config["syslog"], save=save or config["save"], host=host or config["host"], port=get_port(port) or get_port(config["port"]), timeout=60, datadir=redis_datadir, ) redis_executor.start() request.addfinalizer(redis_executor.stop) return redis_executor
def output_file_url(request: pytest.FixtureRequest, file_server: SimpleWebServer) -> str: from bokeh.io import output_file filename = request.function.__name__ + '.html' file_obj = request.fspath.dirpath().join(filename) file_path = file_obj.strpath url = file_path.replace('\\', '/') # Windows-proof output_file(file_path, mode='inline') def tear_down() -> None: if file_obj.isfile(): file_obj.remove() request.addfinalizer(tear_down) return file_server.where_is(url)
def _push_request_context(request: pytest.FixtureRequest): """During tests execution request context has been pushed, e.g. `url_for`, `session`, etc. can be used in tests as is:: def test_app(app, client): assert client.get(url_for('myview')).status_code == 200 """ if "app" not in request.fixturenames: return app = request.getfixturevalue("app") ctx = app.test_request_context() ctx.push() def teardown(): ctx.pop() request.addfinalizer(teardown)
async def session_context( request: pytest.FixtureRequest, event_loop: AbstractEventLoop, ) -> contextvars.Context: if "nosession" not in request.keywords: await initialize_connection("spellbot-test", use_transaction=True) test_session = db_session_maker() DatabaseSession.set(test_session) # type: ignore BlockFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore ChannelFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore ConfigFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore GameFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore GuildAwardFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore GuildFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore PlayFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore UserAwardFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore UserFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore VerifyFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore WatchFactory._meta.sqlalchemy_session = DatabaseSession # type: ignore def cleanup_session(): async def finalizer() -> None: try: await rollback_transaction() except Exception: # pragma: no cover pass event_loop.run_until_complete(finalizer()) request.addfinalizer(cleanup_session) context = contextvars.copy_context() def cleanup_context(): nonlocal context for c in context: c.set(context[c]) request.addfinalizer(cleanup_context) return context
def reports_test_dir(request: pytest.FixtureRequest) -> str: """Returns the relative path to the test reports directory. Also, adds a finaliser function to remove the test reports directory and its contents once the test has completed. Args: request (pytest.FixtureRequest): A pytest fixture providing information of the requesting test function. Returns: str: The relative path to the test reports directory. """ # Declare relative path to the test reports directory # to be created in the relevant tests reports_test_dir_path = "./src/test/reports" # Add finalizer function to remove the test reports directory # and its contents once the test has finished def remove_reports_dir_contents(): shutil.rmtree(reports_test_dir_path) # Add finalizer function request.addfinalizer(remove_reports_dir_contents) return reports_test_dir_path
def file_server(request: pytest.FixtureRequest) -> SimpleWebServer: server = SimpleWebServer() server.start() request.addfinalizer(server.stop) return server
async def dashboard(redis_cache: redis_utils.RedisCache, request: pytest.FixtureRequest) -> DashboardFixture: is_unittest_class = request.cls is not None subscription_active = False marker = request.node.get_closest_marker("subscription") if marker: subscription_active = marker.args[0] elif is_unittest_class: subscription_active = request.cls.SUBSCRIPTION_ACTIVE api_key_admin = "a" * 64 sub = subscription.Subscription( redis_cache, config.TESTING_ORGANIZATION_ID, "You're not nice", frozenset( getattr(subscription.Features, f) for f in subscription.Features.__members__) if subscription_active else frozenset([subscription.Features.PUBLIC_REPOSITORY]), ) await sub._save_subscription_to_cache() user_tokens = user_tokens_mod.UserTokens( redis_cache, config.TESTING_ORGANIZATION_ID, [ { "id": github_types.GitHubAccountIdType(config.ORG_ADMIN_ID), "login": github_types.GitHubLogin("mergify-test1"), "oauth_access_token": config.ORG_ADMIN_GITHUB_APP_OAUTH_TOKEN, "name": None, "email": None, }, { "id": github_types.GitHubAccountIdType(config.ORG_USER_ID), "login": github_types.GitHubLogin("mergify-test4"), "oauth_access_token": config.ORG_USER_PERSONAL_TOKEN, "name": None, "email": None, }, ], ) await typing.cast(user_tokens_mod.UserTokensSaas, user_tokens).save_to_cache() real_get_subscription = subscription.Subscription.get_subscription async def fake_retrieve_subscription_from_db(redis_cache, owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return sub return subscription.Subscription( redis_cache, owner_id, "We're just testing", set(subscription.Features.PUBLIC_REPOSITORY), ) async def fake_subscription(redis_cache, owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return await real_get_subscription(redis_cache, owner_id) return subscription.Subscription( redis_cache, owner_id, "We're just testing", set(subscription.Features.PUBLIC_REPOSITORY), ) patcher = mock.patch( "mergify_engine.dashboard.subscription.Subscription._retrieve_subscription_from_db", side_effect=fake_retrieve_subscription_from_db, ) patcher.start() request.addfinalizer(patcher.stop) patcher = mock.patch( "mergify_engine.dashboard.subscription.Subscription.get_subscription", side_effect=fake_subscription, ) patcher.start() request.addfinalizer(patcher.stop) async def fake_retrieve_user_tokens_from_db(redis_cache, owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return user_tokens return user_tokens_mod.UserTokens(redis_cache, owner_id, {}) real_get_user_tokens = user_tokens_mod.UserTokens.get async def fake_user_tokens(redis_cache, owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return await real_get_user_tokens(redis_cache, owner_id) return user_tokens_mod.UserTokens(redis_cache, owner_id, {}) patcher = mock.patch( "mergify_engine.dashboard.user_tokens.UserTokensSaas._retrieve_from_db", side_effect=fake_retrieve_user_tokens_from_db, ) patcher.start() request.addfinalizer(patcher.stop) patcher = mock.patch( "mergify_engine.dashboard.user_tokens.UserTokensSaas.get", side_effect=fake_user_tokens, ) patcher.start() request.addfinalizer(patcher.stop) async def fake_application_get(redis_cache, api_access_key, api_secret_key, account_scope): if (api_access_key == api_key_admin[:32] and api_secret_key == api_key_admin[32:]): return application_mod.Application( redis_cache, 123, "testing application", api_access_key, api_secret_key, account_scope={ "id": config.TESTING_ORGANIZATION_ID, "login": config.TESTING_ORGANIZATION_NAME, }, ) raise application_mod.ApplicationUserNotFound() patcher = mock.patch( "mergify_engine.dashboard.application.ApplicationSaas.get", side_effect=fake_application_get, ) patcher.start() request.addfinalizer(patcher.stop) return DashboardFixture( api_key_admin, sub, user_tokens, )
async def recorder( request: pytest.FixtureRequest, ) -> typing.Optional[RecorderFixture]: is_unittest_class = request.cls is not None marker = request.node.get_closest_marker("recorder") if not is_unittest_class and marker is None: return None if is_unittest_class: cassette_library_dir = os.path.join( CASSETTE_LIBRARY_DIR_BASE, request.cls.__name__, request.node.name, ) else: cassette_library_dir = os.path.join( CASSETTE_LIBRARY_DIR_BASE, request.node.module.__name__.replace( "mergify_engine.tests.functional.", "").replace(".", "/"), request.node.name, ) # Recording stuffs if RECORD: if os.path.exists(cassette_library_dir): shutil.rmtree(cassette_library_dir) os.makedirs(cassette_library_dir) recorder = vcr.VCR( cassette_library_dir=cassette_library_dir, record_mode="all" if RECORD else "none", match_on=["method", "uri"], ignore_localhost=True, filter_headers=[ ("Authorization", "<TOKEN>"), ("X-Hub-Signature", "<SIGNATURE>"), ("User-Agent", None), ("Accept-Encoding", None), ("Connection", None), ], before_record_response=pyvcr_response_filter, before_record_request=pyvcr_request_filter, ) if RECORD: github.CachedToken.STORAGE = {} else: # Never expire token during replay patcher = mock.patch.object(github_app, "get_or_create_jwt", return_value="<TOKEN>") patcher.start() request.addfinalizer(patcher.stop) patcher = mock.patch.object( github.GithubAppInstallationAuth, "get_access_token", return_value="<TOKEN>", ) patcher.start() request.addfinalizer(patcher.stop) # Let's start recording cassette = recorder.use_cassette("http.json") cassette.__enter__() request.addfinalizer(cassette.__exit__) record_config_file = os.path.join(cassette_library_dir, "config.json") if RECORD: with open(record_config_file, "w") as f: f.write( json.dumps( RecordConfigType({ "organization_id": config.TESTING_ORGANIZATION_ID, "organization_name": config.TESTING_ORGANIZATION_NAME, "repository_id": config.TESTING_REPOSITORY_ID, "repository_name": github_types.GitHubRepositoryName( config.TESTING_REPOSITORY_NAME), "branch_prefix": datetime.datetime.utcnow().strftime("%Y%m%d%H%M%S"), }))) with open(record_config_file, "r") as f: return RecorderFixture( typing.cast(RecordConfigType, json.loads(f.read())), recorder)
def app(request: pytest.FixtureRequest) -> "SecurityFixture": app = SecurityFixture(__name__) app.response_class = Response app.debug = True app.config["SECRET_KEY"] = "secret" app.config["TESTING"] = True app.config["LOGIN_DISABLED"] = False app.config["WTF_CSRF_ENABLED"] = False # Our test emails/domain isn't necessarily valid app.config["SECURITY_EMAIL_VALIDATOR_ARGS"] = {"check_deliverability": False} app.config["SECURITY_TWO_FACTOR_SECRET"] = { "1": "TjQ9Qa31VOrfEzuPy4VHQWPCTmRzCnFzMKLxXYiZu9B" } app.config["SECURITY_SMS_SERVICE"] = "test" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False app.config["SECURITY_PASSWORD_SALT"] = "salty" # Make this plaintext for most tests - reduces unit test time by 50% app.config["SECURITY_PASSWORD_HASH"] = "plaintext" # Make this hex_md5 for token tests app.config["SECURITY_HASHING_SCHEMES"] = ["hex_md5"] app.config["SECURITY_DEPRECATED_HASHING_SCHEMES"] = [] for opt in [ "changeable", "recoverable", "registerable", "trackable", "passwordless", "confirmable", "two_factor", "unified_signin", "webauthn", ]: app.config["SECURITY_" + opt.upper()] = opt in request.keywords pytest_major = int(pytest.__version__.split(".")[0]) if pytest_major >= 4: marker_getter = request.node.get_closest_marker else: marker_getter = request.keywords.get settings = marker_getter("settings") if settings is not None: for key, value in settings.kwargs.items(): app.config["SECURITY_" + key.upper()] = value app.mail = Mail(app) # type: ignore app.json_encoder = JSONEncoder # use babel marker to signify tests that need babel extension. babel = marker_getter("babel") if babel: if NO_BABEL: raise pytest.skip("Requires Babel") Babel(app) @app.route("/") def index(): return render_template("index.html", content="Home Page") @app.route("/profile") @auth_required() def profile(): if hasattr(app, "security"): if app.security._want_json(flask_request): return jsonify(message="profile") return render_template("index.html", content="Profile Page") @app.route("/post_login") @login_required def post_login(): return render_template("index.html", content="Post Login") @app.route("/http") @http_auth_required def http(): return "HTTP Authentication" @app.route("/http_admin_required") @http_auth_required @permissions_required("admin") def http_admin_required(): assert get_request_attr("fs_authn_via") == "basic" return "HTTP Authentication" @app.route("/http_custom_realm") @http_auth_required("My Realm") def http_custom_realm(): assert get_request_attr("fs_authn_via") == "basic" return render_template("index.html", content="HTTP Authentication") @app.route("/token", methods=["GET", "POST"]) @auth_token_required def token(): assert get_request_attr("fs_authn_via") == "token" return render_template("index.html", content="Token Authentication") @app.route("/multi_auth") @auth_required("session", "token", "basic") def multi_auth(): return render_template("index.html", content="Session, Token, Basic auth") @app.route("/post_logout") def post_logout(): return render_template("index.html", content="Post Logout") @app.route("/post_register") def post_register(): return render_template("index.html", content="Post Register") @app.route("/post_confirm") def post_confirm(): return render_template("index.html", content="Post Confirm") @app.route("/admin") @roles_required("admin") def admin(): assert get_request_attr("fs_authn_via") == "session" return render_template("index.html", content="Admin Page") @app.route("/admin_and_editor") @roles_required("admin", "editor") def admin_and_editor(): return render_template("index.html", content="Admin and Editor Page") @app.route("/admin_or_editor") @roles_accepted("admin", "editor") def admin_or_editor(): return render_template("index.html", content="Admin or Editor Page") @app.route("/simple") @roles_accepted("simple") def simple(): return render_template("index.html", content="SimplePage") @app.route("/admin_perm") @permissions_accepted("full-write", "super") def admin_perm(): return render_template( "index.html", content="Admin Page with full-write or super" ) @app.route("/admin_perm_required") @permissions_required("full-write", "super") def admin_perm_required(): return render_template("index.html", content="Admin Page required") @app.route("/page1") def page_1(): return "Page 1" @app.route("/json", methods=["GET", "POST"]) def echo_json(): return jsonify(flask_request.get_json()) @app.route("/unauthz", methods=["GET", "POST"]) def unauthz(): return render_template("index.html", content="Unauthorized") @app.route("/fresh", methods=["GET", "POST"]) @auth_required(within=60) def fresh(): if app.security._want_json(flask_request): return jsonify(title="Fresh Only") else: return render_template("index.html", content="Fresh Only") def revert_forms(): # Some forms/tests have dynamic fields - be sure to revert them. if hasattr(app, "security"): if hasattr(app.security.login_form, "email"): del app.security.login_form.email if hasattr(app.security.register_form, "username"): del app.security.register_form.username if hasattr(app.security.confirm_register_form, "username"): del app.security.confirm_register_form.username request.addfinalizer(revert_forms) return app
def jupyter_notebook(request: pytest.FixtureRequest, log_file: IO[str]) -> str: """ Starts a jupyter notebook server at the beginning of a session, and closes at the end of a session. Adds custom.js that runs all the cells on notebook opening. Cleans out this custom.js at the end of the test run. Returns the url that the jupyter notebook is running at. """ # First - set-up the notebooks to run all cells when they're opened # # Can be cleaned up further to remember the user's existing customJS # and then restore it after the test run. from jupyter_core import paths config_dir = paths.jupyter_config_dir() body = """ require(["base/js/namespace", "base/js/events"], function (IPython, events) { events.on("kernel_ready.Kernel", function () { IPython.notebook.execute_all_cells(); }); }); """ custom = join(config_dir, "custom") if not exists(custom): os.makedirs(custom) customjs = join(custom, "custom.js") old_customjs = None if exists(customjs): with open(customjs) as f: old_customjs = f.read() with open(customjs, "w") as f: f.write(body) # Add in the clean-up code def clean_up_customjs() -> None: text = old_customjs if old_customjs is not None else "" with open(customjs, "w") as f: f.write(text) request.addfinalizer(clean_up_customjs) # Second - Run a notebook server at the examples directory # notebook_port = request.config.option.notebook_port env = os.environ.copy() env['BOKEH_RESOURCES'] = 'server' # Launch from the base directory of bokeh repo notebook_dir = join(dirname(__file__), pardir, pardir) cmd = ["jupyter", "notebook"] argv = [ "--no-browser", f"--port={notebook_port}", f"--notebook-dir={notebook_dir}" ] jupter_notebook_url = f"http://localhost:{notebook_port}" try: proc = subprocess.Popen(cmd + argv, env=env, stdout=log_file, stderr=log_file) except OSError: write(f"Failed to run: {' '.join(cmd + argv)}") sys.exit(1) else: # Add in the clean-up code def stop_jupyter_notebook() -> None: write("Shutting down jupyter-notebook ...") proc.kill() request.addfinalizer(stop_jupyter_notebook) def wait_until(func: Callable[[], Any], timeout: float = 5.0, interval: float = 0.01) -> bool: start = time.time() while True: if func(): return True if time.time() - start > timeout: return False time.sleep(interval) def wait_for_jupyter_notebook() -> bool: def helper() -> Any: if proc.returncode is not None: return True try: # type: ignore[unreachable] # XXX: typeshed bug, proc.returncode: int return requests.get(jupter_notebook_url) except ConnectionError: return False return wait_until(helper) if not wait_for_jupyter_notebook(): write(f"Timeout when running: {' '.join(cmd + argv)}") sys.exit(1) if proc.returncode is not None: write(f"Jupyter notebook exited with code {proc.returncode}") sys.exit(1) return jupter_notebook_url # type: ignore[unreachable] # XXX: typeshed bug, proc.returncode: int