Each linked overlay gets a checkbox on the blueprint detail page that opts
its server.cfg in as exec server_overlay_<id>. The web app builds the
spec with {path, alias} per overlay and prepends exec server_overlay_<id>
lines to the blueprint config in lowest-overlay-first order. The host
stages those copies in the overlayfs upper layer before mounting (avoids
copy-up writes against a sandbox-uid file). A live preview block above the
Config textarea shows what gets auto-executed.
Schema:
- alembic 0007: BlueprintOverlay.expose_server_cfg BOOLEAN
Spec contract:
- l4d2host OverlayRef(path, alias?). load_spec accepts both bare-string
and {path, alias} entries.
Side effects folded in (same file in l4d2_facade):
- start_server auto-initializes; the manual Initialize step is no longer
needed before Start.
- initialize_server no longer runs blueprint builders — builds happen on
overlay save, not on every server Start.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
58 lines
1.6 KiB
Python
58 lines
1.6 KiB
Python
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from l4d2host.spec import OverlayRef, load_spec
|
|
|
|
|
|
def test_minimal_spec_parses_string_shorthand(tmp_path: Path) -> None:
|
|
path = tmp_path / "server.yaml"
|
|
path.write_text("port: 27015\noverlays: [standard]\n")
|
|
spec = load_spec(path)
|
|
assert spec.port == 27015
|
|
assert spec.overlays == [OverlayRef(path="standard")]
|
|
|
|
|
|
def test_overlay_dict_with_alias(tmp_path: Path) -> None:
|
|
path = tmp_path / "server.yaml"
|
|
path.write_text(
|
|
"port: 27015\n"
|
|
"overlays:\n"
|
|
" - {path: '6', alias: overlay_6}\n"
|
|
" - path: '7'\n"
|
|
)
|
|
spec = load_spec(path)
|
|
assert spec.overlays == [
|
|
OverlayRef(path="6", alias="overlay_6"),
|
|
OverlayRef(path="7", alias=None),
|
|
]
|
|
|
|
|
|
def test_defaults_are_empty_lists(tmp_path: Path) -> None:
|
|
path = tmp_path / "server.yaml"
|
|
path.write_text("port: 27015\n")
|
|
spec = load_spec(path)
|
|
assert spec.overlays == []
|
|
assert spec.arguments == []
|
|
assert spec.config == []
|
|
|
|
|
|
def test_missing_port_fails(tmp_path: Path) -> None:
|
|
path = tmp_path / "server.yaml"
|
|
path.write_text("overlays: [standard]\n")
|
|
with pytest.raises((KeyError, ValueError, TypeError)):
|
|
load_spec(path)
|
|
|
|
|
|
def test_unknown_keys_ignored(tmp_path: Path) -> None:
|
|
path = tmp_path / "server.yaml"
|
|
path.write_text("port: 27015\nfoo: bar\n")
|
|
spec = load_spec(path)
|
|
assert spec.port == 27015
|
|
|
|
|
|
def test_overlay_dict_missing_path_rejected(tmp_path: Path) -> None:
|
|
path = tmp_path / "server.yaml"
|
|
path.write_text("port: 27015\noverlays:\n - {alias: overlay_6}\n")
|
|
with pytest.raises(ValueError):
|
|
load_spec(path)
|