diff --git a/.github/workflows/deploy-gcp.yml b/.github/workflows/deploy-gcp.yml index d8af220d..411fb539 100644 --- a/.github/workflows/deploy-gcp.yml +++ b/.github/workflows/deploy-gcp.yml @@ -48,8 +48,51 @@ env: REGION: europe-west6 # Zurich region jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Determine environment + id: env + run: | + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + ENV_TYPE="${{ github.event.inputs.environment }}" + elif [ "${{ github.ref }}" == "refs/heads/int" ]; then + ENV_TYPE="int" + else + ENV_TYPE="prod" + fi + echo "env_file=env-gateway-${ENV_TYPE}.env" >> $GITHUB_OUTPUT + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.11' + + - name: Set environment file + run: | + ENV_FILE="${{ steps.env.outputs.env_file }}" + test -f "$ENV_FILE" + cp "$ENV_FILE" .env + rm -f env-gateway-dev.env env-gateway-int.env env-gateway-prod.env env-gateway-prod-forgejo.env + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + if [ -f requirements.lock ]; then + pip install -r requirements.lock --no-cache-dir + else + pip install -r requirements.txt --no-cache-dir + fi + + - name: Run tests + run: python -m pytest tests/ --ignore=tests/demo + deploy: runs-on: ubuntu-latest + needs: test permissions: contents: read id-token: write # Required for Workload Identity Federation diff --git a/.github/workflows/int_gateway-int.yml b/.github/workflows/int_gateway-int.yml index 0ea8ea9d..a896c0a7 100644 --- a/.github/workflows/int_gateway-int.yml +++ b/.github/workflows/int_gateway-int.yml @@ -16,8 +16,37 @@ concurrency: cancel-in-progress: true jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Set up Python version + uses: actions/setup-python@v6 + with: + python-version: '3.11' + + - name: Set environment file + run: | + test -f env-gateway-int.env + cp env-gateway-int.env .env + rm -f env-gateway-dev.env env-gateway-int.env env-gateway-prod.env env-gateway-prod-forgejo.env + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + if [ -f requirements.lock ]; then + pip install -r requirements.lock --no-cache-dir + else + pip install -r requirements.txt --no-cache-dir + fi + + - name: Run tests + run: python -m pytest tests/ --ignore=tests/demo + build: runs-on: ubuntu-latest + needs: test permissions: contents: read #This is required for actions/checkout @@ -43,8 +72,6 @@ jobs: pip install -r requirements.txt --no-cache-dir fi - # Optional: Add step to run tests here (PyTest, Django test suites, etc.) - - name: Zip artifact for deployment run: zip release.zip ./* -r diff --git a/.github/workflows/main_gateway-prod.yml b/.github/workflows/main_gateway-prod.yml index 6634091f..018a2179 100644 --- a/.github/workflows/main_gateway-prod.yml +++ b/.github/workflows/main_gateway-prod.yml @@ -16,8 +16,37 @@ concurrency: cancel-in-progress: true jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Set up Python version + uses: actions/setup-python@v6 + with: + python-version: '3.11' + + - name: Set environment file + run: | + test -f env-gateway-prod.env + cp env-gateway-prod.env .env + rm -f env-gateway-dev.env env-gateway-int.env env-gateway-prod.env env-gateway-prod-forgejo.env + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + if [ -f requirements.lock ]; then + pip install -r requirements.lock --no-cache-dir + else + pip install -r requirements.txt --no-cache-dir + fi + + - name: Run tests + run: python -m pytest tests/ --ignore=tests/demo + build: runs-on: ubuntu-latest + needs: test permissions: contents: read #This is required for actions/checkout @@ -43,8 +72,6 @@ jobs: pip install -r requirements.txt --no-cache-dir fi - # Optional: Add step to run tests here (PyTest, Django test suites, etc.) - - name: Zip artifact for deployment run: zip release.zip ./* -r diff --git a/.github/workflows/update-requirements-lock.yml b/.github/workflows/update-requirements-lock.yml index b3961874..3d21839f 100644 --- a/.github/workflows/update-requirements-lock.yml +++ b/.github/workflows/update-requirements-lock.yml @@ -38,6 +38,25 @@ jobs: - name: Generate requirements.lock run: pip-compile requirements.txt -o requirements.lock + - name: Set environment file + run: | + if [ "${{ github.ref }}" == "refs/heads/int" ]; then + ENV_FILE="env-gateway-int.env" + else + ENV_FILE="env-gateway-prod.env" + fi + test -f "$ENV_FILE" + cp "$ENV_FILE" .env + + - name: Install dependencies from generated lock + run: pip install -r requirements.lock --no-cache-dir + + - name: Run tests + run: python -m pytest tests/ --ignore=tests/demo + + - name: Clean up .env before commit + run: rm -f .env + - name: Commit and push requirements.lock run: | git config user.name "github-actions[bot]" diff --git a/tests/unit/connectors/test_connectorDbPostgre_pool.py b/tests/unit/connectors/test_connectorDbPostgre_pool.py index 9c389add..1d8d5d1d 100644 --- a/tests/unit/connectors/test_connectorDbPostgre_pool.py +++ b/tests/unit/connectors/test_connectorDbPostgre_pool.py @@ -18,9 +18,13 @@ hangs in `recv()`. They read DB credentials from `APP_CONFIG` (which loads `.env`) and are auto-skipped when the connection fails (no local Postgres, wrong creds, etc.) so `pytest` keeps working in CI-only environments. -To run them locally: +To run them locally, use a reachable Postgres and either: - pytest gateway/tests/unit/connectors/test_connectorDbPostgre_pool.py -v +* ``DB_PASSWORD=`` in ``.env`` (no master key), or +* ``DB_PASSWORD_SECRET`` with ``APP_KEY_SYSVAR`` pointing at the master key file + (as on the Infomaniak VM). + + .venv/bin/python -m pytest tests/unit/connectors/test_connectorDbPostgre_pool.py -v They use a throwaway database name (`poweron_pool_test_<uuid>`) and drop it in fixture teardown so they leave nothing behind. @@ -43,19 +47,34 @@ from modules.connectors.connectorDbPostgre import ( closeAllPools, ) from modules.datamodels.datamodelBase import PowerOnModel -from modules.shared.configuration import APP_CONFIG def _dbConfig(): - """Read DB connection params from APP_CONFIG (`.env`). + """Read DB connection params from APP_CONFIG (``.env``). - Returns ``None`` when host/user/password are not all present so the - test module can skip cleanly instead of blowing up at import time. + Returns ``None`` when host/user/password are not all present, or when + secrets cannot be decrypted (e.g. no master key locally), so the module + skips cleanly instead of failing at import/collection time. + + Prefer plaintext ``DB_PASSWORD`` when set (typical on a dev laptop); only + decrypt ``DB_PASSWORD_SECRET`` when no plaintext password is configured. """ - host = APP_CONFIG.get("DB_HOST") - user = APP_CONFIG.get("DB_USER") - password = APP_CONFIG.get("DB_PASSWORD_SECRET") - port = APP_CONFIG.get("DB_PORT", 5432) + try: + from modules.shared.configuration import APP_CONFIG + except Exception: + return None + try: + host = APP_CONFIG.get("DB_HOST") + user = APP_CONFIG.get("DB_USER") + port = APP_CONFIG.get("DB_PORT", 5432) + password = APP_CONFIG.get("DB_PASSWORD") + if password is None: + try: + password = APP_CONFIG.get("DB_PASSWORD_SECRET") + except Exception: + password = None + except Exception: + return None if not host or not user or password is None: return None return {"host": host, "user": user, "password": password, "port": int(port)} @@ -74,10 +93,26 @@ def _canReachPostgres(cfg) -> bool: return False -_DB_CFG = _dbConfig() +def _poolTestSkipReason() -> str: + cfg = _dbConfig() + if cfg is None: + return ( + "No DB credentials for live-Postgres pool tests " + "(set DB_PASSWORD in .env, or provide master key for DB_PASSWORD_SECRET)" + ) + if not _canReachPostgres(cfg): + return ( + f"PostgreSQL not reachable at {cfg['host']}:{cfg['port']} " + "(VPN, firewall, or local Postgres required)" + ) + return "" + + +_POOL_SKIP_REASON = _poolTestSkipReason() +_DB_CFG = None if _POOL_SKIP_REASON else _dbConfig() pytestmark = pytest.mark.skipif( - _DB_CFG is None or not _canReachPostgres(_DB_CFG), - reason="No reachable PostgreSQL — skipping live-Postgres pool tests", + bool(_POOL_SKIP_REASON), + reason=_POOL_SKIP_REASON, )