Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-httpclient

Named HTTP clients for Pudu: a client factory, delegating handlers, pooled keep-alive connections, handler lifetimes, logging, and resilience

0.1.1Apache-2.01

InstallClose

CookieTest.pudu

Pudu66 lines6.2 KB

GitHub ↗
1/** @Test.Domain.Cookie.Suite — cookies read from responses and matched to requests */2module PuduLangHttpClient.Domain.CookieTest34import Std.Io as Io5import Std.Json as Json6import Std.Test as Test7import PuduLangHttpClient.Domain.Cookie as Cookie89/// The moment every check is taken at.10const NOW: Int = 10000001112/// A cookie set by `host` for a request to `path`, known to parse.13fn parsed(header: Str, host: Str, path: Str) -> Cookie.Stored {14  match Cookie.parse(header, host, path, NOW) {15    case Some(found) => found16    case None => panic("did not parse: " + header)17  }18}1920/// Runs the suite.21fn main() -> Int {22  let plain = parsed("sid=\"abc\"", "Api.Test", "/v1/users")23  let full = parsed("pref=1; Domain=.api.test; Path=/v1; Secure; HttpOnly; Max-Age=60; Expires=Thu, 01 Jan 1970 00:00:00 GMT", "www.api.test", "/")24  let dated = parsed("d=1; Expires=Thu, 01 Jan 1970 00:16:41 GMT", "a.test", "/")25  let gone = parsed("g=1; Max-Age=0", "a.test", "/")26  let jar = Cookie.store(&Cookie.store(&[], &plain, NOW), &parsed("sid=new", "api.test", "/v1/x"), NOW + 5)27  let checks = Test.suite("Domain.Cookie", &[28      Test.equals("a plain cookie is host-only under the request's directory", &plain, &Cookie.Stored{name: "sid", value: "abc", domain: "api.test", hostOnly: true, path: "/v1", secure: false, httpOnly: false, expires: None, created: NOW}),29      Test.equals("attributes set the domain, path, flags, and a max-age over expires", &full, &Cookie.Stored{name: "pref", value: "1", domain: "api.test", hostOnly: false, path: "/v1", secure: true, httpOnly: true, expires: Some(NOW + 60000), created: NOW}),30      Test.equals("an expires date is used without a max-age", &dated.expires, &Some(1001000)),31      Test.equals("a max-age of zero or less expires at once", &(gone.expires, parsed("g=1; Max-Age=-5", "a.test", "/").expires), &(Some(NOW), Some(NOW))),32      Test.equals("a domain the host is not in, a missing name, or no equals sign is refused", &[Cookie.parse("x=1; Domain=other.test", "api.test", "/", NOW), Cookie.parse("=1", "a", "/", NOW), Cookie.parse("novalue", "a", "/", NOW), Cookie.parse(" =1", "a", "/", NOW)], &[None, None, None, None]),33      Test.equals("an empty domain or a relative path is ignored", &(parsed("x=1; Domain=; Path=rel", "a.test", "/p/q").hostOnly, parsed("x=1; Domain=; Path=rel", "a.test", "/p/q").path), &(true, "/p")),34      Test.that("a host matches its domain and the names under it", Cookie.domainMatches("a.test", "a.test") && Cookie.domainMatches("www.a.test", "a.test")),35      Test.not("a host does not match a lookalike or an address's suffix", Cookie.domainMatches("evila.test", "a.test") || Cookie.domainMatches("1.2.3.4", "2.3.4") || Cookie.domainMatches("::1", ":1")),36      Test.that("a path matches itself and the paths under it", Cookie.pathMatches("/v1", "/v1") && Cookie.pathMatches("/v1/x", "/v1") && Cookie.pathMatches("/v1/x", "/v1/") && Cookie.pathMatches("/v1/x", "/")),37      Test.not("a path does not match a lookalike", Cookie.pathMatches("/v10", "/v1") || Cookie.pathMatches("/v", "/v1")),38      Test.equals("default paths stop at the last segment", &[Cookie.defaultPath(""), Cookie.defaultPath("x"), Cookie.defaultPath("/"), Cookie.defaultPath("/a"), Cookie.defaultPath("/a/b"), Cookie.defaultPath("/a/b/")], &["/", "/", "/", "/", "/a", "/a/b"]),39      Test.that("a host-only cookie applies only to its host", Cookie.applies(&plain, "API.test", "/v1/a", false, NOW) && !Cookie.applies(&plain, "www.api.test", "/v1/a", false, NOW)),40      Test.that("a domain cookie applies under its domain only over a secured connection", Cookie.applies(&full, "x.api.test", "/v1", true, NOW) && !Cookie.applies(&full, "x.api.test", "/v1", false, NOW)),41      Test.that("a cookie applies to an empty path when its path is the root", Cookie.applies(&dated, "a.test", "", false, NOW)),42      Test.not("an expired cookie applies nowhere", Cookie.applies(&full, "api.test", "/v1", true, NOW + 60000)),43      Test.that("a cookie expires exactly at its expiry", Cookie.expired(&full, NOW + 60000) && !Cookie.expired(&full, NOW + 59999) && !Cookie.expired(&plain, NOW * 1000)),44      Test.equals("storing replaces a cookie of the same name, domain, and path and keeps its creation time", &jar.map(|c: Cookie.Stored| (c.value, c.created)), &[("new", NOW)]),45      Test.equals("storing an expired cookie removes the one it replaces", &Cookie.store(&jar, &parsed("sid=x; Max-Age=0", "api.test", "/v1/x"), NOW), &[]),46      Test.equals("storing drops cookies that have expired since", &Cookie.store(&[full], &plain, NOW + 70000).map(|c: Cookie.Stored| c.name), &["sid"]),47      Test.equals("longer paths are sent first, then older cookies", &Cookie.render(&[Cookie.Stored{..plain, name: "b", path: "/", created: 2}, Cookie.Stored{..plain, name: "a", path: "/", created: 1}, Cookie.Stored{..plain, name: "c", path: "/v1/deep"}]), &"c=abc; a=abc; b=abc"),48      Test.equals("cookies differing in name, domain, or path are kept apart", &[49          Cookie.store(&[plain], &Cookie.Stored{..plain, name: "other"}, NOW).length(),50          Cookie.store(&[plain], &Cookie.Stored{..plain, domain: "other.test"}, NOW).length(),51          Cookie.store(&[plain], &Cookie.Stored{..plain, path: "/other"}, NOW).length()52        ], &[2, 2, 2]),53      Test.not("an address ending in a zero or a nine does not match by suffix", Cookie.domainMatches("10.0.0.0", "0.0.0") || Cookie.domainMatches("19.9.9.9", "9.9.9")),54      Test.equals("an empty quoted value is empty and an unquoted short value is kept", &(parsed("e=\"\"", "a.test", "/").value, parsed("s=ab", "a.test", "/").value, parsed("q=\"", "a.test", "/").value), &("", "ab", "\"")),55      Test.equals("an attribute without a name is ignored", &parsed("x=1; =v; Path=/p", "a.test", "/").path, &"/p"),56      Test.equals("cookies with equal paths and times keep their order", &Cookie.render(&[Cookie.Stored{..plain, name: "x"}, Cookie.Stored{..plain, name: "y"}]), &"x=abc; y=abc"),57      Test.that("only a cookie with an expiry is persistent", Cookie.isPersistent(&full) && !Cookie.isPersistent(&plain)),58      Test.equals("a stored cookie writes itself as JSON and reads back the same", &Json.decode(Json.encode(&full.encode())), &Ok(full.encode()))59    ])60  let ran = Test.run(&checks)61  for failure in Test.failuresOf(&ran) {62    let _reported = Io.writeErrorLine(failure)63  }64  Test.report(&ran)65}66