Files
CosmicClash/Game/tests/cases/test_server_config.gd
T
2026-08-21 18:38:30 +01:00

129 lines
6.0 KiB
GDScript

extends "res://tests/test_case.gd"
# Task 6.3. The behaviour that matters is not "it parses a port" — it is the
# precedence order an operator relies on, and the refusal to run on a typo.
const ServerConfigScript = preload("res://scripts/server_config.gd")
func _parse(args: Array) -> Variant:
var argv := PackedStringArray()
for a in args:
argv.append(a)
return ServerConfigScript.parse(argv)
func _temp_config(body: String) -> String:
var path := "user://test_server_%d.cfg" % Time.get_ticks_usec()
var f := FileAccess.open(path, FileAccess.WRITE)
f.store_string(body)
f.close()
return path
func test_defaults_apply_when_nothing_is_given() -> void:
var config = _parse([])
assert_true(config.is_valid(), "an empty command line is valid")
assert_eq(config.get_value("port"), 7777, "default port")
assert_eq(config.get_value("max-matches"), 0, "0 means run forever")
assert_eq(config.get_value("log-level"), "info", "default log level")
func test_command_line_values_are_typed_not_strings() -> void:
var config = _parse(["--port=7000", "--match-length=90.5", "--fill-bots"])
assert_true(config.is_valid(), "valid: %s" % str(config.errors))
assert_eq(config.get_value("port"), 7000, "int stays an int")
assert_almost_eq(config.get_value("match-length"), 90.5, 0.001, "float stays a float")
assert_eq(config.get_value("fill-bots"), true, "a bare bool flag is true")
func test_an_unknown_flag_is_an_error_not_a_shrug() -> void:
# The whole reason this class exists: `--max-clientss=8` used to run a
# server on the default cap and say nothing at all.
var config = _parse(["--max-clientss=8"])
assert_true(not config.is_valid(), "a typo'd flag is rejected")
assert_true("max-clientss" in " ".join(config.errors), "and the error names it: %s" % str(config.errors))
func test_a_value_that_is_not_the_declared_type_is_rejected() -> void:
var config = _parse(["--port=seven"])
assert_true(not config.is_valid(), "a non-numeric port is rejected")
var config_ok = _parse(["--port=7000"])
assert_true(config_ok.is_valid(), "control: a numeric port is accepted")
func test_a_non_bool_flag_without_a_value_is_rejected() -> void:
# Silently defaulting here would hide a shell-quoting mistake.
var config = _parse(["--port"])
assert_true(not config.is_valid(), "--port with no value is an error")
func test_no_prefix_turns_a_bool_off() -> void:
var config = _parse(["--no-fill-bots"])
assert_true(config.is_valid(), "valid: %s" % str(config.errors))
assert_eq(config.get_value("fill-bots"), false, "--no- inverts it")
# And it must not be listed separately, or --help doubles in length.
var help: String = ServerConfigScript.help_text()
assert_eq(help.count("--no-fill-bots"), 1, "--no- form appears once, in the default hint")
func test_the_command_line_beats_the_config_file() -> void:
var path := _temp_config("[server]\nport=8100\nmax-clients=4\n")
var config = _parse(["--config=%s" % path, "--port=9200"])
assert_true(config.is_valid(), "valid: %s" % str(config.errors))
assert_eq(config.get_value("port"), 9200, "the command line wins")
assert_eq(config.get_value("max-clients"), 4, "the file still supplies what the command line omits")
DirAccess.remove_absolute(ProjectSettings.globalize_path(path))
func test_the_config_file_beats_the_default() -> void:
var path := _temp_config("[server]\nport=8100\n")
var config = _parse(["--config=%s" % path])
assert_true(config.is_valid(), "valid: %s" % str(config.errors))
assert_eq(config.get_value("port"), 8100, "the file overrides the default")
DirAccess.remove_absolute(ProjectSettings.globalize_path(path))
func test_a_missing_or_malformed_config_file_is_an_error() -> void:
var config = _parse(["--config=user://definitely_not_here_%d.cfg" % Time.get_ticks_usec()])
assert_true(not config.is_valid(), "a config file that cannot be read is an error, not silence")
var path := _temp_config("[server]\nnonsense=1\n")
var unknown_key = _parse(["--config=%s" % path])
assert_true(not unknown_key.is_valid(), "an unknown key in the file is rejected too")
DirAccess.remove_absolute(ProjectSettings.globalize_path(path))
func test_out_of_range_values_are_rejected_with_their_own_message() -> void:
assert_true(not _parse(["--port=0"]).is_valid(), "port 0 is out of range")
assert_true(not _parse(["--port=70000"]).is_valid(), "port 70000 is out of range")
assert_true(not _parse(["--max-clients=0"]).is_valid(), "a server for nobody is rejected")
assert_true(not _parse(["--match-length=0"]).is_valid(), "a zero-length match is rejected")
assert_true(not _parse(["--log-level=chatty"]).is_valid(), "an undefined log level is rejected")
assert_true(not _parse(["--arena-rotation=spiral"]).is_valid(), "an undefined rotation mode is rejected")
assert_true(not _parse(["--smoke-force-goal-after=-2"]).is_valid(), "only -1 disables the deterministic smoke goal")
# Control: the same flags at legal values all pass together.
var ok = _parse(["--port=7000", "--max-clients=6", "--match-length=90", "--log-level=warn", "--arena-rotation=random"])
assert_true(ok.is_valid(), "control: legal values pass (%s)" % str(ok.errors))
func test_a_repeated_flag_is_rejected_rather_than_last_wins() -> void:
# Last-wins hides a duplicated line in a generated systemd unit.
var config = _parse(["--port=7000", "--port=8000"])
assert_true(not config.is_valid(), "the same flag twice is an error")
func test_help_documents_every_declared_flag() -> void:
# The acceptance criterion is literally "--help documents every flag", so
# assert it against the declaration rather than against a hand-kept list.
var help: String = ServerConfigScript.help_text()
for spec in ServerConfigScript.specs():
assert_true("--%s" % spec.key in help, "--%s appears in --help" % spec.key)
assert_true(spec.help in help, "and so does its description")
func test_help_is_requested_without_needing_a_valid_command_line() -> void:
var config = _parse(["--help"])
assert_true(config.help_requested, "--help is recognised")
var short = _parse(["-h"])
assert_true(short.help_requested, "-h too")