55
66from dojo .filters import ApiFindingFilter , FindingFilterHelper
77from dojo .models import (
8+ Dojo_User ,
89 Engagement ,
910 Finding ,
1011 Product ,
1415)
1516
1617
17- def _make_finding (title , mitigation , product ):
18+ def _make_finding (title , mitigation , product , reporter ):
1819 test_type , _ = Test_Type .objects .get_or_create (name = "Unit Test" )
1920 engagement = Engagement .objects .create (
2021 name = "Test Engagement" ,
@@ -33,6 +34,7 @@ def _make_finding(title, mitigation, product):
3334 test = test ,
3435 severity = "Medium" ,
3536 mitigation = mitigation ,
37+ reporter = reporter ,
3638 verified = True ,
3739 active = True ,
3840 )
@@ -41,62 +43,174 @@ def _make_finding(title, mitigation, product):
4143class MitigationFilterTestCase (TestCase ):
4244 @classmethod
4345 def setUpTestData (cls ):
46+ cls .reporter = Dojo_User .objects .create_user (
47+ username = "mitigation-filter-api" ,
48+ email = "mitigation-filter-api@example.com" ,
49+ password = "password123" , # noqa: S106
50+ )
4451 prod_type = Product_Type .objects .create (name = "Test Type" )
4552 product = Product .objects .create (
4653 name = "Test Product" ,
4754 prod_type = prod_type ,
4855 )
49- cls .finding_with_mitigation = _make_finding ("Finding A" , "apply patch" , product )
50- cls .finding_null_mitigation = _make_finding ("Finding B" , None , product )
51- cls .finding_empty_mitigation = _make_finding ("Finding C" , "" , product )
56+ cls .finding_with_mitigation = _make_finding ("Finding A" , "apply patch" , product , cls .reporter )
57+ cls .finding_upper_mitigation = _make_finding ("Finding D" , "APPLY PATCH" , product , cls .reporter )
58+ cls .finding_whitespace_mitigation = _make_finding ("Finding E" , " " , product , cls .reporter )
59+ cls .finding_null_mitigation = _make_finding ("Finding B" , None , product , cls .reporter )
60+ cls .finding_empty_mitigation = _make_finding ("Finding C" , "" , product , cls .reporter )
5261
5362 def _api_filter (self , params ):
54- qs = Finding .objects .all ()
63+ qs = Finding .objects .filter (
64+ title__in = ["Finding A" , "Finding B" , "Finding C" , "Finding D" , "Finding E" ]
65+ )
5566 f = ApiFindingFilter (params , queryset = qs )
5667 return set (f .qs .values_list ("id" , flat = True ))
5768
58- def test_mitigation_icontains (self ):
59- # Filtering by mitigation text returns only findings whose mitigation contains that substring
60- pass
69+ # --- mitigation icontains ---
70+
71+ def test_mitigation_icontains_lowercase (self ):
72+ # Substring match: "patch" should hit "apply patch" and "APPLY PATCH"
73+ result = self ._api_filter ({"mitigation" : "patch" })
74+ self .assertIn (self .finding_with_mitigation .id , result )
75+ self .assertIn (self .finding_upper_mitigation .id , result )
76+ self .assertNotIn (self .finding_null_mitigation .id , result )
77+ self .assertNotIn (self .finding_empty_mitigation .id , result )
78+
79+ def test_mitigation_icontains_uppercase (self ):
80+ # Case-insensitive: uppercase query also matches lowercase stored value
81+ result = self ._api_filter ({"mitigation" : "PATCH" })
82+ self .assertIn (self .finding_with_mitigation .id , result )
83+ self .assertIn (self .finding_upper_mitigation .id , result )
84+
85+ def test_mitigation_icontains_no_match (self ):
86+ result = self ._api_filter ({"mitigation" : "ZZZNOMATCH" })
87+ self .assertEqual (result , set ())
88+
89+ def test_mitigation_icontains_partial (self ):
90+ # Partial substring match
91+ result = self ._api_filter ({"mitigation" : "apply" })
92+ self .assertIn (self .finding_with_mitigation .id , result )
93+ self .assertIn (self .finding_upper_mitigation .id , result )
94+ self .assertNotIn (self .finding_null_mitigation .id , result )
95+ self .assertNotIn (self .finding_empty_mitigation .id , result )
96+
97+ # --- mitigation_available=true ---
6198
6299 def test_mitigation_available_true (self ):
63- # mitigation_available=true returns only findings with a non-null, non-empty mitigation
64- pass
100+ # Returns only findings with non-null, non-empty mitigation
101+ result = self ._api_filter ({"mitigation_available" : "true" })
102+ self .assertIn (self .finding_with_mitigation .id , result )
103+ self .assertIn (self .finding_upper_mitigation .id , result )
104+ # Whitespace-only is NOT null and NOT empty string — current impl includes it
105+ self .assertIn (self .finding_whitespace_mitigation .id , result )
106+ self .assertNotIn (self .finding_null_mitigation .id , result )
107+ self .assertNotIn (self .finding_empty_mitigation .id , result )
108+
109+ # --- mitigation_available=false ---
65110
66111 def test_mitigation_available_false (self ):
67- # mitigation_available=false returns only findings with a null or empty mitigation
68- pass
112+ # Returns findings where mitigation is null OR empty string
113+ result = self ._api_filter ({"mitigation_available" : "false" })
114+ self .assertIn (self .finding_null_mitigation .id , result )
115+ self .assertIn (self .finding_empty_mitigation .id , result )
116+ self .assertNotIn (self .finding_with_mitigation .id , result )
117+ self .assertNotIn (self .finding_upper_mitigation .id , result )
69118
70119 def test_mitigation_available_false_handles_null (self ):
71- # mitigation_available=false includes findings where mitigation is NULL
72- pass
120+ # NULL mitigation is explicitly captured by the false branch
121+ result = self ._api_filter ({"mitigation_available" : "false" })
122+ self .assertIn (self .finding_null_mitigation .id , result )
73123
74124 def test_mitigation_available_false_handles_empty_string (self ):
75- # mitigation_available=false includes findings where mitigation is an empty string
76- pass
125+ # Empty-string mitigation is explicitly captured by the false branch
126+ result = self ._api_filter ({"mitigation_available" : "false" })
127+ self .assertIn (self .finding_empty_mitigation .id , result )
128+
129+ def test_mitigation_available_false_excludes_whitespace (self ):
130+ # Whitespace-only mitigation (" ") is NOT null and NOT empty-string,
131+ # so the false branch does NOT include it — document current behavior.
132+ result = self ._api_filter ({"mitigation_available" : "false" })
133+ self .assertNotIn (self .finding_whitespace_mitigation .id , result )
134+
135+ # --- no filter parameter ---
136+
137+ def test_no_filter_returns_full_set (self ):
138+ # Baseline: no params → all five findings returned
139+ result = self ._api_filter ({})
140+ expected = {
141+ self .finding_with_mitigation .id ,
142+ self .finding_upper_mitigation .id ,
143+ self .finding_whitespace_mitigation .id ,
144+ self .finding_null_mitigation .id ,
145+ self .finding_empty_mitigation .id ,
146+ }
147+ self .assertEqual (result , expected )
148+
149+ # --- combined filters (intersection) ---
150+
151+ def test_combined_mitigation_text_and_available_true (self ):
152+ # "patch" icontains AND mitigation_available=true → only the two "patch" findings
153+ result = self ._api_filter ({"mitigation" : "patch" , "mitigation_available" : "true" })
154+ self .assertEqual (
155+ result ,
156+ {self .finding_with_mitigation .id , self .finding_upper_mitigation .id },
157+ )
158+
159+ def test_combined_mitigation_text_and_available_false (self ):
160+ # text filter AND mitigation_available=false → empty: false branch returns null/empty,
161+ # icontains on null/empty returns nothing matching "patch"
162+ result = self ._api_filter ({"mitigation" : "patch" , "mitigation_available" : "false" })
163+ self .assertEqual (result , set ())
77164
78165
79166class MitigationUIFilterTestCase (TestCase ):
80167 @classmethod
81168 def setUpTestData (cls ):
169+ cls .reporter = Dojo_User .objects .create_user (
170+ username = "mitigation-filter-ui" ,
171+ email = "mitigation-filter-ui@example.com" ,
172+ password = "password123" , # noqa: S106
173+ )
82174 prod_type = Product_Type .objects .create (name = "UI Test Type" )
83175 product = Product .objects .create (
84176 name = "UI Test Product" ,
85177 prod_type = prod_type ,
86178 )
87- cls .finding_with_mitigation = _make_finding ("UI Finding A" , "upgrade to v2" , product )
88- cls .finding_null_mitigation = _make_finding ("UI Finding B" , None , product )
89- cls .finding_empty_mitigation = _make_finding ("UI Finding C" , "" , product )
179+ cls .finding_with_mitigation = _make_finding ("UI Finding A" , "upgrade to v2" , product , cls .reporter )
180+ cls .finding_whitespace_mitigation = _make_finding ("UI Finding D" , " " , product , cls .reporter )
181+ cls .finding_null_mitigation = _make_finding ("UI Finding B" , None , product , cls .reporter )
182+ cls .finding_empty_mitigation = _make_finding ("UI Finding C" , "" , product , cls .reporter )
90183
91184 def _ui_filter (self , params ):
92- qs = Finding .objects .all ()
185+ qs = Finding .objects .filter (
186+ title__in = ["UI Finding A" , "UI Finding B" , "UI Finding C" , "UI Finding D" ]
187+ )
93188 f = FindingFilterHelper (params , queryset = qs )
94189 return set (f .qs .values_list ("id" , flat = True ))
95190
96191 def test_mitigation_available_true (self ):
97- # mitigation_available=true returns only findings with a non-null, non-empty mitigation
98- pass
192+ # True branch: excludes null and empty string; whitespace-only is included
193+ result = self ._ui_filter ({"mitigation_available" : "true" })
194+ self .assertIn (self .finding_with_mitigation .id , result )
195+ self .assertIn (self .finding_whitespace_mitigation .id , result )
196+ self .assertNotIn (self .finding_null_mitigation .id , result )
197+ self .assertNotIn (self .finding_empty_mitigation .id , result )
99198
100199 def test_mitigation_available_false (self ):
101- # mitigation_available=false returns only findings with a null or empty mitigation
102- pass
200+ # False branch: returns null and empty string, excludes non-empty
201+ result = self ._ui_filter ({"mitigation_available" : "false" })
202+ self .assertIn (self .finding_null_mitigation .id , result )
203+ self .assertIn (self .finding_empty_mitigation .id , result )
204+ self .assertNotIn (self .finding_with_mitigation .id , result )
205+ # Whitespace-only is not null/empty → not in false branch
206+ self .assertNotIn (self .finding_whitespace_mitigation .id , result )
207+
208+ def test_no_filter_returns_full_set (self ):
209+ result = self ._ui_filter ({})
210+ expected = {
211+ self .finding_with_mitigation .id ,
212+ self .finding_whitespace_mitigation .id ,
213+ self .finding_null_mitigation .id ,
214+ self .finding_empty_mitigation .id ,
215+ }
216+ self .assertEqual (result , expected )
0 commit comments