Files
medusa-store/integration-tests/modules/__tests__/index/sync.spec.ts
Carlos R. L. Rodrigues f764b3a364 feat(index): $nin and $not operators (#13289)
* feat(index): add $not and $nin operators

* logical operator

* test

* test

* types

* logical

* schema ID

* types and $ilike fix

* index type
2025-08-28 11:56:17 +02:00

202 lines
6.1 KiB
TypeScript

import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import { IndexTypes } from "@medusajs/types"
import { defaultCurrencies, Modules } from "@medusajs/utils"
import { setTimeout } from "timers/promises"
import {
adminHeaders,
createAdminUser,
} from "../../../helpers/create-admin-user"
jest.setTimeout(300000)
process.env.ENABLE_INDEX_MODULE = "true"
async function populateData(
api: any,
{ productCount = 1, variantCount = 1, priceCount = 1 } = {}
) {
const shippingProfile = (
await api.post(
`/admin/shipping-profiles`,
{ name: "Test", type: "default" },
adminHeaders
)
).data.shipping_profile
for (let i = 0; i < productCount; i++) {
const payload = {
title: "Test Giftcard " + i,
shipping_profile_id: shippingProfile.id,
description: "test-giftcard-description " + i,
options: [{ title: "Denominations", values: ["100"] }],
variants: new Array(variantCount).fill(0).map((_, j) => ({
title: `Test variant ${i} ${j}`,
sku: `test-variant-${i}-${j}`,
prices: new Array(priceCount).fill(0).map((_, k) => ({
currency_code: Object.values(defaultCurrencies)[k].code,
amount: 10 * k,
})),
options: {
Denominations: "100",
},
})),
}
await api.post("/admin/products", payload, adminHeaders)
}
}
medusaIntegrationTestRunner({
testSuite: ({ getContainer, dbConnection, api, dbConfig }) => {
let indexEngine: IndexTypes.IIndexService
let appContainer
beforeAll(() => {
appContainer = getContainer()
indexEngine = appContainer.resolve(Modules.INDEX)
})
afterAll(() => {
process.env.ENABLE_INDEX_MODULE = "false"
})
beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer)
})
describe("Index engine syncing", () => {
it("should sync the data to the index based on the indexation configuration", async () => {
await populateData(api, {
productCount: 2,
variantCount: 2,
priceCount: 2,
})
await setTimeout(1000)
await dbConnection.raw('TRUNCATE TABLE "index_data";')
await dbConnection.raw('TRUNCATE TABLE "index_relation";')
await dbConnection.raw('TRUNCATE TABLE "index_metadata";')
await dbConnection.raw('TRUNCATE TABLE "index_sync";')
const { data: indexedDataAfterCreation } =
await indexEngine.query<"product">({
fields: [
"product.*",
"product.variants.*",
"product.variants.prices.*",
],
})
expect(indexedDataAfterCreation.length).toBe(0)
// Prevent storage provider to be triggered though
;(indexEngine as any).storageProvider_.onApplicationStart = jest.fn()
// Trigger a sync
;(indexEngine as any).schemaObjectRepresentation_ = null
await (indexEngine as any).onApplicationStart_()
// 28 ms - 6511 records
const { data: results } = await indexEngine.query<"product">({
fields: [
"product.*",
"product.variants.*",
"product.variants.prices.*",
],
})
expect(results.length).toBe(2)
for (const result of results) {
expect(result.variants.length).toBe(2)
for (const variant of result.variants) {
expect(variant.prices.length).toBe(2)
}
}
})
})
it("should sync the data to the index based on the updated indexation configuration", async () => {
await populateData(api)
await setTimeout(1000)
await dbConnection.raw('TRUNCATE TABLE "index_data";')
await dbConnection.raw('TRUNCATE TABLE "index_relation";')
await dbConnection.raw('TRUNCATE TABLE "index_metadata";')
await dbConnection.raw('TRUNCATE TABLE "index_sync";')
const { data: indexedDataAfterCreation } =
await indexEngine.query<"product">({
fields: [
"product.*",
"product.variants.*",
"product.variants.prices.*",
],
})
expect(indexedDataAfterCreation.length).toBe(0)
// Prevent storage provider to be triggered though
;(indexEngine as any).storageProvider_.onApplicationStart = jest.fn()
// Trigger a sync
;(indexEngine as any).schemaObjectRepresentation_ = null
await (indexEngine as any).onApplicationStart_()
const { data: results } = await indexEngine.query<"product">({
fields: [
"product.*",
"product.variants.*",
"product.variants.prices.*",
],
})
expect(results.length).toBe(1)
expect(results[0].variants.length).toBe(1)
expect(results[0].variants[0].prices.length).toBe(1)
// Manually change the indexation configuration
;(indexEngine as any).schemaObjectRepresentation_ = null
;(indexEngine as any).moduleOptions_ = {
...(indexEngine as any).moduleOptions_,
schema: `
type Product @Listeners(values: ["product.created", "product.updated", "product.deleted"]) {
id: ID
title: String
handle: String
variants: [ProductVariant]
}
type ProductVariant @Listeners(values: ["variant.created", "variant.updated", "variant.deleted"]) {
id: ID
product_id: String
sku: String
description: String
}
`,
}
// Trigger a sync
;(indexEngine as any).schemaObjectRepresentation_ = null
await (indexEngine as any).onApplicationStart_()
await setTimeout(3000)
const { data: updatedResults } = await indexEngine.query<"product">({
fields: ["product.*", "product.variants.*"],
})
expect(updatedResults.length).toBe(1)
expect(updatedResults[0].variants.length).toBe(1)
let staledRaws = await dbConnection.raw(
'SELECT * FROM "index_data" WHERE "staled_at" IS NOT NULL'
)
expect(staledRaws.rows.length).toBe(0)
staledRaws = await dbConnection.raw(
'SELECT * FROM "index_relation" WHERE "staled_at" IS NOT NULL'
)
expect(staledRaws.rows.length).toBe(0)
})
},
})