from datetime import datetime, timedelta, timezone from decimal import Decimal from typing import TYPE_CHECKING, Any, Optional from unittest import mock import orjson import time_machine from django.utils.timezone import now as timezone_now from typing_extensions import override from corporate.lib.stripe import RealmBillingSession, add_months from corporate.models import ( Customer, CustomerPlan, LicenseLedger, SponsoredPlanTypes, ZulipSponsorshipRequest, get_current_plan_by_customer, get_customer_by_realm, ) from zerver.actions.invites import do_create_multiuse_invite_link 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 from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_helpers import reset_email_visibility_to_everyone_in_zulip_realm from zerver.models import MultiuseInvite, PreregistrationUser, Realm, UserMessage, UserProfile from zerver.models.realms import OrgTypeEnum, get_org_type_display_name, get_realm from zilencer.lib.remote_counts import MissingDataError if TYPE_CHECKING: from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse import uuid from zilencer.models import RemoteZulipServer class TestRemoteServerSupportEndpoint(ZulipTestCase): @override def setUp(self) -> None: 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, ) super().setUp() # 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() ) # 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, ) 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, ) 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"}) self.assert_in_success_response(["

zulip-1.example.com

"], result) self.assert_in_success_response(["Max monthly messages: 1000"], result) self.assert_not_in_success_response(["

zulip-2.example.com

"], result) # Sponsorship request information self.assert_in_success_response(["
  • Organization type: Community
  • "], result) self.assert_in_success_response( ["
  • Organization website: No website submitted
  • "], result ) self.assert_in_success_response(["
  • Paid users: None
  • "], result) self.assert_in_success_response(["
  • Requested plan: Business
  • "], result) self.assert_in_success_response( ["
  • Organization description: We help people.
  • "], result ) self.assert_in_success_response(["
  • Estimated total users: 20-35
  • "], result) self.assert_in_success_response(["
  • Description of paid users:
  • "], result) 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(["

    zulip-1.example.com

    "], result) self.assert_in_success_response( ["Max monthly messages: Recent data missing"], result ) self.assert_not_in_success_response(["

    zulip-2.example.com

    "], result) result = self.client_get("/activity/remote/support", {"q": "example.com"}) for i in range(20): self.assert_in_success_response([f"

    zulip-{i}.example.com

    "], result) result = self.client_get("/activity/remote/support", {"q": "admin@zulip-2.example.com"}) self.assert_in_success_response(["

    zulip-2.example.com

    "], result) self.assert_in_success_response(["Contact email: admin@zulip-2.example.com"], result) self.assert_not_in_success_response(["

    zulip-1.example.com

    "], result) # Sponsorship request information self.assert_in_success_response( ["
  • Organization type: Open-source project
  • "], result ) self.assert_in_success_response( ["
  • Organization website: example.org
  • "], result ) self.assert_in_success_response(["
  • Paid users:
  • "], result) self.assert_in_success_response(["
  • Requested plan: Community
  • "], result) self.assert_in_success_response( ["
  • Organization description: We help people.
  • "], result ) self.assert_in_success_response(["
  • Estimated total users: 20-35
  • "], result) self.assert_in_success_response(["
  • Description of paid users:
  • "], result) result = self.client_get("/activity/remote/support", {"q": "admin@zulip-3.example.com"}) self.assert_in_success_response(["

    zulip-3.example.com

    "], result) self.assert_in_success_response(["Contact email: admin@zulip-3.example.com"], result) self.assert_not_in_success_response(["

    zulip-1.example.com

    "], result) # Sponsorship request information self.assert_not_in_success_response( ["
  • Organization description: We help people.
  • "], result ) self.assert_not_in_success_response( ["
  • Estimated total users: 20-35
  • "], result ) self.assert_not_in_success_response(["
  • Description of paid users:
  • "], result) class TestSupportEndpoint(ZulipTestCase): def create_customer_and_plan(self, realm: Realm, monthly: bool = False) -> Customer: now = datetime(2016, 1, 2, tzinfo=timezone.utc) billing_schedule = CustomerPlan.BILLING_SCHEDULE_ANNUAL price_per_license = 8000 months = 12 if monthly: billing_schedule = CustomerPlan.BILLING_SCHEDULE_MONTHLY price_per_license = 800 months = 1 customer = Customer.objects.create(realm=realm) plan = CustomerPlan.objects.create( customer=customer, billing_cycle_anchor=now, billing_schedule=billing_schedule, tier=CustomerPlan.TIER_CLOUD_STANDARD, price_per_license=price_per_license, next_invoice_date=add_months(now, months), ) LicenseLedger.objects.create( licenses=10, licenses_at_next_renewal=10, event_time=timezone_now(), is_renewal=True, plan=plan, ) return customer def test_search(self) -> None: reset_email_visibility_to_everyone_in_zulip_realm() lear_user = self.lear_user("king") lear_user.is_staff = True lear_user.save(update_fields=["is_staff"]) lear_realm = get_realm("lear") def assert_user_details_in_html_response( html_response: "TestHttpResponse", full_name: str, email: str, role: str ) -> None: self.assert_in_success_response( [ 'user\n', f"

    {full_name}

    ", f"Email: {email}", "Is active: True
    ", f"Role: {role}
    ", ], html_response, ) 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", ) def check_hamlet_user_query_result(result: "TestHttpResponse") -> None: assert_user_details_in_html_response( result, "King Hamlet", self.example_email("hamlet"), "Member" ) self.assert_in_success_response( [ f"Admins: {self.example_email('iago')}\n", f"Owners: {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") ), ], result, ) def check_lear_user_query_result(result: "TestHttpResponse") -> None: assert_user_details_in_html_response( result, lear_user.full_name, lear_user.email, "Member" ) def check_othello_user_query_result(result: "TestHttpResponse") -> None: assert_user_details_in_html_response( result, "Othello, the Moor of Venice", self.example_email("othello"), "Member" ) def check_polonius_user_query_result(result: "TestHttpResponse") -> None: assert_user_details_in_html_response( result, "Polonius", self.example_email("polonius"), "Guest" ) def check_zulip_realm_query_result(result: "TestHttpResponse") -> None: zulip_realm = get_realm("zulip") first_human_user = zulip_realm.get_first_human_user() assert first_human_user is not None self.assert_in_success_response( [ f"First human user: {first_human_user.delivery_email}\n", f'", '', '', 'input type="number" name="discount" value="None"', '', '', f'', '', 'input type="number" name="discount" value="None"', '', '', 'scrub-realm-button">', 'data-string-id="lear"', "Plan name: Zulip Cloud Standard", "Status: Active", "Billing schedule: Annual", "Licenses: 2/10 (Manual)", "Price per license: $80.0", "Next invoice date: 02 January 2017", '