chore(medusa-test-utils):Handle errors gracefully (#6901)

**What**
- Better error handling and error message
- update deps management and dynamic import/require
- Pass a new flag to the modules loaders for the module loaders to be able to act depending on it. In that case, the module can determine what should be run or not. e.g in the workflow engine redis, when we are only partially loading the module, we do not want to set the Distributed transaction storage
This commit is contained in:
Adrien de Peretti authored and GitHub committed 2024-04-02 14:10:17 +00:00
1 parent 7895ff3849
commit 82a176e30e
13 files changed
+112 -49

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
---
"@medusajs/medusa": patch
"medusa-test-utils": patch
"@medusajs/modules-sdk": patch
"@medusajs/types": patch
"@medusajs/workflow-engine-redis": patch
---
chore(medusa-test-utils): Handle errors gracefully + Do not set Distributed storage on partial loading
+9 -13
View File
@@ -14,8 +14,7 @@ async function bootstrapApp({ cwd, env = {} } = {}) {
const loaders = require("@medusajs/medusa/dist/loaders").default const loaders = require("@medusajs/medusa/dist/loaders").default
const { container, dbConnection, pgConnection, disposeResources } = const { container, shutdown } = await loaders({
await loaders({
directory: path.resolve(cwd || process.cwd()), directory: path.resolve(cwd || process.cwd()),
expressApp: app, expressApp: app,
isTest: false, isTest: false,
@@ -24,10 +23,8 @@ async function bootstrapApp({ cwd, env = {} } = {}) {
const PORT = await getPort() const PORT = await getPort()
return { return {
disposeResources, shutdown,
container, container,
db: dbConnection,
pgConnection,
app, app,
port: PORT, port: PORT,
} }
@@ -40,7 +37,12 @@ module.exports = {
env = {}, env = {},
skipExpressListen = false, skipExpressListen = false,
} = {}) => { } = {}) => {
const { app, port, container, db, pgConnection } = await bootstrapApp({ const {
app,
port,
container,
shutdown: medusaShutdown,
} = await bootstrapApp({
cwd, cwd,
env, env,
}) })
@@ -53,13 +55,7 @@ module.exports = {
} }
const shutdown = async () => { const shutdown = async () => {
await Promise.all([ await Promise.all([expressServer.close(), medusaShutdown()])
container.dispose(),
expressServer.close(),
db?.destroy(),
pgConnection?.context?.destroy(),
container.dispose(),
])
if (typeof global !== "undefined" && global?.gc) { if (typeof global !== "undefined" && global?.gc) {
global.gc() global.gc()
+1 -1
View File
@@ -32,6 +32,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@medusajs/medusa": ">1.19", "@medusajs/medusa": ">1.19",
"@medusajs/modules-sdk": "^1.12.10",
"axios": "^0.28.0", "axios": "^0.28.0",
"express": "^4.18.3", "express": "^4.18.3",
"get-port": "^5.1.0", "get-port": "^5.1.0",
@@ -59,7 +60,6 @@
} }
}, },
"dependencies": { "dependencies": {
"@medusajs/modules-sdk": "^1.12.10",
"@medusajs/utils": "^1.11.8", "@medusajs/utils": "^1.11.8",
"@mikro-orm/migrations": "5.9.7", "@mikro-orm/migrations": "5.9.7",
"@mikro-orm/postgresql": "5.9.7", "@mikro-orm/postgresql": "5.9.7",
+13 -7
View File
@@ -1,9 +1,8 @@
import { import {
MedusaApp, ExternalModuleDeclaration,
MedusaModule, InternalModuleDeclaration,
MedusaModuleConfig,
ModuleJoinerConfig, ModuleJoinerConfig,
} from "@medusajs/modules-sdk" } from "@medusajs/types"
import { import {
ContainerRegistrationKeys, ContainerRegistrationKeys,
ModulesSdkUtils, ModulesSdkUtils,
@@ -16,7 +15,12 @@ export interface InitModulesOptions {
clientUrl: string clientUrl: string
schema?: string schema?: string
} }
modulesConfig: MedusaModuleConfig modulesConfig: {
[key: string]:
| string
| boolean
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
}
joinerConfig?: ModuleJoinerConfig[] joinerConfig?: ModuleJoinerConfig[]
preventConnectionDestroyWarning?: boolean preventConnectionDestroyWarning?: boolean
} }
@@ -28,6 +32,8 @@ export async function initModules({
joinerConfig, joinerConfig,
preventConnectionDestroyWarning = false, preventConnectionDestroyWarning = false,
}: InitModulesOptions) { }: InitModulesOptions) {
const moduleSdkImports = require("@medusajs/modules-sdk")
injectedDependencies ??= {} injectedDependencies ??= {}
let sharedPgConnection = let sharedPgConnection =
@@ -44,7 +50,7 @@ export async function initModules({
sharedPgConnection sharedPgConnection
} }
const medusaApp = await MedusaApp({ const medusaApp = await moduleSdkImports.MedusaApp({
modulesConfig, modulesConfig,
servicesConfig: joinerConfig, servicesConfig: joinerConfig,
injectedDependencies, injectedDependencies,
@@ -64,7 +70,7 @@ export async function initModules({
) )
} }
} }
MedusaModule.clearInstances() moduleSdkImports.MedusaModule.clearInstances()
} }
return { return {
@@ -34,6 +34,10 @@ const dbTestUtilFactory = (): any => ({
schema, schema,
}: { forceDelete?: string[]; schema?: string } = {}) { }: { forceDelete?: string[]; schema?: string } = {}) {
forceDelete ??= [] forceDelete ??= []
if (!this.db_) {
return
}
const manager = this.db_.manager const manager = this.db_.manager
schema ??= "public" schema ??= "public"
@@ -90,9 +94,7 @@ export function medusaIntegrationTestRunner({
schema?: string schema?: string
debug?: boolean debug?: boolean
force_modules_migration?: boolean force_modules_migration?: boolean
testSuite: <TService = unknown>( testSuite: <TService = unknown>(options: MedusaSuiteOptions<TService>) => void
options: MedusaSuiteOptions<TService>
) => () => void
}) { }) {
const tempName = parseInt(process.env.JEST_WORKER_ID || "1") const tempName = parseInt(process.env.JEST_WORKER_ID || "1")
moduleName = moduleName ?? Math.random().toString(36).substring(7) moduleName = moduleName ?? Math.random().toString(36).substring(7)
@@ -147,6 +149,11 @@ export function medusaIntegrationTestRunner({
const beforeAll_ = async () => { const beforeAll_ = async () => {
await dbUtils.create(dbName) await dbUtils.create(dbName)
let dataSourceRes
let pgConnectionRes
try {
const { dbDataSource, pgConnection } = await initDb({ const { dbDataSource, pgConnection } = await initDb({
cwd, cwd,
env, env,
@@ -155,29 +162,50 @@ export function medusaIntegrationTestRunner({
dbUrl: dbConfig.clientUrl, dbUrl: dbConfig.clientUrl,
dbSchema: dbConfig.schema, dbSchema: dbConfig.schema,
}) })
dbUtils.db_ = dbDataSource
dbUtils.pgConnection_ = pgConnection
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 { const {
shutdown: serverShutdown, shutdown = () => void 0,
container: container_, container,
port, port,
} = await startBootstrapApp({ } = await startBootstrapApp({
cwd, cwd,
env, env,
}) })
const cancelTokenSource = axios.CancelToken.source() containerRes = container
apiUtils = axios.create({ serverShutdownRes = shutdown
baseURL: `http://localhost:${port}`, portRes = port
cancelToken: cancelTokenSource.token, } catch (error) {
}) console.error("Error starting the app", error?.message)
throw error
}
container = container_ const cancelTokenSource = axios.CancelToken.source()
container = containerRes
shutdown = async () => { shutdown = async () => {
await serverShutdown() await serverShutdownRes()
cancelTokenSource.cancel("Request canceled by shutdown") cancelTokenSource.cancel("Request canceled by shutdown")
} }
apiUtils = axios.create({
baseURL: `http://localhost:${portRes}`,
cancelToken: cancelTokenSource.token,
})
} }
const beforeEach_ = async () => { const beforeEach_ = async () => {
@@ -191,26 +219,37 @@ export function medusaIntegrationTestRunner({
const copiedContainer = createMedusaContainer({}, container) const copiedContainer = createMedusaContainer({}, container)
if (process.env.MEDUSA_FF_MEDUSA_V2 != "true") { if (process.env.MEDUSA_FF_MEDUSA_V2 != "true") {
try {
const defaultLoader = const defaultLoader =
require("@medusajs/medusa/dist/loaders/defaults").default require("@medusajs/medusa/dist/loaders/defaults").default
await defaultLoader({ await defaultLoader({
container: copiedContainer, container: copiedContainer,
}) })
} catch (error) {
console.error("Error runner medusa loaders", error?.message)
throw error
}
} }
try {
const medusaAppLoaderRunner = const medusaAppLoaderRunner =
require("@medusajs/medusa/dist/loaders/medusa-app").runModulesLoader require("@medusajs/medusa/dist/loaders/medusa-app").runModulesLoader
await medusaAppLoaderRunner({ await medusaAppLoaderRunner({
container: copiedContainer, container: copiedContainer,
configModule: container.resolve("configModule"), configModule: container.resolve("configModule"),
}) })
} catch (error) {
console.error("Error runner modules loaders", error?.message)
throw error
}
} }
const afterEach_ = async () => { const afterEach_ = async () => {
try { try {
await dbUtils.teardown({ schema }) await dbUtils.teardown({ schema })
} catch (error) { } catch (error) {
console.error("Error tearing down database:", error) console.error("Error tearing down database:", error?.message)
throw error
} }
} }
@@ -1,13 +1,12 @@
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils" import { initModules, InitModulesOptions } from "./init-modules"
import { InitModulesOptions, initModules } from "./init-modules" import { getDatabaseURL, getMikroOrmWrapper, TestDatabase } from "./database"
import { MedusaAppOutput, ModulesDefinition } from "@medusajs/modules-sdk"
import { TestDatabase, getDatabaseURL, getMikroOrmWrapper } from "./database"
import { MockEventBusService } from "." import { MockEventBusService } from "."
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
export interface SuiteOptions<TService = unknown> { export interface SuiteOptions<TService = unknown> {
MikroOrmWrapper: TestDatabase MikroOrmWrapper: TestDatabase
medusaApp: MedusaAppOutput medusaApp: any
service: TService service: TService
dbConfig: { dbConfig: {
schema: string schema: string
@@ -37,6 +36,8 @@ export function moduleIntegrationTestRunner({
debug?: boolean debug?: boolean
testSuite: <TService = unknown>(options: SuiteOptions<TService>) => () => void testSuite: <TService = unknown>(options: SuiteOptions<TService>) => () => void
}) { }) {
const moduleSdkImports = require("@medusajs/modules-sdk")
process.env.LOG_LEVEL = "error" process.env.LOG_LEVEL = "error"
moduleModels ??= Object.values(require(`${process.cwd()}/src/models`)) moduleModels ??= Object.values(require(`${process.cwd()}/src/models`))
@@ -62,7 +63,7 @@ export function moduleIntegrationTestRunner({
const modulesConfig_ = { const modulesConfig_ = {
[moduleName]: { [moduleName]: {
definition: ModulesDefinition[moduleName], definition: moduleSdkImports.ModulesDefinition[moduleName],
resolve, resolve,
options: { options: {
defaultAdapterOptions: { defaultAdapterOptions: {
@@ -89,7 +90,7 @@ export function moduleIntegrationTestRunner({
let shutdown: () => Promise<void> let shutdown: () => Promise<void>
let moduleService let moduleService
let medusaApp: MedusaAppOutput = {} as MedusaAppOutput let medusaApp = {}
const options = { const options = {
MikroOrmWrapper, MikroOrmWrapper,
@@ -100,7 +101,7 @@ export function moduleIntegrationTestRunner({
return medusaApp[prop] return medusaApp[prop]
}, },
} }
) as MedusaAppOutput, ),
service: new Proxy( service: new Proxy(
{}, {},
{ {
@@ -123,7 +124,7 @@ export function moduleIntegrationTestRunner({
await MikroOrmWrapper.clearDatabase() await MikroOrmWrapper.clearDatabase()
await shutdown() await shutdown()
moduleService = {} moduleService = {}
medusaApp = {} as MedusaAppOutput medusaApp = {}
} }
return describe("", () => { return describe("", () => {
@@ -108,6 +108,7 @@ export async function loadInternalModule(
container: localContainer, container: localContainer,
logger, logger,
options: resolution.options, options: resolution.options,
dataLoaderOnly: loaderOnly,
}, },
resolution.moduleDeclaration as InternalModuleDeclaration resolution.moduleDeclaration as InternalModuleDeclaration
) )
+3 -1
View File
@@ -67,7 +67,9 @@ export type ModuleBootstrapOptions = {
*/ */
migrationOnly?: boolean migrationOnly?: boolean
/** /**
* Forces the modules bootstrapper to only run the modules loaders and return prematurely * Forces the modules bootstrapper to only run the modules loaders and return prematurely. This
* is meant for modules that have data loader. In a test env, in order to clear all data
* and load them back, we need to run those loader again
*/ */
loaderOnly?: boolean loaderOnly?: boolean
workerMode?: "shared" | "worker" | "server" workerMode?: "shared" | "worker" | "server"
+1
View File
@@ -118,6 +118,7 @@ export type LoaderOptions<TOptions = Record<string, unknown>> = {
container: MedusaContainer container: MedusaContainer
options?: TOptions options?: TOptions
logger?: Logger logger?: Logger
dataLoaderOnly?: boolean
} }
export type ModuleLoaderFunction = ( export type ModuleLoaderFunction = (
@@ -7,6 +7,7 @@ export default async ({
container, container,
logger, logger,
options, options,
dataLoaderOnly
}: LoaderOptions): Promise<void> => { }: LoaderOptions): Promise<void> => {
const { const {
url, url,
@@ -58,6 +59,7 @@ export default async ({
} }
container.register({ container.register({
partialLoading: asValue(true),
redisConnection: asValue(connection), redisConnection: asValue(connection),
redisWorkerConnection: asValue(workerConnection), redisWorkerConnection: asValue(workerConnection),
redisPublisher: asValue(redisPublisher), redisPublisher: asValue(redisPublisher),
@@ -1,10 +1,11 @@
import { asClass } from "awilix" import { asClass, asValue } from "awilix"
import { RedisDistributedTransactionStorage } from "../utils" import { RedisDistributedTransactionStorage } from "../utils"
export default async ({ container }): Promise<void> => { export default async ({ container, dataLoaderOnly }): Promise<void> => {
container.register({ container.register({
redisDistributedTransactionStorage: asClass( redisDistributedTransactionStorage: asClass(
RedisDistributedTransactionStorage RedisDistributedTransactionStorage
).singleton(), ).singleton(),
dataLoaderOnly: asValue(!!dataLoaderOnly),
}) })
} }
@@ -79,10 +79,12 @@ export class WorkflowOrchestratorService {
protected redisDistributedTransactionStorage_: RedisDistributedTransactionStorage protected redisDistributedTransactionStorage_: RedisDistributedTransactionStorage
constructor({ constructor({
dataLoaderOnly,
redisDistributedTransactionStorage, redisDistributedTransactionStorage,
redisPublisher, redisPublisher,
redisSubscriber, redisSubscriber,
}: { }: {
dataLoaderOnly: boolean
redisDistributedTransactionStorage: RedisDistributedTransactionStorage redisDistributedTransactionStorage: RedisDistributedTransactionStorage
workflowOrchestratorService: WorkflowOrchestratorService workflowOrchestratorService: WorkflowOrchestratorService
redisPublisher: Redis redisPublisher: Redis
@@ -92,7 +94,10 @@ export class WorkflowOrchestratorService {
this.redisSubscriber = redisSubscriber this.redisSubscriber = redisSubscriber
redisDistributedTransactionStorage.setWorkflowOrchestratorService(this) redisDistributedTransactionStorage.setWorkflowOrchestratorService(this)
if (!dataLoaderOnly) {
DistributedTransaction.setStorage(redisDistributedTransactionStorage) DistributedTransaction.setStorage(redisDistributedTransactionStorage)
}
this.redisDistributedTransactionStorage_ = this.redisDistributedTransactionStorage_ =
redisDistributedTransactionStorage redisDistributedTransactionStorage
+1 -1
View File
@@ -38528,7 +38528,6 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "medusa-test-utils@workspace:packages/medusa-test-utils" resolution: "medusa-test-utils@workspace:packages/medusa-test-utils"
dependencies: dependencies:
"@medusajs/modules-sdk": ^1.12.10
"@medusajs/types": ^1.11.15 "@medusajs/types": ^1.11.15
"@medusajs/utils": ^1.11.8 "@medusajs/utils": ^1.11.8
"@mikro-orm/migrations": 5.9.7 "@mikro-orm/migrations": 5.9.7
@@ -38541,6 +38540,7 @@ __metadata:
typescript: ^5.1.6 typescript: ^5.1.6
peerDependencies: peerDependencies:
"@medusajs/medusa": ">1.19" "@medusajs/medusa": ">1.19"
"@medusajs/modules-sdk": ^1.12.10
axios: ^0.28.0 axios: ^0.28.0
express: ^4.18.3 express: ^4.18.3
get-port: ^5.1.0 get-port: ^5.1.0