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
2024-04-02 16:10:17 +02:00
committed by GitHub
parent 7895ff3849
commit 82a176e30e
13 changed files with 141 additions and 78 deletions

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

View File

@@ -14,20 +14,17 @@ async function bootstrapApp({ cwd, env = {} } = {}) {
const loaders = require("@medusajs/medusa/dist/loaders").default
const { container, dbConnection, pgConnection, disposeResources } =
await loaders({
directory: path.resolve(cwd || process.cwd()),
expressApp: app,
isTest: false,
})
const { container, shutdown } = await loaders({
directory: path.resolve(cwd || process.cwd()),
expressApp: app,
isTest: false,
})
const PORT = await getPort()
return {
disposeResources,
shutdown,
container,
db: dbConnection,
pgConnection,
app,
port: PORT,
}
@@ -40,7 +37,12 @@ module.exports = {
env = {},
skipExpressListen = false,
} = {}) => {
const { app, port, container, db, pgConnection } = await bootstrapApp({
const {
app,
port,
container,
shutdown: medusaShutdown,
} = await bootstrapApp({
cwd,
env,
})
@@ -53,13 +55,7 @@ module.exports = {
}
const shutdown = async () => {
await Promise.all([
container.dispose(),
expressServer.close(),
db?.destroy(),
pgConnection?.context?.destroy(),
container.dispose(),
])
await Promise.all([expressServer.close(), medusaShutdown()])
if (typeof global !== "undefined" && global?.gc) {
global.gc()

View File

@@ -32,6 +32,7 @@
},
"peerDependencies": {
"@medusajs/medusa": ">1.19",
"@medusajs/modules-sdk": "^1.12.10",
"axios": "^0.28.0",
"express": "^4.18.3",
"get-port": "^5.1.0",
@@ -59,7 +60,6 @@
}
},
"dependencies": {
"@medusajs/modules-sdk": "^1.12.10",
"@medusajs/utils": "^1.11.8",
"@mikro-orm/migrations": "5.9.7",
"@mikro-orm/postgresql": "5.9.7",

View File

@@ -1,9 +1,8 @@
import {
MedusaApp,
MedusaModule,
MedusaModuleConfig,
ExternalModuleDeclaration,
InternalModuleDeclaration,
ModuleJoinerConfig,
} from "@medusajs/modules-sdk"
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
ModulesSdkUtils,
@@ -16,7 +15,12 @@ export interface InitModulesOptions {
clientUrl: string
schema?: string
}
modulesConfig: MedusaModuleConfig
modulesConfig: {
[key: string]:
| string
| boolean
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
}
joinerConfig?: ModuleJoinerConfig[]
preventConnectionDestroyWarning?: boolean
}
@@ -28,6 +32,8 @@ export async function initModules({
joinerConfig,
preventConnectionDestroyWarning = false,
}: InitModulesOptions) {
const moduleSdkImports = require("@medusajs/modules-sdk")
injectedDependencies ??= {}
let sharedPgConnection =
@@ -44,7 +50,7 @@ export async function initModules({
sharedPgConnection
}
const medusaApp = await MedusaApp({
const medusaApp = await moduleSdkImports.MedusaApp({
modulesConfig,
servicesConfig: joinerConfig,
injectedDependencies,
@@ -64,7 +70,7 @@ export async function initModules({
)
}
}
MedusaModule.clearInstances()
moduleSdkImports.MedusaModule.clearInstances()
}
return {

View File

@@ -34,6 +34,10 @@ const dbTestUtilFactory = (): any => ({
schema,
}: { forceDelete?: string[]; schema?: string } = {}) {
forceDelete ??= []
if (!this.db_) {
return
}
const manager = this.db_.manager
schema ??= "public"
@@ -90,9 +94,7 @@ export function medusaIntegrationTestRunner({
schema?: string
debug?: boolean
force_modules_migration?: boolean
testSuite: <TService = unknown>(
options: MedusaSuiteOptions<TService>
) => () => void
testSuite: <TService = unknown>(options: MedusaSuiteOptions<TService>) => void
}) {
const tempName = parseInt(process.env.JEST_WORKER_ID || "1")
moduleName = moduleName ?? Math.random().toString(36).substring(7)
@@ -147,37 +149,63 @@ export function medusaIntegrationTestRunner({
const beforeAll_ = async () => {
await dbUtils.create(dbName)
const { dbDataSource, pgConnection } = await initDb({
cwd,
env,
force_modules_migration,
database_extra: {},
dbUrl: dbConfig.clientUrl,
dbSchema: dbConfig.schema,
})
dbUtils.db_ = dbDataSource
dbUtils.pgConnection_ = pgConnection
const {
shutdown: serverShutdown,
container: container_,
port,
} = await startBootstrapApp({
cwd,
env,
})
let dataSourceRes
let pgConnectionRes
try {
const { dbDataSource, 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({
cwd,
env,
})
containerRes = container
serverShutdownRes = shutdown
portRes = port
} catch (error) {
console.error("Error starting the app", error?.message)
throw error
}
const cancelTokenSource = axios.CancelToken.source()
apiUtils = axios.create({
baseURL: `http://localhost:${port}`,
cancelToken: cancelTokenSource.token,
})
container = container_
container = containerRes
shutdown = async () => {
await serverShutdown()
await serverShutdownRes()
cancelTokenSource.cancel("Request canceled by shutdown")
}
apiUtils = axios.create({
baseURL: `http://localhost:${portRes}`,
cancelToken: cancelTokenSource.token,
})
}
const beforeEach_ = async () => {
@@ -191,26 +219,37 @@ export function medusaIntegrationTestRunner({
const copiedContainer = createMedusaContainer({}, container)
if (process.env.MEDUSA_FF_MEDUSA_V2 != "true") {
const defaultLoader =
require("@medusajs/medusa/dist/loaders/defaults").default
await defaultLoader({
container: copiedContainer,
})
try {
const defaultLoader =
require("@medusajs/medusa/dist/loaders/defaults").default
await defaultLoader({
container: copiedContainer,
})
} catch (error) {
console.error("Error runner medusa loaders", error?.message)
throw error
}
}
const medusaAppLoaderRunner =
require("@medusajs/medusa/dist/loaders/medusa-app").runModulesLoader
await medusaAppLoaderRunner({
container: copiedContainer,
configModule: container.resolve("configModule"),
})
try {
const medusaAppLoaderRunner =
require("@medusajs/medusa/dist/loaders/medusa-app").runModulesLoader
await medusaAppLoaderRunner({
container: copiedContainer,
configModule: container.resolve("configModule"),
})
} catch (error) {
console.error("Error runner modules loaders", error?.message)
throw error
}
}
const afterEach_ = async () => {
try {
await dbUtils.teardown({ schema })
} catch (error) {
console.error("Error tearing down database:", error)
console.error("Error tearing down database:", error?.message)
throw error
}
}

View File

@@ -1,13 +1,12 @@
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
import { InitModulesOptions, initModules } from "./init-modules"
import { MedusaAppOutput, ModulesDefinition } from "@medusajs/modules-sdk"
import { TestDatabase, getDatabaseURL, getMikroOrmWrapper } from "./database"
import { initModules, InitModulesOptions } from "./init-modules"
import { getDatabaseURL, getMikroOrmWrapper, TestDatabase } from "./database"
import { MockEventBusService } from "."
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
export interface SuiteOptions<TService = unknown> {
MikroOrmWrapper: TestDatabase
medusaApp: MedusaAppOutput
medusaApp: any
service: TService
dbConfig: {
schema: string
@@ -37,6 +36,8 @@ export function moduleIntegrationTestRunner({
debug?: boolean
testSuite: <TService = unknown>(options: SuiteOptions<TService>) => () => void
}) {
const moduleSdkImports = require("@medusajs/modules-sdk")
process.env.LOG_LEVEL = "error"
moduleModels ??= Object.values(require(`${process.cwd()}/src/models`))
@@ -62,7 +63,7 @@ export function moduleIntegrationTestRunner({
const modulesConfig_ = {
[moduleName]: {
definition: ModulesDefinition[moduleName],
definition: moduleSdkImports.ModulesDefinition[moduleName],
resolve,
options: {
defaultAdapterOptions: {
@@ -89,7 +90,7 @@ export function moduleIntegrationTestRunner({
let shutdown: () => Promise<void>
let moduleService
let medusaApp: MedusaAppOutput = {} as MedusaAppOutput
let medusaApp = {}
const options = {
MikroOrmWrapper,
@@ -100,7 +101,7 @@ export function moduleIntegrationTestRunner({
return medusaApp[prop]
},
}
) as MedusaAppOutput,
),
service: new Proxy(
{},
{
@@ -123,7 +124,7 @@ export function moduleIntegrationTestRunner({
await MikroOrmWrapper.clearDatabase()
await shutdown()
moduleService = {}
medusaApp = {} as MedusaAppOutput
medusaApp = {}
}
return describe("", () => {

View File

@@ -108,6 +108,7 @@ export async function loadInternalModule(
container: localContainer,
logger,
options: resolution.options,
dataLoaderOnly: loaderOnly,
},
resolution.moduleDeclaration as InternalModuleDeclaration
)

View File

@@ -67,7 +67,9 @@ export type ModuleBootstrapOptions = {
*/
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
workerMode?: "shared" | "worker" | "server"

View File

@@ -118,6 +118,7 @@ export type LoaderOptions<TOptions = Record<string, unknown>> = {
container: MedusaContainer
options?: TOptions
logger?: Logger
dataLoaderOnly?: boolean
}
export type ModuleLoaderFunction = (

View File

@@ -7,6 +7,7 @@ export default async ({
container,
logger,
options,
dataLoaderOnly
}: LoaderOptions): Promise<void> => {
const {
url,
@@ -58,6 +59,7 @@ export default async ({
}
container.register({
partialLoading: asValue(true),
redisConnection: asValue(connection),
redisWorkerConnection: asValue(workerConnection),
redisPublisher: asValue(redisPublisher),

View File

@@ -1,10 +1,11 @@
import { asClass } from "awilix"
import { asClass, asValue } from "awilix"
import { RedisDistributedTransactionStorage } from "../utils"
export default async ({ container }): Promise<void> => {
export default async ({ container, dataLoaderOnly }): Promise<void> => {
container.register({
redisDistributedTransactionStorage: asClass(
RedisDistributedTransactionStorage
).singleton(),
dataLoaderOnly: asValue(!!dataLoaderOnly),
})
}

View File

@@ -79,10 +79,12 @@ export class WorkflowOrchestratorService {
protected redisDistributedTransactionStorage_: RedisDistributedTransactionStorage
constructor({
dataLoaderOnly,
redisDistributedTransactionStorage,
redisPublisher,
redisSubscriber,
}: {
dataLoaderOnly: boolean
redisDistributedTransactionStorage: RedisDistributedTransactionStorage
workflowOrchestratorService: WorkflowOrchestratorService
redisPublisher: Redis
@@ -92,7 +94,10 @@ export class WorkflowOrchestratorService {
this.redisSubscriber = redisSubscriber
redisDistributedTransactionStorage.setWorkflowOrchestratorService(this)
DistributedTransaction.setStorage(redisDistributedTransactionStorage)
if (!dataLoaderOnly) {
DistributedTransaction.setStorage(redisDistributedTransactionStorage)
}
this.redisDistributedTransactionStorage_ =
redisDistributedTransactionStorage

View File

@@ -38528,7 +38528,6 @@ __metadata:
version: 0.0.0-use.local
resolution: "medusa-test-utils@workspace:packages/medusa-test-utils"
dependencies:
"@medusajs/modules-sdk": ^1.12.10
"@medusajs/types": ^1.11.15
"@medusajs/utils": ^1.11.8
"@mikro-orm/migrations": 5.9.7
@@ -38541,6 +38540,7 @@ __metadata:
typescript: ^5.1.6
peerDependencies:
"@medusajs/medusa": ">1.19"
"@medusajs/modules-sdk": ^1.12.10
axios: ^0.28.0
express: ^4.18.3
get-port: ^5.1.0