From 624219ad0819fbf08ebf8ed6115e3587cb21cfc1 Mon Sep 17 00:00:00 2001 From: jkhsjdhjs Date: Tue, 26 Nov 2019 04:16:16 +0100 Subject: [PATCH] add a few tests for cookie.mjs + errors.mjs --- package.json | 2 +- test/cookie.mjs | 285 ++++++++++++++++++++++++++++++++++++++++++++++++ test/errors.mjs | 27 +++++ test/index.mjs | 45 ++++++++ 4 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 test/cookie.mjs create mode 100644 test/errors.mjs create mode 100644 test/index.mjs diff --git a/package.json b/package.json index 723d99f..c664ef2 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "src/" ], "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "node test/index.mjs" }, "repository": { "type": "git", diff --git a/test/cookie.mjs b/test/cookie.mjs new file mode 100644 index 0000000..0051a0c --- /dev/null +++ b/test/cookie.mjs @@ -0,0 +1,285 @@ +import Cookie from "../src/cookie.mjs"; +import {CookieParseError} from "../src/errors.mjs"; + +export default Test => [ + new Test("cookie parser", () => { + const inputs = [ + [ // type error + 123, + "" + ], + [ // type error + "id=a3fWa", + 123 + ], + [ // type error invalid url + "id=a3fWa", + "" + ], + [ // type error invalid url + "id=a3fWa", + "github.com" + ], + // TODO: fix this test case, parser shouldn't allow it. it is currently ignored + [ // type error invalid url + "id=a3fWa", + "https:abc/abs" + ], + [ // cookie parse error invalid cookie name + "", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28: GMT; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Expires=Wed, 21 Onv 2015 07:28:00 GMT; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Expires=Wed, 21 Oct 20151 07:28:00 GMT; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Expires=Wed, 32 Oct 2015 07:28:00 GMT; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Expires=Wed, 21 Oct 2015 25:28:00 GMT; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Expires=Wed, 21 Oct 2015 07:61:00 GMT; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 UTC; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT+2; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Expires=San, 21 Onv 2015 07:28:00 GMT; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Expires=Wed, 31 Dec 1969 07:28:00 GMT; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid value for expires + "id=a3fWa; Max-Age=121252a; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid key secur + "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secur; HttpOnly", + "https://github.com" + ], + [ // cookie parse error invalid key HttpOly with value 2 + "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOly=2", + "https://github.com" + ], + [ // cookie parse error not set via https + "__Secure-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly", + "http://github.com" + ], + [ // cookie parse error secure not set + "__Secure-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; HttpOnly", + "https://github.com" + ], + [ // cookie parse error secure not set + "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; HttpOnly; Path=/", + "https://github.com" + ], + [ // cookie parse error not set via https + "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; Path=/", + "http://github.com" + ], + [ // cookie parse error domain is set + "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; Domain=github.com; Path=/", + "https://github.com" + ], + [ // cookie parse error path is not set + "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly", + "https://github.com" + ], + [ // cookie parse error path is not equal to / + "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; Path=/lel/", + "https://github.com" + ], + [ // cookie parse error domain is not a subdomain + "id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Domain=github.com", + "https://gist.github.com" + ], + [ // cookie parse error domain is not a subdomain + "id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Domain=npmjs.com", + "https://gist.github.com" + ], + [ // success + "__Secure-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly", + "https://github.com" + ], + [ // success + "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; Path=/", + "https://github.com" + ], + [ // success + "__Host-id=a3fWa; Expires=Wed, 21 Nov 2099 20:28:33 GMT; Secure; HttpOnly; Path=/", + "https://github.com" + ], + [ // success + "id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; Path=/lul/; Domain=.usercontent.github.com", + "https://github.com" + ], + [ // success + "id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; SameSite=Strict; Path=/lul/; Domain=usercontent.github.com", + "https://github.com" + ], + [ // success max-age takes precendence over expires + "id=a3fWa; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=1000", + "https://github.com" + ], + [ // success max-age takes precendence over expires + "id=a3fWa; Max-Age=1000; Expires=Thu, 01 Jan 1970 00:00:00 GMT", + "https://github.com" + ], + ]; + + const catchErrorTest = (input, catchFnc) => { + try { + new Cookie(...input); + return false; + } + catch(error) { + return catchFnc(error); + } + }; + + const catchErrorTypeMessageTest = (input, type, message) => catchErrorTest(input, e => e instanceof type && e.message === message); + + const compareCookieProps = (input, expiryFnc, properties) => { + const cookie = new Cookie(...input); + return Object.entries(properties).every(([prop, value]) => cookie[prop] === value) + && expiryFnc(cookie.expiry); + }; + + return inputs.slice(0, 3).every(input => catchErrorTest(input, e => e instanceof TypeError)) + + // cookies[4] is the test case that is ignored for now + + && catchErrorTypeMessageTest(inputs[5], CookieParseError, "Invalid cookie name \"\"!") + && catchErrorTypeMessageTest(inputs[6], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 2015 07:28: GMT\"!") + && catchErrorTypeMessageTest(inputs[7], CookieParseError, "Invalid value for Expires \"Wed, 21 Onv 2015 07:28:00 GMT\"!") + && catchErrorTypeMessageTest(inputs[8], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 20151 07:28:00 GMT\"!") + && catchErrorTypeMessageTest(inputs[9], CookieParseError, "Invalid value for Expires \"Wed, 32 Oct 2015 07:28:00 GMT\"!") + && catchErrorTypeMessageTest(inputs[10], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 2015 25:28:00 GMT\"!") + && catchErrorTypeMessageTest(inputs[11], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 2015 07:61:00 GMT\"!") + && catchErrorTypeMessageTest(inputs[12], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 2015 07:28:00 UTC\"!") + && catchErrorTypeMessageTest(inputs[13], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 2015 07:28:00 GMT+2\"!") + && catchErrorTypeMessageTest(inputs[14], CookieParseError, "Invalid value for Expires \"San, 21 Onv 2015 07:28:00 GMT\"!") + && catchErrorTypeMessageTest(inputs[15], CookieParseError, "Invalid value for Expires \"Wed, 31 Dec 1969 07:28:00 GMT\"!") + && catchErrorTypeMessageTest(inputs[16], CookieParseError, "Invalid value for Max-Age \"121252a\"!") + && catchErrorTypeMessageTest(inputs[17], CookieParseError, "Invalid key \"secur\" specified!") + && catchErrorTypeMessageTest(inputs[18], CookieParseError, "Invalid key \"httpoly\" with value \"2\" specified!") + + && inputs.slice(19, 21).every(input => catchErrorTypeMessageTest(input, CookieParseError, "Cookie has \"__Secure-\" prefix but \"Secure\" isn't set or the cookie is not set via https!")) + + && inputs.slice(21, 26).every(input => catchErrorTypeMessageTest(input, CookieParseError, "Cookie has \"__Host-\" prefix but \"Secure\" isn't set, the cookie is not set via https, \"Domain\" is set or \"Path\" is not equal to \"/\"!")) + + && catchErrorTypeMessageTest(inputs[26], CookieParseError, "Invalid value for Domain \"github.com\": cookie was received from \"gist.github.com\"!") + && catchErrorTypeMessageTest(inputs[27], CookieParseError, "Invalid value for Domain \"npmjs.com\": cookie was received from \"gist.github.com\"!") + + && compareCookieProps( + inputs[28], + exp => exp.getTime() === new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), + { + name: "__Secure-id", + value: "a3fWa", + secure: true, + domain: "github.com", + subdomains: false, + path: "/" + } + ) + + && compareCookieProps( + inputs[29], + exp => exp.getTime() === new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), + { + name: "__Host-id", + value: "a3fWa", + secure: true, + domain: "github.com", + subdomains: false, + path: "/" + } + ) + + && compareCookieProps( + inputs[30], + exp => exp.getTime() === new Date("Wed, 21 Nov 2099 20:28:33 GMT").getTime(), + { + name: "__Host-id", + value: "a3fWa", + secure: true, + domain: "github.com", + subdomains: false, + path: "/" + } + ) + + && compareCookieProps( + inputs[31], + exp => exp.getTime() === new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), + { + name: "id", + value: "a3fWa", + secure: true, + domain: "usercontent.github.com", + subdomains: true, + path: "/lul/" + } + ) + + && compareCookieProps( + inputs[32], + exp => exp.getTime() === new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), + { + name: "id", + value: "a3fWa", + secure: true, + domain: "usercontent.github.com", + subdomains: true, + path: "/lul/" + } + ) + + && compareCookieProps( + inputs[33], + exp => exp.getTime() > new Date("Thu, 01 Jan 1970 00:00:00 GMT").getTime(), + { + name: "id", + value: "a3fWa", + secure: false, + domain: "github.com", + subdomains: false, + path: "/" + } + ) + + && compareCookieProps( + inputs[34], + exp => exp.getTime() > new Date("Thu, 01 Jan 1970 00:00:00 GMT").getTime(), + { + name: "id", + value: "a3fWa", + secure: false, + domain: "github.com", + subdomains: false, + path: "/" + } + ); + }) +]; diff --git a/test/errors.mjs b/test/errors.mjs new file mode 100644 index 0000000..7129a2b --- /dev/null +++ b/test/errors.mjs @@ -0,0 +1,27 @@ +import {CookieParseError, paramError} from "../src/errors.mjs"; + +export default Test => [ + new Test("function paramError", () => { + const position = "something"; + const paramName = "some_param"; + const functionName = "some_func"; + const validTypes = ["lol", "lel", "lul", "lal"]; + const errors = [ + paramError(position, paramName, functionName, validTypes[0]), + paramError(position, paramName, functionName, validTypes.slice(0, 2)), + paramError(position, paramName, functionName, validTypes) + ]; + return errors.every(e => e instanceof TypeError) + && errors.every(e => e.name === "TypeError") + && errors[0].message === "something parameter \"some_param\" passed to \"some_func\" is not of type \"lol\"!" + && errors[1].message === "something parameter \"some_param\" passed to \"some_func\" is not of type \"lol\" or \"lel\"!" + && errors[2].message === "something parameter \"some_param\" passed to \"some_func\" is not of type \"lol\", \"lel\", \"lul\" or \"lal\"!"; + }), + new Test("class CookieParseError", () => { + const message = "this is a test error"; + const error = new CookieParseError(message); + return error instanceof CookieParseError + && error.name === "CookieParseError" + && error.message === message; + }) +]; diff --git a/test/index.mjs b/test/index.mjs new file mode 100644 index 0000000..9c33aa2 --- /dev/null +++ b/test/index.mjs @@ -0,0 +1,45 @@ +import cookie from "./cookie.mjs"; +import errors from "./errors.mjs"; + +class Test { + constructor(name, fnc) { + this.name = name + this.fnc = fnc + } + async runTest() { + return this.fnc(); + } +} + +const tests = [ + cookie, + errors +].flatMap(t => t(Test)); + +(async () => { + console.log("running tests..."); + const testResults = await Promise.all(tests.map(async t => { + try { + t.result = await t.runTest(); + if(typeof t.result !== "boolean") { + t.result = false; + console.error("test did not return a boolean: " + t.name); + } + } + catch(error) { + console.error("uncaught error in test: " + t.name + "\n", error); + t.result = false; + } + return t; + })); + testResults.forEach(t => { + if(t.result) + console.info("✔ " + t.name); + else + console.warn("✘ " + t.name); + }); + const succeededTests = testResults.map(t => t.result).reduce((a, b) => a + b); + const success = succeededTests === testResults.length; + (success ? console.info : console.warn)((success ? "✔" : "✘") + " " + succeededTests + "/" + testResults.length + " tests successful"); + !success && process.exit(1); +})();