importBackup.test.ts 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. /**
  2. * Tests for api.importBackup's handling of a refused restore.
  3. *
  4. * A restore the server declines is an HTTPException, so its body is
  5. * `{detail}` — not the `{success, message}` a successful restore returns.
  6. * importBackup used to hand that body straight back, which left `success`
  7. * undefined (falsy, so the UI took the failure branch) and `message`
  8. * undefined with it: the operator got an empty error toast and no idea why.
  9. *
  10. * It matters most for exactly the case it was found in — a backup this version
  11. * cannot import. That refusal names the columns and both version numbers, and
  12. * all of it was being dropped on the floor.
  13. */
  14. import { describe, it, expect, beforeAll, afterEach, afterAll } from 'vitest';
  15. import { http, HttpResponse } from 'msw';
  16. import { setupServer } from 'msw/node';
  17. import { api } from '../../api/client';
  18. const server = setupServer();
  19. beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' }));
  20. afterEach(() => server.resetHandlers());
  21. afterAll(() => server.close());
  22. const backupFile = () => new File(['(a backup)'], 'backup.zip', { type: 'application/zip' });
  23. describe('api.importBackup', () => {
  24. it('passes a successful restore through unchanged', async () => {
  25. server.use(
  26. http.post('*/settings/restore', () =>
  27. HttpResponse.json({ success: true, message: 'Backup restored successfully.' })
  28. )
  29. );
  30. const result = await api.importBackup(backupFile());
  31. expect(result.success).toBe(true);
  32. expect(result.message).toBe('Backup restored successfully.');
  33. });
  34. it('turns a refusal into a failure carrying the reason', async () => {
  35. const detail =
  36. 'This backup cannot be restored by this version of Bambuddy. It carries no value for ' +
  37. '1 column(s) this version requires and cannot default: cost_centers.name.';
  38. server.use(http.post('*/settings/restore', () => HttpResponse.json({ detail }, { status: 400 })));
  39. const result = await api.importBackup(backupFile());
  40. expect(result.success).toBe(false);
  41. expect(result.message).toBe(detail);
  42. });
  43. it('reports a failure even when the error body is not JSON', async () => {
  44. server.use(
  45. http.post('*/settings/restore', () => new HttpResponse('upstream exploded', { status: 502 }))
  46. );
  47. const result = await api.importBackup(backupFile());
  48. expect(result.success).toBe(false);
  49. expect(result.message).toBe('');
  50. });
  51. });