feat(product): Create (+ workflow), delete, restore (#4459)
* Feat: create product with product module * feat: create product wip * feat: create product wip * feat: update product relation and generate image migration * lint * conitnue implementation * continue implementation and add integration tests for produceService.create * Add integration tests for product creation at the module level for the complete flow * only use persist since write operations are always wrapped in a transaction which will be committed and flushed * simplify the transaction wrapper to make future changes easier * feat: move some utils to the utils package to simplify its usage * tests: fix unit tests * feat: create variants along side the product * Add more integration tests an update migrations * chore: Update actions workflow to include packages integration tests * small types and utils cleanup * chore: Add support for database debug option * chore: Add missing types in package.json from types and util, validate that all the models are sync with medusa * expose retrieve method * fix types issues * fix unit tests and move integration tests workflow with the plugins integration tests * chore: remove migration function export from the definition to prevent them to be ran by the medusa cli just in case * fix package.json script * chore: workflows * feat: start creating the create product workflow * feat: add empty step for prices and sales channel * tests: update scripts and action envs * fix imports * feat: Add proper soft deleted support + add product deletion service public api * chore: update migrations * chore: update migrations * chore: update todo * feat: Add product deletion to the create-product workflow as compensation * chore: cleanup product utils * feat: Add support for cascade soft-remove * feat: refactor repository to take into account withDeleted * fix integration tests * Add support for force delete -> delete, cleanup repositories and improvements * Add support for restoring a product and add integration tests * cleaup + tests * types * fix integration tests * remove unnecessary comments * move specific mikro orm usage to the DAL * Cleanup workflow functions * Make deleted_at optional at the property level and add url index for the images * address feedback + cleanup * fix export * merge migrations into one * feat(product, types): added missing product variant methods (#4475) * chore: added missing product variant methods * chore: address PR feedback * chore: catch undefined case for retrieve + specs for variant service * chore: align TEntity + add changeset * chore: revert changeset, TEntity to ProductVariant * chore: write tests for pagination, unskip the test * Create chilled-mice-deliver.md * update integration fixtuers * update pipeline node version * rename github action * fix pipeline * feat(medusa, types): added missing category tests and service methods (#4499) * chore: added missing category tests and service methods * chore: added type changes to module service * chore: address pr feedback * update repositories manager usage and serialisation from the write public API * move serializisation to the DAL * rename template args * chore: added collection methods for module and collection service (#4505) * chore: added collection methods for module and collection service * Create fresh-islands-teach.md * chore: move retrieve entity to utils package * chore: make products optional in DTO type --------- Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> * feat(product): Apply transaction decorators to the services (#4512) --------- Co-authored-by: Riqwan Thamir <rmthamir@gmail.com> Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
This commit is contained in:
co-authored by
Oliver Windall Juhl
Riqwan Thamir
Carlos R. L. Rodrigues
parent
5b91a3503a
commit
befc2f1c80
@@ -1,14 +1,9 @@
|
||||
import { FindOptions, RepositoryService } from "@medusajs/types"
|
||||
import { BaseRepository } from "../../src/repositories/base"
|
||||
|
||||
class CustomRepository implements RepositoryService {
|
||||
constructor() {}
|
||||
|
||||
find(options?: FindOptions): Promise<any[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
findAndCount(options?: FindOptions): Promise<[any[], number]> {
|
||||
throw new Error("Method not implemented.")
|
||||
class CustomRepository extends BaseRepository {
|
||||
constructor({ manager }) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import faker from "faker"
|
||||
import { Image } from "@models"
|
||||
|
||||
export const buildProductOnlyData = ({
|
||||
title,
|
||||
description,
|
||||
subtitle,
|
||||
is_giftcard,
|
||||
discountable,
|
||||
thumbnail,
|
||||
images,
|
||||
status,
|
||||
}: {
|
||||
title?: string
|
||||
description?: string
|
||||
subtitle?: string
|
||||
is_giftcard?: boolean
|
||||
discountable?: boolean
|
||||
thumbnail?: string
|
||||
images?: { id?: string; url: string }[]
|
||||
status?: ProductTypes.ProductStatus
|
||||
} = {}) => {
|
||||
return {
|
||||
title: title ?? faker.commerce.productName(),
|
||||
description: description ?? faker.commerce.productName(),
|
||||
subtitle: subtitle ?? faker.commerce.productName(),
|
||||
is_giftcard: is_giftcard ?? false,
|
||||
discountable: discountable ?? true,
|
||||
thumbnail: thumbnail as string,
|
||||
status: status ?? ProductTypes.ProductStatus.PUBLISHED,
|
||||
images: (images ?? []) as Image[],
|
||||
}
|
||||
}
|
||||
|
||||
export const buildProductAndRelationsData = ({
|
||||
title,
|
||||
description,
|
||||
subtitle,
|
||||
is_giftcard,
|
||||
discountable,
|
||||
thumbnail,
|
||||
images,
|
||||
status,
|
||||
type,
|
||||
tags,
|
||||
options,
|
||||
variants,
|
||||
}: Partial<ProductTypes.CreateProductDTO>) => {
|
||||
const defaultOptionTitle = faker.commerce.productName()
|
||||
return {
|
||||
title: title ?? faker.commerce.productName(),
|
||||
description: description ?? faker.commerce.productName(),
|
||||
subtitle: subtitle ?? faker.commerce.productName(),
|
||||
is_giftcard: is_giftcard ?? false,
|
||||
discountable: discountable ?? true,
|
||||
thumbnail: thumbnail as string,
|
||||
status: status ?? ProductTypes.ProductStatus.PUBLISHED,
|
||||
images: (images ?? []) as Image[],
|
||||
type: type ? { value: type } : { value: faker.commerce.productName() },
|
||||
tags: tags ?? [{ value: "tag-1" }],
|
||||
options: options ?? [
|
||||
{
|
||||
title: defaultOptionTitle,
|
||||
},
|
||||
],
|
||||
variants: variants ?? [
|
||||
{
|
||||
title: faker.commerce.productName(),
|
||||
sku: faker.commerce.productName(),
|
||||
options: [
|
||||
{
|
||||
value: defaultOptionTitle + faker.commerce.productName(),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
// TODO: add categories, must be created first
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import {
|
||||
Image,
|
||||
Product,
|
||||
ProductCategory,
|
||||
ProductCollection,
|
||||
@@ -7,9 +9,16 @@ import {
|
||||
} from "@models"
|
||||
import ProductOption from "../../../src/models/product-option"
|
||||
|
||||
export * from "./data/create-product"
|
||||
|
||||
export async function createProductAndTags(
|
||||
manager: SqlEntityManager,
|
||||
data: any[]
|
||||
data: {
|
||||
id?: string
|
||||
title: string
|
||||
status: ProductTypes.ProductStatus
|
||||
tags?: { id: string; value: string }[]
|
||||
}[]
|
||||
) {
|
||||
const products: any[] = data.map((productData) => {
|
||||
return manager.create(Product, productData)
|
||||
@@ -35,7 +44,11 @@ export async function createProductVariants(
|
||||
|
||||
export async function createCollections(
|
||||
manager: SqlEntityManager,
|
||||
collectionData: any[]
|
||||
collectionData: {
|
||||
id?: string
|
||||
title: string
|
||||
handle?: string
|
||||
}[]
|
||||
) {
|
||||
const collections: any[] = collectionData.map((collectionData) => {
|
||||
return manager.create(ProductCollection, collectionData)
|
||||
@@ -48,10 +61,21 @@ export async function createCollections(
|
||||
|
||||
export async function createOptions(
|
||||
manager: SqlEntityManager,
|
||||
optionsData: any[]
|
||||
optionsData: {
|
||||
id?: string
|
||||
product: { id: string }
|
||||
title: string
|
||||
value?: string
|
||||
values?: {
|
||||
id?: string
|
||||
value: string
|
||||
variant?: { id: string } & any
|
||||
}[]
|
||||
variant?: { id: string } & any
|
||||
}[]
|
||||
) {
|
||||
const options: any[] = optionsData.map((o) => {
|
||||
return manager.create(ProductOption, o)
|
||||
const options: any[] = optionsData.map((option) => {
|
||||
return manager.create(ProductOption, option)
|
||||
})
|
||||
|
||||
await manager.persistAndFlush(options)
|
||||
@@ -59,6 +83,19 @@ export async function createOptions(
|
||||
return options
|
||||
}
|
||||
|
||||
export async function createImages(
|
||||
manager: SqlEntityManager,
|
||||
imagesData: string[]
|
||||
) {
|
||||
const images: any[] = imagesData.map((img) => {
|
||||
return manager.create(Image, { url: img })
|
||||
})
|
||||
|
||||
await manager.persistAndFlush(images)
|
||||
|
||||
return images
|
||||
}
|
||||
|
||||
export async function assignCategoriesToProduct(
|
||||
manager: SqlEntityManager,
|
||||
product: Product,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import faker from "faker"
|
||||
|
||||
export const buildProductVariantOnlyData = ({
|
||||
title,
|
||||
sku,
|
||||
barcode,
|
||||
ean,
|
||||
upc,
|
||||
allow_backorder,
|
||||
inventory_quantity,
|
||||
manage_inventory,
|
||||
hs_code,
|
||||
origin_country,
|
||||
mid_code,
|
||||
material,
|
||||
weight,
|
||||
length,
|
||||
height,
|
||||
width,
|
||||
options,
|
||||
metadata,
|
||||
}: Partial<ProductTypes.CreateProductVariantOnlyDTO>) => {
|
||||
return {
|
||||
title: title ?? faker.commerce.productName(),
|
||||
sku: sku ?? faker.commerce.productName(),
|
||||
barcode,
|
||||
ean,
|
||||
upc,
|
||||
allow_backorder,
|
||||
inventory_quantity,
|
||||
manage_inventory,
|
||||
hs_code,
|
||||
origin_country,
|
||||
mid_code,
|
||||
material,
|
||||
weight,
|
||||
length,
|
||||
height,
|
||||
width,
|
||||
options,
|
||||
metadata,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./data/create-variant"
|
||||
@@ -1,11 +1,13 @@
|
||||
import { MedusaModule } from "@medusajs/modules-sdk"
|
||||
import { Product } from "@models"
|
||||
import { initialize } from "../../src"
|
||||
import * as CustomRepositories from "../__fixtures__/module"
|
||||
import { ProductRepository } from "../__fixtures__/module"
|
||||
import { createProductAndTags } from "../__fixtures__/product"
|
||||
import { productsData } from "../__fixtures__/product/data"
|
||||
import { DB_URL, TestDatabase } from "../utils"
|
||||
import { buildProductAndRelationsData } from "../__fixtures__/product/data/create-product"
|
||||
import { kebabCase } from "@medusajs/utils"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
|
||||
const beforeEach_ = async () => {
|
||||
await TestDatabase.setupDatabase()
|
||||
@@ -18,12 +20,11 @@ const afterEach_ = async () => {
|
||||
|
||||
describe("Product module", function () {
|
||||
describe("Using built-in data access layer", function () {
|
||||
let module
|
||||
let products: Product[]
|
||||
let module: IProductModuleService
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await beforeEach_()
|
||||
products = await createProductAndTags(testManager, productsData)
|
||||
await createProductAndTags(testManager, productsData)
|
||||
|
||||
module = await initialize({
|
||||
database: {
|
||||
@@ -46,13 +47,12 @@ describe("Product module", function () {
|
||||
})
|
||||
|
||||
describe("Using custom data access layer", function () {
|
||||
let module
|
||||
let products: Product[]
|
||||
let module: IProductModuleService
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await beforeEach_()
|
||||
|
||||
products = await createProductAndTags(testManager, productsData)
|
||||
await createProductAndTags(testManager, productsData)
|
||||
|
||||
module = await initialize({
|
||||
database: {
|
||||
@@ -78,12 +78,11 @@ describe("Product module", function () {
|
||||
})
|
||||
|
||||
describe("Using custom data access layer and connection", function () {
|
||||
let module
|
||||
let products: Product[]
|
||||
let module: IProductModuleService
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await beforeEach_()
|
||||
products = await createProductAndTags(testManager, productsData)
|
||||
await createProductAndTags(testManager, productsData)
|
||||
|
||||
MedusaModule.clearInstances()
|
||||
|
||||
@@ -106,4 +105,244 @@ describe("Product module", function () {
|
||||
expect(products).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", function () {
|
||||
let module: IProductModuleService
|
||||
let images = ["image-1"]
|
||||
|
||||
beforeEach(async () => {
|
||||
await beforeEach_()
|
||||
|
||||
MedusaModule.clearInstances()
|
||||
|
||||
module = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(afterEach_)
|
||||
|
||||
it("should create a product", async () => {
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0],
|
||||
})
|
||||
|
||||
const products = await module.create([data])
|
||||
|
||||
expect(products).toHaveLength(1)
|
||||
|
||||
expect(products[0].images).toHaveLength(1)
|
||||
expect(products[0].options).toHaveLength(1)
|
||||
expect(products[0].tags).toHaveLength(1)
|
||||
expect(products[0].categories).toHaveLength(0)
|
||||
expect(products[0].variants).toHaveLength(1)
|
||||
|
||||
expect(products[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: data.title,
|
||||
handle: kebabCase(data.title),
|
||||
description: data.description,
|
||||
subtitle: data.subtitle,
|
||||
is_giftcard: data.is_giftcard,
|
||||
discountable: data.discountable,
|
||||
thumbnail: images[0],
|
||||
status: data.status,
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
url: images[0],
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: data.options[0].title,
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.variants[0].options?.[0].value,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
tags: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.tags[0].value,
|
||||
}),
|
||||
]),
|
||||
type: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.type.value,
|
||||
}),
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: data.variants[0].title,
|
||||
sku: data.variants[0].sku,
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
inventory_quantity: 100,
|
||||
variant_rank: 0,
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.variants[0].options?.[0].value,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("softDelete", function () {
|
||||
let module: IProductModuleService
|
||||
let images = ["image-1"]
|
||||
|
||||
beforeEach(async () => {
|
||||
await beforeEach_()
|
||||
|
||||
MedusaModule.clearInstances()
|
||||
|
||||
module = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(afterEach_)
|
||||
|
||||
it("should soft delete a product and its cascaded relations", async () => {
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0],
|
||||
})
|
||||
|
||||
const products = await module.create([data])
|
||||
|
||||
await module.softDelete([products[0].id])
|
||||
|
||||
const deletedProducts = await module.list(
|
||||
{ id: products[0].id },
|
||||
{
|
||||
relations: [
|
||||
"variants",
|
||||
"variants.options",
|
||||
"options",
|
||||
"options.values",
|
||||
],
|
||||
withDeleted: true,
|
||||
}
|
||||
)
|
||||
|
||||
expect(deletedProducts).toHaveLength(1)
|
||||
expect(deletedProducts[0].deleted_at).not.toBeNull()
|
||||
|
||||
for (const option of deletedProducts[0].options) {
|
||||
expect(option.deleted_at).not.toBeNull()
|
||||
}
|
||||
|
||||
const productOptionsValues = deletedProducts[0].options
|
||||
.map((o) => o.values)
|
||||
.flat()
|
||||
|
||||
for (const optionValue of productOptionsValues) {
|
||||
expect(optionValue.deleted_at).not.toBeNull()
|
||||
}
|
||||
|
||||
for (const variant of deletedProducts[0].variants) {
|
||||
expect(variant.deleted_at).not.toBeNull()
|
||||
}
|
||||
|
||||
const variantsOptions = deletedProducts[0].options
|
||||
.map((o) => o.values)
|
||||
.flat()
|
||||
|
||||
for (const option of variantsOptions) {
|
||||
expect(option.deleted_at).not.toBeNull()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("restore", function () {
|
||||
let module: IProductModuleService
|
||||
let images = ["image-1"]
|
||||
|
||||
beforeEach(async () => {
|
||||
await beforeEach_()
|
||||
|
||||
MedusaModule.clearInstances()
|
||||
|
||||
module = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(afterEach_)
|
||||
|
||||
it("should restore a soft deleted product and its cascaded relations", async () => {
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0],
|
||||
})
|
||||
|
||||
const products = await module.create([data])
|
||||
|
||||
await module.softDelete([products[0].id])
|
||||
await module.restore([products[0].id])
|
||||
|
||||
const deletedProducts = await module.list(
|
||||
{ id: products[0].id },
|
||||
{
|
||||
relations: [
|
||||
"variants",
|
||||
"variants.options",
|
||||
"variants.options",
|
||||
"options",
|
||||
"options.values",
|
||||
],
|
||||
withDeleted: true,
|
||||
}
|
||||
)
|
||||
|
||||
expect(deletedProducts).toHaveLength(1)
|
||||
expect(deletedProducts[0].deleted_at).toBeNull()
|
||||
|
||||
for (const option of deletedProducts[0].options) {
|
||||
expect(option.deleted_at).toBeNull()
|
||||
}
|
||||
|
||||
const productOptionsValues = deletedProducts[0].options
|
||||
.map((o) => o.values)
|
||||
.flat()
|
||||
|
||||
for (const optionValue of productOptionsValues) {
|
||||
expect(optionValue.deleted_at).toBeNull()
|
||||
}
|
||||
|
||||
for (const variant of deletedProducts[0].variants) {
|
||||
expect(variant.deleted_at).toBeNull()
|
||||
}
|
||||
|
||||
const variantsOptions = deletedProducts[0].options
|
||||
.map((o) => o.values)
|
||||
.flat()
|
||||
|
||||
for (const option of variantsOptions) {
|
||||
expect(option.deleted_at).toBeNull()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ import { productCategoriesData } from "../../../__fixtures__/product-category/da
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("ProductCategory Service", () => {
|
||||
describe("Product category Service", () => {
|
||||
let service: ProductCategoryService
|
||||
let testManager: SqlEntityManager
|
||||
let repositoryManager: SqlEntityManager
|
||||
@@ -47,7 +47,7 @@ describe("ProductCategory Service", () => {
|
||||
const productCategoryResults = await service.list(
|
||||
{},
|
||||
{
|
||||
select: ["id", "parent_category_id"] as any,
|
||||
select: ["id", "parent_category_id"],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -128,7 +128,7 @@ describe("ProductCategory Service", () => {
|
||||
include_descendants_tree: true,
|
||||
},
|
||||
{
|
||||
select: ["id", "handle"] as any,
|
||||
select: ["id", "handle"],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -192,7 +192,7 @@ describe("ProductCategory Service", () => {
|
||||
is_internal: false,
|
||||
},
|
||||
{
|
||||
select: ["id", "handle"] as any,
|
||||
select: ["id", "handle"],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -230,4 +230,334 @@ describe("ProductCategory Service", () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
const categoryOneId = "category-1"
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
productCategories = await createProductCategories(
|
||||
testManager,
|
||||
productCategoriesData
|
||||
)
|
||||
})
|
||||
|
||||
it("should return category for the given id", async () => {
|
||||
const productCategoryResults = await service.retrieve(
|
||||
categoryOneId,
|
||||
)
|
||||
|
||||
expect(productCategoryResults).toEqual(
|
||||
expect.objectContaining({
|
||||
id: categoryOneId
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when category with id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual('ProductCategory with id: does-not-exist was not found')
|
||||
})
|
||||
|
||||
it("should throw an error when an id is not provided", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve(undefined as unknown as string)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual('"productCategoryId" must be defined')
|
||||
})
|
||||
|
||||
it("should return category based on config select param", async () => {
|
||||
const productCategoryResults = await service.retrieve(
|
||||
categoryOneId,
|
||||
{
|
||||
select: ["id", "parent_category_id"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategoryResults).toEqual(
|
||||
expect.objectContaining({
|
||||
id: categoryOneId,
|
||||
parent_category_id: "category-0",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should return category based on config relation param", async () => {
|
||||
const productCategoryResults = await service.retrieve(
|
||||
categoryOneId,
|
||||
{
|
||||
select: ["id", "parent_category_id"],
|
||||
relations: ["parent_category"]
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategoryResults).toEqual(
|
||||
expect.objectContaining({
|
||||
id: categoryOneId,
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: 'category-1-a',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 'category-1-b',
|
||||
})
|
||||
],
|
||||
parent_category: expect.objectContaining({
|
||||
id: "category-0"
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCount", () => {
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
productCategories = await createProductCategories(
|
||||
testManager,
|
||||
productCategoriesData
|
||||
)
|
||||
})
|
||||
|
||||
it("should return categories and count based on take and skip", async () => {
|
||||
let results = await service.listAndCount(
|
||||
{},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(5)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "category-0",
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listAndCount(
|
||||
{},
|
||||
{
|
||||
take: 1,
|
||||
skip: 1
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(5)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "category-1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return all product categories and count", async () => {
|
||||
const productCategoryResults = await service.listAndCount(
|
||||
{},
|
||||
{
|
||||
select: ["id", "parent_category_id"],
|
||||
relations: ["parent_category"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategoryResults[1]).toEqual(5)
|
||||
expect(productCategoryResults[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "category-0",
|
||||
parent_category: null
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "category-1",
|
||||
parent_category: expect.objectContaining({
|
||||
id: "category-0",
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "category-1-a",
|
||||
parent_category: expect.objectContaining({
|
||||
id: "category-1",
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "category-1-b",
|
||||
parent_category: expect.objectContaining({
|
||||
id: "category-1",
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "category-1-b-1",
|
||||
parent_category: expect.objectContaining({
|
||||
id: "category-1-b",
|
||||
}),
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should only return categories that are scoped by parent_category_id", async () => {
|
||||
let productCategoryResults = await service.listAndCount(
|
||||
{ parent_category_id: null },
|
||||
{
|
||||
select: ["id"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategoryResults[1]).toEqual(1)
|
||||
expect(productCategoryResults[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "category-0",
|
||||
}),
|
||||
])
|
||||
|
||||
productCategoryResults = await service.listAndCount({
|
||||
parent_category_id: "category-0",
|
||||
})
|
||||
|
||||
expect(productCategoryResults[1]).toEqual(1)
|
||||
expect(productCategoryResults[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "category-1",
|
||||
}),
|
||||
])
|
||||
|
||||
productCategoryResults = await service.listAndCount({
|
||||
parent_category_id: ["category-1-b", "category-0"],
|
||||
})
|
||||
|
||||
expect(productCategoryResults[1]).toEqual(2)
|
||||
expect(productCategoryResults[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "category-1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "category-1-b-1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should includes descendants when include_descendants_tree is true", async () => {
|
||||
const productCategoryResults = await service.listAndCount(
|
||||
{
|
||||
parent_category_id: null,
|
||||
include_descendants_tree: true,
|
||||
},
|
||||
{
|
||||
select: ["id", "handle"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategoryResults[1]).toEqual(1)
|
||||
|
||||
const serializedObject = JSON.parse(
|
||||
JSON.stringify(productCategoryResults[0])
|
||||
)
|
||||
|
||||
expect(serializedObject).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "category-0",
|
||||
handle: "category-0",
|
||||
mpath: "category-0.",
|
||||
parent_category_id: null,
|
||||
parent_category: null,
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1",
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: "category-0",
|
||||
parent_category: "category-0",
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1-a",
|
||||
handle: "category-1-a",
|
||||
mpath: "category-0.category-1.category-1-a.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: "category-1",
|
||||
category_children: [],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "category-1-b",
|
||||
handle: "category-1-b",
|
||||
mpath: "category-0.category-1.category-1-b.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: "category-1",
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1-b-1",
|
||||
handle: "category-1-b-1",
|
||||
mpath:
|
||||
"category-0.category-1.category-1-b.category-1-b-1.",
|
||||
parent_category_id: "category-1-b",
|
||||
parent_category: "category-1-b",
|
||||
category_children: [],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should filter out children when include_descendants_tree is true", async () => {
|
||||
const productCategoryResults = await service.listAndCount(
|
||||
{
|
||||
parent_category_id: null,
|
||||
include_descendants_tree: true,
|
||||
is_internal: false,
|
||||
},
|
||||
{
|
||||
select: ["id", "handle"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategoryResults[1]).toEqual(1)
|
||||
|
||||
const serializedObject = JSON.parse(
|
||||
JSON.stringify(productCategoryResults[0])
|
||||
)
|
||||
|
||||
expect(serializedObject).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "category-0",
|
||||
handle: "category-0",
|
||||
mpath: "category-0.",
|
||||
parent_category_id: null,
|
||||
parent_category: null,
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1",
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: "category-0",
|
||||
parent_category: "category-0",
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1-a",
|
||||
handle: "category-1-a",
|
||||
mpath: "category-0.category-1.category-1-a.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: "category-1",
|
||||
category_children: [],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@ import { createCollections } from "../../../__fixtures__/product"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("Product Service", () => {
|
||||
describe("Product collection Service", () => {
|
||||
let service: ProductCollectionService
|
||||
let testManager: SqlEntityManager
|
||||
let repositoryManager: SqlEntityManager
|
||||
@@ -59,9 +59,9 @@ describe("Product Service", () => {
|
||||
})
|
||||
|
||||
it("list product collections", async () => {
|
||||
const tagsResults = await service.list()
|
||||
const productCollectionResults = await service.list()
|
||||
|
||||
expect(tagsResults).toEqual([
|
||||
expect(productCollectionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
@@ -82,9 +82,9 @@ describe("Product Service", () => {
|
||||
})
|
||||
|
||||
it("list product collections by id", async () => {
|
||||
const tagsResults = await service.list({ id: data![0].id })
|
||||
const productCollectionResults = await service.list({ id: data![0].id })
|
||||
|
||||
expect(tagsResults).toEqual([
|
||||
expect(productCollectionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
@@ -93,9 +93,9 @@ describe("Product Service", () => {
|
||||
})
|
||||
|
||||
it("list product collections by title matching string", async () => {
|
||||
const tagsResults = await service.list({ title: "col 3 extra" })
|
||||
const productCollectionResults = await service.list({ title: "col 3 extra" })
|
||||
|
||||
expect(tagsResults).toEqual([
|
||||
expect(productCollectionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-3",
|
||||
title: "col 3 extra",
|
||||
@@ -103,4 +103,174 @@ describe("Product Service", () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCount", () => {
|
||||
const data = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "col 2",
|
||||
},
|
||||
{
|
||||
id: "test-3",
|
||||
title: "col 3 extra",
|
||||
},
|
||||
{
|
||||
id: "test-4",
|
||||
title: "col 4 extra",
|
||||
},
|
||||
]
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
collectionsData = await createCollections(testManager, data)
|
||||
})
|
||||
|
||||
it("should return all collections and count", async () => {
|
||||
const [productCollectionResults, count] = await service.listAndCount()
|
||||
const serialized = JSON.parse(JSON.stringify(productCollectionResults))
|
||||
|
||||
expect(serialized).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-2",
|
||||
title: "col 2",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-3",
|
||||
title: "col 3 extra",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-4",
|
||||
title: "col 4 extra",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return count and collections based on filter data", async () => {
|
||||
const [productCollectionResults, count] = await service.listAndCount({ id: data![0].id })
|
||||
const serialized = JSON.parse(JSON.stringify(productCollectionResults))
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(serialized).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return count and collections based on config data", async () => {
|
||||
const [productCollectionResults, count] = await service.listAndCount({}, {
|
||||
relations: ['products'],
|
||||
select: ['title'],
|
||||
take: 1,
|
||||
skip: 1,
|
||||
})
|
||||
const serialized = JSON.parse(JSON.stringify(productCollectionResults))
|
||||
|
||||
expect(count).toEqual(4)
|
||||
expect(serialized).toEqual([
|
||||
{
|
||||
id: "test-2",
|
||||
title: "col 2",
|
||||
products: []
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
const collectionData = {
|
||||
id: "collection-1",
|
||||
title: "collection 1",
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
await createCollections(testManager, [collectionData])
|
||||
})
|
||||
|
||||
it("should return collection for the given id", async () => {
|
||||
const productCollectionResults = await service.retrieve(
|
||||
collectionData.id,
|
||||
)
|
||||
|
||||
expect(productCollectionResults).toEqual(
|
||||
expect.objectContaining({
|
||||
id: collectionData.id
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when collection with id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual('ProductCollection with id: does-not-exist was not found')
|
||||
})
|
||||
|
||||
it("should throw an error when an id is not provided", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve(undefined as unknown as string)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual('"productCollectionId" must be defined')
|
||||
})
|
||||
|
||||
it("should return collection based on config select param", async () => {
|
||||
const productCollectionResults = await service.retrieve(
|
||||
collectionData.id,
|
||||
{
|
||||
select: ["id", "title"],
|
||||
}
|
||||
)
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(productCollectionResults))
|
||||
|
||||
expect(serialized).toEqual(
|
||||
{
|
||||
id: collectionData.id,
|
||||
title: collectionData.title,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should return collection based on config relation param", async () => {
|
||||
const productCollectionResults = await service.retrieve(
|
||||
collectionData.id,
|
||||
{
|
||||
select: ["id", "title"],
|
||||
relations: ["products"]
|
||||
}
|
||||
)
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(productCollectionResults))
|
||||
|
||||
expect(serialized).toEqual(
|
||||
{
|
||||
id: collectionData.id,
|
||||
title: collectionData.title,
|
||||
products: []
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
import { Product, ProductCategory } from "@models"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
|
||||
import { initialize } from "../../../../src"
|
||||
import { DB_URL, TestDatabase } from "../../../utils"
|
||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||
|
||||
describe("ProductModuleService product categories", () => {
|
||||
let service: IProductModuleService
|
||||
let testManager: SqlEntityManager
|
||||
let repositoryManager: SqlEntityManager
|
||||
let productOne: Product
|
||||
let productTwo: Product
|
||||
let productCategoryOne: ProductCategory
|
||||
let productCategoryTwo: ProductCategory
|
||||
let productCategories: ProductCategory[]
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestDatabase.setupDatabase()
|
||||
repositoryManager = await TestDatabase.forkManager()
|
||||
|
||||
service = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
})
|
||||
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
productTwo = testManager.create(Product, {
|
||||
id: "product-2",
|
||||
title: "product 2",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
const productCategoriesData = [{
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [productOne],
|
||||
},{
|
||||
id: "test-2",
|
||||
name: "category",
|
||||
products: [productTwo],
|
||||
}]
|
||||
|
||||
productCategories = await createProductCategories(
|
||||
testManager,
|
||||
productCategoriesData
|
||||
)
|
||||
|
||||
productCategoryOne = productCategories[0]
|
||||
productCategoryTwo = productCategories[1]
|
||||
|
||||
await testManager.persistAndFlush([productCategoryOne, productCategoryTwo])
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await TestDatabase.clearDatabase()
|
||||
})
|
||||
|
||||
describe("listCategories", () => {
|
||||
it("should return categories queried by ID", async () => {
|
||||
const results = await service.listCategories({
|
||||
id: productCategoryOne.id,
|
||||
})
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return categories based on the options and filter parameter", async () => {
|
||||
let results = await service.listCategories(
|
||||
{
|
||||
id: productCategoryOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listCategories({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for categories", async () => {
|
||||
const results = await service.listCategories(
|
||||
{
|
||||
id: productCategoryOne.id,
|
||||
},
|
||||
{
|
||||
select: ["id", "name", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
})],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCountCategories", () => {
|
||||
it("should return categories and count queried by ID", async () => {
|
||||
const results = await service.listAndCountCategories({
|
||||
id: productCategoryOne.id,
|
||||
})
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return categories and count based on the options and filter parameter", async () => {
|
||||
let results = await service.listAndCountCategories(
|
||||
{
|
||||
id: productCategoryOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listAndCountCategories({}, { take: 1 })
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
|
||||
results = await service.listAndCountCategories({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for categories", async () => {
|
||||
const results = await service.listAndCountCategories(
|
||||
{
|
||||
id: productCategoryOne.id,
|
||||
},
|
||||
{
|
||||
select: ["id", "name", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
})],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveCategory", () => {
|
||||
it("should return the requested category", async () => {
|
||||
const result = await service.retrieveCategory(productCategoryOne.id)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const result = await service.retrieveCategory(
|
||||
productCategoryOne.id,
|
||||
{
|
||||
select: ["id", "name", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
})],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when a category with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieveCategory("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("ProductCategory with id: does-not-exist was not found")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
import { Product, ProductCollection } from "@models"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
|
||||
import { initialize } from "../../../../src"
|
||||
import { DB_URL, TestDatabase } from "../../../utils"
|
||||
import { createCollections } from "../../../__fixtures__/product"
|
||||
|
||||
describe("ProductModuleService product collections", () => {
|
||||
let service: IProductModuleService
|
||||
let testManager: SqlEntityManager
|
||||
let repositoryManager: SqlEntityManager
|
||||
let productOne: Product
|
||||
let productTwo: Product
|
||||
let productCollectionOne: ProductCollection
|
||||
let productCollectionTwo: ProductCollection
|
||||
let productCollections: ProductCollection[]
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestDatabase.setupDatabase()
|
||||
repositoryManager = await TestDatabase.forkManager()
|
||||
|
||||
service = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
})
|
||||
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
productTwo = testManager.create(Product, {
|
||||
id: "product-2",
|
||||
title: "product 2",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
const productCollectionsData = [{
|
||||
id: "test-1",
|
||||
title: "collection 1",
|
||||
products: [productOne],
|
||||
},{
|
||||
id: "test-2",
|
||||
title: "collection",
|
||||
products: [productTwo],
|
||||
}]
|
||||
|
||||
productCollections = await createCollections(
|
||||
testManager,
|
||||
productCollectionsData
|
||||
)
|
||||
|
||||
productCollectionOne = productCollections[0]
|
||||
productCollectionTwo = productCollections[1]
|
||||
|
||||
await testManager.persistAndFlush([productCollectionOne, productCollectionTwo])
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await TestDatabase.clearDatabase()
|
||||
})
|
||||
|
||||
describe("listCollections", () => {
|
||||
it("should return collections queried by ID", async () => {
|
||||
const results = await service.listCollections({
|
||||
id: productCollectionOne.id,
|
||||
})
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return collections based on the options and filter parameter", async () => {
|
||||
let results = await service.listCollections(
|
||||
{
|
||||
id: productCollectionOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listCollections({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for collections", async () => {
|
||||
const results = await service.listCollections(
|
||||
{
|
||||
id: productCollectionOne.id,
|
||||
},
|
||||
{
|
||||
select: ["id", "title", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "collection 1",
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
})
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCountCollections", () => {
|
||||
it("should return collections and count queried by ID", async () => {
|
||||
const results = await service.listAndCountCollections({
|
||||
id: productCollectionOne.id,
|
||||
})
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return collections and count based on the options and filter parameter", async () => {
|
||||
let results = await service.listAndCountCollections(
|
||||
{
|
||||
id: productCollectionOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listAndCountCollections({}, { take: 1 })
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
|
||||
results = await service.listAndCountCollections({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for collections", async () => {
|
||||
const results = await service.listAndCountCollections(
|
||||
{
|
||||
id: productCollectionOne.id,
|
||||
},
|
||||
{
|
||||
select: ["id", "title", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "collection 1",
|
||||
products: [expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
})],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveCollection", () => {
|
||||
it("should return the requested collection", async () => {
|
||||
const result = await service.retrieveCollection(productCollectionOne.id)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "collection 1",
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const result = await service.retrieveCollection(
|
||||
productCollectionOne.id,
|
||||
{
|
||||
select: ["id", "title", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "collection 1",
|
||||
products: [expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
})],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when a collection with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieveCollection("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("ProductCollection with id: does-not-exist was not found")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
import { initialize } from "../../../../src"
|
||||
import { DB_URL, TestDatabase } from "../../../utils"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
import { Product, ProductVariant } from "@models"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
|
||||
describe("ProductModuleService product variants", () => {
|
||||
let service: IProductModuleService
|
||||
let testManager: SqlEntityManager
|
||||
let repositoryManager: SqlEntityManager
|
||||
let variantOne: ProductVariant
|
||||
let variantTwo: ProductVariant
|
||||
let productOne: Product
|
||||
let productTwo: Product
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestDatabase.setupDatabase()
|
||||
repositoryManager = await TestDatabase.forkManager()
|
||||
|
||||
service = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
})
|
||||
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
productTwo = testManager.create(Product, {
|
||||
id: "product-2",
|
||||
title: "product 2",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
variantOne = testManager.create(ProductVariant, {
|
||||
id: "test-1",
|
||||
title: "variant 1",
|
||||
inventory_quantity: 10,
|
||||
product: productOne,
|
||||
})
|
||||
|
||||
variantTwo = testManager.create(ProductVariant, {
|
||||
id: "test-2",
|
||||
title: "variant",
|
||||
inventory_quantity: 10,
|
||||
product: productTwo,
|
||||
})
|
||||
|
||||
await testManager.persistAndFlush([variantOne, variantTwo])
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await TestDatabase.clearDatabase()
|
||||
})
|
||||
|
||||
describe("listAndCountVariants", () => {
|
||||
it("should return variants and count queried by ID", async () => {
|
||||
const results = await service.listAndCountVariants({
|
||||
id: variantOne.id,
|
||||
})
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: variantOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return variants and count based on the options and filter parameter", async () => {
|
||||
let results = await service.listAndCountVariants(
|
||||
{
|
||||
id: variantOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: variantOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listAndCountVariants({}, { take: 1 })
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
|
||||
results = await service.listAndCountVariants({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: variantTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for variants", async () => {
|
||||
const results = await service.listAndCountVariants(
|
||||
{
|
||||
id: variantOne.id,
|
||||
},
|
||||
{
|
||||
select: ["id", "title", "product.title"] as any,
|
||||
relations: ["product"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "variant 1",
|
||||
product_id: "product-1",
|
||||
// TODO: investigate why this is returning more than the expected results
|
||||
product: expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveVariant", () => {
|
||||
it("should return the requested variant", async () => {
|
||||
const result = await service.retrieveVariant(variantOne.id)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "variant 1",
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const result = await service.retrieveVariant(
|
||||
variantOne.id,
|
||||
{
|
||||
select: ["id", "title", "product.title"] as any,
|
||||
relations: ["product"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "variant 1",
|
||||
product_id: "product-1",
|
||||
product: expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when a variant with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieveVariant("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("ProductVariant with id: does-not-exist was not found")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ProductTypes } from "@medusajs/types"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("Product Service", () => {
|
||||
describe("Product tag Service", () => {
|
||||
let service: ProductTagService
|
||||
let testManager: SqlEntityManager
|
||||
let repositoryManager: SqlEntityManager
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TestDatabase } from "../../../utils"
|
||||
import { ProductVariantService } from "@services"
|
||||
import { ProductVariantRepository } from "@repositories"
|
||||
import { ProductService, ProductVariantService } from "@services"
|
||||
import { ProductRepository, ProductVariantRepository } from "@repositories"
|
||||
import { Product, ProductTag, ProductVariant } from "@models"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { Collection } from "@mikro-orm/core"
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
createProductVariants,
|
||||
} from "../../../__fixtures__/product"
|
||||
import { productsData, variantsData } from "../../../__fixtures__/product/data"
|
||||
import { buildProductVariantOnlyData } from "../../../__fixtures__/variant/data/create-variant"
|
||||
|
||||
describe("ProductVariant Service", () => {
|
||||
let service: ProductVariantService
|
||||
@@ -20,6 +21,7 @@ describe("ProductVariant Service", () => {
|
||||
let variantOne: ProductVariant
|
||||
let variantTwo: ProductVariant
|
||||
let productOne: Product
|
||||
const productVariantTestOne = "test-1"
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestDatabase.setupDatabase()
|
||||
@@ -28,8 +30,17 @@ describe("ProductVariant Service", () => {
|
||||
const productVariantRepository = new ProductVariantRepository({
|
||||
manager: repositoryManager,
|
||||
})
|
||||
const productRepository = new ProductRepository({
|
||||
manager: repositoryManager,
|
||||
})
|
||||
|
||||
service = new ProductVariantService({ productVariantRepository })
|
||||
const productService = new ProductService({
|
||||
productRepository,
|
||||
})
|
||||
service = new ProductVariantService({
|
||||
productService,
|
||||
productVariantRepository,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -47,7 +58,7 @@ describe("ProductVariant Service", () => {
|
||||
})
|
||||
|
||||
variantOne = testManager.create(ProductVariant, {
|
||||
id: "test-1",
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
inventory_quantity: 10,
|
||||
product: productOne,
|
||||
@@ -95,7 +106,7 @@ describe("ProductVariant Service", () => {
|
||||
it("passing populate, scopes the results of the response", async () => {
|
||||
const results = await service.list(
|
||||
{
|
||||
id: "test-1",
|
||||
id: productVariantTestOne,
|
||||
},
|
||||
{
|
||||
select: ["id", "title", "product.title"] as any,
|
||||
@@ -105,7 +116,7 @@ describe("ProductVariant Service", () => {
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
product: expect.objectContaining({
|
||||
id: "product-1",
|
||||
@@ -118,7 +129,7 @@ describe("ProductVariant Service", () => {
|
||||
|
||||
expect(JSON.parse(JSON.stringify(results))).toEqual([
|
||||
{
|
||||
id: "test-1",
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
product_id: "product-1",
|
||||
product: {
|
||||
@@ -175,11 +186,148 @@ describe("ProductVariant Service", () => {
|
||||
|
||||
expect(JSON.parse(JSON.stringify(variants))).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
id: productVariantTestOne,
|
||||
title: "variant title",
|
||||
sku: "sku 1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", function () {
|
||||
let products: Product[]
|
||||
let productOptions!: ProductOption[]
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
products = (await createProductAndTags(
|
||||
testManager,
|
||||
productsData
|
||||
)) as Product[]
|
||||
|
||||
productOptions = await createOptions(testManager, [
|
||||
{
|
||||
id: "test-option-1",
|
||||
title: "size",
|
||||
product: products[0],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should create a variant", async () => {
|
||||
const data = buildProductVariantOnlyData({
|
||||
options: [
|
||||
{
|
||||
option: productOptions[0],
|
||||
value: "XS",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const variants = await service.create(products[0].id, [data])
|
||||
|
||||
expect(variants).toHaveLength(1)
|
||||
expect(variants[0].options).toHaveLength(1)
|
||||
|
||||
expect(JSON.parse(JSON.stringify(variants[0]))).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: data.title,
|
||||
sku: data.sku,
|
||||
inventory_quantity: 100,
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
variant_rank: 0,
|
||||
product: expect.objectContaining({
|
||||
id: products[0].id,
|
||||
}),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.options![0].value,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
variantOne = testManager.create(ProductVariant, {
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
inventory_quantity: 10,
|
||||
product: productOne,
|
||||
})
|
||||
|
||||
await testManager.persistAndFlush([variantOne])
|
||||
})
|
||||
|
||||
it("should return the requested variant", async () => {
|
||||
const result = await service.retrieve(variantOne.id)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const result = await service.retrieve(
|
||||
variantOne.id,
|
||||
{
|
||||
select: ["id", "title", "product.title"] as any,
|
||||
relations: ["product"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
product_id: "product-1",
|
||||
product: expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when a variant with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("ProductVariant with id: does-not-exist was not found")
|
||||
})
|
||||
|
||||
it("should throw an error when an id is not provided", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve(undefined as unknown as string)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual('"productVariantId" must be defined')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { TestDatabase } from "../../../utils"
|
||||
import {
|
||||
ProductService,
|
||||
ProductTagService,
|
||||
ProductVariantService,
|
||||
} from "@services"
|
||||
import { ProductService } from "@services"
|
||||
import { ProductRepository } from "@repositories"
|
||||
import { Product, ProductCategory, ProductVariant } from "@models"
|
||||
import { Image, Product, ProductCategory, ProductVariant } from "@models"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { ProductDTO } from "@medusajs/types"
|
||||
|
||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||
import {
|
||||
assignCategoriesToProduct,
|
||||
createImages,
|
||||
createProductAndTags,
|
||||
createProductVariants,
|
||||
} from "../../../__fixtures__/product"
|
||||
@@ -20,13 +17,8 @@ import {
|
||||
productsData,
|
||||
variantsData,
|
||||
} from "../../../__fixtures__/product/data"
|
||||
|
||||
const productVariantService = {
|
||||
list: jest.fn(),
|
||||
} as unknown as ProductVariantService
|
||||
const productTagService = {
|
||||
list: jest.fn(),
|
||||
} as unknown as ProductTagService
|
||||
import { buildProductOnlyData } from "../../../__fixtures__/product/data/create-product"
|
||||
import { kebabCase } from "@medusajs/utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
@@ -48,8 +40,6 @@ describe("Product Service", () => {
|
||||
|
||||
service = new ProductService({
|
||||
productRepository,
|
||||
productVariantService,
|
||||
productTagService,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -57,7 +47,74 @@ describe("Product Service", () => {
|
||||
await TestDatabase.clearDatabase()
|
||||
})
|
||||
|
||||
describe("create", function () {
|
||||
let images: Image[] = []
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
images = await createImages(testManager, ["image-1"])
|
||||
})
|
||||
|
||||
it("should create a product", async () => {
|
||||
const data = buildProductOnlyData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
|
||||
expect(products).toHaveLength(1)
|
||||
expect(JSON.parse(JSON.stringify(products[0]))).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: data.title,
|
||||
handle: kebabCase(data.title),
|
||||
description: data.description,
|
||||
subtitle: data.subtitle,
|
||||
is_giftcard: data.is_giftcard,
|
||||
discountable: data.discountable,
|
||||
thumbnail: images[0].url,
|
||||
status: data.status,
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: images[0].id,
|
||||
url: images[0].url,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("list", () => {
|
||||
describe("soft deleted", function () {
|
||||
let deletedProduct
|
||||
let product
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
const products = await createProductAndTags(testManager, productsData)
|
||||
|
||||
product = products[1]
|
||||
deletedProduct = await service.softDelete([products[0].id])
|
||||
})
|
||||
|
||||
it("should list all products that are not deleted", async () => {
|
||||
const products = await service.list()
|
||||
|
||||
expect(products).toHaveLength(1)
|
||||
expect(products[0].id).toEqual(product.id)
|
||||
})
|
||||
|
||||
it("should list all products including the deleted", async () => {
|
||||
const products = await service.list({}, { withDeleted: true })
|
||||
|
||||
expect(products).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe("relation: tags", () => {
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
@@ -65,7 +122,7 @@ describe("Product Service", () => {
|
||||
products = await createProductAndTags(testManager, productsData)
|
||||
})
|
||||
|
||||
it("filter by id and including relations", async () => {
|
||||
it("should filter by id and including relations", async () => {
|
||||
const productsResult = await service.list(
|
||||
{
|
||||
id: products[0].id,
|
||||
@@ -95,7 +152,7 @@ describe("Product Service", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("filter by id and without relations", async () => {
|
||||
it("should filter by id and without relations", async () => {
|
||||
const productsResult = await service.list({
|
||||
id: products[0].id,
|
||||
})
|
||||
@@ -137,7 +194,7 @@ describe("Product Service", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("filter by categories relation and scope fields", async () => {
|
||||
it("should filter by categories relation and scope fields", async () => {
|
||||
const products = await service.list(
|
||||
{
|
||||
id: workingProduct.id,
|
||||
@@ -187,7 +244,7 @@ describe("Product Service", () => {
|
||||
])
|
||||
})
|
||||
|
||||
it("returns empty array when querying for a category that doesnt exist", async () => {
|
||||
it("should returns empty array when querying for a category that doesnt exist", async () => {
|
||||
const products = await service.list(
|
||||
{
|
||||
id: workingProduct.id,
|
||||
@@ -215,7 +272,7 @@ describe("Product Service", () => {
|
||||
variants = await createProductVariants(testManager, variantsData)
|
||||
})
|
||||
|
||||
it("filter by id and including relations", async () => {
|
||||
it("should filter by id and including relations", async () => {
|
||||
const productsResult = await service.list(
|
||||
{
|
||||
id: products[0].id,
|
||||
@@ -254,4 +311,52 @@ describe("Product Service", () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("softDelete", function () {
|
||||
let images: Image[] = []
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
images = await createImages(testManager, ["image-1"])
|
||||
})
|
||||
|
||||
it("should soft delete a product", async () => {
|
||||
const data = buildProductOnlyData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
const deleteProducts = await service.softDelete(products.map((p) => p.id))
|
||||
|
||||
expect(deleteProducts).toHaveLength(1)
|
||||
expect(deleteProducts[0].deleted_at).not.toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe("restore", function () {
|
||||
let images: Image[] = []
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await TestDatabase.forkManager()
|
||||
|
||||
images = await createImages(testManager, ["image-1"])
|
||||
})
|
||||
|
||||
it("should restore a soft deleted product", async () => {
|
||||
const data = buildProductOnlyData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
const product = products[0]
|
||||
await service.softDelete([product.id])
|
||||
const restoreProducts = await service.restore([product.id])
|
||||
|
||||
expect(restoreProducts).toHaveLength(1)
|
||||
expect(restoreProducts[0].deleted_at).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const { dropDatabase } = require("pg-god")
|
||||
|
||||
const DB_HOST = process.env.DB_HOST
|
||||
const DB_USERNAME = process.env.DB_USERNAME
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD
|
||||
const DB_HOST = process.env.DB_HOST ?? "localhost"
|
||||
const DB_USERNAME = process.env.DB_USERNAME ?? "postgres"
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD ?? ""
|
||||
const DB_NAME = process.env.DB_TEMP_NAME
|
||||
|
||||
const pgGodCredentials = {
|
||||
@@ -16,7 +16,7 @@ afterAll(async () => {
|
||||
await dropDatabase({ databaseName: DB_NAME }, pgGodCredentials)
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`This might fail if it is run during the unit tests since there is no database to drop. Otherwise, please check what is the issue. ${e}`
|
||||
`This might fail if it is run during the unit tests since there is no database to drop. Otherwise, please check what is the issue. ${e.message}`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user