nostr_core_lib/tests/relay_pool_test.c

319 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* NOSTR Relay Pool Test Program (READ-ONLY)
*
* Tests the relay pool event processing functionality by:
* - Creating a pool with hardcoded relays
* - Subscribing to kind 1 events (text notes) from other users
* - Using the new event processing functions
* - Displaying raw data output without interpretation
*
* IMPORTANT: This test is READ-ONLY and never publishes events.
* It only sends REQ (subscription) messages and receives EVENT responses.
* Any test events seen in output are from other users or previous test runs.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include "../nostr_core/nostr_core.h"
#include "../cjson/cJSON.h"
// Global variables for clean shutdown
static volatile int keep_running = 1;
static nostr_relay_pool_t* g_pool = NULL;
static nostr_pool_subscription_t* g_subscription = NULL;
// Statistics tracking
static int events_received = 0;
static int events_per_relay[3] = {0, 0, 0}; // Track events per relay
static const char* relay_urls[] = {
"wss://relay.laantungir.net",
"ws://127.0.0.1:7777",
"wss://nostr.mom"
};
static const int relay_count = 3;
// Signal handler for clean shutdown
void signal_handler(int sig) {
(void)sig; // Unused parameter
printf("\n🛑 Received shutdown signal, cleaning up...\n");
keep_running = 0;
}
// Event callback - called when events are received
void on_event_received(cJSON* event, const char* relay_url, void* user_data) {
(void)user_data; // Unused parameter
events_received++;
// Track events per relay
for (int i = 0; i < relay_count; i++) {
if (strcmp(relay_url, relay_urls[i]) == 0) {
events_per_relay[i]++;
break;
}
}
// Print raw event data
char* event_json = cJSON_Print(event);
if (event_json) {
printf("\n📨 EVENT from %s:\n", relay_url);
printf("Raw JSON: %s\n", event_json);
printf("---\n");
free(event_json);
}
// Also extract and display key fields for readability
cJSON* id = cJSON_GetObjectItem(event, "id");
cJSON* pubkey = cJSON_GetObjectItem(event, "pubkey");
cJSON* created_at = cJSON_GetObjectItem(event, "created_at");
cJSON* content = cJSON_GetObjectItem(event, "content");
printf("📄 Parsed fields:\n");
if (id && cJSON_IsString(id)) {
printf(" ID: %s\n", cJSON_GetStringValue(id));
}
if (pubkey && cJSON_IsString(pubkey)) {
printf(" Author: %s\n", cJSON_GetStringValue(pubkey));
}
if (created_at && cJSON_IsNumber(created_at)) {
time_t timestamp = (time_t)cJSON_GetNumberValue(created_at);
printf(" Created: %s", ctime(&timestamp));
}
if (content && cJSON_IsString(content)) {
const char* text = cJSON_GetStringValue(content);
printf(" Content: %.100s%s\n", text, strlen(text) > 100 ? "..." : "");
}
printf("===============================\n");
}
// EOSE callback - called when all relays have sent "End of Stored Events"
void on_eose_received(void* user_data) {
(void)user_data; // Unused parameter
printf("✅ EOSE: All relays have finished sending stored events\n");
}
// Display relay status
void display_relay_status() {
char** urls;
nostr_pool_relay_status_t* statuses;
int count = nostr_relay_pool_list_relays(g_pool, &urls, &statuses);
if (count > 0) {
printf("\n🔗 RELAY STATUS:\n");
for (int i = 0; i < count; i++) {
const char* status_icon;
const char* status_text;
switch (statuses[i]) {
case NOSTR_POOL_RELAY_CONNECTED:
status_icon = "🟢";
status_text = "Connected";
break;
case NOSTR_POOL_RELAY_CONNECTING:
status_icon = "🟡";
status_text = "Connecting...";
break;
case NOSTR_POOL_RELAY_DISCONNECTED:
status_icon = "🔴";
status_text = "Disconnected";
break;
case NOSTR_POOL_RELAY_ERROR:
status_icon = "";
status_text = "Error";
break;
default:
status_icon = "";
status_text = "Unknown";
break;
}
// Get publish and query latency statistics
double query_latency = nostr_relay_pool_get_relay_query_latency(g_pool, urls[i]);
const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(g_pool, urls[i]);
// Get events count from relay statistics (more accurate)
int relay_events = 0;
if (stats) {
relay_events = stats->events_received;
} else {
// Fallback to local counter
for (int j = 0; j < relay_count; j++) {
if (strcmp(urls[i], relay_urls[j]) == 0) {
relay_events = events_per_relay[j];
break;
}
}
}
// Display status with latency information
if (query_latency >= 0.0) {
printf(" %s %-25s %s (query: %.0fms, events: %d)\n",
status_icon, urls[i], status_text, query_latency, relay_events);
} else {
printf(" %s %-25s %s (query: ---, events: %d)\n",
status_icon, urls[i], status_text, relay_events);
}
// Show additional latency statistics if available
if (stats) {
if (stats->publish_samples > 0) {
printf(" 📊 Publish latency: avg=%.0fms (%d samples)\n",
stats->publish_latency_avg, stats->publish_samples);
}
if (stats->query_samples > 0) {
printf(" 📊 Query latency: avg=%.0fms (%d samples)\n",
stats->query_latency_avg, stats->query_samples);
}
if (stats->events_published > 0) {
printf(" 📤 Published: %d events (%d OK, %d failed)\n",
stats->events_published, stats->events_published_ok,
stats->events_published_failed);
}
}
free(urls[i]);
}
free(urls);
free(statuses);
printf("📊 Total events received: %d\n", events_received);
}
}
int main() {
printf("🚀 NOSTR Relay Pool Test Program\n");
printf("=================================\n");
printf("Testing relays:\n");
for (int i = 0; i < relay_count; i++) {
printf(" - %s\n", relay_urls[i]);
}
printf("\n");
// Set up signal handler for clean shutdown
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// Initialize NOSTR core library
printf("🔧 Initializing NOSTR core library...\n");
if (nostr_init() != NOSTR_SUCCESS) {
fprintf(stderr, "❌ Failed to initialize NOSTR core library\n");
return 1;
}
// Create relay pool
printf("🏊 Creating relay pool...\n");
g_pool = nostr_relay_pool_create();
if (!g_pool) {
fprintf(stderr, "❌ Failed to create relay pool\n");
nostr_cleanup();
return 1;
}
// Add relays to pool
printf(" Adding relays to pool...\n");
for (int i = 0; i < relay_count; i++) {
printf(" Adding: %s\n", relay_urls[i]);
int result = nostr_relay_pool_add_relay(g_pool, relay_urls[i]);
if (result != NOSTR_SUCCESS) {
printf(" ⚠️ Warning: Failed to add relay %s (error: %s)\n",
relay_urls[i], nostr_strerror(result));
}
}
// Create filter for kind 1 events (text notes)
printf("🔍 Creating subscription filter for kind 1 events...\n");
cJSON* filter = cJSON_CreateObject();
if (!filter) {
fprintf(stderr, "❌ Failed to create filter\n");
nostr_relay_pool_destroy(g_pool);
nostr_cleanup();
return 1;
}
// Add kinds array with kind 1 (text notes)
cJSON* kinds = cJSON_CreateArray();
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(1));
cJSON_AddItemToObject(filter, "kinds", kinds);
// Limit to recent events to avoid flooding
cJSON_AddNumberToObject(filter, "limit", 1);
// Subscribe to events from all relays
printf("📡 Subscribing to events from all relays...\n");
g_subscription = nostr_relay_pool_subscribe(
g_pool,
relay_urls,
relay_count,
filter,
on_event_received,
on_eose_received,
NULL
);
if (!g_subscription) {
fprintf(stderr, "❌ Failed to create subscription\n");
cJSON_Delete(filter);
nostr_relay_pool_destroy(g_pool);
nostr_cleanup();
return 1;
}
printf("✅ Subscription created successfully!\n");
printf("⏱️ Starting event processing...\n");
printf(" (Press Ctrl+C to stop)\n\n");
// Display initial status
display_relay_status();
printf("<EFBFBD> Starting continuous monitoring...\n\n");
// Run event processing loop
time_t last_status_update = time(NULL);
while (keep_running) {
// Process events for 1 second
int events_processed = nostr_relay_pool_run(g_pool, 1000);
// Display status every 5 seconds
if (time(NULL) - last_status_update >= 5) {
display_relay_status();
last_status_update = time(NULL);
}
// Small status indicator
if (events_processed > 0) {
printf(".");
fflush(stdout);
}
}
printf("\n\n🏁 Test completed!\n");
// Final status display
display_relay_status();
// Cleanup
printf("🧹 Cleaning up...\n");
if (g_subscription) {
nostr_pool_subscription_close(g_subscription);
}
if (g_pool) {
nostr_relay_pool_destroy(g_pool);
}
cJSON_Delete(filter);
nostr_cleanup();
printf("✅ Test program finished successfully!\n");
printf("📈 Final stats:\n");
printf(" Total events: %d\n", events_received);
for (int i = 0; i < relay_count; i++) {
printf(" %s: %d events\n", relay_urls[i], events_per_relay[i]);
}
return 0;
}