'use strict'; const assert=require('assert/strict'); const fs=require('fs'); const fsp=fs.promises; const os=require('os'); const path=require('path'); const {spawn,spawnSync}=require('child_process'); const {read,functionSource,root}=require('./helpers/app-source'); const app=read('app.js'),runtime=read('runtime-config.js'),html=read('index.html'),serverSource=read('server.js'),serviceSource=read('scripts/service-control.js'); assert.match(runtime,/cloudApi:true/); assert.match(runtime,/singleSharedWorld:true/); assert(!app.includes('同期コード')&&!app.includes('端末のみ'),'Retired local/sync-code UI remains'); assert(html.includes('class="shared-indicator"')&&html.includes('共有')&&!html.includes('共有 · 接続中')&&!html.includes('端末のみ'),'Shared state is not a fixed-size indicator'); assert(!functionSource('setCloudStatus').includes('textContent')&&functionSource('setCloudStatus').includes('cloudBtn.dataset.state'),'Shared status still changes visible text or player names'); assert(functionSource('init').indexOf('await initCloudSync({startup:true})')new Promise(resolve=>setTimeout(resolve,ms)); (async()=>{ const temp=await fsp.mkdtemp(path.join(os.tmpdir(),'linkfield-single-world-')); const publicDir=path.join(temp,'public'); const dataRoot=path.join(temp,'data-root'); const env={...process.env,HOST:'127.0.0.1',PORT:'0',LINK_FIELD_PUBLIC_DIR:publicDir,LINK_FIELD_TEST_DATA_ROOT:dataRoot,LINK_FIELD_APACHE_BRIDGE:'0'}; const first=spawn(process.execPath,[path.join(root,'server.js')],{env,stdio:['ignore','pipe','pipe']}); let firstErr='';first.stderr.on('data',chunk=>firstErr+=chunk); try{ let port=0; for(let i=0;i<100;i++){ try{port=Number((await fsp.readFile(path.join(publicDir,'.linkfield-port'),'utf8')).trim());if(port)break}catch(_){ } if(first.exitCode!=null)throw new Error(firstErr||`First server exited with ${first.exitCode}`); await sleep(40); } assert(port>0,'First single-world server did not start'); const status=await (await fetch(`http://127.0.0.1:${port}/api/cloud/status`)).json(); assert.equal(status.singleWorld,true); assert.equal(status.revision,0); const second=spawnSync(process.execPath,[path.join(root,'server.js')],{env,encoding:'utf8',timeout:10000}); assert.notEqual(second.status,0,'A second shared-world server started against the same data root'); assert.match(`${second.stdout}\n${second.stderr}`,/already running/i); }finally{ first.kill('SIGTERM'); for(let i=0;i<50&&first.exitCode==null;i++)await sleep(20); if(first.exitCode==null)first.kill('SIGKILL'); await fsp.rm(temp,{recursive:true,force:true}); } console.log('LinkField v48.0 /link-field/world single shared world guards passed'); })().catch(error=>{console.error(error);process.exitCode=1});