-
Notifications
You must be signed in to change notification settings - Fork 11
Add unit tests for environment with fake HTTP server. #139
Conversation
src/environment.cc
Outdated
@@ -83,7 +80,7 @@ Environment::Environment(const Configuration& config) | |||
std::string Environment::GetMetadataString(const std::string& path) const { | |||
http::client::options options; | |||
http::client client(options.timeout(2)); | |||
http::client::request request(kGceMetadataServerAddress + path); | |||
http::client::request request(config_.GceMetadataServerAddress() + path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not make this a configuration parameter. There is no reason for anything but our tests to override this. You could instead make this an instance field of the Environment
object, and either add a private constructor that injects it, or a private test-only setter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to not making this a config parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
TEST_F(EnvironmentTest, GetMetadataStringWithFakeServer) { | ||
// Start a server in a separate thread, and allow it to choose an | ||
// available port. | ||
FakeHandler handler(std::map<std::string, std::string>({{"/a/b/c", "hello"}})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be nicer to follow the style of Python testing and add a SetResponse(path, response)
method (maybe even SetResponse(path, code, response)
)... Then you can fold the handler and the options
object into the FakeServer
implementation, rather than expose them to clients.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (did not include code yet).
test/environment_unittest.cc
Outdated
FakeServer::options options(handler); | ||
FakeServer server(options.address("127.0.0.1").port("")); | ||
server.listen(); | ||
std::thread t1([&server] { server.run(); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your fake server is always single-threaded, so you can fold the thread into the server object, regardless (calling join()
in the destructor).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
"{\"client_email\":\"[email protected]\",\"private_key\":\"some_key\"}"); | ||
std::string cfg; | ||
Configuration config(std::istringstream( | ||
"CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary for testing a fake server?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha, it's not! Removed.
test/environment_unittest.cc
Outdated
std::string cfg; | ||
Configuration config(std::istringstream( | ||
"CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" | ||
"GceMetadataServerAddress: 'http://invalidaddress'\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fragile, and breaks in environments where an ISP intercepts invalid lookups:
environment_unittest.cc:171: Failure
Expected equality of these values:
""
environment.GetMetadataString("/a/b/c")
Which is: "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html><head><meta http-equiv=\"refresh\" content=\"0;url=http://searchassist.verizon.com/main?ParticipantID=euekiz39ksg8nwp7iqj2fp5wzfwi5q76&FailedURI=http%3A%2F%2Finvalidaddress%2Fa%2Fb%2Fc&FailureMode=1&Implementation=&AddInType=4&Version=pywr1.0&ClientLocation=us\"/><script type=\"text/javascript\">url=\"http://searchassist.verizon.com/main?ParticipantID=euekiz39ksg8nwp7iqj2fp5wzfwi5q76&FailedURI=http%3A%2F%2Finvalidaddress%2Fa%2Fb%2Fc&FailureMode=1&Implementation=&AddInType=4&Version=pywr1.0&ClientLocation=us\";if(top.location!=location){var w=window,d=document,e=d.documentElement,b=d.body,x=w.innerWidth||e.clientWidth||b.clientWidth,y=w.innerHeight||e.clientHeight||b.clientHeight;url+=\"&w=\"+x+\"&h=\"+y;}window.location.replace(url);</script></head><body></body></html>"
We should instead make the fake server throw boost::system::system_error
on simulated resolution failures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this test altogether, as the other test already exercises this catch clause.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks PTAL
src/environment.cc
Outdated
@@ -83,7 +80,7 @@ Environment::Environment(const Configuration& config) | |||
std::string Environment::GetMetadataString(const std::string& path) const { | |||
http::client::options options; | |||
http::client client(options.timeout(2)); | |||
http::client::request request(kGceMetadataServerAddress + path); | |||
http::client::request request(config_.GceMetadataServerAddress() + path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
TEST_F(EnvironmentTest, GetMetadataStringWithFakeServer) { | ||
// Start a server in a separate thread, and allow it to choose an | ||
// available port. | ||
FakeHandler handler(std::map<std::string, std::string>({{"/a/b/c", "hello"}})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (did not include code yet).
test/environment_unittest.cc
Outdated
FakeServer::options options(handler); | ||
FakeServer server(options.address("127.0.0.1").port("")); | ||
server.listen(); | ||
std::thread t1([&server] { server.run(); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
"{\"client_email\":\"[email protected]\",\"private_key\":\"some_key\"}"); | ||
std::string cfg; | ||
Configuration config(std::istringstream( | ||
"CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha, it's not! Removed.
test/environment_unittest.cc
Outdated
std::string cfg; | ||
Configuration config(std::istringstream( | ||
"CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" | ||
"GceMetadataServerAddress: 'http://invalidaddress'\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this test altogether, as the other test already exercises this catch clause.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some naming/location nits.
test/environment_unittest.cc
Outdated
} else { | ||
// Note: We have to set headers; otherwise, an exception is thrown. | ||
connection->set_status(FakeServer::connection::not_found); | ||
connection->set_headers(std::map<std::string, std::string>({})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unfortunate that this bit is necessary, but we can deal with this later.
src/environment.cc
Outdated
@@ -78,12 +78,14 @@ constexpr const char kGceInstanceResourceType[] = "gce_instance"; | |||
} | |||
|
|||
Environment::Environment(const Configuration& config) | |||
: config_(config), application_default_credentials_read_(false) {} | |||
: config_(config), | |||
gce_metadata_server_address_(kGceMetadataServerAddress), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just call it metadata_server_url_
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/environment.h
Outdated
const Configuration& config_; | ||
|
||
std::string gce_metadata_server_address_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mutable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/environment.h
Outdated
const Configuration& config_; | ||
|
||
std::string gce_metadata_server_address_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is technically cached data as well. Let's put this just before application_default_credentials_read_
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/environment.h
Outdated
@@ -48,8 +48,13 @@ class Environment { | |||
|
|||
void ReadApplicationDefaultCredentials() const; | |||
|
|||
// Used to override server address in tests. | |||
void SetGceMetadataServerAddress(const std::string& address); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SetMetadataServerUrlForTest
, and you can get rid of the comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
class FakeServerThread { | ||
public: | ||
FakeServerThread() | ||
: server_(Server::options(handler_).address("127.0.0.1").port("")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "empty port" behavior seems to be undocumented. Sigh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acknowledged.
test/environment_unittest.cc
Outdated
handler_.path_responses[path] = response; | ||
} | ||
|
||
std::string HostPort() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, why HostPort
and not GetUrl
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to GetUrl
.
test/environment_unittest.cc
Outdated
connection->set_headers(std::map<std::string, std::string>()); | ||
} | ||
} | ||
std::map<std::string, std::string> path_responses; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this map live in the FakeServer
class instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd have to pass in a FakeServer
reference to the Handler
, which seems more complicated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also just pass a const reference to the map... But it's also fine the way it is.
test/environment_unittest.cc
Outdated
|
||
Handler handler_; | ||
Server server_; | ||
std::thread thread_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
server_thread_
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
|
||
// Starts a server in a separate thread, allowing it to choose an | ||
// available port. | ||
class FakeServerThread { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we pull this out to a separate header/.cc
file now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PTAL, thanks!
src/environment.cc
Outdated
@@ -78,12 +78,14 @@ constexpr const char kGceInstanceResourceType[] = "gce_instance"; | |||
} | |||
|
|||
Environment::Environment(const Configuration& config) | |||
: config_(config), application_default_credentials_read_(false) {} | |||
: config_(config), | |||
gce_metadata_server_address_(kGceMetadataServerAddress), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/environment.h
Outdated
@@ -48,8 +48,13 @@ class Environment { | |||
|
|||
void ReadApplicationDefaultCredentials() const; | |||
|
|||
// Used to override server address in tests. | |||
void SetGceMetadataServerAddress(const std::string& address); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/environment.h
Outdated
const Configuration& config_; | ||
|
||
std::string gce_metadata_server_address_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/environment.h
Outdated
const Configuration& config_; | ||
|
||
std::string gce_metadata_server_address_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
@@ -12,9 +13,15 @@ class EnvironmentTest : public ::testing::Test { | |||
static void ReadApplicationDefaultCredentials(const Environment& environment) { | |||
environment.ReadApplicationDefaultCredentials(); | |||
} | |||
|
|||
static void SetGceMetadataServerAddress(Environment* environment, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
|
||
Handler handler_; | ||
Server server_; | ||
std::thread thread_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
|
||
// Starts a server in a separate thread, allowing it to choose an | ||
// available port. | ||
class FakeServerThread { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
|
||
// Starts a server in a separate thread, allowing it to choose an | ||
// available port. | ||
class FakeServerThread { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
connection->set_headers(std::map<std::string, std::string>()); | ||
} | ||
} | ||
std::map<std::string, std::string> path_responses; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd have to pass in a FakeServer
reference to the Handler
, which seems more complicated.
src/environment.h
Outdated
@@ -48,8 +48,13 @@ class Environment { | |||
|
|||
void ReadApplicationDefaultCredentials() const; | |||
|
|||
// Used to override server address in tests. | |||
void SetGceMetadataServerAddress(const std::string& address); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
public: | ||
FakeServerThread() | ||
: server_(Server::options(handler_).address("127.0.0.1").port("")) { | ||
server_.listen(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, can you please add a comment saying exactly that?
test/environment_unittest.cc
Outdated
connection->set_headers(std::map<std::string, std::string>()); | ||
} | ||
} | ||
std::map<std::string, std::string> path_responses; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also just pass a const reference to the map... But it's also fine the way it is.
src/environment.h
Outdated
@@ -48,6 +48,10 @@ class Environment { | |||
|
|||
void ReadApplicationDefaultCredentials() const; | |||
|
|||
void SetMetadataServerUrlForTest(const std::string& url) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could add a comment saying that the url
must end in a '/'
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/environment_unittest.cc
Outdated
@@ -98,4 +105,17 @@ TEST_F(EnvironmentTest, ReadApplicationDefaultCredentialsCaches) { | |||
); | |||
EXPECT_EQ("some_key", environment.CredentialsPrivateKey()); | |||
} | |||
|
|||
TEST_F(EnvironmentTest, GetMetadataStringWithFakeServer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed this one before... I'd just call it GetMetadataString
— FakeServer
is an implementation detail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
namespace google { | ||
namespace testing { | ||
|
||
FakeServer::FakeServer() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about a comment along the lines of "an empty port selects a random available port (undocumented)"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't a class comment — let's move it to where the empty port is passed in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/fake_http_server.cc
Outdated
} | ||
|
||
std::string FakeServer::GetUrl() { | ||
return std::string("http://") + server_.address() + ":" + server_.port() + "/"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Optional]
network::uri_builder builder;
builder.scheme("http").host(server_.address()).port(server_.port()).path("/");
return builder.uri().to_string();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/fake_http_server.cc
Outdated
handler_.path_responses[path] = response; | ||
} | ||
|
||
void FakeServer::Handler::operator() (Server::request const &request, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No space after operator()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/fake_http_server.h
Outdated
|
||
// Handler that maps paths to response strings. | ||
struct Handler { | ||
void operator() (Server::request const &request, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No space after operator()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One remaining comment.
namespace google { | ||
namespace testing { | ||
|
||
FakeServer::FakeServer() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't a class comment — let's move it to where the empty port is passed in.
@@ -48,6 +48,11 @@ class Environment { | |||
|
|||
void ReadApplicationDefaultCredentials() const; | |||
|
|||
// The url must end in a '/'. | |||
void SetMetadataServerUrlForTest(const std::string& url) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this only "ForTest"? It seems like a reasonable enough setter function to remove that suffix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make it clear that the method only exists because we need to override the URL in test code. If we ever need to use it in production code, we can rename the method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
No description provided.