diff --git a/src/api_server.cc b/src/api_server.cc index 3b347c68..6768b9a4 100644 --- a/src/api_server.cc +++ b/src/api_server.cc @@ -31,7 +31,7 @@ MetadataApiServer::Dispatcher::Dispatcher( void MetadataApiServer::Dispatcher::operator()( const HttpServer::request& request, - std::shared_ptr conn) { + std::shared_ptr conn) const { if (verbose_) { LOG(INFO) << "Dispatcher called: " << request.method << " " << request.destination @@ -40,7 +40,7 @@ void MetadataApiServer::Dispatcher::operator()( } // Look for the longest match first. This means going backwards through // the map, since strings are sorted in lexicographical order. - for (auto it = handlers_.crbegin(); it != handlers_.crend(); --it) { + for (auto it = handlers_.crbegin(); it != handlers_.crend(); ++it) { const std::string& method = it->first.first; const std::string& prefix = it->first.second; #ifdef VERBOSE @@ -61,7 +61,7 @@ void MetadataApiServer::Dispatcher::operator()( } } -void MetadataApiServer::Dispatcher::log(const HttpServer::string_type& info) { +void MetadataApiServer::Dispatcher::log(const HttpServer::string_type& info) const { LOG(ERROR) << info; } diff --git a/src/api_server.h b/src/api_server.h index efbcad7f..850eb8be 100644 --- a/src/api_server.h +++ b/src/api_server.h @@ -44,6 +44,8 @@ class MetadataApiServer { ~MetadataApiServer(); private: + friend class ApiServerTest; + class Dispatcher; using HttpServer = http::server; using Handler = std::function, Handler>; Dispatcher(const HandlerMap& handlers, bool verbose); void operator()(const HttpServer::request& request, - std::shared_ptr conn); - void log(const HttpServer::string_type& info); + std::shared_ptr conn) const; + void log(const HttpServer::string_type& info) const; private: // A mapping from a method/prefix pair to the handler function. // Order matters: later entries override earlier ones. diff --git a/test/Makefile b/test/Makefile index e46b57ab..b729dfda 100644 --- a/test/Makefile +++ b/test/Makefile @@ -24,7 +24,7 @@ YAML_CPP_LIBS=$(YAML_CPP_LIBDIR)/libyaml-cpp.a CPPFLAGS+= \ -DENABLE_KUBERNETES_METADATA \ -isystem $(GTEST_DIR)/include -I$(GMOCK_DIR)/include \ - -I$(YAML_CPP_DIR)/include + -I$(CPP_NETLIB_DIR) -I$(NETWORK_URI_DIR)/include -I$(YAML_CPP_DIR)/include CXXFLAGS=\ -std=c++11 -g -pthread -Wno-write-strings -Wno-deprecated LDFLAGS=-L$(CPP_NETLIB_LIBDIR) -L$(NETWORK_URI_LIBDIR) -L$(YAML_CPP_LIBDIR) @@ -47,6 +47,7 @@ TEST_DIR=. TEST_SOURCES=$(wildcard $(TEST_DIR)/*_unittest.cc) TEST_OBJS=$(TEST_SOURCES:$(TEST_DIR)/%.cc=%.o) TESTS=\ + api_server_unittest \ base64_unittest \ configuration_unittest \ environment_unittest \ @@ -62,7 +63,7 @@ TESTS=\ GTEST_LIB=gtest_lib.a -all: $(TESTS) +all: $(SRC_DIR)/build-cpp-netlib $(SRC_DIR)/build-yaml-cpp $(TESTS) # TODO: Running the test prints "Running main() from gtest_main.cc". # Figure out how to fix this. @@ -83,13 +84,17 @@ init-submodules: git submodule update --init $(GTEST_MODULE) touch init-submodules -$(CPP_NETLIB_LIBS): - cd $(SRC_DIR) && $(MAKE) $@ +$(SRC_DIR)/init-submodules: + cd $(SRC_DIR) && $(MAKE) $(@:$(SRC_DIR)/%=%) + +$(SRC_DIR)/build-cpp-netlib $(SRC_DIR)/build-yaml-cpp: $(SRC_DIR)/init-submodules + cd $(SRC_DIR) && $(MAKE) $(@:$(SRC_DIR)/%=%) -$(YAML_CPP_LIBS): - cd $(SRC_DIR) && $(MAKE) $@ +$(CPP_NETLIB_LIBS): $(SRC_DIR)/build-cpp-netlib -$(SRC_DIR)/%.o: $(SRC_DIR)/%.cc +$(YAML_CPP_LIBS): $(SRC_DIR)/build-yaml-cpp + +$(SRC_DIR)/%.o: $(SRC_DIR)/build-cpp-netlib $(SRC_DIR)/build-yaml-cpp $(SRC_DIR)/%.cc cd $(SRC_DIR) && $(MAKE) $(@:$(SRC_DIR)/%=%) $(GTEST_SOURCEDIR)/gtest-all.cc $(GTEST_SOURCEDIR)/gtest_main.cc: init-submodules @@ -106,8 +111,11 @@ $(GTEST_LIB): gtest-all.o gtest_main.o $(TESTS): $(GTEST_LIB) $(CPP_NETLIB_LIBS) $(YAML_CPP_LIBS) # All unittest objects depend on GTEST_LIB. -$(TESTS:%=%.o): $(GTEST_LIB) +# Some headers need CPP_NETLIB_LIBS and YAML_CPP_LIBS. +$(TESTS:%=%.o): $(GTEST_LIB) $(CPP_NETLIB_LIBS) $(YAML_CPP_LIBS) +api_server_unittest: api_server_unittest.o $(SRC_DIR)/api_server.o $(SRC_DIR)/configuration.o $(SRC_DIR)/store.o $(SRC_DIR)/json.o $(SRC_DIR)/resource.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o + $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ base64_unittest: base64_unittest.o $(SRC_DIR)/base64.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ configuration_unittest: configuration_unittest.o $(SRC_DIR)/configuration.o diff --git a/test/api_server_unittest.cc b/test/api_server_unittest.cc new file mode 100644 index 00000000..f3e5e334 --- /dev/null +++ b/test/api_server_unittest.cc @@ -0,0 +1,82 @@ +#include "../src/api_server.h" +#include "../src/configuration.h" +#include "../src/store.h" +#include "gtest/gtest.h" + +namespace google { + +class ApiServerTest : public ::testing::Test { + protected: + using Dispatcher = MetadataApiServer::Dispatcher; + std::unique_ptr CreateDispatcher( + const std::map, + std::function>& handlers) { + Dispatcher::HandlerMap handler_map; + for (const auto& element : handlers) { + std::function handler = element.second; + handler_map.emplace(element.first, [handler]( + const MetadataApiServer::HttpServer::request& request, + std::shared_ptr conn) { + handler(); + }); + } + return std::unique_ptr(new Dispatcher(handler_map, false)); + } + + void InvokeDispatcher( + const std::unique_ptr& dispatcher, + const std::string& method, const std::string& path) { + MetadataApiServer::HttpServer::request request; + request.method = method; + request.destination = path; + (*dispatcher)(request, nullptr); + } +}; + +TEST_F(ApiServerTest, DispatcherMatchesFullPath) { + bool handler_called = false; + bool bad_handler_called = false; + std::unique_ptr dispatcher = CreateDispatcher({ + {{"GET", "/testPath/"}, [&handler_called]() { + handler_called = true; + }}, + {{"GET", "/badPath/"}, [&bad_handler_called]() { + bad_handler_called = true; + }}, + }); + + InvokeDispatcher(dispatcher, "GET", "/testPath/"); + EXPECT_TRUE(handler_called); + EXPECT_FALSE(bad_handler_called); +} + +TEST_F(ApiServerTest, DispatcherUnmatchedMethod) { + bool handler_called = false; + std::unique_ptr dispatcher = CreateDispatcher({ + {{"GET", "/testPath/"}, [&handler_called]() { + handler_called = true; + }}, + }); + + InvokeDispatcher(dispatcher, "POST", "/testPath/"); + EXPECT_FALSE(handler_called); +} + +TEST_F(ApiServerTest, DispatcherSubstringCheck) { + bool handler_called = false; + std::unique_ptr dispatcher = CreateDispatcher({ + {{"GET", "/testPath/"}, [&handler_called]() { + handler_called = true; + }}, + }); + + InvokeDispatcher(dispatcher, "GET", "/testPathFoo/"); + EXPECT_FALSE(handler_called); + InvokeDispatcher(dispatcher, "GET", "/test/"); + EXPECT_FALSE(handler_called); + InvokeDispatcher(dispatcher, "GET", "/testFooPath/"); + EXPECT_FALSE(handler_called); + InvokeDispatcher(dispatcher, "GET", "/testPath/subPath/"); + EXPECT_TRUE(handler_called); +} +} // namespace google