Skip to content

Commit b6e10c5

Browse files
test: use fresh request(app) inside describe('Logout') blocks (#3472) (#3490)
The shared supertest `agent` is cookie-stateful per Express app. When a prior `describe` block signs up a user, then deletes it in afterEach (`UserService.remove(user)`), the agent keeps the now-stale `TOKEN` cookie. Subsequent describe blocks that reuse the same agent can then hit 401s / socket hang ups on the first auth-middleware call — the symptom reported downstream on trawl_node `historys.integration.tests.js`. Canonical pattern (already used in `tasks.integration.tests.js`): describe('Logout', () => { test(..., async () => { await request(app).get('/api/...').expect(401); }); }); Applied to: - modules/home/tests/home.integration.tests.js — Logout block now uses `request(app)` for every public endpoint call; admin/health cases still use explicit `set('Cookie', TOKEN=...)` with JWTs. - modules/users/tests/user.account.integration.tests.js — Logout block switched to `request(app)` for all 6 unauthenticated assertions. Both files now expose `app = init.app` in `beforeAll` so later blocks can build fresh requests without relying on agent cookie state. `tasks.integration.tests.js` was already correct — kept as the canonical source-of-truth pattern for downstream propagation. Fixes #3472
1 parent 93f23e0 commit b6e10c5

2 files changed

Lines changed: 32 additions & 20 deletions

File tree

modules/home/tests/home.integration.tests.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import config from '../../../config/index.js';
1515
* Unit tests
1616
*/
1717
describe('Home integration tests:', () => {
18+
let app; // Express app instance for fresh (unauthenticated) requests (#3472)
1819
let agent;
1920
let HomeService;
2021
let adminToken;
@@ -40,7 +41,8 @@ describe('Home integration tests:', () => {
4041
config.organizations.enabled = false;
4142
const init = await bootstrap();
4243
HomeService = (await import(path.resolve('./modules/home/services/home.service.js'))).default;
43-
agent = request.agent(init.app);
44+
app = init.app;
45+
agent = request.agent(app);
4446

4547
// Create admin user and sign JWT for health endpoint test
4648
const User = mongoose.model('User');
@@ -72,24 +74,28 @@ describe('Home integration tests:', () => {
7274
}
7375
});
7476

77+
// Public ("logged out") route tests. Use a fresh `request(app)` per test
78+
// instead of the shared `agent` so no cookie state from this block leaks
79+
// into later describes (#3472). Auth-required cases still use explicit
80+
// `set('Cookie', 'TOKEN=...')` headers derived from JWTs.
7581
describe('Logout', () => {
7682
test('should be able to get releases', async () => {
77-
const result = await agent.get('/api/home/releases').expect(200);
83+
const result = await request(app).get('/api/home/releases').expect(200);
7884
expect(result.body.type).toBe('success');
7985
expect(result.body.message).toBe('releases');
8086
expect(result.body.data).toBeInstanceOf(Array);
8187
});
8288

8389
test('should be able to get changelogs', async () => {
84-
const result = await agent.get('/api/home/changelogs').expect(200);
90+
const result = await request(app).get('/api/home/changelogs').expect(200);
8591
expect(result.body.type).toBe('success');
8692
expect(result.body.message).toBe('changelogs');
8793
expect(result.body.data).toBeInstanceOf(Array);
8894
});
8995

9096
test('should be able to get team members', async () => {
9197
try {
92-
const result = await agent.get('/api/home/team').expect(200);
98+
const result = await request(app).get('/api/home/team').expect(200);
9399
expect(result.body.type).toBe('success');
94100
expect(result.body.message).toBe('team list');
95101
expect(result.body.data).toBeInstanceOf(Array);
@@ -101,7 +107,7 @@ describe('Home integration tests:', () => {
101107

102108
test('should be able to get an existing page', async () => {
103109
try {
104-
const result = await agent.get('/api/home/pages/terms').expect(200);
110+
const result = await request(app).get('/api/home/pages/terms').expect(200);
105111
expect(result.body.type).toBe('success');
106112
expect(result.body.message).toBe('page');
107113
expect(result.body.data[0].title).toBe('Terms');
@@ -115,7 +121,7 @@ describe('Home integration tests:', () => {
115121

116122
test('should be able to catch error of unknown page', async () => {
117123
try {
118-
const result = await agent.get('/api/home/pages/test').expect(404);
124+
const result = await request(app).get('/api/home/pages/test').expect(404);
119125
expect(result.body.type).toBe('error');
120126
expect(result.body.message).toBe('Not Found');
121127
expect(result.body.description).toBe('No page with that name has been found');
@@ -127,15 +133,15 @@ describe('Home integration tests:', () => {
127133

128134
test('should return empty releases gracefully when GitHub API fails', async () => {
129135
axios.get.mockRejectedValueOnce(new Error('GitHub API unavailable'));
130-
const result = await agent.get('/api/home/releases').expect(200);
136+
const result = await request(app).get('/api/home/releases').expect(200);
131137
expect(result.body.type).toBe('success');
132138
expect(result.body.message).toBe('releases');
133139
expect(result.body.data).toEqual([]);
134140
});
135141

136142
test('should return empty changelogs gracefully when GitHub API fails', async () => {
137143
axios.get.mockRejectedValueOnce(new Error('GitHub API unavailable'));
138-
const result = await agent.get('/api/home/changelogs').expect(200);
144+
const result = await request(app).get('/api/home/changelogs').expect(200);
139145
expect(result.body.type).toBe('success');
140146
expect(result.body.message).toBe('changelogs');
141147
expect(result.body.data).toEqual([]);
@@ -146,7 +152,7 @@ describe('Home integration tests:', () => {
146152
axios.get.mockClear();
147153
// Temporarily set a fake token to cover the token-truthy branch in home.service releases()
148154
config.repos = originalRepos.map((repo) => ({ ...repo, token: 'fake-test-token' }));
149-
const result = await agent.get('/api/home/releases').expect(200);
155+
const result = await request(app).get('/api/home/releases').expect(200);
150156
expect(result.body.type).toBe('success');
151157
const releaseCalls = axios.get.mock.calls.filter(([url]) => url.includes('/releases'));
152158
expect(releaseCalls.length).toBeGreaterThan(0);
@@ -160,7 +166,7 @@ describe('Home integration tests:', () => {
160166
const originalRepos = config.repos;
161167
axios.get.mockClear();
162168
config.repos = originalRepos.map((repo) => ({ ...repo, token: 'fake-test-token' }));
163-
const result = await agent.get('/api/home/changelogs').expect(200);
169+
const result = await request(app).get('/api/home/changelogs').expect(200);
164170
expect(result.body.type).toBe('success');
165171
const changelogCalls = axios.get.mock.calls.filter(([url]) => url.includes('/contents/'));
166172
expect(changelogCalls.length).toBeGreaterThan(0);
@@ -171,15 +177,15 @@ describe('Home integration tests:', () => {
171177
});
172178

173179
test('should return minimal health status without auth', async () => {
174-
const result = await agent.get('/api/health').expect(200);
180+
const result = await request(app).get('/api/health').expect(200);
175181
expect(result.body.type).toBe('success');
176182
expect(result.body.data.status).toBe('ok');
177183
expect(result.body.data.db).toBeUndefined();
178184
expect(result.body.data.memory).toBeUndefined();
179185
});
180186

181187
test('should return detailed health status for admin', async () => {
182-
const result = await agent.get('/api/health').set('Cookie', `TOKEN=${adminToken}`).expect(200);
188+
const result = await request(app).get('/api/health').set('Cookie', `TOKEN=${adminToken}`).expect(200);
183189
expect(result.body.type).toBe('success');
184190
expect(result.body.data.status).toBe('ok');
185191
expect(result.body.data.db).toBe('connected');
@@ -196,7 +202,7 @@ describe('Home integration tests:', () => {
196202
version: '0.0.0',
197203
memory: process.memoryUsage(),
198204
});
199-
const result = await agent.get('/api/health').expect(503);
205+
const result = await request(app).get('/api/health').expect(503);
200206
expect(result.body.type).toBe('error');
201207
expect(result.body.message).toBe('Service Unavailable');
202208
});

modules/users/tests/user.account.integration.tests.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import mongooseService from '../../../lib/services/mongoose.js';
1414
*/
1515
describe('User integration tests:', () => {
1616
let UserService = null;
17+
let app; // Express app instance for fresh (unauthenticated) requests (#3472)
1718
let agent;
1819
let credentials;
1920
let user;
@@ -27,7 +28,8 @@ describe('User integration tests:', () => {
2728
try {
2829
const init = await bootstrap();
2930
UserService = (await import(path.resolve('./modules/users/services/users.service.js'))).default;
30-
agent = request.agent(init.app);
31+
app = init.app;
32+
agent = request.agent(app);
3133
} catch (err) {
3234
console.log(err);
3335
expect(err).toBeFalsy();
@@ -415,10 +417,14 @@ describe('User integration tests:', () => {
415417
});
416418
});
417419

420+
// Unauthenticated ("logged out") route tests. Use a fresh `request(app)` per
421+
// test instead of the shared `agent` so a stale cookie from a previous
422+
// describe block (user deleted in afterEach) can never invalidate subsequent
423+
// authenticated tests in later describes. See #3472.
418424
describe('Logout', () => {
419425
test('should not be able to update Terms sign date if not logged in', async () => {
420426
try {
421-
await agent.get('/api/users/terms').expect(401);
427+
await request(app).get('/api/users/terms').expect(401);
422428
} catch (err) {
423429
console.log(err);
424430
expect(err).toBeFalsy();
@@ -427,7 +433,7 @@ describe('User integration tests:', () => {
427433

428434
test('should not be able to change user own password if not signed in', async () => {
429435
try {
430-
await agent
436+
await request(app)
431437
.post('/api/users/password')
432438
.send({
433439
newPassword: '1234567890Aa$',
@@ -445,7 +451,7 @@ describe('User integration tests:', () => {
445451

446452
test('should not be able to get any user details if not logged in', async () => {
447453
try {
448-
await agent.get('/api/users/me').expect(401);
454+
await request(app).get('/api/users/me').expect(401);
449455
// TODO error message
450456
// result.body.message.should.equal('User is not signed in');
451457
} catch (err) {
@@ -461,7 +467,7 @@ describe('User integration tests:', () => {
461467
lastName: 'user_update_last',
462468
};
463469

464-
await agent.put('/api/users').send(userUpdate).expect(401);
470+
await request(app).put('/api/users').send(userUpdate).expect(401);
465471
// TODO error message
466472
// result.body.message.should.equal('User is not signed in');
467473
} catch (err) {
@@ -472,7 +478,7 @@ describe('User integration tests:', () => {
472478

473479
test('should not be able to update own user profile avatar without being logged-in', async () => {
474480
try {
475-
await agent.post('/api/users/avatar').send({}).expect(401);
481+
await request(app).post('/api/users/avatar').send({}).expect(401);
476482
// TODO error message
477483
// result.body.message.should.equal('User is not signed in');
478484
} catch (err) {
@@ -483,7 +489,7 @@ describe('User integration tests:', () => {
483489

484490
test('should be able to get a users stats', async () => {
485491
try {
486-
const result = await agent.get('/api/users/stats').expect(200);
492+
const result = await request(app).get('/api/users/stats').expect(200);
487493
expect(result.body.type).toBe('success');
488494
expect(result.body.message).toBe('users stats');
489495
} catch (err) {

0 commit comments

Comments
 (0)