168 lines
4.9 KiB
Python
168 lines
4.9 KiB
Python
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
SERVER_ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(SERVER_ROOT))
|
|
|
|
from ndbs_core import ( # noqa: E402
|
|
NdbsPaths,
|
|
create_rule,
|
|
ensure_within_root,
|
|
infer_schema,
|
|
list_definition_files,
|
|
list_rule_files,
|
|
load_definition_file,
|
|
parse_tres_array,
|
|
parse_tres_value,
|
|
resolve_paths,
|
|
update_tres_resource,
|
|
write_definition_file,
|
|
)
|
|
|
|
|
|
def make_paths(tmp_path: Path) -> NdbsPaths:
|
|
project_root = tmp_path / "project"
|
|
project_root.mkdir()
|
|
(project_root / "project.godot").write_text("[gd_project]\n")
|
|
|
|
definitions_root = project_root / "resources" / "definitions"
|
|
definitions_root.mkdir(parents=True)
|
|
|
|
rules_root = project_root / "scripts" / "Rules" / "Generated"
|
|
rules_root.mkdir(parents=True)
|
|
|
|
backup_root = project_root / "tools" / "ndbs" / "backups"
|
|
backup_root.mkdir(parents=True)
|
|
|
|
web_root = project_root / "tools" / "ndbs" / "server" / "web"
|
|
web_root.mkdir(parents=True)
|
|
(web_root / "index.html").write_text("<html>ndbs</html>")
|
|
|
|
return NdbsPaths(
|
|
project_root=project_root,
|
|
definitions_root=definitions_root,
|
|
rules_root=rules_root,
|
|
backup_root=backup_root,
|
|
web_root=web_root,
|
|
)
|
|
|
|
|
|
def seed_definitions(paths: NdbsPaths) -> None:
|
|
traits_path = paths.definitions_root / "traits.json"
|
|
traits_path.write_text(
|
|
json.dumps({"id": "core:trait_test", "value": 1}), encoding="utf-8"
|
|
)
|
|
|
|
tres_path = paths.definitions_root / "discipline_biology.tres"
|
|
tres_path.write_text(
|
|
"[gd_resource type=\"Resource\" format=3]\n"
|
|
"\n"
|
|
"[resource]\n"
|
|
"Id = \"core:discipline_test\"\n"
|
|
"NameFallback = \"Biology\"\n"
|
|
"Tags = [ \"discipline\", \"lab\" ]\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
|
|
def test_parse_tres_values() -> None:
|
|
assert parse_tres_value("\"hello\"") == "hello"
|
|
assert parse_tres_value("true") is True
|
|
assert parse_tres_value("42") == 42
|
|
assert parse_tres_array('[ "a", "b" ]') == ["a", "b"]
|
|
|
|
|
|
def test_list_definitions_and_schema(tmp_path: Path) -> None:
|
|
paths = make_paths(tmp_path)
|
|
seed_definitions(paths)
|
|
|
|
files = list_definition_files(paths)
|
|
assert "traits.json" in files
|
|
assert "discipline_biology.tres" in files
|
|
|
|
content = load_definition_file(paths.definitions_root / "traits.json")
|
|
assert content["id"] == "core:trait_test"
|
|
|
|
schema = infer_schema(content)
|
|
assert schema["type"] == "object"
|
|
assert "id" in schema["fields"]
|
|
|
|
|
|
def test_write_json_creates_backup(tmp_path: Path) -> None:
|
|
paths = make_paths(tmp_path)
|
|
seed_definitions(paths)
|
|
|
|
write_definition_file(
|
|
paths.definitions_root / "traits.json",
|
|
{"id": "core:trait_test", "value": 2},
|
|
paths,
|
|
)
|
|
|
|
saved = json.loads(
|
|
(paths.definitions_root / "traits.json").read_text(encoding="utf-8")
|
|
)
|
|
assert saved["value"] == 2
|
|
backups = list(paths.backup_root.rglob("traits.json"))
|
|
assert backups
|
|
|
|
|
|
def test_tres_roundtrip(tmp_path: Path) -> None:
|
|
paths = make_paths(tmp_path)
|
|
seed_definitions(paths)
|
|
|
|
original = load_definition_file(paths.definitions_root / "discipline_biology.tres")
|
|
assert original["Tags"] == ["discipline", "lab"]
|
|
|
|
write_definition_file(
|
|
paths.definitions_root / "discipline_biology.tres",
|
|
{"Tags": ["alpha", "beta"], "NameFallback": "Bio 2"},
|
|
paths,
|
|
)
|
|
|
|
updated = load_definition_file(paths.definitions_root / "discipline_biology.tres")
|
|
assert updated["Tags"] == ["alpha", "beta"]
|
|
assert updated["NameFallback"] == "Bio 2"
|
|
|
|
|
|
def test_update_tres_resource_adds_keys() -> None:
|
|
original = "[resource]\nId = \"core:test\"\n"
|
|
updated = update_tres_resource(original, {"NewKey": "Value"})
|
|
assert "NewKey" in updated
|
|
|
|
|
|
def test_create_rule(tmp_path: Path) -> None:
|
|
paths = make_paths(tmp_path)
|
|
|
|
filename = create_rule("test_rule", paths)
|
|
assert filename.endswith(".cs")
|
|
rule_path = paths.rules_root / filename
|
|
assert rule_path.exists()
|
|
assert "rule:test_rule" in rule_path.read_text(encoding="utf-8")
|
|
|
|
rules = list_rule_files(paths)
|
|
assert filename in rules
|
|
|
|
|
|
def test_ensure_within_root_rejects_traversal(tmp_path: Path) -> None:
|
|
paths = make_paths(tmp_path)
|
|
with pytest.raises(ValueError):
|
|
ensure_within_root(paths.definitions_root, "../evil.json", {".json"})
|
|
|
|
|
|
def test_resolve_paths_defaults(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
project_root = tmp_path / "game"
|
|
project_root.mkdir()
|
|
(project_root / "project.godot").write_text("[gd_project]\n")
|
|
server_root = tmp_path / "server"
|
|
server_root.mkdir()
|
|
|
|
monkeypatch.chdir(server_root)
|
|
monkeypatch.setenv("NDBS_PROJECT_ROOT", str(project_root))
|
|
|
|
resolved = resolve_paths()
|
|
assert resolved.project_root == project_root
|
|
assert resolved.definitions_root.name == "definitions"
|