diff --git a/include/sqids/sqids.hpp b/include/sqids/sqids.hpp index 5591429..48f6a49 100644 --- a/include/sqids/sqids.hpp +++ b/include/sqids/sqids.hpp @@ -29,8 +29,8 @@ /// /// -/// @file sqids.hpp -/// @link https://github.com/sqids/sqids-cpp +/// @file sqids.hpp +/// @link https://github.com/sqids/sqids-cpp /// @author Heikki Johannes Hildén /// #pragma once @@ -66,7 +66,7 @@ struct SqidsOptions /// /// The minimum allowed length of IDs. /// - size_t minLength = 0; + uint8_t minLength = 0; /// /// A list of words that must never appear in IDs. @@ -111,9 +111,9 @@ class Sqids std::string encode(const std::vector& numbers) const; std::vector decode(const std::string& id) const; -private: static constexpr T maxValue = std::numeric_limits::max(); +private: struct Encoder { Encoder(const Sqids* _sqids, const std::vector& _numbers); @@ -213,11 +213,6 @@ Sqids::Sqids(const SqidsOptions& options) throw std::runtime_error("Alphabet must not contain duplicate characters."); } - // Minimum length cannot be greater than the alphabet size - if (options.minLength > alphabetSize) { - throw std::runtime_error("Minimum length cannot be greater than the alphabet size."); - } - const std::string lowercaseAlphabet(lowercaseString(options.alphabet)); // Clean up blocklist @@ -351,7 +346,7 @@ typename std::vector Sqids::decode(const std::string& id) const numbers.push_back(toNumber(chunks[0], alphabet.substr(1))); // If this ID has multiple numbers, shuffle the alphabet, just as - // the encoding function did + // the encoding function does if (chunks.size() > 1) { shuffle(alphabet); } @@ -448,18 +443,22 @@ bool Sqids::isBlockedId(const std::string& id) const } template -Sqids::Encoder::Encoder(const Sqids* _sqids, const Numbers& _numbers) +Sqids::Encoder::Encoder(const Sqids* _sqids, const std::vector& _numbers) : sqids(_sqids), numbers(_numbers) { } template -std::string Sqids::Encoder::run(bool partitioned) +std::string Sqids::Encoder::run(unsigned int increment) { - // Get a semi-random offset from input numbers const size_t alphabetSize = sqids->_alphabet.size(); + if (increment > alphabetSize) { + throw std::runtime_error("Reached max attempts to re-generate the ID."); + } + + // Get a semi-random offset from input numbers auto a = numbers.size(); for (unsigned int i = 0; i < numbers.size(); i++) { @@ -467,7 +466,7 @@ std::string Sqids::Encoder::run(bool partitioned) a += i + sqids->_alphabet[v % alphabetSize]; } - const auto offset = a % alphabetSize; + const auto offset = (a + increment) % alphabetSize; // Re-arrange alphabet so that second-half goes in front of the first-half std::string alphabet(sqids->_alphabet.substr(offset) + sqids->_alphabet.substr(0, offset)); @@ -475,75 +474,46 @@ std::string Sqids::Encoder::run(bool partitioned) // `prefix` is the first character in the generated ID, used for randomization const auto prefix = alphabet[0]; - // `partition` is the character used instead of the first separator to - // indicate that the first number in the input array is a throwaway - // number. This character is used only once to handle blocklist and/or - // padding. it's omitted completely in all other cases - const auto partition = alphabet[1]; - - // The alphabet should not contain the `prefix` or the `partition` character - alphabet.erase(0, 2); + // Reverse alphabet + std::reverse(alphabet.begin(), alphabet.end()); // The final ID will always have the `prefix` character at the beginning std::string id = { prefix }; // Encode the input array for (auto it = numbers.cbegin(); it != numbers.cend(); ++it) { - // The last character of the alphabet is going to be reserved for the `separator` - const auto alphabetWithoutSeparator = alphabet.substr(0, alphabet.size() - 1); + + // The first character of the alphabet is going to be reserved for the `separator` + const auto alphabetWithoutSeparator = alphabet.substr(1); id += sqids->toId(*it, alphabetWithoutSeparator); // If not the last number if (std::next(it) != numbers.cend()) { // `separator` character is used to isolate numbers within the ID - const auto separator = alphabet[alphabet.size() - 1]; - - // For the barrier, use the `separator` unless this is the first - // iteration and the first number is a throwaway number -- then use - // the `partition` character - if (partitioned && it == numbers.cbegin()) { - id.push_back(partition); - } else { - id.push_back(separator); - } + id.push_back(alphabet[0]); // Shuffle on every iteration sqids->shuffle(alphabet); } } - // If `minLength` is used and the ID is too short, add a throwaway number + // Handle `minLength` requirement, if the ID is too short if (sqids->_minLength > id.size()) { - // Partitioning is required so we can safely throw away chunk of the ID - // during decoding - if (!partitioned) { - numbers.insert(numbers.begin(), 0); - id = run(true); - } + // Append a separator + id.push_back(alphabet[0]); - // If adding a `partition` number did not make the length meet the - // `minLength` requirement, then make the new id this format: - // `prefix` character + a slice of the alphabet to make up the missing - // length + the rest of the ID without the `prefix` character - if (sqids->_minLength > id.size()) { - id = id[0] + alphabet.substr(0, sqids->_minLength - id.size()) + id.substr(1); + // For decoding: two separators next to each other is what tells us the + // rest are junk characters + while (sqids->_minLength - id.size() > 0) { + sqids->shuffle(alphabet); + id += alphabet.substr(0, std::min(sqids->_minLength - id.size(), alphabet.size())); } } - // if ID has a blocked word anywhere, add a throwaway number and start over + // if ID has a blocked word anywhere, restart with a +1 increment if (sqids->isBlockedId(id)) { - if (partitioned) { - if (numbers[0] + 1 > sqids->maxValue) { - throw std::runtime_error("Ran out of range checking against the blocklist."); - } else { - numbers[0] += 1; - } - } else { - numbers.insert(numbers.begin(), 0); - } - - id = run(true); + return run(increment + 1); } return id; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 32ae1c7..598c43a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,8 +16,7 @@ target_link_libraries(GTest::GTest INTERFACE gtest_main) add_executable( sqids_tests - alphabetTests.cpp blocklistTests.cpp encodingTests.cpp minLengthTests.cpp - uniquesTests.cpp) + alphabetTests.cpp blocklistTests.cpp encodingTests.cpp minLengthTests.cpp) target_link_libraries(sqids_tests PRIVATE GTest::GTest sqids) diff --git a/tests/alphabetTests.cpp b/tests/alphabetTests.cpp index 10e3cb3..7d46149 100644 --- a/tests/alphabetTests.cpp +++ b/tests/alphabetTests.cpp @@ -5,14 +5,14 @@ TEST(AlphabetTest, Simple) { sqidscxx::Sqids<> sqids({ alphabet: "0123456789abcdef" }); const auto numbers(sqids.numbers({ 1, 2, 3 })); - const auto id = "4d9fd2"; + const auto id = "489158"; EXPECT_EQ(sqids.encode(numbers), id); EXPECT_EQ(sqids.decode(id), numbers); } TEST(AlphabetTest, ShortAlphabet) { - sqidscxx::Sqids<> sqids({ alphabet: "abcde" }); + sqidscxx::Sqids<> sqids({ alphabet: "abc" }); const auto numbers(sqids.numbers({ 1, 2, 3 })); diff --git a/tests/blocklistTests.cpp b/tests/blocklistTests.cpp index 9faa91c..26fa69b 100644 --- a/tests/blocklistTests.cpp +++ b/tests/blocklistTests.cpp @@ -4,57 +4,57 @@ TEST(Blocklist, IfNoCustomBlocklistParamUseTheDefaultBlocklist) { sqidscxx::Sqids<> sqids; - EXPECT_EQ(sqids.decode("sexy"), sqids.numbers({ 200044 })); - EXPECT_EQ(sqids.encode({ 200044 }), "d171vI"); + EXPECT_EQ(sqids.decode("aho1e"), sqids.numbers({ 4572721 })); + EXPECT_EQ(sqids.encode({ 4572721 }), "JExTR"); } TEST(Blocklist, IfAnEmptyBlocklistParamPassedDontUseAnyBlocklist) { sqidscxx::Sqids<> sqids({ blocklist: {} }); - EXPECT_EQ(sqids.decode("sexy"), sqids.numbers({ 200044 })); - EXPECT_EQ(sqids.encode({ 200044 }), "sexy"); + EXPECT_EQ(sqids.decode("aho1e"), sqids.numbers({ 4572721 })); + EXPECT_EQ(sqids.encode({ 4572721 }), "aho1e"); } TEST(Blocklist, IfANonEmptyBlocklistParamPassedUseOnlyThat) { sqidscxx::Sqids<> sqids({ blocklist: { - "AvTg" // originally encoded [100000] + "ArUO" // originally encoded [100000] } }); // Make sure we don't use the default blocklist - EXPECT_EQ(sqids.decode("sexy"), sqids.numbers({ 200044 })); - EXPECT_EQ(sqids.encode({ 200044 }), "sexy"); + EXPECT_EQ(sqids.decode("aho1e"), sqids.numbers({ 4572721 })); + EXPECT_EQ(sqids.encode({ 4572721 }), "aho1e"); // Make sure we are using the passed blocklist - EXPECT_EQ(sqids.decode("AvTg"), sqids.numbers({ 100000 })); - EXPECT_EQ(sqids.encode({ 100000 }), "7T1X8k"); - EXPECT_EQ(sqids.decode("7T1X8k"), sqids.numbers({ 100000 })); + EXPECT_EQ(sqids.decode("ArUO"), sqids.numbers({ 100000 })); + EXPECT_EQ(sqids.encode({ 100000 }), "QyG4"); + EXPECT_EQ(sqids.decode("QyG4"), sqids.numbers({ 100000 })); } TEST(Blocklist, Blocklist) { sqidscxx::Sqids<> sqids({ blocklist: { - "8QRLaD", // Normal result of 1st encoding -- let's block that word on purpose - "7T1cd0dL", // Result of 2nd encoding - "UeIe", // Result of 3rd encoding is `RA8UeIe7` -- let's block a substring - "imhw", // Result of 4th encoding is `WM3Limhw` -- let's block the postfix - "LfUQ" // Result of 4th encoding is `LfUQh4HN` -- let's block the prefix + "JSwXFaosAN", // Normal result of 1st encoding. Let's block that word on purpose + "OCjV9JK64o", // Result of 2nd encoding + "rBHf", // Result of 3rd encoding is `4rBHfOiqd3`. Let's block a substring + "79SM", // Result of 4th encoding is `dyhgw479SM`. Let's block the postfix + "7tE6" // Result of 4th encoding is `7tE6jdAHLe`. Let's block the prefix } }); - EXPECT_EQ(sqids.encode({ 1, 2, 3}), "TM0x1Mxz"); - EXPECT_EQ(sqids.decode("TM0x1Mxz"), sqids.numbers({ 1, 2, 3 })); + EXPECT_EQ(sqids.encode({ 1'000'000, 2'000'000 }), "1aYeB7bRUt"); + EXPECT_EQ(sqids.decode("1aYeB7bRUt"), sqids.numbers({ 1'000'000, 2'000'000 })); } TEST(Blocklist, DecodingBlocklistWordsShouldStillWork) { - sqidscxx::Sqids<> sqids({ blocklist: { "8QRLaD", "7T1cd0dL", "RA8UeIe7", "WM3Limhw", "LfUQh4HN" } }); + sqidscxx::Sqids<> sqids({ blocklist: { "86Rf07", "se8ojk", "ARsz1p", "Q8AI49", "5sQRZO" } }); - EXPECT_EQ(sqids.decode("8QRLaD"), sqids.numbers({ 1, 2, 3 })); - EXPECT_EQ(sqids.decode("7T1cd0dL"), sqids.numbers({ 1, 2, 3 })); - EXPECT_EQ(sqids.decode("RA8UeIe7"), sqids.numbers({ 1, 2, 3 })); - EXPECT_EQ(sqids.decode("WM3Limhw"), sqids.numbers({ 1, 2, 3 })); - EXPECT_EQ(sqids.decode("LfUQh4HN"), sqids.numbers({ 1, 2, 3 })); + EXPECT_EQ(sqids.decode("86Rf07"), sqids.numbers({ 1, 2, 3 })); + EXPECT_EQ(sqids.decode("se8ojk"), sqids.numbers({ 1, 2, 3 })); + EXPECT_EQ(sqids.decode("ARsz1p"), sqids.numbers({ 1, 2, 3 })); + EXPECT_EQ(sqids.decode("Q8AI49"), sqids.numbers({ 1, 2, 3 })); + EXPECT_EQ(sqids.decode("5sQRZO"), sqids.numbers({ 1, 2, 3 })); } TEST(Blocklist, MatchAgainstAShortBlocklistWord) { - sqidscxx::Sqids<> sqids({ blocklist: { "pPQ" } }); + sqidscxx::Sqids<> sqids({ blocklist: { "pnd" } }); EXPECT_EQ(sqids.decode(sqids.encode({ 1000 })), sqids.numbers({ 1000 })); } @@ -62,12 +62,29 @@ TEST(Blocklist, MatchAgainstAShortBlocklistWord) { TEST(Blocklist, BlocklistFilteringInConstructor) { sqidscxx::Sqids<> sqids({ alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - blocklist: { "sqnmpn" } // Lowercase blocklist in uppercase-only alphabet + blocklist: { "sxnzkl" } // Lowercase blocklist in uppercase-only alphabet }); auto id = sqids.encode({ 1, 2, 3 }); auto numbers = sqids.decode(id); - EXPECT_EQ(id, "ULPBZGBM"); // Without blocklist, would've been "SQNMPN" + EXPECT_EQ(id, "IBSHOZ"); // Without blocklist, would've been "SXNZKL" EXPECT_EQ(numbers, sqids.numbers({ 1, 2, 3 })); } + +TEST(Blocklist, MaxEncodingAttempts) { + std::string alphabet = "abc"; + size_t minLength = 3; + std::set blocklist = { "cab", "abc", "bca" }; + + sqidscxx::Sqids<> sqids({ + alphabet: alphabet, + minLength: minLength, + blocklist: blocklist + }); + + EXPECT_EQ(alphabet.size(), minLength); + EXPECT_EQ(blocklist.size(), minLength); + + ASSERT_THROW(sqids.encode({ 0 }), std::runtime_error); +} diff --git a/tests/encodingTests.cpp b/tests/encodingTests.cpp index 945711f..f3513fa 100644 --- a/tests/encodingTests.cpp +++ b/tests/encodingTests.cpp @@ -5,7 +5,7 @@ TEST(Encoding, Simple) { sqidscxx::Sqids<> sqids; const auto numbers(sqids.numbers({ 1, 2, 3 })); - const auto id = "8QRLaD"; + const auto id = "86Rf07"; EXPECT_EQ(sqids.encode(numbers), id); EXPECT_EQ(sqids.decode(id), numbers); @@ -25,16 +25,16 @@ TEST(Encoding, IncrementalNumbers) { sqidscxx::Sqids<> sqids; const IdAndNumbersPairs pairs = { - { "bV", { 0 } }, - { "U9", { 1 } }, - { "g8", { 2 } }, - { "Ez", { 3 } }, - { "V8", { 4 } }, - { "ul", { 5 } }, - { "O3", { 6 } }, - { "AF", { 7 } }, - { "ph", { 8 } }, - { "n8", { 9 } } + { "bM", { 0 } }, + { "Uk", { 1 } }, + { "gb", { 2 } }, + { "Ef", { 3 } }, + { "Vq", { 4 } }, + { "uw", { 5 } }, + { "OI", { 6 } }, + { "AX", { 7 } }, + { "p6", { 8 } }, + { "nJ", { 9 } } }; for (auto pair : pairs) { @@ -50,16 +50,16 @@ TEST(Encoding, IncrementalNumbersSameIndex0) { sqidscxx::Sqids<> sqids; const IdAndNumbersPairs pairs = { - { "SrIu", { 0, 0 } }, - { "nZqE", { 0, 1 } }, - { "tJyf", { 0, 2 } }, - { "e86S", { 0, 3 } }, - { "rtC7", { 0, 4 } }, - { "sQ8R", { 0, 5 } }, - { "uz2n", { 0, 6 } }, - { "7Td9", { 0, 7 } }, - { "3nWE", { 0, 8 } }, - { "mIxM", { 0, 9 } } + { "SvIz", { 0, 0 } }, + { "n3qa", { 0, 1 } }, + { "tryF", { 0, 2 } }, + { "eg6q", { 0, 3 } }, + { "rSCF", { 0, 4 } }, + { "sR8x", { 0, 5 } }, + { "uY2M", { 0, 6 } }, + { "74dI", { 0, 7 } }, + { "30WX", { 0, 8 } }, + { "moxr", { 0, 9 } } }; for (auto pair : pairs) { @@ -75,16 +75,16 @@ TEST(Encoding, IncrementalNumbersSameIndex1) { sqidscxx::Sqids<> sqids; const IdAndNumbersPairs pairs = { - { "SrIu", { 0, 0 } }, - { "nbqh", { 1, 0 } }, - { "t4yj", { 2, 0 } }, - { "eQ6L", { 3, 0 } }, - { "r4Cc", { 4, 0 } }, - { "sL82", { 5, 0 } }, - { "uo2f", { 6, 0 } }, - { "7Zdq", { 7, 0 } }, - { "36Wf", { 8, 0 } }, - { "m4xT", { 9, 0 } } + { "SvIz", { 0, 0 } }, + { "nWqP", { 1, 0 } }, + { "tSyw", { 2, 0 } }, + { "eX68", { 3, 0 } }, + { "rxCY", { 4, 0 } }, + { "sV8a", { 5, 0 } }, + { "uf2K", { 6, 0 } }, + { "7Cdk", { 7, 0 } }, + { "3aWP", { 8, 0 } }, + { "m2xn", { 9, 0 } } }; for (auto pair : pairs) { @@ -130,15 +130,9 @@ TEST(Decoding, DecodingAnIdWithAnInvalidCharacter) { EXPECT_EQ(sqids.decode("*"), sqids.numbers({})); } -TEST(Decoding, DecodingAnInvalidIdWithARepeatingReservedCharacter) { - sqidscxx::Sqids<> sqids; - - EXPECT_EQ(sqids.decode("fff"), sqids.numbers({})); -} - TEST(Decoding, EncodeOutOfRangeNumbers) { sqidscxx::Sqids sqids; - ASSERT_THROW(sqids.encode({ sqids.minValue - 1 }), std::runtime_error); + ASSERT_THROW(sqids.encode({ -1 }), std::runtime_error); ASSERT_THROW(sqids.encode({ static_cast(sqids.maxValue + 1) }), std::runtime_error); } diff --git a/tests/minLengthTests.cpp b/tests/minLengthTests.cpp index 8e86e12..77a1ba3 100644 --- a/tests/minLengthTests.cpp +++ b/tests/minLengthTests.cpp @@ -2,31 +2,69 @@ #include TEST(MinLength, Simple) { - sqidscxx::Sqids<> sqids({ minLength: sqidscxx::SqidsOptions().alphabet.size() }); + sqidscxx::Sqids<> sqids({ minLength: static_cast(sqidscxx::SqidsOptions().alphabet.size()) }); const auto numbers(sqids.numbers({ 1, 2, 3 })); - const auto id = "75JILToVsGerOADWmHlY38xvbaNZKQ9wdFS0B6kcMEtnRpgizhjU42qT1cd0dL"; + const auto id = "86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTM"; EXPECT_EQ(sqids.encode(numbers), id); EXPECT_EQ(sqids.decode(id), numbers); } -typedef std::vector>> IdAndNumbersPairs; +TEST(MinLength, Incremental) { + sqidscxx::Sqids<> sqids; + + const auto numbers(sqids.numbers({ 1, 2, 3 })); + + sqidscxx::SqidsOptions defaultOptions; + + const std::vector> pairs = { + { 6, "86Rf07" }, + { 7, "86Rf07x" }, + { 8, "86Rf07xd" }, + { 9, "86Rf07xd4" }, + { 10, "86Rf07xd4z" }, + { 11, "86Rf07xd4zB" }, + { 12, "86Rf07xd4zBm" }, + { 13, "86Rf07xd4zBmi" }, + { defaultOptions.alphabet.size() + 0, + "86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTM" }, + { defaultOptions.alphabet.size() + 1, + "86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMy" }, + { defaultOptions.alphabet.size() + 2, + "86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMyf" }, + { defaultOptions.alphabet.size() + 3, + "86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMyf1" } + }; + + for (auto pair : pairs) { + const auto minLength = pair.first; + const auto id = pair.second; + + sqidscxx::Sqids<> sqids({ minLength: static_cast(minLength) }); + + EXPECT_EQ(sqids.encode(numbers), id); + EXPECT_EQ(sqids.encode(numbers).size(), minLength); + EXPECT_EQ(sqids.decode(id), numbers); + } +} TEST(MinLength, IncrementalNumbers) { - sqidscxx::Sqids<> sqids({ minLength: sqidscxx::SqidsOptions().alphabet.size() }); + using IdAndNumbersPairs = std::vector>>; + + sqidscxx::Sqids<> sqids({ minLength: static_cast(sqidscxx::SqidsOptions().alphabet.size()) }); const IdAndNumbersPairs pairs = { - { "jf26PLNeO5WbJDUV7FmMtlGXps3CoqkHnZ8cYd19yIiTAQuvKSExzhrRghBlwf", { 0, 0 } }, - { "vQLUq7zWXC6k9cNOtgJ2ZK8rbxuipBFAS10yTdYeRa3ojHwGnmMV4PDhESI2jL", { 0, 1 } }, - { "YhcpVK3COXbifmnZoLuxWgBQwtjsSaDGAdr0ReTHM16yI9vU8JNzlFq5Eu2oPp", { 0, 2 } }, - { "OTkn9daFgDZX6LbmfxI83RSKetJu0APihlsrYoz5pvQw7GyWHEUcN2jBqd4kJ9", { 0, 3 } }, - { "h2cV5eLNYj1x4ToZpfM90UlgHBOKikQFvnW36AC8zrmuJ7XdRytIGPawqYEbBe", { 0, 4 } }, - { "7Mf0HeUNkpsZOTvmcj836P9EWKaACBubInFJtwXR2DSzgYGhQV5i4lLxoT1qdU", { 0, 5 } }, - { "APVSD1ZIY4WGBK75xktMfTev8qsCJw6oyH2j3OnLcXRlhziUmpbuNEar05QCsI", { 0, 6 } }, - { "P0LUhnlT76rsWSofOeyRGQZv1cC5qu3dtaJYNEXwk8Vpx92bKiHIz4MgmiDOF7", { 0, 7 } }, - { "xAhypZMXYIGCL4uW0te6lsFHaPc3SiD1TBgw5O7bvodzjqUn89JQRfk2Nvm4JI", { 0, 8 } }, - { "94dRPIZ6irlXWvTbKywFuAhBoECQOVMjDJp53s2xeqaSzHY8nc17tmkLGwfGNl", { 0, 9 } } + { "SvIzsqYMyQwI3GWgJAe17URxX8V924Co0DaTZLtFjHriEn5bPhcSkfmvOslpBu", { 0, 0 } }, + { "n3qafPOLKdfHpuNw3M61r95svbeJGk7aAEgYn4WlSjXURmF8IDqZBy0CT2VxQc", { 0, 1 } }, + { "tryFJbWcFMiYPg8sASm51uIV93GXTnvRzyfLleh06CpodJD42B7OraKtkQNxUZ", { 0, 2 } }, + { "eg6ql0A3XmvPoCzMlB6DraNGcWSIy5VR8iYup2Qk4tjZFKe1hbwfgHdUTsnLqE", { 0, 3 } }, + { "rSCFlp0rB2inEljaRdxKt7FkIbODSf8wYgTsZM1HL9JzN35cyoqueUvVWCm4hX", { 0, 4 } }, + { "sR8xjC8WQkOwo74PnglH1YFdTI0eaf56RGVSitzbjuZ3shNUXBrqLxEJyAmKv2", { 0, 5 } }, + { "uY2MYFqCLpgx5XQcjdtZK286AwWV7IBGEfuS9yTmbJvkzoUPeYRHr4iDs3naN0", { 0, 6 } }, + { "74dID7X28VLQhBlnGmjZrec5wTA1fqpWtK4YkaoEIM9SRNiC3gUJH0OFvsPDdy", { 0, 7 } }, + { "30WXpesPhgKiEI5RHTY7xbB1GnytJvXOl2p0AcUjdF6waZDo9Qk8VLzMuWrqCS", { 0, 8 } }, + { "moxr3HqLAK0GsTND6jowfZz3SUx7cQ8aC54Pl1RbIvFXmEJuBMYVeW9yrdOtin", { 0, 9 } } }; for (auto pair : pairs) { @@ -39,21 +77,24 @@ TEST(MinLength, IncrementalNumbers) { } TEST(MinLength, MinLengths) { + using Numbers = std::vector; + sqidscxx::Sqids<> sqids; std::vector minLengths = { 0, 1, 5, 10, sqidscxx::SqidsOptions().alphabet.size() }; - const std::vector::Numbers> numbersList = { - { sqids.minValue }, + const std::vector numbersList = { + { 0 }, { 0, 0, 0, 0, 0 }, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { 100, 200, 300 }, { 1'000, 2'000, 3'000 }, { 1'000'000 }, - { sqids.maxValue } }; + { sqids.maxValue } + }; for (auto minLength : minLengths) { for (auto numbers : numbersList) { - sqidscxx::Sqids<> sqids({ minLength: minLength }); + sqidscxx::Sqids<> sqids({ minLength: static_cast(minLength) }); const auto id = sqids.encode(numbers); const auto output = sqids.decode(id); @@ -63,7 +104,3 @@ TEST(MinLength, MinLengths) { } } } - -TEST(MinLength, OutOfRangeInvalidMinLength) { - ASSERT_THROW(sqidscxx::Sqids<>({ minLength: sqidscxx::SqidsOptions().alphabet.size() + 1 }), std::runtime_error); -} diff --git a/tests/uniquesTests.cpp b/tests/uniquesTests.cpp deleted file mode 100644 index 534720a..0000000 --- a/tests/uniquesTests.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include - -#define UPPER 1'000'000 - -TEST(Uniques, WithPadding) { - sqidscxx::Sqids<> sqids({ minLength: sqidscxx::SqidsOptions().alphabet.size() }); - std::set set; - - for (uint64_t i = 0; i != UPPER; i++) { - const auto numbers = sqids.numbers({ i }); - const auto id = sqids.encode(numbers); - set.insert(id); - - EXPECT_EQ(sqids.decode(id), numbers); - } - - EXPECT_EQ(set.size(), UPPER); -} - -TEST(Uniques, LowRanges) { - sqidscxx::Sqids<> sqids; - std::set set; - - for (uint64_t i = 0; i != UPPER; i++) { - const auto numbers = sqids.numbers({ i }); - const auto id = sqids.encode(numbers); - set.insert(id); - - EXPECT_EQ(sqids.decode(id), numbers); - } - - EXPECT_EQ(set.size(), UPPER); -} - -TEST(Uniques, HighRanges) { - sqidscxx::Sqids<> sqids; - std::set set; - - for (uint64_t i = 100'000'000; i != 100'000'000 + UPPER; i++) { - const auto numbers = sqids.numbers({ i }); - const auto id = sqids.encode(numbers); - set.insert(id); - - EXPECT_EQ(sqids.decode(id), numbers); - } - - EXPECT_EQ(set.size(), UPPER); -} - -TEST(Uniques, Multi) { - sqidscxx::Sqids<> sqids; - std::set set; - - for (uint64_t i = 0; i != UPPER; i++) { - const auto numbers = sqids.numbers({ i, i, i, i, i }); - const auto id = sqids.encode(numbers); - set.insert(id); - - EXPECT_EQ(sqids.decode(id), numbers); - } - - EXPECT_EQ(set.size(), UPPER); -}