test_bot_config.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. from __future__ import annotations
  2. import json
  3. import pytest
  4. from aiohttp import CookieJar
  5. from aiohttp.test_utils import TestClient, TestServer
  6. def test_multiple_bot_profiles_are_atomic_and_redacted(app_modules, tmp_path):
  7. config = app_modules.load("wbb.admin.bot_config")
  8. path = tmp_path / "bots.json"
  9. first_token = "123456:" + "A" * 30
  10. second_token = "654321:" + "B" * 30
  11. config.update_telegram_config(path, {"api_id": 12345, "api_hash": "a" * 32})
  12. first = config.create_bot_profile(
  13. path,
  14. {
  15. "label": "Primary",
  16. "bot_token": first_token,
  17. "sudo_users_id": "100 200 100",
  18. "message_dump_chat": -100123,
  19. },
  20. )
  21. second = config.create_bot_profile(
  22. path,
  23. {"label": "Secondary", "bot_token": second_token, "enabled": False},
  24. )
  25. status = config.telegram_config_status(path)
  26. assert status["api_ready"] is True
  27. assert len(status["bots"]) == 2
  28. assert status["bots"][0]["sudo_users_id"] == ["100", "200"]
  29. serialized = json.dumps(status)
  30. assert first_token not in serialized
  31. assert second_token not in serialized
  32. assert "a" * 32 not in serialized
  33. assert path.stat().st_mode & 0o777 == 0o600
  34. config.update_bot_profile(path, first["bot_id"], {"label": "Renamed", "bot_token": ""})
  35. stored = config.get_bot_profile_secrets(path, first["bot_id"])
  36. assert stored["bot_token"] == first_token
  37. assert stored["label"] == "Renamed"
  38. config.delete_bot_profile(path, second["bot_id"])
  39. assert len(config.telegram_config_status(path)["bots"]) == 1
  40. def test_bot_profile_validation(app_modules, tmp_path):
  41. config = app_modules.load("wbb.admin.bot_config")
  42. path = tmp_path / "bots.json"
  43. with pytest.raises(config.BotConfigError) as error:
  44. config.create_bot_profile(path, {"label": "Bad", "bot_token": "not-a-token"})
  45. assert error.value.code == "invalid_bot_token"
  46. with pytest.raises(config.BotConfigError) as error:
  47. config.update_telegram_config(
  48. path,
  49. {"api_id": 12345, "api_hash": "123456:" + "A" * 30},
  50. )
  51. assert error.value.code == "invalid_api_hash"
  52. assert "不能使用机器人令牌" in str(error.value)
  53. def test_supervisor_assigns_each_worker_an_isolated_database(app_modules, tmp_path):
  54. supervisor_module = app_modules.load("wbb.admin.supervisor")
  55. supervisor = supervisor_module.BotSupervisor(tmp_path / "bots.json", project_root=tmp_path)
  56. profile = {
  57. "bot_id": "bot.one/secondary",
  58. "bot_token": "123456:" + "A" * 30,
  59. "api_id": 12345,
  60. "api_hash": "a" * 32,
  61. "sudo_users_id": [],
  62. "log_group_id": 0,
  63. "gban_log_group_id": 0,
  64. "message_dump_chat": 0,
  65. }
  66. environment = supervisor._worker_environment(profile, 18088)
  67. assert environment["WBB_BOT_PROFILE_ID"] == "bot.one/secondary"
  68. assert environment["WBB_BOT_DATABASE"] == "wbb_bot_bot_one_secondary"
  69. async def test_bot_admin_api_crud_and_token_test_are_redacted(app_modules, monkeypatch):
  70. admin_api = app_modules.load("wbb.admin.api")
  71. application = admin_api.build_admin_application()
  72. await application["admin_api"].initialize()
  73. client = TestClient(TestServer(application), cookie_jar=CookieJar(unsafe=True))
  74. await client.start_server()
  75. token = "123456:" + "C" * 30
  76. try:
  77. login = await client.post(
  78. "/api/admin/v1/auth/login",
  79. json={"username": "admin", "password": "qwe0.123456"},
  80. )
  81. login_data = (await login.json())["data"]
  82. changed = await client.put(
  83. "/api/admin/v1/auth/password",
  84. headers={"X-CSRF-Token": login_data["csrf_token"]},
  85. json={
  86. "current_password": "qwe0.123456",
  87. "new_password": "changed-pass-123",
  88. },
  89. )
  90. csrf = (await changed.json())["data"]["csrf_token"]
  91. headers = {"X-CSRF-Token": csrf}
  92. telegram = await client.put(
  93. "/api/admin/v1/settings/telegram",
  94. headers=headers,
  95. json={"api_id": 12345, "api_hash": "d" * 32, "confirm": True},
  96. )
  97. assert telegram.status == 200
  98. assert (await telegram.json())["data"]["api_hash_configured"] is True
  99. created = await client.post(
  100. "/api/admin/v1/bots",
  101. headers=headers,
  102. json={
  103. "label": "Test Bot",
  104. "bot_token": token,
  105. "enabled": False,
  106. "confirm": True,
  107. },
  108. )
  109. assert created.status == 201
  110. created_payload = await created.json()
  111. profile = created_payload["data"]
  112. assert profile["bot_token_configured"] is True
  113. assert token not in json.dumps(created_payload)
  114. async def fake_test_bot_token(_token: str):
  115. assert _token == token
  116. return {"id": "123456", "username": "test_bot", "name": "Test Bot"}
  117. monkeypatch.setattr(admin_api, "test_bot_token", fake_test_bot_token)
  118. tested = await client.post(
  119. f"/api/admin/v1/bots/{profile['bot_id']}/test",
  120. headers=headers,
  121. json={"confirm": True},
  122. )
  123. assert tested.status == 200
  124. assert (await tested.json())["data"]["username"] == "test_bot"
  125. listed = await client.get("/api/admin/v1/bots")
  126. listed_payload = await listed.json()
  127. assert listed_payload["data"]["items"][0]["identity"]["username"] == "test_bot"
  128. assert token not in json.dumps(listed_payload)
  129. deleted = await client.delete(
  130. f"/api/admin/v1/bots/{profile['bot_id']}",
  131. headers=headers,
  132. json={"confirm": True},
  133. )
  134. assert deleted.status == 200
  135. finally:
  136. await client.close()