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, RemoteRealmBillingSession, 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 RemoteRealm, RemoteZulipServer, RemoteZulipServerAuditLog class TestRemoteServerSupportEndpoint(ZulipTestCase): @override def setUp(self) -> None: def add_sponsorship_request( name: str, org_type: int, website: str, paid_users: str, plan: str ) -> None: remote_realm = RemoteRealm.objects.get(name=name) customer = Customer.objects.create(remote_realm=remote_realm, 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, ) def upgrade_legacy_plan(legacy_plan: CustomerPlan) -> None: billed_licenses = 10 assert legacy_plan.end_date is not None last_ledger_entry = ( LicenseLedger.objects.filter(plan=legacy_plan).order_by("-id").first() ) assert last_ledger_entry is not None last_ledger_entry.licenses_at_next_renewal = billed_licenses last_ledger_entry.save(update_fields=["licenses_at_next_renewal"]) legacy_plan.status = CustomerPlan.SWITCH_PLAN_TIER_AT_PLAN_END legacy_plan.save(update_fields=["status"]) plan_params = { "automanage_licenses": True, "charge_automatically": False, "price_per_license": 100, "discount": legacy_plan.customer.default_discount, "billing_cycle_anchor": legacy_plan.end_date, "billing_schedule": CustomerPlan.BILLING_SCHEDULE_MONTHLY, "tier": CustomerPlan.TIER_SELF_HOSTED_BASIC, "status": CustomerPlan.NEVER_STARTED, } CustomerPlan.objects.create( customer=legacy_plan.customer, next_invoice_date=legacy_plan.end_date, **plan_params ) def add_legacy_plan(name: str, upgrade: bool) -> None: legacy_anchor = datetime(2050, 1, 1, tzinfo=timezone.utc) next_plan_anchor = datetime(2050, 2, 1, tzinfo=timezone.utc) remote_realm = RemoteRealm.objects.get(name=name) billing_session = RemoteRealmBillingSession(remote_realm) billing_session.migrate_customer_to_legacy_plan(legacy_anchor, next_plan_anchor) customer = billing_session.get_customer() assert customer is not None legacy_plan = billing_session.get_remote_server_legacy_plan(customer) assert legacy_plan is not None assert legacy_plan.end_date is not None if upgrade: upgrade_legacy_plan(legacy_plan) super().setUp() # Set up some initial example data. for i in range(6): hostname = f"zulip-{i}.example.com" remote_server = RemoteZulipServer.objects.create( hostname=hostname, contact_email=f"admin@{hostname}", uuid=uuid.uuid4() ) RemoteZulipServerAuditLog.objects.create( event_type=RemoteZulipServerAuditLog.REMOTE_SERVER_CREATED, server=remote_server, event_time=remote_server.last_updated, ) # We want at least one RemoteZulipServer that has no RemoteRealm # as an example of a pre-8.0 release registered remote server. if i > 1: realm_name = f"realm-name-{i}" realm_host = f"realm-host-{i}" realm_uuid = uuid.uuid4() RemoteRealm.objects.create( server=remote_server, uuid=realm_uuid, host=realm_host, name=realm_name, realm_date_created=datetime(2023, 12, 1, tzinfo=timezone.utc), ) # Add a deactivated server, which should be excluded from search results. server = RemoteZulipServer.objects.get(hostname="zulip-0.example.com") server.deactivated = True server.save(update_fields=["deactivated"]) # Add example sponsorship request data add_sponsorship_request( name="realm-name-2", org_type=OrgTypeEnum.Community.value, website="", paid_users="None", plan=SponsoredPlanTypes.BUSINESS.value, ) add_sponsorship_request( name="realm-name-3", org_type=OrgTypeEnum.OpenSource.value, website="example.org", paid_users="", plan=SponsoredPlanTypes.COMMUNITY.value, ) # Add expected legacy customer and plan data: # with upgrade scheduled add_legacy_plan(name="realm-name-4", upgrade=True) # without upgrade scheduled add_legacy_plan(name="realm-name-5", upgrade=False) def test_search(self) -> None: def assert_server_details_in_response( html_response: "TestHttpResponse", hostname: str ) -> None: self.assert_in_success_response( [ f"