Files
2026-09-18 15:38:52 +08:00

102 lines
5.8 KiB
Python

"""合成参数接口验收:隔离 H2/Go Edge/Postgres/酒店,无文件上传、无现有服务操作。"""
import argparse
import json
import os
from pathlib import Path
import secrets
import subprocess
import sys
import time
HERE = Path(__file__).resolve().parent
PROJECT = HERE.parents[3]
CLASSIFICATION = 'LOCAL SIMULATION ONLY'
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--run-dir', required=True, type=Path, help='必须是尚不存在的独立目录')
parser.add_argument('--platform-dir', required=True, type=Path)
parser.add_argument('--pg-bin-dir', required=True, type=Path)
parser.add_argument('--java-home', required=True, type=Path)
parser.add_argument('--go', default='go')
parser.add_argument('--catalog', type=Path, default=HERE / 'catalog.example.json')
parser.add_argument('--classpath', type=Path, help='已完成项目编译时,可复用测试依赖classpath文件')
args = parser.parse_args()
directory = args.run_dir.resolve()
directory.mkdir(mode=0o700, parents=True, exist_ok=False)
token = secrets.token_hex(24)
marker = directory / '.local-fit-simulation.json'
with os.fdopen(os.open(marker, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600), 'w') as stream:
json.dump({'classification': CLASSIFICATION, 'token': token}, stream)
env = {k: v for k, v in os.environ.items() if k in {'PATH', 'HOME', 'USER', 'LOGNAME', 'TMPDIR', 'LANG', 'LC_ALL'}}
env['JAVA_HOME'] = str(args.java_home.resolve())
stage = 'compile'
result = {'classification': CLASSIFICATION, 'status': 'running', 'inputs': 'synthetic confirmed parameters, no uploaded files'}
edge = None
log = None
def run(command, name, cwd=PROJECT):
with (directory / (name + '.log')).open('w') as output:
subprocess.run(list(map(str, command)), cwd=cwd, env=env, stdout=output, stderr=subprocess.STDOUT, check=True)
try:
dependency = args.classpath.resolve() if args.classpath else directory / 'classpath.txt'
if args.classpath is None:
run([PROJECT / 'server/mvnw', '-q', '-DskipTests', 'test-compile', 'dependency:build-classpath',
'-Dmdep.includeScope=test', '-Dmdep.outputFile=' + str(dependency)], 'maven', PROJECT / 'server')
classes = directory / 'classes'; classes.mkdir()
classpath = os.pathsep.join([str(classes), str(PROJECT / 'server/target/classes'), dependency.read_text().strip()])
run([args.java_home / 'bin/javac', '-cp', classpath, '-d', classes, *sorted((HERE / 'java').glob('*.java'))], 'fixture-compile')
main_class = 'cn.nianxx.thhotel.integrations.ohip.edge.adapter.'
run([args.java_home / 'bin/java', '-cp', classpath, main_class + 'LocalBookingCatalogCheck', args.catalog.resolve()], 'catalog-check')
stage = 'edge-start'
log = (directory / 'edge-supervisor.log').open('w')
edge = subprocess.Popen([sys.executable, str(HERE / 'run_edge.py'), '--output-dir', str(directory / 'edge'),
'--platform-dir', str(args.platform_dir.resolve()), '--pg-bin-dir', str(args.pg_bin_dir.resolve()),
'--java-home', str(args.java_home.resolve()), '--go', args.go, '--token', token, '--catalog', str(args.catalog.resolve())],
cwd=PROJECT, env=env, stdout=log, stderr=subprocess.STDOUT)
connection = directory / 'edge/runtime-connection.json'
deadline = time.monotonic() + 300
while not connection.is_file():
if edge.poll() is not None: raise RuntimeError('edge_start_failed')
if time.monotonic() > deadline: raise TimeoutError('edge_start_timeout')
time.sleep(.2)
ta_requests = 0
for mode in ('parameters', 'faults'):
stage = mode
print(json.dumps({'classification': CLASSIFICATION, 'stage': mode}), flush=True)
run([args.java_home / 'bin/java', '-cp', classpath, main_class + 'LocalBookingEvolutionCheck', connection, directory / mode, mode], mode)
# 只有创建Reservation的原生externalReferences TA类型允许携带TA。
for trace in (directory / mode / 'interface-audit').glob('*/*.json'):
audit = json.loads(trace.read_text())
request = audit.get('request_values', [])
for row in request:
if row.get('field') in {'taRecordLocatorList', '__localSimulationTaRecordLocator'}:
raise RuntimeError('ta_search_or_legacy_field_forbidden')
if row.get('field') == 'taRecordLocator':
if audit.get('operation') != 'postReservation' or not row.get('path', '').endswith('/externalReferences/0/id'):
raise RuntimeError('ta_only_allowed_on_fit_creation')
if not any(r.get('field') == 'referenceType' and r.get('value') == 'TA_RECORD_LOCATOR' for r in request):
raise RuntimeError('ta_reference_type_missing')
ta_requests += 1
if ta_requests == 0:
raise RuntimeError('ta_creation_not_exercised')
result['ta_requests'] = ta_requests
result['status'] = 'passed'
except BaseException as error:
result.update(status='failed', stage=stage)
raise RuntimeError("Parameter acceptance failed at " + stage + "; inspect its local log") from None
finally:
if edge is not None and edge.poll() is None:
edge.terminate()
try:
edge.wait(timeout=50)
except subprocess.TimeoutExpired:
result.update(status='cleanup_required', owned_supervisor_pid=edge.pid)
if log is not None: log.close()
(directory / 'acceptance-result.json').write_text(json.dumps(result, indent=2) + '\n')
print(json.dumps(result), flush=True)
if __name__ == '__main__':
main()