from datetime import timedelta from django.test import TestCase from django.utils import timezone from labs.billing.exceptions import InvalidVoucherCodeError from labs.billing.voucher import redeem_voucher from labs.models import ( Exam, ExamAttempt, ExamVariant, ExamVoucher, User, UserExamRedemption, ) class ExamVoucherTests(TestCase): fixtures = [ "labs/tests/fixtures/user0.json", ] def setUp(self): self.user = User.objects.get(email="test2@example.com") self.employee_user = User.objects.get(email="employee1@pingcap.com") def test_redeem_exam_voucher_success(self): exam = Exam.objects.create(code="exam_paid", credits=1, published=True) voucher_code = "EXAM-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE" voucher = ExamVoucher.objects.create( unique_code=voucher_code, exam=exam, valid_before=(timezone.now() + timedelta(days=30)).date(), used=ExamVoucher.UsedChoices.N, marketing_code="test", ) redeem_voucher(self.user, voucher_code.lower()) voucher.refresh_from_db() self.assertEqual(voucher.used, ExamVoucher.UsedChoices.Y) self.assertIsNotNone(voucher.redeem_at) self.assertEqual(voucher.redeem_by, self.user) redemption = UserExamRedemption.objects.get( user=self.user, exam=exam, exam_attempt__isnull=True, ) self.assertEqual(redemption.redeem_type, UserExamRedemption.RedeemType.VOUCHER) self.assertEqual( redemption.redeem_detail, { "voucher_code": voucher_code, "marketing_code": "test", }, ) def test_redeem_exam_voucher_unpublished_exam_fails(self): exam = Exam.objects.create(code="exam_paid", credits=1, published=False) voucher_code = "EXAM-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE" voucher = ExamVoucher.objects.create( unique_code=voucher_code, exam=exam, valid_before=(timezone.now() + timedelta(days=30)).date(), used=ExamVoucher.UsedChoices.N, marketing_code="test", ) with self.assertRaises(InvalidVoucherCodeError) as ctx: redeem_voucher(self.user, voucher_code.lower()) self.assertEqual( str(ctx.exception), f"Voucher code {voucher_code} does not exist.", ) voucher.refresh_from_db() self.assertEqual(voucher.used, ExamVoucher.UsedChoices.N) self.assertEqual( UserExamRedemption.objects.filter( user=self.user, exam=exam, ).count(), 0, ) def test_redeem_exam_voucher_used_fails(self): exam = Exam.objects.create(code="exam_paid", credits=1, published=True) voucher_code = "EXAM-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE" ExamVoucher.objects.create( unique_code=voucher_code, exam=exam, valid_before=(timezone.now() + timedelta(days=30)).date(), used=ExamVoucher.UsedChoices.N, marketing_code="test", ) redeem_voucher(self.user, voucher_code) with self.assertRaises(InvalidVoucherCodeError): redeem_voucher(self.user, voucher_code) self.assertEqual( UserExamRedemption.objects.filter( user=self.user, exam=exam, exam_attempt__isnull=True, ).count(), 1, ) def test_redeem_exam_voucher_expired_fails(self): exam = Exam.objects.create(code="exam_paid", credits=1, published=True) voucher_code = "EXAM-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE" voucher = ExamVoucher.objects.create( unique_code=voucher_code, exam=exam, valid_before=(timezone.now() - timedelta(days=30)).date(), used=ExamVoucher.UsedChoices.N, marketing_code="test", ) with self.assertRaises(InvalidVoucherCodeError): redeem_voucher(self.user, voucher_code) voucher.refresh_from_db() self.assertEqual(voucher.used, ExamVoucher.UsedChoices.N) self.assertEqual( UserExamRedemption.objects.filter( user=self.user, exam=exam, ).count(), 0, ) def test_redeem_exam_voucher_fails_when_unused_redemption_exists(self): exam = Exam.objects.create(code="exam_paid", credits=1, published=True) UserExamRedemption.objects.create( user=self.user, exam=exam, exam_attempt=None, redeem_type=UserExamRedemption.RedeemType.CREDITS, redeem_detail={"credits": 1}, ) voucher_code = "EXAM-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE" voucher = ExamVoucher.objects.create( unique_code=voucher_code, exam=exam, valid_before=(timezone.now() + timedelta(days=30)).date(), used=ExamVoucher.UsedChoices.N, marketing_code="test", ) with self.assertRaises(InvalidVoucherCodeError): redeem_voucher(self.user, voucher_code) voucher.refresh_from_db() self.assertEqual(voucher.used, ExamVoucher.UsedChoices.N) def test_redeem_exam_voucher_employee_only_fails_for_non_employee(self): exam = Exam.objects.create( code="exam_employee_paid", credits=1, employee_only=True, published=True ) voucher_code = "EXAM-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE" voucher = ExamVoucher.objects.create( unique_code=voucher_code, exam=exam, valid_before=(timezone.now() + timedelta(days=30)).date(), used=ExamVoucher.UsedChoices.N, marketing_code="test", ) with self.assertRaises(InvalidVoucherCodeError): redeem_voucher(self.user, voucher_code) voucher.refresh_from_db() self.assertEqual(voucher.used, ExamVoucher.UsedChoices.N) def test_redeem_exam_voucher_fails_when_user_already_passed(self): exam = Exam.objects.create(code="exam_paid", credits=1, published=True) variant = ExamVariant.objects.create( exam=exam, language_code="en", title="Exam Paid (EN)", ) ExamAttempt.objects.create( user=self.user, exam=exam, variant=variant, language_code="en", is_passed=True, status=ExamAttempt.Status.COMPLETED, ) voucher_code = "EXAM-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE" voucher = ExamVoucher.objects.create( unique_code=voucher_code, exam=exam, valid_before=(timezone.now() + timedelta(days=30)).date(), used=ExamVoucher.UsedChoices.N, marketing_code="test", ) with self.assertRaises(InvalidVoucherCodeError): redeem_voucher(self.user, voucher_code) voucher.refresh_from_db() self.assertEqual(voucher.used, ExamVoucher.UsedChoices.N) def test_redeem_exam_voucher_fails_for_pingcap_employee(self): exam = Exam.objects.create(code="exam_paid", credits=1, published=True) voucher_code = "EXAM-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE" voucher = ExamVoucher.objects.create( unique_code=voucher_code, exam=exam, valid_before=(timezone.now() + timedelta(days=30)).date(), used=ExamVoucher.UsedChoices.N, marketing_code="test", ) with self.assertRaises(InvalidVoucherCodeError): redeem_voucher(self.employee_user, voucher_code) voucher.refresh_from_db() self.assertEqual(voucher.used, ExamVoucher.UsedChoices.N) def test_redeem_exam_voucher_fails_for_free_exam(self): exam = Exam.objects.create(code="exam_free", credits=0, published=True) voucher_code = "EXAM-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE" voucher = ExamVoucher.objects.create( unique_code=voucher_code, exam=exam, valid_before=(timezone.now() + timedelta(days=30)).date(), used=ExamVoucher.UsedChoices.N, marketing_code="test", ) with self.assertRaises(InvalidVoucherCodeError): redeem_voucher(self.user, voucher_code) voucher.refresh_from_db() self.assertEqual(voucher.used, ExamVoucher.UsedChoices.N)