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:
@@ -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)
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user