View Javadoc

1   /*
2    * Copyright (C) 2011 Atlassian
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package it;
18  
19  import com.atlassian.jira.nimblefunctests.annotation.JiraBuildNumberDependent;
20  import com.atlassian.jira.rest.client.IntegrationTestUtil;
21  import com.atlassian.jira.rest.client.TestUtil;
22  import com.atlassian.jira.rest.client.api.RestClientException;
23  import com.atlassian.jira.rest.client.api.SearchRestClient;
24  import com.atlassian.jira.rest.client.api.domain.BasicPriority;
25  import com.atlassian.jira.rest.client.api.domain.BasicProject;
26  import com.atlassian.jira.rest.client.api.domain.BasicVotes;
27  import com.atlassian.jira.rest.client.api.domain.BasicWatchers;
28  import com.atlassian.jira.rest.client.api.domain.Comment;
29  import com.atlassian.jira.rest.client.api.domain.EntityHelper;
30  import com.atlassian.jira.rest.client.api.domain.Issue;
31  import com.atlassian.jira.rest.client.api.domain.IssueLink;
32  import com.atlassian.jira.rest.client.api.domain.IssueLinkType;
33  import com.atlassian.jira.rest.client.api.domain.SearchResult;
34  import com.atlassian.jira.rest.client.api.domain.TimeTracking;
35  import com.atlassian.jira.rest.client.api.domain.Version;
36  import com.atlassian.jira.rest.client.api.domain.Worklog;
37  import com.atlassian.jira.rest.client.internal.json.TestConstants;
38  import com.atlassian.jira.rest.client.test.matchers.AddressableEntityMatchers;
39  import com.atlassian.jira.rest.client.test.matchers.NamedEntityMatchers;
40  import com.google.common.collect.ImmutableSet;
41  import com.google.common.collect.Iterables;
42  import com.google.common.collect.Sets;
43  import org.hamcrest.collection.IsIterableContainingInOrder;
44  import org.junit.Before;
45  import org.junit.Test;
46  
47  import javax.ws.rs.core.Response;
48  import java.lang.reflect.InvocationTargetException;
49  import java.util.Set;
50  
51  import static com.atlassian.jira.nimblefunctests.annotation.LongCondition.LESS_THAN;
52  import static com.atlassian.jira.rest.client.IntegrationTestUtil.resolveURI;
53  import static com.atlassian.jira.rest.client.TestUtil.assertEmptyIterable;
54  import static com.atlassian.jira.rest.client.TestUtil.toDateTime;
55  import static com.atlassian.jira.rest.client.internal.ServerVersionConstants.BN_JIRA_6_1;
56  import static org.hamcrest.Matchers.allOf;
57  import static org.hamcrest.Matchers.anyOf;
58  import static org.hamcrest.Matchers.hasProperty;
59  import static org.hamcrest.Matchers.is;
60  import static org.hamcrest.Matchers.notNullValue;
61  import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
62  import static org.junit.Assert.assertEquals;
63  import static org.junit.Assert.assertNull;
64  import static org.junit.Assert.assertThat;
65  
66  public class AsynchronousSearchRestClientTest extends AbstractAsynchronousRestClientTest {
67  
68      public static final Set<String> REQUIRED_ISSUE_FIELDS = ImmutableSet.of("summary", "issuetype", "created", "updated",
69              "project", "status");
70  
71      private static boolean alreadyRestored;
72  
73      @Before
74      public void setup() {
75          if (!alreadyRestored) {
76              IntegrationTestUtil.restoreAppropriateJiraData(TestConstants.DEFAULT_JIRA_DUMP_FILE, administration);
77              alreadyRestored = true;
78          }
79      }
80  
81      @Test
82      public void testJqlSearch() {
83          final SearchResult searchResultForNull = client.getSearchClient().searchJql(null).claim();
84          assertEquals(11, searchResultForNull.getTotal());
85  
86          final SearchResult searchResultForReporterWseliga = client.getSearchClient().searchJql("reporter=wseliga").claim();
87          assertEquals(1, searchResultForReporterWseliga.getTotal());
88      }
89  
90      @Test
91      public void testJqlSearchWithStartAt() {
92          final int maxResults = 3;
93  
94          // returns: 0,1,2
95          final SearchResult searchResultFrom0 = client.getSearchClient().searchJql(null, maxResults, 0, null).claim();
96          final Issue secondIssueFromFirstSearch = Iterables.get(searchResultFrom0.getIssues(), 1);
97  
98          // returns: 1,2,3
99          final SearchResult searchResultFrom1 = client.getSearchClient().searchJql(null, maxResults, 1, null).claim();
100         final Issue firstIssueFromSecondSearch = Iterables.get(searchResultFrom1.getIssues(), 0);
101 
102         assertEquals(secondIssueFromFirstSearch, firstIssueFromSecondSearch);
103     }
104 
105     @Test
106     public void testJqlSearchWithNullStartAtShouldUseDefault0ForStartAtAndPreserveMaxResults() {
107         final int maxResults = 21;
108         final SearchResult searchResult = client.getSearchClient().searchJql(null, maxResults, null, null).claim();
109         assertEquals(0, searchResult.getStartIndex());
110         assertEquals(maxResults, searchResult.getMaxResults());
111     }
112 
113     @Test
114     public void testJqlSearchWithNullMaxResultsShouldUseDefault50ForMaxResultsAndPreserveStartAt() {
115         final int startAt = 7;
116         final SearchResult searchResult = client.getSearchClient().searchJql(null, null, startAt, null).claim();
117         assertEquals(50, searchResult.getMaxResults());
118         assertEquals(startAt, searchResult.getStartIndex());
119     }
120 
121     @Test
122     public void testJqlSearchWithNullStartAtAndMaxResultsShouldUseAsDefault0ForStartIndexAnd50ForMaxResults() {
123         final SearchResult searchResult = client.getSearchClient().searchJql(null, null, null, null).claim();
124         assertEquals(50, searchResult.getMaxResults());
125         assertEquals(0, searchResult.getStartIndex());
126     }
127 
128     @Test
129     public void testJqlSearchAsAnonymous() {
130         setAnonymousMode();
131         final SearchResult searchResultForNull = client.getSearchClient().searchJql(null).claim();
132         assertEquals(3, searchResultForNull.getTotal());
133 
134         final SearchResult searchResultForReporterWseliga = client.getSearchClient().searchJql("reporter=wseliga").claim();
135         assertEquals(0, searchResultForReporterWseliga.getTotal());
136     }
137 
138     @Test
139     public void testJqlSearchWithPaging() {
140         final SearchResult searchResultForNull = client.getSearchClient().searchJql(null, 3, 3, null).claim();
141         assertEquals(11, searchResultForNull.getTotal());
142         assertEquals(3, Iterables.size(searchResultForNull.getIssues()));
143         assertEquals(3, searchResultForNull.getStartIndex());
144         assertEquals(3, searchResultForNull.getMaxResults());
145 
146         final SearchResult search2 = client.getSearchClient().searchJql("assignee is not EMPTY", 2, 1, null).claim();
147         assertEquals(11, search2.getTotal());
148         assertEquals(2, Iterables.size(search2.getIssues()));
149         assertEquals("TST-6", Iterables.get(search2.getIssues(), 0).getKey());
150         assertEquals("TST-5", Iterables.get(search2.getIssues(), 1).getKey());
151         assertEquals(1, search2.getStartIndex());
152         assertEquals(2, search2.getMaxResults());
153 
154         setUser1();
155         final SearchResult search3 = client.getSearchClient().searchJql("assignee is not EMPTY", 10, 5, null).claim();
156         assertEquals(10, search3.getTotal());
157         assertEquals(5, Iterables.size(search3.getIssues()));
158         assertEquals(5, search3.getStartIndex());
159         assertEquals(10, search3.getMaxResults());
160     }
161 
162     @Test
163     public void testVeryLongJqlWhichWillBePost() {
164         final String longJql = generateVeryLongJql() + " or summary is not empty"; // so that effectively all issues are returned;
165         final SearchResult searchResultForNull = client.getSearchClient().searchJql(longJql, 3, 6, null).claim();
166         assertEquals(11, searchResultForNull.getTotal());
167         assertEquals(3, Iterables.size(searchResultForNull.getIssues()));
168         assertEquals(6, searchResultForNull.getStartIndex());
169         assertEquals(3, searchResultForNull.getMaxResults());
170     }
171 
172     private String generateVeryLongJql() {
173         final String coreJql = "(reporter is not empty OR reporter is empty)";
174         StringBuilder sb = new StringBuilder(coreJql);
175         for (int i = 0; i < 500; i++) {
176             sb.append(" and ").append(coreJql);
177         }
178         return sb.toString();
179     }
180 
181     @Test
182     public void testJqlSearchUnescapedCharacter() {
183         TestUtil.assertErrorCode(Response.Status.BAD_REQUEST, new Runnable() {
184             @Override
185             public void run() {
186                 client.getSearchClient().searchJql("reporter=a/user/with/slash").claim();
187             }
188         });
189     }
190 
191     @JiraBuildNumberDependent(value = BN_JIRA_6_1, condition = LESS_THAN)
192     @Test
193     public void jqlSearchShouldReturnIssueWithDetailsBefore6_1() throws InvocationTargetException, IllegalAccessException {
194         jqlSearchShouldReturnIssueWithDetails("rest/api/2/project/TST");
195     }
196 
197     @JiraBuildNumberDependent(value = BN_JIRA_6_1)
198     @Test
199     public void jqlSearchShouldReturnIssueWithDetails() throws InvocationTargetException, IllegalAccessException {
200         jqlSearchShouldReturnIssueWithDetails("rest/api/2/project/10000");
201     }
202 
203     private void jqlSearchShouldReturnIssueWithDetails(String projectSelf) {
204         final SearchResult searchResult = client.getSearchClient().searchJql("reporter=wseliga").claim();
205         final Issue issue = Iterables.getOnlyElement(searchResult.getIssues());
206 
207         assertEquals("TST-7", issue.getKey());
208         assertEquals(Long.valueOf(10040), issue.getId());
209         assertEquals(resolveURI("rest/api/latest/issue/10040"), issue.getSelf());
210         assertEquals("A task where someone will vote", issue.getSummary());
211         assertNull(issue.getDescription()); // by default search doesn't retrieve description
212         assertEquals(new BasicPriority(resolveURI("rest/api/2/priority/3"), 3L, "Major"), issue.getPriority());
213         assertThat(issue.getStatus(), allOf(
214                 hasProperty("self", is(resolveURI("rest/api/2/status/1"))),
215                 hasProperty("id", is(1L)),
216                 hasProperty("name", is("Open")),
217                 hasProperty("description", is("The issue is open and ready for the assignee to start work on it.")),
218                 anyOf(
219                         hasProperty("iconUrl", is(resolveURI("images/icons/statuses/open.png"))), // Jira >= 5.2
220                         hasProperty("iconUrl", is(resolveURI("images/icons/status_open.gif"))) // Jira < 5.2
221                 )));
222         assertEmptyIterable(issue.getComments());  // not expanded by default
223         assertEmptyIterable(issue.getComponents());
224         assertEmptyIterable(issue.getWorklogs());
225         assertEmptyIterable(issue.getSubtasks());
226         assertEmptyIterable(issue.getIssueLinks());
227         assertEmptyIterable(issue.getFixVersions());
228         assertEmptyIterable(issue.getAffectedVersions());
229         assertEmptyIterable(issue.getLabels());
230         assertNull(issue.getDueDate());
231         assertNull(issue.getTimeTracking());
232         assertNull(issue.getResolution());
233         assertNull(issue.getChangelog());
234         assertNull(issue.getAttachments());
235         // JIRA does not store timezone information in its dump file, so no timezone here
236         assertEquals(toDateTime("2010-09-22T18:06:32.000"), issue.getUpdateDate());
237         assertEquals(toDateTime("2010-09-22T18:06:32.000"), issue.getCreationDate());
238         assertEquals(IntegrationTestUtil.USER1_FULL, issue.getReporter());
239         assertEquals(IntegrationTestUtil.USER_ADMIN_FULL, issue.getAssignee());
240         assertEquals(new BasicProject(resolveURI(projectSelf), "TST", 10000L, "Test Project"), issue.getProject());
241         assertEquals(new BasicVotes(resolveURI("rest/api/2/issue/TST-7/votes"), 0, false), issue.getVotes());
242         assertEquals(new BasicWatchers(resolveURI("rest/api/2/issue/TST-7/watchers"), false, 0), issue.getWatchers());
243         assertThat(issue.getIssueType(), notNullValue());
244         assertThat(issue.getIssueType().getSelf(), is(resolveURI("rest/api/2/issuetype/3")));
245         assertThat(issue.getIssueType().getId(), is(3L));
246         assertThat(issue.getIssueType().getName(), is("Task"));
247         assertThat(issue.getIssueType().isSubtask(), is(false));
248         assertThat(issue.getIssueType().getDescription(), is("A task that needs to be done."));
249         assertThat(issue.getIssueType().getIconUri(), anyOf(
250                 is(resolveURI("images/icons/issuetypes/task.png")),
251                 is(resolveURI("images/icons/task.gif")),
252                 is(resolveURI("secure/viewavatar?size=xsmall&avatarId=10178&avatarType=issuetype"))
253         ));
254     }
255 
256     @Test
257     public void jqlSearchWithAllFieldsRequestedShouldReturnIssueWithAllFields() throws Exception {
258         jqlSearchWithAllFieldsImpl("key=TST-2");
259     }
260 
261     @Test
262     public void jqlSearchUsingPostWithAllFieldsRequestedShouldReturnIssueWithAllFields() throws Exception {
263         jqlSearchWithAllFieldsImpl(generateVeryLongJql() + "and key=TST-2");
264     }
265 
266     private void jqlSearchWithAllFieldsImpl(String jql) {
267         final ImmutableSet<String> fields = ImmutableSet.of("*all");
268         final SearchResult searchResult = client.getSearchClient().searchJql(jql, null, null, fields).claim();
269         final Issue issue = Iterables.getOnlyElement(searchResult.getIssues());
270 
271         assertEquals("TST-2", issue.getKey());
272         assertEquals("Testing attachem2", issue.getSummary());
273         assertEquals(new TimeTracking(0, 0, 145), issue.getTimeTracking());
274         assertThat(issue.getComponents(), NamedEntityMatchers.entitiesWithNames("Component A", "Component B"));
275 
276         // comments
277         final Iterable<Comment> comments = issue.getComments();
278         assertEquals(3, Iterables.size(comments));
279         assertEquals("a comment viewable only by jira-users", Iterables.getLast(comments).getBody());
280 
281         // worklogs
282         final Iterable<Worklog> worklogs = issue.getWorklogs();
283         assertThat(worklogs, AddressableEntityMatchers.entitiesWithSelf(
284                 resolveURI("rest/api/2/issue/10010/worklog/10010"),
285                 resolveURI("rest/api/2/issue/10010/worklog/10011"),
286                 resolveURI("rest/api/2/issue/10010/worklog/10012"),
287                 resolveURI("rest/api/2/issue/10010/worklog/10020"),
288                 resolveURI("rest/api/2/issue/10010/worklog/10021")
289         ));
290 
291         final Worklog actualWorklog = Iterables.getLast(worklogs);
292         final Worklog expectedWorklog = new Worklog(resolveURI("rest/api/2/issue/10010/worklog/10021"),
293                 resolveURI("rest/api/latest/issue/10010"), IntegrationTestUtil.USER_ADMIN, IntegrationTestUtil.USER_ADMIN,
294                 "Another work for 7 min", toDateTime("2010-08-27T15:00:02.104"), toDateTime("2010-08-27T15:00:02.104"),
295                 toDateTime("2010-08-27T14:59:00.000"), 7, null);
296         assertEquals(expectedWorklog, actualWorklog);
297 
298         // issue links
299         assertThat(issue.getIssueLinks(), IsIterableContainingInOrder.contains(
300                 new IssueLink("TST-1", resolveURI("rest/api/2/issue/10000"), new IssueLinkType("Duplicate", "duplicates", IssueLinkType.Direction.OUTBOUND)),
301                 new IssueLink("TST-1", resolveURI("rest/api/2/issue/10000"), new IssueLinkType("Duplicate", "is duplicated by", IssueLinkType.Direction.INBOUND))
302         ));
303 
304         // fix versions
305         final Version actualFixVersion = Iterables.getOnlyElement(issue.getFixVersions());
306         final Version expectedFixVersion = new Version(resolveURI("rest/api/2/version/10000"), 10000L, "1.1", "Some version", false, false, toDateTime("2010-08-25T00:00:00.000"));
307         assertEquals(expectedFixVersion, actualFixVersion);
308 
309         // affected versions
310         assertThat(issue.getAffectedVersions(), IsIterableContainingInOrder.contains(
311                 new Version(resolveURI("rest/api/2/version/10001"), 10001L, "1", "initial version", false, false, null),
312                 new Version(resolveURI("rest/api/2/version/10000"), 10000L, "1.1", "Some version", false, false, toDateTime("2010-08-25T00:00:00.000"))
313         ));
314 
315         // dates
316         assertNull(issue.getDueDate());
317         assertEquals(toDateTime("2010-08-30T10:49:33.000"), issue.getUpdateDate());
318         assertEquals(toDateTime("2010-07-26T13:29:18.000"), issue.getCreationDate());
319 
320         // attachments
321         final Iterable<String> attachmentsNames = EntityHelper.toFileNamesList(issue.getAttachments());
322         assertThat(attachmentsNames, containsInAnyOrder("10000_thumb_snipe.jpg", "Admal pompa ciepła.pdf",
323                 "apache-tomcat-5.5.30.zip", "jira_logo.gif", "snipe.png", "transparent-png.png"));
324     }
325 
326     @Test
327     public void jqlSearchShouldReturnIssueWithLabelsAndDueDate() throws Exception {
328         final SearchResult searchResult = client.getSearchClient().searchJql("key=TST-1").claim();
329         final Issue issue = Iterables.getOnlyElement(searchResult.getIssues());
330         assertEquals("TST-1", issue.getKey());
331         assertThat(issue.getLabels(), containsInAnyOrder("a", "bcds"));
332         assertEquals(toDateTime("2010-07-05T00:00:00.000"), issue.getDueDate());
333     }
334 
335     @Test
336     public void jqlSearchWithMinimalFieldSetShouldReturnParseableIssues() throws Exception {
337         final SearchRestClient searchClient = client.getSearchClient();
338         final SearchResult searchResult = searchClient.searchJql("key=TST-1", null, null, REQUIRED_ISSUE_FIELDS).claim();
339         final Issue issue = Iterables.getOnlyElement(searchResult.getIssues());
340         assertEquals("TST-1", issue.getKey());
341         assertEquals("My sample test", issue.getSummary());
342         assertEquals("Bug", issue.getIssueType().getName());
343         assertEquals(toDateTime("2010-07-23T12:16:56.000"), issue.getCreationDate());
344         assertEquals(toDateTime("2010-08-17T16:36:29.000"), issue.getUpdateDate());
345         assertEquals("Test Project", issue.getProject().getName());
346         assertEquals(Long.valueOf(10000), issue.getId());
347         assertEquals("Open", issue.getStatus().getName());
348 
349         // this issue has labels, but they were not returned by JIRA REST API
350         assertEmptyIterable(issue.getLabels());
351         final Issue fullIssue = client.getIssueClient().getIssue(issue.getKey()).claim();
352         assertThat(fullIssue.getLabels(), containsInAnyOrder("a", "bcds"));
353     }
354 
355     /**
356      * If this test fails, then maybe we accept missing field in issue parser? If yes, then we need to update
357      * javadoc for {@link SearchRestClient#searchJql(String, Integer, Integer, Set)}
358      */
359     @Test
360     public void jqlSearchWithoutOneOfRequiredFieldsShouldCauseParserFailure() {
361         final SearchRestClient searchClient = client.getSearchClient();
362 
363         for (final String missingField : REQUIRED_ISSUE_FIELDS) {
364             final Set<String> fieldsToRetrieve = Sets.difference(REQUIRED_ISSUE_FIELDS, Sets.newHashSet(missingField));
365 
366             try {
367                 searchClient.searchJql(null, 1, 0, fieldsToRetrieve).claim();
368                 throw new java.lang.AssertionError(String.format(
369                         "The above statement should throw exception. fieldsToRetrieve = %s, fields = %s, requiredFields = %s",
370                         missingField, fieldsToRetrieve, REQUIRED_ISSUE_FIELDS));
371             } catch (RestClientException e) {
372                 final String expectedMessage = String.format(
373                         "org.codehaus.jettison.json.JSONException: JSONObject[\"%s\"] not found.", missingField);
374                 assertEquals(expectedMessage, e.getMessage());
375             }
376         }
377     }
378 }