#include "test.h" #define TEST(name) TEST_CASE("cookie-jar: " name, "[cookie_jar]") auto now = panda::date::Date::now(); auto past = now - 3600; auto ancient = now - 3600 * 5; auto future = now + 3600; TEST("add cookie") { CookieJarSP jar(new CookieJar()); auto& dc = jar->domain_cookies; URISP origin(new URI("https://www.perl.org/my-path")); Response::Cookie coo("v"); coo.domain("crazypanda.ru"); coo.path("/p"); SECTION("no domain -> get it from origin") { coo.domain(""); coo.path("/p"); jar->add("k", coo, origin); CHECK(dc.size() == 1); CHECK(dc[".www.perl.org"][0].host_only() == true); } SECTION("session cookie is added") { jar->add("k", coo, origin); CHECK(dc.size() == 1); CHECK(dc.count(".crazypanda.ru") == 1); CHECK(dc[".crazypanda.ru"][0].name() == "k"); Response::Cookie &coo = dc[".crazypanda.ru"][0]; /* downcast */ CHECK(coo.to_string("k") == coo.to_string("k")); } SECTION("non-expired cookie is added") { coo.expires(future); jar->add("k", coo, origin, now); CHECK(dc.size() == 1); } SECTION("expired cookie isn't added") { coo.expires(past); jar->add("k", coo, origin, now); CHECK(dc.size() == 0); } SECTION("2 cookies on same domain with differen paths are added") { jar->add("k", coo, origin); coo.path("/p2"); jar->add("k", coo, origin); CHECK(dc.size() == 1); auto& cookies = dc[".crazypanda.ru"]; CHECK(cookies.size() == 2); } SECTION("2 cookies on different domains are addred") { jar->add("k", coo, origin); coo.domain("example.org"); jar->add("k", coo, origin); CHECK(dc.size() == 2); } SECTION("updated") { jar->add("k", coo, origin); coo.value("v2"); jar->add("k", coo, origin); auto& cookies = dc[".crazypanda.ru"]; CHECK(cookies.size() == 1); CHECK(cookies[0].value() == "v2"); } SECTION("remove cookie") { jar->add("k", coo, origin); coo.expires(past); jar->add("k", coo, origin, now); CHECK(dc.size() == 0); } SECTION("remove cookie") { coo.path(""); jar->add("k", coo, origin); jar->add("k", coo, origin, past); REQUIRE(dc.size() == 1); auto& cookies = dc[".crazypanda.ru"]; CHECK(cookies.at(0).path() == "/my-path"); } } TEST("remove cookies") { CookieJarSP jar(new CookieJar()); auto& dc = jar->domain_cookies; URISP origin(new URI("https://www.perl.org/my-path")); Response::Cookie coo("v"); coo.domain("crazypanda.ru"); coo.path("/p"); jar->add("c1", coo, origin); Response::Cookie coo2("v"); coo.domain("crazypanda.ru"); coo.path("/"); jar->add("c2", coo, origin); Response::Cookie coo3("v"); coo.domain("poker.crazypanda.ru"); coo.path("/perl"); jar->add("c3", coo, origin); Response::Cookie coo4("v"); coo.domain("poker.crazypanda.ru"); coo.path("/cpp"); jar->add("c4", coo, origin); REQUIRE(!dc.empty()); SECTION("by domain") { SECTION("all") { auto cookies = jar->remove("crazypanda.ru"); CHECK(dc.empty()); CHECK(cookies.size() == 4); } SECTION("patrial") { auto cookies = jar->remove("poker.crazypanda.ru"); CHECK(!dc.empty()); CHECK(cookies.size() == 2); CHECK(cookies[0].name() == "c3"); CHECK(cookies[1].name() == "c4"); } SECTION("none") { auto cookies = jar->remove("panda.ru"); CHECK(!dc.empty()); CHECK(cookies.size() == 0); } SECTION("strict") { auto cookies = jar->remove(".crazypanda.ru"); CHECK(!dc.empty()); CHECK(cookies.size() == 2); CHECK(cookies[0].name() == "c1"); CHECK(cookies[1].name() == "c2"); } } SECTION("by name") { auto cookies = jar->remove("", "c4"); CHECK(!dc.empty()); CHECK(cookies.size() == 1); CHECK(cookies[0].name() == "c4"); } SECTION("by path") { SECTION("prefix") { auto cookies = jar->remove("", "", "/p"); CHECK(!dc.empty()); CHECK(cookies.size() == 2); CHECK(cookies[0].name() == "c3"); CHECK(cookies[1].name() == "c1"); } SECTION("full path") { auto cookies = jar->remove("", "", "/perl"); CHECK(!dc.empty()); CHECK(cookies.size() == 1); CHECK(cookies[0].name() == "c3"); } } SECTION("all") { auto cookies = jar->remove(); CHECK(dc.empty()); CHECK(cookies.size() == 4); } SECTION("full match") { auto cookies = jar->remove(".poker.crazypanda.ru", "c3", "/perl"); CHECK(!dc.empty()); CHECK(cookies.size() == 1); CHECK(cookies[0].name() == "c3"); } } TEST("find/match cookie") { CookieJarSP jar(new CookieJar()); URISP origin(new URI("https://perl.perl.org/")); Response::Cookie coo1("v1"); coo1.domain("crazypanda.ru"); coo1.path("/p1"); coo1.expires(now); Response::Cookie coo2("v2"); coo2.domain("crazypanda.ru"); coo2.path("/p1/p2"); coo2.expires(now); Response::Cookie coo3("v3"); coo3.domain("perl.crazypanda.ru"); coo3.path("/pp3"); coo3.expires(past); coo3.secure(true); jar->add("k1", coo1, origin, ancient); jar->add("k2", coo2, origin, ancient); jar->add("k3", coo3, origin, ancient); SECTION("prepreq"){ auto& dc = jar->domain_cookies; auto cookies = &dc.at(".crazypanda.ru"); REQUIRE(cookies->size() == 2); REQUIRE(cookies->at(0).name() == "k1"); REQUIRE(cookies->at(1).name() == "k2"); cookies = &dc.at(".perl.crazypanda.ru"); REQUIRE(cookies->size() == 1); REQUIRE(cookies->at(0).name() == "k3"); } SECTION("find nothing (path mismatch)") { auto cookies = jar->find(URISP{new URI("http://crazypanda.ru/404")}, past); REQUIRE(cookies.size() == 0); } SECTION("find nothing (domain mismatch)") { auto cookies = jar->find(URISP{new URI("http://example.org/")}); REQUIRE(cookies.size() == 0); } SECTION("find most precise") { auto cookies = jar->find(URISP{new URI("http://crazypanda.ru/p1/p2")}, past); REQUIRE(cookies.size() == 1); auto& c = cookies[0]; CHECK(c.name() == "k2"); CHECK(c.value() == "v2"); } SECTION("2 of 3 match (by path)") { auto cookies = jar->find(URISP{new URI("http://crazypanda.ru/p1")}, past); REQUIRE(cookies.size() == 2); CHECK(cookies[0].name() == "k2"); CHECK(cookies[1].name() == "k1"); } SECTION("2 of 3 match (by domain)") { auto cookies = jar->find(URISP{new URI("https://crazypanda.ru/p")}, past); REQUIRE(cookies.size() == 2); CHECK(cookies[0].name() == "k2"); CHECK(cookies[1].name() == "k1"); } SECTION("3 of 3 match (by domain)") { auto cookies = jar->find(URISP{new URI("https://perl.crazypanda.ru/")}, past); REQUIRE(cookies.size() == 3); CHECK(cookies[0].name() == "k2"); CHECK(cookies[1].name() == "k3"); CHECK(cookies[2].name() == "k1"); } SECTION("2 of 3 match (by domain, and security)") { auto cookies = jar->find(URISP{new URI("http://perl.crazypanda.ru/")}, past); REQUIRE(cookies.size() == 2); CHECK(cookies[0].name() == "k2"); CHECK(cookies[1].name() == "k1"); } SECTION("3 of 3 match (by subdomain)") { auto cookies = jar->find(URISP{new URI("https://cpp.and.perl.crazypanda.ru/")}, past); REQUIRE(cookies.size() == 3); CHECK(cookies[0].name() == "k2"); CHECK(cookies[1].name() == "k3"); CHECK(cookies[2].name() == "k1"); } SECTION("3 of 3 match by subdomain, but mismatch my date") { auto cookies = jar->find(URISP{new URI("https://cpp.and.perl.crazypanda.ru/")}, future); REQUIRE(cookies.size() == 0); } SECTION("3 of 3 match by subdomain, 2 match my date") { auto cookies = jar->find(URISP{new URI("https://perl.crazypanda.ru/")}, now); REQUIRE(cookies.size() == 2); CHECK(cookies[0].name() == "k2"); CHECK(cookies[1].name() == "k1"); } SECTION("same-site policy") { CookieJarSP jar(new CookieJar()); REQUIRE(jar->domain_cookies.size() == 0); URISP origin(new URI("https://my.crazypanda.ru/")); Response::Cookie coo1("v1"); coo1.domain("crazypanda.ru"); coo1.path("/p1"); coo1.expires(future); Response::Cookie coo4("v4"); coo4.domain("crazypanda.ru"); coo4.path("/cpp"); coo4.expires(future); coo4.same_site(Response::Cookie::SameSite::Strict); Response::Cookie coo5("v5"); coo5.domain("crazypanda.ru"); coo5.path("/cpp"); coo5.expires(future); coo5.same_site(Response::Cookie::SameSite::Lax); jar->add("k1", coo1, origin, ancient); jar->add("k4", coo4, origin, ancient); jar->add("k5", coo5, origin, ancient); auto& dc = jar->domain_cookies; REQUIRE(dc.at(".crazypanda.ru").size() == 3); SECTION("request matches origin") { auto cookies = jar->find(origin, now); REQUIRE(cookies.size() == 3); } SECTION("different site") { auto cookies = jar->find(URISP{new URI("https://public.crazypanda.ru/")}, past); REQUIRE(cookies.size() == 1); CHECK(cookies[0].name() == "k1"); } SECTION("different site, lax context") { auto cookies = jar->find(URISP{new URI("https://public.crazypanda.ru/")}, past, true); REQUIRE(cookies.size() == 2); CHECK(cookies[0].name() == "k5"); CHECK(cookies[1].name() == "k1"); } SECTION("subdomain") { auto cookies = jar->find(URISP{new URI("https://static.my.crazypanda.ru/")}, past); REQUIRE(cookies.size() == 3); } } SECTION("session cookies") { CookieJarSP jar(new CookieJar()); Response::Cookie coo1("v1"); coo1.domain("crazypanda.ru"); coo1.path("/p1"); jar->add("k1", coo1, origin); auto cookies = jar->find(URISP{new URI("https://games.crazypanda.ru/")}); REQUIRE(cookies.size() == 1); } SECTION("host-only cookies (missing domain)") { CookieJarSP jar(new CookieJar()); auto origin = URISP{new URI("https://ya.ru/")}; Response::Cookie coo1("v1"); jar->add("k1", coo1, origin); auto cookies = jar->find(origin); REQUIRE(cookies.size() == 1); cookies = jar->find(URISP{new URI("https://www.ya.ru/")}); REQUIRE(cookies.size() == 0); } } TEST("cookies collection from the request") { CookieJarSP jar(new CookieJar()); URISP req_uri = new URI("http://games.crazypanda.ru/hello/world"); auto res = Response::Builder() .cookie("c1", Response::Cookie("v1")) .cookie("c2", Response::Cookie("v2").domain("crazypanda.ru").path("/hi")) .cookie("c3", Response::Cookie("v3").domain("google.com")) .build(); SECTION("same origin -> 2 cookies") { jar->collect(*res, req_uri); auto cookies = jar->find(URISP{new URI("http://games.crazypanda.ru")}); REQUIRE(cookies.size() == 2); CHECK(cookies[0].name() == "c1"); CHECK(cookies[1].name() == "c2"); } SECTION("differnt subdomain -> 1 cookie") { jar->collect(*res, req_uri); auto cookies = jar->find(URISP{new URI("http://ww.games.crazypanda.ru")}); REQUIRE(cookies.size() == 1); CHECK(cookies[0].name() == "c2"); } SECTION("ignore predicate") { CookieJar::ignore_fn fn([](auto&, auto&){ return true; }); jar->set_ignore(fn); jar->collect(*res, req_uri); auto cookies = jar->find(URISP{new URI("http://games.crazypanda.ru")}); REQUIRE(cookies.size() == 0); } } TEST("cookies population to thr response") { CookieJarSP jar(new CookieJar()); URISP uri(new URI("https://crazypanda.ru/")); Response::Cookie coo1("v1"); jar->add("k1", coo1, uri); auto req = Request::Builder().uri(uri).build(); REQUIRE(req->cookies.size() == 0); jar->populate(*req); REQUIRE(req->cookies.size() == 1); CHECK(req->cookies.get("k1") == "v1"); } TEST("(de)serialization") { URISP origin(new URI("https://www.tut.by")); CookieJarSP jar(new CookieJar()); SECTION("single cookie serialization") { auto& dc = jar->domain_cookies; Response::Cookie coo("v"); coo.domain("tut.by"); coo.path("/news"); jar->add("k", coo, origin); REQUIRE(dc.size() == 1); REQUIRE(dc.count(".tut.by") == 1); CHECK(dc[".tut.by"][0].name() == "k"); auto &jcoo = dc[".tut.by"][0]; REQUIRE(jcoo.to_string() == "{\"key\":\"k\", \"value\":\"v\", \"domain\":\"tut.by\", \"path\":\"/news\"}"); CHECK(jar->to_string(true) == "[\n{\"key\":\"k\", \"value\":\"v\", \"domain\":\"tut.by\", \"path\":\"/news\"}]"); CHECK(jar->to_string(false) == "[\n]"); CookieJar::DomainCookies dc2; REQUIRE(CookieJar::parse_cookies(jar->to_string(true), dc2) == std::error_code()); REQUIRE(dc2[".tut.by"].size() == 1); CHECK(dc2[".tut.by"][0].to_string() == jcoo.to_string()); } SECTION("by date filtration") { panda::time::tzset("Europe/Moscow"); panda::date::Date expires(2020, 05, 18, 5); auto past = expires - 3600; auto future = expires + 3600; Response::Cookie coo("v"); coo.domain("tut.by"); coo.path("/news"); coo.expires(expires); jar->add("k", coo, origin, past); CHECK(jar->to_string(false, past) == "[\n{\"key\":\"k\", \"value\":\"v\", \"domain\":\"tut.by\", \"path\":\"/news\", \"expires\":\"1589767200\"}]"); CHECK(jar->to_string(false, future) == "[\n]"); } SECTION("samesite & origin") { Response::Cookie coo("v"); coo.domain("tut.by"); coo.same_site(Response::Cookie::SameSite::Strict); jar->add("k", coo, origin); CHECK(jar->to_string(true) == "[\n{\"key\":\"k\", \"value\":\"v\", \"domain\":\"tut.by\", \"path\":\"/\", \"same_site\":\"S\", \"origin\":\"https://www.tut.by\"}]"); } SECTION("samesite & origin") { Response::Cookie coo("v"); jar->add("k", coo, origin); CHECK(jar->to_string(true) == "[\n{\"key\":\"k\", \"value\":\"v\", \"domain\":\"www.tut.by\", \"path\":\"/\", \"host_only\":\"1\"}]"); } SECTION("parsing") { string data = R"DATA([ {"key":"k1", "value":"v1", "domain":"tut.by", "path":"/", "same_site":"S", "origin":"https://www.tut.by", "same_site":"S"}, {"key":"k2", "value":"v2", "domain":"ya.ru", "path":"/", "host_only":"1"}])DATA"; CookieJarSP jar(new CookieJar(data)); auto& dc = jar->domain_cookies; REQUIRE(dc[".tut.by"].size() == 1); auto& c1 = dc[".tut.by"][0]; CHECK(c1.name() == "k1"); CHECK(c1.value() == "v1"); CHECK(c1.domain() == "tut.by"); CHECK(c1.same_site() == Response::Cookie::SameSite::Strict); CHECK(c1.origin()->to_string() == "https://www.tut.by"); REQUIRE(dc[".ya.ru"].size() == 1); auto& c2 = dc[".ya.ru"][0]; CHECK(c2.name() == "k2"); CHECK(c2.value() == "v2"); CHECK(c2.domain() == "ya.ru"); CHECK(c2.host_only()); } }