"""Test to ensure that bootstrapping a new instance works with the fietsupdate script, as described in the documentation. """ import configparser import contextlib import logging import os import shutil import subprocess import venv from pathlib import Path import sqlalchemy LOGGER = logging.getLogger(__name__) REPO_BASE = Path(__file__).parent.parent.parent @contextlib.contextmanager def chdir(path: Path): """Change path as context manager. Changes back to the previous path on ``__exit__``. """ cwd = Path.cwd() os.chdir(path) try: yield finally: os.chdir(cwd) def install_fietsboek(venv_path: Path) -> Path: """Creates a virtual env in the given path and installs fietsboek. :param venv_path: Path in which the virtual environment should be created. :return: The path to the ``bin`` directory in which all scripts reside. """ venv.create(venv_path, with_pip=True) subprocess.check_call([venv_path / "bin" / "pip", "install", str(REPO_BASE)]) return venv_path / "bin" def create_config(config_name: Path): """Copies the testing.ini config and fills in the placeholders. :param config_name: Path to the resulting config. """ shutil.copy(REPO_BASE / "testing.ini", config_name) config = config_name.read_text() config = config.replace("# %% fietsboek.data_dir %%", "fietsboek.data_dir = %(here)s/data") config_name.write_text(config) Path("data").mkdir() def cleanup_database(config_name: Path): """Connects to the database and ensures everything is reset. :param config_name: Path to the config file. """ if not config_name.exists(): return parser = configparser.ConfigParser() parser["DEFAULT"]["here"] = str(config_name.parent) parser.read(config_name) db_url = parser["app:main"]["sqlalchemy.url"] engine = sqlalchemy.create_engine(db_url) match engine.name: case "sqlite": pass case "postgresql": with engine.connect() as connection: connection.execute(sqlalchemy.text("DROP SCHEMA public CASCADE;")) connection.execute(sqlalchemy.text("CREATE SCHEMA public;")) connection.commit() def test_setup_via_fietsupdate(tmpdir): with chdir(tmpdir): try: # We create a new temporary virtual environment with a fresh install, just # to be sure there's as little interference as possible. LOGGER.info("Installing Fietsboek into clean env") binaries_path = install_fietsboek(tmpdir / "venv") LOGGER.info("Installing additional SQL engines") subprocess.check_call( [binaries_path / "pip", "install", "psycopg2"] ) LOGGER.info("Creating a test configuration") create_config(Path("testing.ini")) # Try to run the migrations subprocess.check_call( [binaries_path / "fietsupdate", "update", "-c", "testing.ini", "-f"] ) # Also try to add an administrator subprocess.check_call([ binaries_path / "fietsctl", "user", "add", "-c", "testing.ini", "--email", "foobar@example.com", "--name", "Foo Bar", "--password", "raboof", "--admin", ]) assert True finally: # Clean up the database. This is important with anything but SQLite, as # the tables would otherwise persist and interfere with the other tests. cleanup_database(Path("testing.ini"))