fix(product, integration-tests): Fix integration tests (#4674)

* fix(product, integration-tests): Fix integration tests

* improve test

* Create dirty-eagles-shop.md
This commit is contained in:
Adrien de Peretti
2023-08-02 13:44:53 +02:00
committed by GitHub
parent 7f1421c218
commit 4b80ba8a35
5 changed files with 158 additions and 113 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@medusajs/modules-sdk": patch
"@medusajs/product": patch
---
fix(product, integration-tests): Fix integration tests
@@ -8,6 +8,7 @@ import productSeeder from "../../../../helpers/product-seeder"
import { simpleSalesChannelFactory } from "../../../../factories" import { simpleSalesChannelFactory } from "../../../../factories"
import { AxiosInstance } from "axios" import { AxiosInstance } from "axios"
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
jest.setTimeout(50000) jest.setTimeout(50000)
@@ -21,11 +22,14 @@ describe("/admin/products", () => {
let medusaProcess let medusaProcess
let dbConnection let dbConnection
let express let express
let medusaContainer
beforeAll(async () => { beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", "..", "..")) const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
dbConnection = await initDb({ cwd } as any) dbConnection = await initDb({ cwd } as any)
const { app, port } = await bootstrapApp({ cwd }) const { app, port, container } = await bootstrapApp({ cwd })
medusaContainer = container
setPort(port) setPort(port)
express = app.listen(port, () => { express = app.listen(port, () => {
process.send?.(port) process.send?.(port)
@@ -39,6 +43,14 @@ describe("/admin/products", () => {
medusaProcess.kill() medusaProcess.kill()
}) })
it("Should have loaded the product module", function () {
const productRegistrationName =
ModulesDefinition[Modules.PRODUCT].registrationName
expect(
medusaContainer.hasRegistration(productRegistrationName)
).toBeTruthy()
})
describe("POST /admin/products", () => { describe("POST /admin/products", () => {
beforeEach(async () => { beforeEach(async () => {
await productSeeder(dbConnection) await productSeeder(dbConnection)
+5 -4
View File
@@ -1,3 +1,4 @@
const { Modules } = require("@medusajs/modules-sdk")
const DB_HOST = process.env.DB_HOST const DB_HOST = process.env.DB_HOST
const DB_USERNAME = process.env.DB_USERNAME const DB_USERNAME = process.env.DB_USERNAME
const DB_PASSWORD = process.env.DB_PASSWORD const DB_PASSWORD = process.env.DB_PASSWORD
@@ -31,21 +32,21 @@ module.exports = {
database_extra: { idle_in_transaction_session_timeout: 0 }, database_extra: { idle_in_transaction_session_timeout: 0 },
}, },
modules: { modules: {
stockLocationService: { [Modules.STOCK_LOCATION]: {
scope: "internal", scope: "internal",
resources: "shared", resources: "shared",
resolve: "@medusajs/stock-location", resolve: "@medusajs/stock-location",
}, },
inventoryService: { [Modules.INVENTORY]: {
scope: "internal", scope: "internal",
resources: "shared", resources: "shared",
resolve: "@medusajs/inventory", resolve: "@medusajs/inventory",
}, },
cacheService: { [Modules.CACHE]: {
resolve: "@medusajs/cache-inmemory", resolve: "@medusajs/cache-inmemory",
options: { ttl: 5 }, options: { ttl: 5 },
}, },
productModuleService: { [Modules.PRODUCT]: {
scope: "internal", scope: "internal",
resources: "shared", resources: "shared",
resolve: "@medusajs/product", resolve: "@medusajs/product",
+64 -63
View File
@@ -12,74 +12,75 @@ export enum Modules {
PRODUCT = "productService", PRODUCT = "productService",
} }
export const ModulesDefinition: { [key: string]: ModuleDefinition } = { export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } =
[Modules.EVENT_BUS]: { {
key: Modules.EVENT_BUS, [Modules.EVENT_BUS]: {
registrationName: "eventBusModuleService", key: Modules.EVENT_BUS,
defaultPackage: "@medusajs/event-bus-local", registrationName: "eventBusModuleService",
label: "EventBusModuleService", defaultPackage: "@medusajs/event-bus-local",
canOverride: true, label: "EventBusModuleService",
isRequired: true, canOverride: true,
dependencies: ["logger"], isRequired: true,
defaultModuleDeclaration: { dependencies: ["logger"],
scope: MODULE_SCOPE.INTERNAL, defaultModuleDeclaration: {
resources: MODULE_RESOURCE_TYPE.SHARED, scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
}, },
}, [Modules.STOCK_LOCATION]: {
[Modules.STOCK_LOCATION]: { key: Modules.STOCK_LOCATION,
key: Modules.STOCK_LOCATION, registrationName: "stockLocationService",
registrationName: "stockLocationService", defaultPackage: false,
defaultPackage: false, label: "StockLocationService",
label: "StockLocationService", isRequired: false,
isRequired: false, canOverride: true,
canOverride: true, isQueryable: true,
isQueryable: true, dependencies: ["eventBusService"],
dependencies: ["eventBusService"], defaultModuleDeclaration: {
defaultModuleDeclaration: { scope: MODULE_SCOPE.INTERNAL,
scope: MODULE_SCOPE.INTERNAL, resources: MODULE_RESOURCE_TYPE.SHARED,
resources: MODULE_RESOURCE_TYPE.SHARED, },
}, },
}, [Modules.INVENTORY]: {
[Modules.INVENTORY]: { key: Modules.INVENTORY,
key: Modules.INVENTORY, registrationName: "inventoryService",
registrationName: "inventoryService", defaultPackage: false,
defaultPackage: false, label: "InventoryService",
label: "InventoryService", isRequired: false,
isRequired: false, canOverride: true,
canOverride: true, isQueryable: true,
isQueryable: true, dependencies: ["eventBusService"],
dependencies: ["eventBusService"], defaultModuleDeclaration: {
defaultModuleDeclaration: { scope: MODULE_SCOPE.INTERNAL,
scope: MODULE_SCOPE.INTERNAL, resources: MODULE_RESOURCE_TYPE.SHARED,
resources: MODULE_RESOURCE_TYPE.SHARED, },
}, },
}, [Modules.CACHE]: {
[Modules.CACHE]: { key: Modules.CACHE,
key: Modules.CACHE, registrationName: "cacheService",
registrationName: "cacheService", defaultPackage: "@medusajs/cache-inmemory",
defaultPackage: "@medusajs/cache-inmemory", label: "CacheService",
label: "CacheService", isRequired: true,
isRequired: true, canOverride: true,
canOverride: true, defaultModuleDeclaration: {
defaultModuleDeclaration: { scope: MODULE_SCOPE.INTERNAL,
scope: MODULE_SCOPE.INTERNAL, resources: MODULE_RESOURCE_TYPE.SHARED,
resources: MODULE_RESOURCE_TYPE.SHARED, },
}, },
}, [Modules.PRODUCT]: {
[Modules.PRODUCT]: { key: Modules.PRODUCT,
key: Modules.PRODUCT, registrationName: "productModuleService",
registrationName: "productModuleService", defaultPackage: false,
defaultPackage: false, label: "ProductModuleService",
label: "ProductModuleService", isRequired: false,
isRequired: false, canOverride: true,
canOverride: true, isQueryable: true,
isQueryable: true, dependencies: [],
dependencies: [], defaultModuleDeclaration: {
defaultModuleDeclaration: { scope: MODULE_SCOPE.EXTERNAL,
scope: MODULE_SCOPE.EXTERNAL, },
}, },
}, }
}
export const MODULE_DEFINITIONS: ModuleDefinition[] = export const MODULE_DEFINITIONS: ModuleDefinition[] =
Object.values(ModulesDefinition) Object.values(ModulesDefinition)
@@ -26,10 +26,12 @@ import {
JoinerServiceConfig, JoinerServiceConfig,
ProductTypes, ProductTypes,
} from "@medusajs/types" } from "@medusajs/types"
import { serialize } from "@mikro-orm/core"
import ProductImageService from "./product-image" import ProductImageService from "./product-image"
import { ProductServiceTypes, ProductVariantServiceTypes } from "../types/services" import {
ProductServiceTypes,
ProductVariantServiceTypes,
} from "../types/services"
import { import {
InjectManager, InjectManager,
InjectTransactionManager, InjectTransactionManager,
@@ -393,11 +395,12 @@ export default class ProductModuleService<
config: FindConfig<ProductTypes.ProductOptionDTO> = {}, config: FindConfig<ProductTypes.ProductOptionDTO> = {},
@MedusaContext() sharedContext: Context = {} @MedusaContext() sharedContext: Context = {}
): Promise<[ProductTypes.ProductOptionDTO[], number]> { ): Promise<[ProductTypes.ProductOptionDTO[], number]> {
const [productOptions, count] = await this.productOptionService_.listAndCount( const [productOptions, count] =
filters, await this.productOptionService_.listAndCount(
config, filters,
sharedContext config,
) sharedContext
)
return [JSON.parse(JSON.stringify(productOptions)), count] return [JSON.parse(JSON.stringify(productOptions)), count]
} }
@@ -599,7 +602,10 @@ export default class ProductModuleService<
return JSON.parse(JSON.stringify(categories)) return JSON.parse(JSON.stringify(categories))
} }
async create(data: ProductTypes.CreateProductDTO[], sharedContext?: Context): Promise<ProductTypes.ProductDTO[]> { async create(
data: ProductTypes.CreateProductDTO[],
sharedContext?: Context
): Promise<ProductTypes.ProductDTO[]> {
const products = await this.create_(data, sharedContext) const products = await this.create_(data, sharedContext)
return this.baseRepository_.serialize<ProductTypes.ProductDTO[]>(products, { return this.baseRepository_.serialize<ProductTypes.ProductDTO[]>(products, {
@@ -613,9 +619,7 @@ export default class ProductModuleService<
): Promise<ProductTypes.ProductDTO[]> { ): Promise<ProductTypes.ProductDTO[]> {
const products = await this.update_(data, sharedContext) const products = await this.update_(data, sharedContext)
return this.baseRepository_.serialize< return this.baseRepository_.serialize<ProductTypes.ProductDTO[]>(products, {
ProductTypes.ProductDTO[]
>(products, {
populate: true, populate: true,
}) })
} }
@@ -659,9 +663,18 @@ export default class ProductModuleService<
productData.discountable = false productData.discountable = false
} }
await this.upsertAndAssignImagesToProductData(productData, sharedContext) await this.upsertAndAssignImagesToProductData(
await this.upsertAndAssignProductTagsToProductData(productData, sharedContext) productData,
await this.upsertAndAssignProductTypeToProductData(productData, sharedContext) sharedContext
)
await this.upsertAndAssignProductTagsToProductData(
productData,
sharedContext
)
await this.upsertAndAssignProductTypeToProductData(
productData,
sharedContext
)
return productData as CreateProductOnlyDTO return productData as CreateProductOnlyDTO
}) })
@@ -679,7 +692,9 @@ export default class ProductModuleService<
const productOptionsData = [...productOptionsMap] const productOptionsData = [...productOptionsMap]
.map(([handle, options]) => { .map(([handle, options]) => {
return options.map((option) => { return options.map((option) => {
const productOptionsData: ProductTypes.CreateProductOptionOnlyDTO = { ...option } const productOptionsData: ProductTypes.CreateProductOptionOnlyDTO = {
...option,
}
const product = productByHandleMap.get(handle) const product = productByHandleMap.get(handle)
const productId = product?.id! const productId = product?.id!
@@ -729,31 +744,29 @@ export default class ProductModuleService<
data: ProductTypes.UpdateProductDTO[], data: ProductTypes.UpdateProductDTO[],
@MedusaContext() sharedContext: Context = {} @MedusaContext() sharedContext: Context = {}
): Promise<TProduct[]> { ): Promise<TProduct[]> {
const productIds = data.map(pd => pd.id) const productIds = data.map((pd) => pd.id)
const existingProductVariants = await this.productVariantService_.list( const existingProductVariants = await this.productVariantService_.list(
{ product_id: productIds }, { product_id: productIds },
{}, {},
sharedContext sharedContext
) )
const existingProductVariantsMap = new Map< const existingProductVariantsMap = new Map<string, ProductVariant[]>(
string,
ProductVariant[]
>(
data.map((productData) => { data.map((productData) => {
const productVariantsForProduct = existingProductVariants const productVariantsForProduct = existingProductVariants.filter(
.filter((variant) => variant.product_id === productData.id) (variant) => variant.product_id === productData.id
)
return [ return [productData.id, productVariantsForProduct]
productData.id,
productVariantsForProduct,
]
}) })
) )
const productVariantsMap = new Map< const productVariantsMap = new Map<
string, string,
(ProductTypes.CreateProductVariantDTO | ProductTypes.UpdateProductVariantDTO)[] (
| ProductTypes.CreateProductVariantDTO
| ProductTypes.UpdateProductVariantDTO
)[]
>() >()
const productOptionsMap = new Map< const productOptionsMap = new Map<
@@ -779,9 +792,18 @@ export default class ProductModuleService<
productData.discountable = false productData.discountable = false
} }
await this.upsertAndAssignImagesToProductData(productData, sharedContext) await this.upsertAndAssignImagesToProductData(
await this.upsertAndAssignProductTagsToProductData(productData, sharedContext) productData,
await this.upsertAndAssignProductTypeToProductData(productData, sharedContext) sharedContext
)
await this.upsertAndAssignProductTagsToProductData(
productData,
sharedContext
)
await this.upsertAndAssignProductTypeToProductData(
productData,
sharedContext
)
return productData as ProductServiceTypes.UpdateProductDTO return productData as ProductServiceTypes.UpdateProductDTO
}) })
@@ -797,10 +819,12 @@ export default class ProductModuleService<
) )
const productOptionsData = [...productOptionsMap] const productOptionsData = [...productOptionsMap]
.map(([id, options]) => options.map((option) => ({ .map(([id, options]) =>
...option, options.map((option) => ({
product: productByIdMap.get(id)!, ...option,
}))) product: productByIdMap.get(id)!,
}))
)
.flat() .flat()
const productOptions = await this.productOptionService_.create( const productOptions = await this.productOptionService_.create(
@@ -825,7 +849,7 @@ export default class ProductModuleService<
const existingVariants = existingProductVariantsMap.get(productId) const existingVariants = existingProductVariantsMap.get(productId)
variants.forEach((variant) => { variants.forEach((variant) => {
const isVariantIdDefined = ("id" in variant) && isDefined(variant.id) const isVariantIdDefined = "id" in variant && isDefined(variant.id)
if (isVariantIdDefined) { if (isVariantIdDefined) {
variantsToUpdate.push(variant as ProductTypes.UpdateProductVariantDTO) variantsToUpdate.push(variant as ProductTypes.UpdateProductVariantDTO)
@@ -849,13 +873,13 @@ export default class ProductModuleService<
productVariantsToCreateMap.set(productId, variantsToCreate) productVariantsToCreateMap.set(productId, variantsToCreate)
productVariantsToUpdateMap.set(productId, variantsToUpdate) productVariantsToUpdateMap.set(productId, variantsToUpdate)
const variantsToUpdateIds = variantsToUpdate.map(v => v?.id) as string[] const variantsToUpdateIds = variantsToUpdate.map((v) => v?.id) as string[]
const existingVariantIds = existingVariants?.map(v => v.id) || [] const existingVariantIds = existingVariants?.map((v) => v.id) || []
const variantsToUpdateSet = new Set(variantsToUpdateIds) const variantsToUpdateSet = new Set(variantsToUpdateIds)
productVariantIdsToDelete.push( productVariantIdsToDelete.push(
...new Set( ...new Set(
existingVariantIds.filter(x => !variantsToUpdateSet.has(x)) existingVariantIds.filter((x) => !variantsToUpdateSet.has(x))
) )
) )
} }
@@ -884,7 +908,10 @@ export default class ProductModuleService<
if (productVariantIdsToDelete.length) { if (productVariantIdsToDelete.length) {
promises.push( promises.push(
this.productVariantService_.delete(productVariantIdsToDelete, sharedContext) this.productVariantService_.delete(
productVariantIdsToDelete,
sharedContext
)
) )
} }
@@ -930,14 +957,12 @@ export default class ProductModuleService<
sharedContext: Context = {} sharedContext: Context = {}
) { ) {
if (isDefined(productData.type)) { if (isDefined(productData.type)) {
const productType = ( const productType = await this.productTypeService_.upsert(
await this.productTypeService_.upsert( [productData.type],
[productData.type], sharedContext
sharedContext
)
) )
productData.type_id = productType?.[0]?.id productData.type = productType?.[0] as ProductTypes.CreateProductTypeDTO
} }
} }