test.c (57991B)
1 2 #include "nostrdb.h" 3 #include "hex.h" 4 #include "io.h" 5 #include "bolt11/bolt11.h" 6 #include "invoice.h" 7 #include "block.h" 8 #include "protected_queue.h" 9 #include "memchr.h" 10 #include "print_util.h" 11 #include "bindings/c/profile_reader.h" 12 #include "bindings/c/profile_verifier.h" 13 #include "bindings/c/meta_reader.h" 14 #include "bindings/c/meta_verifier.h" 15 16 #include <stdio.h> 17 #include <assert.h> 18 #include <unistd.h> 19 20 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 21 22 int ndb_print_kind_keys(struct ndb_txn *txn); 23 static const char *test_dir = "./testdata/db"; 24 25 static NdbProfile_table_t lookup_profile(struct ndb_txn *txn, uint64_t pk) 26 { 27 void *root; 28 size_t len; 29 assert((root = ndb_get_profile_by_key(txn, pk, &len))); 30 assert(root); 31 32 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root); 33 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 34 return profile; 35 } 36 37 static void print_search(struct ndb_txn *txn, struct ndb_search *search) 38 { 39 NdbProfile_table_t profile = lookup_profile(txn, search->profile_key); 40 const char *name = NdbProfile_name_get(profile); 41 const char *display_name = NdbProfile_display_name_get(profile); 42 printf("searched_name name:'%s' display_name:'%s' pk:%" PRIu64 " ts:%" PRIu64 " id:", name, display_name, search->profile_key, search->key->timestamp); 43 print_hex(search->key->id, 32); 44 printf("\n"); 45 } 46 47 static void test_filters() 48 { 49 struct ndb_filter filter, *f; 50 struct ndb_filter_elements *current; 51 struct ndb_note *note; 52 unsigned char buffer[4096]; 53 54 const char *test_note = "{\"id\": \"160e76ca67405d7ce9ef7d2dd72f3f36401c8661a73d45498af842d40b01b736\",\"pubkey\": \"67c67870aebc327eb2a2e765e6dbb42f0f120d2c4e4e28dc16b824cf72a5acc1\",\"created_at\": 1700688516,\"kind\": 1337,\"tags\": [[\"t\",\"hashtag\"],[\"t\",\"grownostr\"],[\"p\",\"4d2e7a6a8e08007ace5a03391d21735f45caf1bf3d67b492adc28967ab46525e\"]],\"content\": \"\",\"sig\": \"20c2d070261ed269559ada40ca5ac395c389681ee3b5f7d50de19dd9b328dd70cf27d9d13875e87c968d9b49fa05f66e90f18037be4529b9e582c7e2afac3f06\"}"; 55 56 f = &filter; 57 assert(ndb_note_from_json(test_note, strlen(test_note), ¬e, buffer, sizeof(buffer))); 58 59 assert(ndb_filter_init(f)); 60 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 61 assert(ndb_filter_add_int_element(f, 1337)); 62 assert(ndb_filter_add_int_element(f, 2)); 63 64 current = ndb_filter_current_element(f); 65 assert(current->count == 2); 66 assert(current->field.type == NDB_FILTER_KINDS); 67 68 // can't start if we've already started 69 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS) == 0); 70 assert(ndb_filter_start_field(f, NDB_FILTER_TAGS) == 0); 71 ndb_filter_end_field(f); 72 ndb_filter_end(f); 73 74 // should be sorted after end 75 assert(current->elements[0] == 2); 76 assert(current->elements[1] == 1337); 77 78 // try matching the filter 79 assert(ndb_filter_matches(f, note)); 80 81 _ndb_note_set_kind(note, 1); 82 83 // inverse match 84 assert(!ndb_filter_matches(f, note)); 85 86 // should also match 2 87 _ndb_note_set_kind(note, 2); 88 assert(ndb_filter_matches(f, note)); 89 90 // don't free, just reset data pointers 91 ndb_filter_destroy(f); 92 ndb_filter_init(f); 93 94 // now try generic matches 95 assert(ndb_filter_start_tag_field(f, 't')); 96 assert(ndb_filter_add_str_element(f, "grownostr")); 97 ndb_filter_end_field(f); 98 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 99 assert(ndb_filter_add_int_element(f, 3)); 100 ndb_filter_end_field(f); 101 ndb_filter_end(f); 102 103 // shouldn't match the kind filter 104 assert(!ndb_filter_matches(f, note)); 105 106 _ndb_note_set_kind(note, 3); 107 108 // now it should 109 assert(ndb_filter_matches(f, note)); 110 111 ndb_filter_destroy(f); 112 ndb_filter_init(f); 113 assert(ndb_filter_start_field(f, NDB_FILTER_AUTHORS)); 114 assert(ndb_filter_add_id_element(f, ndb_note_pubkey(note))); 115 ndb_filter_end_field(f); 116 ndb_filter_end(f); 117 assert(f->current == -1); 118 assert(ndb_filter_matches(f, note)); 119 120 ndb_filter_destroy(f); 121 } 122 123 static void test_filter_json() 124 { 125 struct ndb_filter filter, *f = &filter; 126 char buf[1024]; 127 128 unsigned char pk[32] = { 0x32, 0xe1, 0x82, 0x76, 0x35, 0x45, 0x0e, 0xbb, 0x3c, 0x5a, 0x7d, 0x12, 0xc1, 0xf8, 0xe7, 0xb2, 0xb5, 0x14, 0x43, 0x9a, 0xc1, 0x0a, 0x67, 0xee, 0xf3, 0xd9, 0xfd, 0x9c, 0x5c, 0x68, 0xe2, 0x45 }; 129 130 unsigned char pk2[32] = { 131 0xd1, 0x2c, 0x17, 0xbd, 0xe3, 0x09, 0x4a, 0xd3, 0x2f, 0x4a, 0xb8, 0x62, 0xa6, 0xcc, 0x6f, 0x5c, 0x28, 0x9c, 0xfe, 0x7d, 0x58, 0x02, 0x27, 0x0b, 0xdf, 0x34, 0x90, 0x4d, 0xf5, 0x85, 0xf3, 0x49 132 }; 133 134 ndb_filter_init(f); 135 assert(ndb_filter_start_field(f, NDB_FILTER_UNTIL)); 136 assert(ndb_filter_add_int_element(f, 42)); 137 ndb_filter_end_field(f); 138 ndb_filter_end(f); 139 assert(ndb_filter_json(f, buf, sizeof(buf))); 140 assert(!strcmp("{\"until\":42}", buf)); 141 ndb_filter_destroy(f); 142 143 ndb_filter_init(f); 144 assert(ndb_filter_start_field(f, NDB_FILTER_UNTIL)); 145 assert(ndb_filter_add_int_element(f, 42)); 146 ndb_filter_end_field(f); 147 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 148 assert(ndb_filter_add_int_element(f, 1)); 149 assert(ndb_filter_add_int_element(f, 2)); 150 ndb_filter_end_field(f); 151 ndb_filter_end(f); 152 assert(ndb_filter_json(f, buf, sizeof(buf))); 153 assert(!strcmp("{\"until\":42,\"kinds\":[1,2]}", buf)); 154 ndb_filter_destroy(f); 155 156 ndb_filter_init(f); 157 assert(ndb_filter_start_field(f, NDB_FILTER_IDS)); 158 assert(ndb_filter_add_id_element(f, pk)); 159 assert(ndb_filter_add_id_element(f, pk2)); 160 ndb_filter_end_field(f); 161 ndb_filter_end(f); 162 assert(ndb_filter_json(f, buf, sizeof(buf))); 163 assert(!strcmp("{\"ids\":[\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"d12c17bde3094ad32f4ab862a6cc6f5c289cfe7d5802270bdf34904df585f349\"]}", buf)); 164 ndb_filter_destroy(f); 165 } 166 167 static void test_invoice_encoding(const char *bolt11_str) 168 { 169 unsigned char buf[4096]; 170 char *fail = NULL; 171 struct cursor cur; 172 struct ndb_invoice invoice; 173 struct bolt11 *bolt11; 174 175 bolt11 = bolt11_decode_minimal(NULL, bolt11_str, &fail); 176 make_cursor(buf, buf + sizeof(buf), &cur); 177 178 assert(fail == NULL); 179 assert(ndb_encode_invoice(&cur, bolt11)); 180 cur.p = cur.start; 181 assert(ndb_decode_invoice(&cur, &invoice)); 182 183 assert(bolt11->msat->millisatoshis == invoice.amount); 184 assert(bolt11->timestamp == invoice.timestamp); 185 assert(bolt11->expiry == invoice.expiry); 186 187 if (bolt11->description != NULL && invoice.description != NULL) 188 assert(!strcmp(bolt11->description, invoice.description)); 189 else if (bolt11->description_hash != NULL && invoice.description_hash != NULL) 190 assert(!memcmp(bolt11->description_hash->u.u8, invoice.description_hash, 32)); 191 else 192 assert(0); 193 194 tal_free(bolt11); 195 } 196 197 static void test_encode_decode_invoice() 198 { 199 const char *deschash = "lnbc12n1pjctuljsp57l6za0xry37prkrz7vuv4324ljnssm8ukr2vrf6qvvrgclsmpyhspp5xqfuk89duzjlt2yg56ym7p3enrfxxltyfpc364qc8nsu3kznkl8shp5eugmd894yph7wq68u09gke5x2hmn7mg3zrwd06fs57gmcrjm0uxsxqyjw5qcqpjrzjqd7yw3w4kvhx8uvcj7qusfw4uqre3j56zjz9t07nd2u55yuya3awsrqdlcqqdzcqqqqqqqqqqqqqqzqqyg9qxpqysgqwm2tsc448ellvf5xem2c95hfvc07lakph9r8hffh704uxqhs22r9s4ly0jel48zv6f7fy8zjkgmjt5h2l4jc9gyj4av42s40qvve2ysqwuega8"; 200 const char *desc = "lnbc12u1pjctuklsp5lg8wdhq2g5xfphkqd5k6gf0femt06wfevu94uuqfprc4ggyqma7spp54lmpmz0mhv3lczepdckr0acf3gdany2654u4k2s8fp5xh0yanjhsdq5w3jhxapdd9h8vmmfvdjsxqyjw5qcqpjrzjqgtsq68q0s9wdadpg32gcfu7hslgkhdpaysj2ha3dtnm8882wa6jyzahpqqqpsgqqyqqqqlgqqqqqpsq9q9qxpqysgqdqzhl8gz46nmalhg27stl25z2u7mqtclv3zz223mjwut90m24fa46xqprjewsqys78j2uljfznz5vtefctu6fw7375ee66e62tj965gpcs85tc"; 201 202 test_invoice_encoding(deschash); 203 test_invoice_encoding(desc); 204 } 205 206 // Test the created_at query plan via a contact-list query 207 static void test_timeline_query() 208 { 209 struct ndb *ndb; 210 struct ndb_filter filter; 211 struct ndb_config config; 212 struct ndb_txn txn; 213 struct ndb_query_result results[10]; 214 int count; 215 ndb_default_config(&config); 216 217 assert(ndb_init(&ndb, test_dir, &config)); 218 219 ndb_filter_init(&filter); 220 ndb_filter_start_field(&filter, NDB_FILTER_AUTHORS); 221 #include "testdata/author-filter.c" 222 ndb_filter_end_field(&filter); 223 ndb_filter_end(&filter); 224 225 ndb_begin_query(ndb, &txn); 226 assert(ndb_query(&txn, &filter, 1, results, 227 sizeof(results)/sizeof(results[0]), &count)); 228 ndb_end_query(&txn); 229 230 assert(count == 10); 231 } 232 233 // Test fetched_at profile records. These are saved when new profiles are 234 // processed, or the last time we've fetched the profile. 235 static void test_fetched_at() 236 { 237 struct ndb *ndb; 238 struct ndb_txn txn; 239 uint64_t fetched_at, t1, t2; 240 struct ndb_config config; 241 ndb_default_config(&config); 242 243 assert(ndb_init(&ndb, test_dir, &config)); 244 245 const unsigned char pubkey[] = { 0x87, 0xfb, 0xc6, 0xd5, 0x98, 0x31, 0xa8, 0x23, 0xa4, 0x5d, 0x10, 0x1f, 246 0x86, 0x94, 0x2c, 0x41, 0xcd, 0xe2, 0x90, 0x23, 0xf4, 0x09, 0x20, 0x24, 247 0xa2, 0x7c, 0x50, 0x10, 0x3c, 0x15, 0x40, 0x01 }; 248 249 const char profile_1[] = "[\"EVENT\",{\"id\": \"a44eb8fb6931d6155b04038bef0624407e46c85c61e5758392cbb615f00184ca\",\"pubkey\": \"87fbc6d59831a823a45d101f86942c41cde29023f4092024a27c50103c154001\",\"created_at\": 1695593354,\"kind\": 0,\"tags\": [],\"content\": \"{\\\"name\\\":\\\"b\\\"}\",\"sig\": \"7540bbde4b4479275e20d95acaa64027359a73989927f878825093cba2f468bd8e195919a77b4c230acecddf92e6b4bee26918b0c0842f84ec7c1fae82453906\"}]"; 250 251 t1 = time(NULL); 252 253 // process the first event, this should set the fetched_at 254 assert(ndb_process_client_event(ndb, profile_1, sizeof(profile_1))); 255 256 // we sleep for a second because we want to make sure the fetched_at is not 257 // updated for the next record, which is an older profile. 258 sleep(1); 259 260 assert(ndb_begin_query(ndb, &txn)); 261 262 // this should be set to t1 263 fetched_at = ndb_read_last_profile_fetch(&txn, pubkey); 264 265 if (fetched_at != t1) { 266 printf("fetched_at != t1? %" PRIu64 " != %" PRIu64 "\n", fetched_at, t1); 267 } 268 assert(fetched_at == t1); 269 270 t2 = time(NULL); 271 assert(t1 != t2); // sanity 272 273 const char profile_2[] = "[\"EVENT\",{\"id\": \"9b2861dda8fc602ec2753f92f1a443c9565de606e0c8f4fd2db4f2506a3b13ca\",\"pubkey\": \"87fbc6d59831a823a45d101f86942c41cde29023f4092024a27c50103c154001\",\"created_at\": 1695593347,\"kind\": 0,\"tags\": [],\"content\": \"{\\\"name\\\":\\\"a\\\"}\",\"sig\": \"f48da228f8967d33c3caf0a78f853b5144631eb86c7777fd25949123a5272a92765a0963d4686dd0efe05b7a9b986bfac8d43070b234153acbae5006d5a90f31\"}]"; 274 275 t2 = time(NULL); 276 277 // process the second event, since this is older it should not change 278 // fetched_at 279 assert(ndb_process_client_event(ndb, profile_2, sizeof(profile_2))); 280 281 // we sleep for a second because we want to make sure the fetched_at is not 282 // updated for the next record, which is an older profile. 283 sleep(1); 284 285 fetched_at = ndb_read_last_profile_fetch(&txn, pubkey); 286 assert(fetched_at == t1); 287 } 288 289 static void test_reaction_counter() 290 { 291 static const int alloc_size = 1024 * 1024; 292 char *json = malloc(alloc_size); 293 struct ndb *ndb; 294 size_t len; 295 void *root; 296 int written, reactions, results; 297 NdbEventMeta_table_t meta; 298 struct ndb_txn txn; 299 struct ndb_config config; 300 ndb_default_config(&config); 301 static const int num_reactions = 3; 302 uint64_t note_ids[num_reactions], subid; 303 304 assert(ndb_init(&ndb, test_dir, &config)); 305 306 read_file("testdata/reactions.json", (unsigned char*)json, alloc_size, &written); 307 308 assert((subid = ndb_subscribe(ndb, NULL, 0))); 309 310 assert(ndb_process_client_events(ndb, json, written)); 311 312 for (reactions = 0; reactions < num_reactions;) { 313 results = ndb_wait_for_notes(ndb, subid, note_ids, num_reactions); 314 reactions += results; 315 fprintf(stderr, "got %d notes, total %d\n", results, reactions); 316 assert(reactions > 0); 317 } 318 319 assert(ndb_begin_query(ndb, &txn)); 320 321 const unsigned char id[32] = { 322 0x1a, 0x41, 0x56, 0x30, 0x31, 0x09, 0xbb, 0x4a, 0x66, 0x0a, 0x6a, 0x90, 323 0x04, 0xb0, 0xcd, 0xce, 0x8d, 0x83, 0xc3, 0x99, 0x1d, 0xe7, 0x86, 0x4f, 324 0x18, 0x76, 0xeb, 0x0f, 0x62, 0x2c, 0x68, 0xe8 325 }; 326 327 assert((root = ndb_get_note_meta(&txn, id, &len))); 328 assert(0 == NdbEventMeta_verify_as_root(root, len)); 329 assert((meta = NdbEventMeta_as_root(root))); 330 331 reactions = NdbEventMeta_reactions_get(meta); 332 //printf("counted reactions: %d\n", reactions); 333 assert(reactions == 2); 334 ndb_end_query(&txn); 335 ndb_destroy(ndb); 336 free(json); 337 } 338 339 static void test_profile_search(struct ndb *ndb) 340 { 341 struct ndb_txn txn; 342 struct ndb_search search; 343 int i; 344 const char *name; 345 NdbProfile_table_t profile; 346 347 assert(ndb_begin_query(ndb, &txn)); 348 assert(ndb_search_profile(&txn, &search, "jean")); 349 //print_search(&txn, &search); 350 profile = lookup_profile(&txn, search.profile_key); 351 name = NdbProfile_name_get(profile); 352 assert(!strncmp(name, "jean", 4)); 353 354 assert(ndb_search_profile_next(&search)); 355 //print_search(&txn, &search); 356 profile = lookup_profile(&txn, search.profile_key); 357 name = NdbProfile_name_get(profile); 358 //assert(strncmp(name, "jean", 4)); 359 360 for (i = 0; i < 3; i++) { 361 ndb_search_profile_next(&search); 362 //print_search(&txn, &search); 363 } 364 365 //assert(!strcmp(name, "jb55")); 366 367 ndb_search_profile_end(&search); 368 ndb_end_query(&txn); 369 } 370 371 static void test_profile_updates() 372 { 373 static const int alloc_size = 1024 * 1024; 374 static const int num_notes = 3; 375 char *json; 376 int written, i; 377 size_t len; 378 struct ndb *ndb; 379 struct ndb_config config; 380 struct ndb_txn txn; 381 uint64_t key, subid; 382 uint64_t note_ids[num_notes]; 383 void *record; 384 385 json = malloc(alloc_size); 386 387 ndb_default_config(&config); 388 assert(ndb_init(&ndb, test_dir, &config)); 389 390 subid = ndb_subscribe(ndb, NULL, 0); 391 392 ndb_debug("testing profile updates\n"); 393 read_file("testdata/profile-updates.json", (unsigned char*)json, alloc_size, &written); 394 assert(ndb_process_client_events(ndb, json, written)); 395 396 for (i = 0; i < num_notes;) 397 i += ndb_wait_for_notes(ndb, subid, note_ids, num_notes); 398 399 assert(ndb_begin_query(ndb, &txn)); 400 const unsigned char pk[32] = { 401 0x1c, 0x55, 0x46, 0xe4, 0xf5, 0x93, 0x3b, 0xbe, 0x86, 0x66, 402 0x2a, 0x8e, 0xc3, 0x28, 0x9a, 0x29, 0x87, 0xc0, 0x5d, 0xab, 403 0x25, 0x6c, 0x06, 0x8b, 0x77, 0x42, 0x9f, 0x0f, 0x08, 0xa7, 404 0xa0, 0x90 405 }; 406 record = ndb_get_profile_by_pubkey(&txn, pk, &len, &key); 407 408 assert(record); 409 int res = NdbProfileRecord_verify_as_root(record, len); 410 assert(res == 0); 411 412 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(record); 413 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 414 const char *name = NdbProfile_name_get(profile); 415 416 assert(!strcmp(name, "c")); 417 418 ndb_destroy(ndb); 419 free(json); 420 } 421 422 static void test_load_profiles() 423 { 424 static const int alloc_size = 1024 * 1024; 425 char *json = malloc(alloc_size); 426 struct ndb *ndb; 427 int written; 428 struct ndb_config config; 429 ndb_default_config(&config); 430 431 assert(ndb_init(&ndb, test_dir, &config)); 432 433 read_file("testdata/profiles.json", (unsigned char*)json, alloc_size, &written); 434 435 assert(ndb_process_events(ndb, json, written)); 436 437 ndb_destroy(ndb); 438 439 assert(ndb_init(&ndb, test_dir, &config)); 440 unsigned char id[32] = { 441 0x22, 0x05, 0x0b, 0x6d, 0x97, 0xbb, 0x9d, 0xa0, 0x9e, 0x90, 0xed, 0x0c, 442 0x6d, 0xd9, 0x5e, 0xed, 0x1d, 0x42, 0x3e, 0x27, 0xd5, 0xcb, 0xa5, 0x94, 443 0xd2, 0xb4, 0xd1, 0x3a, 0x55, 0x43, 0x09, 0x07 }; 444 const char *expected_content = "{\"website\":\"selenejin.com\",\"lud06\":\"\",\"nip05\":\"selenejin@BitcoinNostr.com\",\"picture\":\"https://nostr.build/i/3549697beda0fe1f4ae621f359c639373d92b7c8d5c62582b656c5843138c9ed.jpg\",\"display_name\":\"Selene Jin\",\"about\":\"INTJ | Founding Designer @Blockstream\",\"name\":\"SeleneJin\"}"; 445 446 struct ndb_txn txn; 447 assert(ndb_begin_query(ndb, &txn)); 448 struct ndb_note *note = ndb_get_note_by_id(&txn, id, NULL, NULL); 449 assert(note != NULL); 450 assert(!strcmp(ndb_note_content(note), expected_content)); 451 ndb_end_query(&txn); 452 453 test_profile_search(ndb); 454 455 ndb_destroy(ndb); 456 457 free(json); 458 } 459 460 static void test_fuzz_events() { 461 struct ndb *ndb; 462 const char *str = "[\"EVENT\"\"\"{\"content\"\"created_at\":0 \"id\"\"5086a8f76fe1da7fb56a25d1bebbafd70fca62e36a72c6263f900ff49b8f8604\"\"kind\":0 \"pubkey\":9c87f94bcbe2a837adc28d46c34eeaab8fc2e1cdf94fe19d4b99ae6a5e6acedc \"sig\"\"27374975879c94658412469cee6db73d538971d21a7b580726a407329a4cafc677fb56b946994cea59c3d9e118fef27e4e61de9d2c46ac0a65df14153 ea93cf5\"\"tags\"[[][\"\"]]}]"; 463 struct ndb_config config; 464 ndb_default_config(&config); 465 466 ndb_init(&ndb, test_dir, &config); 467 ndb_process_event(ndb, str, strlen(str)); 468 ndb_destroy(ndb); 469 } 470 471 static void test_migrate() { 472 static const char *v0_dir = "testdata/db/v0"; 473 struct ndb *ndb; 474 struct ndb_config config; 475 struct ndb_txn txn; 476 477 ndb_default_config(&config); 478 ndb_config_set_flags(&config, NDB_FLAG_NOMIGRATE); 479 480 fprintf(stderr, "testing migrate on v0\n"); 481 assert(ndb_init(&ndb, v0_dir, &config)); 482 assert(ndb_begin_query(ndb, &txn)); 483 assert(ndb_db_version(&txn) == 0); 484 assert(ndb_end_query(&txn)); 485 ndb_destroy(ndb); 486 487 ndb_config_set_flags(&config, 0); 488 489 assert(ndb_init(&ndb, v0_dir, &config)); 490 ndb_destroy(ndb); 491 assert(ndb_init(&ndb, v0_dir, &config)); 492 493 assert(ndb_begin_query(ndb, &txn)); 494 assert(ndb_db_version(&txn) == 3); 495 assert(ndb_end_query(&txn)); 496 497 test_profile_search(ndb); 498 ndb_destroy(ndb); 499 } 500 501 static void test_basic_event() { 502 unsigned char buf[512]; 503 struct ndb_builder builder, *b = &builder; 504 struct ndb_note *note; 505 int ok; 506 507 unsigned char id[32]; 508 memset(id, 1, 32); 509 510 unsigned char pubkey[32]; 511 memset(pubkey, 2, 32); 512 513 unsigned char sig[64]; 514 memset(sig, 3, 64); 515 516 const char *hex_pk = "5d9b81b2d4d5609c5565286fc3b511dc6b9a1b3d7d1174310c624d61d1f82bb9"; 517 518 ok = ndb_builder_init(b, buf, sizeof(buf)); 519 assert(ok); 520 note = builder.note; 521 522 //memset(note->padding, 3, sizeof(note->padding)); 523 524 ok = ndb_builder_set_content(b, hex_pk, strlen(hex_pk)); assert(ok); 525 ndb_builder_set_id(b, id); assert(ok); 526 ndb_builder_set_pubkey(b, pubkey); assert(ok); 527 ndb_builder_set_sig(b, sig); assert(ok); 528 529 ok = ndb_builder_new_tag(b); assert(ok); 530 ok = ndb_builder_push_tag_str(b, "p", 1); assert(ok); 531 ok = ndb_builder_push_tag_str(b, hex_pk, 64); assert(ok); 532 533 ok = ndb_builder_new_tag(b); assert(ok); 534 ok = ndb_builder_push_tag_str(b, "word", 4); assert(ok); 535 ok = ndb_builder_push_tag_str(b, "words", 5); assert(ok); 536 ok = ndb_builder_push_tag_str(b, "w", 1); assert(ok); 537 538 ok = ndb_builder_finalize(b, ¬e, NULL); 539 assert(ok); 540 541 // content should never be packed id 542 // TODO: figure out how to test this now that we don't expose it 543 // assert(note->content.packed.flag != NDB_PACKED_ID); 544 assert(ndb_tags_count(ndb_note_tags(note)) == 2); 545 546 // test iterator 547 struct ndb_iterator iter, *it = &iter; 548 549 ndb_tags_iterate_start(note, it); 550 ok = ndb_tags_iterate_next(it); 551 assert(ok); 552 553 assert(ndb_tag_count(it->tag) == 2); 554 const char *p = ndb_iter_tag_str(it, 0).str; 555 struct ndb_str hpk = ndb_iter_tag_str(it, 1); 556 557 hex_decode(hex_pk, 64, id, 32); 558 559 assert(hpk.flag == NDB_PACKED_ID); 560 assert(memcmp(hpk.id, id, 32) == 0); 561 assert(!strcmp(p, "p")); 562 563 ok = ndb_tags_iterate_next(it); 564 assert(ok); 565 assert(ndb_tag_count(it->tag) == 3); 566 assert(!strcmp(ndb_iter_tag_str(it, 0).str, "word")); 567 assert(!strcmp(ndb_iter_tag_str(it, 1).str, "words")); 568 assert(!strcmp(ndb_iter_tag_str(it, 2).str, "w")); 569 570 ok = ndb_tags_iterate_next(it); 571 assert(!ok); 572 } 573 574 static void test_empty_tags() { 575 struct ndb_builder builder, *b = &builder; 576 struct ndb_iterator iter, *it = &iter; 577 struct ndb_note *note; 578 int ok; 579 unsigned char buf[1024]; 580 581 ok = ndb_builder_init(b, buf, sizeof(buf)); 582 assert(ok); 583 584 ok = ndb_builder_finalize(b, ¬e, NULL); 585 assert(ok); 586 587 assert(ndb_tags_count(ndb_note_tags(note)) == 0); 588 589 ndb_tags_iterate_start(note, it); 590 ok = ndb_tags_iterate_next(it); 591 assert(!ok); 592 } 593 594 static void print_tag(struct ndb_note *note, struct ndb_tag *tag) { 595 struct ndb_str str; 596 int tag_count = ndb_tag_count(tag); 597 for (int i = 0; i < tag_count; i++) { 598 str = ndb_tag_str(note, tag, i); 599 if (str.flag == NDB_PACKED_ID) { 600 printf("<id> "); 601 } else { 602 printf("%s ", str.str); 603 } 604 } 605 printf("\n"); 606 } 607 608 static void test_parse_contact_list() 609 { 610 int size, written = 0; 611 unsigned char id[32]; 612 static const int alloc_size = 2 << 18; 613 unsigned char *json = malloc(alloc_size); 614 unsigned char *buf = malloc(alloc_size); 615 struct ndb_note *note; 616 617 read_file("testdata/contacts.json", json, alloc_size, &written); 618 619 size = ndb_note_from_json((const char*)json, written, ¬e, buf, alloc_size); 620 printf("ndb_note_from_json size %d\n", size); 621 assert(size > 0); 622 assert(size == 34328); 623 624 memcpy(id, ndb_note_id(note), 32); 625 memset(ndb_note_id(note), 0, 32); 626 assert(ndb_calculate_id(note, json, alloc_size)); 627 assert(!memcmp(ndb_note_id(note), id, 32)); 628 629 const char* expected_content = 630 "{\"wss://nos.lol\":{\"write\":true,\"read\":true}," 631 "\"wss://relay.damus.io\":{\"write\":true,\"read\":true}," 632 "\"ws://monad.jb55.com:8080\":{\"write\":true,\"read\":true}," 633 "\"wss://nostr.wine\":{\"write\":true,\"read\":true}," 634 "\"wss://welcome.nostr.wine\":{\"write\":true,\"read\":true}," 635 "\"wss://eden.nostr.land\":{\"write\":true,\"read\":true}," 636 "\"wss://relay.mostr.pub\":{\"write\":true,\"read\":true}," 637 "\"wss://nostr-pub.wellorder.net\":{\"write\":true,\"read\":true}}"; 638 639 assert(!strcmp(expected_content, ndb_note_content(note))); 640 assert(ndb_note_created_at(note) == 1689904312); 641 assert(ndb_note_kind(note) == 3); 642 assert(ndb_tags_count(ndb_note_tags(note)) == 786); 643 //printf("note content length %d\n", ndb_note_content_length(note)); 644 printf("ndb_content_len %d, expected_len %ld\n", 645 ndb_note_content_length(note), 646 strlen(expected_content)); 647 assert(ndb_note_content_length(note) == strlen(expected_content)); 648 649 struct ndb_iterator iter, *it = &iter; 650 ndb_tags_iterate_start(note, it); 651 652 int tags = 0; 653 int total_elems = 0; 654 655 while (ndb_tags_iterate_next(it)) { 656 total_elems += ndb_tag_count(it->tag); 657 //printf("tag %d: ", tags); 658 if (tags == 0 || tags == 1 || tags == 2) 659 assert(ndb_tag_count(it->tag) == 3); 660 661 if (tags == 6) 662 assert(ndb_tag_count(it->tag) == 2); 663 664 if (tags == 7) 665 assert(!strcmp(ndb_tag_str(note, it->tag, 2).str, "wss://nostr-pub.wellorder.net")); 666 667 if (tags == 786) { 668 static unsigned char h[] = { 0x74, 0xfa, 0xe6, 0x66, 0x4c, 0x9e, 0x79, 0x98, 0x0c, 0x6a, 0xc1, 0x1c, 0x57, 0x75, 0xed, 0x30, 0x93, 0x2b, 0xe9, 0x26, 0xf5, 0xc4, 0x5b, 0xe8, 0xd6, 0x55, 0xe0, 0x0e, 0x35, 0xec, 0xa2, 0x88 }; 669 assert(!memcmp(ndb_tag_str(note, it->tag, 1).id, h, 32)); 670 } 671 672 //print_tag(it->note, it->tag); 673 674 tags += 1; 675 } 676 677 assert(tags == 786); 678 //printf("total_elems %d\n", total_elems); 679 assert(total_elems == 1580); 680 681 write_file("test_contacts_ndb_note", (unsigned char *)note, size); 682 printf("wrote test_contacts_ndb_note (raw ndb_note)\n"); 683 684 free(json); 685 free(buf); 686 } 687 688 static void test_replacement() 689 { 690 static const int alloc_size = 1024 * 1024; 691 char *json = malloc(alloc_size); 692 unsigned char *buf = malloc(alloc_size); 693 struct ndb *ndb; 694 size_t len; 695 int written; 696 struct ndb_config config; 697 ndb_default_config(&config); 698 699 assert(ndb_init(&ndb, test_dir, &config)); 700 701 read_file("testdata/old-new.json", (unsigned char*)json, alloc_size, &written); 702 assert(ndb_process_events(ndb, json, written)); 703 704 ndb_destroy(ndb); 705 assert(ndb_init(&ndb, test_dir, &config)); 706 707 struct ndb_txn txn; 708 assert(ndb_begin_query(ndb, &txn)); 709 710 unsigned char pubkey[32] = { 0x1e, 0x48, 0x9f, 0x6a, 0x4f, 0xc5, 0xc7, 0xac, 0x47, 0x5e, 0xa9, 0x04, 0x17, 0x43, 0xb8, 0x53, 0x11, 0x73, 0x25, 0x92, 0x61, 0xec, 0x71, 0x54, 0x26, 0x41, 0x05, 0x1e, 0x22, 0xa3, 0x82, 0xac }; 711 712 void *root = ndb_get_profile_by_pubkey(&txn, pubkey, &len, NULL); 713 714 assert(root); 715 int res = NdbProfileRecord_verify_as_root(root, len); 716 assert(res == 0); 717 718 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root); 719 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 720 const char *name = NdbProfile_name_get(profile); 721 722 assert(!strcmp(name, "jb55")); 723 724 ndb_end_query(&txn); 725 726 free(json); 727 free(buf); 728 } 729 730 static void test_fetch_last_noteid() 731 { 732 static const int alloc_size = 1024 * 1024; 733 char *json = malloc(alloc_size); 734 unsigned char *buf = malloc(alloc_size); 735 struct ndb *ndb; 736 size_t len; 737 int written; 738 struct ndb_config config; 739 ndb_default_config(&config); 740 741 assert(ndb_init(&ndb, test_dir, &config)); 742 743 read_file("testdata/random.json", (unsigned char*)json, alloc_size, &written); 744 assert(ndb_process_events(ndb, json, written)); 745 746 ndb_destroy(ndb); 747 748 assert(ndb_init(&ndb, test_dir, &config)); 749 750 unsigned char id[32] = { 0xdc, 0x96, 0x4f, 0x4c, 0x89, 0x83, 0x64, 0x13, 0x8e, 0x81, 0x96, 0xf0, 0xc7, 0x33, 0x38, 0xc8, 0xcc, 0x3e, 0xbf, 0xa3, 0xaf, 0xdd, 0xbc, 0x7d, 0xd1, 0x58, 0xb4, 0x84, 0x7c, 0x1e, 0xbf, 0xa0 }; 751 752 struct ndb_txn txn; 753 assert(ndb_begin_query(ndb, &txn)); 754 struct ndb_note *note = ndb_get_note_by_id(&txn, id, &len, NULL); 755 assert(note != NULL); 756 assert(ndb_note_created_at(note) == 1650054135); 757 758 unsigned char pk[32] = { 0x32, 0xe1, 0x82, 0x76, 0x35, 0x45, 0x0e, 0xbb, 0x3c, 0x5a, 0x7d, 0x12, 0xc1, 0xf8, 0xe7, 0xb2, 0xb5, 0x14, 0x43, 0x9a, 0xc1, 0x0a, 0x67, 0xee, 0xf3, 0xd9, 0xfd, 0x9c, 0x5c, 0x68, 0xe2, 0x45 }; 759 760 unsigned char profile_note_id[32] = { 761 0xd1, 0x2c, 0x17, 0xbd, 0xe3, 0x09, 0x4a, 0xd3, 0x2f, 0x4a, 0xb8, 0x62, 0xa6, 0xcc, 0x6f, 0x5c, 0x28, 0x9c, 0xfe, 0x7d, 0x58, 0x02, 0x27, 0x0b, 0xdf, 0x34, 0x90, 0x4d, 0xf5, 0x85, 0xf3, 0x49 762 }; 763 764 void *root = ndb_get_profile_by_pubkey(&txn, pk, &len, NULL); 765 766 assert(root); 767 int res = NdbProfileRecord_verify_as_root(root, len); 768 printf("NdbProfileRecord verify result %d\n", res); 769 assert(res == 0); 770 771 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root); 772 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 773 const char *lnurl = NdbProfileRecord_lnurl_get(profile_record); 774 const char *name = NdbProfile_name_get(profile); 775 uint64_t key = NdbProfileRecord_note_key_get(profile_record); 776 assert(name); 777 assert(lnurl); 778 assert(!strcmp(name, "jb55")); 779 assert(!strcmp(lnurl, "fixme")); 780 781 printf("note_key %" PRIu64 "\n", key); 782 783 struct ndb_note *n = ndb_get_note_by_key(&txn, key, NULL); 784 ndb_end_query(&txn); 785 assert(memcmp(profile_note_id, ndb_note_id(n), 32) == 0); 786 787 //fwrite(profile, len, 1, stdout); 788 789 ndb_destroy(ndb); 790 791 free(json); 792 free(buf); 793 } 794 795 static void test_parse_contact_event() 796 { 797 int written; 798 static const int alloc_size = 2 << 18; 799 char *json = malloc(alloc_size); 800 unsigned char *buf = malloc(alloc_size); 801 struct ndb_tce tce; 802 803 assert(read_file("testdata/contacts-event.json", (unsigned char*)json, 804 alloc_size, &written)); 805 assert(ndb_ws_event_from_json(json, written, &tce, buf, alloc_size, NULL)); 806 807 assert(tce.evtype == NDB_TCE_EVENT); 808 809 free(json); 810 free(buf); 811 } 812 813 static void test_content_len() 814 { 815 int written; 816 static const int alloc_size = 2 << 18; 817 char *json = malloc(alloc_size); 818 unsigned char *buf = malloc(alloc_size); 819 struct ndb_tce tce; 820 821 assert(read_file("testdata/failed_size.json", (unsigned char*)json, 822 alloc_size, &written)); 823 assert(ndb_ws_event_from_json(json, written, &tce, buf, alloc_size, NULL)); 824 825 assert(tce.evtype == NDB_TCE_EVENT); 826 assert(ndb_note_content_length(tce.event.note) == 0); 827 828 free(json); 829 free(buf); 830 } 831 832 static void test_parse_filter_json() 833 { 834 int i; 835 unsigned char buffer[1024]; 836 unsigned char *pid; 837 const char *str; 838 uint64_t val; 839 struct ndb_filter_elements *elems; 840 841 const unsigned char id_bytes[] = { 0x50, 0x04, 0xa0, 0x81, 0xe3, 0x97, 842 0xc6, 0xda, 0x9d, 0xc2, 0xf2, 0xd6, 0xb3, 0x13, 0x40, 0x06, 843 0xa9, 0xd0, 0xe8, 0xc1, 0xb4, 0x66, 0x89, 0xd9, 0xfe, 0x15, 844 0x0b, 0xb2, 0xf2, 0x1a, 0x20, 0x4d }; 845 846 const unsigned char id2_bytes[] = { 0xb1, 0x69, 0xf5, 0x96, 0x96, 0x89, 847 0x17, 0xa1, 0xab, 0xeb, 0x42, 0x34, 0xd3, 0xcf, 0x3a, 0xa9, 848 0xba, 0xee, 0x21, 0x12, 0xe5, 0x89, 0x98, 0xd1, 0x7c, 0x6d, 849 0xb4, 0x16, 0xad, 0x33, 0xfe, 0x40 }; 850 851 #define HEX_ID "5004a081e397c6da9dc2f2d6b3134006a9d0e8c1b46689d9fe150bb2f21a204d" 852 #define HEX_PK "b169f596968917a1abeb4234d3cf3aa9baee2112e58998d17c6db416ad33fe40" 853 854 static const char *json = "{\"ids\": [\"" HEX_ID "\", \"" HEX_PK "\"], \"kinds\": [1,2,3], \"limit\": 10, \"#e\":[\"" HEX_PK "\"], \"#t\": [\"hashtag\"]}"; 855 struct ndb_filter filter, *f = &filter; 856 ndb_filter_init(f); 857 858 assert(ndb_filter_from_json(json, strlen(json), f, buffer, sizeof(buffer))); 859 assert(filter.finalized); 860 861 for (i = 0; i < filter.num_elements; i++) { 862 elems = ndb_filter_get_elements(f, i); 863 864 switch (i) { 865 case 0: 866 assert(elems->field.type == NDB_FILTER_IDS); 867 assert(elems->count == 2); 868 869 pid = ndb_filter_get_id_element(f, elems, 0); 870 assert(!memcmp(pid, id_bytes, 32)); 871 pid = ndb_filter_get_id_element(f, elems, 1); 872 assert(!memcmp(pid, id2_bytes, 32)); 873 break; 874 case 1: 875 assert(elems->field.type == NDB_FILTER_KINDS); 876 assert(elems->count == 3); 877 val = ndb_filter_get_int_element(elems, 0); 878 assert(val == 1); 879 val = ndb_filter_get_int_element(elems, 1); 880 assert(val == 2); 881 val = ndb_filter_get_int_element(elems, 2); 882 assert(val == 3); 883 break; 884 885 case 2: 886 assert(elems->field.type == NDB_FILTER_LIMIT); 887 val = ndb_filter_get_int_element(elems, 0); 888 assert(val == 10); 889 break; 890 891 case 3: 892 assert(elems->field.type == NDB_FILTER_TAGS); 893 assert(elems->field.tag == 'e'); 894 pid = ndb_filter_get_id_element(f, elems, 0); 895 assert(pid != NULL); 896 assert(!memcmp(pid, id2_bytes, 32)); 897 break; 898 899 case 4: 900 assert(elems->field.type == NDB_FILTER_TAGS); 901 assert(elems->field.tag == 't'); 902 str = ndb_filter_get_string_element(f, elems, 0); 903 assert(!strcmp(str, "hashtag")); 904 break; 905 } 906 } 907 908 } 909 910 static void test_parse_json() { 911 char hex_id[32] = {0}; 912 unsigned char buffer[1024]; 913 struct ndb_note *note; 914 #define HEX_ID "5004a081e397c6da9dc2f2d6b3134006a9d0e8c1b46689d9fe150bb2f21a204d" 915 #define HEX_PK "b169f596968917a1abeb4234d3cf3aa9baee2112e58998d17c6db416ad33fe40" 916 static const char *json = 917 "{\"id\": \"" HEX_ID "\",\"pubkey\": \"" HEX_PK "\",\"created_at\": 1689836342,\"kind\": 1,\"tags\": [[\"p\",\"" HEX_ID "\"], [\"word\", \"words\", \"w\"]],\"content\": \"共通語\",\"sig\": \"e4d528651311d567f461d7be916c37cbf2b4d530e672f29f15f353291ed6df60c665928e67d2f18861c5ca88\"}"; 918 int ok; 919 920 ok = ndb_note_from_json(json, strlen(json), ¬e, buffer, sizeof(buffer)); 921 assert(ok); 922 923 const char *content = ndb_note_content(note); 924 unsigned char *id = ndb_note_id(note); 925 926 hex_decode(HEX_ID, 64, hex_id, sizeof(hex_id)); 927 928 assert(!strcmp(content, "共通語")); 929 assert(!memcmp(id, hex_id, 32)); 930 931 assert(ndb_tags_count(ndb_note_tags(note)) == 2); 932 933 struct ndb_iterator iter, *it = &iter; 934 ndb_tags_iterate_start(note, it); assert(ok); 935 ok = ndb_tags_iterate_next(it); assert(ok); 936 assert(ndb_tag_count(it->tag) == 2); 937 assert(!strcmp(ndb_iter_tag_str(it, 0).str, "p")); 938 assert(!memcmp(ndb_iter_tag_str(it, 1).id, hex_id, 32)); 939 940 ok = ndb_tags_iterate_next(it); assert(ok); 941 assert(ndb_tag_count(it->tag) == 3); 942 assert(!strcmp(ndb_iter_tag_str(it, 0).str, "word")); 943 assert(!strcmp(ndb_iter_tag_str(it, 1).str, "words")); 944 assert(!strcmp(ndb_iter_tag_str(it, 2).str, "w")); 945 } 946 947 static void test_strings_work_before_finalization() { 948 struct ndb_builder builder, *b = &builder; 949 struct ndb_note *note; 950 int ok; 951 unsigned char buf[1024]; 952 953 ok = ndb_builder_init(b, buf, sizeof(buf)); assert(ok); 954 ndb_builder_set_content(b, "hello", 5); 955 956 assert(!strcmp(ndb_note_content(b->note), "hello")); 957 assert(ndb_builder_finalize(b, ¬e, NULL)); 958 959 assert(!strcmp(ndb_note_content(note), "hello")); 960 } 961 962 static void test_parse_content() { 963 } 964 965 static void test_parse_nevent() { 966 unsigned char buf[4096]; 967 const char *content = "nostr:nevent1qqs9qhc0pjvp6jl2w6ppk5cft8ets8fhxy7fcqcjnp7g38whjy0x5aqpzpmhxue69uhkummnw3ezuamfdejsyg86np9a0kajstc8u9h846rmy6320wdepdeydfz8w8cv7kh9sqv02g947d58,#hashtag"; 968 struct ndb_blocks *blocks; 969 struct ndb_block *block = NULL; 970 struct nostr_bech32 *bech32; 971 struct ndb_block_iterator iterator, *iter; 972 iter = &iterator; 973 int ok = 0; 974 975 static unsigned char event_id[] = { 0x50, 0x5f, 0x0f, 0x0c, 0x98, 0x1d, 0x4b, 0xea, 0x76, 0x82, 0x1b, 0x53, 976 0x09, 0x59, 0xf2, 0xb8, 0x1d, 0x37, 0x31, 0x3c, 0x9c, 0x03, 0x12, 0x98, 977 0x7c, 0x88, 0x9d, 0xd7, 0x91, 0x1e, 0x6a, 0x74 }; 978 979 assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks)); 980 ndb_blocks_iterate_start(content, blocks, iter); 981 assert(blocks->num_blocks == 3); 982 while ((block = ndb_blocks_iterate_next(iter))) { 983 switch (++ok) { 984 case 1: 985 assert(ndb_get_block_type(block) == BLOCK_MENTION_BECH32); 986 bech32 = ndb_bech32_block(block); 987 assert(bech32->type == NOSTR_BECH32_NEVENT); 988 assert(!memcmp(bech32->nevent.event_id, event_id, 32)); 989 break; 990 case 2: 991 assert(ndb_get_block_type(block) == BLOCK_TEXT); 992 assert(ndb_str_block_ptr(ndb_block_str(block))[0] == ','); 993 break; 994 case 3: 995 assert(ndb_get_block_type(block) == BLOCK_HASHTAG); 996 assert(!strncmp("hashtag", ndb_str_block_ptr(ndb_block_str(block)), 7)); 997 break; 998 } 999 } 1000 assert(ok == 3); 1001 } 1002 1003 static void test_bech32_parsing() { 1004 unsigned char buf[4096]; 1005 const char *content = "https://damus.io/notedeck nostr:note1thp5828zk5xujrcuwdppcjnwlz43altca6269demenja3vqm5m2qclq35h"; 1006 1007 struct ndb_blocks *blocks; 1008 struct ndb_block *block; 1009 struct ndb_str_block *str; 1010 struct ndb_block_iterator iterator, *iter; 1011 1012 iter = &iterator; 1013 assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks)); 1014 assert(blocks->num_blocks == 3); 1015 1016 ndb_blocks_iterate_start(content, blocks, iter); 1017 int i = 0; 1018 while ((block = ndb_blocks_iterate_next(iter))) { 1019 str = ndb_block_str(block); 1020 switch (++i) { 1021 case 1: 1022 assert(ndb_get_block_type(block) == BLOCK_URL); 1023 assert(!strncmp(str->str, "https://damus.io/notedeck", str->len)); 1024 break; 1025 case 2: 1026 assert(ndb_get_block_type(block) == BLOCK_TEXT); 1027 assert(!strncmp(str->str, " ", str->len)); 1028 break; 1029 case 3: 1030 assert(ndb_get_block_type(block) == BLOCK_MENTION_BECH32); 1031 assert(!strncmp(str->str, "note1thp5828zk5xujrcuwdppcjnwlz43altca6269demenja3vqm5m2qclq35h", str->len)); 1032 break; 1033 } 1034 } 1035 1036 assert(i == 3); 1037 } 1038 1039 static void test_single_url_parsing() { 1040 unsigned char buf[4096]; 1041 const char *content = "https://damus.io/notedeck"; 1042 1043 struct ndb_blocks *blocks; 1044 struct ndb_block *block; 1045 struct ndb_str_block *str; 1046 struct ndb_block_iterator iterator, *iter; 1047 1048 iter = &iterator; 1049 assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks)); 1050 assert(blocks->num_blocks == 1); 1051 1052 ndb_blocks_iterate_start(content, blocks, iter); 1053 int i = 0; 1054 while ((block = ndb_blocks_iterate_next(iter))) { 1055 str = ndb_block_str(block); 1056 switch (++i) { 1057 case 1: 1058 assert(ndb_get_block_type(block) == BLOCK_URL); 1059 assert(!strncmp(str->str, "https://damus.io/notedeck", str->len)); 1060 break; 1061 } 1062 } 1063 1064 assert(i == 1); 1065 } 1066 1067 static void test_comma_url_parsing() { 1068 unsigned char buf[4096]; 1069 const char *content = "http://example.com,http://example.com"; 1070 1071 struct ndb_blocks *blocks; 1072 struct ndb_block *block; 1073 struct ndb_str_block *str; 1074 struct ndb_block_iterator iterator, *iter; 1075 1076 iter = &iterator; 1077 assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks)); 1078 assert(blocks->num_blocks == 3); 1079 1080 ndb_blocks_iterate_start(content, blocks, iter); 1081 int i = 0; 1082 while ((block = ndb_blocks_iterate_next(iter))) { 1083 str = ndb_block_str(block); 1084 switch (++i) { 1085 case 1: 1086 assert(ndb_get_block_type(block) == BLOCK_URL); 1087 assert(!strncmp(str->str, "http://example.com", str->len)); 1088 break; 1089 case 2: 1090 assert(ndb_get_block_type(block) == BLOCK_TEXT); 1091 assert(!strncmp(str->str, ",", str->len)); 1092 break; 1093 case 3: 1094 assert(ndb_get_block_type(block) == BLOCK_URL); 1095 assert(!strncmp(str->str, "http://example.com", str->len)); 1096 break; 1097 } 1098 } 1099 1100 assert(i == 3); 1101 } 1102 1103 static void test_url_parsing() { 1104 unsigned char buf[4096]; 1105 #define DAMUSIO "https://github.com/damus-io" 1106 #define JB55COM "https://jb55.com/" 1107 #define WIKIORG "http://wikipedia.org" 1108 const char *content = DAMUSIO ", " JB55COM ", " WIKIORG; 1109 struct ndb_blocks *blocks; 1110 struct ndb_block *block; 1111 struct ndb_str_block *str; 1112 struct ndb_block_iterator iterator, *iter; 1113 iter = &iterator; 1114 1115 assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks)); 1116 assert(blocks->num_blocks == 5); 1117 1118 ndb_blocks_iterate_start(content, blocks, iter); 1119 int i = 0; 1120 while ((block = ndb_blocks_iterate_next(iter))) { 1121 str = ndb_block_str(block); 1122 switch (++i) { 1123 case 1: 1124 assert(ndb_get_block_type(block) == BLOCK_URL); 1125 assert(!strncmp(str->str, DAMUSIO, str->len)); 1126 break; 1127 case 2: 1128 assert(ndb_get_block_type(block) == BLOCK_TEXT); 1129 assert(!strncmp(str->str, ", ", str->len)); 1130 break; 1131 case 3: 1132 assert(ndb_get_block_type(block) == BLOCK_URL); 1133 assert(!strncmp(str->str, JB55COM, str->len)); 1134 break; 1135 case 4: 1136 assert(ndb_get_block_type(block) == BLOCK_TEXT); 1137 assert(!strncmp(str->str, ", ", str->len)); 1138 break; 1139 case 5: 1140 assert(ndb_get_block_type(block) == BLOCK_URL); 1141 assert(!strncmp(str->str, WIKIORG, str->len)); break; 1142 } 1143 } 1144 1145 assert(i == 5); 1146 } 1147 1148 1149 static void test_bech32_objects() { 1150 struct nostr_bech32 obj; 1151 unsigned char buf[4096]; 1152 const char *nevent = "nevent1qqstjtqmd3lke9m3ftv49pagzxth4q2va4hy2m6kprl0p4y6es4vvnspz3mhxue69uhhyetvv9ujuerpd46hxtnfduqsuamn8ghj7mr0vdskc6r0wd6qegay04"; 1153 1154 unsigned char id[32] = { 1155 0xb9, 0x2c, 0x1b, 0x6c, 0x7f, 0x6c, 0x97, 0x71, 0x4a, 0xd9, 0x52, 0x87, 1156 0xa8, 0x11, 0x97, 0x7a, 0x81, 0x4c, 0xed, 0x6e, 0x45, 0x6f, 0x56, 0x08, 1157 0xfe, 0xf0, 0xd4, 0x9a, 0xcc, 0x2a, 0xc6, 0x4e }; 1158 1159 assert(parse_nostr_bech32(buf, sizeof(buf), nevent, strlen(nevent), &obj)); 1160 assert(obj.type == NOSTR_BECH32_NEVENT); 1161 assert(!memcmp(obj.nevent.event_id, id, 32)); 1162 assert(obj.nevent.relays.num_relays == 2); 1163 const char damus_relay[] = "wss://relay.damus.io"; 1164 const char local_relay[] = "ws://localhost"; 1165 assert(sizeof(damus_relay)-1 == obj.nevent.relays.relays[0].len); 1166 assert(!memcmp(obj.nevent.relays.relays[0].str, damus_relay, sizeof(damus_relay)-1)); 1167 assert(!memcmp(obj.nevent.relays.relays[1].str, local_relay, sizeof(local_relay)-1)); 1168 } 1169 1170 static void test_tce_eose() { 1171 unsigned char buf[1024]; 1172 const char json[] = "[\"EOSE\",\"s\"]"; 1173 struct ndb_tce tce; 1174 int ok; 1175 1176 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 1177 assert(ok); 1178 1179 assert(tce.evtype == NDB_TCE_EOSE); 1180 assert(tce.subid_len == 1); 1181 assert(!memcmp(tce.subid, "s", 1)); 1182 } 1183 1184 static void test_tce_command_result() { 1185 unsigned char buf[1024]; 1186 const char json[] = "[\"OK\",\"\",true,\"blocked: ok\"]"; 1187 struct ndb_tce tce; 1188 int ok; 1189 1190 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 1191 assert(ok); 1192 1193 assert(tce.evtype == NDB_TCE_OK); 1194 assert(tce.subid_len == 0); 1195 assert(tce.command_result.ok == 1); 1196 assert(!memcmp(tce.subid, "", 0)); 1197 } 1198 1199 static void test_tce_command_result_empty_msg() { 1200 unsigned char buf[1024]; 1201 const char json[] = "[\"OK\",\"b1d8f68d39c07ce5c5ea10c235100d529b2ed2250140b36a35d940b712dc6eff\",true,\"\"]"; 1202 struct ndb_tce tce; 1203 int ok; 1204 1205 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 1206 assert(ok); 1207 1208 assert(tce.evtype == NDB_TCE_OK); 1209 assert(tce.subid_len == 64); 1210 assert(tce.command_result.ok == 1); 1211 assert(tce.command_result.msglen == 0); 1212 assert(!memcmp(tce.subid, "b1d8f68d39c07ce5c5ea10c235100d529b2ed2250140b36a35d940b712dc6eff", 0)); 1213 } 1214 1215 // test to-client event 1216 static void test_tce() { 1217 1218 #define HEX_ID "5004a081e397c6da9dc2f2d6b3134006a9d0e8c1b46689d9fe150bb2f21a204d" 1219 #define HEX_PK "b169f596968917a1abeb4234d3cf3aa9baee2112e58998d17c6db416ad33fe40" 1220 #define JSON "{\"id\": \"" HEX_ID "\",\"pubkey\": \"" HEX_PK "\",\"created_at\": 1689836342,\"kind\": 1,\"tags\": [[\"p\",\"" HEX_ID "\"], [\"word\", \"words\", \"w\"]],\"content\": \"共通語\",\"sig\": \"e4d528651311d567f461d7be916c37cbf2b4d530e672f29f15f353291ed6df60c665928e67d2f18861c5ca88\"}" 1221 unsigned char buf[1024]; 1222 const char json[] = "[\"EVENT\",\"subid123\"," JSON "]"; 1223 struct ndb_tce tce; 1224 int ok; 1225 1226 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 1227 assert(ok); 1228 1229 assert(tce.evtype == NDB_TCE_EVENT); 1230 assert(tce.subid_len == 8); 1231 assert(!memcmp(tce.subid, "subid123", 8)); 1232 1233 #undef HEX_ID 1234 #undef HEX_PK 1235 #undef JSON 1236 } 1237 1238 #define TEST_BUF_SIZE 10 // For simplicity 1239 1240 static void test_queue_init_pop_push() { 1241 struct prot_queue q; 1242 int buffer[TEST_BUF_SIZE]; 1243 int data; 1244 1245 // Initialize 1246 assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1); 1247 1248 // Push and Pop 1249 data = 5; 1250 assert(prot_queue_push(&q, &data) == 1); 1251 prot_queue_pop(&q, &data); 1252 assert(data == 5); 1253 1254 // Push to full, and then fail to push 1255 for (int i = 0; i < TEST_BUF_SIZE; i++) { 1256 assert(prot_queue_push(&q, &i) == 1); 1257 } 1258 assert(prot_queue_push(&q, &data) == 0); // Should fail as queue is full 1259 1260 // Pop to empty, and then fail to pop 1261 for (int i = 0; i < TEST_BUF_SIZE; i++) { 1262 assert(prot_queue_try_pop_all(&q, &data, 1) == 1); 1263 assert(data == i); 1264 } 1265 assert(prot_queue_try_pop_all(&q, &data, 1) == 0); // Should fail as queue is empty 1266 } 1267 1268 // This function will be used by threads to test thread safety. 1269 void* thread_func(void* arg) { 1270 struct prot_queue* q = (struct prot_queue*) arg; 1271 int data; 1272 1273 for (int i = 0; i < 100; i++) { 1274 data = i; 1275 prot_queue_push(q, &data); 1276 prot_queue_pop(q, &data); 1277 } 1278 return NULL; 1279 } 1280 1281 static void test_queue_thread_safety() { 1282 struct prot_queue q; 1283 int buffer[TEST_BUF_SIZE]; 1284 pthread_t threads[2]; 1285 1286 assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1); 1287 1288 // Create threads 1289 for (int i = 0; i < 2; i++) { 1290 pthread_create(&threads[i], NULL, thread_func, &q); 1291 } 1292 1293 // Join threads 1294 for (int i = 0; i < 2; i++) { 1295 pthread_join(threads[i], NULL); 1296 } 1297 1298 // After all operations, the queue should be empty 1299 int data; 1300 assert(prot_queue_try_pop_all(&q, &data, 1) == 0); 1301 } 1302 1303 static void test_queue_boundary_conditions() { 1304 struct prot_queue q; 1305 int buffer[TEST_BUF_SIZE]; 1306 int data; 1307 1308 // Initialize 1309 assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1); 1310 1311 // Push to full 1312 for (int i = 0; i < TEST_BUF_SIZE; i++) { 1313 assert(prot_queue_push(&q, &i) == 1); 1314 } 1315 1316 // Try to push to a full queue 1317 int old_head = q.head; 1318 int old_tail = q.tail; 1319 int old_count = q.count; 1320 assert(prot_queue_push(&q, &data) == 0); 1321 1322 // Assert the queue's state has not changed 1323 assert(old_head == q.head); 1324 assert(old_tail == q.tail); 1325 assert(old_count == q.count); 1326 1327 // Pop to empty 1328 for (int i = 0; i < TEST_BUF_SIZE; i++) { 1329 assert(prot_queue_try_pop_all(&q, &data, 1) == 1); 1330 } 1331 1332 // Try to pop from an empty queue 1333 old_head = q.head; 1334 old_tail = q.tail; 1335 old_count = q.count; 1336 assert(prot_queue_try_pop_all(&q, &data, 1) == 0); 1337 1338 // Assert the queue's state has not changed 1339 assert(old_head == q.head); 1340 assert(old_tail == q.tail); 1341 assert(old_count == q.count); 1342 } 1343 1344 static void test_fast_strchr() 1345 { 1346 // Test 1: Basic test 1347 const char *testStr1 = "Hello, World!"; 1348 assert(fast_strchr(testStr1, 'W', strlen(testStr1)) == testStr1 + 7); 1349 1350 // Test 2: Character not present in the string 1351 assert(fast_strchr(testStr1, 'X', strlen(testStr1)) == NULL); 1352 1353 // Test 3: Multiple occurrences of the character 1354 const char *testStr2 = "Multiple occurrences."; 1355 assert(fast_strchr(testStr2, 'u', strlen(testStr2)) == testStr2 + 1); 1356 1357 // Test 4: Check with an empty string 1358 const char *testStr3 = ""; 1359 assert(fast_strchr(testStr3, 'a', strlen(testStr3)) == NULL); 1360 1361 // Test 5: Check with a one-character string 1362 const char *testStr4 = "a"; 1363 assert(fast_strchr(testStr4, 'a', strlen(testStr4)) == testStr4); 1364 1365 // Test 6: Check the last character in the string 1366 const char *testStr5 = "Last character check"; 1367 assert(fast_strchr(testStr5, 'k', strlen(testStr5)) == testStr5 + 19); 1368 1369 // Test 7: Large string test (>16 bytes) 1370 char *testStr6 = "This is a test for large strings with more than 16 bytes."; 1371 assert(fast_strchr(testStr6, 'm', strlen(testStr6)) == testStr6 + 38); 1372 } 1373 1374 static void test_tag_query() 1375 { 1376 struct ndb *ndb; 1377 struct ndb_txn txn; 1378 struct ndb_filter filters[1], *f = &filters[0]; 1379 struct ndb_config config; 1380 struct ndb_query_result results[4]; 1381 int count, cap; 1382 uint64_t subid, note_ids[1]; 1383 ndb_default_config(&config); 1384 1385 cap = sizeof(results) / sizeof(results[0]); 1386 1387 assert(ndb_init(&ndb, test_dir, &config)); 1388 1389 const char *ev = "[\"EVENT\",\"s\",{\"id\": \"7fd6e4286e595b60448bf69d8ec4a472c5ad14521555813cdfce1740f012aefd\",\"pubkey\": \"b85beab689aed6a10110cc3cdd6e00ac37a2f747c4e60b18a31f4352a5bfb6ed\",\"created_at\": 1704762185,\"kind\": 1,\"tags\": [[\"t\",\"hashtag\"]],\"content\": \"hi\",\"sig\": \"5b05669af5a322730731b13d38667464ea3b45bef1861e26c99ef1815d7e8d557a76e06afa5fffa1dcd207402b92ae7dda6ef411ea515df2bca58d74e6f2772e\"}]"; 1390 1391 f = &filters[0]; 1392 ndb_filter_init(f); 1393 ndb_filter_start_tag_field(f, 't'); 1394 ndb_filter_add_str_element(f, "hashtag"); 1395 ndb_filter_end_field(f); 1396 ndb_filter_end(f); 1397 1398 assert((subid = ndb_subscribe(ndb, f, 1))); 1399 assert(ndb_process_event(ndb, ev, strlen(ev))); 1400 ndb_wait_for_notes(ndb, subid, note_ids, 1); 1401 1402 ndb_begin_query(ndb, &txn); 1403 1404 assert(ndb_query(&txn, f, 1, results, cap, &count)); 1405 assert(count == 1); 1406 assert(!strcmp(ndb_note_content(results[0].note), "hi")); 1407 1408 ndb_end_query(&txn); 1409 ndb_destroy(ndb); 1410 } 1411 1412 static void test_query() 1413 { 1414 struct ndb *ndb; 1415 struct ndb_txn txn; 1416 struct ndb_filter filters[2], *f; 1417 struct ndb_config config; 1418 struct ndb_query_result results[4]; 1419 int count, cap; 1420 uint64_t subid, note_ids[4]; 1421 ndb_default_config(&config); 1422 1423 cap = sizeof(results) / sizeof(results[0]); 1424 1425 const unsigned char id[] = { 1426 0x03, 0x36, 0x94, 0x8b, 0xdf, 0xbf, 0x5f, 0x93, 0x98, 0x02, 0xeb, 0xa0, 1427 0x3a, 0xa7, 0x87, 0x35, 0xc8, 0x28, 0x25, 0x21, 0x1e, 0xec, 0xe9, 0x87, 1428 0xa6, 0xd2, 0xe2, 0x0e, 0x3c, 0xff, 0xf9, 0x30 1429 }; 1430 1431 const unsigned char id2[] = { 1432 0x0a, 0x35, 0x0c, 0x58, 0x51, 0xaf, 0x6f, 0x6c, 0xe3, 0x68, 0xba, 0xb4, 1433 0xe2, 0xd4, 0xfe, 0x44, 0x2a, 0x13, 0x18, 0x64, 0x2c, 0x7f, 0xe5, 0x8d, 1434 0xe5, 0x39, 0x21, 0x03, 0x70, 0x0c, 0x10, 0xfc 1435 }; 1436 1437 const char *ev = "[\"EVENT\",\"s\",{\"id\": \"0336948bdfbf5f939802eba03aa78735c82825211eece987a6d2e20e3cfff930\",\"pubkey\": \"aeadd3bf2fd92e509e137c9e8bdf20e99f286b90be7692434e03c015e1d3bbfe\",\"created_at\": 1704401597,\"kind\": 1,\"tags\": [],\"content\": \"hello\",\"sig\": \"232395427153b693e0426b93d89a8319324d8657e67d23953f014a22159d2127b4da20b95644b3e34debd5e20be0401c283e7308ccb63c1c1e0f81cac7502f09\"}]"; 1438 const char *ev2 = "[\"EVENT\",\"s\",{\"id\": \"0a350c5851af6f6ce368bab4e2d4fe442a1318642c7fe58de5392103700c10fc\",\"pubkey\": \"dfa3fc062f7430dab3d947417fd3c6fb38a7e60f82ffe3387e2679d4c6919b1d\",\"created_at\": 1704404822,\"kind\": 1,\"tags\": [],\"content\": \"hello2\",\"sig\": \"48a0bb9560b89ee2c6b88edcf1cbeeff04f5e1b10d26da8564cac851065f30fa6961ee51f450cefe5e8f4895e301e8ffb2be06a2ff44259684fbd4ea1c885696\"}]"; 1439 const char *ev3 = "[\"EVENT\",\"s\",{\"id\": \"20d2b66e1a3ac4a2afe22866ad742091b6267e6e614303de062adb33e12c9931\",\"pubkey\": \"7987bfb2632d561088fc8e3c30a95836f822e4f53633228ec92ae2f5cd6690aa\",\"created_at\": 1704408561,\"kind\": 2,\"tags\": [],\"content\": \"what\",\"sig\": \"cc8533bf177ac87771a5218a04bed24f7a1706f0b2d92700045cdeb38accc5507c6c8de09525e43190df3652012b554d4efe7b82ab268a87ff6f23da44e16a8f\"}]"; 1440 const char *ev4 = "[\"EVENT\",\"s\",{\"id\": \"8a2057c13c1c57b536eab78e6c55428732d33b6b5b234c1f5eab2b5918c37fa1\",\"pubkey\": \"303b5851504da5caa14142e9e2e1b1b60783c48d6f137c205019d46d09244c26\",\"created_at\": 1704408730,\"kind\": 2,\"tags\": [],\"content\": \"hmm\",\"sig\": \"e7cd3029042d41964192411929cade59592840af766da6420077ccc57a61405312db6ca879150db01f53c3b81c477cec5d6bd49f9dc10937267cacf7e5c784b3\"}]"; 1441 1442 assert(ndb_init(&ndb, test_dir, &config)); 1443 1444 f = &filters[0]; 1445 ndb_filter_init(f); 1446 ndb_filter_start_field(f, NDB_FILTER_IDS); 1447 ndb_filter_add_id_element(f, id2); 1448 ndb_filter_add_id_element(f, id); 1449 assert(ndb_filter_current_element(f)->count == 2); 1450 ndb_filter_end_field(f); 1451 ndb_filter_end(f); 1452 1453 assert((subid = ndb_subscribe(ndb, f, 1))); 1454 1455 assert(ndb_process_event(ndb, ev, strlen(ev))); 1456 assert(ndb_process_event(ndb, ev2, strlen(ev2))); 1457 assert(ndb_process_event(ndb, ev3, strlen(ev3))); 1458 assert(ndb_process_event(ndb, ev4, strlen(ev4))); 1459 1460 for (count = 0; count < 2;) 1461 count += ndb_wait_for_notes(ndb, subid, note_ids+count, 4-count); 1462 1463 ndb_begin_query(ndb, &txn); 1464 assert(ndb_query(&txn, f, 1, results, cap, &count)); 1465 assert(count == 2); 1466 assert(0 == memcmp(ndb_note_id(results[0].note), id2, 32)); 1467 1468 ndb_filter_destroy(f); 1469 ndb_filter_init(f); 1470 ndb_filter_start_field(f, NDB_FILTER_KINDS); 1471 ndb_filter_add_int_element(f, 2); 1472 ndb_filter_end_field(f); 1473 ndb_filter_start_field(f, NDB_FILTER_LIMIT); 1474 ndb_filter_add_int_element(f, 2); 1475 ndb_filter_end_field(f); 1476 ndb_filter_end(f); 1477 1478 count = 0; 1479 assert(ndb_query(&txn, f, 1, results, cap, &count)); 1480 ndb_print_kind_keys(&txn); 1481 printf("count %d\n", count); 1482 assert(count == 2); 1483 assert(!strcmp(ndb_note_content(results[0].note), "hmm")); 1484 assert(!strcmp(ndb_note_content(results[1].note), "what")); 1485 1486 ndb_end_query(&txn); 1487 ndb_destroy(ndb); 1488 } 1489 1490 static void test_fulltext() 1491 { 1492 struct ndb *ndb; 1493 struct ndb_txn txn; 1494 int written; 1495 static const int alloc_size = 2 << 18; 1496 char *json = malloc(alloc_size); 1497 struct ndb_text_search_results results; 1498 struct ndb_config config; 1499 struct ndb_text_search_config search_config; 1500 ndb_default_config(&config); 1501 ndb_default_text_search_config(&search_config); 1502 1503 assert(ndb_init(&ndb, test_dir, &config)); 1504 1505 read_file("testdata/search.json", (unsigned char*)json, alloc_size, &written); 1506 assert(ndb_process_client_events(ndb, json, written)); 1507 ndb_destroy(ndb); 1508 assert(ndb_init(&ndb, test_dir, &config)); 1509 1510 ndb_begin_query(ndb, &txn); 1511 ndb_text_search(&txn, "Jump Over", &results, &search_config); 1512 ndb_end_query(&txn); 1513 1514 ndb_destroy(ndb); 1515 1516 free(json); 1517 1518 } 1519 1520 static void test_varint(uint64_t value) { 1521 unsigned char buffer[10]; 1522 struct cursor cursor; 1523 uint64_t result; 1524 1525 // Initialize cursor 1526 cursor.start = buffer; 1527 cursor.p = buffer; 1528 cursor.end = buffer + sizeof(buffer); 1529 1530 // Push the value 1531 assert(cursor_push_varint(&cursor, value)); 1532 1533 // Reset cursor for reading 1534 cursor.p = buffer; 1535 1536 // Pull the value 1537 if (!cursor_pull_varint(&cursor, &result)) { 1538 printf("Test failed for value %" PRIu64 " \n", value); 1539 assert(!"Failed to pull value"); 1540 } 1541 1542 // Check if the pushed and pulled values are the same 1543 if (value != result) { 1544 printf("Test failed for value %" PRIu64 "\n", value); 1545 assert(!"test failed"); 1546 } 1547 } 1548 1549 static int test_varints() { 1550 test_varint(0); 1551 test_varint(127); // Edge case for 1-byte varint 1552 test_varint(128); // Edge case for 2-byte varint 1553 test_varint(16383); // Edge case for 2-byte varint 1554 test_varint(16384); // Edge case for 3-byte varint 1555 test_varint(2097151); // Edge case for 3-byte varint 1556 test_varint(2097152); // Edge case for 4-byte varint 1557 test_varint(268435455); // Edge case for 4-byte varint 1558 test_varint(268435456); // Edge case for 5-byte varint 1559 test_varint(34359738367ULL); // Edge case for 5-byte varint 1560 test_varint(34359738368ULL); // Edge case for 6-byte varint 1561 test_varint(4398046511103ULL); // Edge case for 6-byte varint 1562 test_varint(4398046511104ULL); // Edge case for 7-byte varint 1563 test_varint(562949953421311ULL); // Edge case for 7-byte varint 1564 test_varint(562949953421312ULL); // Edge case for 8-byte varint 1565 test_varint(72057594037927935ULL); // Edge case for 8-byte varint 1566 test_varint(72057594037927936ULL); // Edge case for 9-byte varint 1567 test_varint(9223372036854775807ULL); // Maximum 64-bit integer 1568 1569 return 0; 1570 } 1571 1572 static void test_subscriptions() 1573 { 1574 struct ndb *ndb; 1575 struct ndb_config config; 1576 struct ndb_filter filter, *f = &filter; 1577 uint64_t subid; 1578 uint64_t note_id = 0; 1579 struct ndb_txn txn; 1580 struct ndb_note *note; 1581 ndb_default_config(&config); 1582 1583 const char *ev = "[\"EVENT\",\"s\",{\"id\": \"3718b368de4d01a021990e6e00dce4bdf860caed21baffd11b214ac498e7562e\",\"pubkey\": \"57c811c86a871081f52ca80e657004fe0376624a978f150073881b6daf0cbf1d\",\"created_at\": 1704300579,\"kind\": 1337,\"tags\": [],\"content\": \"test\",\"sig\": \"061c36d4004d8342495eb22e8e7c2e2b6e1a1c7b4ae6077fef09f9a5322c561b88bada4f63ff05c9508cb29d03f50f71ef3c93c0201dbec440fc32eda87f273b\"}]"; 1584 1585 assert(ndb_init(&ndb, test_dir, &config)); 1586 1587 assert(ndb_filter_init(f)); 1588 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 1589 assert(ndb_filter_add_int_element(f, 1337)); 1590 ndb_filter_end_field(f); 1591 ndb_filter_end(f); 1592 1593 assert((subid = ndb_subscribe(ndb, f, 1))); 1594 1595 assert(ndb_process_event(ndb, ev, strlen(ev))); 1596 1597 assert(ndb_wait_for_notes(ndb, subid, ¬e_id, 1) == 1); 1598 assert(note_id > 0); 1599 assert(ndb_begin_query(ndb, &txn)); 1600 1601 assert((note = ndb_get_note_by_key(&txn, note_id, NULL))); 1602 assert(!strcmp(ndb_note_content(note), "test")); 1603 1604 // unsubscribe 1605 assert(ndb_num_subscriptions(ndb) == 1); 1606 assert(ndb_unsubscribe(ndb, subid)); 1607 assert(ndb_num_subscriptions(ndb) == 0); 1608 1609 ndb_end_query(&txn); 1610 ndb_destroy(ndb); 1611 } 1612 1613 static void test_weird_note_corruption() { 1614 struct ndb *ndb; 1615 struct ndb_config config; 1616 struct ndb_blocks *blocks; 1617 struct ndb_block *block; 1618 struct ndb_str_block *str; 1619 struct ndb_block_iterator iterator, *iter = &iterator; 1620 struct ndb_filter filter, *f = &filter; 1621 uint64_t subid; 1622 uint64_t note_id = 0; 1623 struct ndb_txn txn; 1624 struct ndb_note *note; 1625 ndb_default_config(&config); 1626 1627 const char *ev = "[\"EVENT\",\"a\",{\"content\":\"https://damus.io/notedeck\",\"created_at\":1722537589,\"id\":\"1876ca8cd29afba5805e698cf04ac6611d50e5e5a22e1efb895816a4c5790a1b\",\"kind\":1,\"pubkey\":\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"sig\":\"2478aac6e9e66e5a04938d54544928589b55d2a324a7229ef7e709903b5dd12dc9a279abf81ed5753692cd30f62213fd3e0adf8b835543616a60e2d7010f0627\",\"tags\":[[\"e\",\"423fdf3f6e438fded84fe496643008eada5c1db7ba80428521c2c098f1173b83\",\"\",\"root\"],[\"p\",\"406a61077fe67f0eda4be931572c522f937952ddb024c87673e3de6b37e9a98f\"]]}]"; 1628 assert(ndb_init(&ndb, test_dir, &config)); 1629 1630 assert(ndb_filter_init(f)); 1631 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 1632 assert(ndb_filter_add_int_element(f, 1)); 1633 ndb_filter_end_field(f); 1634 ndb_filter_end(f); 1635 1636 assert((subid = ndb_subscribe(ndb, f, 1))); 1637 assert(ndb_process_event(ndb, ev, strlen(ev))); 1638 1639 assert(ndb_wait_for_notes(ndb, subid, ¬e_id, 1) == 1); 1640 assert(note_id > 0); 1641 assert(ndb_begin_query(ndb, &txn)); 1642 1643 assert((note = ndb_get_note_by_key(&txn, note_id, NULL))); 1644 assert(!strcmp(ndb_note_content(note), "https://damus.io/notedeck")); 1645 assert(ndb_note_content_length(note) == 25); 1646 1647 assert(ndb_num_subscriptions(ndb) == 1); 1648 assert(ndb_unsubscribe(ndb, subid)); 1649 assert(ndb_num_subscriptions(ndb) == 0); 1650 1651 blocks = ndb_get_blocks_by_key(ndb, &txn, note_id); 1652 1653 ndb_blocks_iterate_start(ndb_note_content(note), blocks, iter); 1654 //printf("url num blocks: %d\n", blocks->num_blocks); 1655 assert(blocks->num_blocks == 1); 1656 int i = 0; 1657 while ((block = ndb_blocks_iterate_next(iter))) { 1658 str = ndb_block_str(block); 1659 //printf("block (%d): %d:'%.*s'\n", ndb_get_block_type(block), str->len, str->len, str->str); 1660 switch (++i) { 1661 case 1: 1662 assert(ndb_get_block_type(block) == BLOCK_URL); 1663 assert(str->len == 25); 1664 assert(!strncmp(str->str, "https://damus.io/notedeck", str->len)); 1665 break; 1666 } 1667 } 1668 assert(i == 1); 1669 1670 ndb_end_query(&txn); 1671 ndb_destroy(ndb); 1672 } 1673 1674 static void test_filter_eq() { 1675 struct ndb_filter filter, *f = &filter; 1676 struct ndb_filter filter2, *f2 = &filter2; 1677 1678 ndb_filter_init(f); 1679 assert(ndb_filter_start_field(f, NDB_FILTER_UNTIL)); 1680 assert(ndb_filter_add_int_element(f, 42)); 1681 ndb_filter_end_field(f); 1682 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 1683 assert(ndb_filter_add_int_element(f, 1)); 1684 assert(ndb_filter_add_int_element(f, 2)); 1685 ndb_filter_end_field(f); 1686 ndb_filter_end(f); 1687 1688 ndb_filter_init(f2); 1689 assert(ndb_filter_start_field(f2, NDB_FILTER_KINDS)); 1690 assert(ndb_filter_add_int_element(f2, 2)); 1691 assert(ndb_filter_add_int_element(f2, 1)); 1692 ndb_filter_end_field(f2); 1693 assert(ndb_filter_start_field(f2, NDB_FILTER_UNTIL)); 1694 assert(ndb_filter_add_int_element(f2, 42)); 1695 ndb_filter_end_field(f2); 1696 ndb_filter_end(f2); 1697 1698 assert(ndb_filter_eq(f, f2)); 1699 } 1700 1701 static void test_filter_is_subset() { 1702 struct ndb_filter global, *g = &global; 1703 struct ndb_filter kind, *k = &kind; 1704 struct ndb_filter kind_and_id, *ki = &kind_and_id; 1705 1706 const unsigned char id[] = { 1707 0x03, 0x36, 0x94, 0x8b, 0xdf, 0xbf, 0x5f, 0x93, 0x98, 0x02, 0xeb, 0xa0, 1708 0x3a, 0xa7, 0x87, 0x35, 0xc8, 0x28, 0x25, 0x21, 0x1e, 0xec, 0xe9, 0x87, 1709 0xa6, 0xd2, 0xe2, 0x0e, 0x3c, 0xff, 0xf9, 0x30 1710 }; 1711 1712 ndb_filter_init(g); 1713 ndb_filter_end(g); 1714 1715 ndb_filter_init(k); 1716 assert(ndb_filter_start_field(k, NDB_FILTER_KINDS)); 1717 assert(ndb_filter_add_int_element(k, 1)); 1718 ndb_filter_end_field(k); 1719 ndb_filter_end(k); 1720 1721 ndb_filter_init(ki); 1722 assert(ndb_filter_start_field(ki, NDB_FILTER_KINDS)); 1723 assert(ndb_filter_add_int_element(ki, 1)); 1724 ndb_filter_end_field(ki); 1725 assert(ndb_filter_start_field(ki, NDB_FILTER_IDS)); 1726 assert(ndb_filter_add_id_element(ki, id)); 1727 ndb_filter_end_field(ki); 1728 ndb_filter_end(ki); 1729 1730 assert(ndb_filter_is_subset_of(g, k) == 0); 1731 assert(ndb_filter_is_subset_of(k, g) == 1); 1732 assert(ndb_filter_is_subset_of(ki, k) == 1); 1733 assert(ndb_filter_is_subset_of(k, ki) == 0); 1734 } 1735 1736 int main(int argc, const char *argv[]) { 1737 test_parse_filter_json(); 1738 test_filter_eq(); 1739 test_filter_is_subset(); 1740 test_filter_json(); 1741 test_bech32_parsing(); 1742 test_single_url_parsing(); 1743 test_url_parsing(); 1744 test_query(); 1745 test_tag_query(); 1746 test_weird_note_corruption(); 1747 test_parse_content(); 1748 test_subscriptions(); 1749 test_comma_url_parsing(); 1750 test_varints(); 1751 test_bech32_objects(); 1752 //test_block_coding(); 1753 test_encode_decode_invoice(); 1754 test_filters(); 1755 //test_migrate(); 1756 test_fetched_at(); 1757 test_profile_updates(); 1758 test_reaction_counter(); 1759 test_load_profiles(); 1760 test_basic_event(); 1761 test_empty_tags(); 1762 test_parse_json(); 1763 test_parse_contact_list(); 1764 test_strings_work_before_finalization(); 1765 test_tce(); 1766 test_tce_command_result(); 1767 test_tce_eose(); 1768 test_tce_command_result_empty_msg(); 1769 test_content_len(); 1770 test_fuzz_events(); 1771 1772 // note fetching 1773 test_fetch_last_noteid(); 1774 1775 test_timeline_query(); 1776 1777 // fulltext 1778 test_fulltext(); 1779 1780 // protected queue tests 1781 test_queue_init_pop_push(); 1782 test_queue_thread_safety(); 1783 test_queue_boundary_conditions(); 1784 1785 // memchr stuff 1786 test_fast_strchr(); 1787 1788 // profiles 1789 test_replacement(); 1790 1791 printf("All tests passed!\n"); // Print this if all tests pass. 1792 } 1793 1794 1795