Initial commit

This commit is contained in:
2026-06-24 23:47:55 -07:00
commit d134b480a0
297 changed files with 30726 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
import { QueryTypes } from "@sequelize/core"
import { isNil } from "lodash"
import db from "@/db/db-client"
async function getTableNames() {
const query = /* sql */ `
SELECT
table_name as "tableName"
FROM
information_schema.tables
WHERE
table_schema = 'public'
AND table_type = 'BASE TABLE'
AND table_name != 'SequelizeMeta'
AND table_name != 'knex_migrations'
AND table_name != 'knex_migrations_lock';
`
try {
const result = await db.query<{ tableName: string }>(query, { type: QueryTypes.SELECT })
const tableNames = result.map((row) => row.tableName)
return tableNames
} catch (error) {
console.error("Error fetching table names:", error)
throw error
}
}
async function buildCleanDatabaseQuery() {
const tableNames = await getTableNames()
const quotedTableNames = tableNames.map((name) => `"${name}"`)
return /* sql */ `
TRUNCATE TABLE ${quotedTableNames.join(",\n ")} RESTART IDENTITY CASCADE;
`
}
let cleanDatabaseQuery: string | null = null
export async function cleanDatabase() {
if (isNil(cleanDatabaseQuery)) {
cleanDatabaseQuery = await buildCleanDatabaseQuery()
}
try {
// TODO: once all tables are in Sequelize models, use this instead:
// await db.truncate({ cascade: true, restartIdentity: true })
await db.query(cleanDatabaseQuery, { raw: true })
return true
} catch (error) {
console.error(error)
return false
}
}
export default cleanDatabase
+6
View File
@@ -0,0 +1,6 @@
export { cleanDatabase } from "./clean-database"
export { loadTestData } from "./load-test-data"
export { mockCurrentUser } from "./mock-current-user"
export { mockedAxios } from "./mock-axios"
export { request } from "./request"
export { testWithCustomLogLevel } from "./test-with-custom-log-level"
+39
View File
@@ -0,0 +1,39 @@
import path from "path"
import { readFileSync } from "fs"
import { APP_ROOT_PATH } from "@/config"
import arrayWrap from "@/utils/array-wrap"
/**
* Usage:
* - `testDataPath('path/to/my-file.json')`
* - `testDataPath('path', 'to', 'my-file.json')`
*/
export function testDataPath(pathOrPathSegment: string | string[]): string {
const pathSegments = arrayWrap(pathOrPathSegment)
return path.join(APP_ROOT_PATH, "tests", "data", ...pathSegments)
}
/**
* Usage:
* - `specData('path/to/my-file.json')`
* - `specData('path', 'to', 'my-file.json')`
*
* Returns:
* - JSON parsed object if file is a JSON file
* - Raw file content otherwise
*/
export function loadTestData(pathOrPathSegment: string | string[]): string | object {
const filePath = testDataPath(pathOrPathSegment)
const rawData = readFileSync(filePath)
const extension = path.extname(filePath)
switch (extension) {
case ".json":
return JSON.parse(rawData.toString())
default:
return rawData.toString()
}
}
export default loadTestData
+6
View File
@@ -0,0 +1,6 @@
import axios from "axios"
import MockAdapter from "axios-mock-adapter"
export const mockedAxios = new MockAdapter(axios)
export default mockedAxios
+38
View File
@@ -0,0 +1,38 @@
import { Request, Response, NextFunction } from "express"
import {
authorizationMiddleware,
type AuthorizationRequest,
} from "@/middlewares/authorization-middleware"
import { User } from "@/models"
/**
* Usage:
* At the top level of a test file import:
* import { mockCurrentUser } from "@/support"
*
* Then where you want to set the current user:
* mockCurrentUser(currentUser)
*
* @param newCurrentUser - The user to set as the current user
*/
export function mockCurrentUser(newCurrentUser: User) {
vi.mock("@/middlewares/jwt-middleware", () => ({
default: async (_req: Request, _res: Response, next: NextFunction) => next(),
jwtMiddleware: async (_req: Request, _res: Response, next: NextFunction) => next(),
}))
vi.mock("@/middlewares/authorization-middleware")
const authorizationMiddlewareMock = vi.mocked(authorizationMiddleware)
authorizationMiddlewareMock.mockImplementation(
async (req: AuthorizationRequest, _res: Response, next: NextFunction) => {
const currentUser = await User.withScope(["asCurrentUser"]).findByPk(newCurrentUser.id, {
rejectOnEmpty: true,
})
req.currentUser = currentUser
next()
}
)
}
+10
View File
@@ -0,0 +1,10 @@
import supertest, { AgentOptions } from "supertest"
import { App } from "supertest/types"
import defaultApp from "@/app"
export function request(options?: AgentOptions | undefined, app: App = defaultApp) {
return supertest(app, options)
}
export default request
@@ -0,0 +1,19 @@
import logger from "@/utils/logger"
function setLogLevel(level: string) {
logger.level = level
}
export const testWithCustomLogLevel = test.extend<{
setLogLevel: (level: string) => void
}>({
// eslint-disable-next-line no-empty-pattern
setLogLevel: async ({}, use) => {
const originalLogLevel = logger.level
try {
await use(setLogLevel)
} finally {
setLogLevel(originalLogLevel)
}
},
})