mirror of
https://github.com/latentPrion/libspinscale.git
synced 2026-06-23 19:48:32 +00:00
158 lines
4.2 KiB
C++
158 lines
4.2 KiB
C++
|
|
#include <cstdlib>
|
||
|
|
#include <chrono>
|
||
|
|
#include <filesystem>
|
||
|
|
#include <fstream>
|
||
|
|
#include <sstream>
|
||
|
|
#include <string>
|
||
|
|
#include <vector>
|
||
|
|
|
||
|
|
#include <gtest/gtest.h>
|
||
|
|
|
||
|
|
#include <spinscale/envKvStore.h>
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
class EnvKvStoreTest
|
||
|
|
: public testing::Test
|
||
|
|
{
|
||
|
|
protected:
|
||
|
|
void SetUp() override
|
||
|
|
{
|
||
|
|
root = std::filesystem::temp_directory_path()
|
||
|
|
/ ("spinscale-env-test-"
|
||
|
|
+ std::to_string(std::chrono::steady_clock::now()
|
||
|
|
.time_since_epoch().count())
|
||
|
|
+ "-" + std::to_string(testCounter++));
|
||
|
|
std::filesystem::create_directories(root);
|
||
|
|
unsetenv("SSCL_ENV_TEST_VALUE");
|
||
|
|
}
|
||
|
|
|
||
|
|
void TearDown() override
|
||
|
|
{
|
||
|
|
unsetenv("SSCL_ENV_TEST_VALUE");
|
||
|
|
std::filesystem::remove_all(root);
|
||
|
|
}
|
||
|
|
|
||
|
|
std::filesystem::path writeFile(
|
||
|
|
const std::string &filename,
|
||
|
|
const std::string &contents)
|
||
|
|
{
|
||
|
|
std::filesystem::path path = root / filename;
|
||
|
|
std::ofstream file(path);
|
||
|
|
file << contents;
|
||
|
|
return path;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::filesystem::path root;
|
||
|
|
static inline int testCounter = 0;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
TEST_F(EnvKvStoreTest, ParsesSupportedDotenvForms)
|
||
|
|
{
|
||
|
|
std::filesystem::path envFile = writeFile(
|
||
|
|
"one.env",
|
||
|
|
"\n"
|
||
|
|
"# comment\n"
|
||
|
|
"PLAIN=value\n"
|
||
|
|
" TRIMMED = value with spaces \n"
|
||
|
|
"SINGLE=' preserved value '\n"
|
||
|
|
"DOUBLE=\"another preserved value\"\n"
|
||
|
|
"ESCAPED=\"quote: \\\" slash: \\\\ tab: \\t\"\n"
|
||
|
|
"COMMENTED=value # comment\n");
|
||
|
|
|
||
|
|
std::ostringstream warnings;
|
||
|
|
sscl::EnvKvStore store({envFile}, warnings);
|
||
|
|
|
||
|
|
EXPECT_EQ(store.get("PLAIN"), "value");
|
||
|
|
EXPECT_EQ(store.get("TRIMMED"), "value with spaces");
|
||
|
|
EXPECT_EQ(store.get("SINGLE"), " preserved value ");
|
||
|
|
EXPECT_EQ(store.get("DOUBLE"), "another preserved value");
|
||
|
|
EXPECT_EQ(store.get("ESCAPED"), "quote: \" slash: \\ tab: \t");
|
||
|
|
EXPECT_EQ(store.get("COMMENTED"), "value");
|
||
|
|
EXPECT_TRUE(warnings.str().empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(EnvKvStoreTest, LaterFilesOverwriteEarlierFilesAndWarn)
|
||
|
|
{
|
||
|
|
std::filesystem::path first = writeFile("first.env", "VALUE=first\n");
|
||
|
|
std::filesystem::path second = writeFile("second.env", "VALUE=second\n");
|
||
|
|
|
||
|
|
std::ostringstream warnings;
|
||
|
|
sscl::EnvKvStore store({first, second}, warnings);
|
||
|
|
|
||
|
|
EXPECT_EQ(store.get("VALUE"), "second");
|
||
|
|
EXPECT_NE(warnings.str().find("VALUE"), std::string::npos);
|
||
|
|
EXPECT_NE(warnings.str().find("first"), std::string::npos);
|
||
|
|
EXPECT_NE(warnings.str().find("second"), std::string::npos);
|
||
|
|
EXPECT_NE(warnings.str().find(second.string()), std::string::npos);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(EnvKvStoreTest, DuplicateKeysInsideSameFileOverwriteAndWarn)
|
||
|
|
{
|
||
|
|
std::filesystem::path envFile =
|
||
|
|
writeFile("one.env", "VALUE=first\nVALUE=second\n");
|
||
|
|
|
||
|
|
std::ostringstream warnings;
|
||
|
|
sscl::EnvKvStore store({envFile}, warnings);
|
||
|
|
|
||
|
|
EXPECT_EQ(store.get("VALUE"), "second");
|
||
|
|
EXPECT_NE(warnings.str().find("VALUE"), std::string::npos);
|
||
|
|
EXPECT_NE(warnings.str().find("first"), std::string::npos);
|
||
|
|
EXPECT_NE(warnings.str().find("second"), std::string::npos);
|
||
|
|
EXPECT_NE(warnings.str().find(envFile.string()), std::string::npos);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(EnvKvStoreTest, ProcessEnvironmentOverridesStoreSilently)
|
||
|
|
{
|
||
|
|
std::filesystem::path envFile =
|
||
|
|
writeFile("one.env", "SSCL_ENV_TEST_VALUE=file\n");
|
||
|
|
setenv("SSCL_ENV_TEST_VALUE", "process", 1);
|
||
|
|
|
||
|
|
std::ostringstream warnings;
|
||
|
|
sscl::EnvKvStore store({envFile}, warnings);
|
||
|
|
|
||
|
|
EXPECT_EQ(store.get("SSCL_ENV_TEST_VALUE"), "process");
|
||
|
|
EXPECT_TRUE(warnings.str().empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(EnvKvStoreTest, EmptyProcessEnvironmentValueOverridesStoreSilently)
|
||
|
|
{
|
||
|
|
std::filesystem::path envFile =
|
||
|
|
writeFile("one.env", "SSCL_ENV_TEST_VALUE=file\n");
|
||
|
|
setenv("SSCL_ENV_TEST_VALUE", "", 1);
|
||
|
|
|
||
|
|
std::ostringstream warnings;
|
||
|
|
sscl::EnvKvStore store({envFile}, warnings);
|
||
|
|
|
||
|
|
EXPECT_EQ(store.get("SSCL_ENV_TEST_VALUE"), "");
|
||
|
|
EXPECT_TRUE(warnings.str().empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(EnvKvStoreTest, MissingFileThrows)
|
||
|
|
{
|
||
|
|
std::ostringstream warnings;
|
||
|
|
EXPECT_THROW(
|
||
|
|
sscl::EnvKvStore({root / "missing.env"}, warnings),
|
||
|
|
std::runtime_error);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(EnvKvStoreTest, MalformedLineThrows)
|
||
|
|
{
|
||
|
|
std::filesystem::path envFile = writeFile("bad.env", "NOT AN ASSIGNMENT\n");
|
||
|
|
std::ostringstream warnings;
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
sscl::EnvKvStore store({envFile}, warnings);
|
||
|
|
FAIL() << "Expected malformed env file to throw.";
|
||
|
|
}
|
||
|
|
catch (const std::runtime_error &e)
|
||
|
|
{
|
||
|
|
std::string message = e.what();
|
||
|
|
EXPECT_NE(message.find(envFile.string()), std::string::npos);
|
||
|
|
EXPECT_NE(message.find(":1:"), std::string::npos);
|
||
|
|
}
|
||
|
|
}
|