import importlib.util import os import sys import types import unittest from pathlib import Path from types import SimpleNamespace from unittest.mock import Mock, patch DETECTORS = ( ("oom-detector", "oom_detector.py"), ("tikv-oom-detector", "tikv_oom_detector.py"), ("tiflash-oom-detector", "tiflash_oom_detector.py"), ) REQUIRED_ENV = { "ACTIVITY_BUCKET": "test-bucket", "ACTIVITY_HUB_ENDPOINT": "https://activity-hub.invalid", "CE_ENDPOINT": "https://compute-engine.invalid", "OOM_ANALYSIS_TASK": "oom-analyzer", "SOURCE": "compute-engine", "SOURCE_KEY": "source-key", } def stub_module(name, **attributes): module = types.ModuleType(name) for attribute, value in attributes.items(): setattr(module, attribute, value) return module def load_detector(directory, filename, jira_alerts, jira_env=None): activity_type = SimpleNamespace(EVENT="event") severity = SimpleNamespace(WARNING="warning") modules = { "aiohttp": stub_module("aiohttp"), "boto3": stub_module("boto3"), "sinks": stub_module("sinks", FullActivityClient=object, jira_alerts=jira_alerts), "tuotuo": stub_module("tuotuo", client=Mock()), "tuotuo.activities": stub_module( "tuotuo.activities", Activity=object, ActivityClient=object, ActivityType=activity_type, Sevirity=severity, ), "tuotuo.logs": stub_module("tuotuo.logs", LogEntity=object), "tuotuo.metrics": stub_module("tuotuo.metrics", QueryParam=object, QueryRange=object), } environment = dict(REQUIRED_ENV) environment.update(jira_env or {}) module_path = Path(__file__).parents[1] / directory / filename module_name = f"test_{directory.replace('-', '_')}_{id(jira_alerts)}" spec = importlib.util.spec_from_file_location(module_name, module_path) module = importlib.util.module_from_spec(spec) with patch.dict(os.environ, environment, clear=True), patch.dict(sys.modules, modules): spec.loader.exec_module(module) return module def sample_activity(): return SimpleNamespace( activity_id="activity-id", application_id="tenant-id", calibration_time=0, payload={"instance": "db-0"}, target_id="cluster-id", ) class JiraConfigurationTest(unittest.TestCase): def test_missing_cloud_credentials_do_not_break_detectors(self): for directory, filename in DETECTORS: with self.subTest(detector=directory): jira_alerts = Mock() module = load_detector(directory, filename, jira_alerts) module.alert_to_jira(sample_activity(), "org-id") jira_alerts.assert_not_called() def test_complete_cloud_credentials_are_forwarded(self): for directory, filename in DETECTORS: with self.subTest(detector=directory): jira_alerts = Mock(return_value={"status": "ok"}) module = load_detector( directory, filename, jira_alerts, { "JIRA_ENDPOINT": "https://jira.invalid", "JIRA_EMAIL": "alerts@example.invalid", "JIRA_API_TOKEN": "api-token", }, ) module.alert_to_jira(sample_activity(), "org-id") jira_alerts.assert_called_once() self.assertEqual(jira_alerts.call_args.kwargs["jira_url"], "https://jira.invalid") self.assertEqual(jira_alerts.call_args.kwargs["email"], "alerts@example.invalid") self.assertEqual(jira_alerts.call_args.kwargs["token"], "api-token") def test_jira_failure_does_not_break_detector_chain(self): for directory, filename in DETECTORS: with self.subTest(detector=directory): jira_alerts = Mock(side_effect=RuntimeError("jira unavailable")) module = load_detector( directory, filename, jira_alerts, { "JIRA_ENDPOINT": "https://jira.invalid", "JIRA_EMAIL": "alerts@example.invalid", "JIRA_API_TOKEN": "api-token", }, ) module.alert_to_jira(sample_activity(), "org-id") jira_alerts.assert_called_once() if __name__ == "__main__": unittest.main()