check_names: add --fix-ids to repair wrong steam_ids in meta.cpp
ID_COLLISION status (previously NOT_ON_SERVER) now has its own label and shows the bad steam_id in the report output. --fix-ids cross-references comparison.json to find the correct publishedid for each colliding folder and rewrites it in-place in meta.cpp using regex, preserving all other content. New helpers: _lookup_detailed (returns server_name + local_sid tuple), _write_steam_id (regex-replace publishedid in meta.cpp), _build_comparison_id_map (builds normalized_name -> steam_id map from comparison.json). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
168
test_suite.py
168
test_suite.py
@@ -1064,7 +1064,11 @@ test("all exported symbols are callable or types", _test_exports_are_callable)
|
||||
|
||||
group("check_names helpers")
|
||||
|
||||
from check_names import _server_name_from_url, _read_local_steam_id, _lookup_server_name, _resolve_status
|
||||
from check_names import (
|
||||
_server_name_from_url, _read_local_steam_id,
|
||||
_lookup_detailed, _resolve_status, _write_steam_id,
|
||||
_build_comparison_id_map,
|
||||
)
|
||||
|
||||
|
||||
def _test_server_name_from_url():
|
||||
@@ -1091,7 +1095,7 @@ def _test_read_local_steam_id_no_id_in_file():
|
||||
assert _read_local_steam_id(d) is None
|
||||
|
||||
|
||||
def _test_lookup_server_name_by_steam_id():
|
||||
def _test_lookup_detailed_by_steam_id():
|
||||
index = {
|
||||
"by_steam_id": {"463939057": "https://x.com/@ace3/"},
|
||||
"by_name": {},
|
||||
@@ -1099,23 +1103,23 @@ def _test_lookup_server_name_by_steam_id():
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
d = Path(d)
|
||||
(d / "meta.cpp").write_text("publishedid = 463939057;", encoding="utf-8")
|
||||
result = _lookup_server_name("@ACE3", d, index)
|
||||
assert_eq(result, "@ace3")
|
||||
server_name, local_sid = _lookup_detailed("@ACE3", d, index)
|
||||
assert_eq(server_name, "@ace3")
|
||||
assert_eq(local_sid, "463939057")
|
||||
|
||||
|
||||
def _test_lookup_server_name_by_name_fallback():
|
||||
def _test_lookup_detailed_by_name_fallback():
|
||||
index = {
|
||||
"by_steam_id": {},
|
||||
"by_name": {"cbaa3": "https://x.com/@cba_a3/"},
|
||||
}
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
# No meta.cpp — name fallback
|
||||
result = _lookup_server_name("@CBA_A3", Path(d), index)
|
||||
assert_eq(result, "@cba_a3")
|
||||
server_name, local_sid = _lookup_detailed("@CBA_A3", Path(d), index)
|
||||
assert_eq(server_name, "@cba_a3")
|
||||
assert local_sid is None # no meta.cpp
|
||||
|
||||
|
||||
def _test_lookup_server_name_steam_id_beats_name():
|
||||
"""steam_id lookup must take priority over name fallback."""
|
||||
def _test_lookup_detailed_steam_id_beats_name():
|
||||
index = {
|
||||
"by_steam_id": {"111": "https://x.com/@correct/"},
|
||||
"by_name": {"mymod": "https://x.com/@wrong/"},
|
||||
@@ -1123,55 +1127,149 @@ def _test_lookup_server_name_steam_id_beats_name():
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
d = Path(d)
|
||||
(d / "meta.cpp").write_text("publishedid = 111;", encoding="utf-8")
|
||||
result = _lookup_server_name("@MyMod", d, index)
|
||||
assert_eq(result, "@correct")
|
||||
server_name, local_sid = _lookup_detailed("@MyMod", d, index)
|
||||
assert_eq(server_name, "@correct")
|
||||
assert_eq(local_sid, "111")
|
||||
|
||||
|
||||
def _test_lookup_server_name_not_found():
|
||||
def _test_lookup_detailed_not_found():
|
||||
index = {"by_steam_id": {}, "by_name": {}}
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
result = _lookup_server_name("@Unknown", Path(d), index)
|
||||
assert result is None
|
||||
server_name, local_sid = _lookup_detailed("@Unknown", Path(d), index)
|
||||
assert server_name is None
|
||||
|
||||
|
||||
test("_server_name_from_url: extracts name from URL", _test_server_name_from_url)
|
||||
test("_read_local_steam_id: reads meta.cpp", _test_read_local_steam_id_present)
|
||||
test("_read_local_steam_id: no meta.cpp -> None", _test_read_local_steam_id_missing)
|
||||
test("_read_local_steam_id: meta.cpp without id -> None", _test_read_local_steam_id_no_id_in_file)
|
||||
test("_lookup_server_name: matches by steam_id", _test_lookup_server_name_by_steam_id)
|
||||
test("_lookup_server_name: falls back to name", _test_lookup_server_name_by_name_fallback)
|
||||
test("_lookup_server_name: steam_id beats name fallback", _test_lookup_server_name_steam_id_beats_name)
|
||||
test("_lookup_server_name: not found -> None", _test_lookup_server_name_not_found)
|
||||
def _test_lookup_detailed_returns_sid_even_when_not_on_server():
|
||||
"""local_sid should be returned even when server lookup fails."""
|
||||
index = {"by_steam_id": {}, "by_name": {}}
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
d = Path(d)
|
||||
(d / "meta.cpp").write_text("publishedid = 999;", encoding="utf-8")
|
||||
server_name, local_sid = _lookup_detailed("@SomeMod", d, index)
|
||||
assert server_name is None
|
||||
assert_eq(local_sid, "999")
|
||||
|
||||
# _resolve_status tests (the false-positive filter)
|
||||
|
||||
test("_lookup_detailed: matches by steam_id", _test_lookup_detailed_by_steam_id)
|
||||
test("_lookup_detailed: falls back to name", _test_lookup_detailed_by_name_fallback)
|
||||
test("_lookup_detailed: steam_id beats name fallback", _test_lookup_detailed_steam_id_beats_name)
|
||||
test("_lookup_detailed: not found -> None", _test_lookup_detailed_not_found)
|
||||
test("_lookup_detailed: returns local_sid even when not on server", _test_lookup_detailed_returns_sid_even_when_not_on_server)
|
||||
|
||||
# _resolve_status tests
|
||||
|
||||
def _test_resolve_status_ok():
|
||||
status, col = _resolve_status("@ace", "@ace", ok_disk_names={"@ace"})
|
||||
status, col = _resolve_status("@ace", "@ace", None, ok_disk_names={"@ace"})
|
||||
assert_eq(status, "OK")
|
||||
assert_eq(col, "@ace")
|
||||
|
||||
def _test_resolve_status_mismatch():
|
||||
status, col = _resolve_status("@ACE3", "@ace3", ok_disk_names={"@cba_a3"})
|
||||
status, col = _resolve_status("@ACE3", "@ace3", None, ok_disk_names={"@cba_a3"})
|
||||
assert_eq(status, "MISMATCH")
|
||||
assert_eq(col, "@ace3")
|
||||
|
||||
def _test_resolve_status_not_found():
|
||||
status, col = _resolve_status("@Unknown", None, ok_disk_names=set())
|
||||
status, col = _resolve_status("@Unknown", None, None, ok_disk_names=set())
|
||||
assert_eq(status, "NOT_ON_SERVER")
|
||||
|
||||
def _test_resolve_status_steam_id_collision():
|
||||
# server_name is already correctly held by another folder → false positive
|
||||
def _test_resolve_status_id_collision():
|
||||
ok = {"@Realistic Ragdoll Physics"}
|
||||
status, col = _resolve_status("@NIArms All in One- ACE Compatibility",
|
||||
"@Realistic Ragdoll Physics",
|
||||
ok_disk_names=ok)
|
||||
assert_eq(status, "NOT_ON_SERVER", "Should be reclassified due to collision")
|
||||
assert "collision" in col
|
||||
status, col = _resolve_status(
|
||||
"@NIArms All in One- ACE Compatibility",
|
||||
"@Realistic Ragdoll Physics",
|
||||
"1234567",
|
||||
ok_disk_names=ok,
|
||||
)
|
||||
assert_eq(status, "ID_COLLISION")
|
||||
assert "1234567" in col # bad steam_id shown in output
|
||||
|
||||
def _test_resolve_status_collision_without_sid():
|
||||
ok = {"@Realistic Ragdoll Physics"}
|
||||
status, col = _resolve_status(
|
||||
"@Some Mod", "@Realistic Ragdoll Physics", None, ok_disk_names=ok
|
||||
)
|
||||
assert_eq(status, "ID_COLLISION")
|
||||
|
||||
test("_resolve_status: OK match", _test_resolve_status_ok)
|
||||
test("_resolve_status: genuine mismatch", _test_resolve_status_mismatch)
|
||||
test("_resolve_status: not found", _test_resolve_status_not_found)
|
||||
test("_resolve_status: steam_id collision reclassified as NOT_ON_SERVER", _test_resolve_status_steam_id_collision)
|
||||
test("_resolve_status: ID_COLLISION shows bad steam_id", _test_resolve_status_id_collision)
|
||||
test("_resolve_status: ID_COLLISION without local sid", _test_resolve_status_collision_without_sid)
|
||||
|
||||
# _write_steam_id tests
|
||||
|
||||
def _test_write_steam_id_updates_existing():
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
d = Path(d)
|
||||
meta = d / "meta.cpp"
|
||||
meta.write_text('publishedid = 111;\nname = "OldMod";\n', encoding="utf-8")
|
||||
ok, msg = _write_steam_id(d, "999")
|
||||
assert ok
|
||||
assert "999" in meta.read_text(encoding="utf-8")
|
||||
assert "111" not in meta.read_text(encoding="utf-8")
|
||||
assert 'name = "OldMod"' in meta.read_text(encoding="utf-8") # preserved
|
||||
|
||||
def _test_write_steam_id_creates_if_missing():
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
d = Path(d)
|
||||
ok, msg = _write_steam_id(d, "12345")
|
||||
assert ok
|
||||
meta = d / "meta.cpp"
|
||||
assert meta.exists()
|
||||
assert "12345" in meta.read_text(encoding="utf-8")
|
||||
|
||||
def _test_write_steam_id_appends_if_no_line():
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
d = Path(d)
|
||||
meta = d / "meta.cpp"
|
||||
meta.write_text('name = "NoId";\n', encoding="utf-8")
|
||||
ok, msg = _write_steam_id(d, "777")
|
||||
assert ok
|
||||
content = meta.read_text(encoding="utf-8")
|
||||
assert "777" in content
|
||||
assert 'name = "NoId"' in content
|
||||
|
||||
test("_write_steam_id: updates existing publishedid", _test_write_steam_id_updates_existing)
|
||||
test("_write_steam_id: creates meta.cpp if missing", _test_write_steam_id_creates_if_missing)
|
||||
test("_write_steam_id: appends if no publishedid line", _test_write_steam_id_appends_if_no_line)
|
||||
|
||||
# _build_comparison_id_map tests
|
||||
|
||||
def _test_build_comparison_id_map_reads_json():
|
||||
comparison = {
|
||||
"compared_presets": ["A", "B"],
|
||||
"shared": {"mod_count": 1, "mods": [
|
||||
{"name": "CBA_A3", "steam_id": "450814997", "url": None, "source": "steam"},
|
||||
]},
|
||||
"unique": {
|
||||
"A": {"mod_count": 1, "mods": [
|
||||
{"name": "ACE3", "steam_id": "463939057", "url": None, "source": "steam"},
|
||||
]},
|
||||
"B": {"mod_count": 0, "mods": []},
|
||||
},
|
||||
}
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
cfg_data = {
|
||||
"server": {"base_url": "x", "username": "u", "password": "p"},
|
||||
"paths": {"arma_dir": d, "downloads": d,
|
||||
"modlist_html": d, "modlist_json": d},
|
||||
}
|
||||
import json as _json
|
||||
cfg_file = Path(d) / "config.json"
|
||||
cfg_file.write_text(_json.dumps(cfg_data), encoding="utf-8")
|
||||
from arma_modlist_tools.config import load_config as _lc
|
||||
cfg = _lc(str(cfg_file))
|
||||
# Write a fake comparison.json
|
||||
comp_path = Path(d) / "comparison.json"
|
||||
comp_path.write_text(_json.dumps(comparison), encoding="utf-8")
|
||||
id_map = _build_comparison_id_map(cfg)
|
||||
|
||||
assert_in("cbaa3", id_map)
|
||||
assert_eq(id_map["cbaa3"][0], "450814997")
|
||||
assert_in("ace3", id_map)
|
||||
assert_eq(id_map["ace3"][0], "463939057")
|
||||
|
||||
test("_build_comparison_id_map: reads steam_ids from comparison.json", _test_build_comparison_id_map_reads_json)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user