Files
EscapistsReintroduced/tests/test_dedicated_preflight.py
T
jakub cab998fb64 Combine Workshop addon and fix prison startup
Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
2026-09-20 16:42:52 +02:00

105 lines
6.0 KiB
Python

"""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/core'
path = addon/header
path.parent.mkdir(parents=True, exist_ok=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()