607 lines
18 KiB
C
607 lines
18 KiB
C
/*
|
|
* NIP-34 Git Stuff Test Suite
|
|
* Tests repository announcement, state, patch, PR, issue, and status event creation
|
|
* Following TESTS POLICY: Shows expected vs actual values, prints entire JSON events
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include "../nostr_core/nip034.h"
|
|
#include "../nostr_core/nip001.h"
|
|
#include "../nostr_core/nostr_common.h"
|
|
#include "../nostr_core/utils.h"
|
|
#include "../cjson/cJSON.h"
|
|
|
|
// Test private key (well-known test key, DO NOT use in production)
|
|
static const unsigned char test_private_key[32] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
|
};
|
|
|
|
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_event(cJSON* event, const char* label) {
|
|
if (!event) {
|
|
printf("%s: NULL\n", label);
|
|
return;
|
|
}
|
|
char* json = cJSON_Print(event);
|
|
printf("%s:\n%s\n", label, json ? json : "NULL");
|
|
if (json) free(json);
|
|
}
|
|
|
|
// Test 1: Create a repository announcement event
|
|
int test_repo_announcement(void) {
|
|
print_test_header("Repository Announcement (kind 30617)");
|
|
|
|
nostr_signer_t* signer = nostr_signer_local(test_private_key);
|
|
if (!signer) {
|
|
printf("❌ Failed to create signer\n");
|
|
return 0;
|
|
}
|
|
|
|
const char* clone_urls[] = {
|
|
"https://laantungir.net/grasp/npub1test/c-relay.git",
|
|
"https://gitgrasp.com/npub1test/c-relay.git",
|
|
NULL
|
|
};
|
|
const char* relay_urls[] = {
|
|
"wss://relay.laantungir.net",
|
|
"wss://relay.damus.io",
|
|
NULL
|
|
};
|
|
const char* maintainers[] = {
|
|
"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4",
|
|
NULL
|
|
};
|
|
|
|
cJSON* event = nostr_nip34_create_repo_announcement(
|
|
signer,
|
|
"c-relay",
|
|
"c-relay",
|
|
"A blazingly fast, production-ready Nostr relay",
|
|
"https://git.laantungir.net/laantungir/c-relay",
|
|
clone_urls, 2,
|
|
relay_urls, 2,
|
|
"abc123def456abc123def456abc123def456abc123def456abc123def456abc123",
|
|
maintainers, 1
|
|
);
|
|
|
|
if (!event) {
|
|
printf("❌ Failed to create repo announcement event\n");
|
|
nostr_signer_free(signer);
|
|
return 0;
|
|
}
|
|
|
|
print_json_event(event, "Repository Announcement Event");
|
|
|
|
// Validate the event
|
|
int rc = nostr_validate_event(event);
|
|
printf("Validation result: %d (expected 0 = NOSTR_SUCCESS)\n", rc);
|
|
|
|
// Check kind
|
|
cJSON* kind = cJSON_GetObjectItem(event, "kind");
|
|
int kind_val = kind ? (int)cJSON_GetNumberValue(kind) : -1;
|
|
printf("Kind: %d (expected 30617)\n", kind_val);
|
|
|
|
// Check tags
|
|
cJSON* tags = cJSON_GetObjectItem(event, "tags");
|
|
int tag_count = tags ? cJSON_GetArraySize(tags) : 0;
|
|
printf("Tag count: %d (expected >= 6)\n", tag_count);
|
|
|
|
cJSON_Delete(event);
|
|
nostr_signer_free(signer);
|
|
|
|
int passed = (rc == NOSTR_SUCCESS && kind_val == 30617 && tag_count >= 6);
|
|
print_test_result(passed, "Repository Announcement");
|
|
return passed;
|
|
}
|
|
|
|
// Test 2: Create a repository state event
|
|
int test_repo_state(void) {
|
|
print_test_header("Repository State (kind 30618)");
|
|
|
|
nostr_signer_t* signer = nostr_signer_local(test_private_key);
|
|
if (!signer) {
|
|
printf("❌ Failed to create signer\n");
|
|
return 0;
|
|
}
|
|
|
|
const char* ref_names[] = {
|
|
"refs/heads/master",
|
|
"refs/heads/develop",
|
|
"refs/tags/v1.0.0",
|
|
"HEAD",
|
|
NULL
|
|
};
|
|
const char* ref_values[] = {
|
|
"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6",
|
|
"f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1",
|
|
"0011223344550011223344550011223344550011223344550011223344550011",
|
|
"ref: refs/heads/master",
|
|
NULL
|
|
};
|
|
|
|
cJSON* event = nostr_nip34_create_repo_state(
|
|
signer,
|
|
"c-relay",
|
|
ref_names, ref_values, 4
|
|
);
|
|
|
|
if (!event) {
|
|
printf("❌ Failed to create repo state event\n");
|
|
nostr_signer_free(signer);
|
|
return 0;
|
|
}
|
|
|
|
print_json_event(event, "Repository State Event");
|
|
|
|
int rc = nostr_validate_event(event);
|
|
printf("Validation result: %d (expected 0)\n", rc);
|
|
|
|
cJSON* kind = cJSON_GetObjectItem(event, "kind");
|
|
int kind_val = kind ? (int)cJSON_GetNumberValue(kind) : -1;
|
|
printf("Kind: %d (expected 30618)\n", kind_val);
|
|
|
|
cJSON_Delete(event);
|
|
nostr_signer_free(signer);
|
|
|
|
int passed = (rc == NOSTR_SUCCESS && kind_val == 30618);
|
|
print_test_result(passed, "Repository State");
|
|
return passed;
|
|
}
|
|
|
|
// Test 3: Create a patch event
|
|
int test_patch(void) {
|
|
print_test_header("Patch (kind 1617)");
|
|
|
|
nostr_signer_t* signer = nostr_signer_local(test_private_key);
|
|
if (!signer) {
|
|
printf("❌ Failed to create signer\n");
|
|
return 0;
|
|
}
|
|
|
|
const char* patch_content =
|
|
"From 1234567890abcdef1234567890abcdef12345678 Mon Sep 17 00:00:00 2024\n"
|
|
"From: Test User <test@example.com>\n"
|
|
"Date: Mon, 1 Jan 2024 12:00:00 +0000\n"
|
|
"Subject: [PATCH] Add new feature\n"
|
|
"\n"
|
|
"This is a test patch.\n"
|
|
"---\n"
|
|
" src/main.c | 2 ++\n"
|
|
" 1 file changed, 2 insertions(+)\n"
|
|
"\n"
|
|
"diff --git a/src/main.c b/src/main.c\n"
|
|
"index abc123..def456 100644\n"
|
|
"--- a/src/main.c\n"
|
|
"+++ b/src/main.c\n"
|
|
"@@ -1,3 +1,5 @@\n"
|
|
" #include <stdio.h>\n"
|
|
"+\n"
|
|
"+// New feature\n"
|
|
" int main(void) {\n"
|
|
" return 0;\n"
|
|
" }\n";
|
|
|
|
cJSON* event = nostr_nip34_create_patch(
|
|
signer,
|
|
"30617:aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4:c-relay",
|
|
"abc123def456abc123def456abc123def456abc123def456abc123def456abc123",
|
|
patch_content,
|
|
"1234567890abcdef1234567890abcdef12345678",
|
|
"abcdef1234567890abcdef1234567890abcdef12",
|
|
"-----BEGIN PGP SIGNATURE-----\n\niQEzBAABCAAdFiEE...\n-----END PGP SIGNATURE-----",
|
|
"Test User",
|
|
"test@example.com",
|
|
"1704110400",
|
|
0,
|
|
1, // is_root
|
|
1 // is_root_revision
|
|
);
|
|
|
|
if (!event) {
|
|
printf("❌ Failed to create patch event\n");
|
|
nostr_signer_free(signer);
|
|
return 0;
|
|
}
|
|
|
|
print_json_event(event, "Patch Event");
|
|
|
|
int rc = nostr_validate_event(event);
|
|
printf("Validation result: %d (expected 0)\n", rc);
|
|
|
|
cJSON* kind = cJSON_GetObjectItem(event, "kind");
|
|
int kind_val = kind ? (int)cJSON_GetNumberValue(kind) : -1;
|
|
printf("Kind: %d (expected 1617)\n", kind_val);
|
|
|
|
cJSON_Delete(event);
|
|
nostr_signer_free(signer);
|
|
|
|
int passed = (rc == NOSTR_SUCCESS && kind_val == 1617);
|
|
print_test_result(passed, "Patch");
|
|
return passed;
|
|
}
|
|
|
|
// Test 4: Create a pull request event
|
|
int test_pull_request(void) {
|
|
print_test_header("Pull Request (kind 1618)");
|
|
|
|
nostr_signer_t* signer = nostr_signer_local(test_private_key);
|
|
if (!signer) {
|
|
printf("❌ Failed to create signer\n");
|
|
return 0;
|
|
}
|
|
|
|
const char* clone_urls[] = {
|
|
"https://github.com/user/c-relay.git",
|
|
NULL
|
|
};
|
|
|
|
cJSON* event = nostr_nip34_create_pull_request(
|
|
signer,
|
|
"30617:aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4:c-relay",
|
|
"abc123def456abc123def456abc123def456abc123def456abc123def456abc123",
|
|
"Fix critical bug in relay connection handling",
|
|
"This PR fixes a race condition in the relay connection code.\n\nChanges:\n- Added mutex locking\n- Fixed timeout handling",
|
|
"fedcba9876543210fedcba9876543210fedcba98",
|
|
clone_urls, 1,
|
|
"fix-race-condition",
|
|
NULL, // no root patch event
|
|
"abcdef1234567890abcdef1234567890abcdef12" // merge base
|
|
);
|
|
|
|
if (!event) {
|
|
printf("❌ Failed to create pull request event\n");
|
|
nostr_signer_free(signer);
|
|
return 0;
|
|
}
|
|
|
|
print_json_event(event, "Pull Request Event");
|
|
|
|
int rc = nostr_validate_event(event);
|
|
printf("Validation result: %d (expected 0)\n", rc);
|
|
|
|
cJSON* kind = cJSON_GetObjectItem(event, "kind");
|
|
int kind_val = kind ? (int)cJSON_GetNumberValue(kind) : -1;
|
|
printf("Kind: %d (expected 1618)\n", kind_val);
|
|
|
|
cJSON_Delete(event);
|
|
nostr_signer_free(signer);
|
|
|
|
int passed = (rc == NOSTR_SUCCESS && kind_val == 1618);
|
|
print_test_result(passed, "Pull Request");
|
|
return passed;
|
|
}
|
|
|
|
// Test 5: Create an issue event
|
|
int test_issue(void) {
|
|
print_test_header("Issue (kind 1621)");
|
|
|
|
nostr_signer_t* signer = nostr_signer_local(test_private_key);
|
|
if (!signer) {
|
|
printf("❌ Failed to create signer\n");
|
|
return 0;
|
|
}
|
|
|
|
const char* labels[] = {
|
|
"bug",
|
|
"high-priority",
|
|
NULL
|
|
};
|
|
|
|
cJSON* event = nostr_nip34_create_issue(
|
|
signer,
|
|
"30617:aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4:c-relay",
|
|
"Connection timeout on startup",
|
|
"When starting the relay with a large database, the connection times out after 30 seconds.\n\nSteps to reproduce:\n1. Start relay with 1M events\n2. Try to connect\n3. Observe timeout",
|
|
labels, 2
|
|
);
|
|
|
|
if (!event) {
|
|
printf("❌ Failed to create issue event\n");
|
|
nostr_signer_free(signer);
|
|
return 0;
|
|
}
|
|
|
|
print_json_event(event, "Issue Event");
|
|
|
|
int rc = nostr_validate_event(event);
|
|
printf("Validation result: %d (expected 0)\n", rc);
|
|
|
|
cJSON* kind = cJSON_GetObjectItem(event, "kind");
|
|
int kind_val = kind ? (int)cJSON_GetNumberValue(kind) : -1;
|
|
printf("Kind: %d (expected 1621)\n", kind_val);
|
|
|
|
cJSON_Delete(event);
|
|
nostr_signer_free(signer);
|
|
|
|
int passed = (rc == NOSTR_SUCCESS && kind_val == 1621);
|
|
print_test_result(passed, "Issue");
|
|
return passed;
|
|
}
|
|
|
|
// Test 6: Create a status event
|
|
int test_status(void) {
|
|
print_test_header("Status (kind 1631 - Merged)");
|
|
|
|
nostr_signer_t* signer = nostr_signer_local(test_private_key);
|
|
if (!signer) {
|
|
printf("❌ Failed to create signer\n");
|
|
return 0;
|
|
}
|
|
|
|
cJSON* event = nostr_nip34_create_status(
|
|
signer,
|
|
NOSTR_NIP34_KIND_STATUS_MERGED,
|
|
"abc123def456abc123def456abc123def456abc123def456abc123def456abc123",
|
|
"30617:aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4:c-relay",
|
|
"abc123def456abc123def456abc123def456abc123def456abc123def456abc123",
|
|
"Patch has been merged to master"
|
|
);
|
|
|
|
if (!event) {
|
|
printf("❌ Failed to create status event\n");
|
|
nostr_signer_free(signer);
|
|
return 0;
|
|
}
|
|
|
|
print_json_event(event, "Status Event (Merged)");
|
|
|
|
int rc = nostr_validate_event(event);
|
|
printf("Validation result: %d (expected 0)\n", rc);
|
|
|
|
cJSON* kind = cJSON_GetObjectItem(event, "kind");
|
|
int kind_val = kind ? (int)cJSON_GetNumberValue(kind) : -1;
|
|
printf("Kind: %d (expected 1631)\n", kind_val);
|
|
|
|
cJSON_Delete(event);
|
|
nostr_signer_free(signer);
|
|
|
|
int passed = (rc == NOSTR_SUCCESS && kind_val == 1631);
|
|
print_test_result(passed, "Status");
|
|
return passed;
|
|
}
|
|
|
|
// Test 7: Create a grasp list event
|
|
int test_grasp_list(void) {
|
|
print_test_header("Grasp List (kind 10317)");
|
|
|
|
nostr_signer_t* signer = nostr_signer_local(test_private_key);
|
|
if (!signer) {
|
|
printf("❌ Failed to create signer\n");
|
|
return 0;
|
|
}
|
|
|
|
const char* grasp_urls[] = {
|
|
"wss://grasp.laantungir.net",
|
|
"wss://gitgrasp.com",
|
|
NULL
|
|
};
|
|
|
|
cJSON* event = nostr_nip34_create_grasp_list(
|
|
signer,
|
|
grasp_urls, 2
|
|
);
|
|
|
|
if (!event) {
|
|
printf("❌ Failed to create grasp list event\n");
|
|
nostr_signer_free(signer);
|
|
return 0;
|
|
}
|
|
|
|
print_json_event(event, "Grasp List Event");
|
|
|
|
int rc = nostr_validate_event(event);
|
|
printf("Validation result: %d (expected 0)\n", rc);
|
|
|
|
cJSON* kind = cJSON_GetObjectItem(event, "kind");
|
|
int kind_val = kind ? (int)cJSON_GetNumberValue(kind) : -1;
|
|
printf("Kind: %d (expected 10317)\n", kind_val);
|
|
|
|
cJSON_Delete(event);
|
|
nostr_signer_free(signer);
|
|
|
|
int passed = (rc == NOSTR_SUCCESS && kind_val == 10317);
|
|
print_test_result(passed, "Grasp List");
|
|
return passed;
|
|
}
|
|
|
|
// Test 8: Parse nostr:// URLs
|
|
int test_parse_nostr_url(void) {
|
|
print_test_header("Parse nostr:// URLs");
|
|
|
|
int all_passed = 1;
|
|
|
|
// Test 1: nostr://<npub>/<identifier>
|
|
{
|
|
char npub[256], ident[256], relay[512];
|
|
int rc = nostr_nip34_parse_nostr_url(
|
|
"nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/c-relay",
|
|
npub, ident, relay
|
|
);
|
|
printf("Test 1 - npub/identifier:\n");
|
|
printf(" Result: %d (expected 0)\n", rc);
|
|
printf(" npub: %s\n", npub);
|
|
printf(" ident: %s\n", ident);
|
|
printf(" relay: %s\n", relay);
|
|
if (rc != 0 || strlen(npub) == 0 || strlen(ident) == 0) {
|
|
printf(" ❌ FAIL\n");
|
|
all_passed = 0;
|
|
} else {
|
|
printf(" ✅ PASS\n");
|
|
}
|
|
}
|
|
|
|
// Test 2: nostr://<npub>/<relay>/<identifier>
|
|
{
|
|
char npub[256], ident[256], relay[512];
|
|
int rc = nostr_nip34_parse_nostr_url(
|
|
"nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/relay.ngit.dev/ngit",
|
|
npub, ident, relay
|
|
);
|
|
printf("Test 2 - npub/relay/identifier:\n");
|
|
printf(" Result: %d (expected 0)\n", rc);
|
|
printf(" npub: %s\n", npub);
|
|
printf(" relay: %s\n", relay);
|
|
printf(" ident: %s\n", ident);
|
|
if (rc != 0 || strlen(npub) == 0 || strlen(relay) == 0 || strlen(ident) == 0) {
|
|
printf(" ❌ FAIL\n");
|
|
all_passed = 0;
|
|
} else {
|
|
printf(" ✅ PASS\n");
|
|
}
|
|
}
|
|
|
|
// Test 3: nostr://<nip05>/<identifier>
|
|
{
|
|
char npub[256], ident[256], relay[512];
|
|
int rc = nostr_nip34_parse_nostr_url(
|
|
"nostr://danconwaydev.com/ws%3A%2F%2Flocalhost%3A7334/my-local-only-repo",
|
|
npub, ident, relay
|
|
);
|
|
printf("Test 3 - nip05/relay/identifier (percent-encoded):\n");
|
|
printf(" Result: %d (expected 0)\n", rc);
|
|
printf(" npub: %s\n", npub);
|
|
printf(" relay: %s\n", relay);
|
|
printf(" ident: %s\n", ident);
|
|
if (rc != 0 || strlen(npub) == 0 || strlen(relay) == 0 || strcmp(ident, "my-local-only-repo") != 0) {
|
|
printf(" ❌ FAIL\n");
|
|
all_passed = 0;
|
|
} else {
|
|
printf(" ✅ PASS\n");
|
|
}
|
|
}
|
|
|
|
// Test 4: Invalid URL (no nostr:// prefix)
|
|
{
|
|
char npub[256], ident[256], relay[512];
|
|
int rc = nostr_nip34_parse_nostr_url(
|
|
"https://example.com/repo.git",
|
|
npub, ident, relay
|
|
);
|
|
printf("Test 4 - invalid URL (https):\n");
|
|
printf(" Result: %d (expected -1)\n", rc);
|
|
if (rc != -1) {
|
|
printf(" ❌ FAIL\n");
|
|
all_passed = 0;
|
|
} else {
|
|
printf(" ✅ PASS\n");
|
|
}
|
|
}
|
|
|
|
print_test_result(all_passed, "Parse nostr:// URLs");
|
|
return all_passed;
|
|
}
|
|
|
|
// Test 9: Create a PR update event
|
|
int test_pr_update(void) {
|
|
print_test_header("PR Update (kind 1619)");
|
|
|
|
nostr_signer_t* signer = nostr_signer_local(test_private_key);
|
|
if (!signer) {
|
|
printf("❌ Failed to create signer\n");
|
|
return 0;
|
|
}
|
|
|
|
const char* clone_urls[] = {
|
|
"https://github.com/user/c-relay.git",
|
|
NULL
|
|
};
|
|
|
|
cJSON* event = nostr_nip34_create_pr_update(
|
|
signer,
|
|
"30617:aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4:c-relay",
|
|
"abc123def456abc123def456abc123def456abc123def456abc123def456abc123",
|
|
"pr_event_id_1234567890abcdef",
|
|
"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4",
|
|
"new_tip_commit_hash_1234567890abcdef",
|
|
clone_urls, 1,
|
|
"updated_merge_base_hash_1234567890"
|
|
);
|
|
|
|
if (!event) {
|
|
printf("❌ Failed to create PR update event\n");
|
|
nostr_signer_free(signer);
|
|
return 0;
|
|
}
|
|
|
|
print_json_event(event, "PR Update Event");
|
|
|
|
int rc = nostr_validate_event(event);
|
|
printf("Validation result: %d (expected 0)\n", rc);
|
|
|
|
cJSON* kind = cJSON_GetObjectItem(event, "kind");
|
|
int kind_val = kind ? (int)cJSON_GetNumberValue(kind) : -1;
|
|
printf("Kind: %d (expected 1619)\n", kind_val);
|
|
|
|
cJSON_Delete(event);
|
|
nostr_signer_free(signer);
|
|
|
|
int passed = (rc == NOSTR_SUCCESS && kind_val == 1619);
|
|
print_test_result(passed, "PR Update");
|
|
return passed;
|
|
}
|
|
|
|
int main(void) {
|
|
printf("========================================\n");
|
|
printf(" NIP-34 Git Stuff Test Suite\n");
|
|
printf("========================================\n");
|
|
|
|
// Initialize crypto
|
|
if (nostr_crypto_init() != 0) {
|
|
printf("❌ Failed to initialize crypto\n");
|
|
return 1;
|
|
}
|
|
|
|
// Run all tests
|
|
int results[9];
|
|
results[0] = test_repo_announcement();
|
|
results[1] = test_repo_state();
|
|
results[2] = test_patch();
|
|
results[3] = test_pull_request();
|
|
results[4] = test_issue();
|
|
results[5] = test_status();
|
|
results[6] = test_grasp_list();
|
|
results[7] = test_parse_nostr_url();
|
|
results[8] = test_pr_update();
|
|
|
|
// Summary
|
|
printf("\n========================================\n");
|
|
printf(" TEST SUMMARY\n");
|
|
printf("========================================\n");
|
|
printf("Total tests: %d\n", test_count);
|
|
printf("Passed: %d\n", passed_tests);
|
|
printf("Failed: %d\n", test_count - passed_tests);
|
|
|
|
nostr_crypto_cleanup();
|
|
|
|
if (passed_tests == test_count) {
|
|
printf("🎉 ALL TESTS PASSED! NIP-34 implementation is working correctly.\n");
|
|
return 0;
|
|
} else {
|
|
printf("❌ SOME TESTS FAILED!\n");
|
|
return 1;
|
|
}
|
|
}
|