test.c (30758B)
1 2 #include "nostrdb.h" 3 #include "hex.h" 4 #include "io.h" 5 #include "bolt11/bolt11.h" 6 #include "bolt11/amount.h" 7 #include "protected_queue.h" 8 #include "memchr.h" 9 #include "print_util.h" 10 #include "bindings/c/profile_reader.h" 11 #include "bindings/c/profile_verifier.h" 12 #include "bindings/c/meta_reader.h" 13 #include "bindings/c/meta_verifier.h" 14 15 #include <stdio.h> 16 #include <assert.h> 17 #include <unistd.h> 18 19 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 20 21 static const char *test_dir = "./testdata/db"; 22 23 static NdbProfile_table_t lookup_profile(struct ndb_txn *txn, uint64_t pk) 24 { 25 void *root; 26 size_t len; 27 assert((root = ndb_get_profile_by_key(txn, pk, &len))); 28 assert(root); 29 30 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root); 31 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 32 return profile; 33 } 34 35 static void print_search(struct ndb_txn *txn, struct ndb_search *search) 36 { 37 NdbProfile_table_t profile = lookup_profile(txn, search->profile_key); 38 const char *name = NdbProfile_name_get(profile); 39 const char *display_name = NdbProfile_display_name_get(profile); 40 printf("searched_name name:'%s' display_name:'%s' pk:%" PRIu64 " ts:%" PRIu64 " id:", name, display_name, search->profile_key, search->key->timestamp); 41 print_hex(search->key->id, 32); 42 printf("\n"); 43 } 44 45 46 static void test_filters() 47 { 48 struct ndb_filter filter, *f; 49 struct ndb_note *note; 50 unsigned char buffer[4096]; 51 52 const char *test_note = "{\"id\": \"160e76ca67405d7ce9ef7d2dd72f3f36401c8661a73d45498af842d40b01b736\",\"pubkey\": \"67c67870aebc327eb2a2e765e6dbb42f0f120d2c4e4e28dc16b824cf72a5acc1\",\"created_at\": 1700688516,\"kind\": 1337,\"tags\": [[\"t\",\"hashtag\"],[\"t\",\"grownostr\"],[\"p\",\"4d2e7a6a8e08007ace5a03391d21735f45caf1bf3d67b492adc28967ab46525e\"]],\"content\": \"\",\"sig\": \"20c2d070261ed269559ada40ca5ac395c389681ee3b5f7d50de19dd9b328dd70cf27d9d13875e87c968d9b49fa05f66e90f18037be4529b9e582c7e2afac3f06\"}"; 53 54 f = &filter; 55 assert(ndb_note_from_json(test_note, strlen(test_note), ¬e, buffer, sizeof(buffer))); 56 57 assert(ndb_filter_init(f)); 58 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 59 assert(ndb_filter_add_int_element(f, 1337)); 60 assert(ndb_filter_add_int_element(f, 2)); 61 62 assert(f->current->count == 2); 63 assert(f->current->field.type == NDB_FILTER_KINDS); 64 65 // can't start if we've already started 66 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS) == 0); 67 assert(ndb_filter_start_field(f, NDB_FILTER_GENERIC) == 0); 68 ndb_filter_end_field(f); 69 70 // try matching the filter 71 assert(ndb_filter_matches(f, note)); 72 73 _ndb_note_set_kind(note, 1); 74 75 // inverse match 76 assert(!ndb_filter_matches(f, note)); 77 78 // should also match 2 79 _ndb_note_set_kind(note, 2); 80 assert(ndb_filter_matches(f, note)); 81 82 // don't free, just reset data pointers 83 ndb_filter_reset(f); 84 85 // now try generic matches 86 assert(ndb_filter_start_generic_field(f, 't')); 87 assert(ndb_filter_add_str_element(f, "grownostr")); 88 ndb_filter_end_field(f); 89 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 90 assert(ndb_filter_add_int_element(f, 3)); 91 ndb_filter_end_field(f); 92 93 // shouldn't match the kind filter 94 assert(!ndb_filter_matches(f, note)); 95 96 _ndb_note_set_kind(note, 3); 97 98 // now it should 99 assert(ndb_filter_matches(f, note)); 100 101 ndb_filter_reset(f); 102 assert(ndb_filter_start_field(f, NDB_FILTER_AUTHORS)); 103 assert(ndb_filter_add_id_element(f, ndb_note_pubkey(note))); 104 ndb_filter_end_field(f); 105 assert(f->current == NULL); 106 assert(ndb_filter_matches(f, note)); 107 108 ndb_filter_free(f); 109 } 110 111 // Test fetched_at profile records. These are saved when new profiles are 112 // processed, or the last time we've fetched the profile. 113 static void test_fetched_at() 114 { 115 struct ndb *ndb; 116 struct ndb_txn txn; 117 uint64_t fetched_at, t1, t2; 118 struct ndb_config config; 119 ndb_default_config(&config); 120 121 assert(ndb_init(&ndb, test_dir, &config)); 122 123 const unsigned char pubkey[] = { 0x87, 0xfb, 0xc6, 0xd5, 0x98, 0x31, 0xa8, 0x23, 0xa4, 0x5d, 0x10, 0x1f, 124 0x86, 0x94, 0x2c, 0x41, 0xcd, 0xe2, 0x90, 0x23, 0xf4, 0x09, 0x20, 0x24, 125 0xa2, 0x7c, 0x50, 0x10, 0x3c, 0x15, 0x40, 0x01 }; 126 127 const char profile_1[] = "[\"EVENT\",{\"id\": \"a44eb8fb6931d6155b04038bef0624407e46c85c61e5758392cbb615f00184ca\",\"pubkey\": \"87fbc6d59831a823a45d101f86942c41cde29023f4092024a27c50103c154001\",\"created_at\": 1695593354,\"kind\": 0,\"tags\": [],\"content\": \"{\\\"name\\\":\\\"b\\\"}\",\"sig\": \"7540bbde4b4479275e20d95acaa64027359a73989927f878825093cba2f468bd8e195919a77b4c230acecddf92e6b4bee26918b0c0842f84ec7c1fae82453906\"}]"; 128 129 t1 = time(NULL); 130 131 // process the first event, this should set the fetched_at 132 assert(ndb_process_client_event(ndb, profile_1, sizeof(profile_1))); 133 134 // we sleep for a second because we want to make sure the fetched_at is not 135 // updated for the next record, which is an older profile. 136 sleep(1); 137 138 assert(ndb_begin_query(ndb, &txn)); 139 140 // this should be set to t1 141 fetched_at = ndb_read_last_profile_fetch(&txn, pubkey); 142 143 assert(fetched_at == t1); 144 145 t2 = time(NULL); 146 assert(t1 != t2); // sanity 147 148 const char profile_2[] = "[\"EVENT\",{\"id\": \"9b2861dda8fc602ec2753f92f1a443c9565de606e0c8f4fd2db4f2506a3b13ca\",\"pubkey\": \"87fbc6d59831a823a45d101f86942c41cde29023f4092024a27c50103c154001\",\"created_at\": 1695593347,\"kind\": 0,\"tags\": [],\"content\": \"{\\\"name\\\":\\\"a\\\"}\",\"sig\": \"f48da228f8967d33c3caf0a78f853b5144631eb86c7777fd25949123a5272a92765a0963d4686dd0efe05b7a9b986bfac8d43070b234153acbae5006d5a90f31\"}]"; 149 150 t2 = time(NULL); 151 152 // process the second event, since this is older it should not change 153 // fetched_at 154 assert(ndb_process_client_event(ndb, profile_2, sizeof(profile_2))); 155 156 // we sleep for a second because we want to make sure the fetched_at is not 157 // updated for the next record, which is an older profile. 158 sleep(1); 159 160 fetched_at = ndb_read_last_profile_fetch(&txn, pubkey); 161 assert(fetched_at == t1); 162 } 163 164 static void test_reaction_counter() 165 { 166 static const int alloc_size = 1024 * 1024; 167 char *json = malloc(alloc_size); 168 struct ndb *ndb; 169 size_t len; 170 void *root; 171 int written, reactions; 172 NdbEventMeta_table_t meta; 173 struct ndb_txn txn; 174 struct ndb_config config; 175 ndb_default_config(&config); 176 177 assert(ndb_init(&ndb, test_dir, &config)); 178 179 read_file("testdata/reactions.json", (unsigned char*)json, alloc_size, &written); 180 assert(ndb_process_client_events(ndb, json, written)); 181 ndb_destroy(ndb); 182 183 assert(ndb_init(&ndb, test_dir, &config)); 184 185 assert(ndb_begin_query(ndb, &txn)); 186 187 const unsigned char id[32] = { 188 0x1a, 0x41, 0x56, 0x30, 0x31, 0x09, 0xbb, 0x4a, 0x66, 0x0a, 0x6a, 0x90, 189 0x04, 0xb0, 0xcd, 0xce, 0x8d, 0x83, 0xc3, 0x99, 0x1d, 0xe7, 0x86, 0x4f, 190 0x18, 0x76, 0xeb, 0x0f, 0x62, 0x2c, 0x68, 0xe8 191 }; 192 193 assert((root = ndb_get_note_meta(&txn, id, &len))); 194 assert(0 == NdbEventMeta_verify_as_root(root, len)); 195 assert((meta = NdbEventMeta_as_root(root))); 196 197 reactions = NdbEventMeta_reactions_get(meta); 198 //printf("counted reactions: %d\n", reactions); 199 assert(reactions == 2); 200 ndb_end_query(&txn); 201 ndb_destroy(ndb); 202 } 203 204 static void test_profile_search(struct ndb *ndb) 205 { 206 struct ndb_txn txn; 207 struct ndb_search search; 208 int i; 209 const char *name; 210 NdbProfile_table_t profile; 211 212 assert(ndb_begin_query(ndb, &txn)); 213 assert(ndb_search_profile(&txn, &search, "jean")); 214 //print_search(&txn, &search); 215 profile = lookup_profile(&txn, search.profile_key); 216 name = NdbProfile_name_get(profile); 217 assert(!strncmp(name, "jean", 4)); 218 219 assert(ndb_search_profile_next(&search)); 220 //print_search(&txn, &search); 221 profile = lookup_profile(&txn, search.profile_key); 222 name = NdbProfile_name_get(profile); 223 //assert(strncmp(name, "jean", 4)); 224 225 for (i = 0; i < 3; i++) { 226 ndb_search_profile_next(&search); 227 //print_search(&txn, &search); 228 } 229 230 //assert(!strcmp(name, "jb55")); 231 232 ndb_search_profile_end(&search); 233 ndb_end_query(&txn); 234 } 235 236 static void test_profile_updates() 237 { 238 static const int alloc_size = 1024 * 1024; 239 char *json = malloc(alloc_size); 240 struct ndb *ndb; 241 size_t len; 242 void *record; 243 int written; 244 struct ndb_txn txn; 245 uint64_t key; 246 struct ndb_config config; 247 ndb_default_config(&config); 248 249 assert(ndb_init(&ndb, test_dir, &config)); 250 251 read_file("testdata/profile-updates.json", (unsigned char*)json, alloc_size, &written); 252 253 assert(ndb_process_client_events(ndb, json, written)); 254 255 ndb_destroy(ndb); 256 257 assert(ndb_init(&ndb, test_dir, &config)); 258 259 assert(ndb_begin_query(ndb, &txn)); 260 const unsigned char pk[32] = { 261 0x87, 0xfb, 0xc6, 0xd5, 0x98, 0x31, 0xa8, 0x23, 0xa4, 0x5d, 262 0x10, 0x1f, 0x86, 0x94, 0x2c, 0x41, 0xcd, 0xe2, 0x90, 0x23, 263 0xf4, 0x09, 0x20, 0x24, 0xa2, 0x7c, 0x50, 0x10, 0x3c, 0x15, 264 0x40, 0x01 265 }; 266 record = ndb_get_profile_by_pubkey(&txn, pk, &len, &key); 267 268 assert(record); 269 int res = NdbProfileRecord_verify_as_root(record, len); 270 assert(res == 0); 271 272 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(record); 273 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 274 const char *name = NdbProfile_name_get(profile); 275 276 assert(!strcmp(name, "c")); 277 278 ndb_destroy(ndb); 279 } 280 281 static void test_load_profiles() 282 { 283 static const int alloc_size = 1024 * 1024; 284 char *json = malloc(alloc_size); 285 struct ndb *ndb; 286 int written; 287 struct ndb_config config; 288 ndb_default_config(&config); 289 290 assert(ndb_init(&ndb, test_dir, &config)); 291 292 read_file("testdata/profiles.json", (unsigned char*)json, alloc_size, &written); 293 294 assert(ndb_process_events(ndb, json, written)); 295 296 ndb_destroy(ndb); 297 298 assert(ndb_init(&ndb, test_dir, &config)); 299 unsigned char id[32] = { 300 0x22, 0x05, 0x0b, 0x6d, 0x97, 0xbb, 0x9d, 0xa0, 0x9e, 0x90, 0xed, 0x0c, 301 0x6d, 0xd9, 0x5e, 0xed, 0x1d, 0x42, 0x3e, 0x27, 0xd5, 0xcb, 0xa5, 0x94, 302 0xd2, 0xb4, 0xd1, 0x3a, 0x55, 0x43, 0x09, 0x07 }; 303 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\"}"; 304 305 struct ndb_txn txn; 306 assert(ndb_begin_query(ndb, &txn)); 307 struct ndb_note *note = ndb_get_note_by_id(&txn, id, NULL, NULL); 308 assert(note != NULL); 309 assert(!strcmp(ndb_note_content(note), expected_content)); 310 ndb_end_query(&txn); 311 312 test_profile_search(ndb); 313 314 ndb_destroy(ndb); 315 316 free(json); 317 } 318 319 static void test_fuzz_events() { 320 struct ndb *ndb; 321 const char *str = "[\"EVENT\"\"\"{\"content\"\"created_at\":0 \"id\"\"5086a8f76fe1da7fb56a25d1bebbafd70fca62e36a72c6263f900ff49b8f8604\"\"kind\":0 \"pubkey\":9c87f94bcbe2a837adc28d46c34eeaab8fc2e1cdf94fe19d4b99ae6a5e6acedc \"sig\"\"27374975879c94658412469cee6db73d538971d21a7b580726a407329a4cafc677fb56b946994cea59c3d9e118fef27e4e61de9d2c46ac0a65df14153 ea93cf5\"\"tags\"[[][\"\"]]}]"; 322 struct ndb_config config; 323 ndb_default_config(&config); 324 325 ndb_init(&ndb, test_dir, &config); 326 ndb_process_event(ndb, str, strlen(str)); 327 ndb_destroy(ndb); 328 } 329 330 static void test_migrate() { 331 static const char *v0_dir = "testdata/db/v0"; 332 struct ndb *ndb; 333 struct ndb_config config; 334 ndb_default_config(&config); 335 ndb_config_set_flags(&config, NDB_FLAG_NOMIGRATE); 336 337 fprintf(stderr, "testing migrate on v0\n"); 338 assert(ndb_init(&ndb, v0_dir, &config)); 339 assert(ndb_db_version(ndb) == 0); 340 ndb_destroy(ndb); 341 342 ndb_config_set_flags(&config, 0); 343 344 assert(ndb_init(&ndb, v0_dir, &config)); 345 ndb_destroy(ndb); 346 assert(ndb_init(&ndb, v0_dir, &config)); 347 assert(ndb_db_version(ndb) == 3); 348 349 test_profile_search(ndb); 350 ndb_destroy(ndb); 351 } 352 353 static void test_basic_event() { 354 unsigned char buf[512]; 355 struct ndb_builder builder, *b = &builder; 356 struct ndb_note *note; 357 int ok; 358 359 unsigned char id[32]; 360 memset(id, 1, 32); 361 362 unsigned char pubkey[32]; 363 memset(pubkey, 2, 32); 364 365 unsigned char sig[64]; 366 memset(sig, 3, 64); 367 368 const char *hex_pk = "5d9b81b2d4d5609c5565286fc3b511dc6b9a1b3d7d1174310c624d61d1f82bb9"; 369 370 ok = ndb_builder_init(b, buf, sizeof(buf)); 371 assert(ok); 372 note = builder.note; 373 374 //memset(note->padding, 3, sizeof(note->padding)); 375 376 ok = ndb_builder_set_content(b, hex_pk, strlen(hex_pk)); assert(ok); 377 ndb_builder_set_id(b, id); assert(ok); 378 ndb_builder_set_pubkey(b, pubkey); assert(ok); 379 ndb_builder_set_sig(b, sig); assert(ok); 380 381 ok = ndb_builder_new_tag(b); assert(ok); 382 ok = ndb_builder_push_tag_str(b, "p", 1); assert(ok); 383 ok = ndb_builder_push_tag_str(b, hex_pk, 64); assert(ok); 384 385 ok = ndb_builder_new_tag(b); assert(ok); 386 ok = ndb_builder_push_tag_str(b, "word", 4); assert(ok); 387 ok = ndb_builder_push_tag_str(b, "words", 5); assert(ok); 388 ok = ndb_builder_push_tag_str(b, "w", 1); assert(ok); 389 390 ok = ndb_builder_finalize(b, ¬e, NULL); 391 assert(ok); 392 393 // content should never be packed id 394 // TODO: figure out how to test this now that we don't expose it 395 // assert(note->content.packed.flag != NDB_PACKED_ID); 396 assert(ndb_tags_count(ndb_note_tags(note)) == 2); 397 398 // test iterator 399 struct ndb_iterator iter, *it = &iter; 400 401 ndb_tags_iterate_start(note, it); 402 ok = ndb_tags_iterate_next(it); 403 assert(ok); 404 405 assert(ndb_tag_count(it->tag) == 2); 406 const char *p = ndb_iter_tag_str(it, 0).str; 407 struct ndb_str hpk = ndb_iter_tag_str(it, 1); 408 409 hex_decode(hex_pk, 64, id, 32); 410 411 assert(hpk.flag == NDB_PACKED_ID); 412 assert(memcmp(hpk.id, id, 32) == 0); 413 assert(!strcmp(p, "p")); 414 415 ok = ndb_tags_iterate_next(it); 416 assert(ok); 417 assert(ndb_tag_count(it->tag) == 3); 418 assert(!strcmp(ndb_iter_tag_str(it, 0).str, "word")); 419 assert(!strcmp(ndb_iter_tag_str(it, 1).str, "words")); 420 assert(!strcmp(ndb_iter_tag_str(it, 2).str, "w")); 421 422 ok = ndb_tags_iterate_next(it); 423 assert(!ok); 424 } 425 426 static void test_empty_tags() { 427 struct ndb_builder builder, *b = &builder; 428 struct ndb_iterator iter, *it = &iter; 429 struct ndb_note *note; 430 int ok; 431 unsigned char buf[1024]; 432 433 ok = ndb_builder_init(b, buf, sizeof(buf)); 434 assert(ok); 435 436 ok = ndb_builder_finalize(b, ¬e, NULL); 437 assert(ok); 438 439 assert(ndb_tags_count(ndb_note_tags(note)) == 0); 440 441 ndb_tags_iterate_start(note, it); 442 ok = ndb_tags_iterate_next(it); 443 assert(!ok); 444 } 445 446 static void print_tag(struct ndb_note *note, struct ndb_tag *tag) { 447 struct ndb_str str; 448 int tag_count = ndb_tag_count(tag); 449 for (int i = 0; i < tag_count; i++) { 450 str = ndb_tag_str(note, tag, i); 451 if (str.flag == NDB_PACKED_ID) { 452 printf("<id> "); 453 } else { 454 printf("%s ", str.str); 455 } 456 } 457 printf("\n"); 458 } 459 460 static void test_parse_contact_list() 461 { 462 int size, written = 0; 463 unsigned char id[32]; 464 static const int alloc_size = 2 << 18; 465 unsigned char *json = malloc(alloc_size); 466 unsigned char *buf = malloc(alloc_size); 467 struct ndb_note *note; 468 469 read_file("testdata/contacts.json", json, alloc_size, &written); 470 471 size = ndb_note_from_json((const char*)json, written, ¬e, buf, alloc_size); 472 printf("ndb_note_from_json size %d\n", size); 473 assert(size > 0); 474 assert(size == 34328); 475 476 memcpy(id, ndb_note_id(note), 32); 477 memset(ndb_note_id(note), 0, 32); 478 assert(ndb_calculate_id(note, json, alloc_size)); 479 assert(!memcmp(ndb_note_id(note), id, 32)); 480 481 const char* expected_content = 482 "{\"wss://nos.lol\":{\"write\":true,\"read\":true}," 483 "\"wss://relay.damus.io\":{\"write\":true,\"read\":true}," 484 "\"ws://monad.jb55.com:8080\":{\"write\":true,\"read\":true}," 485 "\"wss://nostr.wine\":{\"write\":true,\"read\":true}," 486 "\"wss://welcome.nostr.wine\":{\"write\":true,\"read\":true}," 487 "\"wss://eden.nostr.land\":{\"write\":true,\"read\":true}," 488 "\"wss://relay.mostr.pub\":{\"write\":true,\"read\":true}," 489 "\"wss://nostr-pub.wellorder.net\":{\"write\":true,\"read\":true}}"; 490 491 assert(!strcmp(expected_content, ndb_note_content(note))); 492 assert(ndb_note_created_at(note) == 1689904312); 493 assert(ndb_note_kind(note) == 3); 494 assert(ndb_tags_count(ndb_note_tags(note)) == 786); 495 //printf("note content length %d\n", ndb_note_content_length(note)); 496 printf("ndb_content_len %d, expected_len %ld\n", 497 ndb_note_content_length(note), 498 strlen(expected_content)); 499 assert(ndb_note_content_length(note) == strlen(expected_content)); 500 501 struct ndb_iterator iter, *it = &iter; 502 ndb_tags_iterate_start(note, it); 503 504 int tags = 0; 505 int total_elems = 0; 506 507 while (ndb_tags_iterate_next(it)) { 508 total_elems += ndb_tag_count(it->tag); 509 //printf("tag %d: ", tags); 510 if (tags == 0 || tags == 1 || tags == 2) 511 assert(ndb_tag_count(it->tag) == 3); 512 513 if (tags == 6) 514 assert(ndb_tag_count(it->tag) == 2); 515 516 if (tags == 7) 517 assert(!strcmp(ndb_tag_str(note, it->tag, 2).str, "wss://nostr-pub.wellorder.net")); 518 519 if (tags == 786) { 520 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 }; 521 assert(!memcmp(ndb_tag_str(note, it->tag, 1).id, h, 32)); 522 } 523 524 //print_tag(it->note, it->tag); 525 526 tags += 1; 527 } 528 529 assert(tags == 786); 530 //printf("total_elems %d\n", total_elems); 531 assert(total_elems == 1580); 532 533 write_file("test_contacts_ndb_note", (unsigned char *)note, size); 534 printf("wrote test_contacts_ndb_note (raw ndb_note)\n"); 535 536 free(json); 537 free(buf); 538 } 539 540 static void test_replacement() 541 { 542 static const int alloc_size = 1024 * 1024; 543 char *json = malloc(alloc_size); 544 unsigned char *buf = malloc(alloc_size); 545 struct ndb *ndb; 546 size_t len; 547 int written; 548 struct ndb_config config; 549 ndb_default_config(&config); 550 551 assert(ndb_init(&ndb, test_dir, &config)); 552 553 read_file("testdata/old-new.json", (unsigned char*)json, alloc_size, &written); 554 assert(ndb_process_events(ndb, json, written)); 555 556 ndb_destroy(ndb); 557 assert(ndb_init(&ndb, test_dir, &config)); 558 559 struct ndb_txn txn; 560 assert(ndb_begin_query(ndb, &txn)); 561 562 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 }; 563 564 void *root = ndb_get_profile_by_pubkey(&txn, pubkey, &len, NULL); 565 566 assert(root); 567 int res = NdbProfileRecord_verify_as_root(root, len); 568 assert(res == 0); 569 570 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root); 571 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 572 const char *name = NdbProfile_name_get(profile); 573 574 assert(!strcmp(name, "jb55")); 575 576 ndb_end_query(&txn); 577 578 free(json); 579 free(buf); 580 } 581 582 static void test_fetch_last_noteid() 583 { 584 static const int alloc_size = 1024 * 1024; 585 char *json = malloc(alloc_size); 586 unsigned char *buf = malloc(alloc_size); 587 struct ndb *ndb; 588 size_t len; 589 int written; 590 struct ndb_config config; 591 ndb_default_config(&config); 592 593 assert(ndb_init(&ndb, test_dir, &config)); 594 595 read_file("testdata/random.json", (unsigned char*)json, alloc_size, &written); 596 assert(ndb_process_events(ndb, json, written)); 597 598 ndb_destroy(ndb); 599 600 assert(ndb_init(&ndb, test_dir, &config)); 601 602 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 }; 603 604 struct ndb_txn txn; 605 assert(ndb_begin_query(ndb, &txn)); 606 struct ndb_note *note = ndb_get_note_by_id(&txn, id, &len, NULL); 607 assert(note != NULL); 608 assert(ndb_note_created_at(note) == 1650054135); 609 610 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 }; 611 612 unsigned char profile_note_id[32] = { 613 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 614 }; 615 616 void *root = ndb_get_profile_by_pubkey(&txn, pk, &len, NULL); 617 618 assert(root); 619 int res = NdbProfileRecord_verify_as_root(root, len); 620 printf("NdbProfileRecord verify result %d\n", res); 621 assert(res == 0); 622 623 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root); 624 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 625 const char *lnurl = NdbProfileRecord_lnurl_get(profile_record); 626 const char *name = NdbProfile_name_get(profile); 627 uint64_t key = NdbProfileRecord_note_key_get(profile_record); 628 assert(name); 629 assert(lnurl); 630 assert(!strcmp(name, "jb55")); 631 assert(!strcmp(lnurl, "fixme")); 632 633 printf("note_key %" PRIu64 "\n", key); 634 635 struct ndb_note *n = ndb_get_note_by_key(&txn, key, NULL); 636 ndb_end_query(&txn); 637 assert(memcmp(profile_note_id, ndb_note_id(n), 32) == 0); 638 639 //fwrite(profile, len, 1, stdout); 640 641 ndb_destroy(ndb); 642 643 free(json); 644 free(buf); 645 } 646 647 static void test_parse_contact_event() 648 { 649 int written; 650 static const int alloc_size = 2 << 18; 651 char *json = malloc(alloc_size); 652 unsigned char *buf = malloc(alloc_size); 653 struct ndb_tce tce; 654 655 assert(read_file("testdata/contacts-event.json", (unsigned char*)json, 656 alloc_size, &written)); 657 assert(ndb_ws_event_from_json(json, written, &tce, buf, alloc_size, NULL)); 658 659 assert(tce.evtype == NDB_TCE_EVENT); 660 661 free(json); 662 free(buf); 663 } 664 665 static void test_content_len() 666 { 667 int written; 668 static const int alloc_size = 2 << 18; 669 char *json = malloc(alloc_size); 670 unsigned char *buf = malloc(alloc_size); 671 struct ndb_tce tce; 672 673 assert(read_file("testdata/failed_size.json", (unsigned char*)json, 674 alloc_size, &written)); 675 assert(ndb_ws_event_from_json(json, written, &tce, buf, alloc_size, NULL)); 676 677 assert(tce.evtype == NDB_TCE_EVENT); 678 assert(ndb_note_content_length(tce.event.note) == 0); 679 680 free(json); 681 free(buf); 682 } 683 684 static void test_parse_json() { 685 char hex_id[32] = {0}; 686 unsigned char buffer[1024]; 687 struct ndb_note *note; 688 #define HEX_ID "5004a081e397c6da9dc2f2d6b3134006a9d0e8c1b46689d9fe150bb2f21a204d" 689 #define HEX_PK "b169f596968917a1abeb4234d3cf3aa9baee2112e58998d17c6db416ad33fe40" 690 static const char *json = 691 "{\"id\": \"" HEX_ID "\",\"pubkey\": \"" HEX_PK "\",\"created_at\": 1689836342,\"kind\": 1,\"tags\": [[\"p\",\"" HEX_ID "\"], [\"word\", \"words\", \"w\"]],\"content\": \"共通語\",\"sig\": \"e4d528651311d567f461d7be916c37cbf2b4d530e672f29f15f353291ed6df60c665928e67d2f18861c5ca88\"}"; 692 int ok; 693 694 ok = ndb_note_from_json(json, strlen(json), ¬e, buffer, sizeof(buffer)); 695 assert(ok); 696 697 const char *content = ndb_note_content(note); 698 unsigned char *id = ndb_note_id(note); 699 700 hex_decode(HEX_ID, 64, hex_id, sizeof(hex_id)); 701 702 assert(!strcmp(content, "共通語")); 703 assert(!memcmp(id, hex_id, 32)); 704 705 assert(ndb_tags_count(ndb_note_tags(note)) == 2); 706 707 struct ndb_iterator iter, *it = &iter; 708 ndb_tags_iterate_start(note, it); assert(ok); 709 ok = ndb_tags_iterate_next(it); assert(ok); 710 assert(ndb_tag_count(it->tag) == 2); 711 assert(!strcmp(ndb_iter_tag_str(it, 0).str, "p")); 712 assert(!memcmp(ndb_iter_tag_str(it, 1).id, hex_id, 32)); 713 714 ok = ndb_tags_iterate_next(it); assert(ok); 715 assert(ndb_tag_count(it->tag) == 3); 716 assert(!strcmp(ndb_iter_tag_str(it, 0).str, "word")); 717 assert(!strcmp(ndb_iter_tag_str(it, 1).str, "words")); 718 assert(!strcmp(ndb_iter_tag_str(it, 2).str, "w")); 719 } 720 721 static void test_strings_work_before_finalization() { 722 struct ndb_builder builder, *b = &builder; 723 struct ndb_note *note; 724 int ok; 725 unsigned char buf[1024]; 726 727 ok = ndb_builder_init(b, buf, sizeof(buf)); assert(ok); 728 ndb_builder_set_content(b, "hello", 5); 729 730 assert(!strcmp(ndb_note_content(b->note), "hello")); 731 assert(ndb_builder_finalize(b, ¬e, NULL)); 732 733 assert(!strcmp(ndb_note_content(note), "hello")); 734 } 735 736 static void test_tce_eose() { 737 unsigned char buf[1024]; 738 const char json[] = "[\"EOSE\",\"s\"]"; 739 struct ndb_tce tce; 740 int ok; 741 742 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 743 assert(ok); 744 745 assert(tce.evtype == NDB_TCE_EOSE); 746 assert(tce.subid_len == 1); 747 assert(!memcmp(tce.subid, "s", 1)); 748 } 749 750 static void test_tce_command_result() { 751 unsigned char buf[1024]; 752 const char json[] = "[\"OK\",\"\",true,\"blocked: ok\"]"; 753 struct ndb_tce tce; 754 int ok; 755 756 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 757 assert(ok); 758 759 assert(tce.evtype == NDB_TCE_OK); 760 assert(tce.subid_len == 0); 761 assert(tce.command_result.ok == 1); 762 assert(!memcmp(tce.subid, "", 0)); 763 } 764 765 static void test_tce_command_result_empty_msg() { 766 unsigned char buf[1024]; 767 const char json[] = "[\"OK\",\"b1d8f68d39c07ce5c5ea10c235100d529b2ed2250140b36a35d940b712dc6eff\",true,\"\"]"; 768 struct ndb_tce tce; 769 int ok; 770 771 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 772 assert(ok); 773 774 assert(tce.evtype == NDB_TCE_OK); 775 assert(tce.subid_len == 64); 776 assert(tce.command_result.ok == 1); 777 assert(tce.command_result.msglen == 0); 778 assert(!memcmp(tce.subid, "b1d8f68d39c07ce5c5ea10c235100d529b2ed2250140b36a35d940b712dc6eff", 0)); 779 } 780 781 // test to-client event 782 static void test_tce() { 783 784 #define HEX_ID "5004a081e397c6da9dc2f2d6b3134006a9d0e8c1b46689d9fe150bb2f21a204d" 785 #define HEX_PK "b169f596968917a1abeb4234d3cf3aa9baee2112e58998d17c6db416ad33fe40" 786 #define JSON "{\"id\": \"" HEX_ID "\",\"pubkey\": \"" HEX_PK "\",\"created_at\": 1689836342,\"kind\": 1,\"tags\": [[\"p\",\"" HEX_ID "\"], [\"word\", \"words\", \"w\"]],\"content\": \"共通語\",\"sig\": \"e4d528651311d567f461d7be916c37cbf2b4d530e672f29f15f353291ed6df60c665928e67d2f18861c5ca88\"}" 787 unsigned char buf[1024]; 788 const char json[] = "[\"EVENT\",\"subid123\"," JSON "]"; 789 struct ndb_tce tce; 790 int ok; 791 792 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 793 assert(ok); 794 795 assert(tce.evtype == NDB_TCE_EVENT); 796 assert(tce.subid_len == 8); 797 assert(!memcmp(tce.subid, "subid123", 8)); 798 799 #undef HEX_ID 800 #undef HEX_PK 801 #undef JSON 802 } 803 804 #define TEST_BUF_SIZE 10 // For simplicity 805 806 static void test_queue_init_pop_push() { 807 struct prot_queue q; 808 int buffer[TEST_BUF_SIZE]; 809 int data; 810 811 // Initialize 812 assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1); 813 814 // Push and Pop 815 data = 5; 816 assert(prot_queue_push(&q, &data) == 1); 817 prot_queue_pop(&q, &data); 818 assert(data == 5); 819 820 // Push to full, and then fail to push 821 for (int i = 0; i < TEST_BUF_SIZE; i++) { 822 assert(prot_queue_push(&q, &i) == 1); 823 } 824 assert(prot_queue_push(&q, &data) == 0); // Should fail as queue is full 825 826 // Pop to empty, and then fail to pop 827 for (int i = 0; i < TEST_BUF_SIZE; i++) { 828 assert(prot_queue_try_pop(&q, &data) == 1); 829 assert(data == i); 830 } 831 assert(prot_queue_try_pop(&q, &data) == 0); // Should fail as queue is empty 832 } 833 834 // This function will be used by threads to test thread safety. 835 void* thread_func(void* arg) { 836 struct prot_queue* q = (struct prot_queue*) arg; 837 int data; 838 839 for (int i = 0; i < 100; i++) { 840 data = i; 841 prot_queue_push(q, &data); 842 prot_queue_pop(q, &data); 843 } 844 return NULL; 845 } 846 847 static void test_queue_thread_safety() { 848 struct prot_queue q; 849 int buffer[TEST_BUF_SIZE]; 850 pthread_t threads[2]; 851 852 assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1); 853 854 // Create threads 855 for (int i = 0; i < 2; i++) { 856 pthread_create(&threads[i], NULL, thread_func, &q); 857 } 858 859 // Join threads 860 for (int i = 0; i < 2; i++) { 861 pthread_join(threads[i], NULL); 862 } 863 864 // After all operations, the queue should be empty 865 int data; 866 assert(prot_queue_try_pop(&q, &data) == 0); 867 } 868 869 static void test_queue_boundary_conditions() { 870 struct prot_queue q; 871 int buffer[TEST_BUF_SIZE]; 872 int data; 873 874 // Initialize 875 assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1); 876 877 // Push to full 878 for (int i = 0; i < TEST_BUF_SIZE; i++) { 879 assert(prot_queue_push(&q, &i) == 1); 880 } 881 882 // Try to push to a full queue 883 int old_head = q.head; 884 int old_tail = q.tail; 885 int old_count = q.count; 886 assert(prot_queue_push(&q, &data) == 0); 887 888 // Assert the queue's state has not changed 889 assert(old_head == q.head); 890 assert(old_tail == q.tail); 891 assert(old_count == q.count); 892 893 // Pop to empty 894 for (int i = 0; i < TEST_BUF_SIZE; i++) { 895 assert(prot_queue_try_pop(&q, &data) == 1); 896 } 897 898 // Try to pop from an empty queue 899 old_head = q.head; 900 old_tail = q.tail; 901 old_count = q.count; 902 assert(prot_queue_try_pop(&q, &data) == 0); 903 904 // Assert the queue's state has not changed 905 assert(old_head == q.head); 906 assert(old_tail == q.tail); 907 assert(old_count == q.count); 908 } 909 910 static void test_fast_strchr() 911 { 912 // Test 1: Basic test 913 const char *testStr1 = "Hello, World!"; 914 assert(fast_strchr(testStr1, 'W', strlen(testStr1)) == testStr1 + 7); 915 916 // Test 2: Character not present in the string 917 assert(fast_strchr(testStr1, 'X', strlen(testStr1)) == NULL); 918 919 // Test 3: Multiple occurrences of the character 920 const char *testStr2 = "Multiple occurrences."; 921 assert(fast_strchr(testStr2, 'u', strlen(testStr2)) == testStr2 + 1); 922 923 // Test 4: Check with an empty string 924 const char *testStr3 = ""; 925 assert(fast_strchr(testStr3, 'a', strlen(testStr3)) == NULL); 926 927 // Test 5: Check with a one-character string 928 const char *testStr4 = "a"; 929 assert(fast_strchr(testStr4, 'a', strlen(testStr4)) == testStr4); 930 931 // Test 6: Check the last character in the string 932 const char *testStr5 = "Last character check"; 933 assert(fast_strchr(testStr5, 'k', strlen(testStr5)) == testStr5 + 19); 934 935 // Test 7: Large string test (>16 bytes) 936 char *testStr6 = "This is a test for large strings with more than 16 bytes."; 937 assert(fast_strchr(testStr6, 'm', strlen(testStr6)) == testStr6 + 38); 938 } 939 940 static void test_fulltext() 941 { 942 struct ndb *ndb; 943 struct ndb_txn txn; 944 int written; 945 static const int alloc_size = 2 << 18; 946 char *json = malloc(alloc_size); 947 struct ndb_text_search_results results; 948 struct ndb_config config; 949 struct ndb_text_search_config search_config; 950 ndb_default_config(&config); 951 ndb_default_text_search_config(&search_config); 952 953 assert(ndb_init(&ndb, test_dir, &config)); 954 955 read_file("testdata/search.json", (unsigned char*)json, alloc_size, &written); 956 assert(ndb_process_client_events(ndb, json, written)); 957 ndb_destroy(ndb); 958 assert(ndb_init(&ndb, test_dir, &config)); 959 960 ndb_begin_query(ndb, &txn); 961 ndb_text_search(&txn, "Jump Over", &results, &search_config); 962 ndb_end_query(&txn); 963 964 ndb_destroy(ndb); 965 966 free(json); 967 } 968 969 int main(int argc, const char *argv[]) { 970 test_filters(); 971 test_migrate(); 972 test_fetched_at(); 973 test_profile_updates(); 974 test_reaction_counter(); 975 test_load_profiles(); 976 test_basic_event(); 977 test_empty_tags(); 978 test_parse_json(); 979 test_parse_contact_list(); 980 test_strings_work_before_finalization(); 981 test_tce(); 982 test_tce_command_result(); 983 test_tce_eose(); 984 test_tce_command_result_empty_msg(); 985 test_content_len(); 986 test_fuzz_events(); 987 988 // note fetching 989 test_fetch_last_noteid(); 990 991 // fulltext 992 test_fulltext(); 993 994 // protected queue tests 995 test_queue_init_pop_push(); 996 test_queue_thread_safety(); 997 test_queue_boundary_conditions(); 998 999 // memchr stuff 1000 test_fast_strchr(); 1001 1002 // profiles 1003 test_replacement(); 1004 1005 printf("All tests passed!\n"); // Print this if all tests pass. 1006 } 1007 1008 1009