1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
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
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
113 assertEquals(new TimeTracking(0, 0, 145), issue.getTimeTracking());
114
115
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
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
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
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
162 assertEquals(1.457, issue.getField("customfield_10000").getValue());
163
164
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
218
219
220
221
222
223
224
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 }