import { Test, TestingModule } from '@nestjs/testing'; import { getDataSourceToken } from '@nestjs/typeorm'; import { DatabaseMigrationsService } from './database-migrations.service'; function createRunner(columns: string[]) { return { connect: jest.fn(), release: jest.fn(), query: jest.fn().mockResolvedValue([]), getTables: jest.fn().mockResolvedValue(columns.length ? [{ name: 'deposits' }] : []), getTable: jest.fn().mockResolvedValue({ name: 'deposits', columns: columns.map((name) => ({ name })), }), renameColumn: jest.fn().mockResolvedValue(undefined), dropColumn: jest.fn().mockResolvedValue(undefined), }; } async function createService(runner: ReturnType) { const module: TestingModule = await Test.createTestingModule({ providers: [ DatabaseMigrationsService, { provide: getDataSourceToken(), useValue: { options: { type: 'better-sqlite3' }, createQueryRunner: jest.fn().mockReturnValue(runner), }, }, ], }).compile(); return module.get(DatabaseMigrationsService) as DatabaseMigrationsService & { cleanupDepositRefundColumns(): Promise; }; } describe('DatabaseMigrationsService — deposit refund cleanup', () => { it('renames refund audit fields and drops approval-flow remnants', async () => { const runner = createRunner([ 'id', 'refund_status', 'refund_requested_at', 'refund_approved_by', 'refund_approved_at', 'refund_rejected_reason', ]); const service = await createService(runner); await service.cleanupDepositRefundColumns(); expect(runner.renameColumn).toHaveBeenCalledWith( 'deposits', 'refund_approved_by', 'refunded_by', ); expect(runner.renameColumn).toHaveBeenCalledWith( 'deposits', 'refund_approved_at', 'refunded_at', ); expect(runner.dropColumn).toHaveBeenCalledWith('deposits', 'refund_status'); expect(runner.dropColumn).toHaveBeenCalledWith('deposits', 'refund_requested_at'); expect(runner.dropColumn).toHaveBeenCalledWith('deposits', 'refund_rejected_reason'); expect(runner.release).toHaveBeenCalled(); }); it('merges legacy audit values before dropping duplicate legacy columns', async () => { const runner = createRunner([ 'id', 'refund_approved_by', 'refund_approved_at', 'refunded_by', 'refunded_at', ]); const service = await createService(runner); await service.cleanupDepositRefundColumns(); expect(runner.query).toHaveBeenCalledWith( 'UPDATE deposits SET refunded_by = COALESCE(refunded_by, refund_approved_by)', ); expect(runner.query).toHaveBeenCalledWith( 'UPDATE deposits SET refunded_at = COALESCE(refunded_at, refund_approved_at)', ); expect(runner.dropColumn).toHaveBeenCalledWith('deposits', 'refund_approved_by'); expect(runner.dropColumn).toHaveBeenCalledWith('deposits', 'refund_approved_at'); expect(runner.renameColumn).not.toHaveBeenCalled(); }); it('does nothing when the deposits table is absent', async () => { const runner = createRunner([]); const service = await createService(runner); await service.cleanupDepositRefundColumns(); expect(runner.renameColumn).not.toHaveBeenCalled(); expect(runner.dropColumn).not.toHaveBeenCalled(); expect(runner.release).toHaveBeenCalled(); }); });