Skip to content
Snippets Groups Projects
Select Git revision
  • 7d19efc219f0e8638b4cc6ded8b104292c047efd
  • master default protected
  • forbid-save-as
  • upload-via-token
  • moodle-integration
  • patch-double-tap-seek
  • patch_datum_anzeigen
  • patch_raum_anzeigen
  • intros
  • live_sources
  • bootstrap4
  • modules
12 results

db_example.sql

Blame
  • Forked from Video AG Infrastruktur / website
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    object_modifications.py 68.95 KiB
    from api_test import ApiTest
    
    from api.miscellaneous.constants import *
    
    
    class ObjectModificationsTest(ApiTest):
        
        def assert_valid_field_description(self, description):
            self.assert_contains(description, "id")
            self.assert_contains(description, "type")
            self.assert_contains(["int", "long", "boolean", "string", "semester_string", "duration", "datetime",
                                  "timestamp", "announcement_type", "announcement_visibility", "featured_type",
                                  "permission_type", "course_filter_criteria", "lecture_id" "user_id", "user_id_list",
                                  "course_id_string", "legacy_announcement_type", "view_permissions"],
                                 description["type"])
        
        def assert_valid_field_value(self, field):
            self.assert_contains(field, "description")
            self.assert_valid_field_description(field["description"])
        
        def assert_valid_config(self, in_creation: bool, config):
            self.assert_contains(config, "fields")
            for field in config["fields"]:
                if in_creation:
                    self.assert_valid_field_description(field)
                else:
                    self.assert_valid_field_value(field)
        
        def get_field_value(self, config: dict, field_id: str):
            self.assert_contains(config, "fields")
            fields = config["fields"]
            for field in fields:
                self.assert_contains(field, "value")
                self.assert_contains(field, "description")
                description = field["description"]
                self.assert_contains(description, "id")
                if description["id"] != field_id:
                    continue
                return field["value"]
            else:
                raise AssertionError(f"Missing field {field_id} in {config}")
        
        def assert_field_has_value(self, config: dict, field_id: str, expected_value):
            self.assertEqual(expected_value, self.get_field_value(config, field_id))
        
        def test_get_config(self):
            self.do_json_request(
                "GET",
                f"/object_management/announcement/0/configuration",
                expected_response_code=HTTP_401_UNAUTHORIZED,
                use_moderator_login=False,
            )
            self.do_json_request(
                "GET",
                f"/object_management/announcement/0/configuration",
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True,
            )
            self.do_json_request(
                "GET",
                f"/object_management/announcement/1/configuration",
                expected_response_code=HTTP_401_UNAUTHORIZED,
                use_moderator_login=False,
            )
            status, config = self.do_json_request(
                "GET",
                f"/object_management/announcement/1/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(False, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/featured/1/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(False, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/course/2/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(False, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/lecture/3/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(False, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/permission/5/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(False, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/chapter/3/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(False, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/media_source/186/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(False, config)
        
        def test_patch_config(self):
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {},
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_401_UNAUTHORIZED
            )
            self.do_json_request(
                "POST",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {},
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_405_METHOD_NOT_ALLOWED,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/100/configuration",
                {
                    "updates": {
                        "text": "This announcement does not exist!"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/187/configuration",
                {
                    "updates": {
                        "title": "This lecture is deleted!"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {},
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {},
                    "expected_current_values": {}
                },
                assert_empty_response=True,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "some definitely invalid id": 1
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "text": "Hallo"
                    },
                    "expected_current_values": {
                        "text": "Definitely not the current value"
                    }
                },
                expected_response_code=HTTP_409_CONFLICT,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "text": "Hallo"
                    },
                    "expected_current_values": {
                        "text": "Test Ankündigung"
                    }
                },
                use_moderator_login=True
            )
        
        def test_patch_many(self):
            self.do_json_request(
                "POST",
                "/object_management/modify_many",
                {
                    "objects": []
                },
                expected_response_code=HTTP_401_UNAUTHORIZED
            )
            self.do_json_request(
                "PATCH",
                "/object_management/modify_many",
                {
                    "objects": []
                },
                expected_response_code=HTTP_405_METHOD_NOT_ALLOWED,
                use_moderator_login=True
            )
            self.do_json_request(
                "POST",
                "/object_management/modify_many",
                {
                    "objects": [
                        {
                            "type": "announcement",
                            "id": 3,
                            "updates": {
                                "text": "New text"
                            },
                            "expected_current_values": {
                                "text": "Wrong current text"
                            }
                        },
                        {
                            "type": "featured",
                            "id": 2,
                            "updates": {
                                "text": "New featured text"
                            },
                            "expected_current_values": {
                                "text": ""
                            }
                        }
                    ]
                },
                expected_response_code=HTTP_409_CONFLICT,
                use_moderator_login=True
            )
            self.do_json_request(
                "POST",
                "/object_management/modify_many",
                {
                    "objects": [
                        {
                            "type": "announcement",
                            "id": 3,
                            "updates": {
                                "text": "New text"
                            },
                            "expected_current_values": {
                                "text": "Versteckte Ankündigung"
                            }
                        },
                        {
                            "type": "featured",
                            "id": 2,
                            "updates": {
                                "text": "New featured text"
                            },
                            "expected_current_values": {
                                "text": ""
                            }
                        }
                    ]
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/announcement/3/configuration",
                use_moderator_login=True
            )[1], "text", "New text")
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/featured/2/configuration",
                use_moderator_login=True
            )[1], "text", "New featured text")
        
        def test_patch_string(self):
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "text": 1
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "text": None
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "text": "WA" + ("A" * 42000) + "AY too long text"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "text": "Actually valid text"
                    },
                    "expected_current_values": {}
                },
                assert_empty_response=True,
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/announcement/1/configuration",
                use_moderator_login=True
            )[1], "text", "Actually valid text")
        
        def test_patch_boolean(self):
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "may_be_visible": 1
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "may_be_visible": 0
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "may_be_visible": None
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "may_be_visible": False
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/announcement/1/configuration",
                use_moderator_login=True
            )[1], "may_be_visible", False)
        
        def test_patch_course_id(self):
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "id_string": None
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "id_string": ""
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "id_string": "abc def"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "id_string": "07ws-diskrete"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "id_string": "a/b"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "id_string": "courses"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "id_string": "00ws"
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/course/2/configuration",
                use_moderator_login=True
            )[1], "id_string", "00ws")
        
        def test_patch_semester_string(self):
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "semester": 1
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "semester": "2000"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "semester": None
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "id_string": "2000ws"
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/course/2/configuration",
                use_moderator_login=True
            )[1], "id_string", "2000ws")
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "id_string": "none"
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/course/2/configuration",
                use_moderator_login=True
            )[1], "id_string", "none")
        
        def test_patch_duration(self):
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "duration": "90"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "duration": None
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "duration": -1
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "duration": 10000000000000000000000000000000000000000000
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "duration": 0
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/lecture/29/configuration",
                use_moderator_login=True
            )[1], "duration", 0)
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "duration": 90
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/lecture/29/configuration",
                use_moderator_login=True
            )[1], "duration", 90)
        
        def test_patch_datetime(self):
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "time": None
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "time": 100
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "time": "2000-01-01"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "time": "2000-01-01 01:01:01"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "time": "2000-01-01T01:01:01.0000"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/lecture/29/configuration",
                {
                    "updates": {
                        "time": "2000-01-01T01:01:01"
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/lecture/29/configuration",
                use_moderator_login=True
            )[1], "time", "2000-01-01T01:01:01")
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/3/configuration",
                {
                    "updates": {
                        "publish_time": None
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/announcement/3/configuration",
                use_moderator_login=True
            )[1], "publish_time", None)
        
        def test_patch_timestamp(self):
            self.do_json_request(
                "PATCH",
                "/object_management/chapter/3/configuration",
                {
                    "updates": {
                        "start_time": "90"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/chapter/3/configuration",
                {
                    "updates": {
                        "start_time": None
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/chapter/3/configuration",
                {
                    "updates": {
                        "start_time": -1
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/chapter/3/configuration",
                {
                    "updates": {
                        "start_time": 10000000000000000000000000000000000000000000
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/chapter/3/configuration",
                {
                    "updates": {
                        "start_time": 0
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assertEqual(self.do_json_request(
                "GET",
                "/lecture/26",
                use_moderator_login=True
            )[1]["lecture"]["chapters"][0]["start_time"], 0)
            self.do_json_request(
                "PATCH",
                "/object_management/chapter/3/configuration",
                {
                    "updates": {
                        "start_time": 90
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assertEqual(self.do_json_request(
                "GET",
                "/lecture/26",
                use_moderator_login=True
            )[1]["lecture"]["chapters"][0]["start_time"], 90)
    
        def test_patch_mapped_string(self):
            self.do_json_request(
                "PATCH",
                "/object_management/featured/3/configuration",
                {
                    "updates": {
                        "filter_criteria": None
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/featured/3/configuration",
                {
                    "updates": {
                        "filter_criteria": False
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/featured/3/configuration",
                {
                    "updates": {
                        "filter_criteria": 1
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/featured/3/configuration",
                {
                    "updates": {
                        "filter_criteria": ""
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/featured/3/configuration",
                {
                    "updates": {
                        "filter_criteria": "something wrong"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/featured/3/configuration",
                {
                    "updates": {
                        "filter_criteria": "title"
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/featured/3/configuration",
                use_moderator_login=True
            )[1], "filter_criteria", "title")
            self.do_json_request(
                "PATCH",
                "/object_management/featured/3/configuration",
                {
                    "updates": {
                        "filter_criteria": "topic"
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/featured/3/configuration",
                use_moderator_login=True
            )[1], "filter_criteria", "topic")
        
        def test_patch_lecture_id(self):
            self.do_json_request(
                "PATCH",
                "/object_management/featured/4/configuration",
                {
                    "updates": {
                        "lecture_id": None
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/featured/4/configuration",
                {
                    "updates": {
                        "lecture_id": "3"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/featured/4/configuration",
                {
                    "updates": {
                        "lecture_id": -1
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/featured/4/configuration",
                {
                    "updates": {
                        "lecture_id": 1000
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/featured/4/configuration",
                {
                    "updates": {
                        "lecture_id": 3
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/featured/4/configuration",
                use_moderator_login=True
            )[1], "lecture_id", 3)
        
        def _assert_display_priorities(self, *feature_order: int):
            homepage = self.do_json_request(
                "GET",
                "/homepage",
                None,
                use_moderator_login=True
            )[1]
            features = homepage["featured"]
            if len(features) != len(feature_order):
                raise AssertionError(f"Got {len(features)} featured but expected {len(feature_order)}")
            for id, feature in zip(feature_order, features):
                if feature["id"] != id:
                    raise AssertionError(f"Expected feature with id {id} next but got {feature['id']}")
        
        def test_patch_featured_display_priority(self):
            self._assert_display_priorities(2, 3, 4, 1)
            self.do_json_request(
                "PATCH",
                "/object_management/featured/2/configuration",
                {
                    "updates": {
                        "display_priority": 0
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self._assert_display_priorities(2, 3, 4, 1)
            self.do_json_request(
                "PATCH",
                "/object_management/featured/2/configuration",
                {
                    "updates": {
                        "display_priority": -1
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self._assert_display_priorities(2, 3, 4, 1)
            self.do_json_request(
                "PATCH",
                "/object_management/featured/1/configuration",
                {
                    "updates": {
                        "display_priority": 3
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self._assert_display_priorities(2, 3, 4, 1)
            self.do_json_request(
                "PATCH",
                "/object_management/featured/1/configuration",
                {
                    "updates": {
                        "display_priority": 10
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self._assert_display_priorities(2, 3, 4, 1)
            self.do_json_request(
                "PATCH",
                "/object_management/featured/3/configuration",
                {
                    "updates": {
                        "display_priority": 10
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self._assert_display_priorities(2, 4, 1, 3)
            self.do_json_request(
                "PATCH",
                "/object_management/featured/3/configuration",
                {
                    "updates": {
                        "display_priority": 0
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self._assert_display_priorities(2, 3, 4, 1)
            self.do_json_request(
                "PATCH",
                "/object_management/featured/4/configuration",
                {
                    "updates": {
                        "display_priority": 0
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self._assert_display_priorities(2, 4, 3, 1)
            self.do_json_request(
                "PATCH",
                "/object_management/featured/1/configuration",
                {
                    "updates": {
                        "display_priority": 0
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self._assert_display_priorities(2, 1, 4, 3)
        
        def test_patch_user_list(self):
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "responsible": None
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "responsible": 42
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "responsible": [42, 5000]
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "responsible": [5000]
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "responsible": "42"
                    },
                    "expected_current_values": {}
                },
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "responsible": []
                    },
                    "expected_current_values": {
                        "responsible": []
                    }
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/course/2/configuration",
                use_moderator_login=True
            )[1], "responsible", [])
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "responsible": [42]
                    },
                    "expected_current_values": {
                        "responsible": [100]
                    }
                },
                expected_response_code=HTTP_409_CONFLICT,
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "responsible": [42]
                    },
                    "expected_current_values": {
                        "responsible": []
                    }
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/course/2/configuration",
                use_moderator_login=True
            )[1], "responsible", [42])
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "responsible": [42, 43]
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/course/2/configuration",
                use_moderator_login=True
            )[1], "responsible", [42, 43])
            self.do_json_request(
                "PATCH",
                "/object_management/course/2/configuration",
                {
                    "updates": {
                        "responsible": [42]
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.assert_field_has_value(self.do_json_request(
                "GET",
                "/object_management/course/2/configuration",
                use_moderator_login=True
            )[1], "responsible", [42])
        
        def test_patch_view_permissions(self):
            def test_perm(perm: dict):
                current_course_value = self.get_field_value(self.do_json_request(
                    "GET",
                    "/object_management/course/2/configuration",
                    use_moderator_login=True
                )[1], "view_permissions")
                current_lecture_value = self.get_field_value(self.do_json_request(
                    "GET",
                    "/object_management/lecture/2/configuration",
                    use_moderator_login=True
                )[1], "view_permissions")
                self.do_json_request(
                    "PATCH",
                    "/object_management/lecture/2/configuration",
                    {
                        "updates": {
                            "view_permissions": perm
                        },
                        "expected_current_values": {
                            "view_permissions":  current_lecture_value
                        }
                    },
                    use_moderator_login=True
                )
                self.assert_field_has_value(
                    self.do_json_request(
                        "GET",
                        "/object_management/lecture/2/configuration",
                        use_moderator_login=True
                    )[1],
                    "view_permissions",
                    perm
                )
                self.assert_field_has_value(
                    self.do_json_request(
                        "GET",
                        "/object_management/course/2/configuration",
                        use_moderator_login=True
                    )[1],
                    "view_permissions",
                    current_course_value
                )
            
            test_perm({"type": "public"})
            test_perm({"type": "private"})
            test_perm({"type": "public"})
            test_perm({"type": "inherit"})
            test_perm({"type": "private"})
            test_perm({"type": "inherit"})
            test_perm({"type": "public"})
            test_perm({"type": "authentication",
                       "rwth_authentication": True, "fsmpi_authentication": False,
                       "moodle_course_ids": [], "passwords": {}})
            test_perm({"type": "authentication",
                       "rwth_authentication": True, "fsmpi_authentication": True,
                       "moodle_course_ids": [], "passwords": {}})
            test_perm({"type": "authentication",
                       "rwth_authentication": False, "fsmpi_authentication": True,
                       "moodle_course_ids": [], "passwords": {}})
            test_perm({"type": "authentication",
                       "rwth_authentication": False, "fsmpi_authentication": False,
                       "moodle_course_ids": [7, 8], "passwords": {}})
            test_perm({"type": "authentication",
                       "rwth_authentication": False, "fsmpi_authentication": True,
                       "moodle_course_ids": [8, 9], "passwords": {"admin": "12345678"}})
            test_perm({"type": "private"})
            test_perm({"type": "authentication",
                       "rwth_authentication": False, "fsmpi_authentication": True,
                       "moodle_course_ids": [8, 9], "passwords": {"admin": "12345678"}})
            test_perm({"type": "authentication",
                       "rwth_authentication": True, "fsmpi_authentication": False,
                       "moodle_course_ids": [], "passwords": {"noone": "42"}})
        
        def test_get_create_config(self):
            self.do_json_request(
                "GET",
                f"/object_management/announcement/new/configuration",
                expected_response_code=HTTP_401_UNAUTHORIZED,
                use_moderator_login=False,
            )
            status, config = self.do_json_request(
                "GET",
                f"/object_management/announcement/new/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(True, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/featured/new/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(True, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/course/new/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(True, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/lecture/new/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(True, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/permission/new/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(True, config)
            
            status, config = self.do_json_request(
                "GET",
                f"/object_management/chapter/new/configuration",
                use_moderator_login=True,
            )
            self.assert_valid_config(True, config)
            
            self.do_json_request(
                "GET",
                f"/object_management/media_source/new/configuration",  # Path does not exist
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True,
            )
        
        def test_create(self):
            self.do_json_request(
                "PUT",
                f"/object_management/featured/new",
                expected_response_code=HTTP_401_UNAUTHORIZED,
                use_moderator_login=False,
            )
            self.do_json_request(
                "GET",
                f"/object_management/featured/new",
                expected_response_code=HTTP_405_METHOD_NOT_ALLOWED,
                use_moderator_login=True,
            )
            self.do_json_request(
                "PUT",
                f"/object_management/featured/new/",
                {
                    "values": {
                        "title": "Hallo",
                        "text": "Something",
                        "is_visible": True,
                        "type": "image_url",
                        "lecture_id": 3,
                    }
                },
                expected_response_code=HTTP_400_BAD_REQUEST,  # Missing image_url
                use_moderator_login=True,
            )
            self.do_json_request(
                "PUT",
                f"/object_management/featured/new",
                {
                    "values": {
                        "title": "Hallo",
                        "text": "Something",
                        "is_visible": True,
                        "type": "lecture"
                    }
                },
                expected_response_code=HTTP_400_BAD_REQUEST,  # Missing lecture id
                use_moderator_login=True,
            )
            self.do_json_request(
                "PUT",
                f"/object_management/featured/new",
                {
                    "values": {
                        "title": "Hallo",
                        "text": "Something",
                        "is_visible": True
                    }
                },
                expected_response_code=HTTP_400_BAD_REQUEST,  # Missing type
                use_moderator_login=True,
            )
            id = self.do_json_request(
                "PUT",
                f"/object_management/featured/new",
                {
                    "variant": "lecture",
                    "values": {
                        "title": "Hallo",
                        "text": "Something",
                        "is_visible": True,
                        "lecture_id": 3
                    }
                },
                expected_response_code=HTTP_201_CREATED,
                use_moderator_login=True,
            )[1]["id"]
            config = self.do_json_request(
                "GET",
                f"/object_management/featured/{id}/configuration",
                use_moderator_login=True,
            )[1]
            self.assert_field_has_value(config, "title", "Hallo")
            self.assert_field_has_value(config, "text", "Something")
            self.assert_field_has_value(config, "is_visible", True)
            self.assert_field_has_value(config, "lecture_id", 3)
    
            self.do_json_request(
                "PUT",
                f"/object_management/lecture/new",
                {
                    "values": {
                        "title": "Hallo",
                        "speaker": "Someone",
                        "location": "Somewhere",
                        "time": "2000-01-01T00:00:00",
                        "duration": 90,
                        "description": "Something",
                        "livestream_planned": True,
                        "no_recording": False,
                        "is_visible": True,
                        "internal_comment": False
                    }
                },
                expected_response_code=HTTP_400_BAD_REQUEST,  # Missing parent type and id
                use_moderator_login=True,
            )
            self.do_json_request(
                "PUT",
                f"/object_management/lecture/new",
                {
                    "parent_type": "course",
                    "values": {
                        "title": "Hallo",
                        "speaker": "Someone",
                        "location": "Somewhere",
                        "time": "2000-01-01T00:00:00",
                        "duration": 90,
                        "description": "Something",
                        "livestream_planned": True,
                        "no_recording": False,
                        "is_visible": True,
                        "internal_comment": False
                    }
                },
                expected_response_code=HTTP_400_BAD_REQUEST,  # Missing parent id
                use_moderator_login=True,
            )
            self.do_json_request(
                "PUT",
                f"/object_management/lecture/new",
                {
                    "parent_id": 13,
                    "values": {
                        "title": "Hallo",
                        "speaker": "Someone",
                        "location": "Somewhere",
                        "time": "2000-01-01T00:00:00",
                        "duration": 90,
                        "description": "Something",
                        "livestream_planned": True,
                        "no_recording": False,
                        "is_visible": True,
                        "internal_comment": False
                    }
                },
                expected_response_code=HTTP_400_BAD_REQUEST,  # Missing parent type
                use_moderator_login=True,
            )
            self.do_json_request(
                "PUT",
                f"/object_management/lecture/new",
                {
                    "parent_type": "course",
                    "parent_id": 1000,
                    "values": {
                        "title": "Hallo",
                        "speaker": "Someone",
                        "location": "Somewhere",
                        "time": "2000-01-01T00:00:00",
                        "duration": 90,
                        "description": "Something",
                        "livestream_planned": True,
                        "no_recording": False,
                        "is_visible": True,
                        "internal_comment": False
                    }
                },
                expected_response_code=HTTP_400_BAD_REQUEST,  # Parent does not exist
                use_moderator_login=True,
            )
            id = self.do_json_request(
                "PUT",
                f"/object_management/lecture/new",
                {
                    "parent_type": "course",
                    "parent_id": 13,
                    "values": {
                        "title": "Hallo",
                        "speaker": "Someone",
                        "location": "Somewhere",
                        "time": "2000-01-01T00:00:00",
                        "duration": 90,
                        "description": "Something",
                        "livestream_planned": True,
                        "no_recording": False,
                        "is_visible": True,
                        "internal_comment": "No"
                    }
                },
                expected_response_code=HTTP_201_CREATED,
                use_moderator_login=True,
            )[1]["id"]
            config = self.do_json_request(
                "GET",
                f"/object_management/lecture/{id}/configuration",
                use_moderator_login=True,
            )[1]
            self.assert_field_has_value(config, "title", "Hallo")
            self.assert_field_has_value(config, "description", "Something")
            self.do_json_request(
                "GET",
                f"/lecture/{id}",
                use_moderator_login=True,
            )
            
            id = self.do_json_request(
                "PUT",
                f"/object_management/course/new",
                {
                    "values": {
                        "full_name": "No",
                        "short_name": "N.",
                        "semester": "2042ss",
                        "organizer": "Noone",
                        "description": "Nothing",
                        "is_visible": True,
                        "is_listed": True,
                        "allow_download": True,
                        "show_chapters_on_course": True,
                        "id_string": "42ss-no",
                        "topic": "The Answer",
                        "internal_comment": "Don't know",
                        "responsible": [42, 43]
                    }
                },
                expected_response_code=HTTP_201_CREATED,
                use_moderator_login=True,
            )[1]["id"]
            config = self.do_json_request(
                "GET",
                f"/object_management/course/{id}/configuration",
                use_moderator_login=True,
            )[1]
            self.assert_field_has_value(config, "responsible", [42, 43])
            self.do_json_request(
                "GET",
                f"/course/{id}",
                use_moderator_login=True,
            )
            
            id = self.do_json_request(
                "PUT",
                f"/object_management/course/new",
                {
                    "values": {
                        "full_name": "No",
                        "short_name": "N.",
                        "semester": "2042ss",
                        "organizer": "Noone",
                        "description": "Nothing",
                        "is_visible": True,
                        "is_listed": True,
                        "allow_download": True,
                        "show_chapters_on_course": True,
                        "id_string": "42ss-no-second",
                        "topic": "The Answer",
                        "internal_comment": "Don't know",
                        "responsible": []
                    }
                },
                expected_response_code=HTTP_201_CREATED,
                use_moderator_login=True,
            )[1]["id"]
            config = self.do_json_request(
                "GET",
                f"/object_management/course/{id}/configuration",
                use_moderator_login=True,
            )[1]
            self.assert_field_has_value(config, "responsible", [])
        
        def test_deletion(self):
            self.do_json_request(
                "DELETE",
                f"/object_management/announcement/1",
                expected_response_code=HTTP_401_UNAUTHORIZED,
                use_moderator_login=False,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/course/1",
                expected_response_code=HTTP_401_UNAUTHORIZED,
                use_moderator_login=False,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/announcement/1",
                assert_empty_response=True,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/announcement/10",
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/featured/4",
                assert_empty_response=True,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/lecture/100",  # Does not exist
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/lecture/187",  # Already deleted
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/lecture/1188",
                assert_empty_response=True,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/chapter/10",  # Does not exist
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/chapter/4",  # Already deleted
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/chapter/5",
                assert_empty_response=True,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/permission/10",  # Does not exist
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/permission/6",  # Already deleted
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/permission/19",
                assert_empty_response=True,
                use_moderator_login=True,
            )
            self.do_json_request(
                "DELETE",
                f"/object_management/media_source/204",
                assert_empty_response=True,
                use_moderator_login=True,
            )
            self.do_json_request(
                "POST",
                f"/object_management/media_source/204/resurrect",
                expected_response_code=HTTP_401_UNAUTHORIZED
            )
            self.do_json_request(
                "POST",
                f"/object_management/media_source/12000/resurrect",
                expected_response_code=HTTP_404_NOT_FOUND,
                use_moderator_login=True
            )
            self.do_json_request(
                "POST",
                f"/object_management/media_source/204/resurrect",
                assert_empty_response=True,
                use_moderator_login=True,
            )
            self.do_json_request(
                "POST",
                f"/object_management/media_source/204/resurrect",
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
        
        def test_changelog(self):
            self.do_json_request(
                "GET",
                f"/object_management/changelog",
                None,
                expected_response_code=HTTP_401_UNAUTHORIZED
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog?page=0&entries_per_page=0",
                None,
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog?page=0&entries_per_page=-10",
                None,
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog?page=0&entries_per_page=5000",
                None,
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog?page=0&entries_per_page=5000",
                None,
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog?page=-1&entries_per_page=50",
                None,
                expected_response_code=HTTP_400_BAD_REQUEST,
                use_moderator_login=True
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog",
                None,
                {
                    "page_count": 0,
                    "page": []
                },
                use_moderator_login=True
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog?page=10",
                None,
                {
                    "page_count": 0,
                    "page": []
                },
                use_moderator_login=True
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog?page=0&entries_per_page=50",
                None,
                {
                    "page_count": 0,
                    "page": []
                },
                use_moderator_login=True
            )
            
            self.do_json_request(
                "PATCH",
                "/object_management/announcement/1/configuration",
                {
                    "updates": {
                        "text": "Changelog Test"
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            _CHANGELOG_ENTRY_ANNOUNCEMENT_1_TEXT = {
                    "type": "modification",
                    "modifying_user_id": 43,
                    "object_type": "announcement",
                    "object_id": 1,
                    "field_description": {
                        "id": "text",
                        "type": "string"
                    },
                    "old_value": "Test Ankündigung",
                    "new_value": "Changelog Test"
                }
            _CHANGELOG_ENTRY_FEATURED_2_TITLE = {
                    "type": "modification",
                    "modifying_user_id": 43,
                    "object_type": "featured",
                    "object_id": 2,
                    "field_description": {
                        "id": "text",
                        "type": "string"
                    },
                    "old_value": "Image Panel",
                    "new_value": "Featured Changelog Test"
                }
            _CHANGELOG_ENTRY_FEATURED_2_DELETION = {
                    "type": "deletion_change",
                    "modifying_user_id": 43,
                    "object_type": "featured",
                    "object_id": 2,
                    "is_now_deleted": True
                }
            _CHANGELOG_ENTRY_FEATURED_2_UNDO_DELETION = {
                    "type": "deletion_change",
                    "modifying_user_id": 43,
                    "object_type": "featured",
                    "object_id": 2,
                    "is_now_deleted": False
                }
            self.do_json_request(
                "GET",
                f"/object_management/changelog",
                None,
                {
                    "page_count": 1,
                    "page": [
                        _CHANGELOG_ENTRY_ANNOUNCEMENT_1_TEXT
                    ]
                },
                ignore_value_keys=["id", "change_time", "max_length", "min_length"],
                use_moderator_login=True
            )
            self.do_json_request(
                "PATCH",
                "/object_management/featured/2/configuration",
                {
                    "updates": {
                        "title": "Featured Changelog Test"
                    },
                    "expected_current_values": {}
                },
                use_moderator_login=True
            )
            self.do_json_request(
                "DELETE",
                "/object_management/featured/2",
                use_moderator_login=True
            )
            self.do_json_request(
                "POST",
                "/object_management/featured/2/resurrect",
                use_moderator_login=True
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog",
                None,
                {
                    "page_count": 1,
                    "page": [
                        _CHANGELOG_ENTRY_FEATURED_2_UNDO_DELETION,
                        _CHANGELOG_ENTRY_FEATURED_2_DELETION,
                        _CHANGELOG_ENTRY_FEATURED_2_TITLE,
                        _CHANGELOG_ENTRY_ANNOUNCEMENT_1_TEXT
                    ]
                },
                ignore_value_keys=["id", "change_time", "max_length", "min_length"],
                use_moderator_login=True
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog?object_type=featured&object_id=2",
                None,
                {
                    "page_count": 1,
                    "page": [
                        _CHANGELOG_ENTRY_FEATURED_2_UNDO_DELETION,
                        _CHANGELOG_ENTRY_FEATURED_2_DELETION,
                        _CHANGELOG_ENTRY_FEATURED_2_TITLE
                    ]
                },
                ignore_value_keys=["id", "change_time", "max_length", "min_length"],
                use_moderator_login=True
            )
            self.do_json_request(
                "GET",
                f"/object_management/changelog?object_type=featured&object_id=2&field=title",
                None,
                {
                    "page_count": 1,
                    "page": [
                        _CHANGELOG_ENTRY_FEATURED_2_TITLE
                    ]
                },
                ignore_value_keys=["id", "change_time", "max_length", "min_length"],
                use_moderator_login=True
            )
            for i in range(0, 100):
                self.do_json_request(
                    "PATCH",
                    "/object_management/featured/3/configuration",
                    {
                        "updates": {
                            "title": f"Test {i}"
                        },
                        "expected_current_values": {}
                    },
                    use_moderator_login=True
                )
            for route, page_count in [("/object_management/changelog?", 11),
                                      ("/object_management/changelog?object_type=featured&object_id=3&", 10),
                                      ("/object_management/changelog?object_type=featured&object_id=3&field=title&", 10)]:
                self.do_json_request(
                    "GET",
                    f"{route}entries_per_page=10&page=0",
                    None,
                    {
                        "page_count": page_count
                    },
                    ignore_value_keys=["$.page"],
                    use_moderator_login=True
                )
        
        def test_changelog_creation(self):
            featured_id = self.do_json_request(
                "PUT",
                "/object_management/featured/new",
                {
                    "variant": "plain",
                    "values": {
                        "title": "Changelog",
                        "text": "Test",
                        "is_visible": False
                    }
                },
                expected_response_code=HTTP_201_CREATED,
                use_moderator_login=True
            )[1]["id"]
            
            _CHANGELOG_ENTRY_FEATURED_NEW_CREATION = {
                    "type": "creation",
                    "modifying_user_id": 43,
                    "object_type": "featured",
                    "object_id": featured_id,
                    "variant": "plain"
                }
            _CHANGELOG_ENTRY_FEATURED_NEW_CREATION_TITLE = {
                    "type": "modification",
                    "modifying_user_id": 43,
                    "object_type": "featured",
                    "object_id": featured_id,
                    "field_description": {
                        "id": "title",
                        "type": "string"
                    },
                    "new_value": "Changelog"
                }
            _CHANGELOG_ENTRY_FEATURED_NEW_CREATION_TEXT = {
                    "type": "modification",
                    "modifying_user_id": 43,
                    "object_type": "featured",
                    "object_id": featured_id,
                    "field_description": {
                        "id": "text",
                        "type": "string"
                    },
                    "new_value": "Test"
                }
            _CHANGELOG_ENTRY_FEATURED_NEW_CREATION_IS_VISIBLE = {
                    "type": "modification",
                    "modifying_user_id": 43,
                    "object_type": "featured",
                    "object_id": featured_id,
                    "field_description": {
                        "id": "is_visible",
                        "type": "boolean"
                    },
                    "new_value": False
                }
            self.do_json_request(
                "GET",
                f"/object_management/changelog",
                None,
                {
                    "page_count": 1,
                    "page": [
                        _CHANGELOG_ENTRY_FEATURED_NEW_CREATION_TITLE,
                        _CHANGELOG_ENTRY_FEATURED_NEW_CREATION_TEXT,
                        _CHANGELOG_ENTRY_FEATURED_NEW_CREATION_IS_VISIBLE,
                        _CHANGELOG_ENTRY_FEATURED_NEW_CREATION
                    ]
                },
                ignore_array_order_keys=["$.page"],
                ignore_value_keys=["id", "change_time", "max_length", "min_length", "default_value"],
                use_moderator_login=True
            )
            
            lecture_id = self.do_json_request(
                "PUT",
                "/object_management/lecture/new",
                {
                    "parent_type": "course",
                    "parent_id": 3,
                    "values": {
                        "title": "New Year",
                        "time": "2024-01-01T00:00:00",
                        "duration": 42
                    }
                },
                expected_response_code=HTTP_201_CREATED,
                use_moderator_login=True
            )[1]["id"]
            
            _CHANGELOG_ENTRY_LECTURE_NEW_CREATION = {
                    "type": "creation",
                    "modifying_user_id": 43,
                    "object_type": "lecture",
                    "object_id": lecture_id,
                    "parent_type": "course",
                    "parent_id": 3
                }
            resp = self.do_json_request(
                "GET",
                f"/object_management/changelog?object_type=lecture&object_id={lecture_id}",
                None,
                use_moderator_login=True
            )[1]
            for entry in resp["page"]:
                if entry["type"] != "creation":
                    continue
                self.assertJsonEqual(_CHANGELOG_ENTRY_LECTURE_NEW_CREATION, entry, ignore_value_keys=["id", "change_time"])
                break