From bcd885c09f6b03750cd50fa7d9459441bcd78dfd Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Date: Mon, 15 Jun 2026 13:17:25 +0200 Subject: [PATCH] feat(commands): Expose commands to module (#33) * Expose commands to module * Add missing tests stubs --- flake.lock | 15 +- flake.nix | 3 +- src/logos_blockchain_module.cpp | 175 ++++++++++++++++++++++ src/logos_blockchain_module.h | 39 +++++ tests/CMakeLists.txt | 2 + tests/event_stubs.cpp | 17 +++ tests/mocks/mock_logos_blockchain.cpp | 72 +++++++++ tests/stubs/logos_blockchain.h | 36 +++++ tests/test_blockchain.cpp | 201 ++++++++++++++++++++++++++ 9 files changed, 552 insertions(+), 8 deletions(-) create mode 100644 tests/event_stubs.cpp diff --git a/flake.lock b/flake.lock index 2e5d1c5..909afae 100644 --- a/flake.lock +++ b/flake.lock @@ -24,16 +24,16 @@ "rust-rapidsnark": "rust-rapidsnark" }, "locked": { - "lastModified": 1781108021, - "narHash": "sha256-YOJR3TMNHgQedoN7ftga+EP3VzWkCVMW59xYTAMF/pU=", + "lastModified": 1781288172, + "narHash": "sha256-4anGBb7slL6WfMCGlI4BmLAjim7kQOMD/qv4FCPJwII=", "owner": "logos-blockchain", "repo": "logos-blockchain", - "rev": "feddb4616512bf7954b503d8de4287d2ed59c4ba", + "rev": "86e24cfd985b6341e96bba76ecad36ff379bd243", "type": "github" }, "original": { "owner": "logos-blockchain", - "ref": "feddb4616512bf7954b503d8de4287d2ed59c4ba", + "ref": "86e24cfd985b6341e96bba76ecad36ff379bd243", "repo": "logos-blockchain", "type": "github" } @@ -53,6 +53,7 @@ "original": { "owner": "logos-blockchain", "repo": "logos-blockchain-circuits", + "rev": "2846ee7a4cfa24458bb8063412ab2e753b344d2f", "type": "github" } }, @@ -6313,11 +6314,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1780749050, - "narHash": "sha256-3av0pIjlOWQ6rDbNOmpUSvbNnJkGORQKKjb4LtCZsIY=", + "lastModified": 1781074563, + "narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a799d3e3886da994fa307f817a6bc705ae538eeb", + "rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 498df56..740daf9 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,8 @@ inputs = { logos-module-builder.url = "github:logos-co/logos-module-builder"; - logos-blockchain.url = "github:logos-blockchain/logos-blockchain?ref=feddb4616512bf7954b503d8de4287d2ed59c4ba"; # v0.1.3-rc.10-compatible + rust-rapidsnark nix fixes + # v0.1.3-rc.10-compatible + rust-rapidsnark nix fixes + cli commands + logos-blockchain.url = "github:logos-blockchain/logos-blockchain?ref=86e24cfd985b6341e96bba76ecad36ff379bd243"; }; outputs = inputs@{ logos-module-builder, ... }: diff --git a/src/logos_blockchain_module.cpp b/src/logos_blockchain_module.cpp index bbfa972..f1cd115 100644 --- a/src/logos_blockchain_module.cpp +++ b/src/logos_blockchain_module.cpp @@ -1,7 +1,9 @@ #include "logos_blockchain_module.h" +#include #include #include +#include #include #include #include @@ -52,6 +54,23 @@ namespace { return out; } + // Map a "ed25519" / "zk" string (case-insensitive) to the C KeyType enum. + bool parse_key_type(const std::string& s, KeyType& out) { + std::string lower = s; + boost::algorithm::trim(lower); + std::transform(lower.begin(), lower.end(), lower.begin(), + [](unsigned char c) { return std::tolower(c); }); + if (lower == "ed25519") { + out = KeyType::Ed25519; + return true; + } + if (lower == "zk") { + out = KeyType::Zk; + return true; + } + return false; + } + // Wrapper that owns data and provides GenerateConfigArgs struct OwnedGenerateConfigArgs { std::vector initial_peers_data; @@ -323,6 +342,162 @@ int LogosBlockchainModule::stop() { return 0; } +// Config management + +int LogosBlockchainModule::update_user_config(const std::string& user_config_path, const std::string& keystore_path) { + const std::string config = localPathFromFileUrl(user_config_path); + const std::string keystore = localPathFromFileUrl(keystore_path); + + const OperationStatus status = ::update_user_config(config.c_str(), keystore.c_str()); + if (!is_ok(&status)) { + fprintf(stderr, "Failed to update user config. Error: %d\n", status); + return 1; + } + return 0; +} + +int LogosBlockchainModule::migrate_user_config(const std::string& output_path, const std::string& keystore_path) { + const std::string output = localPathFromFileUrl(output_path); + const std::string keystore = localPathFromFileUrl(keystore_path); + + const OperationStatus status = ::migrate_user_config(output.c_str(), keystore.c_str()); + if (!is_ok(&status)) { + fprintf(stderr, "Failed to migrate user config. Error: %d\n", status); + return 1; + } + return 0; +} + +int LogosBlockchainModule::migrate_user_config_0_1_2( + const std::string& new_config_path, + const std::string& old_config_path, + const std::string& keystore_path +) { + const std::string new_config = localPathFromFileUrl(new_config_path); + const std::string old_config = localPathFromFileUrl(old_config_path); + const std::string keystore = localPathFromFileUrl(keystore_path); + + const OperationStatus status = + ::migrate_user_config_0_1_2(new_config.c_str(), old_config.c_str(), keystore.c_str()); + if (!is_ok(&status)) { + fprintf(stderr, "Failed to migrate 0.1.2 config. Error: %d\n", status); + return 1; + } + return 0; +} + +int LogosBlockchainModule::participate( + const std::string& config_path, + const std::string& keystore_path, + const std::string& output_dir, + const std::string& external_address +) { + const std::string config = localPathFromFileUrl(config_path); + const std::string keystore = localPathFromFileUrl(keystore_path); + const std::string output = localPathFromFileUrl(output_dir); + const char* external_address_ptr = external_address.empty() ? nullptr : external_address.c_str(); + + const OperationStatus status = + ::participate(config.c_str(), keystore.c_str(), output.c_str(), external_address_ptr); + if (!is_ok(&status)) { + fprintf(stderr, "Failed to generate participation data. Error: %d\n", status); + return 1; + } + return 0; +} + +// Keystore + +std::string LogosBlockchainModule::generate_key( + const std::string& user_config_path, + const std::string& keystore_path, + const std::string& key_type, + const std::string& key_title +) { + KeyType type{}; + if (!parse_key_type(key_type, type)) { + return "Error: Invalid key_type (expected \"ed25519\" or \"zk\")."; + } + + const std::string config = localPathFromFileUrl(user_config_path); + const std::string keystore = localPathFromFileUrl(keystore_path); + const char* key_title_ptr = key_title.empty() ? nullptr : key_title.c_str(); + + auto [value, error] = ::generate_key(config.c_str(), keystore.c_str(), type, key_title_ptr); + if (!is_ok(&error)) { + fprintf(stderr, "Failed to generate key. Error: %d\n", error); + return "Error: Failed to generate key: " + std::to_string(error); + } + + std::string result(value); + const OperationStatus free_status = free_cstring(value); + if (!is_ok(&free_status)) { + fprintf(stderr, "Failed to free key id string. Error: %d\n", free_status); + } + return result; +} + +int LogosBlockchainModule::add_key( + const std::string& user_config_path, + const std::string& keystore_path, + const std::string& key_type, + const std::string& key_hex, + const std::string& key_title +) { + KeyType type{}; + if (!parse_key_type(key_type, type)) { + fprintf(stderr, "Invalid key_type (expected \"ed25519\" or \"zk\").\n"); + return 1; + } + + const std::string config = localPathFromFileUrl(user_config_path); + const std::string keystore = localPathFromFileUrl(keystore_path); + const char* key_title_ptr = key_title.empty() ? nullptr : key_title.c_str(); + + const OperationStatus status = + ::add_key(config.c_str(), keystore.c_str(), type, key_hex.c_str(), key_title_ptr); + if (!is_ok(&status)) { + fprintf(stderr, "Failed to add key. Error: %d\n", status); + return 1; + } + return 0; +} + +int LogosBlockchainModule::remove_key( + const std::string& user_config_path, + const std::string& keystore_path, + const std::string& key_title +) { + const std::string config = localPathFromFileUrl(user_config_path); + const std::string keystore = localPathFromFileUrl(keystore_path); + + const OperationStatus status = ::remove_key(config.c_str(), keystore.c_str(), key_title.c_str()); + if (!is_ok(&status)) { + fprintf(stderr, "Failed to remove key. Error: %d\n", status); + return 1; + } + return 0; +} + +// Identity + +std::string LogosBlockchainModule::get_peer_id(const std::string& config_path) { + const std::string config = localPathFromFileUrl(config_path); + + auto [value, error] = ::get_peer_id(config.c_str()); + if (!is_ok(&error)) { + fprintf(stderr, "Failed to get peer id. Error: %d\n", error); + return "Error: Failed to get peer id: " + std::to_string(error); + } + + std::string result(value); + const OperationStatus free_status = free_cstring(value); + if (!is_ok(&free_status)) { + fprintf(stderr, "Failed to free peer id string. Error: %d\n", free_status); + } + return result; +} + // Wallet std::string LogosBlockchainModule::wallet_get_balance(const std::string& address_hex) { diff --git a/src/logos_blockchain_module.h b/src/logos_blockchain_module.h index a2ae1d5..237ea5e 100644 --- a/src/logos_blockchain_module.h +++ b/src/logos_blockchain_module.h @@ -26,6 +26,45 @@ public: int start(const std::string& config_path, const std::string& deployment); int stop(); + // Config management + int update_user_config(const std::string& user_config_path, const std::string& keystore_path); + int migrate_user_config(const std::string& output_path, const std::string& keystore_path); + int migrate_user_config_0_1_2( + const std::string& new_config_path, + const std::string& old_config_path, + const std::string& keystore_path + ); + int participate( + const std::string& config_path, + const std::string& keystore_path, + const std::string& output_dir, + const std::string& external_address + ); + + // Keystore + // key_type is "ed25519" or "zk". key_title may be empty (auto-generated). + std::string generate_key( + const std::string& user_config_path, + const std::string& keystore_path, + const std::string& key_type, + const std::string& key_title + ); + int add_key( + const std::string& user_config_path, + const std::string& keystore_path, + const std::string& key_type, + const std::string& key_hex, + const std::string& key_title + ); + int remove_key( + const std::string& user_config_path, + const std::string& keystore_path, + const std::string& key_title + ); + + // Identity + std::string get_peer_id(const std::string& config_path); + // Wallet std::string wallet_get_balance(const std::string& address_hex); std::string wallet_transfer_funds( diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 01a4ea8..337eef9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,7 @@ logos_test( TEST_SOURCES main.cpp test_blockchain.cpp + event_stubs.cpp MOCK_C_SOURCES mocks/mock_logos_blockchain.cpp EXTRA_INCLUDES @@ -35,6 +36,7 @@ if(LIBLOGOS_BLOCKCHAIN_PATH) TEST_SOURCES main.cpp test_blockchain.cpp + event_stubs.cpp EXTRA_INCLUDES ../lib EXTRA_LINK_LIBS diff --git a/tests/event_stubs.cpp b/tests/event_stubs.cpp new file mode 100644 index 0000000..f68110c --- /dev/null +++ b/tests/event_stubs.cpp @@ -0,0 +1,17 @@ +// Test-only definitions for `logos_events:` methods. +// +// In a real build the cpp-generator emits the bodies of every `logos_events:` +// method into a sidecar `_events.cpp` (each forwards through +// `emitEventImpl_`). The unit/integration test targets compile the module +// source directly without running codegen, so those bodies are absent and the +// link fails on `on_new_block_callback`'s reference to `newBlock`. +// +// `emitEventImpl_` is a no-op outside a framework-provisioned context (no emit +// callback is installed in tests), so forwarding here mirrors production +// behaviour while keeping the test build link-complete. + +#include "logos_blockchain_module.h" + +void LogosBlockchainModule::newBlock(const std::string& blockJson) { + emitEventImpl_("newBlock", nullptr); +} diff --git a/tests/mocks/mock_logos_blockchain.cpp b/tests/mocks/mock_logos_blockchain.cpp index 5374ff3..cb3439e 100644 --- a/tests/mocks/mock_logos_blockchain.cpp +++ b/tests/mocks/mock_logos_blockchain.cpp @@ -42,6 +42,78 @@ OperationStatus stop_node(LogosBlockchainNode* node) { return 0; } +OperationStatus update_user_config(const char* user_config_path, const char* keystore_path) { + LOGOS_CMOCK_RECORD("update_user_config"); + return LOGOS_CMOCK_RETURN(int, "update_user_config"); +} + +OperationStatus migrate_user_config(const char* output_path, const char* keystore_path) { + LOGOS_CMOCK_RECORD("migrate_user_config"); + return LOGOS_CMOCK_RETURN(int, "migrate_user_config"); +} + +OperationStatus migrate_user_config_0_1_2( + const char* new_config_path, + const char* old_config_path, + const char* keystore_path) +{ + LOGOS_CMOCK_RECORD("migrate_user_config_0_1_2"); + return LOGOS_CMOCK_RETURN(int, "migrate_user_config_0_1_2"); +} + +OperationStatus participate( + const char* config_path, + const char* keystore_path, + const char* output_dir, + const char* external_address) +{ + LOGOS_CMOCK_RECORD("participate"); + return LOGOS_CMOCK_RETURN(int, "participate"); +} + +StringResult generate_key( + const char* user_config_path, + const char* keystore_path, + KeyType key_type, + const char* key_title) +{ + LOGOS_CMOCK_RECORD("generate_key"); + StringResult result; + const char* id = LOGOS_CMOCK_RETURN_STRING("generate_key"); + result.value = id ? strdup(id) : nullptr; + result.error = LOGOS_CMOCK_RETURN(int, "generate_key_error"); + return result; +} + +OperationStatus add_key( + const char* user_config_path, + const char* keystore_path, + KeyType key_type, + const char* key_hex, + const char* key_title) +{ + LOGOS_CMOCK_RECORD("add_key"); + return LOGOS_CMOCK_RETURN(int, "add_key"); +} + +OperationStatus remove_key( + const char* user_config_path, + const char* keystore_path, + const char* key_title) +{ + LOGOS_CMOCK_RECORD("remove_key"); + return LOGOS_CMOCK_RETURN(int, "remove_key"); +} + +StringResult get_peer_id(const char* config_path) { + LOGOS_CMOCK_RECORD("get_peer_id"); + StringResult result; + const char* id = LOGOS_CMOCK_RETURN_STRING("get_peer_id"); + result.value = id ? strdup(id) : nullptr; + result.error = LOGOS_CMOCK_RETURN(int, "get_peer_id_error"); + return result; +} + OperationStatus subscribe_to_new_blocks(LogosBlockchainNode* node, BlockCallback callback) { LOGOS_CMOCK_RECORD("subscribe_to_new_blocks"); return LOGOS_CMOCK_RETURN(int, "subscribe_to_new_blocks"); diff --git a/tests/stubs/logos_blockchain.h b/tests/stubs/logos_blockchain.h index 22d5f62..6eb1b3b 100644 --- a/tests/stubs/logos_blockchain.h +++ b/tests/stubs/logos_blockchain.h @@ -30,6 +30,9 @@ typedef enum { Devnet } WellKnownDeployment; // Consensus state enum typedef enum { Bootstrapping, Online } State; +// Key type for generate_key / add_key +typedef enum { Ed25519, Zk } KeyType; + // Deployment configuration typedef struct { DeploymentType deployment_type; @@ -97,6 +100,39 @@ NodeResult start_lb_node(const char* config_path, const char* deployment); OperationStatus stop_node(LogosBlockchainNode* node); OperationStatus subscribe_to_new_blocks(LogosBlockchainNode* node, BlockCallback callback); +// Config management +OperationStatus update_user_config(const char* user_config_path, const char* keystore_path); +OperationStatus migrate_user_config(const char* output_path, const char* keystore_path); +OperationStatus migrate_user_config_0_1_2( + const char* new_config_path, + const char* old_config_path, + const char* keystore_path); +OperationStatus participate( + const char* config_path, + const char* keystore_path, + const char* output_dir, + const char* external_address); + +// Keystore +StringResult generate_key( + const char* user_config_path, + const char* keystore_path, + KeyType key_type, + const char* key_title); +OperationStatus add_key( + const char* user_config_path, + const char* keystore_path, + KeyType key_type, + const char* key_hex, + const char* key_title); +OperationStatus remove_key( + const char* user_config_path, + const char* keystore_path, + const char* key_title); + +// Identity +StringResult get_peer_id(const char* config_path); + // Wallet BalanceResult get_balance(LogosBlockchainNode* node, const uint8_t* address, const void* reserved); TransferHashResult transfer_funds(LogosBlockchainNode* node, const TransferFundsArguments* args); diff --git a/tests/test_blockchain.cpp b/tests/test_blockchain.cpp index 9f22d0f..e6c8f59 100644 --- a/tests/test_blockchain.cpp +++ b/tests/test_blockchain.cpp @@ -727,3 +727,204 @@ LOGOS_TEST(get_cryptarchia_info_returns_error_on_ffi_failure) { LOGOS_ASSERT_TRUE(starts_with(module->get_cryptarchia_info(), "Error:")); delete module; } + +// ============================================================================ +// Config management (operate on file paths, no running node required) +// ============================================================================ + +LOGOS_TEST(update_user_config_returns_0_on_success) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("update_user_config").returns(0); + + LOGOS_ASSERT_EQ(module.update_user_config("/tmp/config.yaml", "/tmp/keystore.yaml"), 0); + LOGOS_ASSERT(t.cFunctionCalled("update_user_config")); +} + +LOGOS_TEST(update_user_config_returns_1_on_failure) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("update_user_config").returns(1); + + LOGOS_ASSERT_EQ(module.update_user_config("/tmp/config.yaml", "/tmp/keystore.yaml"), 1); +} + +LOGOS_TEST(migrate_user_config_returns_0_on_success) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("migrate_user_config").returns(0); + + LOGOS_ASSERT_EQ(module.migrate_user_config("/tmp/out.yaml", "/tmp/keystore.yaml"), 0); + LOGOS_ASSERT(t.cFunctionCalled("migrate_user_config")); +} + +LOGOS_TEST(migrate_user_config_returns_1_on_failure) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("migrate_user_config").returns(1); + + LOGOS_ASSERT_EQ(module.migrate_user_config("/tmp/out.yaml", "/tmp/keystore.yaml"), 1); +} + +LOGOS_TEST(migrate_user_config_0_1_2_returns_0_on_success) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("migrate_user_config_0_1_2").returns(0); + + LOGOS_ASSERT_EQ(module.migrate_user_config_0_1_2("/tmp/new.yaml", "/tmp/old.yaml", "/tmp/keystore.yaml"), 0); + LOGOS_ASSERT(t.cFunctionCalled("migrate_user_config_0_1_2")); +} + +LOGOS_TEST(migrate_user_config_0_1_2_returns_1_on_failure) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("migrate_user_config_0_1_2").returns(1); + + LOGOS_ASSERT_EQ(module.migrate_user_config_0_1_2("/tmp/new.yaml", "/tmp/old.yaml", "/tmp/keystore.yaml"), 1); +} + +LOGOS_TEST(participate_returns_0_on_success) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("participate").returns(0); + + LOGOS_ASSERT_EQ(module.participate("/tmp/config.yaml", "/tmp/keystore.yaml", "/tmp/out", ""), 0); + LOGOS_ASSERT(t.cFunctionCalled("participate")); +} + +LOGOS_TEST(participate_returns_1_on_failure) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("participate").returns(1); + + LOGOS_ASSERT_EQ(module.participate("/tmp/config.yaml", "/tmp/keystore.yaml", "/tmp/out", "1.2.3.4"), 1); +} + +// ============================================================================ +// Keystore (generate_key / add_key / remove_key) +// ============================================================================ + +LOGOS_TEST(generate_key_returns_id_on_success) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("generate_key").returns("key-abc123"); + t.mockCFunction("generate_key_error").returns(0); + + std::string result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ed25519", ""); + LOGOS_ASSERT_EQ(result, std::string("key-abc123")); + LOGOS_ASSERT(t.cFunctionCalled("generate_key")); + LOGOS_ASSERT(t.cFunctionCalled("free_cstring")); +} + +LOGOS_TEST(generate_key_accepts_zk_type) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("generate_key").returns("zk-key"); + t.mockCFunction("generate_key_error").returns(0); + + std::string result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ZK", "my-title"); + LOGOS_ASSERT_EQ(result, std::string("zk-key")); +} + +LOGOS_TEST(generate_key_rejects_invalid_key_type) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + std::string result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "rsa", ""); + LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); + LOGOS_ASSERT_TRUE(contains(result, "key_type")); + LOGOS_ASSERT_FALSE(t.cFunctionCalled("generate_key")); +} + +LOGOS_TEST(generate_key_returns_error_on_ffi_failure) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("generate_key_error").returns(1); + + std::string result = module.generate_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ed25519", ""); + LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); +} + +LOGOS_TEST(add_key_returns_0_on_success) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("add_key").returns(0); + + LOGOS_ASSERT_EQ(module.add_key("/tmp/config.yaml", "/tmp/keystore.yaml", "ed25519", VALID_HEX, ""), 0); + LOGOS_ASSERT(t.cFunctionCalled("add_key")); +} + +LOGOS_TEST(add_key_rejects_invalid_key_type) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + LOGOS_ASSERT_EQ(module.add_key("/tmp/config.yaml", "/tmp/keystore.yaml", "bogus", VALID_HEX, ""), 1); + LOGOS_ASSERT_FALSE(t.cFunctionCalled("add_key")); +} + +LOGOS_TEST(add_key_returns_1_on_failure) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("add_key").returns(1); + + LOGOS_ASSERT_EQ(module.add_key("/tmp/config.yaml", "/tmp/keystore.yaml", "zk", VALID_HEX, "title"), 1); +} + +LOGOS_TEST(remove_key_returns_0_on_success) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("remove_key").returns(0); + + LOGOS_ASSERT_EQ(module.remove_key("/tmp/config.yaml", "/tmp/keystore.yaml", "my-key"), 0); + LOGOS_ASSERT(t.cFunctionCalled("remove_key")); +} + +LOGOS_TEST(remove_key_returns_1_on_failure) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("remove_key").returns(1); + + LOGOS_ASSERT_EQ(module.remove_key("/tmp/config.yaml", "/tmp/keystore.yaml", "my-key"), 1); +} + +// ============================================================================ +// Identity (get_peer_id) +// ============================================================================ + +LOGOS_TEST(get_peer_id_returns_id_on_success) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("get_peer_id").returns("12D3KooWPeerId"); + t.mockCFunction("get_peer_id_error").returns(0); + + std::string result = module.get_peer_id("/tmp/config.yaml"); + LOGOS_ASSERT_EQ(result, std::string("12D3KooWPeerId")); + LOGOS_ASSERT(t.cFunctionCalled("get_peer_id")); + LOGOS_ASSERT(t.cFunctionCalled("free_cstring")); +} + +LOGOS_TEST(get_peer_id_returns_error_on_ffi_failure) { + auto t = LogosTestContext("blockchain_module"); + LogosBlockchainModule module; + + t.mockCFunction("get_peer_id_error").returns(1); + + std::string result = module.get_peer_id("/tmp/config.yaml"); + LOGOS_ASSERT_TRUE(starts_with(result, "Error:")); +}