From d18415c1e9b3a8cc0dced9c278e704be210ae76f Mon Sep 17 00:00:00 2001 From: parthod0x Date: Fri, 9 Oct 2026 00:17:15 +0530 Subject: [PATCH] Add unit tests for the sanity_check.h input validators qubic-cli has no unit tests today (test_utils.* are interactive, node-connected helpers compiled into the binary). This adds a first, self-contained gtest suite for the pure input validators in sanity_check.h: isValidIpAddress plus death tests (exit code 1) for the validators that abort on bad input (sanityCheckValidAssetName, sanityCheckTxType, checkValidEpoch, sanityCheckMainAuxStatus, sanityCheckUnitofMeasurement, sanityCheckLoggingMode, sanityCheckExtraDataSize). The suite needs neither the core submodule nor the crypto/network code: it includes only sanity_check.h and provides the two symbols that header expects from its usual include context (DEFAULT_SEED and a stubbed checkSumIdentity). Tests are opt-in via -DQUBIC_CLI_BUILD_TESTS=ON (default OFF), so the normal build and existing CI are unchanged; googletest is fetched with FetchContent. cmake -DQUBIC_CLI_BUILD_TESTS=ON -S . -B build cmake --build build --target qubic-cli-tests && ctest --test-dir build 11 tests across 8 suites, all passing. --- CMakeLists.txt | 22 +++++++ tests/sanity_check_test.cpp | 113 ++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 tests/sanity_check_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 46b1708..79ca7c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,3 +99,25 @@ target_compile_options(fourq-qubic PRIVATE -DBUILD_4Q_LIB) install(TARGETS fourq-qubic LIBRARY) install(TARGETS qubic-cli RUNTIME) + +# Opt-in unit tests (off by default, so normal and CI builds are unchanged): +# cmake -DQUBIC_CLI_BUILD_TESTS=ON -S . -B build +# cmake --build build --target qubic-cli-tests && ctest --test-dir build +option(QUBIC_CLI_BUILD_TESTS "Build qubic-cli unit tests" OFF) +if(QUBIC_CLI_BUILD_TESTS) + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip + ) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + + enable_testing() + add_executable(qubic-cli-tests tests/sanity_check_test.cpp) + target_include_directories(qubic-cli-tests PRIVATE ${CMAKE_SOURCE_DIR}) + target_link_libraries(qubic-cli-tests PRIVATE GTest::gtest_main) + + include(GoogleTest) + gtest_discover_tests(qubic-cli-tests) +endif() diff --git a/tests/sanity_check_test.cpp b/tests/sanity_check_test.cpp new file mode 100644 index 0000000..d3cc955 --- /dev/null +++ b/tests/sanity_check_test.cpp @@ -0,0 +1,113 @@ +// Unit tests for the input validators in sanity_check.h. +// +// sanity_check.h expects a few symbols to be visible before it is included (the +// CLI pulls them in via earlier headers). We provide them here so the validators +// can be tested in isolation, without dragging in the crypto / core-submodule +// dependency graph: for the str/mem calls, DEFAULT_SEED, and a stubbed +// checkSumIdentity (the identity-format tests below exit before the checksum, so +// the stub is never relied upon for a result). +#include +#include +#include + +#include "gtest/gtest.h" + +#define DEFAULT_SEED "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +static bool checkSumIdentity(const char*) { return true; } + +#include "sanity_check.h" + +TEST(IsValidIpAddress, AcceptsWellFormedIPv4) +{ + char ip[] = "192.168.1.1"; + EXPECT_TRUE(isValidIpAddress(ip)); + char zero[] = "0.0.0.0"; + EXPECT_TRUE(isValidIpAddress(zero)); + char max[] = "255.255.255.255"; + EXPECT_TRUE(isValidIpAddress(max)); +} + +TEST(IsValidIpAddress, RejectsMalformedOrOutOfRange) +{ + char over[] = "256.1.1.1"; + EXPECT_FALSE(isValidIpAddress(over)); + char neg[] = "-1.2.3.4"; + EXPECT_FALSE(isValidIpAddress(neg)); + char tooFew[] = "1.2.3"; + EXPECT_FALSE(isValidIpAddress(tooFew)); + char text[] = "not.an.ip.addr"; + EXPECT_FALSE(isValidIpAddress(text)); +} + +// The remaining validators abort the process with exit(1) on bad input and +// simply return on good input, so they are exercised with death tests. + +TEST(SanityCheckValidAssetName, AcceptsValidNames) +{ + sanityCheckValidAssetName("QX"); // upper-case start + sanityCheckValidAssetName("CFB123"); // letters + digits, <= 7 chars + SUCCEED(); +} + +TEST(SanityCheckValidAssetName, RejectsInvalidNames) +{ + EXPECT_EXIT(sanityCheckValidAssetName("qx"), ::testing::ExitedWithCode(1), ""); // lower-case start + EXPECT_EXIT(sanityCheckValidAssetName("TOOLONG1"), ::testing::ExitedWithCode(1), ""); // 8 chars + EXPECT_EXIT(sanityCheckValidAssetName("1ABC"), ::testing::ExitedWithCode(1), ""); // digit start + EXPECT_EXIT(sanityCheckValidAssetName(nullptr), ::testing::ExitedWithCode(1), ""); // null +} + +TEST(SanityCheckTxType, AcceptsKnownRejectsUnknown) +{ + sanityCheckTxType(0); + sanityCheckTxType(1); + SUCCEED(); + EXPECT_EXIT(sanityCheckTxType(2), ::testing::ExitedWithCode(1), ""); +} + +TEST(CheckValidEpoch, BoundsAreEnforced) +{ + checkValidEpoch(0); + checkValidEpoch(999); + SUCCEED(); + EXPECT_EXIT(checkValidEpoch(-1), ::testing::ExitedWithCode(1), ""); + EXPECT_EXIT(checkValidEpoch(1000), ::testing::ExitedWithCode(1), ""); +} + +TEST(SanityCheckMainAuxStatus, OnlyMainOrAux) +{ + sanityCheckMainAuxStatus("MAIN"); + sanityCheckMainAuxStatus("AUX"); + SUCCEED(); + EXPECT_EXIT(sanityCheckMainAuxStatus("FOO"), ::testing::ExitedWithCode(1), ""); + EXPECT_EXIT(sanityCheckMainAuxStatus("MAIX"), ::testing::ExitedWithCode(1), ""); + EXPECT_EXIT(sanityCheckMainAuxStatus(nullptr), ::testing::ExitedWithCode(1), ""); +} + +TEST(SanityCheckUnitOfMeasurement, AcceptsSevenChars) +{ + sanityCheckUnitofMeasurement("1234567"); // length 7 is accepted + SUCCEED(); +} + +TEST(SanityCheckUnitOfMeasurement, RejectsWrongLengthOrNull) +{ + EXPECT_EXIT(sanityCheckUnitofMeasurement("SHORT"), ::testing::ExitedWithCode(1), ""); + EXPECT_EXIT(sanityCheckUnitofMeasurement(nullptr), ::testing::ExitedWithCode(1), ""); +} + +TEST(SanityCheckLoggingMode, ZeroToTwoOnly) +{ + sanityCheckLoggingMode(0); + sanityCheckLoggingMode(2); + SUCCEED(); + EXPECT_EXIT(sanityCheckLoggingMode(3), ::testing::ExitedWithCode(1), ""); + EXPECT_EXIT(sanityCheckLoggingMode((char)-1), ::testing::ExitedWithCode(1), ""); +} + +TEST(SanityCheckExtraDataSize, RejectsOverLimit) +{ + sanityCheckExtraDataSize(1024); + SUCCEED(); + EXPECT_EXIT(sanityCheckExtraDataSize(1025), ::testing::ExitedWithCode(1), ""); +}