From bbc373bd2e9617c8da829b3a180e9c42f139a380 Mon Sep 17 00:00:00 2001 From: Max Nanis Date: Thu, 10 Sep 2026 10:27:51 -0700 Subject: Less from init, more from generalresearch, basic db test from shared conftest --- tests/test_postgres.py | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/test_postgres.py (limited to 'tests/test_postgres.py') diff --git a/tests/test_postgres.py b/tests/test_postgres.py new file mode 100644 index 0000000..6db4f82 --- /dev/null +++ b/tests/test_postgres.py @@ -0,0 +1,53 @@ +import socket +import subprocess +from collections.abc import Callable +from typing import TYPE_CHECKING + +from generalresearch.pg_helper import PostgresConfig +from pydantic import PostgresDsn + +if TYPE_CHECKING: + from generalresearch.models.custom_types import InternalHostname, PostgresDict + + +def is_port_open(host: InternalHostname, port: int = 5432, timeout: int = 3): + try: + with socket.create_connection((host, port), timeout=timeout): + return True + except (TimeoutError, ConnectionRefusedError, OSError): + return False + + +def can_ping(host: InternalHostname): + return ( + subprocess.call( + ["ping", "-c", "1", str(host)], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + == 0 + ) + + +class TestPostgresDSN: + + def test_ping(self, postgres_instance_host: InternalHostname): + assert can_ping(host=postgres_instance_host) + + def test_port(self, postgres_instance_host: InternalHostname): + assert is_port_open(host=postgres_instance_host) + + def test_conn(self, postgres_instance: PostgresDsn): + config = PostgresConfig( + dsn=postgres_instance, + connect_timeout=1, + statement_timeout=1, + ) + res = config.execute_sql_query(query="SELECT 1;") + assert len(res) == 1 + + +class TestPostgresDjangoCreation: + + def test_ping(self, postgres_instance_dict: PostgresDict): + assert can_ping(host=postgres_instance_dict["host"]) -- cgit v1.2.3 From 26167c923448fae157f5134350e771928ad09b0b Mon Sep 17 00:00:00 2001 From: Max Nanis Date: Thu, 10 Sep 2026 11:17:07 -0700 Subject: basic tests should be working. Jenkins p1 --- Jenkinsfile | 88 ++++----------- carer/app/__init__.py | 0 carer/app/manage.py | 9 ++ carer/app/settings.py | 24 ++++ carer/apps.py | 9 ++ carer/carer/__init__.py | 0 carer/carer/mtwerk/__init__.py | 0 carer/carer/mtwerk/migrations/0001_initial.py | 157 -------------------------- carer/carer/mtwerk/migrations/__init__.py | 0 carer/carer/mtwerk/models.py | 130 --------------------- carer/carer/settings/base.py | 18 --- carer/carer/settings/unittest.py | 30 ----- carer/manage.py | 22 ---- carer/mtwerk/__init__.py | 0 carer/mtwerk/migrations/0001_initial.py | 157 ++++++++++++++++++++++++++ carer/mtwerk/migrations/__init__.py | 0 carer/mtwerk/models.py | 130 +++++++++++++++++++++ tests/conftest.py | 153 +++++++++++++++++++++++-- tests/test_postgres.py | 16 +++ 19 files changed, 508 insertions(+), 435 deletions(-) create mode 100644 carer/app/__init__.py create mode 100644 carer/app/manage.py create mode 100644 carer/app/settings.py create mode 100644 carer/apps.py delete mode 100644 carer/carer/__init__.py delete mode 100644 carer/carer/mtwerk/__init__.py delete mode 100644 carer/carer/mtwerk/migrations/0001_initial.py delete mode 100644 carer/carer/mtwerk/migrations/__init__.py delete mode 100644 carer/carer/mtwerk/models.py delete mode 100644 carer/carer/settings/base.py delete mode 100644 carer/carer/settings/unittest.py delete mode 100644 carer/manage.py create mode 100644 carer/mtwerk/__init__.py create mode 100644 carer/mtwerk/migrations/0001_initial.py create mode 100644 carer/mtwerk/migrations/__init__.py create mode 100644 carer/mtwerk/models.py (limited to 'tests/test_postgres.py') diff --git a/Jenkinsfile b/Jenkinsfile index eb53bfd..0632ee8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -10,95 +10,47 @@ pipeline { pollSCM('H */3 * * *') } + options { + skipDefaultCheckout() + } + environment { DATA_SRC = "${env.WORKSPACE}/mnt/" - - AMT_JB_CARER_VENV = "${env.WORKSPACE}/amt-jb-carer-venv" - AMT_JB_VENV = "${env.WORKSPACE}/amt-jb-venv" + VENV = "${env.WORKSPACE}/amt-jb-venv" } stages { - stage('Setup DB') { - steps { - script { - env.DB_NAME = 'unittest-amt-jb-' + UUID.randomUUID().toString().replace('-', '').take(12) - env.AMT_JB_DB = "postgres://${env.DB_USER}:${env.DB_PASSWORD}@${env.DB_POSTGRESQL_HOST}/${env.DB_NAME}" - echo "Using database: ${env.DB_NAME}" - } - sh """ - PGPASSWORD=${env.DB_PASSWORD} psql -h ${env.DB_POSTGRESQL_HOST} -U ${env.DB_USER} -d postgres < Settings: # --- Database Connectors --- +@pytest.fixture(scope="session") +def postgres_instance(settings: Settings) -> Generator[PostgresDsn]: + """Create a ephemeral postgresql instance for us to use during pytest. + + This does not create any tables, or schema definitions within the instance. + What this does is simply: + + 1. Create a database on a known, consistent, staging or unittest + defined Postgres server. + + 2. Return the PostgresDsn of that table + + 3. On shutdown, go ahead and delete that database after the + tests have finished. + """ + + msg = "Must define Postgres test settings" + assert settings.testing_postgres, msg + assert settings.testing_postgres_user, msg + assert settings.testing_postgres_pass, msg + + db_uri, db_user, db_pass = ( + settings.testing_postgres, + settings.testing_postgres_user, + settings.testing_postgres_pass, + ) + + # Connect to default DB to create the new one + from psycopg import connect + from psycopg.sql import SQL, Identifier + + now = datetime.now(UTC) + ts: str = now.strftime("%Y-%m-%d") + db_name = f"unittest-{ts}-{uuid4().hex[:6]}" + + db_path_connect = f"postgres://{db_user}:{db_pass}@{db_uri}" + db_path = f"{db_path_connect}/{db_name}" + + # The DATABASE does NOT yet exist on the Postgres SERVER, thus + # we first must connect only to the SERVER (eg: default postgres path used) + conn = connect(f"{db_path_connect}/postgres") + conn.autocommit = True + cur = conn.cursor() + cur.execute(SQL("CREATE DATABASE {}").format(Identifier(db_name))) + cur.close() + conn.close() + + yield PostgresDsn(db_path) + + # Teardown: drop the DB after the session + conn = connect(f"{db_path_connect}/postgres") + conn.autocommit = True + cur = conn.cursor() + cur.execute(SQL("DROP DATABASE {} WITH (FORCE)").format(Identifier(db_name))) + cur.close() + conn.close() + + +@pytest.fixture(scope="session") +def django_settings_file( + postgres_instance_dict: PostgresDict, + tmp_path_factory: TempPathFactory, +) -> Callable[..., tuple[str, Path]]: + + def _inner(extra_installed_apps: list[str] | None = None) -> tuple[str, Path]: + installed_apps = [ + "django.contrib.postgres", + "django.contrib.contenttypes", + ] + (extra_installed_apps or []) + + settings_dir = tmp_path_factory.mktemp("django-settings") + settings_module = "test_settings" + + settings_content = f"""DATABASES = {{ + "default": {{ + "ENGINE": "django.db.backends.postgresql", + "NAME": {postgres_instance_dict["name"]!r}, + "USER": {postgres_instance_dict["username"]!r}, + "PASSWORD": {postgres_instance_dict["password"]!r}, + "HOST": {postgres_instance_dict["host"]!r}, + "PORT": {postgres_instance_dict["port"]!r}, + }} +}} +INSTALLED_APPS = {installed_apps!r} +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" +LANGUAGE_CODE = "en-us" +TIME_ZONE = "UTC" +USE_I18N = True +USE_L10N = True +USE_TZ = True +""" + settings_file_path = settings_dir / f"{settings_module}.py" + settings_file_path.write_text(settings_content, encoding="utf-8") + + return settings_module, settings_dir + + return _inner + + +@pytest.fixture(scope="session") +def postgres_instance_dict( + postgres_instance: PostgresDsn, +) -> Generator[PostgresDict]: + host = postgres_instance.hosts()[0] + assert host is not None + + msg = "Must have full Postgres details" + assert host["host"], msg + assert host["username"], msg + assert host["password"], msg + + assert postgres_instance.path + + yield PostgresDict( + username=host["username"], + password=host["password"], + host=host["host"], + name=postgres_instance.path.lstrip("/"), + port=5432, + ) + + +@pytest.fixture(scope="session") +def postgres_instance_host( + postgres_instance_dict: PostgresDict, +) -> Generator[InternalHostname]: + adapter = TypeAdapter(InternalHostname) + value = adapter.validate_python(postgres_instance_dict["host"]) + yield value + + @pytest.fixture(scope="session") def django_db_factory( postgres_instance: PostgresDsn, - gr_repo: Callable[..., Path], django_settings_file: Callable[..., tuple[str, Path]], postgres_instance_dict: PostgresDict, tmp_path_factory: TempPathFactory, @@ -105,7 +236,7 @@ def django_db_factory( _ran = {} def _inner( - django_project: str = "generalresearch.thl_django", + django_project: str = "carer.mtwerk", ) -> PostgresDsn | None: if _ran.get(django_project, False): @@ -117,7 +248,7 @@ def django_db_factory( _manage_path = "generalresearch.thl_django.app.manage" _settings_module, _settings_dir = django_settings_file( extra_installed_apps=[ - "generalresearch.thl_django", + "carer.mtwerk", ], ) @@ -177,11 +308,13 @@ def django_db_factory( @pytest.fixture(scope="session") -def pg_config(settings: Settings) -> PostgresConfig: +def pg_config(django_db_factory: Callable[..., PostgresDsn]) -> PostgresConfig: + _dsn = django_db_factory() + return PostgresConfig( - dsn=settings.amt_jb_db, + dsn=_dsn, connect_timeout=1, - statement_timeout=1, + statement_timeout=5, ) @@ -193,7 +326,7 @@ def redis(settings: Settings): from generalresearch.redis_helper import RedisConfig redis_config = RedisConfig( - dsn=settings.redis, + dsn=settings.testing_redis, decode_responses=True, socket_timeout=settings.redis_timeout, socket_connect_timeout=settings.redis_timeout, diff --git a/tests/test_postgres.py b/tests/test_postgres.py index 6db4f82..244c132 100644 --- a/tests/test_postgres.py +++ b/tests/test_postgres.py @@ -51,3 +51,19 @@ class TestPostgresDjangoCreation: def test_ping(self, postgres_instance_dict: PostgresDict): assert can_ping(host=postgres_instance_dict["host"]) + + def test_django_creation( + self, + django_db_factory: Callable[..., None], + ): + dsn = django_db_factory() + assert isinstance(dsn, PostgresDsn) + + def test_django_tables(self, pg_config: PostgresConfig): + res = pg_config.execute_sql_query(query=""" + SELECT COUNT(*) + FROM information_schema.tables + WHERE table_schema = 'public'; + """) + assert len(res) == 1 + assert res[0]["count"] == 7 -- cgit v1.2.3