githubBackupApi.test.ts 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /**
  2. * Tests for the GitHub Backup API client functions.
  3. */
  4. import { describe, it, expect } from 'vitest';
  5. import { http, HttpResponse } from 'msw';
  6. import { setupServer } from 'msw/node';
  7. import type {
  8. GitHubBackupConfig,
  9. GitHubBackupStatus,
  10. GitHubBackupLog,
  11. } from '../../api/client';
  12. // Mock API base URL
  13. const API_BASE = 'http://localhost:5000/api/v1';
  14. // Create MSW server
  15. const server = setupServer();
  16. beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' }));
  17. afterEach(() => server.resetHandlers());
  18. afterAll(() => server.close());
  19. describe('GitHub Backup API Types', () => {
  20. it('GitHubBackupConfig has correct shape', () => {
  21. const config: GitHubBackupConfig = {
  22. id: 1,
  23. repository_url: 'https://github.com/test/repo',
  24. has_token: true,
  25. branch: 'main',
  26. schedule_enabled: true,
  27. schedule_type: 'daily',
  28. backup_kprofiles: true,
  29. backup_cloud_profiles: true,
  30. backup_settings: false,
  31. backup_spools: false,
  32. backup_archives: false,
  33. enabled: true,
  34. last_backup_at: '2026-01-27T10:00:00Z',
  35. last_backup_status: 'success',
  36. last_backup_message: null,
  37. last_backup_commit_sha: 'abc123',
  38. next_scheduled_run: '2026-01-28T00:00:00Z',
  39. created_at: '2026-01-01T00:00:00Z',
  40. updated_at: '2026-01-27T10:00:00Z',
  41. };
  42. expect(config.id).toBe(1);
  43. expect(config.has_token).toBe(true);
  44. expect(config.schedule_type).toBe('daily');
  45. expect(config.backup_spools).toBe(false);
  46. expect(config.backup_archives).toBe(false);
  47. });
  48. it('GitHubBackupStatus has correct shape', () => {
  49. const status: GitHubBackupStatus = {
  50. configured: true,
  51. enabled: true,
  52. is_running: false,
  53. progress: null,
  54. last_backup_at: '2026-01-27T10:00:00Z',
  55. last_backup_status: 'success',
  56. next_scheduled_run: '2026-01-28T00:00:00Z',
  57. };
  58. expect(status.configured).toBe(true);
  59. expect(status.is_running).toBe(false);
  60. });
  61. it('GitHubBackupStatus can have progress', () => {
  62. const status: GitHubBackupStatus = {
  63. configured: true,
  64. enabled: true,
  65. is_running: true,
  66. progress: 'Pushing to GitHub...',
  67. last_backup_at: null,
  68. last_backup_status: null,
  69. next_scheduled_run: null,
  70. };
  71. expect(status.is_running).toBe(true);
  72. expect(status.progress).toBe('Pushing to GitHub...');
  73. });
  74. it('GitHubBackupLog has correct shape', () => {
  75. const log: GitHubBackupLog = {
  76. id: 1,
  77. config_id: 1,
  78. started_at: '2026-01-27T10:00:00Z',
  79. completed_at: '2026-01-27T10:01:00Z',
  80. status: 'success',
  81. trigger: 'manual',
  82. commit_sha: 'abc123',
  83. files_changed: 5,
  84. error_message: null,
  85. };
  86. expect(log.status).toBe('success');
  87. expect(log.trigger).toBe('manual');
  88. expect(log.files_changed).toBe(5);
  89. });
  90. it('GitHubBackupConfig supports spool and archive backup toggles', () => {
  91. const config: GitHubBackupConfig = {
  92. id: 2,
  93. repository_url: 'https://github.com/test/full-backup',
  94. has_token: true,
  95. branch: 'main',
  96. schedule_enabled: false,
  97. schedule_type: 'daily',
  98. backup_kprofiles: true,
  99. backup_cloud_profiles: false,
  100. backup_settings: false,
  101. backup_spools: true,
  102. backup_archives: true,
  103. enabled: true,
  104. last_backup_at: null,
  105. last_backup_status: null,
  106. last_backup_message: null,
  107. last_backup_commit_sha: null,
  108. next_scheduled_run: null,
  109. created_at: '2026-04-01T00:00:00Z',
  110. updated_at: '2026-04-01T00:00:00Z',
  111. };
  112. expect(config.backup_spools).toBe(true);
  113. expect(config.backup_archives).toBe(true);
  114. expect(config.backup_cloud_profiles).toBe(false);
  115. });
  116. it('GitHubBackupLog can have error', () => {
  117. const log: GitHubBackupLog = {
  118. id: 2,
  119. config_id: 1,
  120. started_at: '2026-01-27T10:00:00Z',
  121. completed_at: '2026-01-27T10:00:30Z',
  122. status: 'failed',
  123. trigger: 'scheduled',
  124. commit_sha: null,
  125. files_changed: 0,
  126. error_message: 'Authentication failed',
  127. };
  128. expect(log.status).toBe('failed');
  129. expect(log.error_message).toBe('Authentication failed');
  130. expect(log.commit_sha).toBeNull();
  131. });
  132. });
  133. describe('GitHub Backup API Endpoints', () => {
  134. it('GET /github-backup/config returns null when not configured', async () => {
  135. server.use(
  136. http.get(`${API_BASE}/github-backup/config`, () => {
  137. return HttpResponse.json(null);
  138. })
  139. );
  140. const response = await fetch(`${API_BASE}/github-backup/config`);
  141. const data = await response.json();
  142. expect(data).toBeNull();
  143. });
  144. it('GET /github-backup/config returns config when exists', async () => {
  145. const mockConfig: GitHubBackupConfig = {
  146. id: 1,
  147. repository_url: 'https://github.com/test/repo',
  148. has_token: true,
  149. branch: 'main',
  150. schedule_enabled: false,
  151. schedule_type: 'daily',
  152. backup_kprofiles: true,
  153. backup_cloud_profiles: true,
  154. backup_settings: false,
  155. backup_spools: true,
  156. backup_archives: true,
  157. enabled: true,
  158. last_backup_at: null,
  159. last_backup_status: null,
  160. last_backup_message: null,
  161. last_backup_commit_sha: null,
  162. next_scheduled_run: null,
  163. created_at: '2026-01-01T00:00:00Z',
  164. updated_at: '2026-01-01T00:00:00Z',
  165. };
  166. server.use(
  167. http.get(`${API_BASE}/github-backup/config`, () => {
  168. return HttpResponse.json(mockConfig);
  169. })
  170. );
  171. const response = await fetch(`${API_BASE}/github-backup/config`);
  172. const data = await response.json();
  173. expect(data.repository_url).toBe('https://github.com/test/repo');
  174. expect(data.has_token).toBe(true);
  175. });
  176. it('GET /github-backup/status returns not configured status', async () => {
  177. const mockStatus: GitHubBackupStatus = {
  178. configured: false,
  179. enabled: false,
  180. is_running: false,
  181. progress: null,
  182. last_backup_at: null,
  183. last_backup_status: null,
  184. next_scheduled_run: null,
  185. };
  186. server.use(
  187. http.get(`${API_BASE}/github-backup/status`, () => {
  188. return HttpResponse.json(mockStatus);
  189. })
  190. );
  191. const response = await fetch(`${API_BASE}/github-backup/status`);
  192. const data = await response.json();
  193. expect(data.configured).toBe(false);
  194. expect(data.enabled).toBe(false);
  195. });
  196. it('GET /github-backup/logs returns empty list when no logs', async () => {
  197. server.use(
  198. http.get(`${API_BASE}/github-backup/logs`, () => {
  199. return HttpResponse.json([]);
  200. })
  201. );
  202. const response = await fetch(`${API_BASE}/github-backup/logs`);
  203. const data = await response.json();
  204. expect(data).toEqual([]);
  205. });
  206. it('GET /github-backup/logs returns log entries', async () => {
  207. const mockLogs: GitHubBackupLog[] = [
  208. {
  209. id: 1,
  210. config_id: 1,
  211. started_at: '2026-01-27T10:00:00Z',
  212. completed_at: '2026-01-27T10:01:00Z',
  213. status: 'success',
  214. trigger: 'manual',
  215. commit_sha: 'abc123',
  216. files_changed: 5,
  217. error_message: null,
  218. },
  219. ];
  220. server.use(
  221. http.get(`${API_BASE}/github-backup/logs`, () => {
  222. return HttpResponse.json(mockLogs);
  223. })
  224. );
  225. const response = await fetch(`${API_BASE}/github-backup/logs`);
  226. const data = await response.json();
  227. expect(data.length).toBe(1);
  228. expect(data[0].status).toBe('success');
  229. });
  230. it('POST /github-backup/run returns 404 when not configured', async () => {
  231. server.use(
  232. http.post(`${API_BASE}/github-backup/run`, () => {
  233. return HttpResponse.json(
  234. { detail: 'No configuration found' },
  235. { status: 404 }
  236. );
  237. })
  238. );
  239. const response = await fetch(`${API_BASE}/github-backup/run`, {
  240. method: 'POST',
  241. });
  242. expect(response.status).toBe(404);
  243. });
  244. it('POST /github-backup/test returns success on valid credentials', async () => {
  245. server.use(
  246. http.post(`${API_BASE}/github-backup/test`, () => {
  247. return HttpResponse.json({
  248. success: true,
  249. message: 'Connection successful',
  250. repo_name: 'test/repo',
  251. default_branch: 'main',
  252. });
  253. })
  254. );
  255. const response = await fetch(
  256. `${API_BASE}/github-backup/test?repo_url=https://github.com/test/repo&token=ghp_test`,
  257. { method: 'POST' }
  258. );
  259. const data = await response.json();
  260. expect(data.success).toBe(true);
  261. expect(data.repo_name).toBe('test/repo');
  262. });
  263. it('POST /github-backup/test returns failure on invalid credentials', async () => {
  264. server.use(
  265. http.post(`${API_BASE}/github-backup/test`, () => {
  266. return HttpResponse.json({
  267. success: false,
  268. message: 'Authentication failed',
  269. repo_name: null,
  270. default_branch: null,
  271. });
  272. })
  273. );
  274. const response = await fetch(
  275. `${API_BASE}/github-backup/test?repo_url=https://github.com/test/repo&token=invalid`,
  276. { method: 'POST' }
  277. );
  278. const data = await response.json();
  279. expect(data.success).toBe(false);
  280. expect(data.message).toBe('Authentication failed');
  281. });
  282. });