Files
ocf-managed-container/tests/test_lab_cycle.py
T
Lyncoln ec2077f979 Freeze lab-tested PostgreSQL plugin engine
Extract PostgreSQL, Podman runtime and systemd lifecycle adapters while preserving existing lifecycle and scoring behavior. Update RPM layout and add guarded operator-run lab harnesses.

Record successful manual stop/start, promotion, demotion and rewind-only rejoin with pinned installed hashes. The unfenced lab does not validate automatic failover, Pacemaker-managed transitions or divergent-data rewind.

Validation: 103 offline tests, original-function differential checks, checksum manifest and whitespace checks passed.
2026-09-13 01:57:04 -04:00

66 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Stop/start harness tests with mocked commands and temporary evidence only."""
import contextlib
import importlib.util
import io
import os
from pathlib import Path
import tempfile
from types import SimpleNamespace
import unittest
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location('cycle', ROOT / 'scripts/test-elitedesk-cycle.py')
cycle = importlib.util.module_from_spec(spec)
spec.loader.exec_module(cycle)
class Cycle(unittest.TestCase):
def scenario(self, failure=None):
with tempfile.TemporaryDirectory() as directory, contextlib.ExitStack() as stack:
state = {'running': True, 'started': 'old'}
calls = []
def command(args, **kwargs):
action = args[-1]; calls.append(action)
self.assertIn(action, ['validate-all', 'monitor', 'stop', 'start', 'status'])
if action == failure:
return SimpleNamespace(returncode=1, stdout='', stderr='injected failure')
if action == 'stop': state['running'] = False
if action == 'start': state.update(running=True, started='new')
rc = 7 if action == 'monitor' and not state['running'] else 0
return SimpleNamespace(returncode=rc, stdout='', stderr='')
stack.enter_context(patch.object(cycle.os, 'geteuid', return_value=0))
stack.enter_context(patch.object(cycle.socket, 'gethostname', return_value='DAVID-ELITEDESK'))
stack.enter_context(patch.object(cycle.lab, 'BACKUP_ROOT', Path(directory)))
stack.enter_context(patch.object(cycle.lab, 'cluster', return_value={}))
stack.enter_context(patch.object(cycle.lab, 'observe', side_effect=lambda:
dict(system_id='same', started=state['started'])))
stack.enter_context(patch.object(cycle, 'verify_payload'))
stack.enter_context(patch.object(cycle, 'owner_command', side_effect=lambda *args:
'inactive' if args[0] == 'systemctl' else ''))
stack.enter_context(patch.object(cycle, 'score', side_effect=lambda:
'100' if state['running'] else '-INFINITY'))
stack.enter_context(patch.object(cycle.subprocess, 'run', side_effect=command))
old_mask = os.umask(0o022)
try:
with contextlib.redirect_stdout(io.StringIO()):
if failure:
with self.assertRaises(RuntimeError): cycle.main()
self.assertEqual(calls[-1], failure)
self.assertEqual(calls.count(failure), 1)
else:
cycle.main()
self.assertEqual(calls, ['validate-all','monitor','stop','monitor','start','monitor','status'])
finally:
os.umask(old_mask)
self.assertEqual(len(list(Path(directory).glob('*/results.json'))), 1)
def test_successful_cycle(self): self.scenario()
def test_stop_failure_halts_without_start(self): self.scenario('stop')
def test_start_failure_halts_without_retry(self): self.scenario('start')
if __name__ == '__main__':
unittest.main(verbosity=2)