def test_2_subscriptions_expiring_within_range( self, mock_license_expiration_task): """ Verifies that all expired subscriptions within the expired range have their license uuids sent to edx-enterprise """ expired_subscription_1 = SubscriptionPlanFactory.create( start_date=datetime.strptime('2013-1-01', '%Y-%m-%d'), expiration_date=datetime.strptime('2014-1-01', '%Y-%m-%d'), ) expired_subscription_2 = SubscriptionPlanFactory.create( start_date=datetime.strptime('2015-1-01', '%Y-%m-%d'), expiration_date=datetime.strptime('2016-1-01', '%Y-%m-%d'), ) # Create an activated license on each subscription expired_license_1 = LicenseFactory.create( status=ACTIVATED, subscription_plan=expired_subscription_1) expired_license_2 = LicenseFactory.create( status=ACTIVATED, subscription_plan=expired_subscription_2) call_command(self.command_name, "--expired-after=2013-1-01", "--expired-before=2016-1-01") actual_args = mock_license_expiration_task.call_args_list[0][0][0] assert sorted(actual_args) == sorted( [str(expired_license_1.uuid), str(expired_license_2.uuid)])
def test_1_subscription_expiring_outside_date_range( self, mock_license_expiration_task): """ Verifies that only expired subscriptions within the expired range have their license uuids sent to edx-enterprise """ # A recently expired subscription that should be processed expired_subscription = SubscriptionPlanFactory.create( start_date=self.today - timedelta(days=7), expiration_date=self.today, ) # An expired subscription that expired about a month ago older_expired_subscription = SubscriptionPlanFactory.create( start_date=self.today - timedelta(days=60), expiration_date=self.today - timedelta(days=30), ) # Create an activated license on each subscription license_to_expire_enrollments = LicenseFactory.create( status=ACTIVATED, subscription_plan=expired_subscription) LicenseFactory.create(status=ACTIVATED, subscription_plan=older_expired_subscription) call_command(self.command_name) mock_license_expiration_task.assert_called_with( [str(license_to_expire_enrollments.uuid)])
def setUpTestData(cls): super().setUpTestData() # technically this will be off on leap years, but we just need something later than now, so it's not a problem ONE_YEAR_FROM_NOW = localized_datetime_from_datetime(datetime.now() + timedelta(days=365)) TWO_YEARS_FROM_NOW = localized_datetime_from_datetime(datetime.now() + timedelta(days=730)) cls.user_email = '*****@*****.**' cls.enterprise_customer_uuid = uuid.uuid4() cls.customer_agreement = CustomerAgreementFactory.create( enterprise_customer_uuid=cls.enterprise_customer_uuid, ) cls.subscription_plan = SubscriptionPlanFactory() cls.active_current_plan = SubscriptionPlanFactory.create( customer_agreement=cls.customer_agreement, is_active=True, start_date=localized_datetime(2021, 1, 1), expiration_date=ONE_YEAR_FROM_NOW, ) cls.active_current_license = LicenseFactory.create( user_email=cls.user_email, subscription_plan=cls.active_current_plan, ) cls.inactive_current_plan = SubscriptionPlanFactory.create( customer_agreement=cls.customer_agreement, is_active=False, start_date=localized_datetime(2021, 1, 1), expiration_date=ONE_YEAR_FROM_NOW, ) cls.inactive_current_license = LicenseFactory.create( user_email=cls.user_email, subscription_plan=cls.inactive_current_plan, ) cls.non_current_active_plan = SubscriptionPlanFactory.create( customer_agreement=cls.customer_agreement, is_active=True, start_date=ONE_YEAR_FROM_NOW, expiration_date=TWO_YEARS_FROM_NOW, ) cls.non_current_active_license = LicenseFactory.create( user_email=cls.user_email, subscription_plan=cls.non_current_active_plan, ) cls.non_current_inactive_plan = SubscriptionPlanFactory.create( customer_agreement=cls.customer_agreement, is_active=False, start_date=ONE_YEAR_FROM_NOW, expiration_date=TWO_YEARS_FROM_NOW, ) cls.non_current_inactive_license = LicenseFactory.create( user_email=cls.user_email, subscription_plan=cls.non_current_inactive_plan, )
def test_prior_renewals(self): renewed_subscription_plan_1 = SubscriptionPlanFactory.create() renewed_subscription_plan_2 = SubscriptionPlanFactory.create() renewal_1 = SubscriptionPlanRenewalFactory.create( prior_subscription_plan=self.subscription_plan, renewed_subscription_plan=renewed_subscription_plan_1 ) renewal_2 = SubscriptionPlanRenewalFactory.create( prior_subscription_plan=renewed_subscription_plan_1, renewed_subscription_plan=renewed_subscription_plan_2 ) self.assertEqual(renewed_subscription_plan_2.prior_renewals, [renewal_1, renewal_2])
def _create_expired_plan_with_licenses(self, unassigned_licenses_count=1, assigned_licenses_count=2, activated_licenses_count=3, revoked_licenses_count=4, start_date=today - timedelta(days=7), expiration_date=today, expiration_processed=False): """ Creates a plan with licenses. The plan is expired by default. """ expired_plan = SubscriptionPlanFactory.create( start_date=start_date, expiration_date=expiration_date, expiration_processed=expiration_processed) LicenseFactory.create_batch(unassigned_licenses_count, status=UNASSIGNED, subscription_plan=expired_plan) LicenseFactory.create_batch(assigned_licenses_count, status=ASSIGNED, subscription_plan=expired_plan) LicenseFactory.create_batch(activated_licenses_count, status=ACTIVATED, subscription_plan=expired_plan) LicenseFactory.create_batch(revoked_licenses_count, status=REVOKED, subscription_plan=expired_plan) return expired_plan
def setUp(self): super().setUp() self.activated_user = UserFactory() self.assigned_user = UserFactory() self.unlicensed_user = UserFactory() self.enterprise_customer_uuid = uuid4() self.enterprise_catalog_uuid = uuid4() self.course_key = 'testX' self.customer_agreement = CustomerAgreementFactory( enterprise_customer_uuid=self.enterprise_customer_uuid, ) self.active_subscription_for_customer = SubscriptionPlanFactory.create( customer_agreement=self.customer_agreement, enterprise_catalog_uuid=self.enterprise_catalog_uuid, is_active=True, ) self.activated_license = LicenseFactory.create( status=constants.ACTIVATED, user_email=self.activated_user.email, subscription_plan=self.active_subscription_for_customer, ) self.assigned_license = LicenseFactory.create( status=constants.ASSIGNED, user_email=self.assigned_user.email, subscription_plan=self.active_subscription_for_customer, )
def test_1_subscription_expiring_today(self, mock_license_expiration_task): """ When there is a subscription expiring verify only the assigned and activated licenses are sent to edx-enterprise """ expired_subscription = SubscriptionPlanFactory.create( start_date=self.today - timedelta(days=7), expiration_date=self.today, ) # Create licenses with a variety of statuses LicenseFactory.create_batch(3, subscription_plan=expired_subscription) LicenseFactory.create_batch(2, status=ASSIGNED, subscription_plan=expired_subscription) LicenseFactory.create(status=ACTIVATED, subscription_plan=expired_subscription) LicenseFactory.create(status=REVOKED, subscription_plan=expired_subscription) call_command(self.command_name) expired_license_uuids = [ str(license.uuid) for license in expired_subscription.licenses.filter( status__in=[ASSIGNED, ACTIVATED]) ] mock_license_expiration_task.assert_called_with(expired_license_uuids)
def test_activated_license_is_revoked( self, is_revocation_cap_enabled, ): agreement = CustomerAgreementFactory.create( enterprise_customer_uuid=uuid.UUID( '00000000-1111-2222-3333-444444444444'), ) subscription_plan = SubscriptionPlanFactory.create( customer_agreement=agreement, is_revocation_cap_enabled=is_revocation_cap_enabled, num_revocations_applied=0, revoke_max_percentage=100, ) original_license = LicenseFactory.create( status=constants.ACTIVATED, subscription_plan=subscription_plan, lms_user_id=123, ) with freezegun.freeze_time(NOW): api.revoke_license(original_license) original_license.refresh_from_db() self.assertEqual(original_license.status, constants.REVOKED) self.assertEqual(original_license.revoked_date, NOW) # There should now be 1 unassigned license self.assertEqual(subscription_plan.unassigned_licenses.count(), 1)
def test_invalid_start_date_before_today(self): prior_subscription_plan = SubscriptionPlanFactory.create() form = make_bound_subscription_plan_renewal_form( prior_subscription_plan=prior_subscription_plan, effective_date=localized_utcnow() - timedelta(1), renewed_expiration_date=prior_subscription_plan.expiration_date + timedelta(366), ) assert not form.is_valid()
def test_is_locked_for_renewal_processing(self, is_locked_for_renewal_processing): today = localized_utcnow() with freezegun.freeze_time(today): renewed_subscription_plan = SubscriptionPlanFactory.create(expiration_date=today) renewal_kwargs = {'prior_subscription_plan': renewed_subscription_plan} if is_locked_for_renewal_processing: renewal_kwargs.update({'effective_date': renewed_subscription_plan.expiration_date}) SubscriptionPlanRenewalFactory.create(**renewal_kwargs) self.assertEqual(renewed_subscription_plan.is_locked_for_renewal_processing, is_locked_for_renewal_processing)
def test_valid_start_and_expiration_dates(self): prior_subscription_plan = SubscriptionPlanFactory.create() form = make_bound_subscription_plan_renewal_form( prior_subscription_plan=prior_subscription_plan, effective_date=prior_subscription_plan.expiration_date + timedelta(1), renewed_expiration_date=prior_subscription_plan.expiration_date + timedelta(366), ) assert form.is_valid()
def create_subscription_with_renewal(self, effective_date, processed=False): prior_subscription_plan = SubscriptionPlanFactory.create( start_date=self.now - timedelta(days=7), expiration_date=self.now, ) renewed_subscription_plan = SubscriptionPlanFactory.create( start_date=self.now, expiration_date=self.now + timedelta(days=7), ) SubscriptionPlanRenewalFactory.create( prior_subscription_plan=prior_subscription_plan, renewed_subscription_plan=renewed_subscription_plan, effective_date=effective_date, processed=processed) return (prior_subscription_plan)
def test_auto_applied_licenses_count_since(self): """ Tests that the correct auto-applied license count is returned. """ subscription_plan = SubscriptionPlanFactory.create(should_auto_apply_licenses=True) timestamp_1 = localized_utcnow() LicenseFactory.create_batch(1, subscription_plan=subscription_plan, auto_applied=True, activation_date=timestamp_1) LicenseFactory.create_batch(3, subscription_plan=subscription_plan, auto_applied=False, activation_date=timestamp_1) self.assertEqual(subscription_plan.auto_applied_licenses_count_since(), 1) timestamp_2 = timestamp_1 + timedelta(seconds=1) self.assertEqual(subscription_plan.auto_applied_licenses_count_since(timestamp_2), 0) LicenseFactory.create_batch(5, subscription_plan=subscription_plan, auto_applied=True, activation_date=timestamp_2) self.assertEqual(subscription_plan.auto_applied_licenses_count_since(timestamp_2), 5)
def test_auto_apply_licenses_turned_on_at(self): """ Tests that auto_apply_licenses_turned_on_at returns the correct time. """ subscription_plan = SubscriptionPlanFactory.create() subscription_plan.should_auto_apply_licenses = True subscription_plan.save() auto_apply_licenses_turned_on_at = subscription_plan.history.latest().history_date subscription_plan.is_active = True subscription_plan.save() latest_history_date = subscription_plan.history.latest().history_date self.assertEqual(subscription_plan.auto_apply_licenses_turned_on_at, auto_apply_licenses_turned_on_at) self.assertNotEqual(subscription_plan.auto_apply_licenses_turned_on_at, latest_history_date)
def test_expiring_10k_licenses_batched(self, mock_license_expiration_task): """ Verifies that all expired subscriptions within the expired range have their license uuids sent to edx-enterprise """ # A recently expired subscription that should be processed expired_subscription_1 = SubscriptionPlanFactory.create( start_date=self.today - timedelta(days=7), expiration_date=self.today, ) expired_subscription_2 = SubscriptionPlanFactory.create( start_date=self.today - timedelta(days=7), expiration_date=self.today, ) LicenseFactory.create_batch(500, status=ACTIVATED, subscription_plan=expired_subscription_1) LicenseFactory.create_batch(500, status=ACTIVATED, subscription_plan=expired_subscription_2) call_command(self.command_name) expected_call_count = math.ceil(1000 / LICENSE_EXPIRATION_BATCH_SIZE) assert expected_call_count == mock_license_expiration_task.call_count
def test_track_event_via_braze_alias(mock_braze_client): test_email = '*****@*****.**' assigned_license = LicenseFactory.create( subscription_plan=SubscriptionPlanFactory.create(), lms_user_id=5, user_email=test_email, status=ASSIGNED, auto_applied=True) test_event_name = 'test-event-name' test_event_properties = { 'license_uuid': str(assigned_license.uuid), 'enterprise_customer_slug': assigned_license.subscription_plan.customer_agreement. enterprise_customer_slug, } expected_user_alias = { "alias_name": test_email, "alias_label": ENTERPRISE_BRAZE_ALIAS_LABEL, } expected_attributes = { "user_alias": expected_user_alias, "email": test_email, "is_enterprise_learner": True, "_update_existing_only": False, "license_uuid": str(assigned_license.uuid), "enterprise_customer_slug": assigned_license.subscription_plan.customer_agreement. enterprise_customer_slug, } expected_event = { "user_alias": expected_user_alias, "name": test_event_name, "time": _iso_8601_format_string(localized_utcnow()), "properties": test_event_properties, "_update_existing_only": False, } _track_event_via_braze_alias(test_email, test_event_name, test_event_properties) mock_braze_client().create_braze_alias.assert_any_call( [test_email], ENTERPRISE_BRAZE_ALIAS_LABEL) mock_braze_client().track_user.assert_any_call( attributes=[expected_attributes], events=[expected_event])
def test_cannot_revoke_license_if_not_assigned_or_activated( self, license_status): subscription_plan = SubscriptionPlanFactory.create( is_revocation_cap_enabled=False, ) original_license = LicenseFactory.create( status=license_status, subscription_plan=subscription_plan, ) expected_msg = 'status of {} cannot be revoked'.format(license_status) with self.assertRaisesRegex(exceptions.LicenseRevocationError, expected_msg): api.revoke_license(original_license) original_license.refresh_from_db() self.assertEqual(original_license.status, license_status)
def test_revocation_limit_reached_raises_error( self, revoke_max_percentage, number_licenses_to_create, ): subscription_plan = SubscriptionPlanFactory.create( is_revocation_cap_enabled=True, revoke_max_percentage=revoke_max_percentage, # SubscriptionPlan.num_revocations_remaining defines the absolute revocation cap # as: # # revocations_remaining = # ceil(num_licenses * revoke_max_percentage) - num_revocations_applied # # So given the revoke max percentage and number of licenses to create, # we can derive the number of revocations previously applied in the plan # needed in order to raise an exception the "next time" we attempt a revocation # by setting revocations_remaining to 0 and solving for num_revocations_applied: # # 0 = ceil(num_licenses * revoke_max_percent) - num_revocations_applied # ==> # num_revocations_applied = ceil(num_licenses * revoke_max_percent) # # (the "next time we revoke" being the call to revoke_license() below). num_revocations_applied=ceil(revoke_max_percentage * number_licenses_to_create), ) original_licenses = LicenseFactory.create_batch( number_licenses_to_create, status=constants.ACTIVATED, subscription_plan=subscription_plan, ) for original_license in original_licenses: with self.assertRaisesRegex(exceptions.LicenseRevocationError, 'limit has been reached'): api.revoke_license(original_license) original_license.refresh_from_db() self.assertEqual(original_license.status, constants.ACTIVATED)
def test_get_license_tracking_properties(): test_email = '*****@*****.**' assigned_license = LicenseFactory.create( subscription_plan=SubscriptionPlanFactory.create(), lms_user_id=5, user_email=test_email, status=ASSIGNED, auto_applied=True) flat_data = get_license_tracking_properties(assigned_license) assert flat_data['license_uuid'] == str(assigned_license.uuid) assert flat_data['license_activation_key'] == str( assigned_license.activation_key) assert flat_data['previous_license_uuid'] == '' assert flat_data['assigned_date'] == _iso_8601_format_string( assigned_license.assigned_date) assert flat_data['activation_date'] == _iso_8601_format_string( assigned_license.activation_date) assert flat_data['assigned_lms_user_id'] == assigned_license.lms_user_id assert flat_data['assigned_email'] == test_email assert flat_data['auto_applied'] is True assert flat_data['enterprise_customer_uuid'] \ == str(assigned_license.subscription_plan.customer_agreement.enterprise_customer_uuid) assert flat_data['enterprise_customer_slug'] \ == assigned_license.subscription_plan.customer_agreement.enterprise_customer_slug assert flat_data['expiration_processed'] \ == assigned_license.subscription_plan.expiration_processed assert flat_data['customer_agreement_uuid'] \ == str(assigned_license.subscription_plan.customer_agreement.uuid) assert flat_data['enterprise_customer_name'] \ == assigned_license.subscription_plan.customer_agreement.enterprise_customer_name # Check that all the data is a basic type that can be serialized so it will be clean in segment: for k, v in flat_data.items(): assert isinstance(k, str) assert (isinstance(v, str) or isinstance(v, int) or isinstance(v, bool))