test: cover isolated SQLite invariants and durability
This commit is contained in:
+55
-1
@@ -1,5 +1,6 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import request from 'supertest';
|
||||
import { App } from 'supertest/types';
|
||||
import { AppModule } from './../src/app.module';
|
||||
@@ -21,7 +22,7 @@ describe('Authentication (e2e)', () => {
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
configureApp(app);
|
||||
await app.init();
|
||||
await app.listen(0, '127.0.0.1');
|
||||
await app.get(PrismaService).session.deleteMany();
|
||||
await app.get(PrismaService).tbankConnection.deleteMany();
|
||||
await app.get(PrismaService).user.deleteMany();
|
||||
@@ -59,6 +60,59 @@ describe('Authentication (e2e)', () => {
|
||||
await agent.post('/api/v1/auth/logout').expect(204);
|
||||
});
|
||||
|
||||
it('rotates one concurrent refresh on SQLite and keeps the winner usable', async () => {
|
||||
const registration = await request(app.getHttpServer())
|
||||
.post('/api/v1/auth/register')
|
||||
.send({ email: 'race@example.test', password: 'a secure password' })
|
||||
.expect(200);
|
||||
const cookie = registration.headers['set-cookie'][0];
|
||||
// Both requests read the old hash before either reaches the real SQLite CAS.
|
||||
const jwt = app.get(JwtService);
|
||||
const sign = jwt.signAsync.bind(jwt);
|
||||
let arrived = 0;
|
||||
let release = () => {};
|
||||
const ready = new Promise<void>((resolve) => {
|
||||
release = resolve;
|
||||
});
|
||||
const barrier = jest
|
||||
.spyOn(jwt, 'signAsync')
|
||||
.mockImplementation(async (...args) => {
|
||||
const signed = await sign(...args);
|
||||
if (
|
||||
typeof args[0] === 'object' &&
|
||||
'type' in args[0] &&
|
||||
args[0].type === 'refresh'
|
||||
) {
|
||||
if (++arrived === 2) release();
|
||||
await ready;
|
||||
}
|
||||
return signed;
|
||||
});
|
||||
const responses = await Promise.all(
|
||||
Array.from({ length: 2 }, () =>
|
||||
request(app.getHttpServer())
|
||||
.post('/api/v1/auth/refresh')
|
||||
.set('Cookie', cookie),
|
||||
),
|
||||
);
|
||||
barrier.mockRestore();
|
||||
expect(arrived).toBe(2);
|
||||
expect(responses.map((response) => response.status).sort()).toEqual([
|
||||
200, 401,
|
||||
]);
|
||||
const winner = responses.find((response) => response.status === 200);
|
||||
if (!winner) throw new Error('Expected one successful rotation');
|
||||
await request(app.getHttpServer())
|
||||
.post('/api/v1/auth/refresh')
|
||||
.set('Cookie', cookie)
|
||||
.expect(401);
|
||||
await request(app.getHttpServer())
|
||||
.post('/api/v1/auth/refresh')
|
||||
.set('Cookie', winner.headers['set-cookie'][0])
|
||||
.expect(200);
|
||||
expect(await app.get(PrismaService).session.count()).toBe(1);
|
||||
});
|
||||
|
||||
it('rejects duplicate registration and invalid credentials', async () => {
|
||||
await request(app.getHttpServer())
|
||||
.post('/api/v1/auth/register')
|
||||
|
||||
Reference in New Issue
Block a user