feat: add tested dependency updates with runtime rollback
This commit is contained in:
1 parent
34a7a3d039
commit
6438558ea3
10 files changed
+408
-2
No files matched your search
@@ -0,0 +1,64 @@
|
||||
"""Transaction tests: a failed health check must never leave the candidate active."""
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from jr_einvoice.maintenance import Maintenance, locked, point
|
||||
|
||||
|
||||
class MaintenanceTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.tmp.cleanup)
|
||||
self.root = Path(self.tmp.name)
|
||||
self.maintenance = Maintenance(self.root, logging.getLogger('maintenance-test'))
|
||||
self.state = self.maintenance.state
|
||||
self.state.mkdir()
|
||||
self.old = self.state / 'old'
|
||||
self.new = self.state / 'new'
|
||||
self.old.mkdir()
|
||||
self.new.mkdir()
|
||||
point(self.state / 'current', self.old)
|
||||
|
||||
def test_success_and_manual_rollback(self):
|
||||
with patch.object(self.maintenance, 'health'):
|
||||
self.maintenance.activate(self.new)
|
||||
self.assertEqual((self.state / 'current').resolve(), self.new)
|
||||
self.maintenance.rollback()
|
||||
self.assertEqual((self.state / 'current').resolve(), self.old)
|
||||
self.assertEqual((self.state / 'previous').resolve(), self.new)
|
||||
|
||||
def test_post_activation_failure_and_interrupt_restore_old(self):
|
||||
for failure in (RuntimeError('bad invoice'), KeyboardInterrupt()):
|
||||
with self.subTest(failure=type(failure).__name__):
|
||||
with patch.object(self.maintenance, 'health', side_effect=failure):
|
||||
with self.assertRaises(type(failure)):
|
||||
self.maintenance.activate(self.new)
|
||||
self.assertEqual((self.state / 'current').resolve(), self.old)
|
||||
|
||||
def test_failed_rollback_precheck_keeps_active(self):
|
||||
point(self.state / 'previous', self.new)
|
||||
with patch.object(self.maintenance, 'health', side_effect=RuntimeError('bad')):
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.maintenance.rollback()
|
||||
self.assertEqual((self.state / 'current').resolve(), self.old)
|
||||
|
||||
def test_parallel_update_is_rejected(self):
|
||||
with locked(self.state):
|
||||
with self.assertRaisesRegex(RuntimeError, 'anderer'):
|
||||
with locked(self.state):
|
||||
self.fail('lock not enforced')
|
||||
|
||||
def test_download_failure_never_switches(self):
|
||||
with patch.object(self.maintenance, 'run'), patch('jr_einvoice.maintenance.install_mustang', side_effect=RuntimeError('checksum')):
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.maintenance.update(True, '2.26.0')
|
||||
self.assertEqual((self.state / 'current').resolve(), self.old)
|
||||
|
||||
def test_invalid_version_is_rejected_before_commands(self):
|
||||
with patch.object(self.maintenance, 'run') as run:
|
||||
with self.assertRaises(ValueError):
|
||||
self.maintenance.update(True, '../bad')
|
||||
run.assert_not_called()
|
||||
Reference in new issue
Block a user