from __future__ import annotations import os import stat import sys import tempfile import unittest from pathlib import Path from unittest import mock HERE = Path(__file__).resolve().parent ROOT = HERE.parent sys.path.insert(0, str(ROOT / "forge-stack-orchestrator" / "scripts")) import operator_env as oe # noqa: E402 class UpdateEnvFileTests(unittest.TestCase): def test_replaces_commented_placeholders_and_preserves_mode(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: env_path = Path(tmpdir) / ".env" env_path.write_text( "FORGE_STACK_DEVPI_PASSWORD=change-me\n" "# FORGE_STACK_DEVPI_GITHUB_TOKEN=\n" "FORGE_STACK_DEVPI_PYARMOR_DEVICE_ID=1\n", encoding="utf-8", ) os.chmod(env_path, 0o600) oe.update_env_file( env_path, { "FORGE_STACK_DEVPI_PASSWORD": "secret-pass", "FORGE_STACK_DEVPI_GITHUB_TOKEN": "ghp_test", }, ) rendered = env_path.read_text(encoding="utf-8") self.assertIn("FORGE_STACK_DEVPI_PASSWORD=secret-pass\n", rendered) self.assertIn("FORGE_STACK_DEVPI_GITHUB_TOKEN=ghp_test\n", rendered) self.assertEqual(stat.S_IMODE(env_path.stat().st_mode), 0o600) class CollectUpdatesTests(unittest.TestCase): def test_interactive_accepts_dev_tty_when_stdin_is_not_tty(self) -> None: fake_tty = mock.mock_open() with mock.patch.object(oe.sys.stdin, "isatty", return_value=False), mock.patch.object( oe.sys.stderr, "isatty", return_value=True, ), mock.patch("builtins.open", fake_tty): self.assertTrue(oe.interactive()) fake_tty.assert_called_once_with("/dev/tty", "r", encoding="utf-8") def test_collects_password_github_and_pyarmor_when_missing(self) -> None: env_values = { oe.DEVPI_PASSWORD_KEY: "change-me", oe.GITHUB_TOKEN_KEY: "", oe.PYARMOR_ACTIVATION_KEY: "", oe.PYARMOR_REGFILE_KEY: "", oe.PYARMOR_DEVICE_KEY: "1", } with mock.patch.object(oe, "interactive", return_value=True), mock.patch.object( oe, "prompt_value", side_effect=[ "backend-pass", "ghp_123", "/secure/license-email.txt", "/secure/pyarmor-reg.zip", "7", ], ), mock.patch.object(oe, "prompt_bool", return_value=True): updates = oe.collect_updates(env_values, created_env=False) self.assertEqual(updates[oe.DEVPI_PASSWORD_KEY], "backend-pass") self.assertEqual(updates[oe.GITHUB_TOKEN_KEY], "ghp_123") self.assertEqual(updates[oe.PYARMOR_ACTIVATION_KEY], "/secure/license-email.txt") self.assertEqual(updates[oe.PYARMOR_REGFILE_KEY], "/secure/pyarmor-reg.zip") self.assertEqual(updates[oe.PYARMOR_DEVICE_KEY], "7") def test_skips_pyarmor_when_operator_declines_release_host_mode(self) -> None: env_values = { oe.DEVPI_PASSWORD_KEY: "backend-pass", oe.GITHUB_TOKEN_KEY: "", oe.PYARMOR_ACTIVATION_KEY: "", oe.PYARMOR_REGFILE_KEY: "", oe.PYARMOR_DEVICE_KEY: "1", } with mock.patch.object(oe, "interactive", return_value=True), mock.patch.object( oe, "prompt_value", side_effect=[""], ), mock.patch.object(oe, "prompt_bool", return_value=False): updates = oe.collect_updates(env_values, created_env=False) self.assertNotIn(oe.GITHUB_TOKEN_KEY, updates) self.assertNotIn(oe.PYARMOR_ACTIVATION_KEY, updates) self.assertNotIn(oe.PYARMOR_REGFILE_KEY, updates) self.assertNotIn(oe.PYARMOR_DEVICE_KEY, updates) if __name__ == "__main__": unittest.main()