diff --git a/.gitea/workflows/module-matrix.yml b/.gitea/workflows/module-matrix.yml index 13c6dbd..74381dc 100644 --- a/.gitea/workflows/module-matrix.yml +++ b/.gitea/workflows/module-matrix.yml @@ -42,6 +42,7 @@ jobs: python -m venv .venv .venv/bin/python tools/repo/sync-python-environment.py --requirements requirements-release.txt --python .venv/bin/python --upgrade-pip .venv/bin/python -m pip install '../govoplan-core[dev]' + .venv/bin/python -m pip install --no-deps ../govoplan-search - name: Install WebUI release dependencies with test scripts working-directory: govoplan run: bash tools/release/install-webui-release-dependencies.sh ../govoplan-core/webui diff --git a/.gitea/workflows/security-audit.yml b/.gitea/workflows/security-audit.yml index cbf4006..3b969b3 100644 --- a/.gitea/workflows/security-audit.yml +++ b/.gitea/workflows/security-audit.yml @@ -29,7 +29,7 @@ jobs: git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "ssh://git@git.add-ideas.de/GovOPlaN/govoplan" - name: Bootstrap GovOPlaN repositories working-directory: govoplan - run: python tools/repo/bootstrap-repositories.py --parent .. --transport public-https + run: python tools/repo/bootstrap-repositories.py --parent .. --transport public-https --exclude-repo addideas-govoplan-website - name: Run whole-system security audit working-directory: govoplan run: tools/checks/security-audit/run.sh --mode "$SECURITY_AUDIT_MODE" --scope "$SECURITY_AUDIT_SCOPE" --reports-dir audit-reports diff --git a/tests/test_postgres_integration_check.py b/tests/test_postgres_integration_check.py new file mode 100644 index 0000000..633ccba --- /dev/null +++ b/tests/test_postgres_integration_check.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import importlib.util +import os +from pathlib import Path +import unittest +from unittest.mock import patch + + +META_ROOT = Path(__file__).resolve().parents[1] +SCRIPT = META_ROOT / "tools" / "checks" / "postgres-integration-check.py" + + +def _load_script(): + spec = importlib.util.spec_from_file_location( + "postgres_integration_check", + SCRIPT, + ) + if spec is None or spec.loader is None: + raise RuntimeError(f"Could not load {SCRIPT}") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class PostgresIntegrationCheckTests(unittest.TestCase): + def test_staging_check_has_explicit_runtime_safety_settings(self) -> None: + module = _load_script() + + with patch.dict(os.environ, {}, clear=True): + env = module._check_env( + database_url="postgresql+psycopg://user:password@postgres/db", + modules="tenancy,access,search", + app_env="staging", + ) + + self.assertEqual( + env["CORS_ORIGINS"], + "http://127.0.0.1:5173,http://localhost:5173", + ) + self.assertEqual( + env["GOVOPLAN_TRUSTED_HOSTS"], + "127.0.0.1,localhost,testserver", + ) + self.assertEqual( + env["GOVOPLAN_CONNECTOR_ALLOW_PRIVATE_NETWORKS"], + "false", + ) + + def test_existing_runtime_safety_settings_are_preserved(self) -> None: + module = _load_script() + configured = { + "CORS_ORIGINS": "https://govoplan.example.test", + "GOVOPLAN_TRUSTED_HOSTS": "govoplan.example.test", + "GOVOPLAN_CONNECTOR_ALLOW_PRIVATE_NETWORKS": "true", + } + + with patch.dict(os.environ, configured, clear=True): + env = module._check_env( + database_url="postgresql+psycopg://user:password@postgres/db", + modules="tenancy,access,search", + app_env="staging", + ) + + for key, value in configured.items(): + self.assertEqual(env[key], value) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_release_integration.py b/tests/test_release_integration.py index 62b69a1..ee195d7 100644 --- a/tests/test_release_integration.py +++ b/tests/test_release_integration.py @@ -141,6 +141,15 @@ def test_pytest_style_function(): self.assertIn('--core-root "$ROOT"', script) self.assertNotIn('"$PYTHON" -m unittest tests.test_module_system', script) + def test_module_matrix_installs_search_before_postgres_validation(self) -> None: + meta_root = Path(__file__).resolve().parents[1] + workflow = (meta_root / ".gitea" / "workflows" / "module-matrix.yml").read_text(encoding="utf-8") + + install = workflow.index("pip install --no-deps ../govoplan-search") + validation = workflow.index("Validate Search against PostgreSQL") + + self.assertLess(install, validation) + def test_release_workflow_installs_tagged_source_test_harness(self) -> None: meta_root = Path(__file__).resolve().parents[1] workflow = (meta_root / ".gitea" / "workflows" / "release-integration.yml").read_text(encoding="utf-8") diff --git a/tests/test_repository_bootstrap.py b/tests/test_repository_bootstrap.py index 101dcba..aeb95d5 100644 --- a/tests/test_repository_bootstrap.py +++ b/tests/test_repository_bootstrap.py @@ -359,6 +359,35 @@ class RepositoryBootstrapTests(unittest.TestCase): self.assertEqual(1, status) runner.assert_not_called() + def test_anonymous_ci_bootstrap_excludes_registered_transport_repositories( + self, + ) -> None: + manifest = json.loads( + (META_ROOT / "repositories.json").read_text(encoding="utf-8") + ) + registered_only = { + entry["name"] + for entry in manifest["repositories"] + if entry.get("bootstrap_transport") == "registered" + } + self.assertTrue(registered_only) + + for workflow in sorted( + (META_ROOT / ".gitea" / "workflows").glob("*.yml") + ): + contents = workflow.read_text(encoding="utf-8") + if ( + "bootstrap-repositories.py" not in contents + or "--transport public-https" not in contents + ): + continue + with self.subTest(workflow=workflow.name): + for repository in registered_only: + self.assertIn( + f"--exclude-repo {repository}", + contents, + ) + if __name__ == "__main__": unittest.main() diff --git a/tools/checks/postgres-integration-check.py b/tools/checks/postgres-integration-check.py index 036f0c3..b3c5258 100644 --- a/tools/checks/postgres-integration-check.py +++ b/tools/checks/postgres-integration-check.py @@ -116,6 +116,9 @@ def _check_env(*, database_url: str, modules: str, app_env: str) -> dict[str, st env.setdefault("FILE_STORAGE_BACKEND", "local") env.setdefault("FILE_STORAGE_LOCAL_ROOT", str(ROOT / "runtime" / "postgres-check-files")) env.setdefault("MOCK_MAILBOX_DIR", str(ROOT / "runtime" / "postgres-check-mock-mailbox")) + env.setdefault("CORS_ORIGINS", "http://127.0.0.1:5173,http://localhost:5173") + env.setdefault("GOVOPLAN_TRUSTED_HOSTS", "127.0.0.1,localhost,testserver") + env.setdefault("GOVOPLAN_CONNECTOR_ALLOW_PRIVATE_NETWORKS", "false") env["PYTHONPATH"] = os.pathsep.join(part for part in (str(SRC), env.get("PYTHONPATH", "")) if part) return env