sapling/eden/fs/config/test/ConfigSettingTest.cpp
Katie Mancini fa180ad585 check multiple locations for x509 certs
Summary:
This stack updates eden to be able to check all of the locations that able
users certificate may reside.

There can be multiple places where a cert may reside (we cant always
definitively choose one place to look based on the platform). Thus we
need to be able to configure multiple locations for certs in our eden
config.

This makes the switch over in eden from using one place for the client
cert to use and using the first available client cert from a list.

NOTE: most of this diff is fixing unit tests take a look at `EdenConfig.h` and `EdenConfig.cpp` first

Reviewed By: wez

Differential Revision: D23359939

fbshipit-source-id: 44beecce3ef098a734dbd7c5eb3fa5f0aad6b50b
2020-09-23 19:58:52 -07:00

414 lines
15 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include "eden/fs/config/EdenConfig.h"
#include "eden/fs/utils/PathFuncs.h"
#include <folly/experimental/TestUtil.h>
#include <folly/test/TestUtils.h>
#include <gtest/gtest.h>
#include "eden/fs/utils/PathFuncs.h"
using folly::StringPiece;
using namespace facebook::eden;
using namespace folly::string_piece_literals;
using namespace std::chrono_literals;
class ConfigSettingTest : public ::testing::Test {
protected:
static constexpr folly::StringPiece defaultDirSp_{"/DEFAULT_DIR"};
static constexpr folly::StringPiece systemConfigDirSp_{
"/SYSTEM_CONFIG_SETTING"};
static constexpr folly::StringPiece userConfigDirSp_{"/USER_CONFIG_SETTING"};
static constexpr folly::StringPiece otherDirSp_{"/OTHER_DIR"};
AbsolutePath defaultDir_;
AbsolutePath systemConfigDir_;
AbsolutePath userConfigDir_;
AbsolutePath otherDir_;
void SetUp() override {
defaultDir_ = normalizeBestEffort(defaultDirSp_);
systemConfigDir_ = normalizeBestEffort(systemConfigDirSp_);
userConfigDir_ = normalizeBestEffort(userConfigDirSp_);
otherDir_ = normalizeBestEffort(otherDirSp_);
}
};
TEST_F(ConfigSettingTest, initStateCheck) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
// Initial should be default
EXPECT_EQ(testDir.getValue(), defaultDir_);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getConfigKey(), dirKey);
}
TEST_F(ConfigSettingTest, configSetStringValue) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
std::map<std::string, std::string> attrMap;
auto rslt = testDir.setStringValue(
systemConfigDirSp_, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(testDir.getValue(), systemConfigDir_);
EXPECT_EQ(systemConfigDir_, testDir.getStringValue());
rslt = testDir.setStringValue(
userConfigDirSp_, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(testDir.getValue(), userConfigDir_);
EXPECT_EQ(userConfigDir_, testDir.getStringValue());
}
TEST_F(ConfigSettingTest, configSetAssign) {
// Setup our target copy
auto otherKey = "otherKey"_sp;
ConfigSetting<AbsolutePath> copyOfTestDir{otherKey, otherDir_, nullptr};
// Check the copy states first, so we know where starting point is.
EXPECT_EQ(copyOfTestDir.getConfigKey(), otherKey);
EXPECT_EQ(copyOfTestDir.getSource(), ConfigSource::Default);
EXPECT_EQ(copyOfTestDir.getValue(), otherDir_);
auto dirKey = "dirKey"_sp;
{
// Setup the copy source - sufficiently different
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
std::map<std::string, std::string> attrMap;
auto rslt = testDir.setStringValue(
systemConfigDirSp_, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getConfigKey(), dirKey);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(testDir.getValue(), systemConfigDir_);
copyOfTestDir.copyFrom(testDir);
}
// Check all attributes copied.
EXPECT_EQ(copyOfTestDir.getConfigKey(), dirKey);
EXPECT_EQ(copyOfTestDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(copyOfTestDir.getValue(), systemConfigDir_);
// Check references still valid
copyOfTestDir.clearValue(ConfigSource::Default);
}
TEST_F(ConfigSettingTest, configSetInvalidStringValue) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
std::map<std::string, std::string> attrMap;
auto rslt = testDir.setStringValue(
systemConfigDirSp_, attrMap, ConfigSource::SystemConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getSource(), ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getValue(), systemConfigDir_);
folly::StringPiece userConfigDir{"INVALID USER_CONFIG_SETTING/"};
rslt =
testDir.setStringValue(userConfigDir, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), true);
EXPECT_EQ(
rslt.error(),
"Cannot convert value 'INVALID USER_CONFIG_SETTING/' to an absolute path");
EXPECT_EQ(testDir.getSource(), ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getValue(), systemConfigDir_);
}
TEST_F(ConfigSettingTest, configSetEnvSubTest) {
AbsolutePath defaultDir{"/home/bob"};
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir, nullptr};
folly::StringPiece userConfigDir{"${HOME}/test_dir"};
std::map<std::string, std::string> attrMap;
attrMap["HOME"] = "/home/bob";
attrMap["USER"] = "bob";
auto rslt =
testDir.setStringValue(userConfigDir, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
AbsolutePath bobTestDir = normalizeBestEffort("/home/bob/test_dir");
EXPECT_EQ(testDir.getValue(), bobTestDir);
EXPECT_EQ(bobTestDir, testDir.getStringValue());
folly::StringPiece homeUserConfigDir{"/home/${USER}/test_dir"};
rslt = testDir.setStringValue(
homeUserConfigDir, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(testDir.getValue(), bobTestDir);
EXPECT_EQ(bobTestDir, testDir.getStringValue());
}
TEST_F(ConfigSettingTest, configSettingIgnoreDefault) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
// Initial should be default
EXPECT_EQ(testDir.getValue(), defaultDir_);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
// Setting default value should be ignored
AbsolutePath notDefaultDir{"/NOT_THE_DEFAULT_DIR"};
testDir.setValue(notDefaultDir, ConfigSource::Default);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
// Clearing the default value should be ignored
testDir.clearValue(ConfigSource::Default);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
}
TEST_F(ConfigSettingTest, configSettingClearNonExistingSource) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
// Initially, it should be default value
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
// Clear unset priorities
testDir.clearValue(ConfigSource::CommandLine);
testDir.clearValue(ConfigSource::UserConfig);
testDir.clearValue(ConfigSource::SystemConfig);
testDir.clearValue(ConfigSource::Default);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
}
TEST_F(ConfigSettingTest, configSettingSetAndClearTest) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
AbsolutePath systemEdenDir{"/SYSTEM_DIR"};
// Initially, it should be default value
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
// Over-ride default
testDir.setValue(systemEdenDir, ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getValue(), systemEdenDir);
// Clear the over-ride
testDir.clearValue(ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
}
TEST_F(ConfigSettingTest, configSetOverRiddenSource) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
AbsolutePath cliEdenDir{"/CLI_DIR"};
AbsolutePath systemEdenDir{"/SYSTEM_DIR"};
// Initially, it should be default value
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
// Set the highest priority item
testDir.setValue(cliEdenDir, ConfigSource::CommandLine);
EXPECT_EQ(testDir.getSource(), ConfigSource::CommandLine);
EXPECT_EQ(testDir.getValue(), cliEdenDir);
// Set a middle priority item (results same as above)
testDir.setValue(systemEdenDir, ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::CommandLine);
EXPECT_EQ(testDir.getValue(), cliEdenDir);
// Clear current highest priority
testDir.clearValue(ConfigSource::CommandLine);
EXPECT_EQ(testDir.getSource(), ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getValue(), systemEdenDir);
}
TEST_F(ConfigSettingTest, configClearOverRiddenSource) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
AbsolutePath cliEdenDir{"/CLI_DIR"};
AbsolutePath userEdenDir{"/USER_DIR"};
AbsolutePath systemEdenDir{"/SYSTEM_DIR"};
// Initially, it should be default value
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
// Set next higher over-ride priority
testDir.setValue(systemEdenDir, ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getValue(), systemEdenDir);
// Set next higher over-ride priority
testDir.setValue(userEdenDir, ConfigSource::UserConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(testDir.getValue(), userEdenDir);
// Set next higher over-ride priority
testDir.setValue(cliEdenDir, ConfigSource::CommandLine);
EXPECT_EQ(testDir.getSource(), ConfigSource::CommandLine);
EXPECT_EQ(testDir.getValue(), cliEdenDir);
// Clear the middle priority item (no effect on source/value)
testDir.clearValue(ConfigSource::UserConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::CommandLine);
EXPECT_EQ(testDir.getValue(), cliEdenDir);
// Clear the middle priority item (no effect on source/value)
testDir.clearValue(ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::CommandLine);
EXPECT_EQ(testDir.getValue(), cliEdenDir);
// Clear highest priority - back to default
testDir.clearValue(ConfigSource::CommandLine);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
}
namespace {
template <typename T, typename FieldConverter, typename ExpectedType = T>
void checkSet(
ConfigSetting<T, FieldConverter>& setting,
ExpectedType expected,
StringPiece str) {
SCOPED_TRACE(str.str());
std::map<std::string, std::string> attrMap;
auto setResult =
setting.setStringValue(str, attrMap, ConfigSource::UserConfig);
ASSERT_FALSE(setResult.hasError()) << setResult.error();
if constexpr (std::is_floating_point<T>::value) {
EXPECT_FLOAT_EQ(expected, setting.getValue());
} else {
EXPECT_EQ(expected, setting.getValue());
}
}
template <typename T, typename FieldConverter>
void checkSetError(
ConfigSetting<T, FieldConverter>& setting,
StringPiece expectedError,
StringPiece str) {
SCOPED_TRACE(str.str());
std::map<std::string, std::string> attrMap;
auto setResult =
setting.setStringValue(str, attrMap, ConfigSource::UserConfig);
ASSERT_TRUE(setResult.hasError());
EXPECT_EQ(expectedError.str(), setResult.error());
}
} // namespace
TEST_F(ConfigSettingTest, setBool) {
ConfigSetting<bool> defaultTrue{"test:value2", true, nullptr};
EXPECT_EQ(true, defaultTrue.getValue());
ConfigSetting<bool> setting{"test:value", false, nullptr};
EXPECT_EQ(false, setting.getValue());
checkSet(setting, true, "true");
checkSet(setting, true, "1");
checkSet(setting, true, "y");
checkSet(setting, true, "yes");
checkSet(setting, true, "Y");
checkSet(setting, true, "on");
EXPECT_EQ("true", setting.getStringValue());
checkSet(setting, false, "n");
checkSet(setting, false, "0");
checkSet(setting, false, "false");
checkSet(setting, false, "off");
EXPECT_EQ("false", setting.getStringValue());
checkSetError(setting, "Empty input string", "");
checkSetError(setting, "Invalid value for bool: \"bogus\"", "bogus");
checkSetError(
setting,
"Non-whitespace character found after end of conversion: \"yes_and\"",
"yes_and");
}
TEST_F(ConfigSettingTest, setArithmetic) {
ConfigSetting<int> intSetting{"test:value", 1, nullptr};
EXPECT_EQ(1, intSetting.getValue());
checkSet(intSetting, 9, "9");
checkSet(intSetting, 1234, "1234");
checkSetError(intSetting, "Empty input string", "");
checkSetError(intSetting, "Invalid leading character: \"bogus\"", "bogus");
// In the future it might be nice to support parsing hexadecimal input.
checkSetError(
intSetting,
"Non-whitespace character found after end of conversion: \"0x15\"",
"0x15");
ConfigSetting<uint8_t> u8Setting{"test:value", 0, nullptr};
checkSet(u8Setting, 9, "9");
checkSetError(u8Setting, "Overflow during conversion: \"300\"", "300");
checkSetError(u8Setting, "Non-digit character found: \"-10\"", "-10");
ConfigSetting<float> floatSetting{"test:value", 0, nullptr};
checkSet(floatSetting, 123.0f, "123");
checkSet(floatSetting, 0.001f, "0.001");
checkSetError(
floatSetting,
"Non-whitespace character found after end of conversion: \"0.001.9\"",
"0.001.9");
}
TEST_F(ConfigSettingTest, setDuration) {
ConfigSetting<std::chrono::nanoseconds> setting{"test:value", 5ms, nullptr};
EXPECT_EQ(5ms, setting.getValue());
checkSet(setting, 90s, "1m30s");
checkSet(setting, -90s, "-1m30s");
checkSet(setting, 42ns, "42ns");
checkSet(setting, 300s, "5m");
checkSetError(setting, "empty input string", "");
checkSetError(setting, "unknown duration unit specifier", "90");
checkSetError(setting, "non-digit character found", "bogus");
}
TEST_F(ConfigSettingTest, setArray) {
ConfigSetting<std::vector<std::string>> setting{
"test:value", std::vector<std::string>{}, nullptr};
EXPECT_EQ(std::vector<std::string>{}, setting.getValue());
checkSet(setting, std::vector<std::string>{"a"}, "[\"a\"]");
checkSet(setting, std::vector<std::string>{"a", "b"}, "[\"a\", \"b\"]");
checkSet(setting, std::vector<std::string>{}, "[]");
checkSetError(
setting,
"Error parsing an array of strings: Failed to parse value type at line 1",
"");
checkSetError(
setting,
"Error parsing an array of strings: Unidentified trailing character ','--"
"-did you forget a '#'? at line 1",
"\"a\", \"b\", \"c\"");
}
TEST_F(ConfigSettingTest, setArrayDuration) {
ConfigSetting<std::vector<std::chrono::nanoseconds>> setting{
"test:value", std::vector<std::chrono::nanoseconds>{}, nullptr};
checkSet(setting, std::vector<std::chrono::nanoseconds>{90s}, "[\"1m30s\"]");
}
TEST_F(ConfigSettingTest, setArrayOptional) {
ConfigSetting<std::vector<std::optional<std::string>>> setting{
"test:value", std::vector<std::optional<std::string>>{}, nullptr};
checkSet(
setting, std::vector<std::optional<std::string>>{"foo"}, "[\"foo\"]");
}