Coverage for tests/endpoints/tests_models.py: 100%
432 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-15 22:43 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-15 22:43 +0000
1from unittest.mock import patch
3from canonicalwebteam.candid import CandidClient
4from canonicalwebteam.exceptions import (
5 StoreApiResponseErrorList,
6 StoreApiResourceNotFound,
7)
8from webapp.helpers import api_publisher_session
9from tests.endpoints.endpoint_testing import TestModelServiceEndpoints
11candid = CandidClient(api_publisher_session)
14class TestCreateModel(TestModelServiceEndpoints):
15 @patch(
16 "canonicalwebteam.store_api.publishergw.PublisherGW.create_store_model"
17 )
18 def test_create_model(self, mock_create_store_model):
19 mock_create_store_model.return_value = None
21 payload = {"name": "Test Model", "api_key": self.api_key}
22 response = self.client.post("/api/store/1/models", data=payload)
23 data = response.json
25 self.assertEqual(response.status_code, 201)
26 self.assertTrue(data["success"])
28 def test_create_model_with_invalid_api_key(self):
29 payload = {"name": "Test Model", "api_key": "invalid_api_key"}
30 response = self.client.post("/api/store/1/models", data=payload)
31 data = response.json
33 self.assertEqual(response.status_code, 500)
34 self.assertFalse(data["success"])
35 self.assertIn("Invalid API key", data["message"])
37 def test_name_too_long(self):
38 payload = {"name": "some_random_long_name" * 7}
39 response = self.client.post("/api/store/1/models", data=payload)
40 data = response.json
42 self.assertEqual(response.status_code, 500)
43 self.assertFalse(data["success"])
44 self.assertEqual(
45 data["message"], "Name is too long. Limit 128 characters"
46 )
48 def test_missing_name(self):
49 payload = {"api_key": self.api_key}
50 response = self.client.post("/api/store/1/models", data=payload)
51 data = response.json
53 self.assertEqual(response.status_code, 500)
54 self.assertFalse(data["success"])
55 self.assertEqual(data["message"], "An error occurred")
58class TestGetModels(TestModelServiceEndpoints):
59 @patch(
60 "canonicalwebteam.store_api.publishergw.PublisherGW.get_store_models"
61 )
62 def test_get_models(self, mock_get_store_models):
63 mock_get_store_models.return_value = {
64 "models": [{"name": "test_model"}]
65 }
67 response = self.client.get("/api/store/1/models")
68 data = response.json
70 self.assertEqual(response.status_code, 200)
71 self.assertIsNotNone(data["data"])
73 @patch(
74 "canonicalwebteam.store_api.publishergw.PublisherGW.get_store_models"
75 )
76 def test_store_has_no_models(self, mock_get_store_models):
77 mock_get_store_models.return_value = {"models": []}
79 response = self.client.get("/api/store/2/models")
80 data = response.json["data"]
82 success = response.json["success"]
83 self.assertEqual(response.status_code, 200)
84 self.assertTrue(success)
85 self.assertEqual(data["models"], [])
87 @patch(
88 "canonicalwebteam.store_api.publishergw.PublisherGW.get_store_models"
89 )
90 def test_invalid_store_id(self, mock_get_store_models):
91 mock_get_store_models.side_effect = StoreApiResponseErrorList(
92 "Store not found",
93 404,
94 [{"message": "Store not found"}],
95 )
97 response = self.client.get("/api/store/3/models")
98 data = response.json
100 self.assertEqual(response.status_code, 500)
101 self.assertFalse(data["success"])
102 self.assertIn("Store not found", data["message"])
104 @patch(
105 "canonicalwebteam.store_api.publishergw.PublisherGW.get_store_models"
106 )
107 def test_unauthorized_user(self, mock_get_store_models):
108 mock_get_store_models.side_effect = StoreApiResponseErrorList(
109 "unauthorized", 401, [{"message": "unauthorized"}]
110 )
112 response = self.client.get("/api/store/1/models")
113 data = response.json
115 self.assertEqual(response.status_code, 500)
116 self.assertFalse(data["success"])
117 self.assertIn("Store not found", data["message"])
120class TestUpdateModel(TestModelServiceEndpoints):
121 @patch(
122 "canonicalwebteam.store_api.publishergw.PublisherGW.update_store_model"
123 )
124 def test_update_model(self, mock_update_store_model):
125 mock_update_store_model.return_value = None
127 payload = {"api_key": self.api_key}
128 response = self.client.patch(
129 "/api/store/1/models/Model1", data=payload
130 )
131 data = response.json
133 self.assertEqual(response.status_code, 200)
134 self.assertTrue(data["success"])
136 def test_update_model_with_invalid_api_key(self):
137 payload = {"api_key": "invalid_api_key"}
138 response = self.client.patch(
139 "/api/store/1/models/Model1", data=payload
140 )
141 data = response.json
143 self.assertEqual(response.status_code, 500)
144 self.assertFalse(data["success"])
145 self.assertEqual(data["message"], "Invalid API key")
147 @patch(
148 "canonicalwebteam.store_api.publishergw.PublisherGW.update_store_model"
149 )
150 def test_model_not_found(self, mock_update_store_model):
151 mock_update_store_model.side_effect = StoreApiResourceNotFound(
152 "Model not found", 404, [{"message": "Model not found"}]
153 )
155 payload = {"api_key": self.api_key}
156 response = self.client.patch(
157 "/api/store/1/models/Model1", data=payload
158 )
159 data = response.json
161 self.assertEqual(response.status_code, 500)
162 self.assertFalse(data["success"])
163 self.assertEqual(data["message"], "Model not found")
166class TestGetRemodelAllowlist(TestModelServiceEndpoints):
167 @patch(
168 "canonicalwebteam.store_api.publishergw.PublisherGW"
169 + ".get_remodel_allowlist"
170 )
171 def test_get_remodel_allowlist_success(self, mock_get_remodel_allowlist):
172 mock_allowlist = {
173 "allowlist": [
174 {
175 "created-at": "2026-02-03T11:31:06Z",
176 "created-by": "test-user-id",
177 "description": "Test description",
178 "from-model": "test-from-model",
179 "from-serial": "test-from-serial",
180 "modified-at": None,
181 "modified-by": None,
182 "to-model": "test-to-model",
183 }
184 ]
185 }
186 mock_get_remodel_allowlist.return_value = mock_allowlist
188 response = self.client.get("/api/store/1/models/remodel-allowlist")
189 data = response.json
191 self.assertEqual(response.status_code, 200)
192 self.assertTrue(data["success"])
193 self.assertEqual(data["data"], mock_allowlist)
195 @patch(
196 "canonicalwebteam.store_api.publishergw.PublisherGW"
197 + ".get_remodel_allowlist"
198 )
199 def test_get_remodel_allowlist_empty(self, mock_get_remodel_allowlist):
200 mock_get_remodel_allowlist.return_value = {"allowlist": []}
202 response = self.client.get("/api/store/1/models/remodel-allowlist")
203 data = response.json
205 self.assertEqual(response.status_code, 200)
206 self.assertTrue(data["success"])
207 self.assertEqual(data["data"]["allowlist"], [])
209 @patch(
210 "canonicalwebteam.store_api.publishergw.PublisherGW"
211 + ".get_remodel_allowlist"
212 )
213 def test_get_remodel_allowlist_unauthorized(
214 self, mock_get_remodel_allowlist
215 ):
216 mock_get_remodel_allowlist.side_effect = StoreApiResponseErrorList(
217 "unauthorized", 401, [{"message": "unauthorized"}]
218 )
220 response = self.client.get("/api/store/1/models/remodel-allowlist")
221 data = response.json
223 self.assertEqual(response.status_code, 500)
224 self.assertFalse(data["success"])
225 self.assertEqual(data["message"], "Store not found")
227 @patch(
228 "canonicalwebteam.store_api.publishergw.PublisherGW"
229 + ".get_remodel_allowlist"
230 )
231 def test_get_remodel_allowlist_store_not_found(
232 self, mock_get_remodel_allowlist
233 ):
234 mock_get_remodel_allowlist.side_effect = StoreApiResponseErrorList(
235 "Store not found", 404, [{"message": "Store not found"}]
236 )
238 response = self.client.get("/api/store/999/models/remodel-allowlist")
239 data = response.json
241 self.assertEqual(response.status_code, 500)
242 self.assertFalse(data["success"])
243 self.assertEqual(data["message"], "Store not found")
245 @patch(
246 "canonicalwebteam.store_api.publishergw.PublisherGW"
247 + ".get_remodel_allowlist"
248 )
249 def test_get_remodel_allowlist_general_error(
250 self, mock_get_remodel_allowlist
251 ):
252 mock_get_remodel_allowlist.side_effect = StoreApiResponseErrorList(
253 "Internal server error",
254 500,
255 [{"message": "Internal server error"}],
256 )
258 response = self.client.get("/api/store/1/models/remodel-allowlist")
259 data = response.json
261 self.assertEqual(response.status_code, 500)
262 self.assertFalse(data["success"])
263 self.assertEqual(data["message"], "Internal server error")
265 @patch(
266 "canonicalwebteam.store_api.publishergw.PublisherGW"
267 + ".get_remodel_allowlist"
268 )
269 def test_get_remodel_allowlist_with_page_parameter(
270 self, mock_get_remodel_allowlist
271 ):
272 mock_get_remodel_allowlist.return_value = {"allowlist": []}
274 response = self.client.get(
275 "/api/store/1/models/remodel-allowlist?page=2"
276 )
277 data = response.json
279 self.assertEqual(response.status_code, 200)
280 self.assertTrue(data["success"])
281 mock_get_remodel_allowlist.assert_called_once_with(
282 mock_get_remodel_allowlist.call_args[0][0], # flask.session
283 "1", # store_id
284 {"cursor": "2", "from-model": None, "page-size": None}, # params
285 )
287 @patch(
288 "canonicalwebteam.store_api.publishergw.PublisherGW"
289 + ".get_remodel_allowlist"
290 )
291 def test_get_remodel_allowlist_with_from_model_parameter(
292 self, mock_get_remodel_allowlist
293 ):
294 mock_get_remodel_allowlist.return_value = {"allowlist": []}
296 response = self.client.get(
297 "/api/store/1/models/remodel-allowlist?from-model=test-model"
298 )
299 data = response.json
301 self.assertEqual(response.status_code, 200)
302 self.assertTrue(data["success"])
303 mock_get_remodel_allowlist.assert_called_once_with(
304 mock_get_remodel_allowlist.call_args[0][0], # flask.session
305 "1", # store_id
306 {"cursor": None, "from-model": "test-model", "page-size": None},
307 )
309 @patch(
310 "canonicalwebteam.store_api.publishergw.PublisherGW"
311 + ".get_remodel_allowlist"
312 )
313 def test_get_remodel_allowlist_with_page_size_parameter(
314 self, mock_get_remodel_allowlist
315 ):
316 mock_get_remodel_allowlist.return_value = {"allowlist": []}
318 response = self.client.get(
319 "/api/store/1/models/remodel-allowlist?page-size=50"
320 )
321 data = response.json
323 self.assertEqual(response.status_code, 200)
324 self.assertTrue(data["success"])
325 mock_get_remodel_allowlist.assert_called_once_with(
326 mock_get_remodel_allowlist.call_args[0][0], # flask.session
327 "1", # store_id
328 {"cursor": None, "from-model": None, "page-size": "50"},
329 )
331 @patch(
332 "canonicalwebteam.store_api.publishergw.PublisherGW"
333 + ".get_remodel_allowlist"
334 )
335 def test_get_remodel_allowlist_with_multiple_parameters(
336 self, mock_get_remodel_allowlist
337 ):
338 mock_get_remodel_allowlist.return_value = {"allowlist": []}
340 response = self.client.get(
341 "/api/store/1/models/remodel-allowlist"
342 "?page=3&from-model=ubuntu-core&page-size=25"
343 )
344 data = response.json
346 self.assertEqual(response.status_code, 200)
347 self.assertTrue(data["success"])
348 mock_get_remodel_allowlist.assert_called_once_with(
349 mock_get_remodel_allowlist.call_args[0][0], # flask.session
350 "1", # store_id
351 {
352 "cursor": "3",
353 "from-model": "ubuntu-core",
354 "page-size": "25",
355 },
356 )
358 @patch(
359 "canonicalwebteam.store_api.publishergw.PublisherGW"
360 + ".get_remodel_allowlist"
361 )
362 def test_get_remodel_allowlist_with_no_parameters(
363 self, mock_get_remodel_allowlist
364 ):
365 mock_get_remodel_allowlist.return_value = {"allowlist": []}
367 response = self.client.get("/api/store/1/models/remodel-allowlist")
368 data = response.json
370 self.assertEqual(response.status_code, 200)
371 self.assertTrue(data["success"])
372 mock_get_remodel_allowlist.assert_called_once_with(
373 mock_get_remodel_allowlist.call_args[0][0], # flask.session
374 "1", # store_id
375 {"cursor": None, "from-model": None, "page-size": None},
376 )
379class TestCreateRemodelAllowlist(TestModelServiceEndpoints):
380 @patch(
381 "canonicalwebteam.store_api.publishergw.PublisherGW"
382 + ".create_remodel_allowlist"
383 )
384 def test_create_remodel_allowlist_success(
385 self, mock_create_remodel_allowlist
386 ):
387 mock_create_remodel_allowlist.return_value = None
389 payload = {
390 "description": "Test remodel allowlist",
391 "from-model": "test-from-model",
392 "from-serial": "test-from-serial",
393 "to-model": "test-to-model",
394 }
396 response = self.client.post(
397 "/api/store/1/models/remodel-allowlist", json=payload
398 )
399 data = response.json
401 self.assertEqual(response.status_code, 201)
402 self.assertTrue(data["success"])
403 mock_create_remodel_allowlist.assert_called_once()
405 @patch(
406 "canonicalwebteam.store_api.publishergw.PublisherGW"
407 + ".create_remodel_allowlist"
408 )
409 def test_create_remodel_allowlist_store_not_found(
410 self, mock_create_remodel_allowlist
411 ):
412 mock_create_remodel_allowlist.side_effect = StoreApiResponseErrorList(
413 "Store not found", 404, [{"message": "Store not found"}]
414 )
416 payload = {
417 "description": "Test remodel allowlist",
418 "from-model": "test-from-model",
419 "to-model": "test-to-model",
420 }
422 response = self.client.post(
423 "/api/store/999/models/remodel-allowlist", json=payload
424 )
425 data = response.json
427 self.assertEqual(response.status_code, 404)
428 self.assertFalse(data["success"])
429 self.assertEqual(data["message"], "Store not found")
431 @patch(
432 "canonicalwebteam.store_api.publishergw.PublisherGW"
433 + ".create_remodel_allowlist"
434 )
435 def test_create_remodel_allowlist_general_error(
436 self, mock_create_remodel_allowlist
437 ):
438 mock_create_remodel_allowlist.side_effect = StoreApiResponseErrorList(
439 "Internal server error",
440 500,
441 [{"message": "An error occurred"}],
442 )
444 payload = {
445 "description": "Test remodel allowlist",
446 "from-model": "test-from-model",
447 "to-model": "test-to-model",
448 }
450 response = self.client.post(
451 "/api/store/999/models/remodel-allowlist", json=payload
452 )
453 data = response.json
455 self.assertEqual(response.status_code, 500)
456 self.assertFalse(data["success"])
457 self.assertEqual(data["message"], "An error occurred")
460class TestUpdateRemodelAllowlist(TestModelServiceEndpoints):
461 @patch(
462 "canonicalwebteam.store_api.publishergw.PublisherGW"
463 + ".update_remodel_allowlist"
464 )
465 def test_update_remodel_allowlist_success(
466 self, mock_update_remodel_allowlist
467 ):
468 mock_update_remodel_allowlist.return_value = None
470 payload = {
471 "description": "Updated remodel allowlist",
472 "from-model": "updated-from-model",
473 "from-serial": "updated-from-serial",
474 "to-model": "updated-to-model",
475 }
477 response = self.client.patch(
478 "/api/store/1/models/remodel-allowlist", json=payload
479 )
480 data = response.json
482 self.assertEqual(response.status_code, 200)
483 self.assertTrue(data["success"])
484 # Verify dict payload was wrapped in a list
485 mock_update_remodel_allowlist.assert_called_once_with(
486 mock_update_remodel_allowlist.call_args[0][0], # flask.session
487 "1", # store_id
488 [payload], # Dict should be wrapped in a list
489 )
491 @patch(
492 "canonicalwebteam.store_api.publishergw.PublisherGW"
493 + ".update_remodel_allowlist"
494 )
495 def test_update_remodel_allowlist_store_not_found(
496 self, mock_update_remodel_allowlist
497 ):
498 mock_update_remodel_allowlist.side_effect = StoreApiResponseErrorList(
499 "Store not found", 404, [{"message": "Store not found"}]
500 )
502 payload = {
503 "description": "Updated remodel allowlist",
504 "from-model": "updated-from-model",
505 "to-model": "updated-to-model",
506 }
508 response = self.client.patch(
509 "/api/store/999/models/remodel-allowlist", json=payload
510 )
511 data = response.json
513 self.assertEqual(response.status_code, 404)
514 self.assertFalse(data["success"])
515 self.assertEqual(data["message"], "Store not found")
517 @patch(
518 "canonicalwebteam.store_api.publishergw.PublisherGW"
519 + ".update_remodel_allowlist"
520 )
521 def test_update_remodel_allowlist_models_not_found(
522 self, mock_update_remodel_allowlist
523 ):
524 mock_update_remodel_allowlist.side_effect = StoreApiResourceNotFound(
525 "Remodel allowlist not found",
526 404,
527 [{"message": "Remodel allowlist not found"}],
528 )
530 payload = {
531 "description": "Updated remodel allowlist",
532 "from-model": "updated-from-model",
533 "to-model": "updated-to-model",
534 }
536 response = self.client.patch(
537 "/api/store/1/models/remodel-allowlist", json=payload
538 )
539 data = response.json
541 self.assertEqual(response.status_code, 404)
542 self.assertFalse(data["success"])
543 self.assertEqual(data["message"], "Remodel allowlist not found")
545 @patch(
546 "canonicalwebteam.store_api.publishergw.PublisherGW"
547 + ".update_remodel_allowlist"
548 )
549 def test_update_remodel_allowlist_api_error(
550 self, mock_update_remodel_allowlist
551 ):
552 mock_update_remodel_allowlist.side_effect = StoreApiResponseErrorList(
553 "Internal server error",
554 500,
555 [{"message": "An error occurred"}],
556 )
558 payload = {
559 "description": "Updated remodel allowlist",
560 "from-model": "updated-from-model",
561 "to-model": "updated-to-model",
562 }
564 response = self.client.patch(
565 "/api/store/1/models/remodel-allowlist", json=payload
566 )
567 data = response.json
569 self.assertEqual(response.status_code, 500)
570 self.assertFalse(data["success"])
571 self.assertEqual(data["message"], "An error occurred")
573 @patch(
574 "canonicalwebteam.store_api.publishergw.PublisherGW"
575 + ".update_remodel_allowlist"
576 )
577 def test_update_remodel_allowlist_general_exception(
578 self, mock_update_remodel_allowlist
579 ):
580 mock_update_remodel_allowlist.side_effect = Exception(
581 "Unexpected error"
582 )
584 payload = {
585 "description": "Updated remodel allowlist",
586 "from-model": "updated-from-model",
587 "to-model": "updated-to-model",
588 "from-serial": "test-serial",
589 }
591 response = self.client.patch(
592 "/api/store/1/models/remodel-allowlist", json=payload
593 )
594 data = response.json
596 self.assertEqual(response.status_code, 500)
597 self.assertFalse(data["success"])
598 self.assertEqual(data["message"], "An error occurred")
600 def test_update_remodel_allowlist_missing_json_payload(self):
601 """Test update remodel allowlist with missing JSON payload."""
602 response = self.client.patch(
603 "/api/store/1/models/remodel-allowlist",
604 headers={"Content-Type": "application/json"},
605 data="",
606 )
607 data = response.json
609 self.assertEqual(response.status_code, 400)
610 self.assertFalse(data["success"])
611 self.assertEqual(data["message"], "Missing or invalid JSON payload")
613 def test_update_remodel_allowlist_invalid_json_payload(self):
614 """Test update remodel allowlist with invalid JSON payload."""
615 response = self.client.patch(
616 "/api/store/1/models/remodel-allowlist",
617 headers={"Content-Type": "application/json"},
618 data="{invalid json}",
619 )
620 data = response.json
622 self.assertEqual(response.status_code, 400)
623 self.assertFalse(data["success"])
624 self.assertEqual(data["message"], "Missing or invalid JSON payload")
626 def test_update_remodel_allowlist_no_content_type(self):
627 """Test update remodel allowlist without content-type header."""
628 response = self.client.patch(
629 "/api/store/1/models/remodel-allowlist", data='{"test": "data"}'
630 )
631 data = response.json
633 self.assertEqual(response.status_code, 400)
634 self.assertFalse(data["success"])
635 self.assertEqual(data["message"], "Missing or invalid JSON payload")
637 @patch(
638 "canonicalwebteam.store_api.publishergw.PublisherGW"
639 + ".update_remodel_allowlist"
640 )
641 def test_update_remodel_allowlist_with_list_payload(
642 self, mock_update_remodel_allowlist
643 ):
644 mock_update_remodel_allowlist.return_value = None
646 payload = [
647 {
648 "description": "Updated remodel allowlist 1",
649 "from-model": "updated-from-model-1",
650 "from-serial": "updated-from-serial-1",
651 "to-model": "updated-to-model-1",
652 },
653 {
654 "description": "Updated remodel allowlist 2",
655 "from-model": "updated-from-model-2",
656 "from-serial": "updated-from-serial-2",
657 "to-model": "updated-to-model-2",
658 },
659 ]
661 response = self.client.patch(
662 "/api/store/1/models/remodel-allowlist", json=payload
663 )
664 data = response.json
666 self.assertEqual(response.status_code, 200)
667 self.assertTrue(data["success"])
668 # Verify the list was passed as-is, not wrapped in another list
669 mock_update_remodel_allowlist.assert_called_once_with(
670 mock_update_remodel_allowlist.call_args[0][0], # flask.session
671 "1", # store_id
672 payload, # Should be the original list, not [payload]
673 )
676class TestDeleteRemodelAllowlist(TestModelServiceEndpoints):
677 @patch(
678 "canonicalwebteam.store_api.publishergw.PublisherGW"
679 + ".delete_remodel_allowlist"
680 )
681 def test_delete_remodel_allowlist_success(
682 self, mock_delete_remodel_allowlist
683 ):
684 mock_delete_remodel_allowlist.return_value = None
686 payload = {
687 "from-model": "test-from-model",
688 "from-serial": "test-from-serial",
689 "to-model": "test-to-model",
690 }
692 response = self.client.delete(
693 "/api/store/1/models/remodel-allowlist", json=payload
694 )
695 data = response.json
697 self.assertEqual(response.status_code, 200)
698 self.assertTrue(data["success"])
699 # Verify dict payload was wrapped in a list
700 mock_delete_remodel_allowlist.assert_called_once_with(
701 mock_delete_remodel_allowlist.call_args[0][0], # flask.session
702 "1", # store_id
703 [payload], # Dict should be wrapped in a list
704 )
706 @patch(
707 "canonicalwebteam.store_api.publishergw.PublisherGW"
708 + ".delete_remodel_allowlist"
709 )
710 def test_delete_remodel_allowlist_store_not_found(
711 self, mock_delete_remodel_allowlist
712 ):
713 mock_delete_remodel_allowlist.side_effect = StoreApiResponseErrorList(
714 "Store not found", 404, [{"message": "Store not found"}]
715 )
717 payload = {
718 "from-model": "test-from-model",
719 "from-serial": "test-from-serial",
720 "to-model": "test-to-model",
721 }
723 response = self.client.delete(
724 "/api/store/999/models/remodel-allowlist", json=payload
725 )
726 data = response.json
728 self.assertEqual(response.status_code, 404)
729 self.assertFalse(data["success"])
730 self.assertEqual(data["message"], "Store not found")
732 @patch(
733 "canonicalwebteam.store_api.publishergw.PublisherGW"
734 + ".delete_remodel_allowlist"
735 )
736 def test_delete_remodel_allowlist_allowlist_not_found(
737 self, mock_delete_remodel_allowlist
738 ):
739 mock_delete_remodel_allowlist.side_effect = StoreApiResourceNotFound(
740 "Remodel allowlist not found",
741 404,
742 [{"message": "Remodel allowlist not found"}],
743 )
745 payload = {
746 "from-model": "test-from-model",
747 "from-serial": "test-from-serial",
748 "to-model": "test-to-model",
749 }
751 response = self.client.delete(
752 "/api/store/1/models/remodel-allowlist", json=payload
753 )
754 data = response.json
756 self.assertEqual(response.status_code, 404)
757 self.assertFalse(data["success"])
758 self.assertEqual(data["message"], "Remodel allowlist not found")
760 @patch(
761 "canonicalwebteam.store_api.publishergw.PublisherGW"
762 + ".delete_remodel_allowlist"
763 )
764 def test_delete_remodel_allowlist_api_error(
765 self, mock_delete_remodel_allowlist
766 ):
767 mock_delete_remodel_allowlist.side_effect = StoreApiResponseErrorList(
768 "Internal server error",
769 500,
770 [{"message": "An error occurred"}],
771 )
773 payload = {
774 "from-model": "test-from-model",
775 "from-serial": "test-from-serial",
776 "to-model": "test-to-model",
777 }
779 response = self.client.delete(
780 "/api/store/1/models/remodel-allowlist", json=payload
781 )
782 data = response.json
784 self.assertEqual(response.status_code, 500)
785 self.assertFalse(data["success"])
786 self.assertEqual(data["message"], "An error occurred")
788 @patch(
789 "canonicalwebteam.store_api.publishergw.PublisherGW"
790 + ".delete_remodel_allowlist"
791 )
792 def test_delete_remodel_allowlist_general_exception(
793 self, mock_delete_remodel_allowlist
794 ):
795 mock_delete_remodel_allowlist.side_effect = Exception(
796 "Unexpected error"
797 )
799 payload = {
800 "from-model": "test-from-model",
801 "from-serial": "test-serial",
802 "to-model": "test-to-model",
803 }
805 response = self.client.delete(
806 "/api/store/1/models/remodel-allowlist", json=payload
807 )
808 data = response.json
810 self.assertEqual(response.status_code, 500)
811 self.assertFalse(data["success"])
812 self.assertEqual(data["message"], "An error occurred")
814 def test_delete_remodel_allowlist_missing_json_payload(self):
815 """Test delete remodel allowlist with missing JSON payload."""
816 response = self.client.delete(
817 "/api/store/1/models/remodel-allowlist",
818 headers={"Content-Type": "application/json"},
819 data="",
820 )
821 data = response.json
823 self.assertEqual(response.status_code, 400)
824 self.assertFalse(data["success"])
825 self.assertEqual(data["message"], "Missing or invalid JSON payload")
827 def test_delete_remodel_allowlist_invalid_json_payload(self):
828 """Test delete remodel allowlist with invalid JSON payload."""
829 response = self.client.delete(
830 "/api/store/1/models/remodel-allowlist",
831 headers={"Content-Type": "application/json"},
832 data="{invalid json}",
833 )
834 data = response.json
836 self.assertEqual(response.status_code, 400)
837 self.assertFalse(data["success"])
838 self.assertEqual(data["message"], "Missing or invalid JSON payload")
840 def test_delete_remodel_allowlist_no_content_type(self):
841 """Test delete remodel allowlist without content-type header."""
842 response = self.client.delete(
843 "/api/store/1/models/remodel-allowlist", data='{"test": "data"}'
844 )
845 data = response.json
847 self.assertEqual(response.status_code, 400)
848 self.assertFalse(data["success"])
849 self.assertEqual(data["message"], "Missing or invalid JSON payload")
851 @patch(
852 "canonicalwebteam.store_api.publishergw.PublisherGW"
853 + ".delete_remodel_allowlist"
854 )
855 def test_delete_remodel_allowlist_with_list_payload(
856 self, mock_delete_remodel_allowlist
857 ):
858 mock_delete_remodel_allowlist.return_value = None
860 payload = [
861 {
862 "from-model": "test-from-model-1",
863 "from-serial": "test-from-serial-1",
864 "to-model": "test-to-model-1",
865 },
866 {
867 "from-model": "test-from-model-2",
868 "from-serial": "test-from-serial-2",
869 "to-model": "test-to-model-2",
870 },
871 ]
873 response = self.client.delete(
874 "/api/store/1/models/remodel-allowlist", json=payload
875 )
876 data = response.json
878 self.assertEqual(response.status_code, 200)
879 self.assertTrue(data["success"])
880 # Verify the list was passed as-is, not wrapped in another list
881 mock_delete_remodel_allowlist.assert_called_once_with(
882 mock_delete_remodel_allowlist.call_args[0][0], # flask.session
883 "1", # store_id
884 payload, # Should be the original list, not [payload]
885 )
888class TestGetSerialLog(TestModelServiceEndpoints):
889 @patch(
890 "canonicalwebteam.store_api.publishergw.PublisherGW"
891 + ".get_store_model_serial_logs"
892 )
893 def test_get_serial_log_success(self, mock_get_serial_logs):
894 mock_serial_logs = {
895 "items": [
896 {
897 "brand-id": "test-brand-id",
898 "created-at": "2026-03-27T14:34:23.666Z",
899 "model-name": "test-model",
900 "serial": "test-serial",
901 }
902 ]
903 }
904 mock_get_serial_logs.return_value = mock_serial_logs
906 response = self.client.get(
907 "/api/store/test-store-id/models/test-model/serial-log"
908 )
909 data = response.json
911 self.assertEqual(response.status_code, 200)
912 self.assertTrue(data["success"])
913 self.assertEqual(data["data"], mock_serial_logs)
915 @patch(
916 "canonicalwebteam.store_api.publishergw.PublisherGW"
917 + ".get_store_model_serial_logs"
918 )
919 def test_get_serial_log_empty(self, mock_get_serial_logs):
920 mock_get_serial_logs.return_value = {"items": []}
922 response = self.client.get(
923 "/api/store/test-store-id/models/test-model/serial-log"
924 )
925 data = response.json
927 self.assertEqual(response.status_code, 200)
928 self.assertTrue(data["success"])
929 self.assertEqual(data["data"]["items"], [])
931 @patch(
932 "canonicalwebteam.store_api.publishergw.PublisherGW"
933 + ".get_store_model_serial_logs"
934 )
935 def test_get_serial_log_unauthorized(self, mock_get_serial_logs):
936 mock_get_serial_logs.side_effect = StoreApiResponseErrorList(
937 "unauthorized", 401, [{"message": "unauthorized"}]
938 )
940 response = self.client.get(
941 "/api/store/test-store-id/models/test-model/serial-log"
942 )
943 data = response.json
945 self.assertEqual(response.status_code, 500)
946 self.assertFalse(data["success"])
947 self.assertEqual(data["message"], "Store not found")
949 @patch(
950 "canonicalwebteam.store_api.publishergw.PublisherGW"
951 + ".get_store_model_serial_logs"
952 )
953 def test_get_serial_log_store_not_found(self, mock_get_serial_logs):
954 mock_get_serial_logs.side_effect = StoreApiResponseErrorList(
955 "Store not found", 404, [{"message": "Store not found"}]
956 )
958 response = self.client.get(
959 "/api/store/999/models/test-model/serial-log"
960 )
961 data = response.json
963 self.assertEqual(response.status_code, 500)
964 self.assertFalse(data["success"])
965 self.assertEqual(data["message"], "Store not found")
967 @patch(
968 "canonicalwebteam.store_api.publishergw.PublisherGW"
969 + ".get_store_model_serial_logs"
970 )
971 def test_get_serial_log_general_error(self, mock_get_serial_logs):
972 mock_get_serial_logs.side_effect = StoreApiResponseErrorList(
973 "Internal server error",
974 500,
975 [{"message": "Internal server error"}],
976 )
978 response = self.client.get(
979 "/api/store/test-store-id/models/test-model/serial-log"
980 )
981 data = response.json
983 self.assertEqual(response.status_code, 500)
984 self.assertFalse(data["success"])
985 self.assertEqual(data["message"], "Internal server error")
987 @patch(
988 "canonicalwebteam.store_api.publishergw.PublisherGW"
989 + ".get_store_model_serial_logs"
990 )
991 def test_get_serial_log_with_page_parameter(self, mock_get_serial_logs):
992 mock_get_serial_logs.return_value = {"items": []}
994 response = self.client.get(
995 "/api/store/test-store-id/models/test-model/serial-log?"
996 "page=page-cursor"
997 )
998 data = response.json
1000 self.assertEqual(response.status_code, 200)
1001 self.assertTrue(data["success"])
1002 mock_get_serial_logs.assert_called_once_with(
1003 mock_get_serial_logs.call_args[0][0],
1004 "test-store-id",
1005 "test-model",
1006 {
1007 "cursor": "page-cursor",
1008 "start_time": None,
1009 "end_time": None,
1010 "page_size": None,
1011 },
1012 )
1014 @patch(
1015 "canonicalwebteam.store_api.publishergw.PublisherGW"
1016 + ".get_store_model_serial_logs"
1017 )
1018 def test_get_serial_log_with_start_and_end_time_parameter(
1019 self, mock_get_serial_logs
1020 ):
1021 mock_get_serial_logs.return_value = {"items": []}
1023 response = self.client.get(
1024 "/api/store/test-store-id/models/test-model/serial-log?"
1025 "start-time=2026-04-01T23:59:59Z&"
1026 "end-time=2026-04-30T23:59:59Z"
1027 )
1028 data = response.json
1030 self.assertEqual(response.status_code, 200)
1031 self.assertTrue(data["success"])
1032 mock_get_serial_logs.assert_called_once_with(
1033 mock_get_serial_logs.call_args[0][0],
1034 "test-store-id",
1035 "test-model",
1036 {
1037 "cursor": None,
1038 "start_time": "2026-04-01T23:59:59Z",
1039 "end_time": "2026-04-30T23:59:59Z",
1040 "page_size": None,
1041 },
1042 )
1044 @patch(
1045 "canonicalwebteam.store_api.publishergw"
1046 ".PublisherGW.get_store_model_serial_logs"
1047 )
1048 def test_get_serial_log_with_page_size_parameter(
1049 self, mock_get_serial_logs
1050 ):
1051 mock_get_serial_logs.return_value = {"items": []}
1053 response = self.client.get(
1054 "/api/store/test-store-id/models/test-model/serial-log?"
1055 "page-size=25"
1056 )
1057 data = response.json
1059 self.assertEqual(response.status_code, 200)
1060 self.assertTrue(data["success"])
1061 mock_get_serial_logs.assert_called_once_with(
1062 mock_get_serial_logs.call_args[0][0],
1063 "test-store-id",
1064 "test-model",
1065 {
1066 "cursor": None,
1067 "start_time": None,
1068 "end_time": None,
1069 "page_size": "25",
1070 },
1071 )
1073 @patch(
1074 "canonicalwebteam.store_api.publishergw.PublisherGW"
1075 + ".get_store_model_serial_logs"
1076 )
1077 def test_get_serial_log_with_multiple_parameters(
1078 self, mock_get_serial_logs
1079 ):
1080 mock_get_serial_logs.return_value = {"items": []}
1082 response = self.client.get(
1083 "/api/store/test-store-id/models/test-model/serial-log"
1084 "?page=page-cursor&start-time=2026-04-01T00:00:00Z&"
1085 "end-time=2026-04-30T23:59:59Z&page-size=25"
1086 )
1087 data = response.json
1089 self.assertEqual(response.status_code, 200)
1090 self.assertTrue(data["success"])
1091 mock_get_serial_logs.assert_called_once_with(
1092 mock_get_serial_logs.call_args[0][0],
1093 "test-store-id",
1094 "test-model",
1095 {
1096 "cursor": "page-cursor",
1097 "start_time": "2026-04-01T00:00:00Z",
1098 "end_time": "2026-04-30T23:59:59Z",
1099 "page_size": "25",
1100 },
1101 )
1103 @patch(
1104 "canonicalwebteam.store_api.publishergw.PublisherGW"
1105 + ".get_store_model_serial_logs"
1106 )
1107 def test_get_serial_log_with_no_parameters(self, mock_get_serial_logs):
1108 mock_get_serial_logs.return_value = {"items": []}
1110 response = self.client.get(
1111 "/api/store/test-store-id/models/test-model/serial-log"
1112 )
1113 data = response.json
1115 self.assertEqual(response.status_code, 200)
1116 self.assertTrue(data["success"])
1117 mock_get_serial_logs.assert_called_once_with(
1118 mock_get_serial_logs.call_args[0][0],
1119 "test-store-id",
1120 "test-model",
1121 {
1122 "cursor": None,
1123 "start_time": None,
1124 "end_time": None,
1125 "page_size": None,
1126 },
1127 )