495 lines
18 KiB
C
495 lines
18 KiB
C
/*
|
|
* NIP-01 Event Validation Test Suite
|
|
* Tests event structure validation and cryptographic verification
|
|
* Following TESTS POLICY: Shows expected vs actual values, prints entire JSON events
|
|
*/
|
|
|
|
#define _GNU_SOURCE // For strdup on Linux
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include "../nostr_core/nip001.h"
|
|
#include "../nostr_core/nostr_common.h"
|
|
#include "../nostr_core/utils.h"
|
|
#include "../cjson/cJSON.h"
|
|
|
|
// Ensure strdup is declared
|
|
#ifndef strdup
|
|
extern char *strdup(const char *s);
|
|
#endif
|
|
|
|
// Test counter for tracking progress
|
|
static int test_count = 0;
|
|
static int passed_tests = 0;
|
|
|
|
void print_test_header(const char* test_name) {
|
|
test_count++;
|
|
printf("\n=== TEST %d: %s ===\n", test_count, test_name);
|
|
}
|
|
|
|
void print_test_result(int passed, const char* test_name) {
|
|
if (passed) {
|
|
passed_tests++;
|
|
printf("✅ PASS: %s\n", test_name);
|
|
} else {
|
|
printf("❌ FAIL: %s\n", test_name);
|
|
}
|
|
}
|
|
|
|
void print_json_comparison(const char* label, cJSON* expected, cJSON* actual) {
|
|
char* expected_str = cJSON_Print(expected);
|
|
char* actual_str;
|
|
|
|
if (actual) {
|
|
actual_str = cJSON_Print(actual);
|
|
} else {
|
|
actual_str = strdup("NULL");
|
|
}
|
|
|
|
printf("%s Expected JSON:\n%s\n", label, expected_str ? expected_str : "NULL");
|
|
printf("%s Actual JSON:\n%s\n", label, actual_str ? actual_str : "NULL");
|
|
|
|
if (expected_str) free(expected_str);
|
|
if (actual_str) free(actual_str);
|
|
}
|
|
|
|
// Test vector 1: Valid event from nak (should pass all validation)
|
|
int test_valid_event_1(void) {
|
|
print_test_header("Valid Event from nak - Basic");
|
|
|
|
// Generated with: nak event --sec nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99 -c "Test event 1" -k 1
|
|
const char* event_json = "{"
|
|
"\"kind\":1,"
|
|
"\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
|
|
"\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
|
|
"\"created_at\":1755599338,"
|
|
"\"tags\":[],"
|
|
"\"content\":\"Test event 1\","
|
|
"\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
|
|
"}";
|
|
|
|
printf("Input Event JSON:\n%s\n\n", event_json);
|
|
|
|
cJSON* event = cJSON_Parse(event_json);
|
|
if (!event) {
|
|
printf("❌ JSON Parse Error\n");
|
|
return 0;
|
|
}
|
|
|
|
printf("Testing Structure Validation...\n");
|
|
int structure_result = nostr_validate_event_structure(event);
|
|
printf("Expected: NOSTR_SUCCESS (0)\n");
|
|
printf("Actual: %d (%s)\n", structure_result, nostr_strerror(structure_result));
|
|
|
|
if (structure_result != NOSTR_SUCCESS) {
|
|
printf("❌ Structure validation failed\n");
|
|
cJSON_Delete(event);
|
|
return 0;
|
|
}
|
|
printf("✅ Structure validation passed\n\n");
|
|
|
|
printf("Testing Cryptographic Verification...\n");
|
|
int crypto_result = nostr_verify_event_signature(event);
|
|
printf("Expected: NOSTR_SUCCESS (0)\n");
|
|
printf("Actual: %d (%s)\n", crypto_result, nostr_strerror(crypto_result));
|
|
|
|
if (crypto_result != NOSTR_SUCCESS) {
|
|
printf("❌ Cryptographic verification failed\n");
|
|
cJSON_Delete(event);
|
|
return 0;
|
|
}
|
|
printf("✅ Cryptographic verification passed\n\n");
|
|
|
|
printf("Testing Complete Validation...\n");
|
|
int complete_result = nostr_validate_event(event);
|
|
printf("Expected: NOSTR_SUCCESS (0)\n");
|
|
printf("Actual: %d (%s)\n", complete_result, nostr_strerror(complete_result));
|
|
|
|
cJSON_Delete(event);
|
|
return (complete_result == NOSTR_SUCCESS);
|
|
}
|
|
|
|
// Test vector 2: Valid event with tags (should pass all validation)
|
|
int test_valid_event_with_tags(void) {
|
|
print_test_header("Valid Event with Tags");
|
|
|
|
// Generated with: nak event --sec nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99 -c "Test event with tags" -k 1 -t "hello=world" -t "test=value"
|
|
const char* event_json = "{"
|
|
"\"kind\":1,"
|
|
"\"id\":\"781bf3185479315350c4039f719c3a8859a1ebb21a4b04e0e649addf0ae4665b\","
|
|
"\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
|
|
"\"created_at\":1755599345,"
|
|
"\"tags\":[[\"hello\",\"world\"],[\"test\",\"value\"]],"
|
|
"\"content\":\"Test event with tags\","
|
|
"\"sig\":\"3a390ece9d746bd576cb8fbba6f9ed59730099781d0d414d1194f832ca072f23e7764935451a025399202e02f28a59fde2d4c25399e241f3de2ad0b3a4830d6a\""
|
|
"}";
|
|
|
|
printf("Input Event JSON:\n%s\n\n", event_json);
|
|
|
|
cJSON* event = cJSON_Parse(event_json);
|
|
if (!event) {
|
|
printf("❌ JSON Parse Error\n");
|
|
return 0;
|
|
}
|
|
|
|
int result = nostr_validate_event(event);
|
|
printf("Expected: NOSTR_SUCCESS (0)\n");
|
|
printf("Actual: %d (%s)\n", result, nostr_strerror(result));
|
|
|
|
cJSON_Delete(event);
|
|
return (result == NOSTR_SUCCESS);
|
|
}
|
|
|
|
// Test vector 3: Valid metadata event (kind 0)
|
|
int test_valid_metadata_event(void) {
|
|
print_test_header("Valid Metadata Event (Kind 0)");
|
|
|
|
// Generated with: nak event --sec nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99 -c "" -k 0 --ts 1640995200
|
|
const char* event_json = "{"
|
|
"\"kind\":0,"
|
|
"\"id\":\"a2ed5175224f6597bc3a553e91d6fcea2e13f199e56afc90ccecca1c890b308a\","
|
|
"\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
|
|
"\"created_at\":1640995200,"
|
|
"\"tags\":[],"
|
|
"\"content\":\"\","
|
|
"\"sig\":\"b9b2859ded73bbc010331145e2d297b30205d2b94d80ef0619077b687d53756b8d9eb3954ef1483b814059cd879c992153ca9d7b50dcf7053a3d530cd82fb884\""
|
|
"}";
|
|
|
|
printf("Input Event JSON:\n%s\n\n", event_json);
|
|
|
|
cJSON* event = cJSON_Parse(event_json);
|
|
if (!event) {
|
|
printf("❌ JSON Parse Error\n");
|
|
return 0;
|
|
}
|
|
|
|
int result = nostr_validate_event(event);
|
|
printf("Expected: NOSTR_SUCCESS (0)\n");
|
|
printf("Actual: %d (%s)\n", result, nostr_strerror(result));
|
|
|
|
cJSON_Delete(event);
|
|
return (result == NOSTR_SUCCESS);
|
|
}
|
|
|
|
// Test 4: Missing required field (id)
|
|
int test_missing_id_field(void) {
|
|
print_test_header("Missing Required Field - ID");
|
|
|
|
const char* event_json = "{"
|
|
"\"kind\":1,"
|
|
"\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
|
|
"\"created_at\":1755599338,"
|
|
"\"tags\":[],"
|
|
"\"content\":\"Test event 1\","
|
|
"\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
|
|
"}";
|
|
|
|
printf("Input Event JSON (missing 'id' field):\n%s\n\n", event_json);
|
|
|
|
cJSON* event = cJSON_Parse(event_json);
|
|
if (!event) {
|
|
printf("❌ JSON Parse Error\n");
|
|
return 0;
|
|
}
|
|
|
|
int result = nostr_validate_event_structure(event);
|
|
printf("Expected: NOSTR_ERROR_EVENT_INVALID_STRUCTURE (-30)\n");
|
|
printf("Actual: %d (%s)\n", result, nostr_strerror(result));
|
|
|
|
cJSON_Delete(event);
|
|
return (result == NOSTR_ERROR_EVENT_INVALID_STRUCTURE);
|
|
}
|
|
|
|
// Test 5: Invalid hex string length (pubkey too short)
|
|
int test_invalid_pubkey_length(void) {
|
|
print_test_header("Invalid Pubkey Length");
|
|
|
|
const char* event_json = "{"
|
|
"\"kind\":1,"
|
|
"\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
|
|
"\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7\"," // Too short (63 chars instead of 64)
|
|
"\"created_at\":1755599338,"
|
|
"\"tags\":[],"
|
|
"\"content\":\"Test event 1\","
|
|
"\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
|
|
"}";
|
|
|
|
printf("Input Event JSON (pubkey too short - 63 chars instead of 64):\n%s\n\n", event_json);
|
|
|
|
cJSON* event = cJSON_Parse(event_json);
|
|
if (!event) {
|
|
printf("❌ JSON Parse Error\n");
|
|
return 0;
|
|
}
|
|
|
|
int result = nostr_validate_event_structure(event);
|
|
printf("Expected: NOSTR_ERROR_EVENT_INVALID_PUBKEY (-32)\n");
|
|
printf("Actual: %d (%s)\n", result, nostr_strerror(result));
|
|
|
|
cJSON_Delete(event);
|
|
return (result == NOSTR_ERROR_EVENT_INVALID_PUBKEY);
|
|
}
|
|
|
|
// Test 6: Invalid kind (negative)
|
|
int test_invalid_kind_negative(void) {
|
|
print_test_header("Invalid Kind - Negative Value");
|
|
|
|
const char* event_json = "{"
|
|
"\"kind\":-1," // Invalid negative kind
|
|
"\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
|
|
"\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
|
|
"\"created_at\":1755599338,"
|
|
"\"tags\":[],"
|
|
"\"content\":\"Test event 1\","
|
|
"\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
|
|
"}";
|
|
|
|
printf("Input Event JSON (kind = -1):\n%s\n\n", event_json);
|
|
|
|
cJSON* event = cJSON_Parse(event_json);
|
|
if (!event) {
|
|
printf("❌ JSON Parse Error\n");
|
|
return 0;
|
|
}
|
|
|
|
int result = nostr_validate_event_structure(event);
|
|
printf("Expected: NOSTR_ERROR_EVENT_INVALID_KIND (-35)\n");
|
|
printf("Actual: %d (%s)\n", result, nostr_strerror(result));
|
|
|
|
cJSON_Delete(event);
|
|
return (result == NOSTR_ERROR_EVENT_INVALID_KIND);
|
|
}
|
|
|
|
// Test 7: Invalid tags (not an array)
|
|
int test_invalid_tags_not_array(void) {
|
|
print_test_header("Invalid Tags - Not Array");
|
|
|
|
const char* event_json = "{"
|
|
"\"kind\":1,"
|
|
"\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
|
|
"\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
|
|
"\"created_at\":1755599338,"
|
|
"\"tags\":\"not an array\"," // Should be array
|
|
"\"content\":\"Test event 1\","
|
|
"\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
|
|
"}";
|
|
|
|
printf("Input Event JSON (tags is string instead of array):\n%s\n\n", event_json);
|
|
|
|
cJSON* event = cJSON_Parse(event_json);
|
|
if (!event) {
|
|
printf("❌ JSON Parse Error\n");
|
|
return 0;
|
|
}
|
|
|
|
int result = nostr_validate_event_structure(event);
|
|
printf("Expected: NOSTR_ERROR_EVENT_INVALID_TAGS (-36)\n");
|
|
printf("Actual: %d (%s)\n", result, nostr_strerror(result));
|
|
|
|
cJSON_Delete(event);
|
|
return (result == NOSTR_ERROR_EVENT_INVALID_TAGS);
|
|
}
|
|
|
|
// Test 8: Wrong event ID (cryptographic test)
|
|
int test_wrong_event_id(void) {
|
|
print_test_header("Wrong Event ID - Cryptographic Test");
|
|
|
|
// Take valid event but change the ID to something incorrect
|
|
const char* event_json = "{"
|
|
"\"kind\":1,"
|
|
"\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\"," // Wrong ID
|
|
"\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
|
|
"\"created_at\":1755599338,"
|
|
"\"tags\":[],"
|
|
"\"content\":\"Test event 1\","
|
|
"\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
|
|
"}";
|
|
|
|
printf("Input Event JSON (ID changed to all zeros):\n%s\n\n", event_json);
|
|
|
|
cJSON* event = cJSON_Parse(event_json);
|
|
if (!event) {
|
|
printf("❌ JSON Parse Error\n");
|
|
return 0;
|
|
}
|
|
|
|
// Structure should pass
|
|
int structure_result = nostr_validate_event_structure(event);
|
|
printf("Structure validation - Expected: NOSTR_SUCCESS (0)\n");
|
|
printf("Structure validation - Actual: %d (%s)\n", structure_result, nostr_strerror(structure_result));
|
|
|
|
if (structure_result != NOSTR_SUCCESS) {
|
|
printf("❌ Unexpected structure validation failure\n");
|
|
cJSON_Delete(event);
|
|
return 0;
|
|
}
|
|
|
|
// Crypto should fail
|
|
int crypto_result = nostr_verify_event_signature(event);
|
|
printf("Crypto verification - Expected: NOSTR_ERROR_EVENT_INVALID_ID (-31)\n");
|
|
printf("Crypto verification - Actual: %d (%s)\n", crypto_result, nostr_strerror(crypto_result));
|
|
|
|
cJSON_Delete(event);
|
|
return (crypto_result == NOSTR_ERROR_EVENT_INVALID_ID);
|
|
}
|
|
|
|
// Test 9: Invalid signature (cryptographic test)
|
|
int test_invalid_signature(void) {
|
|
print_test_header("Invalid Signature - Cryptographic Test");
|
|
|
|
// Take valid event but change the signature to a structurally valid but cryptographically wrong signature
|
|
const char* event_json = "{"
|
|
"\"kind\":1,"
|
|
"\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
|
|
"\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
|
|
"\"created_at\":1755599338,"
|
|
"\"tags\":[],"
|
|
"\"content\":\"Test event 1\","
|
|
"\"sig\":\"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\"" // Wrong but valid hex
|
|
"}";
|
|
|
|
printf("Input Event JSON (signature changed to invalid hex):\n%s\n\n", event_json);
|
|
|
|
cJSON* event = cJSON_Parse(event_json);
|
|
if (!event) {
|
|
printf("❌ JSON Parse Error\n");
|
|
return 0;
|
|
}
|
|
|
|
// Structure should pass
|
|
int structure_result = nostr_validate_event_structure(event);
|
|
printf("Structure validation - Expected: NOSTR_SUCCESS (0)\n");
|
|
printf("Structure validation - Actual: %d (%s)\n", structure_result, nostr_strerror(structure_result));
|
|
|
|
if (structure_result != NOSTR_SUCCESS) {
|
|
printf("❌ Unexpected structure validation failure\n");
|
|
cJSON_Delete(event);
|
|
return 0;
|
|
}
|
|
|
|
// Crypto should fail
|
|
int crypto_result = nostr_verify_event_signature(event);
|
|
printf("Crypto verification - Expected: NOSTR_ERROR_EVENT_INVALID_SIGNATURE (-33)\n");
|
|
printf("Crypto verification - Actual: %d (%s)\n", crypto_result, nostr_strerror(crypto_result));
|
|
|
|
cJSON_Delete(event);
|
|
return (crypto_result == NOSTR_ERROR_EVENT_INVALID_SIGNATURE);
|
|
}
|
|
|
|
// Test 10: Integration test with our own event creation
|
|
int test_integration_with_event_creation(void) {
|
|
print_test_header("Integration Test - Validate Our Own Created Event");
|
|
|
|
// Create a test event using our existing function
|
|
const char* private_key_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe";
|
|
unsigned char private_key[32];
|
|
nostr_hex_to_bytes(private_key_hex, private_key, 32);
|
|
|
|
cJSON* tags = cJSON_CreateArray();
|
|
cJSON* tag = cJSON_CreateArray();
|
|
cJSON_AddItemToArray(tag, cJSON_CreateString("test"));
|
|
cJSON_AddItemToArray(tag, cJSON_CreateString("integration"));
|
|
cJSON_AddItemToArray(tags, tag);
|
|
|
|
printf("Creating event with our library...\n");
|
|
cJSON* created_event = nostr_create_and_sign_event(1, "Integration test message", tags, private_key, time(NULL));
|
|
|
|
if (!created_event) {
|
|
printf("❌ Event creation failed\n");
|
|
cJSON_Delete(tags);
|
|
return 0;
|
|
}
|
|
|
|
char* event_str = cJSON_Print(created_event);
|
|
printf("Created Event JSON:\n%s\n\n", event_str);
|
|
free(event_str);
|
|
|
|
printf("Validating our created event...\n");
|
|
int result = nostr_validate_event(created_event);
|
|
printf("Expected: NOSTR_SUCCESS (0)\n");
|
|
printf("Actual: %d (%s)\n", result, nostr_strerror(result));
|
|
|
|
if (result == NOSTR_SUCCESS) {
|
|
printf("✅ Our library creates valid events that pass validation\n");
|
|
} else {
|
|
printf("❌ Our own created event failed validation\n");
|
|
}
|
|
|
|
cJSON_Delete(created_event);
|
|
return (result == NOSTR_SUCCESS);
|
|
}
|
|
|
|
int main(void) {
|
|
printf("=== NIP-01 Event Validation Test Suite ===\n");
|
|
printf("Following TESTS POLICY: Shows expected vs actual values, prints entire JSON events\n");
|
|
|
|
// Initialize crypto library
|
|
if (nostr_init() != NOSTR_SUCCESS) {
|
|
printf("❌ Failed to initialize nostr library\n");
|
|
return 1;
|
|
}
|
|
|
|
int all_passed = 1;
|
|
int test_result;
|
|
|
|
// Valid event tests
|
|
test_result = test_valid_event_1();
|
|
print_test_result(test_result, "Valid Event from nak - Basic");
|
|
if (!test_result) all_passed = 0;
|
|
|
|
test_result = test_valid_event_with_tags();
|
|
print_test_result(test_result, "Valid Event with Tags");
|
|
if (!test_result) all_passed = 0;
|
|
|
|
test_result = test_valid_metadata_event();
|
|
print_test_result(test_result, "Valid Metadata Event (Kind 0)");
|
|
if (!test_result) all_passed = 0;
|
|
|
|
// Invalid structure tests
|
|
test_result = test_missing_id_field();
|
|
print_test_result(test_result, "Missing Required Field - ID");
|
|
if (!test_result) all_passed = 0;
|
|
|
|
test_result = test_invalid_pubkey_length();
|
|
print_test_result(test_result, "Invalid Pubkey Length");
|
|
if (!test_result) all_passed = 0;
|
|
|
|
test_result = test_invalid_kind_negative();
|
|
print_test_result(test_result, "Invalid Kind - Negative Value");
|
|
if (!test_result) all_passed = 0;
|
|
|
|
test_result = test_invalid_tags_not_array();
|
|
print_test_result(test_result, "Invalid Tags - Not Array");
|
|
if (!test_result) all_passed = 0;
|
|
|
|
// Invalid cryptography tests
|
|
test_result = test_wrong_event_id();
|
|
print_test_result(test_result, "Wrong Event ID - Cryptographic Test");
|
|
if (!test_result) all_passed = 0;
|
|
|
|
test_result = test_invalid_signature();
|
|
print_test_result(test_result, "Invalid Signature - Cryptographic Test");
|
|
if (!test_result) all_passed = 0;
|
|
|
|
// Integration test
|
|
test_result = test_integration_with_event_creation();
|
|
print_test_result(test_result, "Integration Test - Validate Our Own Created Event");
|
|
if (!test_result) all_passed = 0;
|
|
|
|
// Summary
|
|
printf("\n=== TEST SUMMARY ===\n");
|
|
printf("Total tests: %d\n", test_count);
|
|
printf("Passed: %d\n", passed_tests);
|
|
printf("Failed: %d\n", test_count - passed_tests);
|
|
|
|
if (all_passed) {
|
|
printf("🎉 ALL TESTS PASSED! Event validation implementation is working correctly.\n");
|
|
} else {
|
|
printf("❌ SOME TESTS FAILED. Please review the output above.\n");
|
|
}
|
|
|
|
nostr_cleanup();
|
|
return all_passed ? 0 : 1;
|
|
}
|