Files
medusa-store/packages/modules/locking/integration-tests/__tests__/index.spec.ts

204 lines
5.6 KiB
TypeScript

import { ILockingModule } from "@medusajs/framework/types"
import { Modules, promiseAll } from "@medusajs/framework/utils"
import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
import { setTimeout } from "node:timers/promises"
jest.setTimeout(10000)
moduleIntegrationTestRunner<ILockingModule>({
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(
`Failed to acquire lock for key "key_name"`
)
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",
}
await expect(
service.acquire(keyToLock, user_1)
).resolves.toBeUndefined()
await expect(
service.acquire(keyToLock, user_1)
).resolves.toBeUndefined()
await expect(service.acquire(keyToLock, user_2)).rejects.toThrowError(
`Failed to acquire lock for key "${keyToLock}"`
)
await expect(service.acquire(keyToLock, user_2)).rejects.toThrowError(
`Failed to acquire lock for key "${keyToLock}"`
)
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 fail to acquire the same key when no owner is provided", async () => {
const keyToLock = "mySpecialKey"
const user_2 = {
ownerId: "user_id_000",
}
await expect(service.acquire(keyToLock)).resolves.toBeUndefined()
await expect(service.acquire(keyToLock)).rejects.toThrow(
`Failed to acquire lock for key "${keyToLock}"`
)
await expect(service.acquire(keyToLock)).rejects.toThrow(
`Failed to acquire lock for key "${keyToLock}"`
)
await expect(service.acquire(keyToLock, user_2)).rejects.toThrow(
`Failed to acquire lock for key "${keyToLock}"`
)
await expect(service.acquire(keyToLock, user_2)).rejects.toThrow(
`Failed to acquire lock for key "${keyToLock}"`
)
const releaseNotLocked = await service.release(keyToLock, {
ownerId: "user_id_000",
})
expect(releaseNotLocked).toBe(false)
const release = await service.release(keyToLock)
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)
})
it("should release lock in case of timeout failure", async () => {
const fn_1 = jest.fn(async () => {
await setTimeout(1010)
return "fn_1"
})
const fn_2 = jest.fn(async () => {
return "fn_2"
})
const fn_3 = jest.fn(async () => {
return "fn_3"
})
const ops = [
service
.execute("lock_key", fn_1, {
timeout: 1,
})
.catch((e) => e),
service
.execute("lock_key", fn_2, {
timeout: 1,
})
.catch((e) => e),
service
.execute("lock_key", fn_3, {
timeout: 2,
})
.catch((e) => e),
]
const res = await promiseAll(ops)
expect(res).toEqual(["fn_1", Error("Timed-out acquiring lock."), "fn_3"])
expect(fn_1).toHaveBeenCalledTimes(1)
expect(fn_2).toHaveBeenCalledTimes(0)
expect(fn_3).toHaveBeenCalledTimes(1)
})
},
})