77import java .io .IOException ;
88import java .time .Instant ;
99import java .util .Date ;
10+ import java .util .List ;
11+ import java .util .stream .Collectors ;
1012
1113import static org .hamcrest .Matchers .*;
1214
1820 */
1921public class GHMilestoneTest extends AbstractGitHubWireMockTest {
2022
23+ private static List <String > filterTestMilestones (List <GHMilestone > milestones ) {
24+ return milestones .stream ()
25+ .map (GHMilestone ::getTitle )
26+ .filter (t -> t .startsWith ("Milestone Sort " ))
27+ .collect (Collectors .toList ());
28+ }
29+
2130 /**
2231 * Create default GHMilestoneTest instance
2332 */
@@ -38,14 +47,84 @@ public void cleanUp() throws Exception {
3847 return ;
3948 }
4049
41- for (GHMilestone milestone : getRepository (getNonRecordingGitHub ()).listMilestones (GHIssueState .ALL )) {
50+ GHRepository repo = getRepository (getNonRecordingGitHub ());
51+
52+ for (GHIssue issue : repo .queryIssues ().state (GHIssueState .ALL ).list ()) {
53+ if (issue .getTitle ().endsWith ("for sort test" ) || issue .getTitle ().equals ("Issue for testUnsetMilestone" )) {
54+ issue .close ();
55+ }
56+ }
57+
58+ for (GHMilestone milestone : repo .listMilestones (GHIssueState .ALL )) {
4259 if ("Original Title" .equals (milestone .getTitle ()) || "Updated Title" .equals (milestone .getTitle ())
43- || "Unset Test Milestone" .equals (milestone .getTitle ())) {
60+ || "Unset Test Milestone" .equals (milestone .getTitle ())
61+ || "Milestone Sort A" .equals (milestone .getTitle ())
62+ || "Milestone Sort B" .equals (milestone .getTitle ())) {
4463 milestone .delete ();
4564 }
4665 }
4766 }
4867
68+ /**
69+ * Test list milestones with sort and direction.
70+ *
71+ * @throws IOException
72+ * Signals that an I/O exception has occurred.
73+ */
74+ @ Test
75+ public void testListMilestonesWithSort () throws IOException {
76+ GHRepository repo = getRepository ();
77+ GHMilestone milestoneA = repo .createMilestone ("Milestone Sort A" , "First milestone" );
78+ milestoneA .setDueOn (GitHubClient .parseInstant ("2025-06-01T00:00:00Z" ));
79+ GHMilestone milestoneB = repo .createMilestone ("Milestone Sort B" , "Second milestone" );
80+ milestoneB .setDueOn (GitHubClient .parseInstant ("2025-12-01T00:00:00Z" ));
81+
82+ // List with due_on sort ascending (default)
83+ List <String > ascending = filterTestMilestones (repo .queryMilestones ()
84+ .state (GHIssueState .OPEN )
85+ .sort (GHMilestoneQueryBuilder .Sort .DUE_ON )
86+ .direction (GHDirection .ASC )
87+ .list ()
88+ .toList ());
89+ assertThat (ascending , contains ("Milestone Sort A" , "Milestone Sort B" ));
90+
91+ // List with due_on sort descending
92+ List <String > descending = filterTestMilestones (repo .queryMilestones ()
93+ .state (GHIssueState .OPEN )
94+ .sort (GHMilestoneQueryBuilder .Sort .DUE_ON )
95+ .direction (GHDirection .DESC )
96+ .list ()
97+ .toList ());
98+ assertThat (descending , contains ("Milestone Sort B" , "Milestone Sort A" ));
99+
100+ // Create issues to test completeness sort
101+ // Milestone A: 1 open, 1 closed = 50% complete
102+ GHIssue issueA1 = repo .createIssue ("Issue A1 for sort test" ).milestone (milestoneA ).create ();
103+ GHIssue issueA2 = repo .createIssue ("Issue A2 for sort test" ).milestone (milestoneA ).create ();
104+ issueA2 .close ();
105+
106+ // Milestone B: 1 open, 0 closed = 0% complete
107+ GHIssue issueB1 = repo .createIssue ("Issue B1 for sort test" ).milestone (milestoneB ).create ();
108+
109+ // List with completeness sort ascending (least complete first)
110+ List <String > byCompleteness = filterTestMilestones (repo .queryMilestones ()
111+ .state (GHIssueState .OPEN )
112+ .sort (GHMilestoneQueryBuilder .Sort .COMPLETENESS )
113+ .direction (GHDirection .ASC )
114+ .list ()
115+ .toList ());
116+ assertThat (byCompleteness , contains ("Milestone Sort B" , "Milestone Sort A" ));
117+
118+ // List with completeness sort descending (most complete first)
119+ List <String > byCompletenessDesc = filterTestMilestones (repo .queryMilestones ()
120+ .state (GHIssueState .OPEN )
121+ .sort (GHMilestoneQueryBuilder .Sort .COMPLETENESS )
122+ .direction (GHDirection .DESC )
123+ .list ()
124+ .toList ());
125+ assertThat (byCompletenessDesc , contains ("Milestone Sort A" , "Milestone Sort B" ));
126+ }
127+
49128 /**
50129 * Test unset milestone.
51130 *
0 commit comments