#include "ut_common.h"

#include <library/cpp/testing/unittest/registar.h>

using namespace NSrvKernel;
using namespace NSrvKernel::NCookie;

// A selection (while avoiding test duplication) from the web-platform-tests web standards test suite.
// https://github.com/web-platform-tests/wpt/tree/master/cookies/http-state/
// https://wpt.fyi/results/cookies/http-state?label=master&label=stable&aligned&q=cookies

Y_UNIT_TEST_SUITE(TWPTTest) {
    Y_UNIT_TEST(TestGeneral) {
        // 0002
        TestParse(
            "foo=bar; Expires=Fri, 01 Jan 2038 00:00:00 GMT",
            TSetCookie{
                .Name="foo",
                .Value=FromStringBuf("bar"),
                .Expires=TExpires{.Year=2038, .Month=1, .MDay=1}
            }
        );
        // 0003
        TestParse(
            "foo=bar; Expires=Fri, 07 Aug 2007 08:04:19 GMT",
            TSetCookie{
                .Name="foo",
                .Value=FromStringBuf("bar"),
                .Expires=TExpires{.Year=2007, .Month=8, .MDay=7, .Hour=8, .Minute=4, .Second=19}
            }
        );
        // 0005
        TestParse(
            "foo=bar; max-age=10000;",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .MaxAge=10000},
            "foo=bar; Max-Age=10000"
        );
        // 0006
        TestParse(
            "foo=bar; max-age=0;",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .MaxAge=0},
            "foo=bar; Max-Age=0"
        );
        // 0007
        // We forbid unknown attributes in balancer.
        TestParse(
            "foo=bar; version=1;",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::AttrUnknown}
        );
        // 0010
        TestParse(
            "foo=bar; secure",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Secure=true},
            "foo=bar; Secure"
        );
        // 0017
        TestParse(
            "z=y, a=b",
            TSetCookie{.Name="z", .Value=FromStringBuf("y, a=b")}
        );
        // 0019
        // We forbid malformed max-age in balancer.
        TestParse(
            "foo=b;max-age=3600, c=d;path=/",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::MaxAgeValueInvalid}
        );
    }

    Y_UNIT_TEST(TestAttribute) {
        // attribute0002
        TestParse(
            "foo=bar; seCURe",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Secure=true},
            "foo=bar; Secure"
        );
        // attribute0003
        // We forbid unknown attributes in balancer.
        TestParse(
            "foo=bar; \"Secure\"",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::AttrUnknown}
        );
        // attribute0004
        TestParse(
            "foo=bar; Secure=",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Secure=true},
            "foo=bar; Secure"
        );
        // attribute0005
        TestParse(
            "foo=bar; Secure=aaaa",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Secure=true},
            "foo=bar; Secure"
        );
        // attribute0012
        TestParse(
            "foo=bar;                Secure",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Secure=true},
            "foo=bar; Secure"
        );
        // attribute0013
        TestParse(
            "foo=bar;       Secure     ;",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Secure=true},
            "foo=bar; Secure"
        );
        // attribute0014
        // We forbid invalid Path attrs in balancer.
        TestParse(
            "foo=bar; Path",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::PathValueInvalid}
        );
        // attribute0015
        // We forbid invalid Path attrs in balancer.
        TestParse(
            "foo=bar; Path=",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::PathValueInvalid}
        );
        // attribute0016
        TestParse(
            "foo=bar; Path=/",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Path="/"}
        );
        // attribute0018
        TestParse(
            "foo=bar; Path    =/qux",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Path="/qux"},
            "foo=bar; Path=/qux"
        );
        // attribute0019
        TestParse(
            "foo=bar; Path=    /qux",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Path="/qux"},
            "foo=bar; Path=/qux"
        );
        // attribute0021
        TestParse(
            "foo=bar; Path=/qux; Path=/",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::PathValueConflict}
        );
    }

    Y_UNIT_TEST(TestCharset) {
        // charset0001
        // We forbid nonascii octets in balancer.
        TestParse(
            "foo=春节回家路·春运完全手册",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::InvalidOctet}
        );
    }

    Y_UNIT_TEST(TestChromium) {
        // These are just some tests called chromium in the original test suite for some reason.
        // chromium0014
        // We forbid empty and unknown attrs in balancer.
        TestParse(
            "A  = BC  ;foo;;;   bar",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::AttrEmpty, ESetCookieSyntaxError::AttrUnknown}
        );
        // chromium0016
        TestParse(
            "foo=\"zohNumRKgI0oxyhSsV3Z7D\"  ; expires=Sun, 18-Apr-2027 21:06:29 GMT ; path=/  ;",
            TSetCookie{
                .Name="foo",
                .Value=FromStringBuf("\"zohNumRKgI0oxyhSsV3Z7D\""),
                .Path="/",
                .Expires=TExpires{.Year=2027, .Month=4, .MDay=18, .Hour=21, .Minute=6, .Second=29}
            },
            "foo=\"zohNumRKgI0oxyhSsV3Z7D\"; Path=/; Expires=Sun, 18-Apr-2027 21:06:29 GMT"
        );
        // chromium0017
        TestParse(
            "foo=zohNumRKgI0oxyhSsV3Z7D  ; expires=Sun, 18-Apr-2027 21:06:29 GMT ; path=/  ;",
            TSetCookie{
                .Name="foo",
                .Value=FromStringBuf("zohNumRKgI0oxyhSsV3Z7D"),
                .Path="/",
                .Expires=TExpires{.Year=2027, .Month=4, .MDay=18, .Hour=21, .Minute=6, .Second=29}
            },
            "foo=zohNumRKgI0oxyhSsV3Z7D; Path=/; Expires=Sun, 18-Apr-2027 21:06:29 GMT"
        );
        // chromium0019
        TestParse(
            "a=" + TString(4094, 'a'),
            TSetCookie{.Name="a", .Value=TBlob::FromString(TString(4094, 'a'))}
        );
        // chromium0021
        TestParse(
            "",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::CookieEmpty}
        );
    }

    Y_UNIT_TEST(TestComma) {
        // comma0001
        TestParse(
            "foo=bar, baz=qux",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar, baz=qux")}
        );
        // comma0005
        // We forbid malformed max-age in balancer.
        TestParse(
            "foo=bar; Max-Age=50,399",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::MaxAgeValueInvalid}
        );
        // comma0007
        // We forbid malformed expires in balancer.
        TestParse(
            "foo=bar; Expires=Fri 01 Jan 2038 00:00:00 GMT, baz=qux",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::ExpiresValueInvalid}
        );
    }

    Y_UNIT_TEST(TestDomain) {
        // domain0001
        TestParse(
            "foo=bar; domain=home.example.org",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Domain="home.example.org"},
            "foo=bar; Domain=home.example.org"
        );
        // domain0003
        TestParse(
            "foo=bar; domain=.home.example.org",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Domain=".home.example.org"},
            "foo=bar; Domain=.home.example.org"
        );
        // domain0010
        // The domain is clearly invalid but we will ignore such bugs at the balancer for simplicity
        // as not causing possible client-side state corruption.
        TestParse(
            "foo=bar; domain=..home.example.org",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Domain="..home.example.org"},
            "foo=bar; Domain=..home.example.org"
        );
        // domain0012
        TestParse(
            "foo=bar; domain=  .home.example.org",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Domain=".home.example.org"},
            "foo=bar; Domain=.home.example.org"
        );
        // domain0013
        // The domain is clearly invalid but we will ignore such bugs at the balancer for simplicity
        // as not causing possible client-side state corruption.
        TestParse(
            "foo=bar; domain=  .  home.example.org",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Domain=".  home.example.org"},
            "foo=bar; Domain=.  home.example.org"
        );
        // domain0021
        // The domain is clearly invalid but we will ignore such bugs at the balancer for simplicity
        // as not causing possible client-side state corruption.
        TestParse(
            "foo=bar; domain=\"home.example.org\"",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Domain="\"home.example.org\""},
            "foo=bar; Domain=\"home.example.org\""
        );
        // domain0024
        // We forbid conflicting attrs in balancer.
        TestParse(
            "foo=bar; domain=.example.org; domain=home.example.org",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::DomainValueConflict}
        );
        // domain0026
        // Normalizing the domain case.
        TestParse(
            "foo=bar; domain=home.eXaMpLe.org",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Domain="home.example.org"},
            "foo=bar; Domain=home.example.org"
        );
        // domain0038
        // Nonconflicting duplicate domains are ok.
        TestParse(
            "foo=bar; domain=home.example.org; domain=home.example.org",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Domain="home.example.org"},
            "foo=bar; Domain=home.example.org"
        );
    }

    Y_UNIT_TEST(TestMozilla) {
        // These are just some tests called mozilla in the original test suite for some reason.
        // mozilla0002
        TestParse(
            "foo=bar; max-age=0",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .MaxAge=0},
            "foo=bar; Max-Age=0"
        );
        // mozilla0005
        // Although negative max-age are supported by the browsers, their treatment is ambiguous (deletion or ignoring).
        // We forbid negative values.
        TestParse(
            "foo=bar; max-age=-20",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::MaxAgeValueInvalid}
        );
    }

    Y_UNIT_TEST(TestName) {
        // name0003
        TestParse(
            "$=bar",
            TSetCookie{.Name="$", .Value=FromStringBuf("bar")}
        );
        // name0016
        TestParse(
            "+=bar",
            TSetCookie{.Name="+", .Value=FromStringBuf("bar")}
        );
        // name0018
        TestParse(
            "a =bar",
            TSetCookie{.Name="a", .Value=FromStringBuf("bar")},
            "a=bar"
        );
        // name0020
        TestParse(
            "\"a=b\"=bar",
            TSetCookie{.Name="\"a", .Value=FromStringBuf("b\"=bar")}
        );
        // name0022
        TestParse(
            "   foo=bar",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar")},
            "foo=bar"
        );
        // name0024
        // We forbid unknown attrs in balancer.
        TestParse(
            "$Version=1; foo=bar",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::AttrUnknown}
        );
        TestParse(
            "$Version=1",
            TSetCookie{.Name="$Version", .Value=FromStringBuf("1")}
        );
        // name0030
        TestParse(
            "foo bar=baz",
            TSetCookie{.Name="foo bar", .Value=FromStringBuf("baz")}
        );
    }

    Y_UNIT_TEST(TestPath) {
        // path0019
        // We forbid invalid Path attrs in balancer.
        TestParse(
            "foo=bar; path",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::PathValueInvalid}
        );
        // path0020
        // We forbid invalid Path attrs in balancer.
        TestParse(
            "foo=bar; path=",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::PathValueInvalid}
        );
        // path0022
        TestParse(
            "foo=bar; path= /",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar"), .Path="/"},
            "foo=bar; Path=/"
        );
        // path0027
        // We forbid invalid Path attrs in balancer.
        TestParse(
            "foo=bar; path=\"/cookie-parser-result/foo/qux;\"",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::PathValueInvalid, ESetCookieSyntaxError::AttrUnknown}
        );
        // path0029
        // We forbid ascii controls in set-cookie in balancer.
        TestParse(
            "a=b; \tpath\t=\t/cookie-parser-result",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::InvalidOctet}
        );
        // path0030
        // We do not allow invalid and conflicting Path attrs in balancer.
        TestParse(
            "foo=bar; path=/dog; path=",
            TSetCookieSyntaxErrors{
                ESetCookieSyntaxError::PathValueInvalid, ESetCookieSyntaxError::PathValueConflict
            }
        );
    }

    Y_UNIT_TEST(TestValue) {
        // value0001
        TestParse(
            "foo=  bar",
            TSetCookie{.Name="foo", .Value=FromStringBuf("bar")},
            "foo=bar"
        );
        // value0002
        TestParse(
            "foo=\"bar\"",
            TSetCookie{.Name="foo", .Value=FromStringBuf("\"bar\"")}
        );
        // value0003
        TestParse(
            "foo=\"  bar \"",
            TSetCookie{.Name="foo", .Value=FromStringBuf("\"  bar \"")}
        );
        // value0004
        // Browsers disagree in what the resulting cookie is. We forbid such cases in balancer.
        TestParse(
            "foo=\"bar;baz\"",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::AttrUnknown}
        );
        // value0005
        TestParse(
            "foo=\"bar=baz\"",
            TSetCookie{.Name="foo", .Value=FromStringBuf("\"bar=baz\"")}
        );
        // value0006
        // We forbid unknown attrs in balancer.
        TestParse(
            "foo\t=\tbar\t \t;\tttt",
            TSetCookieSyntaxErrors{ESetCookieSyntaxError::InvalidOctet, ESetCookieSyntaxError::AttrUnknown}
        );
    }
}
