#!/usr/bin/env python3 """Require the exact unskipped native recovery browser node and retain bounded proof.""" import argparse import hashlib import json import os import subprocess import sys import tempfile import xml.etree.ElementTree as ET from pathlib import Path ROOT = Path(__file__).resolve().parents[1] NODE = 'scripts/native_recovery/test_journey.py::test_native_reset_directory_smtp_issuer_and_app' def run(output): source_output = output.with_suffix('.sources.txt') if output.exists() or source_output.exists(): raise ValueError('Fresh receipt and source manifest required') with tempfile.TemporaryDirectory(prefix='csoc-native-recovery-gate-') as directory: work = Path(directory) config = work / 'pytest.ini' config.write_text('[pytest]\nmarkers=\n browser: real Chromium\n' ' usecase(id): stable journey\n') receipt = work / 'native.json' report = work / 'junit.xml' env = {'PATH': os.environ['PATH'], 'HOME': str(Path.home()), 'LANG': 'C.UTF-8', 'PYTHONDONTWRITEBYTECODE': '1', 'PYTEST_DISABLE_PLUGIN_AUTOLOAD': '1', 'CSOC_NATIVE_RECEIPT': str(receipt)} result = subprocess.run([ sys.executable, '-m', 'pytest', '-c', str(config), '--rootdir', str(ROOT), '-p', 'no:cacheprovider', '--basetemp', str(work / 'pytest'), '--junitxml', str(report), NODE], cwd=ROOT, env=env, capture_output=True, timeout=360) if result.returncode: fd, diagnostic = tempfile.mkstemp(prefix='csoc-native-gate-failure-', suffix='.log') with os.fdopen(fd, 'wb') as stream: stream.write(result.stdout + result.stderr) raise RuntimeError('Native gate failed; private fixture diagnostic: ' + diagnostic) suites = ET.parse(report).getroot() tests = suites.findall('.//testcase') if (len(tests) != 1 or tests[0].get('classname') != 'scripts.native_recovery.test_journey' or tests[0].get('name') != NODE.split('::')[1] or any(tests[0].find(n) is not None for n in ['failure', 'error', 'skipped'])): raise RuntimeError('Exact unskipped native node required') proof = json.loads(receipt.read_text()) sources = receipt.with_suffix('.sources.txt').read_bytes() if hashlib.sha256(sources).hexdigest() != proof['source_manifest_sha256']: raise RuntimeError('Native source manifest differs') proof['native_browser_gate'] = {'command': 'make check-native-recovery', 'node': NODE, 'passed': 1, 'skipped': 0, 'failures': 0, 'errors': 0, 'junit_sha256': hashlib.sha256( report.read_bytes()).hexdigest()} with source_output.open('xb') as stream: stream.write(sources) with output.open('x') as stream: stream.write(json.dumps(proof, indent=2) + '\n') return {'status': proof['status'], 'passed': 1, 'skipped': 0, 'production_or_mfa_acceptance': False} if __name__ == '__main__': parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--receipt', type=Path, required=True) args = parser.parse_args() print(json.dumps(run(args.receipt)))