diff --git a/test/server_search.cpp b/test/server_search.cpp
index 6749ad323..16e4d7fd8 100644
--- a/test/server_search.cpp
+++ b/test/server_search.cpp
@@ -137,6 +137,25 @@ std::string makeSearchResultsHtml(const std::string& pattern,
return html;
}
+std::string makeSearchResultsXml(const std::string& header,
+ const std::string& results)
+{
+ const char SEARCHRESULTS_XML_TEMPLATE[] = R"XML(
+
+
+ %HEADER%%RESULTS%
+
+
+)XML";
+
+ std::string html = removeEOLWhitespaceMarkers(SEARCHRESULTS_XML_TEMPLATE);
+ html = replace(html, "%HEADER%", header);
+ html = replace(html, "%RESULTS%", results);
+ return html;
+}
+
struct SearchResult
{
std::string link;
@@ -155,6 +174,18 @@ struct SearchResult
+ "
from " + bookTitle + "
\n"
+ " " + wordCount + " words
\n";
}
+
+ std::string getXml() const
+ {
+ return std::string()
+ + " " + title + "\n"
+ + " " + replace(link, "'", "'") + "\n"
+ + " " + snippet + "\n"
+ + " \n"
+ + " " + bookTitle + "\n"
+ + " \n"
+ + " " + wordCount + "";
+ }
};
#define SEARCH_RESULT(LINK, TITLE, SNIPPET, BOOK_TITLE, WORDCOUNT) \
@@ -569,6 +600,27 @@ Snippets extractSearchResultSnippetsFromHtml(const std::string& html)
return snippets;
}
+const char SNIPPET_REGEX_FOR_XML[] = "(?!Search result for)(.+)";
+
+std::string maskSnippetsInXmlSearchResults(std::string s)
+{
+ return replace(s, SNIPPET_REGEX_FOR_XML, "SNIPPET TEXT WAS MASKED");
+}
+
+Snippets extractSearchResultSnippetsFromXml(const std::string& xml)
+{
+ Snippets snippets;
+ const std::regex snippetRegex(SNIPPET_REGEX_FOR_XML);
+ std::sregex_iterator snippetIt(xml.begin(), xml.end(), snippetRegex);
+ const std::sregex_iterator end;
+ for ( ; snippetIt != end; ++snippetIt)
+ {
+ const std::smatch snippetMatch = *snippetIt;
+ snippets.push_back(snippetMatch[1].str());
+ }
+ return snippets;
+}
+
bool isValidSnippet(const std::string& s)
{
return s.size() >= 250
@@ -650,19 +702,37 @@ struct TestData
return url;
}
- std::string getPattern() const
+ std::string extractQueryValue(const std::string& key) const
{
- const std::string p = "pattern=";
+ const std::string p = key + "=";
const size_t i = query.find(p);
+ if (i == std::string::npos) {
+ return "";
+ }
std::string r = query.substr(i + p.size());
return r.substr(0, r.find("&"));
}
+ std::string getPattern() const
+ {
+ return extractQueryValue("pattern");
+ }
+
+ std::string getLang() const
+ {
+ return extractQueryValue("books.filter.lang");
+ }
+
std::string url() const
{
return makeUrl(query, start, resultsPerPage);
}
+ std::string xmlSearchUrl() const
+ {
+ return url() + "&format=xml";
+ }
+
std::string expectedHtmlHeader() const
{
if ( totalResultCount == 0 ) {
@@ -736,11 +806,70 @@ struct TestData
);
}
+ std::string expectedXmlHeader() const
+ {
+ std::string header = R"(Search: PATTERN
+ URL
+ Search result for PATTERN
+ RESULTCOUNT
+ FIRSTRESULT
+ ITEMCOUNT
+
+ )";
+
+ const auto realResultsPerPage = resultsPerPage?resultsPerPage:25;
+ const auto url = makeUrl(query + "&format=xml", firstResultIndex, realResultsPerPage);
+ header = replace(header, "URL", replace(url, "&", "&"));
+ header = replace(header, "FIRSTRESULT", to_string(firstResultIndex));
+ header = replace(header, "ITEMCOUNT", to_string(realResultsPerPage));
+ header = replace(header, "RESULTCOUNT", to_string(totalResultCount));
+ header = replace(header, "PATTERN", getPattern());
+ auto queryLang = getLang();
+ if (queryLang.empty()) {
+ header = replace(header, "LANGQUERY", "");
+ } else {
+ header = replace(header, "LANGQUERY", "\n language=\""+queryLang+"\"");
+ }
+ return header;
+ }
+
+ std::string expectedXmlResultsString() const
+ {
+ if ( results.empty() ) {
+ return "\n ";
+ }
+
+ std::string s;
+ for ( const auto& r : results ) {
+ s += "\n - \n";
+ s += maskSnippetsInXmlSearchResults(r.getXml());
+ s += "\n
";
+ }
+ return s;
+ }
+
+ std::string expectedXml() const
+ {
+ return makeSearchResultsXml(
+ expectedXmlHeader(),
+ expectedXmlResultsString()
+ );
+ }
+
TestContext testContext() const
{
return TestContext{ { "url", url() } };
}
+ TestContext xmlTestContext() const
+ {
+ return TestContext{ { "url", xmlSearchUrl() } };
+ }
+
void checkHtml(const std::string& html) const
{
EXPECT_EQ(maskSnippetsInHtmlSearchResults(html), expectedHtml())
@@ -749,6 +878,14 @@ struct TestData
checkSnippets(extractSearchResultSnippetsFromHtml(html));
}
+ void checkXml(const std::string& xml) const
+ {
+ EXPECT_EQ(maskSnippetsInXmlSearchResults(xml), expectedXml())
+ << xmlTestContext();
+
+ checkSnippets(extractSearchResultSnippetsFromXml(xml));
+ }
+
void checkSnippets(const Snippets& snippets) const
{
ASSERT_EQ(snippets.size(), results.size());
@@ -1319,5 +1456,13 @@ TEST_F(ServerTest, searchResults)
const auto htmlRes = taskbarlessZimFileServer().GET(htmlSearchUrl.c_str());
EXPECT_EQ(htmlRes->status, 200);
t.checkHtml(htmlRes->body);
+
+ const std::string xmlSearchUrl = t.xmlSearchUrl();
+ const auto xmlRes1 = zfs1_->GET(xmlSearchUrl.c_str());
+ const auto xmlRes2 = taskbarlessZimFileServer().GET(xmlSearchUrl.c_str());
+ EXPECT_EQ(xmlRes1->status, 200);
+ EXPECT_EQ(xmlRes2->status, 200);
+ EXPECT_EQ(xmlRes1->body, xmlRes2->body);
+ t.checkXml(xmlRes1->body);
}
}