Skip to content

Commit c5c3b8c

Browse files
committed
Lint errors in test scripts
1 parent a074488 commit c5c3b8c

6 files changed

Lines changed: 120 additions & 15 deletions

File tree

app/controllers/authController.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,12 @@ export const login = async (req, res) => {
123123
// === LOGOUT ===
124124
export const logout = async (req, res) => {
125125
try {
126-
logger.info(`User logout attempt`);
127-
res.status(200).json({ message: 'Logout handled on client side. Token deleted.' });
126+
const userId = req.user?.id; // if you have verifyToken middleware
127+
if (userId) {
128+
await User.findByIdAndUpdate(userId, { $unset: { refreshToken: 1 } });
129+
logger.info(`User ${userId} logged out — refresh token removed from DB`);
130+
}
131+
res.status(200).json({ message: 'Logout successful. Refresh token removed.' });
128132
} catch (err) {
129133
logger.error('Logout error', err);
130134
res.status(500).json({ message: 'Server error during logout.' });
@@ -137,17 +141,20 @@ export const refreshToken = async (req, res) => {
137141
if (!token) return res.status(401).json({ message: 'Refresh token required' });
138142

139143
try {
144+
console.log("Incoming refresh token:", token);
140145
const user = await User.findOne({ refreshToken: token });
146+
console.log("User found:", !!user);
147+
141148
if (!user) return res.status(403).json({ message: 'Invalid refresh token' });
142149

143-
jwt.verify(token, JWT_REFRESH_SECRET, (err, decoded) => {
150+
jwt.verify(token, JWT_REFRESH_SECRET, async (err, decoded) => {
144151
if (err || decoded.id !== user._id.toString()) {
145152
return res.status(403).json({ message: 'Invalid or expired refresh token' });
146153
}
147154

148155
const { accessToken, refreshToken: newRefreshToken } = generateTokens(user);
149156
user.refreshToken = newRefreshToken;
150-
user.save();
157+
await user.save(); // ✅ Ensure DB is updated before returning
151158

152159
res.json({ accessToken, refreshToken: newRefreshToken });
153160
});
@@ -157,6 +164,7 @@ export const refreshToken = async (req, res) => {
157164
}
158165
};
159166

167+
160168
// === DEV REGISTER ===
161169
export const devRegister = async (req, res) => {
162170
try {

app/jobs/tokenCleanupJob.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import cron from 'node-cron';
2+
import jwt from 'jsonwebtoken';
3+
import User from '../models/User.js';
4+
import winston from 'winston';
5+
import nodemailer from 'nodemailer';
6+
import dotenv from 'dotenv';
7+
8+
dotenv.config();
9+
10+
// Logger setup
11+
const logger = winston.createLogger({
12+
level: 'info',
13+
format: winston.format.combine(
14+
winston.format.timestamp(),
15+
winston.format.json()
16+
),
17+
transports: [new winston.transports.File({ filename: 'logs/auth.log' })],
18+
});
19+
20+
// Email transport (only if email reporting enabled)
21+
let transporter = null;
22+
if (process.env.SEND_CLEANUP_EMAIL === 'true') {
23+
transporter = nodemailer.createTransport({
24+
service: process.env.EMAIL_SERVICE, // e.g., 'gmail'
25+
auth: {
26+
user: process.env.EMAIL_USER,
27+
pass: process.env.EMAIL_PASS
28+
}
29+
});
30+
}
31+
32+
const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET;
33+
34+
async function cleanupExpiredTokens() {
35+
logger.info("🧹 Starting refresh token cleanup...");
36+
37+
const usersWithTokens = await User.find({ refreshToken: { $exists: true, $ne: null } });
38+
let removedCount = 0;
39+
let keptCount = 0;
40+
41+
for (const user of usersWithTokens) {
42+
try {
43+
// Verify without throwing
44+
jwt.verify(user.refreshToken, JWT_REFRESH_SECRET);
45+
keptCount++;
46+
} catch (err) {
47+
if (err.name === 'TokenExpiredError') {
48+
await User.updateOne({ _id: user._id }, { $unset: { refreshToken: "" } });
49+
removedCount++;
50+
} else {
51+
logger.error(`❌ Error verifying token for ${user.email}: ${err.message}`);
52+
}
53+
}
54+
}
55+
56+
const summary = `✅ Cleanup complete: Removed ${removedCount} expired refresh tokens, kept ${keptCount} active ones.`;
57+
logger.info(summary);
58+
console.log(summary);
59+
60+
// Optional email report
61+
if (transporter) {
62+
await transporter.sendMail({
63+
from: process.env.EMAIL_USER,
64+
to: process.env.CLEANUP_EMAIL_TO,
65+
subject: "Refresh Token Cleanup Summary",
66+
text: summary
67+
});
68+
logger.info("📧 Cleanup summary email sent.");
69+
}
70+
}
71+
72+
// Schedule job — daily at midnight
73+
cron.schedule('0 0 * * *', cleanupExpiredTokens);
74+
75+
export default cleanupExpiredTokens;

app/logs/auth.log

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,28 @@
2020
{"level":"info","message":"✅ User registered: viewer_1754615267686@test.com by 68954de468228c1b5a79ecaa"}
2121
{"level":"info","message":"✅ User login: viewer_1754615267686@test.com"}
2222
{"level":"info","message":"User logout attempt"}
23+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
24+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
25+
{"level":"info","message":"✅ User registered: viewer_1754615939253@test.com by 68954de468228c1b5a79ecaa"}
26+
{"level":"info","message":"✅ User login: viewer_1754615939253@test.com"}
27+
{"level":"info","message":"User logout attempt"}
28+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
29+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
30+
{"level":"info","message":"✅ User registered: viewer_1754616462441@test.com by 68954de468228c1b5a79ecaa"}
31+
{"level":"info","message":"✅ User login: viewer_1754616462441@test.com"}
32+
{"level":"info","message":"User logout attempt"}
33+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
34+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
35+
{"level":"info","message":"✅ User registered: viewer_1754616551458@test.com by 68954de468228c1b5a79ecaa"}
36+
{"level":"info","message":"✅ User login: viewer_1754616551458@test.com"}
37+
{"level":"info","message":"User logout attempt"}
38+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
39+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
40+
{"level":"info","message":"✅ User registered: viewer_1754617415185@test.com by 68954de468228c1b5a79ecaa"}
41+
{"level":"info","message":"✅ User login: viewer_1754617415185@test.com"}
42+
{"level":"info","message":"User logout attempt"}
43+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
44+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
45+
{"level":"info","message":"✅ User registered: viewer_1754617445194@test.com by 68954de468228c1b5a79ecaa"}
46+
{"level":"info","message":"✅ User login: viewer_1754617445194@test.com"}
47+
{"level":"info","message":"User logout attempt"}

app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"helmet": "^8.1.0",
5151
"jsonwebtoken": "^9.0.2",
5252
"mongoose": "^8.17.0",
53+
"node-cron": "^4.2.1",
5354
"winston": "^3.17.0"
5455
},
5556
"devDependencies": {

app/server.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import mongoose from 'mongoose';
44
import cors from 'cors';
55
import helmet from 'helmet';
66
import authRoutes from './routes/authRoutes.js';
7+
import './jobs/tokenCleanupJob.js';
78

89
dotenv.config();
910

app/tests/auth.test.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// app/tests/auth.full.test.js
1+
/* eslint-env mocha */
22
import request from 'supertest';
33
import { expect } from 'chai';
44
import mongoose from 'mongoose';
@@ -74,7 +74,7 @@ describe('Auth API - Full Supertest Suite', function () {
7474
if (r.note) console.log(` note: ${r.note}`);
7575
});
7676
} finally {
77-
// leave DB intact by request (you said you want to inspect data)
77+
// leave DB intact by request
7878
await mongoose.disconnect();
7979
console.log('Disconnected from MongoDB');
8080
}
@@ -86,7 +86,6 @@ describe('Auth API - Full Supertest Suite', function () {
8686
const res = await request(app).get('/health');
8787
expect(res.status).to.equal(200);
8888
expect(res.body).to.have.property('status');
89-
// Accept both 'OK' and 'Ok' or 'ok' depending on your implementation
9089
results.push({ name: testName, status: 'passed', note: `status=${res.body.status}` });
9190
} catch (err) {
9291
console.error('TEST ERROR - Health check:', err);
@@ -106,6 +105,10 @@ describe('Auth API - Full Supertest Suite', function () {
106105
expect(res.body).to.have.property('accessToken');
107106
expect(res.body).to.have.property('refreshToken');
108107

108+
// 🔹 Update latest tokens so refresh test always uses the fresh DB one
109+
adminToken = res.body.accessToken;
110+
adminRefreshToken = res.body.refreshToken;
111+
109112
results.push({ name: testName, status: 'passed' });
110113
} catch (err) {
111114
console.error('TEST ERROR - Admin login:', err);
@@ -117,7 +120,6 @@ describe('Auth API - Full Supertest Suite', function () {
117120
it('Register viewer (admin) — POST /auth/register with admin token', async () => {
118121
const testName = 'Register viewer (admin)';
119122
try {
120-
// ensure uniqueness (we already set viewerEmail unique)
121123
const res = await request(app)
122124
.post('/auth/register')
123125
.set('Authorization', `Bearer ${adminToken}`)
@@ -130,13 +132,10 @@ describe('Auth API - Full Supertest Suite', function () {
130132
addedBy: adminId
131133
});
132134

133-
// 201 expected on create; backend returns 400 if user exists
134135
expect(res.status).to.equal(201);
135136
results.push({ name: testName, status: 'passed', note: `created ${viewerEmail}` });
136137
} catch (err) {
137-
// If backend replies with 400 user already exists (shouldn't happen because unique email)
138138
console.error('TEST ERROR - Register viewer:', err);
139-
// capture response body if available (supertest supplies err.response)
140139
const body = err.response ? err.response.body : undefined;
141140
results.push({ name: testName, status: 'failed', error: err.message, body });
142141
expect.fail(err.message);
@@ -157,7 +156,6 @@ describe('Auth API - Full Supertest Suite', function () {
157156
addedBy: adminId
158157
});
159158

160-
// Expect 401 (missing token) or 403 depending on your verifyToken implementation.
161159
expect([401, 403]).to.include(res.status);
162160
results.push({ name: testName, status: 'passed', note: `status=${res.status}` });
163161
} catch (err) {
@@ -173,7 +171,6 @@ describe('Auth API - Full Supertest Suite', function () {
173171
const res = await request(app).post('/auth/refresh').send({ token: adminRefreshToken });
174172
expect(res.status).to.equal(200);
175173
expect(res.body).to.have.property('accessToken');
176-
// optionally capture the new refresh token if backend returns it
177174
results.push({ name: testName, status: 'passed' });
178175
} catch (err) {
179176
console.error('TEST ERROR - Refresh token:', err);
@@ -186,7 +183,6 @@ describe('Auth API - Full Supertest Suite', function () {
186183
it('Viewer cannot call admin-only register — POST /auth/register (viewer token)', async () => {
187184
const testName = 'Viewer blocked from admin route';
188185
try {
189-
// login viewer first (we created viewer earlier with unique email)
190186
const logRes = await request(app).post('/auth/login').send({ email: viewerEmail, password: viewerPassword });
191187
expect(logRes.status).to.equal(200);
192188
const viewerToken = logRes.body.accessToken;
@@ -202,7 +198,6 @@ describe('Auth API - Full Supertest Suite', function () {
202198
permissions: []
203199
});
204200

205-
// Expect forbidden: 403 (if your role middleware uses 403), or other non-200
206201
expect(res.status).to.equal(403);
207202
results.push({ name: testName, status: 'passed' });
208203
} catch (err) {

0 commit comments

Comments
 (0)