Skip to content

Commit 037dfa5

Browse files
committed
test: fix auth test error
1 parent 009e71c commit 037dfa5

2 files changed

Lines changed: 26 additions & 24 deletions

File tree

‎AGENTS.md‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,8 @@ For Jest in this repo, `--runInBand` is often safer in constrained Windows envir
161161
## Current version: v0.8.0
162162
Social auth (GitHub + Google) shipped. Next: v0.9.0 — Webhooks + BYOK Resend mail.
163163

164-
## Planned for v0.9.0
164+
## Planned for v0.9.0 (already done)
165165
- Webhook system: per-project config, HMAC-SHA256, retry, delivery logs
166166
- BYOK Resend mail key: project-level Resend API key, custom domain mail
167167
- Follow same encryption pattern as authProviders for storing Resend key
168168
- Webhook model: separate MongoDB collection (not embedded in Project)
169-
170-
## Webhook system (v0.9.0) - Already done
171-
- Model: packages/common/src/models/Webhook.js
172-
- Delivery log: packages/common/src/models/WebhookDelivery.js
173-
- Dispatcher: apps/public-api/src/utils/webhookDispatcher.js
174-
- Queue: BullMQ + existing Redis connection
175-
- Retry: exponential backoff, max 5 attempts, stop on 4xx
176-
- Signature: HMAC-SHA256 in X-urBackend-Signature header

‎apps/dashboard-api/src/__tests__/auth.controller.test.js‎

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ jest.mock('@urbackend/common', () => {
3535
Developer.findById = jest.fn();
3636
Developer.findByIdAndDelete = jest.fn();
3737

38+
// Helper to mock a Mongoose query that is both chainable (select) and thenable
39+
Developer.__mockQuery = (value) => {
40+
const query = {
41+
select: jest.fn().mockReturnThis(),
42+
then: (resolve, reject) => Promise.resolve(value).then(resolve, reject),
43+
catch: (reject) => Promise.resolve(value).catch(reject),
44+
};
45+
return query;
46+
};
47+
3848
const Otp = jest.fn().mockImplementation((data) => ({
3949
...data,
4050
save: jest.fn().mockResolvedValue(undefined),
@@ -177,7 +187,7 @@ describe('auth.controller', () => {
177187

178188
test('returns 200 with cookie tokens on valid credentials', async () => {
179189
const user = mockUser();
180-
Developer.findOne.mockResolvedValue(user);
190+
Developer.findOne.mockReturnValue(Developer.__mockQuery(user));
181191
bcrypt.compare.mockResolvedValue(true);
182192
jwt.sign.mockReturnValue('signed_token');
183193

@@ -196,7 +206,7 @@ describe('auth.controller', () => {
196206
});
197207

198208
test('returns 400 when user is not found', async () => {
199-
Developer.findOne.mockResolvedValue(null);
209+
Developer.findOne.mockReturnValue(Developer.__mockQuery(null));
200210

201211
const req = makeReq({ email: 'noone@example.com', password: 'pass' });
202212
const res = makeRes();
@@ -208,7 +218,7 @@ describe('auth.controller', () => {
208218
});
209219

210220
test('returns 400 on invalid password', async () => {
211-
Developer.findOne.mockResolvedValue(mockUser());
221+
Developer.findOne.mockReturnValue(Developer.__mockQuery(mockUser()));
212222
bcrypt.compare.mockResolvedValue(false);
213223

214224
const req = makeReq({ email: 'test@example.com', password: 'wrongpass' });
@@ -245,7 +255,7 @@ describe('auth.controller', () => {
245255
test('returns 200 with new tokens when refresh token is valid', async () => {
246256
const user = mockUser();
247257
jwt.verify.mockReturnValue({ _id: 'dev_id_1' });
248-
Developer.findById.mockResolvedValue(user);
258+
Developer.findById.mockReturnValue(Developer.__mockQuery(user));
249259
jwt.sign.mockReturnValue('new_token');
250260

251261
const req = makeReq({}, null, { refreshToken: 'valid_refresh_token' });
@@ -280,7 +290,7 @@ describe('auth.controller', () => {
280290

281291
test('returns 403 when stored refresh token does not match', async () => {
282292
jwt.verify.mockReturnValue({ _id: 'dev_id_1' });
283-
Developer.findById.mockResolvedValue({ ...mockUser(), refreshToken: 'different_token' });
293+
Developer.findById.mockReturnValue(Developer.__mockQuery({ ...mockUser(), refreshToken: 'different_token' }));
284294

285295
const req = makeReq({}, null, { refreshToken: 'valid_refresh_token' });
286296
const res = makeRes();
@@ -370,7 +380,7 @@ describe('auth.controller', () => {
370380
password: 'old_hashed',
371381
save: jest.fn().mockResolvedValue(undefined),
372382
};
373-
Developer.findById.mockResolvedValue(mockUser);
383+
Developer.findById.mockReturnValue(Developer.__mockQuery(mockUser));
374384
bcrypt.compare.mockResolvedValue(true);
375385
bcrypt.genSalt.mockResolvedValue('salt');
376386
bcrypt.hash.mockResolvedValue('new_hashed');
@@ -391,10 +401,10 @@ describe('auth.controller', () => {
391401
});
392402

393403
test('returns 400 when current password is incorrect', async () => {
394-
Developer.findById.mockResolvedValue({
404+
Developer.findById.mockReturnValue(Developer.__mockQuery({
395405
password: 'old_hashed',
396406
save: jest.fn(),
397-
});
407+
}));
398408
bcrypt.compare.mockResolvedValue(false);
399409

400410
const req = makeReq(
@@ -413,32 +423,32 @@ describe('auth.controller', () => {
413423
// -----------------------------------------------------------------------
414424
describe('sendOtp', () => {
415425
test('returns 400 when user is not found', async () => {
416-
Developer.findOne.mockResolvedValue(null);
426+
Developer.findOne.mockReturnValue(Developer.__mockQuery(null));
417427

418428
const req = makeReq({ email: 'noone@example.com' });
419429
const res = makeRes();
420430

421431
await authController.sendOtp(req, res);
422432

423433
expect(res.status).toHaveBeenCalledWith(400);
424-
expect(res.json).toHaveBeenCalledWith({ error: 'User not found' });
434+
expect(res.json).toHaveBeenCalledWith({ error: 'User not found. Ensure you are using the correct email.' });
425435
});
426436

427437
test('returns 400 when user is already verified', async () => {
428-
Developer.findOne.mockResolvedValue({ _id: 'u1', isVerified: true });
438+
Developer.findOne.mockReturnValue(Developer.__mockQuery({ _id: 'u1', isVerified: true }));
429439

430440
const req = makeReq({ email: 'verified@example.com' });
431441
const res = makeRes();
432442

433443
await authController.sendOtp(req, res);
434444

435445
expect(res.status).toHaveBeenCalledWith(400);
436-
expect(res.json).toHaveBeenCalledWith({ error: 'User already verified' });
446+
expect(res.json).toHaveBeenCalledWith({ error: 'Account is already verified. Please login.' });
437447
});
438448

439449
test('sends OTP and returns success for unverified user', async () => {
440450
const user = { _id: 'u1', isVerified: false };
441-
Developer.findOne.mockResolvedValue(user);
451+
Developer.findOne.mockReturnValue(Developer.__mockQuery(user));
442452
Otp.deleteOne.mockResolvedValue(undefined);
443453
bcrypt.genSalt.mockResolvedValue('salt');
444454
bcrypt.hash.mockResolvedValue('hashed_otp');
@@ -459,7 +469,7 @@ describe('auth.controller', () => {
459469
// -----------------------------------------------------------------------
460470
describe('forgotPassword', () => {
461471
test('returns the same message regardless of whether user exists (prevents email enumeration)', async () => {
462-
Developer.findOne.mockResolvedValue(null);
472+
Developer.findOne.mockReturnValue(Developer.__mockQuery(null));
463473

464474
const req = makeReq({ email: 'ghost@example.com' });
465475
const res = makeRes();
@@ -490,7 +500,7 @@ describe('auth.controller', () => {
490500
save: jest.fn().mockResolvedValue(undefined),
491501
};
492502

493-
Developer.findOne.mockResolvedValue(mockUser);
503+
Developer.findOne.mockReturnValue(Developer.__mockQuery(mockUser));
494504
Otp.findOne.mockResolvedValue(mockOtpDoc);
495505
bcrypt.compare.mockResolvedValue(true);
496506
bcrypt.genSalt.mockResolvedValue('salt');

0 commit comments

Comments
 (0)