summaryrefslogtreecommitdiff
path: root/addons/website_slides_survey/tests
diff options
context:
space:
mode:
authorAurélien Warnon <awa@odoo.com>2019-03-08 10:24:10 +0000
committerAurélien Warnon <awa@odoo.com>2019-03-18 14:46:32 +0000
commit199ecdd30faec64e463d69a9dd34a6e911209ffb (patch)
tree32d5bece7515461a61c97c7921b4be8bdb8878d7 /addons/website_slides_survey/tests
parent2fb9fe279aebc24f642d1d5778cdc10012ff960f (diff)
[IMP] website_slides_survey: handle certification re-enroll/purchase flow
Task #1945036 Purpose ======= If the user fails his last attempt at a course certification, we remove him from the members of the course (and he has to enroll again). He receives an email in the process notifying him of his failure and suggesting he enrolls to the course again. The purpose is to have a 'certification flow' where the user can re-purchase the certification when they have failed it. This could lead to some issues if the course containing the certification also has other slides with content because the user will not have access to them after failing. This also prevents configuring courses with multiple certifications since the membership will be removed at the first failure. These use cases are considered "non standard" by the business and are thus not handled in the code. We assume that users will configure their courses "correctly".
Diffstat (limited to 'addons/website_slides_survey/tests')
-rw-r--r--addons/website_slides_survey/tests/__init__.py4
-rw-r--r--addons/website_slides_survey/tests/test_course_certification_failure.py122
2 files changed, 126 insertions, 0 deletions
diff --git a/addons/website_slides_survey/tests/__init__.py b/addons/website_slides_survey/tests/__init__.py
new file mode 100644
index 00000000000..c2f581dd98c
--- /dev/null
+++ b/addons/website_slides_survey/tests/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import test_course_certification_failure
diff --git a/addons/website_slides_survey/tests/test_course_certification_failure.py b/addons/website_slides_survey/tests/test_course_certification_failure.py
new file mode 100644
index 00000000000..bdb72b5d294
--- /dev/null
+++ b/addons/website_slides_survey/tests/test_course_certification_failure.py
@@ -0,0 +1,122 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.survey.tests.common import SurveyCase
+
+
+class TestCourseCertificationFailureFlow(SurveyCase):
+ def test_course_certification_failure_flow(self):
+ # Step 1: create a simple certification
+ # --------------------------------------------------
+ with self.sudo(self.survey_user):
+ certification = self.env['survey.survey'].create({
+ 'title': 'Small course certification',
+ 'access_mode': 'public',
+ 'users_login_required': True,
+ 'scoring_type': 'scoring_with_answers',
+ 'certificate': True,
+ 'is_attempts_limited': True,
+ 'passing_score': 100.0,
+ 'attempts_limit': 2,
+ 'stage_id': self.env['survey.stage'].search([('closed', '=', False)]).id
+ })
+
+ self._add_question(
+ None, 'Question 1', 'simple_choice',
+ sequence=1,
+ survey_id=certification.id,
+ labels=[
+ {'value': 'Wrong answer'},
+ {'value': 'Correct answer', 'is_correct': True, 'answer_score': 1.0}
+ ])
+
+ self._add_question(
+ None, 'Question 2', 'simple_choice',
+ sequence=2,
+ survey_id=certification.id,
+ labels=[
+ {'value': 'Wrong answer'},
+ {'value': 'Correct answer', 'is_correct': True, 'answer_score': 1.0}
+ ])
+
+ # Step 1.1: create a simple channel
+ self.channel = self.env['slide.channel'].sudo().create({
+ 'name': 'Test Channel',
+ 'channel_type': 'training',
+ 'enroll': 'public',
+ 'visibility': 'public',
+ 'website_published': True,
+ })
+
+ # Step 2: link the certification to a slide of type 'certification'
+ self.slide_certification = self.env['slide.slide'].sudo().create({
+ 'name': 'Certification slide',
+ 'channel_id': self.channel.id,
+ 'slide_type': 'certification',
+ 'survey_id': certification.id,
+ 'website_published': True,
+ })
+ # Step 3: add public user as member of the channel
+ self.channel._action_add_members(self.user_public.partner_id)
+ # forces recompute of partner_ids as we create directly in relation
+ self.channel.invalidate_cache()
+ slide_partner = self.slide_certification._action_set_viewed(self.user_public.partner_id)
+ self.slide_certification.sudo(self.user_public)._generate_certification_url()
+
+ self.assertEqual(1, len(slide_partner.user_input_ids), 'A user input should have been automatically created upon slide view')
+
+ # Step 4: fill in the created user_input with wrong answers
+ self.fill_in_answer(slide_partner.user_input_ids[0], certification.question_ids)
+
+ self.assertFalse(slide_partner.survey_quizz_passed, 'Quizz should not be marked as passed with wrong answers')
+ # forces recompute of partner_ids as we delete directly in relation
+ self.channel.invalidate_cache()
+ self.assertIn(self.user_public.partner_id, self.channel.partner_ids, 'Public user should still be a member of the course because he still has attempts left')
+
+ # Step 5: simulate a 'retry'
+ retry_user_input = self.slide_certification.survey_id.sudo()._create_answer(
+ partner=self.user_public.partner_id,
+ **{
+ 'slide_id': self.slide_certification.id,
+ 'slide_partner_id': slide_partner.id
+ },
+ invite_token=slide_partner.user_input_ids[0].invite_token
+ )
+ # Step 6: fill in the new user_input with wrong answers again
+ self.fill_in_answer(retry_user_input, certification.question_ids)
+ # forces recompute of partner_ids as we delete directly in relation
+ self.channel.invalidate_cache()
+ self.assertNotIn(self.user_public.partner_id, self.channel.partner_ids, 'Public user should have been kicked out of the course because he failed his last attempt')
+
+ # Step 7: add public user as member of the channel once again
+ self.channel._action_add_members(self.user_public.partner_id)
+ # forces recompute of partner_ids as we create directly in relation
+ self.channel.invalidate_cache()
+
+ self.assertIn(self.user_public.partner_id, self.channel.partner_ids, 'Public user should be a member of the course once again')
+ new_slide_partner = self.slide_certification._action_set_viewed(self.user_public.partner_id)
+ self.slide_certification.sudo(self.user_public)._generate_certification_url()
+ self.assertEqual(1, len(new_slide_partner.user_input_ids.filtered(lambda user_input: user_input.state != 'done')), 'A new user input should have been automatically created upon slide view')
+
+ # Step 8: fill in the created user_input with correct answers this time
+ self.fill_in_answer(new_slide_partner.user_input_ids.filtered(lambda user_input: user_input.state != 'done')[0], certification.question_ids, good_answers=True)
+ self.assertTrue(new_slide_partner.survey_quizz_passed, 'Quizz should be marked as passed with correct answers')
+ # forces recompute of partner_ids as we delete directly in relation
+ self.channel.invalidate_cache()
+ self.assertIn(self.user_public.partner_id, self.channel.partner_ids, 'Public user should still be a member of the course')
+
+ def fill_in_answer(self, answer, questions, good_answers=False):
+ """ Fills in the user_input with answers for all given questions.
+ You can control whether the answer will be correct or not with the 'good_answers' param.
+ (It's assumed that wrong answers are at index 0 of question.labels_ids and good answers at index 1) """
+ answer.write({
+ 'state': 'done',
+ 'user_input_line_ids': [
+ (0, 0, {
+ 'question_id': question.id,
+ 'answer_type': 'suggestion',
+ 'answer_score': 1 if good_answers else 0,
+ 'value_suggested': question.labels_ids[1 if good_answers else 0].id
+ }) for question in questions
+ ]
+ })