restore.test.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
  2. import { DatabaseRestore } from '@d8d/server/utils/restore'
  3. import path from 'path'
  4. // Mock pg-dump-restore
  5. vi.mock('pg-dump-restore', () => ({
  6. pgRestore: vi.fn().mockResolvedValue(undefined),
  7. }))
  8. // Mock fs with importOriginal for partial mocking
  9. vi.mock('fs', async (importOriginal) => {
  10. const actual = await importOriginal() as typeof import('fs')
  11. return {
  12. ...actual,
  13. promises: {
  14. ...actual.promises,
  15. readdir: vi.fn().mockResolvedValue([]),
  16. access: vi.fn().mockResolvedValue(undefined),
  17. stat: vi.fn().mockResolvedValue({ size: 1024, mtime: new Date() }),
  18. },
  19. }
  20. })
  21. // Mock logger
  22. vi.mock('@d8d/server/utils/logger', () => ({
  23. logger: {
  24. db: vi.fn(),
  25. error: vi.fn(),
  26. api: vi.fn(),
  27. middleware: vi.fn(),
  28. },
  29. }))
  30. describe('DatabaseRestore', () => {
  31. let restore: DatabaseRestore
  32. beforeEach(() => {
  33. vi.clearAllMocks()
  34. restore = new DatabaseRestore()
  35. })
  36. afterEach(() => {
  37. vi.restoreAllMocks()
  38. })
  39. describe('getDbConfig', () => {
  40. it('应该返回正确的数据库配置', () => {
  41. process.env.DB_HOST = 'test-host'
  42. process.env.DB_PORT = '5433'
  43. process.env.DB_DATABASE = 'test-db'
  44. process.env.DB_USERNAME = 'test-user'
  45. process.env.DB_PASSWORD = 'test-password'
  46. const config = (restore as any).getDbConfig()
  47. expect(config).toEqual({
  48. host: 'test-host',
  49. port: 5433,
  50. database: 'test-db',
  51. username: 'test-user',
  52. password: 'test-password',
  53. })
  54. })
  55. it('应该使用默认值当环境变量未设置时', () => {
  56. delete process.env.DB_HOST
  57. delete process.env.DB_PORT
  58. delete process.env.DB_DATABASE
  59. delete process.env.DB_USERNAME
  60. delete process.env.DB_PASSWORD
  61. const config = (restore as any).getDbConfig()
  62. expect(config).toEqual({
  63. host: 'localhost',
  64. port: 5432,
  65. database: 'postgres',
  66. username: 'postgres',
  67. password: '',
  68. })
  69. })
  70. })
  71. describe('findLatestBackup', () => {
  72. it('应该返回最新的备份文件', async () => {
  73. const fs = await import('fs')
  74. vi.mocked(fs.promises.readdir).mockResolvedValue([
  75. 'backup-2024-01-01T00-00-00Z.dump',
  76. 'backup-2024-01-03T00-00-00Z.dump',
  77. 'backup-2024-01-02T00-00-00Z.dump',
  78. ] as any)
  79. const latest = await restore.findLatestBackup()
  80. expect(latest).toBe(path.join('./backups', 'backup-2024-01-03T00-00-00Z.dump'))
  81. })
  82. it('应该返回null当没有备份文件时', async () => {
  83. const fs = await import('fs')
  84. vi.mocked(fs.promises.readdir).mockResolvedValue([
  85. 'some-other-file.txt'
  86. ] as any)
  87. const latest = await restore.findLatestBackup()
  88. expect(latest).toBeNull()
  89. })
  90. it('应该在读取目录失败时返回null', async () => {
  91. const fs = await import('fs')
  92. const { logger } = await import('@d8d/server/utils/logger')
  93. vi.mocked(fs.promises.readdir).mockRejectedValueOnce(new Error('读取目录失败'))
  94. const latest = await restore.findLatestBackup()
  95. expect(latest).toBeNull()
  96. expect(logger.error).toHaveBeenCalled()
  97. })
  98. })
  99. describe('listBackups', () => {
  100. it('应该返回所有备份文件列表', async () => {
  101. const fs = await import('fs')
  102. vi.mocked(fs.promises.readdir).mockResolvedValue([
  103. 'backup-2024-01-01.dump',
  104. 'some-other-file.txt',
  105. 'backup-2024-01-02.dump',
  106. ] as any)
  107. const backups = await restore.listBackups()
  108. expect(backups).toEqual([
  109. 'backup-2024-01-01.dump',
  110. 'backup-2024-01-02.dump',
  111. ])
  112. })
  113. it('应该在读取目录失败时返回空数组', async () => {
  114. const fs = await import('fs')
  115. const { logger } = await import('@d8d/server/utils/logger')
  116. vi.mocked(fs.promises.readdir).mockRejectedValueOnce(new Error('读取目录失败'))
  117. const backups = await restore.listBackups()
  118. expect(backups).toEqual([])
  119. expect(logger.error).toHaveBeenCalled()
  120. })
  121. })
  122. describe('backupExists', () => {
  123. it('应该返回true当备份文件存在时', async () => {
  124. const fs = await import('fs')
  125. const exists = await restore.backupExists('/path/to/backup.dump')
  126. expect(exists).toBe(true)
  127. expect(fs.promises.access).toHaveBeenCalledWith('/path/to/backup.dump')
  128. })
  129. it('应该返回false当备份文件不存在时', async () => {
  130. const fs = await import('fs')
  131. vi.mocked(fs.promises.access).mockRejectedValueOnce(new Error('文件不存在'))
  132. const exists = await restore.backupExists('/path/to/backup.dump')
  133. expect(exists).toBe(false)
  134. })
  135. })
  136. describe('getBackupInfo', () => {
  137. it('应该返回备份文件信息', async () => {
  138. const fs = await import('fs')
  139. const testDate = new Date()
  140. vi.mocked(fs.promises.stat).mockResolvedValueOnce({
  141. size: 1048576,
  142. mtime: testDate,
  143. } as any)
  144. const info = await restore.getBackupInfo('/path/to/backup.dump')
  145. expect(info).toEqual({
  146. size: 1048576,
  147. mtime: testDate,
  148. formattedSize: '1 MB',
  149. })
  150. })
  151. it('应该在获取信息失败时抛出错误', async () => {
  152. const fs = await import('fs')
  153. vi.mocked(fs.promises.stat).mockRejectedValueOnce(new Error('获取文件信息失败'))
  154. await expect(restore.getBackupInfo('/path/to/backup.dump')).rejects.toThrow('获取备份信息失败')
  155. })
  156. })
  157. describe('formatFileSize', () => {
  158. it('应该正确格式化文件大小', () => {
  159. const formatFileSize = (restore as any).formatFileSize
  160. expect(formatFileSize(0)).toBe('0 B')
  161. expect(formatFileSize(1024)).toBe('1 KB')
  162. expect(formatFileSize(1048576)).toBe('1 MB')
  163. expect(formatFileSize(1073741824)).toBe('1 GB')
  164. })
  165. })
  166. })