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 it;
18  
19  import com.atlassian.jira.rest.client.IntegrationTestUtil;
20  import com.atlassian.jira.rest.client.IssueRestClient;
21  import com.atlassian.jira.rest.client.IterableMatcher;
22  import com.atlassian.jira.rest.client.NullProgressMonitor;
23  import com.atlassian.jira.rest.client.domain.Attachment;
24  import com.atlassian.jira.rest.client.domain.Comment;
25  import com.atlassian.jira.rest.client.domain.input.FieldInput;
26  import com.atlassian.jira.rest.client.domain.Issue;
27  import com.atlassian.jira.rest.client.domain.Transition;
28  import com.atlassian.jira.rest.client.domain.input.TransitionInput;
29  import com.atlassian.jira.rest.client.domain.Votes;
30  import com.atlassian.jira.rest.client.domain.Watchers;
31  import com.google.common.collect.Iterables;
32  import org.hamcrest.Matchers;
33  import org.joda.time.DateTime;
34  import org.joda.time.format.ISODateTimeFormat;
35  import org.junit.Assert;
36  import org.junit.Test;
37  
38  import javax.ws.rs.core.Response;
39  import java.text.NumberFormat;
40  import java.util.Arrays;
41  import java.util.Collections;
42  import java.util.Locale;
43  
44  import static com.atlassian.jira.rest.client.IntegrationTestUtil.*;
45  import static com.atlassian.jira.rest.client.TestUtil.assertErrorCode;
46  import static com.atlassian.jira.rest.client.internal.json.TestConstants.USER1_USERNAME;
47  import static com.atlassian.jira.rest.client.internal.json.TestConstants.USER2_USERNAME;
48  import static org.junit.Assert.assertThat;
49  
50  
51  public class JerseyIssueRestClientTest extends AbstractRestoringJiraStateJerseyRestClientTest {
52  
53  	// no timezone here, as JIRA does not store timezone information in its dump file
54  	private final DateTime dateTime = ISODateTimeFormat.dateTimeParser().parseDateTime("2010-08-04T17:46:45.454");
55  
56  	@Test
57  	public void testGetWatchers() throws Exception {
58  		final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
59  		final Watchers watchers = client.getIssueClient().getWatchers(issue.getWatchers().getSelf(), new NullProgressMonitor());
60  		assertEquals(1, watchers.getNumWatchers());
61  		assertFalse(watchers.isWatching());
62  		assertThat(watchers.getUsers(), IterableMatcher.hasOnlyElements(USER1));
63  	}
64  
65  	public void testGetWatcherForAnonymouslyAccessibleIssue() {
66  		setAnonymousMode();
67  		final Issue issue = client.getIssueClient().getIssue("ANNON-1", new NullProgressMonitor());
68  		final Watchers watchers = client.getIssueClient().getWatchers(issue.getWatchers().getSelf(), pm);
69  		assertEquals(1, watchers.getNumWatchers());
70  		assertFalse(watchers.isWatching());
71  		assertTrue("JRADEV-3594 bug!!!", Iterables.isEmpty(watchers.getUsers()));
72  	}
73  
74  
75  	@Test
76  	public void testGetIssue() throws Exception {
77  		final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
78  		assertEquals("TST-1", issue.getKey());
79  		assertTrue(issue.getSelf().toString().startsWith(jiraUri.toString()));
80  		assertEquals(IntegrationTestUtil.USER_ADMIN, issue.getReporter());
81  		assertEquals(IntegrationTestUtil.USER_ADMIN, issue.getAssignee());
82  
83  		assertEquals(3, Iterables.size(issue.getComments()));
84  		assertThat(issue.getExpandos(), IterableMatcher.hasOnlyElements("html"));
85  		assertTrue(Iterables.size(issue.getFields()) > 0);
86  
87  		assertEquals(IntegrationTestUtil.START_PROGRESS_TRANSITION_ID, Iterables.size(issue.getAttachments()));
88  		final Iterable<Attachment> items = issue.getAttachments();
89  		assertNotNull(items);
90  		Attachment attachment1 = new Attachment(IntegrationTestUtil.concat(jiraRestRootUri, "/attachment/10040"),
91  				"dla Paw\u0142a.txt", IntegrationTestUtil.USER_ADMIN, dateTime, 643, "text/plain",
92  				IntegrationTestUtil.concat(jiraUri, "/secure/attachment/10040/dla+Paw%C5%82a.txt"), null);
93  
94  		assertEquals(attachment1, items.iterator().next());
95  
96  	}
97  
98  
99  	public void testGetIssueWithNonTrivialComments() {
100 		final Issue issue = client.getIssueClient().getIssue("TST-2", pm);
101 		final Iterable<Comment> comments = issue.getComments();
102 		assertEquals(3, Iterables.size(comments));
103 		final Comment c1 = Iterables.get(comments, 0);
104 		assertEquals("Administrators", c1.getRoleLevel());
105 		assertNull(c1.getGroupLevel());
106 
107 		final Comment c3 = Iterables.get(comments, 2);
108 		assertEquals("jira-users", c3.getGroupLevel());
109 		assertNull(c3.getRoleLevel());
110 
111 	}
112 
113 	public void testGetIssueWithNoViewWatchersPermission() {
114 		setUser1();
115 		assertTrue(client.getIssueClient().getIssue("TST-1", pm).getWatchers().isWatching());
116 
117 		setUser2();
118 		final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
119 		assertFalse(issue.getWatchers().isWatching());
120 		client.getIssueClient().watch(issue.getWatchers().getSelf(), pm);
121 		final Issue watchedIssue = client.getIssueClient().getIssue("TST-1", pm);
122 		assertTrue(watchedIssue.getWatchers().isWatching());
123 		assertEquals(2, watchedIssue.getWatchers().getNumWatchers());
124 
125 		// although there are 2 watchers, only one is listed with details - the caller itself, as the caller does not
126 		// have view watchers and voters permission 
127 		assertThat(client.getIssueClient().getWatchers(watchedIssue.getWatchers().getSelf(), pm).getUsers(), IterableMatcher.hasOnlyElements(USER2));
128 	}
129 
130 	@Test
131 	public void testGetVoter() {
132 		final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
133 		final Votes votes = client.getIssueClient().getVotes(issue.getVotes().getSelf(), pm);
134 		assertFalse(votes.hasVoted());
135 		assertThat(votes.getUsers(), IterableMatcher.hasOnlyElements(USER1));
136 	}
137 
138 	@Test
139 	public void testGetVotersWithoutViewIssuePermission() {
140 		final Issue issue = client.getIssueClient().getIssue("RST-1", pm);
141 		setUser2();
142 		assertErrorCode(Response.Status.FORBIDDEN, "You do not have the permission to see the specified issue", new Runnable() {
143 			@Override
144 			public void run() {
145 				client.getIssueClient().getVotes(issue.getVotes().getSelf(), pm);
146 			}
147 		});
148 	}
149 
150 	@Test
151 	public void testGetVotersWithoutViewVotersPermission() {
152 		setUser2();
153 		assertNumVotesAndNoVotersDetails("TST-1", 1);
154 	}
155 
156 	@Test
157 	public void testGetVotersAnonymously() {
158 		setAnonymousMode();
159 		assertNumVotesAndNoVotersDetails("ANNON-1", 0);
160 	}
161 
162 
163 	private void assertNumVotesAndNoVotersDetails(final String issueKey, final int numVotes) {
164 		final Issue issue = client.getIssueClient().getIssue(issueKey, pm);
165 		assertEquals(numVotes, issue.getVotes().getVotes());
166 		assertFalse(issue.getVotes().hasVoted());
167 		final Votes votes = client.getIssueClient().getVotes(issue.getVotes().getSelf(), pm);
168 		assertFalse(votes.hasVoted());
169 		assertEquals(numVotes, votes.getVotes());
170 		assertTrue(Iterables.isEmpty(votes.getUsers()));
171 	}
172 
173 
174 	@Test
175 	public void testGetTransitions() throws Exception {
176 		final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
177 		final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue.getTransitionsUri(), pm);
178 		assertEquals(4, Iterables.size(transitions));
179 		assertTrue(Iterables.contains(transitions, new Transition("Start Progress", IntegrationTestUtil.START_PROGRESS_TRANSITION_ID, Collections.<Transition.Field>emptyList())));
180 	}
181 
182 	@Test
183 	public void testTransition() throws Exception {
184 		final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
185 		final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue.getTransitionsUri(), pm);
186 		assertEquals(4, Iterables.size(transitions));
187 		final Transition startProgressTransition = new Transition("Start Progress", IntegrationTestUtil.START_PROGRESS_TRANSITION_ID, Collections.<Transition.Field>emptyList());
188 		assertTrue(Iterables.contains(transitions, startProgressTransition));
189 
190 		client.getIssueClient().transition(issue.getTransitionsUri(), new TransitionInput(IntegrationTestUtil.START_PROGRESS_TRANSITION_ID,
191 				Collections.<FieldInput>emptyList(), Comment.valueOf("My test comment")), new NullProgressMonitor()) ;
192 		final Issue transitionedIssue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
193 		assertEquals("In Progress", transitionedIssue.getStatus().getName());
194 		final Iterable<Transition> transitionsAfterTransition = client.getIssueClient().getTransitions(issue.getTransitionsUri(), pm);
195 		assertFalse(Iterables.contains(transitionsAfterTransition, startProgressTransition));
196 		final Transition stopProgressTransition = new Transition("Stop Progress", IntegrationTestUtil.STOP_PROGRESS_TRANSITION_ID, Collections.<Transition.Field>emptyList());
197 		assertTrue(Iterables.contains(transitionsAfterTransition, stopProgressTransition));
198 	}
199 
200 
201 	@Test
202 	public void testTransitionWithNumericCustomFieldPolishLocale() throws Exception {
203 		final double newValue = 123.45;
204 		final FieldInput fieldInput = new FieldInput(NUMERIC_CUSTOMFIELD_ID,
205 				NumberFormat.getNumberInstance(new Locale("pl")).format(newValue));
206 		assertTransitionWithNumericCustomField(fieldInput, newValue);
207 	}
208 
209 	@Test
210 	public void testTransitionWithNumericCustomFieldEnglishLocale() throws Exception {
211 		setUser1();
212 		final double newValue = 123.45;
213 		final FieldInput fieldInput = new FieldInput(NUMERIC_CUSTOMFIELD_ID,
214 				NumberFormat.getNumberInstance(new Locale("pl")).format(newValue));
215 
216 		assertErrorCode(Response.Status.BAD_REQUEST, "'" + fieldInput.getValue() + "' is an invalid number", new Runnable() {
217 			@Override
218 			public void run() {
219 				assertTransitionWithNumericCustomField(fieldInput, newValue);
220 			}
221 		});
222 
223 		final FieldInput fieldInput2 = new FieldInput(NUMERIC_CUSTOMFIELD_ID, newValue); // this will be serialized always with "." according to JSL
224 		assertTransitionWithNumericCustomField(fieldInput2, newValue);
225 
226 	}
227 
228 
229 	private void assertTransitionWithNumericCustomField(FieldInput fieldInput, Double expectedValue) {
230 		final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
231 		assertNull(issue.getField(NUMERIC_CUSTOMFIELD_ID).getValue());
232 		final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue.getTransitionsUri(), pm);
233 
234 		final Transition transitionFound = getTransitionByName(transitions, "Estimate");
235 		assertNotNull(transitionFound);
236 		assertTrue(Iterables.contains(transitionFound.getFields(),
237 				new Transition.Field(NUMERIC_CUSTOMFIELD_ID, false, NUMERIC_CUSTOMFIELD_TYPE)));
238 		client.getIssueClient().transition(issue.getTransitionsUri(), new TransitionInput(transitionFound.getId(), Arrays.asList(fieldInput),
239 				Comment.valueOf("My test comment")), new NullProgressMonitor());
240 		final Issue changedIssue = client.getIssueClient().getIssue("TST-1", pm);
241 		assertTrue(changedIssue.getField(NUMERIC_CUSTOMFIELD_ID).getValue().equals(expectedValue));
242 	}
243 
244 	@Test
245 	public void testTransitionWithNumericCustomFieldAndInteger() throws Exception {
246 		final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
247 		assertNull(issue.getField(NUMERIC_CUSTOMFIELD_ID).getValue());
248 		final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue.getTransitionsUri(), pm);
249 		Transition transitionFound = getTransitionByName(transitions, "Estimate");
250 
251 		assertNotNull(transitionFound);
252 		assertTrue(Iterables.contains(transitionFound.getFields(),
253 				new Transition.Field(NUMERIC_CUSTOMFIELD_ID, false, NUMERIC_CUSTOMFIELD_TYPE)));
254 		final double newValue = 123;
255 		final FieldInput fieldInput = new FieldInput(NUMERIC_CUSTOMFIELD_ID, newValue);
256 		client.getIssueClient().transition(issue.getTransitionsUri(), new TransitionInput(transitionFound.getId(), Arrays.asList(fieldInput),
257 				Comment.valueOf("My test comment")), pm);
258 		final Issue changedIssue = client.getIssueClient().getIssue("TST-1", pm);
259 		assertEquals(newValue, changedIssue.getField(NUMERIC_CUSTOMFIELD_ID).getValue());
260 	}
261 
262 	@Test
263 	public void testTransitionWithInvalidNumericField() throws Exception {
264 		final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
265 		assertNull(issue.getField(NUMERIC_CUSTOMFIELD_ID).getValue());
266 		final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue.getTransitionsUri(), pm);
267 		final Transition transitionFound = getTransitionByName(transitions, "Estimate");
268 
269 		assertNotNull(transitionFound);
270 		assertTrue(Iterables.contains(transitionFound.getFields(),
271 				new Transition.Field(NUMERIC_CUSTOMFIELD_ID, false, NUMERIC_CUSTOMFIELD_TYPE)));
272 		final FieldInput fieldInput = new FieldInput(NUMERIC_CUSTOMFIELD_ID, "]432jl");
273 		// warning: Polish language here - I am asserting if the messages are indeed localized
274 		assertErrorCode(Response.Status.BAD_REQUEST, "']432jl' nie jest prawid\u0142ow\u0105 liczb\u0105", new Runnable() {
275 			@Override
276 			public void run() {
277 				client.getIssueClient().transition(issue.getTransitionsUri(), new TransitionInput(transitionFound.getId(), Arrays.asList(fieldInput),
278 						Comment.valueOf("My test comment")), pm);
279 			}
280 		});
281 	}
282 
283 
284 	public void testTransitionWithNoRoleOrGroup() {
285 		Comment comment = Comment.valueOf("My text which I am just adding " + new DateTime());
286 		testTransitionImpl(comment);
287 	}
288 
289 	@Test
290 	public void testTransitionWithRoleLevel() {
291 		Comment comment = Comment.createWithRoleLevel("My text which I am just adding " + new DateTime(), "Users");
292 		testTransitionImpl(comment);
293 	}
294 
295 	@Test
296 	public void testTransitionWithGroupLevel() {
297 		Comment comment = Comment.createWithGroupLevel("My text which I am just adding " + new DateTime(), "jira-users");
298 		testTransitionImpl(comment);
299 	}
300 
301 	@Test
302 	public void testTransitionWithInvalidRole() {
303 		final Comment comment = Comment.createWithRoleLevel("My text which I am just adding " + new DateTime(), "some-fake-role");
304 		final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
305 		final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue.getTransitionsUri(), pm);
306 		final Transition transitionFound = getTransitionByName(transitions, "Estimate");
307 		// @todo restore asserting for error message when JRA-22516 is fixed
308 		assertErrorCode(Response.Status.BAD_REQUEST, /*"Invalid role [some-fake-role]", */new Runnable() {
309 			@Override
310 			public void run() {
311 				client.getIssueClient().transition(issue.getTransitionsUri(), new TransitionInput(transitionFound.getId(), comment), pm);
312 			}
313 		});
314 	}
315 
316 	private void testTransitionImpl(Comment comment) {
317 		final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
318 		final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue.getTransitionsUri(), pm);
319 		Transition transitionFound = getTransitionByName(transitions, "Estimate");
320 		DateTime now = new DateTime();
321 		client.getIssueClient().transition(issue.getTransitionsUri(), new TransitionInput(transitionFound.getId(), comment), pm);
322 
323 		final Issue changedIssue = client.getIssueClient().getIssue("TST-1", pm);
324 		final Comment lastComment = Iterables.getLast(changedIssue.getComments());
325 		assertEquals(comment.getBody(), lastComment.getBody());
326 		assertEquals(USER_ADMIN, lastComment.getAuthor());
327 		assertEquals(USER_ADMIN, lastComment.getUpdateAuthor());
328 		assertEquals(lastComment.getCreationDate(), lastComment.getUpdateDate());
329 		assertTrue(lastComment.getCreationDate().isAfter(now) || lastComment.getCreationDate().isEqual(now));
330 		assertEquals(comment.getGroupLevel(), lastComment.getGroupLevel());
331 		assertEquals(comment.getRoleLevel(), lastComment.getRoleLevel());
332 	}
333 
334 	@Test
335 	public void testVoteUnvote() {
336 		final Issue issue1 = client.getIssueClient().getIssue("TST-1", pm);
337 		assertFalse(issue1.getVotes().hasVoted());
338 		assertEquals(1, issue1.getVotes().getVotes()); // the other user has voted
339 
340 		// I hope that such Polish special characters (for better testing local specific behaviour of REST
341 		assertErrorCode(Response.Status.NOT_FOUND, "Nie mo\u017cesz g\u0142osowa\u0107 na zadanie kt\u00f3re utworzy\u0142e\u015b.", new Runnable() {
342 			@Override
343 			public void run() {
344 				client.getIssueClient().vote(issue1.getVotesUri(), pm);
345 			}
346 		});
347 
348 
349 		final String issueKey = "TST-7";
350 		Issue issue = client.getIssueClient().getIssue(issueKey, pm);
351 		assertFalse(issue.getVotes().hasVoted());
352 		assertEquals(0, issue.getVotes().getVotes());
353 
354 		client.getIssueClient().vote(issue.getVotesUri(), pm);
355 		issue = client.getIssueClient().getIssue(issueKey, pm);
356 		assertTrue(issue.getVotes().hasVoted());
357 		assertEquals(1, issue.getVotes().getVotes());
358 
359 		client.getIssueClient().unvote(issue.getVotesUri(), pm);
360 		issue = client.getIssueClient().getIssue(issueKey, pm);
361 		assertFalse(issue.getVotes().hasVoted());
362 		assertEquals(0, issue.getVotes().getVotes());
363 
364 		setUser2();
365 		issue = client.getIssueClient().getIssue(issueKey, pm);
366 		assertFalse(issue.getVotes().hasVoted());
367 		assertEquals(0, issue.getVotes().getVotes());
368 		final Issue finalIssue = issue;
369 		assertErrorCode(Response.Status.NOT_FOUND, "Cannot remove a vote for an issue that the user has not already voted for.", new Runnable() {
370 			@Override
371 			public void run() {
372 				client.getIssueClient().unvote(finalIssue.getVotesUri(), pm);
373 			}
374 		});
375 
376 
377 		issue = client.getIssueClient().getIssue(issueKey, pm);
378 		assertFalse(issue.getVotes().hasVoted());
379 		assertEquals(0, issue.getVotes().getVotes());
380 		client.getIssueClient().vote(issue.getVotesUri(), pm);
381 		issue = client.getIssueClient().getIssue(issueKey, pm);
382 		assertTrue(issue.getVotes().hasVoted());
383 		assertEquals(1, issue.getVotes().getVotes());
384 
385 		setClient(ADMIN_USERNAME, ADMIN_PASSWORD);
386 		client.getIssueClient().vote(issue.getVotesUri(), pm);
387 		issue = client.getIssueClient().getIssue(issueKey, pm);
388 		assertTrue(issue.getVotes().hasVoted());
389 		assertEquals(2, issue.getVotes().getVotes());
390 	}
391 
392 	@Test
393 	public void testWatchUnwatch() {
394 		final IssueRestClient issueClient = client.getIssueClient();
395 		final Issue issue1 = issueClient.getIssue("TST-1", pm);
396 
397 		Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(),
398 				Matchers.not(IterableMatcher.contains(USER_ADMIN)));
399 
400 		issueClient.watch(issue1.getWatchers().getSelf(), pm);
401 		Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(), IterableMatcher.contains(USER_ADMIN));
402 
403 		issueClient.unwatch(issue1.getWatchers().getSelf(), pm);
404 		Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(), Matchers.not(IterableMatcher.contains(USER_ADMIN)));
405 
406 		Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(), IterableMatcher.contains(USER1));
407 		issueClient.removeWatcher(issue1.getWatchers().getSelf(), USER1.getName(), pm);
408 		Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(), Matchers.not(IterableMatcher.contains(USER1)));
409 		issueClient.addWatcher(issue1.getWatchers().getSelf(), USER1.getName(), pm);
410 		Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(), IterableMatcher.contains(USER1));
411 	}
412 
413 	@Test
414 	public void testRemoveWatcherUnauthorized() {
415 		final IssueRestClient issueClient = client.getIssueClient();
416 		final Issue issue1 = issueClient.getIssue("TST-1", pm);
417 		issueClient.watch(issue1.getWatchers().getSelf(), pm);
418 
419 		setUser1();
420 		final IssueRestClient issueClient2 = client.getIssueClient();
421 		assertErrorCode(Response.Status.UNAUTHORIZED,
422 				"User 'wseliga' is not allowed to remove watchers from issue 'TST-1'", new Runnable() {
423 			@Override
424 			public void run() {
425 				issueClient2.removeWatcher(issue1.getWatchers().getSelf(), ADMIN_USERNAME, pm);
426 			}
427 		});
428 	}
429 
430 
431 	@Test
432 	public void testWatchAlreadyWatched() {
433 		setUser1();
434 		final IssueRestClient issueClient = client.getIssueClient();
435 		final Issue issue = issueClient.getIssue("TST-1", pm);
436 		Assert.assertThat(client.getIssueClient().getWatchers(issue.getWatchers().getSelf(), pm).getUsers(), IterableMatcher.contains(USER1));
437 		// JIRA allows to watch already watched issue by you - such action effectively has no effect
438 		issueClient.watch(issue.getWatchers().getSelf(), pm);
439 		Assert.assertThat(client.getIssueClient().getWatchers(issue.getWatchers().getSelf(), pm).getUsers(), IterableMatcher.contains(USER1));
440 	}
441 
442 	@Test
443 	public void testAddWatcherUnauthorized() {
444 		final IssueRestClient issueClient = client.getIssueClient();
445 		final Issue issue1 = issueClient.getIssue("TST-1", pm);
446 		issueClient.addWatcher(issue1.getWatchers().getSelf(), USER1_USERNAME, pm);
447 		assertThat(client.getIssueClient().getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(), IterableMatcher.contains(USER1));
448 
449 		setUser1();
450 		assertTrue(client.getIssueClient().getIssue("TST-1", pm).getWatchers().isWatching());
451 
452 		// @todo restore assertion for the message when JRADEV-3516 is fixed
453 		assertErrorCode(Response.Status.UNAUTHORIZED/*, "User '" + USER1_USERNAME
454 				+ "' is not allowed to add watchers to issue 'TST-1'"*/, new Runnable() {
455 			@Override
456 			public void run() {
457 				client.getIssueClient().addWatcher(issue1.getWatchers().getSelf(), ADMIN_USERNAME, pm);
458 			}
459 		});
460 	}
461 
462 	//@Test restore when JRADEV-3666 is fixed (I don't want to pollute JRJC integration test results)
463 	public void xtestAddWatcherWhoDoesNotHaveViewIssuePermissions() {
464 		final IssueRestClient issueClient = client.getIssueClient();
465 		final Issue issue1 = issueClient.getIssue("RST-1", pm);
466 		assertErrorCode(Response.Status.BAD_REQUEST, "The user \"" + USER2_USERNAME
467 				+ "\" does not have permission to view this issue. This user will not be added to the watch list.",
468 				new Runnable() {
469 					@Override
470 					public void run() {
471 						issueClient.addWatcher(issue1.getWatchers().getSelf(), USER2_USERNAME, pm);
472 					}
473 				});
474 
475 	}
476 }