Skip to content
This repository was archived by the owner on Aug 19, 2019. It is now read-only.

Add unit tests for environment with fake HTTP server.#139

Merged
davidbtucker merged 7 commits intomasterfrom
davidbtucker-http-mocks
Jul 17, 2018
Merged

Add unit tests for environment with fake HTTP server.#139
davidbtucker merged 7 commits intomasterfrom
davidbtucker-http-mocks

Conversation

@davidbtucker
Copy link
Copy Markdown
Contributor

No description provided.

Comment thread src/environment.cc Outdated
http::client::options options;
http::client client(options.timeout(2));
http::client::request request(kGceMetadataServerAddress + path);
http::client::request request(config_.GceMetadataServerAddress() + path);
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread 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"}}));
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

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).

Comment thread 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(); });
Copy link
Copy Markdown
Contributor

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).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated
"{\"client_email\":\"user@example.com\",\"private_key\":\"some_key\"}");
std::string cfg;
Configuration config(std::istringstream(
"CredentialsFile: '" + credentials_file.FullPath().native() + "'\n"
Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor Author

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.

Comment thread test/environment_unittest.cc Outdated
std::string cfg;
Configuration config(std::istringstream(
"CredentialsFile: '" + credentials_file.FullPath().native() + "'\n"
"GceMetadataServerAddress: 'http://invalidaddress'\n"
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

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.

Copy link
Copy Markdown
Contributor Author

@davidbtucker davidbtucker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks PTAL

Comment thread src/environment.cc Outdated
http::client::options options;
http::client client(options.timeout(2));
http::client::request request(kGceMetadataServerAddress + path);
http::client::request request(config_.GceMetadataServerAddress() + path);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread 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"}}));
Copy link
Copy Markdown
Contributor Author

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).

Comment thread 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(); });
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated
"{\"client_email\":\"user@example.com\",\"private_key\":\"some_key\"}");
std::string cfg;
Configuration config(std::istringstream(
"CredentialsFile: '" + credentials_file.FullPath().native() + "'\n"
Copy link
Copy Markdown
Contributor Author

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.

Comment thread test/environment_unittest.cc Outdated
std::string cfg;
Configuration config(std::istringstream(
"CredentialsFile: '" + credentials_file.FullPath().native() + "'\n"
"GceMetadataServerAddress: 'http://invalidaddress'\n"
Copy link
Copy Markdown
Contributor Author

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.

Copy link
Copy Markdown
Contributor

@supriyagarg supriyagarg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown
Contributor

@igorpeshansky igorpeshansky left a 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.

Comment thread 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>({}));
Copy link
Copy Markdown
Contributor

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.

Comment thread src/environment.cc Outdated
Environment::Environment(const Configuration& config)
: config_(config), application_default_credentials_read_(false) {}
: config_(config),
gce_metadata_server_address_(kGceMetadataServerAddress),
Copy link
Copy Markdown
Contributor

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_.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/environment.h Outdated

const Configuration& config_;

std::string gce_metadata_server_address_;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mutable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/environment.h Outdated

const Configuration& config_;

std::string gce_metadata_server_address_;
Copy link
Copy Markdown
Contributor

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_.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/environment.h Outdated
void ReadApplicationDefaultCredentials() const;

// Used to override server address in tests.
void SetGceMetadataServerAddress(const std::string& address);
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated
class FakeServerThread {
public:
FakeServerThread()
: server_(Server::options(handler_).address("127.0.0.1").port("")) {
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged.

Comment thread test/environment_unittest.cc Outdated
handler_.path_responses[path] = response;
}

std::string HostPort() {
Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor Author

@davidbtucker davidbtucker May 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to GetUrl.

Comment thread test/environment_unittest.cc Outdated
connection->set_headers(std::map<std::string, std::string>());
}
}
std::map<std::string, std::string> path_responses;
Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor Author

@davidbtucker davidbtucker May 30, 2018

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.

Copy link
Copy Markdown
Contributor

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.

Comment thread test/environment_unittest.cc Outdated

Handler handler_;
Server server_;
std::thread thread_;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

server_thread_?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated

// Starts a server in a separate thread, allowing it to choose an
// available port.
class FakeServerThread {
Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Copy Markdown
Contributor Author

@davidbtucker davidbtucker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PTAL, thanks!

Comment thread src/environment.cc Outdated
Environment::Environment(const Configuration& config)
: config_(config), application_default_credentials_read_(false) {}
: config_(config),
gce_metadata_server_address_(kGceMetadataServerAddress),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/environment.h Outdated
void ReadApplicationDefaultCredentials() const;

// Used to override server address in tests.
void SetGceMetadataServerAddress(const std::string& address);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/environment.h Outdated

const Configuration& config_;

std::string gce_metadata_server_address_;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/environment.h Outdated

const Configuration& config_;

std::string gce_metadata_server_address_;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated
environment.ReadApplicationDefaultCredentials();
}

static void SetGceMetadataServerAddress(Environment* environment,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated

Handler handler_;
Server server_;
std::thread thread_;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated

// Starts a server in a separate thread, allowing it to choose an
// available port.
class FakeServerThread {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated

// Starts a server in a separate thread, allowing it to choose an
// available port.
class FakeServerThread {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated
connection->set_headers(std::map<std::string, std::string>());
}
}
std::map<std::string, std::string> path_responses;
Copy link
Copy Markdown
Contributor Author

@davidbtucker davidbtucker May 30, 2018

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.

Comment thread src/environment.h Outdated
void ReadApplicationDefaultCredentials() const;

// Used to override server address in tests.
void SetGceMetadataServerAddress(const std::string& address);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated
public:
FakeServerThread()
: server_(Server::options(handler_).address("127.0.0.1").port("")) {
server_.listen();
Copy link
Copy Markdown
Contributor

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?

Comment thread test/environment_unittest.cc Outdated
connection->set_headers(std::map<std::string, std::string>());
}
}
std::map<std::string, std::string> path_responses;
Copy link
Copy Markdown
Contributor

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.

Comment thread src/environment.h

void ReadApplicationDefaultCredentials() const;

void SetMetadataServerUrlForTest(const std::string& url) {
Copy link
Copy Markdown
Contributor

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 '/'...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/environment_unittest.cc Outdated
EXPECT_EQ("some_key", environment.CredentialsPrivateKey());
}

TEST_F(EnvironmentTest, GetMetadataStringWithFakeServer) {
Copy link
Copy Markdown
Contributor

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 GetMetadataStringFakeServer is an implementation detail.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/fake_http_server.cc
namespace google {
namespace testing {

FakeServer::FakeServer()
Copy link
Copy Markdown
Contributor

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)"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/fake_http_server.cc Outdated
}

std::string FakeServer::GetUrl() {
return std::string("http://") + server_.address() + ":" + server_.port() + "/";
Copy link
Copy Markdown
Contributor

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();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/fake_http_server.cc Outdated
handler_.path_responses[path] = response;
}

void FakeServer::Handler::operator() (Server::request const &request,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No space after operator().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread test/fake_http_server.h Outdated

// Handler that maps paths to response strings.
struct Handler {
void operator() (Server::request const &request,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No space after operator().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Copy Markdown
Contributor

@igorpeshansky igorpeshansky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One remaining comment.

Comment thread test/fake_http_server.cc
namespace google {
namespace testing {

FakeServer::FakeServer()
Copy link
Copy Markdown
Contributor

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.

Comment thread src/environment.h
void ReadApplicationDefaultCredentials() const;

// The url must end in a '/'.
void SetMetadataServerUrlForTest(const std::string& url) {
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

Copy link
Copy Markdown
Contributor

@bmoyles0117 bmoyles0117 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown
Contributor

@igorpeshansky igorpeshansky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM :shipit:

@davidbtucker davidbtucker merged commit 8ec2e47 into master Jul 17, 2018
@davidbtucker davidbtucker deleted the davidbtucker-http-mocks branch July 17, 2018 20:34
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants