Advance standalone admission, finite artillery and native validation
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
"""Synthetic files test no-launch planning; never execute a server or client."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]/'tools'))
|
||||
import dedicated_preflight as tool
|
||||
|
||||
|
||||
class DedicatedPreflightTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.tmp.cleanup)
|
||||
root = Path(self.tmp.name)
|
||||
self.source, self.runtime = root/'source', root/'runtime'
|
||||
self.runtime.mkdir()
|
||||
for project, header in tool.HEADERS.items():
|
||||
addon = self.source/'addons'/project
|
||||
path = addon/header
|
||||
path.parent.mkdir(parents=True)
|
||||
(addon/'addon.gproj').write_text('GameProject { GUID "2528466DE220D91C" }')
|
||||
path.write_text('SCR_MissionHeader { World "'+tool.WORLD+'" SystemsConfig "'+tool.SYSTEMS+'" }')
|
||||
path.with_suffix('.conf.meta').write_text('MetaFileClass { Name "{AD62322D5213C440}'+header+'" }')
|
||||
(self.runtime/'runtime-origin.json').write_text(json.dumps({'app_id':1874900,'purpose':'private anonymous Reforger dedicated server preparation; no server launched'}))
|
||||
|
||||
def plan(self, **kwargs):
|
||||
return tool.build_plan(self.source,self.runtime,**kwargs)
|
||||
|
||||
def installed(self, app='1874900', architecture=62):
|
||||
directory=self.runtime/'server';directory.mkdir(exist_ok=True)
|
||||
binary=directory/'ArmaReforgerServer'
|
||||
data=bytearray(20);data[:6]=b'\x7fELF\x02\x01';data[18:20]=architecture.to_bytes(2,'little')
|
||||
binary.write_bytes(data);binary.chmod(0o700)
|
||||
manifests=directory/'steamapps';manifests.mkdir(exist_ok=True)
|
||||
(manifests/'appmanifest_1874900.acf').write_text('"AppState" { "appid" "'+app+'" "buildid" "synthetic-build" "StateFlags" "4" }')
|
||||
return binary
|
||||
|
||||
def test_missing_runtime_still_prepares_strict_loopback_review(self):
|
||||
result,config,_=self.plan()
|
||||
self.assertFalse(result['launch_allowed']);self.assertFalse(result['multiplayer_passed'])
|
||||
self.assertEqual(result['genuine_clients_verified'],0)
|
||||
self.assertIn('missing or incomplete',result['blockers'][0])
|
||||
self.assertEqual(config['bindAddress'],'127.0.0.1')
|
||||
self.assertEqual(config['publicAddress'],'127.0.0.1')
|
||||
self.assertFalse(config['game']['visible'])
|
||||
self.assertTrue(config['game']['gameProperties']['fastValidation'])
|
||||
self.assertTrue(config['game']['gameProperties']['battlEye'])
|
||||
self.assertTrue(config['operating']['disableCrashReporter'])
|
||||
self.assertNotIn('rcon',config);self.assertNotIn('a2s',config)
|
||||
|
||||
def test_binary_is_inspected_never_executed_and_cannot_establish_mp(self):
|
||||
self.installed()
|
||||
with patch('subprocess.run',side_effect=AssertionError('must not run')),patch('subprocess.Popen',side_effect=AssertionError('must not spawn')):
|
||||
result,_,_=self.plan()
|
||||
runtime=result['runtime']
|
||||
self.assertTrue(runtime['binary_elf_x86_64']);self.assertTrue(runtime['install_complete'])
|
||||
self.assertEqual(runtime['steam_build_id'],'synthetic-build')
|
||||
self.assertIsNone(runtime['engine_version']);self.assertFalse(runtime['native_runtime_executed'])
|
||||
self.assertFalse(result['launch_allowed']);self.assertTrue(result['blockers'])
|
||||
|
||||
def test_wrong_app_rejected_wrong_arch_incomplete(self):
|
||||
self.installed(app='1890870')
|
||||
with self.assertRaisesRegex(ValueError,'stable dedicated'):self.plan()
|
||||
self.installed(architecture=183)
|
||||
result,_,_=self.plan();self.assertFalse(result['runtime']['binary_elf_x86_64'])
|
||||
self.assertIn('missing or incomplete',result['blockers'][0])
|
||||
|
||||
def test_symlink_to_existing_server_rejected(self):
|
||||
binary=self.installed();target=self.runtime/'other-server';binary.rename(target);binary.symlink_to(target)
|
||||
with self.assertRaisesRegex(ValueError,'symlink'):self.plan()
|
||||
|
||||
def test_header_and_port_validation(self):
|
||||
for port in (0,1023,65536):
|
||||
with self.assertRaisesRegex(ValueError,'unprivileged'):self.plan(port=port)
|
||||
header=self.source/'addons/core'/tool.HEADERS['core']
|
||||
header.write_text(header.read_text().replace(tool.SYSTEMS,'wrong'))
|
||||
with self.assertRaisesRegex(ValueError,'SystemsConfig'):self.plan()
|
||||
|
||||
def test_project_selection_and_source_snapshot_change(self):
|
||||
before,config,_=self.plan(project='chat')
|
||||
self.assertEqual(before['scenario']['project'],'chat')
|
||||
self.assertTrue(config['game']['scenarioId'].endswith(tool.HEADERS['chat']))
|
||||
(self.source/'addons/core/new.c').write_text('// independent new source')
|
||||
after,_,_=self.plan(project='chat')
|
||||
self.assertNotEqual(before['source_manifest_sha256'],after['source_manifest_sha256'])
|
||||
|
||||
def test_write_new_private_review_only_preserves_existing_evidence(self):
|
||||
result,config,manifest=self.plan();destination=self.runtime/'review'
|
||||
tool.write_plan(destination,result,config,manifest)
|
||||
self.assertEqual(json.loads((destination/'server-config.review.json').read_text()),config)
|
||||
self.assertFalse(json.loads((destination/'preflight.json').read_text())['listener_started'])
|
||||
self.assertEqual(tool.digest(destination/'source-manifest.json'),result['source_manifest_sha256'])
|
||||
with self.assertRaisesRegex(ValueError,'never overwritten'):tool.write_plan(destination,result,config,manifest)
|
||||
with self.assertRaisesRegex(ValueError,'direct child'):tool.write_plan(self.source/'review',result,config,manifest)
|
||||
|
||||
def test_unmarked_runtime_and_source_overlap_refused(self):
|
||||
result,config,manifest=self.plan();(self.runtime/'runtime-origin.json').unlink()
|
||||
with self.assertRaisesRegex(ValueError,'origin marker'):tool.write_plan(self.runtime/'review',result,config,manifest)
|
||||
with self.assertRaisesRegex(ValueError,'separate'):tool.build_plan(self.source,self.source/'runtime')
|
||||
|
||||
|
||||
if __name__=='__main__':unittest.main()
|
||||
Reference in New Issue
Block a user