mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	We used the data setup from the get test case since it needed the most number of folders.
		
			
				
	
	
		
			352 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			352 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from typing import Any
 | 
						|
 | 
						|
import orjson
 | 
						|
from typing_extensions import override
 | 
						|
 | 
						|
from zerver.actions.channel_folders import check_add_channel_folder, do_archive_channel_folder
 | 
						|
from zerver.actions.streams import do_change_stream_folder, do_deactivate_stream
 | 
						|
from zerver.lib.test_classes import ZulipTestCase
 | 
						|
from zerver.models import ChannelFolder
 | 
						|
from zerver.models.realms import get_realm
 | 
						|
from zerver.models.streams import get_stream
 | 
						|
 | 
						|
 | 
						|
class ChannelFoldersTestCase(ZulipTestCase):
 | 
						|
    @override
 | 
						|
    def setUp(self) -> None:
 | 
						|
        super().setUp()
 | 
						|
        zulip_realm = get_realm("zulip")
 | 
						|
        iago = self.example_user("iago")
 | 
						|
        desdemona = self.example_user("desdemona")
 | 
						|
        lear_user = self.lear_user("cordelia")
 | 
						|
 | 
						|
        check_add_channel_folder(
 | 
						|
            zulip_realm, "Frontend", "Channels for frontend discussions", acting_user=iago
 | 
						|
        )
 | 
						|
        check_add_channel_folder(
 | 
						|
            zulip_realm, "Backend", "Channels for **backend** discussions", acting_user=iago
 | 
						|
        )
 | 
						|
        check_add_channel_folder(
 | 
						|
            zulip_realm, "Marketing", "Channels for marketing discussions", acting_user=desdemona
 | 
						|
        )
 | 
						|
        check_add_channel_folder(
 | 
						|
            lear_user.realm, "Devops", "Channels for devops discussions", acting_user=lear_user
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
class ChannelFolderCreationTest(ZulipTestCase):
 | 
						|
    def test_creating_channel_folder(self) -> None:
 | 
						|
        self.login("shiva")
 | 
						|
        realm = get_realm("zulip")
 | 
						|
 | 
						|
        params = {"name": "Frontend", "description": ""}
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_error(result, "Must be an organization administrator")
 | 
						|
 | 
						|
        self.login("iago")
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        channel_folder = ChannelFolder.objects.filter(realm=realm).last()
 | 
						|
        assert channel_folder is not None
 | 
						|
        self.assertEqual(channel_folder.name, "Frontend")
 | 
						|
        self.assertEqual(channel_folder.description, "")
 | 
						|
        response = orjson.loads(result.content)
 | 
						|
        self.assertEqual(response["channel_folder_id"], channel_folder.id)
 | 
						|
 | 
						|
    def test_creating_channel_folder_with_duplicate_name(self) -> None:
 | 
						|
        self.login("iago")
 | 
						|
        realm = get_realm("zulip")
 | 
						|
 | 
						|
        params = {"name": "Frontend", "description": ""}
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        self.assertTrue(ChannelFolder.objects.filter(realm=realm, name="Frontend").exists())
 | 
						|
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_error(result, "Channel folder name already in use")
 | 
						|
 | 
						|
        # Archived folder names can be reused.
 | 
						|
        folder = ChannelFolder.objects.get(name="Frontend", realm=realm)
 | 
						|
        do_archive_channel_folder(folder, acting_user=self.example_user("iago"))
 | 
						|
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        self.assertTrue(ChannelFolder.objects.filter(realm=realm, name="Frontend").exists())
 | 
						|
 | 
						|
        # Folder names should be unique case-insensitively.
 | 
						|
        params["name"] = "frontEND"
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_error(result, "Channel folder name already in use")
 | 
						|
 | 
						|
    def test_rendered_description_for_channel_folder(self) -> None:
 | 
						|
        self.login("iago")
 | 
						|
        realm = get_realm("zulip")
 | 
						|
 | 
						|
        params = {"name": "Frontend", "description": "Channels for frontend discussions"}
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        channel_folder = ChannelFolder.objects.get(realm=realm, name="Frontend")
 | 
						|
        self.assertEqual(channel_folder.description, "Channels for frontend discussions")
 | 
						|
        self.assertEqual(
 | 
						|
            channel_folder.rendered_description, "<p>Channels for frontend discussions</p>"
 | 
						|
        )
 | 
						|
 | 
						|
        params = {"name": "Backend", "description": "Channels for **backend** discussions"}
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        channel_folder = ChannelFolder.objects.get(realm=realm, name="Backend")
 | 
						|
        self.assertEqual(channel_folder.description, "Channels for **backend** discussions")
 | 
						|
        self.assertEqual(
 | 
						|
            channel_folder.rendered_description,
 | 
						|
            "<p>Channels for <strong>backend</strong> discussions</p>",
 | 
						|
        )
 | 
						|
 | 
						|
    def test_invalid_params_for_channel_folder(self) -> None:
 | 
						|
        self.login("iago")
 | 
						|
 | 
						|
        params = {"name": "", "description": "Channels for frontend discussions"}
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_error(result, "Channel folder name can't be empty.")
 | 
						|
 | 
						|
        invalid_name = "abc\000"
 | 
						|
        params = {"name": invalid_name, "description": "Channels for frontend discussions"}
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_error(result, "Invalid character in channel folder name, at position 4.")
 | 
						|
 | 
						|
        long_name = "a" * (ChannelFolder.MAX_NAME_LENGTH + 1)
 | 
						|
        params = {"name": long_name, "description": "Channels for frontend discussions"}
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_error(
 | 
						|
            result, f"name is too long (limit: {ChannelFolder.MAX_NAME_LENGTH} characters)"
 | 
						|
        )
 | 
						|
 | 
						|
        long_description = "a" * (ChannelFolder.MAX_DESCRIPTION_LENGTH + 1)
 | 
						|
        params = {"name": "Frontend", "description": long_description}
 | 
						|
        result = self.client_post("/json/channel_folders/create", params)
 | 
						|
        self.assert_json_error(
 | 
						|
            result,
 | 
						|
            f"description is too long (limit: {ChannelFolder.MAX_DESCRIPTION_LENGTH} characters)",
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
class GetChannelFoldersTest(ChannelFoldersTestCase):
 | 
						|
    def test_get_channel_folders(self) -> None:
 | 
						|
        iago = self.example_user("iago")
 | 
						|
        desdemona = self.example_user("desdemona")
 | 
						|
        zulip_realm = iago.realm
 | 
						|
        frontend_folder = ChannelFolder.objects.get(name="Frontend", realm=zulip_realm)
 | 
						|
        backend_folder = ChannelFolder.objects.get(name="Backend", realm=zulip_realm)
 | 
						|
        marketing_folder = ChannelFolder.objects.get(name="Marketing", realm=zulip_realm)
 | 
						|
 | 
						|
        def check_channel_folders_in_zulip_realm(
 | 
						|
            channel_folders: list[dict[str, Any]], marketing_folder_included: bool = True
 | 
						|
        ) -> None:
 | 
						|
            if marketing_folder_included:
 | 
						|
                self.assert_length(channel_folders, 3)
 | 
						|
            else:
 | 
						|
                self.assert_length(channel_folders, 2)
 | 
						|
 | 
						|
            self.assertEqual(channel_folders[0]["id"], frontend_folder.id)
 | 
						|
            self.assertEqual(channel_folders[0]["name"], "Frontend")
 | 
						|
            self.assertEqual(channel_folders[0]["description"], "Channels for frontend discussions")
 | 
						|
            self.assertEqual(
 | 
						|
                channel_folders[0]["rendered_description"],
 | 
						|
                "<p>Channels for frontend discussions</p>",
 | 
						|
            )
 | 
						|
            self.assertEqual(channel_folders[0]["is_archived"], frontend_folder.is_archived)
 | 
						|
            self.assertEqual(channel_folders[0]["creator_id"], iago.id)
 | 
						|
 | 
						|
            self.assertEqual(channel_folders[1]["id"], backend_folder.id)
 | 
						|
            self.assertEqual(channel_folders[1]["name"], "Backend")
 | 
						|
            self.assertEqual(
 | 
						|
                channel_folders[1]["description"], "Channels for **backend** discussions"
 | 
						|
            )
 | 
						|
            self.assertEqual(
 | 
						|
                channel_folders[1]["rendered_description"],
 | 
						|
                "<p>Channels for <strong>backend</strong> discussions</p>",
 | 
						|
            )
 | 
						|
            self.assertEqual(channel_folders[1]["is_archived"], backend_folder.is_archived)
 | 
						|
            self.assertEqual(channel_folders[1]["creator_id"], iago.id)
 | 
						|
 | 
						|
            if marketing_folder_included:
 | 
						|
                self.assertEqual(channel_folders[2]["id"], marketing_folder.id)
 | 
						|
                self.assertEqual(channel_folders[2]["name"], "Marketing")
 | 
						|
                self.assertEqual(
 | 
						|
                    channel_folders[2]["description"], "Channels for marketing discussions"
 | 
						|
                )
 | 
						|
                self.assertEqual(
 | 
						|
                    channel_folders[2]["rendered_description"],
 | 
						|
                    "<p>Channels for marketing discussions</p>",
 | 
						|
                )
 | 
						|
                self.assertEqual(channel_folders[2]["is_archived"], marketing_folder.is_archived)
 | 
						|
                self.assertEqual(channel_folders[2]["creator_id"], desdemona.id)
 | 
						|
 | 
						|
        self.login("iago")
 | 
						|
        result = self.client_get("/json/channel_folders")
 | 
						|
        channel_folders_data = orjson.loads(result.content)["channel_folders"]
 | 
						|
        check_channel_folders_in_zulip_realm(channel_folders_data)
 | 
						|
 | 
						|
        # Check member user can also see all channel folders.
 | 
						|
        self.login("hamlet")
 | 
						|
        result = self.client_get("/json/channel_folders")
 | 
						|
        channel_folders_data = orjson.loads(result.content)["channel_folders"]
 | 
						|
        check_channel_folders_in_zulip_realm(channel_folders_data)
 | 
						|
 | 
						|
        # Check guest can also see all channel folders.
 | 
						|
        self.login("polonius")
 | 
						|
        result = self.client_get("/json/channel_folders")
 | 
						|
        channel_folders_data = orjson.loads(result.content)["channel_folders"]
 | 
						|
        check_channel_folders_in_zulip_realm(channel_folders_data)
 | 
						|
 | 
						|
        marketing_folder.is_archived = True
 | 
						|
        marketing_folder.save()
 | 
						|
 | 
						|
        result = self.client_get("/json/channel_folders")
 | 
						|
        channel_folders_data = orjson.loads(result.content)["channel_folders"]
 | 
						|
        check_channel_folders_in_zulip_realm(channel_folders_data, False)
 | 
						|
 | 
						|
        result = self.client_get(
 | 
						|
            "/json/channel_folders", {"include_archived": orjson.dumps(True).decode()}
 | 
						|
        )
 | 
						|
        channel_folders_data = orjson.loads(result.content)["channel_folders"]
 | 
						|
        check_channel_folders_in_zulip_realm(channel_folders_data)
 | 
						|
 | 
						|
 | 
						|
class UpdateChannelFoldersTest(ChannelFoldersTestCase):
 | 
						|
    def test_updating_channel_folder_name(self) -> None:
 | 
						|
        realm = get_realm("zulip")
 | 
						|
        channel_folder = ChannelFolder.objects.get(name="Frontend", realm=realm)
 | 
						|
        channel_folder_id = channel_folder.id
 | 
						|
 | 
						|
        self.login("hamlet")
 | 
						|
 | 
						|
        params = {"name": "Web frontend"}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(result, "Must be an organization administrator")
 | 
						|
 | 
						|
        self.login("iago")
 | 
						|
 | 
						|
        # Test invalid channel folder ID.
 | 
						|
        result = self.client_patch("/json/channel_folders/999", params)
 | 
						|
        self.assert_json_error(result, "Invalid channel folder ID")
 | 
						|
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        channel_folder = ChannelFolder.objects.get(id=channel_folder_id)
 | 
						|
        self.assertEqual(channel_folder.name, "Web frontend")
 | 
						|
 | 
						|
        params = {"name": ""}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(result, "Channel folder name can't be empty.")
 | 
						|
 | 
						|
        params = {"name": "Backend"}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(result, "Channel folder name already in use")
 | 
						|
 | 
						|
        # Folder names should be unique case-insensitively.
 | 
						|
        params = {"name": "backEND"}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(result, "Channel folder name already in use")
 | 
						|
 | 
						|
        invalid_name = "abc\000"
 | 
						|
        params = {"name": invalid_name}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(result, "Invalid character in channel folder name, at position 4.")
 | 
						|
 | 
						|
        long_name = "a" * (ChannelFolder.MAX_NAME_LENGTH + 1)
 | 
						|
        params = {"name": long_name}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(
 | 
						|
            result, f"name is too long (limit: {ChannelFolder.MAX_NAME_LENGTH} characters)"
 | 
						|
        )
 | 
						|
 | 
						|
    def test_updating_channel_folder_description(self) -> None:
 | 
						|
        channel_folder = ChannelFolder.objects.get(name="Frontend", realm=get_realm("zulip"))
 | 
						|
        channel_folder_id = channel_folder.id
 | 
						|
 | 
						|
        self.login("hamlet")
 | 
						|
 | 
						|
        params = {"description": "Channels for **frontend** discussions"}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(result, "Must be an organization administrator")
 | 
						|
 | 
						|
        self.login("iago")
 | 
						|
 | 
						|
        # Test invalid channel folder ID.
 | 
						|
        result = self.client_patch("/json/channel_folders/999", params)
 | 
						|
        self.assert_json_error(result, "Invalid channel folder ID")
 | 
						|
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        channel_folder = ChannelFolder.objects.get(id=channel_folder_id)
 | 
						|
        self.assertEqual(channel_folder.description, "Channels for **frontend** discussions")
 | 
						|
        self.assertEqual(
 | 
						|
            channel_folder.rendered_description,
 | 
						|
            "<p>Channels for <strong>frontend</strong> discussions</p>",
 | 
						|
        )
 | 
						|
 | 
						|
        # Channel folder descriptions can be empty.
 | 
						|
        params = {"description": ""}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        channel_folder = ChannelFolder.objects.get(id=channel_folder_id)
 | 
						|
        self.assertEqual(channel_folder.description, "")
 | 
						|
        self.assertEqual(channel_folder.rendered_description, "")
 | 
						|
 | 
						|
        params = {"description": "a" * (ChannelFolder.MAX_DESCRIPTION_LENGTH + 1)}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(
 | 
						|
            result,
 | 
						|
            f"description is too long (limit: {ChannelFolder.MAX_DESCRIPTION_LENGTH} characters)",
 | 
						|
        )
 | 
						|
 | 
						|
    def test_archiving_and_unarchiving_channel_folder(self) -> None:
 | 
						|
        desdemona = self.example_user("desdemona")
 | 
						|
        realm = get_realm("zulip")
 | 
						|
        channel_folder = ChannelFolder.objects.get(name="Frontend", realm=realm)
 | 
						|
        channel_folder_id = channel_folder.id
 | 
						|
 | 
						|
        self.login("hamlet")
 | 
						|
 | 
						|
        params = {"is_archived": orjson.dumps(True).decode()}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(result, "Must be an organization administrator")
 | 
						|
 | 
						|
        self.login("iago")
 | 
						|
 | 
						|
        # Test invalid channel folder ID.
 | 
						|
        result = self.client_patch("/json/channel_folders/999", params)
 | 
						|
        self.assert_json_error(result, "Invalid channel folder ID")
 | 
						|
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        channel_folder = ChannelFolder.objects.get(id=channel_folder_id)
 | 
						|
        self.assertTrue(channel_folder.is_archived)
 | 
						|
 | 
						|
        params = {"is_archived": orjson.dumps(False).decode()}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        channel_folder = ChannelFolder.objects.get(id=channel_folder_id)
 | 
						|
        self.assertFalse(channel_folder.is_archived)
 | 
						|
 | 
						|
        # Folder containing channels cannot be archived.
 | 
						|
        stream = get_stream("Verona", realm)
 | 
						|
        do_change_stream_folder(stream, channel_folder, acting_user=desdemona)
 | 
						|
        params = {"is_archived": orjson.dumps(True).decode()}
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(
 | 
						|
            result, "You need to remove all the channels from this folder to archive it."
 | 
						|
        )
 | 
						|
 | 
						|
        do_deactivate_stream(stream, acting_user=desdemona)
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_error(
 | 
						|
            result, "You need to remove all the channels from this folder to archive it."
 | 
						|
        )
 | 
						|
 | 
						|
        do_change_stream_folder(stream, None, acting_user=desdemona)
 | 
						|
        result = self.client_patch(f"/json/channel_folders/{channel_folder_id}", params)
 | 
						|
        self.assert_json_success(result)
 | 
						|
        channel_folder = ChannelFolder.objects.get(id=channel_folder_id)
 | 
						|
        self.assertTrue(channel_folder.is_archived)
 |