import {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next";
import { withIronSessionApiRoute, withIronSessionSsr } from ".";
const password = "Gbm49ATjnqnkCCCdhV4uDBhbfnPqsCW0";
const cookieName = "test";
// same structure as other tests otherwise linting will fail because you can have only one per running eslint
declare module "iron-session" {
interface IronSessionData {
user?: { id: number; meta?: string };
admin?: boolean;
}
}
test("withIronSessionApiRoute: req.session exists", async () => {
const wrappedHandler = withIronSessionApiRoute(
function handler(req) {
expect(req.session).toMatchInlineSnapshot(`Object {}`);
},
{
password,
cookieName,
},
);
await wrappedHandler(getDefaultReq(), getDefaultRes());
});
test("withIronSessionApiRoute: req.session.save creates a cookie", async () => {
const wrappedHandler = withIronSessionApiRoute(
async function handler(req, res) {
expect(req.session).toMatchInlineSnapshot(`Object {}`);
req.session.user = { id: 200 };
await req.session.save();
const headerName = (res.setHeader as jest.Mock).mock.calls[0][0];
expect(headerName).toMatchInlineSnapshot(`"set-cookie"`);
const headerValue = (res.setHeader as jest.Mock).mock.calls[0][1];
expect(Array.isArray(headerValue)).toBe(true);
expect(headerValue).toHaveLength(1);
const cookie = headerValue[0];
const seal = cookie.split(";")[0].split("=")[1];
expect(seal).toHaveLength(265);
const cookieParams = cookie.split(";").slice(1).join(";");
expect(cookieParams).toMatchInlineSnapshot(
`" Max-Age=1295940; Path=/; HttpOnly; Secure; SameSite=Lax"`,
);
},
{
password,
cookieName,
},
);
await wrappedHandler(getDefaultReq(), getDefaultRes());
});
test("withIconSessionApiRoute: IronSessionOptions passed as a function works correctly", async () => {
const wrappedHandler = withIronSessionApiRoute(
async function handler(req, res) {
req.session.user = { id: 200 };
await req.session.save();
const headerValue = (res.setHeader as jest.Mock).mock.calls[0][1];
const cookie = headerValue[0];
const cookieParams = cookie.split(";").slice(1).join(";");
const cookieName = cookie.split("=")[0];
// When giving session, iron implementation substracts 60 seconds.
const maxAgeValue = Number(req.headers["value"]) - 60;
expect(cookieParams).toMatchInlineSnapshot(
`" Max-Age=${maxAgeValue}; Path=/; HttpOnly; Secure; SameSite=Lax"`,
);
expect(cookieName).toBe("dynamic-cookie-name");
},
(request) => ({
cookieName: "dynamic-cookie-name",
password,
ttl: Number(request.headers["value"]),
}),
);
// Run it twice to make sure different value is assigned on computed session option from request
await wrappedHandler(getDefaultReq(), getDefaultRes());
await wrappedHandler(getDefaultReq(), getDefaultRes());
});
test("withIronSessionApiRoute: req.session.destroy removes the cookie", async () => {
const wrappedHandler = withIronSessionApiRoute(
async function handler(req, res) {
req.session.user = { id: 300 };
await req.session.save();
expect(req.session).toMatchInlineSnapshot(`
Object {
"user": Object {
"id": 300,
},
}
`);
req.session.destroy();
expect(req.session).toMatchInlineSnapshot(`Object {}`);
expect((res.setHeader as jest.Mock).mock.calls[1]).toMatchInlineSnapshot(`
Array [
"set-cookie",
Array [
"test=; Max-Age=0; Path=/; HttpOnly; Secure; SameSite=Lax",
],
]
`);
},
{
password,
cookieName,
},
);
await wrappedHandler(getDefaultReq(), getDefaultRes());
});
test("withIronSessionApiRoute: full session rewrite works too", async () => {
const wrappedHandler = withIronSessionApiRoute(
async function handler(req) {
req.session.user = { id: 200 };
await req.session.save();
expect(req.session).toMatchInlineSnapshot(`
Object {
"user": Object {
"id": 200,
},
}
`);
// @ts-ignore TypeScript warns about save and destroy not being here,
// so this example is most probably to catch misuage of the API and because it works the same as destructuring code wise
req.session = {
admin: true,
};
expect(req.session).toMatchInlineSnapshot(`
Object {
"admin": true,
}
`);
},
{
password,
cookieName,
},
);
await wrappedHandler(getDefaultReq(), getDefaultRes());
});
test("withIronSessionApiRoute: req.session can be overridden, save and destroy will stay", async () => {
const wrappedHandler = withIronSessionApiRoute(
async function handler(req) {
req.session = {
...req.session,
user: { id: 400 },
};
await req.session.save();
expect(req.session).toMatchInlineSnapshot(`
Object {
"user": Object {
"id": 400,
},
}
`);
},
{
password,
cookieName,
},
);
await wrappedHandler(getDefaultReq(), getDefaultRes());
});
test("withIronSessionSsr: req.session exists", async () => {
const getServerSideProps = withIronSessionSsr(
async function getServerSideProps({ req }) {
expect(req.session).toMatchInlineSnapshot(`Object {}`);
return { props: {} };
},
{
password,
cookieName,
},
);
await getServerSideProps({
req: getDefaultReq(),
res: getDefaultRes(),
} as unknown as GetServerSidePropsContext);
});
test("withIronSessionSsr: IronSessionOptions passed as a function to be computed on each request work correctly", async () => {
const wrappedHandler = withIronSessionSsr(
async function handler(context) {
context.req.session.user = { id: 200 };
await context.req.session.save();
const headerValue = (context.res.setHeader as jest.Mock).mock.calls[0][1];
const cookie = headerValue[0];
const cookieParams = cookie.split(";").slice(1).join(";");
const cookieName = cookie.split("=")[0];
// When giving session, iron implementation substracts 60 seconds.
const maxAgeValue = Number(context.req.headers["value"]) - 60;
expect(cookieParams).toMatchInlineSnapshot(
`" Max-Age=${maxAgeValue}; Path=/; HttpOnly; Secure; SameSite=Lax"`,
);
expect(cookieName).toBe("dynamic-cookie-name");
return {
props: {},
};
},
(request) => ({
cookieName: "dynamic-cookie-name",
password,
ttl: Number(request.headers["value"]),
}),
);
// Run it twice to make sure different value is assigned on computed session option from request
await wrappedHandler({
req: getDefaultReq(),
res: getDefaultRes(),
} as unknown as GetServerSidePropsContext);
await wrappedHandler({
req: getDefaultReq(),
res: getDefaultRes(),
} as unknown as GetServerSidePropsContext);
});
function getDefaultReq() {
return {
headers: {
// Random number between 100 to 1000
value: Math.floor(Math.random() * 900) + 100,
},
socket: {
encrypted: true,
},
} as unknown as NextApiRequest;
}
function getDefaultRes() {
return {
getHeader: jest.fn(),
setHeader: jest.fn(),
} as unknown as NextApiResponse;
}