Files
logos-storage-module/tests/test_storage.cpp

598 lines
17 KiB
C++
Raw Permalink Normal View History

2026-04-14 13:59:13 -04:00
// Unit tests for StorageModuleImpl.
2026-04-06 10:07:22 -04:00
// All libstorage C functions are mocked at link time via mock_libstorage.cpp.
2026-04-14 13:59:13 -04:00
// Async mocks invoke the callback immediately so the condvar is signalled
// before waitSync's first check.
2026-04-06 10:07:22 -04:00
#include <logos_test.h>
#include "storage_module_plugin.h"
2026-04-14 13:59:13 -04:00
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// Helper: create an impl with a mocked, successfully initialized storage context.
static StorageModuleImpl* createInitializedImpl(LogosTestContext& t) {
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_new").returns(1);
2026-04-14 13:59:13 -04:00
auto* impl = new StorageModuleImpl();
LOGOS_ASSERT_TRUE(impl->init("{\"data-dir\":\"/tmp/test\"}"));
return impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// init
2026-04-06 10:07:22 -04:00
LOGOS_TEST(init_succeeds_when_storage_new_returns_context) {
auto t = LogosTestContext("storage_module");
t.mockCFunction("storage_new").returns(1);
2026-04-14 13:59:13 -04:00
StorageModuleImpl impl;
LOGOS_ASSERT_TRUE(impl.init("{\"data-dir\":\"/tmp/test\"}"));
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_new"));
}
LOGOS_TEST(init_fails_when_storage_new_returns_null) {
auto t = LogosTestContext("storage_module");
t.mockCFunction("storage_new").returns(0);
2026-04-14 13:59:13 -04:00
StorageModuleImpl impl;
LOGOS_ASSERT_FALSE(impl.init("{\"data-dir\":\"/tmp/test\"}"));
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// version
2026-04-06 10:07:22 -04:00
LOGOS_TEST(version_returns_mocked_string) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_version").returns("1.2.3-test");
2026-04-14 13:59:13 -04:00
StdLogosResult vr = impl->version();
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(vr.success);
LOGOS_ASSERT_EQ(vr.value.get<std::string>(), std::string("1.2.3-test"));
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_version"));
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-06-24 17:14:12 +04:00
// The value is injected at build time from metadata.json; the fallback
// "0.0.0-dev" only survives when the build failed to pass the define, so
// asserting against it verifies the injection pipeline actually ran.
LOGOS_TEST(moduleVersion_is_injected_from_build) {
StorageModuleImpl impl;
std::string v = impl.moduleVersion();
LOGOS_ASSERT_FALSE(v.empty());
LOGOS_ASSERT(v != std::string("0.0.0-dev"));
}
2026-04-14 13:59:13 -04:00
// start / stop
2026-04-06 10:07:22 -04:00
LOGOS_TEST(start_returns_true_after_init) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(impl->start());
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_start"));
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
LOGOS_TEST(start_returns_false_without_init) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
StorageModuleImpl impl;
LOGOS_ASSERT_FALSE(impl.start());
2026-04-06 10:07:22 -04:00
}
LOGOS_TEST(stop_fails_without_init) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
StorageModuleImpl impl;
LOGOS_ASSERT_FALSE(impl.stop().success);
2026-04-06 10:07:22 -04:00
}
LOGOS_TEST(stop_succeeds_after_init) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(impl->stop().success);
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_stop"));
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// destroy
2026-04-06 10:07:22 -04:00
2026-06-09 12:07:59 +04:00
LOGOS_TEST(destroy_without_init_returns_error) {
2026-04-06 10:07:22 -04:00
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
StorageModuleImpl impl;
2026-06-09 12:07:59 +04:00
// destroy() must not touch the C API when there is no context
LOGOS_ASSERT_FALSE(impl.destroy().success);
LOGOS_ASSERT(!t.cFunctionCalled("storage_destroy"));
2026-04-06 10:07:22 -04:00
}
LOGOS_TEST(destroy_succeeds_after_init) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(impl->destroy().success);
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_close"));
LOGOS_ASSERT(t.cFunctionCalled("storage_destroy"));
2026-04-14 13:59:13 -04:00
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// peerId / spr / dataDir
2026-04-06 10:07:22 -04:00
LOGOS_TEST(peerId_returns_mocked_value) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_peer_id").returns("QmTestPeerId123");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->peerId();
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_EQ(r.value.get<std::string>(), std::string("QmTestPeerId123"));
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
LOGOS_TEST(spr_returns_mocked_value) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_spr").returns("spr:ABCD1234");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->spr();
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_EQ(r.value.get<std::string>(), std::string("spr:ABCD1234"));
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
LOGOS_TEST(dataDir_returns_mocked_value) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_repo").returns("/tmp/test-data");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->dataDir();
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_EQ(r.value.get<std::string>(), std::string("/tmp/test-data"));
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
LOGOS_TEST(peerId_returns_failure_without_init) {
2026-04-06 10:07:22 -04:00
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
StorageModuleImpl impl;
LOGOS_ASSERT_FALSE(impl.peerId().success);
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// debug
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_TEST(debug_returns_parsed_map) {
2026-04-06 10:07:22 -04:00
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_debug")
.returns(R"({"id":"QmNode","addrs":[],"announceAddresses":[],"table":{}})");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->debug();
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_TRUE(r.value.is_object());
LOGOS_ASSERT_FALSE(r.value.empty());
LOGOS_ASSERT_TRUE(r.value.contains("id"));
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-06-09 14:03:32 +04:00
LOGOS_TEST(debug_fails_on_invalid_json) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
2026-06-09 14:16:46 +04:00
t.mockCFunction("storage_debug").returns("not json");
2026-06-09 14:03:32 +04:00
StdLogosResult r = impl->debug();
LOGOS_ASSERT_FALSE(r.success);
impl->destroy();
delete impl;
}
2026-06-22 19:44:54 +02:00
// collectMetrics
LOGOS_TEST(collectMetrics_returns_parsed_metrics) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
t.mockCFunction("storage_get_metrics")
.returns(R"({"metrics":[{"name":"test_metric","type":"gauge","help":"Test metric","value":1.0,"labels":{}}]})");
LogosMap r = impl->collectMetrics();
LOGOS_ASSERT_TRUE(r.is_object());
LOGOS_ASSERT_TRUE(r.contains("metrics"));
LOGOS_ASSERT_TRUE(r["metrics"].is_array());
LOGOS_ASSERT_EQ(static_cast<int>(r["metrics"].size()), 1);
LOGOS_ASSERT(t.cFunctionCalled("storage_get_metrics"));
impl->destroy();
delete impl;
}
LOGOS_TEST(collectMetrics_returns_empty_metrics_on_libstorage_error) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
t.mockCFunction("storage_get_metrics").returns(1);
LogosMap r = impl->collectMetrics();
LOGOS_ASSERT_TRUE(r.is_object());
LOGOS_ASSERT_TRUE(r.contains("metrics"));
LOGOS_ASSERT_TRUE(r["metrics"].is_array());
LOGOS_ASSERT_TRUE(r["metrics"].empty());
impl->destroy();
delete impl;
}
2026-06-22 19:44:54 +02:00
LOGOS_TEST(collectMetrics_returns_empty_metrics_on_invalid_json) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
t.mockCFunction("storage_get_metrics").returns("not json");
LogosMap r = impl->collectMetrics();
LOGOS_ASSERT_TRUE(r.is_object());
LOGOS_ASSERT_TRUE(r.contains("metrics"));
LOGOS_ASSERT_TRUE(r["metrics"].is_array());
LOGOS_ASSERT_TRUE(r["metrics"].empty());
impl->destroy();
delete impl;
}
LOGOS_TEST(collectMetrics_returns_empty_metrics_when_payload_is_array) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
t.mockCFunction("storage_get_metrics").returns(R"([])");
LogosMap r = impl->collectMetrics();
LOGOS_ASSERT_TRUE(r.is_object());
LOGOS_ASSERT_TRUE(r.contains("metrics"));
LOGOS_ASSERT_TRUE(r["metrics"].is_array());
LOGOS_ASSERT_TRUE(r["metrics"].empty());
impl->destroy();
delete impl;
}
LOGOS_TEST(collectMetrics_returns_empty_metrics_when_metrics_missing) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
t.mockCFunction("storage_get_metrics").returns(R"({"other":[]})");
LogosMap r = impl->collectMetrics();
LOGOS_ASSERT_TRUE(r.is_object());
LOGOS_ASSERT_TRUE(r.contains("metrics"));
LOGOS_ASSERT_TRUE(r["metrics"].is_array());
LOGOS_ASSERT_TRUE(r["metrics"].empty());
impl->destroy();
delete impl;
}
LOGOS_TEST(collectMetrics_returns_empty_metrics_when_metrics_is_not_array) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
t.mockCFunction("storage_get_metrics").returns(R"({"metrics":{}})");
LogosMap r = impl->collectMetrics();
LOGOS_ASSERT_TRUE(r.is_object());
LOGOS_ASSERT_TRUE(r.contains("metrics"));
LOGOS_ASSERT_TRUE(r["metrics"].is_array());
LOGOS_ASSERT_TRUE(r["metrics"].empty());
impl->destroy();
delete impl;
}
2026-04-14 13:59:13 -04:00
// updateLogLevel
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_TEST(updateLogLevel_returns_true_on_success) {
2026-04-06 10:07:22 -04:00
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(impl->updateLogLevel("DEBUG").success);
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_log_level"));
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// exists
2026-04-06 10:07:22 -04:00
LOGOS_TEST(exists_returns_true_when_cid_found) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_exists").returns("true");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->exists("QmSomeCid");
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_TRUE(r.value.get<bool>());
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
LOGOS_TEST(exists_returns_false_when_cid_not_found) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_exists").returns("false");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->exists("QmMissingCid");
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_FALSE(r.value.get<bool>());
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-06-22 16:31:49 +04:00
// togglePrivateQueries
LOGOS_TEST(togglePrivateQueries_returns_previous_state) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
t.mockCFunction("storage_toggle_private_queries").returns("false");
StdLogosResult r = impl->togglePrivateQueries(true);
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_FALSE(r.value.get<bool>());
LOGOS_ASSERT(t.cFunctionCalled("storage_toggle_private_queries"));
impl->destroy();
delete impl;
}
LOGOS_TEST(togglePrivateQueries_maps_true_previous_state) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
t.mockCFunction("storage_toggle_private_queries").returns("true");
StdLogosResult r = impl->togglePrivateQueries(false);
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_TRUE(r.value.get<bool>());
impl->destroy();
delete impl;
}
2026-04-14 13:59:13 -04:00
// fetch / remove
2026-04-06 10:07:22 -04:00
LOGOS_TEST(fetch_calls_storage_fetch) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(impl->fetch("QmSomeCid").success);
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_fetch"));
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-06-28 21:06:57 +04:00
LOGOS_TEST(remove_dispatches_and_emits_event) {
logos_test::EventCapture events;
2026-04-06 10:07:22 -04:00
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(impl->remove("QmSomeCid").success);
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_delete"));
2026-06-28 21:06:57 +04:00
LOGOS_ASSERT_TRUE(events.has("storageRemoveDone"));
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// space
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_TEST(space_returns_parsed_map) {
2026-04-06 10:07:22 -04:00
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_space")
.returns(R"({"totalBlocks":100,"quotaMaxBytes":1000,"quotaUsedBytes":50,"quotaReservedBytes":10})");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->space();
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_TRUE(r.value.is_object());
LOGOS_ASSERT_FALSE(r.value.empty());
LOGOS_ASSERT_TRUE(r.value.contains("totalBlocks"));
LOGOS_ASSERT_TRUE(r.value.contains("quotaMaxBytes"));
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// manifests
2026-04-06 10:07:22 -04:00
LOGOS_TEST(manifests_returns_parsed_list) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_list")
.returns(R"([{"cid":"QmABC","manifest":{"treeCid":"QmTree","datasetSize":1024,"blockSize":64,"filename":"test.txt","mimetype":"text/plain"}}])");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->manifests();
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_TRUE(r.value.is_array());
LOGOS_ASSERT_EQ(static_cast<int>(r.value.size()), 1);
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// downloadManifest
2026-04-06 10:07:22 -04:00
2026-06-26 15:36:28 +04:00
LOGOS_TEST(downloadManifest_dispatches_and_emits_event) {
logos_test::EventCapture events;
2026-04-06 10:07:22 -04:00
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_download_manifest")
.returns(R"({"treeCid":"QmTree","datasetSize":2048,"blockSize":64,"filename":"data.bin","mimetype":"application/octet-stream"})");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->downloadManifest("QmSomeCid");
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(r.success);
2026-06-26 15:36:28 +04:00
LOGOS_ASSERT_TRUE(events.has("storageDownloadManifestDone"));
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// uploadInit / uploadFinalize / uploadCancel
2026-04-06 10:07:22 -04:00
LOGOS_TEST(uploadInit_returns_session_id) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_upload_init").returns("session-abc-123");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->uploadInit("test.txt", 65536);
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_EQ(r.value.get<std::string>(), std::string("session-abc-123"));
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
LOGOS_TEST(uploadFinalize_returns_cid) {
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
t.mockCFunction("storage_upload_finalize").returns("QmFinalCid");
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->uploadFinalize("session-abc-123");
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(r.success);
LOGOS_ASSERT_EQ(r.value.get<std::string>(), std::string("QmFinalCid"));
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_upload_finalize"));
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
LOGOS_TEST(uploadCancel_returns_true) {
2026-04-06 10:07:22 -04:00
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_ASSERT_TRUE(impl->uploadCancel("session-abc-123").success);
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_upload_cancel"));
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-04-14 13:59:13 -04:00
// uploadUrl input validation
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_TEST(uploadUrl_fails_with_nonexistent_file) {
2026-04-06 10:07:22 -04:00
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
auto* impl = createInitializedImpl(t);
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
StdLogosResult r = impl->uploadUrl("/nonexistent/path/file.txt", 65536);
LOGOS_ASSERT_FALSE(r.success);
impl->destroy();
delete impl;
}
LOGOS_TEST(uploadUrl_fails_with_zero_chunk_size) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
StdLogosResult r = impl->uploadUrl("/tmp/test.txt", 0);
LOGOS_ASSERT_FALSE(r.success);
impl->destroy();
delete impl;
}
// downloadCancel
LOGOS_TEST(downloadCancel_returns_true) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
LOGOS_ASSERT_TRUE(impl->downloadCancel("QmSomeCid").success);
2026-04-06 10:07:22 -04:00
LOGOS_ASSERT(t.cFunctionCalled("storage_download_cancel"));
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}
2026-06-09 14:16:46 +04:00
LOGOS_TEST(downloadChunks_cancels_session_when_stream_fails) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
// storage_download_init succeeds, but the stream dispatch fails: the
// already-open session must be cancelled and the call must report failure.
t.mockCFunction("storage_download_stream").returns(1);
StdLogosResult r = impl->downloadChunks("QmSomeCid", false, 65536);
LOGOS_ASSERT_FALSE(r.success);
LOGOS_ASSERT(t.cFunctionCalled("storage_download_cancel"));
impl->destroy();
delete impl;
}
2026-04-14 13:59:13 -04:00
// connect
2026-04-06 10:07:22 -04:00
2026-04-14 13:59:13 -04:00
LOGOS_TEST(connect_fails_without_init) {
2026-04-06 10:07:22 -04:00
auto t = LogosTestContext("storage_module");
2026-04-14 13:59:13 -04:00
StorageModuleImpl impl;
LOGOS_ASSERT_FALSE(impl.connect("QmPeer", {"addr1"}).success);
}
LOGOS_TEST(connect_succeeds_after_init) {
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
LOGOS_ASSERT_TRUE(impl->connect("QmPeer", {"/ip4/127.0.0.1/tcp/1234"}).success);
LOGOS_ASSERT(t.cFunctionCalled("storage_connect"));
impl->destroy();
delete impl;
}
2026-06-23 20:24:44 +02:00
// Event wiring — typed `logos_events:` methods are forwarded to
// logos_test::recordEvent by tests/storage_events_test.cpp, so tests observe
// them via EventCapture. Construct the capture before the impl so it outlives
// any background thread the impl's destructor joins.
2026-04-14 13:59:13 -04:00
LOGOS_TEST(start_emits_storageStart_event) {
2026-06-23 20:24:44 +02:00
logos_test::EventCapture events;
2026-04-14 13:59:13 -04:00
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
LOGOS_ASSERT_TRUE(impl->start());
2026-06-23 20:24:44 +02:00
LOGOS_ASSERT_TRUE(events.has("storageStart"));
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
}
LOGOS_TEST(stop_emits_storageStop_event) {
2026-06-23 20:24:44 +02:00
logos_test::EventCapture events;
2026-04-14 13:59:13 -04:00
auto t = LogosTestContext("storage_module");
auto* impl = createInitializedImpl(t);
LOGOS_ASSERT_TRUE(impl->stop().success);
2026-06-23 20:24:44 +02:00
LOGOS_ASSERT_TRUE(events.has("storageStop"));
2026-04-14 13:59:13 -04:00
impl->destroy();
delete impl;
2026-04-06 10:07:22 -04:00
}