test(): Test the create product workflow compensation (#4716)

**What**
Integration tests to validate the workflow compensation.
Also, fix the transaction state when the workflow is compensating and some steps does not have any compensation
This commit is contained in:
Adrien de Peretti authored and GitHub committed 2023-08-09 14:33:04 +00:00
1 parent 86c314eb2a
commit ac866ebb51
21 files changed
+391 -193

No files matched your search

+1 -1
View File
@@ -40,7 +40,7 @@
"prepare": "cross-env NODE_ENV=production yarn run build",
"build": "rimraf dist && tsc --build",
"serve": "node dist/app.js",
"test": "jest"
"test": "jest --maxWorkers=50% --detectOpenHandles --forceExit"
},
"peerDependencies": {
"medusa-interfaces": "^1.3.7",
@@ -2,6 +2,7 @@ import { IdMap } from "medusa-test-utils"
import { request } from "../../../../../helpers/test-request"
import { PriceListServiceMock } from "../../../../../services/__mocks__/price-list"
jest.setTimeout(10000)
describe("POST /price-lists", () => {
describe("successfully creates a price list", () => {
let subject
@@ -35,7 +35,7 @@ import {
import { DistributedTransaction } from "@medusajs/orchestration"
import { IInventoryService, WorkflowTypes } from "@medusajs/types"
import { FlagRouter } from "@medusajs/utils"
import { Workflows, createProducts } from "@medusajs/workflows"
import { createProducts, Workflows } from "@medusajs/workflows"
import { Type } from "class-transformer"
import { EntityManager } from "typeorm"
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
@@ -142,6 +142,8 @@ export default async (req, res) => {
)
}
let product
if (isWorkflowEnabled && !!productModuleService) {
const createProductWorkflow = createProducts(req.scope)
@@ -157,119 +159,114 @@ export default async (req, res) => {
},
}
const { result: products } = await createProductWorkflow.run({
const { result } = await createProductWorkflow.run({
input,
context: {
manager: entityManager,
},
})
product = result[0]
} else {
product = await entityManager.transaction(async (manager) => {
const { variants } = validated
delete validated.variants
return res.json({ product: products[0] })
if (!validated.thumbnail && validated.images && validated.images.length) {
validated.thumbnail = validated.images[0]
}
let shippingProfile
// Get default shipping profile
if (validated.is_giftcard) {
shippingProfile = await shippingProfileService
.withTransaction(manager)
.retrieveGiftCardDefault()
} else {
shippingProfile = await shippingProfileService
.withTransaction(manager)
.retrieveDefault()
}
// Provided that the feature flag is enabled and
// no sales channels are available, set the default one
if (
featureFlagRouter.isFeatureEnabled(SalesChannelFeatureFlag.key) &&
!validated?.sales_channels?.length
) {
const defaultSalesChannel = await salesChannelService
.withTransaction(manager)
.retrieveDefault()
validated.sales_channels = [defaultSalesChannel]
}
const newProduct = await productService
.withTransaction(manager)
.create({ ...validated, profile_id: shippingProfile.id })
if (variants) {
for (const [index, variant] of variants.entries()) {
variant["variant_rank"] = index
}
const optionIds =
validated?.options?.map(
(o) => newProduct.options.find((newO) => newO.title === o.title)?.id
) || []
const allVariantTransactions: DistributedTransaction[] = []
const transactionDependencies = {
manager,
inventoryService,
productVariantInventoryService,
productVariantService,
}
try {
const variantsInputData = variants.map((variant) => {
const options =
variant?.options?.map((option, index) => ({
...option,
option_id: optionIds[index],
})) || []
return {
...variant,
options,
} as CreateProductVariantInput
})
const varTransaction = await createVariantsTransaction(
transactionDependencies,
newProduct.id,
variantsInputData
)
allVariantTransactions.push(varTransaction)
} catch (e) {
await Promise.all(
allVariantTransactions.map(async (transaction) => {
await revertVariantTransaction(
transactionDependencies,
transaction
).catch(() => logger.warn("Transaction couldn't be reverted."))
})
)
throw e
}
}
return newProduct
})
}
const product = await entityManager.transaction(async (manager) => {
const { variants } = validated
delete validated.variants
if (!validated.thumbnail && validated.images && validated.images.length) {
validated.thumbnail = validated.images[0]
}
let shippingProfile
// Get default shipping profile
if (validated.is_giftcard) {
shippingProfile = await shippingProfileService
.withTransaction(manager)
.retrieveGiftCardDefault()
} else {
shippingProfile = await shippingProfileService
.withTransaction(manager)
.retrieveDefault()
}
// Provided that the feature flag is enabled and
// no sales channels are available, set the default one
if (
featureFlagRouter.isFeatureEnabled(SalesChannelFeatureFlag.key) &&
!validated?.sales_channels?.length
) {
const defaultSalesChannel = await salesChannelService
.withTransaction(manager)
.retrieveDefault()
validated.sales_channels = [defaultSalesChannel]
}
const newProduct = await productService
.withTransaction(manager)
.create({ ...validated, profile_id: shippingProfile.id })
if (variants) {
for (const [index, variant] of variants.entries()) {
variant["variant_rank"] = index
}
const optionIds =
validated?.options?.map(
(o) => newProduct.options.find((newO) => newO.title === o.title)?.id
) || []
const allVariantTransactions: DistributedTransaction[] = []
const transactionDependencies = {
manager,
inventoryService,
productVariantInventoryService,
productVariantService,
}
try {
const variantsInputData = variants.map((variant) => {
const options =
variant?.options?.map((option, index) => ({
...option,
option_id: optionIds[index],
})) || []
return {
...variant,
options,
} as CreateProductVariantInput
})
const varTransaction = await createVariantsTransaction(
transactionDependencies,
newProduct.id,
variantsInputData
)
allVariantTransactions.push(varTransaction)
} catch (e) {
await Promise.all(
allVariantTransactions.map(async (transaction) => {
await revertVariantTransaction(
transactionDependencies,
transaction
).catch(() => logger.warn("Transaction couldn't be reverted."))
})
)
throw e
}
}
const rawProduct = await productService
.withTransaction(manager)
.retrieve(newProduct.id, {
select: defaultAdminProductFields,
relations: defaultAdminProductRelations,
})
const [product] = await pricingService
.withTransaction(manager)
.setProductPrices([rawProduct])
return product
const rawProduct = await productService.retrieve(product.id, {
select: defaultAdminProductFields,
relations: defaultAdminProductRelations,
})
res.json({ product })
const [pricedProduct] = await pricingService.setProductPrices([rawProduct])
res.json({ product: pricedProduct })
}
class ProductVariantOptionReq {