324 lines
22 KiB
JavaScript
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/)
|
|
})
|