|
| 1 | +import socket |
| 2 | +import urllib.request |
| 3 | + |
| 4 | +import prometheus_client |
| 5 | +import pytest |
| 6 | + |
| 7 | +from common.gunicorn.metrics_server import start_metrics_server |
| 8 | +from tests import GetLogsFixture |
| 9 | + |
| 10 | + |
| 11 | +@pytest.mark.prometheus_multiprocess_mode |
| 12 | +def test_start_metrics_server__multiprocess_mode__serves_metrics( |
| 13 | + unused_tcp_port: int, |
| 14 | + test_metric: prometheus_client.Counter, |
| 15 | +) -> None: |
| 16 | + # Given |
| 17 | + test_metric.labels(test_name="standalone_server_test").inc() |
| 18 | + |
| 19 | + # When |
| 20 | + start_metrics_server(port=unused_tcp_port) |
| 21 | + |
| 22 | + # Then |
| 23 | + with urllib.request.urlopen( |
| 24 | + f"http://localhost:{unused_tcp_port}/metrics" |
| 25 | + ) as response: |
| 26 | + content = response.read().decode() |
| 27 | + |
| 28 | + assert response.status == 200 |
| 29 | + assert "pytest_tests_run_total" in content |
| 30 | + assert 'test_name="standalone_server_test"' in content |
| 31 | + |
| 32 | + |
| 33 | +def test_start_metrics_server__multiproc_dir_unset__logs_warning_and_skips( |
| 34 | + get_logs: GetLogsFixture, |
| 35 | +) -> None: |
| 36 | + # Given |
| 37 | + # PROMETHEUS_MULTIPROC_DIR is not set (default state) |
| 38 | + |
| 39 | + # When |
| 40 | + start_metrics_server() |
| 41 | + |
| 42 | + # Then |
| 43 | + logs = get_logs("common.gunicorn.metrics_server") |
| 44 | + assert ( |
| 45 | + "WARNING", |
| 46 | + "PROMETHEUS_MULTIPROC_DIR not set, skipping metrics server", |
| 47 | + ) in logs |
| 48 | + |
| 49 | + |
| 50 | +@pytest.mark.prometheus_multiprocess_mode |
| 51 | +def test_start_metrics_server__called_multiple_times__remains_idempotent( |
| 52 | + unused_tcp_port: int, |
| 53 | +) -> None: |
| 54 | + # Given |
| 55 | + start_metrics_server(port=unused_tcp_port) |
| 56 | + |
| 57 | + # When |
| 58 | + start_metrics_server(port=unused_tcp_port) |
| 59 | + start_metrics_server(port=unused_tcp_port) |
| 60 | + |
| 61 | + # Then |
| 62 | + with urllib.request.urlopen( |
| 63 | + f"http://localhost:{unused_tcp_port}/metrics" |
| 64 | + ) as response: |
| 65 | + assert response.status == 200 |
| 66 | + |
| 67 | + |
| 68 | +@pytest.mark.prometheus_multiprocess_mode |
| 69 | +def test_start_metrics_server__port_unavailable__logs_error( |
| 70 | + unused_tcp_port: int, |
| 71 | + get_logs: GetLogsFixture, |
| 72 | +) -> None: |
| 73 | + # Given |
| 74 | + # Bind to 0.0.0.0 to match prometheus_client's default address |
| 75 | + blocker = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 76 | + blocker.bind(("0.0.0.0", unused_tcp_port)) |
| 77 | + blocker.listen(1) |
| 78 | + |
| 79 | + try: |
| 80 | + # When |
| 81 | + start_metrics_server(port=unused_tcp_port) |
| 82 | + |
| 83 | + # Then |
| 84 | + logs = get_logs("common.gunicorn.metrics_server") |
| 85 | + assert any( |
| 86 | + level == "ERROR" and "Failed to start metrics server" in msg |
| 87 | + for level, msg in logs |
| 88 | + ) |
| 89 | + finally: |
| 90 | + blocker.close() |
0 commit comments