@@ -255,28 +255,44 @@ def test_constructor_w_directed_read_options(self):
255255 expected_scopes , creds , directed_read_options = self .DIRECTED_READ_OPTIONS
256256 )
257257
258+ @mock .patch ("google.cloud.spanner_v1.client.metrics" )
259+ @mock .patch ("google.cloud.spanner_v1.client.CloudMonitoringMetricsExporter" )
260+ @mock .patch ("google.cloud.spanner_v1.client.PeriodicExportingMetricReader" )
261+ @mock .patch ("google.cloud.spanner_v1.client.MeterProvider" )
258262 @mock .patch ("google.cloud.spanner_v1.client.SpannerMetricsTracerFactory" )
259263 @mock .patch .dict (os .environ , {"SPANNER_DISABLE_BUILTIN_METRICS" : "false" })
260264 def test_constructor_w_metrics_initialization_error (
261- self , mock_spanner_metrics_factory
265+ self ,
266+ mock_spanner_metrics_factory ,
267+ mock_meter_provider ,
268+ mock_periodic_reader ,
269+ mock_exporter ,
270+ mock_metrics ,
262271 ):
263272 """
264273 Test that Client constructor handles exceptions during metrics
265274 initialization and logs a warning.
266275 """
267276 from google .cloud .spanner_v1 .client import Client
277+ from google .cloud .spanner_v1 import client as MUT
268278
279+ MUT ._metrics_monitor_initialized = False
269280 mock_spanner_metrics_factory .side_effect = Exception ("Metrics init failed" )
270281 creds = build_scoped_credentials ()
271-
272- with self .assertLogs ("google.cloud.spanner_v1.client" , level = "WARNING" ) as log :
273- client = Client (project = self .PROJECT , credentials = creds )
274- self .assertIsNotNone (client )
275- self .assertIn (
276- "Failed to initialize Spanner built-in metrics. Error: Metrics init failed" ,
277- log .output [0 ],
278- )
279- mock_spanner_metrics_factory .assert_called_once ()
282+ try :
283+ with self .assertLogs (
284+ "google.cloud.spanner_v1.client" , level = "WARNING"
285+ ) as log :
286+ client = Client (project = self .PROJECT , credentials = creds )
287+ self .assertIsNotNone (client )
288+ self .assertIn (
289+ "Failed to initialize Spanner built-in metrics. Error: Metrics init failed" ,
290+ log .output [0 ],
291+ )
292+ mock_spanner_metrics_factory .assert_called_once ()
293+ mock_metrics .set_meter_provider .assert_called_once ()
294+ finally :
295+ MUT ._metrics_monitor_initialized = False
280296
281297 @mock .patch ("google.cloud.spanner_v1.client.SpannerMetricsTracerFactory" )
282298 @mock .patch .dict (os .environ , {"SPANNER_DISABLE_BUILTIN_METRICS" : "true" })
@@ -293,6 +309,58 @@ def test_constructor_w_disable_builtin_metrics_using_env(
293309 self .assertIsNotNone (client )
294310 mock_spanner_metrics_factory .assert_called_once_with (enabled = False )
295311
312+ @mock .patch ("google.cloud.spanner_v1.client.metrics" )
313+ @mock .patch ("google.cloud.spanner_v1.client.CloudMonitoringMetricsExporter" )
314+ @mock .patch ("google.cloud.spanner_v1.client.PeriodicExportingMetricReader" )
315+ @mock .patch ("google.cloud.spanner_v1.client.MeterProvider" )
316+ @mock .patch ("google.cloud.spanner_v1.client.SpannerMetricsTracerFactory" )
317+ @mock .patch .dict (os .environ , {"SPANNER_DISABLE_BUILTIN_METRICS" : "false" })
318+ def test_constructor_metrics_singleton_behavior (
319+ self ,
320+ mock_spanner_metrics_factory ,
321+ mock_meter_provider ,
322+ mock_periodic_reader ,
323+ mock_exporter ,
324+ mock_metrics ,
325+ ):
326+ """
327+ Test that metrics are only initialized once.
328+ """
329+ from google .cloud .spanner_v1 import client as MUT
330+
331+ # Reset global state for this test
332+ MUT ._metrics_monitor_initialized = False
333+ try :
334+ creds = build_scoped_credentials ()
335+
336+ # First client initialization
337+ client1 = MUT .Client (project = self .PROJECT , credentials = creds )
338+ self .assertIsNotNone (client1 )
339+ mock_metrics .set_meter_provider .assert_called_once ()
340+ mock_spanner_metrics_factory .assert_called_once ()
341+
342+ # Verify MeterProvider chain was created
343+ mock_meter_provider .assert_called_once ()
344+ mock_periodic_reader .assert_called_once ()
345+ mock_exporter .assert_called_once ()
346+
347+ self .assertTrue (MUT ._metrics_monitor_initialized )
348+
349+ # Reset mocks to verify they are NOT called again
350+ mock_metrics .set_meter_provider .reset_mock ()
351+ mock_spanner_metrics_factory .reset_mock ()
352+ mock_meter_provider .reset_mock ()
353+
354+ # Second client initialization
355+ client2 = MUT .Client (project = self .PROJECT , credentials = creds )
356+ self .assertIsNotNone (client2 )
357+ mock_metrics .set_meter_provider .assert_not_called ()
358+ mock_spanner_metrics_factory .assert_not_called ()
359+ mock_meter_provider .assert_not_called ()
360+ self .assertTrue (MUT ._metrics_monitor_initialized )
361+ finally :
362+ MUT ._metrics_monitor_initialized = False
363+
296364 @mock .patch ("google.cloud.spanner_v1.client.SpannerMetricsTracerFactory" )
297365 def test_constructor_w_disable_builtin_metrics_using_option (
298366 self , mock_spanner_metrics_factory
0 commit comments