test_server_list.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import base64
  2. from dataclasses import FrozenInstanceError
  3. import pytest
  4. from cq2_sdk.login.server_list import (
  5. ServerListError,
  6. decode_detailed_server_list,
  7. fetch_server_info,
  8. resolve_server,
  9. )
  10. ENCRYPTED_SERVER_LIST = (
  11. b"NG1lSm5QeWzEk/B+5xXXfjnMHIpGB/PPbL/y7tEPyFCzWvBJq2fmPD5PiYHcKj/FZ"
  12. b"+tehXBa0XEMs9YMS3E1N7HCzgBWTVsHbF5MEs9KDXnW4j1XaHjjnjRVMP2z94vFm"
  13. b"LOtIDVedS9/y8FZfCpM1CigHp3uJMWqZDWQMLiTVL9RZOrKik67VPrH3MBdtPhLRb"
  14. b"t00X1Uyd+JuagpL6xRX4nTFP3VdO9TWOvsw/ZqOK0C54GFugWGMAjsydz5A3A/mf"
  15. b"/x+A=="
  16. )
  17. INVALID_PADDED_LENGTH_SERVER_LIST = b"NG1lSm5QeWxWnBdj3dyvl1nAc+J5GmVq"
  18. def test_decode_and_resolve_compact_server_fields():
  19. data = decode_detailed_server_list(ENCRYPTED_SERVER_LIST)
  20. assert data["srvRoot"] == "edge.example"
  21. server = resolve_server(data, 7, channel_id="110585", game_id=206923)
  22. assert server.server_id == 7
  23. assert server.main_server_id == 9
  24. assert server.name == "Seven"
  25. assert server.host == "s9-edge.example"
  26. assert server.port == 8041
  27. assert server.state == 1
  28. assert server.url_suffix == "v"
  29. assert server.tag == 1
  30. assert server.gm_url_suffix == ""
  31. assert server.explicit_ip == ""
  32. assert server.proxy_port == 9000
  33. assert server.beta is False
  34. assert server.channel_id == "110585"
  35. assert server.game_id == 206923
  36. def test_resolve_server_prefers_explicit_ip():
  37. data = decode_detailed_server_list(ENCRYPTED_SERVER_LIST)
  38. server = resolve_server(data, 8, channel_id="c", game_id=1)
  39. assert server.host == "1.2.3.4"
  40. assert server.explicit_ip == "1.2.3.4"
  41. assert server.main_server_id == 8
  42. assert server.proxy_port is None
  43. def test_resolve_server_expands_long_form_aliases():
  44. server = resolve_server(
  45. {
  46. "srvRoot": "edge.example",
  47. "serverData": [
  48. {
  49. "srvid": "17",
  50. "port": "8041",
  51. "state": "4",
  52. "urlsuffix": "stable-v2",
  53. "mainsrvid": "19",
  54. "tag": "featured",
  55. "srvname": "Seventeen",
  56. "gmurlsuffix": "gm-v2",
  57. "srvip": "10.0.0.17",
  58. "proxyport": "9001",
  59. "beta": "1",
  60. }
  61. ],
  62. },
  63. 17,
  64. channel_id="channel",
  65. game_id=42,
  66. )
  67. assert server.server_id == 17
  68. assert server.port == 8041
  69. assert server.state == 4
  70. assert server.url_suffix == "stable-v2"
  71. assert server.main_server_id == 19
  72. assert server.tag == "featured"
  73. assert server.name == "Seventeen"
  74. assert server.gm_url_suffix == "gm-v2"
  75. assert server.explicit_ip == "10.0.0.17"
  76. assert server.host == "10.0.0.17"
  77. assert server.proxy_port == 9001
  78. assert server.beta is True
  79. @pytest.mark.parametrize(
  80. ("payload", "message"),
  81. [
  82. (b"not-base64!", "Base64"),
  83. (base64.b64encode(b"wrong-sign"), "marker"),
  84. (base64.b64encode(b"4meJnPyl" + b"short"), "XXTEA"),
  85. ],
  86. )
  87. def test_decode_rejects_malformed_payload(payload, message):
  88. with pytest.raises(ServerListError, match=message):
  89. decode_detailed_server_list(payload)
  90. def test_decode_rejects_more_than_three_padding_bytes():
  91. with pytest.raises(ServerListError, match="plaintext length"):
  92. decode_detailed_server_list(INVALID_PADDED_LENGTH_SERVER_LIST)
  93. def test_resolve_server_rejects_unknown_server_id():
  94. data = decode_detailed_server_list(ENCRYPTED_SERVER_LIST)
  95. with pytest.raises(ServerListError, match="server 999"):
  96. resolve_server(data, 999, channel_id="110585", game_id=206923)
  97. @pytest.mark.asyncio
  98. async def test_fetch_server_info_uses_channel_specific_endpoint():
  99. seen = []
  100. class Response:
  101. content = ENCRYPTED_SERVER_LIST
  102. def raise_for_status(self):
  103. return None
  104. class Client:
  105. async def get(self, url):
  106. seen.append(url)
  107. return Response()
  108. server = await fetch_server_info(
  109. 7,
  110. channel_id="110585",
  111. game_id=206923,
  112. client=Client(),
  113. )
  114. assert seen == [
  115. "https://list-box.dhsf.xqhuyu.com/serverlist/110585/serverlist.txt"
  116. ]
  117. assert server.host == "s9-edge.example"
  118. @pytest.mark.asyncio
  119. async def test_fetch_server_info_does_not_disable_tls_verification(monkeypatch):
  120. constructed = []
  121. class Response:
  122. content = ENCRYPTED_SERVER_LIST
  123. def raise_for_status(self):
  124. return None
  125. class Client:
  126. def __init__(self, **kwargs):
  127. constructed.append(kwargs)
  128. async def get(self, _url):
  129. return Response()
  130. async def aclose(self):
  131. return None
  132. monkeypatch.setattr("cq2_sdk.login.server_list.httpx.AsyncClient", Client)
  133. await fetch_server_info(7, channel_id="110585", game_id=206923)
  134. assert constructed[0].get("verify", True) is True
  135. def test_server_info_is_immutable_and_copies_config_versions():
  136. source_versions = {"cfg_stditem": "hash-1"}
  137. server = resolve_server(
  138. {
  139. "srvRoot": "edge.example",
  140. "serverData": [{"d": 7, "p": 8041, "s": 1}],
  141. },
  142. 7,
  143. channel_id="110585",
  144. game_id=206923,
  145. )
  146. server_with_config = type(server)(
  147. server_id=server.server_id,
  148. name=server.name,
  149. host=server.host,
  150. port=server.port,
  151. config_versions=source_versions,
  152. )
  153. source_versions["cfg_stditem"] = "mutated"
  154. with pytest.raises(FrozenInstanceError):
  155. server_with_config.port = 9000
  156. with pytest.raises(TypeError):
  157. server_with_config.config_versions["new"] = "value"
  158. assert dict(server_with_config.config_versions) == {"cfg_stditem": "hash-1"}