test(server): ensure session info exists

This commit is contained in:
Elian Doran 2025-06-07 09:55:55 +03:00
parent 2ceab66b98
commit e003ec3b6f
No known key found for this signature in database
3 changed files with 59 additions and 8 deletions

View File

@ -2,14 +2,19 @@ import { beforeAll, describe, expect, it } from "vitest";
import supertest from "supertest";
import type { Application } from "express";
import dayjs from "dayjs";
import type { SQLiteSessionStore } from "./session_parser.js";
import { promisify } from "util";
import { SessionData } from "express-session";
let app: Application;
let sessionStore: SQLiteSessionStore;
describe("Login Route test", () => {
beforeAll(async () => {
const buildApp = (await import("../app.js")).default;
app = await buildApp();
sessionStore = (await import("./session_parser.js")).sessionStore;
});
it("should return the login page, when using a GET request", async () => {
@ -52,7 +57,7 @@ describe("Login Route test", () => {
// match for e.g. "Expires=Wed, 07 May 2025 07:02:59 GMT;"
const expiresCookieRegExp = /Expires=(?<date>[\w\s,:]+)/;
const expiresCookieMatch = setCookieHeader.match(expiresCookieRegExp);
const actualExpiresDate = new Date(expiresCookieMatch?.groups?.date || "").toUTCString()
const actualExpiresDate = new Date(expiresCookieMatch?.groups?.date || "").toUTCString();
expect(actualExpiresDate).to.not.eql("Invalid Date");
@ -60,6 +65,13 @@ describe("Login Route test", () => {
// if for some reason execution is slow between calculation of expected and actual
expect(actualExpiresDate.slice(0,23)).toBe(expectedExpiresDate.slice(0,23))
// Check the session is stored in the database.
const session = await getSessionFromCookie(setCookieHeader);
expect(session!).toBeTruthy();
expect(session!.cookie.expires).toBeTruthy();
expect(new Date(session!.cookie.expires!).toUTCString().substring(0, 23))
.toBe(expectedExpiresDate.substring(0, 23));
expect(session!.loggedIn).toBe(true);
}, 10_000);
// use 10 sec (10_000 ms) timeout for now, instead of default 5 sec to work around
// failing CI, because for some reason it currently takes approx. 6 secs to run
@ -67,7 +79,6 @@ describe("Login Route test", () => {
it("does not set Expires, when 'Remember Me' is not ticked", async () => {
const res = await supertest(app)
.post("/login")
.send({ password: "demo1234" })
@ -76,14 +87,38 @@ describe("Login Route test", () => {
const setCookieHeader = res.headers["set-cookie"][0];
// match for e.g. "Expires=Wed, 07 May 2025 07:02:59 GMT;"
const expiresCookieRegExp = /Expires=(?<date>[\w\s,:]+)/;
const expiresCookieMatch = setCookieHeader.match(expiresCookieRegExp);
expect(expiresCookieMatch).toBeNull();
expect(setCookieHeader).not.toMatch(/Expires=(?<date>[\w\s,:]+)/)
// Check the session is stored in the database.
const session = await getSessionFromCookie(setCookieHeader);
expect(session!).toBeTruthy();
expect(session!.cookie.expires).toBeUndefined();
expect(session!.loggedIn).toBe(true);
}, 10_000);
// use 10 sec (10_000 ms) timeout for now, instead of default 5 sec to work around
// failing CI, because for some reason it currently takes approx. 6 secs to run
// TODO: actually identify what is causing this and fix the flakiness
});
async function getSessionFromCookie(setCookieHeader: string) {
// Extract the session ID from the cookie.
const sessionIdMatch = setCookieHeader.match(/trilium.sid=(?<sessionId>[^;]+)/)?.[1];
expect(sessionIdMatch).toBeTruthy();
// Check the session is stored in the database.
const sessionId = decodeURIComponent(sessionIdMatch!).slice(2).split(".")[0];
return await getSessionFromStore(sessionId);
}
function getSessionFromStore(sessionId: string) {
return new Promise<SessionData | null | undefined>((resolve, reject) => {
sessionStore.get(sessionId, (err, session) => {
if (err) {
reject(err);
} else {
resolve(session);
}
});
});
}

View File

@ -0,0 +1,14 @@
import { beforeAll, describe, expect, it } from "vitest";
import supertest from "supertest";
import type { Application } from "express";
import dayjs from "dayjs";
let app: Application;
describe("Session parser", () => {
beforeAll(async () => {
const buildApp = (await import("../app.js")).default;
app = await buildApp();
});
});

View File

@ -5,7 +5,7 @@ import config from "../services/config.js";
import log from "../services/log.js";
import type express from "express";
class SQLiteSessionStore extends Store {
export class SQLiteSessionStore extends Store {
get(sid: string, callback: (err: any, session?: session.SessionData | null) => void): void {
try {
@ -52,6 +52,8 @@ class SQLiteSessionStore extends Store {
}
export const sessionStore = new SQLiteSessionStore();
const sessionParser: express.RequestHandler = session({
secret: sessionSecret,
resave: false, // true forces the session to be saved back to the session store, even if the session was never modified during the request.
@ -62,7 +64,7 @@ const sessionParser: express.RequestHandler = session({
maxAge: config.Session.cookieMaxAge * 1000 // needs value in milliseconds
},
name: "trilium.sid",
store: new SQLiteSessionStore()
store: sessionStore
});
setInterval(() => {