diff --git a/src/ssb.tests.c b/src/ssb.tests.c index 26295007..5aaab141 100644 --- a/src/ssb.tests.c +++ b/src/ssb.tests.c @@ -695,3 +695,152 @@ void tf_ssb_test_bench(const tf_test_options_t* options) uv_loop_close(&loop); } + +static void _ssb_test_room_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection, void* user_data) +{ + const char* changes[] = + { + "create", "connect", "remove" + }; + tf_printf("change=%s %p connection=%s:%d\n", changes[change], connection, tf_ssb_connection_get_host(connection), tf_ssb_connection_get_port(connection)); +} + +typedef struct _close_t +{ + tf_ssb_t* ssb; + tf_ssb_connection_t* connection; + char id[k_id_base64_len]; + int32_t request_number; + uv_timer_t timer; +} close_t; + +static void _timer_close(uv_handle_t* handle) +{ + tf_free(handle->data); +} + +static void _close_callback(uv_timer_t* timer) +{ + close_t* data = timer->data; + tf_printf("breaking %s %p\n", data->id, data->connection); + const char* message = "{\"name\":\"Error\",\"message\":\"whoops\",\"stack\":\"nah\"}"; + tf_ssb_connection_rpc_send( + data->connection, + k_ssb_rpc_flag_stream | k_ssb_rpc_flag_json | k_ssb_rpc_flag_end_error, + data->request_number, + (const uint8_t*)message, + strlen(message), + NULL, + NULL, + NULL); + uv_close((uv_handle_t*)timer, _timer_close); +} + +static void _break_in_a_bit(tf_ssb_t* ssb, tf_ssb_connection_t* connection, const char* id, int32_t request_number) +{ + close_t* data = tf_malloc(sizeof(close_t)); + *data = (close_t) + { + .ssb = ssb, + .connection = connection, + .request_number = request_number, + .timer = + { + .data = data, + }, + }; + snprintf(data->id, sizeof(data->id), "%s", id); + uv_timer_init(tf_ssb_get_loop(ssb), &data->timer); + uv_timer_start(&data->timer, _close_callback, 3000, 0); +} + +static void _ssb_test_room_broadcasts_visit(const char* host, const struct sockaddr_in* addr, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data) +{ + tf_ssb_t* ssb = user_data; + char id[k_id_base64_len] = { 0 }; + tf_ssb_id_bin_to_str(id, sizeof(id), pub); + tf_ssb_connection_t* connections[8]; + if (tunnel && + strcmp(id, "@Jqm63iKumgaWfUI6mXtmQCDHiQJhzMiEWXYUqtcGs9o=.ed25519") != 0 && + tf_ssb_get_connections(ssb, connections, 8) == 1) + { + tf_printf("%s %p %s\n", host, tunnel, id); + + int32_t tunnel_request_number = tf_ssb_connection_next_request_number(tunnel); + + char portal[k_id_base64_len] = { 0 }; + char target[k_id_base64_len] = { 0 }; + tf_ssb_connection_get_id(tunnel, portal, sizeof(portal)); + tf_ssb_id_bin_to_str(target, sizeof(target), pub); + + JSContext* context = tf_ssb_get_context(ssb); + JSValue message = JS_NewObject(context); + JSValue name = JS_NewArray(context); + JS_SetPropertyUint32(context, name, 0, JS_NewString(context, "tunnel")); + JS_SetPropertyUint32(context, name, 1, JS_NewString(context, "connect")); + JS_SetPropertyStr(context, message, "name", name); + JSValue args = JS_NewArray(context); + JSValue arg = JS_NewObject(context); + JS_SetPropertyStr(context, arg, "portal", JS_NewString(context, portal)); + JS_SetPropertyStr(context, arg, "target", JS_NewString(context, target)); + JS_SetPropertyUint32(context, args, 0, arg); + JS_SetPropertyStr(context, message, "args", args); + JS_SetPropertyStr(context, message, "type", JS_NewString(context, "duplex")); + + tf_ssb_connection_rpc_send_json( + tunnel, + k_ssb_rpc_flag_stream | k_ssb_rpc_flag_new_request, + tunnel_request_number, + message, + NULL, + NULL, + NULL); + JS_FreeValue(context, message); + + tf_printf("tunnel create ssb=%p portal=%s rn=%d target=%s\n", ssb, portal, (int)tunnel_request_number, target); + tf_ssb_connection_tunnel_create(ssb, portal, tunnel_request_number, target); + _break_in_a_bit(ssb, tunnel, target, tunnel_request_number); + } +} + +static void _ssb_test_room_broadcasts_changed(tf_ssb_t* ssb, void* user_data) +{ + tf_ssb_visit_broadcasts(ssb, _ssb_test_room_broadcasts_visit, ssb); +} + +void tf_ssb_test_go_ssb_room(const tf_test_options_t* options) +{ + tf_printf("Testing go_ssb_room.\n"); + + uv_loop_t loop = { 0 }; + uv_loop_init(&loop); + + tf_trace_t* trace = tf_trace_create(); + + unlink("out/test_db0.sqlite"); + tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite"); + tf_ssb_set_trace(ssb0, trace); + tf_ssb_generate_keys(ssb0); + + unlink("out/test_db1.sqlite"); + tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite"); + tf_ssb_set_trace(ssb1, trace); + tf_ssb_generate_keys(ssb1); + + tf_ssb_add_connections_changed_callback(ssb0, _ssb_test_room_connections_changed, NULL, NULL); + tf_ssb_add_connections_changed_callback(ssb1, _ssb_test_room_connections_changed, NULL, NULL); + + tf_ssb_add_broadcasts_changed_callback(ssb0, _ssb_test_room_broadcasts_changed, NULL, NULL); + + tf_ssb_connect_str(ssb0, "net:linode.unprompted.com:8008~shs:Q0pc/7kXQJGIlqJxuwayL2huayzddgkVDoGkYVWQS1Y=:SSB+Room+PSK3TLYC2T86EHQCUHBUHASCASE18JBV24="); + tf_ssb_connect_str(ssb1, "net:linode.unprompted.com:8008~shs:Q0pc/7kXQJGIlqJxuwayL2huayzddgkVDoGkYVWQS1Y=:SSB+Room+PSK3TLYC2T86EHQCUHBUHASCASE18JBV24="); + + uv_run(&loop, UV_RUN_DEFAULT); + + tf_ssb_destroy(ssb0); + tf_ssb_destroy(ssb1); + + tf_trace_destroy(trace); + + uv_loop_close(&loop); +} diff --git a/src/ssb.tests.h b/src/ssb.tests.h index 76cf063d..6b8f667d 100644 --- a/src/ssb.tests.h +++ b/src/ssb.tests.h @@ -7,3 +7,4 @@ void tf_ssb_test_ssb(const tf_test_options_t* options); void tf_ssb_test_following(const tf_test_options_t* options); void tf_ssb_test_rooms(const tf_test_options_t* options); void tf_ssb_test_bench(const tf_test_options_t* options); +void tf_ssb_test_go_ssb_room(const tf_test_options_t* options); diff --git a/src/tests.c b/src/tests.c index 5ad9900b..36a7744b 100644 --- a/src/tests.c +++ b/src/tests.c @@ -658,7 +658,7 @@ static void _test_b64(const tf_test_options_t* options) unlink("out/test.js"); } -static void _tf_test_run(const tf_test_options_t* options, const char* name, void (*test)(const tf_test_options_t* options)) +static void _tf_test_run(const tf_test_options_t* options, const char* name, void (*test)(const tf_test_options_t* options), bool opt_in) { bool specified = false; if (options->tests) @@ -677,7 +677,7 @@ static void _tf_test_run(const tf_test_options_t* options, const char* name, voi tf_free(dup); } - if (!options->tests || specified) + if ((!opt_in && !options->tests) || specified) { #define GREEN "\e[1;32m" #define MAGENTA "\e[1;35m" @@ -695,27 +695,28 @@ static void _tf_test_run(const tf_test_options_t* options, const char* name, voi void tf_tests(const tf_test_options_t* options) { - _tf_test_run(options, "ssb", tf_ssb_test_ssb); - _tf_test_run(options, "ssb_id", tf_ssb_test_id_conversion); - _tf_test_run(options, "ssb_following", tf_ssb_test_following); - _tf_test_run(options, "nop", _test_nop); - _tf_test_run(options, "child", _test_child); - _tf_test_run(options, "promise", _test_promise); - _tf_test_run(options, "promise_remote_throw", _test_promise_remote_throw); - _tf_test_run(options, "promise_remote_reject", _test_promise_remote_reject); - _tf_test_run(options, "database", _test_database); - _tf_test_run(options, "this", _test_this); - _tf_test_run(options, "await", _test_await); - _tf_test_run(options, "import", _test_import); - _tf_test_run(options, "exit", _test_exit); - _tf_test_run(options, "icu", _test_icu); - _tf_test_run(options, "uint8array", _test_uint8array); - _tf_test_run(options, "float", _test_float); - _tf_test_run(options, "socket", _test_socket); - _tf_test_run(options, "file", _test_file); - _tf_test_run(options, "sign", _test_sign); - _tf_test_run(options, "b64", _test_b64); - _tf_test_run(options, "rooms", tf_ssb_test_rooms); - _tf_test_run(options, "bench", tf_ssb_test_bench); + _tf_test_run(options, "ssb", tf_ssb_test_ssb, false); + _tf_test_run(options, "ssb_id", tf_ssb_test_id_conversion, false); + _tf_test_run(options, "ssb_following", tf_ssb_test_following, false); + _tf_test_run(options, "nop", _test_nop, false); + _tf_test_run(options, "child", _test_child, false); + _tf_test_run(options, "promise", _test_promise, false); + _tf_test_run(options, "promise_remote_throw", _test_promise_remote_throw, false); + _tf_test_run(options, "promise_remote_reject", _test_promise_remote_reject, false); + _tf_test_run(options, "database", _test_database, false); + _tf_test_run(options, "this", _test_this, false); + _tf_test_run(options, "await", _test_await, false); + _tf_test_run(options, "import", _test_import, false); + _tf_test_run(options, "exit", _test_exit, false); + _tf_test_run(options, "icu", _test_icu, false); + _tf_test_run(options, "uint8array", _test_uint8array, false); + _tf_test_run(options, "float", _test_float, false); + _tf_test_run(options, "socket", _test_socket, false); + _tf_test_run(options, "file", _test_file, false); + _tf_test_run(options, "sign", _test_sign, false); + _tf_test_run(options, "b64", _test_b64, false); + _tf_test_run(options, "rooms", tf_ssb_test_rooms, false); + _tf_test_run(options, "bench", tf_ssb_test_bench, false); + _tf_test_run(options, "go-ssb-room", tf_ssb_test_go_ssb_room, true); tf_printf("Tests completed.\n"); }