diff --git a/apps/server/src/routes/login.spec.ts b/apps/server/src/routes/login.spec.ts index 6fa0af944..6a0af3c52 100644 --- a/apps/server/src/routes/login.spec.ts +++ b/apps/server/src/routes/login.spec.ts @@ -2,18 +2,20 @@ import { beforeAll, describe, expect, it } from "vitest"; import supertest, { type Response } from "supertest"; import type { Application } from "express"; import dayjs from "dayjs"; -import type { SQLiteSessionStore } from "./session_parser.js"; +import { type SQLiteSessionStore } from "./session_parser.js"; import { SessionData } from "express-session"; let app: Application; let sessionStore: SQLiteSessionStore; +let CLEAN_UP_INTERVAL: number; describe("Login Route test", () => { beforeAll(async () => { + vi.useFakeTimers(); const buildApp = (await import("../app.js")).default; app = await buildApp(); - sessionStore = (await import("./session_parser.js")).sessionStore; + ({ sessionStore, CLEAN_UP_INTERVAL } = (await import("./session_parser.js"))); }); it("should return the login page, when using a GET request", async () => { @@ -79,6 +81,17 @@ describe("Login Route test", () => { expect(session!.loggedIn).toBe(true); expect(expiry).toStrictEqual(new Date(session!.cookie.expires!)); }); + + it("cleans up expired sessions", async () => { + let { session, expiry } = await getSessionFromCookie(setCookieHeader); + expect(session).toBeTruthy(); + expect(expiry).toBeTruthy(); + + vi.setSystemTime(expiry!); + vi.advanceTimersByTime(CLEAN_UP_INTERVAL); + ({ session } = await getSessionFromCookie(setCookieHeader)); + expect(session).toBeFalsy(); + }); }); describe("Login when 'Remember Me' is not ticked", async () => { @@ -107,7 +120,18 @@ describe("Login Route test", () => { const expectedExpirationDate = dayjs().utc().add(1, "hour").toDate(); expect(expiry?.getTime()).toBeGreaterThan(new Date().getTime()); - expect(expiry?.getTime()).toBeLessThan(expectedExpirationDate.getTime()); + expect(expiry?.getTime()).toBeLessThanOrEqual(expectedExpirationDate.getTime()); + }); + + it("cleans up expired sessions", async () => { + let { session, expiry } = await getSessionFromCookie(setCookieHeader); + expect(session).toBeTruthy(); + expect(expiry).toBeTruthy(); + + vi.setSystemTime(expiry!); + vi.advanceTimersByTime(CLEAN_UP_INTERVAL); + ({ session } = await getSessionFromCookie(setCookieHeader)); + expect(session).toBeFalsy(); }); }); }); diff --git a/apps/server/src/routes/session_parser.ts b/apps/server/src/routes/session_parser.ts index e8100bc7e..1c058f14d 100644 --- a/apps/server/src/routes/session_parser.ts +++ b/apps/server/src/routes/session_parser.ts @@ -5,6 +5,11 @@ import config from "../services/config.js"; import log from "../services/log.js"; import type express from "express"; +/** + * The amount of time in milliseconds after which expired sessions are cleaned up. + */ +export const CLEAN_UP_INTERVAL = 60 * 60 * 1000; // 1 hour + export class SQLiteSessionStore extends Store { get(sid: string, callback: (err: any, session?: session.SessionData | null) => void): void { @@ -88,6 +93,6 @@ setInterval(() => { const now = Date.now(); const result = sql.execute(/*sql*/`DELETE FROM sessions WHERE expires < ?`, now); console.log("Cleaning up expired sessions: ", result.changes); -}, 60 * 60 * 1000); +}, CLEAN_UP_INTERVAL); export default sessionParser;