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