import { ILockingModule } from "@medusajs/framework/types" import { Modules } from "@medusajs/framework/utils" import { moduleIntegrationTestRunner } from "medusa-test-utils" import { setTimeout } from "node:timers/promises" jest.setTimeout(10000) moduleIntegrationTestRunner({ moduleName: Modules.LOCKING, testSuite: ({ service }) => { describe("Locking Module Service", () => { let stock = 5 function replenishStock() { stock = 5 } function hasStock() { return stock > 0 } async function reduceStock() { await setTimeout(10) stock-- } async function buy() { if (hasStock()) { await reduceStock() return true } return false } it("should execute functions respecting the key locked", async () => { // 10 parallel calls to buy should oversell the stock const prom: any[] = [] for (let i = 0; i < 10; i++) { prom.push(buy()) } await Promise.all(prom) expect(stock).toBe(-5) replenishStock() // 10 parallel calls to buy with lock should not oversell the stock const promWLock: any[] = [] for (let i = 0; i < 10; i++) { promWLock.push(service.execute("item_1", buy)) } await Promise.all(promWLock) expect(stock).toBe(0) }) it("should acquire lock and release it", async () => { await service.acquire("key_name", { ownerId: "user_id_123", }) const userReleased = await service.release("key_name", { ownerId: "user_id_456", }) const anotherUserLock = service.acquire("key_name", { ownerId: "user_id_456", }) expect(userReleased).toBe(false) await expect(anotherUserLock).rejects.toThrowError( `"key_name" is already locked.` ) const releasing = await service.release("key_name", { ownerId: "user_id_123", }) expect(releasing).toBe(true) }) it("should acquire lock and release it during parallel calls", async () => { const keyToLock = "mySpecialKey" const user_1 = { ownerId: "user_id_456", } const user_2 = { ownerId: "user_id_000", } expect(service.acquire(keyToLock, user_1)).resolves.toBeUndefined() expect(service.acquire(keyToLock, user_1)).resolves.toBeUndefined() expect(service.acquire(keyToLock, user_2)).rejects.toThrowError( `"${keyToLock}" is already locked.` ) expect(service.acquire(keyToLock, user_2)).rejects.toThrowError( `"${keyToLock}" is already locked.` ) await service.acquire(keyToLock, user_1) const releaseNotLocked = await service.release(keyToLock, { ownerId: "user_id_000", }) expect(releaseNotLocked).toBe(false) const release = await service.release(keyToLock, user_1) expect(release).toBe(true) }) }) it("should release lock in case of failure", async () => { const fn_1 = jest.fn(async () => { throw new Error("Error") }) const fn_2 = jest.fn(async () => {}) await service.execute("lock_key", fn_1).catch(() => {}) await service.execute("lock_key", fn_2).catch(() => {}) expect(fn_1).toBeCalledTimes(1) expect(fn_2).toBeCalledTimes(1) }) }, })