@better-auth/test-utils 1.5.0-beta.10
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/.turbo/turbo-build.log +21 -0
- package/LICENSE.md +20 -0
- package/dist/adapter.d.mts +3 -0
- package/dist/adapter.mjs +4 -0
- package/dist/create-test-suite.d.mts +118 -0
- package/dist/create-test-suite.mjs +462 -0
- package/dist/create-test-suite.mjs.map +1 -0
- package/dist/test-adapter.d.mts +76 -0
- package/dist/test-adapter.mjs +163 -0
- package/dist/test-adapter.mjs.map +1 -0
- package/package.json +31 -0
- package/src/adapter/create-test-suite.ts +899 -0
- package/src/adapter/index.ts +7 -0
- package/src/adapter/test-adapter.ts +384 -0
- package/tsconfig.json +11 -0
- package/tsdown.config.ts +13 -0
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createTestSuite } from "./create-test-suite.mjs";
|
|
2
|
+
import { DBAdapter } from "@better-auth/core/db/adapter";
|
|
3
|
+
import { Awaitable, BetterAuthOptions } from "@better-auth/core";
|
|
4
|
+
|
|
5
|
+
//#region src/adapter/test-adapter.d.ts
|
|
6
|
+
type Logger = {
|
|
7
|
+
info: (...args: any[]) => void;
|
|
8
|
+
success: (...args: any[]) => void;
|
|
9
|
+
warn: (...args: any[]) => void;
|
|
10
|
+
error: (...args: any[]) => void;
|
|
11
|
+
debug: (...args: any[]) => void;
|
|
12
|
+
};
|
|
13
|
+
declare const testAdapter: ({
|
|
14
|
+
adapter: getAdapter,
|
|
15
|
+
runMigrations,
|
|
16
|
+
overrideBetterAuthOptions,
|
|
17
|
+
additionalCleanups,
|
|
18
|
+
tests,
|
|
19
|
+
prefixTests,
|
|
20
|
+
onFinish,
|
|
21
|
+
customIdGenerator,
|
|
22
|
+
transformIdOutput
|
|
23
|
+
}: {
|
|
24
|
+
/**
|
|
25
|
+
* A function that will return the adapter instance to test with.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* testAdapter({
|
|
30
|
+
* adapter: (options) => drizzleAdapter(drizzle(db), {
|
|
31
|
+
* schema: generateSchema(options),
|
|
32
|
+
* }),
|
|
33
|
+
* })
|
|
34
|
+
*/
|
|
35
|
+
adapter: (options: BetterAuthOptions) => Awaitable<(options: BetterAuthOptions) => DBAdapter<BetterAuthOptions>>;
|
|
36
|
+
/**
|
|
37
|
+
* A function that will run the database migrations.
|
|
38
|
+
*/
|
|
39
|
+
runMigrations: (betterAuthOptions: BetterAuthOptions) => Promise<void> | void;
|
|
40
|
+
/**
|
|
41
|
+
* Any potential better-auth options overrides.
|
|
42
|
+
*/
|
|
43
|
+
overrideBetterAuthOptions?: (betterAuthOptions: BetterAuthOptions) => BetterAuthOptions;
|
|
44
|
+
/**
|
|
45
|
+
* By default we will cleanup all tables automatically,
|
|
46
|
+
* but if you have additional cleanup logic, you can pass it here.
|
|
47
|
+
*
|
|
48
|
+
* Such as deleting a DB file that could had been created.
|
|
49
|
+
*/
|
|
50
|
+
additionalCleanups?: () => Promise<void> | void;
|
|
51
|
+
/**
|
|
52
|
+
* A test suite to run.
|
|
53
|
+
*/
|
|
54
|
+
tests: ReturnType<ReturnType<typeof createTestSuite>>[];
|
|
55
|
+
/**
|
|
56
|
+
* A prefix to add to the test suite name.
|
|
57
|
+
*/
|
|
58
|
+
prefixTests?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Upon finish of the tests, this function will be called.
|
|
61
|
+
*/
|
|
62
|
+
onFinish?: () => Promise<void> | void;
|
|
63
|
+
/**
|
|
64
|
+
* Custom ID generator function to be used by the helper functions. (such as `insertRandom`)
|
|
65
|
+
*/
|
|
66
|
+
customIdGenerator?: () => any;
|
|
67
|
+
/**
|
|
68
|
+
* A function that will transform the ID output.
|
|
69
|
+
*/
|
|
70
|
+
transformIdOutput?: (id: any) => any;
|
|
71
|
+
}) => Promise<{
|
|
72
|
+
execute: () => void;
|
|
73
|
+
}>;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { Logger, testAdapter };
|
|
76
|
+
//# sourceMappingURL=test-adapter.d.mts.map
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { deepmerge, initGetModelName } from "@better-auth/core/db/adapter";
|
|
2
|
+
import { TTY_COLORS } from "@better-auth/core/env";
|
|
3
|
+
import { afterAll, beforeAll, describe } from "vitest";
|
|
4
|
+
import { getAuthTables } from "better-auth/db";
|
|
5
|
+
|
|
6
|
+
//#region src/adapter/test-adapter.ts
|
|
7
|
+
const testAdapter = async ({ adapter: getAdapter, runMigrations, overrideBetterAuthOptions, additionalCleanups, tests, prefixTests, onFinish, customIdGenerator, transformIdOutput }) => {
|
|
8
|
+
const defaultBAOptions = {};
|
|
9
|
+
let betterAuthOptions = {
|
|
10
|
+
...defaultBAOptions,
|
|
11
|
+
...overrideBetterAuthOptions?.(defaultBAOptions) || {}
|
|
12
|
+
};
|
|
13
|
+
let adapter = (await getAdapter(betterAuthOptions))(betterAuthOptions);
|
|
14
|
+
const adapterName = adapter.options?.adapterConfig.adapterName;
|
|
15
|
+
const adapterId = adapter.options?.adapterConfig.adapterId || adapter.id;
|
|
16
|
+
const adapterDisplayName = adapterName || adapterId;
|
|
17
|
+
const refreshAdapter = async (betterAuthOptions) => {
|
|
18
|
+
adapter = (await getAdapter(betterAuthOptions))(betterAuthOptions);
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* A helper function to log to the console.
|
|
22
|
+
*/
|
|
23
|
+
const log = {
|
|
24
|
+
info: (...args) => console.log(`${TTY_COLORS.fg.blue}INFO ${TTY_COLORS.reset} [${adapterDisplayName}]`, ...args),
|
|
25
|
+
success: (...args) => console.log(`${TTY_COLORS.fg.green}SUCCESS${TTY_COLORS.reset} [${adapterDisplayName}]`, ...args),
|
|
26
|
+
warn: (...args) => console.log(`${TTY_COLORS.fg.yellow}WARN ${TTY_COLORS.reset} [${adapterDisplayName}]`, ...args),
|
|
27
|
+
error: (...args) => console.log(`${TTY_COLORS.fg.red}ERROR ${TTY_COLORS.reset} [${adapterDisplayName}]`, ...args),
|
|
28
|
+
debug: (...args) => console.log(`${TTY_COLORS.fg.magenta}DEBUG ${TTY_COLORS.reset} [${adapterDisplayName}]`, ...args)
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Cleanup function to remove all rows from the database.
|
|
32
|
+
*/
|
|
33
|
+
const cleanup = async () => {
|
|
34
|
+
const start = performance.now();
|
|
35
|
+
await refreshAdapter(betterAuthOptions);
|
|
36
|
+
const getAllModels = getAuthTables(betterAuthOptions);
|
|
37
|
+
for (const model of Object.keys(getAllModels)) {
|
|
38
|
+
const getModelName = initGetModelName({
|
|
39
|
+
usePlural: adapter.options?.adapterConfig?.usePlural,
|
|
40
|
+
schema: getAllModels
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
const modelName = getModelName(model);
|
|
44
|
+
await adapter.deleteMany({
|
|
45
|
+
model: modelName,
|
|
46
|
+
where: []
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
const msg = `Error while cleaning up all rows from ${model}`;
|
|
50
|
+
log.error(msg, error);
|
|
51
|
+
throw new Error(msg, { cause: error });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
await additionalCleanups?.();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const msg = `Error while running additional cleanups`;
|
|
58
|
+
log.error(msg, error);
|
|
59
|
+
throw new Error(msg, { cause: error });
|
|
60
|
+
}
|
|
61
|
+
await refreshAdapter(betterAuthOptions);
|
|
62
|
+
log.success(`${TTY_COLORS.bright}CLEAN-UP${TTY_COLORS.reset} completed successfully (${(performance.now() - start).toFixed(3)}ms)`);
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* A function that will run the database migrations.
|
|
66
|
+
*/
|
|
67
|
+
const migrate = async () => {
|
|
68
|
+
const start = performance.now();
|
|
69
|
+
try {
|
|
70
|
+
await runMigrations(betterAuthOptions);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const msg = `Error while running migrations`;
|
|
73
|
+
log.error(msg, error);
|
|
74
|
+
throw new Error(msg, { cause: error });
|
|
75
|
+
}
|
|
76
|
+
log.success(`${TTY_COLORS.bright}MIGRATIONS${TTY_COLORS.reset} completed successfully (${(performance.now() - start).toFixed(3)}ms)`);
|
|
77
|
+
};
|
|
78
|
+
return { execute: () => {
|
|
79
|
+
describe(adapterDisplayName, async () => {
|
|
80
|
+
const allSuiteStats = [];
|
|
81
|
+
beforeAll(async () => {
|
|
82
|
+
await migrate();
|
|
83
|
+
}, 6e4);
|
|
84
|
+
afterAll(async () => {
|
|
85
|
+
await cleanup();
|
|
86
|
+
if (allSuiteStats.length > 0) {
|
|
87
|
+
const totalMigrations = allSuiteStats.reduce((sum, stats) => sum + stats.migrationCount, 0);
|
|
88
|
+
const totalMigrationTime = allSuiteStats.reduce((sum, stats) => sum + stats.totalMigrationTime, 0);
|
|
89
|
+
const totalTests = allSuiteStats.reduce((sum, stats) => sum + stats.testCount, 0);
|
|
90
|
+
const totalDuration = allSuiteStats.reduce((sum, stats) => sum + stats.suiteDuration, 0);
|
|
91
|
+
const separator = `${"─".repeat(80)}`;
|
|
92
|
+
console.log(`\n${TTY_COLORS.fg.cyan}${separator}`);
|
|
93
|
+
console.log(`${TTY_COLORS.fg.cyan}${TTY_COLORS.bright}TEST SUITE STATISTICS SUMMARY${TTY_COLORS.reset}`);
|
|
94
|
+
console.log(`${TTY_COLORS.fg.cyan}${separator}${TTY_COLORS.reset}\n`);
|
|
95
|
+
for (const stats of allSuiteStats) {
|
|
96
|
+
const avgMigrationTime = stats.migrationCount > 0 ? (stats.totalMigrationTime / stats.migrationCount).toFixed(2) : "0.00";
|
|
97
|
+
console.log(`${TTY_COLORS.fg.magenta}${stats.suiteName}${TTY_COLORS.reset}:`);
|
|
98
|
+
console.log(` Tests: ${TTY_COLORS.fg.green}${stats.testCount}${TTY_COLORS.reset}`);
|
|
99
|
+
console.log(` Migrations: ${TTY_COLORS.fg.yellow}${stats.migrationCount}${TTY_COLORS.reset} (avg: ${avgMigrationTime}ms)`);
|
|
100
|
+
console.log(` Total Migration Time: ${TTY_COLORS.fg.yellow}${stats.totalMigrationTime.toFixed(2)}ms${TTY_COLORS.reset}`);
|
|
101
|
+
console.log(` Suite Duration: ${TTY_COLORS.fg.blue}${stats.suiteDuration.toFixed(2)}ms${TTY_COLORS.reset}`);
|
|
102
|
+
if (stats.groupingStats) {
|
|
103
|
+
const { totalGroups, averageTestsPerGroup, largestGroupSize, smallestGroupSize, groupsWithMultipleTests } = stats.groupingStats;
|
|
104
|
+
console.log(` Test Groups: ${TTY_COLORS.fg.cyan}${totalGroups}${TTY_COLORS.reset}`);
|
|
105
|
+
if (totalGroups > 0) {
|
|
106
|
+
console.log(` Avg Tests/Group: ${TTY_COLORS.fg.cyan}${averageTestsPerGroup.toFixed(2)}${TTY_COLORS.reset}`);
|
|
107
|
+
console.log(` Largest Group: ${TTY_COLORS.fg.cyan}${largestGroupSize}${TTY_COLORS.reset}`);
|
|
108
|
+
console.log(` Smallest Group: ${TTY_COLORS.fg.cyan}${smallestGroupSize}${TTY_COLORS.reset}`);
|
|
109
|
+
console.log(` Groups w/ Multiple Tests: ${TTY_COLORS.fg.cyan}${groupsWithMultipleTests}${TTY_COLORS.reset}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
console.log("");
|
|
113
|
+
}
|
|
114
|
+
const avgMigrationTime = totalMigrations > 0 ? (totalMigrationTime / totalMigrations).toFixed(2) : "0.00";
|
|
115
|
+
const totalGroups = allSuiteStats.reduce((sum, stats) => sum + (stats.groupingStats?.totalGroups || 0), 0);
|
|
116
|
+
const totalGroupsWithMultipleTests = allSuiteStats.reduce((sum, stats) => sum + (stats.groupingStats?.groupsWithMultipleTests || 0), 0);
|
|
117
|
+
const totalTestsInGroups = allSuiteStats.reduce((sum, stats) => sum + (stats.groupingStats?.totalTestsInGroups || 0), 0);
|
|
118
|
+
const avgTestsPerGroup = totalGroups > 0 ? totalTestsInGroups / totalGroups : 0;
|
|
119
|
+
console.log(`${TTY_COLORS.fg.cyan}${separator}`);
|
|
120
|
+
console.log(`${TTY_COLORS.fg.cyan}${TTY_COLORS.bright}TOTALS${TTY_COLORS.reset}`);
|
|
121
|
+
console.log(` Total Tests: ${TTY_COLORS.fg.green}${totalTests}${TTY_COLORS.reset}`);
|
|
122
|
+
console.log(` Total Migrations: ${TTY_COLORS.fg.yellow}${totalMigrations}${TTY_COLORS.reset} (avg: ${avgMigrationTime}ms)`);
|
|
123
|
+
console.log(` Total Migration Time: ${TTY_COLORS.fg.yellow}${totalMigrationTime.toFixed(2)}ms${TTY_COLORS.reset}`);
|
|
124
|
+
console.log(` Total Duration: ${TTY_COLORS.fg.blue}${totalDuration.toFixed(2)}ms${TTY_COLORS.reset}`);
|
|
125
|
+
if (totalGroups > 0) {
|
|
126
|
+
console.log(` Total Test Groups: ${TTY_COLORS.fg.cyan}${totalGroups}${TTY_COLORS.reset}`);
|
|
127
|
+
console.log(` Avg Tests/Group: ${TTY_COLORS.fg.cyan}${avgTestsPerGroup.toFixed(2)}${TTY_COLORS.reset}`);
|
|
128
|
+
console.log(` Groups w/ Multiple Tests: ${TTY_COLORS.fg.cyan}${totalGroupsWithMultipleTests}${TTY_COLORS.reset}`);
|
|
129
|
+
}
|
|
130
|
+
console.log(`${TTY_COLORS.fg.cyan}${separator}${TTY_COLORS.reset}\n`);
|
|
131
|
+
}
|
|
132
|
+
await onFinish?.();
|
|
133
|
+
}, 6e4);
|
|
134
|
+
for (const testSuite of tests) await testSuite({
|
|
135
|
+
adapter: async () => {
|
|
136
|
+
await refreshAdapter(betterAuthOptions);
|
|
137
|
+
return adapter;
|
|
138
|
+
},
|
|
139
|
+
adapterDisplayName,
|
|
140
|
+
log,
|
|
141
|
+
getBetterAuthOptions: () => betterAuthOptions,
|
|
142
|
+
modifyBetterAuthOptions: async (options) => {
|
|
143
|
+
const newOptions = deepmerge(defaultBAOptions, options);
|
|
144
|
+
betterAuthOptions = deepmerge(newOptions, overrideBetterAuthOptions?.(newOptions) || {});
|
|
145
|
+
await refreshAdapter(betterAuthOptions);
|
|
146
|
+
return betterAuthOptions;
|
|
147
|
+
},
|
|
148
|
+
cleanup,
|
|
149
|
+
prefixTests,
|
|
150
|
+
runMigrations: migrate,
|
|
151
|
+
onTestFinish: async (stats) => {
|
|
152
|
+
allSuiteStats.push(stats);
|
|
153
|
+
},
|
|
154
|
+
customIdGenerator,
|
|
155
|
+
transformIdOutput
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
} };
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
//#endregion
|
|
162
|
+
export { testAdapter };
|
|
163
|
+
//# sourceMappingURL=test-adapter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-adapter.mjs","names":[],"sources":["../src/adapter/test-adapter.ts"],"sourcesContent":["import type { Awaitable, BetterAuthOptions } from \"@better-auth/core\";\nimport type { DBAdapter } from \"@better-auth/core/db/adapter\";\nimport { deepmerge, initGetModelName } from \"@better-auth/core/db/adapter\";\nimport { TTY_COLORS } from \"@better-auth/core/env\";\nimport { getAuthTables } from \"better-auth/db\";\nimport { afterAll, beforeAll, describe } from \"vitest\";\nimport type { createTestSuite, TestSuiteStats } from \"./create-test-suite\";\n\nexport type Logger = {\n\tinfo: (...args: any[]) => void;\n\tsuccess: (...args: any[]) => void;\n\twarn: (...args: any[]) => void;\n\terror: (...args: any[]) => void;\n\tdebug: (...args: any[]) => void;\n};\n\nexport const testAdapter = async ({\n\tadapter: getAdapter,\n\trunMigrations,\n\toverrideBetterAuthOptions,\n\tadditionalCleanups,\n\ttests,\n\tprefixTests,\n\tonFinish,\n\tcustomIdGenerator,\n\ttransformIdOutput,\n}: {\n\t/**\n\t * A function that will return the adapter instance to test with.\n\t *\n\t * @example\n\t * ```ts\n\t * testAdapter({\n\t * adapter: (options) => drizzleAdapter(drizzle(db), {\n\t * schema: generateSchema(options),\n\t * }),\n\t * })\n\t */\n\tadapter: (\n\t\toptions: BetterAuthOptions,\n\t) => Awaitable<(options: BetterAuthOptions) => DBAdapter<BetterAuthOptions>>;\n\t/**\n\t * A function that will run the database migrations.\n\t */\n\trunMigrations: (betterAuthOptions: BetterAuthOptions) => Promise<void> | void;\n\t/**\n\t * Any potential better-auth options overrides.\n\t */\n\toverrideBetterAuthOptions?: (\n\t\tbetterAuthOptions: BetterAuthOptions,\n\t) => BetterAuthOptions;\n\t/**\n\t * By default we will cleanup all tables automatically,\n\t * but if you have additional cleanup logic, you can pass it here.\n\t *\n\t * Such as deleting a DB file that could had been created.\n\t */\n\tadditionalCleanups?: () => Promise<void> | void;\n\t/**\n\t * A test suite to run.\n\t */\n\ttests: ReturnType<ReturnType<typeof createTestSuite>>[];\n\t/**\n\t * A prefix to add to the test suite name.\n\t */\n\tprefixTests?: string;\n\t/**\n\t * Upon finish of the tests, this function will be called.\n\t */\n\tonFinish?: () => Promise<void> | void;\n\t/**\n\t * Custom ID generator function to be used by the helper functions. (such as `insertRandom`)\n\t */\n\tcustomIdGenerator?: () => any;\n\t/**\n\t * A function that will transform the ID output.\n\t */\n\ttransformIdOutput?: (id: any) => any;\n}) => {\n\tconst defaultBAOptions = {} satisfies BetterAuthOptions;\n\tlet betterAuthOptions = (() => {\n\t\treturn {\n\t\t\t...defaultBAOptions,\n\t\t\t...(overrideBetterAuthOptions?.(defaultBAOptions) || {}),\n\t\t} satisfies BetterAuthOptions;\n\t})();\n\n\tlet adapter: DBAdapter<BetterAuthOptions> = (\n\t\tawait getAdapter(betterAuthOptions)\n\t)(betterAuthOptions);\n\n\tconst adapterName = adapter.options?.adapterConfig.adapterName;\n\tconst adapterId = adapter.options?.adapterConfig.adapterId || adapter.id;\n\tconst adapterDisplayName = adapterName || adapterId;\n\n\tconst refreshAdapter = async (betterAuthOptions: BetterAuthOptions) => {\n\t\tadapter = (await getAdapter(betterAuthOptions))(betterAuthOptions);\n\t};\n\n\t/**\n\t * A helper function to log to the console.\n\t */\n\tconst log: Logger = (() => {\n\t\treturn {\n\t\t\tinfo: (...args: any[]) =>\n\t\t\t\tconsole.log(\n\t\t\t\t\t`${TTY_COLORS.fg.blue}INFO ${TTY_COLORS.reset} [${adapterDisplayName}]`,\n\t\t\t\t\t...args,\n\t\t\t\t),\n\t\t\tsuccess: (...args: any[]) =>\n\t\t\t\tconsole.log(\n\t\t\t\t\t`${TTY_COLORS.fg.green}SUCCESS${TTY_COLORS.reset} [${adapterDisplayName}]`,\n\t\t\t\t\t...args,\n\t\t\t\t),\n\t\t\twarn: (...args: any[]) =>\n\t\t\t\tconsole.log(\n\t\t\t\t\t`${TTY_COLORS.fg.yellow}WARN ${TTY_COLORS.reset} [${adapterDisplayName}]`,\n\t\t\t\t\t...args,\n\t\t\t\t),\n\t\t\terror: (...args: any[]) =>\n\t\t\t\tconsole.log(\n\t\t\t\t\t`${TTY_COLORS.fg.red}ERROR ${TTY_COLORS.reset} [${adapterDisplayName}]`,\n\t\t\t\t\t...args,\n\t\t\t\t),\n\t\t\tdebug: (...args: any[]) =>\n\t\t\t\tconsole.log(\n\t\t\t\t\t`${TTY_COLORS.fg.magenta}DEBUG ${TTY_COLORS.reset} [${adapterDisplayName}]`,\n\t\t\t\t\t...args,\n\t\t\t\t),\n\t\t};\n\t})();\n\n\t/**\n\t * Cleanup function to remove all rows from the database.\n\t */\n\tconst cleanup = async () => {\n\t\tconst start = performance.now();\n\t\tawait refreshAdapter(betterAuthOptions);\n\t\tconst getAllModels = getAuthTables(betterAuthOptions);\n\n\t\t// Clean up all rows from all models\n\t\tfor (const model of Object.keys(getAllModels)) {\n\t\t\tconst getModelName = initGetModelName({\n\t\t\t\tusePlural: adapter.options?.adapterConfig?.usePlural,\n\t\t\t\tschema: getAllModels,\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tconst modelName = getModelName(model);\n\t\t\t\tawait adapter.deleteMany({ model: modelName, where: [] });\n\t\t\t} catch (error) {\n\t\t\t\tconst msg = `Error while cleaning up all rows from ${model}`;\n\t\t\t\tlog.error(msg, error);\n\t\t\t\tthrow new Error(msg, {\n\t\t\t\t\tcause: error,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Run additional cleanups\n\t\ttry {\n\t\t\tawait additionalCleanups?.();\n\t\t} catch (error) {\n\t\t\tconst msg = `Error while running additional cleanups`;\n\t\t\tlog.error(msg, error);\n\t\t\tthrow new Error(msg, {\n\t\t\t\tcause: error,\n\t\t\t});\n\t\t}\n\t\tawait refreshAdapter(betterAuthOptions);\n\t\tlog.success(\n\t\t\t`${TTY_COLORS.bright}CLEAN-UP${TTY_COLORS.reset} completed successfully (${(performance.now() - start).toFixed(3)}ms)`,\n\t\t);\n\t};\n\n\t/**\n\t * A function that will run the database migrations.\n\t */\n\tconst migrate = async () => {\n\t\tconst start = performance.now();\n\n\t\ttry {\n\t\t\tawait runMigrations(betterAuthOptions);\n\t\t} catch (error) {\n\t\t\tconst msg = `Error while running migrations`;\n\t\t\tlog.error(msg, error);\n\t\t\tthrow new Error(msg, {\n\t\t\t\tcause: error,\n\t\t\t});\n\t\t}\n\t\tlog.success(\n\t\t\t`${TTY_COLORS.bright}MIGRATIONS${TTY_COLORS.reset} completed successfully (${(performance.now() - start).toFixed(3)}ms)`,\n\t\t);\n\t};\n\n\treturn {\n\t\texecute: () => {\n\t\t\tdescribe(adapterDisplayName, async () => {\n\t\t\t\t// Collect statistics from all test suites\n\t\t\t\tconst allSuiteStats: TestSuiteStats[] = [];\n\n\t\t\t\tbeforeAll(async () => {\n\t\t\t\t\tawait migrate();\n\t\t\t\t}, 60000);\n\n\t\t\t\tafterAll(async () => {\n\t\t\t\t\tawait cleanup();\n\n\t\t\t\t\t// Display statistics summary\n\t\t\t\t\tif (allSuiteStats.length > 0) {\n\t\t\t\t\t\tconst totalMigrations = allSuiteStats.reduce(\n\t\t\t\t\t\t\t(sum, stats) => sum + stats.migrationCount,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst totalMigrationTime = allSuiteStats.reduce(\n\t\t\t\t\t\t\t(sum, stats) => sum + stats.totalMigrationTime,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst totalTests = allSuiteStats.reduce(\n\t\t\t\t\t\t\t(sum, stats) => sum + stats.testCount,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst totalDuration = allSuiteStats.reduce(\n\t\t\t\t\t\t\t(sum, stats) => sum + stats.suiteDuration,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst dash = \"─\";\n\t\t\t\t\t\tconst separator = `${dash.repeat(80)}`;\n\n\t\t\t\t\t\tconsole.log(`\\n${TTY_COLORS.fg.cyan}${separator}`);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`${TTY_COLORS.fg.cyan}${TTY_COLORS.bright}TEST SUITE STATISTICS SUMMARY${TTY_COLORS.reset}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`${TTY_COLORS.fg.cyan}${separator}${TTY_COLORS.reset}\\n`,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Per-suite breakdown\n\t\t\t\t\t\tfor (const stats of allSuiteStats) {\n\t\t\t\t\t\t\tconst avgMigrationTime =\n\t\t\t\t\t\t\t\tstats.migrationCount > 0\n\t\t\t\t\t\t\t\t\t? (stats.totalMigrationTime / stats.migrationCount).toFixed(2)\n\t\t\t\t\t\t\t\t\t: \"0.00\";\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t`${TTY_COLORS.fg.magenta}${stats.suiteName}${TTY_COLORS.reset}:`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t` Tests: ${TTY_COLORS.fg.green}${stats.testCount}${TTY_COLORS.reset}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t` Migrations: ${TTY_COLORS.fg.yellow}${stats.migrationCount}${TTY_COLORS.reset} (avg: ${avgMigrationTime}ms)`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t` Total Migration Time: ${TTY_COLORS.fg.yellow}${stats.totalMigrationTime.toFixed(2)}ms${TTY_COLORS.reset}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t` Suite Duration: ${TTY_COLORS.fg.blue}${stats.suiteDuration.toFixed(2)}ms${TTY_COLORS.reset}`,\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t// Display grouping statistics if available\n\t\t\t\t\t\t\tif (stats.groupingStats) {\n\t\t\t\t\t\t\t\tconst {\n\t\t\t\t\t\t\t\t\ttotalGroups,\n\t\t\t\t\t\t\t\t\taverageTestsPerGroup,\n\t\t\t\t\t\t\t\t\tlargestGroupSize,\n\t\t\t\t\t\t\t\t\tsmallestGroupSize,\n\t\t\t\t\t\t\t\t\tgroupsWithMultipleTests,\n\t\t\t\t\t\t\t\t} = stats.groupingStats;\n\t\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t\t` Test Groups: ${TTY_COLORS.fg.cyan}${totalGroups}${TTY_COLORS.reset}`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tif (totalGroups > 0) {\n\t\t\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t\t\t` Avg Tests/Group: ${TTY_COLORS.fg.cyan}${averageTestsPerGroup.toFixed(2)}${TTY_COLORS.reset}`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t\t\t` Largest Group: ${TTY_COLORS.fg.cyan}${largestGroupSize}${TTY_COLORS.reset}`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t\t\t` Smallest Group: ${TTY_COLORS.fg.cyan}${smallestGroupSize}${TTY_COLORS.reset}`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t\t\t` Groups w/ Multiple Tests: ${TTY_COLORS.fg.cyan}${groupsWithMultipleTests}${TTY_COLORS.reset}`,\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}\n\n\t\t\t\t\t\t\tconsole.log(\"\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Totals\n\t\t\t\t\t\tconst avgMigrationTime =\n\t\t\t\t\t\t\ttotalMigrations > 0\n\t\t\t\t\t\t\t\t? (totalMigrationTime / totalMigrations).toFixed(2)\n\t\t\t\t\t\t\t\t: \"0.00\";\n\n\t\t\t\t\t\t// Calculate total grouping statistics\n\t\t\t\t\t\tconst totalGroups = allSuiteStats.reduce(\n\t\t\t\t\t\t\t(sum, stats) => sum + (stats.groupingStats?.totalGroups || 0),\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst totalGroupsWithMultipleTests = allSuiteStats.reduce(\n\t\t\t\t\t\t\t(sum, stats) =>\n\t\t\t\t\t\t\t\tsum + (stats.groupingStats?.groupsWithMultipleTests || 0),\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst totalTestsInGroups = allSuiteStats.reduce(\n\t\t\t\t\t\t\t(sum, stats) =>\n\t\t\t\t\t\t\t\tsum + (stats.groupingStats?.totalTestsInGroups || 0),\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst avgTestsPerGroup =\n\t\t\t\t\t\t\ttotalGroups > 0 ? totalTestsInGroups / totalGroups : 0;\n\n\t\t\t\t\t\tconsole.log(`${TTY_COLORS.fg.cyan}${separator}`);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`${TTY_COLORS.fg.cyan}${TTY_COLORS.bright}TOTALS${TTY_COLORS.reset}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t` Total Tests: ${TTY_COLORS.fg.green}${totalTests}${TTY_COLORS.reset}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t` Total Migrations: ${TTY_COLORS.fg.yellow}${totalMigrations}${TTY_COLORS.reset} (avg: ${avgMigrationTime}ms)`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t` Total Migration Time: ${TTY_COLORS.fg.yellow}${totalMigrationTime.toFixed(2)}ms${TTY_COLORS.reset}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t` Total Duration: ${TTY_COLORS.fg.blue}${totalDuration.toFixed(2)}ms${TTY_COLORS.reset}`,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Display total grouping statistics\n\t\t\t\t\t\tif (totalGroups > 0) {\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t` Total Test Groups: ${TTY_COLORS.fg.cyan}${totalGroups}${TTY_COLORS.reset}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t` Avg Tests/Group: ${TTY_COLORS.fg.cyan}${avgTestsPerGroup.toFixed(2)}${TTY_COLORS.reset}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t` Groups w/ Multiple Tests: ${TTY_COLORS.fg.cyan}${totalGroupsWithMultipleTests}${TTY_COLORS.reset}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`${TTY_COLORS.fg.cyan}${separator}${TTY_COLORS.reset}\\n`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tawait onFinish?.();\n\t\t\t\t}, 60000);\n\n\t\t\t\tfor (const testSuite of tests) {\n\t\t\t\t\tawait testSuite({\n\t\t\t\t\t\tadapter: async () => {\n\t\t\t\t\t\t\tawait refreshAdapter(betterAuthOptions);\n\t\t\t\t\t\t\treturn adapter;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tadapterDisplayName,\n\t\t\t\t\t\tlog,\n\t\t\t\t\t\tgetBetterAuthOptions: () => betterAuthOptions,\n\t\t\t\t\t\tmodifyBetterAuthOptions: async (options) => {\n\t\t\t\t\t\t\tconst newOptions = deepmerge(defaultBAOptions, options);\n\t\t\t\t\t\t\tbetterAuthOptions = deepmerge(\n\t\t\t\t\t\t\t\tnewOptions,\n\t\t\t\t\t\t\t\toverrideBetterAuthOptions?.(newOptions) || {},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tawait refreshAdapter(betterAuthOptions);\n\t\t\t\t\t\t\treturn betterAuthOptions;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcleanup,\n\t\t\t\t\t\tprefixTests,\n\t\t\t\t\t\trunMigrations: migrate,\n\t\t\t\t\t\tonTestFinish: async (stats: TestSuiteStats) => {\n\t\t\t\t\t\t\tallSuiteStats.push(stats);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcustomIdGenerator,\n\t\t\t\t\t\ttransformIdOutput,\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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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@better-auth/test-utils",
|
|
3
|
+
"version": "1.5.0-beta.10",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/better-auth/better-auth.git"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
"./adapter": {
|
|
11
|
+
"dev-source": "./src/adapter.ts",
|
|
12
|
+
"default": "./dist/adapter.mjs",
|
|
13
|
+
"types": "./dist/adapter.d.mts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"tsdown": "^0.20.1",
|
|
18
|
+
"vitest": "^4.0.18",
|
|
19
|
+
"@better-auth/core": "1.5.0-beta.10",
|
|
20
|
+
"better-auth": "1.5.0-beta.10"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"vitest": "^4.0.18",
|
|
24
|
+
"@better-auth/core": "1.5.0-beta.10",
|
|
25
|
+
"better-auth": "1.5.0-beta.10"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsdown",
|
|
29
|
+
"dev": "tsdown --watch"
|
|
30
|
+
}
|
|
31
|
+
}
|