1   /*
2    * Copyright (C) 2012 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  package it;
17  
18  import com.atlassian.jira.nimblefunctests.annotation.JiraBuildNumberDependent;
19  import com.atlassian.jira.nimblefunctests.annotation.RestoreOnce;
20  import com.atlassian.jira.rest.client.GetCreateIssueMetadataOptionsBuilder;
21  import com.atlassian.jira.rest.client.IntegrationTestUtil;
22  import com.atlassian.jira.rest.client.IssueRestClient;
23  import com.atlassian.jira.rest.client.NullProgressMonitor;
24  import com.atlassian.jira.rest.client.domain.Attachment;
25  import com.atlassian.jira.rest.client.domain.BasicUser;
26  import com.atlassian.jira.rest.client.domain.ChangelogGroup;
27  import com.atlassian.jira.rest.client.domain.ChangelogItem;
28  import com.atlassian.jira.rest.client.domain.CimIssueType;
29  import com.atlassian.jira.rest.client.domain.CimProject;
30  import com.atlassian.jira.rest.client.domain.Comment;
31  import com.atlassian.jira.rest.client.domain.Issue;
32  import com.atlassian.jira.rest.client.domain.TimeTracking;
33  import com.atlassian.jira.rest.client.domain.Transition;
34  import com.atlassian.jira.rest.client.domain.Visibility;
35  import com.atlassian.jira.rest.client.domain.Votes;
36  import com.atlassian.jira.rest.client.domain.Watchers;
37  import com.atlassian.jira.rest.client.domain.Worklog;
38  import com.atlassian.jira.rest.client.domain.input.FieldInput;
39  import com.atlassian.jira.rest.client.domain.input.TransitionInput;
40  import com.atlassian.jira.rest.client.internal.json.TestConstants;
41  import com.google.common.base.Predicate;
42  import com.google.common.collect.ImmutableList;
43  import com.google.common.collect.Iterables;
44  import org.joda.time.DateTime;
45  import org.joda.time.format.ISODateTimeFormat;
46  import org.junit.Test;
47  
48  import javax.ws.rs.core.Response;
49  import javax.ws.rs.core.UriBuilder;
50  import java.net.URISyntaxException;
51  import java.util.Collections;
52  import java.util.EnumSet;
53  import java.util.Iterator;
54  import java.util.List;
55  
56  import static com.atlassian.jira.rest.client.IntegrationTestUtil.USER1;
57  import static com.atlassian.jira.rest.client.IntegrationTestUtil.USER2;
58  import static com.atlassian.jira.rest.client.TestUtil.assertErrorCode;
59  import static com.atlassian.jira.rest.client.internal.ServerVersionConstants.BN_JIRA_5;
60  import static com.google.common.collect.Iterables.toArray;
61  import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
62  import static org.junit.Assert.*;
63  
64  /**
65   * Those tests mustn't change anything on server side, as jira is restored only once
66   */
67  @SuppressWarnings("ConstantConditions") // To ignore "May produce NPE" warnings
68  @RestoreOnce(TestConstants.DEFAULT_JIRA_DUMP_FILE)
69  public class JerseyIssueRestClientReadOnlyTest extends AbstractJerseyRestClientTest {
70  
71  	// no timezone here, as JIRA does not store timezone information in its dump file
72  	private final DateTime dateTime = ISODateTimeFormat.dateTimeParser().parseDateTime("2010-08-04T17:46:45.454");
73  
74  	@Test
75  	public void testGetWatchers() throws Exception {
76  		final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
77  		final Watchers watchers = client.getIssueClient().getWatchers(issue.getWatchers().getSelf(), new NullProgressMonitor());
78  		assertEquals(1, watchers.getNumWatchers());
79  		assertFalse(watchers.isWatching());
80  		assertThat(watchers.getUsers(), containsInAnyOrder(USER1));
81  	}
82  
83  	@Test
84  	public void testGetWatcherForAnonymouslyAccessibleIssue() {
85  		setAnonymousMode();
86  		final Issue issue = client.getIssueClient().getIssue("ANNON-1", new NullProgressMonitor());
87  		final Watchers watchers = client.getIssueClient().getWatchers(issue.getWatchers().getSelf(), pm);
88  		assertEquals(1, watchers.getNumWatchers());
89  		assertFalse(watchers.isWatching());
90  		assertTrue("JRADEV-3594 bug!!!", Iterables.isEmpty(watchers.getUsers()));
91  		// to save time
92  		assertEquals(new TimeTracking(2700, 2400, null), issue.getTimeTracking());
93  	}
94  
95  	@Test
96  	public void testGetIssueWithAnonymouslyCreatedAttachment() {
97  		setAnonymousMode();
98  		final Issue issue = client.getIssueClient().getIssue("ANONEDIT-1", new NullProgressMonitor());
99  		final Iterator<Attachment> attachmentIterator = issue.getAttachments().iterator();
100 		assertTrue(attachmentIterator.hasNext());
101 		assertNull(attachmentIterator.next().getAuthor());
102 	}
103 
104 	@Test
105 	public void testGetIssueWithAnonymouslyCreatedWorklogEntry() {
106 		setAnonymousMode();
107 		final Issue issue = client.getIssueClient().getIssue("ANONEDIT-2", new NullProgressMonitor());
108 		final Iterator<Worklog> worklogIterator = issue.getWorklogs().iterator();
109 		assertTrue(worklogIterator.hasNext());
110 		assertNull(worklogIterator.next().getAuthor());
111 	}
112 
113 	// URIs are broken in 5.0 - https://jdog.atlassian.com/browse/JRADEV-7691
114 	private void assertEqualsNoUri(BasicUser expected, BasicUser actual) {
115 		assertEquals(expected.getName(), actual.getName());
116 		assertEquals(expected.getDisplayName(), actual.getDisplayName());
117 	}
118 
119 
120 	@Test
121 	public void testGetIssue() throws Exception {
122 		final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
123 		assertEquals("TST-1", issue.getKey());
124 		assertTrue(issue.getSelf().toString().startsWith(jiraUri.toString()));
125 		assertEqualsNoUri(IntegrationTestUtil.USER_ADMIN, issue.getReporter());
126 		assertEqualsNoUri(IntegrationTestUtil.USER_ADMIN, issue.getAssignee());
127 
128 		assertThat(issue.getLabels(), containsInAnyOrder("a", "bcds"));
129 
130 		assertEquals(3, Iterables.size(issue.getComments()));
131 		final Iterable<String> expectedExpandos = isJira5xOrNewer()
132 				? ImmutableList.of("renderedFields", "names", "schema", "transitions", "operations", "editmeta", "changelog") : ImmutableList.of("html");
133 		assertThat(ImmutableList.copyOf(issue.getExpandos()), containsInAnyOrder(toArray(expectedExpandos, String.class)));
134 		assertEquals(new TimeTracking(null, 0, 190), issue.getTimeTracking());
135 		assertTrue(Iterables.size(issue.getFields()) > 0);
136 
137 		assertEquals(IntegrationTestUtil.START_PROGRESS_TRANSITION_ID, Iterables.size(issue.getAttachments()));
138 		final Iterable<Attachment> items = issue.getAttachments();
139 		assertNotNull(items);
140 		Attachment attachment1 = new Attachment(IntegrationTestUtil.concat(
141 				IntegrationTestUtil.TESTING_JIRA_5_OR_NEWER ? UriBuilder.fromUri(jiraUri).path("/rest/api/2/").build()
142 						: jiraRestRootUri, "/attachment/10040"),
143 				"dla Paw\u0142a.txt", IntegrationTestUtil.USER_ADMIN, dateTime, 643, "text/plain",
144 				IntegrationTestUtil.concat(jiraUri, "/secure/attachment/10040/dla+Paw%C5%82a.txt"), null);
145 
146 		assertEquals(attachment1, items.iterator().next());
147 
148 		// test for changelog
149 		assertNull(issue.getChangelog());
150 
151 		final Issue issueWithChangelog = client.getIssueClient().getIssue("TST-2", EnumSet.of(IssueRestClient.Expandos.CHANGELOG), pm);
152 		final Iterable<ChangelogGroup> changelog = issueWithChangelog.getChangelog();
153 		if (isJira5xOrNewer()) {
154 			assertNotNull(changelog);
155 			final ChangelogGroup chg1 = Iterables.get(changelog, 18);
156 			assertEquals("admin", chg1.getAuthor().getName());
157 			assertEquals("Administrator", chg1.getAuthor().getDisplayName());
158 			assertEquals(new DateTime(2010, 8, 17, 16, 40, 34, 924).toInstant(), chg1.getCreated().toInstant());
159 
160 			assertEquals(Collections
161 					.singletonList(new ChangelogItem(ChangelogItem.FieldType.JIRA, "status", "1", "Open", "3", "In Progress")), chg1
162 					.getItems());
163 
164 			final ChangelogGroup chg2 = Iterables.get(changelog, 20);
165 			assertEquals("admin", chg2.getAuthor().getName());
166 			assertEquals("Administrator", chg2.getAuthor().getDisplayName());
167 			assertEquals(new DateTime(2010, 8, 24, 16, 10, 23, 468).toInstant(), chg2.getCreated().toInstant());
168 
169 			final List<ChangelogItem> expected = ImmutableList.of(
170 					new ChangelogItem(ChangelogItem.FieldType.JIRA, "timeoriginalestimate", null, null, "0", "0"),
171 					new ChangelogItem(ChangelogItem.FieldType.CUSTOM, "My Radio buttons", null, null, null, "Another"),
172 					new ChangelogItem(ChangelogItem.FieldType.CUSTOM, "project3", null, null, "10000", "Test Project"),
173 					new ChangelogItem(ChangelogItem.FieldType.CUSTOM, "My Number Field New", null, null, null, "1.45")
174 			);
175 			assertEquals(expected, chg2.getItems());
176 		}
177 	}
178 
179 	@Test
180 	public void testGetIssueWithNonTrivialComments() {
181 		final Issue issue = client.getIssueClient().getIssue("TST-2", pm);
182 		final Iterable<Comment> comments = issue.getComments();
183 		assertEquals(3, Iterables.size(comments));
184 		final Comment c1 = Iterables.get(comments, 0);
185 		assertEquals(Visibility.role("Administrators"), c1.getVisibility());
186 
187 		final Comment c3 = Iterables.get(comments, 2);
188 		assertEquals(Visibility.group("jira-users"), c3.getVisibility());
189 
190 	}
191 
192 	@Test
193 	public void testGetIssueWithNoViewWatchersPermission() {
194 		setUser1();
195 		assertTrue(client.getIssueClient().getIssue("TST-1", pm).getWatchers().isWatching());
196 
197 		setUser2();
198 		final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
199 		assertFalse(issue.getWatchers().isWatching());
200 		client.getIssueClient().watch(issue.getWatchers().getSelf(), pm);
201 		final Issue watchedIssue = client.getIssueClient().getIssue("TST-1", pm);
202 		assertTrue(watchedIssue.getWatchers().isWatching());
203 		assertEquals(2, watchedIssue.getWatchers().getNumWatchers());
204 
205 		// although there are 2 watchers, only one is listed with details - the caller itself, as the caller does not
206 		// have view watchers and voters permission
207 		assertThat(client.getIssueClient().getWatchers(watchedIssue.getWatchers().getSelf(), pm).getUsers(), containsInAnyOrder(USER2));
208 	}
209 
210 	@Test
211 	public void testGetVoter() {
212 		final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
213 		final Votes votes = client.getIssueClient().getVotes(issue.getVotes().getSelf(), pm);
214 		assertFalse(votes.hasVoted());
215 		assertThat(votes.getUsers(), containsInAnyOrder(USER1));
216 	}
217 
218 	@Test
219 	public void testGetVotersWithoutViewIssuePermission() {
220 		final Issue issue = client.getIssueClient().getIssue("RST-1", pm);
221 		setUser2();
222 		final String optionalDot = isJira5xOrNewer() ? "." : "";
223 		assertErrorCode(Response.Status.FORBIDDEN, "You do not have the permission to see the specified issue" + optionalDot, new Runnable() {
224 			@Override
225 			public void run() {
226 				client.getIssueClient().getVotes(issue.getVotes().getSelf(), pm);
227 			}
228 		});
229 	}
230 
231 	@Test
232 	public void testGetVotersWithoutViewVotersPermission() {
233 		setUser2();
234 		assertNumVotesAndNoVotersDetails("TST-1", 1);
235 	}
236 
237 	@Test
238 	public void testGetVotersAnonymously() {
239 		setAnonymousMode();
240 		assertNumVotesAndNoVotersDetails("ANNON-1", 0);
241 	}
242 
243 
244 	private void assertNumVotesAndNoVotersDetails(final String issueKey, final int numVotes) {
245 		final Issue issue = client.getIssueClient().getIssue(issueKey, pm);
246 		assertEquals(numVotes, issue.getVotes().getVotes());
247 		assertFalse(issue.getVotes().hasVoted());
248 		final Votes votes = client.getIssueClient().getVotes(issue.getVotes().getSelf(), pm);
249 		assertFalse(votes.hasVoted());
250 		assertEquals(numVotes, votes.getVotes());
251 		assertTrue(Iterables.isEmpty(votes.getUsers()));
252 	}
253 
254 
255 	@Test
256 	public void testGetTransitions() throws Exception {
257 		final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
258 		final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue, pm);
259 		assertEquals(4, Iterables.size(transitions));
260 		assertTrue(Iterables.contains(transitions, new Transition("Start Progress", IntegrationTestUtil.START_PROGRESS_TRANSITION_ID, Collections.<Transition.Field>emptyList())));
261 	}
262 
263 	@Test
264 	public void testTransition() throws Exception {
265 		final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
266 		final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue, pm);
267 		assertEquals(4, Iterables.size(transitions));
268 		final Transition startProgressTransition = new Transition("Start Progress", IntegrationTestUtil.START_PROGRESS_TRANSITION_ID, Collections.<Transition.Field>emptyList());
269 		assertTrue(Iterables.contains(transitions, startProgressTransition));
270 
271 		client.getIssueClient().transition(issue, new TransitionInput(IntegrationTestUtil.START_PROGRESS_TRANSITION_ID,
272 				Collections.<FieldInput>emptyList(), Comment.valueOf("My test comment")), new NullProgressMonitor()) ;
273 		final Issue transitionedIssue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
274 		assertEquals("In Progress", transitionedIssue.getStatus().getName());
275 		final Iterable<Transition> transitionsAfterTransition = client.getIssueClient().getTransitions(issue, pm);
276 		assertFalse(Iterables.contains(transitionsAfterTransition, startProgressTransition));
277 		final Transition stopProgressTransition = new Transition("Stop Progress", IntegrationTestUtil.STOP_PROGRESS_TRANSITION_ID, Collections.<Transition.Field>emptyList());
278 		assertTrue(Iterables.contains(transitionsAfterTransition, stopProgressTransition));
279 	}
280 
281 	@JiraBuildNumberDependent(BN_JIRA_5)
282 	@Test
283 	public void testGetCreateIssueMetadata() throws URISyntaxException {
284 		final Iterable<CimProject> metadataProjects = client
285 				.getIssueClient()
286 				.getCreateIssueMetadata(new GetCreateIssueMetadataOptionsBuilder().withExpandedIssueTypesFields().build(), pm);
287 
288 		assertEquals(4, Iterables.size(metadataProjects));
289 
290 		final CimProject project = Iterables.find(metadataProjects, new Predicate<CimProject>() {
291 			@Override
292 			public boolean apply(CimProject input) {
293 				return "ANONEDIT".equals(input.getKey());
294 			}
295 		});
296 
297 		assertEquals(project.getName(), "Anonymous Editable Project");
298 
299 		for (CimIssueType issueType : project.getIssueTypes()) {
300 			assertFalse(String.format("Issue type ('%s') fields are not empty!", issueType.getName()), issueType.getFields().isEmpty());
301 		}
302 	}
303 
304 	@JiraBuildNumberDependent(BN_JIRA_5)
305 	@Test
306 	public void testGetCreateIssueMetadataWithFieldsNotExpanded() throws URISyntaxException {
307 		final Iterable<CimProject> metadataProjects = client
308 				.getIssueClient()
309 				.getCreateIssueMetadata(null, pm);
310 
311 		assertEquals(4, Iterables.size(metadataProjects));
312 
313 		final CimProject project = Iterables.find(metadataProjects, new Predicate<CimProject>() {
314 			@Override
315 			public boolean apply(CimProject input) {
316 				return "ANONEDIT".equals(input.getKey());
317 			}
318 		});
319 
320 		assertEquals(project.getName(), "Anonymous Editable Project");
321 		assertEquals(5, Iterables.size(project.getIssueTypes()));
322 
323 		for (CimIssueType issueType : project.getIssueTypes()) {
324 			assertTrue(issueType.getFields().isEmpty());
325 		}
326 	}
327 
328 	@JiraBuildNumberDependent(BN_JIRA_5)
329 	@Test
330 	public void testGetCreateIssueMetadataWithProjectKeyFilter() throws URISyntaxException {
331 		final Iterable<CimProject> metadataProjects = client
332 				.getIssueClient()
333 				.getCreateIssueMetadata(
334 						new GetCreateIssueMetadataOptionsBuilder().withProjectKeys("ANONEDIT", "TST").withExpandedIssueTypesFields().build(),
335 						pm
336 				);
337 
338 		assertEquals(2, Iterables.size(metadataProjects));
339 
340 		final CimProject project = Iterables.find(metadataProjects, new Predicate<CimProject>() {
341 			@Override
342 			public boolean apply(CimProject input) {
343 				return "TST".equals(input.getKey());
344 			}
345 		});
346 
347 		assertEquals(project.getName(), "Test Project");
348 		assertEquals(5, Iterables.size(project.getIssueTypes()));
349 
350 		for (CimIssueType issueType : project.getIssueTypes()) {
351 			assertFalse(issueType.getFields().isEmpty());
352 		}
353 	}
354 }