diff --git a/test/library_server.cpp b/test/library_server.cpp
index aa5400c3..e5cc2bd2 100644
--- a/test/library_server.cpp
+++ b/test/library_server.cpp
@@ -778,6 +778,130 @@ TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_search_terms)
);
}
+TEST_F(LibraryServerTest, catalog_v2_entries_filtering_special_queries)
+{
+ {
+ // 'or' acts as a Xapian boolean operator, resulting in malformed query
+ const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=Or");
+ EXPECT_EQ(r->status, 500);
+ }
+
+ {
+ // 'and' acts as a Xapian boolean operator, resulting in malformed query
+ const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=and");
+ EXPECT_EQ(r->status, 500);
+ }
+
+ {
+ // 'not' acts as a Xapian boolean operator, resulting in malformed query
+ const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=not");
+ EXPECT_EQ(r->status, 500);
+ }
+
+ {
+ // 'xor' acts as a Xapian boolean operator, resulting in malformed query
+ const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=xor");
+ EXPECT_EQ(r->status, 500);
+ }
+
+ {
+ // 'or' acts as a Xapian boolean operator
+ const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=wikipedia%20or%20library");
+ EXPECT_EQ(r->status, 200);
+ EXPECT_EQ(maskVariableOPDSFeedData(r->body),
+ CATALOG_V2_ENTRIES_PREAMBLE("?q=wikipedia%20or%20library")
+ "
Filtered Entries (q=wikipedia%20or%20library)\n"
+ " YYYY-MM-DDThh:mm:ssZ\n"
+ " 3\n"
+ " 0\n"
+ " 3\n"
+ UNCATEGORIZED_RAY_CHARLES_CATALOG_ENTRY
+ CHARLES_RAY_CATALOG_ENTRY
+ RAY_CHARLES_CATALOG_ENTRY
+ "\n"
+ );
+ }
+
+ {
+ // 'and' acts as a Xapian boolean operator
+ const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=wikipedia%20and%20articles");
+ EXPECT_EQ(r->status, 200);
+ EXPECT_EQ(maskVariableOPDSFeedData(r->body),
+ CATALOG_V2_ENTRIES_PREAMBLE("?q=wikipedia%20and%20articles")
+ " Filtered Entries (q=wikipedia%20and%20articles)\n"
+ " YYYY-MM-DDThh:mm:ssZ\n"
+ " 2\n"
+ " 0\n"
+ " 2\n"
+ CHARLES_RAY_CATALOG_ENTRY
+ RAY_CHARLES_CATALOG_ENTRY
+ "\n"
+ );
+ }
+
+ {
+ // 'near' doesn't act as a Xapian query operator
+ const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=near");
+ EXPECT_EQ(r->status, 200);
+ EXPECT_EQ(maskVariableOPDSFeedData(r->body),
+ CATALOG_V2_ENTRIES_PREAMBLE("?q=near")
+ " Filtered Entries (q=near)\n"
+ " YYYY-MM-DDThh:mm:ssZ\n"
+ " 1\n"
+ " 0\n"
+ " 1\n"
+ RAY_CHARLES_CATALOG_ENTRY
+ "\n"
+ );
+ }
+
+ {
+ // 'adj' doesn't act as a Xapian query operator
+ const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=adj");
+ EXPECT_EQ(r->status, 200);
+ EXPECT_EQ(maskVariableOPDSFeedData(r->body),
+ CATALOG_V2_ENTRIES_PREAMBLE("?q=adj")
+ " Filtered Entries (q=adj)\n"
+ " YYYY-MM-DDThh:mm:ssZ\n"
+ " 1\n"
+ " 0\n"
+ " 1\n"
+ UNCATEGORIZED_RAY_CHARLES_CATALOG_ENTRY
+ "\n"
+ );
+ }
+
+ {
+ // 'near' doesn't act as a Xapian query operator
+ const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=charles%20near%20why");
+ EXPECT_EQ(r->status, 200);
+ EXPECT_EQ(maskVariableOPDSFeedData(r->body),
+ CATALOG_V2_ENTRIES_PREAMBLE("?q=charles%20near%20why")
+ " Filtered Entries (q=charles%20near%20why)\n"
+ " YYYY-MM-DDThh:mm:ssZ\n"
+ " 0\n"
+ " 0\n"
+ " 0\n"
+ "\n"
+ );
+ }
+
+ {
+ // 'adj' doesn't act as a Xapian query operator
+ const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=charles%20adj%20why");
+ EXPECT_EQ(r->status, 200);
+ EXPECT_EQ(maskVariableOPDSFeedData(r->body),
+ CATALOG_V2_ENTRIES_PREAMBLE("?q=charles%20adj%20why")
+ " Filtered Entries (q=charles%20adj%20why)\n"
+ " YYYY-MM-DDThh:mm:ssZ\n"
+ " 0\n"
+ " 0\n"
+ " 0\n"
+ "\n"
+ );
+ }
+}
+
TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_language)
{
{