From 333b415635de506c6954841ba8a45da072558884 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 18 Sep 2023 13:46:59 +0200 Subject: [PATCH] Add tests --- .../backend-core/src/cache/tests/user.spec.ts | 95 +++++++++++++++++++ packages/backend-core/src/cache/user.ts | 15 ++- 2 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 packages/backend-core/src/cache/tests/user.spec.ts diff --git a/packages/backend-core/src/cache/tests/user.spec.ts b/packages/backend-core/src/cache/tests/user.spec.ts new file mode 100644 index 0000000000..3d746fc506 --- /dev/null +++ b/packages/backend-core/src/cache/tests/user.spec.ts @@ -0,0 +1,95 @@ +import { User } from "@budibase/types" +import { tenancy } from "../.." +import { generator, structures } from "../../../tests" +import { DBTestConfiguration } from "../../../tests/extra" +import { getUsers } from "../user" +import { getGlobalDB, getGlobalDBName } from "../../context" +import _ from "lodash" +import { getDB } from "../../db" +import type * as TenancyType from "../../tenancy" + +const config = new DBTestConfiguration() + +// This mock is required to ensure that getTenantDB returns always as a singleton. +// This will allow us to spy on the db +const staticDb = getDB(getGlobalDBName(config.tenantId)) +jest.mock("../../tenancy", (): typeof TenancyType => ({ + ...jest.requireActual("../../tenancy"), + getTenantDB: jest.fn().mockImplementation(() => staticDb), +})) + +describe("user cache", () => { + describe("getUsers", () => { + const users: User[] = [] + beforeAll(async () => { + const userCount = 10 + const userIds = generator.arrayOf(() => generator.guid(), { + min: userCount, + max: userCount, + }) + + await config.doInTenant(async () => { + const db = getGlobalDB() + for (const userId of userIds) { + const user = structures.users.user({ _id: userId }) + await db.put(user) + users.push(user) + } + }) + }) + + beforeEach(() => { + jest.clearAllMocks() + }) + + it("when no user is in cache, all of them are retrieved from db", async () => { + const usersToRequest = _.sampleSize(users, 5) + + const userIdsToRequest = usersToRequest.map(x => x._id!) + + jest.spyOn(staticDb, "allDocs") + + const results = await getUsers(userIdsToRequest, config.tenantId) + + expect(results).toHaveLength(5) + expect(results).toEqual( + usersToRequest.map(u => ({ + ...u, + budibaseAccess: true, + _rev: expect.any(String), + })) + ) + + expect(tenancy.getTenantDB).toBeCalledTimes(1) + expect(tenancy.getTenantDB).toBeCalledWith(config.tenantId) + expect(staticDb.allDocs).toBeCalledTimes(1) + expect(staticDb.allDocs).toBeCalledWith({ + keys: userIdsToRequest, + include_docs: true, + limit: 5, + }) + }) + + it("on a second all, all of them are retrieved from cache", async () => { + const usersToRequest = _.sampleSize(users, 5) + + const userIdsToRequest = usersToRequest.map(x => x._id!) + + jest.spyOn(staticDb, "allDocs") + + await getUsers(userIdsToRequest, config.tenantId) + const resultsFromCache = await getUsers(userIdsToRequest, config.tenantId) + + expect(resultsFromCache).toHaveLength(5) + expect(resultsFromCache).toEqual( + usersToRequest.map(u => ({ + ...u, + budibaseAccess: true, + _rev: expect.any(String), + })) + ) + + expect(staticDb.allDocs).toBeCalledTimes(1) + }) + }) +}) diff --git a/packages/backend-core/src/cache/user.ts b/packages/backend-core/src/cache/user.ts index f05cdd42ac..9742b41b65 100644 --- a/packages/backend-core/src/cache/user.ts +++ b/packages/backend-core/src/cache/user.ts @@ -115,11 +115,18 @@ export async function getUsers(userIds: string[], tenantId: string) { // try cache let usersFromCache = await client.bulkGet(userIds) const missingUsersFromCache = userIds.filter(uid => !usersFromCache[uid]) - const usersFromDb = await populateUsersFromDB(missingUsersFromCache, tenantId) - for (const userToCache of usersFromDb) { - await client.store(userToCache._id, userToCache, EXPIRY_SECONDS) + const users = Object.values(usersFromCache) + + if (missingUsersFromCache.length) { + const usersFromDb = await populateUsersFromDB( + missingUsersFromCache, + tenantId + ) + for (const userToCache of usersFromDb) { + await client.store(userToCache._id, userToCache, EXPIRY_SECONDS) + } + users.push(...usersFromDb) } - const users = [...Object.values(usersFromCache), ...usersFromDb] return users }