mirror of
https://github.com/boostorg/url.git
synced 2025-05-09 09:23:52 +00:00
263 lines
12 KiB
C++
263 lines
12 KiB
C++
//
|
|
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
// Official repository: https://github.com/boostorg/url
|
|
//
|
|
|
|
// Test that header file is self-contained.
|
|
#ifndef BOOST_URL_SOURCE
|
|
#define BOOST_URL_SOURCE
|
|
#endif
|
|
|
|
#include "router.hpp"
|
|
|
|
#include "test_suite.hpp"
|
|
|
|
namespace boost {
|
|
namespace urls {
|
|
|
|
struct router_test
|
|
{
|
|
static
|
|
void
|
|
testPatterns()
|
|
{
|
|
// literal
|
|
good("user", "user");
|
|
good("user/view", "user/view");
|
|
|
|
// empty
|
|
good("", "/");
|
|
good("/", "/");
|
|
good("", "");
|
|
good("/", "");
|
|
|
|
// replace segment match
|
|
good("user/{}", "user/johndoe", {"johndoe"});
|
|
good("user/{name}", "user/johndoe", {"johndoe"}, {{"name", "johndoe"}});
|
|
good("user/{id}", "user/123", {"123"}, {{"id", "123"}});
|
|
good("user/{name}/{op}", "user/johndoe/r", {"johndoe", "r"}, {{"name", "johndoe"}, {"op", "r"}});
|
|
good("user/{name}/op/{op}", "user/johndoe/op/r", {"johndoe", "r"}, {{"name", "johndoe"}, {"op", "r"}});
|
|
|
|
// path dot segments
|
|
good("user/{name}", "user/././johndoe", {"johndoe"}, {{"name", "johndoe"}});
|
|
good("user/{name}", "user/b/../johndoe", {"johndoe"}, {{"name", "johndoe"}});
|
|
good("user/b", "user/c/../b");
|
|
good("user/b", "../a/user/c/../b");
|
|
|
|
// template dot segments
|
|
good("user/././{name}", "user/johndoe", {"johndoe"}, {{"name", "johndoe"}});
|
|
good("user/b/../{name}", "user/johndoe", {"johndoe"}, {{"name", "johndoe"}});
|
|
good("user/c/../b", "user/b");
|
|
good("user/{name}/../{name}", "user/johndoe", {"johndoe"}, {{"name", "johndoe"}});
|
|
good("../a/user/c/../b", "user/b");
|
|
|
|
// pct-encoded segments
|
|
good("%75ser/{name}", "user/johndoe", {"johndoe"}, {{"name", "johndoe"}});
|
|
good("user/{name}", "%75ser/johndoe", {"johndoe"}, {{"name", "johndoe"}});
|
|
|
|
// empty segments
|
|
good("user//{name}", "user//johndoe", {"johndoe"}, {{"name", "johndoe"}});
|
|
bad("user//{name}", "user/johndoe");
|
|
|
|
// optional segment match
|
|
good("user/{name}/{op?}", "user/johndoe", {"johndoe", ""}, {{"name", "johndoe"}, {"op", ""}});
|
|
good("user/{name}/{op?}", "user/johndoe/", {"johndoe", ""}, {{"name", "johndoe"}, {"op", ""}});
|
|
good("user/{name}/{op?}", "user/johndoe/r", {"johndoe", "r"}, {{"name", "johndoe"}, {"op", "r"}});
|
|
bad("user/{name}/{op?}", "user/johndoe/r/r");
|
|
good("user/{name}/{op?}/b", "user/johndoe/r/b", {"johndoe", "r"}, {{"name", "johndoe"}, {"op", "r"}});
|
|
good("user/{name}/{op?}/b", "user/johndoe/b", {"johndoe", ""}, {{"name", "johndoe"}, {"op", ""}});
|
|
good("user/{name}/{op?}/{op2?}", "user/johndoe", {"johndoe", "", ""}, {{"name", "johndoe"}, {"op", ""}, {"op2", ""}});
|
|
good("user/{name}/{op?}/{op2?}", "user/johndoe/", {"johndoe", "", ""}, {{"name", "johndoe"}, {"op", ""}, {"op2", ""}});
|
|
good("user/{name}/{op?}/{op2?}", "user/johndoe/r", {"johndoe", "r", ""}, {{"name", "johndoe"}, {"op", "r"}, {"op2", ""}});
|
|
good("user/{name}/{op?}/{op2?}/b", "user/johndoe/b", {"johndoe", "", ""}, {{"name", "johndoe"}, {"op", ""}, {"op2", ""}});
|
|
good("user/{name}/{op?}/{op2?}/b", "user/johndoe//b", {"johndoe", "", ""}, {{"name", "johndoe"}, {"op", ""}, {"op2", ""}});
|
|
good("user/{name}/{op?}/{op2?}/b", "user/johndoe/r/b", {"johndoe", "r", ""}, {{"name", "johndoe"}, {"op", "r"}, {"op2", ""}});
|
|
|
|
// optional segment match + dot segments
|
|
good("user/{name}/{op?}", "user/johndoe/.", {"johndoe", ""}, {{"name", "johndoe"}, {"op", ""}});
|
|
good("user/{name}/{op?}", "user/johndoe/./r", {"johndoe", "r"}, {{"name", "johndoe"}, {"op", "r"}});
|
|
bad("user/{name}/{op?}", "user/johndoe/..");
|
|
good("user/{name}/{op?}", "user/johndoe/../maria", {"maria", ""}, {{"name", "maria"}, {"op", ""}});
|
|
good("user/{name}/{op?}", "user/johndoe/../maria/./r", {"maria", "r"}, {{"name", "maria"}, {"op", "r"}});
|
|
good("user/{name}/{op?}", "user/johndoe/r/../s", {"johndoe", "s"}, {{"name", "johndoe"}, {"op", "s"}});
|
|
|
|
// star segment
|
|
good("user/{name}/{op*}", "user/johndoe", {"johndoe", ""}, {{"name", "johndoe"}, {"op", ""}});
|
|
good("user/{name}/{op*}", "user/johndoe/", {"johndoe", ""}, {{"name", "johndoe"}, {"op", ""}});
|
|
good("user/{name}/{op*}", "user/johndoe/r", {"johndoe", "r"}, {{"name", "johndoe"}, {"op", "r"}});
|
|
good("user/{name}/{op*}", "user/johndoe/r/r1", {"johndoe", "r/r1"}, {{"name", "johndoe"}, {"op", "r/r1"}});
|
|
good("user/{name}/{op*}", "user/johndoe/r/r1/r2", {"johndoe", "r/r1/r2"}, {{"name", "johndoe"}, {"op", "r/r1/r2"}});
|
|
good("user/{name}/{op*}/{op2*}", "user/johndoe", {"johndoe", "", ""}, {{"name", "johndoe"}, {"op", ""}, {"op2", ""}});
|
|
good("user/{name}/{op*}/{op2*}", "user/johndoe/", {"johndoe", "", ""}, {{"name", "johndoe"}, {"op", ""}, {"op2", ""}});
|
|
good("user/{name}/{op*}/{op2*}", "user/johndoe/r", {"johndoe", "r", ""}, {{"name", "johndoe"}, {"op", "r"}, {"op2", ""}});
|
|
good("user/{name}/{op*}/b", "user/johndoe/b", {"johndoe", ""}, {{"name", "johndoe"}, {"op", ""}});
|
|
good("user/{name}/{op*}/b", "user/johndoe/r/b", {"johndoe", "r"}, {{"name", "johndoe"}, {"op", "r"}});
|
|
good("user/{name}/{op*}/b", "user/johndoe/r/r1/b", {"johndoe", "r/r1"}, {{"name", "johndoe"}, {"op", "r/r1"}});
|
|
good("user/{name}/{op*}/b", "user/johndoe/r/r1/r2/b", {"johndoe", "r/r1/r2"}, {{"name", "johndoe"}, {"op", "r/r1/r2"}});
|
|
good("user/{name}/{op*}/b", "user/johndoe/b", {"johndoe", ""}, {{"name", "johndoe"}, {"op", ""}});
|
|
|
|
// star segment match + dot segments
|
|
good("user/{name}/{op*}", "user/johndoe/../r/../s", {"s", ""}, {{"name", "s"}, {"op", ""}});
|
|
bad("user/{name}/{op*}", "user/johndoe/../../r/s");
|
|
good("user/{name}/{op*}", "user/johndoe/../../user/s", {"s", ""}, {{"name", "s"}, {"op", ""}});
|
|
good("user/{name}/{op*}", "user/johndoe/r/../../../user/s", {"s", ""}, {{"name", "s"}, {"op", ""}});
|
|
good("user/{name}/{op*}", "user/johndoe/r/../s", {"johndoe", "r/../s"}, {{"name", "johndoe"}, {"op", "r/../s"}});
|
|
good("user/{name}/{op*}", "user/johndoe/r/../s/..", {"johndoe", "r/../s/.."}, {{"name", "johndoe"}, {"op", "r/../s/.."}});
|
|
bad("user/{name}/{op*}", "user/johndoe/r/../s/../..");
|
|
|
|
// plus segment
|
|
bad("user/{name}/{op+}", "user/johndoe");
|
|
good("user/{name}/{op+}", "user/johndoe/", {"johndoe", ""}, {{"name", "johndoe"}, {"op", ""}});
|
|
good("user/{name}/{op+}", "user/johndoe/r", {"johndoe", "r"}, {{"name", "johndoe"}, {"op", "r"}});
|
|
good("user/{name}/{op+}", "user/johndoe/r/r1", {"johndoe", "r/r1"}, {{"name", "johndoe"}, {"op", "r/r1"}});
|
|
good("user/{name}/{op+}", "user/johndoe/r/r1/r2", {"johndoe", "r/r1/r2"}, {{"name", "johndoe"}, {"op", "r/r1/r2"}});
|
|
bad("user/{name}/{op+}/{op2+}", "user/johndoe");
|
|
bad("user/{name}/{op+}/{op2+}", "user/johndoe/");
|
|
bad("user/{name}/{op+}/{op2+}", "user/johndoe/r");
|
|
bad("user/{name}/{op+}/b", "user/johndoe/b");
|
|
good("user/{name}/{op+}/b", "user/johndoe/r/b", {"johndoe", "r"}, {{"name", "johndoe"}, {"op", "r"}});
|
|
good("user/{name}/{op+}/b", "user/johndoe/r/r1/b", {"johndoe", "r/r1"}, {{"name", "johndoe"}, {"op", "r/r1"}});
|
|
good("user/{name}/{op+}/b", "user/johndoe/r/r1/r2/b", {"johndoe", "r/r1/r2"}, {{"name", "johndoe"}, {"op", "r/r1/r2"}});
|
|
bad("user/{name}/{op+}/b", "user/johndoe/b");
|
|
|
|
// plus segment match + dot segments
|
|
bad("user/{name}/{op+}", "user/johndoe/../r/../s");
|
|
good("user/{name}/{op+}", "user/johndoe/../r/../s/", {"s", ""}, {{"name", "s"}, {"op", ""}});
|
|
bad("user/{name}/{op+}", "user/johndoe/../../r/s");
|
|
bad("user/{name}/{op+}", "user/johndoe/../../user/s");
|
|
good("user/{name}/{op+}", "user/johndoe/../../user/s/", {"s", ""}, {{"name", "s"}, {"op", ""}});
|
|
good("user/{name}/{op+}", "user/johndoe/../../user/s/r", {"s", "r"}, {{"name", "s"}, {"op", "r"}});
|
|
bad("user/{name}/{op+}", "user/johndoe/r/../../../user/s");
|
|
good("user/{name}/{op+}", "user/johndoe/r/../../../user/s/", {"s", ""}, {{"name", "s"}, {"op", ""}});
|
|
good("user/{name}/{op+}", "user/johndoe/r/../s", {"johndoe", "s"}, {{"name", "johndoe"}, {"op", "s"}});
|
|
bad("user/{name}/{op+}", "user/johndoe/r/../s/..");
|
|
bad("user/{name}/{op+}", "user/johndoe/r/../s/../..");
|
|
|
|
// segment tree
|
|
good({"user/view", "user/{}"}, 1, "user/johndoe", {"johndoe"});
|
|
good({"user/view", "user/{}"}, 0, "user/view", {});
|
|
good({"user/{}", "user/view"}, 1, "user/view", {});
|
|
good({"user/view", "user/{file*}"}, 1, "user/path/to/file.txt", {"path/to/file.txt"}, {{"file", "path/to/file.txt"}});
|
|
good({"user/view", "user/{name}", "user/{file+}"}, 1, "user/johndoe", {"johndoe"}, {{"name", "johndoe"}});
|
|
good({"user/view", "user/{name}", "user/{file+}"}, 2, "user/path/to/file.txt", {"path/to/file.txt"}, {{"file", "path/to/file.txt"}});
|
|
good({"a/a", "a/b", "a/c", "a/d", "a/e", "a/f", "a/g", "a/h", "a/i", "a/j", "a/k"}, 0, "a/a", {}, {});
|
|
|
|
// above root
|
|
bad("user/../..");
|
|
|
|
// neither segment nor field
|
|
bad("user/{", "user/johndoe");
|
|
}
|
|
|
|
static
|
|
void
|
|
good(
|
|
std::initializer_list<string_view> patterns,
|
|
std::size_t match_idx,
|
|
string_view request,
|
|
std::initializer_list<string_view> ms = {},
|
|
std::initializer_list<std::pair<string_view, string_view>> args = {})
|
|
{
|
|
router<int> r;
|
|
int i = 0;
|
|
for (auto p: patterns)
|
|
r.insert(p, i++);
|
|
matches m;
|
|
int const* v;
|
|
v = r.find(request, m);
|
|
if (!BOOST_TEST(v))
|
|
return;
|
|
BOOST_TEST_EQ(*v, match_idx);
|
|
|
|
BOOST_TEST_EQ(ms.size(), m.size());
|
|
BOOST_TEST_EQ(ms.size() == 0, m.empty());
|
|
|
|
{
|
|
auto it0 = ms.begin();
|
|
auto it1 = m.begin();
|
|
auto end0 = ms.end();
|
|
auto end1 = m.end();
|
|
std::size_t j = 0;
|
|
while (
|
|
it0 != end0 &&
|
|
it1 != end1)
|
|
{
|
|
BOOST_TEST_EQ(*it0, *it1++);
|
|
BOOST_TEST_EQ(*it0, m[j]);
|
|
BOOST_TEST_EQ(*it0++, m.at(j++));
|
|
}
|
|
BOOST_TEST_THROWS(m.at(j), std::out_of_range);
|
|
}
|
|
|
|
{
|
|
auto it = args.begin();
|
|
auto end = args.end();
|
|
while (it != end)
|
|
{
|
|
BOOST_TEST(m.find(it->first) != m.end());
|
|
BOOST_TEST_EQ(m[it->first], it->second);
|
|
BOOST_TEST_EQ(m.at(it->first), it->second);
|
|
++it;
|
|
}
|
|
BOOST_TEST(m.find("XXX") == m.end());
|
|
BOOST_TEST_THROWS(m.at("XXX"), std::out_of_range);
|
|
}
|
|
};
|
|
|
|
static
|
|
void
|
|
good(
|
|
string_view pattern,
|
|
string_view request,
|
|
std::initializer_list<string_view> matches = {},
|
|
std::initializer_list<std::pair<string_view, string_view>> args = {})
|
|
{
|
|
return good({pattern}, 0, request, matches, args);
|
|
};
|
|
|
|
static
|
|
void
|
|
bad(
|
|
string_view pattern,
|
|
string_view request)
|
|
{
|
|
router<int> r;
|
|
try
|
|
{
|
|
r.insert(pattern, 0);
|
|
}
|
|
catch (...)
|
|
{
|
|
BOOST_TEST(true);
|
|
return;
|
|
}
|
|
matches m;
|
|
int const* v;
|
|
v = r.find(request, m);
|
|
BOOST_TEST_NOT(v);
|
|
};
|
|
|
|
static
|
|
void
|
|
bad(string_view pattern)
|
|
{
|
|
router<int> r;
|
|
BOOST_TEST_THROWS(r.insert(pattern, 0), system_error);
|
|
};
|
|
|
|
void
|
|
run()
|
|
{
|
|
testPatterns();
|
|
}
|
|
};
|
|
|
|
TEST_SUITE(router_test, "boost.url.router");
|
|
|
|
} // urls
|
|
} // boost
|