diff --git a/src/core/uritemplate/include/sourcemeta/core/uritemplate_router.h b/src/core/uritemplate/include/sourcemeta/core/uritemplate_router.h index 5bc8c20e7..f140a2630 100644 --- a/src/core/uritemplate/include/sourcemeta/core/uritemplate_router.h +++ b/src/core/uritemplate/include/sourcemeta/core/uritemplate_router.h @@ -11,6 +11,7 @@ #include // std::function #include // std::unique_ptr #include // std::span +#include // std::string #include // std::string_view #include // std::pair #include // std::variant @@ -99,9 +100,12 @@ class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouter { [[nodiscard]] auto arguments() const noexcept -> const std::vector>> &; + /// Access the base path prefix + [[nodiscard]] auto base_path() const noexcept -> std::string_view; + private: Node root_; - std::string_view base_path_; + std::string base_path_; std::vector>> arguments_; }; @@ -109,25 +113,6 @@ class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouter { /// A read-only view of a serialized URI Template router class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouterView { public: - /// A serialized node in the binary format -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4324) -#endif - struct alignas(8) Node { - std::uint32_t string_offset; - std::uint32_t string_length; - std::uint32_t first_literal_child; - std::uint32_t literal_child_count; - std::uint32_t variable_child; - URITemplateRouter::NodeType type; - std::uint8_t padding; - URITemplateRouter::Identifier identifier; - }; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - /// Save a router to a binary file static auto save(const URITemplateRouter &router, const std::filesystem::path &path) -> void; @@ -153,6 +138,9 @@ class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouterView { const URITemplateRouter::ArgumentCallback &callback) const -> void; + /// Access the base path prefix + [[nodiscard]] auto base_path() const noexcept -> std::string_view; + private: std::vector data_; }; diff --git a/src/core/uritemplate/uritemplate_router.cc b/src/core/uritemplate/uritemplate_router.cc index c4b44d08c..4f6bfc965 100644 --- a/src/core/uritemplate/uritemplate_router.cc +++ b/src/core/uritemplate/uritemplate_router.cc @@ -97,6 +97,16 @@ inline auto extract_segment(const char *start, const char *end) URITemplateRouter::URITemplateRouter(const std::string_view base_path) : base_path_{base_path} { assert(this->base_path_.empty() || this->base_path_.front() == '/'); + const auto last = this->base_path_.find_last_not_of('/'); + if (last == std::string::npos) { + this->base_path_.clear(); + } else { + this->base_path_.erase(last + 1); + } +} + +auto URITemplateRouter::base_path() const noexcept -> std::string_view { + return this->base_path_; } auto URITemplateRouter::add(const std::string_view uri_template, diff --git a/src/core/uritemplate/uritemplate_router_view.cc b/src/core/uritemplate/uritemplate_router_view.cc index 2a2b876ac..c3481f24a 100644 --- a/src/core/uritemplate/uritemplate_router_view.cc +++ b/src/core/uritemplate/uritemplate_router_view.cc @@ -14,7 +14,7 @@ namespace sourcemeta::core { namespace { constexpr std::uint32_t ROUTER_MAGIC = 0x52544552; // "RTER" -constexpr std::uint32_t ROUTER_VERSION = 2; +constexpr std::uint32_t ROUTER_VERSION = 3; constexpr std::uint32_t NO_CHILD = std::numeric_limits::max(); // Type tags for argument value serialization @@ -28,6 +28,8 @@ struct RouterHeader { std::uint32_t node_count; std::uint32_t string_table_offset; std::uint32_t arguments_offset; + std::uint32_t base_path_offset; + std::uint32_t base_path_length; }; struct ArgumentEntryHeader { @@ -36,9 +38,20 @@ struct ArgumentEntryHeader { std::uint32_t blob_length; }; +struct alignas(8) SerializedNode { + std::uint32_t string_offset; + std::uint32_t string_length; + std::uint32_t first_literal_child; + std::uint32_t literal_child_count; + std::uint32_t variable_child; + URITemplateRouter::NodeType type; + std::uint8_t padding; + URITemplateRouter::Identifier identifier; +}; + // Binary search for a literal child matching the given segment inline auto binary_search_literal_children( - const URITemplateRouterView::Node *nodes, const char *string_table, + const SerializedNode *nodes, const char *string_table, const std::size_t string_table_size, const std::uint32_t first_child, const std::uint32_t child_count, const char *segment, const std::uint32_t segment_length) noexcept -> std::uint32_t { @@ -82,7 +95,7 @@ inline auto binary_search_literal_children( auto URITemplateRouterView::save(const URITemplateRouter &router, const std::filesystem::path &path) -> void { - std::vector nodes; + std::vector nodes; std::string string_table; std::queue queue; std::unordered_map @@ -90,7 +103,7 @@ auto URITemplateRouterView::save(const URITemplateRouter &router, const auto &root = router.root(); - Node root_serialized{}; + SerializedNode root_serialized{}; root_serialized.string_offset = 0; root_serialized.string_length = 0; root_serialized.type = URITemplateRouter::NodeType::Root; @@ -125,7 +138,7 @@ auto URITemplateRouterView::save(const URITemplateRouter &router, const auto *node = queue.front(); queue.pop(); - Node serialized{}; + SerializedNode serialized{}; serialized.string_offset = static_cast(string_table.size()); serialized.type = node->type; serialized.string_length = static_cast(node->value.size()); @@ -224,14 +237,22 @@ auto URITemplateRouterView::save(const URITemplateRouter &router, argument_entries.push_back(entry); } + // Append the base path to the string table + const auto base_path_string_offset = + static_cast(string_table.size()); + const auto base_path_value = router.base_path(); + string_table.append(base_path_value.data(), base_path_value.size()); + RouterHeader header{}; header.magic = ROUTER_MAGIC; header.version = ROUTER_VERSION; header.node_count = static_cast(nodes.size()); header.string_table_offset = static_cast( - sizeof(RouterHeader) + nodes.size() * sizeof(Node)); + sizeof(RouterHeader) + nodes.size() * sizeof(SerializedNode)); header.arguments_offset = static_cast( header.string_table_offset + string_table.size()); + header.base_path_offset = base_path_string_offset; + header.base_path_length = static_cast(base_path_value.size()); std::ofstream file(path, std::ios::binary); if (!file) { @@ -239,8 +260,9 @@ auto URITemplateRouterView::save(const URITemplateRouter &router, } file.write(reinterpret_cast(&header), sizeof(header)); - file.write(reinterpret_cast(nodes.data()), - static_cast(nodes.size() * sizeof(Node))); + file.write( + reinterpret_cast(nodes.data()), + static_cast(nodes.size() * sizeof(SerializedNode))); file.write(string_table.data(), static_cast(string_table.size())); @@ -306,15 +328,15 @@ auto URITemplateRouterView::match(const std::string_view path, } if (header->node_count == 0 || - header->node_count > - (this->data_.size() - sizeof(RouterHeader)) / sizeof(Node)) { + header->node_count > (this->data_.size() - sizeof(RouterHeader)) / + sizeof(SerializedNode)) { return 0; } - const auto *nodes = - reinterpret_cast(this->data_.data() + sizeof(RouterHeader)); + const auto *nodes = reinterpret_cast( + this->data_.data() + sizeof(RouterHeader)); const auto nodes_size = - static_cast(header->node_count) * sizeof(Node); + static_cast(header->node_count) * sizeof(SerializedNode); const auto expected_string_table_offset = sizeof(RouterHeader) + nodes_size; if (header->string_table_offset < expected_string_table_offset || header->string_table_offset > this->data_.size()) { @@ -599,4 +621,37 @@ auto URITemplateRouterView::arguments( } } +auto URITemplateRouterView::base_path() const noexcept -> std::string_view { + if (this->data_.size() < sizeof(RouterHeader)) { + return {}; + } + + const auto *header = + reinterpret_cast(this->data_.data()); + if (header->magic != ROUTER_MAGIC || header->version != ROUTER_VERSION) { + return {}; + } + + if (header->base_path_length == 0) { + return {}; + } + + if (header->string_table_offset > this->data_.size() || + header->arguments_offset < header->string_table_offset || + header->arguments_offset > this->data_.size()) { + return {}; + } + + const auto *string_table = reinterpret_cast( + this->data_.data() + header->string_table_offset); + const auto string_table_size = + header->arguments_offset - header->string_table_offset; + if (header->base_path_offset > string_table_size || + header->base_path_length > string_table_size - header->base_path_offset) { + return {}; + } + + return {string_table + header->base_path_offset, header->base_path_length}; +} + } // namespace sourcemeta::core diff --git a/test/uritemplate/uritemplate_router_test.cc b/test/uritemplate/uritemplate_router_test.cc index 7d32c0f87..65046ad7a 100644 --- a/test/uritemplate/uritemplate_router_test.cc +++ b/test/uritemplate/uritemplate_router_test.cc @@ -25,6 +25,7 @@ TEST(URITemplateRouter, single_literal_route) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); EXPECT_ROUTER_MATCH(router, "/users", 1, captures); EXPECT_EQ(captures.size(), 0); @@ -32,6 +33,7 @@ TEST(URITemplateRouter, single_literal_route) { TEST(URITemplateRouter, single_literal_route_no_match) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); EXPECT_ROUTER_MATCH(router, "/posts", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -39,6 +41,7 @@ TEST(URITemplateRouter, single_literal_route_no_match) { TEST(URITemplateRouter, multi_segment_literal) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/list", 1); EXPECT_ROUTER_MATCH(router, "/users/list", 1, captures); EXPECT_EQ(captures.size(), 0); @@ -46,6 +49,7 @@ TEST(URITemplateRouter, multi_segment_literal) { TEST(URITemplateRouter, single_variable) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/{id}", 1); EXPECT_ROUTER_MATCH(router, "/users/123", 1, captures); EXPECT_EQ(captures.size(), 1); @@ -54,6 +58,7 @@ TEST(URITemplateRouter, single_variable) { TEST(URITemplateRouter, multiple_variables) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/{id}/posts/{post_id}", 1); EXPECT_ROUTER_MATCH(router, "/users/42/posts/99", 1, captures); EXPECT_EQ(captures.size(), 2); @@ -63,6 +68,7 @@ TEST(URITemplateRouter, multiple_variables) { TEST(URITemplateRouter, literal_before_variable_precedence) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/me", 1); router.add("/users/{id}", 2); EXPECT_ROUTER_MATCH(router, "/users/me", 1, captures); @@ -71,6 +77,7 @@ TEST(URITemplateRouter, literal_before_variable_precedence) { TEST(URITemplateRouter, variable_fallback) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/me", 1); router.add("/users/{id}", 2); EXPECT_ROUTER_MATCH(router, "/users/123", 2, captures); @@ -80,6 +87,7 @@ TEST(URITemplateRouter, variable_fallback) { TEST(URITemplateRouter, multiple_routes_match_users) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); router.add("/users/{id}", 2); router.add("/posts", 3); @@ -90,6 +98,7 @@ TEST(URITemplateRouter, multiple_routes_match_users) { TEST(URITemplateRouter, multiple_routes_match_users_id) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); router.add("/users/{id}", 2); router.add("/posts", 3); @@ -101,6 +110,7 @@ TEST(URITemplateRouter, multiple_routes_match_users_id) { TEST(URITemplateRouter, multiple_routes_match_posts) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); router.add("/users/{id}", 2); router.add("/posts", 3); @@ -111,6 +121,7 @@ TEST(URITemplateRouter, multiple_routes_match_posts) { TEST(URITemplateRouter, multiple_routes_match_posts_id) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); router.add("/users/{id}", 2); router.add("/posts", 3); @@ -122,24 +133,28 @@ TEST(URITemplateRouter, multiple_routes_match_posts_id) { TEST(URITemplateRouter, no_match_partial_path) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/{id}/posts", 1); EXPECT_ROUTER_MATCH(router, "/users/123", 0, captures); } TEST(URITemplateRouter, no_match_extra_segments) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); EXPECT_ROUTER_MATCH(router, "/users/123", 0, captures); } TEST(URITemplateRouter, empty_path) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); EXPECT_ROUTER_MATCH(router, "", 0, captures); } TEST(URITemplateRouter, root_template_matches_root) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/", 1); EXPECT_ROUTER_MATCH(router, "/", 1, captures); EXPECT_EQ(captures.size(), 0); @@ -147,6 +162,7 @@ TEST(URITemplateRouter, root_template_matches_root) { TEST(URITemplateRouter, root_template_no_match_empty) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/", 1); EXPECT_ROUTER_MATCH(router, "", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -154,6 +170,7 @@ TEST(URITemplateRouter, root_template_no_match_empty) { TEST(URITemplateRouter, root_template_no_match_path) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/", 1); EXPECT_ROUTER_MATCH(router, "/foo", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -161,6 +178,7 @@ TEST(URITemplateRouter, root_template_no_match_path) { TEST(URITemplateRouter, empty_template_matches_empty) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("", 1); EXPECT_ROUTER_MATCH(router, "", 1, captures); EXPECT_EQ(captures.size(), 0); @@ -168,6 +186,7 @@ TEST(URITemplateRouter, empty_template_matches_empty) { TEST(URITemplateRouter, empty_template_no_match_root) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("", 1); EXPECT_ROUTER_MATCH(router, "/", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -175,6 +194,7 @@ TEST(URITemplateRouter, empty_template_no_match_root) { TEST(URITemplateRouter, empty_template_no_match_path) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("", 1); EXPECT_ROUTER_MATCH(router, "/foo", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -182,6 +202,7 @@ TEST(URITemplateRouter, empty_template_no_match_path) { TEST(URITemplateRouter, root_and_other_routes_match_root) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/", 1); router.add("/users", 2); router.add("/users/{id}", 3); @@ -191,6 +212,7 @@ TEST(URITemplateRouter, root_and_other_routes_match_root) { TEST(URITemplateRouter, root_and_other_routes_match_users) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/", 1); router.add("/users", 2); router.add("/users/{id}", 3); @@ -200,6 +222,7 @@ TEST(URITemplateRouter, root_and_other_routes_match_users) { TEST(URITemplateRouter, root_and_other_routes_match_users_id) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/", 1); router.add("/users", 2); router.add("/users/{id}", 3); @@ -210,6 +233,7 @@ TEST(URITemplateRouter, root_and_other_routes_match_users_id) { TEST(URITemplateRouter, empty_and_root_together_match_empty) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("", 1); router.add("/", 2); EXPECT_ROUTER_MATCH(router, "", 1, captures); @@ -218,6 +242,7 @@ TEST(URITemplateRouter, empty_and_root_together_match_empty) { TEST(URITemplateRouter, empty_and_root_together_match_root) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("", 1); router.add("/", 2); EXPECT_ROUTER_MATCH(router, "/", 2, captures); @@ -226,6 +251,7 @@ TEST(URITemplateRouter, empty_and_root_together_match_root) { TEST(URITemplateRouter, empty_and_root_together_no_match_path) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("", 1); router.add("/", 2); EXPECT_ROUTER_MATCH(router, "/foo", 0, captures); @@ -234,6 +260,7 @@ TEST(URITemplateRouter, empty_and_root_together_no_match_path) { TEST(URITemplateRouter, empty_and_root_and_others_match_empty) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("", 1); router.add("/", 2); router.add("/users", 3); @@ -244,6 +271,7 @@ TEST(URITemplateRouter, empty_and_root_and_others_match_empty) { TEST(URITemplateRouter, empty_and_root_and_others_match_root) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("", 1); router.add("/", 2); router.add("/users", 3); @@ -254,6 +282,7 @@ TEST(URITemplateRouter, empty_and_root_and_others_match_root) { TEST(URITemplateRouter, empty_and_root_and_others_match_users) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("", 1); router.add("/", 2); router.add("/users", 3); @@ -264,6 +293,7 @@ TEST(URITemplateRouter, empty_and_root_and_others_match_users) { TEST(URITemplateRouter, empty_and_root_and_others_match_users_id) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("", 1); router.add("/", 2); router.add("/users", 3); @@ -275,6 +305,7 @@ TEST(URITemplateRouter, empty_and_root_and_others_match_users_id) { TEST(URITemplateRouter, binary_search_literals_gamma) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/alpha", 1); router.add("/beta", 2); router.add("/gamma", 3); @@ -286,6 +317,7 @@ TEST(URITemplateRouter, binary_search_literals_gamma) { TEST(URITemplateRouter, binary_search_literals_alpha) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/alpha", 1); router.add("/beta", 2); router.add("/gamma", 3); @@ -297,6 +329,7 @@ TEST(URITemplateRouter, binary_search_literals_alpha) { TEST(URITemplateRouter, binary_search_literals_epsilon) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/alpha", 1); router.add("/beta", 2); router.add("/gamma", 3); @@ -308,6 +341,7 @@ TEST(URITemplateRouter, binary_search_literals_epsilon) { TEST(URITemplateRouter, conflicting_variable_names_throws) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/{user_id}/posts", 1); try { router.add("/users/{id}/comments", 2); @@ -322,6 +356,7 @@ TEST(URITemplateRouter, conflicting_variable_names_throws) { TEST(URITemplateRouter, same_variable_names_allowed) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/{id}/posts", 1); router.add("/users/{id}/comments", 2); EXPECT_ROUTER_MATCH(router, "/users/123/posts", 1, captures); @@ -331,6 +366,7 @@ TEST(URITemplateRouter, same_variable_names_allowed) { TEST(URITemplateRouter, conflicting_expansion_variable_names_throws) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{id}", 1); try { router.add("/files/{+path}", 2); @@ -345,6 +381,7 @@ TEST(URITemplateRouter, conflicting_expansion_variable_names_throws) { TEST(URITemplateRouter, reserved_expansion_catch_all) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{+path}", 1); EXPECT_ROUTER_MATCH(router, "/files/foo/bar/baz.txt", 1, captures); EXPECT_EQ(captures.size(), 1); @@ -353,6 +390,7 @@ TEST(URITemplateRouter, reserved_expansion_catch_all) { TEST(URITemplateRouter, reserved_expansion_single_segment) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{+path}", 1); EXPECT_ROUTER_MATCH(router, "/files/readme.md", 1, captures); EXPECT_EQ(captures.size(), 1); @@ -361,6 +399,7 @@ TEST(URITemplateRouter, reserved_expansion_single_segment) { TEST(URITemplateRouter, reserved_expansion_with_literal_prefix) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/api/v1/proxy/{+url}", 1); EXPECT_ROUTER_MATCH(router, "/api/v1/proxy/https://example.com/path", 1, captures); @@ -370,6 +409,7 @@ TEST(URITemplateRouter, reserved_expansion_with_literal_prefix) { TEST(URITemplateRouter, reserved_expansion_matches_single_segment) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{+path}", 1); EXPECT_ROUTER_MATCH(router, "/files/123", 1, captures); EXPECT_EQ(captures.size(), 1); @@ -378,6 +418,7 @@ TEST(URITemplateRouter, reserved_expansion_matches_single_segment) { TEST(URITemplateRouter, reserved_expansion_matches_multi_segment) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{+path}", 1); EXPECT_ROUTER_MATCH(router, "/files/foo/bar", 1, captures); EXPECT_EQ(captures.size(), 1); @@ -386,6 +427,7 @@ TEST(URITemplateRouter, reserved_expansion_matches_multi_segment) { TEST(URITemplateRouter, expansion_takes_priority_over_variable) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{path}", 1); router.add("/files/{+path}", 2); EXPECT_ROUTER_MATCH(router, "/files/readme.md", 2, captures); @@ -395,6 +437,7 @@ TEST(URITemplateRouter, expansion_takes_priority_over_variable) { TEST(URITemplateRouter, expansion_takes_priority_multi_segment) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{path}", 1); router.add("/files/{+path}", 2); EXPECT_ROUTER_MATCH(router, "/files/foo/bar/baz", 2, captures); @@ -404,6 +447,7 @@ TEST(URITemplateRouter, expansion_takes_priority_multi_segment) { TEST(URITemplateRouter, expansion_first_then_variable) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{+path}", 1); router.add("/files/{path}", 2); EXPECT_ROUTER_MATCH(router, "/files/readme.md", 1, captures); @@ -413,6 +457,7 @@ TEST(URITemplateRouter, expansion_first_then_variable) { TEST(URITemplateRouter, literal_takes_priority_over_expansion) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{+path}", 1); router.add("/files/special", 2); EXPECT_ROUTER_MATCH(router, "/files/special", 2, captures); @@ -421,6 +466,7 @@ TEST(URITemplateRouter, literal_takes_priority_over_expansion) { TEST(URITemplateRouter, expansion_fallback_from_literal) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{+path}", 1); router.add("/files/special", 2); EXPECT_ROUTER_MATCH(router, "/files/other", 1, captures); @@ -430,192 +476,230 @@ TEST(URITemplateRouter, expansion_fallback_from_literal) { TEST(URITemplateRouter, literal_suffix_after_variable_throws) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{bar}.json", 1, "{bar}.json"); } TEST(URITemplateRouter, literal_prefix_before_variable_throws) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/prefix{bar}", 1, "prefix{bar}"); } TEST(URITemplateRouter, mixed_segment_variable_then_literal_suffix) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/{id}.html", 1, "{id}.html"); } TEST(URITemplateRouter, mixed_segment_expansion_then_literal_suffix) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/files/{+path}.bak", 1, "{+path}.bak"); } TEST(URITemplateRouter, mixed_segment_prefix_before_expansion_throws) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/api/v1{+rest}", 1, "v1{+rest}"); } TEST(URITemplateRouter, mixed_segment_suffix_in_middle) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{bar}.json/baz", 1, "{bar}.json"); } TEST(URITemplateRouter, mixed_segment_prefix_in_middle) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/prefix{bar}/baz", 1, "prefix{bar}"); } TEST(URITemplateRouter, mixed_segment_expansion_suffix_in_middle) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/files/{+path}.txt/meta", 1, "{+path}.txt"); } TEST(URITemplateRouter, mixed_segment_expansion_prefix_in_middle) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/api/v2{+path}/end", 1, "v2{+path}"); } TEST(URITemplateRouter, error_unclosed_brace) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{bar", 1, "{bar"); } TEST(URITemplateRouter, error_unclosed_brace_at_end) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/bar{", 1, "bar{"); } TEST(URITemplateRouter, error_empty_variable) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{}", 1, "{}"); } TEST(URITemplateRouter, error_empty_expansion) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{+}", 1, "{+}"); } TEST(URITemplateRouter, error_nested_brace) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{{bar}}", 1, "{{bar}}"); } TEST(URITemplateRouter, error_unmatched_close_brace) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/bar}", 1, "bar}"); } TEST(URITemplateRouter, error_space_before_variable) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{ bar}", 1, "{ bar}"); } TEST(URITemplateRouter, error_space_after_variable) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{bar }", 1, "{bar }"); } TEST(URITemplateRouter, error_space_in_variable) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{bar baz}", 1, "{bar baz}"); } TEST(URITemplateRouter, error_invalid_variable_start) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{-bar}", 1, "{-bar}"); } TEST(URITemplateRouter, error_invalid_character_in_variable) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{bar!baz}", 1, "{bar!baz}"); } TEST(URITemplateRouter, error_reserved_operator_equals) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{=bar}", 1, "{=bar}"); } TEST(URITemplateRouter, error_reserved_operator_pipe) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{|bar}", 1, "{|bar}"); } TEST(URITemplateRouter, error_reserved_operator_exclamation) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{!bar}", 1, "{!bar}"); } TEST(URITemplateRouter, error_reserved_operator_at) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{@bar}", 1, "{@bar}"); } TEST(URITemplateRouter, error_bracket_in_variable) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{bar[0]}", 1, "{bar[0]}"); } TEST(URITemplateRouter, error_multiple_variables) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{bar,baz}", 1, "{bar,baz}"); } TEST(URITemplateRouter, error_prefix_modifier) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{bar:3}", 1, "{bar:3}"); } TEST(URITemplateRouter, error_explode_modifier) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{bar*}", 1, "{bar*}"); } TEST(URITemplateRouter, error_unsupported_fragment_expansion) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{#bar}", 1, "{#bar}"); } TEST(URITemplateRouter, error_unsupported_label_expansion) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{.bar}", 1, "{.bar}"); } TEST(URITemplateRouter, error_unsupported_path_expansion) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{/bar}", 1, "{/bar}"); } TEST(URITemplateRouter, error_unsupported_path_parameter_expansion) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{;bar}", 1, "{;bar}"); } TEST(URITemplateRouter, error_unsupported_query_expansion) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{?bar}", 1, "{?bar}"); } TEST(URITemplateRouter, error_unsupported_query_continuation_expansion) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{&bar}", 1, "{&bar}"); } TEST(URITemplateRouter, error_expansion_not_last_segment_literal) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{+bar}/baz", 1, "{+bar}"); } TEST(URITemplateRouter, error_expansion_not_last_segment_variable) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{+bar}/{baz}", 1, "{+bar}"); } TEST(URITemplateRouter, error_expansion_not_last_segment_trailing_slash) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); EXPECT_ROUTER_SEGMENT_ERROR(router, "/foo/{+bar}/", 1, "{+bar}"); } TEST(URITemplateRouter, trailing_slash_no_match) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); EXPECT_ROUTER_MATCH(router, "/users/", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -623,6 +707,7 @@ TEST(URITemplateRouter, trailing_slash_no_match) { TEST(URITemplateRouter, multiple_trailing_slashes_no_match) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); EXPECT_ROUTER_MATCH(router, "/users///", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -630,6 +715,7 @@ TEST(URITemplateRouter, multiple_trailing_slashes_no_match) { TEST(URITemplateRouter, leading_double_slash_no_match) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users", 1); EXPECT_ROUTER_MATCH(router, "//users", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -637,6 +723,7 @@ TEST(URITemplateRouter, leading_double_slash_no_match) { TEST(URITemplateRouter, internal_double_slashes_no_match) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/posts", 1); EXPECT_ROUTER_MATCH(router, "/users//posts", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -644,6 +731,7 @@ TEST(URITemplateRouter, internal_double_slashes_no_match) { TEST(URITemplateRouter, trailing_slash_with_variable_no_match) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/{id}", 1); EXPECT_ROUTER_MATCH(router, "/users/123/", 0, captures); EXPECT_EQ(captures.size(), 1); @@ -652,6 +740,7 @@ TEST(URITemplateRouter, trailing_slash_with_variable_no_match) { TEST(URITemplateRouter, internal_double_slash_with_variable_no_match) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/users/{id}/posts", 1); EXPECT_ROUTER_MATCH(router, "/users//123//posts", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -659,6 +748,7 @@ TEST(URITemplateRouter, internal_double_slash_with_variable_no_match) { TEST(URITemplateRouter, expansion_matches_trailing_slash) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{+path}", 1); EXPECT_ROUTER_MATCH(router, "/files/foo/bar/", 1, captures); EXPECT_EQ(captures.size(), 1); @@ -667,6 +757,7 @@ TEST(URITemplateRouter, expansion_matches_trailing_slash) { TEST(URITemplateRouter, expansion_matches_double_slashes) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/files/{+path}", 1); EXPECT_ROUTER_MATCH(router, "/files/foo//bar", 1, captures); EXPECT_EQ(captures.size(), 1); @@ -675,6 +766,7 @@ TEST(URITemplateRouter, expansion_matches_double_slashes) { TEST(URITemplateRouter, add_with_single_string_argument) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"responseSchema", std::string_view{"some/path"}}}}; router.add("/test", 1, arguments); @@ -694,6 +786,7 @@ TEST(URITemplateRouter, add_with_single_string_argument) { TEST(URITemplateRouter, add_with_single_integer_argument) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"maxItems", std::int64_t{42}}}}; router.add("/test", 1, arguments); @@ -713,6 +806,7 @@ TEST(URITemplateRouter, add_with_single_integer_argument) { TEST(URITemplateRouter, add_with_single_boolean_argument_true) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"verbose", true}}}; router.add("/test", 1, arguments); @@ -732,6 +826,7 @@ TEST(URITemplateRouter, add_with_single_boolean_argument_true) { TEST(URITemplateRouter, add_with_single_boolean_argument_false) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"verbose", false}}}; router.add("/test", 1, arguments); @@ -751,6 +846,7 @@ TEST(URITemplateRouter, add_with_single_boolean_argument_false) { TEST(URITemplateRouter, add_with_multiple_arguments) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"responseSchema", std::string_view{"some/path"}}, {"maxItems", std::int64_t{100}}, @@ -776,6 +872,7 @@ TEST(URITemplateRouter, add_with_multiple_arguments) { TEST(URITemplateRouter, add_with_empty_arguments_span) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); router.add("/test", 1, std::span{}); std::vector> @@ -805,6 +903,7 @@ TEST(URITemplateRouter, add_without_arguments_parameter) { TEST(URITemplateRouter, add_multiple_routes_with_arguments) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments_one{{{"schema", std::string_view{"path/one"}}}}; const std::array @@ -855,6 +954,7 @@ TEST(URITemplateRouter, add_multiple_routes_with_arguments) { TEST(URITemplateRouter, add_arguments_negative_integer) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"offset", std::int64_t{INT64_MIN}}}}; router.add("/test", 1, arguments); @@ -874,6 +974,7 @@ TEST(URITemplateRouter, add_arguments_negative_integer) { TEST(URITemplateRouter, add_arguments_zero_integer) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"count", std::int64_t{0}}}}; router.add("/test", 1, arguments); @@ -893,6 +994,7 @@ TEST(URITemplateRouter, add_arguments_zero_integer) { TEST(URITemplateRouter, add_arguments_max_integer) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"limit", std::int64_t{INT64_MAX}}}}; router.add("/test", 1, arguments); @@ -912,6 +1014,7 @@ TEST(URITemplateRouter, add_arguments_max_integer) { TEST(URITemplateRouter, add_arguments_empty_string_value) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"description", std::string_view{""}}}}; router.add("/test", 1, arguments); @@ -931,6 +1034,7 @@ TEST(URITemplateRouter, add_arguments_empty_string_value) { TEST(URITemplateRouter, add_arguments_empty_string_name) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"", std::string_view{"some_value"}}}}; router.add("/test", 1, arguments); @@ -950,6 +1054,7 @@ TEST(URITemplateRouter, add_arguments_empty_string_name) { TEST(URITemplateRouter, match_still_works_with_arguments) { sourcemeta::core::URITemplateRouter router; + EXPECT_TRUE(router.base_path().empty()); const std::array arguments{ {{"responseSchema", std::string_view{"schemas/user"}}}}; router.add("/users/{id}", 1, arguments); @@ -960,6 +1065,7 @@ TEST(URITemplateRouter, match_still_works_with_arguments) { TEST(URITemplateRouter, base_path_single_segment) { sourcemeta::core::URITemplateRouter router{"/prefix"}; + EXPECT_EQ(router.base_path(), "/prefix"); router.add("/foo", 1); EXPECT_ROUTER_MATCH(router, "/prefix/foo", 1, captures); EXPECT_EQ(captures.size(), 0); @@ -967,6 +1073,7 @@ TEST(URITemplateRouter, base_path_single_segment) { TEST(URITemplateRouter, base_path_without_prefix_no_match) { sourcemeta::core::URITemplateRouter router{"/prefix"}; + EXPECT_EQ(router.base_path(), "/prefix"); router.add("/foo", 1); EXPECT_ROUTER_MATCH(router, "/foo", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -974,6 +1081,7 @@ TEST(URITemplateRouter, base_path_without_prefix_no_match) { TEST(URITemplateRouter, base_path_multi_segment) { sourcemeta::core::URITemplateRouter router{"/v1/catalog"}; + EXPECT_EQ(router.base_path(), "/v1/catalog"); router.add("/api/list", 1); router.add("/{+path}", 2); EXPECT_ROUTER_MATCH(router, "/v1/catalog/api/list", 1, captures_list); @@ -985,6 +1093,7 @@ TEST(URITemplateRouter, base_path_multi_segment) { TEST(URITemplateRouter, base_path_with_variable) { sourcemeta::core::URITemplateRouter router{"/prefix"}; + EXPECT_EQ(router.base_path(), "/prefix"); router.add("/users/{id}", 1); EXPECT_ROUTER_MATCH(router, "/prefix/users/42", 1, captures); EXPECT_EQ(captures.size(), 1); @@ -993,6 +1102,7 @@ TEST(URITemplateRouter, base_path_with_variable) { TEST(URITemplateRouter, base_path_prefix_boundary_no_match) { sourcemeta::core::URITemplateRouter router{"/prefix"}; + EXPECT_EQ(router.base_path(), "/prefix"); router.add("/foo", 1); EXPECT_ROUTER_MATCH(router, "/prefixfoo", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -1000,6 +1110,7 @@ TEST(URITemplateRouter, base_path_prefix_boundary_no_match) { TEST(URITemplateRouter, base_path_with_empty_template) { sourcemeta::core::URITemplateRouter router{"/v1/catalog"}; + EXPECT_EQ(router.base_path(), "/v1/catalog"); router.add("", 1); EXPECT_ROUTER_MATCH(router, "/v1/catalog", 1, captures); EXPECT_EQ(captures.size(), 0); @@ -1007,6 +1118,7 @@ TEST(URITemplateRouter, base_path_with_empty_template) { TEST(URITemplateRouter, base_path_slash_only_is_no_base_path) { sourcemeta::core::URITemplateRouter router{"/"}; + EXPECT_TRUE(router.base_path().empty()); router.add("/foo", 1); EXPECT_ROUTER_MATCH(router, "/foo", 1, captures); EXPECT_EQ(captures.size(), 0); @@ -1014,6 +1126,7 @@ TEST(URITemplateRouter, base_path_slash_only_is_no_base_path) { TEST(URITemplateRouter, base_path_trailing_slash_normalized) { sourcemeta::core::URITemplateRouter router{"/prefix/"}; + EXPECT_EQ(router.base_path(), "/prefix"); router.add("/foo", 1); EXPECT_ROUTER_MATCH(router, "/prefix/foo", 1, captures); EXPECT_EQ(captures.size(), 0); @@ -1021,6 +1134,7 @@ TEST(URITemplateRouter, base_path_trailing_slash_normalized) { TEST(URITemplateRouter, base_path_multiple_trailing_slashes_normalized) { sourcemeta::core::URITemplateRouter router{"/prefix///"}; + EXPECT_EQ(router.base_path(), "/prefix"); router.add("/foo", 1); EXPECT_ROUTER_MATCH(router, "/prefix/foo", 1, captures); EXPECT_EQ(captures.size(), 0); @@ -1028,6 +1142,7 @@ TEST(URITemplateRouter, base_path_multiple_trailing_slashes_normalized) { TEST(URITemplateRouter, base_path_expansion) { sourcemeta::core::URITemplateRouter router{"/api"}; + EXPECT_EQ(router.base_path(), "/api"); router.add("/files/{+path}", 1); EXPECT_ROUTER_MATCH(router, "/api/files/a/b/c", 1, captures); EXPECT_EQ(captures.size(), 1); @@ -1036,12 +1151,14 @@ TEST(URITemplateRouter, base_path_expansion) { TEST(URITemplateRouter, base_path_trailing_slash_on_request_no_match) { sourcemeta::core::URITemplateRouter router{"/v1/catalog"}; + EXPECT_EQ(router.base_path(), "/v1/catalog"); router.add("/foo", 1); EXPECT_ROUTER_MATCH(router, "/v1/catalog/foo/", 0, captures); } TEST(URITemplateRouter, base_path_empty_string_is_no_base_path) { sourcemeta::core::URITemplateRouter router{""}; + EXPECT_TRUE(router.base_path().empty()); router.add("/foo", 1); EXPECT_ROUTER_MATCH(router, "/foo", 1, captures); EXPECT_EQ(captures.size(), 0); @@ -1049,6 +1166,7 @@ TEST(URITemplateRouter, base_path_empty_string_is_no_base_path) { TEST(URITemplateRouter, base_path_wrong_prefix_no_match) { sourcemeta::core::URITemplateRouter router{"/v1/catalog"}; + EXPECT_EQ(router.base_path(), "/v1/catalog"); router.add("/api/list", 1); EXPECT_ROUTER_MATCH(router, "/v2/catalog/api/list", 0, captures); EXPECT_EQ(captures.size(), 0); @@ -1056,6 +1174,7 @@ TEST(URITemplateRouter, base_path_wrong_prefix_no_match) { TEST(URITemplateRouter, base_path_partial_prefix_no_match) { sourcemeta::core::URITemplateRouter router{"/v1/catalog"}; + EXPECT_EQ(router.base_path(), "/v1/catalog"); router.add("/api/list", 1); EXPECT_ROUTER_MATCH(router, "/v1/api/list", 0, captures); EXPECT_EQ(captures.size(), 0); diff --git a/test/uritemplate/uritemplate_router_view_test.cc b/test/uritemplate/uritemplate_router_view_test.cc index be4288b45..2de42497d 100644 --- a/test/uritemplate/uritemplate_router_view_test.cc +++ b/test/uritemplate/uritemplate_router_view_test.cc @@ -33,6 +33,7 @@ TEST_F(URITemplateRouterViewTest, single_literal_route) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -45,6 +46,7 @@ TEST_F(URITemplateRouterViewTest, single_literal_route_no_match) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/posts", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -57,6 +59,7 @@ TEST_F(URITemplateRouterViewTest, multi_segment_literal) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/list", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -69,6 +72,7 @@ TEST_F(URITemplateRouterViewTest, single_variable) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/123", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "123"); @@ -82,6 +86,7 @@ TEST_F(URITemplateRouterViewTest, multiple_variables) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/42/posts/99", 1, captures); EXPECT_EQ(captures.size(), 2); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "42"); @@ -97,6 +102,7 @@ TEST_F(URITemplateRouterViewTest, literal_before_variable_precedence) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/me", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -110,6 +116,7 @@ TEST_F(URITemplateRouterViewTest, variable_fallback) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/123", 2, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "123"); @@ -126,6 +133,7 @@ TEST_F(URITemplateRouterViewTest, multiple_routes_match_users) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -141,6 +149,7 @@ TEST_F(URITemplateRouterViewTest, multiple_routes_match_users_id) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/42", 2, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "42"); @@ -157,6 +166,7 @@ TEST_F(URITemplateRouterViewTest, multiple_routes_match_posts) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/posts", 3, captures); EXPECT_EQ(captures.size(), 0); } @@ -172,6 +182,7 @@ TEST_F(URITemplateRouterViewTest, multiple_routes_match_posts_id) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/posts/99", 4, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "99"); @@ -189,6 +200,7 @@ TEST_F(URITemplateRouterViewTest, binary_search_literals_gamma) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/gamma", 3, captures); EXPECT_EQ(captures.size(), 0); } @@ -205,6 +217,7 @@ TEST_F(URITemplateRouterViewTest, binary_search_literals_alpha) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/alpha", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -221,6 +234,7 @@ TEST_F(URITemplateRouterViewTest, binary_search_literals_epsilon) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/epsilon", 5, captures); EXPECT_EQ(captures.size(), 0); } @@ -233,6 +247,7 @@ TEST_F(URITemplateRouterViewTest, root_template_matches_root) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -245,6 +260,7 @@ TEST_F(URITemplateRouterViewTest, root_template_no_match_empty) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -257,6 +273,7 @@ TEST_F(URITemplateRouterViewTest, root_template_no_match_path) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/foo", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -269,6 +286,7 @@ TEST_F(URITemplateRouterViewTest, empty_template_matches_empty) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -281,6 +299,7 @@ TEST_F(URITemplateRouterViewTest, empty_template_no_match_root) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -293,6 +312,7 @@ TEST_F(URITemplateRouterViewTest, empty_template_no_match_path) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/foo", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -307,6 +327,7 @@ TEST_F(URITemplateRouterViewTest, root_and_other_routes_match_root) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -321,6 +342,7 @@ TEST_F(URITemplateRouterViewTest, root_and_other_routes_match_users) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users", 2, captures); EXPECT_EQ(captures.size(), 0); } @@ -335,6 +357,7 @@ TEST_F(URITemplateRouterViewTest, root_and_other_routes_match_users_id) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/123", 3, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "123"); @@ -349,6 +372,7 @@ TEST_F(URITemplateRouterViewTest, empty_and_root_together_match_empty) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -362,6 +386,7 @@ TEST_F(URITemplateRouterViewTest, empty_and_root_together_match_root) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/", 2, captures); EXPECT_EQ(captures.size(), 0); } @@ -375,6 +400,7 @@ TEST_F(URITemplateRouterViewTest, empty_and_root_together_no_match_path) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/foo", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -390,6 +416,7 @@ TEST_F(URITemplateRouterViewTest, empty_and_root_and_others_match_empty) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -405,6 +432,7 @@ TEST_F(URITemplateRouterViewTest, empty_and_root_and_others_match_root) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/", 2, captures); EXPECT_EQ(captures.size(), 0); } @@ -420,6 +448,7 @@ TEST_F(URITemplateRouterViewTest, empty_and_root_and_others_match_users) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users", 3, captures); EXPECT_EQ(captures.size(), 0); } @@ -435,6 +464,7 @@ TEST_F(URITemplateRouterViewTest, empty_and_root_and_others_match_users_id) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/42", 4, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "42"); @@ -449,6 +479,7 @@ TEST_F(URITemplateRouterViewTest, same_variable_names_allowed) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/123/comments", 2, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "123"); @@ -462,6 +493,7 @@ TEST_F(URITemplateRouterViewTest, reserved_expansion_catch_all) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/files/foo/bar/baz.txt", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "path", "foo/bar/baz.txt"); @@ -475,6 +507,7 @@ TEST_F(URITemplateRouterViewTest, reserved_expansion_with_literal_prefix) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/api/v1/proxy/https://example.com/path", 1, captures); EXPECT_EQ(captures.size(), 1); @@ -489,6 +522,7 @@ TEST_F(URITemplateRouterViewTest, reserved_expansion_matches_multi_segment) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/files/foo/bar", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "path", "foo/bar"); @@ -503,6 +537,7 @@ TEST_F(URITemplateRouterViewTest, expansion_takes_priority_over_variable) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/files/readme.md", 2, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "path", "readme.md"); @@ -517,6 +552,7 @@ TEST_F(URITemplateRouterViewTest, expansion_takes_priority_multi_segment) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/files/foo/bar/baz", 2, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "path", "foo/bar/baz"); @@ -531,6 +567,7 @@ TEST_F(URITemplateRouterViewTest, expansion_first_then_variable) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/files/readme.md", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "path", "readme.md"); @@ -545,6 +582,7 @@ TEST_F(URITemplateRouterViewTest, literal_takes_priority_over_expansion) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/files/special", 2, captures); EXPECT_EQ(captures.size(), 0); } @@ -558,6 +596,7 @@ TEST_F(URITemplateRouterViewTest, expansion_fallback_from_literal) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/files/other", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "path", "other"); @@ -571,6 +610,7 @@ TEST_F(URITemplateRouterViewTest, trailing_slash_no_match) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -583,6 +623,7 @@ TEST_F(URITemplateRouterViewTest, multiple_trailing_slashes_no_match) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users///", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -595,6 +636,7 @@ TEST_F(URITemplateRouterViewTest, leading_double_slash_no_match) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "//users", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -607,6 +649,7 @@ TEST_F(URITemplateRouterViewTest, internal_double_slashes_no_match) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users//posts", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -619,6 +662,7 @@ TEST_F(URITemplateRouterViewTest, trailing_slash_with_variable_no_match) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/123/", 0, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "123"); @@ -633,6 +677,7 @@ TEST_F(URITemplateRouterViewTest, } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users//123//posts", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -645,6 +690,7 @@ TEST_F(URITemplateRouterViewTest, expansion_matches_trailing_slash) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/files/foo/bar/", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "path", "foo/bar/"); @@ -658,6 +704,7 @@ TEST_F(URITemplateRouterViewTest, expansion_matches_double_slashes) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/files/foo//bar", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "path", "foo//bar"); @@ -915,6 +962,7 @@ TEST_F(URITemplateRouterViewTest, arguments_single_string) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/first", 1, captures_first); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/alpha", 1, captures_alpha); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); std::vector> @@ -1611,6 +1678,7 @@ TEST_F(URITemplateRouterViewTest, arguments_route_without_arguments) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/test", 1, captures); std::vectorpath}; + EXPECT_TRUE(restored.base_path().empty()); std::vector> @@ -1660,6 +1729,7 @@ TEST_F(URITemplateRouterViewTest, arguments_identifier_zero) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); std::vector> @@ -1684,6 +1754,7 @@ TEST_F(URITemplateRouterViewTest, arguments_with_variable_capture) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/42", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "42"); @@ -1717,6 +1788,7 @@ TEST_F(URITemplateRouterViewTest, arguments_with_expansion) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/files/a/b/c", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "path", "a/b/c"); @@ -1758,6 +1830,7 @@ TEST_F(URITemplateRouterViewTest, arguments_multiple_routes_match_and_args) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/1", 1, captures_users); EXPECT_EQ(captures_users.size(), 1); @@ -1818,6 +1891,7 @@ TEST_F(URITemplateRouterViewTest, arguments_do_not_affect_match_precedence) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/users/me", 1, captures_me); EXPECT_EQ(captures_me.size(), 0); @@ -1875,6 +1949,7 @@ TEST_F(URITemplateRouterViewTest, arguments_survive_large_trie) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/api/v1/alpha", 1, captures_alpha); EXPECT_ROUTER_MATCH(restored, "/api/v1/beta", 2, captures_beta); @@ -1938,6 +2013,7 @@ TEST_F(URITemplateRouterViewTest, base_path_single_segment) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_EQ(restored.base_path(), "/prefix"); EXPECT_ROUTER_MATCH(restored, "/prefix/foo", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -1950,6 +2026,7 @@ TEST_F(URITemplateRouterViewTest, base_path_without_prefix_no_match) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_EQ(restored.base_path(), "/prefix"); EXPECT_ROUTER_MATCH(restored, "/foo", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -1963,6 +2040,7 @@ TEST_F(URITemplateRouterViewTest, base_path_multi_segment) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_EQ(restored.base_path(), "/v1/catalog"); EXPECT_ROUTER_MATCH(restored, "/v1/catalog/api/list", 1, captures_list); EXPECT_EQ(captures_list.size(), 0); EXPECT_ROUTER_MATCH(restored, "/v1/catalog/foo/bar", 2, captures_expansion); @@ -1978,6 +2056,7 @@ TEST_F(URITemplateRouterViewTest, base_path_with_variable) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_EQ(restored.base_path(), "/prefix"); EXPECT_ROUTER_MATCH(restored, "/prefix/users/42", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "id", "42"); @@ -1991,6 +2070,7 @@ TEST_F(URITemplateRouterViewTest, base_path_prefix_boundary_no_match) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_EQ(restored.base_path(), "/prefix"); EXPECT_ROUTER_MATCH(restored, "/prefixfoo", 0, captures); EXPECT_EQ(captures.size(), 0); } @@ -2003,6 +2083,7 @@ TEST_F(URITemplateRouterViewTest, base_path_with_empty_template) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_EQ(restored.base_path(), "/v1/catalog"); EXPECT_ROUTER_MATCH(restored, "/v1/catalog", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -2015,6 +2096,7 @@ TEST_F(URITemplateRouterViewTest, base_path_no_base_path_unchanged) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_TRUE(restored.base_path().empty()); EXPECT_ROUTER_MATCH(restored, "/foo", 1, captures); EXPECT_EQ(captures.size(), 0); } @@ -2027,7 +2109,35 @@ TEST_F(URITemplateRouterViewTest, base_path_expansion) { } const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_EQ(restored.base_path(), "/api"); EXPECT_ROUTER_MATCH(restored, "/api/files/a/b/c", 1, captures); EXPECT_EQ(captures.size(), 1); EXPECT_ROUTER_CAPTURE(captures, 0, "path", "a/b/c"); } + +TEST_F(URITemplateRouterViewTest, base_path_trailing_slash_normalized) { + { + sourcemeta::core::URITemplateRouter router{"/prefix/"}; + router.add("/foo", 1); + sourcemeta::core::URITemplateRouterView::save(router, this->path); + } + + const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_EQ(restored.base_path(), "/prefix"); + EXPECT_ROUTER_MATCH(restored, "/prefix/foo", 1, captures); + EXPECT_EQ(captures.size(), 0); +} + +TEST_F(URITemplateRouterViewTest, + base_path_multiple_trailing_slashes_normalized) { + { + sourcemeta::core::URITemplateRouter router{"/prefix///"}; + router.add("/foo", 1); + sourcemeta::core::URITemplateRouterView::save(router, this->path); + } + + const sourcemeta::core::URITemplateRouterView restored{this->path}; + EXPECT_EQ(restored.base_path(), "/prefix"); + EXPECT_ROUTER_MATCH(restored, "/prefix/foo", 1, captures); + EXPECT_EQ(captures.size(), 0); +}