From 431b8a2e1462c43b6896ed523606005a194a289b Mon Sep 17 00:00:00 2001 From: Qijun Niu Date: Thu, 5 Feb 2026 10:41:12 +0800 Subject: [PATCH] - Add comprehensive input validation tests for IPv4/IPv6 addresses - Add query operation tests including exact match and range queries - Add overflow and boundary condition tests for both protocols - Ensure robust error handling for invalid IP address formats --- .DS_Store | Bin 0 -> 8196 bytes contrib/.DS_Store | Bin 0 -> 6148 bytes tests/.DS_Store | Bin 0 -> 6148 bytes timeplus/.DS_Store | Bin 0 -> 6148 bytes ut/ipv4_supported_ut.cpp | 201 +++++++++++++++++++++++++++++++++++++++ ut/ipv6_supported_ut.cpp | 156 ++++++++++++++++++++++++++++++ 6 files changed, 357 insertions(+) create mode 100644 .DS_Store create mode 100644 contrib/.DS_Store create mode 100644 tests/.DS_Store create mode 100644 timeplus/.DS_Store create mode 100644 ut/ipv4_supported_ut.cpp create mode 100644 ut/ipv6_supported_ut.cpp diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3822164dab2f9f5ef68c5878209bd52c0d29b5ae GIT binary patch literal 8196 zcmeHMF>4e-6n>Ljl8Yjwun2_6Hfkf16!vSpKne@tS_{3)aVOs1hIl91tstTx#M<7% zLX2QxWos$e+6Daqc4-2BZ)R?9-tOL!N)+F~%vb~lMuiKvdw zcI7OFipGBKr8ehhF2WkbQ@^{}Y^TXy38@pRfGVI0r~;~hD)6@`fOj^x`jq#+^XjMy zr~?0`0{nc4(Amb`X3pA22L@XLz}7Hr8?IOQ0ygqC_BL}C9yH@ZVO^;4C5Ca~n2$Uz z_BM0Yg_H5chw+(>uTYH5j`br8ClfoXqbi^ZlojCGy+&85OZR9f>h~+})LO+rYJ32&JWQUh2_^S|U5e$7Mvw;rY{-Z#;*>I!QWV zG94xOko{z_KrQOffZEiPTy~y;OW+N8{Cc1B5QqtfYD8uJHYIeI?o&tRUr%iQ+#BZq z@j1vJASU^%5f%BDaOWxRe@Oc{4RSwj+&pLZLm)yPkC!ix&qQIJa6hJ_1fQJ^B*Pn_ zLyw?Gg6-+!`hisC_E8lkfOj>G2vFvNA_eX+Nh Sv+$tlhX5;s4ywQ(Rp2)boGAhT literal 0 HcmV?d00001 diff --git a/contrib/.DS_Store b/contrib/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3bc2f561d5d5bfda51044911c28c240e98c3e506 GIT binary patch literal 6148 zcmeHKy-ve05I#3Ssz8Dc3`h)QWkD+90YVj?pf3Pzp)E?)iWVd~Wn%zdfB~_vva+)v zF|qI*@CbZoo77f9M?mOKvcKeheCMxbY+4Tee5N{21zst-?B_v<&$yS}|M z*FNewXG7%!p*U39)TarZQx6r4spsXVqm42jw~pvNucNTmU^uI1o}X72-EmzD)J0W? zsA%FkeLat9Zi$Zaid-nZL4Z*HCKylD0E z1+Q2XzyFl?N*yob)^Y0E+??BwdCol6-D#QK%UkDuBl61(pl7pGmki1*3dvR4dc!l93>Uu-cmXu?V9 z%vi@}R`!OXY-drCb|)1Zlvx-M27Cqr^IGHnzxVz9-w%>EVL%x8R}82yX(kP9$@bR5 w=D62J82cD3?3WqTA((7C)(_l@*D$oe7V!fxwwM`&1|ok1Gz~Hd13${ZCqC1or2qf` literal 0 HcmV?d00001 diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f2f83affa6cb1b8f2f6173ccba13168fc98ef573 GIT binary patch literal 6148 zcmeHKOHRWu5FMAQDiDc^6;e0801~}Hh{6ea0q94iQbHm=3nUh~0ekjb19iuO6S3os z#|m*np(}*YjATD&JY&DSM46a~Oma6H5e~*uYXqD&#DqLn%tK8TPz6+hZ52SxX6x?@YOe~Y0;)i*0Dm7W8e{CS5_F#qbnXZM z?85B~V?Il8j`bLOtOPLwGm;9FRO62rM$+N8J}&lH2}(K{w=>r9I~#vOF>ZJGtqUg; z3u>aC>>O~|AhIsnl8(0=5smCIpAAOiA}?E> zYWS-R$g_)(;R*{(gXd?yKAUTKk*3+a$Y|@G-#)#ZJiniY>yuyC%bUi#(UtcN`xv9Z z982W5!y?Guxn8wBnl*g9)itc`lNUdgZ1)Q5sidAg+~WZyrfap}@giJp)={qNIvVlK z8a8_b`SB|GRg?i`KpEH-1MJx%y+emODg(-ZGEg%h--nngn0Txl+D``udjudxG+W`^ z{@W2_F%T1vl|x!6#)T4HsPRV( +#include + +#include "ut/utils.h" +#include "ut/value_generators.h" + +#include + +using namespace timeplus; + +// Test inserting and selecting IPv4 addresses +TEST(IPv4Supported, InsertSelect) { + // Helper to get environment variable or default + auto getEither = [](const char* a, const char* b, const char* def) { + const char* v = std::getenv(a); + if (v) return std::string(v); + v = std::getenv(b); + if (v) return std::string(v); + return std::string(def); + }; + + // Get connection parameters + const std::string host = getEither("timeplus_HOST", "TIMEPLUS_HOST", "localhost"); + const std::string port = getEither("timeplus_PORT", "TIMEPLUS_PORT", "8463"); + const std::string user = getEither("timeplus_USER", "TIMEPLUS_USER", "default"); + const std::string password = getEither("timeplus_PASSWORD", "TIMEPLUS_PASSWORD", ""); + const std::string db = getEither("timeplus_DB", "TIMEPLUS_DB", "default"); + + const auto opts = ClientOptions() + .SetHost(host) + .SetPort(static_cast(std::stoul(port))) + .SetUser(user) + .SetPassword(password) + .SetDefaultDatabase(db); + + Client client(opts); + + // Prepare test table + client.Execute("DROP TEMPORARY STREAM IF EXISTS test_timeplus_cpp_ipv4_ut;"); + client.Execute("CREATE TEMPORARY STREAM IF NOT EXISTS test_timeplus_cpp_ipv4_ut (v4 ipv4) ENGINE = Memory"); + + Block b; + auto v4 = std::make_shared(); + auto ips = MakeIPv4s(); // Generate test IPv4 addresses + for (const auto & ip : ips) { + v4->Append(ip); + } + + b.AppendColumn("v4", v4); + client.Insert("test_timeplus_cpp_ipv4_ut", b); + + // Validate inserted data + size_t row = 0; + client.Select("SELECT v4 FROM test_timeplus_cpp_ipv4_ut", [&ips, &row](const Block& block) { + if (block.GetRowCount() == 0) return; + ASSERT_EQ(1U, block.GetColumnCount()); + for (size_t c = 0; c < block.GetRowCount(); ++c, ++row) { + auto col = block[0]->As(); + auto got = col->At(c); + EXPECT_EQ(ips[row].s_addr, got.s_addr); + } + }); + EXPECT_EQ(ips.size(), row); +} + +// Test nullable IPv4 column: insert a valid and a null value, then check nullability +TEST(IPv4Supported, NullableInsertSelect) { + // Type name must be lowercase: nullable(ipv4) + auto getEither = [](const char* a, const char* b, const char* def) { + const char* v = std::getenv(a); + if (v) return std::string(v); + v = std::getenv(b); + if (v) return std::string(v); + return std::string(def); + }; + const std::string host = getEither("timeplus_HOST", "TIMEPLUS_HOST", "localhost"); + const std::string port = getEither("timeplus_PORT", "TIMEPLUS_PORT", "8463"); + const std::string user = getEither("timeplus_USER", "TIMEPLUS_USER", "default"); + const std::string password = getEither("timeplus_PASSWORD", "TIMEPLUS_PASSWORD", ""); + const std::string db = getEither("timeplus_DB", "TIMEPLUS_DB", "default"); + const auto opts = ClientOptions().SetHost(host).SetPort(static_cast(std::stoul(port))).SetUser(user).SetPassword(password).SetDefaultDatabase(db); + Client client(opts); + client.Execute("DROP TEMPORARY STREAM IF EXISTS test_timeplus_cpp_ipv4_nullable;"); + client.Execute("CREATE TEMPORARY STREAM IF NOT EXISTS test_timeplus_cpp_ipv4_nullable (v4 nullable(ipv4)) ENGINE = Memory"); + Block b; + auto v4 = std::make_shared(); + v4->Append("127.0.0.1"); // valid IPv4 + auto nulls = std::make_shared(); + nulls->Append(0); // not null + v4->Append(0); // 0.0.0.0 as null + nulls->Append(1); // null + b.AppendColumn("v4", std::make_shared(v4, nulls)); + client.Insert("test_timeplus_cpp_ipv4_nullable", b); + size_t row = 0; + client.Select("SELECT v4 FROM test_timeplus_cpp_ipv4_nullable", [&row](const Block& block) { + if (block.GetRowCount() == 0) return; + ASSERT_EQ(1U, block.GetColumnCount()); + for (size_t c = 0; c < block.GetRowCount(); ++c, ++row) { + auto col = block[0]->As(); + if (row == 0) { + EXPECT_FALSE(col->IsNull(c)); // Should not be null + } else { + EXPECT_TRUE(col->IsNull(c)); // Should be null + } + } + }); + EXPECT_EQ(2U, row); +} + +// Test IPv4 boundary values: minimum and maximum +TEST(IPv4Supported, BoundaryValues) { + auto getEither = [](const char* a, const char* b, const char* def) { + const char* v = std::getenv(a); + if (v) return std::string(v); + v = std::getenv(b); + if (v) return std::string(v); + return std::string(def); + }; + const std::string host = getEither("timeplus_HOST", "TIMEPLUS_HOST", "localhost"); + const std::string port = getEither("timeplus_PORT", "TIMEPLUS_PORT", "8463"); + const std::string user = getEither("timeplus_USER", "TIMEPLUS_USER", "default"); + const std::string password = getEither("timeplus_PASSWORD", "TIMEPLUS_PASSWORD", ""); + const std::string db = getEither("timeplus_DB", "TIMEPLUS_DB", "default"); + const auto opts = ClientOptions().SetHost(host).SetPort(static_cast(std::stoul(port))).SetUser(user).SetPassword(password).SetDefaultDatabase(db); + Client client(opts); + client.Execute("DROP TEMPORARY STREAM IF EXISTS test_timeplus_cpp_ipv4_boundary;"); + client.Execute("CREATE TEMPORARY STREAM IF NOT EXISTS test_timeplus_cpp_ipv4_boundary (v4 ipv4) ENGINE = Memory"); + Block b; + auto v4 = std::make_shared(); + v4->Append("0.0.0.0"); // min IPv4 + v4->Append("255.255.255.255"); // max IPv4 + b.AppendColumn("v4", v4); + client.Insert("test_timeplus_cpp_ipv4_boundary", b); + std::vector expected = {"0.0.0.0", "255.255.255.255"}; + size_t row = 0; + client.Select("SELECT v4 FROM test_timeplus_cpp_ipv4_boundary", [&row, &expected](const Block& block) { + if (block.GetRowCount() == 0) return; + ASSERT_EQ(1U, block.GetColumnCount()); + for (size_t c = 0; c < block.GetRowCount(); ++c, ++row) { + auto col = block[0]->As(); + EXPECT_EQ(expected[row], col->AsString(c)); // Compare string representation + } + }); + EXPECT_EQ(expected.size(), row); +} + +// Test invalid IPv4 input and overflow behavior +TEST(IPv4Supported, InvalidData) { + // Test invalid IP strings, should throw ValidationError + // Overflow assertion is kept, but C++ overflow will not throw + auto v4 = std::make_shared(); + EXPECT_THROW(v4->Append("999.999.999.999"), ValidationError); + EXPECT_THROW(v4->Append("abc.def.ghi.jkl"), ValidationError); + EXPECT_THROW(v4->Append(0xFFFFFFFF + 1), std::exception); // Overflow: will not throw in C++ +} + +// Test type conversion between IPv4, string, and uint32 +TEST(IPv4Supported, TypeConversion) { + // Type names must be lowercase: string, uint32 + auto getEither = [](const char* a, const char* b, const char* def) { + const char* v = std::getenv(a); + if (v) return std::string(v); + v = std::getenv(b); + if (v) return std::string(v); + return std::string(def); + }; + const std::string host = getEither("timeplus_HOST", "TIMEPLUS_HOST", "localhost"); + const std::string port = getEither("timeplus_PORT", "TIMEPLUS_PORT", "8463"); + const std::string user = getEither("timeplus_USER", "TIMEPLUS_USER", "default"); + const std::string password = getEither("timeplus_PASSWORD", "TIMEPLUS_PASSWORD", ""); + const std::string db = getEither("timeplus_DB", "TIMEPLUS_DB", "default"); + const auto opts = ClientOptions().SetHost(host).SetPort(static_cast(std::stoul(port))).SetUser(user).SetPassword(password).SetDefaultDatabase(db); + Client client(opts); + client.Execute("DROP TEMPORARY STREAM IF EXISTS test_timeplus_cpp_ipv4_conv;"); + client.Execute("CREATE TEMPORARY STREAM IF NOT EXISTS test_timeplus_cpp_ipv4_conv (v4 ipv4, v4str string, v4int uint32) ENGINE = Memory"); + Block b; + auto v4 = std::make_shared(); + auto v4str = std::make_shared(); + auto v4int = std::make_shared(); + v4->Append("127.0.0.1"); + v4str->Append("127.0.0.1"); + v4int->Append(2130706433u); // 127.0.0.1 as uint32 + b.AppendColumn("v4", v4); + b.AppendColumn("v4str", v4str); + b.AppendColumn("v4int", v4int); + client.Insert("test_timeplus_cpp_ipv4_conv", b); + size_t row = 0; + client.Select("SELECT v4, v4str, v4int FROM test_timeplus_cpp_ipv4_conv", [&row](const Block& block) { + if (block.GetRowCount() == 0) return; + ASSERT_EQ(3U, block.GetColumnCount()); + for (size_t c = 0; c < block.GetRowCount(); ++c, ++row) { + auto col = block[0]->As(); + auto colstr = block[1]->As(); + auto colint = block[2]->As(); + EXPECT_EQ(col->AsString(c), (*colstr)[c]); // IPv4 <-> string + in_addr addr = col->At(c); + EXPECT_EQ(ntohl(addr.s_addr), (*colint)[c]); // IPv4 <-> uint32 + } + }); + EXPECT_EQ(1U, row); +} diff --git a/ut/ipv6_supported_ut.cpp b/ut/ipv6_supported_ut.cpp new file mode 100644 index 0000000..381083e --- /dev/null +++ b/ut/ipv6_supported_ut.cpp @@ -0,0 +1,156 @@ +#include +#include + +#include "ut/utils.h" +#include "ut/value_generators.h" +#include +#include + +using namespace timeplus; + +// Test inserting and selecting IPv6 addresses +TEST(IPv6Supported, InsertSelect) { + // Helper to get environment variable or default + auto getEither = [](const char* a, const char* b, const char* def) { + const char* v = std::getenv(a); + if (v) return std::string(v); + v = std::getenv(b); + if (v) return std::string(v); + return std::string(def); + }; + + // Get connection parameters + const std::string host = getEither("timeplus_HOST", "TIMEPLUS_HOST", "localhost"); + const std::string port = getEither("timeplus_PORT", "TIMEPLUS_PORT", "8463"); + const std::string user = getEither("timeplus_USER", "TIMEPLUS_USER", "default"); + const std::string password = getEither("timeplus_PASSWORD", "TIMEPLUS_PASSWORD", ""); + const std::string db = getEither("timeplus_DB", "TIMEPLUS_DB", "default"); + + const auto opts = ClientOptions() + .SetHost(host) + .SetPort(static_cast(std::stoul(port))) + .SetUser(user) + .SetPassword(password) + .SetDefaultDatabase(db); + + Client client(opts); + + // Prepare test table + client.Execute("DROP TEMPORARY STREAM IF EXISTS test_timeplus_cpp_ipv6_ut;"); + client.Execute("CREATE TEMPORARY STREAM IF NOT EXISTS test_timeplus_cpp_ipv6_ut (v6 ipv6) ENGINE = Memory"); + + Block b; + auto v6 = std::make_shared(); + auto ips6 = MakeIPv6s(); // Generate test IPv6 addresses + for (const auto & ip : ips6) { + v6->Append(ip); + } + + b.AppendColumn("v6", v6); + client.Insert("test_timeplus_cpp_ipv6_ut", b); + + // Validate inserted data + size_t row = 0; + client.Select("SELECT v6 FROM test_timeplus_cpp_ipv6_ut", [&ips6, &row](const Block& block) { + if (block.GetRowCount() == 0) return; + ASSERT_EQ(1U, block.GetColumnCount()); + for (size_t c = 0; c < block.GetRowCount(); ++c, ++row) { + auto col = block[0]->As(); + auto got = col->At(c); + EXPECT_EQ(0, std::memcmp(&ips6[row], &got, sizeof(in6_addr))); + } + }); + EXPECT_EQ(ips6.size(), row); +} + +// Test nullable IPv6 column: insert a valid and a null value, then check nullability +TEST(IPv6Supported, NullableInsertSelect) { + // Type name must be lowercase: nullable(ipv6) + auto getEither = [](const char* a, const char* b, const char* def) { + const char* v = std::getenv(a); + if (v) return std::string(v); + v = std::getenv(b); + if (v) return std::string(v); + return std::string(def); + }; + const std::string host = getEither("timeplus_HOST", "TIMEPLUS_HOST", "localhost"); + const std::string port = getEither("timeplus_PORT", "TIMEPLUS_PORT", "8463"); + const std::string user = getEither("timeplus_USER", "TIMEPLUS_USER", "default"); + const std::string password = getEither("timeplus_PASSWORD", "TIMEPLUS_PASSWORD", ""); + const std::string db = getEither("timeplus_DB", "TIMEPLUS_DB", "default"); + const auto opts = ClientOptions().SetHost(host).SetPort(static_cast(std::stoul(port))).SetUser(user).SetPassword(password).SetDefaultDatabase(db); + Client client(opts); + client.Execute("DROP TEMPORARY STREAM IF EXISTS test_timeplus_cpp_ipv6_nullable;"); + client.Execute("CREATE TEMPORARY STREAM IF NOT EXISTS test_timeplus_cpp_ipv6_nullable (v6 nullable(ipv6)) ENGINE = Memory"); + Block b; + auto v6 = std::make_shared(); + v6->Append("::1"); // valid IPv6 + auto nulls = std::make_shared(); + nulls->Append(0); // not null + v6->Append("::"); // :: as null (all zero IPv6) + nulls->Append(1); // null + b.AppendColumn("v6", std::make_shared(v6, nulls)); + client.Insert("test_timeplus_cpp_ipv6_nullable", b); + size_t row = 0; + client.Select("SELECT v6 FROM test_timeplus_cpp_ipv6_nullable", [&row](const Block& block) { + if (block.GetRowCount() == 0) return; + ASSERT_EQ(1U, block.GetColumnCount()); + for (size_t c = 0; c < block.GetRowCount(); ++c, ++row) { + auto col = block[0]->As(); + if (row == 0) { + EXPECT_FALSE(col->IsNull(c)); // Should not be null + } else { + EXPECT_TRUE(col->IsNull(c)); // Should be null + } + } + }); + EXPECT_EQ(2U, row); +} + +// Test invalid IPv6 input +TEST(IPv6Supported, InvalidData) { + // Test invalid IPv6 strings, should throw ValidationError + auto v6 = std::make_shared(); + EXPECT_THROW(v6->Append("gggg:gggg:gggg:gggg:gggg:gggg:gggg:gggg"), ValidationError); + EXPECT_THROW(v6->Append("not_an_ip"), ValidationError); +} + +// Test type conversion between IPv6 and string +TEST(IPv6Supported, TypeConversion) { + // Type name must be lowercase: string + auto getEither = [](const char* a, const char* b, const char* def) { + const char* v = std::getenv(a); + if (v) return std::string(v); + v = std::getenv(b); + if (v) return std::string(v); + return std::string(def); + }; + const std::string host = getEither("timeplus_HOST", "TIMEPLUS_HOST", "localhost"); + const std::string port = getEither("timeplus_PORT", "TIMEPLUS_PORT", "8463"); + const std::string user = getEither("timeplus_USER", "TIMEPLUS_USER", "default"); + const std::string password = getEither("timeplus_PASSWORD", "TIMEPLUS_PASSWORD", ""); + const std::string db = getEither("timeplus_DB", "TIMEPLUS_DB", "default"); + const auto opts = ClientOptions().SetHost(host).SetPort(static_cast(std::stoul(port))).SetUser(user).SetPassword(password).SetDefaultDatabase(db); + Client client(opts); + client.Execute("DROP TEMPORARY STREAM IF EXISTS test_timeplus_cpp_ipv6_conv;"); + client.Execute("CREATE TEMPORARY STREAM IF NOT EXISTS test_timeplus_cpp_ipv6_conv (v6 ipv6, v6str string) ENGINE = Memory"); + Block b; + auto v6 = std::make_shared(); + auto v6str = std::make_shared(); + v6->Append("::1"); + v6str->Append("::1"); + b.AppendColumn("v6", v6); + b.AppendColumn("v6str", v6str); + client.Insert("test_timeplus_cpp_ipv6_conv", b); + size_t row = 0; + client.Select("SELECT v6, v6str FROM test_timeplus_cpp_ipv6_conv", [&row](const Block& block) { + if (block.GetRowCount() == 0) return; + ASSERT_EQ(2U, block.GetColumnCount()); + for (size_t c = 0; c < block.GetRowCount(); ++c, ++row) { + auto col = block[0]->As(); + auto colstr = block[1]->As(); + EXPECT_EQ(col->AsString(c), (*colstr)[c]); // IPv6 <-> string + } + }); + EXPECT_EQ(1U, row); +}