import contextlib import importlib.util import io from pathlib import Path import tempfile import unittest from unittest.mock import patch MODULE_PATH = Path(__file__).resolve().parents[1] / "scripts/run-backend.py" SPEC = importlib.util.spec_from_file_location("backend_config", MODULE_PATH) CONFIG = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(CONFIG) class BackendConfigurationTest(unittest.TestCase): def test_env_file_works_without_php_or_connection_json(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) (root / ".env").write_text("DB_URL=jdbc:mariadb://test.invalid/db\nDB_USER=fixture\nDB_PASSWORD=literal$#=secret\\end\n") result = CONFIG.database_environment(root, {}) self.assertEqual(result["DB_PASSWORD"], "literal$#=secret\\end") self.assertEqual(result["DB_USER"], "fixture") def test_process_environment_overrides_env_file(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) (root / ".env").write_text("DB_URL=jdbc:mariadb://test.invalid/db\nDB_USER=fixture\nDB_PASSWORD=local\n") result = CONFIG.database_environment(root, {"DB_USER": "override", "DB_PASSWORD": ""}) self.assertEqual(result["DB_USER"], "override") self.assertEqual(result["DB_PASSWORD"], "") def test_explicit_environment_needs_no_file(self): expected = {"DB_URL": "jdbc:mariadb://test.invalid/db", "DB_USER": "fixture", "DB_PASSWORD": "secret"} with tempfile.TemporaryDirectory() as directory: self.assertEqual(CONFIG.database_environment(Path(directory), expected), expected) def test_missing_credentials_do_not_fall_back_to_legacy_files(self): with tempfile.TemporaryDirectory() as directory: with self.assertRaises(ValueError): CONFIG.database_environment(Path(directory), {"DB_URL": "jdbc:mariadb://test.invalid/db"}) def test_jar_version_is_not_hardcoded_and_original_is_ignored(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) target = root / "backend/target" target.mkdir(parents=True) jar = target / "votian-backend-0.1.jar" jar.touch() (target / "votian-backend-0.1.jar.original").touch() self.assertEqual(CONFIG.backend_jar(root), jar) (target / "votian-backend-1.0.0-SNAPSHOT.jar").touch() with self.assertRaises(FileNotFoundError): CONFIG.backend_jar(root) def test_check_config_does_not_find_jar_or_launch_java(self): environment = {"DB_URL": "jdbc:mariadb://test.invalid/db", "DB_USER": "fixture", "DB_PASSWORD": "secret"} with patch.object(CONFIG.sys, "argv", ["run-backend.py", "--check-config"]), \ patch.object(CONFIG, "database_environment", return_value=environment), \ patch.object(CONFIG, "backend_jar") as jar, \ patch.object(CONFIG.os, "execvpe") as execute, contextlib.redirect_stdout(io.StringIO()): self.assertEqual(CONFIG.main(), 0) jar.assert_not_called() execute.assert_not_called() def test_launch_uses_env_port_and_keeps_secrets_out_of_arguments(self): for arguments, expected_port in (([], "18081"), (["--port", "18082"], "18082")): environment = {"DB_URL": "jdbc:mariadb://test.invalid/db", "DB_USER": "fixture", "DB_PASSWORD": "secret", "PORT": "18081"} with self.subTest(arguments=arguments), \ patch.object(CONFIG.sys, "argv", ["run-backend.py", *arguments]), \ patch.object(CONFIG, "database_environment", return_value=environment), \ patch.object(CONFIG, "backend_jar", return_value=Path("/fixture/backend.jar")), \ patch.object(CONFIG.os, "chdir"), patch.object(CONFIG.os, "execvpe") as execute: self.assertEqual(CONFIG.main(), 0) command, actual_arguments, actual_environment = execute.call_args.args self.assertEqual(command, "java") self.assertEqual(actual_arguments, ["java", "-jar", "/fixture/backend.jar"]) self.assertEqual(actual_environment["PORT"], expected_port) self.assertEqual(actual_environment["DB_PASSWORD"], "secret") def test_configuration_error_never_prints_secret(self): output = io.StringIO() with patch.object(CONFIG.sys, "argv", ["run-backend.py", "--check-config"]), \ patch.object(CONFIG, "database_environment", side_effect=ValueError("secret=do-not-expose")), \ patch.object(CONFIG.os, "execvpe") as execute, contextlib.redirect_stderr(output): self.assertEqual(CONFIG.main(), 2) execute.assert_not_called() self.assertNotIn("do-not-expose", output.getvalue()) if __name__ == "__main__": unittest.main()