def test_initial_verification_for_user(self): """Test that method 'get_initial_verification' of model 'SoftwareSecurePhotoVerification' always returns the initial verification with field 'photo_id_key' set against a user. """ user = UserFactory.create() # No initial verification for the user result = SoftwareSecurePhotoVerification.get_initial_verification(user=user) self.assertIs(result, None) # Make an initial verification with 'photo_id_key' attempt = SoftwareSecurePhotoVerification(user=user, photo_id_key="dummy_photo_id_key") attempt.status = 'approved' attempt.save() # Check that method 'get_initial_verification' returns the correct # initial verification attempt first_result = SoftwareSecurePhotoVerification.get_initial_verification(user=user) self.assertIsNotNone(first_result) # Now create a second verification without 'photo_id_key' attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'submitted' attempt.save() # Test method 'get_initial_verification' still returns the correct # initial verification attempt which have 'photo_id_key' set second_result = SoftwareSecurePhotoVerification.get_initial_verification(user=user) self.assertIsNotNone(second_result) self.assertEqual(second_result, first_result)
def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('none', '')) # test for when one has been created attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'approved' attempt.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # create another one for the same user, make sure the right one is # returned attempt2 = SoftwareSecurePhotoVerification(user=user) attempt2.status = 'denied' attempt2.error_msg = '[{"photoIdReasons": ["Not provided"]}]' attempt2.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # now delete the first one and verify that the denial is being handled # properly attempt.delete() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('must_reverify', "No photo ID was provided."))
def test_state_transitions(self): """ Make sure we can't make unexpected status transitions. The status transitions we expect are:: → → → must_retry ↑ ↑ ↓ created → ready → submitted → approved ↓ ↑ ↓ ↓ → → denied """ user = UserFactory.create() attempt = SoftwareSecurePhotoVerification(user=user) assert_equals(attempt.status, "created") # These should all fail because we're in the wrong starting state. assert_raises(VerificationException, attempt.submit) assert_raises(VerificationException, attempt.approve) assert_raises(VerificationException, attempt.deny) # Now let's fill in some values so that we can pass the mark_ready() call attempt.mark_ready() assert_equals(attempt.status, "ready") # ready (can't approve or deny unless it's "submitted") assert_raises(VerificationException, attempt.approve) assert_raises(VerificationException, attempt.deny) DENY_ERROR_MSG = '[{"photoIdReasons": ["Not provided"]}]' # must_retry attempt.status = "must_retry" attempt.system_error("System error") attempt.approve() attempt.status = "must_retry" attempt.deny(DENY_ERROR_MSG) # submitted attempt.status = "submitted" attempt.deny(DENY_ERROR_MSG) attempt.status = "submitted" attempt.approve() # approved assert_raises(VerificationException, attempt.submit) attempt.approve() # no-op attempt.system_error("System error") # no-op, something processed it without error attempt.deny(DENY_ERROR_MSG) # denied assert_raises(VerificationException, attempt.submit) attempt.deny(DENY_ERROR_MSG) # no-op attempt.system_error("System error") # no-op, something processed it without error attempt.approve()
def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('none', '')) # test for when one has been created attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'approved' attempt.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # create another one for the same user, make sure the right one is # returned attempt2 = SoftwareSecurePhotoVerification(user=user) attempt2.status = 'denied' attempt2.error_msg = '[{"photoIdReasons": ["Not provided"]}]' attempt2.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # now delete the first one and verify that the denial is being handled # properly attempt.delete() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('must_reverify', "No photo ID was provided.")) # test for correct status for reverifications window = MidcourseReverificationWindowFactory() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ('must_reverify', '')) reverify_attempt = SoftwareSecurePhotoVerification(user=user, window=window) reverify_attempt.status = 'approved' reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ('approved', '')) reverify_attempt.status = 'denied' reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ('denied', '')) reverify_attempt.status = 'approved' # pylint: disable=protected-access reverify_attempt.created_at = SoftwareSecurePhotoVerification._earliest_allowed_date() + timedelta(days=-1) reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) message = 'Your {platform_name} verification has expired.'.format(platform_name=settings.PLATFORM_NAME) self.assertEquals(reverify_status, ('expired', message))
def test_parse_error_msg_success(self): user = UserFactory.create() attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'denied' attempt.error_msg = '[{"photoIdReasons": ["Not provided"]}]' parsed_error_msg = attempt.parsed_error_msg() self.assertEquals("No photo ID was provided.", parsed_error_msg)
def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('none', '')) # test for when one has been created attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'approved' attempt.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # create another one for the same user, make sure the right one is # returned attempt2 = SoftwareSecurePhotoVerification(user=user) attempt2.status = 'denied' attempt2.error_msg = '[{"photoIdReasons": ["Not provided"]}]' attempt2.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # now delete the first one and verify that the denial is being handled # properly attempt.delete() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('must_reverify', "No photo ID was provided.")) # test for correct status for reverifications window = MidcourseReverificationWindowFactory() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ('must_reverify', '')) reverify_attempt = SoftwareSecurePhotoVerification(user=user, window=window) reverify_attempt.status = 'approved' reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ('approved', '')) reverify_attempt.status = 'denied' reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ('denied', ''))
def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ("none", "")) # test for when one has been created attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = "approved" attempt.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ("approved", "")) # create another one for the same user, make sure the right one is # returned attempt2 = SoftwareSecurePhotoVerification(user=user) attempt2.status = "denied" attempt2.error_msg = '[{"photoIdReasons": ["Not provided"]}]' attempt2.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ("approved", "")) # now delete the first one and verify that the denial is being handled # properly attempt.delete() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ("must_reverify", "No photo ID was provided.")) # test for correct status for reverifications window = MidcourseReverificationWindowFactory() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ("must_reverify", "")) reverify_attempt = SoftwareSecurePhotoVerification(user=user, window=window) reverify_attempt.status = "approved" reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ("approved", "")) reverify_attempt.status = "denied" reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ("denied", ""))
def test_user_is_reverified_for_all(self): # if there are no windows for a course, this should return True self.assertTrue( SoftwareSecurePhotoVerification.user_is_reverified_for_all( self.course.id, self.user)) # first, make three windows window1 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=15), end_date=datetime.now(pytz.UTC) - timedelta(days=13), ) window2 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=10), end_date=datetime.now(pytz.UTC) - timedelta(days=8), ) window3 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=5), end_date=datetime.now(pytz.UTC) - timedelta(days=3), ) # make two SSPMidcourseReverifications for those windows attempt1 = SoftwareSecurePhotoVerification(status="approved", user=self.user, window=window1) attempt1.save() attempt2 = SoftwareSecurePhotoVerification(status="approved", user=self.user, window=window2) attempt2.save() # should return False because only 2 of 3 windows have verifications self.assertFalse( SoftwareSecurePhotoVerification.user_is_reverified_for_all( self.course.id, self.user)) attempt3 = SoftwareSecurePhotoVerification(status="must_retry", user=self.user, window=window3) attempt3.save() # should return False because the last verification exists BUT is not approved self.assertFalse( SoftwareSecurePhotoVerification.user_is_reverified_for_all( self.course.id, self.user)) attempt3.status = "approved" attempt3.save() # should now return True because all windows have approved verifications self.assertTrue( SoftwareSecurePhotoVerification.user_is_reverified_for_all( self.course.id, self.user))
def test_user_is_verified(self): """ Test to make sure we correctly answer whether a user has been verified. """ user = UserFactory.create() attempt = SoftwareSecurePhotoVerification(user=user) attempt.save() # If it's any of these, they're not verified... for status in ["created", "ready", "denied", "submitted", "must_retry"]: attempt.status = status attempt.save() assert_false(SoftwareSecurePhotoVerification.user_is_verified(user), status) attempt.status = "approved" attempt.save() assert_true(SoftwareSecurePhotoVerification.user_is_verified(user), attempt.status)
def test_user_is_reverified_for_all(self): # if there are no windows for a course, this should return True self.assertTrue(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user)) # first, make three windows window1 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=15), end_date=datetime.now(pytz.UTC) - timedelta(days=13), ) window2 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=10), end_date=datetime.now(pytz.UTC) - timedelta(days=8), ) window3 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=5), end_date=datetime.now(pytz.UTC) - timedelta(days=3), ) # make two SSPMidcourseReverifications for those windows attempt1 = SoftwareSecurePhotoVerification( status="approved", user=self.user, window=window1 ) attempt1.save() attempt2 = SoftwareSecurePhotoVerification( status="approved", user=self.user, window=window2 ) attempt2.save() # should return False because only 2 of 3 windows have verifications self.assertFalse(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user)) attempt3 = SoftwareSecurePhotoVerification( status="must_retry", user=self.user, window=window3 ) attempt3.save() # should return False because the last verification exists BUT is not approved self.assertFalse(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user)) attempt3.status = "approved" attempt3.save() # should now return True because all windows have approved verifications self.assertTrue(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user))
def test_user_has_valid_or_pending(self): """ Determine whether we have to prompt this user to verify, or if they've already at least initiated a verification submission. """ user = UserFactory.create() attempt = SoftwareSecurePhotoVerification(user=user) # If it's any of these statuses, they don't have anything outstanding for status in ["created", "ready", "denied"]: attempt.status = status attempt.save() assert_false(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user), status) # Any of these, and we are. Note the benefit of the doubt we're giving # -- must_retry, and submitted both count until we hear otherwise for status in ["submitted", "must_retry", "approved"]: attempt.status = status attempt.save() assert_true(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user), status)
def test_parse_error_msg_failure(self): user = UserFactory.create() attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'denied' # when we can't parse into json bad_messages = { 'Not Provided', '[{"IdReasons": ["Not provided"]}]', '{"IdReasons": ["Not provided"]}', u'[{"ïḋṚëäṡöṅṡ": ["Ⓝⓞⓣ ⓟⓡⓞⓥⓘⓓⓔⓓ "]}]', } for msg in bad_messages: attempt.error_msg = msg parsed_error_msg = attempt.parsed_error_msg() self.assertEquals(parsed_error_msg, "There was an error verifying your ID photos.")
def test_user_has_valid_or_pending(self): window = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=15), end_date=datetime.now(pytz.UTC) - timedelta(days=13), ) attempt = SoftwareSecurePhotoVerification(status="must_retry", user=self.user, window=window) attempt.save() assert_false(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user=self.user, window=window)) attempt.status = "approved" attempt.save() assert_true(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user=self.user, window=window))
def test_active_for_user(self): """ Make sure we can retrive a user's active (in progress) verification attempt. """ user = UserFactory.create() # This user has no active at the moment... assert_is_none(SoftwareSecurePhotoVerification.active_for_user(user)) # Create an attempt and mark it ready... attempt = SoftwareSecurePhotoVerification(user=user) attempt.mark_ready() assert_equals(attempt, SoftwareSecurePhotoVerification.active_for_user(user)) # A new user won't see this... user2 = UserFactory.create() user2.save() assert_is_none(SoftwareSecurePhotoVerification.active_for_user(user2)) # If it's got a different status, it doesn't count for status in ["submitted", "must_retry", "approved", "denied"]: attempt.status = status attempt.save() assert_is_none( SoftwareSecurePhotoVerification.active_for_user(user)) # But if we create yet another one and mark it ready, it passes again. attempt_2 = SoftwareSecurePhotoVerification(user=user) attempt_2.mark_ready() assert_equals(attempt_2, SoftwareSecurePhotoVerification.active_for_user(user)) # And if we add yet another one with a later created time, we get that # one instead. We always want the most recent attempt marked ready() attempt_3 = SoftwareSecurePhotoVerification( user=user, created_at=attempt_2.created_at + timedelta(days=1)) attempt_3.save() # We haven't marked attempt_3 ready yet, so attempt_2 still wins assert_equals(attempt_2, SoftwareSecurePhotoVerification.active_for_user(user)) # Now we mark attempt_3 ready and expect it to come back attempt_3.mark_ready() assert_equals(attempt_3, SoftwareSecurePhotoVerification.active_for_user(user))
def create_order(request): """ Submit PhotoVerification and create a new Order for this verified cert """ if not SoftwareSecurePhotoVerification.user_has_valid_or_pending( request.user): attempt = SoftwareSecurePhotoVerification(user=request.user) attempt.status = "ready" attempt.save() course_id = request.POST['course_id'] donation_for_course = request.session.get('donation_for_course', {}) current_donation = donation_for_course.get(course_id, decimal.Decimal(0)) contribution = request.POST.get("contribution", donation_for_course.get(course_id, 0)) try: amount = decimal.Decimal(contribution).quantize( decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN) except decimal.InvalidOperation: return HttpResponseBadRequest(_("Selected price is not valid number.")) if amount != current_donation: donation_for_course[course_id] = amount request.session['donation_for_course'] = donation_for_course verified_mode = CourseMode.modes_for_course_dict(course_id).get( 'verified', None) # make sure this course has a verified mode if not verified_mode: return HttpResponseBadRequest( _("This course doesn't support verified certificates")) if amount < verified_mode.min_price: return HttpResponseBadRequest( _("No selected price or selected price is below minimum.")) # I know, we should check this is valid. All kinds of stuff missing here cart = Order.get_cart_for_user(request.user) cart.clear() CertificateItem.add_to_order(cart, course_id, amount, 'verified') params = get_signed_purchase_params(cart) return HttpResponse(json.dumps(params), content_type="text/json")
def test_active_for_user(self): """ Make sure we can retrive a user's active (in progress) verification attempt. """ user = UserFactory.create() # This user has no active at the moment... assert_is_none(SoftwareSecurePhotoVerification.active_for_user(user)) # Create an attempt and mark it ready... attempt = SoftwareSecurePhotoVerification(user=user) attempt.mark_ready() assert_equals(attempt, SoftwareSecurePhotoVerification.active_for_user(user)) # A new user won't see this... user2 = UserFactory.create() user2.save() assert_is_none(SoftwareSecurePhotoVerification.active_for_user(user2)) # If it's got a different status, it doesn't count for status in ["submitted", "must_retry", "approved", "denied"]: attempt.status = status attempt.save() assert_is_none(SoftwareSecurePhotoVerification.active_for_user(user)) # But if we create yet another one and mark it ready, it passes again. attempt_2 = SoftwareSecurePhotoVerification(user=user) attempt_2.mark_ready() assert_equals(attempt_2, SoftwareSecurePhotoVerification.active_for_user(user)) # And if we add yet another one with a later created time, we get that # one instead. We always want the most recent attempt marked ready() attempt_3 = SoftwareSecurePhotoVerification( user=user, created_at=attempt_2.created_at + timedelta(days=1) ) attempt_3.save() # We haven't marked attempt_3 ready yet, so attempt_2 still wins assert_equals(attempt_2, SoftwareSecurePhotoVerification.active_for_user(user)) # Now we mark attempt_3 ready and expect it to come back attempt_3.mark_ready() assert_equals(attempt_3, SoftwareSecurePhotoVerification.active_for_user(user))
def create_order(request): """ Submit PhotoVerification and create a new Order for this verified cert """ if not SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user): attempt = SoftwareSecurePhotoVerification(user=request.user) attempt.status = "ready" attempt.save() course_id = request.POST['course_id'] donation_for_course = request.session.get('donation_for_course', {}) current_donation = donation_for_course.get(course_id, decimal.Decimal(0)) contribution = request.POST.get("contribution", donation_for_course.get(course_id, 0)) try: amount = decimal.Decimal(contribution).quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN) except decimal.InvalidOperation: return HttpResponseBadRequest(_("Selected price is not valid number.")) if amount != current_donation: donation_for_course[course_id] = amount request.session['donation_for_course'] = donation_for_course verified_mode = CourseMode.modes_for_course_dict(course_id).get('verified', None) # make sure this course has a verified mode if not verified_mode: return HttpResponseBadRequest(_("This course doesn't support verified certificates")) if amount < verified_mode.min_price: return HttpResponseBadRequest(_("No selected price or selected price is below minimum.")) # I know, we should check this is valid. All kinds of stuff missing here cart = Order.get_cart_for_user(request.user) cart.clear() CertificateItem.add_to_order(cart, course_id, amount, 'verified') params = get_signed_purchase_params(cart) return HttpResponse(json.dumps(params), content_type="text/json")
def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('none', '')) # test for when one has been created attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'approved' attempt.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # create another one for the same user, make sure the right one is # returned attempt2 = SoftwareSecurePhotoVerification(user=user) attempt2.status = 'denied' attempt2.error_msg = '[{"photoIdReasons": ["Not provided"]}]' attempt2.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # now delete the first one and verify that the denial is being handled # properly attempt.delete() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('must_reverify', "No photo ID was provided.")) # test for correct status for reverifications window = MidcourseReverificationWindowFactory() reverify_status = SoftwareSecurePhotoVerification.user_status( user=user, window=window) self.assertEquals(reverify_status, ('must_reverify', '')) reverify_attempt = SoftwareSecurePhotoVerification(user=user, window=window) reverify_attempt.status = 'approved' reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status( user=user, window=window) self.assertEquals(reverify_status, ('approved', '')) reverify_attempt.status = 'denied' reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status( user=user, window=window) self.assertEquals(reverify_status, ('denied', '')) reverify_attempt.status = 'approved' # pylint: disable=protected-access reverify_attempt.created_at = SoftwareSecurePhotoVerification._earliest_allowed_date( ) + timedelta(days=-1) reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status( user=user, window=window) message = 'Your {platform_name} verification has expired.'.format( platform_name=settings.PLATFORM_NAME) self.assertEquals(reverify_status, ('expired', message))