2020-06-05 06:55:20 +02:00
|
|
|
from datetime import datetime, timedelta, timezone
|
2023-11-30 21:11:54 +01:00
|
|
|
from decimal import Decimal
|
2023-11-28 19:16:58 +01:00
|
|
|
from typing import TYPE_CHECKING, Any, Optional
|
2020-05-26 07:16:25 +02:00
|
|
|
from unittest import mock
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2020-08-07 01:09:47 +02:00
|
|
|
import orjson
|
2023-11-28 19:16:58 +01:00
|
|
|
import time_machine
|
2020-03-31 12:01:48 +02:00
|
|
|
from django.utils.timezone import now as timezone_now
|
2023-10-12 19:43:45 +02:00
|
|
|
from typing_extensions import override
|
2017-02-10 21:52:14 +01:00
|
|
|
|
2023-11-30 21:11:54 +01:00
|
|
|
from corporate.lib.stripe import RealmBillingSession, add_months
|
2023-12-01 12:23:31 +01:00
|
|
|
from corporate.models import (
|
|
|
|
Customer,
|
|
|
|
CustomerPlan,
|
|
|
|
LicenseLedger,
|
2023-12-13 18:25:40 +01:00
|
|
|
SponsoredPlanTypes,
|
|
|
|
ZulipSponsorshipRequest,
|
2023-12-01 12:23:31 +01:00
|
|
|
get_current_plan_by_realm,
|
|
|
|
get_customer_by_realm,
|
|
|
|
)
|
2022-04-14 23:36:07 +02:00
|
|
|
from zerver.actions.invites import do_create_multiuse_invite_link
|
2021-10-26 09:15:16 +02:00
|
|
|
from zerver.actions.realm_settings import do_change_realm_org_type, do_send_realm_reactivation_email
|
|
|
|
from zerver.actions.user_settings import do_change_user_setting
|
2017-11-16 00:55:49 +01:00
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
2023-03-01 07:34:25 +01:00
|
|
|
from zerver.lib.test_helpers import reset_email_visibility_to_everyone_in_zulip_realm
|
2023-12-15 02:14:24 +01:00
|
|
|
from zerver.models import MultiuseInvite, PreregistrationUser, Realm, UserMessage, UserProfile
|
|
|
|
from zerver.models.realms import OrgTypeEnum, get_org_type_display_name, get_realm
|
2023-10-18 13:18:12 +02:00
|
|
|
from zilencer.lib.remote_counts import MissingDataError
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse
|
|
|
|
|
2023-10-04 13:38:12 +02:00
|
|
|
import uuid
|
|
|
|
|
|
|
|
from zilencer.models import RemoteZulipServer
|
|
|
|
|
|
|
|
|
|
|
|
class TestRemoteServerSupportEndpoint(ZulipTestCase):
|
2023-10-12 19:43:45 +02:00
|
|
|
@override
|
2023-10-04 13:38:12 +02:00
|
|
|
def setUp(self) -> None:
|
2023-12-13 18:25:40 +01:00
|
|
|
def add_sponsorship_request(
|
|
|
|
hostname: str, org_type: int, website: str, paid_users: str, plan: str
|
|
|
|
) -> None:
|
|
|
|
remote_server = RemoteZulipServer.objects.get(hostname=hostname)
|
|
|
|
customer = Customer.objects.create(
|
|
|
|
remote_server=remote_server, sponsorship_pending=True
|
|
|
|
)
|
|
|
|
ZulipSponsorshipRequest.objects.create(
|
|
|
|
customer=customer,
|
|
|
|
org_type=org_type,
|
|
|
|
org_website=website,
|
|
|
|
org_description="We help people.",
|
|
|
|
expected_total_users="20-35",
|
|
|
|
paid_users_count=paid_users,
|
|
|
|
paid_users_description="",
|
|
|
|
requested_plan=plan,
|
|
|
|
)
|
|
|
|
|
2023-10-04 13:38:12 +02:00
|
|
|
super().setUp()
|
|
|
|
|
2023-10-12 19:45:37 +02:00
|
|
|
# Set up some initial example data.
|
|
|
|
for i in range(20):
|
|
|
|
hostname = f"zulip-{i}.example.com"
|
|
|
|
RemoteZulipServer.objects.create(
|
|
|
|
hostname=hostname, contact_email=f"admin@{hostname}", plan_type=1, uuid=uuid.uuid4()
|
|
|
|
)
|
2023-10-04 13:38:12 +02:00
|
|
|
|
2023-12-13 18:25:40 +01:00
|
|
|
# Add example sponsorship request data
|
|
|
|
add_sponsorship_request(
|
|
|
|
hostname="zulip-1.example.com",
|
|
|
|
org_type=OrgTypeEnum.Community.value,
|
|
|
|
website="",
|
|
|
|
paid_users="None",
|
|
|
|
plan=SponsoredPlanTypes.BUSINESS.value,
|
|
|
|
)
|
|
|
|
|
|
|
|
add_sponsorship_request(
|
|
|
|
hostname="zulip-2.example.com",
|
|
|
|
org_type=OrgTypeEnum.OpenSource.value,
|
|
|
|
website="example.org",
|
|
|
|
paid_users="",
|
|
|
|
plan=SponsoredPlanTypes.COMMUNITY.value,
|
|
|
|
)
|
|
|
|
|
2023-10-04 13:38:12 +02:00
|
|
|
def test_search(self) -> None:
|
|
|
|
self.login("cordelia")
|
|
|
|
|
|
|
|
result = self.client_get("/activity/remote/support")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
|
|
|
# Iago is the user with the appropriate permissions to access this page.
|
|
|
|
self.login("iago")
|
|
|
|
assert self.example_user("iago").is_staff
|
|
|
|
|
|
|
|
result = self.client_get("/activity/remote/support")
|
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
|
|
|
'input type="text" name="q" class="input-xxlarge search-query" placeholder="hostname or contact email"'
|
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
|
|
|
|
2023-10-18 13:18:12 +02:00
|
|
|
with mock.patch("analytics.views.support.compute_max_monthly_messages", return_value=1000):
|
|
|
|
result = self.client_get("/activity/remote/support", {"q": "zulip-1.example.com"})
|
2023-10-04 13:38:12 +02:00
|
|
|
self.assert_in_success_response(["<h3>zulip-1.example.com</h3>"], result)
|
2023-10-18 13:18:12 +02:00
|
|
|
self.assert_in_success_response(["<b>Max monthly messages</b>: 1000"], result)
|
|
|
|
self.assert_not_in_success_response(["<h3>zulip-2.example.com</h3>"], result)
|
|
|
|
|
2023-12-13 18:25:40 +01:00
|
|
|
# Sponsorship request information
|
|
|
|
self.assert_in_success_response(["<li><b>Organization type</b>: Community</li>"], result)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["<li><b>Organization website</b>: No website submitted</li>"], result
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(["<li><b>Paid users</b>: None</li>"], result)
|
|
|
|
self.assert_in_success_response(["<li><b>Requested plan</b>: Business</li>"], result)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["<li><b>Organization description</b>: We help people.</li>"], result
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(["<li><b>Estimated total users</b>: 20-35</li>"], result)
|
|
|
|
self.assert_in_success_response(["<li><b>Description of paid users</b>: </li>"], result)
|
|
|
|
|
2023-10-18 13:18:12 +02:00
|
|
|
with mock.patch(
|
|
|
|
"analytics.views.support.compute_max_monthly_messages", side_effect=MissingDataError
|
|
|
|
):
|
|
|
|
result = self.client_get("/activity/remote/support", {"q": "zulip-1.example.com"})
|
|
|
|
self.assert_in_success_response(["<h3>zulip-1.example.com</h3>"], result)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["<b>Max monthly messages</b>: Recent data missing"], result
|
|
|
|
)
|
2023-10-04 13:38:12 +02:00
|
|
|
self.assert_not_in_success_response(["<h3>zulip-2.example.com</h3>"], result)
|
|
|
|
|
|
|
|
result = self.client_get("/activity/remote/support", {"q": "example.com"})
|
|
|
|
for i in range(20):
|
|
|
|
self.assert_in_success_response([f"<h3>zulip-{i}.example.com</h3>"], result)
|
|
|
|
|
|
|
|
result = self.client_get("/activity/remote/support", {"q": "admin@zulip-2.example.com"})
|
|
|
|
self.assert_in_success_response(["<h3>zulip-2.example.com</h3>"], result)
|
|
|
|
self.assert_in_success_response(["<b>Contact email</b>: admin@zulip-2.example.com"], result)
|
|
|
|
self.assert_not_in_success_response(["<h3>zulip-1.example.com</h3>"], result)
|
|
|
|
|
2023-12-13 18:25:40 +01:00
|
|
|
# Sponsorship request information
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["<li><b>Organization type</b>: Open-source project</li>"], result
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["<li><b>Organization website</b>: example.org</li>"], result
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(["<li><b>Paid users</b>: </li>"], result)
|
|
|
|
self.assert_in_success_response(["<li><b>Requested plan</b>: Community</li>"], result)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["<li><b>Organization description</b>: We help people.</li>"], result
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(["<li><b>Estimated total users</b>: 20-35</li>"], result)
|
|
|
|
self.assert_in_success_response(["<li><b>Description of paid users</b>: </li>"], result)
|
|
|
|
|
|
|
|
result = self.client_get("/activity/remote/support", {"q": "admin@zulip-3.example.com"})
|
|
|
|
self.assert_in_success_response(["<h3>zulip-3.example.com</h3>"], result)
|
|
|
|
self.assert_in_success_response(["<b>Contact email</b>: admin@zulip-3.example.com"], result)
|
|
|
|
self.assert_not_in_success_response(["<h3>zulip-1.example.com</h3>"], result)
|
|
|
|
|
|
|
|
# Sponsorship request information
|
|
|
|
self.assert_not_in_success_response(
|
|
|
|
["<li><b>Organization description</b>: We help people.</li>"], result
|
|
|
|
)
|
|
|
|
self.assert_not_in_success_response(
|
|
|
|
["<li><b>Estimated total users</b>: 20-35</li>"], result
|
|
|
|
)
|
|
|
|
self.assert_not_in_success_response(["<li><b>Description of paid users</b>: </li>"], result)
|
|
|
|
|
2017-02-10 21:52:14 +01:00
|
|
|
|
2019-03-08 13:02:10 +01:00
|
|
|
class TestSupportEndpoint(ZulipTestCase):
|
|
|
|
def test_search(self) -> None:
|
2023-03-01 07:34:25 +01:00
|
|
|
reset_email_visibility_to_everyone_in_zulip_realm()
|
2022-07-07 21:37:29 +02:00
|
|
|
lear_user = self.lear_user("king")
|
|
|
|
lear_user.is_staff = True
|
|
|
|
lear_user.save(update_fields=["is_staff"])
|
2022-07-07 21:35:41 +02:00
|
|
|
lear_realm = get_realm("lear")
|
2020-03-12 14:17:25 +01:00
|
|
|
|
2021-03-19 17:56:06 +01:00
|
|
|
def assert_user_details_in_html_response(
|
2022-06-08 04:52:09 +02:00
|
|
|
html_response: "TestHttpResponse", full_name: str, email: str, role: str
|
2021-03-19 17:56:06 +01:00
|
|
|
) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
|
|
|
'<span class="label">user</span>\n',
|
2021-03-19 17:56:06 +01:00
|
|
|
f"<h3>{full_name}</h3>",
|
|
|
|
f"<b>Email</b>: {email}",
|
2021-04-21 00:46:14 +02:00
|
|
|
"<b>Is active</b>: True<br />",
|
|
|
|
f"<b>Role</b>: {role}<br />",
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
2021-03-19 17:56:06 +01:00
|
|
|
html_response,
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2022-07-07 20:30:59 +02:00
|
|
|
def create_invitation(
|
|
|
|
stream: str, invitee_email: str, realm: Optional[Realm] = None
|
|
|
|
) -> None:
|
|
|
|
invite_expires_in_minutes = 10 * 24 * 60
|
|
|
|
self.client_post(
|
|
|
|
"/json/invites",
|
|
|
|
{
|
|
|
|
"invitee_emails": [invitee_email],
|
|
|
|
"stream_ids": orjson.dumps([self.get_stream_id(stream, realm)]).decode(),
|
|
|
|
"invite_expires_in_minutes": invite_expires_in_minutes,
|
|
|
|
"invite_as": PreregistrationUser.INVITE_AS["MEMBER"],
|
|
|
|
},
|
|
|
|
subdomain=realm.string_id if realm is not None else "zulip",
|
|
|
|
)
|
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def check_hamlet_user_query_result(result: "TestHttpResponse") -> None:
|
2021-03-19 17:56:06 +01:00
|
|
|
assert_user_details_in_html_response(
|
2021-03-19 19:15:35 +01:00
|
|
|
result, "King Hamlet", self.example_email("hamlet"), "Member"
|
2021-03-19 17:56:06 +01:00
|
|
|
)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
2021-04-01 17:28:27 +02:00
|
|
|
f"<b>Admins</b>: {self.example_email('iago')}\n",
|
|
|
|
f"<b>Owners</b>: {self.example_email('desdemona')}\n",
|
|
|
|
'class="copy-button" data-copytext="{}">'.format(self.example_email("iago")),
|
|
|
|
'class="copy-button" data-copytext="{}">'.format(
|
|
|
|
self.example_email("desdemona")
|
2021-03-19 19:15:35 +01:00
|
|
|
),
|
2021-03-19 16:50:50 +01:00
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
|
|
|
|
2022-07-07 21:37:29 +02:00
|
|
|
def check_lear_user_query_result(result: "TestHttpResponse") -> None:
|
|
|
|
assert_user_details_in_html_response(
|
|
|
|
result, lear_user.full_name, lear_user.email, "Member"
|
|
|
|
)
|
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def check_othello_user_query_result(result: "TestHttpResponse") -> None:
|
2021-03-19 17:56:06 +01:00
|
|
|
assert_user_details_in_html_response(
|
2021-03-19 19:15:35 +01:00
|
|
|
result, "Othello, the Moor of Venice", self.example_email("othello"), "Member"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-10-30 20:23:20 +01:00
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def check_polonius_user_query_result(result: "TestHttpResponse") -> None:
|
2021-03-19 19:15:35 +01:00
|
|
|
assert_user_details_in_html_response(
|
|
|
|
result, "Polonius", self.example_email("polonius"), "Guest"
|
|
|
|
)
|
2021-03-19 17:56:06 +01:00
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def check_zulip_realm_query_result(result: "TestHttpResponse") -> None:
|
2019-07-24 07:22:48 +02:00
|
|
|
zulip_realm = get_realm("zulip")
|
2021-04-16 20:17:48 +02:00
|
|
|
first_human_user = zulip_realm.get_first_human_user()
|
|
|
|
assert first_human_user is not None
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
2021-04-16 20:17:48 +02:00
|
|
|
f"<b>First human user</b>: {first_human_user.delivery_email}\n",
|
2021-02-12 08:19:30 +01:00
|
|
|
f'<input type="hidden" name="realm_id" value="{zulip_realm.id}"',
|
2021-02-12 08:20:45 +01:00
|
|
|
"Zulip Dev</h3>",
|
2022-01-05 03:22:30 +01:00
|
|
|
'<option value="1" selected>Self-hosted</option>',
|
2021-02-12 08:19:30 +01:00
|
|
|
'<option value="2" >Limited</option>',
|
|
|
|
'input type="number" name="discount" value="None"',
|
|
|
|
'<option value="active" selected>Active</option>',
|
|
|
|
'<option value="deactivated" >Deactivated</option>',
|
2021-10-07 21:30:54 +02:00
|
|
|
f'<option value="{zulip_realm.org_type}" selected>',
|
2021-02-12 08:19:30 +01:00
|
|
|
'scrub-realm-button">',
|
|
|
|
'data-string-id="zulip"',
|
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def check_lear_realm_query_result(result: "TestHttpResponse") -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
|
|
|
f'<input type="hidden" name="realm_id" value="{lear_realm.id}"',
|
2021-02-12 08:20:45 +01:00
|
|
|
"Lear & Co.</h3>",
|
2022-01-05 03:22:30 +01:00
|
|
|
'<option value="1" selected>Self-hosted</option>',
|
2021-02-12 08:19:30 +01:00
|
|
|
'<option value="2" >Limited</option>',
|
|
|
|
'input type="number" name="discount" value="None"',
|
|
|
|
'<option value="active" selected>Active</option>',
|
|
|
|
'<option value="deactivated" >Deactivated</option>',
|
|
|
|
'scrub-realm-button">',
|
|
|
|
'data-string-id="lear"',
|
2023-12-06 18:09:34 +01:00
|
|
|
"<b>Plan name</b>: Zulip Cloud Standard",
|
2021-02-12 08:20:45 +01:00
|
|
|
"<b>Status</b>: Active",
|
|
|
|
"<b>Billing schedule</b>: Annual",
|
|
|
|
"<b>Licenses</b>: 2/10 (Manual)",
|
|
|
|
"<b>Price per license</b>: $80.0",
|
|
|
|
"<b>Next invoice date</b>: 02 January 2017",
|
2021-02-12 08:19:30 +01:00
|
|
|
'<option value="send_invoice" selected>',
|
|
|
|
'<option value="charge_automatically" >',
|
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
|
|
|
|
|
|
|
def check_preregistration_user_query_result(
|
2022-06-08 04:52:09 +02:00
|
|
|
result: "TestHttpResponse", email: str, invite: bool = False
|
2021-02-12 08:19:30 +01:00
|
|
|
) -> None:
|
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
|
|
|
'<span class="label">preregistration user</span>\n',
|
2021-02-12 08:20:45 +01:00
|
|
|
f"<b>Email</b>: {email}",
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
2019-09-18 15:04:36 +02:00
|
|
|
if invite:
|
|
|
|
self.assert_in_success_response(['<span class="label">invite</span>'], result)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
2021-04-01 16:56:37 +02:00
|
|
|
"<b>Expires in</b>: 1\xa0week, 3\xa0days",
|
2022-05-06 21:09:00 +02:00
|
|
|
"<b>Status</b>: Link has not been used",
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
2019-09-18 15:04:36 +02:00
|
|
|
self.assert_in_success_response([], result)
|
|
|
|
else:
|
|
|
|
self.assert_not_in_success_response(['<span class="label">invite</span>'], result)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
2021-04-01 16:56:37 +02:00
|
|
|
[
|
|
|
|
"<b>Expires in</b>: 1\xa0day",
|
2022-05-06 21:09:00 +02:00
|
|
|
"<b>Status</b>: Link has not been used",
|
2021-04-01 16:56:37 +02:00
|
|
|
],
|
2021-02-12 08:19:30 +01:00
|
|
|
result,
|
|
|
|
)
|
2019-09-18 15:04:36 +02:00
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def check_realm_creation_query_result(result: "TestHttpResponse", email: str) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
|
|
|
'<span class="label">preregistration user</span>\n',
|
|
|
|
'<span class="label">realm creation</span>\n',
|
2021-02-12 08:20:45 +01:00
|
|
|
"<b>Link</b>: http://testserver/accounts/do_confirm/",
|
2021-04-01 16:56:37 +02:00
|
|
|
"<b>Expires in</b>: 1\xa0day",
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
2019-09-18 15:04:36 +02:00
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def check_multiuse_invite_link_query_result(result: "TestHttpResponse") -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
|
|
|
'<span class="label">multiuse invite</span>\n',
|
2021-02-12 08:20:45 +01:00
|
|
|
"<b>Link</b>: http://zulip.testserver/join/",
|
2021-04-01 16:56:37 +02:00
|
|
|
"<b>Expires in</b>: 1\xa0week, 3\xa0days",
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
2019-09-18 15:04:36 +02:00
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def check_realm_reactivation_link_query_result(result: "TestHttpResponse") -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
|
|
|
'<span class="label">realm reactivation</span>\n',
|
2021-02-12 08:20:45 +01:00
|
|
|
"<b>Link</b>: http://zulip.testserver/reactivate/",
|
|
|
|
"<b>Expires in</b>: 1\xa0day",
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
2019-09-18 15:04:36 +02:00
|
|
|
|
2022-07-07 18:25:09 +02:00
|
|
|
def get_check_query_result(
|
|
|
|
query: str, count: int, subdomain: str = "zulip"
|
|
|
|
) -> "TestHttpResponse":
|
|
|
|
result = self.client_get("/activity/support", {"q": query}, subdomain=subdomain)
|
|
|
|
self.assertEqual(result.content.decode().count("support-query-result"), count)
|
|
|
|
return result
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("cordelia")
|
2019-03-08 13:02:10 +01:00
|
|
|
|
|
|
|
result = self.client_get("/activity/support")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("iago")
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2021-10-26 09:15:16 +02:00
|
|
|
do_change_user_setting(
|
|
|
|
self.example_user("hamlet"),
|
2021-03-01 11:33:24 +01:00
|
|
|
"email_address_visibility",
|
2021-10-26 09:15:16 +02:00
|
|
|
UserProfile.EMAIL_ADDRESS_VISIBILITY_NOBODY,
|
2021-03-01 11:33:24 +01:00
|
|
|
acting_user=None,
|
2021-03-19 16:57:23 +01:00
|
|
|
)
|
|
|
|
|
2022-07-07 21:35:41 +02:00
|
|
|
customer = Customer.objects.create(realm=lear_realm, stripe_customer_id="cus_123")
|
2020-07-03 20:21:13 +02:00
|
|
|
now = datetime(2016, 1, 2, tzinfo=timezone.utc)
|
2021-02-12 08:19:30 +01:00
|
|
|
plan = CustomerPlan.objects.create(
|
|
|
|
customer=customer,
|
|
|
|
billing_cycle_anchor=now,
|
2023-11-30 07:55:53 +01:00
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_ANNUAL,
|
2023-11-30 07:43:06 +01:00
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
2021-02-12 08:19:30 +01:00
|
|
|
price_per_license=8000,
|
|
|
|
next_invoice_date=add_months(now, 12),
|
|
|
|
)
|
|
|
|
LicenseLedger.objects.create(
|
|
|
|
licenses=10,
|
|
|
|
licenses_at_next_renewal=10,
|
|
|
|
event_time=timezone_now(),
|
|
|
|
is_renewal=True,
|
|
|
|
plan=plan,
|
|
|
|
)
|
2020-07-03 20:21:13 +02:00
|
|
|
|
2019-03-08 13:02:10 +01:00
|
|
|
result = self.client_get("/activity/support")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
['<input type="text" name="q" class="input-xxlarge search-query"'], result
|
|
|
|
)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2022-07-07 18:25:09 +02:00
|
|
|
result = get_check_query_result(self.example_email("hamlet"), 1)
|
2019-06-12 16:09:24 +02:00
|
|
|
check_hamlet_user_query_result(result)
|
2021-03-19 16:50:50 +01:00
|
|
|
check_zulip_realm_query_result(result)
|
|
|
|
|
2023-05-16 13:47:56 +02:00
|
|
|
# Search should be case-insensitive:
|
|
|
|
assert self.example_email("hamlet") != self.example_email("hamlet").upper()
|
|
|
|
result = get_check_query_result(self.example_email("hamlet").upper(), 1)
|
|
|
|
check_hamlet_user_query_result(result)
|
|
|
|
check_zulip_realm_query_result(result)
|
|
|
|
|
2022-07-07 21:37:29 +02:00
|
|
|
result = get_check_query_result(lear_user.email, 1)
|
|
|
|
check_lear_user_query_result(result)
|
|
|
|
check_lear_realm_query_result(result)
|
|
|
|
|
2022-07-07 18:25:09 +02:00
|
|
|
result = get_check_query_result(self.example_email("polonius"), 1)
|
2021-03-19 16:50:50 +01:00
|
|
|
check_polonius_user_query_result(result)
|
2019-06-12 16:09:24 +02:00
|
|
|
check_zulip_realm_query_result(result)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2022-07-07 18:25:09 +02:00
|
|
|
result = get_check_query_result("lear", 1)
|
2019-06-12 16:09:24 +02:00
|
|
|
check_lear_realm_query_result(result)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2022-07-07 18:25:09 +02:00
|
|
|
result = get_check_query_result("http://lear.testserver", 1)
|
2019-06-12 16:09:24 +02:00
|
|
|
check_lear_realm_query_result(result)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with self.settings(REALM_HOSTS={"zulip": "localhost"}):
|
2022-07-07 18:25:09 +02:00
|
|
|
result = get_check_query_result("http://localhost", 1)
|
2019-06-12 16:09:24 +02:00
|
|
|
check_zulip_realm_query_result(result)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2022-07-07 18:25:09 +02:00
|
|
|
result = get_check_query_result("hamlet@zulip.com, lear", 2)
|
2019-06-12 16:09:24 +02:00
|
|
|
check_hamlet_user_query_result(result)
|
|
|
|
check_zulip_realm_query_result(result)
|
|
|
|
check_lear_realm_query_result(result)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2022-07-07 18:25:09 +02:00
|
|
|
result = get_check_query_result("King hamlet,lear", 2)
|
2020-10-30 20:23:20 +01:00
|
|
|
check_hamlet_user_query_result(result)
|
|
|
|
check_zulip_realm_query_result(result)
|
|
|
|
check_lear_realm_query_result(result)
|
|
|
|
|
2022-07-07 18:25:09 +02:00
|
|
|
result = get_check_query_result("Othello, the Moor of Venice", 1)
|
2020-10-30 20:23:20 +01:00
|
|
|
check_othello_user_query_result(result)
|
|
|
|
check_zulip_realm_query_result(result)
|
|
|
|
|
2022-07-07 18:25:09 +02:00
|
|
|
result = get_check_query_result("lear, Hamlet <hamlet@zulip.com>", 2)
|
2019-06-12 16:09:24 +02:00
|
|
|
check_hamlet_user_query_result(result)
|
|
|
|
check_zulip_realm_query_result(result)
|
|
|
|
check_lear_realm_query_result(result)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2023-11-28 19:16:58 +01:00
|
|
|
self.client_post("/accounts/home/", {"email": self.nonreg_email("test")})
|
|
|
|
self.login("iago")
|
2019-09-18 15:04:36 +02:00
|
|
|
|
2023-11-28 19:16:58 +01:00
|
|
|
def query_result_from_before(*args: Any) -> "TestHttpResponse":
|
|
|
|
with time_machine.travel((timezone_now() - timedelta(minutes=50)), tick=False):
|
|
|
|
return get_check_query_result(*args)
|
2019-09-18 15:04:36 +02:00
|
|
|
|
2023-11-28 19:16:58 +01:00
|
|
|
result = query_result_from_before(self.nonreg_email("test"), 1)
|
|
|
|
check_preregistration_user_query_result(result, self.nonreg_email("test"))
|
|
|
|
check_zulip_realm_query_result(result)
|
2019-09-18 15:04:36 +02:00
|
|
|
|
2023-11-28 19:16:58 +01:00
|
|
|
create_invitation("Denmark", self.nonreg_email("test1"))
|
|
|
|
result = query_result_from_before(self.nonreg_email("test1"), 1)
|
|
|
|
check_preregistration_user_query_result(result, self.nonreg_email("test1"), invite=True)
|
|
|
|
check_zulip_realm_query_result(result)
|
2019-09-18 15:04:36 +02:00
|
|
|
|
2023-11-28 19:16:58 +01:00
|
|
|
email = self.nonreg_email("alice")
|
|
|
|
self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain="custom-test", realm_name="Zulip test"
|
|
|
|
)
|
|
|
|
result = query_result_from_before(email, 1)
|
|
|
|
check_realm_creation_query_result(result, email)
|
|
|
|
|
|
|
|
invite_expires_in_minutes = 10 * 24 * 60
|
|
|
|
do_create_multiuse_invite_link(
|
|
|
|
self.example_user("hamlet"),
|
|
|
|
invited_as=1,
|
|
|
|
invite_expires_in_minutes=invite_expires_in_minutes,
|
|
|
|
)
|
|
|
|
result = query_result_from_before("zulip", 2)
|
|
|
|
check_multiuse_invite_link_query_result(result)
|
|
|
|
check_zulip_realm_query_result(result)
|
|
|
|
MultiuseInvite.objects.all().delete()
|
2019-09-18 15:04:36 +02:00
|
|
|
|
2023-11-28 19:16:58 +01:00
|
|
|
do_send_realm_reactivation_email(get_realm("zulip"), acting_user=None)
|
|
|
|
result = query_result_from_before("zulip", 2)
|
|
|
|
check_realm_reactivation_link_query_result(result)
|
|
|
|
check_zulip_realm_query_result(result)
|
|
|
|
|
|
|
|
lear_nonreg_email = "newguy@lear.org"
|
|
|
|
self.client_post("/accounts/home/", {"email": lear_nonreg_email}, subdomain="lear")
|
|
|
|
result = query_result_from_before(lear_nonreg_email, 1)
|
|
|
|
check_preregistration_user_query_result(result, lear_nonreg_email)
|
|
|
|
check_lear_realm_query_result(result)
|
|
|
|
|
|
|
|
self.login_user(lear_user)
|
|
|
|
create_invitation("general", "newguy2@lear.org", lear_realm)
|
|
|
|
result = query_result_from_before("newguy2@lear.org", 1, lear_realm.string_id)
|
|
|
|
check_preregistration_user_query_result(result, "newguy2@lear.org", invite=True)
|
|
|
|
check_lear_realm_query_result(result)
|
2022-07-07 21:37:29 +02:00
|
|
|
|
2021-07-15 20:10:15 +02:00
|
|
|
def test_get_org_type_display_name(self) -> None:
|
|
|
|
self.assertEqual(get_org_type_display_name(Realm.ORG_TYPES["business"]["id"]), "Business")
|
|
|
|
self.assertEqual(get_org_type_display_name(883), "")
|
|
|
|
|
2022-07-29 16:03:59 +02:00
|
|
|
def test_unspecified_org_type_correctly_displayed(self) -> None:
|
|
|
|
"""
|
|
|
|
Unspecified org type is special in that it is marked to not be shown
|
|
|
|
on the registration page (because organitions are not meant to be able to choose it),
|
2023-03-23 04:30:27 +01:00
|
|
|
but should be correctly shown at the /support/ endpoint.
|
2022-07-29 16:03:59 +02:00
|
|
|
"""
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
|
|
|
|
do_change_realm_org_type(realm, 0, acting_user=None)
|
|
|
|
self.assertEqual(realm.org_type, 0)
|
|
|
|
|
|
|
|
self.login("iago")
|
|
|
|
|
|
|
|
result = self.client_get("/activity/support", {"q": "zulip"}, subdomain="zulip")
|
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
|
|
|
f'<input type="hidden" name="realm_id" value="{realm.id}"',
|
|
|
|
'<option value="0" selected>',
|
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
|
|
|
|
2023-12-01 12:23:31 +01:00
|
|
|
def test_change_billing_modality(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
2020-08-18 13:48:11 +02:00
|
|
|
self.login_user(cordelia)
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
2023-12-01 12:23:31 +01:00
|
|
|
"/activity/support",
|
|
|
|
{"realm_id": f"{realm.id}", "billing_method": "charge_automatically"},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-08-18 13:48:11 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
2023-12-01 12:23:31 +01:00
|
|
|
customer = Customer.objects.create(realm=realm, stripe_customer_id="cus_12345")
|
|
|
|
CustomerPlan.objects.create(
|
|
|
|
customer=customer,
|
|
|
|
status=CustomerPlan.ACTIVE,
|
|
|
|
billing_cycle_anchor=timezone_now(),
|
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_ANNUAL,
|
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
|
|
|
)
|
|
|
|
|
2020-08-18 13:48:11 +02:00
|
|
|
iago = self.example_user("iago")
|
|
|
|
self.login_user(iago)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support",
|
2023-12-01 12:23:31 +01:00
|
|
|
{"realm_id": f"{realm.id}", "billing_modality": "charge_automatically"},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
2023-12-01 13:19:04 +01:00
|
|
|
["Billing collection method of zulip updated to charge automatically"], result
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2023-12-01 12:23:31 +01:00
|
|
|
plan = get_current_plan_by_realm(realm)
|
|
|
|
assert plan is not None
|
|
|
|
self.assertEqual(plan.charge_automatically, True)
|
2020-08-18 13:48:11 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
2023-12-01 12:23:31 +01:00
|
|
|
"/activity/support", {"realm_id": f"{realm.id}", "billing_modality": "send_invoice"}
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
2023-12-01 13:19:04 +01:00
|
|
|
["Billing collection method of zulip updated to send invoice"], result
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2023-12-01 12:23:31 +01:00
|
|
|
realm.refresh_from_db()
|
|
|
|
plan = get_current_plan_by_realm(realm)
|
|
|
|
assert plan is not None
|
|
|
|
self.assertEqual(plan.charge_automatically, False)
|
2020-08-18 13:48:11 +02:00
|
|
|
|
2021-12-01 02:10:40 +01:00
|
|
|
def test_change_realm_plan_type(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(cordelia)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{cordelia.realm_id}", "plan_type": "2"}
|
|
|
|
)
|
2019-03-08 13:02:10 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
2019-07-24 07:22:48 +02:00
|
|
|
iago = self.example_user("iago")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(iago)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2021-12-01 02:10:40 +01:00
|
|
|
with mock.patch("analytics.views.support.do_change_realm_plan_type") as m:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{iago.realm_id}", "plan_type": "2"}
|
|
|
|
)
|
2020-12-04 10:54:15 +01:00
|
|
|
m.assert_called_once_with(get_realm("zulip"), 2, acting_user=iago)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
2023-12-05 13:09:33 +01:00
|
|
|
["Plan type of zulip changed from Self-hosted to Limited"], result
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2021-12-01 02:10:40 +01:00
|
|
|
with mock.patch("analytics.views.support.do_change_realm_plan_type") as m:
|
2021-09-16 16:05:26 +02:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{iago.realm_id}", "plan_type": "10"}
|
|
|
|
)
|
|
|
|
m.assert_called_once_with(get_realm("zulip"), 10, acting_user=iago)
|
|
|
|
self.assert_in_success_response(
|
2023-12-05 13:09:33 +01:00
|
|
|
["Plan type of zulip changed from Self-hosted to Plus"], result
|
2021-09-16 16:05:26 +02:00
|
|
|
)
|
|
|
|
|
2021-10-07 21:30:54 +02:00
|
|
|
def test_change_org_type(self) -> None:
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
self.login_user(cordelia)
|
|
|
|
|
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{cordelia.realm_id}", "org_type": "70"}
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
self.login_user(iago)
|
|
|
|
|
|
|
|
with mock.patch("analytics.views.support.do_change_realm_org_type") as m:
|
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{iago.realm_id}", "org_type": "70"}
|
|
|
|
)
|
|
|
|
m.assert_called_once_with(get_realm("zulip"), 70, acting_user=iago)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["Org type of zulip changed from Business to Government"], result
|
|
|
|
)
|
|
|
|
|
2019-03-08 13:02:10 +01:00
|
|
|
def test_attach_discount(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
lear_realm = get_realm("lear")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(cordelia)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "discount": "25"}
|
|
|
|
)
|
2019-03-08 13:02:10 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
2020-12-04 11:08:10 +01:00
|
|
|
iago = self.example_user("iago")
|
2023-11-30 21:11:54 +01:00
|
|
|
self.login_user(iago)
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2023-11-30 21:11:54 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "discount": "25"}
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(["Discount for lear changed to 25% from 0%"], result)
|
|
|
|
customer = get_customer_by_realm(lear_realm)
|
|
|
|
assert customer is not None
|
|
|
|
self.assertEqual(customer.default_discount, Decimal(25))
|
2019-03-08 13:02:10 +01:00
|
|
|
|
2020-06-09 12:24:32 +02:00
|
|
|
def test_change_sponsorship_status(self) -> None:
|
|
|
|
lear_realm = get_realm("lear")
|
|
|
|
self.assertIsNone(get_customer_by_realm(lear_realm))
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
2020-06-09 12:24:32 +02:00
|
|
|
self.login_user(cordelia)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "sponsorship_pending": "true"}
|
|
|
|
)
|
2020-06-09 12:24:32 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
self.login_user(iago)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "sponsorship_pending": "true"}
|
|
|
|
)
|
2020-08-18 14:10:53 +02:00
|
|
|
self.assert_in_success_response(["lear marked as pending sponsorship."], result)
|
2020-06-09 12:24:32 +02:00
|
|
|
customer = get_customer_by_realm(lear_realm)
|
2021-02-12 08:19:30 +01:00
|
|
|
assert customer is not None
|
2020-06-09 12:24:32 +02:00
|
|
|
self.assertTrue(customer.sponsorship_pending)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "sponsorship_pending": "false"}
|
|
|
|
)
|
2020-08-18 14:10:53 +02:00
|
|
|
self.assert_in_success_response(["lear is no longer pending sponsorship."], result)
|
2020-06-09 12:24:32 +02:00
|
|
|
customer = get_customer_by_realm(lear_realm)
|
2021-02-12 08:19:30 +01:00
|
|
|
assert customer is not None
|
2020-06-09 12:24:32 +02:00
|
|
|
self.assertFalse(customer.sponsorship_pending)
|
|
|
|
|
2020-07-17 12:56:06 +02:00
|
|
|
def test_approve_sponsorship(self) -> None:
|
2023-11-02 18:17:08 +01:00
|
|
|
support_admin = self.example_user("iago")
|
2020-07-17 12:56:06 +02:00
|
|
|
lear_realm = get_realm("lear")
|
2023-11-30 21:11:54 +01:00
|
|
|
billing_session = RealmBillingSession(
|
|
|
|
user=support_admin, realm=lear_realm, support_session=True
|
|
|
|
)
|
|
|
|
billing_session.update_customer_sponsorship_status(True)
|
2020-07-17 12:56:06 +02:00
|
|
|
king_user = self.lear_user("king")
|
|
|
|
king_user.role = UserProfile.ROLE_REALM_OWNER
|
|
|
|
king_user.save()
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
2020-07-17 12:56:06 +02:00
|
|
|
self.login_user(cordelia)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support",
|
2021-07-29 17:32:18 +02:00
|
|
|
{"realm_id": f"{lear_realm.id}", "approve_sponsorship": "true"},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-17 12:56:06 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
self.login_user(iago)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support",
|
2021-07-29 17:32:18 +02:00
|
|
|
{"realm_id": f"{lear_realm.id}", "approve_sponsorship": "true"},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-08-18 14:10:53 +02:00
|
|
|
self.assert_in_success_response(["Sponsorship approved for lear"], result)
|
2020-07-17 12:56:06 +02:00
|
|
|
lear_realm.refresh_from_db()
|
2021-10-18 23:28:17 +02:00
|
|
|
self.assertEqual(lear_realm.plan_type, Realm.PLAN_TYPE_STANDARD_FREE)
|
2020-07-17 12:56:06 +02:00
|
|
|
customer = get_customer_by_realm(lear_realm)
|
2021-02-12 08:19:30 +01:00
|
|
|
assert customer is not None
|
2020-07-17 12:56:06 +02:00
|
|
|
self.assertFalse(customer.sponsorship_pending)
|
|
|
|
messages = UserMessage.objects.filter(user_profile=king_user)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertIn(
|
|
|
|
"request for sponsored hosting has been approved", messages[0].message.content
|
|
|
|
)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(messages, 1)
|
2020-07-17 12:56:06 +02:00
|
|
|
|
2019-04-19 15:19:49 +02:00
|
|
|
def test_activate_or_deactivate_realm(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
lear_realm = get_realm("lear")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(cordelia)
|
2019-04-19 15:19:49 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "status": "deactivated"}
|
|
|
|
)
|
2019-04-19 15:19:49 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("iago")
|
2019-04-19 15:19:49 +02:00
|
|
|
|
2021-06-17 23:37:52 +02:00
|
|
|
with mock.patch("analytics.views.support.do_deactivate_realm") as m:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "status": "deactivated"}
|
|
|
|
)
|
2021-04-02 17:11:25 +02:00
|
|
|
m.assert_called_once_with(lear_realm, acting_user=self.example_user("iago"))
|
2020-08-18 14:10:53 +02:00
|
|
|
self.assert_in_success_response(["lear deactivated"], result)
|
2019-04-19 15:19:49 +02:00
|
|
|
|
2021-06-17 23:37:52 +02:00
|
|
|
with mock.patch("analytics.views.support.do_send_realm_reactivation_email") as m:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "status": "active"}
|
|
|
|
)
|
2020-12-04 11:46:51 +01:00
|
|
|
m.assert_called_once_with(lear_realm, acting_user=self.example_user("iago"))
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
["Realm reactivation email sent to admins of lear"], result
|
|
|
|
)
|
2019-04-19 15:19:49 +02:00
|
|
|
|
2020-11-17 19:18:22 +01:00
|
|
|
def test_change_subdomain(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
lear_realm = get_realm("lear")
|
2020-11-17 19:18:22 +01:00
|
|
|
self.login_user(cordelia)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "new_subdomain": "new_name"}
|
|
|
|
)
|
2020-11-17 19:18:22 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("iago")
|
2020-11-17 19:18:22 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "new_subdomain": "new-name"}
|
|
|
|
)
|
2020-11-17 19:18:22 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/activity/support?q=new-name")
|
|
|
|
realm_id = lear_realm.id
|
2021-02-12 08:20:45 +01:00
|
|
|
lear_realm = get_realm("new-name")
|
2020-11-17 19:18:22 +01:00
|
|
|
self.assertEqual(lear_realm.id, realm_id)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertTrue(Realm.objects.filter(string_id="lear").exists())
|
|
|
|
self.assertTrue(Realm.objects.filter(string_id="lear")[0].deactivated)
|
2020-11-17 19:18:22 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "new_subdomain": "new-name"}
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
2023-09-25 20:39:58 +02:00
|
|
|
["Subdomain already in use. Please choose a different one."], result
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "new_subdomain": "zulip"}
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
2023-09-25 20:39:58 +02:00
|
|
|
["Subdomain already in use. Please choose a different one."], result
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "new_subdomain": "lear"}
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
2023-09-25 20:39:58 +02:00
|
|
|
["Subdomain already in use. Please choose a different one."], result
|
|
|
|
)
|
|
|
|
|
|
|
|
# Test renaming to a "reserved" subdomain
|
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "new_subdomain": "your-org"}
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["Subdomain reserved. Please choose a different one."], result
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-12-18 20:17:20 +01:00
|
|
|
|
2023-12-01 19:45:11 +01:00
|
|
|
def test_modify_plan_for_downgrade_at_end_of_billing_cycle(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
2020-08-13 13:20:18 +02:00
|
|
|
self.login_user(cordelia)
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
2023-12-01 19:45:11 +01:00
|
|
|
"/activity/support",
|
|
|
|
{"realm_id": f"{realm.id}", "modify_plan": "downgrade_at_billing_cycle_end"},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-08-13 13:20:18 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
2023-12-01 19:45:11 +01:00
|
|
|
customer = Customer.objects.create(realm=realm, stripe_customer_id="cus_12345")
|
|
|
|
CustomerPlan.objects.create(
|
|
|
|
customer=customer,
|
|
|
|
status=CustomerPlan.ACTIVE,
|
|
|
|
billing_cycle_anchor=timezone_now(),
|
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_ANNUAL,
|
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
|
|
|
)
|
|
|
|
|
2020-08-13 13:20:18 +02:00
|
|
|
iago = self.example_user("iago")
|
|
|
|
self.login_user(iago)
|
|
|
|
|
2023-12-01 19:45:11 +01:00
|
|
|
with self.assertLogs("corporate.stripe", "INFO") as m:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support",
|
|
|
|
{
|
2023-12-01 19:45:11 +01:00
|
|
|
"realm_id": f"{realm.id}",
|
2023-04-11 00:14:41 +02:00
|
|
|
"modify_plan": "downgrade_at_billing_cycle_end",
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["zulip marked for downgrade at the end of billing cycle"], result
|
|
|
|
)
|
2023-12-01 19:45:11 +01:00
|
|
|
plan = get_current_plan_by_realm(realm)
|
|
|
|
assert plan is not None
|
|
|
|
self.assertEqual(plan.status, CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE)
|
|
|
|
expected_log = f"INFO:corporate.stripe:Change plan status: Customer.id: {customer.id}, CustomerPlan.id: {plan.id}, status: {CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE}"
|
|
|
|
self.assertEqual(m.output[0], expected_log)
|
2020-08-13 13:20:18 +02:00
|
|
|
|
2023-12-01 19:45:11 +01:00
|
|
|
def test_modify_plan_for_downgrade_now_without_additional_licenses(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
self.login_user(cordelia)
|
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support",
|
|
|
|
{"realm_id": f"{realm.id}", "modify_plan": "downgrade_now_without_additional_licenses"},
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
2020-08-13 13:20:18 +02:00
|
|
|
|
2023-12-01 19:45:11 +01:00
|
|
|
customer = Customer.objects.create(realm=realm, stripe_customer_id="cus_12345")
|
|
|
|
plan = CustomerPlan.objects.create(
|
|
|
|
customer=customer,
|
|
|
|
status=CustomerPlan.ACTIVE,
|
|
|
|
billing_cycle_anchor=timezone_now(),
|
|
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_ANNUAL,
|
|
|
|
tier=CustomerPlan.TIER_CLOUD_STANDARD,
|
|
|
|
)
|
2020-08-13 13:20:18 +02:00
|
|
|
|
2023-12-01 19:45:11 +01:00
|
|
|
iago = self.example_user("iago")
|
|
|
|
self.login_user(iago)
|
|
|
|
|
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support",
|
|
|
|
{
|
|
|
|
"realm_id": f"{iago.realm_id}",
|
|
|
|
"modify_plan": "downgrade_now_without_additional_licenses",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["zulip downgraded without creating additional invoices"], result
|
|
|
|
)
|
|
|
|
|
|
|
|
plan.refresh_from_db()
|
|
|
|
self.assertEqual(plan.status, CustomerPlan.ENDED)
|
|
|
|
realm.refresh_from_db()
|
|
|
|
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_LIMITED)
|
2023-04-11 00:29:38 +02:00
|
|
|
|
2019-04-19 18:17:41 +02:00
|
|
|
def test_scrub_realm(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
lear_realm = get_realm("lear")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(cordelia)
|
2019-04-19 18:17:41 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "discount": "25"}
|
|
|
|
)
|
2019-04-19 18:17:41 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("iago")
|
2019-04-19 18:17:41 +02:00
|
|
|
|
2021-06-17 23:37:52 +02:00
|
|
|
with mock.patch("analytics.views.support.do_scrub_realm") as m:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
2021-07-29 17:32:18 +02:00
|
|
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "scrub_realm": "true"}
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-06-29 12:28:21 +02:00
|
|
|
m.assert_called_once_with(lear_realm, acting_user=self.example_user("iago"))
|
2020-08-18 14:10:53 +02:00
|
|
|
self.assert_in_success_response(["lear scrubbed"], result)
|
2019-04-19 18:17:41 +02:00
|
|
|
|
2021-06-17 23:37:52 +02:00
|
|
|
with mock.patch("analytics.views.support.do_scrub_realm") as m:
|
2020-07-27 20:21:41 +02:00
|
|
|
result = self.client_post("/activity/support", {"realm_id": f"{lear_realm.id}"})
|
|
|
|
self.assert_json_error(result, "Invalid parameters")
|
2019-04-19 18:17:41 +02:00
|
|
|
m.assert_not_called()
|
2023-05-15 23:40:35 +02:00
|
|
|
|
|
|
|
def test_delete_user(self) -> None:
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
hamlet_email = hamlet.delivery_email
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
self.login_user(cordelia)
|
|
|
|
|
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support", {"realm_id": f"{realm.id}", "delete_user_by_id": hamlet.id}
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "/login/")
|
|
|
|
|
|
|
|
self.login("iago")
|
|
|
|
|
|
|
|
with mock.patch("analytics.views.support.do_delete_user_preserving_messages") as m:
|
|
|
|
result = self.client_post(
|
|
|
|
"/activity/support",
|
|
|
|
{"realm_id": f"{realm.id}", "delete_user_by_id": hamlet.id},
|
|
|
|
)
|
|
|
|
m.assert_called_once_with(hamlet)
|
|
|
|
self.assert_in_success_response([f"{hamlet_email} in zulip deleted"], result)
|