import {expect} from 'vitest'; import type {Application} from 'express'; import request from 'supertest'; import {NachklangAdminDB} from '../../src/models/admin/Admin.db.js'; import * as InvitationsService from '../../src/models/admin/invitations/invitations.service.js'; import {AppName} from '../../src/models/admin/admin.schema.js'; const db = NachklangAdminDB.db; // Dev/test cookie name: advanced.cookiePrefix is 'nachklang', and the __Secure- // prefix is only added over https. export const SESSION_COOKIE = 'nachklang.session_token'; /** * Wipes every table between test files. Child tables first - the FKs to `user` * are ON DELETE CASCADE, but the rest are not. * * `rateLimit` matters more than it looks: the limiter is enabled during the * suite, and better-auth caps /sign-in/* at 3 requests per 10 seconds. All * tests resolve to the same client IP, so they share one bucket - without this * reset the suite would start failing with 429s that look like auth bugs as * soon as a third sign-in assertion is added. */ export const resetDatabase = async (): Promise => { await db.deleteFrom('session').execute(); await db.deleteFrom('user_app_permissions').execute(); await db.deleteFrom('passkey').execute(); await db.deleteFrom('invitations').execute(); await db.deleteFrom('verification').execute(); await db.deleteFrom('rateLimit').execute(); await db.deleteFrom('user').execute(); }; export const closeDatabase = async (): Promise => { await db.destroy(); }; /** * Creates an invitation straight through the service (so the test gets the raw * token, which the API deliberately never returns) and redeems it through the * public endpoint. Returns an agent that carries the resulting session cookie. */ export const createAndAcceptInvitation = async ( app: Application, email: string, name: string, apps: AppName[], password = 'devpassword123' ) => { const invitation = await InvitationsService.createInvitation(email, name, apps, null); const agent = request.agent(app); const res = await agent .post('/admin/auth/invitations/accept') .send({token: invitation.token, password}); expect(res.status).toBe(200); return {agent, userId: res.body.user.id, token: invitation.token}; }; export const cookieHeader = (res: request.Response): string[] => { const raw = res.headers['set-cookie']; return Array.isArray(raw) ? raw : raw ? [raw] : []; }; export const sessionCookieFrom = (res: request.Response): string | undefined => { return cookieHeader(res).find(cookie => cookie.startsWith(SESSION_COOKIE)); };