View Javadoc

1   /*
2    * Copyright (C) 2010-2014 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 com.atlassian.jira.rest.client.internal.json;
18  
19  import com.atlassian.jira.rest.client.api.domain.Attachment;
20  import com.atlassian.jira.rest.client.api.domain.BasicComponent;
21  import com.atlassian.jira.rest.client.api.domain.BasicPriority;
22  import com.atlassian.jira.rest.client.api.domain.BasicProject;
23  import com.atlassian.jira.rest.client.api.domain.BasicUser;
24  import com.atlassian.jira.rest.client.api.domain.BasicWatchers;
25  import com.atlassian.jira.rest.client.api.domain.ChangelogGroup;
26  import com.atlassian.jira.rest.client.api.domain.ChangelogItem;
27  import com.atlassian.jira.rest.client.api.domain.Comment;
28  import com.atlassian.jira.rest.client.api.domain.EntityHelper;
29  import com.atlassian.jira.rest.client.api.domain.FieldType;
30  import com.atlassian.jira.rest.client.api.domain.Issue;
31  import com.atlassian.jira.rest.client.api.domain.IssueField;
32  import com.atlassian.jira.rest.client.api.domain.IssueLink;
33  import com.atlassian.jira.rest.client.api.domain.IssueLinkType;
34  import com.atlassian.jira.rest.client.api.domain.IssueType;
35  import com.atlassian.jira.rest.client.api.domain.OperationGroup;
36  import com.atlassian.jira.rest.client.api.domain.OperationHeader;
37  import com.atlassian.jira.rest.client.api.domain.OperationLink;
38  import com.atlassian.jira.rest.client.api.domain.Operations;
39  import com.atlassian.jira.rest.client.api.domain.Subtask;
40  import com.atlassian.jira.rest.client.api.domain.TimeTracking;
41  import com.atlassian.jira.rest.client.api.domain.Visibility;
42  import com.atlassian.jira.rest.client.api.domain.Worklog;
43  import com.google.common.collect.ImmutableList;
44  import com.google.common.collect.Iterables;
45  import org.apache.commons.lang.StringUtils;
46  import org.codehaus.jettison.json.JSONException;
47  import org.codehaus.jettison.json.JSONObject;
48  import org.hamcrest.collection.IsEmptyCollection;
49  import org.hamcrest.collection.IsEmptyIterable;
50  import org.joda.time.format.ISODateTimeFormat;
51  import org.junit.Assert;
52  import org.junit.Test;
53  
54  import java.util.Collections;
55  import java.util.Iterator;
56  
57  import static com.atlassian.jira.rest.client.TestUtil.toDateTime;
58  import static com.atlassian.jira.rest.client.TestUtil.toDateTimeFromIsoDate;
59  import static com.atlassian.jira.rest.client.TestUtil.toUri;
60  import static com.atlassian.jira.rest.client.api.domain.EntityHelper.findAttachmentByFileName;
61  import static org.hamcrest.Matchers.is;
62  import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
63  import static org.junit.Assert.assertEquals;
64  import static org.junit.Assert.assertFalse;
65  import static org.junit.Assert.assertNotNull;
66  import static org.junit.Assert.assertNull;
67  import static org.junit.Assert.assertThat;
68  
69  // Ignore "May produce NPE" warnings, as we know what we are doing in tests
70  @SuppressWarnings("ConstantConditions")
71  public class IssueJsonParserTest {
72      @Test
73      public void testParseIssue() throws Exception {
74          final Issue issue = parseIssue("/json/issue/valid-all-expanded.json");
75  
76          assertEquals("Testing attachem2", issue.getSummary());
77          assertEquals("TST-2", issue.getKey());
78          assertEquals("my description", issue.getDescription());
79          assertEquals(Long.valueOf(10010), issue.getId());
80  
81          final BasicProject expectedProject = new BasicProject(toUri("http://localhost:8090/jira/rest/api/2/project/TST"), "TST", 10000L, "Test Project");
82          assertEquals(expectedProject, issue.getProject());
83  
84          assertEquals("Major", issue.getPriority().getName());
85          assertNull(issue.getResolution());
86          assertEquals(toDateTime("2010-07-26T13:29:18.262+0200"), issue.getCreationDate());
87          assertEquals(toDateTime("2012-12-07T14:52:52.570+01:00"), issue.getUpdateDate());
88          assertEquals(null, issue.getDueDate());
89  
90          final IssueType expectedIssueType = new IssueType(toUri("http://localhost:8090/jira/rest/api/2/issuetype/1"), 1L,
91                  "Bug", false, "A problem which impairs or prevents the functions of the product.",
92                  toUri("http://localhost:8090/jira/images/icons/bug.gif"));
93          assertEquals(expectedIssueType, issue.getIssueType());
94  
95          assertEquals(TestConstants.USER_ADMIN, issue.getReporter());
96          assertEquals(TestConstants.USER1, issue.getAssignee());
97  
98          // issue links
99          Assert.assertThat(issue.getIssueLinks(), containsInAnyOrder(
100                 new IssueLink("TST-1", toUri("http://localhost:8090/jira/rest/api/2/issue/10000"),
101                         new IssueLinkType("Duplicate", "duplicates", IssueLinkType.Direction.OUTBOUND)),
102                 new IssueLink("TST-1", toUri("http://localhost:8090/jira/rest/api/2/issue/10000"),
103                         new IssueLinkType("Duplicate", "is duplicated by", IssueLinkType.Direction.INBOUND))
104         ));
105 
106         // watchers
107         final BasicWatchers watchers = issue.getWatchers();
108         assertFalse(watchers.isWatching());
109         assertEquals(toUri("http://localhost:8090/jira/rest/api/2/issue/TST-2/watchers"), watchers.getSelf());
110         assertEquals(1, watchers.getNumWatchers());
111 
112         // time tracking
113         assertEquals(new TimeTracking(0, 0, 145), issue.getTimeTracking());
114 
115         // attachments
116         final Iterable<Attachment> attachments = issue.getAttachments();
117         assertEquals(7, Iterables.size(attachments));
118         final Attachment attachment = findAttachmentByFileName(attachments, "avatar1.png");
119         assertEquals(TestConstants.USER_ADMIN_BASIC, attachment.getAuthor());
120         assertEquals(359345, attachment.getSize());
121         assertEquals(toUri("http://localhost:8090/jira/secure/thumbnail/10070/_thumb_10070.png"), attachment.getThumbnailUri());
122         assertEquals(toUri("http://localhost:8090/jira/secure/attachment/10070/avatar1.png"), attachment.getContentUri());
123         final Iterable<String> attachmentsNames = EntityHelper.toFileNamesList(attachments);
124         assertThat(attachmentsNames, containsInAnyOrder("10000_thumb_snipe.jpg", "Admal pompa ciepła.pdf",
125                 "apache-tomcat-5.5.30.zip", "avatar1.png", "jira_logo.gif", "snipe.png", "transparent-png.png"));
126 
127         // worklogs
128         final Iterable<Worklog> worklogs = issue.getWorklogs();
129         assertEquals(5, Iterables.size(worklogs));
130         final Worklog expectedWorklog1 = new Worklog(
131                 toUri("http://localhost:8090/jira/rest/api/2/issue/10010/worklog/10011"),
132                 toUri("http://localhost:8090/jira/rest/api/latest/issue/10010"), TestConstants.USER1_BASIC,
133                 TestConstants.USER1_BASIC, "another piece of work",
134                 toDateTime("2010-08-17T16:38:00.013+02:00"), toDateTime("2010-08-17T16:38:24.948+02:00"),
135                 toDateTime("2010-08-17T16:37:00.000+02:00"), 15, Visibility.role("Developers"));
136         final Worklog worklog1 = Iterables.get(worklogs, 1);
137         assertEquals(expectedWorklog1, worklog1);
138 
139         final Worklog worklog2 = Iterables.get(worklogs, 2);
140         assertEquals(Visibility.group("jira-users"), worklog2.getVisibility());
141 
142         final Worklog worklog3 = Iterables.get(worklogs, 3);
143         assertEquals(StringUtils.EMPTY, worklog3.getComment());
144 
145         // comments
146         assertEquals(4, Iterables.size(issue.getComments()));
147         final Comment comment = issue.getComments().iterator().next();
148         assertEquals(Visibility.Type.ROLE, comment.getVisibility().getType());
149         assertEquals(TestConstants.USER_ADMIN_BASIC, comment.getAuthor());
150         assertEquals(TestConstants.USER_ADMIN_BASIC, comment.getUpdateAuthor());
151 
152         // components
153         final Iterable<String> componentsNames = EntityHelper.toNamesList(issue.getComponents());
154         assertThat(componentsNames, containsInAnyOrder("Component A", "Component B"));
155     }
156 
157     @Test
158     public void testParseIssueWithCustomFieldsValues() throws Exception {
159         final Issue issue = parseIssue("/json/issue/valid-all-expanded.json");
160 
161         // test float value: number, com.atlassian.jira.plugin.system.customfieldtypes:float
162         assertEquals(1.457, issue.getField("customfield_10000").getValue());
163 
164         // TODO: add assertions for more custom field types after fixing JRJC-122
165     }
166 
167     private Issue parseIssue(final String resourcePath) throws JSONException {
168         final JSONObject issueJson = ResourceUtil.getJsonObjectFromResource(resourcePath);
169         final IssueJsonParser parser = new IssueJsonParser();
170         return parser.parse(issueJson);
171     }
172 
173     @Test
174     public void testParseIssueWithResolution() throws JSONException {
175         final Issue issue = parseIssue("/json/issue/valid-all-expanded-with-resolution.json");
176         assertEquals("Incomplete", issue.getResolution().getName());
177 
178     }
179 
180     @Test
181     public void testParseIssueWhenWatchersAndVotersAreSwitchedOff() throws JSONException {
182         final Issue issue = parseIssue("/json/issue/valid-no-votes-no-watchers.json");
183         assertNull(issue.getWatchers());
184         assertNull(issue.getVotes());
185     }
186 
187     @Test
188     public void testParseUnassignedIssue() throws JSONException {
189         final Issue issue = parseIssue("/json/issue/valid-unassigned-no-time-tracking.json");
190         assertNull(issue.getAssignee());
191     }
192 
193     @Test
194     public void testParseNoTimeTrackingInfo() throws JSONException {
195         final Issue issue = parseIssue("/json/issue/valid-unassigned-no-time-tracking.json");
196         assertNull(issue.getTimeTracking());
197     }
198 
199     @Test
200     public void testParseIssueWithAnonymousComment() throws JSONException {
201         final Issue issue = parseIssue("/json/issue/valid-anonymous-comment.json");
202         assertEquals(1, Iterables.size(issue.getComments()));
203         final Comment comment = issue.getComments().iterator().next();
204         assertEquals("Comment from anonymous user", comment.getBody());
205         assertNull(comment.getAuthor());
206 
207     }
208 
209     @Test
210     public void testParseIssueWithVisibility() throws JSONException {
211         final Issue issue = parseIssue("/json/issue/valid-visibility.json");
212         assertEquals(Visibility.role("Administrators"), issue.getComments().iterator().next().getVisibility());
213         assertEquals(Visibility.role("Developers"), Iterables.get(issue.getWorklogs(), 1).getVisibility());
214         assertEquals(Visibility.group("jira-users"), Iterables.get(issue.getWorklogs(), 2).getVisibility());
215     }
216 
217     // TODO: temporary disabled as we want to run integration tests. Fix JRJC-122 and re-enable this test
218 //	@Test
219 //	public void testParseIssueWithUserPickerCustomFieldFilledOut() throws JSONException {
220 //		final Issue issue = parseIssue("/json/issue/valid-user-picker-custom-field-filled-out.json");
221 //		final IssueField extraUserField = issue.getFieldByName("Extra User");
222 //		assertNotNull(extraUserField);
223 //		assertEquals(BasicUser.class, extraUserField.getValue().getClass());
224 //		assertEquals(TestConstants.USER1, extraUserField.getValue());
225 //	}
226 
227     @Test
228     public void testParseIssueWithUserPickerCustomFieldEmpty() throws JSONException {
229         final Issue issue = parseIssue("/json/issue/valid-user-picker-custom-field-empty.json");
230         final IssueField extraUserIssueField = issue.getFieldByName("Extra User");
231         assertNotNull(extraUserIssueField);
232         assertNull(extraUserIssueField.getValue());
233     }
234 
235     @Test
236     public void testParseIssueJira5x0Representation() throws JSONException {
237         final Issue issue = parseIssue("/json/issue/valid-5.0.json");
238         assertEquals(3, Iterables.size(issue.getComments()));
239         final BasicPriority priority = issue.getPriority();
240         assertNotNull(priority);
241         assertEquals("Major", priority.getName());
242         assertEquals("my description", issue.getDescription());
243         assertEquals("TST", issue.getProject().getKey());
244         assertEquals(Long.valueOf(10000), issue.getId());
245         assertNotNull(issue.getDueDate());
246         assertEquals(toDateTimeFromIsoDate("2010-07-05"), issue.getDueDate());
247         assertEquals(4, Iterables.size(issue.getAttachments()));
248         assertEquals(1, Iterables.size(issue.getIssueLinks()));
249         assertEquals(1.457, issue.getField("customfield_10000").getValue());
250         assertThat(Iterables.transform(issue
251                 .getComponents(), EntityHelper.GET_ENTITY_NAME_FUNCTION), containsInAnyOrder("Component A", "Component B"));
252         assertEquals(2, Iterables.size(issue.getWorklogs()));
253         assertEquals(1, issue.getWatchers().getNumWatchers());
254         assertFalse(issue.getWatchers().isWatching());
255         assertEquals(new TimeTracking(2700, 2220, 180), issue.getTimeTracking());
256 
257         assertEquals(Visibility.role("Developers"), issue.getWorklogs().iterator().next().getVisibility());
258         assertEquals(Visibility.group("jira-users"), Iterables.get(issue.getWorklogs(), 1).getVisibility());
259 
260     }
261 
262     @Test
263     public void testParseIssueJira50Representation() throws JSONException {
264         final Issue issue = parseIssue("/json/issue/valid-5.0-1.json");
265         assertEquals(Long.valueOf(10001), issue.getId());
266         assertEquals(0, Iterables.size(issue.getComments()));
267         final BasicPriority priority = issue.getPriority();
268         assertNull(priority);
269         assertEquals("Pivotal Tracker provides time tracking information on the project level.\n"
270                 + "JIRA stores time tracking information on issue level, so this issue has been created to store imported time tracking information.", issue
271                 .getDescription());
272         assertEquals("TIMETRACKING", issue.getProject().getKey());
273         assertNull(issue.getDueDate());
274         assertEquals(0, Iterables.size(issue.getAttachments()));
275         assertNull(issue.getIssueLinks());
276         assertNull(issue.getField("customfield_10000").getValue());
277         assertThat(issue.getComponents(), IsEmptyIterable.<BasicComponent>emptyIterable());
278         assertEquals(2, Iterables.size(issue.getWorklogs()));
279         assertEquals(0, issue.getWatchers().getNumWatchers());
280         assertFalse(issue.getWatchers().isWatching());
281         assertEquals(new TimeTracking(null, null, 840), issue.getTimeTracking());
282 
283         assertNull(issue.getWorklogs().iterator().next().getVisibility());
284         assertNull(Iterables.get(issue.getWorklogs(), 1).getVisibility());
285     }
286 
287     @Test
288     public void testParseIssueWithProjectNamePresentInRepresentation() throws JSONException {
289         final Issue issue = parseIssue("/json/issue/issue-with-project-name-present.json");
290         assertEquals("My Test Project", issue.getProject().getName());
291     }
292 
293     @Test
294     public void testParseIssueJiraRepresentationJrjc49() throws JSONException {
295         final Issue issue = parseIssue("/json/issue/jrjc49.json");
296         final Iterable<Worklog> worklogs = issue.getWorklogs();
297         assertEquals(1, Iterables.size(worklogs));
298         final Worklog worklog = Iterables.get(worklogs, 0);
299         assertEquals("Worklog comment should be returned as empty string, when JIRA doesn't include it in reply",
300                 StringUtils.EMPTY, worklog.getComment());
301         assertEquals(180, worklog.getMinutesSpent());
302         assertEquals("deleteduser", worklog.getAuthor().getName());
303     }
304 
305     @Test
306     public void testParseIssueJira5x0RepresentationNullCustomField() throws JSONException {
307         final Issue issue = parseIssue("/json/issue/valid-5.0-null-custom-field.json");
308         assertEquals(null, issue.getField("customfield_10000").getValue());
309         assertNull(issue.getIssueLinks());
310     }
311 
312     @Test
313     public void issueWithSubtasks() throws JSONException {
314         final Issue issue = parseIssue("/json/issue/subtasks-5.json");
315         Iterable<Subtask> subtasks = issue.getSubtasks();
316         assertEquals(1, Iterables.size(subtasks));
317         Subtask subtask = Iterables.get(subtasks, 0, null);
318         assertNotNull(subtask);
319         assertEquals("SAM-2", subtask.getIssueKey());
320         assertEquals("Open", subtask.getStatus().getName());
321         assertEquals("Subtask", subtask.getIssueType().getName());
322     }
323 
324     @Test
325     public void issueWithChangelog() throws JSONException {
326         final Issue issue = parseIssue("/json/issue/valid-5.0-with-changelog.json");
327         assertEquals("HST-1", issue.getKey());
328 
329         final Iterable<ChangelogGroup> changelog = issue.getChangelog();
330         assertNotNull(changelog);
331 
332         assertEquals(4, Iterables.size(changelog));
333         final Iterator<ChangelogGroup> iterator = changelog.iterator();
334 
335         final BasicUser user1 = new BasicUser(toUri("http://localhost:2990/jira/rest/api/2/user?username=user1"), "user1", "User One");
336         final BasicUser user2 = new BasicUser(toUri("http://localhost:2990/jira/rest/api/2/user?username=user2"), "user2", "User Two");
337 
338         verifyChangelog(iterator.next(),
339                 "2012-04-12T14:28:28.255+0200",
340                 user1,
341                 ImmutableList.of(
342                         new ChangelogItem(FieldType.JIRA, "duedate", null, null, "2012-04-12", "2012-04-12 00:00:00.0"),
343                         new ChangelogItem(FieldType.CUSTOM, "Radio Field", null, null, "10000", "One")
344                 ));
345 
346         verifyChangelog(iterator.next(),
347                 "2012-04-12T14:28:44.079+0200",
348                 user1,
349                 ImmutableList.of(
350                         new ChangelogItem(FieldType.JIRA, "assignee", "user1", "User One", "user2", "User Two")
351                 ));
352 
353         verifyChangelog(iterator.next(),
354                 "2012-04-12T14:30:09.690+0200",
355                 user2,
356                 ImmutableList.of(
357                         new ChangelogItem(FieldType.JIRA, "summary", null, "Simple history test", null, "Simple history test - modified"),
358                         new ChangelogItem(FieldType.JIRA, "issuetype", "1", "Bug", "2", "New Feature"),
359                         new ChangelogItem(FieldType.JIRA, "priority", "3", "Major", "4", "Minor"),
360                         new ChangelogItem(FieldType.JIRA, "description", null, "Initial Description", null, "Modified Description"),
361                         new ChangelogItem(FieldType.CUSTOM, "Date Field", "2012-04-11T14:26+0200", "11/Apr/12 2:26 PM", "2012-04-12T14:26+0200", "12/Apr/12 2:26 PM"),
362                         new ChangelogItem(FieldType.JIRA, "duedate", "2012-04-12", "2012-04-12 00:00:00.0", "2012-04-13", "2012-04-13 00:00:00.0"),
363                         new ChangelogItem(FieldType.CUSTOM, "Radio Field", "10000", "One", "10001", "Two"),
364                         new ChangelogItem(FieldType.CUSTOM, "Text Field", null, "Initial text field value", null, "Modified text field value")
365                 ));
366 
367         verifyChangelog(iterator.next(),
368                 "2012-04-12T14:28:44.079+0200",
369                 null,
370                 ImmutableList.of(
371                         new ChangelogItem(FieldType.JIRA, "assignee", "user1", "User One", "user2", "User Two")
372                 ));
373     }
374 
375     private static void verifyChangelog(ChangelogGroup changelogGroup, String createdDate, BasicUser author, Iterable<ChangelogItem> expectedItems) {
376         assertEquals(ISODateTimeFormat.dateTime().parseDateTime(createdDate), changelogGroup.getCreated());
377         assertEquals(author, changelogGroup.getAuthor());
378         assertEquals(expectedItems, changelogGroup.getItems());
379     }
380 
381     @Test
382     public void testParseIssueWithLabelsForJira5x0() throws JSONException {
383         final Issue issue = parseIssue("/json/issue/valid-5.0-with-labels.json");
384         assertThat(issue.getLabels(), containsInAnyOrder("a", "bcds"));
385     }
386 
387     @Test
388     public void testParseIssueWithLabels() throws JSONException {
389         final Issue issue = parseIssue("/json/issue/valid-5.0-with-labels.json");
390         assertThat(issue.getLabels(), containsInAnyOrder("a", "bcds"));
391     }
392 
393     @Test
394     public void testParseIssueWithoutLabelsForJira5x0() throws JSONException {
395         final Issue issue = parseIssue("/json/issue/valid-5.0-without-labels.json");
396         assertThat(issue.getLabels(), IsEmptyCollection.<String>empty());
397     }
398 
399     @Test
400     public void testParseIssueWithOperations() throws JSONException {
401         final Issue issue = parseIssue("/json/issue/valid-5.0-with-operations.json");
402         assertThat(issue.getOperations(), is(new Operations(Collections.singleton(new OperationGroup(
403                 "opsbar-transitions",
404                 Collections.singleton(new OperationLink("action_id_4", "issueaction-workflow-transition",
405                         "Start Progress", "Start work on the issue", "/secure/WorkflowUIDispatcher.jspa?id=93813&action=4&atl_token=",
406                         10, null)),
407                 Collections.singleton(new OperationGroup(
408                         null,
409                         Collections.<OperationLink>emptyList(),
410                         Collections.<OperationGroup>emptyList(),
411                         new OperationHeader("opsbar-transitions_more", "Workflow", null, null),
412                         null)),
413                 null,
414                 20
415         )))));
416     }
417 
418 }