import glob
import json
import os
import random
import re
import string
from datetime import datetime, timedelta
from enum import Enum
from io import StringIO
from pathlib import Path
from typing import Any
from unittest import mock, skipUnless
import orjson
import time_machine
from django.conf import settings
from django.core import mail
from django.test import override_settings
from django.utils.timezone import now as timezone_now
from typing_extensions import override
from confirmation.models import Confirmation, create_confirmation_link
from corporate.models.customers import Customer
from corporate.models.plans import CustomerPlan
from zerver.actions.create_realm import do_change_realm_subdomain, do_create_realm
from zerver.actions.create_user import do_create_user
from zerver.actions.message_send import (
internal_send_group_direct_message,
internal_send_private_message,
internal_send_stream_message,
)
from zerver.actions.realm_settings import (
clean_deactivated_realm_data,
delete_expired_demo_organizations,
do_add_deactivated_redirect,
do_change_realm_max_invites,
do_change_realm_org_type,
do_change_realm_permission_group_setting,
do_change_realm_plan_type,
do_deactivate_realm,
do_delete_all_realm_attachments,
do_delete_realm,
do_reactivate_realm,
do_scrub_realm,
do_send_realm_reactivation_email,
do_set_realm_authentication_methods,
do_set_realm_property,
do_set_realm_user_default_setting,
)
from zerver.actions.streams import do_deactivate_stream, merge_streams
from zerver.actions.user_groups import (
add_subgroups_to_user_group,
check_add_user_group,
do_change_user_group_permission_setting,
)
from zerver.actions.user_settings import do_change_avatar_fields
from zerver.lib.cache import cache_delete, realm_rendered_description_cache_key
from zerver.lib.markdown import version as markdown_version
from zerver.lib.realm_description import get_realm_rendered_description, get_realm_text_description
from zerver.lib.send_email import send_future_email
from zerver.lib.streams import create_stream_if_needed
from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import (
activate_push_notification_service,
get_test_image_file,
read_test_image_file,
)
from zerver.lib.thumbnail import ThumbnailFormat
from zerver.lib.upload import upload_avatar_image, upload_message_attachment
from zerver.models import (
Attachment,
CustomProfileField,
DirectMessageGroup,
ImageAttachment,
Message,
NamedUserGroup,
Realm,
RealmAuditLog,
RealmReactivationStatus,
RealmUserDefault,
Recipient,
ScheduledEmail,
Stream,
Subscription,
UserGroupMembership,
UserMessage,
UserProfile,
)
from zerver.models.groups import SystemGroups
from zerver.models.realm_audit_logs import AuditLogEventType
from zerver.models.realms import get_realm
from zerver.models.recipients import get_or_create_direct_message_group
from zerver.models.scheduled_jobs import ScheduledMessage
from zerver.models.streams import get_stream
from zerver.models.users import get_system_bot, get_user_profile_by_id
if settings.ZILENCER_ENABLED:
from corporate.lib.stripe import get_seat_count
class RealmTest(ZulipTestCase):
def assert_user_profile_cache_gets_new_name(
self, user_profile: UserProfile, new_realm_name: str
) -> None:
self.assertEqual(user_profile.realm.name, new_realm_name)
def test_realm_creation_ensures_internal_realms(self) -> None:
with mock.patch("zerver.actions.create_realm.server_initialized", return_value=False):
with (
mock.patch(
"zerver.actions.create_realm.create_internal_realm"
) as mock_create_internal,
self.assertLogs(level="INFO") as info_logs,
):
do_create_realm("testrealm", "Test Realm")
mock_create_internal.assert_called_once()
self.assertEqual(
info_logs.output,
["INFO:root:Server not yet initialized. Creating the internal realm first."],
)
def test_realm_creation_on_special_subdomains_disallowed(self) -> None:
with self.settings(SOCIAL_AUTH_SUBDOMAIN="zulipauth"), self.assertRaises(AssertionError):
do_create_realm("zulipauth", "Test Realm")
with (
self.settings(SELF_HOSTING_MANAGEMENT_SUBDOMAIN="zulipselfhosting"),
self.assertRaises(AssertionError),
):
do_create_realm("zulipselfhosting", "Test Realm")
def test_permission_for_education_non_profit_organization(self) -> None:
realm = do_create_realm(
"test_education_non_profit",
"education_org_name",
org_type=Realm.ORG_TYPES["education_nonprofit"]["id"],
)
admins_group = NamedUserGroup.objects.get(
name=SystemGroups.ADMINISTRATORS, realm_for_sharding=realm, is_system_group=True
)
self.assertEqual(realm.can_create_public_channel_group_id, admins_group.id)
realm = get_realm("test_education_non_profit")
moderators_group = NamedUserGroup.objects.get(
name=SystemGroups.MODERATORS, realm_for_sharding=realm, is_system_group=True
)
self.assertEqual(realm.can_add_subscribers_group.id, moderators_group.id)
self.assertEqual(realm.can_create_groups.id, moderators_group.id)
self.assertEqual(realm.can_invite_users_group.id, admins_group.id)
self.assertEqual(realm.can_move_messages_between_channels_group.id, moderators_group.id)
def test_permission_for_education_for_profit_organization(self) -> None:
realm = do_create_realm(
"test_education_for_profit",
"education_org_name",
org_type=Realm.ORG_TYPES["education"]["id"],
)
admins_group = NamedUserGroup.objects.get(
name=SystemGroups.ADMINISTRATORS, realm_for_sharding=realm, is_system_group=True
)
self.assertEqual(realm.can_create_public_channel_group_id, admins_group.id)
realm = get_realm("test_education_for_profit")
moderators_group = NamedUserGroup.objects.get(
name=SystemGroups.MODERATORS, realm_for_sharding=realm, is_system_group=True
)
self.assertEqual(realm.can_add_subscribers_group.id, moderators_group.id)
self.assertEqual(realm.can_create_groups.id, moderators_group.id)
self.assertEqual(realm.can_invite_users_group.id, admins_group.id)
self.assertEqual(realm.can_move_messages_between_channels_group.id, moderators_group.id)
def test_realm_enable_spectator_access(self) -> None:
realm = do_create_realm(
"test_web_public_true",
"Foo",
plan_type=Realm.PLAN_TYPE_STANDARD,
enable_spectator_access=True,
)
self.assertEqual(realm.enable_spectator_access, True)
realm = do_create_realm("test_web_public_false", "Boo", enable_spectator_access=False)
self.assertEqual(realm.enable_spectator_access, False)
with self.assertRaises(AssertionError):
realm = do_create_realm("test_web_public_false_1", "Foo", enable_spectator_access=True)
with self.assertRaises(AssertionError):
realm = do_create_realm(
"test_web_public_false_2",
"Foo",
plan_type=Realm.PLAN_TYPE_LIMITED,
enable_spectator_access=True,
)
def test_do_set_realm_name_caching(self) -> None:
"""The main complicated thing about setting realm names is fighting the
cache, and we start by populating the cache for Hamlet, and we end
by checking the cache to ensure that the new value is there."""
realm = get_realm("zulip")
new_name = "Zed You Elle Eye Pea"
do_set_realm_property(realm, "name", new_name, acting_user=None)
self.assertEqual(get_realm(realm.string_id).name, new_name)
self.assert_user_profile_cache_gets_new_name(self.example_user("hamlet"), new_name)
def test_update_realm_name_events(self) -> None:
realm = get_realm("zulip")
new_name = "Puliz"
with self.capture_send_event_calls(expected_num_events=1) as events:
do_set_realm_property(realm, "name", new_name, acting_user=None)
event = events[0]["event"]
self.assertEqual(
event,
dict(
type="realm",
op="update",
property="name",
value=new_name,
),
)
def test_update_realm_description_events(self) -> None:
realm = get_realm("zulip")
new_description = "zulip dev group"
with self.capture_send_event_calls(expected_num_events=1) as events:
do_set_realm_property(realm, "description", new_description, acting_user=None)
event = events[0]["event"]
self.assertEqual(
event,
dict(
type="realm",
op="update_dict",
property="default",
data={
"description": new_description,
"rendered_description": f"
{new_description}
",
},
),
)
def test_update_realm_description(self) -> None:
self.login("iago")
new_description = "zulip dev group"
data = dict(description=new_description)
with self.capture_send_event_calls(expected_num_events=1) as events:
result = self.client_patch("/json/realm", data)
self.assert_json_success(result)
realm = get_realm("zulip")
self.assertEqual(realm.description, new_description)
event = events[0]["event"]
self.assertEqual(
event,
dict(
type="realm",
op="update_dict",
property="default",
data={
"description": new_description,
"rendered_description": f"{new_description}
",
},
),
)
def test_realm_description_length(self) -> None:
new_description = "A" * 1001
data = dict(description=new_description)
# create an admin user
self.login("iago")
result = self.client_patch("/json/realm", data)
self.assert_json_error(result, "description is too long (limit: 1000 characters)")
realm = get_realm("zulip")
self.assertNotEqual(realm.description, new_description)
def test_demo_organization_invite_required(self) -> None:
demo_organization_owner = self.create_demo_organization_owner()
realm = demo_organization_owner.realm
# Demo organization owners must configure an email to invite
# other users.
self.assertTrue(realm.invite_required)
data = dict(invite_required="false")
result = self.client_patch("/json/realm", data, subdomain=realm.subdomain)
self.assert_json_error(result, "Configure owner account email address.")
demo_organization_owner.delivery_email = "test-demo-owner@zulip.com"
demo_organization_owner.save()
result = self.client_patch("/json/realm", data, subdomain=realm.subdomain)
self.assert_json_success(result)
realm.refresh_from_db()
self.assertFalse(realm.invite_required)
def test_realm_convert_demo_organization_errors(self) -> None:
data = dict(string_id="coolrealm")
self.login("iago")
result = self.client_patch("/json/realm", data)
self.assert_json_error(result, "Must be an organization owner")
self.login("desdemona")
result = self.client_patch("/json/realm", data)
self.assert_json_error(result, "Must be a demo organization.")
def test_realm_convert_demo_organization_success(self) -> None:
demo_owner_account = self.create_demo_organization_owner()
realm = demo_owner_account.realm
demo_string_id = realm.string_id
# Confirm there is a scheduled message reminder about automated
# demo organization deletion.
demo_deletion_reminder = ScheduledMessage.objects.filter(
realm_id=realm.id,
sender__email="notification-bot@zulip.com",
).latest("id")
self.assertTrue(
demo_deletion_reminder.content.startswith("As a reminder, this [demo organization]")
)
self.assertIn("will be automatically deleted on