#!/usr/bin/env python3 """Run mapped acceptance tests; report external gaps separately from test results.""" import argparse import json from pathlib import Path import sys import unittest ROOT=Path(__file__).resolve().parents[1] sys.path[:0]=[str(ROOT/'src'),str(ROOT/'tests')] parser=argparse.ArgumentParser() parser.add_argument('--role',choices=['user','tenant_admin','platform_admin']) parser.add_argument('--report',type=Path) parser.add_argument('--require-complete',action='store_true',help='Fail while any journey has an implementation or external acceptance gap') args=parser.parse_args() rows=json.loads((ROOT/'tests/journey-coverage.json').read_text())['journeys'] expected={f'U{i:02}' for i in range(1,14)}|{f'T{i:02}' for i in range(1,9)}|{f'P{i:02}' for i in range(1,9)} if len(rows)!=29 or {r['id'] for r in rows}!=expected: raise SystemExit('Journey coverage must contain each of the 29 journey IDs exactly once') rows=[r for r in rows if not args.role or r['role']==args.role] for row in rows: if not row['tests'] or row['implementation'] not in {'implemented','partial','external-blocked'}: raise SystemExit('Invalid coverage entry: '+row['id']) if row['implementation']!='implemented' and not row['remaining']: raise SystemExit('Unexplained journey gap: '+row['id']) selectors=sorted({name for row in rows for name in row['tests']}) suite=unittest.TestSuite(unittest.defaultTestLoader.loadTestsFromName(name) for name in selectors) result=unittest.TextTestRunner(verbosity=2).run(suite) failed={test.id() for test,_ in result.failures+result.errors} skipped={test.id() for test,_ in result.skipped} report={'tests_run':result.testsRun,'test_success':result.wasSuccessful(),'skipped':len(skipped), 'journeys':[dict(row,automated_result='failed' if failed.intersection(row['tests']) else 'skipped' if skipped.intersection(row['tests']) else 'passed') for row in rows], 'complete':result.wasSuccessful() and not skipped and all(r['implementation']=='implemented' for r in rows)} if args.report: args.report.parent.mkdir(parents=True,exist_ok=True) args.report.write_text(json.dumps(report,indent=2)+'\n') print(json.dumps({'tests_run':report['tests_run'],'test_success':report['test_success'],'complete':report['complete'], 'unresolved_journeys':[r['id'] for r in rows if r['implementation']!='implemented']})) raise SystemExit(0 if result.wasSuccessful() and not skipped and (not args.require_complete or report['complete']) else 1)