"""Offline tests for the fluid-telegram custody move helper against a fake ``bao``. Runs the helper as a real subprocess with a fake ``bao`` first on PATH, so the silence contract (empty stdout and stderr) is tested end to end. """ import json import os from pathlib import Path import subprocess import sys import tempfile import textwrap import unittest ROOT = Path(__file__).resolve().parents[1] SCRIPT = ROOT / 'scripts/fluid_telegram_custody_move.py' CONFIRM = 'MOVE fluid-telegram hall-of-helix to coulomb' SRC = 'workloads/infotech/fluid-telegram/hall-of-helix' DST = 'workloads/coulomb/fluid-telegram/hall-of-helix' VALUES = { 'bot-token': {'token': 'bt-' + 'Q1w2' * 10}, 'operator-app': {'api_id': '1234567', 'api_hash': 'ah-' + 'E3r4' * 8}, 'operator-session': {'session_b64': 'c2Vzc2lvbg' * 12}, 'redaction-salt': {'salt': 'sa-' + 'T5y6' * 8}, } FAKE_BAO = textwrap.dedent('''\ #!/usr/bin/env python3 import json, os, sys sf = os.environ['FAKE_BAO_STATE'] st = json.load(open(sf)) st['calls'].append(sys.argv[1:]) def save(): json.dump(st, open(sf, 'w')) def out(obj): save(); print(json.dumps(obj)); sys.exit(0) def missing(): save(); sys.stderr.write('No value found at x\\n'); sys.exit(2) a = sys.argv[1:] print('Success! noise', file=sys.stderr) if a[0] == 'read': p = a[-1] if p == 'auth/token/lookup-self': out({'data': st['identity']}) mount, kind, rest = p.split('/', 2) e = st['kv'].get(rest) if not e: missing() cv = len(e['versions']) if kind == 'metadata': out({'data': {'current_version': cv, 'versions': {str(i + 1): {} for i in range(cv)}}}) out({'data': {'data': e['versions'][-1], 'metadata': {'version': cv}}}) if a[0] == 'write': p = a[-2].split('/', 2)[2] body = json.loads(sys.stdin.read()) if st.get('fail_write'): save(); sys.exit(2) e = st['kv'].setdefault(p, {'versions': []}) if body['options']['cas'] != len(e['versions']): save(); sys.exit(2) e['versions'].append(body['data']) out({'data': {'version': len(e['versions'])}}) if a[0] == 'delete': st['kv'].pop(a[1].split('/', 2)[2], None) save(); sys.exit(0) save(); sys.exit(9) ''') class CustodyMove(unittest.TestCase): def setUp(self): self.tmp = Path(tempfile.mkdtemp()) (self.tmp / 'bin').mkdir() fake = self.tmp / 'bin/bao' fake.write_text(FAKE_BAO) fake.chmod(0o755) self.home = self.tmp / '.warden-attended-login' / 'session' self.home.mkdir(parents=True) kv = {f'{SRC}/{k}': {'versions': [v]} for k, v in VALUES.items()} kv[f'{SRC}/operator-session']['versions'] = [{'session_b64': 'old'}] * 3 + [VALUES['operator-session']] self.state = {'calls': [], 'identity': {'policies': ['default', 'platform-admin']}, 'kv': kv} def run_helper(self, *extra, confirm=CONFIRM): (self.tmp / 'state.json').write_text(json.dumps(self.state)) receipt = self.tmp / f'receipt-{len(list(self.tmp.glob("receipt-*")))}.json' env = {k: v for k, v in os.environ.items() if k not in ('BAO_TOKEN', 'VAULT_TOKEN')} env.update(PATH=f'{self.tmp / "bin"}:{env["PATH"]}', HOME=str(self.home), FAKE_BAO_STATE=str(self.tmp / 'state.json')) proc = subprocess.run([sys.executable, str(SCRIPT), '--confirm', confirm, '--receipt', str(receipt), *extra], env=env, capture_output=True, text=True) self.assertEqual((proc.stdout, proc.stderr), ('', '')) self.state = json.loads((self.tmp / 'state.json').read_text()) text = receipt.read_text() for value in VALUES.values(): for secret in value.values(): self.assertNotIn(secret, text) return proc.returncode, json.loads(text) def test_moves_all_four_then_deletes_sources(self): code, receipt = self.run_helper() self.assertEqual((code, receipt['outcome']), (0, 'moved')) for entry, value in VALUES.items(): self.assertEqual(self.state['kv'][f'{DST}/{entry}']['versions'], [value]) self.assertNotIn(f'{SRC}/{entry}', self.state['kv']) self.assertTrue(receipt['entries'][entry]['source_deleted']) self.assertEqual(receipt['entries']['operator-session']['source_version'], 4) self.assertFalse(receipt['credential_values_emitted']) def test_rerun_after_move_is_already_moved(self): self.run_helper() code, receipt = self.run_helper() self.assertEqual((code, receipt['outcome']), (0, 'already_moved')) def test_dry_run_reads_no_values(self): code, receipt = self.run_helper('--dry-run') self.assertEqual((code, receipt['outcome']), (0, 'dry_run_would_move')) self.assertFalse(any(c[0] == 'read' and '/data/' in c[-1] for c in self.state['calls'])) self.assertFalse(any(c[0] in ('write', 'delete') for c in self.state['calls'])) def test_keep_source_copies_without_deleting(self): code, receipt = self.run_helper('--keep-source') self.assertEqual((code, receipt['outcome']), (0, 'copied_source_kept')) self.assertIn(f'{SRC}/bot-token', self.state['kv']) self.assertIn(f'{DST}/bot-token', self.state['kv']) def test_differing_target_refuses_and_deletes_nothing(self): self.state['kv'][f'{DST}/redaction-salt'] = {'versions': [{'salt': 'other'}]} code, receipt = self.run_helper() self.assertEqual((code, receipt['outcome']), (3, 'target_differs')) self.assertFalse(any(c[0] == 'delete' for c in self.state['calls'])) self.assertEqual(self.state['kv'][f'{DST}/redaction-salt']['versions'], [{'salt': 'other'}]) def test_write_failure_deletes_nothing(self): self.state['fail_write'] = True code, receipt = self.run_helper() self.assertEqual((code, receipt['outcome']), (4, 'write_failed')) self.assertFalse(any(c[0] == 'delete' for c in self.state['calls'])) def test_refuses_without_confirmation_identity_or_envelope(self): code, receipt = self.run_helper(confirm='MOVE') self.assertEqual((code, receipt['outcome']), (2, 'exact_confirmation_required')) self.state['identity'] = {'policies': ['root', 'platform-admin']} code, receipt = self.run_helper() self.assertEqual((code, receipt['outcome']), (2, 'attended_platform_admin_required')) self.assertFalse(any(c[0] in ('write', 'delete') for c in self.state['calls'])) if __name__ == '__main__': unittest.main()