#!/usr/bin/env python3
"""Private-copy mutation tests, including wire {} versus []. Never calls HTTP/DB."""
from copy import deepcopy
import json
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile

if len(sys.argv)!=3:raise SystemExit('Expected B-runtime C-local-copy')
b,c=map(Path,sys.argv[1:]);original=json.loads((c/'responses.json').read_text());report=json.loads((c/'report.json').read_text())
script=Path(__file__).with_name('compare.py');passed=[]
def shape(v):
    if isinstance(v,dict):return {'type':'object','properties':{k:shape(x) for k,x in sorted(v.items())} if v else []}
    if isinstance(v,list):return {'type':'array','items':[shape(x) for x in v]}
    if v is None:return {'type':'NULL'}
    if isinstance(v,bool):return {'type':'boolean'}
    if isinstance(v,(int,float)):return {'type':'number'}
    return {'type':'string'}
with tempfile.TemporaryDirectory(prefix='cdx-spec-wire-compare-') as tmp:
    dest=Path(tmp)/'C';shutil.copytree(c,dest)
    def run(name,mutate=None,mutate_report=None,accept=False,refresh_shape=True):
        data=deepcopy(original);r=deepcopy(report)
        if mutate:mutate(data)
        if mutate_report:mutate_report(r)
        if refresh_shape:
            for response in data.values():response['wire_shape']=shape(response['body'])
        (dest/'responses.json').write_text(json.dumps(data,ensure_ascii=False));(dest/'report.json').write_text(json.dumps(r))
        process=subprocess.run([sys.executable,str(script),str(b),str(dest)],capture_output=True,text=True)
        if (process.returncode==0)!=accept:raise AssertionError('Comparator did not enforce '+name)
        passed.append(name)
    def group(d):return d['TERMINAL_FULL']['body']['data']['list'][0]
    run('ten exact HTTP/source/wire scenarios',accept=True)
    run('spec value changed',lambda d:group(d)['valueList'][0].__setitem__('spec_value','CHANGED'))
    run('numeric ID stringified',lambda d:group(d).__setitem__('spec_id',str(group(d)['spec_id'])))
    run('unknown unowned numeric ID',lambda d:group(d).__setitem__('spec_id',2147483647))
    run('value array reordered',lambda d:group(d)['valueList'].reverse())
    run('empty wire list object versus array',lambda d:d['TERMINAL_CLEANED_FULL']['body']['data'].__setitem__('list',{}))
    run('wire evidence inconsistent with body',lambda d:d['TERMINAL_CLEANED_FULL']['wire_shape']['properties']['data']['properties'].__setitem__('list',{'type':'object','properties':[]}),refresh_shape=False)
    run('business failure',lambda d:d['TERMINAL_FULL']['body'].__setitem__('status',500))
    run('same trace',lambda d:d['TERMINAL_FULL']['body'].__setitem__('trace_id',json.loads((b/'responses.json').read_text())['TERMINAL_FULL']['body']['trace_id']))
    run('fixture cleanup missing',mutate_report=lambda r:r['independent_remaining'].__setitem__('spec',1))
    run('required scenario missing',lambda d:d.pop('TERMINAL_FULL'))
print(json.dumps({'tests':len(passed),'passed':passed,'private_temporary_copies_cleaned':True},indent=2))
