Skip to content

Commit 72739e1

Browse files
committed
PPHA-529: Smoked total years validation on age started
Ensure that the smoked total years for a type of tobacco cannot be longer than the overall total years smoked based on their age when started
1 parent 0fe6dcc commit 72739e1

13 files changed

Lines changed: 198 additions & 59 deletions

features/smoked_total_years.feature

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ Feature: Smoked total years page
33
Scenario: The page is accessible
44
Given I am logged in
55
And I have answered questions showing I am eligible
6+
And I have answered questions showing I have smoked for "10" years
67
And I have answered questions showing I have smoked "Cigarettes"
78
When I go to "/cigarettes-smoked-total-years"
89
Then there are no accessibility violations
910

1011
Scenario: Form errors
1112
Given I am logged in
1213
And I have answered questions showing I am eligible
14+
And I have answered questions showing I have smoked for "10" years
1315
And I have answered questions showing I have smoked "Cigarettes"
1416
When I go to "/cigarettes-smoked-total-years"
1517
And I click "Continue"
@@ -20,27 +22,29 @@ Feature: Smoked total years page
2022
Scenario: Navigating backwards and forwards
2123
Given I am logged in
2224
And I have answered questions showing I am eligible
25+
And I have answered questions showing I have smoked for "10" years
2326
And I have answered questions showing I have smoked "Cigarettes"
2427
When I go to "/cigarettes-smoked-total-years"
2528
Then I see a back link to "/types-tobacco-smoking"
26-
When I fill in "Roughly how many years have you smoked cigarettes?" with "10"
29+
When I fill in "Roughly how many years have you smoked cigarettes?" with "9"
2730
And I submit the form
2831
Then I am on "/check-your-answers"
2932

3033
Scenario: Checking responses and changing them
3134
Given I am logged in
3235
And I have answered questions showing I am eligible
36+
And I have answered questions showing I have smoked for "10" years
3337
And I have answered questions showing I have smoked "Cigarettes"
3438
When I go to "/cigarettes-smoked-total-years"
35-
When I fill in "Roughly how many years have you smoked cigarettes?" with "10"
39+
When I fill in "Roughly how many years have you smoked cigarettes?" with "9"
3640
And I submit the form
3741
When I go to "/check-your-answers"
38-
Then I see "10" as a response to "Total number of years you have smoked cigarettes" under "Smoking history"
42+
Then I see "9" as a response to "Total number of years you have smoked cigarettes" under "Smoking history"
3943
And I see "/cigarettes-smoked-total-years?change=True" as a link to change "Total number of years you have smoked cigarettes" under "Smoking history"
4044
When I click the link to change "Total number of years you have smoked cigarettes" under "Smoking history"
4145
Then I am on "/cigarettes-smoked-total-years?change=True"
42-
And I see "10" filled in for "Roughly how many years have you smoked cigarettes?"
43-
When I fill in "Roughly how many years have you smoked cigarettes?" with "20"
46+
And I see "9" filled in for "Roughly how many years have you smoked cigarettes?"
47+
When I fill in "Roughly how many years have you smoked cigarettes?" with "8"
4448
And I click "Continue"
4549
Then I am on "/check-your-answers"
46-
And I see "20" as a response to "Total number of years you have smoked cigarettes" under "Smoking history"
50+
And I see "8" as a response to "Total number of years you have smoked cigarettes" under "Smoking history"

features/steps/preflight_steps.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,5 @@ def given_i_have_answered_questions_showing_i_have_smoked_tobacco_type(
8787

8888
for tobacco_type in tobacco_types.split(","):
8989
when_i_check_label(context, tobacco_type.strip())
90+
9091
when_i_submit_the_form(context)

features/types_tobacco_smoking.feature

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ Feature: Types tobacco smoking page
33
Scenario: The page is accessible
44
Given I am logged in
55
And I have answered questions showing I am eligible
6+
And I have answered questions showing I have smoked for "10" years
67
When I go to "/types-tobacco-smoking"
78
Then there are no accessibility violations
89

910
Scenario: Form errors
1011
Given I am logged in
1112
And I have answered questions showing I am eligible
13+
And I have answered questions showing I have smoked for "10" years
1214
When I go to "/types-tobacco-smoking"
1315
And I click "Continue"
1416
Then I am on "/types-tobacco-smoking"
@@ -18,6 +20,7 @@ Feature: Types tobacco smoking page
1820
Scenario: Navigating backwards and forwards
1921
Given I am logged in
2022
And I have answered questions showing I am eligible
23+
And I have answered questions showing I have smoked for "10" years
2124
When I go to "/types-tobacco-smoking"
2225
Then I see a back link to "/periods-when-you-stopped-smoking"
2326
When I check "Cigarettes"
@@ -27,6 +30,7 @@ Feature: Types tobacco smoking page
2730
Scenario: Checking responses and changing them
2831
Given I am logged in
2932
And I have answered questions showing I am eligible
33+
And I have answered questions showing I have smoked for "10" years
3034
When I go to "/types-tobacco-smoking"
3135
And I check "Cigarettes"
3236
And I check "Cigars"

lung_cancer_screening/questions/jinja2/question_form.jinja

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
{% block page_content %}
1010
<div class="nhsuk-grid-row">
1111
<div class="nhsuk-grid-column-two-thirds">
12-
<form action="{{ request.path }}" method="POST">
12+
<form action="{{ request.path }}" method="POST" novalidate>
1313
{{ csrf_input }}
1414

1515
{% if request.GET.get("change") == "True" %}

lung_cancer_screening/questions/models/smoked_total_years_response.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from django.db import models
2+
from django.core.exceptions import ValidationError
3+
from django.core.validators import MinValueValidator
24

35
from .base import BaseModel
46
from .tobacco_smoking_history import TobaccoSmokingHistory
@@ -10,4 +12,37 @@ class SmokedTotalYearsResponse(BaseModel):
1012
on_delete=models.CASCADE,
1113
related_name='smoked_total_years_response'
1214
)
13-
value = models.IntegerField()
15+
16+
value = models.IntegerField(
17+
validators=[
18+
MinValueValidator(1, message="The number of years you smoked cigarettes must be at least 1")
19+
]
20+
)
21+
22+
23+
def clean(self):
24+
super().clean()
25+
self._validate_age_when_started_smoking_response_exists()
26+
self._validate_value_fewer_than_total_number_of_years_smoked()
27+
28+
29+
def _validate_age_when_started_smoking_response_exists(self):
30+
if not hasattr(self.tobacco_smoking_history.response_set, 'age_when_started_smoking_response'):
31+
raise ValidationError({
32+
"value": ValidationError(
33+
"You must answer age when started smoking before answering how many years you have smoked cigarettes",
34+
code="age_when_started_smoking_response_not_found"
35+
)
36+
})
37+
38+
def _validate_value_fewer_than_total_number_of_years_smoked(self):
39+
if not self.value:
40+
return None
41+
42+
if self.value > self.tobacco_smoking_history.response_set.age_when_started_smoking_response.years_smoked_including_stopped():
43+
raise ValidationError({
44+
"value": ValidationError(
45+
"The number of years you smoked cigarettes must be fewer than the total number of years you have been smoking",
46+
code="value_greater_than_total_number_of_years_smoked"
47+
)
48+
})

lung_cancer_screening/questions/tests/factories/age_when_started_smoking_response_factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
def calculate_value(instance):
99
if not hasattr(instance.response_set, 'date_of_birth_response'):
1010
return None
11-
return random.randint(1, instance.response_set.date_of_birth_response.age_in_years())
11+
return random.randint(10, instance.response_set.date_of_birth_response.age_in_years())
1212

1313

1414
class AgeWhenStartedSmokingResponseFactory(factory.django.DjangoModelFactory):

lung_cancer_screening/questions/tests/factories/smoked_total_years_response_factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ class Meta:
99
model = SmokedTotalYearsResponse
1010

1111
tobacco_smoking_history = factory.SubFactory(TobaccoSmokingHistoryFactory)
12-
value = 10
12+
value = 1

lung_cancer_screening/questions/tests/unit/forms/test_smoked_total_years_form.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from ...factories.tobacco_smoking_history_factory import TobaccoSmokingHistoryFactory
44
from ...factories.smoked_total_years_response_factory import SmokedTotalYearsResponseFactory
5+
from ...factories.age_when_started_smoking_response_factory import AgeWhenStartedSmokingResponseFactory
56
from ....models.tobacco_smoking_history import TobaccoSmokingHistoryTypes
67
from ....forms.smoked_total_years_form import SmokedTotalYearsForm
78

@@ -12,6 +13,9 @@ def setUp(self):
1213
self.smoking_history = TobaccoSmokingHistoryFactory.create(
1314
type=TobaccoSmokingHistoryTypes.CIGARETTES.value
1415
)
16+
self.age_started_smoking_response = AgeWhenStartedSmokingResponseFactory.create(
17+
response_set=self.smoking_history.response_set
18+
)
1519
self.response = SmokedTotalYearsResponseFactory.build(
1620
tobacco_smoking_history=self.smoking_history
1721
)
@@ -21,13 +25,13 @@ def test_is_valid_with_a_valid_value(self):
2125
form = SmokedTotalYearsForm(
2226
instance=self.response,
2327
data={
24-
"value": 10
28+
"value": self.age_started_smoking_response.years_smoked_including_stopped() - 1
2529
}
2630
)
2731
self.assertTrue(form.is_valid())
2832
self.assertEqual(
2933
form.cleaned_data["value"],
30-
10
34+
self.age_started_smoking_response.years_smoked_including_stopped() - 1
3135
)
3236

3337
def test_is_invalid_with_a_none_value(self):
@@ -38,11 +42,12 @@ def test_is_invalid_with_a_none_value(self):
3842
}
3943
)
4044
self.assertFalse(form.is_valid())
41-
self.assertEqual(
42-
form.errors["value"],
43-
["Enter the number of years you have smoked cigarettes"]
45+
self.assertIn(
46+
"Enter the number of years you have smoked cigarettes",
47+
form.errors["value"]
4448
)
4549

50+
4651
def test_is_invalid_with_a_non_numeric_value(self):
4752
form = SmokedTotalYearsForm(
4853
instance=self.response,
@@ -51,7 +56,7 @@ def test_is_invalid_with_a_non_numeric_value(self):
5156
}
5257
)
5358
self.assertFalse(form.is_valid())
54-
self.assertEqual(
55-
form.errors["value"],
56-
["Years must be in whole numbers"]
59+
self.assertIn(
60+
"Years must be in whole numbers",
61+
form.errors["value"]
5762
)

lung_cancer_screening/questions/tests/unit/models/test_smoked_total_years_response.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
from django.test import TestCase, tag
2+
from django.core.exceptions import ValidationError
23

34
from ...factories.tobacco_smoking_history_factory import TobaccoSmokingHistoryFactory
45
from ...factories.smoked_total_years_response_factory import SmokedTotalYearsResponseFactory
6+
from ...factories.age_when_started_smoking_response_factory import AgeWhenStartedSmokingResponseFactory
57

68

79
@tag("SmokedTotalYears")
810
class TestSmokedTotalYearsResponse(TestCase):
911
def setUp(self):
1012
self.tobacco_smoking_history = TobaccoSmokingHistoryFactory()
13+
self.age_started_smoking_response = AgeWhenStartedSmokingResponseFactory.create(
14+
response_set=self.tobacco_smoking_history.response_set
15+
)
16+
1117

1218
def test_has_a_valid_factory(self):
13-
model = SmokedTotalYearsResponseFactory.build(tobacco_smoking_history=self.tobacco_smoking_history)
19+
model = SmokedTotalYearsResponseFactory.build(
20+
tobacco_smoking_history=self.tobacco_smoking_history
21+
)
1422
model.full_clean()
1523

1624

@@ -29,3 +37,50 @@ def test_has_value_as_int(self):
2937
)
3038

3139
self.assertIsInstance(response.value, int)
40+
41+
42+
def test_is_invalid_if_the_value_is_less_than_0(self):
43+
response = SmokedTotalYearsResponseFactory.build(
44+
tobacco_smoking_history=self.tobacco_smoking_history,
45+
value=0
46+
)
47+
48+
with self.assertRaises(ValidationError) as context:
49+
response.full_clean()
50+
51+
self.assertIn("value", context.exception.message_dict)
52+
self.assertIn("The number of years you smoked cigarettes must be at least 1", context.exception.message_dict["value"])
53+
54+
55+
def test_is_invalid_if_there_is_no_age_when_started_smoking_response(self):
56+
self.age_started_smoking_response.delete()
57+
58+
response = SmokedTotalYearsResponseFactory.build(
59+
tobacco_smoking_history=self.tobacco_smoking_history,
60+
value=1
61+
)
62+
63+
with self.assertRaises(ValidationError) as context:
64+
response.full_clean()
65+
66+
self.assertIn("value", context.exception.message_dict)
67+
self.assertIn(
68+
"You must answer age when started smoking before answering how many years you have smoked cigarettes",
69+
context.exception.message_dict["value"]
70+
)
71+
72+
73+
def test_is_invalid_if_the_value_is_greater_than_the_years_smoked_overall(self):
74+
response = SmokedTotalYearsResponseFactory.build(
75+
tobacco_smoking_history=self.tobacco_smoking_history,
76+
value=self.age_started_smoking_response.years_smoked_including_stopped() + 1
77+
)
78+
79+
with self.assertRaises(ValidationError) as context:
80+
response.full_clean()
81+
82+
self.assertIn("value", context.exception.message_dict)
83+
self.assertIn(
84+
"The number of years you smoked cigarettes must be fewer than the total number of years you have been smoking",
85+
context.exception.message_dict["value"]
86+
)

lung_cancer_screening/questions/tests/unit/presenters/test_response_set_presenter.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,11 +301,15 @@ def test_smoking_history_types_responses_items_sets_the_correct_key(self):
301301

302302

303303
def test_smoking_history_types_responses_items_sets_the_correct_value(self):
304+
AgeWhenStartedSmokingResponseFactory.create(
305+
response_set=self.response_set,
306+
value=10
307+
)
304308
cigarettes = TobaccoSmokingHistoryFactory.create(
305309
response_set=self.response_set, type=TobaccoSmokingHistoryTypes.CIGARETTES
306310
)
307311
smoked_total_years_response = SmokedTotalYearsResponseFactory.create(
308-
tobacco_smoking_history=cigarettes,
312+
tobacco_smoking_history=cigarettes
309313
)
310314

311315
presenter = ResponseSetPresenter(self.response_set)

0 commit comments

Comments
 (0)