UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

1 lines • 13.7 kB
{"version":3,"file":"test-instance.mjs","names":["Kysely","PostgresDialect","MysqlDialect","client"],"sources":["../../src/test-utils/test-instance.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport type {\n\tAwaitable,\n\tBetterAuthClientOptions,\n\tBetterAuthOptions,\n} from \"@better-auth/core\";\nimport type { SuccessContext } from \"@better-fetch/fetch\";\nimport { sql } from \"kysely\";\nimport { afterAll } from \"vitest\";\nimport { betterAuth } from \"../auth\";\nimport { createAuthClient } from \"../client\";\nimport { parseSetCookieHeader, setCookieToHeader } from \"../cookies\";\nimport { getAdapter, getMigrations } from \"../db\";\nimport { bearer } from \"../plugins\";\nimport type { Session, User } from \"../types\";\nimport { getBaseURL } from \"../utils/url\";\n\nconst cleanupSet = new Set<Function>();\n\ntype CurrentUserContext = {\n\theaders: Headers;\n};\nconst currentUserContextStorage = new AsyncLocalStorage<CurrentUserContext>();\n\nafterAll(async () => {\n\tfor (const cleanup of cleanupSet) {\n\t\tawait cleanup();\n\t\tcleanupSet.delete(cleanup);\n\t}\n});\n\nexport async function getTestInstance<\n\tO extends Partial<BetterAuthOptions>,\n\tC extends BetterAuthClientOptions,\n>(\n\toptions?: O | undefined,\n\tconfig?:\n\t\t| {\n\t\t\t\tclientOptions?: C;\n\t\t\t\tport?: number;\n\t\t\t\tdisableTestUser?: boolean;\n\t\t\t\ttestUser?: Partial<User>;\n\t\t\t\ttestWith?: \"sqlite\" | \"postgres\" | \"mongodb\" | \"mysql\";\n\t\t }\n\t\t| undefined,\n) {\n\tconst testWith = config?.testWith || \"sqlite\";\n\n\tasync function getPostgres() {\n\t\tconst { Kysely, PostgresDialect } = await import(\"kysely\");\n\t\tconst { Pool } = await import(\"pg\");\n\t\treturn new Kysely({\n\t\t\tdialect: new PostgresDialect({\n\t\t\t\tpool: new Pool({\n\t\t\t\t\tconnectionString:\n\t\t\t\t\t\t\"postgres://user:password@localhost:5432/better_auth\",\n\t\t\t\t}),\n\t\t\t}),\n\t\t});\n\t}\n\n\tasync function getSqlite() {\n\t\tconst { default: Database } = await import(\"better-sqlite3\");\n\t\treturn new Database(\":memory:\");\n\t}\n\n\tasync function getMysql() {\n\t\tconst { Kysely, MysqlDialect } = await import(\"kysely\");\n\t\tconst { createPool } = await import(\"mysql2/promise\");\n\t\treturn new Kysely({\n\t\t\tdialect: new MysqlDialect(\n\t\t\t\tcreatePool(\"mysql://user:password@localhost:3306/better_auth\"),\n\t\t\t),\n\t\t});\n\t}\n\n\tasync function mongodbClient() {\n\t\tconst { MongoClient } = await import(\"mongodb\");\n\t\tconst dbClient = async (connectionString: string, dbName: string) => {\n\t\t\tconst client = new MongoClient(connectionString);\n\t\t\tawait client.connect();\n\t\t\tconst db = client.db(dbName);\n\t\t\treturn db;\n\t\t};\n\t\tconst db = await dbClient(\"mongodb://127.0.0.1:27017\", \"better-auth\");\n\t\treturn db;\n\t}\n\n\tconst opts = {\n\t\tsocialProviders: {\n\t\t\tgithub: {\n\t\t\t\tclientId: \"test\",\n\t\t\t\tclientSecret: \"test\",\n\t\t\t},\n\t\t\tgoogle: {\n\t\t\t\tclientId: \"test\",\n\t\t\t\tclientSecret: \"test\",\n\t\t\t},\n\t\t},\n\t\tsecret: \"better-auth-secret-that-is-long-enough-for-validation-test\",\n\t\tdatabase:\n\t\t\ttestWith === \"postgres\"\n\t\t\t\t? { db: await getPostgres(), type: \"postgres\" }\n\t\t\t\t: testWith === \"mongodb\"\n\t\t\t\t\t? await Promise.all([\n\t\t\t\t\t\t\tmongodbClient(),\n\t\t\t\t\t\t\tawait import(\"../adapters/mongodb-adapter\"),\n\t\t\t\t\t\t]).then(([db, { mongodbAdapter }]) => mongodbAdapter(db))\n\t\t\t\t\t: testWith === \"mysql\"\n\t\t\t\t\t\t? { db: await getMysql(), type: \"mysql\" }\n\t\t\t\t\t\t: await getSqlite(),\n\t\temailAndPassword: {\n\t\t\tenabled: true,\n\t\t},\n\t\trateLimit: {\n\t\t\tenabled: false,\n\t\t},\n\t\tadvanced: {\n\t\t\tcookies: {},\n\t\t},\n\t\tlogger: {\n\t\t\tlevel: \"debug\",\n\t\t},\n\t} satisfies BetterAuthOptions;\n\n\tconst auth = betterAuth({\n\t\tbaseURL: \"http://localhost:\" + (config?.port || 3000),\n\t\t...opts,\n\t\t...options,\n\t\tplugins: [bearer(), ...(options?.plugins || [])],\n\t} as unknown as O);\n\n\tconst testUser = {\n\t\temail: \"test@test.com\",\n\t\tpassword: \"test123456\",\n\t\tname: \"test user\",\n\t\t...config?.testUser,\n\t};\n\tasync function createTestUser() {\n\t\tif (config?.disableTestUser) {\n\t\t\treturn;\n\t\t}\n\t\t//@ts-expect-error\n\t\tawait auth.api.signUpEmail({\n\t\t\tbody: testUser,\n\t\t});\n\t}\n\n\tif (testWith !== \"mongodb\") {\n\t\tconst { runMigrations } = await getMigrations({\n\t\t\t...auth.options,\n\t\t\tdatabase: opts.database,\n\t\t});\n\t\tawait runMigrations();\n\t}\n\n\tawait createTestUser();\n\n\tconst cleanup = async () => {\n\t\tif (testWith === \"mongodb\") {\n\t\t\tconst db = await mongodbClient();\n\t\t\tawait db.dropDatabase();\n\t\t\treturn;\n\t\t}\n\t\tif (testWith === \"postgres\") {\n\t\t\tconst postgres = await getPostgres();\n\t\t\tawait sql`DROP SCHEMA public CASCADE; CREATE SCHEMA public;`.execute(\n\t\t\t\tpostgres,\n\t\t\t);\n\t\t\tawait postgres.destroy();\n\t\t\treturn;\n\t\t}\n\n\t\tif (testWith === \"mysql\") {\n\t\t\tconst mysql = await getMysql();\n\t\t\tawait sql`SET FOREIGN_KEY_CHECKS = 0;`.execute(mysql);\n\t\t\tconst tables = await mysql.introspection.getTables();\n\t\t\tfor (const table of tables) {\n\t\t\t\t// @ts-expect-error\n\t\t\t\tawait mysql.deleteFrom(table.name).execute();\n\t\t\t}\n\t\t\tawait sql`SET FOREIGN_KEY_CHECKS = 1;`.execute(mysql);\n\t\t\treturn;\n\t\t}\n\t\tif (testWith === \"sqlite\") {\n\t\t\tconst sqlite = await getSqlite();\n\t\t\tsqlite.close();\n\t\t\treturn;\n\t\t}\n\t};\n\tcleanupSet.add(cleanup);\n\n\tconst customFetchImpl = async (\n\t\turl: string | URL | Request,\n\t\tinit?: RequestInit | undefined,\n\t) => {\n\t\tconst headers = init?.headers || {};\n\t\tconst storageHeaders = currentUserContextStorage.getStore()?.headers;\n\t\treturn auth.handler(\n\t\t\tnew Request(\n\t\t\t\turl,\n\t\t\t\tinit\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...init,\n\t\t\t\t\t\t\theaders: new Headers({\n\t\t\t\t\t\t\t\t...(storageHeaders\n\t\t\t\t\t\t\t\t\t? Object.fromEntries(storageHeaders.entries())\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t...(headers instanceof Headers\n\t\t\t\t\t\t\t\t\t? Object.fromEntries(headers.entries())\n\t\t\t\t\t\t\t\t\t: typeof headers === \"object\"\n\t\t\t\t\t\t\t\t\t\t? headers\n\t\t\t\t\t\t\t\t\t\t: {}),\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\t\t\theaders,\n\t\t\t\t\t\t},\n\t\t\t),\n\t\t);\n\t};\n\n\tconst client = createAuthClient({\n\t\t...(config?.clientOptions as C extends undefined ? {} : C),\n\t\tbaseURL: getBaseURL(\n\t\t\toptions?.baseURL || \"http://localhost:\" + (config?.port || 3000),\n\t\t\toptions?.basePath || \"/api/auth\",\n\t\t),\n\t\tfetchOptions: {\n\t\t\tcustomFetchImpl,\n\t\t},\n\t});\n\n\tasync function signInWithTestUser() {\n\t\tif (config?.disableTestUser) {\n\t\t\tthrow new Error(\"Test user is disabled\");\n\t\t}\n\t\tlet headers = new Headers();\n\t\tconst setCookie = (name: string, value: string) => {\n\t\t\tconst current = headers.get(\"cookie\");\n\t\t\theaders.set(\"cookie\", `${current || \"\"}; ${name}=${value}`);\n\t\t};\n\t\t//@ts-expect-error\n\t\tconst { data } = await client.signIn.email({\n\t\t\temail: testUser.email,\n\t\t\tpassword: testUser.password,\n\t\t\tfetchOptions: {\n\t\t\t\t//@ts-expect-error\n\t\t\t\tonSuccess(context) {\n\t\t\t\t\tconst header = context.response.headers.get(\"set-cookie\");\n\t\t\t\t\tconst cookies = parseSetCookieHeader(header || \"\");\n\t\t\t\t\tconst signedCookie = cookies.get(\"better-auth.session_token\")?.value;\n\t\t\t\t\theaders.set(\"cookie\", `better-auth.session_token=${signedCookie}`);\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\t\treturn {\n\t\t\tsession: data.session as Session,\n\t\t\tuser: data.user as User,\n\t\t\theaders,\n\t\t\tsetCookie,\n\t\t\trunWithUser: async (fn: (headers: Headers) => Promise<void>) => {\n\t\t\t\treturn currentUserContextStorage.run({ headers }, async () => {\n\t\t\t\t\tawait fn(headers);\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\t}\n\tasync function signInWithUser(email: string, password: string) {\n\t\tconst headers = new Headers();\n\t\t//@ts-expect-error\n\t\tconst { data } = await client.signIn.email({\n\t\t\temail,\n\t\t\tpassword,\n\t\t\tfetchOptions: {\n\t\t\t\t//@ts-expect-error\n\t\t\t\tonSuccess(context) {\n\t\t\t\t\tconst header = context.response.headers.get(\"set-cookie\");\n\t\t\t\t\tconst cookies = parseSetCookieHeader(header || \"\");\n\t\t\t\t\tconst signedCookie = cookies.get(\"better-auth.session_token\")?.value;\n\t\t\t\t\theaders.set(\"cookie\", `better-auth.session_token=${signedCookie}`);\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\t\treturn {\n\t\t\tres: data as {\n\t\t\t\tuser: User;\n\t\t\t\tsession: Session;\n\t\t\t},\n\t\t\theaders,\n\t\t};\n\t}\n\n\tfunction sessionSetter(headers: Headers) {\n\t\treturn (context: SuccessContext) => {\n\t\t\tconst header = context.response.headers.get(\"set-cookie\");\n\t\t\tif (header) {\n\t\t\t\tconst cookies = parseSetCookieHeader(header || \"\");\n\t\t\t\tconst signedCookie = cookies.get(\"better-auth.session_token\")?.value;\n\t\t\t\theaders.set(\"cookie\", `better-auth.session_token=${signedCookie}`);\n\t\t\t}\n\t\t};\n\t}\n\n\treturn {\n\t\tauth,\n\t\tclient,\n\t\ttestUser,\n\t\tsignInWithTestUser,\n\t\tsignInWithUser,\n\t\tcookieSetter: setCookieToHeader,\n\t\tcustomFetchImpl,\n\t\tsessionSetter,\n\t\tdb: await getAdapter(auth.options),\n\t\trunWithUser: async (\n\t\t\temail: string,\n\t\t\tpassword: string,\n\t\t\tfn: (headers: Headers) => Awaitable<void>,\n\t\t) => {\n\t\t\tconst { headers } = await signInWithUser(email, password);\n\t\t\treturn currentUserContextStorage.run({ headers }, async () => {\n\t\t\t\tawait fn(headers);\n\t\t\t});\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,MAAM,6BAAa,IAAI,KAAe;AAKtC,MAAM,4BAA4B,IAAI,mBAAuC;AAE7E,SAAS,YAAY;AACpB,MAAK,MAAM,WAAW,YAAY;AACjC,QAAM,SAAS;AACf,aAAW,OAAO,QAAQ;;EAE1B;AAEF,eAAsB,gBAIrB,SACA,QASC;CACD,MAAM,WAAW,QAAQ,YAAY;CAErC,eAAe,cAAc;EAC5B,MAAM,EAAE,kBAAQ,uCAAoB,MAAM,OAAO;EACjD,MAAM,EAAE,SAAS,MAAM,OAAO;AAC9B,SAAO,IAAIA,SAAO,EACjB,SAAS,IAAIC,kBAAgB,EAC5B,MAAM,IAAI,KAAK,EACd,kBACC,uDACD,CAAC,EACF,CAAC,EACF,CAAC;;CAGH,eAAe,YAAY;EAC1B,MAAM,EAAE,SAAS,aAAa,MAAM,OAAO;AAC3C,SAAO,IAAI,SAAS,WAAW;;CAGhC,eAAe,WAAW;EACzB,MAAM,EAAE,kBAAQ,iCAAiB,MAAM,OAAO;EAC9C,MAAM,EAAE,eAAe,MAAM,OAAO;AACpC,SAAO,IAAID,SAAO,EACjB,SAAS,IAAIE,eACZ,WAAW,mDAAmD,CAC9D,EACD,CAAC;;CAGH,eAAe,gBAAgB;EAC9B,MAAM,EAAE,gBAAgB,MAAM,OAAO;EACrC,MAAM,WAAW,OAAO,kBAA0B,WAAmB;GACpE,MAAMC,WAAS,IAAI,YAAY,iBAAiB;AAChD,SAAMA,SAAO,SAAS;AAEtB,UADWA,SAAO,GAAG,OAAO;;AAI7B,SADW,MAAM,SAAS,6BAA6B,cAAc;;CAItE,MAAM,OAAO;EACZ,iBAAiB;GAChB,QAAQ;IACP,UAAU;IACV,cAAc;IACd;GACD,QAAQ;IACP,UAAU;IACV,cAAc;IACd;GACD;EACD,QAAQ;EACR,UACC,aAAa,aACV;GAAE,IAAI,MAAM,aAAa;GAAE,MAAM;GAAY,GAC7C,aAAa,YACZ,MAAM,QAAQ,IAAI,CAClB,eAAe,EACf,MAAM,OAAO,yCACb,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,sBAAsB,eAAe,GAAG,CAAC,GACxD,aAAa,UACZ;GAAE,IAAI,MAAM,UAAU;GAAE,MAAM;GAAS,GACvC,MAAM,WAAW;EACvB,kBAAkB,EACjB,SAAS,MACT;EACD,WAAW,EACV,SAAS,OACT;EACD,UAAU,EACT,SAAS,EAAE,EACX;EACD,QAAQ,EACP,OAAO,SACP;EACD;CAED,MAAM,OAAO,WAAW;EACvB,SAAS,uBAAuB,QAAQ,QAAQ;EAChD,GAAG;EACH,GAAG;EACH,SAAS,CAAC,QAAQ,EAAE,GAAI,SAAS,WAAW,EAAE,CAAE;EAChD,CAAiB;CAElB,MAAM,WAAW;EAChB,OAAO;EACP,UAAU;EACV,MAAM;EACN,GAAG,QAAQ;EACX;CACD,eAAe,iBAAiB;AAC/B,MAAI,QAAQ,gBACX;AAGD,QAAM,KAAK,IAAI,YAAY,EAC1B,MAAM,UACN,CAAC;;AAGH,KAAI,aAAa,WAAW;EAC3B,MAAM,EAAE,kBAAkB,MAAM,cAAc;GAC7C,GAAG,KAAK;GACR,UAAU,KAAK;GACf,CAAC;AACF,QAAM,eAAe;;AAGtB,OAAM,gBAAgB;CAEtB,MAAM,UAAU,YAAY;AAC3B,MAAI,aAAa,WAAW;AAE3B,UADW,MAAM,eAAe,EACvB,cAAc;AACvB;;AAED,MAAI,aAAa,YAAY;GAC5B,MAAM,WAAW,MAAM,aAAa;AACpC,SAAM,GAAG,oDAAoD,QAC5D,SACA;AACD,SAAM,SAAS,SAAS;AACxB;;AAGD,MAAI,aAAa,SAAS;GACzB,MAAM,QAAQ,MAAM,UAAU;AAC9B,SAAM,GAAG,8BAA8B,QAAQ,MAAM;GACrD,MAAM,SAAS,MAAM,MAAM,cAAc,WAAW;AACpD,QAAK,MAAM,SAAS,OAEnB,OAAM,MAAM,WAAW,MAAM,KAAK,CAAC,SAAS;AAE7C,SAAM,GAAG,8BAA8B,QAAQ,MAAM;AACrD;;AAED,MAAI,aAAa,UAAU;AAE1B,IADe,MAAM,WAAW,EACzB,OAAO;AACd;;;AAGF,YAAW,IAAI,QAAQ;CAEvB,MAAM,kBAAkB,OACvB,KACA,SACI;EACJ,MAAM,UAAU,MAAM,WAAW,EAAE;EACnC,MAAM,iBAAiB,0BAA0B,UAAU,EAAE;AAC7D,SAAO,KAAK,QACX,IAAI,QACH,KACA,OACG;GACA,GAAG;GACH,SAAS,IAAI,QAAQ;IACpB,GAAI,iBACD,OAAO,YAAY,eAAe,SAAS,CAAC,GAC5C,EAAE;IACL,GAAI,mBAAmB,UACpB,OAAO,YAAY,QAAQ,SAAS,CAAC,GACrC,OAAO,YAAY,WAClB,UACA,EAAE;IACN,CAAC;GACF,GACA,EACA,SACA,CACH,CACD;;CAGF,MAAM,SAAS,iBAAiB;EAC/B,GAAI,QAAQ;EACZ,SAAS,WACR,SAAS,WAAW,uBAAuB,QAAQ,QAAQ,MAC3D,SAAS,YAAY,YACrB;EACD,cAAc,EACb,iBACA;EACD,CAAC;CAEF,eAAe,qBAAqB;AACnC,MAAI,QAAQ,gBACX,OAAM,IAAI,MAAM,wBAAwB;EAEzC,IAAI,UAAU,IAAI,SAAS;EAC3B,MAAM,aAAa,MAAc,UAAkB;GAClD,MAAM,UAAU,QAAQ,IAAI,SAAS;AACrC,WAAQ,IAAI,UAAU,GAAG,WAAW,GAAG,IAAI,KAAK,GAAG,QAAQ;;EAG5D,MAAM,EAAE,SAAS,MAAM,OAAO,OAAO,MAAM;GAC1C,OAAO,SAAS;GAChB,UAAU,SAAS;GACnB,cAAc,EAEb,UAAU,SAAS;IAGlB,MAAM,eADU,qBADD,QAAQ,SAAS,QAAQ,IAAI,aAAa,IACV,GAAG,CACrB,IAAI,4BAA4B,EAAE;AAC/D,YAAQ,IAAI,UAAU,6BAA6B,eAAe;MAEnE;GACD,CAAC;AACF,SAAO;GACN,SAAS,KAAK;GACd,MAAM,KAAK;GACX;GACA;GACA,aAAa,OAAO,OAA4C;AAC/D,WAAO,0BAA0B,IAAI,EAAE,SAAS,EAAE,YAAY;AAC7D,WAAM,GAAG,QAAQ;MAChB;;GAEH;;CAEF,eAAe,eAAe,OAAe,UAAkB;EAC9D,MAAM,UAAU,IAAI,SAAS;EAE7B,MAAM,EAAE,SAAS,MAAM,OAAO,OAAO,MAAM;GAC1C;GACA;GACA,cAAc,EAEb,UAAU,SAAS;IAGlB,MAAM,eADU,qBADD,QAAQ,SAAS,QAAQ,IAAI,aAAa,IACV,GAAG,CACrB,IAAI,4BAA4B,EAAE;AAC/D,YAAQ,IAAI,UAAU,6BAA6B,eAAe;MAEnE;GACD,CAAC;AACF,SAAO;GACN,KAAK;GAIL;GACA;;CAGF,SAAS,cAAc,SAAkB;AACxC,UAAQ,YAA4B;GACnC,MAAM,SAAS,QAAQ,SAAS,QAAQ,IAAI,aAAa;AACzD,OAAI,QAAQ;IAEX,MAAM,eADU,qBAAqB,UAAU,GAAG,CACrB,IAAI,4BAA4B,EAAE;AAC/D,YAAQ,IAAI,UAAU,6BAA6B,eAAe;;;;AAKrE,QAAO;EACN;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACA,IAAI,MAAM,WAAW,KAAK,QAAQ;EAClC,aAAa,OACZ,OACA,UACA,OACI;GACJ,MAAM,EAAE,YAAY,MAAM,eAAe,OAAO,SAAS;AACzD,UAAO,0BAA0B,IAAI,EAAE,SAAS,EAAE,YAAY;AAC7D,UAAM,GAAG,QAAQ;KAChB;;EAEH"}