Browse Source

Unit tests for 11243: loading ri, ei, mds from lists

These tests make sure that entries are actually marked
undownloadable as appropriate.
Nick Mathewson 9 years ago
parent
commit
3efeb711f1

+ 7 - 7
src/or/networkstatus.c

@@ -595,10 +595,10 @@ networkstatus_vote_find_entry_idx(networkstatus_t *ns,
 
 /** As router_get_consensus_status_by_descriptor_digest, but does not return
  * a const pointer. */
-routerstatus_t *
-router_get_mutable_consensus_status_by_descriptor_digest(
+MOCK_IMPL(routerstatus_t *,
+router_get_mutable_consensus_status_by_descriptor_digest,(
                                                  networkstatus_t *consensus,
-                                                 const char *digest)
+                                                 const char *digest))
 {
   if (!consensus)
     consensus = current_consensus;
@@ -628,8 +628,8 @@ router_get_consensus_status_by_descriptor_digest(networkstatus_t *consensus,
 
 /** Given the digest of a router descriptor, return its current download
  * status, or NULL if the digest is unrecognized. */
-download_status_t *
-router_get_dl_status_by_descriptor_digest(const char *d)
+MOCK_IMPL(download_status_t *,
+router_get_dl_status_by_descriptor_digest,(const char *d))
 {
   routerstatus_t *rs;
   if (!current_ns_consensus)
@@ -995,8 +995,8 @@ networkstatus_get_latest_consensus(void)
 
 /** Return the latest consensus we have whose flavor matches <b>f</b>, or NULL
  * if we don't have one. */
-networkstatus_t *
-networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
+MOCK_IMPL(networkstatus_t *,
+networkstatus_get_latest_consensus_by_flavor,(consensus_flavor_t f))
 {
   if (f == FLAV_NS)
     return current_ns_consensus;

+ 11 - 6
src/or/networkstatus.h

@@ -12,6 +12,8 @@
 #ifndef TOR_NETWORKSTATUS_H
 #define TOR_NETWORKSTATUS_H
 
+#include "testsupport.h"
+
 void networkstatus_reset_warnings(void);
 void networkstatus_reset_download_failures(void);
 int router_reload_consensus_networkstatus(void);
@@ -35,16 +37,19 @@ routerstatus_t *networkstatus_vote_find_mutable_entry(networkstatus_t *ns,
                                               const char *digest);
 int networkstatus_vote_find_entry_idx(networkstatus_t *ns,
                                       const char *digest, int *found_out);
-download_status_t *router_get_dl_status_by_descriptor_digest(const char *d);
+
+MOCK_DECL(download_status_t *,router_get_dl_status_by_descriptor_digest,
+          (const char *d));
+
 const routerstatus_t *router_get_consensus_status_by_id(const char *digest);
 routerstatus_t *router_get_mutable_consensus_status_by_id(
                                    const char *digest);
 const routerstatus_t *router_get_consensus_status_by_descriptor_digest(
                                    networkstatus_t *consensus,
                                    const char *digest);
-routerstatus_t *router_get_mutable_consensus_status_by_descriptor_digest(
-                                   networkstatus_t *consensus,
-                                   const char *digest);
+MOCK_DECL(routerstatus_t *,
+          router_get_mutable_consensus_status_by_descriptor_digest,
+          (networkstatus_t *consensus, const char *digest));
 const routerstatus_t *router_get_consensus_status_by_nickname(
                                    const char *nickname,
                                    int warn_if_unnamed);
@@ -60,8 +65,8 @@ int consensus_is_waiting_for_certs(void);
 int client_would_use_router(const routerstatus_t *rs, time_t now,
                             const or_options_t *options);
 networkstatus_t *networkstatus_get_latest_consensus(void);
-networkstatus_t *networkstatus_get_latest_consensus_by_flavor(
-                                                  consensus_flavor_t f);
+MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
+          (consensus_flavor_t f));
 networkstatus_t *networkstatus_get_live_consensus(time_t now);
 networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
                                                              int flavor);

+ 4 - 4
src/or/routerlist.c

@@ -2615,8 +2615,8 @@ router_get_by_descriptor_digest(const char *digest)
 /** Return the signed descriptor for the router in our routerlist whose
  * 20-byte extra-info digest is <b>digest</b>.  Return NULL if no such router
  * is known. */
-signed_descriptor_t *
-router_get_by_extrainfo_digest(const char *digest)
+MOCK_IMPL(signed_descriptor_t *,
+router_get_by_extrainfo_digest,(const char *digest))
 {
   tor_assert(digest);
 
@@ -2939,8 +2939,8 @@ routerlist_insert(routerlist_t *rl, routerinfo_t *ri)
 /** Adds the extrainfo_t <b>ei</b> to the routerlist <b>rl</b>, if there is a
  * corresponding router in rl-\>routers or rl-\>old_routers.  Return true iff
  * we actually inserted <b>ei</b>.  Free <b>ei</b> if it isn't inserted. */
-static int
-extrainfo_insert(routerlist_t *rl, extrainfo_t *ei)
+MOCK_IMPL(STATIC int,
+extrainfo_insert,(routerlist_t *rl, extrainfo_t *ei))
 {
   int r = 0;
   routerinfo_t *ri = rimap_get(rl->identity_map,

+ 4 - 1
src/or/routerlist.h

@@ -82,7 +82,8 @@ int hexdigest_to_digest(const char *hexdigest, char *digest);
 const routerinfo_t *router_get_by_id_digest(const char *digest);
 routerinfo_t *router_get_mutable_by_digest(const char *digest);
 signed_descriptor_t *router_get_by_descriptor_digest(const char *digest);
-signed_descriptor_t *router_get_by_extrainfo_digest(const char *digest);
+MOCK_DECL(signed_descriptor_t *,router_get_by_extrainfo_digest,
+          (const char *digest));
 signed_descriptor_t *extrainfo_get_by_descriptor_digest(const char *digest);
 const char *signed_descriptor_get_body(const signed_descriptor_t *desc);
 const char *signed_descriptor_get_annotations(const signed_descriptor_t *desc);
@@ -215,6 +216,8 @@ STATIC void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
 
 MOCK_DECL(int, router_descriptor_is_older_than, (const routerinfo_t *router,
                                                  int seconds));
+MOCK_DECL(STATIC int, extrainfo_insert,(routerlist_t *rl, extrainfo_t *ei));
+
 #endif
 
 #endif

+ 11 - 10
src/test/example_extrainfo.inc

@@ -133,23 +133,24 @@ static const char EX_EI_BAD_NICKNAME_KEY[] =
   "/UBWNSyXCFDMqnddb/LZ8+VgttmxfYkpeRzSSmDijN3RbOvYJhhBAgMBAAE=\n"
   "-----END RSA PUBLIC KEY-----\n";
 
-static const char EX_EI_BAD_TOKENS[] =
-  "extra-info\n"
+const char EX_EI_BAD_TOKENS[] =
+  "extra-info bob 6F314FB01A31162BD5E473D4977AC570DC5B86BB\n"
+  "published 2014-10-05 20:07:00\n"
   "published 2014-10-05 20:07:00\n"
   "router-signature\n"
   "-----BEGIN SIGNATURE-----\n"
-  "e0b8fW9SkB81WwpkqR+/oLvVnNMpW4VwBKlNAnsPdInnNYySFCeILyOmb8A5j/D3\n"
-  "ZRbfKFgXrApya4ViGeh+1QHz+FM8nWaK0+5JE6cNw+DNiQWsUjrvq2bHoZi/Lv+k\n"
-  "RFkpbb0+IPdub9Jj3qn6cZBVSChD1aeCO9RkchV0744=\n"
+  "lhRIafrkKoQmnUoBLiq4XC8XKXrleGJZ5vefkLcgjOJ5IffsvVdIA7Vqq/ISbPrG\n"
+  "b/Zs0sJNL6naHPxJBglgHJqksSyiYHaeOetXg2Rb+vZ1v2S5BrVgk1nPMDhyIzqc\n"
+  "zU7eCxFf/1sXKtWlEKxGdX4LmVfnIln5aI31Bc4xRrE=\n"
   "-----END SIGNATURE-----\n"
   ;
 
-static const char EX_EI_BAD_TOKENS_FP[] = "1CE7F867C744F0CE635146D82C0A0CC4B3408E0A";
-static const char EX_EI_BAD_TOKENS_KEY[] =
+const char EX_EI_BAD_TOKENS_FP[] = "6F314FB01A31162BD5E473D4977AC570DC5B86BB";
+const char EX_EI_BAD_TOKENS_KEY[] =
   "-----BEGIN RSA PUBLIC KEY-----\n"
-  "MIGJAoGBALfrYfnIA7gTGwEC5tPqqTuSnMiINq/hPyMWXOFUEKEBRmctxba9fjiy\n"
-  "JDstxC4ZBpdHc3+uuFn6krGgBVzFCKvBkBHUQXgFmjlANzdcLrG7J78K6gXuPeeq\n"
-  "MbptD7thkuIdkPdVWMko7BrmBEr30EphgrKQqTWjrtBQj+CZ8Sr1AgMBAAE=\n"
+  "MIGJAoGBAL7Z8tz45Tb4tnEFS2sAyjubBV/giSfZdmXRkDV8Jo4xqWqhWFJn7+zN\n"
+  "AXBWBThGeVH2WXrpz5seNJXgZJPxMTMsrnSCGcRXZw0Npti2MkLuQ6+prZa+OPwE\n"
+  "OyC6jivtAaY/o9iYQjDC2avLXD3N4LvoygyF418KnNcjbzuFygffAgMBAAE=\n"
   "-----END RSA PUBLIC KEY-----\n";
 
 static const char EX_EI_BAD_START[] =

+ 227 - 0
src/test/test_dir.c

@@ -567,6 +567,231 @@ test_dir_parse_router_list(void *arg)
 #undef ADD
 }
 
+static download_status_t dls_minimal;
+static download_status_t dls_maximal;
+static download_status_t dls_bad_fingerprint;
+static download_status_t dls_bad_sig2;
+static download_status_t dls_bad_ports;
+static download_status_t dls_bad_tokens;
+
+static int mock_router_get_dl_status_unrecognized = 0;
+static int mock_router_get_dl_status_calls = 0;
+
+static download_status_t *
+mock_router_get_dl_status(const char *d)
+{
+  ++mock_router_get_dl_status_calls;
+  char hex[HEX_DIGEST_LEN+1];
+  base16_encode(hex, sizeof(hex), d, DIGEST_LEN);
+  if (!strcmp(hex, "3E31D19A69EB719C00B02EC60D13356E3F7A3452")) {
+    return &dls_minimal;
+  } else if (!strcmp(hex, "581D8A368A0FA854ECDBFAB841D88B3F1B004038")) {
+    return &dls_maximal;
+  } else if (!strcmp(hex, "2578AE227C6116CDE29B3F0E95709B9872DEE5F1")) {
+    return &dls_bad_fingerprint;
+  } else if (!strcmp(hex, "16D387D3A58F7DB3CF46638F8D0B90C45C7D769C")) {
+    return &dls_bad_sig2;
+  } else if (!strcmp(hex, "AB9EEAA95E7D45740185B4E519C76EAD756277A9")) {
+    return &dls_bad_ports;
+  } else if (!strcmp(hex, "A0CC2CEFAD59DBF19F468BFEE60E0868C804B422")) {
+    return &dls_bad_tokens;
+  } else {
+    ++mock_router_get_dl_status_unrecognized;
+    return NULL;
+  }
+}
+
+static void
+test_dir_load_routers(void *arg)
+{
+  (void) arg;
+  smartlist_t *chunks = smartlist_new();
+  smartlist_t *wanted = smartlist_new();
+  char buf[DIGEST_LEN];
+  char *mem_op_hex_tmp = NULL;
+
+#define ADD(str)                                                        \
+  do {                                                                  \
+    tt_int_op(0,==,router_get_router_hash(str, strlen(str), buf));      \
+    smartlist_add(wanted, tor_strdup(hex_str(buf, DIGEST_LEN)));        \
+  } while (0)
+
+  MOCK(router_get_dl_status_by_descriptor_digest, mock_router_get_dl_status);
+
+  smartlist_add(chunks, tor_strdup(EX_RI_MINIMAL));
+  smartlist_add(chunks, tor_strdup(EX_RI_BAD_FINGERPRINT));
+  smartlist_add(chunks, tor_strdup(EX_RI_BAD_SIG2));
+  smartlist_add(chunks, tor_strdup(EX_RI_MAXIMAL));
+  smartlist_add(chunks, tor_strdup(EX_RI_BAD_PORTS));
+  smartlist_add(chunks, tor_strdup(EX_RI_BAD_TOKENS));
+
+  /* not ADDing MINIMIAL */
+  ADD(EX_RI_MAXIMAL);
+  ADD(EX_RI_BAD_FINGERPRINT);
+  ADD(EX_RI_BAD_SIG2);
+  /* Not ADDing BAD_PORTS */
+  ADD(EX_RI_BAD_TOKENS);
+
+  char *list = smartlist_join_strings(chunks, "", 0, NULL);
+  tt_int_op(1, ==,
+            router_load_routers_from_string(list, NULL, SAVED_IN_JOURNAL,
+                                            wanted, 1, NULL));
+
+  /* The "maximal" router was added. */
+  /* "minimal" was not. */
+  tt_int_op(smartlist_len(router_get_routerlist()->routers),==,1);
+  routerinfo_t *r = smartlist_get(router_get_routerlist()->routers, 0);
+  test_memeq_hex(r->cache_info.signed_descriptor_digest,
+                 "581D8A368A0FA854ECDBFAB841D88B3F1B004038");
+  tt_int_op(dls_minimal.n_download_failures, ==, 0);
+  tt_int_op(dls_maximal.n_download_failures, ==, 0);
+
+  /* "Bad fingerprint" and "Bad tokens" should have gotten marked
+   * non-retriable. */
+  tt_want_int_op(mock_router_get_dl_status_calls, ==, 2);
+  tt_want_int_op(mock_router_get_dl_status_unrecognized, ==, 0);
+  tt_int_op(dls_bad_fingerprint.n_download_failures, ==, 255);
+  tt_int_op(dls_bad_tokens.n_download_failures, ==, 255);
+
+  /* bad_sig2 and bad ports" are retriable -- one since only the signature
+   * was bad, and one because we didn't ask for it. */
+  tt_int_op(dls_bad_sig2.n_download_failures, ==, 0);
+  tt_int_op(dls_bad_ports.n_download_failures, ==, 0);
+
+  /* Wanted still contains "BAD_SIG2" */
+  tt_int_op(smartlist_len(wanted), ==, 1);
+  tt_str_op(smartlist_get(wanted, 0), ==,
+            "E0A3753CEFD54128EAB239F294954121DB23D2EF");
+
+#undef ADD
+
+ done:
+  tor_free(mem_op_hex_tmp);
+  UNMOCK(router_get_dl_status_by_descriptor_digest);
+  SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+  smartlist_free(chunks);
+  SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
+  smartlist_free(wanted);
+}
+
+static int mock_get_by_ei_dd_calls = 0;
+static int mock_get_by_ei_dd_unrecognized = 0;
+
+static signed_descriptor_t sd_ei_minimal;
+static signed_descriptor_t sd_ei_bad_nickname;
+static signed_descriptor_t sd_ei_maximal;
+static signed_descriptor_t sd_ei_bad_tokens;
+static signed_descriptor_t sd_ei_bad_sig2;
+
+static signed_descriptor_t *
+mock_get_by_ei_desc_digest(const char *d)
+{
+
+  ++mock_get_by_ei_dd_calls;
+  char hex[HEX_DIGEST_LEN+1];
+  base16_encode(hex, sizeof(hex), d, DIGEST_LEN);
+
+  if (!strcmp(hex, "11E0EDF526950739F7769810FCACAB8C882FAEEE")) {
+    return &sd_ei_minimal;
+  } else if (!strcmp(hex, "47803B02A0E70E9E8BDA226CB1D74DE354D67DFF")) {
+    return &sd_ei_maximal;
+  } else if (!strcmp(hex, "D5DF4AA62EE9FFC9543D41150C9864908E0390AF")) {
+    return &sd_ei_bad_nickname;
+  } else if (!strcmp(hex, "16D387D3A58F7DB3CF46638F8D0B90C45C7D769C")) {
+    return &sd_ei_bad_sig2;
+  } else if (!strcmp(hex, "9D90F8C42955BBC57D54FB05E54A3F083AF42E8B")) {
+    return &sd_ei_bad_tokens;
+  } else {
+    ++mock_get_by_ei_dd_unrecognized;
+    return NULL;
+  }
+}
+
+static smartlist_t *mock_ei_insert_list = NULL;
+static int
+mock_ei_insert(routerlist_t *rl, extrainfo_t *ei)
+{
+  (void) rl;
+  smartlist_add(mock_ei_insert_list, ei);
+  return 1;
+}
+
+static void
+test_dir_load_extrainfo(void *arg)
+{
+  (void) arg;
+  smartlist_t *chunks = smartlist_new();
+  smartlist_t *wanted = smartlist_new();
+  char buf[DIGEST_LEN];
+  char *mem_op_hex_tmp = NULL;
+
+#define ADD(str)                                                        \
+  do {                                                                  \
+    tt_int_op(0,==,router_get_extrainfo_hash(str, strlen(str), buf));   \
+    smartlist_add(wanted, tor_strdup(hex_str(buf, DIGEST_LEN)));        \
+  } while (0)
+
+  mock_ei_insert_list = smartlist_new();
+  MOCK(router_get_by_extrainfo_digest, mock_get_by_ei_desc_digest);
+  MOCK(extrainfo_insert, mock_ei_insert);
+
+  smartlist_add(chunks, tor_strdup(EX_EI_MINIMAL));
+  smartlist_add(chunks, tor_strdup(EX_EI_BAD_NICKNAME));
+  smartlist_add(chunks, tor_strdup(EX_EI_MAXIMAL));
+  smartlist_add(chunks, tor_strdup(EX_EI_BAD_PUBLISHED));
+  smartlist_add(chunks, tor_strdup(EX_EI_BAD_TOKENS));
+
+  /* not ADDing MINIMIAL */
+  ADD(EX_EI_MAXIMAL);
+  ADD(EX_EI_BAD_NICKNAME);
+  /* Not ADDing BAD_PUBLISHED */
+  ADD(EX_EI_BAD_TOKENS);
+  ADD(EX_EI_BAD_SIG2);
+
+  char *list = smartlist_join_strings(chunks, "", 0, NULL);
+  router_load_extrainfo_from_string(list, NULL, SAVED_IN_JOURNAL, wanted, 1);
+
+  /* The "maximal" router was added. */
+  /* "minimal" was also added, even though we didn't ask for it, since
+   * that's what we do with extrainfos. */
+  tt_int_op(smartlist_len(mock_ei_insert_list),==,2);
+
+  extrainfo_t *e = smartlist_get(mock_ei_insert_list, 0);
+  test_memeq_hex(e->cache_info.signed_descriptor_digest,
+                 "11E0EDF526950739F7769810FCACAB8C882FAEEE");
+
+  e = smartlist_get(mock_ei_insert_list, 1);
+  test_memeq_hex(e->cache_info.signed_descriptor_digest,
+                 "47803B02A0E70E9E8BDA226CB1D74DE354D67DFF");
+  tt_int_op(dls_minimal.n_download_failures, ==, 0);
+  tt_int_op(dls_maximal.n_download_failures, ==, 0);
+
+  /* "Bad nickname" and "Bad tokens" should have gotten marked
+   * non-retriable. */
+  tt_want_int_op(mock_get_by_ei_dd_calls, ==, 2);
+  tt_want_int_op(mock_get_by_ei_dd_unrecognized, ==, 0);
+  tt_int_op(sd_ei_bad_nickname.ei_dl_status.n_download_failures, ==, 255);
+  tt_int_op(sd_ei_bad_tokens.ei_dl_status.n_download_failures, ==, 255);
+
+  /* bad_ports is retriable -- because we didn't ask for it. */
+  tt_int_op(dls_bad_ports.n_download_failures, ==, 0);
+
+  /* Wanted still contains "BAD_SIG2" */
+  tt_int_op(smartlist_len(wanted), ==, 1);
+  tt_str_op(smartlist_get(wanted, 0), ==,
+            "16D387D3A58F7DB3CF46638F8D0B90C45C7D769C");
+
+#undef ADD
+
+ done:
+  tor_free(mem_op_hex_tmp);
+  UNMOCK(router_get_by_extrainfo_digest);
+  SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+  smartlist_free(chunks);
+  SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
+  smartlist_free(wanted);
+}
+
 static void
 test_dir_versions(void *arg)
 {
@@ -2669,6 +2894,8 @@ struct testcase_t dir_tests[] = {
   DIR(routerparse_bad, 0),
   DIR(extrainfo_parsing, 0),
   DIR(parse_router_list, TT_FORK),
+  DIR(load_routers, TT_FORK),
+  DIR(load_extrainfo, TT_FORK),
   DIR_LEGACY(versions),
   DIR_LEGACY(fp_pairs),
   DIR(split_fps, 0),

+ 97 - 0
src/test/test_microdesc.c

@@ -7,6 +7,7 @@
 #include "config.h"
 #include "dirvote.h"
 #include "microdesc.h"
+#include "networkstatus.h"
 #include "routerlist.h"
 #include "routerparse.h"
 
@@ -618,11 +619,107 @@ test_md_parse(void *arg)
   tor_free(mem_op_hex_tmp);
 }
 
+static int mock_rgsbd_called = 0;
+static routerstatus_t *mock_rgsbd_val_a = NULL;
+static routerstatus_t *mock_rgsbd_val_b = NULL;
+static routerstatus_t *
+mock_router_get_status_by_digest(networkstatus_t *c, const char *d)
+{
+  (void) c;
+  ++mock_rgsbd_called;
+
+  if (fast_memeq(d, "\x5d\x76", 2)) {
+    memcpy(mock_rgsbd_val_a->descriptor_digest, d, 32);
+    return mock_rgsbd_val_a;
+  } else if (fast_memeq(d, "\x20\xd1", 2)) {
+    memcpy(mock_rgsbd_val_b->descriptor_digest, d, 32);
+    return mock_rgsbd_val_b;
+  } else {
+    return NULL;
+  }
+}
+
+static networkstatus_t *mock_ns_val = NULL;
+static networkstatus_t *
+mock_ns_get_by_flavor(consensus_flavor_t f)
+{
+  (void)f;
+  return mock_ns_val;
+}
+
+static void
+test_md_reject_cache(void *arg)
+{
+  (void) arg;
+  microdesc_cache_t *mc = NULL ;
+  smartlist_t *added = NULL, *wanted = smartlist_new();
+  or_options_t *options = get_options_mutable();
+  char buf[DIGEST256_LEN];
+
+  tor_free(options->DataDirectory);
+  options->DataDirectory = tor_strdup(get_fname("md_datadir_test_rej"));
+  mock_rgsbd_val_a = tor_malloc_zero(sizeof(routerstatus_t));
+  mock_rgsbd_val_b = tor_malloc_zero(sizeof(routerstatus_t));
+  mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
+
+  mock_ns_val->valid_after = time(NULL) - 86400;
+  mock_ns_val->valid_until = time(NULL) + 86400;
+  mock_ns_val->flavor = FLAV_MICRODESC;
+
+#ifdef _WIN32
+  tt_int_op(0, ==, mkdir(options->DataDirectory));
+#else
+  tt_int_op(0, ==, mkdir(options->DataDirectory, 0700));
+#endif
+
+  MOCK(router_get_mutable_consensus_status_by_descriptor_digest,
+       mock_router_get_status_by_digest);
+  MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor);
+
+  mc = get_microdesc_cache();
+#define ADD(hex)                                                        \
+  do {                                                                  \
+    tt_int_op(0,==,base16_decode(buf,sizeof(buf),hex,strlen(hex)));     \
+    smartlist_add(wanted, tor_memdup(buf, DIGEST256_LEN));              \
+  } while (0)
+
+  /* invalid,0 */
+  ADD("5d76bf1c6614e885614a1e0ad074e1ab4ea14655ebeefb1736a71b5ed8a15a51");
+  /* invalid,2 */
+  ADD("20d1576c5ab11bbcff0dedb1db4a3cfcc8bc8dd839d8cbfef92d00a1a7d7b294");
+  /* valid, 6 */
+  ADD("53f740bd222ab37f19f604b1d3759aa65eff1fbce9ac254bd0fa50d4af9b1bae");
+  /* valid, 8 */
+  ADD("a0a155562d8093d8fd0feb7b93b7226e17f056c2142aab7a4ea8c5867a0376d5");
+
+  added = microdescs_add_to_cache(mc, MD_PARSE_TEST_DATA, NULL,
+                                  SAVED_NOWHERE, 0, time(NULL), wanted);
+
+  tt_int_op(smartlist_len(added), ==, 2);
+  tt_int_op(mock_rgsbd_called, ==, 2);
+  tt_int_op(mock_rgsbd_val_a->dl_status.n_download_failures, ==, 255);
+  tt_int_op(mock_rgsbd_val_b->dl_status.n_download_failures, ==, 255);
+
+ done:
+  UNMOCK(networkstatus_get_latest_consensus_by_flavor);
+  UNMOCK(router_get_mutable_consensus_status_by_descriptor_digest);
+  if (options)
+    tor_free(options->DataDirectory);
+  microdesc_free_all();
+  smartlist_free(added);
+  SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
+  smartlist_free(wanted);
+  tor_free(mock_rgsbd_val_a);
+  tor_free(mock_rgsbd_val_b);
+  tor_free(mock_ns_val);
+}
+
 struct testcase_t microdesc_tests[] = {
   { "cache", test_md_cache, TT_FORK, NULL, NULL },
   { "broken_cache", test_md_cache_broken, TT_FORK, NULL, NULL },
   { "generate", test_md_generate, 0, NULL, NULL },
   { "parse", test_md_parse, 0, NULL, NULL },
+  { "reject_cache", test_md_reject_cache, TT_FORK, NULL, NULL },
   END_OF_TESTCASES
 };