GitHubBackupSettings.history.test.tsx 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. /**
  2. * Backup History must distinguish a restore from a backup (#2656).
  3. *
  4. * A restore writes a `github_backup_logs` row too — same table, same statuses,
  5. * `trigger: 'restore'`. The table rendered date / status / commit only, so the
  6. * row read as a successful backup dated now, while "Last backup" said something
  7. * else entirely. The column below is the only thing telling the two apart.
  8. */
  9. import { describe, it, expect, vi, beforeEach } from 'vitest';
  10. import { screen, within } from '@testing-library/react';
  11. import { render } from '../utils';
  12. import { GitHubBackupSettings } from '../../components/GitHubBackupSettings';
  13. import { api } from '../../api/client';
  14. vi.mock('../../api/client', () => ({
  15. api: {
  16. getGitHubBackupConfig: vi.fn().mockResolvedValue({
  17. id: 1,
  18. repository_url: 'https://github.com/someone/backup',
  19. enabled: true,
  20. }),
  21. getGitHubBackupStatus: vi.fn().mockResolvedValue({ is_running: false, configured: true, enabled: true }),
  22. getGitHubBackupLogs: vi.fn(),
  23. getCloudStatus: vi.fn().mockResolvedValue({ is_authenticated: false }),
  24. getPrinters: vi.fn().mockResolvedValue([]),
  25. getPrinterStatus: vi.fn().mockResolvedValue({ connected: false }),
  26. getSettings: vi.fn().mockResolvedValue({}),
  27. updateSettings: vi.fn().mockResolvedValue({}),
  28. getLocalBackups: vi.fn().mockResolvedValue([]),
  29. getLocalBackupStatus: vi.fn().mockResolvedValue({
  30. enabled: false,
  31. is_running: false,
  32. last_backup_at: null,
  33. last_status: null,
  34. last_message: null,
  35. next_run: null,
  36. }),
  37. checkLocalBackupPath: vi.fn().mockResolvedValue({ writable: true, path: '/data', code: 'ok' }),
  38. },
  39. }));
  40. const log = (id: number, trigger: string) => ({
  41. id,
  42. config_id: 1,
  43. started_at: '2026-08-02T09:00:00',
  44. completed_at: '2026-08-02T09:00:05',
  45. status: 'success',
  46. trigger,
  47. commit_sha: null,
  48. files_changed: 0,
  49. error_message: null,
  50. });
  51. const historyRows = async () => {
  52. const table = (await screen.findByText('History')).closest('div[id="card-backup-history"]');
  53. return within(table as HTMLElement).getAllByRole('row').slice(1); // drop the header
  54. };
  55. describe('GitHubBackupSettings — backup history', () => {
  56. beforeEach(() => {
  57. vi.clearAllMocks();
  58. });
  59. it('labels a restore as a restore, not a successful backup', async () => {
  60. vi.mocked(api.getGitHubBackupLogs).mockResolvedValue([log(1, 'restore')]);
  61. render(<GitHubBackupSettings />);
  62. const [row] = await historyRows();
  63. expect(within(row).getByText('Restore')).toBeInTheDocument();
  64. });
  65. it('tells the three trigger kinds apart in one history', async () => {
  66. vi.mocked(api.getGitHubBackupLogs).mockResolvedValue([
  67. log(1, 'restore'),
  68. log(2, 'scheduled'),
  69. log(3, 'manual'),
  70. ]);
  71. render(<GitHubBackupSettings />);
  72. const rows = await historyRows();
  73. expect(within(rows[0]).getByText('Restore')).toBeInTheDocument();
  74. expect(within(rows[1]).getByText('Backup (scheduled)')).toBeInTheDocument();
  75. expect(within(rows[2]).getByText('Backup (manual)')).toBeInTheDocument();
  76. });
  77. it('falls back to the raw trigger rather than blanking an unknown one', async () => {
  78. vi.mocked(api.getGitHubBackupLogs).mockResolvedValue([log(1, 'something-new')]);
  79. render(<GitHubBackupSettings />);
  80. const [row] = await historyRows();
  81. expect(within(row).getByText('something-new')).toBeInTheDocument();
  82. });
  83. });