From 2e7e7af42d49ff7a2a4e6daa662a7e6bba1110e9 Mon Sep 17 00:00:00 2001 From: Arvin <17693119+vindard@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:45:16 -0400 Subject: [PATCH] test(core): move lock service integration tests (#3828) * test(core): move 1st LockService test * test(core): remove tests for unused 'signal' logic * test(core): move lock service ordering test * test(core): move error-releases-lock test --- core/api/test/helpers/random.ts | 2 + .../integration/services/lock-service.spec.ts | 92 +++++++++++++ .../legacy-integration/services/lock.spec.ts | 127 ------------------ 3 files changed, 94 insertions(+), 127 deletions(-) create mode 100644 core/api/test/integration/services/lock-service.spec.ts delete mode 100644 core/api/test/legacy-integration/services/lock.spec.ts diff --git a/core/api/test/helpers/random.ts b/core/api/test/helpers/random.ts index fa97f76976..bf2a79390c 100644 --- a/core/api/test/helpers/random.ts +++ b/core/api/test/helpers/random.ts @@ -11,3 +11,5 @@ export const randomPhone = () => export const randomUserId = () => randomUUID() as UserId export const randomDeviceId = () => randomUUID() as DeviceId + +export const randomWalletId = () => randomUUID() as WalletId diff --git a/core/api/test/integration/services/lock-service.spec.ts b/core/api/test/integration/services/lock-service.spec.ts new file mode 100644 index 0000000000..d6e40aa7a4 --- /dev/null +++ b/core/api/test/integration/services/lock-service.spec.ts @@ -0,0 +1,92 @@ +import { Redis } from "ioredis" + +import { ResourceAttemptsRedlockServiceError } from "@/domain/lock" + +import { LockService } from "@/services/lock" +import { redis } from "@/services/redis" + +import { sleep } from "@/utils" + +import { randomWalletId } from "test/helpers" + +const lockService = LockService() + +describe("LockService", () => { + describe("lockWalletId", () => { + it("returns value from lock", async () => { + const walletId = randomWalletId() + + const lockFn = async () => { + return 1 + } + + const result = await lockFn() + expect(result).toEqual(1) + + const resultFromLock = await lockService.lockWalletId(walletId, lockFn) + expect(result).toEqual(resultFromLock) + }) + + it("fails for multiple calls on locked resource", async () => { + const walletId = randomWalletId() + + const slowLock = lockService.lockWalletId(walletId, async () => { + await sleep(5000) + return 1 + }) + + const fastLock = lockService.lockWalletId(walletId, async () => { + return 2 + }) + + const result = await Promise.race([slowLock, fastLock]) + expect(result).toBeInstanceOf(ResourceAttemptsRedlockServiceError) + }) + + it("second loop starts after first loop has ended", async () => { + const walletId = randomWalletId() + + const order: number[] = [] + + const lock1Fn = async () => { + order.push(1) + await sleep(500) + order.push(2) + return "1st" + } + + const lock2Fn = async () => { + order.push(3) + await sleep(500) + order.push(4) + return "2nd" + } + + await Promise.all([ + lockService.lockWalletId(walletId, lock1Fn), + lockService.lockWalletId(walletId, lock2Fn), + ]) + + expect(order).toStrictEqual([1, 2, 3, 4]) + }) + + it("releases the lock when error is thrown", async () => { + const walletId = randomWalletId() + + const checkLockExist = (client: Redis) => + new Promise((resolve) => + client.get(walletId, (err, res) => { + resolve(!!res) + }), + ) + + await lockService.lockWalletId(walletId, async () => { + expect(await checkLockExist(redis)).toBeTruthy() + await sleep(500) + throw Error("dummy error") + }) + + expect(await checkLockExist(redis)).toBeFalsy() + }) + }) +}) diff --git a/core/api/test/legacy-integration/services/lock.spec.ts b/core/api/test/legacy-integration/services/lock.spec.ts deleted file mode 100644 index ddca915729..0000000000 --- a/core/api/test/legacy-integration/services/lock.spec.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { sleep } from "@/utils" -import { LockService, redlock } from "@/services/lock" - -import { redis } from "@/services/redis" -import { baseLogger } from "@/services/logger" -import { ResourceAttemptsRedlockServiceError } from "@/domain/lock" - -describe("Lock", () => { - describe("lockWalletId", () => { - it("returns ResourceAttemptsLockServiceError when exceed attempts", async () => { - const walletId = "walletId" as WalletId - const lockService = LockService() - - const lock1 = lockService.lockWalletId(walletId, async () => { - await sleep(5000) - return 1 - }) - - const lock2 = lockService.lockWalletId(walletId, async () => { - return 2 - }) - - const result = await Promise.race([lock1, lock2]) - expect(result).toBeInstanceOf(ResourceAttemptsRedlockServiceError) - }) - }) -}) - -const walletId = "1234" - -/* eslint @typescript-eslint/ban-ts-comment: "off" */ -// @ts-ignore-next-line no-implicit-any error -const checkLockExist = (client) => - new Promise((resolve) => - // @ts-ignore-next-line no-implicit-any error - client.get(walletId, (err, res) => { - resolve(!!res) - }), - ) - -describe("Redlock", () => { - it("return value is passed with a promise", async () => { - const result = await redlock({ - path: walletId, - asyncFn: async () => { - return "r" - }, - }) - - expect(result).toBe("r") - }) - - it("use signal if this exist", async () => { - const result = await redlock({ - path: walletId, - asyncFn: async (signal) => { - return redlock({ - path: walletId, - signal, - asyncFn: async () => { - return "r" - }, - }) - }, - }) - - expect(result).toBe("r") - }) - - it("relocking fail if signal is not passed down the tree", async () => { - await expect( - redlock({ - path: walletId, - asyncFn: async () => { - return redlock({ - path: walletId, - asyncFn: async () => { - return "r" - }, - }) - }, - }), - ).resolves.toBeInstanceOf(ResourceAttemptsRedlockServiceError) - }) - - it("second loop start after first loop has ended", async () => { - const order: number[] = [] - - await Promise.all([ - redlock({ - path: walletId, - asyncFn: async () => { - order.push(1) - await sleep(500) - order.push(2) - }, - }), - redlock({ - path: walletId, - asyncFn: async () => { - order.push(3) - await sleep(500) - order.push(4) - }, - }), - ]) - - expect(order).toStrictEqual([1, 2, 3, 4]) - }) - - it("throwing error releases the lock", async () => { - try { - await redlock({ - path: walletId, - asyncFn: async () => { - expect(await checkLockExist(redis)).toBeTruthy() - await sleep(500) - throw Error("dummy error") - }, - }) - } catch (err) { - baseLogger.info(`error is being caught ${err}`) - } - - expect(await checkLockExist(redis)).toBeFalsy() - }) -})