123 lines
3.4 KiB
TypeScript
123 lines
3.4 KiB
TypeScript
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<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(
|
|
`"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)
|
|
})
|
|
},
|
|
})
|