chore: Cleanup medusa test utils (#7464)

* chore: Cleanup medusa test utils

* update yarn

* cleanup

* fix key

* cleanup

* fixes

* fixes

* fixes

* fixes

* fix runners

* fix bootstrap

* naming

* Improve runners options

* Improve runners options

* Improve runners options

* cleanup

* fix tests
This commit is contained in:
Adrien de Peretti
2024-05-27 14:14:32 +02:00
committed by GitHub
parent ab2e8fcd45
commit e275e01d85
19 changed files with 172 additions and 412 deletions

View File

@@ -12,7 +12,6 @@ const adminHeaders = {
jest.setTimeout(30000)
medusaIntegrationTestRunner({
force_modules_migration: true,
env: {
MEDUSA_FF_MEDUSA_V2: true,
},

View File

@@ -5,14 +5,13 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils/dist"
jest.setTimeout(5000000)
medusaIntegrationTestRunner({
force_modules_migration: true,
testSuite: ({ dbConnection, getContainer }) => {
testSuite: ({ getContainer, dbConfig: { clientUrl } }) => {
let DB_URL
let container
let links
beforeAll(async () => {
DB_URL = dbConnection.manager.connection.options.url
DB_URL = clientUrl
container = getContainer()
const linkDefinition: ModuleJoinerConfig[] = [

View File

@@ -5,11 +5,10 @@ import { IProductModuleService } from "@medusajs/types"
jest.setTimeout(30000)
medusaIntegrationTestRunner({
force_modules_migration: true,
testSuite: ({ dbConnection }) => {
testSuite: ({ dbConfig: { clientUrl } }) => {
describe("Standalone Modules", () => {
beforeAll(async () => {
process.env.POSTGRES_URL = dbConnection.manager.connection.options.url
process.env.POSTGRES_URL = clientUrl
})
afterAll(async () => {

View File

@@ -36,8 +36,7 @@
"axios": "^0.28.0",
"express": "^4.18.3",
"get-port": "^5.1.0",
"pg-god": "^1.0.12",
"typeorm": "^0.2.43"
"pg-god": "^1.0.12"
},
"peerDependenciesMeta": {
"@medusajs/medusa": {
@@ -54,9 +53,6 @@
},
"pg-god": {
"optional": true
},
"typeorm": {
"optional": true
}
},
"dependencies": {

View File

@@ -2,8 +2,6 @@ export * as TestDatabaseUtils from "./database"
export * as TestEventUtils from "./events"
export * as JestUtils from "./jest"
export { default as IdMap } from "./id-map"
export { default as MockManager } from "./mock-manager"
export { default as MockRepository } from "./mock-repository"
export * from "./init-modules"
export { default as MockEventBusService } from "./mock-event-bus-service"
export * from "./module-test-runner"

View File

@@ -58,6 +58,8 @@ export async function initModules({
async function shutdown() {
if (shouldDestroyConnectionAutomatically) {
await medusaApp.onApplicationPrepareShutdown()
await promiseAll([
(sharedPgConnection as any).context?.destroy(),
(sharedPgConnection as any).destroy(),

View File

@@ -1,81 +0,0 @@
const path = require("path")
const express = require("express")
const getPort = require("get-port")
const { isObject, promiseAll } = require("@medusajs/utils")
// TODO: fix that once we find the appropriate place to put this util
const {
GracefulShutdownServer,
} = require("@medusajs/medusa/dist/utils/graceful-shutdown-server")
async function bootstrapApp({ cwd, env = {} } = {}) {
const app = express()
if (isObject(env)) {
Object.entries(env).forEach(([k, v]) => (process.env[k] = v))
}
const loaders = require("@medusajs/medusa/dist/loaders").default
const { container, shutdown } = await loaders({
directory: path.resolve(cwd || process.cwd()),
expressApp: app,
})
const PORT = await getPort()
return {
shutdown,
container,
app,
port: PORT,
}
}
module.exports = {
startBootstrapApp: async ({
cwd,
env = {},
skipExpressListen = false,
} = {}) => {
const {
app,
port,
container,
shutdown: medusaShutdown,
} = await bootstrapApp({
cwd,
env,
})
let expressServer
if (skipExpressListen) {
return
}
const shutdown = async () => {
await promiseAll([expressServer.shutdown(), medusaShutdown()])
if (typeof global !== "undefined" && global?.gc) {
global.gc()
}
}
return await new Promise((resolve, reject) => {
const server = app.listen(port, async (err) => {
if (err) {
await shutdown()
return reject(err)
}
process.send(port)
resolve({
shutdown,
container,
port,
})
})
expressServer = GracefulShutdownServer.create(server)
})
},
}

View File

@@ -0,0 +1,83 @@
import express from "express"
import getPort from "get-port"
import { resolve } from "path"
import { isObject, promiseAll } from "@medusajs/utils"
import { MedusaContainer } from "@medusajs/types"
async function bootstrapApp({
cwd,
env = {},
}: { cwd?: string; env?: Record<any, any> } = {}) {
const app = express()
if (isObject(env)) {
Object.entries(env).forEach(([k, v]) => (process.env[k] = v))
}
const loaders = require("@medusajs/medusa/dist/loaders").default
const { container, shutdown } = await loaders({
directory: resolve(cwd || process.cwd()),
expressApp: app,
})
const PORT = await getPort()
return {
shutdown,
container,
app,
port: PORT,
}
}
export async function startApp({
cwd,
env = {},
}: { cwd?: string; env?: Record<any, any> } = {}): Promise<{
shutdown: () => Promise<void>
container: MedusaContainer
port: number
}> {
const {
app,
port,
container,
shutdown: medusaShutdown,
} = await bootstrapApp({
cwd,
env,
})
let expressServer
const shutdown = async () => {
await promiseAll([expressServer.shutdown(), medusaShutdown()])
if (typeof global !== "undefined" && global?.gc) {
global.gc()
}
}
return await new Promise((resolve, reject) => {
const server = app.listen(port).on("error", async (err) => {
await shutdown()
return reject(err)
})
process.send?.(port)
resolve({
shutdown,
container,
port,
})
// TODO: fix that once we find the appropriate place to put this util
const {
GracefulShutdownServer,
} = require("@medusajs/medusa/dist/utils/graceful-shutdown-server")
expressServer = GracefulShutdownServer.create(server)
})
}

View File

@@ -1,101 +0,0 @@
const path = require("path")
const { asValue } = require("awilix")
const {
isObject,
createMedusaContainer,
MedusaV2Flag,
} = require("@medusajs/utils")
const { DataSource } = require("typeorm")
const { ContainerRegistrationKeys } = require("@medusajs/utils")
const { logger } = require("@medusajs/medusa-cli/dist/reporter")
module.exports = {
initDb: async function ({
cwd,
// use for v1 datasource only
database_extra,
env,
force_modules_migration,
dbUrl = "",
dbSchema = "public",
}) {
if (isObject(env)) {
Object.entries(env).forEach(([k, v]) => (process.env[k] = v))
}
const configModuleLoader =
require("@medusajs/medusa/dist/loaders/config").default
const configModule = configModuleLoader(cwd)
const featureFlagsLoader =
require("@medusajs/medusa/dist/loaders/feature-flags").default
const featureFlagRouter = featureFlagsLoader(configModule)
const {
getModuleSharedResources,
} = require("@medusajs/medusa/dist/commands/utils/get-migrations")
const { migrations: moduleMigrations, models: moduleModels } =
getModuleSharedResources(configModule, featureFlagRouter)
const dbDataSource = new DataSource({
type: "postgres",
url: dbUrl || configModule.projectConfig.database_url,
entities: moduleModels,
migrations: moduleMigrations,
extra: database_extra ?? {},
//name: "integration-tests",
schema: dbSchema,
})
await dbDataSource.initialize()
await dbDataSource.runMigrations()
if (
force_modules_migration ||
featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)
) {
const pgConnectionLoader =
require("@medusajs/medusa/dist/loaders/pg-connection").default
const featureFlagLoader =
require("@medusajs/medusa/dist/loaders/feature-flags").default
const container = createMedusaContainer()
const featureFlagRouter = await featureFlagLoader(configModule)
const pgConnection = await pgConnectionLoader({
configModule: {
...configModule,
projectConfig: {
...configModule.projectConfig,
database_url: dbUrl || configModule.projectConfig.database_url,
},
},
container,
})
container.register({
[ContainerRegistrationKeys.CONFIG_MODULE]: asValue(configModule),
[ContainerRegistrationKeys.LOGGER]: asValue(logger),
[ContainerRegistrationKeys.MANAGER]: asValue(dbDataSource.manager),
[ContainerRegistrationKeys.PG_CONNECTION]: asValue(pgConnection),
featureFlagRouter: asValue(featureFlagRouter),
})
const {
migrateMedusaApp,
} = require("@medusajs/medusa/dist/loaders/medusa-app")
await migrateMedusaApp(
{ configModule, container },
{ registerInContainer: false }
)
}
return { dbDataSource, pgConnection }
},
}

View File

@@ -0,0 +1,51 @@
import {
ContainerRegistrationKeys,
createMedusaContainer,
isObject,
} from "@medusajs/utils"
import { asValue } from "awilix"
export async function initDb({
cwd,
env = {},
}: {
cwd: string
env?: Record<any, any>
}) {
if (isObject(env)) {
Object.entries(env).forEach(([k, v]) => (process.env[k] = v))
}
const container = createMedusaContainer()
const configModule =
await require("@medusajs/medusa/dist/loaders/config").default(cwd)
const pgConnection =
await require("@medusajs/medusa/dist/loaders/pg-connection").default({
configModule,
container,
})
const featureFlagRouter =
require("@medusajs/medusa/dist/loaders/feature-flags").default(configModule)
container.register({
[ContainerRegistrationKeys.CONFIG_MODULE]: asValue(configModule),
[ContainerRegistrationKeys.LOGGER]: asValue(console),
[ContainerRegistrationKeys.PG_CONNECTION]: asValue(pgConnection),
[ContainerRegistrationKeys.FEATURE_FLAG_ROUTER]: asValue(featureFlagRouter),
})
try {
const {
migrateMedusaApp,
} = require("@medusajs/medusa/dist/loaders/medusa-app")
await migrateMedusaApp({ configModule, container })
} catch (err) {
console.error("Something went wrong while running the migrations")
throw err
}
return pgConnection
}

View File

@@ -1,8 +1,11 @@
import { ContainerLike, MedusaContainer } from "@medusajs/types"
import { createMedusaContainer } from "@medusajs/utils"
import {
ContainerRegistrationKeys,
createMedusaContainer,
} from "@medusajs/utils"
import { createDatabase, dropDatabase } from "pg-god"
import { getDatabaseURL } from "./database"
import { startBootstrapApp } from "./medusa-test-runner-utils/bootstrap-app"
import { startApp } from "./medusa-test-runner-utils/bootstrap-app"
import { initDb } from "./medusa-test-runner-utils/use-db"
const axios = require("axios").default
@@ -18,45 +21,35 @@ const pgGodCredentials = {
}
const dbTestUtilFactory = (): any => ({
db_: null,
pgConnection_: null,
clear: async function () {
this.db_?.synchronize(true)
},
create: async function (dbName: string) {
await createDatabase({ databaseName: dbName }, pgGodCredentials)
},
teardown: async function ({
forceDelete,
schema,
}: { forceDelete?: string[]; schema?: string } = {}) {
forceDelete ??= []
if (!this.db_) {
teardown: async function ({ schema }: { schema?: string } = {}) {
if (!this.pgConnection_) {
return
}
const manager = this.db_.manager
const runRawQuery = this.pgConnection_.raw.bind(this.pgConnection_)
schema ??= "public"
await manager.query(`SET session_replication_role = 'replica';`)
const tableNames = await manager.query(`SELECT table_name
await runRawQuery(`SET session_replication_role = 'replica';`)
const { rows: tableNames } = await runRawQuery(`SELECT table_name
FROM information_schema.tables
WHERE table_schema = '${schema}';`)
for (const { table_name } of tableNames) {
await manager.query(`DELETE
await runRawQuery(`DELETE
FROM ${schema}."${table_name}";`)
}
await manager.query(`SET session_replication_role = 'origin';`)
await runRawQuery(`SET session_replication_role = 'origin';`)
},
shutdown: async function (dbName: string) {
await this.db_?.destroy()
await this.pgConnection_?.context?.destroy()
await this.pgConnection_?.destroy()
@@ -68,8 +61,7 @@ const dbTestUtilFactory = (): any => ({
})
export interface MedusaSuiteOptions<TService = unknown> {
dbUtils: any
dbConnection: any // Legacy typeorm connection
dbConnection: any // knex instance
getContainer: () => MedusaContainer
api: any
dbConfig: {
@@ -84,7 +76,6 @@ export function medusaIntegrationTestRunner({
dbName,
schema = "public",
env = {},
force_modules_migration = false,
debug = false,
testSuite,
}: {
@@ -93,7 +84,6 @@ export function medusaIntegrationTestRunner({
dbName?: string
schema?: string
debug?: boolean
force_modules_migration?: boolean
testSuite: <TService = unknown>(options: MedusaSuiteOptions<TService>) => void
}) {
const tempName = parseInt(process.env.JEST_WORKER_ID || "1")
@@ -130,12 +120,11 @@ export function medusaIntegrationTestRunner({
const cwd = process.cwd()
let shutdown = async () => void 0
let dbUtils = dbTestUtilFactory()
const dbUtils = dbTestUtilFactory()
let container: ContainerLike
let apiUtils: any
let options = {
dbUtils,
api: new Proxy(
{},
{
@@ -148,11 +137,16 @@ export function medusaIntegrationTestRunner({
{},
{
get: (target, prop) => {
return dbUtils.db_[prop]
return dbUtils.pgConnection_[prop]
},
}
),
getContainer: () => container,
dbConfig: {
dbName,
schema,
clientUrl: dbConfig.clientUrl,
},
} as MedusaSuiteOptions
let isFirstTime = true
@@ -160,38 +154,26 @@ export function medusaIntegrationTestRunner({
const beforeAll_ = async () => {
await dbUtils.create(dbName)
let dataSourceRes
let pgConnectionRes
try {
const { dbDataSource, pgConnection } = await initDb({
dbUtils.pgConnection_ = await initDb({
cwd,
env,
force_modules_migration,
database_extra: {},
dbUrl: dbConfig.clientUrl,
dbSchema: dbConfig.schema,
})
dataSourceRes = dbDataSource
pgConnectionRes = pgConnection
} catch (error) {
console.error("Error initializing database", error?.message)
throw error
}
dbUtils.db_ = dataSourceRes
dbUtils.pgConnection_ = pgConnectionRes
let containerRes
let serverShutdownRes
let portRes
try {
const {
shutdown = () => void 0,
container,
port,
} = await startBootstrapApp({
} = await startApp({
cwd,
env,
})
@@ -233,7 +215,9 @@ export function medusaIntegrationTestRunner({
require("@medusajs/medusa/dist/loaders/medusa-app").runModulesLoader
await medusaAppLoaderRunner({
container: copiedContainer,
configModule: container.resolve("configModule"),
configModule: container.resolve(
ContainerRegistrationKeys.CONFIG_MODULE
),
})
} catch (error) {
console.error("Error runner modules loaders", error?.message)

View File

@@ -1,31 +0,0 @@
export default {
connection: {
getMetadata: (target) => {
return (
target["metadata"] ?? {
columns: [],
}
)
},
},
getRepository: function (repo) {
return repo
},
withRepository: function (repo) {
if (repo) {
return Object.assign(repo, { manager: this })
}
return repo
},
transaction: function (isolationOrCb, cb) {
if (typeof isolationOrCb === "string") {
return cb(this)
} else {
return isolationOrCb(this)
}
},
}

View File

@@ -1,127 +0,0 @@
class MockRepo {
constructor({
create,
update,
remove,
softRemove,
find,
findDescendantsTree,
findOne,
findOneWithRelations,
findOneOrFail,
save,
findAndCount,
del,
count,
insertBulk,
metadata,
}) {
this.create_ = create
this.update_ = update
this.remove_ = remove
this.delete_ = del
this.softRemove_ = softRemove
this.find_ = find
this.findDescendantsTree_ = findDescendantsTree
this.findOne_ = findOne
this.findOneOrFail_ = findOneOrFail
this.save_ = save
this.findAndCount_ = findAndCount
this.findOneWithRelations_ = findOneWithRelations
this.insertBulk_ = insertBulk
this.count_ = count
this.metadata = metadata ?? {
columns: [],
}
}
setFindOne(fn) {
this.findOne_ = fn
}
insertBulk = jest.fn().mockImplementation((...args) => {
if (this.insertBulk_) {
return this.insertBulk_(...args)
}
return {}
})
create = jest.fn().mockImplementation((...args) => {
if (this.create_) {
return this.create_(...args)
}
return {}
})
softRemove = jest.fn().mockImplementation((...args) => {
if (this.softRemove_) {
return this.softRemove_(...args)
}
return {}
})
remove = jest.fn().mockImplementation((...args) => {
if (this.remove_) {
return this.remove_(...args)
}
return {}
})
update = jest.fn().mockImplementation((...args) => {
if (this.update_) {
return this.update_(...args)
}
})
findOneOrFail = jest.fn().mockImplementation((...args) => {
if (this.findOneOrFail_) {
return this.findOneOrFail_(...args)
}
})
findOneWithRelations = jest.fn().mockImplementation((...args) => {
if (this.findOneWithRelations_) {
return this.findOneWithRelations_(...args)
}
})
findOne = jest.fn().mockImplementation((...args) => {
if (this.findOne_) {
return this.findOne_(...args)
}
})
findDescendantsTree = jest.fn().mockImplementation((...args) => {
if (this.findDescendantsTree_) {
return this.findDescendantsTree_(...args)
}
})
find = jest.fn().mockImplementation((...args) => {
if (this.find_) {
return this.find_(...args)
}
})
save = jest.fn().mockImplementation((...args) => {
if (this.save_) {
return this.save_(...args)
}
return Promise.resolve(...args)
})
findAndCount = jest.fn().mockImplementation((...args) => {
if (this.findAndCount_) {
return this.findAndCount_(...args)
}
return {}
})
count = jest.fn().mockImplementation((...args) => {
if (this.count_) {
return this.count_(...args)
}
return {}
})
delete = jest.fn().mockImplementation((...args) => {
if (this.delete_) {
return this.delete_(...args)
}
return {}
})
}
export default (methods = {}) => {
return new MockRepo(methods)
}

View File

@@ -66,9 +66,6 @@ export function moduleIntegrationTestRunner({
definition: moduleSdkImports.ModulesDefinition[moduleName],
resolve,
options: {
defaultAdapterOptions: {
database: dbConfig,
},
database: dbConfig,
...moduleOptions,
},
@@ -110,6 +107,10 @@ export function moduleIntegrationTestRunner({
},
}
),
dbConfig: {
schema,
clientUrl: dbConfig.clientUrl,
},
} as SuiteOptions
const beforeEach_ = async () => {

View File

@@ -1,4 +1,3 @@
import { revertIsolatedModulesMigration } from "./utils/get-migrations"
import Logger from "../loaders/logger"
import { migrateMedusaApp, revertMedusaApp } from "../loaders/medusa-app"
import { initializeContainer } from "../loaders"
@@ -22,7 +21,6 @@ const main = async function ({ directory }) {
process.exit()
} else if (args[0] === "revert") {
await revertMedusaApp({ configModule, container })
await revertIsolatedModulesMigration(configModule)
Logger.info("Migrations reverted.")
} else if (args[0] === "show") {

View File

@@ -1,5 +1,4 @@
import { Queue, Worker } from "bullmq"
import { MockManager } from "medusa-test-utils"
import RedisEventBusService from "../event-bus-redis"
jest.genMockFromModule("bullmq")
@@ -15,7 +14,7 @@ const loggerMock = {
const simpleModuleOptions = { redisUrl: "test-url" }
const moduleDeps = {
manager: MockManager,
manager: {},
logger: loggerMock,
eventBusRedisConnection: {},
}

View File

@@ -4,12 +4,6 @@ import { DB_URL } from "./database"
export function getInitModuleConfig() {
const moduleOptions = {
defaultAdapterOptions: {
database: {
clientUrl: DB_URL,
schema: process.env.MEDUSA_PAYMENT_DB_SCHEMA,
},
},
providers: [
{
resolve: "@medusajs/payment-stripe",

View File

@@ -23712,7 +23712,6 @@ __metadata:
express: ^4.18.3
get-port: ^5.1.0
pg-god: ^1.0.12
typeorm: ^0.2.43
peerDependenciesMeta:
"@medusajs/medusa":
optional: true
@@ -23724,8 +23723,6 @@ __metadata:
optional: true
pg-god:
optional: true
typeorm:
optional: true
languageName: unknown
linkType: soft