1 #include <mongoc.h>
2 #include <mongoc-set-private.h>
3 #include <mongoc-topology-description-apm-private.h>
4
5 #include "json-test.h"
6
7 #include "mongoc-client-private.h"
8 #include "test-libmongoc.h"
9 #include "mock_server/mock-server.h"
10 #include "mock_server/future.h"
11 #include "mock_server/future-functions.h"
12
13 #ifdef HAVE_STRINGS_H
14 #include <strings.h>
15 #endif
16
17
18 typedef struct {
19 bson_t events;
20 uint32_t n_events;
21 bson_oid_t topology_id;
22 } context_t;
23
24 static void
context_init(context_t * context)25 context_init (context_t *context)
26 {
27 bson_init (&context->events);
28 context->n_events = 0;
29 bson_oid_init_from_string (&context->topology_id,
30 "000000000000000000000000");
31 }
32
33 static void
context_append(context_t * context,bson_t * event)34 context_append (context_t *context, bson_t *event)
35 {
36 char str[16];
37 const char *key;
38
39 bson_uint32_to_string (context->n_events, &key, str, sizeof str);
40 BSON_APPEND_DOCUMENT (&context->events, key, event);
41
42 context->n_events++;
43
44 bson_destroy (event);
45 }
46
47 static void
context_destroy(context_t * context)48 context_destroy (context_t *context)
49 {
50 bson_destroy (&context->events);
51 }
52
53 static void
append_array(bson_t * bson,const char * key,const bson_t * array)54 append_array (bson_t *bson, const char *key, const bson_t *array)
55 {
56 if (array->len) {
57 BSON_APPEND_ARRAY (bson, key, array);
58 } else {
59 bson_t tmp = BSON_INITIALIZER;
60 BSON_APPEND_ARRAY (bson, key, &tmp);
61 bson_destroy (&tmp);
62 }
63 }
64
65 static void
sd_to_bson(const mongoc_server_description_t * sd,bson_t * bson)66 sd_to_bson (const mongoc_server_description_t *sd, bson_t *bson)
67 {
68 const mongoc_host_list_t *host_list;
69
70 host_list = mongoc_server_description_host (sd);
71
72 bson_init (bson);
73 BSON_APPEND_UTF8 (bson, "address", host_list->host_and_port);
74
75 append_array (bson, "arbiters", &sd->arbiters);
76 append_array (bson, "hosts", &sd->hosts);
77 append_array (bson, "passives", &sd->passives);
78
79 if (sd->current_primary) {
80 BSON_APPEND_UTF8 (bson, "primary", sd->current_primary);
81 }
82
83 if (sd->set_name) {
84 BSON_APPEND_UTF8 (bson, "setName", sd->set_name);
85 }
86
87 BSON_APPEND_UTF8 (bson, "type", mongoc_server_description_type (sd));
88 }
89
90 static void
td_to_bson(const mongoc_topology_description_t * td,bson_t * bson)91 td_to_bson (const mongoc_topology_description_t *td, bson_t *bson)
92 {
93 size_t i;
94 bson_t servers = BSON_INITIALIZER;
95 bson_t server;
96 char str[16];
97 const char *key;
98
99 for (i = 0; i < td->servers->items_len; i++) {
100 bson_uint32_to_string ((uint32_t) i, &key, str, sizeof str);
101 sd_to_bson (mongoc_set_get_item (td->servers, (int) i), &server);
102 BSON_APPEND_DOCUMENT (&servers, key, &server);
103 bson_destroy (&server);
104 }
105
106 bson_init (bson);
107 BSON_APPEND_UTF8 (
108 bson, "topologyType", mongoc_topology_description_type (td));
109
110 if (td->set_name) {
111 BSON_APPEND_UTF8 (bson, "setName", td->set_name);
112 }
113
114 BSON_APPEND_ARRAY (bson, "servers", &servers);
115
116 bson_destroy (&servers);
117 }
118
119 static void
server_changed(const mongoc_apm_server_changed_t * event)120 server_changed (const mongoc_apm_server_changed_t *event)
121 {
122 context_t *ctx;
123 bson_oid_t topology_id;
124 const char *host_and_port;
125 bson_t prev_sd;
126 bson_t new_sd;
127
128 ctx = (context_t *) mongoc_apm_server_changed_get_context (event);
129
130 /* check topology id is consistent */
131 mongoc_apm_server_changed_get_topology_id (event, &topology_id);
132 ASSERT (bson_oid_equal (&topology_id, &ctx->topology_id));
133
134 host_and_port = mongoc_apm_server_changed_get_host (event)->host_and_port;
135 sd_to_bson (mongoc_apm_server_changed_get_previous_description (event),
136 &prev_sd);
137 sd_to_bson (mongoc_apm_server_changed_get_new_description (event), &new_sd);
138
139 context_append (ctx,
140 BCON_NEW ("server_description_changed_event",
141 "{",
142 "topologyId",
143 BCON_UTF8 ("42"),
144 "address",
145 BCON_UTF8 (host_and_port),
146 "previousDescription",
147 BCON_DOCUMENT (&prev_sd),
148 "newDescription",
149 BCON_DOCUMENT (&new_sd),
150 "}"));
151
152 bson_destroy (&prev_sd);
153 bson_destroy (&new_sd);
154 }
155
156 static void
server_opening(const mongoc_apm_server_opening_t * event)157 server_opening (const mongoc_apm_server_opening_t *event)
158 {
159 context_t *ctx;
160 bson_oid_t topology_id;
161 const char *host_and_port;
162
163 ctx = (context_t *) mongoc_apm_server_opening_get_context (event);
164
165 mongoc_apm_server_opening_get_topology_id (event, &topology_id);
166 ASSERT (bson_oid_equal (&topology_id, &ctx->topology_id));
167
168 host_and_port = mongoc_apm_server_opening_get_host (event)->host_and_port;
169 context_append (ctx,
170 BCON_NEW ("server_opening_event",
171 "{",
172 "address",
173 BCON_UTF8 (host_and_port),
174 "topologyId",
175 BCON_UTF8 ("42"),
176 "}"));
177 }
178
179 static void
server_closed(const mongoc_apm_server_closed_t * event)180 server_closed (const mongoc_apm_server_closed_t *event)
181 {
182 context_t *ctx;
183 bson_oid_t topology_id;
184 const char *host_and_port;
185
186 ctx = (context_t *) mongoc_apm_server_closed_get_context (event);
187
188 mongoc_apm_server_closed_get_topology_id (event, &topology_id);
189 ASSERT (bson_oid_equal (&topology_id, &ctx->topology_id));
190
191 host_and_port = mongoc_apm_server_closed_get_host (event)->host_and_port;
192 context_append (ctx,
193 BCON_NEW ("server_closed_event",
194 "{",
195 "address",
196 BCON_UTF8 (host_and_port),
197 "topologyId",
198 BCON_UTF8 ("42"),
199 "}"));
200 }
201
202 static void
topology_changed(const mongoc_apm_topology_changed_t * event)203 topology_changed (const mongoc_apm_topology_changed_t *event)
204 {
205 context_t *ctx;
206 bson_oid_t topology_id;
207 bson_t prev_td;
208 bson_t new_td;
209
210 ctx = (context_t *) mongoc_apm_topology_changed_get_context (event);
211
212 mongoc_apm_topology_changed_get_topology_id (event, &topology_id);
213 ASSERT (bson_oid_equal (&topology_id, &ctx->topology_id));
214
215 td_to_bson (mongoc_apm_topology_changed_get_previous_description (event),
216 &prev_td);
217 td_to_bson (mongoc_apm_topology_changed_get_new_description (event),
218 &new_td);
219
220 context_append (ctx,
221 BCON_NEW ("topology_description_changed_event",
222 "{",
223 "newDescription",
224 BCON_DOCUMENT (&new_td),
225 "previousDescription",
226 BCON_DOCUMENT (&prev_td),
227 "topologyId",
228 BCON_UTF8 ("42"),
229 "}"));
230
231 bson_destroy (&prev_td);
232 bson_destroy (&new_td);
233 }
234
235 static void
topology_opening(const mongoc_apm_topology_opening_t * event)236 topology_opening (const mongoc_apm_topology_opening_t *event)
237 {
238 context_t *ctx;
239 bson_oid_t zeroes;
240
241 /* new event's topology id is NOT all zeroes */
242 bson_oid_init_from_string (&zeroes, "000000000000000000000000");
243 ASSERT (!bson_oid_equal (&event->topology_id, &zeroes));
244
245 ctx = (context_t *) mongoc_apm_topology_opening_get_context (event);
246 mongoc_apm_topology_opening_get_topology_id (event, &ctx->topology_id);
247 context_append (
248 ctx,
249 BCON_NEW (
250 "topology_opening_event", "{", "topologyId", BCON_UTF8 ("42"), "}"));
251 }
252
253 static void
topology_closed(const mongoc_apm_topology_closed_t * event)254 topology_closed (const mongoc_apm_topology_closed_t *event)
255 {
256 context_t *ctx;
257 bson_oid_t topology_id;
258
259 ctx = (context_t *) mongoc_apm_topology_closed_get_context (event);
260 mongoc_apm_topology_closed_get_topology_id (event, &topology_id);
261 ASSERT (bson_oid_equal (&topology_id, &ctx->topology_id));
262 context_append (
263 ctx,
264 BCON_NEW (
265 "topology_closed_event", "{", "topologyId", BCON_UTF8 ("42"), "}"));
266 }
267
268 /* no standard tests in the specs repo for heartbeat events, so invent some */
269 static void
server_heartbeat_started(const mongoc_apm_server_heartbeat_started_t * event)270 server_heartbeat_started (const mongoc_apm_server_heartbeat_started_t *event)
271 {
272 context_t *ctx;
273 const mongoc_host_list_t *host;
274
275 ctx = (context_t *) mongoc_apm_server_heartbeat_started_get_context (event);
276 host = mongoc_apm_server_heartbeat_started_get_host (event);
277 context_append (ctx,
278 BCON_NEW ("heartbeat_started_event",
279 "{",
280 "host",
281 BCON_UTF8 (host->host_and_port),
282 "}"));
283 }
284
285 static void
server_heartbeat_succeeded(const mongoc_apm_server_heartbeat_succeeded_t * event)286 server_heartbeat_succeeded (
287 const mongoc_apm_server_heartbeat_succeeded_t *event)
288 {
289 context_t *ctx;
290 const mongoc_host_list_t *host;
291
292 ctx =
293 (context_t *) mongoc_apm_server_heartbeat_succeeded_get_context (event);
294 host = mongoc_apm_server_heartbeat_succeeded_get_host (event);
295 context_append (ctx,
296 BCON_NEW ("heartbeat_succeeded_event",
297 "{",
298 "host",
299 BCON_UTF8 (host->host_and_port),
300 "}"));
301 }
302
303 static void
server_heartbeat_failed(const mongoc_apm_server_heartbeat_failed_t * event)304 server_heartbeat_failed (const mongoc_apm_server_heartbeat_failed_t *event)
305 {
306 context_t *ctx;
307 const mongoc_host_list_t *host;
308
309 ctx = (context_t *) mongoc_apm_server_heartbeat_failed_get_context (event);
310 host = mongoc_apm_server_heartbeat_failed_get_host (event);
311 context_append (ctx,
312 BCON_NEW ("heartbeat_failed_event",
313 "{",
314 "host",
315 BCON_UTF8 (host->host_and_port),
316 "}"));
317 }
318
319 static mongoc_apm_callbacks_t *
topology_event_callbacks(void)320 topology_event_callbacks (void)
321 {
322 mongoc_apm_callbacks_t *callbacks;
323
324 callbacks = mongoc_apm_callbacks_new ();
325 mongoc_apm_set_server_changed_cb (callbacks, server_changed);
326 mongoc_apm_set_server_opening_cb (callbacks, server_opening);
327 mongoc_apm_set_server_closed_cb (callbacks, server_closed);
328 mongoc_apm_set_topology_changed_cb (callbacks, topology_changed);
329 mongoc_apm_set_topology_opening_cb (callbacks, topology_opening);
330 mongoc_apm_set_topology_closed_cb (callbacks, topology_closed);
331
332 return callbacks;
333 }
334
335 static void
client_set_topology_event_callbacks(mongoc_client_t * client,context_t * context)336 client_set_topology_event_callbacks (mongoc_client_t *client,
337 context_t *context)
338 {
339 mongoc_apm_callbacks_t *callbacks;
340
341 callbacks = topology_event_callbacks ();
342 mongoc_client_set_apm_callbacks (client, callbacks, (void *) context);
343 mongoc_apm_callbacks_destroy (callbacks);
344 }
345
346 static void
pool_set_topology_event_callbacks(mongoc_client_pool_t * pool,context_t * context)347 pool_set_topology_event_callbacks (mongoc_client_pool_t *pool,
348 context_t *context)
349 {
350 mongoc_apm_callbacks_t *callbacks;
351
352 callbacks = topology_event_callbacks ();
353 mongoc_client_pool_set_apm_callbacks (pool, callbacks, (void *) context);
354 mongoc_apm_callbacks_destroy (callbacks);
355 }
356
357 static mongoc_apm_callbacks_t *
heartbeat_event_callbacks(void)358 heartbeat_event_callbacks (void)
359 {
360 mongoc_apm_callbacks_t *callbacks;
361
362 callbacks = mongoc_apm_callbacks_new ();
363 mongoc_apm_set_server_heartbeat_started_cb (callbacks,
364 server_heartbeat_started);
365 mongoc_apm_set_server_heartbeat_succeeded_cb (callbacks,
366 server_heartbeat_succeeded);
367 mongoc_apm_set_server_heartbeat_failed_cb (callbacks,
368 server_heartbeat_failed);
369
370 return callbacks;
371 }
372
373 static void
client_set_heartbeat_event_callbacks(mongoc_client_t * client,context_t * context)374 client_set_heartbeat_event_callbacks (mongoc_client_t *client,
375 context_t *context)
376 {
377 mongoc_apm_callbacks_t *callbacks;
378
379 callbacks = heartbeat_event_callbacks ();
380 mongoc_client_set_apm_callbacks (client, callbacks, (void *) context);
381 mongoc_apm_callbacks_destroy (callbacks);
382 }
383
384 static void
pool_set_heartbeat_event_callbacks(mongoc_client_pool_t * pool,context_t * context)385 pool_set_heartbeat_event_callbacks (mongoc_client_pool_t *pool,
386 context_t *context)
387 {
388 mongoc_apm_callbacks_t *callbacks;
389
390 callbacks = heartbeat_event_callbacks ();
391 mongoc_client_pool_set_apm_callbacks (pool, callbacks, (void *) context);
392 mongoc_apm_callbacks_destroy (callbacks);
393 }
394
395 /*
396 *-----------------------------------------------------------------------
397 *
398 * Run the JSON tests from the SDAM Monitoring spec.
399 *
400 *-----------------------------------------------------------------------
401 */
402 static void
test_sdam_monitoring_cb(bson_t * test)403 test_sdam_monitoring_cb (bson_t *test)
404 {
405 mongoc_client_t *client;
406 mongoc_topology_t *topology;
407 bson_t phase;
408 bson_t phases;
409 bson_t outcome;
410 bson_iter_t phase_iter;
411 bson_iter_t phase_field_iter;
412 bson_iter_t outcome_iter;
413 bson_iter_t iter;
414 bson_t events_expected;
415 context_t context;
416
417 /* parse out the uri and use it to create a client */
418 BSON_ASSERT (bson_iter_init_find (&iter, test, "uri"));
419 client = mongoc_client_new (bson_iter_utf8 (&iter, NULL));
420 topology = client->topology;
421 context_init (&context);
422 client_set_topology_event_callbacks (client, &context);
423
424 /* for each phase, parse and validate */
425 BSON_ASSERT (bson_iter_init_find (&iter, test, "phases"));
426 bson_iter_bson (&iter, &phases);
427 bson_iter_init (&phase_iter, &phases);
428
429 while (bson_iter_next (&phase_iter)) {
430 bson_iter_bson (&phase_iter, &phase);
431
432 /* this test doesn't exercise this code path naturally, see below in
433 * _test_topology_events for a non-hacky test of this event */
434 _mongoc_topology_description_monitor_opening (&topology->description);
435 process_sdam_test_ismaster_responses (&phase,
436 &client->topology->description);
437
438 /* parse out "outcome" and validate */
439 BSON_ASSERT (bson_iter_init_find (&phase_field_iter, &phase, "outcome"));
440 bson_iter_bson (&phase_field_iter, &outcome);
441 bson_iter_init (&outcome_iter, &outcome);
442
443 while (bson_iter_next (&outcome_iter)) {
444 if (strcmp ("events", bson_iter_key (&outcome_iter)) == 0) {
445 bson_iter_bson (&outcome_iter, &events_expected);
446 check_json_apm_events (&context.events, &events_expected);
447 } else {
448 fprintf (stderr,
449 "ERROR: unparsed test field %s\n",
450 bson_iter_key (&outcome_iter));
451 BSON_ASSERT (false);
452 }
453 }
454 }
455
456 mongoc_client_destroy (client);
457 context_destroy (&context);
458 }
459
460 /*
461 *-----------------------------------------------------------------------
462 *
463 * Runner for the JSON tests for SDAM Monitoring..
464 *
465 *-----------------------------------------------------------------------
466 */
467 static void
test_all_spec_tests(TestSuite * suite)468 test_all_spec_tests (TestSuite *suite)
469 {
470 char resolved[PATH_MAX];
471
472 ASSERT (realpath (JSON_DIR "/server_discovery_and_monitoring/monitoring",
473 resolved));
474
475 install_json_test_suite (suite, resolved, &test_sdam_monitoring_cb);
476 }
477
478 static void
_test_topology_events(bool pooled)479 _test_topology_events (bool pooled)
480 {
481 mongoc_client_t *client;
482 mongoc_client_pool_t *pool = NULL;
483 context_t context;
484 bool r;
485 bson_error_t error;
486 bson_iter_t events_iter;
487 bson_iter_t event_iter;
488 uint32_t i;
489
490 context_init (&context);
491
492 if (pooled) {
493 pool = test_framework_client_pool_new ();
494 pool_set_topology_event_callbacks (pool, &context);
495 client = mongoc_client_pool_pop (pool);
496 } else {
497 client = test_framework_client_new ();
498 client_set_topology_event_callbacks (client, &context);
499 }
500
501 r = mongoc_client_command_simple (
502 client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error);
503 ASSERT_OR_PRINT (r, error);
504
505 if (pooled) {
506 mongoc_client_pool_push (pool, client);
507 mongoc_client_pool_destroy (pool);
508 } else {
509 mongoc_client_destroy (client);
510 }
511
512 /* first event is topology opening */
513 bson_iter_init (&events_iter, &context.events);
514 bson_iter_next (&events_iter);
515 ASSERT (bson_iter_recurse (&events_iter, &event_iter));
516 ASSERT (bson_iter_find (&event_iter, "topology_opening_event"));
517
518 /* last event is topology closed */
519 for (i = 1; i < context.n_events; i++) {
520 ASSERT (bson_iter_next (&events_iter));
521 }
522
523 ASSERT (bson_iter_recurse (&events_iter, &event_iter));
524 ASSERT (bson_iter_find (&event_iter, "topology_closed_event"));
525
526 /* no more events */
527 ASSERT (!bson_iter_next (&events_iter));
528
529 context_destroy (&context);
530 }
531
532 static void
test_topology_events_single(void)533 test_topology_events_single (void)
534 {
535 _test_topology_events (false);
536 }
537
538 static void
test_topology_events_pooled(void)539 test_topology_events_pooled (void)
540 {
541 _test_topology_events (true);
542 }
543
544 static bool
responder(request_t * request,void * data)545 responder (request_t *request, void *data)
546 {
547 if (!strcmp (request->command_name, "foo")) {
548 mock_server_replies_simple (request, "{'ok': 1}");
549 request_destroy (request);
550 return true;
551 }
552
553 return false;
554 }
555
556 static void
_test_heartbeat_events(bool pooled,bool succeeded)557 _test_heartbeat_events (bool pooled, bool succeeded)
558 {
559 context_t context;
560 mock_server_t *server;
561 mongoc_uri_t *uri;
562 mongoc_client_t *client;
563 mongoc_client_pool_t *pool = NULL;
564 future_t *future;
565 request_t *request;
566 char *expected_json;
567 bson_error_t error;
568
569 context_init (&context);
570
571 /* auto-respond to "foo" command */
572 server = mock_server_new ();
573 mock_server_run (server);
574 mock_server_autoresponds (server, responder, NULL, NULL);
575 uri = mongoc_uri_copy (mock_server_get_uri (server));
576 mongoc_uri_set_option_as_int32 (uri, "serverSelectionTimeoutMS", 400);
577
578 if (pooled) {
579 pool = mongoc_client_pool_new (uri);
580 pool_set_heartbeat_event_callbacks (pool, &context);
581 client = mongoc_client_pool_pop (pool);
582 } else {
583 client = mongoc_client_new_from_uri (uri);
584 client_set_heartbeat_event_callbacks (client, &context);
585 }
586
587 /* trigger "ismaster" handshake */
588 future = future_client_command_simple (
589 client, "admin", tmp_bson ("{'foo': 1}"), NULL, NULL, &error);
590
591 /* topology scanner calls ismaster once */
592 request = mock_server_receives_ismaster (server);
593
594 if (succeeded) {
595 mock_server_replies_ok_and_destroys (request);
596 } else {
597 mock_server_hangs_up (request);
598 request_destroy (request);
599 }
600
601 /* pooled client opens new socket, handshakes it by calling ismaster again */
602 if (pooled && succeeded) {
603 request = mock_server_receives_ismaster (server);
604 mock_server_replies_ok_and_destroys (request);
605 }
606
607 if (succeeded) {
608 /* "foo" command succeeds */
609 ASSERT_OR_PRINT (future_get_bool (future), error);
610 } else {
611 ASSERT (!future_get_bool (future));
612 }
613
614 if (pooled) {
615 mongoc_client_pool_push (pool, client);
616 mongoc_client_pool_destroy (pool);
617 } else {
618 mongoc_client_destroy (client);
619 }
620
621 /* even if pooled, only topology scanner sends events, so we get one pair */
622 if (succeeded) {
623 expected_json = bson_strdup_printf (
624 "{'0': {'heartbeat_started_event': {'host': '%s'}},"
625 " '1': {'heartbeat_succeeded_event': {'host': '%s'}}}",
626 mock_server_get_host_and_port (server),
627 mock_server_get_host_and_port (server));
628 } else {
629 expected_json = bson_strdup_printf (
630 "{'0': {'heartbeat_started_event': {'host': '%s'}},"
631 " '1': {'heartbeat_failed_event': {'host': '%s'}}}",
632 mock_server_get_host_and_port (server),
633 mock_server_get_host_and_port (server));
634 }
635
636 check_json_apm_events (&context.events, tmp_bson (expected_json));
637
638 future_destroy (future);
639 bson_free (expected_json);
640 mongoc_uri_destroy (uri);
641 mock_server_destroy (server);
642 context_destroy (&context);
643 }
644
645 static void
test_heartbeat_events_single_succeeded(void)646 test_heartbeat_events_single_succeeded (void)
647 {
648 _test_heartbeat_events (false, true);
649 }
650
651 static void
test_heartbeat_events_pooled_succeeded(void)652 test_heartbeat_events_pooled_succeeded (void)
653 {
654 _test_heartbeat_events (true, true);
655 }
656
657
658 static void
test_heartbeat_events_single_failed(void)659 test_heartbeat_events_single_failed (void)
660 {
661 _test_heartbeat_events (false, false);
662 }
663
664 static void
test_heartbeat_events_pooled_failed(void)665 test_heartbeat_events_pooled_failed (void)
666 {
667 _test_heartbeat_events (true, false);
668 }
669
670
671 void
test_sdam_monitoring_install(TestSuite * suite)672 test_sdam_monitoring_install (TestSuite *suite)
673 {
674 test_all_spec_tests (suite);
675 TestSuite_AddLive (
676 suite,
677 "/server_discovery_and_monitoring/monitoring/topology/single",
678 test_topology_events_single);
679 TestSuite_AddLive (
680 suite,
681 "/server_discovery_and_monitoring/monitoring/topology/pooled",
682 test_topology_events_pooled);
683 TestSuite_AddMockServerTest (
684 suite,
685 "/server_discovery_and_monitoring/monitoring/heartbeat/single/succeeded",
686 test_heartbeat_events_single_succeeded);
687 TestSuite_AddMockServerTest (
688 suite,
689 "/server_discovery_and_monitoring/monitoring/heartbeat/single/failed",
690 test_heartbeat_events_single_failed);
691 TestSuite_AddMockServerTest (
692 suite,
693 "/server_discovery_and_monitoring/monitoring/heartbeat/pooled/succeeded",
694 test_heartbeat_events_pooled_succeeded);
695 TestSuite_AddMockServerTest (
696 suite,
697 "/server_discovery_and_monitoring/monitoring/heartbeat/pooled/failed",
698 test_heartbeat_events_pooled_failed);
699 }
700