test_datasource_pool_diagnostics.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. from flask import Flask
  2. from app.core.data_source.models import PoolStatus
  3. UID = "01900000-0000-7000-8000-000000000010"
  4. def healthy_status():
  5. return PoolStatus(
  6. data_source_uid=UID,
  7. credential_version=2,
  8. config_fingerprint="must-not-be-exposed",
  9. pool_state="healthy",
  10. leases=0,
  11. draining=False,
  12. created_at=1,
  13. last_used_at=2,
  14. pool_size=2,
  15. checked_out=0,
  16. checked_in=2,
  17. overflow=0,
  18. )
  19. class FakeDiagnosticService:
  20. def pool_status(self, uid):
  21. assert uid == UID
  22. return healthy_status()
  23. def pool_statuses(self):
  24. return [healthy_status()]
  25. def invalidate_pool(self, uid, *, reason, actor_uid):
  26. assert uid == UID
  27. assert reason == "admin_reset"
  28. del actor_uid
  29. return {"uid": uid, "invalidated": True}
  30. def make_client(monkeypatch):
  31. from app.api.data_source import routes
  32. monkeypatch.setattr(
  33. routes,
  34. "get_data_source_service",
  35. FakeDiagnosticService,
  36. )
  37. app = Flask(__name__)
  38. app.register_blueprint(routes.bp, url_prefix="/api/datasource")
  39. return app.test_client()
  40. def test_admin_pool_status_is_safe(monkeypatch):
  41. client = make_client(monkeypatch)
  42. response = client.get(f"/api/datasource/{UID}/pool")
  43. body = response.get_json()["data"]
  44. text = response.get_data(as_text=True).lower()
  45. assert body["data_source_uid"] == UID
  46. assert body["pool_state"] == "healthy"
  47. assert "config_fingerprint" not in body
  48. assert "host" not in body
  49. assert "username" not in body
  50. assert "password" not in text
  51. def test_pool_invalidation_accepts_only_fixed_reason(monkeypatch):
  52. client = make_client(monkeypatch)
  53. accepted = client.post(
  54. f"/api/datasource/{UID}/pool/invalidate",
  55. json={"reason": "admin_reset"},
  56. )
  57. rejected = client.post(
  58. f"/api/datasource/{UID}/pool/invalidate",
  59. json={"reason": "arbitrary text"},
  60. )
  61. assert accepted.status_code == 200
  62. assert rejected.status_code == 400
  63. def test_platform_health_contains_only_pool_aggregates(monkeypatch):
  64. from app.core.data_source import runtime
  65. from app.core.system.health import data_source_pool_health
  66. class FakeManager:
  67. def snapshot(self):
  68. return [healthy_status()]
  69. monkeypatch.setattr(
  70. runtime,
  71. "peek_data_source_manager",
  72. lambda: FakeManager(),
  73. )
  74. assert data_source_pool_health() == {
  75. "active_pool_count": 1,
  76. "degraded_pool_count": 0,
  77. "open_circuit_count": 0,
  78. "checked_out_total": 0,
  79. "pool_timeout_total": 0,
  80. }
  81. def test_platform_health_does_not_create_runtime_when_unused(monkeypatch):
  82. from app.core.data_source import runtime
  83. from app.core.system.health import data_source_pool_health
  84. monkeypatch.setattr(
  85. runtime,
  86. "peek_data_source_manager",
  87. lambda: None,
  88. )
  89. assert data_source_pool_health()["active_pool_count"] == 0
  90. def test_pool_routes_are_admin_only_in_permission_matrix():
  91. from app.core.system.permissions import (
  92. DATASOURCE_POOL_MANAGE,
  93. permission_for_request,
  94. )
  95. assert permission_for_request(
  96. "/api/datasource/pools",
  97. "GET",
  98. ) == (DATASOURCE_POOL_MANAGE,)
  99. assert permission_for_request(
  100. f"/api/datasource/{UID}/pool/invalidate",
  101. "POST",
  102. ) == (DATASOURCE_POOL_MANAGE,)