@terreno/api 0.1.0 → 0.3.0

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.
Files changed (40) hide show
  1. package/dist/api.d.ts +28 -2
  2. package/dist/api.js +20 -7
  3. package/dist/betterAuth.d.ts +91 -0
  4. package/dist/betterAuth.js +8 -0
  5. package/dist/betterAuth.test.d.ts +1 -0
  6. package/dist/betterAuth.test.js +181 -0
  7. package/dist/betterAuthApp.d.ts +22 -0
  8. package/dist/betterAuthApp.js +38 -0
  9. package/dist/betterAuthApp.test.d.ts +1 -0
  10. package/dist/betterAuthApp.test.js +242 -0
  11. package/dist/betterAuthSetup.d.ts +60 -0
  12. package/dist/betterAuthSetup.js +278 -0
  13. package/dist/betterAuthSetup.test.d.ts +1 -0
  14. package/dist/betterAuthSetup.test.js +684 -0
  15. package/dist/expressServer.js +3 -3
  16. package/dist/index.d.ts +4 -0
  17. package/dist/index.js +4 -0
  18. package/dist/terrenoApp.d.ts +189 -0
  19. package/dist/terrenoApp.js +352 -0
  20. package/dist/terrenoApp.test.d.ts +1 -0
  21. package/dist/terrenoApp.test.js +264 -0
  22. package/dist/terrenoPlugin.d.ts +34 -0
  23. package/package.json +6 -3
  24. package/src/api.ts +61 -3
  25. package/src/betterAuth.test.ts +160 -0
  26. package/src/betterAuth.ts +104 -0
  27. package/src/betterAuthApp.test.ts +114 -0
  28. package/src/betterAuthApp.ts +60 -0
  29. package/src/betterAuthSetup.test.ts +485 -0
  30. package/src/betterAuthSetup.ts +251 -0
  31. package/src/expressServer.ts +5 -6
  32. package/src/index.ts +4 -0
  33. package/src/openApiValidator.ts +1 -1
  34. package/src/terrenoApp.test.ts +201 -0
  35. package/src/terrenoApp.ts +347 -0
  36. package/src/terrenoPlugin.ts +34 -0
  37. package/.claude/CLAUDE.local.md +0 -204
  38. package/.cursor/rules/00-root.mdc +0 -338
  39. package/.github/copilot-instructions.md +0 -333
  40. package/AGENTS.md +0 -333
@@ -0,0 +1,114 @@
1
+ import {afterAll, afterEach, describe, expect, it} from "bun:test";
2
+ import express from "express";
3
+ import {MongoMemoryServer} from "mongodb-memory-server";
4
+ import mongoose, {Schema} from "mongoose";
5
+ import type {UserModel} from "./auth";
6
+ import type {BetterAuthConfig} from "./betterAuth";
7
+ import {BetterAuthApp} from "./betterAuthApp";
8
+
9
+ let conn: mongoose.Connection;
10
+ let mongod: MongoMemoryServer;
11
+ let TestUser: any;
12
+
13
+ const testUserSchema = new Schema({
14
+ admin: {default: false, type: Boolean},
15
+ betterAuthId: {type: String},
16
+ email: {required: true, type: String},
17
+ name: {type: String},
18
+ oauthProvider: {type: String},
19
+ });
20
+
21
+ const setup = (async () => {
22
+ mongod = await MongoMemoryServer.create();
23
+ conn = mongoose.createConnection(mongod.getUri());
24
+ await conn.asPromise();
25
+ TestUser = conn.model("BetterAuthAppTestUser", testUserSchema);
26
+ })();
27
+
28
+ afterAll(async () => {
29
+ await conn?.close();
30
+ await mongod?.stop();
31
+ });
32
+
33
+ afterEach(async () => {
34
+ await setup;
35
+ await TestUser.deleteMany({});
36
+ });
37
+
38
+ describe("BetterAuthApp", () => {
39
+ const makeConfig = (overrides: Partial<BetterAuthConfig> = {}): BetterAuthConfig => ({
40
+ baseURL: "http://localhost:3000",
41
+ enabled: true,
42
+ secret: "test-secret-at-least-32-characters-long",
43
+ ...overrides,
44
+ });
45
+
46
+ it("registers on an express app without throwing", async () => {
47
+ await setup;
48
+ const app = express();
49
+ app.use(express.json());
50
+
51
+ const plugin = new BetterAuthApp({
52
+ config: makeConfig(),
53
+ });
54
+
55
+ expect(() => plugin.register(app)).not.toThrow();
56
+ });
57
+
58
+ it("exposes the Better Auth instance after register", async () => {
59
+ await setup;
60
+ const app = express();
61
+ app.use(express.json());
62
+
63
+ const plugin = new BetterAuthApp({
64
+ config: makeConfig(),
65
+ });
66
+
67
+ expect(plugin.getAuth()).toBeUndefined();
68
+ plugin.register(app);
69
+ expect(plugin.getAuth()).toBeDefined();
70
+ expect(plugin.getAuth()?.api).toBeDefined();
71
+ });
72
+
73
+ it("registers with a userModel", async () => {
74
+ await setup;
75
+ const app = express();
76
+ app.use(express.json());
77
+
78
+ const plugin = new BetterAuthApp({
79
+ config: makeConfig(),
80
+ userModel: TestUser as UserModel,
81
+ });
82
+
83
+ expect(() => plugin.register(app)).not.toThrow();
84
+ expect(plugin.getAuth()).toBeDefined();
85
+ });
86
+
87
+ it("registers with a custom basePath", async () => {
88
+ await setup;
89
+ const app = express();
90
+ app.use(express.json());
91
+
92
+ const plugin = new BetterAuthApp({
93
+ config: makeConfig({basePath: "/custom/auth"}),
94
+ });
95
+
96
+ expect(() => plugin.register(app)).not.toThrow();
97
+ });
98
+
99
+ it("registers with social providers", async () => {
100
+ await setup;
101
+ const app = express();
102
+ app.use(express.json());
103
+
104
+ const plugin = new BetterAuthApp({
105
+ config: makeConfig({
106
+ githubOAuth: {clientId: "gh-id", clientSecret: "gh-secret"},
107
+ googleOAuth: {clientId: "g-id", clientSecret: "g-secret"},
108
+ }),
109
+ });
110
+
111
+ expect(() => plugin.register(app)).not.toThrow();
112
+ expect(plugin.getAuth()).toBeDefined();
113
+ });
114
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * BetterAuthApp plugin for @terreno/api.
3
+ *
4
+ * Registers Better Auth as a TerrenoPlugin, mounting routes, session middleware,
5
+ * and user sync on an existing Express application.
6
+ */
7
+
8
+ import type express from "express";
9
+ import type {UserModel} from "./auth";
10
+ import type {BetterAuthConfig} from "./betterAuth";
11
+ import {
12
+ type BetterAuthInstance,
13
+ createBetterAuth,
14
+ createBetterAuthSessionMiddleware,
15
+ getMongoClientFromMongoose,
16
+ mountBetterAuthRoutes,
17
+ setupBetterAuthUserSync,
18
+ } from "./betterAuthSetup";
19
+ import {logger} from "./logger";
20
+ import type {TerrenoPlugin} from "./terrenoPlugin";
21
+
22
+ export interface BetterAuthAppOptions {
23
+ config: BetterAuthConfig;
24
+ userModel?: UserModel;
25
+ }
26
+
27
+ export class BetterAuthApp implements TerrenoPlugin {
28
+ private auth: BetterAuthInstance | undefined;
29
+ private options: BetterAuthAppOptions;
30
+
31
+ constructor(options: BetterAuthAppOptions) {
32
+ this.options = options;
33
+ }
34
+
35
+ register(app: express.Application): void {
36
+ const {config, userModel} = this.options;
37
+
38
+ const mongoClient = getMongoClientFromMongoose();
39
+ this.auth = createBetterAuth({
40
+ config,
41
+ mongoClient,
42
+ userModel,
43
+ });
44
+
45
+ const basePath = config.basePath ?? "/api/auth";
46
+ mountBetterAuthRoutes(app, this.auth, basePath);
47
+
48
+ app.use(createBetterAuthSessionMiddleware(this.auth, userModel));
49
+
50
+ if (userModel) {
51
+ setupBetterAuthUserSync(this.auth, userModel);
52
+ }
53
+
54
+ logger.info("Better Auth initialized via BetterAuthApp plugin");
55
+ }
56
+
57
+ getAuth(): BetterAuthInstance | undefined {
58
+ return this.auth;
59
+ }
60
+ }
@@ -0,0 +1,485 @@
1
+ import {afterAll, afterEach, describe, expect, it, mock} from "bun:test";
2
+ import express from "express";
3
+ import {MongoMemoryServer} from "mongodb-memory-server";
4
+ import mongoose, {Schema} from "mongoose";
5
+ import type {UserModel} from "./auth";
6
+ import type {BetterAuthConfig, BetterAuthUser} from "./betterAuth";
7
+ import {
8
+ createBetterAuth,
9
+ createBetterAuthSessionMiddleware,
10
+ getBetterAuthSession,
11
+ getMongoClientFromMongoose,
12
+ hasBetterAuthSession,
13
+ mountBetterAuthRoutes,
14
+ setupBetterAuthUserSync,
15
+ syncBetterAuthUser,
16
+ } from "./betterAuthSetup";
17
+
18
+ // Use a separate connection to avoid conflict with bunSetup.ts preload
19
+ let conn: mongoose.Connection;
20
+ let mongod: MongoMemoryServer;
21
+ let TestUser: any;
22
+
23
+ // Simple user schema for testing
24
+ const testUserSchema = new Schema({
25
+ admin: {default: false, type: Boolean},
26
+ betterAuthId: {type: String},
27
+ email: {required: true, type: String},
28
+ name: {type: String},
29
+ oauthProvider: {type: String},
30
+ });
31
+
32
+ // Start memory server and create connection before tests run
33
+ const setup = (async () => {
34
+ mongod = await MongoMemoryServer.create();
35
+ conn = mongoose.createConnection(mongod.getUri());
36
+ await conn.asPromise();
37
+ TestUser = conn.model("BetterAuthTestUser", testUserSchema);
38
+ })();
39
+
40
+ // Helper to get the mongo client from our separate connection
41
+ const getClient = () => (conn as any).client;
42
+
43
+ afterAll(async () => {
44
+ await conn?.close();
45
+ await mongod?.stop();
46
+ });
47
+
48
+ afterEach(async () => {
49
+ await setup;
50
+ await TestUser.deleteMany({});
51
+ });
52
+
53
+ describe("createBetterAuth", () => {
54
+ it("throws if secret is not provided", async () => {
55
+ await setup;
56
+ const originalSecret = process.env.BETTER_AUTH_SECRET;
57
+ delete process.env.BETTER_AUTH_SECRET;
58
+
59
+ const config: BetterAuthConfig = {
60
+ baseURL: "http://localhost:3000",
61
+ enabled: true,
62
+ };
63
+
64
+ expect(() => createBetterAuth({config, mongoClient: getClient()})).toThrow(
65
+ "BETTER_AUTH_SECRET must be set"
66
+ );
67
+
68
+ process.env.BETTER_AUTH_SECRET = originalSecret;
69
+ });
70
+
71
+ it("throws if baseURL is not provided", async () => {
72
+ await setup;
73
+ const originalUrl = process.env.BETTER_AUTH_URL;
74
+ delete process.env.BETTER_AUTH_URL;
75
+
76
+ const config: BetterAuthConfig = {
77
+ enabled: true,
78
+ secret: "test-secret-at-least-32-characters-long",
79
+ };
80
+
81
+ expect(() => createBetterAuth({config, mongoClient: getClient()})).toThrow(
82
+ "BETTER_AUTH_URL must be set"
83
+ );
84
+
85
+ process.env.BETTER_AUTH_URL = originalUrl;
86
+ });
87
+
88
+ it("creates a Better Auth instance with valid config", async () => {
89
+ await setup;
90
+ const config: BetterAuthConfig = {
91
+ baseURL: "http://localhost:3000",
92
+ enabled: true,
93
+ secret: "test-secret-at-least-32-characters-long",
94
+ };
95
+
96
+ const auth = createBetterAuth({config, mongoClient: getClient()});
97
+
98
+ expect(auth).toBeDefined();
99
+ expect(auth.api).toBeDefined();
100
+ });
101
+
102
+ it("creates instance with social providers", async () => {
103
+ await setup;
104
+ const config: BetterAuthConfig = {
105
+ appleOAuth: {clientId: "apple-id", clientSecret: "apple-secret"},
106
+ baseURL: "http://localhost:3000",
107
+ enabled: true,
108
+ githubOAuth: {clientId: "github-id", clientSecret: "github-secret"},
109
+ googleOAuth: {clientId: "google-id", clientSecret: "google-secret"},
110
+ secret: "test-secret-at-least-32-characters-long",
111
+ };
112
+
113
+ const auth = createBetterAuth({config, mongoClient: getClient()});
114
+
115
+ expect(auth).toBeDefined();
116
+ });
117
+
118
+ it("uses env vars as fallback for secret and baseURL", async () => {
119
+ await setup;
120
+ process.env.BETTER_AUTH_SECRET = "env-secret-at-least-32-characters-long";
121
+ process.env.BETTER_AUTH_URL = "http://localhost:4000";
122
+
123
+ const config: BetterAuthConfig = {enabled: true};
124
+ const auth = createBetterAuth({config, mongoClient: getClient()});
125
+
126
+ expect(auth).toBeDefined();
127
+
128
+ delete process.env.BETTER_AUTH_SECRET;
129
+ delete process.env.BETTER_AUTH_URL;
130
+ });
131
+
132
+ it("uses custom basePath when provided", async () => {
133
+ await setup;
134
+ const config: BetterAuthConfig = {
135
+ basePath: "/custom/auth",
136
+ baseURL: "http://localhost:3000",
137
+ enabled: true,
138
+ secret: "test-secret-at-least-32-characters-long",
139
+ };
140
+
141
+ const auth = createBetterAuth({config, mongoClient: getClient()});
142
+ expect(auth).toBeDefined();
143
+ });
144
+ });
145
+
146
+ describe("syncBetterAuthUser", () => {
147
+ const makeBetterAuthUser = (overrides: Partial<BetterAuthUser> = {}): BetterAuthUser => ({
148
+ createdAt: new Date(),
149
+ email: "test@example.com",
150
+ emailVerified: true,
151
+ id: "ba-user-123",
152
+ image: null,
153
+ name: "Test User",
154
+ updatedAt: new Date(),
155
+ ...overrides,
156
+ });
157
+
158
+ it("creates a new user when none exists", async () => {
159
+ await setup;
160
+ const baUser = makeBetterAuthUser();
161
+ const result = await syncBetterAuthUser(TestUser as UserModel, baUser);
162
+
163
+ expect(result).toBeDefined();
164
+ expect(result.email).toBe("test@example.com");
165
+ expect(result.name).toBe("Test User");
166
+ expect(result.betterAuthId).toBe("ba-user-123");
167
+ expect(result.admin).toBe(false);
168
+ });
169
+
170
+ it("updates an existing user matched by betterAuthId", async () => {
171
+ await setup;
172
+ await TestUser.create({
173
+ betterAuthId: "ba-user-123",
174
+ email: "old@example.com",
175
+ name: "Old Name",
176
+ });
177
+
178
+ const baUser = makeBetterAuthUser({email: "new@example.com", name: "New Name"});
179
+ const result = await syncBetterAuthUser(TestUser as UserModel, baUser);
180
+
181
+ expect(result.email).toBe("new@example.com");
182
+ expect(result.name).toBe("New Name");
183
+ expect(result.betterAuthId).toBe("ba-user-123");
184
+
185
+ const count = await TestUser.countDocuments();
186
+ expect(count).toBe(1);
187
+ });
188
+
189
+ it("links existing user matched by email", async () => {
190
+ await setup;
191
+ await TestUser.create({
192
+ email: "test@example.com",
193
+ name: "Existing User",
194
+ });
195
+
196
+ const baUser = makeBetterAuthUser();
197
+ const result = await syncBetterAuthUser(TestUser as UserModel, baUser);
198
+
199
+ expect(result.betterAuthId).toBe("ba-user-123");
200
+ expect(result.email).toBe("test@example.com");
201
+
202
+ const count = await TestUser.countDocuments();
203
+ expect(count).toBe(1);
204
+ });
205
+
206
+ it("sets oauthProvider when linking by email", async () => {
207
+ await setup;
208
+ await TestUser.create({
209
+ email: "test@example.com",
210
+ name: "Existing User",
211
+ });
212
+
213
+ const baUser = makeBetterAuthUser();
214
+ const result = await syncBetterAuthUser(TestUser as UserModel, baUser, "google");
215
+
216
+ expect(result.oauthProvider).toBe("google");
217
+ });
218
+
219
+ it("uses email prefix as name when name is null", async () => {
220
+ await setup;
221
+ const baUser = makeBetterAuthUser({name: null});
222
+ const result = await syncBetterAuthUser(TestUser as UserModel, baUser);
223
+
224
+ expect(result.name).toBe("test");
225
+ });
226
+
227
+ it("does not overwrite name when betterAuthUser.name is null", async () => {
228
+ await setup;
229
+ await TestUser.create({
230
+ betterAuthId: "ba-user-123",
231
+ email: "test@example.com",
232
+ name: "Keep This Name",
233
+ });
234
+
235
+ const baUser = makeBetterAuthUser({name: null});
236
+ const result = await syncBetterAuthUser(TestUser as UserModel, baUser);
237
+
238
+ expect(result.name).toBe("Keep This Name");
239
+ });
240
+ });
241
+
242
+ describe("createBetterAuthSessionMiddleware", () => {
243
+ it("calls next when no session exists", async () => {
244
+ await setup;
245
+ const config: BetterAuthConfig = {
246
+ baseURL: "http://localhost:3000",
247
+ enabled: true,
248
+ secret: "test-secret-at-least-32-characters-long",
249
+ };
250
+
251
+ const auth = createBetterAuth({config, mongoClient: getClient()});
252
+ const middleware = createBetterAuthSessionMiddleware(auth);
253
+ const req = {headers: {}} as any;
254
+ const res = {} as any;
255
+ const next = mock(() => {});
256
+
257
+ await middleware(req, res, next);
258
+
259
+ expect(next).toHaveBeenCalledTimes(1);
260
+ expect(req.user).toBeUndefined();
261
+ });
262
+
263
+ it("calls next on error without crashing", async () => {
264
+ await setup;
265
+ const config: BetterAuthConfig = {
266
+ baseURL: "http://localhost:3000",
267
+ enabled: true,
268
+ secret: "test-secret-at-least-32-characters-long",
269
+ };
270
+
271
+ const auth = createBetterAuth({config, mongoClient: getClient()});
272
+
273
+ // Override getSession to throw
274
+ const origGetSession = auth.api.getSession;
275
+ (auth.api as any).getSession = () => {
276
+ throw new Error("Session error");
277
+ };
278
+
279
+ const middleware = createBetterAuthSessionMiddleware(auth);
280
+ const req = {headers: {}} as any;
281
+ const res = {} as any;
282
+ const next = mock(() => {});
283
+
284
+ await middleware(req, res, next);
285
+
286
+ expect(next).toHaveBeenCalledTimes(1);
287
+ expect(req.user).toBeUndefined();
288
+
289
+ (auth.api as any).getSession = origGetSession;
290
+ });
291
+
292
+ it("attaches basic user data when no userModel is provided", async () => {
293
+ await setup;
294
+ const config: BetterAuthConfig = {
295
+ baseURL: "http://localhost:3000",
296
+ enabled: true,
297
+ secret: "test-secret-at-least-32-characters-long",
298
+ };
299
+
300
+ const auth = createBetterAuth({config, mongoClient: getClient()});
301
+
302
+ const mockSession = {
303
+ session: {id: "session-1", userId: "user-1"},
304
+ user: {
305
+ email: "user@example.com",
306
+ id: "user-1",
307
+ name: "Test User",
308
+ },
309
+ };
310
+
311
+ // Override getSession to return mock session
312
+ (auth.api as any).getSession = async () => mockSession;
313
+
314
+ const middleware = createBetterAuthSessionMiddleware(auth);
315
+ const req = {headers: {}} as any;
316
+ const res = {} as any;
317
+ const next = mock(() => {});
318
+
319
+ await middleware(req, res, next);
320
+
321
+ expect(next).toHaveBeenCalledTimes(1);
322
+ expect(req.user).toBeDefined();
323
+ expect(req.user.email).toBe("user@example.com");
324
+ expect(req.user.betterAuthId).toBe("user-1");
325
+ expect(req.user.admin).toBe(false);
326
+ expect(req.betterAuthSession).toBe(mockSession);
327
+ });
328
+
329
+ it("looks up app user by betterAuthId when userModel is provided", async () => {
330
+ await setup;
331
+ const config: BetterAuthConfig = {
332
+ baseURL: "http://localhost:3000",
333
+ enabled: true,
334
+ secret: "test-secret-at-least-32-characters-long",
335
+ };
336
+
337
+ // Create an existing app user
338
+ const appUser = await TestUser.create({
339
+ betterAuthId: "user-1",
340
+ email: "user@example.com",
341
+ name: "App User",
342
+ });
343
+
344
+ const auth = createBetterAuth({config, mongoClient: getClient()});
345
+
346
+ const mockSession = {
347
+ session: {id: "session-1", userId: "user-1"},
348
+ user: {
349
+ email: "user@example.com",
350
+ id: "user-1",
351
+ name: "Test User",
352
+ },
353
+ };
354
+
355
+ (auth.api as any).getSession = async () => mockSession;
356
+
357
+ const middleware = createBetterAuthSessionMiddleware(auth, TestUser as UserModel);
358
+ const req = {headers: {}} as any;
359
+ const res = {} as any;
360
+ const next = mock(() => {});
361
+
362
+ await middleware(req, res, next);
363
+
364
+ expect(next).toHaveBeenCalledTimes(1);
365
+ expect(req.user).toBeDefined();
366
+ expect(req.user._id.toString()).toBe(appUser._id.toString());
367
+ expect(req.betterAuthSession).toBe(mockSession);
368
+ });
369
+
370
+ it("creates app user via sync when not found by betterAuthId", async () => {
371
+ await setup;
372
+ const config: BetterAuthConfig = {
373
+ baseURL: "http://localhost:3000",
374
+ enabled: true,
375
+ secret: "test-secret-at-least-32-characters-long",
376
+ };
377
+
378
+ const auth = createBetterAuth({config, mongoClient: getClient()});
379
+
380
+ const mockSession = {
381
+ session: {id: "session-1", userId: "new-user-1"},
382
+ user: {
383
+ email: "newuser@example.com",
384
+ id: "new-user-1",
385
+ name: "New User",
386
+ },
387
+ };
388
+
389
+ (auth.api as any).getSession = async () => mockSession;
390
+
391
+ const middleware = createBetterAuthSessionMiddleware(auth, TestUser as UserModel);
392
+ const req = {headers: {}} as any;
393
+ const res = {} as any;
394
+ const next = mock(() => {});
395
+
396
+ await middleware(req, res, next);
397
+
398
+ expect(next).toHaveBeenCalledTimes(1);
399
+ expect(req.user).toBeDefined();
400
+ expect(req.user.betterAuthId).toBe("new-user-1");
401
+ expect(req.user.email).toBe("newuser@example.com");
402
+
403
+ // Verify user was persisted
404
+ const count = await TestUser.countDocuments();
405
+ expect(count).toBe(1);
406
+ });
407
+ });
408
+
409
+ describe("mountBetterAuthRoutes", () => {
410
+ it("mounts routes at the default path", async () => {
411
+ await setup;
412
+ const config: BetterAuthConfig = {
413
+ baseURL: "http://localhost:3000",
414
+ enabled: true,
415
+ secret: "test-secret-at-least-32-characters-long",
416
+ };
417
+
418
+ const auth = createBetterAuth({config, mongoClient: getClient()});
419
+ const app = express();
420
+
421
+ expect(() => mountBetterAuthRoutes(app, auth)).not.toThrow();
422
+ });
423
+
424
+ it("mounts routes at a custom path", async () => {
425
+ await setup;
426
+ const config: BetterAuthConfig = {
427
+ baseURL: "http://localhost:3000",
428
+ enabled: true,
429
+ secret: "test-secret-at-least-32-characters-long",
430
+ };
431
+
432
+ const auth = createBetterAuth({config, mongoClient: getClient()});
433
+ const app = express();
434
+
435
+ expect(() => mountBetterAuthRoutes(app, auth, "/custom/auth")).not.toThrow();
436
+ });
437
+ });
438
+
439
+ describe("getMongoClientFromMongoose", () => {
440
+ it("returns the mongo client when connected", () => {
441
+ // Default mongoose connection is set up by bunSetup.ts preload
442
+ const client = getMongoClientFromMongoose();
443
+ expect(client).toBeDefined();
444
+ });
445
+ });
446
+
447
+ describe("getBetterAuthSession", () => {
448
+ it("returns null when no session is set", () => {
449
+ const req = {} as any;
450
+ expect(getBetterAuthSession(req)).toBeNull();
451
+ });
452
+
453
+ it("returns session data when set", () => {
454
+ const sessionData = {session: {id: "s1"}, user: {id: "u1"}} as any;
455
+ const req = {betterAuthSession: sessionData} as any;
456
+ expect(getBetterAuthSession(req)).toBe(sessionData);
457
+ });
458
+ });
459
+
460
+ describe("hasBetterAuthSession", () => {
461
+ it("returns false when no session is set", () => {
462
+ const req = {} as any;
463
+ expect(hasBetterAuthSession(req)).toBe(false);
464
+ });
465
+
466
+ it("returns true when session is set", () => {
467
+ const req = {betterAuthSession: {session: {}, user: {}}} as any;
468
+ expect(hasBetterAuthSession(req)).toBe(true);
469
+ });
470
+ });
471
+
472
+ describe("setupBetterAuthUserSync", () => {
473
+ it("does not throw", async () => {
474
+ await setup;
475
+ const config: BetterAuthConfig = {
476
+ baseURL: "http://localhost:3000",
477
+ enabled: true,
478
+ secret: "test-secret-at-least-32-characters-long",
479
+ };
480
+
481
+ const auth = createBetterAuth({config, mongoClient: getClient()});
482
+
483
+ expect(() => setupBetterAuthUserSync(auth, TestUser as UserModel)).not.toThrow();
484
+ });
485
+ });