1   /*
2    * Copyright (C) 2010 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.BasicComponentNameExtractionFunction;
20  import com.atlassian.jira.rest.client.IterableMatcher;
21  import com.atlassian.jira.rest.client.domain.Attachment;
22  import com.atlassian.jira.rest.client.domain.BasicIssueType;
23  import com.atlassian.jira.rest.client.domain.BasicPriority;
24  import com.atlassian.jira.rest.client.domain.BasicProject;
25  import com.atlassian.jira.rest.client.domain.BasicUser;
26  import com.atlassian.jira.rest.client.domain.BasicWatchers;
27  import com.atlassian.jira.rest.client.domain.ChangelogGroup;
28  import com.atlassian.jira.rest.client.domain.ChangelogItem;
29  import com.atlassian.jira.rest.client.domain.Comment;
30  import com.atlassian.jira.rest.client.domain.Field;
31  import com.atlassian.jira.rest.client.domain.Issue;
32  import com.atlassian.jira.rest.client.domain.IssueLink;
33  import com.atlassian.jira.rest.client.domain.IssueLinkType;
34  import com.atlassian.jira.rest.client.domain.Subtask;
35  import com.atlassian.jira.rest.client.domain.TimeTracking;
36  import com.atlassian.jira.rest.client.domain.Visibility;
37  import com.atlassian.jira.rest.client.domain.Worklog;
38  import com.google.common.collect.ImmutableList;
39  import com.google.common.collect.Iterables;
40  import org.codehaus.jettison.json.JSONException;
41  import org.codehaus.jettison.json.JSONObject;
42  import org.joda.time.format.ISODateTimeFormat;
43  import org.junit.Assert;
44  import org.junit.Test;
45  
46  import java.util.Iterator;
47  
48  import static com.atlassian.jira.rest.client.TestUtil.toDateTime;
49  import static com.atlassian.jira.rest.client.TestUtil.toUri;
50  import static org.junit.Assert.*;
51  
52  public class IssueJsonParserTest {
53  	@Test
54  	public void testParseIssue() throws Exception {
55  		final Issue issue = parseIssue("/json/issue/valid-all-expanded.json");
56  		assertExpectedIssue(issue);
57  	}
58  
59  	@Test
60  	public void testParseIssueJira4x2() throws Exception {
61  		final Issue issue = parseIssue("/json/issue/valid-all-expanded-jira-4-2.json");
62  		assertExpectedIssue(issue);
63  	}
64  
65  	private void assertExpectedIssue(Issue issue) {
66  		assertEquals("Testing issue", issue.getSummary());
67  		assertEquals("TST-2", issue.getKey());
68  		assertEquals(new BasicIssueType(toUri("http://localhost:8090/jira/rest/api/latest/issueType/1"), "Bug", false),
69  				issue.getIssueType());
70  		assertEquals(new BasicProject(toUri("http://localhost:8090/jira/rest/api/latest/project/TST"), "TST", null), issue.getProject());
71  		assertEquals("Major", issue.getPriority().getName());
72  		assertNull(issue.getResolution());
73  		assertEquals(toDateTime("2010-07-26T13:29:18.262+0200"), issue.getCreationDate());
74  		assertEquals(toDateTime("2010-08-27T15:00:02.107+0200"), issue.getUpdateDate());
75  
76  		assertEquals(TestConstants.USER_ADMIN, issue.getReporter());
77  		assertEquals(TestConstants.USER1, issue.getAssignee());
78  
79  		// issue links
80  		Assert.assertThat(issue.getIssueLinks(), IterableMatcher.hasOnlyElements(
81  				new IssueLink("TST-1", toUri("http://localhost:8090/jira/rest/api/latest/issue/TST-1"),
82  						new IssueLinkType("Duplicate", "duplicates", IssueLinkType.Direction.OUTBOUND)),
83  				new IssueLink("TST-1", toUri("http://localhost:8090/jira/rest/api/latest/issue/TST-1"),
84  						new IssueLinkType("Duplicate", "is duplicated by", IssueLinkType.Direction.INBOUND))
85  		));
86  
87  
88  		// watchers
89  		final BasicWatchers watchers = issue.getWatchers();
90  		assertFalse(watchers.isWatching());
91  		assertEquals(toUri("http://localhost:8090/jira/rest/api/latest/issue/TST-2/watchers"), watchers.getSelf());
92  		assertEquals(1, watchers.getNumWatchers());
93  		assertEquals(new TimeTracking(0, 0, 145), issue.getTimeTracking());
94  
95  		// attachments
96  		final Iterable<Attachment> attachments = issue.getAttachments();
97  		assertEquals(3, Iterables.size(attachments));
98  		final Attachment attachment = attachments.iterator().next();
99  		assertEquals("jira_logo.gif", attachment.getFilename());
100 		assertEquals(TestConstants.USER_ADMIN, attachment.getAuthor());
101 		assertEquals(2517, attachment.getSize());
102 		assertEquals(toUri("http://localhost:8090/jira/secure/thumbnail/10036/10036_jira_logo.gif"), attachment.getThumbnailUri());
103 		final Iterator<Attachment> attachmentIt = attachments.iterator();
104 		attachmentIt.next();
105 		attachmentIt.next();
106 		final Attachment lastAttachment = attachmentIt.next();
107 		assertEquals("transparent-png.png", lastAttachment.getFilename());
108 
109 		// worklogs
110 		final Iterable<Worklog> worklogs = issue.getWorklogs();
111 		assertEquals(5, Iterables.size(worklogs));
112 		final Worklog worklog = Iterables.get(worklogs, 2);
113 		assertEquals(new Worklog(toUri("http://localhost:8090/jira/rest/api/latest/worklog/10012"),
114 				toUri("http://localhost:8090/jira/rest/api/latest/issue/TST-2"), TestConstants.USER1,
115 				TestConstants.USER1, "a worklog viewable just by jira-users",
116 				toDateTime("2010-08-17T16:53:15.848+0200"), toDateTime("2010-08-17T16:53:15.848+0200"),
117 				toDateTime("2010-08-11T16:52:00.000+0200"), 3, Visibility.group("jira-users")), worklog);
118 
119 		final Worklog worklog3 = Iterables.get(worklogs, 3);
120 		assertEquals("", worklog3.getComment());
121 
122 		// comments
123 		assertEquals(3, Iterables.size(issue.getComments()));
124 		final Comment comment = issue.getComments().iterator().next();
125 		assertEquals(Visibility.Type.ROLE, comment.getVisibility().getType());
126 		assertEquals(TestConstants.USER_ADMIN, comment.getAuthor());
127 		assertEquals(TestConstants.USER_ADMIN, comment.getUpdateAuthor());
128 	}
129 
130 	private Issue parseIssue(final String resourcePath) throws JSONException {
131 		final JSONObject issueJson = ResourceUtil.getJsonObjectFromResource(resourcePath);
132 		final IssueJsonParser parser = new IssueJsonParser();
133 		return parser.parse(issueJson);
134 	}
135 
136 	@Test
137 	public void testParseIssueWithResolution() throws JSONException {
138 		final Issue issue = parseIssue("/json/issue/valid-all-expanded-with-resolution.json");
139 		assertEquals("Incomplete", issue.getResolution().getName());
140 
141 	}
142 
143 	@Test
144 	public void testParseIssueWhenWatchersAndVotersAreSwitchedOff() throws JSONException {
145 		final Issue issue = parseIssue("/json/issue/valid-no-votes-no-watchers.json");
146 		assertNull(issue.getWatchers());
147 		assertNull(issue.getVotes());
148 	}
149 
150 	@Test
151 	public void testParseUnassignedIssue() throws JSONException {
152 		final Issue issue = parseIssue("/json/issue/valid-unassigned.json");
153 		assertNull(issue.getAssignee());
154 	}
155 
156 	@Test
157 	public void testParseNoTimeTrackingInfo() throws JSONException {
158 		final Issue issue = parseIssue("/json/issue/valid-unassigned.json");
159 		assertNull(issue.getTimeTracking());
160 	}
161 
162 	@Test
163 	public void testParseUnassignedIssueJira4x3() throws JSONException {
164 		final Issue issue = parseIssue("/json/issue/valid-unassigned-jira-4.3.json");
165 		assertNull(issue.getAssignee());
166 	}
167 
168 	@Test
169 	public void testParseIssueWithAnonymousComment() throws JSONException {
170 		final Issue issue = parseIssue("/json/issue/valid-anonymous-comment-jira-4.3.json");
171 		assertEquals(1, Iterables.size(issue.getComments()));
172 		final Comment comment = issue.getComments().iterator().next();
173 		assertEquals("A comment from anonymous user", comment.getBody());
174 		assertNull(comment.getAuthor());
175 
176 	}
177 
178 	@Test
179 	public void testParseIssueWithVisibilityJira4x3() throws JSONException {
180 		final Issue issue = parseIssue("/json/issue/valid-visibility-jira-4.3.json");
181 		assertEquals(Visibility.role("Administrators"), issue.getComments().iterator().next().getVisibility());
182 		assertEquals(Visibility.role("Developers"), Iterables.get(issue.getWorklogs(), 1).getVisibility());
183 		assertEquals(Visibility.group("jira-users"), Iterables.get(issue.getWorklogs(), 2).getVisibility());
184 	}
185 
186 	@Test
187 	public void testParseIssueWithUserPickerCustomFieldFilledOut() throws JSONException {
188 		final Issue issue = parseIssue("/json/issue/valid-user-picker-custom-field-filled-out.json");
189 		final Field extraUserField = issue.getFieldByName("Extra User");
190 		assertNotNull(extraUserField);
191 		assertEquals(BasicUser.class, extraUserField.getValue().getClass());
192 		assertEquals(TestConstants.USER1, extraUserField.getValue());
193 	}
194 
195 	@Test
196 	public void testParseIssueWithUserPickerCustomFieldEmpty() throws JSONException {
197 		final Issue issue = parseIssue("/json/issue/valid-user-picker-custom-field-empty.json");
198 		final Field extraUserField = issue.getFieldByName("Extra User");
199 		assertNotNull(extraUserField);
200 		assertNull(extraUserField.getValue());
201 	}
202 
203 	@Test
204 	public void testParseIssueJira5x0Representation() throws JSONException {
205 		final Issue issue = parseIssue("/json/issue/valid-5.0.json");
206 		assertEquals(3, Iterables.size(issue.getComments()));
207 		final BasicPriority priority = issue.getPriority();
208 		assertNotNull(priority);
209 		assertEquals("Major", priority.getName());
210 		assertEquals("my description", issue.getDescription());
211 		assertEquals("TST", issue.getProject().getKey());
212 		assertEquals(4, Iterables.size(issue.getAttachments()));
213 		assertEquals(1, Iterables.size(issue.getIssueLinks()));
214 		assertEquals(1.457, issue.getField("customfield_10000").getValue());
215 		assertThat(Iterables.transform(issue.getComponents(), new BasicComponentNameExtractionFunction()), IterableMatcher.hasOnlyElements("Component A", "Component B"));
216 		assertEquals(2, Iterables.size(issue.getWorklogs()));
217 		assertEquals(1, issue.getWatchers().getNumWatchers());
218 		assertFalse(issue.getWatchers().isWatching());
219 		assertEquals(new TimeTracking(2700, 2220, 180), issue.getTimeTracking());
220 
221 		assertEquals(Visibility.role("Developers"), issue.getWorklogs().iterator().next().getVisibility());
222 		assertEquals(Visibility.group("jira-users"), Iterables.get(issue.getWorklogs(), 1).getVisibility());
223 
224 	}
225 
226 	@Test
227 	public void testParseIssueJira50Representation() throws JSONException {
228 		final Issue issue = parseIssue("/json/issue/valid-5.0-1.json");
229 	}
230 
231     @Test
232     public void testParseIssueWithProjectNamePresentInRepresentation() throws JSONException {
233         final Issue issue = parseIssue("/json/issue/issue-with-project-name-present.json");
234         assertEquals("My Test Project", issue.getProject().getName());
235     }
236 
237     @Test
238     public void testParseIssueJiraRepresentationJrjc49() throws JSONException {
239         final Issue issue = parseIssue("/json/issue/jrjc49.json");
240         final Iterable<Worklog> worklogs = issue.getWorklogs();
241         assertEquals(1, Iterables.size(worklogs));
242         final Worklog worklog = Iterables.get(worklogs, 0);
243         assertNull(worklog.getComment());
244         assertEquals(180, worklog.getMinutesSpent());
245         assertEquals("Sample, User", worklog.getAuthor().getDisplayName());
246 
247     }
248 
249 	@Test
250 	public void testParseIssueJira5x0RepresentationNullCustomField() throws JSONException {
251 		final Issue issue = parseIssue("/json/issue/valid-5.0-null-custom-field.json");
252 		assertEquals(null, issue.getField("customfield_10000").getValue());
253 		assertNull(issue.getIssueLinks());
254 	}
255 
256 	@Test
257 	public void issueWithSubtasks() throws JSONException {
258 		final Issue issue = parseIssue("/json/issue/subtasks-5.json");
259 		Iterable<Subtask> subtasks = issue.getSubtasks();
260 		assertEquals(1, Iterables.size(subtasks));
261 		Subtask subtask = Iterables.get(subtasks, 0, null);
262 		assertNotNull(subtask);
263 		assertEquals("SAM-2", subtask.getIssueKey());
264 		assertEquals("Open", subtask.getStatus().getName());
265 		assertEquals("Subtask", subtask.getIssueType().getName());
266 	}
267 
268 	@Test
269 	public void issueWithChangelog() throws JSONException {
270 		final Issue issue = parseIssue("/json/issue/valid-5.0-with-changelog.json");
271 		assertEquals("HST-1", issue.getKey());
272 
273 		final Iterable<ChangelogGroup> changelog = issue.getChangelog();
274 		assertNotNull(changelog);
275 
276 		assertEquals(3, Iterables.size(changelog));
277 		final Iterator<ChangelogGroup> iterator = changelog.iterator();
278 
279 		final BasicUser user1 = new BasicUser(toUri("http://localhost:2990/jira/rest/api/2/user?username=user1"), "user1", "User One");
280 		final BasicUser user2 = new BasicUser(toUri("http://localhost:2990/jira/rest/api/2/user?username=user2"), "user2", "User Two");
281 
282 		verifyChangelog(iterator.next(),
283 				"2012-04-12T14:28:28.255+0200",
284 				user1,
285 				ImmutableList.of(
286 						new ChangelogItem(ChangelogItem.FieldType.JIRA, "duedate", null, null, "2012-04-12", "2012-04-12 00:00:00.0"),
287 						new ChangelogItem(ChangelogItem.FieldType.CUSTOM, "Radio Field", null, null, "10000", "One")
288 				));
289 
290 		verifyChangelog(iterator.next(),
291 				"2012-04-12T14:28:44.079+0200",
292 				user1,
293 				ImmutableList.of(
294 						new ChangelogItem(ChangelogItem.FieldType.JIRA, "assignee", "user1", "User One", "user2", "User Two")
295 				));
296 
297 		verifyChangelog(iterator.next(),
298 				"2012-04-12T14:30:09.690+0200",
299 				user2,
300 				ImmutableList.of(
301 						new ChangelogItem(ChangelogItem.FieldType.JIRA, "summary", null, "Simple history test", null, "Simple history test - modified"),
302 						new ChangelogItem(ChangelogItem.FieldType.JIRA, "issuetype", "1", "Bug", "2", "New Feature"),
303 						new ChangelogItem(ChangelogItem.FieldType.JIRA, "priority", "3", "Major", "4", "Minor"),
304 						new ChangelogItem(ChangelogItem.FieldType.JIRA, "description", null, "Initial Description", null, "Modified Description"),
305 						new ChangelogItem(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"),
306 						new ChangelogItem(ChangelogItem.FieldType.JIRA, "duedate", "2012-04-12", "2012-04-12 00:00:00.0", "2012-04-13", "2012-04-13 00:00:00.0"),
307 						new ChangelogItem(ChangelogItem.FieldType.CUSTOM, "Radio Field", "10000", "One", "10001", "Two"),
308 						new ChangelogItem(ChangelogItem.FieldType.CUSTOM, "Text Field", null, "Initial text field value", null, "Modified text field value")
309 				));
310 	}
311 
312 	private static void verifyChangelog(ChangelogGroup changelogGroup, String createdDate, BasicUser author, Iterable<ChangelogItem> expectedItems) {
313 		assertEquals(ISODateTimeFormat.dateTime().parseDateTime(createdDate), changelogGroup.getCreated());
314 		assertEquals(author, changelogGroup.getAuthor());
315 		assertEquals(expectedItems, changelogGroup.getItems());
316 	}
317 
318 	@Test
319 	public void testParseIssueWithLabelsForJira5x0() throws JSONException {
320 		final Issue issue = parseIssue("/json/issue/valid-5.0-with-labels.json");
321 		assertThat(issue.getLabels(), IterableMatcher.hasOnlyElements("a", "bcds"));
322 	}
323 
324 	@Test
325 	public void testParseIssueWithLabels() throws JSONException {
326 		final Issue issue = parseIssue("/json/issue/valid-5.0-with-labels.json");
327 		assertThat(issue.getLabels(), IterableMatcher.hasOnlyElements("a", "bcds"));
328 	}
329 
330 	@Test
331 	public void testParseIssueWithoutLabelsForJira5x0() throws JSONException {
332 		final Issue issue = parseIssue("/json/issue/valid-5.0-without-labels.json");
333 		assertThat(issue.getLabels(), IterableMatcher.<String>isEmpty());
334 	}
335 
336 	@Test
337 	public void testParseIssueWithoutLabels() throws JSONException {
338 		final Issue issue = parseIssue("/json/issue/valid-without-labels.json");
339 		assertThat(issue.getLabels(), IterableMatcher.<String>isEmpty());
340 	}
341 
342 }