Files
baiyue-firstleg/scripts/order-submit-dependency-fault-executor.test.mjs

324 lines
22 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { spawnSync } from 'node:child_process'
import { fileURLToPath } from 'node:url'
import { buildOrderDependencyFaultPlan } from './admin-test-env-modules/orders.mjs'
import { scopeFor, renderOrderFaultAction, createOrderFaultRuntime, runOrderFaultWindow, waitForRecoveredSubmission } from './order-submit-dependency-fault-executor.mjs'
const image = 'sha256:' + 'a'.repeat(64)
function fixture() {
const detail = id => ({order:{id,tenantId:'20',receivingWarehouseId:'30',orderNo:`FL${id}`,version:2,status:'DRAFT'},cargoes:[],parties:[],internalNotes:[],statusHistory:[]})
const raw={marker:'TEST-DEPENDENCY',customerId:'20',registeredCustomerId:'20',merchant:detail('101'),platform:detail('102')}
return {raw,plan:buildOrderDependencyFaultPlan(raw)}
}
const env={CHONGCHONG_ENV:'test',JENKINS_URL:'https://offline.invalid/',JENKINS_USER:'offline',JENKINS_API_TOKEN:'never-print-this',FBA_CORE_JENKINS_AGENT:'test-agent'}
function recoverySnapshot(projected=true) {
const {raw,plan}=fixture(),detail=structuredClone(raw.merchant)
detail.order.status='SUBMITTED';detail.order.version=3
detail.statusHistory=[{action:'CREATE'},{action:'SUBMITTED'}]
return {plan,snapshot:{detail,task:null,inbounds:projected?[{id:'1001',orderId:'101',orderNo:'FL101',tenantId:'20',orderStatusSnapshot:'SUBMITTED'}]:[],inboundPageComplete:true}}
}
test('recovery waits for the actual inbound projection even when two empty reads are stable',async()=>{
const {plan,snapshot}=recoverySnapshot(),empty=recoverySnapshot(false).snapshot
const values=[empty,empty,snapshot,snapshot];let reads=0
const value=await waitForRecoveredSubmission({plan,role:'merchant',snapshot:async()=>values[Math.min(reads++,values.length-1)],wait:async()=>{}})
assert.equal(reads,4);assert.deepEqual(value,snapshot)
})
test('missing submission projection cannot pass recovery after the bounded wait',async()=>{
const {plan,snapshot}=recoverySnapshot(false);let reads=0
await assert.rejects(waitForRecoveredSubmission({plan,role:'merchant',snapshot:async()=>{reads++;return snapshot},wait:async()=>{}}),/RECOVERY_PROJECTION_TIMEOUT/)
assert.equal(reads,61)
})
test('recovery rejects duplicate or foreign projected inbounds instead of waiting them away',async()=>{
for(const change of [s=>s.inbounds.push({...s.inbounds[0],id:'1002'}),s=>s.inbounds[0].tenantId='21',s=>s.inbounds[0].orderId='102']) {
const {plan,snapshot}=recoverySnapshot();change(snapshot)
await assert.rejects(waitForRecoveredSubmission({plan,role:'merchant',snapshot:async()=>snapshot,wait:async()=>{}}),/RECOVERY_SUBMISSION_FACTS_INVALID/)
}
})
test('recovery projection wait rejects order fact drift and incomplete pagination',async()=>{
const {plan,snapshot}=recoverySnapshot(),changed=structuredClone(snapshot);changed.detail.order.remark='late mutation'
let reads=0
await assert.rejects(waitForRecoveredSubmission({plan,role:'merchant',snapshot:async()=>reads++?changed:snapshot,wait:async()=>{}}),/RECOVERY_ORDER_DRIFT/)
const incomplete=structuredClone(snapshot);incomplete.inboundPageComplete=false
await assert.rejects(waitForRecoveredSubmission({plan,role:'merchant',snapshot:async()=>incomplete,wait:async()=>{}}),/RECOVERY_SUBMISSION_FACTS_INVALID/)
})
test('runtime classifies dependency HTTP stages without exposing tokens, URLs or response bodies',()=>{
const script=String.raw`
import importlib.util,sys,urllib.error
spec=importlib.util.spec_from_file_location('diagnostic_runtime',sys.argv[1]);m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
for path,expected in [('/nacos/v3/auth/user/login','NACOS_LOGIN_404'),('/nacos/v3/admin/cs/config','NACOS_CONFIG_404'),('/internal/finance/wallet','DEPENDENCY_HTTP_404')]:
error=urllib.error.HTTPError('http://private.invalid'+path+'?accessToken=secret',404,'private response secret',{},None)
assert m.safe_error_code(error)==expected
assert m.safe_error_code(urllib.error.URLError('http://private.invalid?secret'))=='DEPENDENCY_TRANSPORT_FAILED'
assert m.safe_error_code(RuntimeError('KNOWN_FAILURE'))=='KNOWN_FAILURE'
assert m.safe_error_code(ValueError('secret in response'))=='ORDER_DEPENDENCY_ACTION_FAILED'
`
const result=spawnSync(process.env.PYTHON||'python',['-c',script,fileURLToPath(new URL('./order-submit-dependency-fault-runtime.py',import.meta.url))],{encoding:'utf8',env:{...process.env,PYTHONDONTWRITEBYTECODE:'1'}})
assert.equal(result.status,0,result.stderr||result.error?.message)
})
test('renderer binds TEST scope and emits no Jenkins credential; ambiguous remote mutation is never retried',async()=>{
const {plan}=fixture(),scope=scopeFor(plan)
assert.ok(!renderOrderFaultAction('status',scope,env.FBA_CORE_JENKINS_AGENT).includes(env.JENKINS_API_TOKEN))
assert.throws(()=>renderOrderFaultAction('delete-all',scope,'agent'))
assert.throws(()=>renderOrderFaultAction('prepare',scope,'agent',{routing:{configurationReviewed:true,password:'must-not-embed'}}))
assert.throws(()=>createOrderFaultRuntime({...env,CHONGCHONG_ENV:'prod'},scope))
for(const mutate of [p=>p.phases[0].expectedCode='A0403',p=>p.phases[0].targetPath+='/other',p=>p.phases[0].expectedMessages.push('anything')]) {
const changed=structuredClone(plan);mutate(changed);assert.throws(()=>scopeFor(changed))
}
let calls=0
const runtime=createOrderFaultRuntime(env,scope,async()=>{calls++;throw Error('connection lost with secret')})
await assert.rejects(runtime('activate'),/OUTCOME_UNKNOWN_USE_STATUS/)
assert.equal(calls,1)
})
test('renderer chunks every embedded Groovy literal and preserves the complete runtime payload',()=>{
const {plan}=fixture(),scope=scopeFor(plan)
const routing={configurationReviewed:true,nacos:{baseUrl:'http://nacos.invalid:8848',loginPath:'/nacos/v3/auth/user/login',configPath:'/nacos/v3/admin/cs/config'},upstreams:{IAM:'http://iam.invalid:9201',FINANCE:'http://finance.invalid:9203',WAREHOUSE:'http://warehouse.invalid:9205'}}
const rendered=renderOrderFaultAction('preflight',scope,'test-agent',{routing})
const arrays=text=>Array.from(text.matchAll(/\[('[A-Za-z0-9+/=]+'(?:,'[A-Za-z0-9+/=]+')*)\]\.join\(''\)/g),match=>Array.from(match[1].matchAll(/'([^']+)'/g),part=>part[1]))
const outer=arrays(rendered)
assert.equal(outer.length,1)
const inner=Buffer.from(outer[0].join(''),'base64').toString('utf8')
assert.ok(Array.from(inner.matchAll(/'([^']*)'/g),match=>match[1].length).every(length=>length<=4000))
const embedded=arrays(inner)
assert.equal(embedded.length,2)
const config=JSON.parse(Buffer.from(embedded[1].join(''),'base64').toString('utf8'))
assert.deepEqual(config.scope,scope)
assert.deepEqual(config.routing,routing)
assert.equal(config.action,'preflight')
assert.ok(Buffer.from(config.commonSource,'base64').length>0)
})
function windowFixture(fault='') {
const {raw,plan}=fixture(),calls=[],records=[]
let mode='PASS'
const options={plan,routing:{configurationReviewed:true},expectedImageId:image,wait:async()=>{},
async action(action,extra={}) {
calls.push(action)
if(action==='prepare'&&fault==='prepare-unknown')throw Error('PREPARE_OUTCOME_UNKNOWN')
if(action==='restore'&&['restore-unknown','prepare-unknown'].includes(fault))throw Error('UNCERTAIN_MUTATION_REQUIRES_RECONCILE')
if(action==='status')return {proxy:{mode:'PASS'}}
if(action==='health')return {healthy:true}
if(action==='mode'){mode=extra.mode;return {mode}}
if(action==='records')return {records}
if(action==='verify-restored')return {originalImageId:image,currentImageId:image,originalFingerprint:'same',currentFingerprint:'same',upstreamReads:{IAM:true,FINANCE:true,WAREHOUSE:true},nacosSourcesUnchanged:true}
if(action==='finish')return {restored:true,healthy:true,proxyStopped:true,orderRoutesVerified:true}
return {}
},
async snapshot(role){return {detail:structuredClone(raw[role]),task:null,inbounds:[],inboundPageComplete:true}},
async submit(role,request){
const phase=plan.phases.find(item=>item.dependency===mode)
records.push({requestId:request.headers['X-Request-Id'],dependency:mode,method:'GET',path:phase.targetPath,
matched:true,responseStatus:503,forwarded:false,internalTokenPresent:true})
return {http:200,body:{code:fault==='wrong-business-code'?'A0403':'S0503',message:phase.expectedMessages[0]}}
},
async recovery(){calls.push('business-recovery');if(fault==='business-recovery-failed')throw Error('RECOVERY_REPLAY_DRIFT')
return {merchant:{orderId:'101',status:'SUBMITTED',sameKeyReplayVerified:true},platform:{orderId:'102',status:'SUBMITTED',sameKeyReplayVerified:true}}},
}
return {options,calls}
}
test('six observations restore original routes and verify business recovery before stopping proxy',async()=>{
const {options,calls}=windowFixture(),report=await runOrderFaultWindow(options)
assert.equal(report.success,true);assert.equal(report.observations.length,6)
assert.ok(calls.indexOf('restore')<calls.indexOf('verify-restored'))
assert.ok(calls.indexOf('verify-restored')<calls.indexOf('business-recovery'))
assert.ok(calls.indexOf('business-recovery')<calls.indexOf('finish'))
})
test('failed business assertion still restores, but never reports fault verification success',async()=>{
const {options,calls}=windowFixture('wrong-business-code'),report=await runOrderFaultWindow(options)
assert.equal(report.success,false);assert.equal(report.restoration.restored,true)
assert.ok(calls.includes('finish'))
})
test('a redirected public warehouse plan stops before any runtime mutation',async()=>{
const {options,calls}=windowFixture()
options.plan.receivingWarehouseId='31'
options.plan.phases[2].targetPath='/internal/warehouses/0/31/active'
const report=await runOrderFaultWindow(options)
assert.equal(report.success,false)
assert.ok(!calls.includes('prepare'))
assert.ok(!calls.includes('activate'))
})
test('unverified warehouse restoration blocks business recovery and proxy finish',async()=>{
const {options,calls}=windowFixture(),original=options.action
options.action=async(action,extra)=>{
const result=await original(action,extra)
if(action==='verify-restored')result.upstreamReads.WAREHOUSE=false
return result
}
const report=await runOrderFaultWindow(options)
assert.equal(report.success,false)
assert.ok(calls.includes('restore'))
assert.ok(!calls.includes('business-recovery'))
assert.ok(!calls.includes('finish'))
})
for(const fault of ['prepare-unknown','restore-unknown','business-recovery-failed'])test(`unsafe cleanup blocks finish: ${fault}`,async()=>{
const {options,calls}=windowFixture(fault),report=await runOrderFaultWindow(options)
assert.equal(report.success,false);assert.equal(calls.filter(action=>action==='prepare').length,1)
assert.equal(calls.filter(action=>action==='restore').length,1);assert.ok(!calls.includes('finish'))
})
test('offline runtime journals Docker transitions, reconciles observed outcomes and retains original configuration (no Docker/HTTP)',()=>{
const script=String.raw`
import importlib.util,sys,copy,json,unittest
from pathlib import Path
spec=importlib.util.spec_from_file_location('runtime',sys.argv[1]);m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
common=m.common_tools(Path(sys.argv[2]).read_text(encoding='utf-8'))
class Fake:
def __init__(self):
self.current={'Id':'original-container-id','Name':'/firstleg-test-order','Image':'sha256:'+'a'*64,
'Config':{'Image':'offline/order:tag','Env':['DEPLOY_ENV=test','SERVER_PORT=9204','INTERNAL_SERVICE_TOKEN=offline-secret-long'],
'Entrypoint':copy.deepcopy(m.ENTRYPOINT),'Cmd':None,'Labels':{'com.docker.compose.project':'firstleg-test'}},
'HostConfig':{'AutoRemove':False,'ExtraHosts':['host.docker.internal:host-gateway']},'Mounts':[],
'NetworkSettings':{'Networks':{'test':{'IPAddress':'172.20.0.10','NetworkID':'n1','Aliases':['order','original-con'],'MacAddress':'aa:bb:cc:dd:ee:ff'}}},
'State':{'Running':True,'Health':{'Status':'healthy'}}}
self.original=copy.deepcopy(self.current);self.calls=[];self.fail=None;self.failBefore=None;self.serial=0
def inspect(self,name):
if not self.current or name not in ('firstleg-test-order',self.current['Id']):raise RuntimeError('DOCKER_404')
return copy.deepcopy(self.current)
def docker(self,method,path,body=None):
if method=='GET':return {'Id':self.original['Image']}
self.calls.append((method,path));result=None
if self.failBefore=='stop' and '/stop?' in path:raise RuntimeError('REQUEST_OUTCOME_UNKNOWN')
if '/stop?' in path:self.current['State']['Running']=False;op='stop'
elif method=='DELETE':self.current=None;op='delete'
elif '/create?' in path:
self.serial+=1;new_id='new-container-'+str(self.serial);op='create'
self.current={'Id':new_id,'Name':'/firstleg-test-order','Image':self.original['Image'],
'Config':{k:copy.deepcopy(v) for k,v in body.items() if k not in ('HostConfig','NetworkingConfig')},
'HostConfig':copy.deepcopy(body['HostConfig']),'Mounts':[],
'NetworkSettings':{'Networks':{}},'State':{'Running':False,'Health':{'Status':'healthy'}}}
for name,entry in body['NetworkingConfig']['EndpointsConfig'].items():
self.current['NetworkSettings']['Networks'][name]={'IPAddress':entry['IPAMConfig']['IPv4Address'],'NetworkID':'n1',
'Aliases':entry['Aliases']+[new_id[:12]],'MacAddress':entry['MacAddress'],'IPAMConfig':entry['IPAMConfig']}
result={'Id':new_id}
elif path.endswith('/start'):self.current['State']['Running']=True;op='start'
else:raise AssertionError(path)
if self.fail==op:self.fail=None;raise RuntimeError('RESPONSE_LOST')
return result
class RuntimeTests(unittest.TestCase):
def fixture(self):
f=Fake();tools=dict(common);tools.update(inspect=f.inspect,docker=f.docker)
scope={'marker':'OFFLINE','customerId':'7','receivingWarehouseId':'30','orderIds':{'merchant':'10','platform':'11'},'planSha256':'b'*64}
r=m.Runtime(scope,tools,Path('offline-not-created'));saved=[];controls=[]
r.save=lambda state:saved.append(copy.deepcopy(state));r.control=lambda state,mode=None,ttl=0:(controls.append(mode) or {'mode':'PASS'})
r.probes=lambda state:{'IAM':True,'FINANCE':True,'WAREHOUSE':True}
r.config_fingerprint=lambda container,routing:'reviewed-config'
state={'scope':scope,'phase':'PROXY_STARTED','originalId':f.original['Id'],'originalImageId':f.original['Image'],
'originalFingerprint':common['fingerprint'](f.original),'originalBody':common['recreation'](f.original,common['env_of'](f.original),full=True),
'proxyPort':12345,'controlPort':12346,'mutation':None,'routing':{},'routingSourceSha256':'reviewed-config'}
return f,r,state,saved,controls
def test_warehouse_probe_requires_the_exact_active_public_domestic_warehouse(self):
import io
from unittest.mock import patch
f,r,s,saved,controls=self.fixture()
state={'internalToken':'never-public','upstreams':{'IAM':'http://iam.invalid','FINANCE':'http://finance.invalid','WAREHOUSE':'http://warehouse.invalid'}}
warehouse={'id':30,'tenantId':0,'status':'ACTIVE','warehouseType':'DOMESTIC','warehouseName':'Scoped receiving warehouse'}
class Response(io.BytesIO):status=200
def response(request,timeout):
url=request.full_url
if '/internal/iam/' in url:data={'eligible':True}
elif '/internal/finance/' in url:data={'actualBalance':0}
else:
self.assertTrue(url.endswith('/internal/warehouses/0/30/active'))
data={'warehouse':warehouse}
return Response(json.dumps({'code':'200','data':data}).encode())
with patch.object(m,'http_open',side_effect=response):
self.assertEqual(m.Runtime.probes(r,state),{'IAM':True,'FINANCE':True,'WAREHOUSE':True})
for field,value in [('id',31),('tenantId',20),('status','DISABLED'),('warehouseType','FBA'),('warehouseName','')]:
original=warehouse[field];warehouse[field]=value
with self.assertRaisesRegex(RuntimeError,'RECEIVING_WAREHOUSE_NOT_READY'):m.Runtime.probes(r,state)
warehouse[field]=original
self.assertEqual(f.calls,[])
def test_original_fingerprint_config_and_image_restored(self):
f,r,s,saved,controls=self.fixture();r.activate(s)
self.assertTrue(r.matches_injected(f.current,s));self.assertEqual(s['phase'],'INJECTED')
self.assertTrue(any(x.get('mutation',{}).get('step')=='CREATE_INTENT' for x in saved if x.get('mutation')))
r.restore(s);result=r.verify_restored(s)
self.assertEqual(result['originalFingerprint'],result['currentFingerprint']);self.assertEqual(f.current['Config'],f.original['Config'])
def test_unknown_outcomes_never_auto_repeat_docker_and_can_be_explicitly_reconciled(self):
for operation in ['stop','delete','create','start']:
f,r,s,saved,controls=self.fixture();f.fail=operation
with self.assertRaisesRegex(RuntimeError,'UNCERTAIN'):r.activate(s)
count=len(f.calls)
with self.assertRaisesRegex(RuntimeError,'RECONCILE'):r.restore(s)
self.assertEqual(len(f.calls),count);self.assertEqual(controls[-1],'PASS')
r.reconcile(s);self.assertEqual(len(f.calls),count)
r.restore(s);self.assertTrue(r.verify_restored(s)['healthy'])
def test_unrecognized_configuration_is_never_overwritten(self):
f,r,s,saved,controls=self.fixture();r.activate(s);f.current['Config']['Env'].append('UNRELATED_CHANGE=1');count=len(f.calls)
with self.assertRaisesRegex(RuntimeError,'UNRECOGNIZED'):r.restore(s)
self.assertEqual(len(f.calls),count)
def test_unknown_stop_without_observed_stop_is_not_misreported_as_restored(self):
f,r,s,saved,controls=self.fixture();f.failBefore='stop'
with self.assertRaisesRegex(RuntimeError,'UNCERTAIN'):r.activate(s)
count=len(f.calls)
with self.assertRaisesRegex(RuntimeError,'NO_BLIND_RECREATE'):r.reconcile(s)
self.assertTrue(s['uncertain']);self.assertTrue(f.current['State']['Running']);self.assertEqual(len(f.calls),count)
def test_restore_still_runs_when_proxy_control_is_unreachable(self):
f,r,s,saved,controls=self.fixture();r.activate(s)
def unavailable(*args):raise RuntimeError('CONTROL_LOST')
r.control=unavailable;r.restore(s);self.assertTrue(r.verify_restored(s)['healthy'])
def test_finish_refuses_unproved_business_route_recovery(self):
f,r,s,saved,controls=self.fixture();r.activate(s);r.restore(s)
from unittest.mock import patch
with patch.object(Path,'read_text',return_value='OFFLINE'),patch.object(Path,'is_symlink',return_value=False):
with self.assertRaisesRegex(RuntimeError,'ROUTES_NOT_PROVED'):r.finish(s,{})
def test_changed_nacos_configuration_prevents_recovery_claim(self):
f,r,s,saved,controls=self.fixture();r.activate(s);r.restore(s);r.config_fingerprint=lambda *args:'changed'
with self.assertRaisesRegex(RuntimeError,'NACOS_ROUTE_CONFIGURATION_CHANGED'):r.verify_restored(s)
def test_nacos_source_reads_are_explicit_authenticated_and_redacted(self):
from unittest.mock import patch
f,r,s,saved,controls=self.fixture()
f.current['Config']['Env']+=['NACOS_SERVER_ADDR=host.docker.internal:8848','NACOS_USERNAME=offline-user',
'NACOS_PASSWORD=offline-password','NACOS_GROUP=BAIYUE','NACOS_NAMESPACE=test-only']
routing={'nacos':{'baseUrl':'http://127.0.0.1:8848','loginPath':'/nacos/v3/auth/user/login','configPath':'/nacos/v3/admin/cs/config'}}
calls=[]
class Response:
status=200
def __init__(self,value):self.value=value
def __enter__(self):return self
def __exit__(self,*args):pass
def read(self):return self.value
def read(req,timeout):
calls.append(req)
if req.data is not None:return Response(b'{"accessToken":"offline-token"}')
query=m.urllib.parse.parse_qs(m.urllib.parse.urlsplit(req.full_url).query)
self.assertEqual(req.get_method(),'GET');self.assertEqual(req.get_header('Authorization'),'Bearer offline-token')
self.assertNotIn('offline-token',req.full_url)
self.assertEqual(query['namespaceId'],['test-only']);self.assertEqual(query['groupName'],['BAIYUE'])
return Response(json.dumps({'code':0,'data':{'dataId':query['dataId'][0],'namespaceId':'test-only','groupName':'BAIYUE','content':'spring:\n test: only\n'}}).encode())
with patch.object(m,'http_open',side_effect=read):
value=m.Runtime.config_fingerprint(r,f.current,routing)
self.assertEqual(len(value),64);self.assertEqual(len(calls),3)
self.assertIn(b'password=offline-password',calls[0].data)
self.assertTrue(all('/nacos/v3/admin/cs/config?' in req.full_url for req in calls[1:]))
with patch.object(m,'http_open',return_value=Response(b'{"accessToken":"offline-token","code":0,"data":{"content":"secret config","dataId":"foreign"}}')):
with self.assertRaisesRegex(RuntimeError,'NACOS_SOURCE_IDENTITY_MISMATCH'):m.Runtime.config_fingerprint(r,f.current,routing)
routing['nacos']['baseUrl']='http://other.invalid:8848'
with self.assertRaisesRegex(RuntimeError,'EXPLICIT_NACOS'):m.Runtime.config_fingerprint(r,f.current,routing)
self.assertEqual(len(calls),3)
def test_effective_source_extraction_excludes_all_event_and_sql_functions(self):
self.assertEqual(set(common),{'DockerConnection','docker','inspect','env_of','require_test','recreation','fingerprint'})
def test_control_token_is_not_sent_to_an_unowned_listener(self):
from unittest.mock import patch
f,r,s,saved,controls=self.fixture();s.update(proxyPid=777,controlToken='private-control')
command=str(r.directory/'proxy.py').encode()+b'\0'
tcp='header\n 0: 00000000:3039 00000000:0000 0A 0:0 0:0 0 1000 0 123\n'
with patch.object(Path,'read_bytes',return_value=command),patch.object(Path,'iterdir',return_value=iter([Path('fd')])), \
patch.object(m.os,'readlink',return_value='socket:[123]'),patch.object(Path,'read_text',return_value=tcp), \
patch.object(m,'http_open') as request:
with self.assertRaisesRegex(RuntimeError,'LISTENERS_NOT_OWNED'):m.Runtime.control(r,s)
request.assert_not_called()
result=unittest.TextTestRunner().run(unittest.defaultTestLoader.loadTestsFromTestCase(RuntimeTests))
sys.exit(0 if result.wasSuccessful() else 1)
`
const result=spawnSync(process.env.PYTHON||'python',['-c',script,
fileURLToPath(new URL('./order-submit-dependency-fault-runtime.py',import.meta.url)),
fileURLToPath(new URL('./test-event-fault-runtime.py',import.meta.url))],{encoding:'utf8',env:{...process.env,PYTHONDONTWRITEBYTECODE:'1'}})
assert.equal(result.status,0,result.stderr || result.error?.message)
assert.match(result.stderr,/Ran 11 tests/)
})