import contextlib import os from pathlib import Path import sys import tempfile import unittest from types import SimpleNamespace from unittest import mock import yaml ROOT = Path(__file__).resolve().parents[1] APP_DIR = ROOT / 'app' sys.path.insert(0, str(APP_DIR)) import migrate_layout import audit_github_tokens import sync_alive_github_tokens from runtime_security import PrivateFileLock, ensure_private_directory, harden_private_file, private_file_ready class LegacyLayoutMigrationTests(unittest.TestCase): def test_default_mode_holds_authority_and_proves_offline_without_mutating(self): with tempfile.TemporaryDirectory() as temp_dir: runtime = os.path.join(temp_dir, 'runtime') layout = { 'root_dir': temp_dir, 'project_dir': os.path.join(temp_dir, 'app'), 'runtime_dir': runtime, 'results_dir': os.path.join(runtime, 'results'), 'queue_dir': os.path.join(runtime, 'queues'), 'log_dir': os.path.join(runtime, 'logs'), 'state_dir': os.path.join(runtime, 'state'), 'keycheck_dir': os.path.join(runtime, 'keychecks'), 'work_dir': os.path.join(temp_dir, 'work'), 'proxy_file': os.path.join(runtime, 'proxy.txt'), } args = SimpleNamespace( config='config.yaml', source_app=str(APP_DIR), target_app=None, in_place=True, old_root=os.path.join(temp_dir, 'legacy'), desktop_hf=os.path.join(temp_dir, 'desktop'), overwrite=False, dry_run=False, apply=False, no_app_copy=True, no_desktop_import=True, ) with mock.patch.object(migrate_layout, 'parse_args', return_value=args), \ mock.patch.object(migrate_layout, 'load_config', return_value={'global': layout}), \ mock.patch.object(migrate_layout, 'ClusterAuthorityLock', return_value=contextlib.nullcontext()) as authority, \ mock.patch.object(migrate_layout, 'require_runtime_hardening_stopped') as stopped: self.assertEqual(migrate_layout.main(), 0) authority.assert_called_once() stopped.assert_called_once() self.assertFalse(os.path.exists(runtime)) def test_overwrite_false_never_merges_or_replaces_existing_directory(self): with tempfile.TemporaryDirectory() as temp_dir: source = os.path.join(temp_dir, 'source') destination = os.path.join(temp_dir, 'destination') os.makedirs(source) os.makedirs(destination) Path(source, 'new.txt').write_text('new', encoding='ascii') Path(destination, 'sentinel.txt').write_text('sentinel', encoding='ascii') self.assertFalse(migrate_layout.copy_dir(source, destination, overwrite=False, dry_run=False)) self.assertEqual(Path(destination, 'sentinel.txt').read_text(encoding='ascii'), 'sentinel') self.assertFalse(Path(destination, 'new.txt').exists()) def test_directory_replacement_is_retired_even_with_apply_marker(self): with tempfile.TemporaryDirectory() as temp_dir: source = os.path.join(temp_dir, 'source') destination = os.path.join(temp_dir, 'destination') os.makedirs(source) os.makedirs(destination) Path(destination, 'sentinel.txt').write_text('sentinel', encoding='ascii') with self.assertRaisesRegex(RuntimeError, 'replacement is retired'): migrate_layout.copy_dir(source, destination, overwrite=True, verified_apply=True) self.assertTrue(Path(destination, 'sentinel.txt').exists()) def test_production_apply_is_retired_before_authority_or_filesystem_work(self): args = SimpleNamespace(apply=True, dry_run=False) with mock.patch.object(migrate_layout, 'parse_args', return_value=args), \ mock.patch.object(migrate_layout, 'load_config') as load_config, \ mock.patch.object(migrate_layout, 'ClusterAuthorityLock') as authority: with self.assertRaisesRegex(SystemExit, 'already migrated'): migrate_layout.main() load_config.assert_not_called() authority.assert_not_called() class AliveTokenSyncTests(unittest.TestCase): @staticmethod def authority_kwargs(secrets_path): return { 'canonical_secrets_path': secrets_path, 'authority_lock': SimpleNamespace(acquired=True), 'stopped_verified': True, } def fixture(self, temp_dir, alive_status='VALID'): ensure_private_directory(temp_dir, reject_reparse=True) secrets_path = os.path.join(temp_dir, 'secrets.yaml') alive_path = os.path.join(temp_dir, 'githubAlive.txt') Path(secrets_path).write_text( 'auth_pools:\n github_main:\n - name: gh_1\n token: ghp_existing_fixture\n', encoding='ascii', ) Path(alive_path).write_text( f'ghp_new_fixture\t{alive_status}\taccepted\tfixture\n', encoding='ascii', ) harden_private_file(secrets_path) harden_private_file(alive_path) return secrets_path, alive_path def docker_fixture(self, temp_dir, existing_username='existing-user'): ensure_private_directory(temp_dir, reject_reparse=True) secrets_path = os.path.join(temp_dir, 'secrets.yaml') alive_path = os.path.join(temp_dir, 'dockerhubAlive.txt') existing_token = 'dckr_pat_' + ('e' * 27) new_token = 'dckr_pat_' + ('n' * 27) Path(secrets_path).write_text( 'auth_pools:\n dockerhub_main:\n' f' - name: dockerhub_1\n username: {existing_username}\n token: {existing_token}\n', encoding='ascii', ) Path(alive_path).write_text( f'new-user:{new_token}\tVALID\taccepted\tfixture\n', encoding='ascii', ) harden_private_file(secrets_path) harden_private_file(alive_path) return secrets_path, alive_path, existing_token, new_token def test_main_defaults_dry_and_requires_cluster_authority_and_stopped_proof(self): args = SimpleNamespace( config='config.yaml', secrets='secrets.yaml', alive_file='alive.txt', pool='github_main', name_prefix='gh', dry_run=False, apply=False, ) result = { 'alive_unique': 0, 'existing_before': 0, 'added': 0, 'normalized_existing': 0, 'pool_after': 0, } with mock.patch.object(sync_alive_github_tokens, 'parse_args', return_value=args), \ mock.patch.object(sync_alive_github_tokens, 'canonical_path', side_effect=lambda value: os.path.abspath(value)), \ mock.patch.object(sync_alive_github_tokens, 'require_private_file'), \ mock.patch.object(sync_alive_github_tokens, 'load_config', return_value={'global': {'secrets_file': os.path.abspath('secrets.yaml')}}), \ mock.patch.object(sync_alive_github_tokens, 'ClusterAuthorityLock', return_value=contextlib.nullcontext()) as authority, \ mock.patch.object(sync_alive_github_tokens, 'require_runtime_hardening_stopped') as stopped, \ mock.patch.object(sync_alive_github_tokens, 'sync_tokens', return_value=result) as sync: self.assertEqual(sync_alive_github_tokens.main(), 0) authority.assert_called_once() stopped.assert_called_once() self.assertFalse(sync.call_args.kwargs['apply']) def test_foreign_secrets_path_is_rejected_before_authority_or_token_read(self): args = SimpleNamespace( config='config.yaml', secrets='foreign.yaml', alive_file='alive.txt', pool='github_main', name_prefix='gh', dry_run=True, apply=False, ) with mock.patch.object(sync_alive_github_tokens, 'parse_args', return_value=args), \ mock.patch.object(sync_alive_github_tokens, 'canonical_path', side_effect=lambda value: os.path.abspath(value)), \ mock.patch.object(sync_alive_github_tokens, 'require_private_file'), \ mock.patch.object(sync_alive_github_tokens, 'load_config', return_value={'global': {'secrets_file': os.path.abspath('canonical.yaml')}}), \ mock.patch.object(sync_alive_github_tokens, 'ClusterAuthorityLock') as authority, \ mock.patch.object(sync_alive_github_tokens, 'sync_tokens') as sync: with self.assertRaisesRegex(SystemExit, 'exactly match'): sync_alive_github_tokens.main() authority.assert_not_called() sync.assert_not_called() def test_unaccepted_alive_status_is_rejected_without_changing_secrets(self): with tempfile.TemporaryDirectory() as temp_dir: secrets_path, alive_path = self.fixture(temp_dir, alive_status='DEAD') before = Path(secrets_path).read_bytes() with self.assertRaisesRegex(ValueError, 'unaccepted'): sync_alive_github_tokens.sync_tokens( secrets_path, alive_path, 'github_main', 'gh', apply=True, **self.authority_kwargs(secrets_path), ) self.assertEqual(Path(secrets_path).read_bytes(), before) def test_atomic_publication_failure_never_truncates_active_secrets(self): with tempfile.TemporaryDirectory() as temp_dir: secrets_path, alive_path = self.fixture(temp_dir) before = Path(secrets_path).read_bytes() with mock.patch.object( sync_alive_github_tokens, '_atomic_write_private_yaml', side_effect=OSError('simulated publication failure'), ): with self.assertRaisesRegex(OSError, 'publication failure'): sync_alive_github_tokens.sync_tokens( secrets_path, alive_path, 'github_main', 'gh', apply=True, **self.authority_kwargs(secrets_path), ) self.assertEqual(Path(secrets_path).read_bytes(), before) def test_sync_lock_rejects_concurrent_writer_without_changing_secrets(self): with tempfile.TemporaryDirectory() as temp_dir: secrets_path, alive_path = self.fixture(temp_dir) before = Path(secrets_path).read_bytes() held = PrivateFileLock(secrets_path + '.sync.lock').acquire() try: with self.assertRaises(BlockingIOError): sync_alive_github_tokens.sync_tokens( secrets_path, alive_path, 'github_main', 'gh', apply=True, **self.authority_kwargs(secrets_path), ) finally: held.release() self.assertEqual(Path(secrets_path).read_bytes(), before) def test_apply_uses_private_atomic_replacement(self): with tempfile.TemporaryDirectory() as temp_dir: secrets_path, alive_path = self.fixture(temp_dir) result = sync_alive_github_tokens.sync_tokens( secrets_path, alive_path, 'github_main', 'gh', apply=True, **self.authority_kwargs(secrets_path), ) value = yaml.safe_load(Path(secrets_path).read_text(encoding='utf-8')) tokens = [entry['token'] for entry in value['auth_pools']['github_main']] self.assertEqual(result['added'], 1) self.assertIn('ghp_new_fixture', tokens) self.assertTrue(private_file_ready(secrets_path)) def test_dockerhub_sync_adds_only_complete_username_token_pairs(self): with tempfile.TemporaryDirectory() as temp_dir: secrets_path, alive_path, _, new_token = self.docker_fixture(temp_dir) result = sync_alive_github_tokens.sync_tokens( secrets_path, alive_path, 'dockerhub_main', 'dockerhub', apply=True, provider='dockerhub', **self.authority_kwargs(secrets_path), ) value = yaml.safe_load(Path(secrets_path).read_text(encoding='utf-8')) added = [entry for entry in value['auth_pools']['dockerhub_main'] if entry['token'] == new_token] self.assertEqual(result['added'], 1) self.assertEqual(added[0]['username'], 'new-user') self.assertTrue(private_file_ready(secrets_path)) def test_dockerhub_sync_skips_alive_token_without_username(self): with tempfile.TemporaryDirectory() as temp_dir: secrets_path, alive_path, _, new_token = self.docker_fixture(temp_dir) Path(alive_path).write_text( f'{new_token}\tVALID\taccepted\tfixture\n', encoding='ascii', ) harden_private_file(alive_path) result = sync_alive_github_tokens.sync_tokens( secrets_path, alive_path, 'dockerhub_main', 'dockerhub', apply=True, provider='dockerhub', **self.authority_kwargs(secrets_path), ) self.assertEqual(result['added'], 0) self.assertEqual(result['skipped_missing_username'], 1) def test_dockerhub_sync_fills_missing_existing_username(self): with tempfile.TemporaryDirectory() as temp_dir: secrets_path, alive_path, existing_token, _ = self.docker_fixture(temp_dir, existing_username='') Path(alive_path).write_text( f'recovered-user:{existing_token}\tVALID\taccepted\tfixture\n', encoding='ascii', ) harden_private_file(alive_path) result = sync_alive_github_tokens.sync_tokens( secrets_path, alive_path, 'dockerhub_main', 'dockerhub', apply=True, provider='dockerhub', **self.authority_kwargs(secrets_path), ) value = yaml.safe_load(Path(secrets_path).read_text(encoding='utf-8')) self.assertEqual(result['username_filled'], 1) self.assertEqual(value['auth_pools']['dockerhub_main'][0]['username'], 'recovered-user') def test_dockerhub_sync_preserves_conflicting_existing_username(self): with tempfile.TemporaryDirectory() as temp_dir: secrets_path, alive_path, existing_token, _ = self.docker_fixture(temp_dir) Path(alive_path).write_text( f'other-user:{existing_token}\tVALID\taccepted\tfixture\n', encoding='ascii', ) harden_private_file(alive_path) before = Path(secrets_path).read_bytes() result = sync_alive_github_tokens.sync_tokens( secrets_path, alive_path, 'dockerhub_main', 'dockerhub', apply=True, provider='dockerhub', **self.authority_kwargs(secrets_path), ) self.assertEqual(Path(secrets_path).read_bytes(), before) self.assertEqual(result['username_conflicts'], 1) self.assertEqual(result['added'], 0) def test_dockerhub_sync_explicitly_replaces_conflicting_username(self): with tempfile.TemporaryDirectory() as temp_dir: secrets_path, alive_path, existing_token, _ = self.docker_fixture(temp_dir) Path(alive_path).write_text( f'validated-user:{existing_token}\tVALID\taccepted\tfixture\n', encoding='ascii', ) harden_private_file(alive_path) result = sync_alive_github_tokens.sync_tokens( secrets_path, alive_path, 'dockerhub_main', 'dockerhub', apply=True, provider='dockerhub', replace_conflicting_usernames=True, **self.authority_kwargs(secrets_path), ) value = yaml.safe_load(Path(secrets_path).read_text(encoding='utf-8')) self.assertEqual(result['username_replaced'], 1) self.assertEqual(result['username_conflicts'], 0) self.assertEqual(value['auth_pools']['dockerhub_main'][0]['username'], 'validated-user') def test_sync_function_refuses_before_files_without_acquired_authority(self): with mock.patch.object(sync_alive_github_tokens.os.path, 'exists') as exists, \ mock.patch('builtins.open') as open_file: with self.assertRaisesRegex(RuntimeError, 'authority lock'): sync_alive_github_tokens.sync_tokens( 'secrets.yaml', 'alive.txt', 'github_main', 'gh', apply=False, canonical_secrets_path='secrets.yaml', authority_lock=SimpleNamespace(acquired=False), stopped_verified=True, ) exists.assert_not_called() open_file.assert_not_called() class RetiredCredentialToolTests(unittest.TestCase): def test_github_audit_is_retired_before_args_files_or_network(self): with self.assertRaisesRegex(SystemExit, 'retired'): audit_github_tokens.main() source = (APP_DIR / 'audit_github_tokens.py').read_text(encoding='utf-8') self.assertNotIn('requests', source) self.assertNotIn('open(', source) def test_openrouter_powershell_checker_has_no_credential_or_network_execution(self): source = (ROOT / 'runtime' / 'check-openrouter-keys.ps1').read_text(encoding='utf-8') self.assertIn('supervisor-managed OpenRouter keychecks', source) for forbidden in ('Get-Content', 'HttpClient', 'SendAsync', 'orkey.txt', 'proxy.txt'): self.assertNotIn(forbidden, source) if __name__ == '__main__': unittest.main()