#include "ssb.h" #include "ssb.db.h" #include "ssb.qjs.h" #include "tests.h" #include #include #include #include #include void tf_ssb_test_id_conversion(const tf_test_options_t* options) { printf("Testing id conversion.\n"); uint8_t bin[k_id_bin_len] = { 0 }; char str[k_id_base64_len] = { 0 }; const char* k_id = "@bzRTe6hgOII2yZ1keGGoNoQgostjQc830trHc453crY=.ed25519"; (void)bin; (void)str; (void)k_id; assert(tf_ssb_id_str_to_bin(bin, k_id)); assert(tf_ssb_id_bin_to_str(str, sizeof(str), bin)); assert(strcmp(str, k_id) == 0); } typedef struct _test_t { tf_ssb_t* ssb0; tf_ssb_t* ssb1; int connection_count0; int connection_count1; } test_t; static void _ssb_test_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection, void* user_data) { test_t* test = user_data; int count = 0; const char** c = tf_ssb_get_connection_ids(ssb); for (const char** p = c; *p; p++) { count++; } free(c); if (ssb == test->ssb0) { printf("callback0 change=%d connection=%p\n", change, connection); test->connection_count0 = count; } else if (ssb == test->ssb1) { printf("callback1 change=%d connection=%p\n", change, connection); test->connection_count1 = count; } printf("conns = %d %d\n", test->connection_count0, test->connection_count1); } static void _count_messages_callback(JSValue row, void* user_data) { int* count = user_data; ++*count; } static int _ssb_test_count_messages(tf_ssb_t* ssb) { int count = 0; tf_ssb_db_visit_query(ssb, "SELECT * FROM messages", JS_UNDEFINED, _count_messages_callback, &count); return count; } void tf_ssb_test_ssb(const tf_test_options_t* options) { printf("Testing SSB.\n"); sqlite3* db0 = NULL; sqlite3* db1 = NULL; assert(sqlite3_open(":memory:", &db0) == SQLITE_OK); assert(sqlite3_open(":memory:", &db1) == SQLITE_OK); uv_loop_t loop = { 0 }; uv_loop_init(&loop); tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, db0, NULL); tf_ssb_init(tf_ssb_get_context(ssb0), ssb0); tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, db1, NULL); tf_ssb_init(tf_ssb_get_context(ssb1), ssb1); test_t test = { .ssb0 = ssb0, .ssb1 = ssb1, }; tf_ssb_add_connections_changed_callback(ssb0, _ssb_test_connections_changed, NULL, &test); tf_ssb_add_connections_changed_callback(ssb1, _ssb_test_connections_changed, NULL, &test); tf_ssb_generate_keys(ssb0); tf_ssb_generate_keys(ssb1); char id0[k_id_base64_len] = { 0 }; char id1[k_id_base64_len] = { 0 }; bool b = tf_ssb_whoami(ssb0, id0, sizeof(id0)); (void)b; assert(b); b = tf_ssb_whoami(ssb1, id1, sizeof(id1)); assert(b); printf("ID %s and %s\n", id0, id1); char blob_id[k_id_base64_len] = { 0 }; const char* k_blob = "Hello, blob!"; b = tf_ssb_db_blob_store(ssb0, (const uint8_t*)k_blob, strlen(k_blob), blob_id, sizeof(blob_id)); assert(b); tf_ssb_append_post(ssb0, "Hello, world!"); tf_ssb_append_post(ssb0, "First post."); JSContext* context = tf_ssb_get_context(ssb0); JSValue message = JS_NewObject(context); JS_SetPropertyStr(context, message, "type", JS_NewString(context, "post")); JS_SetPropertyStr(context, message, "text", JS_NewString(context, "First post.")); JSValue mentions = JS_NewArray(context); JSValue mention = JS_NewObject(context); JS_SetPropertyStr(context, mention, "link", JS_NewString(context, blob_id)); JS_SetPropertyUint32(context, mentions, 0, mention); JS_SetPropertyStr(context, message, "mentions", mentions); tf_ssb_append_message(ssb0, message); JS_FreeValue(context, message); assert(tf_ssb_db_blob_get(ssb0, blob_id, NULL, NULL)); assert(!tf_ssb_db_blob_get(ssb1, blob_id, NULL, NULL)); tf_ssb_server_open(ssb0, 12347); uint8_t id0bin[k_id_bin_len]; tf_ssb_id_str_to_bin(id0bin, id0); tf_ssb_connect(ssb1, "127.0.0.1", 12347, id0bin); while (test.connection_count0 != 1 || test.connection_count1 != 1) { uv_run(&loop, UV_RUN_ONCE); } tf_ssb_server_close(ssb0); while (_ssb_test_count_messages(ssb1) < 3) { uv_run(&loop, UV_RUN_ONCE); } printf("waiting for blob\n"); while (!tf_ssb_db_blob_get(ssb1, blob_id, NULL, NULL)) { uv_run(&loop, UV_RUN_ONCE); } printf("done\n"); tf_ssb_send_close(ssb1); uv_run(&loop, UV_RUN_DEFAULT); tf_ssb_destroy(ssb0); tf_ssb_destroy(ssb1); uv_loop_close(&loop); sqlite3_close(db0); sqlite3_close(db1); } void tf_ssb_test_following(const tf_test_options_t* options) { printf("Testing following.\n"); sqlite3* db0 = NULL; assert(sqlite3_open(":memory:", &db0) == SQLITE_OK); uv_loop_t loop = { 0 }; uv_loop_init(&loop); tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, db0, NULL); tf_ssb_generate_keys(ssb0); tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, db0, NULL); tf_ssb_generate_keys(ssb1); tf_ssb_t* ssb2 = tf_ssb_create(&loop, NULL, db0, NULL); tf_ssb_generate_keys(ssb2); char id0[k_id_base64_len] = { 0 }; char id1[k_id_base64_len] = { 0 }; char id2[k_id_base64_len] = { 0 }; tf_ssb_whoami(ssb0, id0, sizeof(id0)); tf_ssb_whoami(ssb1, id1, sizeof(id1)); tf_ssb_whoami(ssb2, id2, sizeof(id2)); JSContext* context = NULL; JSValue message; #define FOLLOW(ssb, id, follow) \ context = tf_ssb_get_context(ssb); \ message = JS_NewObject(context); \ JS_SetPropertyStr(context, message, "type", JS_NewString(context, "contact")); \ JS_SetPropertyStr(context, message, "contact", JS_NewString(context, id)); \ JS_SetPropertyStr(context, message, "following", follow ? JS_TRUE : JS_FALSE); \ tf_ssb_append_message(ssb, message); \ JS_FreeValue(context, message); \ context = NULL #define DUMP(id, depth) \ do { \ printf("following %d:\n", depth); \ const char** tf_ssb_get_following_deep(tf_ssb_t* ssb_param, const char** ids, int depth_param); \ const char** f = tf_ssb_get_following_deep(ssb0, (const char*[]) { id, NULL }, depth); \ for (const char** p = f; p && *p; p++) { \ printf("* %s\n", *p); \ } \ printf("\n"); \ free(f); \ } while (0) FOLLOW(ssb0, id1, true); FOLLOW(ssb1, id2, true); FOLLOW(ssb2, id0, true); DUMP(id0, 2); DUMP(id1, 2); DUMP(id2, 2); FOLLOW(ssb0, id1, false); //FOLLOW(ssb0, id1, true); //FOLLOW(ssb0, id1, true); DUMP(id0, 1); DUMP(id1, 2); //FOLLOW(ssb0, id1, false); //DUMP(1); //DUMP(1); #undef FOLLOW #undef DUMP uv_run(&loop, UV_RUN_DEFAULT); tf_ssb_destroy(ssb0); tf_ssb_destroy(ssb1); tf_ssb_destroy(ssb2); uv_loop_close(&loop); sqlite3_close(db0); }