Skip to content

Commit

Permalink
Added tests for cookies.
Browse files Browse the repository at this point in the history
  • Loading branch information
rogelio-o committed Dec 25, 2017
1 parent f04ac3b commit 4428710
Show file tree
Hide file tree
Showing 15 changed files with 157 additions and 71 deletions.
3 changes: 1 addition & 2 deletions src/lib/http/HttpRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import IApp from "./../types/IApp";
import INext from "./../types/INext";
import IRawEvent from "./../types/IRawEvent";
import { getCookiesFromHeader, mergeParams, normalizeType } from "./../utils/utils";
import Cookie from "./Cookie";

/**
* A incoming request created when the event is APIGatewayEvent.
Expand Down Expand Up @@ -41,7 +40,7 @@ export default class HttpRequest implements IHttpRequest {
this._headers[key.toLowerCase()] = this._event.headers[key];
}

this._cookies = getCookiesFromHeader(this._headers.Cookie, app.get(configuration.COOKIE_SECRET));
this._cookies = getCookiesFromHeader(this._headers.cookie, app.get(configuration.COOKIE_SECRET));
}

get headers(): { [name: string]: string } {
Expand Down
9 changes: 5 additions & 4 deletions src/lib/http/HttpResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,14 @@ export default class HttpResponse implements IHttpResponse {
opts.signed = true;
}

if ("maxAge" in opts) {
opts.expires = new Date(Date.now() + opts.maxAge);
opts.maxAge /= 1000;
if (cookie.expires) {
opts.expires = cookie.expires;
}

if (opts.path == null) {
if (cookie.path == null) {
opts.path = "/";
} else {
opts.path = cookie.path;
}

this._cookies[cookie.name] = cookie;
Expand Down
2 changes: 1 addition & 1 deletion test/http/HttpLayer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe("HttpLayer", () => {
const callback: DefaultCallback = new DefaultCallback();

beforeEach(() => {
req = new HttpRequest(Object.assign({}, httpEvent));
req = new HttpRequest(app, Object.assign({}, httpEvent));
res = new HttpResponse(app, req, callback);
layer = new HttpLayer(router, "/blog/:id", {});
});
Expand Down
21 changes: 20 additions & 1 deletion test/http/HttpRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as Chai from "chai";
import App from "./../../src/lib/App";
import HttpRequest from "./../../src/lib/http/HttpRequest";
import HttpResponse from "./../../src/lib/http/HttpResponse";
import ICookie from "./../../src/lib/types/http/ICookie";
import IHttpRequest from "./../../src/lib/types/http/IHttpRequest";
import IRawEvent from "./../../src/lib/types/IRawEvent";
import DefaultCallback from "./../utils/DefaultCallback";
Expand All @@ -17,7 +18,7 @@ describe("HttpRequest", () => {
const app = new App();
beforeEach((done) => {
event = Object.assign({}, httpEvent);
request = new HttpRequest(event);
request = new HttpRequest(app, event);

done();
});
Expand Down Expand Up @@ -190,4 +191,22 @@ describe("HttpRequest", () => {
Chai.expect(request.stale(response)).to.be.true;
});

describe("#cookies", () => {
it("returns an object with all the cookies of the `Cookie` header.", () => {
const cookies: { [name: string]: ICookie } = request.cookies;

Chai.expect(cookies.cookie1.name).to.be.equal("cookie1");
Chai.expect(cookies.cookie1.value).to.be.equal("value1");
Chai.expect(cookies.cookie2.name).to.be.equal("cookie2");
Chai.expect(cookies.cookie2.value).to.be.equal("value2");
});
});

describe("#cookie", () => {
it("returns the cookie of the `Cookie` header with the given name.", () => {
Chai.expect(request.cookie("cookie1").name).to.be.equal("cookie1");
Chai.expect(request.cookie("cookie1").value).to.be.equal("value1");
});
});

});
68 changes: 39 additions & 29 deletions test/http/HttpResponse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import { SinonStub, stub } from "sinon";
import App from "./../../src/lib/App";
import configuration from "./../../src/lib/configuration/configuration";
import HttpError from "./../../src/lib/exceptions/HttpError";
import Cookie from "./../../src/lib/http/Cookie";
import HttpRequest from "./../../src/lib/http/HttpRequest";
import HttpResponse from "./../../src/lib/http/HttpResponse";
import TemplateEngine from "./../../src/lib/http/renderEngine/TemplateEngine";
import Router from "./../../src/lib/Router";
import ICookie from "./../../src/lib/types/http/ICookie";
import IHttpRequest from "./../../src/lib/types/http/IHttpRequest";
import IHttpResponse from "./../../src/lib/types/http/IHttpResponse";
import IApp from "./../../src/lib/types/IApp";
Expand All @@ -31,7 +33,7 @@ describe("HttpResponse", () => {
beforeEach((done) => {
app = new App();
event = Object.assign({}, httpEvent);
request = new HttpRequest(event);
request = new HttpRequest(app, event);
callback = new DefaultCallback();
response = new HttpResponse(app, request, callback);

Expand Down Expand Up @@ -322,69 +324,77 @@ describe("HttpResponse", () => {
});

describe("#addCookie", () => {
it("should add the `name` cookie with the `value` and the `options`", () => {
response.addCookie("cookie", "cookieValue", {path: "/test"});
it("should add the cookie to the right header.", () => {
const cookie: ICookie = new Cookie("cookie", "cookieValue", null, "/test");
response.addCookie(cookie);
Chai.expect(response.header("Set-Cookie")).to.be.equal("cookie=cookieValue; Path=/test");
});

it("should throw an error if the option `signed` is true and the app has no `secret`.", () => {
const cookie: ICookie = new Cookie("cookie", "cookieValue", null, "/", true);
app.set(configuration.COOKIE_SECRET, null);
Chai.expect(() => response.addCookie("cookie", "cookieValue", {signed: true})).to.throw("app.set(\"cookie_secret\", \"SECRET\") required for signed cookies.");
Chai.expect(() => response.addCookie(cookie)).to.throw("app.set(\"cookie_secret\", \"SECRET\") required for signed cookies.");
});

it("should stringify the value of the cookie if is an object.", () => {
response.addCookie("cookie", {key: "value"});
const cookie: ICookie = new Cookie("cookie", {key: "value"});
response.addCookie(cookie);
Chai.expect(response.header("Set-Cookie")).to.be.equal("cookie=" + encodeURIComponent("j:{\"key\":\"value\"}") + "; Path=/");
});

it("should sign the value with the secret if the option `signed` is true.", () => {
const cookie: ICookie = new Cookie("cookie", "cookieValue", null, "/", true);
app.set(configuration.COOKIE_SECRET, "SUPER_SECRET");
response.addCookie("cookie", "cookieValue", {signed: true});
response.addCookie(cookie);
Chai.expect(response.header("Set-Cookie")).to.be.equal("cookie=" + encodeURIComponent("s:" + sign("cookieValue", app.get(configuration.COOKIE_SECRET))) + "; Path=/");
});

it("should add `expires` and `maxAge` into the cookie if `maxAge` is given in options.", () => {
it("should add `expires` into the cookie header value if `expires` is given in cookie constructor.", () => {
const oldDate = Date;
global.Date = new Proxy(Date, {
construct: (target, args) => {
return new oldDate(2017, 11, 19, 0, 0, 0, 0);
}
});

response.addCookie("cookie", "cookieValue", {maxAge: 5000}); // 5 seconds
const expires = new Date(Date.now() + 5000);
Chai.expect(response.header("Set-Cookie")).to.be.equal("cookie=cookieValue; Max-Age=5; Path=/; Expires=" + expires.toUTCString());
const expirationDate: Date = new Date();
expirationDate.setSeconds(expirationDate.getSeconds() + 5);
const cookie: ICookie = new Cookie("cookie", "cookieValue", expirationDate);

response.addCookie(cookie);

Chai.expect(response.header("Set-Cookie")).to.be.equal("cookie=cookieValue; Path=/; Expires=" + expirationDate.toUTCString());

global.Date = oldDate;
});
});

it("#addCookies should add all the cookies of `obj` with the `options`", () => {
response.addCookies(
{
cookie1: "cookieValue1",
cookie2: "cookieValue2"
},
{path: "/test"}
);
Chai.expect(response.header("Set-Cookie")).to.contain("cookie1=cookieValue1; Path=/test");
Chai.expect(response.header("Set-Cookie")).to.contain("cookie2=cookieValue2; Path=/test");
it("#addCookies should add all the cookies of the cookies array with their options.", () => {
const cookies: ICookie[] = [];
cookies.push(new Cookie("cookie1", "cookieValue1", null, "/test"));
cookies.push(new Cookie("cookie2", "cookieValue2", null, "/test2"));

response.addCookies(cookies);

Chai.expect(response.header("Set-Cookie")[0]).to.be.equal("cookie1=cookieValue1; Path=/test");
Chai.expect(response.header("Set-Cookie")[1]).to.be.equal("cookie2=cookieValue2; Path=/test2");
});

it("#clearCookie should add the cookie `field` with the `options` and the expires to 1 and the path to /", () => {
response.clearCookie("cookie");
Chai.expect(response.header("Set-Cookie")).to.be.equal("cookie=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT");
});

it("#cookie should return the cookie `name`", () => {
response.addCookies(
{
cookie1: "cookieValue1",
cookie2: "cookieValue2"
},
{path: "/test"}
);
Chai.expect(response.cookie("cookie1")).to.be.equal("cookieValue1");
it("#cookie should return the cookie object if it has been previously added.", () => {
const cookies: ICookie[] = [];
cookies.push(new Cookie("cookie1", "cookieValue1", null, "/test"));
cookies.push(new Cookie("cookie2", "cookieValue2", null, "/test2"));

response.addCookies(cookies);
Chai.expect(response.cookie("cookie1").value).to.be.equal("cookieValue1");
Chai.expect(response.cookie("cookie1").path).to.be.equal("/test");
Chai.expect(response.cookie("cookie2").value).to.be.equal("cookieValue2");
Chai.expect(response.cookie("cookie2").path).to.be.equal("/test2");
});

describe("location", () => {
Expand Down
2 changes: 1 addition & 1 deletion test/http/HttpRoute.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe("HttpRoute", () => {
layer = new HttpLayer(router, "/blog/:id", {});
route = new HttpRoute(layer);
layer.route = route;
req = new HttpRequest(event);
req = new HttpRequest(app, event);
res = new HttpResponse(app, req, callback);
});

Expand Down
2 changes: 1 addition & 1 deletion test/http/HttpRouterExecutor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("HttpRouterExecutor", () => {

beforeEach(() => {
callback = new DefaultCallback();
req = new HttpRequest(Object.assign({}, httpEvent));
req = new HttpRequest(app, Object.assign({}, httpEvent));
res = new HttpResponse(app, req, callback);
router = new Router();
});
Expand Down
13 changes: 8 additions & 5 deletions test/http/bodyParsers/JsonParser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
/* tslint:disable:no-unused-expression */
import * as Chai from "chai";
import { SinonSpy, spy } from "sinon";
import App from "./../../../src/lib/App";
import JsonParser from "./../../../src/lib/http/bodyParsers/JsonParser";
import HttpRequest from "./../../../src/lib/http/HttpRequest";
import IHttpHandler from "./../../../src/lib/types/http/IHttpHandler";
import IHttpRequest from "./../../../src/lib/types/http/IHttpRequest";
import IHttpResponse from "./../../../src/lib/types/http/IHttpResponse";
import IApp from "./../../../src/lib/types/IApp";
import httpEvent from "./../../utils/httpEvent";

/**
* Test for JsonParser.
*/
describe("JsonParser", () => {
const app: IApp = new App();
const res: IHttpResponse = {} as IHttpResponse;
let next: SinonSpy;
let event: any;
Expand All @@ -28,7 +31,7 @@ describe("JsonParser", () => {

it("should call 'next' with a 400 error if the body can not be parsed and header contentType is 'application/json'.", () => {
event.body = "errorBody";
const req: IHttpRequest = new HttpRequest(event);
const req: IHttpRequest = new HttpRequest(app, event);

handler(req, res, next);

Expand All @@ -40,7 +43,7 @@ describe("JsonParser", () => {
it("should call 'next' WITHOUT an error if the body can not be parsed and header contentType is undefined.", () => {
event.body = "errorBody";
event.headers["Content-Type"] = undefined;
const req: IHttpRequest = new HttpRequest(event);
const req: IHttpRequest = new HttpRequest(app, event);

handler(req, res, next);

Expand All @@ -49,7 +52,7 @@ describe("JsonParser", () => {
});

it("should set the body with the parsed body as an object if header contentType is 'application/json'.", () => {
const req: IHttpRequest = new HttpRequest(event);
const req: IHttpRequest = new HttpRequest(app, event);

handler(req, res, next);

Expand All @@ -59,7 +62,7 @@ describe("JsonParser", () => {

it("should set the body with the parsed body as an object if header contentType is undefined.", () => {
event.headers["Content-Type"] = undefined;
const req: IHttpRequest = new HttpRequest(event);
const req: IHttpRequest = new HttpRequest(app, event);

handler(req, res, next);

Expand All @@ -69,7 +72,7 @@ describe("JsonParser", () => {

it("should NOT set the body if header contentType is 'text/html'.", () => {
event.headers["Content-Type"] = "text/html";
const req: IHttpRequest = new HttpRequest(event);
const req: IHttpRequest = new HttpRequest(app, event);

handler(req, res, next);

Expand Down
9 changes: 6 additions & 3 deletions test/http/bodyParsers/MultipartParser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
/* tslint:disable:no-unused-expression */
import * as Chai from "chai";
import { SinonSpy, spy } from "sinon";
import App from "./../../../src/lib/App";
import MultipartParser from "./../../../src/lib/http/bodyParsers/MultipartParser";
import HttpRequest from "./../../../src/lib/http/HttpRequest";
import HttpUploadedFile from "./../../../src/lib/http/HttpUploadedFile";
import IHttpHandler from "./../../../src/lib/types/http/IHttpHandler";
import IHttpRequest from "./../../../src/lib/types/http/IHttpRequest";
import IHttpResponse from "./../../../src/lib/types/http/IHttpResponse";
import IHttpUploadedFile from "./../../../src/lib/types/http/IHttpUploadedFile";
import IApp from "./../../../src/lib/types/IApp";

const mainEvent: any = {
body: "------WebKitFormBoundaryvef1fLxmoUdYZWXp\n"
Expand Down Expand Up @@ -39,6 +41,7 @@ const mainEvent: any = {
* Test for MultipartParser.
*/
describe("MultipartParser", () => {
const app: IApp = new App();
const res: IHttpResponse = {} as IHttpResponse;
let next: SinonSpy;
let event: any;
Expand All @@ -53,7 +56,7 @@ describe("MultipartParser", () => {
it("should call 'next' WITHOUT an error if the body can not be parsed and header contentType is undefined.", () => {
event.body = "errorBody";
event.headers["Content-Type"] = undefined;
const req: IHttpRequest = new HttpRequest(event);
const req: IHttpRequest = new HttpRequest(app, event);

handler(req, res, next);

Expand All @@ -62,7 +65,7 @@ describe("MultipartParser", () => {
});

it("should set the body with the parsed body as an object if header contentType is 'multipart/form-data'.", () => {
const req: IHttpRequest = new HttpRequest(event);
const req: IHttpRequest = new HttpRequest(app, event);

handler(req, res, next);

Expand All @@ -84,7 +87,7 @@ describe("MultipartParser", () => {

it("should NOT set the body if header contentType is 'text/html'.", () => {
event.headers["Content-Type"] = "text/html";
const req: IHttpRequest = new HttpRequest(event);
const req: IHttpRequest = new HttpRequest(app, event);

handler(req, res, next);

Expand Down
Loading

0 comments on commit 4428710

Please sign in to comment.