Files
n_signer/tests/test_dispatcher.c
Laan Tungir 268b33b6d3 first
2026-05-02 12:31:26 -04:00

159 lines
5.7 KiB
C

#include "../src/dispatcher.h"
#include "../src/enforcement.h"
#include "../src/role_table.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int g_passes = 0;
static int g_total = 0;
static void check_condition(const char *name, int condition) {
g_total++;
if (condition) {
printf("PASS: %s\n", name);
g_passes++;
} else {
printf("FAIL: %s\n", name);
}
}
static int response_has(const char *response, const char *needle) {
return (response != NULL && needle != NULL && strstr(response, needle) != NULL);
}
static role_entry_t make_nostr_entry(const char *name, int idx) {
role_entry_t e;
memset(&e, 0, sizeof(e));
strncpy(e.name, name, sizeof(e.name) - 1);
strncpy(e.purpose_str, "nostr", sizeof(e.purpose_str) - 1);
strncpy(e.curve_str, "secp256k1", sizeof(e.curve_str) - 1);
e.purpose = role_purpose_from_str(e.purpose_str);
e.curve = role_curve_from_str(e.curve_str);
e.selector_type = SELECTOR_NOSTR_INDEX;
e.nostr_index = idx;
e.derived = 0;
return e;
}
static role_entry_t make_path_entry(const char *name, const char *purpose, const char *curve, const char *path) {
role_entry_t e;
memset(&e, 0, sizeof(e));
strncpy(e.name, name, sizeof(e.name) - 1);
strncpy(e.purpose_str, purpose, sizeof(e.purpose_str) - 1);
strncpy(e.curve_str, curve, sizeof(e.curve_str) - 1);
strncpy(e.role_path, path, sizeof(e.role_path) - 1);
e.purpose = role_purpose_from_str(e.purpose_str);
e.curve = role_curve_from_str(e.curve_str);
e.selector_type = SELECTOR_ROLE_PATH;
e.nostr_index = -1;
e.derived = 0;
return e;
}
int main(void) {
role_table_t table;
role_entry_t main_role;
role_entry_t ssh_role;
mnemonic_state_t mnemonic;
dispatcher_ctx_t dispatcher;
const char *valid_12 = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
char *resp;
role_table_init(&table);
main_role = make_nostr_entry("main", 0);
ssh_role = make_path_entry("ssh_key", "ssh", "ed25519", "m/44'/822'/0'/0/0");
role_table_add(&table, &main_role);
role_table_add(&table, &ssh_role);
mnemonic_init(&mnemonic);
mnemonic_load(&mnemonic, valid_12);
dispatcher_init(&dispatcher, &table, &mnemonic);
/* 1. Valid get_public_key no selector -> default main, not_yet_derived */
resp = dispatcher_handle_request(&dispatcher,
"{\"id\":\"1\",\"method\":\"get_public_key\",\"params\":[\"\"]}");
check_condition("get_public_key default role returns not_yet_derived",
response_has(resp, "\"id\":\"1\"") && response_has(resp, "\"result\":\"not_yet_derived\""));
free(resp);
/* 2. sign_event with role main */
resp = dispatcher_handle_request(&dispatcher,
"{\"id\":\"2\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"main\"}]}");
check_condition("sign_event with role=main returns stub",
response_has(resp, "\"id\":\"2\"") && response_has(resp, "\"result\":\"stub:sign_event_ok\""));
free(resp);
/* 3. sign_event with nostr_index 0 */
resp = dispatcher_handle_request(&dispatcher,
"{\"id\":\"3\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"nostr_index\":0}]}");
check_condition("sign_event with nostr_index=0 returns stub",
response_has(resp, "\"id\":\"3\"") && response_has(resp, "\"result\":\"stub:sign_event_ok\""));
free(resp);
/* 4. ambiguous selector role + nostr_index */
resp = dispatcher_handle_request(&dispatcher,
"{\"id\":\"4\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"main\",\"nostr_index\":0}]}");
check_condition("ambiguous selector returns 1001",
response_has(resp, "\"id\":\"4\"") && response_has(resp, "\"code\":1001"));
free(resp);
/* 5. role not found */
resp = dispatcher_handle_request(&dispatcher,
"{\"id\":\"5\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"nonexistent\"}]}");
check_condition("role not found returns 1002",
response_has(resp, "\"id\":\"5\"") && response_has(resp, "\"code\":1002"));
free(resp);
/* 6. purpose mismatch: sign_event against ssh role */
resp = dispatcher_handle_request(&dispatcher,
"{\"id\":\"6\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"ssh_key\"}]}");
check_condition("purpose mismatch returns 1004",
response_has(resp, "\"id\":\"6\"") && response_has(resp, "\"code\":1004"));
free(resp);
/* 7. invalid JSON */
resp = dispatcher_handle_request(&dispatcher,
"{\"id\":\"7\",\"method\":\"sign_event\",\"params\":[\"{}\"]");
check_condition("invalid JSON returns -32700",
response_has(resp, "\"code\":-32700"));
free(resp);
/* 8. missing method */
resp = dispatcher_handle_request(&dispatcher,
"{\"id\":\"8\",\"params\":[\"{}\"]}");
check_condition("missing method returns -32600",
response_has(resp, "\"id\":\"8\"") && response_has(resp, "\"code\":-32600"));
free(resp);
/* 9. mnemonic not loaded */
mnemonic_unload(&mnemonic);
resp = dispatcher_handle_request(&dispatcher,
"{\"id\":\"9\",\"method\":\"get_public_key\",\"params\":[\"\"]}");
check_condition("mnemonic not loaded returns 1006",
response_has(resp, "\"id\":\"9\"") && response_has(resp, "\"code\":1006"));
free(resp);
mnemonic_load(&mnemonic, valid_12);
/* 10. unknown verb via enforcement */
resp = dispatcher_handle_request(&dispatcher,
"{\"id\":\"10\",\"method\":\"foo_bar\",\"params\":[\"\"]}");
check_condition("unknown verb returns -32601",
response_has(resp, "\"id\":\"10\"") && response_has(resp, "\"code\":-32601"));
free(resp);
printf("%d/10 tests passed\n", g_passes);
return (g_passes == 10 && g_total == 10) ? 0 : 1;
}