UNPKG

better-auth

Version:

The most comprehensive authentication library for TypeScript.

772 lines (769 loc) 22.3 kB
import { describe, test, expect, beforeAll } from 'vitest'; import { g as generateId } from '../shared/better-auth.BUPPRXfK.mjs'; import 'zod'; import 'better-call'; import '@better-auth/utils/hash'; import '@noble/ciphers/chacha'; import '@noble/ciphers/utils'; import '@noble/ciphers/webcrypto'; import '@better-auth/utils/base64'; import 'jose'; import '@noble/hashes/scrypt'; import '@better-auth/utils'; import '@better-auth/utils/hex'; import '@noble/hashes/utils'; import '../shared/better-auth.B4Qoxdgc.mjs'; import '../shared/better-auth.Cqykj82J.mjs'; import '@better-auth/utils/random'; const adapterTests = { CREATE_MODEL: "create model", CREATE_MODEL_SHOULD_ALWAYS_RETURN_AN_ID: "create model should always return an id", FIND_MODEL: "find model", FIND_MODEL_WITHOUT_ID: "find model without id", FIND_MODEL_WITH_SELECT: "find model with select", FIND_MODEL_WITH_MODIFIED_FIELD_NAME: "find model with modified field name", UPDATE_MODEL: "update model", SHOULD_FIND_MANY: "should find many", SHOULD_FIND_MANY_WITH_WHERE: "should find many with where", SHOULD_FIND_MANY_WITH_OPERATORS: "should find many with operators", SHOULD_WORK_WITH_REFERENCE_FIELDS: "should work with reference fields", SHOULD_FIND_MANY_WITH_SORT_BY: "should find many with sortBy", SHOULD_FIND_MANY_WITH_LIMIT: "should find many with limit", SHOULD_FIND_MANY_WITH_OFFSET: "should find many with offset", SHOULD_UPDATE_WITH_MULTIPLE_WHERE: "should update with multiple where", DELETE_MODEL: "delete model", SHOULD_DELETE_MANY: "should delete many", SHOULD_NOT_THROW_ON_DELETE_RECORD_NOT_FOUND: "shouldn't throw on delete record not found", SHOULD_NOT_THROW_ON_RECORD_NOT_FOUND: "shouldn't throw on record not found", SHOULD_FIND_MANY_WITH_CONTAINS_OPERATOR: "should find many with contains operator", SHOULD_SEARCH_USERS_WITH_STARTS_WITH: "should search users with startsWith", SHOULD_SEARCH_USERS_WITH_ENDS_WITH: "should search users with endsWith", SHOULD_PREFER_GENERATE_ID_IF_PROVIDED: "should prefer generateId if provided" }; const { ...numberIdAdapterTestsCopy } = adapterTests; const numberIdAdapterTests = { ...numberIdAdapterTestsCopy, SHOULD_RETURN_A_NUMBER_ID_AS_A_RESULT: "Should return a number id as a result", SHOULD_INCREMENT_THE_ID_BY_1: "Should increment the id by 1" }; delete numberIdAdapterTests.SHOULD_NOT_THROW_ON_DELETE_RECORD_NOT_FOUND; async function adapterTest({ getAdapter, disableTests: disabledTests, testPrefix }, internalOptions) { const adapter = async () => await getAdapter(internalOptions?.predefinedOptions); async function resetDebugLogs() { (await adapter())?.adapterTestDebugLogs?.resetDebugLogs(); } async function printDebugLogs() { (await adapter())?.adapterTestDebugLogs?.printDebugLogs(); } const user = { name: "user", email: "user@email.com", emailVerified: true, createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() }; test.skipIf(disabledTests?.CREATE_MODEL)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.CREATE_MODEL}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).create({ model: "user", data: user }); user.id = res.id; expect({ name: res.name, email: res.email }).toEqual({ name: user.name, email: user.email }); } ); test.skipIf(disabledTests?.CREATE_MODEL_SHOULD_ALWAYS_RETURN_AN_ID)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.CREATE_MODEL_SHOULD_ALWAYS_RETURN_AN_ID}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).create({ model: "user", data: { name: "test-name-without-id", email: "test-email-without-id@email.com" } }); expect(res).toHaveProperty("id"); expect(typeof res?.id).toEqual("string"); } ); test.skipIf(disabledTests?.FIND_MODEL)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.FIND_MODEL}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).findOne({ model: "user", where: [ { field: "id", value: user.id } ] }); expect({ name: res?.name, email: res?.email }).toEqual({ name: user.name, email: user.email }); } ); test.skipIf(disabledTests?.FIND_MODEL_WITHOUT_ID)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.FIND_MODEL_WITHOUT_ID}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).findOne({ model: "user", where: [ { field: "email", value: user.email } ] }); expect({ name: res?.name, email: res?.email }).toEqual({ name: user.name, email: user.email }); } ); test.skipIf(disabledTests?.FIND_MODEL_WITH_MODIFIED_FIELD_NAME)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.FIND_MODEL_WITH_MODIFIED_FIELD_NAME}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const email = "test-email-with-modified-field@email.com"; const adapter2 = await getAdapter( Object.assign( { user: { fields: { email: "email_address" } } }, internalOptions?.predefinedOptions ) ); const user2 = await adapter2.create({ model: "user", data: { email, name: "test-name-with-modified-field", emailVerified: true, createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() } }); expect(user2.email).toEqual(email); const res = await adapter2.findOne({ model: "user", where: [ { field: "email", value: email } ] }); expect(res).not.toBeNull(); expect(res?.email).toEqual(email); } ); test.skipIf(disabledTests?.FIND_MODEL_WITH_SELECT)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.FIND_MODEL_WITH_SELECT}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).findOne({ model: "user", where: [ { field: "id", value: user.id } ], select: ["email"] }); expect(res).toEqual({ email: user.email }); } ); test.skipIf(disabledTests?.UPDATE_MODEL)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.UPDATE_MODEL}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const newEmail = "updated@email.com"; const res = await (await adapter()).update({ model: "user", where: [ { field: "id", value: user.id } ], update: { email: newEmail } }); expect(res).toMatchObject({ email: newEmail, name: user.name }); } ); test.skipIf(disabledTests?.SHOULD_FIND_MANY)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_FIND_MANY}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).findMany({ model: "user" }); expect(res.length).toBe(3); } ); test.skipIf(disabledTests?.SHOULD_FIND_MANY_WITH_WHERE)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_FIND_MANY_WITH_WHERE}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const user2 = await (await adapter()).create({ model: "user", data: { name: "user2", email: "test@email.com", emailVerified: true, createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() } }); const res = await (await adapter()).findMany({ model: "user", where: [ { field: "id", value: user2.id } ] }); expect(res.length).toBe(1); } ); test.skipIf(disabledTests?.SHOULD_FIND_MANY_WITH_OPERATORS)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_FIND_MANY_WITH_OPERATORS}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const newUser = await (await adapter()).create({ model: "user", data: { name: "user", email: "test-email2@email.com", emailVerified: true, createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() } }); const res = await (await adapter()).findMany({ model: "user", where: [ { field: "id", operator: "in", value: [user.id, newUser.id] } ] }); expect(res.length).toBe(2); } ); test.skipIf(disabledTests?.SHOULD_WORK_WITH_REFERENCE_FIELDS)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_WORK_WITH_REFERENCE_FIELDS}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); let token = null; const user2 = await (await adapter()).create({ model: "user", data: { name: "user", email: "my-email@email.com", emailVerified: true, createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() } }); const session = await (await adapter()).create({ model: "session", data: { token: generateId(), createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date(), userId: user2.id, expiresAt: /* @__PURE__ */ new Date() } }); token = session.token; const res = await (await adapter()).findOne({ model: "session", where: [ { field: "userId", value: user2.id } ] }); const resToken = await (await adapter()).findOne({ model: "session", where: [ { field: "token", value: token } ] }); expect(res).toMatchObject({ userId: user2.id }); expect(resToken).toMatchObject({ userId: user2.id }); } ); test.skipIf(disabledTests?.SHOULD_FIND_MANY_WITH_SORT_BY)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_FIND_MANY_WITH_SORT_BY}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); await (await adapter()).create({ model: "user", data: { name: "a", email: "a@email.com", emailVerified: true, createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() } }); const res = await (await adapter()).findMany({ model: "user", sortBy: { field: "name", direction: "asc" } }); expect(res[0].name).toBe("a"); const res2 = await (await adapter()).findMany({ model: "user", sortBy: { field: "name", direction: "desc" } }); expect(res2[res2.length - 1].name).toBe("a"); } ); test.skipIf(disabledTests?.SHOULD_FIND_MANY_WITH_LIMIT)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_FIND_MANY_WITH_LIMIT}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).findMany({ model: "user", limit: 1 }); expect(res.length).toBe(1); } ); test.skipIf(disabledTests?.SHOULD_FIND_MANY_WITH_OFFSET)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_FIND_MANY_WITH_OFFSET}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).findMany({ model: "user", offset: 2 }); expect(res.length).toBe(5); } ); test.skipIf(disabledTests?.SHOULD_UPDATE_WITH_MULTIPLE_WHERE)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_UPDATE_WITH_MULTIPLE_WHERE}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); await (await adapter()).updateMany({ model: "user", where: [ { field: "name", value: user.name }, { field: "email", value: user.email } ], update: { email: "updated@email.com" } }); const updatedUser = await (await adapter()).findOne({ model: "user", where: [ { field: "email", value: "updated@email.com" } ] }); expect(updatedUser).toMatchObject({ name: user.name, email: "updated@email.com" }); } ); test.skipIf(disabledTests?.DELETE_MODEL)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.DELETE_MODEL}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); await (await adapter()).delete({ model: "user", where: [ { field: "id", value: user.id } ] }); const findRes = await (await adapter()).findOne({ model: "user", where: [ { field: "id", value: user.id } ] }); expect(findRes).toBeNull(); } ); test.skipIf(disabledTests?.SHOULD_DELETE_MANY)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_DELETE_MANY}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); for (const i of ["to-be-delete-1", "to-be-delete-2", "to-be-delete-3"]) { await (await adapter()).create({ model: "user", data: { name: "to-be-deleted", email: `email@test-${i}.com`, emailVerified: true, createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() } }); } const findResFirst = await (await adapter()).findMany({ model: "user", where: [ { field: "name", value: "to-be-deleted" } ] }); expect(findResFirst.length).toBe(3); await (await adapter()).deleteMany({ model: "user", where: [ { field: "name", value: "to-be-deleted" } ] }); const findRes = await (await adapter()).findMany({ model: "user", where: [ { field: "name", value: "to-be-deleted" } ] }); expect(findRes.length).toBe(0); } ); test.skipIf(disabledTests?.SHOULD_NOT_THROW_ON_DELETE_RECORD_NOT_FOUND)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_NOT_THROW_ON_DELETE_RECORD_NOT_FOUND}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); await (await adapter()).delete({ model: "user", where: [ { field: "id", value: "100000" } ] }); } ); test.skipIf(disabledTests?.SHOULD_NOT_THROW_ON_RECORD_NOT_FOUND)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_NOT_THROW_ON_RECORD_NOT_FOUND}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).findOne({ model: "user", where: [ { field: "id", value: "100000" } ] }); expect(res).toBeNull(); } ); test.skipIf(disabledTests?.SHOULD_FIND_MANY_WITH_CONTAINS_OPERATOR)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_FIND_MANY_WITH_CONTAINS_OPERATOR}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).findMany({ model: "user", where: [ { field: "name", operator: "contains", value: "user2" } ] }); expect(res.length).toBe(1); } ); test.skipIf(disabledTests?.SHOULD_SEARCH_USERS_WITH_STARTS_WITH)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_SEARCH_USERS_WITH_STARTS_WITH}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).findMany({ model: "user", where: [ { field: "name", operator: "starts_with", value: "us" } ] }); expect(res.length).toBe(3); } ); test.skipIf(disabledTests?.SHOULD_SEARCH_USERS_WITH_ENDS_WITH)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_SEARCH_USERS_WITH_ENDS_WITH}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).findMany({ model: "user", where: [ { field: "name", operator: "ends_with", value: "er2" } ] }); expect(res.length).toBe(1); } ); test.skipIf(disabledTests?.SHOULD_PREFER_GENERATE_ID_IF_PROVIDED)( `${testPrefix ? `${testPrefix} - ` : ""}${adapterTests.SHOULD_PREFER_GENERATE_ID_IF_PROVIDED}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const customAdapter = await getAdapter( Object.assign( { advanced: { database: { generateId: () => "mocked-id" } } }, internalOptions?.predefinedOptions ) ); const res = await customAdapter.create({ model: "user", data: { name: "user4", email: "user4@email.com", emailVerified: true, createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() } }); expect(res.id).toBe("mocked-id"); } ); } async function runAdapterTest(opts) { return adapterTest(opts); } async function runNumberIdAdapterTest(opts) { const cleanup = []; const adapter = async () => await opts.getAdapter({ advanced: { database: { useNumberId: true } } }); describe("Should run number id specific tests", async () => { let idNumber = -1; async function resetDebugLogs() { (await adapter())?.adapterTestDebugLogs?.resetDebugLogs(); } async function printDebugLogs() { (await adapter())?.adapterTestDebugLogs?.printDebugLogs(); } test.skipIf(opts.disableTests?.SHOULD_RETURN_A_NUMBER_ID_AS_A_RESULT)( `${opts.testPrefix ? `${opts.testPrefix} - ` : ""}${numberIdAdapterTests.SHOULD_RETURN_A_NUMBER_ID_AS_A_RESULT}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { printDebugLogs(); }); const res = await (await adapter()).create({ model: "user", data: { name: "user", email: "user@email.com" } }); cleanup.push({ modelName: "user", id: res.id }); expect(typeof res.id).toBe("string"); expect(parseInt(res.id)).toBeGreaterThan(0); idNumber = parseInt(res.id); } ); test.skipIf(opts.disableTests?.SHOULD_INCREMENT_THE_ID_BY_1)( `${opts.testPrefix ? `${opts.testPrefix} - ` : ""}${numberIdAdapterTests.SHOULD_INCREMENT_THE_ID_BY_1}`, async ({ onTestFailed }) => { resetDebugLogs(); onTestFailed(() => { console.log(`ID number from last create: ${idNumber}`); printDebugLogs(); }); const res = await (await adapter()).create({ model: "user", data: { name: "user2", email: "user2@email.com" } }); cleanup.push({ modelName: "user", id: res.id }); expect(parseInt(res.id)).toBe(idNumber + 1); } ); }); describe("Should run normal adapter tests with number id enabled", async () => { beforeAll(async () => { for (const { modelName, id } of cleanup) { await (await adapter()).delete({ model: modelName, where: [{ field: "id", value: id }] }); } }); await adapterTest( { ...opts, disableTests: { ...opts.disableTests, SHOULD_PREFER_GENERATE_ID_IF_PROVIDED: true } }, { predefinedOptions: { advanced: { database: { useNumberId: true } } } } ); }); } export { runAdapterTest, runNumberIdAdapterTest };