@@ -147,9 +147,79 @@ public async Task CleanseAnalysis_HappyPath_ShouldDetectIssuesFixDataAndReRun()
147147 "Rule 5 should fire for CPH 12/345/6003 (no phones)" ) ;
148148 activeIssues . Should ( ) . Contain ( i => i . IssueCode == RuleIds . SAM_NO_CATTLE_UNIT && i . Cph == "12/345/6003" ,
149149 "Rule 1 should fire for CPH 12/345/6003 (ANIMAL_SPECIES_CODE != CTT)" ) ;
150+ activeIssues . Should ( ) . Contain ( i => i . IssueCode == RuleIds . CTS_SAM_INCONSISTENT_EMAIL_ADDRESSES && i . Cph == "12/345/6003" ,
151+ "Rule 6 should fire for CPH 12/345/6003 (no missing CTS emails triggers inconsistency check)" ) ;
152+
153+ activeIssueCount . Should ( ) . Be ( 6 , "Exactly 6 issues should be raised across the 3 scenarios" ) ;
154+
155+ // --- QueryAsync: retrieve all active issues ---
156+ _output . WriteLine ( " Verifying QueryAsync for active issues..." ) ;
157+ var allActiveQuery = await issueQueries . QueryAsync (
158+ CleanseIssueQueryDto . Create ( ) . WhereActive ( ) . Page ( 0 , 50 ) , ct ) ;
159+ allActiveQuery . TotalCount . Should ( ) . Be ( 6 , "QueryAsync should return 6 active issues" ) ;
160+ allActiveQuery . Items . Should ( ) . HaveCount ( 6 ) ;
161+ allActiveQuery . Skip . Should ( ) . Be ( 0 ) ;
162+ allActiveQuery . Top . Should ( ) . Be ( 50 ) ;
163+ allActiveQuery . HasMore . Should ( ) . BeFalse ( "all 6 issues fit within a single page of 50" ) ;
164+
165+ // --- QueryAsync: filter by issue code ---
166+ _output . WriteLine ( " Verifying QueryAsync filtered by issue code..." ) ;
167+ var rule2aQuery = await issueQueries . QueryAsync (
168+ CleanseIssueQueryDto . Create ( ) . WhereActive ( ) . WithIssueCode ( RuleIds . CTS_CPH_NOT_IN_SAM ) , ct ) ;
169+ rule2aQuery . TotalCount . Should ( ) . Be ( 1 , "Only one Rule 2A issue should exist" ) ;
170+ rule2aQuery . Items . Should ( ) . ContainSingle ( )
171+ . Which . Cph . Should ( ) . Be ( "12/345/6001" ) ;
172+
173+ // --- QueryAsync: filter by CPH contains ---
174+ _output . WriteLine ( " Verifying QueryAsync filtered by CPH contains..." ) ;
175+ var cph6003Query = await issueQueries . QueryAsync (
176+ CleanseIssueQueryDto . Create ( ) . WhereActive ( ) . WithCphContaining ( "6003" ) , ct ) ;
177+ cph6003Query . TotalCount . Should ( ) . Be ( 4 , "CPH 12/345/6003 should have 4 active issues (Rules 4, 5, 1, 6)" ) ;
178+ cph6003Query . Items . Should ( ) . HaveCount ( 4 ) ;
179+ cph6003Query . Items . Should ( ) . OnlyContain ( i => i . Cph == "12/345/6003" ) ;
180+
181+ // --- QueryAsync: sorting ---
182+ _output . WriteLine ( " Verifying QueryAsync with sorting..." ) ;
183+ var sortedByCphAsc = await issueQueries . QueryAsync (
184+ CleanseIssueQueryDto . Create ( )
185+ . WhereActive ( )
186+ . OrderBy ( CleanseIssueSortField . Cph , descending : false )
187+ . Page ( 0 , 50 ) , ct ) ;
188+ sortedByCphAsc . Items . Should ( ) . BeInAscendingOrder ( i => i . Cph , "issues should be sorted by CPH ascending" ) ;
150189
151- activeIssueCount . Should ( ) . Be ( 5 , "Exactly 5 issues should be raised across the 3 scenarios" ) ;
152- _output . WriteLine ( "✓ Step 3 complete: all 5 expected issues verified" ) ;
190+ var sortedByCphDesc = await issueQueries . QueryAsync (
191+ CleanseIssueQueryDto . Create ( )
192+ . WhereActive ( )
193+ . OrderBy ( CleanseIssueSortField . Cph , descending : true )
194+ . Page ( 0 , 50 ) , ct ) ;
195+ sortedByCphDesc . Items . Should ( ) . BeInDescendingOrder ( i => i . Cph , "issues should be sorted by CPH descending" ) ;
196+
197+ // --- QueryAsync: pagination ---
198+ _output . WriteLine ( " Verifying QueryAsync with pagination..." ) ;
199+ var page1 = await issueQueries . QueryAsync (
200+ CleanseIssueQueryDto . Create ( ) . WhereActive ( ) . Page ( 0 , 2 ) , ct ) ;
201+ page1 . Items . Should ( ) . HaveCount ( 2 , "first page should return 2 items" ) ;
202+ page1 . TotalCount . Should ( ) . Be ( 6 , "total count should still reflect all matching issues" ) ;
203+ page1 . HasMore . Should ( ) . BeTrue ( "there are more issues beyond the first page" ) ;
204+
205+ var page2 = await issueQueries . QueryAsync (
206+ CleanseIssueQueryDto . Create ( ) . WhereActive ( ) . Page ( 2 , 2 ) , ct ) ;
207+ page2 . Items . Should ( ) . HaveCount ( 2 , "second page should return 2 items" ) ;
208+ page2 . HasMore . Should ( ) . BeTrue ( "there are still more issues on the next page" ) ;
209+
210+ var page3 = await issueQueries . QueryAsync (
211+ CleanseIssueQueryDto . Create ( ) . WhereActive ( ) . Page ( 4 , 2 ) , ct ) ;
212+ page3 . Items . Should ( ) . HaveCount ( 2 , "third page should return the remaining 2 items" ) ;
213+ page3 . HasMore . Should ( ) . BeFalse ( "no more issues remain" ) ;
214+
215+ // --- QueryAsync: no results ---
216+ _output . WriteLine ( " Verifying QueryAsync returns empty for non-matching filter..." ) ;
217+ var noResults = await issueQueries . QueryAsync (
218+ CleanseIssueQueryDto . Create ( ) . WhereActive ( ) . WithIssueCode ( "NON_EXISTENT_CODE" ) , ct ) ;
219+ noResults . TotalCount . Should ( ) . Be ( 0 ) ;
220+ noResults . Items . Should ( ) . BeEmpty ( ) ;
221+
222+ _output . WriteLine ( "✓ Step 3 complete: all 6 expected issues and QueryAsync verified" ) ;
153223
154224 // ----------------------------------------------------------------
155225 // Step 4: Verify the exported CSV report and notification
@@ -196,7 +266,7 @@ public async Task CleanseAnalysis_HappyPath_ShouldDetectIssuesFixDataAndReRun()
196266 _output . WriteLine ( $ " [{ issue . IssueCode } ] CPH={ issue . Cph } ") ;
197267 }
198268
199- activeCountAfterFix . Should ( ) . Be ( 4 , "One issue ( Rule 2A for 12/345/6001) should have been deactivated " ) ;
269+ activeCountAfterFix . Should ( ) . Be ( 6 , "Rule 2A deactivated for 12/345/6001 but Rule 6 now fires for it (emails match) " ) ;
200270 activeIssuesAfterFix . Should ( ) . NotContain ( i => i . IssueCode == RuleIds . CTS_CPH_NOT_IN_SAM && i . Cph == "12/345/6001" ,
201271 "Rule 2A for 12/345/6001 should no longer be active after fixing the data" ) ;
202272
@@ -303,12 +373,12 @@ private async Task VerifyCsvReportAsync(IReadOnlyList<IssueDto> expectedIssues)
303373 rows . Should ( ) . HaveCount ( expectedIssues . Count ,
304374 $ "CSV should contain { expectedIssues . Count } data rows matching the active issues") ;
305375
306- // Verify ordering: grouped by rule priority (2A → 2B → 4 → 5 → 1), sorted by CPH within each group
307- var expectedRuleOrder = new [ ] { "2A" , "2B" , "4" , "5" , "1" } ;
376+ // Verify ordering: grouped by rule priority (2A → 2B → 4 → 5 → 1 → 6 ), sorted by CPH within each group
377+ var expectedRuleOrder = new [ ] { "2A" , "2B" , "4" , "5" , "1" , "6" } ;
308378 var actualRuleOrder = rows . Select ( r => r [ "Rule No" ] ) . ToArray ( ) ;
309379 actualRuleOrder . Should ( ) . BeEquivalentTo ( expectedRuleOrder ,
310380 options => options . WithStrictOrdering ( ) ,
311- "Rows should be ordered by rule priority (2A, 2B, 4, 5, 1)" ) ;
381+ "Rows should be ordered by rule priority (2A, 2B, 4, 5, 1, 6 )" ) ;
312382
313383 // Verify each row's content
314384 foreach ( var row in rows )
@@ -345,6 +415,12 @@ private async Task VerifyCsvReportAsync(IReadOnlyList<IssueDto> expectedIssues)
345415 rows [ 4 ] [ "Rule No" ] . Should ( ) . Be ( "1" ) ;
346416 rows [ 4 ] [ "Error Code" ] . Should ( ) . Be ( "01" ) ;
347417 rows [ 4 ] [ "Error Description" ] . Should ( ) . Be ( "No cattle unit defined in SAM" ) ;
418+
419+ // Row 6: Rule 6 — CPH 12/345/6003
420+ rows [ 5 ] [ "CPH" ] . Should ( ) . Be ( "12/345/6003" ) ;
421+ rows [ 5 ] [ "Rule No" ] . Should ( ) . Be ( "6" ) ;
422+ rows [ 5 ] [ "Error Code" ] . Should ( ) . Be ( "06" ) ;
423+ rows [ 5 ] [ "Error Description" ] . Should ( ) . Be ( "SAM is missing email addresses found in CTS" ) ;
348424 }
349425
350426 #endregion
0 commit comments