|
1 | 1 | # (C) Datadog, Inc. 2021-present |
2 | 2 | # All rights reserved |
3 | 3 | # Licensed under a 3-clause BSD style license (see LICENSE) |
| 4 | +import json |
4 | 5 | import os |
| 6 | +from collections import namedtuple |
5 | 7 |
|
6 | 8 | import pytest |
7 | 9 |
|
8 | 10 | from datadog_checks.dev.utils import get_metadata_metrics |
9 | 11 | from datadog_checks.kong import Kong |
| 12 | +from datadog_checks.kong.check import KongCheck |
10 | 13 |
|
11 | | -from .common import HERE, METRICS_URL |
| 14 | +from .common import HERE, METRICS_URL, STATUS_URL |
12 | 15 |
|
13 | 16 | pytestmark = [pytest.mark.unit] |
14 | 17 |
|
| 18 | +FakeSample = namedtuple('FakeSample', ['value', 'labels']) |
| 19 | + |
15 | 20 | EXPECTED_METRICS = { |
16 | 21 | 'kong.bandwidth.count': 'monotonic_count', |
17 | 22 | 'kong.http.consumer.status.count': 'monotonic_count', |
@@ -115,3 +120,119 @@ def test_check(aggregator, dd_run_check, mock_http_response): |
115 | 120 | tags=['address:localhost:1004', 'endpoint:{}'.format(METRICS_URL), 'target:target4', 'upstream:upstream4'], |
116 | 121 | count=1, |
117 | 122 | ) |
| 123 | + |
| 124 | + |
| 125 | +def test_v2_default_metric_limit_is_zero(): |
| 126 | + # Kills the core/NumberReplacer mutant at check.py:12 (DEFAULT_METRIC_LIMIT 0 -> -1). |
| 127 | + assert KongCheck.DEFAULT_METRIC_LIMIT == 0 |
| 128 | + |
| 129 | + |
| 130 | +def test_v2_transformer_only_processes_sample_with_value_exactly_one(aggregator): |
| 131 | + # Kills the core/ReplaceComparisonOperator_NotEq_Lt mutant at check.py:28 (sample.value != 1 -> < 1). |
| 132 | + check = KongCheck('kong', {}, [{'openmetrics_endpoint': METRICS_URL}]) |
| 133 | + service_check = check.configure_transformer_upstream_target_health() |
| 134 | + |
| 135 | + sample = FakeSample(value=2, labels={'state': 'healthy'}) |
| 136 | + service_check(None, [(sample, ['state:healthy'], 'host1')], None) |
| 137 | + |
| 138 | + assert len(aggregator.service_checks('kong.upstream.target.health')) == 0 |
| 139 | + |
| 140 | + |
| 141 | +def test_v2_transformer_processes_samples_after_skipping_one(aggregator): |
| 142 | + # Kills the core/ReplaceContinueWithBreak mutant at check.py:29 (continue -> break drops later samples). |
| 143 | + check = KongCheck('kong', {}, [{'openmetrics_endpoint': METRICS_URL}]) |
| 144 | + service_check = check.configure_transformer_upstream_target_health() |
| 145 | + |
| 146 | + sample_data = [ |
| 147 | + (FakeSample(value=2, labels={'state': 'irrelevant'}), ['state:irrelevant'], 'host1'), |
| 148 | + (FakeSample(value=1, labels={'state': 'healthy'}), ['state:healthy'], 'host2'), |
| 149 | + ] |
| 150 | + service_check(None, sample_data, None) |
| 151 | + |
| 152 | + aggregator.assert_service_check( |
| 153 | + 'kong.upstream.target.health', status=KongCheck.OK, tags=[], hostname='host2', count=1 |
| 154 | + ) |
| 155 | + |
| 156 | + |
| 157 | +def test_legacy_new_uses_first_instance_to_choose_check_class(): |
| 158 | + # Kills the core/NumberReplacer mutant at kong.py:25 (instances[0] -> instances[-1]). |
| 159 | + check = Kong('kong', {}, [{'openmetrics_endpoint': METRICS_URL}, {'kong_status_url': STATUS_URL}]) |
| 160 | + assert isinstance(check, KongCheck) |
| 161 | + |
| 162 | + |
| 163 | +def test_legacy_fetch_data_raises_when_status_url_missing(): |
| 164 | + # Kills the core/AddNot mutant at kong.py:42 (double negation flips the missing-config check). |
| 165 | + check = Kong('kong', {}, [{'tags': []}]) |
| 166 | + with pytest.raises(Exception, match='missing "kong_status_url" value'): |
| 167 | + check._fetch_data() |
| 168 | + |
| 169 | + |
| 170 | +def test_legacy_fetch_data_can_connect_tags_use_default_port_and_appended_tags( |
| 171 | + aggregator, dd_run_check, mock_http_response |
| 172 | +): |
| 173 | + # Kills the core/NumberReplacer and ReplaceOrWithAnd mutants at kong.py:49 (default port 80 -> 79/81 or "or"->"and") |
| 174 | + # and the ReplaceBinaryOperator_Add_* / ReplaceBinaryOperator_Mod_* mutants at kong.py:51 |
| 175 | + # (tag list "+" and the "%" string formatting inside it). |
| 176 | + mock_http_response(json_data={'server': {}}) |
| 177 | + instance = {'kong_status_url': 'http://myhost/status/', 'tags': ['env:test']} |
| 178 | + check = Kong('kong', {}, [instance]) |
| 179 | + dd_run_check(check) |
| 180 | + |
| 181 | + aggregator.assert_service_check( |
| 182 | + 'kong.can_connect', status=Kong.OK, tags=['kong_host:myhost', 'kong_port:80', 'env:test'], count=1 |
| 183 | + ) |
| 184 | + |
| 185 | + |
| 186 | +def test_legacy_parse_json_defaults_tags_and_prefixes_metric_names(): |
| 187 | + # Kills the core/ReplaceComparisonOperator_Is_IsNot and AddNot mutants at kong.py:70 (tags is None check), |
| 188 | + # the ZeroIterationForLoop mutant at kong.py:76, and the ReplaceBinaryOperator_Add_* mutants at kong.py:77. |
| 189 | + check = Kong('kong', {}, [{'kong_status_url': 'http://myhost/status/'}]) |
| 190 | + raw = json.dumps({'server': {'total_requests': 42}}).encode('utf-8') |
| 191 | + |
| 192 | + output = check._parse_json(raw) |
| 193 | + |
| 194 | + assert output == [('kong.total_requests', 42, [])] |
| 195 | + |
| 196 | + |
| 197 | +def test_legacy_check_continues_after_metric_submission_error(aggregator, dd_run_check, mock_http_response): |
| 198 | + # Kills the core/ZeroIterationForLoop mutant at kong.py:34 and the ExceptionReplacer mutant at kong.py:38 |
| 199 | + # (a bad metric value must not stop later metrics in the loop from being submitted). |
| 200 | + mock_http_response(json_data={'server': {'bad_metric': 'oops', 'good_metric': 5}}) |
| 201 | + instance = {'kong_status_url': 'http://myhost/status/', 'tags': []} |
| 202 | + check = Kong('kong', {}, [instance]) |
| 203 | + dd_run_check(check) |
| 204 | + |
| 205 | + aggregator.assert_metric('kong.good_metric', value=5, count=1) |
| 206 | + aggregator.assert_metric('kong.bad_metric', count=0) |
| 207 | + |
| 208 | + |
| 209 | +def test_legacy_check_submits_critical_service_check_on_http_error(aggregator, mock_http_response): |
| 210 | + # Kills the core/ExceptionReplacer mutant at kong.py:58 (except Exception -> except CosmicRayTestingException). |
| 211 | + mock_http_response(status_code=500) |
| 212 | + instance = {'kong_status_url': 'http://myhost/status/', 'tags': []} |
| 213 | + check = Kong('kong', {}, [instance]) |
| 214 | + |
| 215 | + with pytest.raises(Exception): |
| 216 | + check.check(None) |
| 217 | + |
| 218 | + aggregator.assert_service_check('kong.can_connect', status=Kong.CRITICAL, count=1) |
| 219 | + |
| 220 | + |
| 221 | +def test_legacy_check_treats_status_code_below_200_as_critical(aggregator, mock_http_response): |
| 222 | + # Kills the core/ReplaceComparisonOperator_Eq_LtE mutant at kong.py:62 (status_code == 200 -> <= 200). |
| 223 | + mock_http_response(status_code=100, json_data={'server': {}}) |
| 224 | + instance = {'kong_status_url': 'http://myhost/status/', 'tags': []} |
| 225 | + check = Kong('kong', {}, [instance]) |
| 226 | + check.check(None) |
| 227 | + |
| 228 | + aggregator.assert_service_check('kong.can_connect', status=Kong.CRITICAL, count=1) |
| 229 | + |
| 230 | + |
| 231 | +def test_legacy_check_treats_status_code_above_200_as_critical(aggregator, mock_http_response): |
| 232 | + # Kills the core/ReplaceComparisonOperator_Eq_GtE mutant at kong.py:62 (status_code == 200 -> >= 200). |
| 233 | + mock_http_response(status_code=204, json_data={'server': {}}) |
| 234 | + instance = {'kong_status_url': 'http://myhost/status/', 'tags': []} |
| 235 | + check = Kong('kong', {}, [instance]) |
| 236 | + check.check(None) |
| 237 | + |
| 238 | + aggregator.assert_service_check('kong.can_connect', status=Kong.CRITICAL, count=1) |
0 commit comments