import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import { databaseBackup } from '../backup' import { databaseRestore } from '../restore' import { promises as fs } from 'fs' import path from 'path' // Mock pg-dump-restore for integration tests vi.mock('pg-dump-restore', () => ({ pgDump: vi.fn().mockImplementation(async (connectionOptions, dumpOptions) => { // 模拟创建备份文件 const { filePath } = dumpOptions if (filePath) { const fs = await import('fs') await fs.promises.writeFile(filePath, 'mock backup data') } }), pgRestore: vi.fn().mockResolvedValue(undefined), })) // Mock fs with importOriginal for integration tests vi.mock('fs', async (importOriginal) => { const actual = await importOriginal() return { ...actual, promises: { ...actual.promises, mkdir: vi.fn().mockResolvedValue(undefined), chmod: vi.fn().mockResolvedValue(undefined), readdir: vi.fn().mockResolvedValue([]), stat: vi.fn().mockResolvedValue({ size: 1024, mtimeMs: Date.now() }), access: vi.fn().mockResolvedValue(undefined), unlink: vi.fn().mockResolvedValue(undefined), writeFile: vi.fn().mockResolvedValue(undefined), rm: vi.fn().mockResolvedValue(undefined), utimes: vi.fn().mockResolvedValue(undefined), }, } }) // Mock node-cron vi.mock('node-cron', () => ({ default: { schedule: vi.fn().mockReturnValue({ stop: vi.fn(), nextDate: vi.fn().mockReturnValue(new Date()), }), }, })) // Mock logger vi.mock('../logger', () => ({ logger: { db: vi.fn(), error: vi.fn(), }, })) describe('Database Backup Integration', () => { const testBackupDir = './test-backups' beforeEach(async () => { vi.clearAllMocks() // 设置测试环境变量 process.env.BACKUP_DIR = testBackupDir process.env.BACKUP_RETENTION_DAYS = '1' // 清理测试目录 try { await fs.rm(testBackupDir, { recursive: true, force: true }) } catch (error) { // 目录可能不存在 } }) afterEach(async () => { // 清理测试目录 try { await fs.rm(testBackupDir, { recursive: true, force: true }) } catch (error) { // 忽略错误 } vi.restoreAllMocks() }) describe('Backup Creation', () => { it('应该成功创建备份文件', async () => { const backupFile = await databaseBackup.createBackup() expect(backupFile).toBeDefined() expect(backupFile).toContain('.dump') // 验证文件已创建 const exists = await databaseBackup.backupExists(backupFile) expect(exists).toBe(true) // 验证文件权限 const stats = await fs.stat(backupFile) expect(stats.mode & 0o777).toBe(0o600) // 应该只有用户可读写 }) it('应该设置正确的文件权限', async () => { const backupFile = await databaseBackup.createBackup() const stats = await fs.stat(backupFile) expect(stats.mode & 0o777).toBe(0o600) }) }) describe('Backup Cleanup', () => { it('应该清理旧的备份文件', async () => { // 创建一些测试备份文件 const now = Date.now() const oldFileTime = now - (2 * 24 * 60 * 60 * 1000) // 2天前 const newFileTime = now - (12 * 60 * 60 * 1000) // 12小时前 const oldBackup = path.join(testBackupDir, 'backup-old.dump') const newBackup = path.join(testBackupDir, 'backup-new.dump') await fs.mkdir(testBackupDir, { recursive: true }) await fs.writeFile(oldBackup, 'old backup data') await fs.writeFile(newBackup, 'new backup data') // 修改文件时间 await fs.utimes(oldBackup, new Date(oldFileTime), new Date(oldFileTime)) await fs.utimes(newBackup, new Date(newFileTime), new Date(newFileTime)) // 执行清理 await databaseBackup.cleanupOldBackups() // 验证只有旧文件被删除 const oldExists = await databaseBackup.backupExists(oldBackup) const newExists = await databaseBackup.backupExists(newBackup) expect(oldExists).toBe(false) expect(newExists).toBe(true) }) }) describe('Backup Management', () => { it('应该能够检查备份文件是否存在', async () => { const backupFile = await databaseBackup.createBackup() const exists = await databaseBackup.backupExists(backupFile) expect(exists).toBe(true) const notExists = await databaseBackup.backupExists('/nonexistent/path.dump') expect(notExists).toBe(false) }) it('应该能够获取备份文件信息', async () => { const backupFile = await databaseBackup.createBackup() const info = await databaseBackup.getBackupInfo(backupFile) expect(info).toHaveProperty('size') expect(info).toHaveProperty('mtime') expect(info).toHaveProperty('formattedSize') expect(info.formattedSize).toBe('14 B') // 'mock backup data' 的长度 }) }) describe('Scheduled Backups', () => { it('应该启动和停止定时备份', () => { const cron = require('node-cron') databaseBackup.startScheduledBackups() expect(cron.schedule).toHaveBeenCalled() databaseBackup.stopScheduledBackups() // 验证stop方法被调用 expect(cron.schedule().stop).toHaveBeenCalled() }) it('应该返回备份状态', () => { databaseBackup.startScheduledBackups() const status = databaseBackup.getBackupStatus() expect(status).toHaveProperty('scheduled') expect(status).toHaveProperty('nextRun') expect(status).toHaveProperty('lastRun') expect(status.scheduled).toBe(true) }) }) describe('Restore Integration', () => { it('应该能够找到最新备份', async () => { // 创建多个备份文件 await databaseBackup.createBackup() await new Promise(resolve => setTimeout(resolve, 100)) // 确保时间戳不同 await databaseBackup.createBackup() const latestBackup = await databaseRestore.findLatestBackup() expect(latestBackup).toBeDefined() expect(latestBackup).toContain('.dump') }) it('应该能够列出所有备份', async () => { // 创建多个备份文件 await databaseBackup.createBackup() await databaseBackup.createBackup() const backups = await databaseRestore.listBackups() expect(backups.length).toBe(2) expect(backups.every(b => b.endsWith('.dump'))).toBe(true) }) }) })