import importlib.util from pathlib import Path import tempfile import unittest spec = importlib.util.spec_from_file_location('push_setup', Path(__file__).resolve().parents[2] / 'scripts/setup-push-remote.py') setup = importlib.util.module_from_spec(spec) spec.loader.exec_module(setup) class PushSetupTest(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory() self.home = Path(self.temp.name) (self.home / 'opt/werkjournal').mkdir(parents=True) self.path = self.home / '.config/werkjournal/environment' self.path.parent.mkdir(parents=True) self.original = 'WERKJOURNAL_GUEST_MODE=false\nWERKJOURNAL_AUTH_BOOTSTRAP_CODE=existing-setup\n' self.path.write_text(self.original) self.path.chmod(0o600) def tearDown(self): self.temp.cleanup() def test_setup_preserves_existing_configuration_and_is_idempotent(self): calls = [] def keys(home): calls.append(home) return 'public-test-key', 'private-test-key' result = setup.configure(self.home, keys) first = self.path.read_bytes() self.assertTrue(first.startswith(self.original.encode())) self.assertIn(b'WERKJOURNAL_PUSH_ENABLED=false', first) self.assertEqual(0o600, self.path.stat().st_mode & 0o777) self.assertNotIn('private-test-key', result) setup.configure(self.home, keys) self.assertEqual(first, self.path.read_bytes()) self.assertEqual(1, len(calls)) def test_generator_failure_preserves_configuration(self): def fail(home): raise RuntimeError('controlled failure') with self.assertRaises(RuntimeError): setup.configure(self.home, fail) self.assertEqual(self.original, self.path.read_text()) def test_partial_keys_are_not_rotated(self): self.path.write_text(self.original + setup.PUBLIC + '=existing-public\n') before = self.path.read_bytes() with self.assertRaises(RuntimeError): setup.configure(self.home, lambda _: self.fail('Must not generate new keys')) self.assertEqual(before, self.path.read_bytes()) def test_insecure_file_or_symlink_is_rejected(self): self.path.chmod(0o644) with self.assertRaises(RuntimeError): setup.configure(self.home) self.path.unlink() target = self.home / 'other' target.write_text('untouched') self.path.symlink_to(target) with self.assertRaises(RuntimeError): setup.configure(self.home) self.assertEqual('untouched', target.read_text())