Files
ARCADIA-Studio/harness/experiments/compact-help-20261001/package/server-smoke-unicode02/run.py
T

116 lines
8.8 KiB
Python

"""Fresh package server/runtime pairing check; prior smoke logic retained."""
import copy, hashlib, http.client, json, os, pathlib, platform, socket, subprocess, sys, time, traceback
ROOT=pathlib.Path(__file__).resolve().parent
QA=ROOT.parent
result={'status':'RUNNING','source':'715036e132677dd2bc2fb37ed316d93dc9987aec','checks':[],'processes':[]}
def dump(name,obj): (ROOT/name).write_text(json.dumps(obj,indent=2,ensure_ascii=False),encoding='utf-8')
def check(value,why):
if not value: raise AssertionError(why)
result['checks'].append(why)
def sha(p): return hashlib.sha256(p.read_bytes()).hexdigest()
record=json.loads((QA/'extraction-check.json').read_text())
PIN=record['archive_sha256']
archive=QA/'dist'/record['archive']
check(sha(archive)==PIN,'Fresh extracted archive identity independently rechecked')
exe=pathlib.Path(record['bundle'])/'arcadia-server.exe'
check(sha(exe)=='6cebacc117e46fdab023c6402a5cebd6929fb55993ae8ff6b89cd1c6c1170237','Exact released server bytes retained')
check(sha(exe.parent/'libunwind.dll')=='f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4','Exact new desktop runtime pairing selected')
package=json.loads((exe.parent/'package.json').read_text(encoding='utf-8-sig'))
dump('package-provenance.json',package)
win=os.environ.get('SystemRoot',r'C:\Windows')
env={key:os.environ[key] for key in ['SystemRoot','WINDIR','TEMP','TMP','USERPROFILE','LOCALAPPDATA','APPDATA','COMSPEC','OS','PROCESSOR_ARCHITECTURE'] if key in os.environ}
env['PATH']=win+'\\System32;'+win+';'+win+'\\System32\\Wbem'
result['identity']={'executable':str(exe),'exe_sha256':sha(exe),'archive':str(archive),'archive_sha256':PIN,'platform':platform.platform(),'python_harness':sys.executable,'child_environment':env,'packaged_dlls':[{ 'name':p.name,'sha256':sha(p)} for p in exe.parent.glob('*.dll')]}
flags=subprocess.CREATE_NO_WINDOW
for arg in ['--version','--help']:
command=[str(exe),arg]
completed=subprocess.run(command,cwd=ROOT,env=env,stdout=subprocess.PIPE,stderr=subprocess.PIPE,creationflags=flags,timeout=15)
label=arg[2:];(ROOT/(label+'.stdout.txt')).write_bytes(completed.stdout);(ROOT/(label+'.stderr.txt')).write_bytes(completed.stderr)
result[label]={'command':command,'exit':completed.returncode}
check(completed.returncode==0,label+' succeeds with sanitized system-only PATH')
store=ROOT/'store'
check(not store.exists(),'Fresh isolated store does not preexist')
store.mkdir()
with socket.socket() as reserved:
reserved.bind(('127.0.0.1',0));port=reserved.getsockname()[1]
result['bind']=f'127.0.0.1:{port}'
process=None;counter=0;streams=[]
def request(method,path,body=None,headers=None):
global counter
counter+=1; prefix=f'{counter:02d}'
raw=b'' if body is None else json.dumps(body,ensure_ascii=False,separators=(',',':')).encode('utf-8')
hdr={'Host':f'127.0.0.1:{port}','Connection':'close'}
if body is not None: hdr.update({'Content-Type':'application/json','Content-Length':str(len(raw))})
hdr.update(headers or {})
(ROOT/(prefix+'.request.http')).write_bytes((method+' '+path+' HTTP/1.1\r\n'+''.join(k+': '+v+'\r\n' for k,v in hdr.items())+'\r\n').encode()+raw)
conn=http.client.HTTPConnection('127.0.0.1',port,timeout=10)
conn.request(method,path,body=raw if body is not None else None,headers=hdr)
response=conn.getresponse();data=response.read();pairs=response.getheaders();status=response.status
(ROOT/(prefix+'.response.http')).write_bytes((f'HTTP/1.1 {status} {response.reason}\r\n'+''.join(k+': '+v+'\r\n' for k,v in pairs)+'\r\n').encode()+data)
conn.close()
return status,dict((k.lower(),v) for k,v in pairs),json.loads(data) if data else None
def start(label):
global process
command=[str(exe),'--bind',f'127.0.0.1:{port}','--data-dir',str(store)]
out=open(ROOT/(label+'.stdout.log'),'wb');err=open(ROOT/(label+'.stderr.log'),'wb');streams.extend([out,err])
process=subprocess.Popen(command,cwd=ROOT,env=env,stdout=out,stderr=err,creationflags=flags)
result['processes'].append({'label':label,'command':command,'pid':process.pid})
for _ in range(100):
if process.poll() is not None: raise RuntimeError('Server exited during startup: '+str(process.returncode))
try:
with socket.create_connection(('127.0.0.1',port),timeout=.1): break
except OSError: time.sleep(.1)
status,_,_=request('GET','/health/ready');check(status==200,label+' readiness 200')
def stop(label):
global process
if process is not None:
process.terminate();code=process.wait(timeout=15)
result['processes'][-1].update({'termination':'Windows TerminateProcess after acknowledged requests (not graceful shutdown claim)','exit':code})
process=None
for stream in streams: stream.flush()
try:
start('first')
status,_,api=request('GET','/api/v1/openapi.json');check(status==200,'Served OpenAPI 200')
packaged_api=json.loads((exe.parent/'docs/openapi.json').read_text(encoding='utf-8-sig'))
check(api==packaged_api,'Served OpenAPI exactly matches packaged JSON contract')
source='arcadia 7;\r\n// Package QA authored comment — preserve exactly.\r\nproject package_qa "Package QA original" id "93200000-0000-4000-8000-000000000001" {\r\n oa_activity inspect_package "Inspect package" id "93200000-0000-4000-8000-000000000002" {}\r\n}\r\n'
bundle={'entry':'model.arcadia','files':{'model.arcadia':source}}
(ROOT/'original.arcadia').write_bytes(source.encode('utf-8'))
status,headers,created=request('POST','/api/v1/projects/import-source-project',bundle)
check(status==201,'Full authored source import 201');resource=created['resource_id'];etag1=headers['etag'];result['resource_id']=resource
path='/api/v1/projects/'+resource
status,headers,original=request('GET',path);check(status==200 and headers['etag']==etag1,'Imported envelope GET preserves exact initial ETag')
check(original['source_project']==bundle,'Import preserves exact entry, CRLF text, comment and stable IDs')
check(original['model']['id']=='93200000-0000-4000-8000-000000000001' and resource!=original['model']['id'],'Model stable ID distinct from allocated resource ID')
status,headers,read=request('GET',path+'/source-project');check(status==200 and read==bundle and headers['etag']==etag1,'Source-project GET exact complete authored bundle')
edited=copy.deepcopy(bundle);edited['files']['model.arcadia']=source.replace('Package QA original','Package QA edited')
(ROOT/'edited.arcadia').write_bytes(edited['files']['model.arcadia'].encode('utf-8'))
status,headers,mutation=request('PUT',path+'/source-project',edited,{'If-Match':etag1})
check(status==200 and mutation['changed'] is True and mutation['revision']==original['revision']+1,'Conditional edit commits one changed revision')
etag2=headers['etag'];check(etag2!=etag1 and mutation['source_project']==edited,'New ETag and exact edited authored bundle returned')
stale=copy.deepcopy(bundle);stale['files']['model.arcadia']=source.replace('Package QA original','STALE MUST NOT PERSIST')
status,_,problem=request('PUT',path+'/source-project',stale,{'If-Match':etag1})
check(status==412 and problem['code']=='revision_conflict','Stale If-Match refused with 412 revision_conflict')
status,headers,accepted=request('GET',path)
expected_model=copy.deepcopy(original['model']);expected_model['name']='Package QA edited'
check(status==200 and headers['etag']==etag2 and accepted['source_project']==edited and accepted['model']==expected_model,'Stale refusal preserves exact edited model/source pair')
stored=store/(resource+'.json');persisted=stored.read_bytes();dump('accepted-envelope.json',accepted)
check(json.loads(persisted)==accepted,'Acknowledged envelope exactly equals committed isolated store')
stop('first')
start('restart')
status,headers,reopened=request('GET',path)
check(status==200 and headers['etag']==etag2 and reopened==accepted,'Restart exact envelope, stable IDs, revision, ETag, comment and CRLF persistence')
status,headers,reopened_source=request('GET',path+'/source-project')
check(status==200 and reopened_source==edited and headers['etag']==etag2,'Restart complete source-project exact readback')
check(stored.read_bytes()==persisted,'Restart and read leave committed envelope bytes unchanged')
result['revision']=accepted['revision'];result['etag_initial']=etag1;result['etag_final']=etag2;result['store_sha256']=hashlib.sha256(persisted).hexdigest();result['status']='PASS'
except Exception:
result['status']='FAIL';result['error']=traceback.format_exc()
finally:
stop('final')
for stream in streams: stream.close()
dump('result.json',result)
print(json.dumps({'status':result['status'],'checks':len(result['checks']),'error':result.get('error')},indent=2))
sys.exit(0 if result['status']=='PASS' else 1)