Fix the OPDS stream to handle custom ROOT prefix

As we render the entry's xml in a separated steps, we need to pass the
rootLocation to all the internal rendering.

Testing with and without root is not so easy.
I've simply made all server tests using a ROOT prefix.
We can assume that if the ROOT is present everywhere we need it, it will not
when we don't need. (As long as we don't hardcode "ROOT" in the server.)
This commit is contained in:
Matthieu Gautier 2021-11-17 15:13:21 +01:00
parent 22e5327dcf
commit 66c40817ee
3 changed files with 132 additions and 130 deletions

View File

@ -95,23 +95,24 @@ kainjow::mustache::object getSingleBookData(const Book& book)
}; };
} }
std::string getSingleBookEntryXML(const Book& book, bool withXMLHeader, const std::string& endpointRoot, bool partial) std::string getSingleBookEntryXML(const Book& book, bool withXMLHeader, const std::string& rootLocation, const std::string& endpointRoot, bool partial)
{ {
auto data = getSingleBookData(book); auto data = getSingleBookData(book);
data["with_xml_header"] = MustacheData(withXMLHeader); data["with_xml_header"] = MustacheData(withXMLHeader);
data["dump_partial_entries"] = MustacheData(partial); data["dump_partial_entries"] = MustacheData(partial);
data["endpoint_root"] = endpointRoot; data["endpoint_root"] = endpointRoot;
data["root"] = rootLocation;
return render_template(RESOURCE::templates::catalog_v2_entry_xml, data); return render_template(RESOURCE::templates::catalog_v2_entry_xml, data);
} }
BooksData getBooksData(const Library* library, const std::vector<std::string>& bookIds, const std::string& endpointRoot, bool partial) BooksData getBooksData(const Library* library, const std::vector<std::string>& bookIds, const std::string& rootLocation, const std::string& endpointRoot, bool partial)
{ {
BooksData booksData; BooksData booksData;
for ( const auto& bookId : bookIds ) { for ( const auto& bookId : bookIds ) {
try { try {
const Book book = library->getBookByIdThreadSafe(bookId); const Book book = library->getBookByIdThreadSafe(bookId);
booksData.push_back(kainjow::mustache::object{ booksData.push_back(kainjow::mustache::object{
{"entry", getSingleBookEntryXML(book, false, endpointRoot, partial)} {"entry", getSingleBookEntryXML(book, false, rootLocation, endpointRoot, partial)}
}); });
} catch ( const std::out_of_range& ) { } catch ( const std::out_of_range& ) {
// the book was removed from the library since its id was obtained // the book was removed from the library since its id was obtained
@ -135,7 +136,7 @@ std::string getLanguageSelfName(const std::string& lang) {
string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds, const std::string& query) const string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds, const std::string& query) const
{ {
const auto booksData = getBooksData(library, bookIds, "", false); const auto booksData = getBooksData(library, bookIds, rootLocation, "", false);
const kainjow::mustache::object template_data{ const kainjow::mustache::object template_data{
{"date", gen_date_str()}, {"date", gen_date_str()},
{"root", rootLocation}, {"root", rootLocation},
@ -153,7 +154,7 @@ string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds, const s
string OPDSDumper::dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const std::string& query, bool partial) const string OPDSDumper::dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const std::string& query, bool partial) const
{ {
const auto endpointRoot = rootLocation + "/catalog/v2"; const auto endpointRoot = rootLocation + "/catalog/v2";
const auto booksData = getBooksData(library, bookIds, endpointRoot, partial); const auto booksData = getBooksData(library, bookIds, rootLocation, endpointRoot, partial);
const char* const endpoint = partial ? "/partial_entries" : "/entries"; const char* const endpoint = partial ? "/partial_entries" : "/entries";
const kainjow::mustache::object template_data{ const kainjow::mustache::object template_data{
@ -174,7 +175,7 @@ string OPDSDumper::dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const
std::string OPDSDumper::dumpOPDSCompleteEntry(const std::string& bookId) const std::string OPDSDumper::dumpOPDSCompleteEntry(const std::string& bookId) const
{ {
return getSingleBookEntryXML(library->getBookById(bookId), true, "", false); return getSingleBookEntryXML(library->getBookById(bookId), true, rootLocation, "", false);
} }
std::string OPDSDumper::categoriesOPDSFeed() const std::string OPDSDumper::categoriesOPDSFeed() const

View File

@ -16,9 +16,9 @@
<articleCount>{{article_count}}</articleCount> <articleCount>{{article_count}}</articleCount>
<mediaCount>{{media_count}}</mediaCount> <mediaCount>{{media_count}}</mediaCount>
{{#icons}}<link rel="http://opds-spec.org/image/thumbnail" {{#icons}}<link rel="http://opds-spec.org/image/thumbnail"
href="/meta?name=Illustration_{{icon_width}}x{{icon_height}}@{{icon_scale}}&amp;content={{{content_id}}}" href="{{root}}/meta?name=Illustration_{{icon_width}}x{{icon_height}}@{{icon_scale}}&amp;content={{{content_id}}}"
type="image/png;width={{icon_width}};height={{icon_height}};scale={{icon_scale}}"/> type="image/png;width={{icon_width}};height={{icon_height}};scale={{icon_scale}}"/>
{{/icons}}<link type="text/html" href="/{{{content_id}}}" /> {{/icons}}<link type="text/html" href="{{root}}/{{{content_id}}}" />
<author> <author>
<name>{{author_name}}</name> <name>{{author_name}}</name>
</author> </author>

View File

@ -84,7 +84,6 @@ ZimFileServer::ZimFileServer(int serverPort, std::string libraryFilePath)
if ( kiwix::isRelativePath(libraryFilePath) ) if ( kiwix::isRelativePath(libraryFilePath) )
libraryFilePath = kiwix::computeAbsolutePath(kiwix::getCurrentDirectory(), libraryFilePath); libraryFilePath = kiwix::computeAbsolutePath(kiwix::getCurrentDirectory(), libraryFilePath);
manager.readFile(libraryFilePath, true, true); manager.readFile(libraryFilePath, true, true);
run(serverPort); run(serverPort);
} }
@ -95,7 +94,6 @@ ZimFileServer::ZimFileServer(int serverPort, const FilePathCollection& zimpaths,
if (!manager.addBookFromPath(zimpath, zimpath, "", false)) if (!manager.addBookFromPath(zimpath, zimpath, "", false))
throw std::runtime_error("Unable to add the ZIM file '" + zimpath + "'"); throw std::runtime_error("Unable to add the ZIM file '" + zimpath + "'");
} }
run(serverPort, indexTemplateString); run(serverPort, indexTemplateString);
} }
@ -104,6 +102,7 @@ void ZimFileServer::run(int serverPort, std::string indexTemplateString)
const std::string address = "127.0.0.1"; const std::string address = "127.0.0.1";
nameMapper.reset(new kiwix::HumanReadableNameMapper(library, false)); nameMapper.reset(new kiwix::HumanReadableNameMapper(library, false));
server.reset(new kiwix::Server(&library, nameMapper.get())); server.reset(new kiwix::Server(&library, nameMapper.get()));
server->setRoot("ROOT");
server->setAddress(address); server->setAddress(address);
server->setPort(serverPort); server->setPort(serverPort);
server->setNbThreads(2); server->setNbThreads(2);
@ -162,52 +161,52 @@ std::ostream& operator<<(std::ostream& out, const Resource& r)
typedef std::vector<Resource> ResourceCollection; typedef std::vector<Resource> ResourceCollection;
const ResourceCollection resources200Compressible{ const ResourceCollection resources200Compressible{
{ WITH_ETAG, "/" }, { WITH_ETAG, "/ROOT/" },
{ WITH_ETAG, "/skin/jquery-ui/jquery-ui.structure.min.css" }, { WITH_ETAG, "/ROOT/skin/jquery-ui/jquery-ui.structure.min.css" },
{ WITH_ETAG, "/skin/jquery-ui/jquery-ui.min.js" }, { WITH_ETAG, "/ROOT/skin/jquery-ui/jquery-ui.min.js" },
{ WITH_ETAG, "/skin/jquery-ui/external/jquery/jquery.js" }, { WITH_ETAG, "/ROOT/skin/jquery-ui/external/jquery/jquery.js" },
{ WITH_ETAG, "/skin/jquery-ui/jquery-ui.theme.min.css" }, { WITH_ETAG, "/ROOT/skin/jquery-ui/jquery-ui.theme.min.css" },
{ WITH_ETAG, "/skin/jquery-ui/jquery-ui.min.css" }, { WITH_ETAG, "/ROOT/skin/jquery-ui/jquery-ui.min.css" },
{ WITH_ETAG, "/skin/taskbar.js" }, { WITH_ETAG, "/ROOT/skin/taskbar.js" },
{ WITH_ETAG, "/skin/taskbar.css" }, { WITH_ETAG, "/ROOT/skin/taskbar.css" },
{ WITH_ETAG, "/skin/block_external.js" }, { WITH_ETAG, "/ROOT/skin/block_external.js" },
{ NO_ETAG, "/catalog/root.xml" }, { NO_ETAG, "/ROOT/catalog/root.xml" },
{ NO_ETAG, "/catalog/searchdescription.xml" }, { NO_ETAG, "/ROOT/catalog/searchdescription.xml" },
{ NO_ETAG, "/catalog/search" }, { NO_ETAG, "/ROOT/catalog/search" },
{ NO_ETAG, "/search?content=zimfile&pattern=a" }, { NO_ETAG, "/ROOT/search?content=zimfile&pattern=a" },
{ NO_ETAG, "/suggest?content=zimfile" }, { NO_ETAG, "/ROOT/suggest?content=zimfile" },
{ NO_ETAG, "/suggest?content=zimfile&term=ray" }, { NO_ETAG, "/ROOT/suggest?content=zimfile&term=ray" },
{ NO_ETAG, "/catch/external?source=www.example.com" }, { NO_ETAG, "/ROOT/catch/external?source=www.example.com" },
{ WITH_ETAG, "/zimfile/A/index" }, { WITH_ETAG, "/ROOT/zimfile/A/index" },
{ WITH_ETAG, "/zimfile/A/Ray_Charles" }, { WITH_ETAG, "/ROOT/zimfile/A/Ray_Charles" },
}; };
const ResourceCollection resources200Uncompressible{ const ResourceCollection resources200Uncompressible{
{ WITH_ETAG, "/skin/jquery-ui/images/animated-overlay.gif" }, { WITH_ETAG, "/ROOT/skin/jquery-ui/images/animated-overlay.gif" },
{ WITH_ETAG, "/skin/caret.png" }, { WITH_ETAG, "/ROOT/skin/caret.png" },
{ WITH_ETAG, "/meta?content=zimfile&name=title" }, { WITH_ETAG, "/ROOT/meta?content=zimfile&name=title" },
{ WITH_ETAG, "/meta?content=zimfile&name=description" }, { WITH_ETAG, "/ROOT/meta?content=zimfile&name=description" },
{ WITH_ETAG, "/meta?content=zimfile&name=language" }, { WITH_ETAG, "/ROOT/meta?content=zimfile&name=language" },
{ WITH_ETAG, "/meta?content=zimfile&name=name" }, { WITH_ETAG, "/ROOT/meta?content=zimfile&name=name" },
{ WITH_ETAG, "/meta?content=zimfile&name=tags" }, { WITH_ETAG, "/ROOT/meta?content=zimfile&name=tags" },
{ WITH_ETAG, "/meta?content=zimfile&name=date" }, { WITH_ETAG, "/ROOT/meta?content=zimfile&name=date" },
{ WITH_ETAG, "/meta?content=zimfile&name=creator" }, { WITH_ETAG, "/ROOT/meta?content=zimfile&name=creator" },
{ WITH_ETAG, "/meta?content=zimfile&name=publisher" }, { WITH_ETAG, "/ROOT/meta?content=zimfile&name=publisher" },
{ WITH_ETAG, "/meta?content=zimfile&name=favicon" }, { WITH_ETAG, "/ROOT/meta?content=zimfile&name=favicon" },
{ WITH_ETAG, "/meta?content=zimfile&name=Illustration_48x48@1" }, { WITH_ETAG, "/ROOT/meta?content=zimfile&name=Illustration_48x48@1" },
{ WITH_ETAG, "/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg" }, { WITH_ETAG, "/ROOT/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg" },
{ WITH_ETAG, "/corner_cases/A/empty.html" }, { WITH_ETAG, "/ROOT/corner_cases/A/empty.html" },
{ WITH_ETAG, "/corner_cases/-/empty.css" }, { WITH_ETAG, "/ROOT/corner_cases/-/empty.css" },
{ WITH_ETAG, "/corner_cases/-/empty.js" }, { WITH_ETAG, "/ROOT/corner_cases/-/empty.js" },
}; };
ResourceCollection all200Resources() ResourceCollection all200Resources()
@ -223,7 +222,7 @@ TEST(indexTemplateStringTest, emptyIndexTemplate) {
}; };
ZimFileServer zfs(PORT, ZIMFILES, ""); ZimFileServer zfs(PORT, ZIMFILES, "");
EXPECT_EQ(200, zfs.GET("/")->status); EXPECT_EQ(200, zfs.GET("/ROOT/")->status);
} }
TEST(indexTemplateStringTest, indexTemplateCheck) { TEST(indexTemplateStringTest, indexTemplateCheck) {
@ -239,9 +238,9 @@ TEST(indexTemplateStringTest, indexTemplateCheck) {
"</html>"); "</html>");
EXPECT_EQ("<!DOCTYPE html><head>" EXPECT_EQ("<!DOCTYPE html><head>"
"<title>Welcome to kiwix library</title>" "<title>Welcome to kiwix library</title>"
"<link type=\"root\" href=\"\">" "<link type=\"root\" href=\"/ROOT\">"
"</head>" "</head>"
"</html>", zfs.GET("/")->body); "</html>", zfs.GET("/ROOT/")->body);
} }
TEST_F(ServerTest, 200) TEST_F(ServerTest, 200)
@ -270,22 +269,24 @@ TEST_F(ServerTest, UncompressibleContentIsNotCompressed)
} }
const char* urls404[] = { const char* urls404[] = {
"/non-existent-item", "/",
"/skin/non-existent-skin-resource", "/zimfile",
"/catalog", "/ROOT/non-existent-item",
"/catalog/non-existent-item", "/ROOT/skin/non-existent-skin-resource",
"/catalogBLABLABLA/root.xml", "/ROOT/catalog",
"/meta", "/ROOT/catalog/non-existent-item",
"/meta?content=zimfile", "/ROOT/catalogBLABLABLA/root.xml",
"/meta?content=zimfile&name=non-existent-item", "/ROOT/meta",
"/meta?content=non-existent-book&name=title", "/ROOT/meta?content=zimfile",
"/random", "/ROOT/meta?content=zimfile&name=non-existent-item",
"/random?content=non-existent-book", "/ROOT/meta?content=non-existent-book&name=title",
"/search", "/ROOT/random",
"/suggest", "/ROOT/random?content=non-existent-book",
"/suggest?content=non-existent-book&term=abcd", "/ROOT/search",
"/catch/external", "/ROOT/suggest",
"/zimfile/A/non-existent-article", "/ROOT/suggest?content=non-existent-book&term=abcd",
"/ROOT/catch/external",
"/ROOT/zimfile/A/non-existent-article",
}; };
TEST_F(ServerTest, 404) TEST_F(ServerTest, 404)
@ -296,7 +297,7 @@ TEST_F(ServerTest, 404)
TEST_F(ServerTest, RandomPageRedirectsToAnExistingArticle) TEST_F(ServerTest, RandomPageRedirectsToAnExistingArticle)
{ {
auto g = zfs1_->GET("/random?content=zimfile"); auto g = zfs1_->GET("/ROOT/random?content=zimfile");
ASSERT_EQ(302, g->status); ASSERT_EQ(302, g->status);
ASSERT_TRUE(g->has_header("Location")); ASSERT_TRUE(g->has_header("Location"));
ASSERT_TRUE(g->get_header_value("Location").find("/zimfile/A/") != std::string::npos); ASSERT_TRUE(g->get_header_value("Location").find("/zimfile/A/") != std::string::npos);
@ -304,10 +305,10 @@ TEST_F(ServerTest, RandomPageRedirectsToAnExistingArticle)
TEST_F(ServerTest, BookMainPageIsRedirectedToArticleIndex) TEST_F(ServerTest, BookMainPageIsRedirectedToArticleIndex)
{ {
auto g = zfs1_->GET("/zimfile"); auto g = zfs1_->GET("/ROOT/zimfile");
ASSERT_EQ(302, g->status); ASSERT_EQ(302, g->status);
ASSERT_TRUE(g->has_header("Location")); ASSERT_TRUE(g->has_header("Location"));
ASSERT_EQ("/zimfile/A/index", g->get_header_value("Location")); ASSERT_EQ("/ROOT/zimfile/A/index", g->get_header_value("Location"));
} }
TEST_F(ServerTest, HeadMethodIsSupported) TEST_F(ServerTest, HeadMethodIsSupported)
@ -466,7 +467,7 @@ TEST_F(ServerTest, IfNoneMatchRequestsWithMismatchingETagResultIn200Responses)
TEST_F(ServerTest, ValidSingleRangeByteRangeRequestsAreHandledProperly) TEST_F(ServerTest, ValidSingleRangeByteRangeRequestsAreHandledProperly)
{ {
const char url[] = "/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; const char url[] = "/ROOT/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg";
const auto full = zfs1_->GET(url); const auto full = zfs1_->GET(url);
EXPECT_FALSE(full->has_header("Content-Range")); EXPECT_FALSE(full->has_header("Content-Range"));
EXPECT_EQ("bytes", full->get_header_value("Accept-Ranges")); EXPECT_EQ("bytes", full->get_header_value("Accept-Ranges"));
@ -516,7 +517,7 @@ TEST_F(ServerTest, ValidSingleRangeByteRangeRequestsAreHandledProperly)
TEST_F(ServerTest, InvalidAndMultiRangeByteRangeRequestsResultIn416Responses) TEST_F(ServerTest, InvalidAndMultiRangeByteRangeRequestsResultIn416Responses)
{ {
const char url[] = "/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; const char url[] = "/ROOT/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg";
const char* invalidRanges[] = { const char* invalidRanges[] = {
"0-10", "bytes=", "bytes=123", "bytes=-10-20", "bytes=10-20xxx", "0-10", "bytes=", "bytes=123", "bytes=-10-20", "bytes=10-20xxx",
@ -537,7 +538,7 @@ TEST_F(ServerTest, InvalidAndMultiRangeByteRangeRequestsResultIn416Responses)
TEST_F(ServerTest, ValidByteRangeRequestsOfZeroSizedEntriesResultIn416Responses) TEST_F(ServerTest, ValidByteRangeRequestsOfZeroSizedEntriesResultIn416Responses)
{ {
const char url[] = "/corner_cases/-/empty.js"; const char url[] = "/ROOT/corner_cases/-/empty.js";
const char* ranges[] = { const char* ranges[] = {
"bytes=0-", "bytes=0-",
@ -556,7 +557,7 @@ TEST_F(ServerTest, ValidByteRangeRequestsOfZeroSizedEntriesResultIn416Responses)
TEST_F(ServerTest, RangeHasPrecedenceOverCompression) TEST_F(ServerTest, RangeHasPrecedenceOverCompression)
{ {
const char url[] = "/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; const char url[] = "/ROOT/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg";
const Headers onlyRange{ {"Range", "bytes=123-456"} }; const Headers onlyRange{ {"Range", "bytes=123-456"} };
Headers rangeAndCompression(onlyRange); Headers rangeAndCompression(onlyRange);
@ -571,7 +572,7 @@ TEST_F(ServerTest, RangeHasPrecedenceOverCompression)
TEST_F(ServerTest, RangeHeaderIsCaseInsensitive) TEST_F(ServerTest, RangeHeaderIsCaseInsensitive)
{ {
const char url[] = "/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; const char url[] = "/ROOT/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg";
const auto r0 = zfs1_->GET(url, { {"Range", "bytes=100-200"} } ); const auto r0 = zfs1_->GET(url, { {"Range", "bytes=100-200"} } );
const char* header_variations[] = { "RANGE", "range", "rAnGe", "RaNgE" }; const char* header_variations[] = { "RANGE", "range", "rAnGe", "RaNgE" };
@ -644,7 +645,7 @@ std::string maskVariableOPDSFeedData(std::string s)
" <link rel=\"self\" href=\"\" type=\"application/atom+xml\" />\n" \ " <link rel=\"self\" href=\"\" type=\"application/atom+xml\" />\n" \
" <link rel=\"search\"" \ " <link rel=\"search\"" \
" type=\"application/opensearchdescription+xml\"" \ " type=\"application/opensearchdescription+xml\"" \
" href=\"/catalog/searchdescription.xml\" />\n" " href=\"/ROOT/catalog/searchdescription.xml\" />\n"
#define CHARLES_RAY_CATALOG_ENTRY \ #define CHARLES_RAY_CATALOG_ENTRY \
" <entry>\n" \ " <entry>\n" \
@ -659,7 +660,7 @@ std::string maskVariableOPDSFeedData(std::string s)
" <tags>unittest;wikipedia;_category:jazz;_pictures:no;_videos:no;_details:no;_ftindex:yes</tags>\n" \ " <tags>unittest;wikipedia;_category:jazz;_pictures:no;_videos:no;_details:no;_ftindex:yes</tags>\n" \
" <articleCount>284</articleCount>\n" \ " <articleCount>284</articleCount>\n" \
" <mediaCount>2</mediaCount>\n" \ " <mediaCount>2</mediaCount>\n" \
" <link type=\"text/html\" href=\"/zimfile\" />\n" \ " <link type=\"text/html\" href=\"/ROOT/zimfile\" />\n" \
" <author>\n" \ " <author>\n" \
" <name>Wikipedia</name>\n" \ " <name>Wikipedia</name>\n" \
" </author>\n" \ " </author>\n" \
@ -683,9 +684,9 @@ std::string maskVariableOPDSFeedData(std::string s)
" <articleCount>284</articleCount>\n" \ " <articleCount>284</articleCount>\n" \
" <mediaCount>2</mediaCount>\n" \ " <mediaCount>2</mediaCount>\n" \
" <link rel=\"http://opds-spec.org/image/thumbnail\"\n" \ " <link rel=\"http://opds-spec.org/image/thumbnail\"\n" \
" href=\"/meta?name=Illustration_48x48@1&amp;content=zimfile\"\n" \ " href=\"/ROOT/meta?name=Illustration_48x48@1&amp;content=zimfile\"\n" \
" type=\"image/png;width=48;height=48;scale=1\"/>\n" \ " type=\"image/png;width=48;height=48;scale=1\"/>\n" \
" <link type=\"text/html\" href=\"/zimfile\" />\n" \ " <link type=\"text/html\" href=\"/ROOT/zimfile\" />\n" \
" <author>\n" \ " <author>\n" \
" <name>Wikipedia</name>\n" \ " <name>Wikipedia</name>\n" \
" </author>\n" \ " </author>\n" \
@ -708,7 +709,7 @@ std::string maskVariableOPDSFeedData(std::string s)
" <tags>unittest;wikipedia;_pictures:no;_videos:no;_details:no</tags>\n" \ " <tags>unittest;wikipedia;_pictures:no;_videos:no;_details:no</tags>\n" \
" <articleCount>284</articleCount>\n" \ " <articleCount>284</articleCount>\n" \
" <mediaCount>2</mediaCount>\n" \ " <mediaCount>2</mediaCount>\n" \
" <link type=\"text/html\" href=\"/zimfile\" />\n" \ " <link type=\"text/html\" href=\"/ROOT/zimfile\" />\n" \
" <author>\n" \ " <author>\n" \
" <name>Wikipedia</name>\n" \ " <name>Wikipedia</name>\n" \
" </author>\n" \ " </author>\n" \
@ -720,7 +721,7 @@ std::string maskVariableOPDSFeedData(std::string s)
TEST_F(LibraryServerTest, catalog_root_xml) TEST_F(LibraryServerTest, catalog_root_xml)
{ {
const auto r = zfs1_->GET("/catalog/root.xml"); const auto r = zfs1_->GET("/ROOT/catalog/root.xml");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -738,7 +739,7 @@ TEST_F(LibraryServerTest, catalog_root_xml)
TEST_F(LibraryServerTest, catalog_searchdescription_xml) TEST_F(LibraryServerTest, catalog_searchdescription_xml)
{ {
const auto r = zfs1_->GET("/catalog/searchdescription.xml"); const auto r = zfs1_->GET("/ROOT/catalog/searchdescription.xml");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(r->body, EXPECT_EQ(r->body,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
@ -749,14 +750,14 @@ TEST_F(LibraryServerTest, catalog_searchdescription_xml)
" xmlns:atom=\"http://www.w3.org/2005/Atom\"\n" " xmlns:atom=\"http://www.w3.org/2005/Atom\"\n"
" xmlns:k=\"http://kiwix.org/opensearchextension/1.0\"\n" " xmlns:k=\"http://kiwix.org/opensearchextension/1.0\"\n"
" indexOffset=\"0\"\n" " indexOffset=\"0\"\n"
" template=\"/catalog/search?q={searchTerms?}&lang={language?}&name={k:name?}&tag={k:tag?}&notag={k:notag?}&maxsize={k:maxsize?}&count={count?}&start={startIndex?}\"/>\n" " template=\"/ROOT/catalog/search?q={searchTerms?}&lang={language?}&name={k:name?}&tag={k:tag?}&notag={k:notag?}&maxsize={k:maxsize?}&count={count?}&start={startIndex?}\"/>\n"
"</OpenSearchDescription>\n" "</OpenSearchDescription>\n"
); );
} }
TEST_F(LibraryServerTest, catalog_search_by_phrase) TEST_F(LibraryServerTest, catalog_search_by_phrase)
{ {
const auto r = zfs1_->GET("/catalog/search?q=\"ray%20charles\""); const auto r = zfs1_->GET("/ROOT/catalog/search?q=\"ray%20charles\"");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -775,7 +776,7 @@ TEST_F(LibraryServerTest, catalog_search_by_phrase)
TEST_F(LibraryServerTest, catalog_search_by_words) TEST_F(LibraryServerTest, catalog_search_by_words)
{ {
const auto r = zfs1_->GET("/catalog/search?q=ray%20charles"); const auto r = zfs1_->GET("/ROOT/catalog/search?q=ray%20charles");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -796,7 +797,7 @@ TEST_F(LibraryServerTest, catalog_search_by_words)
TEST_F(LibraryServerTest, catalog_prefix_search) TEST_F(LibraryServerTest, catalog_prefix_search)
{ {
{ {
const auto r = zfs1_->GET("/catalog/search?q=description:ray%20description:charles"); const auto r = zfs1_->GET("/ROOT/catalog/search?q=description:ray%20description:charles");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -813,7 +814,7 @@ TEST_F(LibraryServerTest, catalog_prefix_search)
); );
} }
{ {
const auto r = zfs1_->GET("/catalog/search?q=title:\"ray%20charles\""); const auto r = zfs1_->GET("/ROOT/catalog/search?q=title:\"ray%20charles\"");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -832,7 +833,7 @@ TEST_F(LibraryServerTest, catalog_prefix_search)
TEST_F(LibraryServerTest, catalog_search_with_word_exclusion) TEST_F(LibraryServerTest, catalog_search_with_word_exclusion)
{ {
const auto r = zfs1_->GET("/catalog/search?q=ray%20-uncategorized"); const auto r = zfs1_->GET("/ROOT/catalog/search?q=ray%20-uncategorized");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -851,7 +852,7 @@ TEST_F(LibraryServerTest, catalog_search_with_word_exclusion)
TEST_F(LibraryServerTest, catalog_search_by_tag) TEST_F(LibraryServerTest, catalog_search_by_tag)
{ {
const auto r = zfs1_->GET("/catalog/search?tag=_category:jazz"); const auto r = zfs1_->GET("/ROOT/catalog/search?tag=_category:jazz");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -869,7 +870,7 @@ TEST_F(LibraryServerTest, catalog_search_by_tag)
TEST_F(LibraryServerTest, catalog_search_by_category) TEST_F(LibraryServerTest, catalog_search_by_category)
{ {
const auto r = zfs1_->GET("/catalog/search?category=jazz"); const auto r = zfs1_->GET("/ROOT/catalog/search?category=jazz");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -888,7 +889,7 @@ TEST_F(LibraryServerTest, catalog_search_by_category)
TEST_F(LibraryServerTest, catalog_search_results_pagination) TEST_F(LibraryServerTest, catalog_search_results_pagination)
{ {
{ {
const auto r = zfs1_->GET("/catalog/search?count=1"); const auto r = zfs1_->GET("/ROOT/catalog/search?count=1");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -904,7 +905,7 @@ TEST_F(LibraryServerTest, catalog_search_results_pagination)
); );
} }
{ {
const auto r = zfs1_->GET("/catalog/search?start=1&count=1"); const auto r = zfs1_->GET("/ROOT/catalog/search?start=1&count=1");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -920,7 +921,7 @@ TEST_F(LibraryServerTest, catalog_search_results_pagination)
); );
} }
{ {
const auto r = zfs1_->GET("/catalog/search?start=100&count=10"); const auto r = zfs1_->GET("/ROOT/catalog/search?start=100&count=10");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
OPDS_FEED_TAG OPDS_FEED_TAG
@ -938,20 +939,20 @@ TEST_F(LibraryServerTest, catalog_search_results_pagination)
TEST_F(LibraryServerTest, catalog_v2_root) TEST_F(LibraryServerTest, catalog_v2_root)
{ {
const auto r = zfs1_->GET("/catalog/v2/root.xml"); const auto r = zfs1_->GET("/ROOT/catalog/v2/root.xml");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
const char expected_output[] = R"(<?xml version="1.0" encoding="UTF-8"?> const char expected_output[] = R"(<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" <feed xmlns="http://www.w3.org/2005/Atom"
xmlns:opds="https://specs.opds.io/opds-1.2"> xmlns:opds="https://specs.opds.io/opds-1.2">
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
<link rel="self" <link rel="self"
href="/catalog/v2/root.xml" href="/ROOT/catalog/v2/root.xml"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/> type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<link rel="start" <link rel="start"
href="/catalog/v2/root.xml" href="/ROOT/catalog/v2/root.xml"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/> type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<link rel="search" <link rel="search"
href="/catalog/v2/searchdescription.xml" href="/ROOT/catalog/v2/searchdescription.xml"
type="application/opensearchdescription+xml"/> type="application/opensearchdescription+xml"/>
<title>OPDS Catalog Root</title> <title>OPDS Catalog Root</title>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
@ -959,7 +960,7 @@ TEST_F(LibraryServerTest, catalog_v2_root)
<entry> <entry>
<title>All entries</title> <title>All entries</title>
<link rel="subsection" <link rel="subsection"
href="/catalog/v2/entries" href="/ROOT/catalog/v2/entries"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/> type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
@ -968,7 +969,7 @@ TEST_F(LibraryServerTest, catalog_v2_root)
<entry> <entry>
<title>All entries (partial)</title> <title>All entries (partial)</title>
<link rel="subsection" <link rel="subsection"
href="/catalog/v2/partial_entries" href="/ROOT/catalog/v2/partial_entries"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/> type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
@ -977,7 +978,7 @@ TEST_F(LibraryServerTest, catalog_v2_root)
<entry> <entry>
<title>List of categories</title> <title>List of categories</title>
<link rel="subsection" <link rel="subsection"
href="/catalog/v2/categories" href="/ROOT/catalog/v2/categories"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/> type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
@ -986,7 +987,7 @@ TEST_F(LibraryServerTest, catalog_v2_root)
<entry> <entry>
<title>List of languages</title> <title>List of languages</title>
<link rel="subsection" <link rel="subsection"
href="/catalog/v2/languages" href="/ROOT/catalog/v2/languages"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/> type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
@ -999,7 +1000,7 @@ TEST_F(LibraryServerTest, catalog_v2_root)
TEST_F(LibraryServerTest, catalog_v2_searchdescription_xml) TEST_F(LibraryServerTest, catalog_v2_searchdescription_xml)
{ {
const auto r = zfs1_->GET("/catalog/v2/searchdescription.xml"); const auto r = zfs1_->GET("/ROOT/catalog/v2/searchdescription.xml");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(r->body, EXPECT_EQ(r->body,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
@ -1010,24 +1011,24 @@ TEST_F(LibraryServerTest, catalog_v2_searchdescription_xml)
" xmlns:atom=\"http://www.w3.org/2005/Atom\"\n" " xmlns:atom=\"http://www.w3.org/2005/Atom\"\n"
" xmlns:k=\"http://kiwix.org/opensearchextension/1.0\"\n" " xmlns:k=\"http://kiwix.org/opensearchextension/1.0\"\n"
" indexOffset=\"0\"\n" " indexOffset=\"0\"\n"
" template=\"/catalog/v2/entries?q={searchTerms?}&lang={language?}&name={k:name?}&tag={k:tag?}&maxsize={k:maxsize?}&count={count?}&start={startIndex?}\"/>\n" " template=\"/ROOT/catalog/v2/entries?q={searchTerms?}&lang={language?}&name={k:name?}&tag={k:tag?}&maxsize={k:maxsize?}&count={count?}&start={startIndex?}\"/>\n"
"</OpenSearchDescription>\n" "</OpenSearchDescription>\n"
); );
} }
TEST_F(LibraryServerTest, catalog_v2_categories) TEST_F(LibraryServerTest, catalog_v2_categories)
{ {
const auto r = zfs1_->GET("/catalog/v2/categories"); const auto r = zfs1_->GET("/ROOT/catalog/v2/categories");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
const char expected_output[] = R"(<?xml version="1.0" encoding="UTF-8"?> const char expected_output[] = R"(<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" <feed xmlns="http://www.w3.org/2005/Atom"
xmlns:opds="https://specs.opds.io/opds-1.2"> xmlns:opds="https://specs.opds.io/opds-1.2">
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
<link rel="self" <link rel="self"
href="/catalog/v2/categories" href="/ROOT/catalog/v2/categories"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/> type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<link rel="start" <link rel="start"
href="/catalog/v2/root.xml" href="/ROOT/catalog/v2/root.xml"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/> type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<title>List of categories</title> <title>List of categories</title>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
@ -1035,7 +1036,7 @@ TEST_F(LibraryServerTest, catalog_v2_categories)
<entry> <entry>
<title>jazz</title> <title>jazz</title>
<link rel="subsection" <link rel="subsection"
href="/catalog/v2/entries?category=jazz" href="/ROOT/catalog/v2/entries?category=jazz"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/> type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
@ -1044,7 +1045,7 @@ TEST_F(LibraryServerTest, catalog_v2_categories)
<entry> <entry>
<title>wikipedia</title> <title>wikipedia</title>
<link rel="subsection" <link rel="subsection"
href="/catalog/v2/entries?category=wikipedia" href="/ROOT/catalog/v2/entries?category=wikipedia"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/> type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
@ -1057,7 +1058,7 @@ TEST_F(LibraryServerTest, catalog_v2_categories)
TEST_F(LibraryServerTest, catalog_v2_languages) TEST_F(LibraryServerTest, catalog_v2_languages)
{ {
const auto r = zfs1_->GET("/catalog/v2/languages"); const auto r = zfs1_->GET("/ROOT/catalog/v2/languages");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
const char expected_output[] = R"(<?xml version="1.0" encoding="UTF-8"?> const char expected_output[] = R"(<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" <feed xmlns="http://www.w3.org/2005/Atom"
@ -1066,10 +1067,10 @@ TEST_F(LibraryServerTest, catalog_v2_languages)
xmlns:thr="http://purl.org/syndication/thread/1.0"> xmlns:thr="http://purl.org/syndication/thread/1.0">
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
<link rel="self" <link rel="self"
href="/catalog/v2/languages" href="/ROOT/catalog/v2/languages"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/> type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<link rel="start" <link rel="start"
href="/catalog/v2/root.xml" href="/ROOT/catalog/v2/root.xml"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/> type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<title>List of languages</title> <title>List of languages</title>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
@ -1079,7 +1080,7 @@ TEST_F(LibraryServerTest, catalog_v2_languages)
<dc:language>eng</dc:language> <dc:language>eng</dc:language>
<thr:count>1</thr:count> <thr:count>1</thr:count>
<link rel="subsection" <link rel="subsection"
href="/catalog/v2/entries?lang=eng" href="/ROOT/catalog/v2/entries?lang=eng"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/> type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
@ -1089,7 +1090,7 @@ TEST_F(LibraryServerTest, catalog_v2_languages)
<dc:language>fra</dc:language> <dc:language>fra</dc:language>
<thr:count>1</thr:count> <thr:count>1</thr:count>
<link rel="subsection" <link rel="subsection"
href="/catalog/v2/entries?lang=fra" href="/ROOT/catalog/v2/entries?lang=fra"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/> type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
@ -1099,7 +1100,7 @@ TEST_F(LibraryServerTest, catalog_v2_languages)
<dc:language>rus</dc:language> <dc:language>rus</dc:language>
<thr:count>1</thr:count> <thr:count>1</thr:count>
<link rel="subsection" <link rel="subsection"
href="/catalog/v2/entries?lang=rus" href="/ROOT/catalog/v2/entries?lang=rus"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/> type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<updated>YYYY-MM-DDThh:mm:ssZ</updated> <updated>YYYY-MM-DDThh:mm:ssZ</updated>
<id>12345678-90ab-cdef-1234-567890abcdef</id> <id>12345678-90ab-cdef-1234-567890abcdef</id>
@ -1117,13 +1118,13 @@ TEST_F(LibraryServerTest, catalog_v2_languages)
" <id>12345678-90ab-cdef-1234-567890abcdef</id>\n" \ " <id>12345678-90ab-cdef-1234-567890abcdef</id>\n" \
"\n" \ "\n" \
" <link rel=\"self\"\n" \ " <link rel=\"self\"\n" \
" href=\"/catalog/v2/" x "\"\n" \ " href=\"/ROOT/catalog/v2/" x "\"\n" \
" type=\"application/atom+xml;profile=opds-catalog;kind=acquisition\"/>\n" \ " type=\"application/atom+xml;profile=opds-catalog;kind=acquisition\"/>\n" \
" <link rel=\"start\"\n" \ " <link rel=\"start\"\n" \
" href=\"/catalog/v2/root.xml\"\n" \ " href=\"/ROOT/catalog/v2/root.xml\"\n" \
" type=\"application/atom+xml;profile=opds-catalog;kind=navigation\"/>\n" \ " type=\"application/atom+xml;profile=opds-catalog;kind=navigation\"/>\n" \
" <link rel=\"up\"\n" \ " <link rel=\"up\"\n" \
" href=\"/catalog/v2/root.xml\"\n" \ " href=\"/ROOT/catalog/v2/root.xml\"\n" \
" type=\"application/atom+xml;profile=opds-catalog;kind=navigation\"/>\n" \ " type=\"application/atom+xml;profile=opds-catalog;kind=navigation\"/>\n" \
"\n" \ "\n" \
@ -1135,7 +1136,7 @@ TEST_F(LibraryServerTest, catalog_v2_languages)
TEST_F(LibraryServerTest, catalog_v2_entries) TEST_F(LibraryServerTest, catalog_v2_entries)
{ {
const auto r = zfs1_->GET("/catalog/v2/entries"); const auto r = zfs1_->GET("/ROOT/catalog/v2/entries");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
CATALOG_V2_ENTRIES_PREAMBLE("") CATALOG_V2_ENTRIES_PREAMBLE("")
@ -1152,7 +1153,7 @@ TEST_F(LibraryServerTest, catalog_v2_entries)
TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_range) TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_range)
{ {
{ {
const auto r = zfs1_->GET("/catalog/v2/entries?start=1"); const auto r = zfs1_->GET("/ROOT/catalog/v2/entries?start=1");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
CATALOG_V2_ENTRIES_PREAMBLE("?start=1") CATALOG_V2_ENTRIES_PREAMBLE("?start=1")
@ -1168,7 +1169,7 @@ TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_range)
} }
{ {
const auto r = zfs1_->GET("/catalog/v2/entries?count=2"); const auto r = zfs1_->GET("/ROOT/catalog/v2/entries?count=2");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
CATALOG_V2_ENTRIES_PREAMBLE("?count=2") CATALOG_V2_ENTRIES_PREAMBLE("?count=2")
@ -1184,7 +1185,7 @@ TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_range)
} }
{ {
const auto r = zfs1_->GET("/catalog/v2/entries?start=1&count=1"); const auto r = zfs1_->GET("/ROOT/catalog/v2/entries?start=1&count=1");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
CATALOG_V2_ENTRIES_PREAMBLE("?count=1&start=1") CATALOG_V2_ENTRIES_PREAMBLE("?count=1&start=1")
@ -1201,7 +1202,7 @@ TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_range)
TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_search_terms) TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_search_terms)
{ {
const auto r = zfs1_->GET("/catalog/v2/entries?q=\"ray%20charles\""); const auto r = zfs1_->GET("/ROOT/catalog/v2/entries?q=\"ray%20charles\"");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
CATALOG_V2_ENTRIES_PREAMBLE("?q=%22ray%20charles%22") CATALOG_V2_ENTRIES_PREAMBLE("?q=%22ray%20charles%22")
@ -1227,7 +1228,7 @@ TEST_F(LibraryServerTest, suggestions_in_range)
{ {
int suggCount = 0; int suggCount = 0;
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
std::string url = "/suggest?content=zimfile&term=ray&start=" + std::to_string(i*5) + "&count=5"; std::string url = "/ROOT/suggest?content=zimfile&term=ray&start=" + std::to_string(i*5) + "&count=5";
const auto r = zfs1_->GET(url.c_str()); const auto r = zfs1_->GET(url.c_str());
std::string body = r->body; std::string body = r->body;
int currCount = std::count(body.begin(), body.end(), '{') - 1; int currCount = std::count(body.begin(), body.end(), '{') - 1;
@ -1239,13 +1240,13 @@ TEST_F(LibraryServerTest, suggestions_in_range)
// Attempt to get 10 suggestions in steps of 5 even though there are only 8 // Attempt to get 10 suggestions in steps of 5 even though there are only 8
{ {
std::string url = "/suggest?content=zimfile&term=song+for+you&start=0&count=5"; std::string url = "/ROOT/suggest?content=zimfile&term=song+for+you&start=0&count=5";
const auto r1 = zfs1_->GET(url.c_str()); const auto r1 = zfs1_->GET(url.c_str());
std::string body = r1->body; std::string body = r1->body;
int currCount = std::count(body.begin(), body.end(), '{') - 1; int currCount = std::count(body.begin(), body.end(), '{') - 1;
ASSERT_EQ(currCount, 5); ASSERT_EQ(currCount, 5);
url = "/suggest?content=zimfile&term=song+for+you&start=5&count=5"; url = "/ROOT/suggest?content=zimfile&term=song+for+you&start=5&count=5";
const auto r2 = zfs1_->GET(url.c_str()); const auto r2 = zfs1_->GET(url.c_str());
body = r2->body; body = r2->body;
currCount = std::count(body.begin(), body.end(), '{') - 1; currCount = std::count(body.begin(), body.end(), '{') - 1;
@ -1254,7 +1255,7 @@ TEST_F(LibraryServerTest, suggestions_in_range)
// Attempt to get 10 suggestions even though there is only 1 // Attempt to get 10 suggestions even though there is only 1
{ {
std::string url = "/suggest?content=zimfile&term=strong&start=0&count=5"; std::string url = "/ROOT/suggest?content=zimfile&term=strong&start=0&count=5";
const auto r = zfs1_->GET(url.c_str()); const auto r = zfs1_->GET(url.c_str());
std::string body = r->body; std::string body = r->body;
int currCount = std::count(body.begin(), body.end(), '{') - 1; int currCount = std::count(body.begin(), body.end(), '{') - 1;
@ -1263,7 +1264,7 @@ TEST_F(LibraryServerTest, suggestions_in_range)
// No Suggestion // No Suggestion
{ {
std::string url = "/suggest?content=zimfile&term=oops&start=0&count=5"; std::string url = "/ROOT/suggest?content=zimfile&term=oops&start=0&count=5";
const auto r = zfs1_->GET(url.c_str()); const auto r = zfs1_->GET(url.c_str());
std::string body = r->body; std::string body = r->body;
int currCount = std::count(body.begin(), body.end(), '{') - 1; int currCount = std::count(body.begin(), body.end(), '{') - 1;
@ -1272,7 +1273,7 @@ TEST_F(LibraryServerTest, suggestions_in_range)
// Out of bound value // Out of bound value
{ {
std::string url = "/suggest?content=zimfile&term=ray&start=-2&count=-1"; std::string url = "/ROOT/suggest?content=zimfile&term=ray&start=-2&count=-1";
const auto r = zfs1_->GET(url.c_str()); const auto r = zfs1_->GET(url.c_str());
std::string body = r->body; std::string body = r->body;
int currCount = std::count(body.begin(), body.end(), '{') - 1; int currCount = std::count(body.begin(), body.end(), '{') - 1;
@ -1282,20 +1283,20 @@ TEST_F(LibraryServerTest, suggestions_in_range)
TEST_F(LibraryServerTest, catalog_v2_individual_entry_access) TEST_F(LibraryServerTest, catalog_v2_individual_entry_access)
{ {
const auto r = zfs1_->GET("/catalog/v2/entry/raycharles"); const auto r = zfs1_->GET("/ROOT/catalog/v2/entry/raycharles");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
RAY_CHARLES_CATALOG_ENTRY RAY_CHARLES_CATALOG_ENTRY
); );
const auto r1 = zfs1_->GET("/catalog/v2/entry/non-existent-entry"); const auto r1 = zfs1_->GET("/ROOT/catalog/v2/entry/non-existent-entry");
EXPECT_EQ(r1->status, 404); EXPECT_EQ(r1->status, 404);
} }
TEST_F(LibraryServerTest, catalog_v2_partial_entries) TEST_F(LibraryServerTest, catalog_v2_partial_entries)
{ {
const auto r = zfs1_->GET("/catalog/v2/partial_entries"); const auto r = zfs1_->GET("/ROOT/catalog/v2/partial_entries");
EXPECT_EQ(r->status, 200); EXPECT_EQ(r->status, 200);
EXPECT_EQ(maskVariableOPDSFeedData(r->body), EXPECT_EQ(maskVariableOPDSFeedData(r->body),
CATALOG_V2_PARTIAL_ENTRIES_PREAMBLE("") CATALOG_V2_PARTIAL_ENTRIES_PREAMBLE("")
@ -1307,7 +1308,7 @@ TEST_F(LibraryServerTest, catalog_v2_partial_entries)
" <title>Charles, Ray</title>\n" " <title>Charles, Ray</title>\n"
" <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n" " <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n"
" <link rel=\"alternate\"\n" " <link rel=\"alternate\"\n"
" href=\"/catalog/v2/entry/charlesray\"\n" " href=\"/ROOT/catalog/v2/entry/charlesray\"\n"
" type=\"application/atom+xml;type=entry;profile=opds-catalog\"/>\n" " type=\"application/atom+xml;type=entry;profile=opds-catalog\"/>\n"
" </entry>\n" " </entry>\n"
" <entry>\n" " <entry>\n"
@ -1315,7 +1316,7 @@ TEST_F(LibraryServerTest, catalog_v2_partial_entries)
" <title>Ray Charles</title>\n" " <title>Ray Charles</title>\n"
" <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n" " <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n"
" <link rel=\"alternate\"\n" " <link rel=\"alternate\"\n"
" href=\"/catalog/v2/entry/raycharles\"\n" " href=\"/ROOT/catalog/v2/entry/raycharles\"\n"
" type=\"application/atom+xml;type=entry;profile=opds-catalog\"/>\n" " type=\"application/atom+xml;type=entry;profile=opds-catalog\"/>\n"
" </entry>\n" " </entry>\n"
" <entry>\n" " <entry>\n"
@ -1323,7 +1324,7 @@ TEST_F(LibraryServerTest, catalog_v2_partial_entries)
" <title>Ray (uncategorized) Charles</title>\n" " <title>Ray (uncategorized) Charles</title>\n"
" <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n" " <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n"
" <link rel=\"alternate\"\n" " <link rel=\"alternate\"\n"
" href=\"/catalog/v2/entry/raycharles_uncategorized\"\n" " href=\"/ROOT/catalog/v2/entry/raycharles_uncategorized\"\n"
" type=\"application/atom+xml;type=entry;profile=opds-catalog\"/>\n" " type=\"application/atom+xml;type=entry;profile=opds-catalog\"/>\n"
" </entry>\n" " </entry>\n"
"</feed>\n" "</feed>\n"