#!/usr/bin/env python3 """Server smoke test: boot a dedicated NeoForge server with the pack, check it over RCON, scan the log, stop. Steps 1. Install the NeoForge server named in pack/pack.toml (cached in build/.cache). 2. Install the server-side mods from build/stage/full with packwiz-installer and check their dependency ranges (tools/check_deps.py). 3. Use the world template from build/world-template when it exists (tools/make_world.py), otherwise generate a fresh island world. 4. Start the server with RCON on a free local port and wait for "Done (". 5. Run every check in tools/smoke_checks.json over RCON (a check may have "setup" and "cleanup" command lists around its command). 6. Stop the server, then fail on log lines that match no entry in tools/smoke_allowlist.txt. Running a dedicated server needs eula=true in eula.txt. The script only writes it when called with --accept-eula, which states that you accept the Minecraft EULA (https://aka.ms/MinecraftEULA) for this test server. --load-only needs no EULA. NeoForge discovers every mod jar and prints its mod list before the server checks eula.txt, then stops at the EULA notice; mods are only constructed after that check. So it proves the loader reads every jar (with tools/check_deps.py covering dependency ranges), not that the mods start; construction, world, datapack and RCON checks need the full run. Usage: tools/smoke_test.py --accept-eula [--keep] [--timeout SECONDS] tools/smoke_test.py --load-only """ import argparse import json import re import shutil import sys import time from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) from mcserver import BUILD, ROOT, SERVER, Server, eula_accepted, prepare, scan_log # noqa: E402 TEMPLATE = BUILD / "world-template" def main(): parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("--accept-eula", action="store_true", help="write eula=true for the test server") parser.add_argument("--load-only", action="store_true", help="stop at the EULA check (no EULA needed)") parser.add_argument("--keep", action="store_true", help="keep the server world between runs") parser.add_argument("--timeout", type=int, default=900, help="seconds to wait for startup") args = parser.parse_args() if not args.accept_eula and not args.load_only: sys.exit("Refusing to start: pass --accept-eula to accept the Minecraft EULA for this test server, " "or --load-only to stop at the EULA check.") args_file = prepare() world = SERVER / "world" if not args.keep: shutil.rmtree(world, ignore_errors=True) if (TEMPLATE / "level.dat").exists(): shutil.copytree(TEMPLATE, world) print(f"using world template {TEMPLATE.relative_to(ROOT)}") if args.load_only: if eula_accepted(): sys.exit("eula.txt already accepts the EULA here; run the full test with --accept-eula instead.") else: (SERVER / "eula.txt").write_text("eula=true\n") recipe = json.loads((ROOT / "world/world.json").read_text()) failures = [] with Server(args_file, {"level-type": recipe["level_type"], "level-seed": recipe["seed"]}, stdout_name="smoke-stdout.log") as server: try: if args.load_only: server.proc.wait(timeout=args.timeout) text = server.log.read_text(errors="replace") if server.log.exists() else "" for pattern, what in ((r"Cobblemon Survival Pack Companion \S+ \(cobblesurvival\)", "companion mod in the mod list"), (r"You need to agree to the EULA", "stop at the EULA check")): if not re.search(pattern, text): failures.append(f"missing {what} in the log") print(f"mod discovery finished in {time.time() - server.started:.0f}s") else: print(f"server started in {server.wait_ready(args.timeout):.0f}s") failures += server.run_checks() server.stop() except Exception as e: # report; the context manager still kills the process failures.append(f"{type(e).__name__}: {e}") if server.log.exists(): failures += [f"log: {line}" for line in scan_log(server.log)] if failures: print("\nSMOKE TEST FAILED") print("\n".join(f" - {f}" for f in failures)) print(f" log: {server.log}") sys.exit(1) print("\nLOAD CHECK PASSED (mod discovery only; the full smoke test still needs --accept-eula)" if args.load_only else "\nSMOKE TEST PASSED") if __name__ == "__main__": main()