handlers.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /**
  2. * MSW request handlers for mocking API responses in tests.
  3. */
  4. import { http, HttpResponse } from 'msw';
  5. // Sample data
  6. const mockSmartPlugs = [
  7. {
  8. id: 1,
  9. name: 'Test Plug',
  10. ip_address: '192.168.1.100',
  11. printer_id: 1,
  12. enabled: true,
  13. auto_on: true,
  14. auto_off: true,
  15. off_delay_mode: 'time',
  16. off_delay_minutes: 5,
  17. off_temp_threshold: 70,
  18. username: null,
  19. password: null,
  20. power_alert_enabled: false,
  21. power_alert_high: null,
  22. power_alert_low: null,
  23. power_alert_last_triggered: null,
  24. schedule_enabled: false,
  25. schedule_on_time: null,
  26. schedule_off_time: null,
  27. last_state: 'ON',
  28. last_checked: null,
  29. auto_off_executed: false,
  30. auto_off_pending: false,
  31. auto_off_pending_since: null,
  32. created_at: '2024-01-01T00:00:00Z',
  33. updated_at: '2024-01-01T00:00:00Z',
  34. },
  35. ];
  36. const mockNotificationProviders = [
  37. {
  38. id: 1,
  39. name: 'Test Webhook',
  40. provider_type: 'webhook',
  41. enabled: true,
  42. config: { webhook_url: 'http://test.local/webhook' },
  43. on_print_start: true,
  44. on_print_complete: true,
  45. on_print_failed: true,
  46. on_print_stopped: false,
  47. on_print_progress: false,
  48. on_printer_offline: false,
  49. on_printer_error: false,
  50. on_filament_low: false,
  51. on_maintenance_due: false,
  52. on_ams_humidity_high: false,
  53. on_ams_temperature_high: false,
  54. on_ams_ht_humidity_high: false,
  55. on_ams_ht_temperature_high: false,
  56. quiet_hours_enabled: false,
  57. quiet_hours_start: null,
  58. quiet_hours_end: null,
  59. daily_digest_enabled: false,
  60. daily_digest_time: null,
  61. printer_id: null,
  62. last_success: null,
  63. last_error: null,
  64. last_error_at: null,
  65. created_at: '2024-01-01T00:00:00Z',
  66. updated_at: '2024-01-01T00:00:00Z',
  67. },
  68. ];
  69. const mockPrinters = [
  70. {
  71. id: 1,
  72. name: 'Test Printer',
  73. serial_number: '00M09A000000000',
  74. ip_address: '192.168.1.200',
  75. is_active: true,
  76. model: 'X1C',
  77. nozzle_count: 1,
  78. auto_archive: true,
  79. location: null,
  80. created_at: '2024-01-01T00:00:00Z',
  81. updated_at: '2024-01-01T00:00:00Z',
  82. },
  83. ];
  84. export const handlers = [
  85. // ========================================================================
  86. // Smart Plugs
  87. // ========================================================================
  88. http.get('/api/v1/smart-plugs/', () => {
  89. return HttpResponse.json(mockSmartPlugs);
  90. }),
  91. http.get('/api/v1/smart-plugs/:id', ({ params }) => {
  92. const plug = mockSmartPlugs.find((p) => p.id === Number(params.id));
  93. if (!plug) {
  94. return new HttpResponse(null, { status: 404 });
  95. }
  96. return HttpResponse.json(plug);
  97. }),
  98. http.post('/api/v1/smart-plugs/', async ({ request }) => {
  99. const body = (await request.json()) as Record<string, unknown>;
  100. const { id: _id, ...baseData } = mockSmartPlugs[0];
  101. const newPlug = {
  102. id: mockSmartPlugs.length + 1,
  103. ...baseData,
  104. ...body,
  105. };
  106. return HttpResponse.json(newPlug);
  107. }),
  108. http.patch('/api/v1/smart-plugs/:id', async ({ params, request }) => {
  109. const body = (await request.json()) as Record<string, unknown>;
  110. const plug = mockSmartPlugs.find((p) => p.id === Number(params.id));
  111. if (!plug) {
  112. return new HttpResponse(null, { status: 404 });
  113. }
  114. return HttpResponse.json({ ...plug, ...body });
  115. }),
  116. http.delete('/api/v1/smart-plugs/:id', ({ params }) => {
  117. const index = mockSmartPlugs.findIndex((p) => p.id === Number(params.id));
  118. if (index === -1) {
  119. return new HttpResponse(null, { status: 404 });
  120. }
  121. return HttpResponse.json({ success: true });
  122. }),
  123. http.get('/api/v1/smart-plugs/:id/status', () => {
  124. return HttpResponse.json({
  125. state: 'ON',
  126. reachable: true,
  127. device_name: 'Test Plug',
  128. energy: {
  129. power: 150.5,
  130. voltage: 120.0,
  131. current: 1.25,
  132. today: 2.5,
  133. total: 100.0,
  134. },
  135. });
  136. }),
  137. http.post('/api/v1/smart-plugs/:id/control', async ({ request }) => {
  138. const body = (await request.json()) as { action: string };
  139. return HttpResponse.json({
  140. success: true,
  141. action: body.action,
  142. });
  143. }),
  144. // ========================================================================
  145. // Notification Providers
  146. // ========================================================================
  147. http.get('/api/v1/notifications/', () => {
  148. return HttpResponse.json(mockNotificationProviders);
  149. }),
  150. http.get('/api/v1/notifications/:id', ({ params }) => {
  151. const provider = mockNotificationProviders.find(
  152. (p) => p.id === Number(params.id)
  153. );
  154. if (!provider) {
  155. return new HttpResponse(null, { status: 404 });
  156. }
  157. return HttpResponse.json(provider);
  158. }),
  159. http.post('/api/v1/notifications/', async ({ request }) => {
  160. const body = (await request.json()) as Record<string, unknown>;
  161. const { id: _id, ...baseData } = mockNotificationProviders[0];
  162. const newProvider = {
  163. id: mockNotificationProviders.length + 1,
  164. ...baseData,
  165. ...body,
  166. };
  167. return HttpResponse.json(newProvider);
  168. }),
  169. http.patch('/api/v1/notifications/:id', async ({ params, request }) => {
  170. const body = (await request.json()) as Record<string, unknown>;
  171. const provider = mockNotificationProviders.find(
  172. (p) => p.id === Number(params.id)
  173. );
  174. if (!provider) {
  175. return new HttpResponse(null, { status: 404 });
  176. }
  177. return HttpResponse.json({ ...provider, ...body });
  178. }),
  179. http.delete('/api/v1/notifications/:id', ({ params }) => {
  180. const index = mockNotificationProviders.findIndex(
  181. (p) => p.id === Number(params.id)
  182. );
  183. if (index === -1) {
  184. return new HttpResponse(null, { status: 404 });
  185. }
  186. return HttpResponse.json({ success: true });
  187. }),
  188. http.post('/api/v1/notifications/:id/test', () => {
  189. return HttpResponse.json({
  190. success: true,
  191. message: 'Test notification sent',
  192. });
  193. }),
  194. // ========================================================================
  195. // Printers
  196. // ========================================================================
  197. http.get('/api/v1/printers/', () => {
  198. return HttpResponse.json(mockPrinters);
  199. }),
  200. http.get('/api/v1/printers/:id', ({ params }) => {
  201. const printer = mockPrinters.find((p) => p.id === Number(params.id));
  202. if (!printer) {
  203. return new HttpResponse(null, { status: 404 });
  204. }
  205. return HttpResponse.json(printer);
  206. }),
  207. http.get('/api/v1/printers/:id/status', ({ params }) => {
  208. return HttpResponse.json({
  209. id: Number(params.id),
  210. name: 'Test Printer',
  211. connected: true,
  212. state: 'IDLE',
  213. progress: 0,
  214. layer_num: 0,
  215. total_layers: 0,
  216. temperatures: {
  217. nozzle: 25,
  218. bed: 25,
  219. chamber: 25,
  220. },
  221. remaining_time: 0,
  222. filename: null,
  223. });
  224. }),
  225. // ========================================================================
  226. // Settings
  227. // ========================================================================
  228. http.get('/api/v1/settings/', () => {
  229. return HttpResponse.json({
  230. auto_archive: true,
  231. save_thumbnails: true,
  232. capture_finish_photo: true,
  233. default_filament_cost: 25.0,
  234. currency: 'USD',
  235. ams_humidity_good: 40,
  236. ams_humidity_fair: 60,
  237. ams_temp_good: 30,
  238. ams_temp_fair: 35,
  239. });
  240. }),
  241. http.patch('/api/v1/settings/', async ({ request }) => {
  242. const body = (await request.json()) as Record<string, unknown>;
  243. return HttpResponse.json(body);
  244. }),
  245. // ========================================================================
  246. // Auth
  247. // ========================================================================
  248. http.get('*/api/v1/auth/status', () => {
  249. return HttpResponse.json({
  250. auth_enabled: false,
  251. requires_setup: false,
  252. });
  253. }),
  254. http.get('/api/v1/auth/me', () => {
  255. return HttpResponse.json({
  256. id: 1,
  257. username: 'admin',
  258. role: 'admin',
  259. is_active: true,
  260. is_admin: true,
  261. groups: [{ id: 1, name: 'Administrators' }],
  262. permissions: [],
  263. created_at: '2024-01-01T00:00:00Z',
  264. });
  265. }),
  266. // ========================================================================
  267. // Groups
  268. // ========================================================================
  269. http.get('/api/v1/groups/', () => {
  270. return HttpResponse.json([
  271. {
  272. id: 1,
  273. name: 'Administrators',
  274. description: 'Full access to all features',
  275. permissions: ['printers:read', 'settings:update', 'users:create'],
  276. is_system: true,
  277. created_at: '2024-01-01T00:00:00Z',
  278. updated_at: '2024-01-01T00:00:00Z',
  279. },
  280. {
  281. id: 2,
  282. name: 'Operators',
  283. description: 'Control printers and manage content',
  284. permissions: ['printers:read', 'printers:control'],
  285. is_system: true,
  286. created_at: '2024-01-01T00:00:00Z',
  287. updated_at: '2024-01-01T00:00:00Z',
  288. },
  289. {
  290. id: 3,
  291. name: 'Viewers',
  292. description: 'Read-only access',
  293. permissions: ['printers:read'],
  294. is_system: true,
  295. created_at: '2024-01-01T00:00:00Z',
  296. updated_at: '2024-01-01T00:00:00Z',
  297. },
  298. ]);
  299. }),
  300. http.get('/api/v1/groups/permissions', () => {
  301. return HttpResponse.json({
  302. 'Printers': ['printers:read', 'printers:create', 'printers:update', 'printers:delete', 'printers:control'],
  303. 'Archives': ['archives:read', 'archives:create', 'archives:update', 'archives:delete'],
  304. 'Settings': ['settings:read', 'settings:update'],
  305. });
  306. }),
  307. http.post('/api/v1/groups/', async ({ request }) => {
  308. const body = (await request.json()) as Record<string, unknown>;
  309. return HttpResponse.json({
  310. id: 4,
  311. ...body,
  312. is_system: false,
  313. created_at: '2024-01-01T00:00:00Z',
  314. updated_at: '2024-01-01T00:00:00Z',
  315. });
  316. }),
  317. http.patch('/api/v1/groups/:id', async ({ params, request }) => {
  318. const body = (await request.json()) as Record<string, unknown>;
  319. return HttpResponse.json({
  320. id: Number(params.id),
  321. name: 'Updated Group',
  322. ...body,
  323. is_system: false,
  324. created_at: '2024-01-01T00:00:00Z',
  325. updated_at: '2024-01-01T00:00:00Z',
  326. });
  327. }),
  328. http.delete('/api/v1/groups/:id', () => {
  329. return new HttpResponse(null, { status: 204 });
  330. }),
  331. // ========================================================================
  332. // Discovery
  333. // ========================================================================
  334. http.get('/api/v1/discovery/info', () => {
  335. return HttpResponse.json({
  336. is_docker: false,
  337. ssdp_running: false,
  338. scan_running: false,
  339. subnets: ['192.168.1.0/24'],
  340. });
  341. }),
  342. // ========================================================================
  343. // Version / Health
  344. // ========================================================================
  345. http.get('/api/v1/version', () => {
  346. return HttpResponse.json({
  347. version: '0.1.5',
  348. build: 'test',
  349. });
  350. }),
  351. http.get('/health', () => {
  352. return HttpResponse.json({ status: 'healthy' });
  353. }),
  354. // ========================================================================
  355. // Archives
  356. // ========================================================================
  357. http.get('/api/v1/archives/:id/plates', ({ params }) => {
  358. const archiveId = Number(params.id);
  359. return HttpResponse.json({
  360. archive_id: Number.isFinite(archiveId) ? archiveId : 0,
  361. filename: 'sample.3mf',
  362. plates: [],
  363. is_multi_plate: false,
  364. });
  365. }),
  366. http.get('/api/v1/archives/:id/filament-requirements', () => {
  367. return HttpResponse.json([]);
  368. }),
  369. // ========================================================================
  370. // Library
  371. // ========================================================================
  372. http.get('/api/v1/library/stats', () => {
  373. return HttpResponse.json({
  374. total_files: 0,
  375. total_size: 0,
  376. total_folders: 0,
  377. });
  378. }),
  379. ];