|
1 | 1 | """Test data for the authorization API.""" |
2 | 2 |
|
| 3 | +from unittest.mock import Mock, patch |
| 4 | + |
3 | 5 | from ddt import data, ddt, unpack |
4 | 6 | from django.test import TestCase |
| 7 | +from opaque_keys.edx.locator import LibraryLocatorV2 |
5 | 8 |
|
6 | 9 | from openedx_authz.api.data import ( |
7 | 10 | ActionData, |
@@ -507,3 +510,111 @@ def test_role_assignment_data_repr(self): |
507 | 510 |
|
508 | 511 | expected_repr = "user^john_doe => [role^instructor, role^library_admin] @ lib^lib:DemoX:CSPROB" |
509 | 512 | self.assertEqual(actual_repr, expected_repr) |
| 513 | + |
| 514 | + |
| 515 | +@ddt |
| 516 | +class TestContentLibraryData(TestCase): |
| 517 | + """Test the ContentLibraryData class.""" |
| 518 | + |
| 519 | + @patch("openedx_authz.api.data.ContentLibrary") |
| 520 | + def test_get_object_success(self, mock_content_library_model): |
| 521 | + """Test get_object returns ContentLibrary when it exists with valid key. |
| 522 | +
|
| 523 | + Expected Result: |
| 524 | + - Returns the ContentLibrary object when library exists |
| 525 | + - Library key matches exactly (canonical validation passes) |
| 526 | + """ |
| 527 | + library_id = "lib:DemoX:CSPROB" |
| 528 | + library_scope = ContentLibraryData(external_key=library_id) |
| 529 | + mock_library_obj = Mock() |
| 530 | + mock_library_obj.library_key = library_scope.library_key |
| 531 | + mock_content_library_model.objects.get_by_key.return_value = mock_library_obj |
| 532 | + |
| 533 | + result = library_scope.get_object() |
| 534 | + |
| 535 | + self.assertEqual(result, mock_library_obj) |
| 536 | + mock_content_library_model.objects.get_by_key.assert_called_once_with(library_scope.library_key) |
| 537 | + |
| 538 | + @patch("openedx_authz.api.data.ContentLibrary") |
| 539 | + def test_get_object_does_not_exist(self, mock_content_library_model): |
| 540 | + """Test get_object returns None when library does not exist. |
| 541 | +
|
| 542 | + Expected Result: |
| 543 | + - Returns None when ContentLibrary.DoesNotExist is raised |
| 544 | + """ |
| 545 | + library_id = "lib:DemoX:NonExistent" |
| 546 | + library_scope = ContentLibraryData(external_key=library_id) |
| 547 | + mock_content_library_model.DoesNotExist = Exception |
| 548 | + mock_content_library_model.objects.get_by_key.side_effect = mock_content_library_model.DoesNotExist |
| 549 | + |
| 550 | + result = library_scope.get_object() |
| 551 | + |
| 552 | + self.assertIsNone(result) |
| 553 | + |
| 554 | + @patch("openedx_authz.api.data.ContentLibrary") |
| 555 | + def test_get_object_invalid_key_format(self, mock_content_library_model): |
| 556 | + """Test get_object returns None when library_id has invalid format. |
| 557 | +
|
| 558 | + Expected Result: |
| 559 | + - Returns None when InvalidKeyError is raised during key parsing |
| 560 | + """ |
| 561 | + mock_content_library_model.DoesNotExist = Exception |
| 562 | + library_scope = ContentLibraryData(external_key="invalid-library-format") |
| 563 | + |
| 564 | + result = library_scope.get_object() |
| 565 | + |
| 566 | + self.assertIsNone(result) |
| 567 | + mock_content_library_model.objects.get_by_key.assert_not_called() |
| 568 | + |
| 569 | + @patch("openedx_authz.api.data.ContentLibrary") |
| 570 | + def test_get_object_non_canonical_key(self, mock_content_library_model): |
| 571 | + """Test get_object returns None when library key is not canonical. |
| 572 | +
|
| 573 | + This test verifies the canonical key validation: get_by_key is case-insensitive, |
| 574 | + but we require exact match to ensure authorization uses canonical library IDs. |
| 575 | +
|
| 576 | + Expected Result: |
| 577 | + - Returns None when retrieved library's key doesn't match exactly |
| 578 | + - Simulates case where user provides 'lib:demox:csprob' but canonical is 'lib:DemoX:CSPROB' |
| 579 | + """ |
| 580 | + library_id = "lib:DemoX:CSPROB" |
| 581 | + library_key = LibraryLocatorV2.from_string(library_id) |
| 582 | + # Convert to lowercase to simulate case-insensitive comparison |
| 583 | + library_scope = ContentLibraryData(external_key=library_id.lower()) |
| 584 | + mock_content_library_model.objects.get_by_key.return_value = Mock(library_key=library_key) |
| 585 | + mock_content_library_model.DoesNotExist = Exception |
| 586 | + |
| 587 | + result = library_scope.get_object() |
| 588 | + |
| 589 | + self.assertIsNone(result) |
| 590 | + |
| 591 | + @patch("openedx_authz.api.data.ContentLibrary") |
| 592 | + def test_exists_returns_true_when_library_exists(self, mock_content_library_model): |
| 593 | + """Test exists() returns True when get_object() returns a library. |
| 594 | +
|
| 595 | + Expected Result: |
| 596 | + - exists() returns True when library object is found |
| 597 | + """ |
| 598 | + library_id = "lib:DemoX:CSPROB" |
| 599 | + library_scope = ContentLibraryData(external_key=library_id) |
| 600 | + mock_content_library_model.objects.get_by_key.return_value = Mock(library_key=library_scope.library_key) |
| 601 | + |
| 602 | + result = library_scope.exists() |
| 603 | + |
| 604 | + self.assertTrue(result) |
| 605 | + |
| 606 | + @patch("openedx_authz.api.data.ContentLibrary") |
| 607 | + def test_exists_returns_false_when_library_does_not_exist(self, mock_content_library_model): |
| 608 | + """Test exists() returns False when get_object() returns None. |
| 609 | +
|
| 610 | + Expected Result: |
| 611 | + - exists() returns False when library is not found |
| 612 | + """ |
| 613 | + library_id = "lib:DemoX:NonExistent" |
| 614 | + library_scope = ContentLibraryData(external_key=library_id) |
| 615 | + mock_content_library_model.DoesNotExist = Exception |
| 616 | + mock_content_library_model.objects.get_by_key.side_effect = mock_content_library_model.DoesNotExist |
| 617 | + |
| 618 | + result = library_scope.exists() |
| 619 | + |
| 620 | + self.assertFalse(result) |
0 commit comments