diff --git a/apps/server/src/wallets/wallets.service.spec.ts b/apps/server/src/wallets/wallets.service.spec.ts index c730c62..adf919f 100644 --- a/apps/server/src/wallets/wallets.service.spec.ts +++ b/apps/server/src/wallets/wallets.service.spec.ts @@ -117,3 +117,54 @@ describe('WalletsService financial boundaries', () => { expect(dataSource.transaction).not.toHaveBeenCalled(); }); }); + +describe('WalletsService wallet locking', () => { + const createQueryManager = () => { + const wallet = { id: 1, studentId: 10, balance: 0 }; + const query = { + where: jest.fn(), + setLock: jest.fn(), + getOne: jest.fn(async () => wallet), + }; + query.where.mockReturnValue(query); + query.setLock.mockReturnValue(query); + return { + wallet, + query, + manager: { + createQueryBuilder: jest.fn(() => query), + findOne: jest.fn(), + }, + }; + }; + + it('skips pessimistic locking for SQLite', async () => { + const ctx = createQueryManager(); + const service = new WalletsService( + {} as any, + {} as any, + {} as any, + { options: { type: 'better-sqlite3' } } as any, + ); + + const result = await (service as any).getOrCreateWallet(ctx.manager, 10, true); + + expect(result).toBe(ctx.wallet); + expect(ctx.query.setLock).not.toHaveBeenCalled(); + expect(ctx.query.getOne).toHaveBeenCalled(); + }); + + it('keeps pessimistic write locking for MySQL', async () => { + const ctx = createQueryManager(); + const service = new WalletsService( + {} as any, + {} as any, + {} as any, + { options: { type: 'mysql' } } as any, + ); + + await (service as any).getOrCreateWallet(ctx.manager, 10, true); + + expect(ctx.query.setLock).toHaveBeenCalledWith('pessimistic_write'); + }); +}); diff --git a/apps/server/src/wallets/wallets.service.ts b/apps/server/src/wallets/wallets.service.ts index 1078446..270d2de 100644 --- a/apps/server/src/wallets/wallets.service.ts +++ b/apps/server/src/wallets/wallets.service.ts @@ -259,10 +259,13 @@ export class WalletsService { if (!lock || !manager.createQueryBuilder) { return manager.findOne(StudentWallet, { where: { studentId } }); } - return manager.createQueryBuilder(StudentWallet, 'wallet') - .where('wallet.studentId = :studentId', { studentId }) - .setLock('pessimistic_write') - .getOne(); + let query = manager + .createQueryBuilder(StudentWallet, 'wallet') + .where('wallet.studentId = :studentId', { studentId }); + if (['mysql', 'mariadb', 'postgres', 'cockroachdb'].includes(this.dataSource.options.type)) { + query = query.setLock('pessimistic_write'); + } + return query.getOne(); }; let wallet = await find(); if (!wallet) {