1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
#include "feedretriever.h"
#include "3rd-party/catch.hpp"
#include "cache.h"
#include "configcontainer.h"
#include "curlhandle.h"
#include "rss/exception.h"
#include "rssfeed.h"
#include "strprintf.h"
#include "test_helpers/httptestserver.h"
#include "test_helpers/misc.h"
#include "test_helpers/tempfile.h"
#include <cstdint>
#include <vector>
using namespace newsboat;
TEST_CASE("Feed retriever retrieves feed successfully", "[FeedRetriever]")
{
auto feed_xml = test_helpers::read_binary_file("data/atom10_1.xml");
ConfigContainer cfg;
Cache rsscache(":memory:", &cfg);
CurlHandle easyHandle;
FeedRetriever feedRetriever(cfg, rsscache, easyHandle);
auto& testServer = test_helpers::HttpTestServer::get_instance();
auto mockRegistration = testServer.add_endpoint("/feed", {}, 200, {
{"content-type", "text/xml"},
}, feed_xml);
const auto address = testServer.get_address();
const auto url = strprintf::fmt("http://%s/feed", address);
const auto feed = feedRetriever.retrieve(url);
REQUIRE(testServer.num_hits(mockRegistration) == 1);
REQUIRE(feed.items.size() == 3);
}
TEST_CASE("Feed retriever adds header with etag info if available", "[FeedRetriever]")
{
ConfigContainer cfg;
Cache rsscache(":memory:", &cfg);
CurlHandle easyHandle;
FeedRetriever feedRetriever(cfg, rsscache, easyHandle);
auto& testServer = test_helpers::HttpTestServer::get_instance();
const auto address = testServer.get_address();
const auto url = strprintf::fmt("http://%s/feed", address);
RssFeed feed_with_etag(&rsscache, url);
rsscache.externalize_rssfeed(feed_with_etag, false);
rsscache.update_lastmodified(url, {}, "some-random-etag");
auto mockRegistration = testServer.add_endpoint("/feed", {
{"If-None-Match", "some-random-etag"},
}, 304, {
{"content-type", "text/xml"},
}, {});
REQUIRE_THROWS_AS(feedRetriever.retrieve(url), rsspp::NotModifiedException);
REQUIRE(testServer.num_hits(mockRegistration) == 1);
}
TEST_CASE("Feed retriever retries download if no data is received",
"[FeedRetriever]")
{
ConfigContainer cfg;
Cache rsscache(":memory:", &cfg);
CurlHandle easyHandle;
FeedRetriever feedRetriever(cfg, rsscache, easyHandle);
auto& testServer = test_helpers::HttpTestServer::get_instance();
const auto address = testServer.get_address();
const auto url = strprintf::fmt("http://%s/feed", address);
auto mockRegistration = testServer.add_endpoint("/feed", {}, 200, {
{"content-type", "text/xml"},
}, {});
cfg.set_configvalue("download-retries", "3");
feedRetriever.retrieve(url);
REQUIRE(testServer.num_hits(mockRegistration) == 3);
}
TEST_CASE("Feed retriever does not retry download on HTTP 304 (Not Modified), 429 (Too Many Requests)",
"[FeedRetriever]")
{
auto http_status = GENERATE(as<std::uint16_t> {}, 304, 429);
ConfigContainer cfg;
Cache rsscache(":memory:", &cfg);
CurlHandle easyHandle;
FeedRetriever feedRetriever(cfg, rsscache, easyHandle);
auto& testServer = test_helpers::HttpTestServer::get_instance();
const auto address = testServer.get_address();
const auto url = strprintf::fmt("http://%s/feed", address);
RssFeed feed_with_etag(&rsscache, url);
rsscache.externalize_rssfeed(feed_with_etag, false);
rsscache.update_lastmodified(url, {}, "some-random-etag");
auto mockRegistration = testServer.add_endpoint("/feed", {
{"If-None-Match", "some-random-etag"},
}, http_status, {
{"content-type", "text/xml"},
}, {});
cfg.set_configvalue("download-retries", "5");
if (http_status == 304) {
REQUIRE_THROWS_AS(feedRetriever.retrieve(url), rsspp::NotModifiedException);
} else if (http_status == 429) {
REQUIRE_THROWS_AS(feedRetriever.retrieve(url), rsspp::Exception);
}
REQUIRE(testServer.num_hits(mockRegistration) == 1);
}
TEST_CASE("Feed retriever throws on HTTP error status codes", "[FeedRetriever]")
{
ConfigContainer cfg;
Cache rsscache(":memory:", &cfg);
CurlHandle easyHandle;
FeedRetriever feedRetriever(cfg, rsscache, easyHandle);
auto& testServer = test_helpers::HttpTestServer::get_instance();
const auto address = testServer.get_address();
const auto url = strprintf::fmt("http://%s/feed", address);
SECTION("Client errors: HTTP status code 4xx") {
auto mockRegistration = testServer.add_endpoint("/feed", {}, 404, {}, {});
REQUIRE_THROWS_AS(feedRetriever.retrieve(url), rsspp::Exception);
REQUIRE(testServer.num_hits(mockRegistration) == 1);
}
SECTION("Server errors: HTTP status code 5xx") {
auto mockRegistration = testServer.add_endpoint("/feed", {}, 500, {}, {});
REQUIRE_THROWS_AS(feedRetriever.retrieve(url), rsspp::Exception);
REQUIRE(testServer.num_hits(mockRegistration) == 1);
}
}
TEST_CASE("Feed retriever remembers cookie between requests if cookie-cache is set",
"[FeedRetriever]")
{
auto feed_xml = test_helpers::read_binary_file("data/atom10_1.xml");
ConfigContainer cfg;
GIVEN("a configured cookie-cache and a test HTTP server") {
test_helpers::TempFile cookie_cache_file;
cfg.set_configvalue("cookie-cache", cookie_cache_file.get_path());
Cache rsscache(":memory:", &cfg);
auto& testServer = test_helpers::HttpTestServer::get_instance();
const auto address = testServer.get_address();
const auto url = strprintf::fmt("http://%s/feed", address);
WHEN("a HTTP request comes back with a Set-Cookie header") {
// Make sure mockRegistration is removed before registering a potentially conflicting next mock endpoint
{
auto mockRegistration = testServer.add_endpoint("/feed", {}, 200, {
{"content-type", "text/xml"},
{"Set-Cookie", "abc=def"},
}, feed_xml);
CurlHandle easyHandle;
FeedRetriever feedRetriever(cfg, rsscache, easyHandle);
const auto feed = feedRetriever.retrieve(url);
REQUIRE(testServer.num_hits(mockRegistration) == 1);
}
THEN("the next HTTP request includes the previously received cookie") {
auto mockRegistration = testServer.add_endpoint("/feed", {
{"Cookie", "abc=def"},
}, 200, {
{"content-type", "text/xml"},
}, feed_xml);
// Make sure to use a different handle because otherwise Curl keeps cookies alive in memory
CurlHandle easyHandle;
FeedRetriever feedRetriever(cfg, rsscache, easyHandle);
const auto feed = feedRetriever.retrieve(url);
REQUIRE(testServer.num_hits(mockRegistration) == 1);
}
}
}
}
|