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("ndbs") 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"