// (C) 2007-2019 GoodData Corporation import { tokenExpiration } from "../tokenExpiration"; import * as Express from "express"; import { noop } from "lodash"; import * as HttpStatusCodes from "http-status-codes"; /** * ExpressBuilder is factory for fake Express Requests and Responses * * Express does not expose any factories or classes that could be used for such purpose, * therefore we have to create real instances of Request/Response and extend them. */ class ExpressBuilder { public constructor(private app = Express()) {} public createRequest(url: string) { return Object.create(this.app.request, { path: { value: url, }, }); } public createResponse() { return Object.create(this.app.response); } } describe("tokenExpiration", () => { it("should skip for the static assets", () => { const middleware = tokenExpiration({ expirationTime: 0, publicPaths: [], }); const assets = [ "/assets/img/avatar.jpg", "/assets/img/avatar.png", "/assets/img/meme.gif", "/static/icon.svg", "/assets/js/main.js", "/assets/css/main.css", "/fonts/comic-sans.woff", "/fonts/comic-sans.ttf", ]; for (const asset of assets) { const builder = new ExpressBuilder(); const req = builder.createRequest(asset); const res = builder.createResponse(); const next = jest.fn(); middleware(req, res, next); expect(next).toHaveBeenCalled(); } }); it("should skip for the root file", () => { const middleware = tokenExpiration({ expirationTime: 0, publicPaths: [], }); const builder = new ExpressBuilder(); const req = builder.createRequest("/"); const res = builder.createResponse(); const next = jest.fn(); middleware(req, res, next); expect(next).toHaveBeenCalled(); }); it("should skip for the public endpoints", () => { const middleware = tokenExpiration({ expirationTime: 0, publicPaths: ["/gdc/account/login"], }); const builder = new ExpressBuilder(); const req = builder.createRequest("/gdc/account/login"); const res = builder.createResponse(); const next = jest.fn(); middleware(req, res, next); expect(next).toHaveBeenCalled(); }); it("should return 200 on `/gdc/account/token` endpoint", () => { const middleware = tokenExpiration({ expirationTime: 0, publicPaths: [], }); const builder = new ExpressBuilder(); const req = builder.createRequest("/gdc/account/token"); const res = builder.createResponse(); const next = jest.fn(); const status = jest.spyOn(res, "status").mockReturnValue({ end: noop }); middleware(req, res, next); expect(next).not.toHaveBeenCalled(); expect(status).toHaveBeenCalledWith(HttpStatusCodes.OK); }); it("should invalidate the token after given period of time", done => { const middleware = tokenExpiration({ expirationTime: 0, publicPaths: [], }); const builder = new ExpressBuilder(); const req = builder.createRequest("/gdc/projects"); const res = builder.createResponse(); const next = jest.fn(); middleware(req, res, next); expect(next).toHaveBeenCalled(); setTimeout(() => { const req = builder.createRequest("/gdc/projects"); const res = builder.createResponse(); const next = jest.fn(); const status = jest.spyOn(res, "status").mockReturnValue({ end: noop }); middleware(req, res, next); try { expect(next).not.toHaveBeenCalled(); expect(status).toHaveBeenCalledWith(HttpStatusCodes.UNAUTHORIZED); done(); } catch (error) { done(error); } }, 1); }); it("should mark the token as valid again after the call to `/gdc/account/token`", done => { const middleware = tokenExpiration({ expirationTime: 0, publicPaths: [], }); const builder = new ExpressBuilder(); const req = builder.createRequest("/gdc/projects"); const res = builder.createResponse(); const next = jest.fn(); middleware(req, res, next); expect(next).toHaveBeenCalled(); setTimeout(() => { try { // Renew the token const req = builder.createRequest("/gdc/account/token"); const res = builder.createResponse(); const next = jest.fn(); const status = jest.spyOn(res, "status").mockReturnValue({ end: noop }); middleware(req, res, next); expect(next).not.toHaveBeenCalled(); expect(status).toHaveBeenCalledWith(HttpStatusCodes.OK); // Send the repeated request to the server, it must pass this time const repeatedReq = builder.createRequest("/gdc/projects"); const repeatedRes = builder.createResponse(); const repeatedNext = jest.fn(); middleware(repeatedReq, repeatedRes, repeatedNext); expect(repeatedNext).toHaveBeenCalled(); done(); } catch (error) { done(error); } }, 1); }); });