diff --git a/apps/hello-cpp/main.cpp b/apps/hello-cpp/main.cpp index a61ee8bac..e474c1462 100644 --- a/apps/hello-cpp/main.cpp +++ b/apps/hello-cpp/main.cpp @@ -18,12 +18,6 @@ using launchdarkly::LogLevel; int main() { Logger logger(std::make_unique("Hello")); - if (auto num = launchdarkly::foo()) { - LD_LOG(logger, LogLevel::kInfo) << "Got: " << *num << '\n'; - } else { - LD_LOG(logger, LogLevel::kInfo) << "Got nothing\n"; - } - net::io_context ioc; char const* key = std::getenv("STG_SDK_KEY"); diff --git a/bindings/c/src/api.cpp b/bindings/c/src/api.cpp index 8985dc227..50f8df868 100644 --- a/bindings/c/src/api.cpp +++ b/bindings/c/src/api.cpp @@ -1,10 +1,2 @@ #include "launchdarkly/client_side/api.hpp" #include - -bool launchdarkly_foo(int32_t* out_result) { - if (auto val = launchdarkly::foo()) { - *out_result = *val; - return true; - } - return false; -} diff --git a/libs/client-sdk/include/launchdarkly/client_side/api.hpp b/libs/client-sdk/include/launchdarkly/client_side/api.hpp index 9428318e6..38f51c52c 100644 --- a/libs/client-sdk/include/launchdarkly/client_side/api.hpp +++ b/libs/client-sdk/include/launchdarkly/client_side/api.hpp @@ -1,8 +1,56 @@ #pragma once -#include "cstdint" -#include "optional" +#include +#include +#include +#include +#include +#include +#include "config/client.hpp" +#include "context.hpp" +#include "error.hpp" +#include "events/event_processor.hpp" +#include "logger.hpp" +#include "value.hpp" -namespace launchdarkly { -std::optional foo(); -} // namespace launchdarkly +namespace launchdarkly::client_side { +class Client { + public: + Client(client::Config config, Context context); + + using FlagKey = std::string; + [[nodiscard]] std::unordered_map AllFlags() const; + + void Track(std::string event_name, Value data, double metric_value); + + void Track(std::string event_name, Value data); + + void Track(std::string event_name); + + void AsyncFlush(); + + void AsyncIdentify(Context context); + + bool BoolVariation(FlagKey const& key, bool default_value); + + std::string StringVariation(FlagKey const& key, std::string default_value); + + double DoubleVariation(FlagKey const& key, double default_value); + + int IntVariation(FlagKey const& key, int default_value); + + Value JsonVariation(FlagKey const& key, Value default_value); + + private: + void TrackInternal(std::string event_name, + std::optional data, + std::optional metric_value); + + Logger logger_; + std::thread thread_; + boost::asio::io_context ioc_; + Context context_; + std::unique_ptr event_processor_; +}; + +} // namespace launchdarkly::client_side diff --git a/libs/client-sdk/src/api.cpp b/libs/client-sdk/src/api.cpp index e7082cd80..60dcd4fb8 100644 --- a/libs/client-sdk/src/api.cpp +++ b/libs/client-sdk/src/api.cpp @@ -1,13 +1,78 @@ #include "launchdarkly/client_side/api.hpp" - -#include +#include #include +#include + +#include "console_backend.hpp" +#include "events/client_events.hpp" +#include "events/detail/asio_event_processor.hpp" -namespace launchdarkly { +namespace launchdarkly::client_side { -auto const kAnswerToLifeTheUniverseAndEverything = 42; +Client::Client(client::Config config, Context context) + : logger_(config.take_logger()), + context_(std::move(context)), + event_processor_( + std::make_unique( + ioc_.get_executor(), + config.events_config(), + config.service_endpoints(), + config.sdk_key(), + logger_)) {} + +std::unordered_map Client::AllFlags() const { + return {}; +} -std::optional foo() { - return kAnswerToLifeTheUniverseAndEverything; +void Client::TrackInternal(std::string event_name, + std::optional data, + std::optional metric_value) { + event_processor_->AsyncSend(events::TrackEventParams{ + std::chrono::system_clock::now(), std::move(event_name), + context_.kinds_to_keys(), std::move(data), metric_value}); } -} // namespace launchdarkly + +void Client::Track(std::string event_name, Value data, double metric_value) { + this->TrackInternal(std::move(event_name), data, metric_value); +} + +void Client::Track(std::string event_name, Value data) { + this->TrackInternal(std::move(event_name), data, std::nullopt); +} + +void Client::Track(std::string event_name) { + this->TrackInternal(std::move(event_name), std::nullopt, std::nullopt); +} + +void Client::AsyncFlush() { + event_processor_->AsyncFlush(); +} + +void Client::AsyncIdentify(Context context) { + event_processor_->AsyncSend(events::client::IdentifyEventParams{ + std::chrono::system_clock::now(), std::move(context)}); +} + +bool Client::BoolVariation(Client::FlagKey const& key, bool default_value) { + return default_value; +} + +std::string Client::StringVariation(Client::FlagKey const& key, + std::string default_value) { + return default_value; +} + +double Client::DoubleVariation(Client::FlagKey const& key, + double default_value) { + return default_value; +} + +int Client::IntVariation(Client::FlagKey const& key, int default_value) { + return default_value; +} + +Value Client::JsonVariation(Client::FlagKey const& key, Value default_value) { + return default_value; +} + +} // namespace launchdarkly::client_side diff --git a/libs/client-sdk/tests/client_test.cpp b/libs/client-sdk/tests/client_test.cpp new file mode 100644 index 000000000..6f9b275c9 --- /dev/null +++ b/libs/client-sdk/tests/client_test.cpp @@ -0,0 +1,18 @@ +#include +#include +#include "context_builder.hpp" +using namespace launchdarkly; + +TEST(ClientTest, ConstructClientWithConfig) { + tl::expected config = + client::ConfigBuilder("sdk-123").build(); + + ASSERT_TRUE(config); + + auto context = ContextBuilder().kind("cat", "shadow").build(); + + client_side::Client client(std::move(*config), context); + + ASSERT_TRUE(client.AllFlags().empty()); + ASSERT_TRUE(client.BoolVariation("cat-food", true)); +} diff --git a/libs/common/include/config/client.hpp b/libs/common/include/config/client.hpp index e6091ba1b..2f1e3c7fd 100644 --- a/libs/common/include/config/client.hpp +++ b/libs/common/include/config/client.hpp @@ -9,6 +9,7 @@ namespace launchdarkly::client { using SDK = config::detail::ClientSDK; +using Defaults = config::detail::Defaults; using ApplicationInfo = config::detail::ApplicationInfo; using Endpoints = config::detail::EndpointsBuilder; using ConfigBuilder = config::detail::ConfigBuilder; diff --git a/libs/common/include/config/detail/config.hpp b/libs/common/include/config/detail/config.hpp index a9bb53a87..51bd9b828 100644 --- a/libs/common/include/config/detail/config.hpp +++ b/libs/common/include/config/detail/config.hpp @@ -4,6 +4,7 @@ #include "config/detail/endpoints_builder.hpp" #include "config/detail/events_builder.hpp" #include "config/detail/http_properties.hpp" +#include "logger.hpp" namespace launchdarkly::config::detail { @@ -14,20 +15,41 @@ namespace launchdarkly::config::detail { */ template struct Config { - std::string sdk_key; - bool offline; - detail::EndpointsBuilder service_endpoints_builder; - std::optional application_tag; - detail::EventsBuilder events_builder; - DataSourceConfig data_source_config; - detail::HttpProperties http_properties; + public: Config(std::string sdk_key, bool offline, - detail::EndpointsBuilder service_endpoints_builder, - detail::EventsBuilder events_builder, + launchdarkly::Logger logger, + ServiceEndpoints endpoints, + Events events, std::optional application_tag, DataSourceConfig data_source_config, detail::HttpProperties http_properties); + + std::string const& sdk_key() const; + + ServiceEndpoints const& service_endpoints() const; + + Events const& events_config() const; + + std::optional const& application_tag() const; + + DataSourceConfig const& data_source_config() const; + + HttpProperties const& http_properties() const; + + bool offline() const; + + Logger take_logger(); + + private: + std::string sdk_key_; + bool offline_; + launchdarkly::Logger logger_; + ServiceEndpoints service_endpoints_; + std::optional application_tag_; + Events events_; + DataSourceConfig data_source_config_; + detail::HttpProperties http_properties_; }; } // namespace launchdarkly::config::detail diff --git a/libs/common/include/config/detail/config_builder.hpp b/libs/common/include/config/detail/config_builder.hpp index 888098e43..fba97ddb1 100644 --- a/libs/common/include/config/detail/config_builder.hpp +++ b/libs/common/include/config/detail/config_builder.hpp @@ -2,12 +2,14 @@ #include #include +#include #include "config/detail/application_info.hpp" #include "config/detail/config.hpp" #include "config/detail/data_source_builder.hpp" #include "config/detail/endpoints_builder.hpp" #include "config/detail/events_builder.hpp" #include "config/detail/http_properties_builder.hpp" + #include "logger.hpp" namespace launchdarkly::config::detail { @@ -22,7 +24,7 @@ class ConfigBuilder { public: using EndpointsBuilder = detail::EndpointsBuilder; using EventsBuilder = detail::EventsBuilder; - using ConfigType = detail::Config; + using ConfigResult = tl::expected, Error>; using DataSourceBuilder = detail::DataSourceBuilder; using HttpPropertiesBuilder = detail::HttpPropertiesBuilder; /** @@ -30,7 +32,6 @@ class ConfigBuilder { * @param sdk_key SDK Key. */ ConfigBuilder(std::string sdk_key); - /** * To customize the endpoints the SDK uses for streaming, polling, and * events, pass in an EndpointsBuilder. @@ -83,7 +84,7 @@ class ConfigBuilder { * Builds a Configuration, suitable for passing into an instance of Client. * @return */ - ConfigType build(Logger& logger) const; + ConfigResult build() const; private: std::string sdk_key_; diff --git a/libs/common/include/config/detail/defaults.hpp b/libs/common/include/config/detail/defaults.hpp index 709fb225d..bbf74e6f3 100644 --- a/libs/common/include/config/detail/defaults.hpp +++ b/libs/common/include/config/detail/defaults.hpp @@ -56,6 +56,8 @@ struct Defaults { static DataSourceConfig data_source_config() { return {Defaults::streaming_config(), false, false}; } + + static bool offline() { return Defaults::offline(); } }; template <> @@ -83,6 +85,8 @@ struct Defaults { static DataSourceConfig data_source_config() { return {Defaults::streaming_config()}; } + + static bool offline() { return Defaults::offline(); } }; } // namespace launchdarkly::config::detail diff --git a/libs/common/include/config/detail/http_properties.hpp b/libs/common/include/config/detail/http_properties.hpp index e5aa4f03f..753d081c9 100644 --- a/libs/common/include/config/detail/http_properties.hpp +++ b/libs/common/include/config/detail/http_properties.hpp @@ -29,4 +29,6 @@ class HttpProperties final { // TODO: Proxy. }; +bool operator==(HttpProperties const& lhs, HttpProperties const& rhs); + } // namespace launchdarkly::config::detail diff --git a/libs/common/include/config/server.hpp b/libs/common/include/config/server.hpp index b49b5adcf..5541c7c72 100644 --- a/libs/common/include/config/server.hpp +++ b/libs/common/include/config/server.hpp @@ -9,6 +9,7 @@ namespace launchdarkly::server { using SDK = config::detail::ServerSDK; +using Defaults = config::detail::Defaults; using ApplicationInfo = config::detail::ApplicationInfo; using Endpoints = config::detail::EndpointsBuilder; using ConfigBuilder = config::detail::ConfigBuilder; diff --git a/libs/common/include/error.hpp b/libs/common/include/error.hpp index 9557caa6e..2ee542b6e 100644 --- a/libs/common/include/error.hpp +++ b/libs/common/include/error.hpp @@ -20,6 +20,8 @@ enum class Error : std::uint32_t { kConfig_Events_ZeroCapacity = 300, + kConfig_SDKKey_Empty = 400, + /* Client-side errors: 10000-19999 */ /* Server-side errors: 20000-29999 */ diff --git a/libs/common/src/config/config.cpp b/libs/common/src/config/config.cpp index d57fea3db..7e4117bd6 100644 --- a/libs/common/src/config/config.cpp +++ b/libs/common/src/config/config.cpp @@ -7,18 +7,60 @@ namespace launchdarkly::config::detail { template Config::Config(std::string sdk_key, bool offline, - detail::EndpointsBuilder service_endpoints_builder, - detail::EventsBuilder events_builder, + Logger logger, + ServiceEndpoints service_endpoints, + Events events, std::optional application_tag, DataSourceConfig data_source_config, detail::HttpProperties http_properties) - : sdk_key(std::move(sdk_key)), - offline(offline), - service_endpoints_builder(std::move(service_endpoints_builder)), - events_builder(std::move(events_builder)), - application_tag(std::move(application_tag)), - data_source_config(std::move(data_source_config)), - http_properties(std::move(http_properties)) {} + : sdk_key_(std::move(sdk_key)), + logger_(std::move(logger)), + offline_(offline), + service_endpoints_(std::move(service_endpoints)), + events_(std::move(events)), + application_tag_(std::move(application_tag)), + data_source_config_(std::move(data_source_config)), + http_properties_(std::move(http_properties)) {} + +template +std::string const& Config::sdk_key() const { + return sdk_key_; +} + +template +ServiceEndpoints const& Config::service_endpoints() const { + return service_endpoints_; +} + +template +Events const& Config::events_config() const { + return events_; +} + +template +std::optional const& Config::application_tag() const { + return application_tag_; +} + +template +DataSourceConfig const& Config::data_source_config() const { + return data_source_config_; +} + +template +HttpProperties const& Config::http_properties() const { + return http_properties_; +} + +template +bool Config::offline() const { + return offline_; +} + +template +Logger Config::take_logger() { + return std::move(logger_); +} template class Config; template class Config; diff --git a/libs/common/src/config/config_builder.cpp b/libs/common/src/config/config_builder.cpp index 40df43ebb..f193d07b9 100644 --- a/libs/common/src/config/config_builder.cpp +++ b/libs/common/src/config/config_builder.cpp @@ -1,12 +1,11 @@ #include "config/detail/config_builder.hpp" #include "config/detail/defaults.hpp" +#include "console_backend.hpp" namespace launchdarkly::config::detail { template ConfigBuilder::ConfigBuilder(std::string sdk_key) - : sdk_key_(std::move(sdk_key)), - offline_(std::nullopt), - service_endpoints_builder_(std::nullopt) {} + : sdk_key_(std::move(sdk_key)) {} template ConfigBuilder& ConfigBuilder::service_endpoints( @@ -50,16 +49,30 @@ ConfigBuilder& ConfigBuilder::http_properties( } template -typename ConfigBuilder::ConfigType ConfigBuilder::build( - Logger& logger) const { - auto key = sdk_key_; - auto offline = offline_.value_or(Defaults::offline()); - auto endpoints = service_endpoints_builder_.value_or(EndpointsBuilder()); - auto events = events_builder_.value_or(EventsBuilder()); +[[nodiscard]] typename ConfigBuilder::ConfigResult +ConfigBuilder::build() const { + auto sdk_key = sdk_key_; + if (sdk_key.empty()) { + return tl::make_unexpected(Error::kConfig_SDKKey_Empty); + } + auto offline = offline_.value_or(Defaults::offline()); + auto endpoints_config = + service_endpoints_builder_.value_or(EndpointsBuilder()).build(); + if (!endpoints_config) { + return tl::make_unexpected(endpoints_config.error()); + } + auto events_config = events_builder_.value_or(EventsBuilder()).build(); + if (!events_config) { + return tl::make_unexpected(events_config.error()); + } + + Logger logger{std::make_unique("LaunchDarkly")}; + std::optional app_tag; if (application_info_builder_) { app_tag = application_info_builder_->build(logger); } + auto data_source_config = data_source_builder_ ? data_source_builder_.value().build() : Defaults::data_source_config(); @@ -67,11 +80,14 @@ typename ConfigBuilder::ConfigType ConfigBuilder::build( auto http_properties = http_properties_builder_ ? http_properties_builder_.value().build() : Defaults::http_properties(); - return {std::move(key), + + return {tl::in_place, + sdk_key, offline, - std::move(endpoints), - std::move(events), - std::move(app_tag), + std::move(logger), + *endpoints_config, + *events_config, + app_tag, std::move(data_source_config), std::move(http_properties)}; } diff --git a/libs/common/src/config/http_properties.cpp b/libs/common/src/config/http_properties.cpp index 588a0e201..131e934d6 100644 --- a/libs/common/src/config/http_properties.cpp +++ b/libs/common/src/config/http_properties.cpp @@ -29,4 +29,11 @@ std::map const& HttpProperties::base_headers() const { return base_headers_; } +bool operator==(HttpProperties const& lhs, HttpProperties const& rhs) { + return lhs.read_timeout() == rhs.read_timeout() && + lhs.connect_timeout() == rhs.connect_timeout() && + lhs.base_headers() == rhs.base_headers() && + lhs.user_agent() == rhs.user_agent(); +} + } // namespace launchdarkly::config::detail diff --git a/libs/common/src/error.cpp b/libs/common/src/error.cpp index 099e6cd64..54340759c 100644 --- a/libs/common/src/error.cpp +++ b/libs/common/src/error.cpp @@ -28,6 +28,8 @@ char const* ErrorToString(Error err) { return "application info: the value contains invalid characters"; case Error::kConfig_Events_ZeroCapacity: return "events: capacity must be non-zero"; + case Error::kConfig_SDKKey_Empty: + return "sdk key: cannot be empty"; case Error::kMax: break; } diff --git a/libs/common/tests/config_builder_test.cpp b/libs/common/tests/config_builder_test.cpp index 25baa5243..cf6bdd07c 100644 --- a/libs/common/tests/config_builder_test.cpp +++ b/libs/common/tests/config_builder_test.cpp @@ -15,31 +15,33 @@ class ConfigBuilderTest TEST_F(ConfigBuilderTest, DefaultConstruction_ClientConfig) { using namespace launchdarkly::client; ConfigBuilder builder("sdk-123"); - Config cfg = builder.build(logger); - ASSERT_EQ(cfg.sdk_key, "sdk-123"); + auto cfg = builder.build(); + ASSERT_TRUE(cfg); + ASSERT_EQ(cfg->sdk_key(), "sdk-123"); } TEST_F(ConfigBuilderTest, DefaultConstruction_ServerConfig) { using namespace launchdarkly::server; ConfigBuilder builder("sdk-123"); - Config cfg = builder.build(logger); - ASSERT_EQ(cfg.sdk_key, "sdk-123"); + auto cfg = builder.build(); + ASSERT_TRUE(cfg); + ASSERT_EQ(cfg->sdk_key(), "sdk-123"); } TEST_F(ConfigBuilderTest, DefaultConstruction_UsesDefaultEndpointsIfNotSupplied) { using namespace launchdarkly::client; ConfigBuilder builder("sdk-123"); - Config cfg = builder.build(logger); - ASSERT_EQ(cfg.service_endpoints_builder, ConfigBuilder::EndpointsBuilder()); + auto cfg = builder.build(); + ASSERT_EQ(cfg->service_endpoints(), Defaults::endpoints()); } TEST_F(ConfigBuilderTest, DefaultConstruction_UsesDefaultOfflineModeIfNotSupplied) { using namespace launchdarkly::client; ConfigBuilder builder("sdk-123"); - Config cfg = builder.build(logger); - ASSERT_FALSE(cfg.offline); + auto cfg = builder.build(); + ASSERT_EQ(cfg->offline(), Defaults::offline()); } // This test should exercise all of the config options. @@ -51,12 +53,14 @@ TEST_F(ConfigBuilderTest, CustomBuilderReflectsChanges) { .service_endpoints(Endpoints().relay_proxy("foo")) .application_info( ApplicationInfo().app_identifier("bar").app_version("baz")) - .build(logger); - - ASSERT_EQ(config.sdk_key, "sdk-123"); - ASSERT_TRUE(config.offline); - ASSERT_EQ(config.service_endpoints_builder, Endpoints().relay_proxy("foo")); - ASSERT_EQ(config.application_tag, + .build(); + + ASSERT_TRUE(config); + ASSERT_EQ(config->sdk_key(), "sdk-123"); + ASSERT_TRUE(config->offline()); + ASSERT_EQ(config->service_endpoints(), + Endpoints().relay_proxy("foo").build()); + ASSERT_EQ(config->application_tag(), "application-id/bar application-version/baz"); } @@ -64,14 +68,14 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_ClientConfig_UsesDefaulDataSourceConfig) { using namespace launchdarkly::client; ConfigBuilder builder("sdk-123"); - Config cfg = builder.build(logger); + auto cfg = builder.build(); - EXPECT_FALSE(cfg.data_source_config.with_reasons); - EXPECT_FALSE(cfg.data_source_config.use_report); + EXPECT_FALSE(cfg->data_source_config().with_reasons); + EXPECT_FALSE(cfg->data_source_config().use_report); // Should be streaming with a 1 second initial retry; EXPECT_EQ(std::chrono::milliseconds{1000}, boost::get( - cfg.data_source_config.method) + cfg->data_source_config().method) .initial_reconnect_delay); } @@ -79,12 +83,12 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_ServerConfig_UsesDefaulDataSourceConfig) { using namespace launchdarkly::server; ConfigBuilder builder("sdk-123"); - Config cfg = builder.build(logger); + auto cfg = builder.build(); // Should be streaming with a 1 second initial retry; EXPECT_EQ(std::chrono::milliseconds{1000}, boost::get( - cfg.data_source_config.method) + cfg->data_source_config().method) .initial_reconnect_delay); } @@ -96,11 +100,11 @@ TEST_F(ConfigBuilderTest, ServerConfig_CanSetDataSource) { ConfigBuilder::DataSourceBuilder::Streaming().initial_reconnect_delay( std::chrono::milliseconds{5000}))); - Config cfg = builder.build(logger); + auto cfg = builder.build(); EXPECT_EQ(std::chrono::milliseconds{5000}, boost::get( - cfg.data_source_config.method) + cfg->data_source_config().method) .initial_reconnect_delay); } @@ -116,13 +120,14 @@ TEST_F(ConfigBuilderTest, ClientConfig_CanSetDataSource) { .use_report(true) .with_reasons(true)); - Config cfg = builder.build(logger); + auto cfg = builder.build(); + ASSERT_TRUE(cfg); - EXPECT_TRUE(cfg.data_source_config.use_report); - EXPECT_TRUE(cfg.data_source_config.with_reasons); + EXPECT_TRUE(cfg->data_source_config().use_report); + EXPECT_TRUE(cfg->data_source_config().with_reasons); EXPECT_EQ(std::chrono::milliseconds{5000}, boost::get( - cfg.data_source_config.method) + cfg->data_source_config().method) .initial_reconnect_delay); } @@ -130,24 +135,17 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_ClientConfig_UsesDefaultHttpProperties) { using namespace launchdarkly::client; ConfigBuilder builder("sdk-123"); - Config cfg = builder.build(logger); + auto cfg = builder.build(); - EXPECT_EQ("CppClient/TODO", cfg.http_properties.user_agent()); - EXPECT_EQ(10000, cfg.http_properties.read_timeout().count()); - EXPECT_EQ(10000, cfg.http_properties.connect_timeout().count()); - EXPECT_TRUE(cfg.http_properties.base_headers().empty()); + ASSERT_EQ(cfg->http_properties(), Defaults::http_properties()); } TEST_F(ConfigBuilderTest, DefaultConstruction_ServerConfig_UsesDefaultHttpProperties) { using namespace launchdarkly::server; ConfigBuilder builder("sdk-123"); - Config cfg = builder.build(logger); - - EXPECT_EQ("CppServer/TODO", cfg.http_properties.user_agent()); - EXPECT_EQ(10000, cfg.http_properties.read_timeout().count()); - EXPECT_EQ(2000, cfg.http_properties.connect_timeout().count()); - EXPECT_TRUE(cfg.http_properties.base_headers().empty()); + auto cfg = builder.build(); + ASSERT_EQ(cfg->http_properties(), Defaults::http_properties()); } TEST_F(ConfigBuilderTest, DefaultConstruction_CanSetHttpProperties) { @@ -162,12 +160,13 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_CanSetHttpProperties) { .custom_headers( std::map{{"color", "green"}})); - Config cfg = builder.build(logger); + auto cfg = builder.build(); + ASSERT_TRUE(cfg); - EXPECT_EQ("CppClient/TODO", cfg.http_properties.user_agent()); - EXPECT_EQ(123456, cfg.http_properties.read_timeout().count()); - EXPECT_EQ(1234, cfg.http_properties.connect_timeout().count()); - EXPECT_EQ("potato/2.0-chip", - cfg.http_properties.base_headers().at("X-LaunchDarkly-Wrapper")); - EXPECT_EQ("green", cfg.http_properties.base_headers().at("color")); + EXPECT_EQ("CppClient/TODO", cfg->http_properties().user_agent()); + EXPECT_EQ(123456, cfg->http_properties().read_timeout().count()); + EXPECT_EQ(1234, cfg->http_properties().connect_timeout().count()); + EXPECT_EQ("potato/2.0-chip", cfg->http_properties().base_headers().at( + "X-LaunchDarkly-Wrapper")); + EXPECT_EQ("green", cfg->http_properties().base_headers().at("color")); }