1+ """Tests for Jupyter Server metrics functionality."""
2+
3+ import pytest
4+ import requests
5+ import time
6+ from unittest .mock import patch
7+
8+ from jupyter_server .prometheus .server import PrometheusMetricsServer , start_metrics_server
9+ from jupyter_server .serverapp import ServerApp
10+
11+
12+ @pytest .fixture
13+ def metrics_server_app ():
14+ """Create a server app with metrics enabled on a specific port."""
15+ # Override the environment variable for this test
16+ with patch .dict ('os.environ' , {'JUPYTER_SERVER_METRICS_PORT' : '9090' }):
17+ app = ServerApp ()
18+ # Set the metrics_port directly as a trait
19+ app .metrics_port = 9090
20+ app .initialize ([])
21+ return app
22+
23+
24+ @pytest .fixture
25+ def metrics_server (metrics_server_app ):
26+ """Start a metrics server for testing."""
27+ server = start_metrics_server (metrics_server_app , 9090 )
28+ # Give the server time to start
29+ time .sleep (0.1 )
30+ yield server
31+ # Cleanup
32+ if hasattr (server , 'stop' ):
33+ server .stop ()
34+
35+
36+ def test_metrics_server_starts (metrics_server ):
37+ """Test that the metrics server starts successfully."""
38+ assert metrics_server is not None
39+ assert hasattr (metrics_server , 'port' )
40+ assert metrics_server .port == 9090
41+
42+
43+ def test_metrics_endpoint_accessible (metrics_server ):
44+ """Test that the metrics endpoint is accessible."""
45+ response = requests .get (f'http://localhost:{ metrics_server .port } /metrics' )
46+ assert response .status_code == 200
47+ assert 'jupyter_server' in response .text
48+
49+
50+ def test_metrics_contains_kernel_metrics (metrics_server ):
51+ """Test that kernel metrics are present."""
52+ response = requests .get (f'http://localhost:{ metrics_server .port } /metrics' )
53+ assert response .status_code == 200
54+ content = response .text
55+ assert 'jupyter_kernel_currently_running_total' in content
56+
57+
58+ def test_metrics_contains_server_info (metrics_server ):
59+ """Test that server info metrics are present."""
60+ response = requests .get (f'http://localhost:{ metrics_server .port } /metrics' )
61+ assert response .status_code == 200
62+ content = response .text
63+ assert 'jupyter_server_info' in content
64+
65+
66+ def test_metrics_server_with_authentication ():
67+ """Test metrics server with authentication enabled."""
68+ app = ServerApp ()
69+ app .metrics_port = 9091
70+ app .authenticate_prometheus = True
71+ app .initialize ([])
72+ app .identity_provider .token = "test_token"
73+
74+ server = start_metrics_server (app , 9091 )
75+ time .sleep (0.1 )
76+
77+ try :
78+ # Without token should fail
79+ response = requests .get (f'http://localhost:{ server .port } /metrics' )
80+ assert response .status_code == 401
81+
82+ # With token should succeed
83+ response = requests .get (f'http://localhost:{ server .port } /metrics?token=test_token' )
84+ assert response .status_code == 200
85+ finally :
86+ if hasattr (server , 'stop' ):
87+ server .stop ()
88+
89+
90+ def test_metrics_server_port_conflict_handling ():
91+ """Test that metrics server handles port conflicts gracefully."""
92+ app = ServerApp ()
93+ app .metrics_port = 9092
94+ app .initialize ([])
95+ server2 = None
96+ # Start first server
97+ server1 = start_metrics_server (app , 9092 )
98+ time .sleep (0.1 )
99+
100+ try :
101+ # Try to start second server on same port
102+ server2 = start_metrics_server (app , 9092 )
103+ time .sleep (0.1 )
104+
105+ # One of them should have failed to start or used a different port
106+ if server2 is not None and hasattr (server2 , 'port' ):
107+ assert server2 .port != 9092 or server1 .port != 9092
108+ finally :
109+ if hasattr (server1 , 'stop' ):
110+ server1 .stop ()
111+ if server2 is not None and hasattr (server2 , 'stop' ):
112+ server2 .stop ()
113+
114+
115+ def test_metrics_server_disabled_when_port_zero ():
116+ """Test that metrics server is not started when port is 0."""
117+ with patch .dict ('os.environ' , {'JUPYTER_SERVER_METRICS_PORT' : '0' }):
118+ app = ServerApp ()
119+ app .metrics_port = 0
120+ app .initialize ([])
121+
122+ # Should not start metrics server
123+ assert not hasattr (app , 'metrics_server' ) or app .metrics_server is None
124+
125+
126+ def test_metrics_url_logging_with_separate_server ():
127+ """Test that metrics URL is logged correctly with separate server."""
128+ app = ServerApp ()
129+ app .metrics_port = 9093
130+ app .authenticate_prometheus = True
131+ app .initialize ([])
132+ app .identity_provider .token = "test_token"
133+
134+ # Start metrics server
135+ server = start_metrics_server (app , 9093 )
136+ time .sleep (0.1 )
137+
138+ try :
139+ # The URL should include the separate port
140+ expected_url = "http://localhost:9093/metrics?token=test_token"
141+ # This is a basic test - in practice you'd capture the log output
142+ assert server .port == 9093
143+ finally :
144+ if hasattr (server , 'stop' ):
145+ server .stop ()
146+
147+
148+ def test_metrics_url_logging_with_main_server ():
149+ """Test that metrics URL is logged correctly when using main server."""
150+ app = ServerApp ()
151+ app .metrics_port = 0 # Disable separate server
152+ app .authenticate_prometheus = True
153+ app .initialize ([])
154+ app .identity_provider .token = "test_token"
155+
156+ # Should use main server's /metrics endpoint
157+ # This would be tested by checking the log output in practice
158+ assert app .metrics_port == 0
0 commit comments