q1b_http_verify.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. """Q1-B 8755短生命周期真实HTTP复验工具。"""
  2. from __future__ import annotations
  3. import json
  4. import os
  5. import sys
  6. import threading
  7. from pathlib import Path
  8. from urllib.parse import urlparse
  9. import requests
  10. from sqlalchemy import func, select
  11. from werkzeug.serving import make_server
  12. BACKEND_ROOT = Path(__file__).resolve().parents[2]
  13. if str(BACKEND_ROOT) not in sys.path:
  14. sys.path.insert(0, str(BACKEND_ROOT))
  15. def _assert_test_database() -> None:
  16. database_url = os.environ.get("DMS_DATABASE_URL", "")
  17. database_name = urlparse(database_url).path.lstrip("/")
  18. if not database_name.endswith("_test"):
  19. raise RuntimeError("Q1-B真实HTTP复验只允许连接以_test结尾的测试库")
  20. def main() -> None:
  21. _assert_test_database()
  22. password = os.environ["DMS_TEST_USER_PASSWORD"]
  23. from app import app
  24. from dms.extensions import db
  25. from dms.models import AuditLog
  26. server = make_server("127.0.0.1", 8755, app, threaded=True)
  27. server_thread = threading.Thread(
  28. target=server.serve_forever,
  29. name="q1b-http-verify",
  30. daemon=True,
  31. )
  32. server_thread.start()
  33. base_url = "http://127.0.0.1:8755/api/v1"
  34. origin = "http://127.0.0.1:9346"
  35. try:
  36. tokens: dict[str, str] = {}
  37. for username in ("admin", "user"):
  38. response = requests.post(
  39. f"{base_url}/auth/login",
  40. json={
  41. "username": username,
  42. "password": password,
  43. "keepSignedIn": False,
  44. },
  45. timeout=5,
  46. )
  47. response.raise_for_status()
  48. tokens[username] = response.json()["data"]["accessToken"]
  49. with app.app_context():
  50. audit_before = db.session.scalar(
  51. select(func.count()).select_from(AuditLog)
  52. )
  53. role_results = {}
  54. stable_data = None
  55. for username, token in tokens.items():
  56. response = requests.get(
  57. f"{base_url}/config/ui-dictionaries",
  58. headers={
  59. "Authorization": f"Bearer {token}",
  60. "Origin": origin,
  61. },
  62. timeout=5,
  63. )
  64. response.raise_for_status()
  65. body = response.json()
  66. assert body["requestId"] == response.headers["X-Request-Id"]
  67. assert response.headers["Access-Control-Allow-Origin"] == origin
  68. exposed = {
  69. value.strip().lower()
  70. for value in response.headers[
  71. "Access-Control-Expose-Headers"
  72. ].split(",")
  73. }
  74. assert "x-request-id" in exposed
  75. assert set(body["data"]["dictionaries"]) == {
  76. "roleCodes",
  77. "allowedModules",
  78. "documentTypes",
  79. "documentStatuses",
  80. "securityLevels",
  81. "visibilityTypes",
  82. "attachmentTypes",
  83. "subjectTypes",
  84. "allowedActions",
  85. "categoryTypes",
  86. "enabledStatuses",
  87. "auditResults",
  88. "auditActions",
  89. "auditTargets",
  90. }
  91. if stable_data is None:
  92. stable_data = body["data"]
  93. else:
  94. assert body["data"] == stable_data
  95. role_results[username] = {
  96. "status": response.status_code,
  97. "requestIdMatches": True,
  98. }
  99. repeated = requests.get(
  100. f"{base_url}/config/ui-dictionaries",
  101. headers={
  102. "Authorization": f"Bearer {tokens['admin']}",
  103. "Origin": origin,
  104. },
  105. timeout=5,
  106. )
  107. repeated.raise_for_status()
  108. assert repeated.json()["data"] == stable_data
  109. unauthenticated = requests.get(
  110. f"{base_url}/config/ui-dictionaries",
  111. headers={"Origin": origin},
  112. timeout=5,
  113. )
  114. assert unauthenticated.status_code == 401
  115. unauthenticated_body = unauthenticated.json()
  116. assert (
  117. unauthenticated_body["requestId"]
  118. == unauthenticated.headers["X-Request-Id"]
  119. )
  120. labels = {
  121. item["code"]: item["label"]
  122. for item in stable_data["dictionaries"]["securityLevels"]
  123. }
  124. assert labels["INTERNAL"] == "内部"
  125. assert repeated.encoding.lower() == "utf-8"
  126. with app.app_context():
  127. audit_after = db.session.scalar(
  128. select(func.count()).select_from(AuditLog)
  129. )
  130. assert audit_after == audit_before
  131. print(
  132. json.dumps(
  133. {
  134. "port": 8755,
  135. "database": "dms_test",
  136. "roles": role_results,
  137. "unauthenticatedStatus": unauthenticated.status_code,
  138. "dictionaryCount": len(stable_data["dictionaries"]),
  139. "utf8Chinese": True,
  140. "continuousResponsesStable": True,
  141. "auditCountBefore": audit_before,
  142. "auditCountAfter": audit_after,
  143. "requestIdMatches": True,
  144. "corsOrigin": origin,
  145. "exposeHeaders": repeated.headers[
  146. "Access-Control-Expose-Headers"
  147. ],
  148. },
  149. ensure_ascii=False,
  150. )
  151. )
  152. finally:
  153. server.shutdown()
  154. server.server_close()
  155. server_thread.join(timeout=5)
  156. if __name__ == "__main__":
  157. main()