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