@better-auth/test-utils 1.5.6 → 1.5.7-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/adapter.mjs CHANGED
@@ -7,5 +7,4 @@ import { transactionsTestSuite } from "./suites/transactions.mjs";
7
7
  import { uuidTestSuite } from "./suites/uuid.mjs";
8
8
  import "./suites/index.mjs";
9
9
  import { testAdapter } from "./test-adapter.mjs";
10
-
11
- export { authFlowTestSuite, createTestSuite, enableJoinTests, getNormalTestSuiteTests, joinsTestSuite, normalTestSuite, numberIdTestSuite, testAdapter, transactionsTestSuite, uuidTestSuite };
10
+ export { authFlowTestSuite, createTestSuite, enableJoinTests, getNormalTestSuiteTests, joinsTestSuite, normalTestSuite, numberIdTestSuite, testAdapter, transactionsTestSuite, uuidTestSuite };
@@ -4,7 +4,6 @@ import { TTY_COLORS } from "@better-auth/core/env";
4
4
  import { generateId } from "@better-auth/core/utils/id";
5
5
  import { betterAuth } from "better-auth";
6
6
  import { test } from "vitest";
7
-
8
7
  //#region src/adapter/create-test-suite.ts
9
8
  /**
10
9
  * Deep equality comparison for BetterAuthOptions.
@@ -456,7 +455,7 @@ const createTestSuite = (suiteName, config, tests) => {
456
455
  };
457
456
  };
458
457
  };
459
-
460
458
  //#endregion
461
459
  export { createTestSuite };
460
+
462
461
  //# sourceMappingURL=create-test-suite.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-test-suite.mjs","names":[],"sources":["../src/adapter/create-test-suite.ts"],"sourcesContent":["import type { BetterAuthOptions } from \"@better-auth/core\";\nimport type {\n\tAccount,\n\tSession,\n\tUser,\n\tVerification,\n} from \"@better-auth/core/db\";\nimport { getAuthTables } from \"@better-auth/core/db\";\nimport type { DBAdapter } from \"@better-auth/core/db/adapter\";\nimport {\n\tcreateAdapterFactory,\n\tdeepmerge,\n\tinitGetDefaultModelName,\n} from \"@better-auth/core/db/adapter\";\nimport { TTY_COLORS } from \"@better-auth/core/env\";\nimport { generateId } from \"@better-auth/core/utils/id\";\nimport type { Auth } from \"better-auth\";\nimport { betterAuth } from \"better-auth\";\nimport { test } from \"vitest\";\nimport type { Logger } from \"./test-adapter\";\n\n/**\n * Test entry type that supports both callback and object formats.\n * Object format allows specifying migration options that will be applied before the test runs.\n */\nexport type TestEntry =\n\t| ((context: {\n\t\t\treadonly skip: {\n\t\t\t\t(note?: string | undefined): never;\n\t\t\t\t(condition: boolean, note?: string | undefined): void;\n\t\t\t};\n\t }) => Promise<void>)\n\t| {\n\t\t\tmigrateBetterAuth?: BetterAuthOptions;\n\t\t\ttest: (context: {\n\t\t\t\treadonly skip: {\n\t\t\t\t\t(note?: string | undefined): never;\n\t\t\t\t\t(condition: boolean, note?: string | undefined): void;\n\t\t\t\t};\n\t\t\t}) => Promise<void>;\n\t };\n\n/**\n * Deep equality comparison for BetterAuthOptions.\n * Handles nested objects, arrays, and primitive values.\n */\nfunction deepEqual(a: any, b: any): boolean {\n\tif (a === b) return true;\n\tif (a == null || b == null) return a === b;\n\tif (typeof a !== typeof b) return false;\n\n\tif (typeof a === \"object\") {\n\t\tif (Array.isArray(a) !== Array.isArray(b)) return false;\n\n\t\tif (Array.isArray(a)) {\n\t\t\tif (a.length !== b.length) return false;\n\t\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\t\tif (!deepEqual(a[i], b[i])) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tconst keysA = Object.keys(a);\n\t\tconst keysB = Object.keys(b);\n\n\t\tif (keysA.length !== keysB.length) return false;\n\n\t\tfor (const key of keysA) {\n\t\t\tif (!keysB.includes(key)) return false;\n\t\t\tif (!deepEqual(a[key], b[key])) return false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n/**\n * Statistics tracking for test suites.\n */\nexport type TestSuiteStats = {\n\tmigrationCount: number;\n\ttotalMigrationTime: number;\n\ttestCount: number;\n\tsuiteStartTime: number;\n\tsuiteDuration: number;\n\tsuiteName: string;\n\tgroupingStats?: {\n\t\ttotalGroups: number;\n\t\taverageTestsPerGroup: number;\n\t\tlargestGroupSize: number;\n\t\tsmallestGroupSize: number;\n\t\tgroupsWithMultipleTests: number;\n\t\ttotalTestsInGroups: number;\n\t};\n};\n\ntype GenerateFn = <M extends \"user\" | \"session\" | \"verification\" | \"account\">(\n\tModel: M,\n) => Promise<\n\tM extends \"user\"\n\t\t? User\n\t\t: M extends \"session\"\n\t\t\t? Session\n\t\t\t: M extends \"verification\"\n\t\t\t\t? Verification\n\t\t\t\t: M extends \"account\"\n\t\t\t\t\t? Account\n\t\t\t\t\t: undefined\n>;\n\ntype Success<T> = {\n\tdata: T;\n\terror: null;\n};\n\ntype Failure<E> = {\n\tdata: null;\n\terror: E;\n};\n\ntype Result<T, E = Error> = Success<T> | Failure<E>;\n\nasync function tryCatch<T, E = Error>(\n\tpromise: Promise<T>,\n): Promise<Result<T, E>> {\n\ttry {\n\t\tconst data = await promise;\n\t\treturn { data, error: null };\n\t} catch (error) {\n\t\treturn { data: null, error: error as E };\n\t}\n}\n\nexport type InsertRandomFn = <\n\tM extends \"user\" | \"session\" | \"verification\" | \"account\",\n\tCount extends number = 1,\n>(\n\tmodel: M,\n\tcount?: Count | undefined,\n) => Promise<\n\tCount extends 1\n\t\t? M extends \"user\"\n\t\t\t? [User]\n\t\t\t: M extends \"session\"\n\t\t\t\t? [User, Session]\n\t\t\t\t: M extends \"verification\"\n\t\t\t\t\t? [Verification]\n\t\t\t\t\t: M extends \"account\"\n\t\t\t\t\t\t? [User, Account]\n\t\t\t\t\t\t: [undefined]\n\t\t: Array<\n\t\t\t\tM extends \"user\"\n\t\t\t\t\t? [User]\n\t\t\t\t\t: M extends \"session\"\n\t\t\t\t\t\t? [User, Session]\n\t\t\t\t\t\t: M extends \"verification\"\n\t\t\t\t\t\t\t? [Verification]\n\t\t\t\t\t\t\t: M extends \"account\"\n\t\t\t\t\t\t\t\t? [User, Account]\n\t\t\t\t\t\t\t\t: [undefined]\n\t\t\t>\n>;\n\nexport const createTestSuite = <\n\tTests extends Record<string, TestEntry>,\n\tAdditionalOptions extends Record<string, any> = {},\n>(\n\tsuiteName: string,\n\tconfig: {\n\t\tdefaultBetterAuthOptions?: BetterAuthOptions | undefined;\n\t\t/**\n\t\t * Helpful if the default better auth options require migrations to be run.\n\t\t */\n\t\talwaysMigrate?: boolean | undefined;\n\t\tprefixTests?: string | undefined;\n\t\tcustomIdGenerator?: () => any | Promise<any> | undefined;\n\t},\n\ttests: (\n\t\thelpers: {\n\t\t\tadapter: DBAdapter;\n\t\t\tlog: Logger;\n\t\t\tgenerate: GenerateFn;\n\t\t\tinsertRandom: InsertRandomFn;\n\t\t\t/**\n\t\t\t * A light cleanup function that will only delete rows it knows about.\n\t\t\t */\n\t\t\tcleanup: () => Promise<void>;\n\t\t\t/**\n\t\t\t * A hard cleanup function that will delete all rows from the database.\n\t\t\t */\n\t\t\thardCleanup: () => Promise<void>;\n\t\t\tmodifyBetterAuthOptions: (\n\t\t\t\toptions: BetterAuthOptions,\n\t\t\t\tshouldRunMigrations: boolean,\n\t\t\t) => Promise<BetterAuthOptions>;\n\t\t\tgetBetterAuthOptions: () => BetterAuthOptions;\n\t\t\tsortModels: (\n\t\t\t\tmodels: Array<\n\t\t\t\t\tRecord<string, any> & {\n\t\t\t\t\t\tid: string;\n\t\t\t\t\t}\n\t\t\t\t>,\n\t\t\t\tby?: (\"id\" | \"createdAt\") | undefined,\n\t\t\t) => (Record<string, any> & {\n\t\t\t\tid: string;\n\t\t\t})[];\n\t\t\tgetAuth: () => Promise<Auth>;\n\t\t\ttryCatch<T, E = Error>(promise: Promise<T>): Promise<Result<T, E>>;\n\t\t\tcustomIdGenerator?: () => any | Promise<any> | undefined;\n\t\t\ttransformIdOutput?: (id: any) => string | undefined;\n\t\t\t/**\n\t\t\t * Some adapters may change the ID type, this function allows you to pass the entire model\n\t\t\t * data and it will return the correct better-auth-expected transformed data.\n\t\t\t *\n\t\t\t * Eg:\n\t\t\t * MongoDB uses ObjectId for IDs, but it's possible the user can disable that option in the adapter config.\n\t\t\t * Because of this, the expected data would be a string.\n\t\t\t * These sorts of conversions will cause issues with the test when you use the `generate` function.\n\t\t\t * This is because the `generate` function will return the raw data expected to be saved in DB, not the excpected BA output.\n\t\t\t */\n\t\t\ttransformGeneratedModel: (\n\t\t\t\tdata: Record<string, any>,\n\t\t\t) => Record<string, any>;\n\t\t},\n\t\tadditionalOptions?: AdditionalOptions | undefined,\n\t) => Tests,\n) => {\n\treturn (\n\t\toptions?:\n\t\t\t| ({\n\t\t\t\t\tdisableTests?: Partial<\n\t\t\t\t\t\tRecord<keyof Tests, boolean> & { ALL?: boolean }\n\t\t\t\t\t>;\n\t\t\t } & AdditionalOptions)\n\t\t\t| undefined,\n\t) => {\n\t\treturn async (helpers: {\n\t\t\tadapter: () => Promise<DBAdapter<BetterAuthOptions>>;\n\t\t\tlog: Logger;\n\t\t\tadapterDisplayName: string;\n\t\t\tgetBetterAuthOptions: () => BetterAuthOptions;\n\t\t\tmodifyBetterAuthOptions: (\n\t\t\t\toptions: BetterAuthOptions,\n\t\t\t) => Promise<BetterAuthOptions>;\n\t\t\tcleanup: () => Promise<void>;\n\t\t\trunMigrations: () => Promise<void>;\n\t\t\tprefixTests?: string | undefined;\n\t\t\tonTestFinish: (stats: TestSuiteStats) => Promise<void>;\n\t\t\tcustomIdGenerator?: () => any | Promise<any> | undefined;\n\t\t\ttransformIdOutput?: (id: any) => string | undefined;\n\t\t}) => {\n\t\t\tconst createdRows: Record<string, any[]> = {};\n\n\t\t\tlet adapter = await helpers.adapter();\n\t\t\tconst wrapperAdapter = (\n\t\t\t\toverrideOptions?: BetterAuthOptions | undefined,\n\t\t\t) => {\n\t\t\t\tconst options = deepmerge(\n\t\t\t\t\tdeepmerge(\n\t\t\t\t\t\thelpers.getBetterAuthOptions(),\n\t\t\t\t\t\tconfig?.defaultBetterAuthOptions || {},\n\t\t\t\t\t),\n\t\t\t\t\toverrideOptions || {},\n\t\t\t\t);\n\t\t\t\tconst adapterConfig = {\n\t\t\t\t\tadapterId: helpers.adapterDisplayName,\n\t\t\t\t\t...(adapter.options?.adapterConfig || {}),\n\t\t\t\t\tadapterName: `Wrapped ${adapter.options?.adapterConfig.adapterName}`,\n\t\t\t\t\tdisableTransformOutput: true,\n\t\t\t\t\tdisableTransformInput: true,\n\t\t\t\t\tdisableTransformJoin: true,\n\t\t\t\t};\n\t\t\t\tconst adapterCreator = (\n\t\t\t\t\toptions: BetterAuthOptions,\n\t\t\t\t): DBAdapter<BetterAuthOptions> =>\n\t\t\t\t\tcreateAdapterFactory({\n\t\t\t\t\t\tconfig: {\n\t\t\t\t\t\t\t...adapterConfig,\n\t\t\t\t\t\t\ttransaction: adapter.transaction,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tadapter: ({ getDefaultModelName }) => {\n\t\t\t\t\t\t\tadapter.transaction = undefined as any;\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcount: async (args: any) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.count(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tdeleteMany: async (args: any) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.deleteMany(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tdelete: async (args: any) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.delete(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tfindOne: async (args) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.findOne(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tfindMany: async (args) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.findMany(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tupdate: async (args: any) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.update(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tupdateMany: async (args) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.updateMany(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tcreateSchema: adapter.createSchema as any,\n\t\t\t\t\t\t\t\tasync create({ data, model, select }) {\n\t\t\t\t\t\t\t\t\tconst defaultModelName = getDefaultModelName(model);\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.create({\n\t\t\t\t\t\t\t\t\t\tdata: data,\n\t\t\t\t\t\t\t\t\t\tmodel: defaultModelName,\n\t\t\t\t\t\t\t\t\t\tselect,\n\t\t\t\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tcreatedRows[model] = [...(createdRows[model] || []), res];\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\toptions: adapter.options,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t},\n\t\t\t\t\t})(options);\n\n\t\t\t\treturn adapterCreator(options);\n\t\t\t};\n\n\t\t\tconst resetDebugLogs = () => {\n\t\t\t\t//@ts-expect-error\n\t\t\t\twrapperAdapter()?.adapterTestDebugLogs?.resetDebugLogs();\n\t\t\t};\n\n\t\t\tconst printDebugLogs = () => {\n\t\t\t\t//@ts-expect-error\n\t\t\t\twrapperAdapter()?.adapterTestDebugLogs?.printDebugLogs();\n\t\t\t};\n\n\t\t\tconst cleanupCreatedRows = async () => {\n\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\tfor (const model of Object.keys(createdRows)) {\n\t\t\t\t\tfor (const row of createdRows[model]!) {\n\t\t\t\t\t\tconst schema = getAuthTables(helpers.getBetterAuthOptions());\n\t\t\t\t\t\tconst getDefaultModelName = initGetDefaultModelName({\n\t\t\t\t\t\t\tschema,\n\t\t\t\t\t\t\tusePlural: adapter.options?.adapterConfig.usePlural,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tlet defaultModelName: string;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tdefaultModelName = getDefaultModelName(model);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!schema[defaultModelName]) continue; // model doesn't exist in the schema anymore, so we skip it\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait adapter.delete({\n\t\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t\twhere: [{ field: \"id\", value: row.id }],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// We ignore any failed attempts to delete the created rows.\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (createdRows[model]!.length === 1) {\n\t\t\t\t\t\t\tdelete createdRows[model];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Track current applied BetterAuth options state\n\t\t\tlet currentAppliedOptions: BetterAuthOptions | null = null;\n\n\t\t\t// Statistics tracking\n\t\t\tconst stats: TestSuiteStats = {\n\t\t\t\tmigrationCount: 0,\n\t\t\t\ttotalMigrationTime: 0,\n\t\t\t\ttestCount: 0,\n\t\t\t\tsuiteStartTime: performance.now(),\n\t\t\t\tsuiteDuration: 0,\n\t\t\t\tsuiteName,\n\t\t\t};\n\n\t\t\t/**\n\t\t\t * Apply BetterAuth options and run migrations if needed.\n\t\t\t * Tracks migration statistics.\n\t\t\t */\n\t\t\tconst applyOptionsAndMigrate = async (\n\t\t\t\toptions: BetterAuthOptions | Partial<BetterAuthOptions>,\n\t\t\t\tforceMigrate: boolean = false,\n\t\t\t): Promise<BetterAuthOptions> => {\n\t\t\t\tconst finalOptions = deepmerge(\n\t\t\t\t\tconfig?.defaultBetterAuthOptions || {},\n\t\t\t\t\toptions || {},\n\t\t\t\t);\n\n\t\t\t\t// Check if options have changed\n\t\t\t\tconst optionsChanged = !deepEqual(currentAppliedOptions, finalOptions);\n\n\t\t\t\tif (optionsChanged || forceMigrate) {\n\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\tawait helpers.modifyBetterAuthOptions(finalOptions);\n\n\t\t\t\t\tif (config.alwaysMigrate || forceMigrate) {\n\t\t\t\t\t\tconst migrationStart = performance.now();\n\t\t\t\t\t\tawait helpers.runMigrations();\n\t\t\t\t\t\tconst migrationTime = performance.now() - migrationStart;\n\n\t\t\t\t\t\tstats.migrationCount++;\n\t\t\t\t\t\tstats.totalMigrationTime += migrationTime;\n\n\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrentAppliedOptions = finalOptions;\n\t\t\t\t} else {\n\t\t\t\t\t// Options haven't changed, just update the reference\n\t\t\t\t\tcurrentAppliedOptions = finalOptions;\n\t\t\t\t}\n\n\t\t\t\treturn finalOptions;\n\t\t\t};\n\n\t\t\tconst transformGeneratedModel = (data: Record<string, any>) => {\n\t\t\t\tconst newData = { ...data };\n\t\t\t\tif (helpers.transformIdOutput) {\n\t\t\t\t\tnewData.id = helpers.transformIdOutput(newData.id);\n\t\t\t\t}\n\t\t\t\treturn newData;\n\t\t\t};\n\n\t\t\tconst idGenerator = async () => {\n\t\t\t\tif (config.customIdGenerator) {\n\t\t\t\t\treturn config.customIdGenerator();\n\t\t\t\t}\n\t\t\t\tif (helpers.customIdGenerator) {\n\t\t\t\t\treturn helpers.customIdGenerator();\n\t\t\t\t}\n\t\t\t\treturn generateId();\n\t\t\t};\n\n\t\t\tconst generateModel: GenerateFn = async (model: string) => {\n\t\t\t\tconst id = await idGenerator();\n\t\t\t\tconst randomDate = new Date(\n\t\t\t\t\tDate.now() - Math.random() * 1000 * 60 * 60 * 24 * 365,\n\t\t\t\t);\n\t\t\t\tif (model === \"user\") {\n\t\t\t\t\tconst user: User = {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcreatedAt: randomDate,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\temail:\n\t\t\t\t\t\t\t`user-${helpers.transformIdOutput?.(id) ?? id}@email.com`.toLowerCase(),\n\t\t\t\t\t\temailVerified: true,\n\t\t\t\t\t\tname: `user-${helpers.transformIdOutput?.(id) ?? id}`,\n\t\t\t\t\t\timage: null,\n\t\t\t\t\t};\n\t\t\t\t\treturn user as any;\n\t\t\t\t}\n\t\t\t\tif (model === \"session\") {\n\t\t\t\t\tconst session: Session = {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcreatedAt: randomDate,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\texpiresAt: new Date(),\n\t\t\t\t\t\ttoken: generateId(32),\n\t\t\t\t\t\tuserId: generateId(),\n\t\t\t\t\t\tipAddress: \"127.0.0.1\",\n\t\t\t\t\t\tuserAgent: \"Some User Agent\",\n\t\t\t\t\t};\n\t\t\t\t\treturn session as any;\n\t\t\t\t}\n\t\t\t\tif (model === \"verification\") {\n\t\t\t\t\tconst verification: Verification = {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcreatedAt: randomDate,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\texpiresAt: new Date(),\n\t\t\t\t\t\tidentifier: `test:${generateId()}`,\n\t\t\t\t\t\tvalue: generateId(),\n\t\t\t\t\t};\n\t\t\t\t\treturn verification as any;\n\t\t\t\t}\n\t\t\t\tif (model === \"account\") {\n\t\t\t\t\tconst account: Account = {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcreatedAt: randomDate,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\taccountId: generateId(),\n\t\t\t\t\t\tproviderId: \"test\",\n\t\t\t\t\t\tuserId: generateId(),\n\t\t\t\t\t\taccessToken: generateId(),\n\t\t\t\t\t\trefreshToken: generateId(),\n\t\t\t\t\t\tidToken: generateId(),\n\t\t\t\t\t\taccessTokenExpiresAt: new Date(),\n\t\t\t\t\t\trefreshTokenExpiresAt: new Date(),\n\t\t\t\t\t\tscope: \"test\",\n\t\t\t\t\t};\n\t\t\t\t\treturn account as any;\n\t\t\t\t}\n\t\t\t\t// This should never happen given the type constraints, but TypeScript needs an exhaustive check\n\t\t\t\tthrow new Error(`Unknown model type: ${model}`);\n\t\t\t};\n\n\t\t\tconst insertRandom: InsertRandomFn = async <\n\t\t\t\tM extends \"user\" | \"session\" | \"verification\" | \"account\",\n\t\t\t\tCount extends number = 1,\n\t\t\t>(\n\t\t\t\tmodel: M,\n\t\t\t\tcount: Count = 1 as Count,\n\t\t\t) => {\n\t\t\t\tconst res: any[] = [];\n\t\t\t\tconst a = wrapperAdapter();\n\n\t\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\t\tconst modelResults = [];\n\n\t\t\t\t\tif (model === \"user\") {\n\t\t\t\t\t\tconst user = await generateModel(\"user\");\n\t\t\t\t\t\tmodelResults.push(\n\t\t\t\t\t\t\tawait a.create({\n\t\t\t\t\t\t\t\tdata: user,\n\t\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (model === \"session\") {\n\t\t\t\t\t\tconst user = await generateModel(\"user\");\n\t\t\t\t\t\tconst userRes = await a.create({\n\t\t\t\t\t\t\tdata: user,\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst session = await generateModel(\"session\");\n\t\t\t\t\t\tsession.userId = userRes.id;\n\t\t\t\t\t\tconst sessionRes = await a.create({\n\t\t\t\t\t\t\tdata: session,\n\t\t\t\t\t\t\tmodel: \"session\",\n\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tmodelResults.push(userRes, sessionRes);\n\t\t\t\t\t}\n\t\t\t\t\tif (model === \"verification\") {\n\t\t\t\t\t\tconst verification = await generateModel(\"verification\");\n\t\t\t\t\t\tmodelResults.push(\n\t\t\t\t\t\t\tawait a.create({\n\t\t\t\t\t\t\t\tdata: verification,\n\t\t\t\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (model === \"account\") {\n\t\t\t\t\t\tconst user = await generateModel(\"user\");\n\t\t\t\t\t\tconst account = await generateModel(\"account\");\n\t\t\t\t\t\tconst userRes = await a.create({\n\t\t\t\t\t\t\tdata: user,\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t\taccount.userId = userRes.id;\n\t\t\t\t\t\tconst accRes = await a.create({\n\t\t\t\t\t\t\tdata: account,\n\t\t\t\t\t\t\tmodel: \"account\",\n\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tmodelResults.push(userRes, accRes);\n\t\t\t\t\t}\n\t\t\t\t\tres.push(modelResults);\n\t\t\t\t}\n\t\t\t\treturn res.length === 1 ? res[0] : (res as any);\n\t\t\t};\n\n\t\t\tconst sortModels = (\n\t\t\t\tmodels: Array<Record<string, any> & { id: string }>,\n\t\t\t\tby: \"id\" | \"createdAt\" = \"id\",\n\t\t\t) => {\n\t\t\t\treturn models.sort((a, b) => {\n\t\t\t\t\tif (by === \"createdAt\") {\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tnew Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn a.id.localeCompare(b.id);\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst modifyBetterAuthOptions = async (\n\t\t\t\topts: BetterAuthOptions,\n\t\t\t\tshouldRunMigrations: boolean,\n\t\t\t) => {\n\t\t\t\treturn await applyOptionsAndMigrate(opts, shouldRunMigrations);\n\t\t\t};\n\n\t\t\tconst additionalOptions = { ...options };\n\t\t\tadditionalOptions.disableTests = undefined;\n\n\t\t\tconst fullTests = tests(\n\t\t\t\t{\n\t\t\t\t\tadapter: new Proxy({} as any, {\n\t\t\t\t\t\tget(target, prop) {\n\t\t\t\t\t\t\tconst adapter = wrapperAdapter();\n\t\t\t\t\t\t\tif (prop === \"transaction\") {\n\t\t\t\t\t\t\t\treturn adapter.transaction;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst value = adapter[prop as keyof typeof adapter];\n\t\t\t\t\t\t\tif (typeof value === \"function\") {\n\t\t\t\t\t\t\t\treturn value.bind(adapter);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tgetAuth: async () => {\n\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\tconst auth = betterAuth({\n\t\t\t\t\t\t\t...helpers.getBetterAuthOptions(),\n\t\t\t\t\t\t\t...(config?.defaultBetterAuthOptions || {}),\n\t\t\t\t\t\t\tdatabase: (options: BetterAuthOptions) => {\n\t\t\t\t\t\t\t\tconst adapter = wrapperAdapter(options);\n\t\t\t\t\t\t\t\treturn adapter;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t} as BetterAuthOptions);\n\t\t\t\t\t\treturn auth;\n\t\t\t\t\t},\n\t\t\t\t\tlog: helpers.log,\n\t\t\t\t\tgenerate: generateModel,\n\t\t\t\t\tcleanup: cleanupCreatedRows,\n\t\t\t\t\thardCleanup: helpers.cleanup,\n\t\t\t\t\tinsertRandom,\n\t\t\t\t\tmodifyBetterAuthOptions,\n\t\t\t\t\tgetBetterAuthOptions: helpers.getBetterAuthOptions,\n\t\t\t\t\tsortModels,\n\t\t\t\t\ttryCatch,\n\t\t\t\t\tcustomIdGenerator: helpers.customIdGenerator,\n\t\t\t\t\ttransformGeneratedModel,\n\t\t\t\t\ttransformIdOutput: helpers.transformIdOutput,\n\t\t\t\t},\n\t\t\t\tadditionalOptions as AdditionalOptions,\n\t\t\t);\n\n\t\t\tconst dash = `─`;\n\t\t\tconst allDisabled: boolean = options?.disableTests?.ALL ?? false;\n\n\t\t\t// Here to display a label in the tests showing the suite name\n\t\t\ttest(`\\n${TTY_COLORS.fg.white}${\" \".repeat(3)}${dash.repeat(35)} [${TTY_COLORS.fg.magenta}${suiteName}${TTY_COLORS.fg.white}] ${dash.repeat(35)}`, async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait helpers.cleanup();\n\t\t\t\t} catch {}\n\t\t\t\tif (config.defaultBetterAuthOptions && !allDisabled) {\n\t\t\t\t\tawait applyOptionsAndMigrate(\n\t\t\t\t\t\tconfig.defaultBetterAuthOptions,\n\t\t\t\t\t\tconfig.alwaysMigrate,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t/**\n\t\t\t * Extract test function and migration options from a test entry.\n\t\t\t */\n\t\t\tconst extractTestEntry = (\n\t\t\t\tentry: TestEntry,\n\t\t\t): {\n\t\t\t\ttestFn: (context: {\n\t\t\t\t\treadonly skip: {\n\t\t\t\t\t\t(note?: string | undefined): never;\n\t\t\t\t\t\t(condition: boolean, note?: string | undefined): void;\n\t\t\t\t\t};\n\t\t\t\t}) => Promise<void>;\n\t\t\t\tmigrateBetterAuth?: BetterAuthOptions;\n\t\t\t} => {\n\t\t\t\tif (typeof entry === \"function\") {\n\t\t\t\t\treturn { testFn: entry };\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\ttestFn: entry.test,\n\t\t\t\t\tmigrateBetterAuth: entry.migrateBetterAuth,\n\t\t\t\t};\n\t\t\t};\n\n\t\t\t// Convert test entries to array with migration info (moved before onFinish for access)\n\t\t\tconst testEntries = Object.entries(fullTests).map(([name, entry]) => {\n\t\t\t\tconst { testFn, migrateBetterAuth } = extractTestEntry(\n\t\t\t\t\tentry as TestEntry,\n\t\t\t\t);\n\t\t\t\treturn { name, testFn, migrateBetterAuth };\n\t\t\t});\n\n\t\t\t/**\n\t\t\t * Group tests by their migrateBetterAuth options.\n\t\t\t * Tests with equal migration options are grouped together.\n\t\t\t */\n\t\t\ttype TestGroup = {\n\t\t\t\tmigrationOptions: BetterAuthOptions | null | undefined;\n\t\t\t\ttestIndices: number[];\n\t\t\t};\n\n\t\t\tconst groupTestsByMigrationOptions = (): TestGroup[] => {\n\t\t\t\tconst groups: TestGroup[] = [];\n\t\t\t\tlet currentGroup: TestGroup | null = null;\n\n\t\t\t\tfor (let i = 0; i < testEntries.length; i++) {\n\t\t\t\t\tconst { migrateBetterAuth } = testEntries[i]!;\n\t\t\t\t\tconst isSkipped =\n\t\t\t\t\t\t(allDisabled &&\n\t\t\t\t\t\t\toptions?.disableTests?.[testEntries[i]!.name] !== false) ||\n\t\t\t\t\t\t(options?.disableTests?.[testEntries[i]!.name] ?? false);\n\n\t\t\t\t\t// Skip grouping for skipped tests - they'll be handled individually\n\t\t\t\t\tif (isSkipped) {\n\t\t\t\t\t\tif (currentGroup) {\n\t\t\t\t\t\t\tgroups.push(currentGroup);\n\t\t\t\t\t\t\tcurrentGroup = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgroups.push({\n\t\t\t\t\t\t\tmigrationOptions: migrateBetterAuth,\n\t\t\t\t\t\t\ttestIndices: [i],\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if this test belongs to the current group\n\t\t\t\t\tif (\n\t\t\t\t\t\tcurrentGroup &&\n\t\t\t\t\t\tdeepEqual(currentGroup.migrationOptions, migrateBetterAuth)\n\t\t\t\t\t) {\n\t\t\t\t\t\tcurrentGroup.testIndices.push(i);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Start a new group\n\t\t\t\t\t\tif (currentGroup) {\n\t\t\t\t\t\t\tgroups.push(currentGroup);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcurrentGroup = {\n\t\t\t\t\t\t\tmigrationOptions: migrateBetterAuth,\n\t\t\t\t\t\t\ttestIndices: [i],\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Add the last group if it exists\n\t\t\t\tif (currentGroup) {\n\t\t\t\t\tgroups.push(currentGroup);\n\t\t\t\t}\n\n\t\t\t\treturn groups;\n\t\t\t};\n\n\t\t\tconst testGroups = groupTestsByMigrationOptions();\n\n\t\t\t// Calculate grouping statistics\n\t\t\tconst calculateGroupingStats = () => {\n\t\t\t\tconst nonSkippedGroups = testGroups.filter(\n\t\t\t\t\t(group) => group.testIndices.length > 0,\n\t\t\t\t);\n\t\t\t\tconst groupSizes = nonSkippedGroups.map(\n\t\t\t\t\t(group) => group.testIndices.length,\n\t\t\t\t);\n\n\t\t\t\tif (groupSizes.length === 0) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttotalGroups: 0,\n\t\t\t\t\t\taverageTestsPerGroup: 0,\n\t\t\t\t\t\tlargestGroupSize: 0,\n\t\t\t\t\t\tsmallestGroupSize: 0,\n\t\t\t\t\t\tgroupsWithMultipleTests: 0,\n\t\t\t\t\t\ttotalTestsInGroups: 0,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tconst totalTestsInGroups = groupSizes.reduce(\n\t\t\t\t\t(sum, size) => sum + size,\n\t\t\t\t\t0,\n\t\t\t\t);\n\t\t\t\tconst groupsWithMultipleTests = groupSizes.filter(\n\t\t\t\t\t(size) => size > 1,\n\t\t\t\t).length;\n\n\t\t\t\treturn {\n\t\t\t\t\ttotalGroups: nonSkippedGroups.length,\n\t\t\t\t\taverageTestsPerGroup: totalTestsInGroups / nonSkippedGroups.length,\n\t\t\t\t\tlargestGroupSize: Math.max(...groupSizes),\n\t\t\t\t\tsmallestGroupSize: Math.min(...groupSizes),\n\t\t\t\t\tgroupsWithMultipleTests,\n\t\t\t\t\ttotalTestsInGroups,\n\t\t\t\t};\n\t\t\t};\n\n\t\t\tconst onFinish = async (testName: string) => {\n\t\t\t\tawait cleanupCreatedRows();\n\n\t\t\t\tconst currentTestIndex = testEntries.findIndex(\n\t\t\t\t\t({ name }) => name === testName,\n\t\t\t\t);\n\t\t\t\tconst isLastTest = currentTestIndex === testEntries.length - 1;\n\n\t\t\t\tif (isLastTest) {\n\t\t\t\t\tstats.suiteDuration = performance.now() - stats.suiteStartTime;\n\t\t\t\t\tstats.groupingStats = calculateGroupingStats();\n\t\t\t\t\tawait helpers.onTestFinish(stats);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Track the current group's migration options\n\t\t\tlet currentGroupMigrationOptions: BetterAuthOptions | null | undefined =\n\t\t\t\tnull;\n\n\t\t\tfor (let i = 0; i < testEntries.length; i++) {\n\t\t\t\tconst { name: testName, testFn, migrateBetterAuth } = testEntries[i]!;\n\n\t\t\t\t// Find which group this test belongs to\n\t\t\t\tconst testGroup = testGroups.find((group) =>\n\t\t\t\t\tgroup.testIndices.includes(i),\n\t\t\t\t);\n\t\t\t\tconst isFirstInGroup = testGroup && testGroup.testIndices[0] === i;\n\n\t\t\t\tconst shouldSkip =\n\t\t\t\t\t(allDisabled && options?.disableTests?.[testName] !== false) ||\n\t\t\t\t\t(options?.disableTests?.[testName] ?? false);\n\n\t\t\t\tlet displayName = testName.replace(\n\t\t\t\t\t\" - \",\n\t\t\t\t\t` ${TTY_COLORS.dim}${dash}${TTY_COLORS.undim} `,\n\t\t\t\t);\n\t\t\t\tif (config.prefixTests) {\n\t\t\t\t\tdisplayName = `${config.prefixTests} ${TTY_COLORS.dim}>${TTY_COLORS.undim} ${displayName}`;\n\t\t\t\t}\n\t\t\t\tif (helpers.prefixTests) {\n\t\t\t\t\tdisplayName = `[${TTY_COLORS.dim}${helpers.prefixTests}${TTY_COLORS.undim}] ${displayName}`;\n\t\t\t\t}\n\n\t\t\t\ttest.skipIf(shouldSkip)(\n\t\t\t\t\tdisplayName,\n\t\t\t\t\t{ timeout: 30000 },\n\t\t\t\t\tasync ({ onTestFailed, skip }) => {\n\t\t\t\t\t\tresetDebugLogs();\n\n\t\t\t\t\t\t// Apply migration options before test runs\n\t\t\t\t\t\tawait (async () => {\n\t\t\t\t\t\t\tif (shouldSkip) return;\n\n\t\t\t\t\t\t\tconst thisMigration = deepmerge(\n\t\t\t\t\t\t\t\tconfig.defaultBetterAuthOptions || {},\n\t\t\t\t\t\t\t\tmigrateBetterAuth || {},\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t// If this is the first test in a group, migrate to the group's options\n\t\t\t\t\t\t\tif (isFirstInGroup && testGroup) {\n\t\t\t\t\t\t\t\tconst groupMigrationOptions = testGroup.migrationOptions;\n\t\t\t\t\t\t\t\tconst groupFinalOptions = deepmerge(\n\t\t\t\t\t\t\t\t\tconfig.defaultBetterAuthOptions || {},\n\t\t\t\t\t\t\t\t\tgroupMigrationOptions || {},\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t// Only migrate if the group's options are different from current state\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t!deepEqual(\n\t\t\t\t\t\t\t\t\t\tcurrentGroupMigrationOptions,\n\t\t\t\t\t\t\t\t\t\tgroupMigrationOptions,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tawait applyOptionsAndMigrate(groupFinalOptions, true);\n\t\t\t\t\t\t\t\t\tcurrentGroupMigrationOptions = groupMigrationOptions;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// If this test is not in a group or not first in group, check if migration is needed\n\t\t\t\t\t\t\telse if (\n\t\t\t\t\t\t\t\t!deepEqual(currentGroupMigrationOptions, migrateBetterAuth)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tawait applyOptionsAndMigrate(thisMigration, true);\n\t\t\t\t\t\t\t\tcurrentGroupMigrationOptions = migrateBetterAuth;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})();\n\n\t\t\t\t\t\tstats.testCount++;\n\n\t\t\t\t\t\tonTestFailed(async () => {\n\t\t\t\t\t\t\tprintDebugLogs();\n\t\t\t\t\t\t\tawait onFinish(testName);\n\t\t\t\t\t\t});\n\t\t\t\t\t\tawait testFn({ skip });\n\t\t\t\t\t\tawait onFinish(testName);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t};\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"create-test-suite.mjs","names":[],"sources":["../src/adapter/create-test-suite.ts"],"sourcesContent":["import type { BetterAuthOptions } from \"@better-auth/core\";\nimport type {\n\tAccount,\n\tSession,\n\tUser,\n\tVerification,\n} from \"@better-auth/core/db\";\nimport { getAuthTables } from \"@better-auth/core/db\";\nimport type { DBAdapter } from \"@better-auth/core/db/adapter\";\nimport {\n\tcreateAdapterFactory,\n\tdeepmerge,\n\tinitGetDefaultModelName,\n} from \"@better-auth/core/db/adapter\";\nimport { TTY_COLORS } from \"@better-auth/core/env\";\nimport { generateId } from \"@better-auth/core/utils/id\";\nimport type { Auth } from \"better-auth\";\nimport { betterAuth } from \"better-auth\";\nimport { test } from \"vitest\";\nimport type { Logger } from \"./test-adapter\";\n\n/**\n * Test entry type that supports both callback and object formats.\n * Object format allows specifying migration options that will be applied before the test runs.\n */\nexport type TestEntry =\n\t| ((context: {\n\t\t\treadonly skip: {\n\t\t\t\t(note?: string | undefined): never;\n\t\t\t\t(condition: boolean, note?: string | undefined): void;\n\t\t\t};\n\t }) => Promise<void>)\n\t| {\n\t\t\tmigrateBetterAuth?: BetterAuthOptions;\n\t\t\ttest: (context: {\n\t\t\t\treadonly skip: {\n\t\t\t\t\t(note?: string | undefined): never;\n\t\t\t\t\t(condition: boolean, note?: string | undefined): void;\n\t\t\t\t};\n\t\t\t}) => Promise<void>;\n\t };\n\n/**\n * Deep equality comparison for BetterAuthOptions.\n * Handles nested objects, arrays, and primitive values.\n */\nfunction deepEqual(a: any, b: any): boolean {\n\tif (a === b) return true;\n\tif (a == null || b == null) return a === b;\n\tif (typeof a !== typeof b) return false;\n\n\tif (typeof a === \"object\") {\n\t\tif (Array.isArray(a) !== Array.isArray(b)) return false;\n\n\t\tif (Array.isArray(a)) {\n\t\t\tif (a.length !== b.length) return false;\n\t\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\t\tif (!deepEqual(a[i], b[i])) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tconst keysA = Object.keys(a);\n\t\tconst keysB = Object.keys(b);\n\n\t\tif (keysA.length !== keysB.length) return false;\n\n\t\tfor (const key of keysA) {\n\t\t\tif (!keysB.includes(key)) return false;\n\t\t\tif (!deepEqual(a[key], b[key])) return false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n/**\n * Statistics tracking for test suites.\n */\nexport type TestSuiteStats = {\n\tmigrationCount: number;\n\ttotalMigrationTime: number;\n\ttestCount: number;\n\tsuiteStartTime: number;\n\tsuiteDuration: number;\n\tsuiteName: string;\n\tgroupingStats?: {\n\t\ttotalGroups: number;\n\t\taverageTestsPerGroup: number;\n\t\tlargestGroupSize: number;\n\t\tsmallestGroupSize: number;\n\t\tgroupsWithMultipleTests: number;\n\t\ttotalTestsInGroups: number;\n\t};\n};\n\ntype GenerateFn = <M extends \"user\" | \"session\" | \"verification\" | \"account\">(\n\tModel: M,\n) => Promise<\n\tM extends \"user\"\n\t\t? User\n\t\t: M extends \"session\"\n\t\t\t? Session\n\t\t\t: M extends \"verification\"\n\t\t\t\t? Verification\n\t\t\t\t: M extends \"account\"\n\t\t\t\t\t? Account\n\t\t\t\t\t: undefined\n>;\n\ntype Success<T> = {\n\tdata: T;\n\terror: null;\n};\n\ntype Failure<E> = {\n\tdata: null;\n\terror: E;\n};\n\ntype Result<T, E = Error> = Success<T> | Failure<E>;\n\nasync function tryCatch<T, E = Error>(\n\tpromise: Promise<T>,\n): Promise<Result<T, E>> {\n\ttry {\n\t\tconst data = await promise;\n\t\treturn { data, error: null };\n\t} catch (error) {\n\t\treturn { data: null, error: error as E };\n\t}\n}\n\nexport type InsertRandomFn = <\n\tM extends \"user\" | \"session\" | \"verification\" | \"account\",\n\tCount extends number = 1,\n>(\n\tmodel: M,\n\tcount?: Count | undefined,\n) => Promise<\n\tCount extends 1\n\t\t? M extends \"user\"\n\t\t\t? [User]\n\t\t\t: M extends \"session\"\n\t\t\t\t? [User, Session]\n\t\t\t\t: M extends \"verification\"\n\t\t\t\t\t? [Verification]\n\t\t\t\t\t: M extends \"account\"\n\t\t\t\t\t\t? [User, Account]\n\t\t\t\t\t\t: [undefined]\n\t\t: Array<\n\t\t\t\tM extends \"user\"\n\t\t\t\t\t? [User]\n\t\t\t\t\t: M extends \"session\"\n\t\t\t\t\t\t? [User, Session]\n\t\t\t\t\t\t: M extends \"verification\"\n\t\t\t\t\t\t\t? [Verification]\n\t\t\t\t\t\t\t: M extends \"account\"\n\t\t\t\t\t\t\t\t? [User, Account]\n\t\t\t\t\t\t\t\t: [undefined]\n\t\t\t>\n>;\n\nexport const createTestSuite = <\n\tTests extends Record<string, TestEntry>,\n\tAdditionalOptions extends Record<string, any> = {},\n>(\n\tsuiteName: string,\n\tconfig: {\n\t\tdefaultBetterAuthOptions?: BetterAuthOptions | undefined;\n\t\t/**\n\t\t * Helpful if the default better auth options require migrations to be run.\n\t\t */\n\t\talwaysMigrate?: boolean | undefined;\n\t\tprefixTests?: string | undefined;\n\t\tcustomIdGenerator?: () => any | Promise<any> | undefined;\n\t},\n\ttests: (\n\t\thelpers: {\n\t\t\tadapter: DBAdapter;\n\t\t\tlog: Logger;\n\t\t\tgenerate: GenerateFn;\n\t\t\tinsertRandom: InsertRandomFn;\n\t\t\t/**\n\t\t\t * A light cleanup function that will only delete rows it knows about.\n\t\t\t */\n\t\t\tcleanup: () => Promise<void>;\n\t\t\t/**\n\t\t\t * A hard cleanup function that will delete all rows from the database.\n\t\t\t */\n\t\t\thardCleanup: () => Promise<void>;\n\t\t\tmodifyBetterAuthOptions: (\n\t\t\t\toptions: BetterAuthOptions,\n\t\t\t\tshouldRunMigrations: boolean,\n\t\t\t) => Promise<BetterAuthOptions>;\n\t\t\tgetBetterAuthOptions: () => BetterAuthOptions;\n\t\t\tsortModels: (\n\t\t\t\tmodels: Array<\n\t\t\t\t\tRecord<string, any> & {\n\t\t\t\t\t\tid: string;\n\t\t\t\t\t}\n\t\t\t\t>,\n\t\t\t\tby?: (\"id\" | \"createdAt\") | undefined,\n\t\t\t) => (Record<string, any> & {\n\t\t\t\tid: string;\n\t\t\t})[];\n\t\t\tgetAuth: () => Promise<Auth>;\n\t\t\ttryCatch<T, E = Error>(promise: Promise<T>): Promise<Result<T, E>>;\n\t\t\tcustomIdGenerator?: () => any | Promise<any> | undefined;\n\t\t\ttransformIdOutput?: (id: any) => string | undefined;\n\t\t\t/**\n\t\t\t * Some adapters may change the ID type, this function allows you to pass the entire model\n\t\t\t * data and it will return the correct better-auth-expected transformed data.\n\t\t\t *\n\t\t\t * Eg:\n\t\t\t * MongoDB uses ObjectId for IDs, but it's possible the user can disable that option in the adapter config.\n\t\t\t * Because of this, the expected data would be a string.\n\t\t\t * These sorts of conversions will cause issues with the test when you use the `generate` function.\n\t\t\t * This is because the `generate` function will return the raw data expected to be saved in DB, not the excpected BA output.\n\t\t\t */\n\t\t\ttransformGeneratedModel: (\n\t\t\t\tdata: Record<string, any>,\n\t\t\t) => Record<string, any>;\n\t\t},\n\t\tadditionalOptions?: AdditionalOptions | undefined,\n\t) => Tests,\n) => {\n\treturn (\n\t\toptions?:\n\t\t\t| ({\n\t\t\t\t\tdisableTests?: Partial<\n\t\t\t\t\t\tRecord<keyof Tests, boolean> & { ALL?: boolean }\n\t\t\t\t\t>;\n\t\t\t } & AdditionalOptions)\n\t\t\t| undefined,\n\t) => {\n\t\treturn async (helpers: {\n\t\t\tadapter: () => Promise<DBAdapter<BetterAuthOptions>>;\n\t\t\tlog: Logger;\n\t\t\tadapterDisplayName: string;\n\t\t\tgetBetterAuthOptions: () => BetterAuthOptions;\n\t\t\tmodifyBetterAuthOptions: (\n\t\t\t\toptions: BetterAuthOptions,\n\t\t\t) => Promise<BetterAuthOptions>;\n\t\t\tcleanup: () => Promise<void>;\n\t\t\trunMigrations: () => Promise<void>;\n\t\t\tprefixTests?: string | undefined;\n\t\t\tonTestFinish: (stats: TestSuiteStats) => Promise<void>;\n\t\t\tcustomIdGenerator?: () => any | Promise<any> | undefined;\n\t\t\ttransformIdOutput?: (id: any) => string | undefined;\n\t\t}) => {\n\t\t\tconst createdRows: Record<string, any[]> = {};\n\n\t\t\tlet adapter = await helpers.adapter();\n\t\t\tconst wrapperAdapter = (\n\t\t\t\toverrideOptions?: BetterAuthOptions | undefined,\n\t\t\t) => {\n\t\t\t\tconst options = deepmerge(\n\t\t\t\t\tdeepmerge(\n\t\t\t\t\t\thelpers.getBetterAuthOptions(),\n\t\t\t\t\t\tconfig?.defaultBetterAuthOptions || {},\n\t\t\t\t\t),\n\t\t\t\t\toverrideOptions || {},\n\t\t\t\t);\n\t\t\t\tconst adapterConfig = {\n\t\t\t\t\tadapterId: helpers.adapterDisplayName,\n\t\t\t\t\t...(adapter.options?.adapterConfig || {}),\n\t\t\t\t\tadapterName: `Wrapped ${adapter.options?.adapterConfig.adapterName}`,\n\t\t\t\t\tdisableTransformOutput: true,\n\t\t\t\t\tdisableTransformInput: true,\n\t\t\t\t\tdisableTransformJoin: true,\n\t\t\t\t};\n\t\t\t\tconst adapterCreator = (\n\t\t\t\t\toptions: BetterAuthOptions,\n\t\t\t\t): DBAdapter<BetterAuthOptions> =>\n\t\t\t\t\tcreateAdapterFactory({\n\t\t\t\t\t\tconfig: {\n\t\t\t\t\t\t\t...adapterConfig,\n\t\t\t\t\t\t\ttransaction: adapter.transaction,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tadapter: ({ getDefaultModelName }) => {\n\t\t\t\t\t\t\tadapter.transaction = undefined as any;\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcount: async (args: any) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.count(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tdeleteMany: async (args: any) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.deleteMany(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tdelete: async (args: any) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.delete(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tfindOne: async (args) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.findOne(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tfindMany: async (args) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.findMany(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tupdate: async (args: any) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.update(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tupdateMany: async (args) => {\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.updateMany(args);\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tcreateSchema: adapter.createSchema as any,\n\t\t\t\t\t\t\t\tasync create({ data, model, select }) {\n\t\t\t\t\t\t\t\t\tconst defaultModelName = getDefaultModelName(model);\n\t\t\t\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\t\t\t\tconst res = await adapter.create({\n\t\t\t\t\t\t\t\t\t\tdata: data,\n\t\t\t\t\t\t\t\t\t\tmodel: defaultModelName,\n\t\t\t\t\t\t\t\t\t\tselect,\n\t\t\t\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tcreatedRows[model] = [...(createdRows[model] || []), res];\n\t\t\t\t\t\t\t\t\treturn res as any;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\toptions: adapter.options,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t},\n\t\t\t\t\t})(options);\n\n\t\t\t\treturn adapterCreator(options);\n\t\t\t};\n\n\t\t\tconst resetDebugLogs = () => {\n\t\t\t\t//@ts-expect-error\n\t\t\t\twrapperAdapter()?.adapterTestDebugLogs?.resetDebugLogs();\n\t\t\t};\n\n\t\t\tconst printDebugLogs = () => {\n\t\t\t\t//@ts-expect-error\n\t\t\t\twrapperAdapter()?.adapterTestDebugLogs?.printDebugLogs();\n\t\t\t};\n\n\t\t\tconst cleanupCreatedRows = async () => {\n\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\tfor (const model of Object.keys(createdRows)) {\n\t\t\t\t\tfor (const row of createdRows[model]!) {\n\t\t\t\t\t\tconst schema = getAuthTables(helpers.getBetterAuthOptions());\n\t\t\t\t\t\tconst getDefaultModelName = initGetDefaultModelName({\n\t\t\t\t\t\t\tschema,\n\t\t\t\t\t\t\tusePlural: adapter.options?.adapterConfig.usePlural,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tlet defaultModelName: string;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tdefaultModelName = getDefaultModelName(model);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!schema[defaultModelName]) continue; // model doesn't exist in the schema anymore, so we skip it\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait adapter.delete({\n\t\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t\twhere: [{ field: \"id\", value: row.id }],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// We ignore any failed attempts to delete the created rows.\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (createdRows[model]!.length === 1) {\n\t\t\t\t\t\t\tdelete createdRows[model];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Track current applied BetterAuth options state\n\t\t\tlet currentAppliedOptions: BetterAuthOptions | null = null;\n\n\t\t\t// Statistics tracking\n\t\t\tconst stats: TestSuiteStats = {\n\t\t\t\tmigrationCount: 0,\n\t\t\t\ttotalMigrationTime: 0,\n\t\t\t\ttestCount: 0,\n\t\t\t\tsuiteStartTime: performance.now(),\n\t\t\t\tsuiteDuration: 0,\n\t\t\t\tsuiteName,\n\t\t\t};\n\n\t\t\t/**\n\t\t\t * Apply BetterAuth options and run migrations if needed.\n\t\t\t * Tracks migration statistics.\n\t\t\t */\n\t\t\tconst applyOptionsAndMigrate = async (\n\t\t\t\toptions: BetterAuthOptions | Partial<BetterAuthOptions>,\n\t\t\t\tforceMigrate: boolean = false,\n\t\t\t): Promise<BetterAuthOptions> => {\n\t\t\t\tconst finalOptions = deepmerge(\n\t\t\t\t\tconfig?.defaultBetterAuthOptions || {},\n\t\t\t\t\toptions || {},\n\t\t\t\t);\n\n\t\t\t\t// Check if options have changed\n\t\t\t\tconst optionsChanged = !deepEqual(currentAppliedOptions, finalOptions);\n\n\t\t\t\tif (optionsChanged || forceMigrate) {\n\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\tawait helpers.modifyBetterAuthOptions(finalOptions);\n\n\t\t\t\t\tif (config.alwaysMigrate || forceMigrate) {\n\t\t\t\t\t\tconst migrationStart = performance.now();\n\t\t\t\t\t\tawait helpers.runMigrations();\n\t\t\t\t\t\tconst migrationTime = performance.now() - migrationStart;\n\n\t\t\t\t\t\tstats.migrationCount++;\n\t\t\t\t\t\tstats.totalMigrationTime += migrationTime;\n\n\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrentAppliedOptions = finalOptions;\n\t\t\t\t} else {\n\t\t\t\t\t// Options haven't changed, just update the reference\n\t\t\t\t\tcurrentAppliedOptions = finalOptions;\n\t\t\t\t}\n\n\t\t\t\treturn finalOptions;\n\t\t\t};\n\n\t\t\tconst transformGeneratedModel = (data: Record<string, any>) => {\n\t\t\t\tconst newData = { ...data };\n\t\t\t\tif (helpers.transformIdOutput) {\n\t\t\t\t\tnewData.id = helpers.transformIdOutput(newData.id);\n\t\t\t\t}\n\t\t\t\treturn newData;\n\t\t\t};\n\n\t\t\tconst idGenerator = async () => {\n\t\t\t\tif (config.customIdGenerator) {\n\t\t\t\t\treturn config.customIdGenerator();\n\t\t\t\t}\n\t\t\t\tif (helpers.customIdGenerator) {\n\t\t\t\t\treturn helpers.customIdGenerator();\n\t\t\t\t}\n\t\t\t\treturn generateId();\n\t\t\t};\n\n\t\t\tconst generateModel: GenerateFn = async (model: string) => {\n\t\t\t\tconst id = await idGenerator();\n\t\t\t\tconst randomDate = new Date(\n\t\t\t\t\tDate.now() - Math.random() * 1000 * 60 * 60 * 24 * 365,\n\t\t\t\t);\n\t\t\t\tif (model === \"user\") {\n\t\t\t\t\tconst user: User = {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcreatedAt: randomDate,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\temail:\n\t\t\t\t\t\t\t`user-${helpers.transformIdOutput?.(id) ?? id}@email.com`.toLowerCase(),\n\t\t\t\t\t\temailVerified: true,\n\t\t\t\t\t\tname: `user-${helpers.transformIdOutput?.(id) ?? id}`,\n\t\t\t\t\t\timage: null,\n\t\t\t\t\t};\n\t\t\t\t\treturn user as any;\n\t\t\t\t}\n\t\t\t\tif (model === \"session\") {\n\t\t\t\t\tconst session: Session = {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcreatedAt: randomDate,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\texpiresAt: new Date(),\n\t\t\t\t\t\ttoken: generateId(32),\n\t\t\t\t\t\tuserId: generateId(),\n\t\t\t\t\t\tipAddress: \"127.0.0.1\",\n\t\t\t\t\t\tuserAgent: \"Some User Agent\",\n\t\t\t\t\t};\n\t\t\t\t\treturn session as any;\n\t\t\t\t}\n\t\t\t\tif (model === \"verification\") {\n\t\t\t\t\tconst verification: Verification = {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcreatedAt: randomDate,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\texpiresAt: new Date(),\n\t\t\t\t\t\tidentifier: `test:${generateId()}`,\n\t\t\t\t\t\tvalue: generateId(),\n\t\t\t\t\t};\n\t\t\t\t\treturn verification as any;\n\t\t\t\t}\n\t\t\t\tif (model === \"account\") {\n\t\t\t\t\tconst account: Account = {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcreatedAt: randomDate,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\taccountId: generateId(),\n\t\t\t\t\t\tproviderId: \"test\",\n\t\t\t\t\t\tuserId: generateId(),\n\t\t\t\t\t\taccessToken: generateId(),\n\t\t\t\t\t\trefreshToken: generateId(),\n\t\t\t\t\t\tidToken: generateId(),\n\t\t\t\t\t\taccessTokenExpiresAt: new Date(),\n\t\t\t\t\t\trefreshTokenExpiresAt: new Date(),\n\t\t\t\t\t\tscope: \"test\",\n\t\t\t\t\t};\n\t\t\t\t\treturn account as any;\n\t\t\t\t}\n\t\t\t\t// This should never happen given the type constraints, but TypeScript needs an exhaustive check\n\t\t\t\tthrow new Error(`Unknown model type: ${model}`);\n\t\t\t};\n\n\t\t\tconst insertRandom: InsertRandomFn = async <\n\t\t\t\tM extends \"user\" | \"session\" | \"verification\" | \"account\",\n\t\t\t\tCount extends number = 1,\n\t\t\t>(\n\t\t\t\tmodel: M,\n\t\t\t\tcount: Count = 1 as Count,\n\t\t\t) => {\n\t\t\t\tconst res: any[] = [];\n\t\t\t\tconst a = wrapperAdapter();\n\n\t\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\t\tconst modelResults = [];\n\n\t\t\t\t\tif (model === \"user\") {\n\t\t\t\t\t\tconst user = await generateModel(\"user\");\n\t\t\t\t\t\tmodelResults.push(\n\t\t\t\t\t\t\tawait a.create({\n\t\t\t\t\t\t\t\tdata: user,\n\t\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (model === \"session\") {\n\t\t\t\t\t\tconst user = await generateModel(\"user\");\n\t\t\t\t\t\tconst userRes = await a.create({\n\t\t\t\t\t\t\tdata: user,\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst session = await generateModel(\"session\");\n\t\t\t\t\t\tsession.userId = userRes.id;\n\t\t\t\t\t\tconst sessionRes = await a.create({\n\t\t\t\t\t\t\tdata: session,\n\t\t\t\t\t\t\tmodel: \"session\",\n\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tmodelResults.push(userRes, sessionRes);\n\t\t\t\t\t}\n\t\t\t\t\tif (model === \"verification\") {\n\t\t\t\t\t\tconst verification = await generateModel(\"verification\");\n\t\t\t\t\t\tmodelResults.push(\n\t\t\t\t\t\t\tawait a.create({\n\t\t\t\t\t\t\t\tdata: verification,\n\t\t\t\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (model === \"account\") {\n\t\t\t\t\t\tconst user = await generateModel(\"user\");\n\t\t\t\t\t\tconst account = await generateModel(\"account\");\n\t\t\t\t\t\tconst userRes = await a.create({\n\t\t\t\t\t\t\tdata: user,\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t\taccount.userId = userRes.id;\n\t\t\t\t\t\tconst accRes = await a.create({\n\t\t\t\t\t\t\tdata: account,\n\t\t\t\t\t\t\tmodel: \"account\",\n\t\t\t\t\t\t\tforceAllowId: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tmodelResults.push(userRes, accRes);\n\t\t\t\t\t}\n\t\t\t\t\tres.push(modelResults);\n\t\t\t\t}\n\t\t\t\treturn res.length === 1 ? res[0] : (res as any);\n\t\t\t};\n\n\t\t\tconst sortModels = (\n\t\t\t\tmodels: Array<Record<string, any> & { id: string }>,\n\t\t\t\tby: \"id\" | \"createdAt\" = \"id\",\n\t\t\t) => {\n\t\t\t\treturn models.sort((a, b) => {\n\t\t\t\t\tif (by === \"createdAt\") {\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tnew Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn a.id.localeCompare(b.id);\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst modifyBetterAuthOptions = async (\n\t\t\t\topts: BetterAuthOptions,\n\t\t\t\tshouldRunMigrations: boolean,\n\t\t\t) => {\n\t\t\t\treturn await applyOptionsAndMigrate(opts, shouldRunMigrations);\n\t\t\t};\n\n\t\t\tconst additionalOptions = { ...options };\n\t\t\tadditionalOptions.disableTests = undefined;\n\n\t\t\tconst fullTests = tests(\n\t\t\t\t{\n\t\t\t\t\tadapter: new Proxy({} as any, {\n\t\t\t\t\t\tget(target, prop) {\n\t\t\t\t\t\t\tconst adapter = wrapperAdapter();\n\t\t\t\t\t\t\tif (prop === \"transaction\") {\n\t\t\t\t\t\t\t\treturn adapter.transaction;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst value = adapter[prop as keyof typeof adapter];\n\t\t\t\t\t\t\tif (typeof value === \"function\") {\n\t\t\t\t\t\t\t\treturn value.bind(adapter);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tgetAuth: async () => {\n\t\t\t\t\t\tadapter = await helpers.adapter();\n\t\t\t\t\t\tconst auth = betterAuth({\n\t\t\t\t\t\t\t...helpers.getBetterAuthOptions(),\n\t\t\t\t\t\t\t...(config?.defaultBetterAuthOptions || {}),\n\t\t\t\t\t\t\tdatabase: (options: BetterAuthOptions) => {\n\t\t\t\t\t\t\t\tconst adapter = wrapperAdapter(options);\n\t\t\t\t\t\t\t\treturn adapter;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t} as BetterAuthOptions);\n\t\t\t\t\t\treturn auth;\n\t\t\t\t\t},\n\t\t\t\t\tlog: helpers.log,\n\t\t\t\t\tgenerate: generateModel,\n\t\t\t\t\tcleanup: cleanupCreatedRows,\n\t\t\t\t\thardCleanup: helpers.cleanup,\n\t\t\t\t\tinsertRandom,\n\t\t\t\t\tmodifyBetterAuthOptions,\n\t\t\t\t\tgetBetterAuthOptions: helpers.getBetterAuthOptions,\n\t\t\t\t\tsortModels,\n\t\t\t\t\ttryCatch,\n\t\t\t\t\tcustomIdGenerator: helpers.customIdGenerator,\n\t\t\t\t\ttransformGeneratedModel,\n\t\t\t\t\ttransformIdOutput: helpers.transformIdOutput,\n\t\t\t\t},\n\t\t\t\tadditionalOptions as AdditionalOptions,\n\t\t\t);\n\n\t\t\tconst dash = `─`;\n\t\t\tconst allDisabled: boolean = options?.disableTests?.ALL ?? false;\n\n\t\t\t// Here to display a label in the tests showing the suite name\n\t\t\ttest(`\\n${TTY_COLORS.fg.white}${\" \".repeat(3)}${dash.repeat(35)} [${TTY_COLORS.fg.magenta}${suiteName}${TTY_COLORS.fg.white}] ${dash.repeat(35)}`, async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait helpers.cleanup();\n\t\t\t\t} catch {}\n\t\t\t\tif (config.defaultBetterAuthOptions && !allDisabled) {\n\t\t\t\t\tawait applyOptionsAndMigrate(\n\t\t\t\t\t\tconfig.defaultBetterAuthOptions,\n\t\t\t\t\t\tconfig.alwaysMigrate,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t/**\n\t\t\t * Extract test function and migration options from a test entry.\n\t\t\t */\n\t\t\tconst extractTestEntry = (\n\t\t\t\tentry: TestEntry,\n\t\t\t): {\n\t\t\t\ttestFn: (context: {\n\t\t\t\t\treadonly skip: {\n\t\t\t\t\t\t(note?: string | undefined): never;\n\t\t\t\t\t\t(condition: boolean, note?: string | undefined): void;\n\t\t\t\t\t};\n\t\t\t\t}) => Promise<void>;\n\t\t\t\tmigrateBetterAuth?: BetterAuthOptions;\n\t\t\t} => {\n\t\t\t\tif (typeof entry === \"function\") {\n\t\t\t\t\treturn { testFn: entry };\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\ttestFn: entry.test,\n\t\t\t\t\tmigrateBetterAuth: entry.migrateBetterAuth,\n\t\t\t\t};\n\t\t\t};\n\n\t\t\t// Convert test entries to array with migration info (moved before onFinish for access)\n\t\t\tconst testEntries = Object.entries(fullTests).map(([name, entry]) => {\n\t\t\t\tconst { testFn, migrateBetterAuth } = extractTestEntry(\n\t\t\t\t\tentry as TestEntry,\n\t\t\t\t);\n\t\t\t\treturn { name, testFn, migrateBetterAuth };\n\t\t\t});\n\n\t\t\t/**\n\t\t\t * Group tests by their migrateBetterAuth options.\n\t\t\t * Tests with equal migration options are grouped together.\n\t\t\t */\n\t\t\ttype TestGroup = {\n\t\t\t\tmigrationOptions: BetterAuthOptions | null | undefined;\n\t\t\t\ttestIndices: number[];\n\t\t\t};\n\n\t\t\tconst groupTestsByMigrationOptions = (): TestGroup[] => {\n\t\t\t\tconst groups: TestGroup[] = [];\n\t\t\t\tlet currentGroup: TestGroup | null = null;\n\n\t\t\t\tfor (let i = 0; i < testEntries.length; i++) {\n\t\t\t\t\tconst { migrateBetterAuth } = testEntries[i]!;\n\t\t\t\t\tconst isSkipped =\n\t\t\t\t\t\t(allDisabled &&\n\t\t\t\t\t\t\toptions?.disableTests?.[testEntries[i]!.name] !== false) ||\n\t\t\t\t\t\t(options?.disableTests?.[testEntries[i]!.name] ?? false);\n\n\t\t\t\t\t// Skip grouping for skipped tests - they'll be handled individually\n\t\t\t\t\tif (isSkipped) {\n\t\t\t\t\t\tif (currentGroup) {\n\t\t\t\t\t\t\tgroups.push(currentGroup);\n\t\t\t\t\t\t\tcurrentGroup = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgroups.push({\n\t\t\t\t\t\t\tmigrationOptions: migrateBetterAuth,\n\t\t\t\t\t\t\ttestIndices: [i],\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if this test belongs to the current group\n\t\t\t\t\tif (\n\t\t\t\t\t\tcurrentGroup &&\n\t\t\t\t\t\tdeepEqual(currentGroup.migrationOptions, migrateBetterAuth)\n\t\t\t\t\t) {\n\t\t\t\t\t\tcurrentGroup.testIndices.push(i);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Start a new group\n\t\t\t\t\t\tif (currentGroup) {\n\t\t\t\t\t\t\tgroups.push(currentGroup);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcurrentGroup = {\n\t\t\t\t\t\t\tmigrationOptions: migrateBetterAuth,\n\t\t\t\t\t\t\ttestIndices: [i],\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Add the last group if it exists\n\t\t\t\tif (currentGroup) {\n\t\t\t\t\tgroups.push(currentGroup);\n\t\t\t\t}\n\n\t\t\t\treturn groups;\n\t\t\t};\n\n\t\t\tconst testGroups = groupTestsByMigrationOptions();\n\n\t\t\t// Calculate grouping statistics\n\t\t\tconst calculateGroupingStats = () => {\n\t\t\t\tconst nonSkippedGroups = testGroups.filter(\n\t\t\t\t\t(group) => group.testIndices.length > 0,\n\t\t\t\t);\n\t\t\t\tconst groupSizes = nonSkippedGroups.map(\n\t\t\t\t\t(group) => group.testIndices.length,\n\t\t\t\t);\n\n\t\t\t\tif (groupSizes.length === 0) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttotalGroups: 0,\n\t\t\t\t\t\taverageTestsPerGroup: 0,\n\t\t\t\t\t\tlargestGroupSize: 0,\n\t\t\t\t\t\tsmallestGroupSize: 0,\n\t\t\t\t\t\tgroupsWithMultipleTests: 0,\n\t\t\t\t\t\ttotalTestsInGroups: 0,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tconst totalTestsInGroups = groupSizes.reduce(\n\t\t\t\t\t(sum, size) => sum + size,\n\t\t\t\t\t0,\n\t\t\t\t);\n\t\t\t\tconst groupsWithMultipleTests = groupSizes.filter(\n\t\t\t\t\t(size) => size > 1,\n\t\t\t\t).length;\n\n\t\t\t\treturn {\n\t\t\t\t\ttotalGroups: nonSkippedGroups.length,\n\t\t\t\t\taverageTestsPerGroup: totalTestsInGroups / nonSkippedGroups.length,\n\t\t\t\t\tlargestGroupSize: Math.max(...groupSizes),\n\t\t\t\t\tsmallestGroupSize: Math.min(...groupSizes),\n\t\t\t\t\tgroupsWithMultipleTests,\n\t\t\t\t\ttotalTestsInGroups,\n\t\t\t\t};\n\t\t\t};\n\n\t\t\tconst onFinish = async (testName: string) => {\n\t\t\t\tawait cleanupCreatedRows();\n\n\t\t\t\tconst currentTestIndex = testEntries.findIndex(\n\t\t\t\t\t({ name }) => name === testName,\n\t\t\t\t);\n\t\t\t\tconst isLastTest = currentTestIndex === testEntries.length - 1;\n\n\t\t\t\tif (isLastTest) {\n\t\t\t\t\tstats.suiteDuration = performance.now() - stats.suiteStartTime;\n\t\t\t\t\tstats.groupingStats = calculateGroupingStats();\n\t\t\t\t\tawait helpers.onTestFinish(stats);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Track the current group's migration options\n\t\t\tlet currentGroupMigrationOptions: BetterAuthOptions | null | undefined =\n\t\t\t\tnull;\n\n\t\t\tfor (let i = 0; i < testEntries.length; i++) {\n\t\t\t\tconst { name: testName, testFn, migrateBetterAuth } = testEntries[i]!;\n\n\t\t\t\t// Find which group this test belongs to\n\t\t\t\tconst testGroup = testGroups.find((group) =>\n\t\t\t\t\tgroup.testIndices.includes(i),\n\t\t\t\t);\n\t\t\t\tconst isFirstInGroup = testGroup && testGroup.testIndices[0] === i;\n\n\t\t\t\tconst shouldSkip =\n\t\t\t\t\t(allDisabled && options?.disableTests?.[testName] !== false) ||\n\t\t\t\t\t(options?.disableTests?.[testName] ?? false);\n\n\t\t\t\tlet displayName = testName.replace(\n\t\t\t\t\t\" - \",\n\t\t\t\t\t` ${TTY_COLORS.dim}${dash}${TTY_COLORS.undim} `,\n\t\t\t\t);\n\t\t\t\tif (config.prefixTests) {\n\t\t\t\t\tdisplayName = `${config.prefixTests} ${TTY_COLORS.dim}>${TTY_COLORS.undim} ${displayName}`;\n\t\t\t\t}\n\t\t\t\tif (helpers.prefixTests) {\n\t\t\t\t\tdisplayName = `[${TTY_COLORS.dim}${helpers.prefixTests}${TTY_COLORS.undim}] ${displayName}`;\n\t\t\t\t}\n\n\t\t\t\ttest.skipIf(shouldSkip)(\n\t\t\t\t\tdisplayName,\n\t\t\t\t\t{ timeout: 30000 },\n\t\t\t\t\tasync ({ onTestFailed, skip }) => {\n\t\t\t\t\t\tresetDebugLogs();\n\n\t\t\t\t\t\t// Apply migration options before test runs\n\t\t\t\t\t\tawait (async () => {\n\t\t\t\t\t\t\tif (shouldSkip) return;\n\n\t\t\t\t\t\t\tconst thisMigration = deepmerge(\n\t\t\t\t\t\t\t\tconfig.defaultBetterAuthOptions || {},\n\t\t\t\t\t\t\t\tmigrateBetterAuth || {},\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t// If this is the first test in a group, migrate to the group's options\n\t\t\t\t\t\t\tif (isFirstInGroup && testGroup) {\n\t\t\t\t\t\t\t\tconst groupMigrationOptions = testGroup.migrationOptions;\n\t\t\t\t\t\t\t\tconst groupFinalOptions = deepmerge(\n\t\t\t\t\t\t\t\t\tconfig.defaultBetterAuthOptions || {},\n\t\t\t\t\t\t\t\t\tgroupMigrationOptions || {},\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t// Only migrate if the group's options are different from current state\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t!deepEqual(\n\t\t\t\t\t\t\t\t\t\tcurrentGroupMigrationOptions,\n\t\t\t\t\t\t\t\t\t\tgroupMigrationOptions,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tawait applyOptionsAndMigrate(groupFinalOptions, true);\n\t\t\t\t\t\t\t\t\tcurrentGroupMigrationOptions = groupMigrationOptions;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// If this test is not in a group or not first in group, check if migration is needed\n\t\t\t\t\t\t\telse if (\n\t\t\t\t\t\t\t\t!deepEqual(currentGroupMigrationOptions, migrateBetterAuth)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tawait applyOptionsAndMigrate(thisMigration, true);\n\t\t\t\t\t\t\t\tcurrentGroupMigrationOptions = migrateBetterAuth;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})();\n\n\t\t\t\t\t\tstats.testCount++;\n\n\t\t\t\t\t\tonTestFailed(async () => {\n\t\t\t\t\t\t\tprintDebugLogs();\n\t\t\t\t\t\t\tawait onFinish(testName);\n\t\t\t\t\t\t});\n\t\t\t\t\t\tawait testFn({ skip });\n\t\t\t\t\t\tawait onFinish(testName);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t};\n};\n"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  import { createTestSuite } from "../create-test-suite.mjs";
2
2
  import { expect } from "vitest";
3
3
  import { setCookieToHeader } from "better-auth/cookies";
4
-
5
4
  //#region src/adapter/suites/auth-flow.ts
6
5
  /**
7
6
  * This test suite tests basic authentication flow using the adapter.
@@ -135,7 +134,7 @@ function recoverProcessTZ() {
135
134
  process.env.TZ = originalTZ;
136
135
  } };
137
136
  }
138
-
139
137
  //#endregion
140
138
  export { authFlowTestSuite };
139
+
141
140
  //# sourceMappingURL=auth-flow.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-flow.mjs","names":[],"sources":["../../src/adapter/suites/auth-flow.ts"],"sourcesContent":["import type { Session, User } from \"@better-auth/core/db\";\nimport { setCookieToHeader } from \"better-auth/cookies\";\nimport { expect } from \"vitest\";\nimport { createTestSuite } from \"../create-test-suite\";\n\n/**\n * This test suite tests basic authentication flow using the adapter.\n */\nexport const authFlowTestSuite = createTestSuite(\n\t\"auth-flow\",\n\t{\n\t\tdefaultBetterAuthOptions: {\n\t\t\temailAndPassword: {\n\t\t\t\tenabled: true,\n\t\t\t\tpassword: {\n\t\t\t\t\thash: async (password) => password,\n\t\t\t\t\tasync verify(data) {\n\t\t\t\t\t\treturn data.hash === data.password;\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\t(\n\t\t{\n\t\t\tgenerate,\n\t\t\tgetAuth,\n\t\t\tmodifyBetterAuthOptions,\n\t\t\ttryCatch,\n\t\t\tgetBetterAuthOptions,\n\t\t},\n\t\tdebug?: { showDB?: () => Promise<void> } | undefined,\n\t) => ({\n\t\t\"should successfully sign up\": async () => {\n\t\t\tconst auth = await getAuth();\n\t\t\tconst user = await generate(\"user\");\n\t\t\tconst start = Date.now();\n\t\t\tconst result = await auth.api.signUpEmail({\n\t\t\t\tbody: {\n\t\t\t\t\temail: user.email,\n\t\t\t\t\tpassword: crypto.randomUUID(),\n\t\t\t\t\tname: user.name,\n\t\t\t\t\timage: user.image || \"\",\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst end = Date.now();\n\t\t\tconsole.log(`signUpEmail took ${end - start}ms (without hashing)`);\n\t\t\texpect(result.user).toBeDefined();\n\t\t\texpect(result.user.email).toBe(user.email);\n\t\t\texpect(result.user.name).toBe(user.name);\n\t\t\texpect(result.user.image).toBe(user.image || \"\");\n\t\t\texpect(result.user.emailVerified).toBe(false);\n\t\t\texpect(result.user.createdAt).toBeDefined();\n\t\t\texpect(result.user.updatedAt).toBeDefined();\n\t\t},\n\t\t\"should successfully sign in\": async () => {\n\t\t\tconst auth = await getAuth();\n\t\t\tconst user = await generate(\"user\");\n\t\t\tconst password = crypto.randomUUID();\n\t\t\tconst signUpResult = await auth.api.signUpEmail({\n\t\t\t\tbody: {\n\t\t\t\t\temail: user.email,\n\t\t\t\t\tpassword: password,\n\t\t\t\t\tname: user.name,\n\t\t\t\t\timage: user.image || \"\",\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst start = Date.now();\n\t\t\tconst result = await auth.api.signInEmail({\n\t\t\t\tbody: { email: user.email, password: password },\n\t\t\t});\n\t\t\tconst end = Date.now();\n\t\t\tconsole.log(`signInEmail took ${end - start}ms (without hashing)`);\n\t\t\texpect(result.user).toBeDefined();\n\t\t\texpect(result.user.id).toBe(signUpResult.user.id);\n\t\t},\n\t\t\"should successfully get session\": async () => {\n\t\t\tconst auth = await getAuth();\n\t\t\tconst user = await generate(\"user\");\n\t\t\tconst password = crypto.randomUUID();\n\t\t\tconst response = await auth.api.signUpEmail({\n\t\t\t\tbody: {\n\t\t\t\t\temail: user.email,\n\t\t\t\t\tpassword: password,\n\t\t\t\t\tname: user.name,\n\t\t\t\t\timage: user.image || \"\",\n\t\t\t\t},\n\t\t\t\tasResponse: true,\n\t\t\t});\n\t\t\tconst headers = new Headers();\n\t\t\tsetCookieToHeader(headers)({ response });\n\t\t\tconst start = Date.now();\n\t\t\tconst result = await auth.api.getSession({\n\t\t\t\theaders,\n\t\t\t});\n\t\t\tconst end = Date.now();\n\t\t\tconsole.log(`getSession took ${end - start}ms`);\n\t\t\tconst signUpResult = (await response.json()) as {\n\t\t\t\tuser: User;\n\t\t\t\tsession: Session;\n\t\t\t};\n\t\t\tsignUpResult.user.createdAt = new Date(signUpResult.user.createdAt);\n\t\t\tsignUpResult.user.updatedAt = new Date(signUpResult.user.updatedAt);\n\t\t\texpect(result?.user).toBeDefined();\n\t\t\texpect(result?.user).toStrictEqual(signUpResult.user);\n\t\t\texpect(result?.session).toBeDefined();\n\t\t},\n\t\t\"should not sign in with invalid email\": async () => {\n\t\t\tconst auth = await getAuth();\n\t\t\tconst user = await generate(\"user\");\n\t\t\tconst { data, error } = await tryCatch(\n\t\t\t\tauth.api.signInEmail({\n\t\t\t\t\tbody: { email: user.email, password: crypto.randomUUID() },\n\t\t\t\t}),\n\t\t\t);\n\t\t\texpect(data).toBeNull();\n\t\t\texpect(error).toBeDefined();\n\t\t},\n\t\t\"should store and retrieve timestamps correctly across timezones\":\n\t\t\tasync () => {\n\t\t\t\tusing _ = recoverProcessTZ();\n\t\t\t\tconst auth = await getAuth();\n\t\t\t\tconst user = await generate(\"user\");\n\t\t\t\tconst password = crypto.randomUUID();\n\t\t\t\tconst userSignUp = await auth.api.signUpEmail({\n\t\t\t\t\tbody: {\n\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\tpassword: password,\n\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\timage: user.image || \"\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tprocess.env.TZ = \"Europe/London\";\n\t\t\t\tconst userSignIn = await auth.api.signInEmail({\n\t\t\t\t\tbody: { email: user.email, password: password },\n\t\t\t\t});\n\t\t\t\tprocess.env.TZ = \"America/Los_Angeles\";\n\t\t\t\texpect(userSignUp.user.createdAt.toISOString()).toStrictEqual(\n\t\t\t\t\tuserSignIn.user.createdAt.toISOString(),\n\t\t\t\t);\n\t\t\t},\n\t\t\"should sign up with additional fields\": async () => {\n\t\t\tawait modifyBetterAuthOptions(\n\t\t\t\t{ user: { additionalFields: { dateField: { type: \"date\" } } } },\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\tconst auth = await getAuth();\n\t\t\tconst user = await generate(\"user\");\n\t\t\tconst dateField = new Date();\n\t\t\tconst response = await auth.api.signUpEmail({\n\t\t\t\tbody: {\n\t\t\t\t\temail: user.email,\n\t\t\t\t\tname: user.name,\n\t\t\t\t\tpassword: crypto.randomUUID(),\n\t\t\t\t\t//@ts-expect-error - we are testing with additional fields\n\t\t\t\t\tdateField: dateField.toISOString(), // using iso string to simulate client to server communication (this should be converted back to Date)\n\t\t\t\t},\n\t\t\t\tasResponse: true,\n\t\t\t});\n\t\t\tconst headers = new Headers();\n\t\t\tsetCookieToHeader(headers)({ response });\n\t\t\tconst result = await auth.api.getSession({\n\t\t\t\theaders,\n\t\t\t});\n\t\t\t//@ts-expect-error - we are testing with additional fields\n\t\t\texpect(result?.user.dateField).toStrictEqual(dateField);\n\t\t},\n\t}),\n);\n\nfunction recoverProcessTZ() {\n\tconst originalTZ = process.env.TZ;\n\treturn {\n\t\t[Symbol.dispose]: () => {\n\t\t\tprocess.env.TZ = originalTZ;\n\t\t},\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"auth-flow.mjs","names":[],"sources":["../../src/adapter/suites/auth-flow.ts"],"sourcesContent":["import type { Session, User } from \"@better-auth/core/db\";\nimport { setCookieToHeader } from \"better-auth/cookies\";\nimport { expect } from \"vitest\";\nimport { createTestSuite } from \"../create-test-suite\";\n\n/**\n * This test suite tests basic authentication flow using the adapter.\n */\nexport const authFlowTestSuite = createTestSuite(\n\t\"auth-flow\",\n\t{\n\t\tdefaultBetterAuthOptions: {\n\t\t\temailAndPassword: {\n\t\t\t\tenabled: true,\n\t\t\t\tpassword: {\n\t\t\t\t\thash: async (password) => password,\n\t\t\t\t\tasync verify(data) {\n\t\t\t\t\t\treturn data.hash === data.password;\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\t(\n\t\t{\n\t\t\tgenerate,\n\t\t\tgetAuth,\n\t\t\tmodifyBetterAuthOptions,\n\t\t\ttryCatch,\n\t\t\tgetBetterAuthOptions,\n\t\t},\n\t\tdebug?: { showDB?: () => Promise<void> } | undefined,\n\t) => ({\n\t\t\"should successfully sign up\": async () => {\n\t\t\tconst auth = await getAuth();\n\t\t\tconst user = await generate(\"user\");\n\t\t\tconst start = Date.now();\n\t\t\tconst result = await auth.api.signUpEmail({\n\t\t\t\tbody: {\n\t\t\t\t\temail: user.email,\n\t\t\t\t\tpassword: crypto.randomUUID(),\n\t\t\t\t\tname: user.name,\n\t\t\t\t\timage: user.image || \"\",\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst end = Date.now();\n\t\t\tconsole.log(`signUpEmail took ${end - start}ms (without hashing)`);\n\t\t\texpect(result.user).toBeDefined();\n\t\t\texpect(result.user.email).toBe(user.email);\n\t\t\texpect(result.user.name).toBe(user.name);\n\t\t\texpect(result.user.image).toBe(user.image || \"\");\n\t\t\texpect(result.user.emailVerified).toBe(false);\n\t\t\texpect(result.user.createdAt).toBeDefined();\n\t\t\texpect(result.user.updatedAt).toBeDefined();\n\t\t},\n\t\t\"should successfully sign in\": async () => {\n\t\t\tconst auth = await getAuth();\n\t\t\tconst user = await generate(\"user\");\n\t\t\tconst password = crypto.randomUUID();\n\t\t\tconst signUpResult = await auth.api.signUpEmail({\n\t\t\t\tbody: {\n\t\t\t\t\temail: user.email,\n\t\t\t\t\tpassword: password,\n\t\t\t\t\tname: user.name,\n\t\t\t\t\timage: user.image || \"\",\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst start = Date.now();\n\t\t\tconst result = await auth.api.signInEmail({\n\t\t\t\tbody: { email: user.email, password: password },\n\t\t\t});\n\t\t\tconst end = Date.now();\n\t\t\tconsole.log(`signInEmail took ${end - start}ms (without hashing)`);\n\t\t\texpect(result.user).toBeDefined();\n\t\t\texpect(result.user.id).toBe(signUpResult.user.id);\n\t\t},\n\t\t\"should successfully get session\": async () => {\n\t\t\tconst auth = await getAuth();\n\t\t\tconst user = await generate(\"user\");\n\t\t\tconst password = crypto.randomUUID();\n\t\t\tconst response = await auth.api.signUpEmail({\n\t\t\t\tbody: {\n\t\t\t\t\temail: user.email,\n\t\t\t\t\tpassword: password,\n\t\t\t\t\tname: user.name,\n\t\t\t\t\timage: user.image || \"\",\n\t\t\t\t},\n\t\t\t\tasResponse: true,\n\t\t\t});\n\t\t\tconst headers = new Headers();\n\t\t\tsetCookieToHeader(headers)({ response });\n\t\t\tconst start = Date.now();\n\t\t\tconst result = await auth.api.getSession({\n\t\t\t\theaders,\n\t\t\t});\n\t\t\tconst end = Date.now();\n\t\t\tconsole.log(`getSession took ${end - start}ms`);\n\t\t\tconst signUpResult = (await response.json()) as {\n\t\t\t\tuser: User;\n\t\t\t\tsession: Session;\n\t\t\t};\n\t\t\tsignUpResult.user.createdAt = new Date(signUpResult.user.createdAt);\n\t\t\tsignUpResult.user.updatedAt = new Date(signUpResult.user.updatedAt);\n\t\t\texpect(result?.user).toBeDefined();\n\t\t\texpect(result?.user).toStrictEqual(signUpResult.user);\n\t\t\texpect(result?.session).toBeDefined();\n\t\t},\n\t\t\"should not sign in with invalid email\": async () => {\n\t\t\tconst auth = await getAuth();\n\t\t\tconst user = await generate(\"user\");\n\t\t\tconst { data, error } = await tryCatch(\n\t\t\t\tauth.api.signInEmail({\n\t\t\t\t\tbody: { email: user.email, password: crypto.randomUUID() },\n\t\t\t\t}),\n\t\t\t);\n\t\t\texpect(data).toBeNull();\n\t\t\texpect(error).toBeDefined();\n\t\t},\n\t\t\"should store and retrieve timestamps correctly across timezones\":\n\t\t\tasync () => {\n\t\t\t\tusing _ = recoverProcessTZ();\n\t\t\t\tconst auth = await getAuth();\n\t\t\t\tconst user = await generate(\"user\");\n\t\t\t\tconst password = crypto.randomUUID();\n\t\t\t\tconst userSignUp = await auth.api.signUpEmail({\n\t\t\t\t\tbody: {\n\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\tpassword: password,\n\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\timage: user.image || \"\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tprocess.env.TZ = \"Europe/London\";\n\t\t\t\tconst userSignIn = await auth.api.signInEmail({\n\t\t\t\t\tbody: { email: user.email, password: password },\n\t\t\t\t});\n\t\t\t\tprocess.env.TZ = \"America/Los_Angeles\";\n\t\t\t\texpect(userSignUp.user.createdAt.toISOString()).toStrictEqual(\n\t\t\t\t\tuserSignIn.user.createdAt.toISOString(),\n\t\t\t\t);\n\t\t\t},\n\t\t\"should sign up with additional fields\": async () => {\n\t\t\tawait modifyBetterAuthOptions(\n\t\t\t\t{ user: { additionalFields: { dateField: { type: \"date\" } } } },\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\tconst auth = await getAuth();\n\t\t\tconst user = await generate(\"user\");\n\t\t\tconst dateField = new Date();\n\t\t\tconst response = await auth.api.signUpEmail({\n\t\t\t\tbody: {\n\t\t\t\t\temail: user.email,\n\t\t\t\t\tname: user.name,\n\t\t\t\t\tpassword: crypto.randomUUID(),\n\t\t\t\t\t//@ts-expect-error - we are testing with additional fields\n\t\t\t\t\tdateField: dateField.toISOString(), // using iso string to simulate client to server communication (this should be converted back to Date)\n\t\t\t\t},\n\t\t\t\tasResponse: true,\n\t\t\t});\n\t\t\tconst headers = new Headers();\n\t\t\tsetCookieToHeader(headers)({ response });\n\t\t\tconst result = await auth.api.getSession({\n\t\t\t\theaders,\n\t\t\t});\n\t\t\t//@ts-expect-error - we are testing with additional fields\n\t\t\texpect(result?.user.dateField).toStrictEqual(dateField);\n\t\t},\n\t}),\n);\n\nfunction recoverProcessTZ() {\n\tconst originalTZ = process.env.TZ;\n\treturn {\n\t\t[Symbol.dispose]: () => {\n\t\t\tprocess.env.TZ = originalTZ;\n\t\t},\n\t};\n}\n"],"mappings":"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"}
@@ -7,7 +7,7 @@ import * as better_auth0 from "better-auth";
7
7
  * This test suite tests the basic CRUD operations of the adapter.
8
8
  */
9
9
  declare const normalTestSuite: (options?: ({
10
- disableTests?: Partial<Record<"init - tests" | "create - should create a model" | "create - should always return an id" | "create - should use generateId if provided" | "create - should return null for nullable foreign keys" | "create - should apply default values to fields" | "findOne - should find a model" | "findOne - should not apply defaultValue if value not found" | "findOne - should find a model using a reference field" | "findOne - should not throw on record not found" | "findOne - should find a model without id" | "findOne - should find a model with join" | "findOne - should find a model with modified field name" | "findOne - should find a model with modified model name" | "findOne - should find a model with additional fields" | "findOne - should select fields" | "findOne - should select fields with one-to-many join" | "findOne - should select fields with one-to-one join" | "findOne - should select fields with multiple joins" | "findOne - should find model with date field" | "findOne - should perform backwards joins" | "findOne - should return an object for one-to-one joins" | "findOne - should return an array for one-to-many joins" | "findOne - should work with both one-to-one and one-to-many joins" | "findOne - should return null for failed base model lookup that has joins" | "findOne - should join a model with modified field name" | "findMany - should find many models" | "findMany - should find many models with date fields" | "findMany - should find many models with join" | "findMany - should find many with join and limit" | "findMany - should select fields" | "findMany - should select fields with one-to-many join" | "findMany - should select fields with one-to-one join" | "findMany - should select fields with multiple joins" | "findMany - should find many with join and offset" | "findMany - should find many with join and sortBy" | "findMany - should find many with join and where clause" | "findMany - should find many with join, where, limit, and offset" | "findMany - should find many with one-to-one join" | "findMany - should find many with both one-to-one and one-to-many joins" | "findMany - should return an empty array when no models are found" | "findMany - should return empty array when base records don't exist with joins" | "findMany - should find many models with starts_with operator" | "findMany - starts_with should not interpret regex patterns" | "findMany - ends_with should not interpret regex patterns" | "findMany - contains should not interpret regex patterns" | "findMany - should find many models with ends_with operator" | "findMany - should find many models with contains operator" | "findMany - should handle multiple where conditions with different operators" | "findMany - should find many models with contains operator (using symbol)" | "findMany - should find many models with eq operator" | "findMany - should find many models with ne operator" | "findMany - should find many models with gt operator" | "findMany - should find many models with gte operator" | "findMany - should find many models with lte operator" | "findMany - should find many models with lt operator" | "findMany - should find many models with in operator" | "findMany - should find many models with not_in operator" | "findMany - should find many models with sortBy" | "findMany - should find many models with limit" | "findMany - should find many models with offset" | "findMany - should find many models with limit and offset" | "findMany - should find many models with sortBy and offset" | "findMany - should find many models with sortBy and limit" | "findMany - should find many models with sortBy and limit and offset" | "findMany - should find many models with sortBy and limit and offset and where" | "update - should update a model" | "updateMany - should update all models when where is empty" | "updateMany - should update many models with a specific where" | "updateMany - should update many models with a multiple where" | "delete - should delete a model" | "delete - should not throw on record not found" | "delete - should delete by non-unique field" | "deleteMany - should delete many models" | "deleteMany - starts_with should not interpret regex patterns" | "deleteMany - ends_with should not interpret regex patterns" | "deleteMany - contains should not interpret regex patterns" | "deleteMany - should delete many models with numeric values" | "deleteMany - should delete many models with boolean values" | "count - should count many models" | "count - should return 0 with no rows to count" | "count - should count with where clause" | "update - should correctly return record when updating a field used in where clause" | "update - should handle updating multiple fields including where clause field" | "update - should work when updated field is not in where clause" | "findOne - backwards join should only return single record not array" | "findMany - backwards join should only return single record not array" | "findOne - backwards join with modified field name (session base, users-table join)" | "findOne - multiple joins should return result even when some joined tables have no matching rows" | "findOne - should be able to perform a limited join" | "findOne - should be able to perform a complex limited join" | "findMany - should be able to perform a limited join" | "findMany - should be able to perform a complex limited join" | "findOne - should return null for one-to-one join when joined record doesn't exist" | "findMany - should return null for one-to-one join when joined records don't exist" | "findMany - should return empty array for one-to-many join when joined records don't exist" | "findMany - should handle mixed joins correctly when some are missing" | "create - should support arrays" | "create - should support json" | "update - should support multiple where conditions under AND connector with unique field", boolean> & {
10
+ disableTests?: Partial<Record<"init - tests" | "create - should create a model" | "create - should always return an id" | "create - should use generateId if provided" | "create - should return null for nullable foreign keys" | "create - should apply default values to fields" | "findOne - should find a model" | "findOne - should not apply defaultValue if value not found" | "findOne - should find a model using a reference field" | "findOne - should not throw on record not found" | "findOne - should find a model without id" | "findOne - should find a model with join" | "findOne - should find a model with modified field name" | "findOne - should find a model with modified model name" | "findOne - should find a model with additional fields" | "findOne - should select fields" | "findOne - should select fields with one-to-many join" | "findOne - should select fields with one-to-one join" | "findOne - should select fields with multiple joins" | "findOne - should find model with date field" | "findOne - should perform backwards joins" | "findOne - should return an object for one-to-one joins" | "findOne - should return an array for one-to-many joins" | "findOne - should work with both one-to-one and one-to-many joins" | "findOne - should return null for failed base model lookup that has joins" | "findOne - should join a model with modified field name" | "findMany - should find many models" | "findMany - should find many models with date fields" | "findMany - should find many models with join" | "findMany - should find many with join and limit" | "findMany - should select fields" | "findMany - should select fields with one-to-many join" | "findMany - should select fields with one-to-one join" | "findMany - should select fields with multiple joins" | "findMany - should find many with join and offset" | "findMany - should find many with join and sortBy" | "findMany - should find many with join and where clause" | "findMany - should find many with join, where, limit, and offset" | "findMany - should find many with one-to-one join" | "findMany - should find many with both one-to-one and one-to-many joins" | "findMany - should return an empty array when no models are found" | "findMany - should return empty array when base records don't exist with joins" | "findMany - should find many models with starts_with operator" | "findMany - starts_with should not interpret regex patterns" | "findMany - ends_with should not interpret regex patterns" | "findMany - contains should not interpret regex patterns" | "findMany - should find many models with ends_with operator" | "findMany - should find many models with contains operator" | "findMany - should handle multiple where conditions with different operators" | "findMany - should find many models with contains operator (using symbol)" | "findMany - should find many models with eq operator" | "findMany - should find many models with ne operator" | "findMany - should find many models with gt operator" | "findMany - should find many models with gte operator" | "findMany - should find many models with lte operator" | "findMany - should find many models with lt operator" | "findMany - should find many models with in operator" | "findMany - should find many models with not_in operator" | "findMany - should find many models with sortBy" | "findMany - should find many models with limit" | "findMany - should find many models with offset" | "findMany - should find many models with limit and offset" | "findMany - should find many models with sortBy and offset" | "findMany - should find many models with sortBy and limit" | "findMany - should find many models with sortBy and limit and offset" | "findMany - should find many models with sortBy and limit and offset and where" | "update - should update a model" | "updateMany - should update all models when where is empty" | "updateMany - should update many models with a specific where" | "updateMany - should update many models with a multiple where" | "delete - should delete a model" | "delete - should not throw on record not found" | "delete - should delete by non-unique field" | "deleteMany - should delete many models" | "deleteMany - starts_with should not interpret regex patterns" | "deleteMany - ends_with should not interpret regex patterns" | "deleteMany - contains should not interpret regex patterns" | "deleteMany - should delete many models with numeric values" | "deleteMany - should delete many models with boolean values" | "count - should count many models" | "count - should return 0 with no rows to count" | "count - should count with where clause" | "update - should correctly return record when updating a field used in where clause" | "update - should handle updating multiple fields including where clause field" | "update - should work when updated field is not in where clause" | "findOne - backwards join should only return single record not array" | "findMany - backwards join should only return single record not array" | "findOne - backwards join with modified field name (session base, users-table join)" | "findOne - multiple joins should return result even when some joined tables have no matching rows" | "findOne - should be able to perform a limited join" | "findOne - should be able to perform a complex limited join" | "findMany - should be able to perform a limited join" | "findMany - should be able to perform a complex limited join" | "findOne - should return null for one-to-one join when joined record doesn't exist" | "findMany - should return null for one-to-one join when joined records don't exist" | "findMany - should return empty array for one-to-many join when joined records don't exist" | "findMany - should handle mixed joins correctly when some are missing" | "create - should support arrays" | "create - should support json" | "update - should support multiple where conditions under AND connector with unique field" | "findMany - eq operator with null value (single condition) should use IS NULL" | "findMany - eq and ne operators with null value in AND group should use IS NULL / IS NOT NULL" | "findMany - eq and ne operators with null value in OR group should use IS NULL / IS NOT NULL" | "update - should return updated record when where condition uses null value", boolean> & {
11
11
  ALL?: boolean;
12
12
  }> | undefined;
13
13
  } & {
@@ -199,8 +199,12 @@ declare const getNormalTestSuiteTests: ({
199
199
  test: () => Promise<void>;
200
200
  };
201
201
  "update - should support multiple where conditions under AND connector with unique field": () => Promise<void>;
202
+ "findMany - eq operator with null value (single condition) should use IS NULL": () => Promise<void>;
203
+ "findMany - eq and ne operators with null value in AND group should use IS NULL / IS NOT NULL": () => Promise<void>;
204
+ "findMany - eq and ne operators with null value in OR group should use IS NULL / IS NOT NULL": () => Promise<void>;
205
+ "update - should return updated record when where condition uses null value": () => Promise<void>;
202
206
  };
203
- declare const enableJoinTests: Partial<Record<"create - should create a model" | "create - should always return an id" | "create - should use generateId if provided" | "create - should return null for nullable foreign keys" | "create - should apply default values to fields" | "findOne - should find a model" | "findOne - should not apply defaultValue if value not found" | "findOne - should find a model using a reference field" | "findOne - should not throw on record not found" | "findOne - should find a model without id" | "findOne - should find a model with join" | "findOne - should find a model with modified field name" | "findOne - should find a model with modified model name" | "findOne - should find a model with additional fields" | "findOne - should select fields" | "findOne - should select fields with one-to-many join" | "findOne - should select fields with one-to-one join" | "findOne - should select fields with multiple joins" | "findOne - should find model with date field" | "findOne - should perform backwards joins" | "findOne - should return an object for one-to-one joins" | "findOne - should return an array for one-to-many joins" | "findOne - should work with both one-to-one and one-to-many joins" | "findOne - should return null for failed base model lookup that has joins" | "findOne - should join a model with modified field name" | "findMany - should find many models" | "findMany - should find many models with date fields" | "findMany - should find many models with join" | "findMany - should find many with join and limit" | "findMany - should select fields" | "findMany - should select fields with one-to-many join" | "findMany - should select fields with one-to-one join" | "findMany - should select fields with multiple joins" | "findMany - should find many with join and offset" | "findMany - should find many with join and sortBy" | "findMany - should find many with join and where clause" | "findMany - should find many with join, where, limit, and offset" | "findMany - should find many with one-to-one join" | "findMany - should find many with both one-to-one and one-to-many joins" | "findMany - should return an empty array when no models are found" | "findMany - should return empty array when base records don't exist with joins" | "findMany - should find many models with starts_with operator" | "findMany - starts_with should not interpret regex patterns" | "findMany - ends_with should not interpret regex patterns" | "findMany - contains should not interpret regex patterns" | "findMany - should find many models with ends_with operator" | "findMany - should find many models with contains operator" | "findMany - should handle multiple where conditions with different operators" | "findMany - should find many models with contains operator (using symbol)" | "findMany - should find many models with eq operator" | "findMany - should find many models with ne operator" | "findMany - should find many models with gt operator" | "findMany - should find many models with gte operator" | "findMany - should find many models with lte operator" | "findMany - should find many models with lt operator" | "findMany - should find many models with in operator" | "findMany - should find many models with not_in operator" | "findMany - should find many models with sortBy" | "findMany - should find many models with limit" | "findMany - should find many models with offset" | "findMany - should find many models with limit and offset" | "findMany - should find many models with sortBy and offset" | "findMany - should find many models with sortBy and limit" | "findMany - should find many models with sortBy and limit and offset" | "findMany - should find many models with sortBy and limit and offset and where" | "update - should update a model" | "updateMany - should update all models when where is empty" | "updateMany - should update many models with a specific where" | "updateMany - should update many models with a multiple where" | "delete - should delete a model" | "delete - should not throw on record not found" | "delete - should delete by non-unique field" | "deleteMany - should delete many models" | "deleteMany - starts_with should not interpret regex patterns" | "deleteMany - ends_with should not interpret regex patterns" | "deleteMany - contains should not interpret regex patterns" | "deleteMany - should delete many models with numeric values" | "deleteMany - should delete many models with boolean values" | "count - should count many models" | "count - should return 0 with no rows to count" | "count - should count with where clause" | "update - should correctly return record when updating a field used in where clause" | "update - should handle updating multiple fields including where clause field" | "update - should work when updated field is not in where clause" | "findOne - backwards join should only return single record not array" | "findMany - backwards join should only return single record not array" | "findOne - backwards join with modified field name (session base, users-table join)" | "findOne - multiple joins should return result even when some joined tables have no matching rows" | "findOne - should be able to perform a limited join" | "findOne - should be able to perform a complex limited join" | "findMany - should be able to perform a limited join" | "findMany - should be able to perform a complex limited join" | "findOne - should return null for one-to-one join when joined record doesn't exist" | "findMany - should return null for one-to-one join when joined records don't exist" | "findMany - should return empty array for one-to-many join when joined records don't exist" | "findMany - should handle mixed joins correctly when some are missing" | "create - should support arrays" | "create - should support json" | "update - should support multiple where conditions under AND connector with unique field", boolean>>;
207
+ declare const enableJoinTests: Partial<Record<"create - should create a model" | "create - should always return an id" | "create - should use generateId if provided" | "create - should return null for nullable foreign keys" | "create - should apply default values to fields" | "findOne - should find a model" | "findOne - should not apply defaultValue if value not found" | "findOne - should find a model using a reference field" | "findOne - should not throw on record not found" | "findOne - should find a model without id" | "findOne - should find a model with join" | "findOne - should find a model with modified field name" | "findOne - should find a model with modified model name" | "findOne - should find a model with additional fields" | "findOne - should select fields" | "findOne - should select fields with one-to-many join" | "findOne - should select fields with one-to-one join" | "findOne - should select fields with multiple joins" | "findOne - should find model with date field" | "findOne - should perform backwards joins" | "findOne - should return an object for one-to-one joins" | "findOne - should return an array for one-to-many joins" | "findOne - should work with both one-to-one and one-to-many joins" | "findOne - should return null for failed base model lookup that has joins" | "findOne - should join a model with modified field name" | "findMany - should find many models" | "findMany - should find many models with date fields" | "findMany - should find many models with join" | "findMany - should find many with join and limit" | "findMany - should select fields" | "findMany - should select fields with one-to-many join" | "findMany - should select fields with one-to-one join" | "findMany - should select fields with multiple joins" | "findMany - should find many with join and offset" | "findMany - should find many with join and sortBy" | "findMany - should find many with join and where clause" | "findMany - should find many with join, where, limit, and offset" | "findMany - should find many with one-to-one join" | "findMany - should find many with both one-to-one and one-to-many joins" | "findMany - should return an empty array when no models are found" | "findMany - should return empty array when base records don't exist with joins" | "findMany - should find many models with starts_with operator" | "findMany - starts_with should not interpret regex patterns" | "findMany - ends_with should not interpret regex patterns" | "findMany - contains should not interpret regex patterns" | "findMany - should find many models with ends_with operator" | "findMany - should find many models with contains operator" | "findMany - should handle multiple where conditions with different operators" | "findMany - should find many models with contains operator (using symbol)" | "findMany - should find many models with eq operator" | "findMany - should find many models with ne operator" | "findMany - should find many models with gt operator" | "findMany - should find many models with gte operator" | "findMany - should find many models with lte operator" | "findMany - should find many models with lt operator" | "findMany - should find many models with in operator" | "findMany - should find many models with not_in operator" | "findMany - should find many models with sortBy" | "findMany - should find many models with limit" | "findMany - should find many models with offset" | "findMany - should find many models with limit and offset" | "findMany - should find many models with sortBy and offset" | "findMany - should find many models with sortBy and limit" | "findMany - should find many models with sortBy and limit and offset" | "findMany - should find many models with sortBy and limit and offset and where" | "update - should update a model" | "updateMany - should update all models when where is empty" | "updateMany - should update many models with a specific where" | "updateMany - should update many models with a multiple where" | "delete - should delete a model" | "delete - should not throw on record not found" | "delete - should delete by non-unique field" | "deleteMany - should delete many models" | "deleteMany - starts_with should not interpret regex patterns" | "deleteMany - ends_with should not interpret regex patterns" | "deleteMany - contains should not interpret regex patterns" | "deleteMany - should delete many models with numeric values" | "deleteMany - should delete many models with boolean values" | "count - should count many models" | "count - should return 0 with no rows to count" | "count - should count with where clause" | "update - should correctly return record when updating a field used in where clause" | "update - should handle updating multiple fields including where clause field" | "update - should work when updated field is not in where clause" | "findOne - backwards join should only return single record not array" | "findMany - backwards join should only return single record not array" | "findOne - backwards join with modified field name (session base, users-table join)" | "findOne - multiple joins should return result even when some joined tables have no matching rows" | "findOne - should be able to perform a limited join" | "findOne - should be able to perform a complex limited join" | "findMany - should be able to perform a limited join" | "findMany - should be able to perform a complex limited join" | "findOne - should return null for one-to-one join when joined record doesn't exist" | "findMany - should return null for one-to-one join when joined records don't exist" | "findMany - should return empty array for one-to-many join when joined records don't exist" | "findMany - should handle mixed joins correctly when some are missing" | "create - should support arrays" | "create - should support json" | "update - should support multiple where conditions under AND connector with unique field" | "findMany - eq operator with null value (single condition) should use IS NULL" | "findMany - eq and ne operators with null value in AND group should use IS NULL / IS NOT NULL" | "findMany - eq and ne operators with null value in OR group should use IS NULL / IS NOT NULL" | "update - should return updated record when where condition uses null value", boolean>>;
204
208
  //#endregion
205
209
  export { enableJoinTests, getNormalTestSuiteTests, normalTestSuite };
206
210
  //# sourceMappingURL=basic.d.mts.map
@@ -1,7 +1,6 @@
1
1
  import { createTestSuite } from "../create-test-suite.mjs";
2
2
  import { expect } from "vitest";
3
3
  import { organization } from "better-auth/plugins/organization";
4
-
5
4
  //#region src/adapter/suites/basic.ts
6
5
  /**
7
6
  * This test suite tests the basic CRUD operations of the adapter.
@@ -2728,6 +2727,188 @@ const getNormalTestSuiteTests = ({ adapter, generate, insertRandom, modifyBetter
2728
2727
  expect(result.name).toBe("Updated Name");
2729
2728
  expect(result.email).toBe(user.email);
2730
2729
  expect(result.id).toBe(user.id);
2730
+ },
2731
+ "findMany - eq operator with null value (single condition) should use IS NULL": async () => {
2732
+ const withNull = await adapter.create({
2733
+ model: "user",
2734
+ data: {
2735
+ ...await generate("user"),
2736
+ image: null
2737
+ },
2738
+ forceAllowId: true
2739
+ });
2740
+ const withImage = await adapter.create({
2741
+ model: "user",
2742
+ data: {
2743
+ ...await generate("user"),
2744
+ image: "https://example.com/avatar.png"
2745
+ },
2746
+ forceAllowId: true
2747
+ });
2748
+ const nullIds = (await adapter.findMany({
2749
+ model: "user",
2750
+ where: [{
2751
+ field: "image",
2752
+ operator: "eq",
2753
+ value: null
2754
+ }]
2755
+ })).map((u) => u.id);
2756
+ expect(nullIds).toContain(withNull.id);
2757
+ expect(nullIds).not.toContain(withImage.id);
2758
+ const notNullIds = (await adapter.findMany({
2759
+ model: "user",
2760
+ where: [{
2761
+ field: "image",
2762
+ operator: "ne",
2763
+ value: null
2764
+ }]
2765
+ })).map((u) => u.id);
2766
+ expect(notNullIds).not.toContain(withNull.id);
2767
+ expect(notNullIds).toContain(withImage.id);
2768
+ },
2769
+ "findMany - eq and ne operators with null value in AND group should use IS NULL / IS NOT NULL": async () => {
2770
+ const nullVerified = await adapter.create({
2771
+ model: "user",
2772
+ data: {
2773
+ ...await generate("user"),
2774
+ image: null,
2775
+ emailVerified: true
2776
+ },
2777
+ forceAllowId: true
2778
+ });
2779
+ const nullUnverified = await adapter.create({
2780
+ model: "user",
2781
+ data: {
2782
+ ...await generate("user"),
2783
+ image: null,
2784
+ emailVerified: false
2785
+ },
2786
+ forceAllowId: true
2787
+ });
2788
+ const imageVerified = await adapter.create({
2789
+ model: "user",
2790
+ data: {
2791
+ ...await generate("user"),
2792
+ image: "https://example.com/avatar.png",
2793
+ emailVerified: true
2794
+ },
2795
+ forceAllowId: true
2796
+ });
2797
+ const eqIds = (await adapter.findMany({
2798
+ model: "user",
2799
+ where: [{
2800
+ field: "image",
2801
+ operator: "eq",
2802
+ value: null,
2803
+ connector: "AND"
2804
+ }, {
2805
+ field: "emailVerified",
2806
+ value: true,
2807
+ connector: "AND"
2808
+ }]
2809
+ })).map((u) => u.id);
2810
+ expect(eqIds).toContain(nullVerified.id);
2811
+ expect(eqIds).not.toContain(nullUnverified.id);
2812
+ expect(eqIds).not.toContain(imageVerified.id);
2813
+ const neIds = (await adapter.findMany({
2814
+ model: "user",
2815
+ where: [{
2816
+ field: "image",
2817
+ operator: "ne",
2818
+ value: null,
2819
+ connector: "AND"
2820
+ }, {
2821
+ field: "emailVerified",
2822
+ value: true,
2823
+ connector: "AND"
2824
+ }]
2825
+ })).map((u) => u.id);
2826
+ expect(neIds).not.toContain(nullVerified.id);
2827
+ expect(neIds).not.toContain(nullUnverified.id);
2828
+ expect(neIds).toContain(imageVerified.id);
2829
+ },
2830
+ "findMany - eq and ne operators with null value in OR group should use IS NULL / IS NOT NULL": async () => {
2831
+ const withNull = await adapter.create({
2832
+ model: "user",
2833
+ data: {
2834
+ ...await generate("user"),
2835
+ image: null
2836
+ },
2837
+ forceAllowId: true
2838
+ });
2839
+ const targetImage = await adapter.create({
2840
+ model: "user",
2841
+ data: {
2842
+ ...await generate("user"),
2843
+ image: "https://example.com/target.png"
2844
+ },
2845
+ forceAllowId: true
2846
+ });
2847
+ const otherImage = await adapter.create({
2848
+ model: "user",
2849
+ data: {
2850
+ ...await generate("user"),
2851
+ image: "https://example.com/other.png"
2852
+ },
2853
+ forceAllowId: true
2854
+ });
2855
+ const eqIds = (await adapter.findMany({
2856
+ model: "user",
2857
+ where: [{
2858
+ field: "image",
2859
+ operator: "eq",
2860
+ value: null,
2861
+ connector: "OR"
2862
+ }, {
2863
+ field: "email",
2864
+ value: targetImage.email,
2865
+ connector: "OR"
2866
+ }]
2867
+ })).map((u) => u.id);
2868
+ expect(eqIds).toContain(withNull.id);
2869
+ expect(eqIds).toContain(targetImage.id);
2870
+ expect(eqIds).not.toContain(otherImage.id);
2871
+ const neIds = (await adapter.findMany({
2872
+ model: "user",
2873
+ where: [{
2874
+ field: "image",
2875
+ operator: "ne",
2876
+ value: null,
2877
+ connector: "OR"
2878
+ }, {
2879
+ field: "email",
2880
+ value: withNull.email,
2881
+ connector: "OR"
2882
+ }]
2883
+ })).map((u) => u.id);
2884
+ expect(neIds).toContain(withNull.id);
2885
+ expect(neIds).toContain(targetImage.id);
2886
+ expect(neIds).toContain(otherImage.id);
2887
+ },
2888
+ "update - should return updated record when where condition uses null value": async () => {
2889
+ const withNull = await adapter.create({
2890
+ model: "user",
2891
+ data: {
2892
+ ...await generate("user"),
2893
+ image: null
2894
+ },
2895
+ forceAllowId: true
2896
+ });
2897
+ const result = await adapter.update({
2898
+ model: "user",
2899
+ where: [{
2900
+ field: "id",
2901
+ value: withNull.id
2902
+ }, {
2903
+ field: "image",
2904
+ operator: "eq",
2905
+ value: null
2906
+ }],
2907
+ update: { name: "null-where-updated" }
2908
+ });
2909
+ expect(result).toBeDefined();
2910
+ expect(result.id).toBe(withNull.id);
2911
+ expect(result.name).toBe("null-where-updated");
2731
2912
  }
2732
2913
  };
2733
2914
  };
@@ -2736,7 +2917,7 @@ const enableJoinTests = getTestKeys().reduce((acc, test) => {
2736
2917
  if (test.includes("join")) acc[test] = false;
2737
2918
  return acc;
2738
2919
  }, {});
2739
-
2740
2920
  //#endregion
2741
2921
  export { enableJoinTests, getNormalTestSuiteTests, normalTestSuite };
2922
+
2742
2923
  //# sourceMappingURL=basic.mjs.map