it.JerseyIssueRestClientTest.java Source code

Java tutorial

Introduction

Here is the source code for it.JerseyIssueRestClientTest.java

Source

/*
 * Copyright (C) 2010 Atlassian
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package it;

import com.atlassian.jira.rest.client.IntegrationTestUtil;
import com.atlassian.jira.rest.client.IssueRestClient;
import com.atlassian.jira.rest.client.IterableMatcher;
import com.atlassian.jira.rest.client.NullProgressMonitor;
import com.atlassian.jira.rest.client.domain.Attachment;
import com.atlassian.jira.rest.client.domain.BasicUser;
import com.atlassian.jira.rest.client.domain.ChangelogGroup;
import com.atlassian.jira.rest.client.domain.ChangelogItem;
import com.atlassian.jira.rest.client.domain.Comment;
import com.atlassian.jira.rest.client.domain.Issue;
import com.atlassian.jira.rest.client.domain.IssueLink;
import com.atlassian.jira.rest.client.domain.IssueLinkType;
import com.atlassian.jira.rest.client.domain.TimeTracking;
import com.atlassian.jira.rest.client.domain.Transition;
import com.atlassian.jira.rest.client.domain.Visibility;
import com.atlassian.jira.rest.client.domain.Votes;
import com.atlassian.jira.rest.client.domain.Watchers;
import com.atlassian.jira.rest.client.domain.input.AttachmentInput;
import com.atlassian.jira.rest.client.domain.input.FieldInput;
import com.atlassian.jira.rest.client.domain.input.LinkIssuesInput;
import com.atlassian.jira.rest.client.domain.input.TransitionInput;
import com.atlassian.jira.rest.client.internal.ServerVersionConstants;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import org.apache.commons.io.IOUtils;
import org.hamcrest.Matchers;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.junit.Assert;
import org.junit.Test;

import javax.annotation.Nullable;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.atlassian.jira.rest.client.IntegrationTestUtil.*;
import static com.atlassian.jira.rest.client.TestUtil.assertErrorCode;
import static com.atlassian.jira.rest.client.internal.json.TestConstants.USER1_USERNAME;
import static com.atlassian.jira.rest.client.internal.json.TestConstants.USER2_USERNAME;
import static org.junit.Assert.assertThat;

public class JerseyIssueRestClientTest extends AbstractRestoringJiraStateJerseyRestClientTest {

    // no timezone here, as JIRA does not store timezone information in its dump file
    private final DateTime dateTime = ISODateTimeFormat.dateTimeParser().parseDateTime("2010-08-04T17:46:45.454");

    @Test
    public void testGetWatchers() throws Exception {
        final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
        final Watchers watchers = client.getIssueClient().getWatchers(issue.getWatchers().getSelf(),
                new NullProgressMonitor());
        assertEquals(1, watchers.getNumWatchers());
        assertFalse(watchers.isWatching());
        assertThat(watchers.getUsers(), IterableMatcher.hasOnlyElements(USER1));
    }

    public void testGetWatcherForAnonymouslyAccessibleIssue() {
        setAnonymousMode();
        final Issue issue = client.getIssueClient().getIssue("ANNON-1", new NullProgressMonitor());
        final Watchers watchers = client.getIssueClient().getWatchers(issue.getWatchers().getSelf(), pm);
        assertEquals(1, watchers.getNumWatchers());
        assertFalse(watchers.isWatching());
        assertTrue("JRADEV-3594 bug!!!", Iterables.isEmpty(watchers.getUsers()));
        // to save time
        assertEquals(new TimeTracking(2700, 2400, null), issue.getTimeTracking());
    }

    // URIs are broken in 5.0 - https://jdog.atlassian.com/browse/JRADEV-7691
    private void assertEqualsNoUri(BasicUser expected, BasicUser actual) {
        assertEquals(expected.getName(), actual.getName());
        assertEquals(expected.getDisplayName(), actual.getDisplayName());
    }

    @Test
    public void testGetIssue() throws Exception {
        final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
        assertEquals("TST-1", issue.getKey());
        assertTrue(issue.getSelf().toString().startsWith(jiraUri.toString()));
        assertEqualsNoUri(IntegrationTestUtil.USER_ADMIN, issue.getReporter());
        assertEqualsNoUri(IntegrationTestUtil.USER_ADMIN, issue.getAssignee());

        assertEquals(3, Iterables.size(issue.getComments()));
        final Iterable<String> expectedExpandos = isJira5xOrNewer()
                ? ImmutableList.of("renderedFields", "names", "schema", "transitions", "operations", "editmeta",
                        "changelog")
                : ImmutableList.of("html");
        assertThat(ImmutableList.copyOf(issue.getExpandos()), IterableMatcher.hasOnlyElements(expectedExpandos));
        assertEquals(new TimeTracking(null, 0, 190), issue.getTimeTracking());
        assertTrue(Iterables.size(issue.getFields()) > 0);

        assertEquals(IntegrationTestUtil.START_PROGRESS_TRANSITION_ID, Iterables.size(issue.getAttachments()));
        final Iterable<Attachment> items = issue.getAttachments();
        assertNotNull(items);
        Attachment attachment1 = new Attachment(
                IntegrationTestUtil.concat(IntegrationTestUtil.TESTING_JIRA_5_OR_NEWER
                        ? UriBuilder.fromUri(jiraUri).path("/rest/api/2/").build()
                        : jiraRestRootUri, "/attachment/10040"),
                "dla Paw\u0142a.txt", IntegrationTestUtil.USER_ADMIN, dateTime, 643, "text/plain",
                IntegrationTestUtil.concat(jiraUri, "/secure/attachment/10040/dla+Paw%C5%82a.txt"), null);

        assertEquals(attachment1, items.iterator().next());

        // test for changelog
        assertNull(issue.getChangelog());

        final Issue issueWithChangelog = client.getIssueClient().getIssue("TST-2",
                EnumSet.of(IssueRestClient.Expandos.CHANGELOG), pm);
        final Iterable<ChangelogGroup> changelog = issueWithChangelog.getChangelog();
        if (isJira5xOrNewer()) {
            assertNotNull(changelog);
            final ChangelogGroup chg1 = Iterables.get(changelog, 18);
            assertEquals("admin", chg1.getAuthor().getName());
            assertEquals("Administrator", chg1.getAuthor().getDisplayName());
            assertEquals(new DateTime(2010, 8, 17, 16, 40, 34, 924).toInstant(), chg1.getCreated().toInstant());

            assertEquals(Collections.singletonList(
                    new ChangelogItem(ChangelogItem.FieldType.JIRA, "status", "1", "Open", "3", "In Progress")),
                    chg1.getItems());

            final ChangelogGroup chg2 = Iterables.get(changelog, 20);
            assertEquals("admin", chg2.getAuthor().getName());
            assertEquals("Administrator", chg2.getAuthor().getDisplayName());
            assertEquals(new DateTime(2010, 8, 24, 16, 10, 23, 468).toInstant(), chg2.getCreated().toInstant());

            final List<ChangelogItem> expected = ImmutableList.of(
                    new ChangelogItem(ChangelogItem.FieldType.JIRA, "timeoriginalestimate", null, null, "0", "0"),
                    new ChangelogItem(ChangelogItem.FieldType.CUSTOM, "My Radio buttons", null, null, null,
                            "Another"),
                    new ChangelogItem(ChangelogItem.FieldType.CUSTOM, "project3", null, null, "10000",
                            "Test Project"),
                    new ChangelogItem(ChangelogItem.FieldType.CUSTOM, "My Number Field New", null, null, null,
                            "1.45"));
            assertEquals(expected, chg2.getItems());
        }
    }

    public void testGetIssueWithNonTrivialComments() {
        final Issue issue = client.getIssueClient().getIssue("TST-2", pm);
        final Iterable<Comment> comments = issue.getComments();
        assertEquals(3, Iterables.size(comments));
        final Comment c1 = Iterables.get(comments, 0);
        assertEquals(Visibility.role("Administrators"), c1.getVisibility());

        final Comment c3 = Iterables.get(comments, 2);
        assertEquals(Visibility.group("jira-users"), c3.getVisibility());

    }

    public void testGetIssueWithNoViewWatchersPermission() {
        setUser1();
        assertTrue(client.getIssueClient().getIssue("TST-1", pm).getWatchers().isWatching());

        setUser2();
        final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
        assertFalse(issue.getWatchers().isWatching());
        client.getIssueClient().watch(issue.getWatchers().getSelf(), pm);
        final Issue watchedIssue = client.getIssueClient().getIssue("TST-1", pm);
        assertTrue(watchedIssue.getWatchers().isWatching());
        assertEquals(2, watchedIssue.getWatchers().getNumWatchers());

        // although there are 2 watchers, only one is listed with details - the caller itself, as the caller does not
        // have view watchers and voters permission 
        assertThat(client.getIssueClient().getWatchers(watchedIssue.getWatchers().getSelf(), pm).getUsers(),
                IterableMatcher.hasOnlyElements(USER2));
    }

    @Test
    public void testGetVoter() {
        final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
        final Votes votes = client.getIssueClient().getVotes(issue.getVotes().getSelf(), pm);
        assertFalse(votes.hasVoted());
        assertThat(votes.getUsers(), IterableMatcher.hasOnlyElements(USER1));
    }

    @Test
    public void testGetVotersWithoutViewIssuePermission() {
        final Issue issue = client.getIssueClient().getIssue("RST-1", pm);
        setUser2();
        final String optionalDot = isJira5xOrNewer() ? "." : "";
        assertErrorCode(Response.Status.FORBIDDEN,
                "You do not have the permission to see the specified issue" + optionalDot, new Runnable() {
                    @Override
                    public void run() {
                        client.getIssueClient().getVotes(issue.getVotes().getSelf(), pm);
                    }
                });
    }

    @Test
    public void testGetVotersWithoutViewVotersPermission() {
        setUser2();
        assertNumVotesAndNoVotersDetails("TST-1", 1);
    }

    @Test
    public void testGetVotersAnonymously() {
        setAnonymousMode();
        assertNumVotesAndNoVotersDetails("ANNON-1", 0);
    }

    private void assertNumVotesAndNoVotersDetails(final String issueKey, final int numVotes) {
        final Issue issue = client.getIssueClient().getIssue(issueKey, pm);
        assertEquals(numVotes, issue.getVotes().getVotes());
        assertFalse(issue.getVotes().hasVoted());
        final Votes votes = client.getIssueClient().getVotes(issue.getVotes().getSelf(), pm);
        assertFalse(votes.hasVoted());
        assertEquals(numVotes, votes.getVotes());
        assertTrue(Iterables.isEmpty(votes.getUsers()));
    }

    @Test
    public void testGetTransitions() throws Exception {
        final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
        final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue, pm);
        assertEquals(4, Iterables.size(transitions));
        assertTrue(Iterables.contains(transitions, new Transition("Start Progress",
                IntegrationTestUtil.START_PROGRESS_TRANSITION_ID, Collections.<Transition.Field>emptyList())));
    }

    @Test
    public void testTransition() throws Exception {
        final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
        final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue, pm);
        assertEquals(4, Iterables.size(transitions));
        final Transition startProgressTransition = new Transition("Start Progress",
                IntegrationTestUtil.START_PROGRESS_TRANSITION_ID, Collections.<Transition.Field>emptyList());
        assertTrue(Iterables.contains(transitions, startProgressTransition));

        client.getIssueClient().transition(issue,
                new TransitionInput(IntegrationTestUtil.START_PROGRESS_TRANSITION_ID,
                        Collections.<FieldInput>emptyList(), Comment.valueOf("My test comment")),
                new NullProgressMonitor());
        final Issue transitionedIssue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
        assertEquals("In Progress", transitionedIssue.getStatus().getName());
        final Iterable<Transition> transitionsAfterTransition = client.getIssueClient().getTransitions(issue, pm);
        assertFalse(Iterables.contains(transitionsAfterTransition, startProgressTransition));
        final Transition stopProgressTransition = new Transition("Stop Progress",
                IntegrationTestUtil.STOP_PROGRESS_TRANSITION_ID, Collections.<Transition.Field>emptyList());
        assertTrue(Iterables.contains(transitionsAfterTransition, stopProgressTransition));
    }

    @Test
    public void testTransitionWithNumericCustomFieldPolishLocale() throws Exception {
        final double newValue = 123.45;
        final FieldInput fieldInput;
        if (IntegrationTestUtil.TESTING_JIRA_5_OR_NEWER) {
            fieldInput = new FieldInput(NUMERIC_CUSTOMFIELD_ID, Double.valueOf(newValue));
        } else {
            fieldInput = new FieldInput(NUMERIC_CUSTOMFIELD_ID,
                    NumberFormat.getNumberInstance(new Locale("pl")).format(newValue));
        }
        assertTransitionWithNumericCustomField(fieldInput, newValue);
    }

    @Test
    public void testTransitionWithNumericCustomFieldEnglishLocale() throws Exception {
        setUser1();
        final double newValue = 123.45;
        final FieldInput fieldInput = new FieldInput(NUMERIC_CUSTOMFIELD_ID,
                NumberFormat.getNumberInstance(new Locale("pl")).format(newValue));

        assertErrorCode(Response.Status.BAD_REQUEST,
                IntegrationTestUtil.TESTING_JIRA_5_OR_NEWER ? "Operation value must be a number"
                        : ("'" + fieldInput.getValue() + "' is an invalid number"),
                new Runnable() {
                    @Override
                    public void run() {
                        assertTransitionWithNumericCustomField(fieldInput, newValue);
                    }
                });

        final FieldInput fieldInput2 = new FieldInput(NUMERIC_CUSTOMFIELD_ID, newValue); // this will be serialized always with "." according to JSL
        assertTransitionWithNumericCustomField(fieldInput2, newValue);

    }

    private void assertTransitionWithNumericCustomField(FieldInput fieldInput, Double expectedValue) {
        final Issue issue = client.getIssueClient().getIssue("TST-1", new NullProgressMonitor());
        assertNull(issue.getField(NUMERIC_CUSTOMFIELD_ID).getValue());
        final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue, pm);

        final Transition transitionFound = getTransitionByName(transitions, "Estimate");
        assertNotNull(transitionFound);
        assertTrue(Iterables.contains(transitionFound.getFields(),
                new Transition.Field(NUMERIC_CUSTOMFIELD_ID, false,
                        IntegrationTestUtil.TESTING_JIRA_5_OR_NEWER ? NUMERIC_CUSTOMFIELD_TYPE_V5
                                : NUMERIC_CUSTOMFIELD_TYPE)));
        client.getIssueClient().transition(issue, new TransitionInput(transitionFound.getId(),
                Arrays.asList(fieldInput), Comment.valueOf("My test comment")), new NullProgressMonitor());
        final Issue changedIssue = client.getIssueClient().getIssue("TST-1", pm);
        assertTrue(changedIssue.getField(NUMERIC_CUSTOMFIELD_ID).getValue().equals(expectedValue));
    }

    @Test
    public void testTransitionWithNumericCustomFieldAndInteger() throws Exception {
        final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
        assertNull(issue.getField(NUMERIC_CUSTOMFIELD_ID).getValue());
        final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue, pm);
        Transition transitionFound = getTransitionByName(transitions, "Estimate");

        assertNotNull(transitionFound);
        assertTrue(Iterables.contains(transitionFound.getFields(),
                new Transition.Field(NUMERIC_CUSTOMFIELD_ID, false,
                        IntegrationTestUtil.TESTING_JIRA_5_OR_NEWER ? NUMERIC_CUSTOMFIELD_TYPE_V5
                                : NUMERIC_CUSTOMFIELD_TYPE)));
        final double newValue = 123;
        final FieldInput fieldInput = new FieldInput(NUMERIC_CUSTOMFIELD_ID, newValue);
        client.getIssueClient().transition(issue, new TransitionInput(transitionFound.getId(),
                Arrays.asList(fieldInput), Comment.valueOf("My test comment")), pm);
        final Issue changedIssue = client.getIssueClient().getIssue("TST-1", pm);
        assertEquals(newValue, changedIssue.getField(NUMERIC_CUSTOMFIELD_ID).getValue());
    }

    @Test
    public void testTransitionWithInvalidNumericField() throws Exception {
        final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
        assertNull(issue.getField(NUMERIC_CUSTOMFIELD_ID).getValue());
        final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue, pm);
        final Transition transitionFound = getTransitionByName(transitions, "Estimate");

        assertNotNull(transitionFound);
        assertTrue(Iterables.contains(transitionFound.getFields(),
                new Transition.Field(NUMERIC_CUSTOMFIELD_ID, false,
                        IntegrationTestUtil.TESTING_JIRA_5_OR_NEWER ? NUMERIC_CUSTOMFIELD_TYPE_V5
                                : NUMERIC_CUSTOMFIELD_TYPE)));
        final FieldInput fieldInput = new FieldInput(NUMERIC_CUSTOMFIELD_ID, "]432jl");
        // warning: Polish language here - I am asserting if the messages are indeed localized
        // since 5.0 messages are changed and not localized
        assertErrorCode(Response.Status.BAD_REQUEST,
                IntegrationTestUtil.TESTING_JIRA_5_OR_NEWER ? "Operation value must be a number"
                        : "']432jl' nie jest prawid\u0142ow\u0105 liczb\u0105",
                new Runnable() {
                    @Override
                    public void run() {
                        client.getIssueClient().transition(issue, new TransitionInput(transitionFound.getId(),
                                Arrays.asList(fieldInput), Comment.valueOf("My test comment")), pm);
                    }
                });
    }

    @Test
    public void testTransitionWithNoRoleOrGroup() {
        Comment comment = Comment.valueOf("My text which I am just adding " + new DateTime());
        testTransitionImpl(comment);
    }

    @Test
    public void testTransitionWithRoleLevel() {
        Comment comment = Comment.createWithRoleLevel("My text which I am just adding " + new DateTime(), "Users");
        testTransitionImpl(comment);
    }

    @Test
    public void testTransitionWithGroupLevel() {
        Comment comment = Comment.createWithGroupLevel("My text which I am just adding " + new DateTime(),
                "jira-users");
        testTransitionImpl(comment);
    }

    @Test
    public void testTransitionWithInvalidRole() {
        final Comment comment = Comment.createWithRoleLevel("My text which I am just adding " + new DateTime(),
                "some-fake-role");
        if (IntegrationTestUtil.TESTING_JIRA_5_OR_NEWER) {
            assertInvalidCommentInput(comment, "Invalid role level specified.");
        } else {
            assertInvalidCommentInput(comment, "Invalid role [some-fake-role]");
        }
    }

    @Test
    public void testTransitionWithInvalidGroup() {
        final Comment comment = Comment.createWithGroupLevel("My text which I am just adding " + new DateTime(),
                "some-fake-group");
        assertInvalidCommentInput(comment, "Group: some-fake-group does not exist.");
    }

    private void assertInvalidCommentInput(final Comment comment, String expectedErrorMsg) {
        final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
        final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue, pm);
        final Transition transitionFound = getTransitionByName(transitions, "Estimate");
        final String errorMsg = doesJiraServeCorrectlyErrorMessagesForBadRequestWhileTransitioningIssue()
                ? expectedErrorMsg
                : null;
        assertErrorCode(Response.Status.BAD_REQUEST, errorMsg, new Runnable() {
            @Override
            public void run() {
                client.getIssueClient().transition(issue, new TransitionInput(transitionFound.getId(), comment),
                        pm);
            }
        });
    }

    private void testTransitionImpl(Comment comment) {
        final Issue issue = client.getIssueClient().getIssue("TST-1", pm);
        final Iterable<Transition> transitions = client.getIssueClient().getTransitions(issue, pm);
        Transition transitionFound = getTransitionByName(transitions, "Estimate");
        DateTime now = new DateTime();
        client.getIssueClient().transition(issue, new TransitionInput(transitionFound.getId(), comment), pm);

        final Issue changedIssue = client.getIssueClient().getIssue("TST-1", pm);
        final Comment lastComment = Iterables.getLast(changedIssue.getComments());
        assertEquals(comment.getBody(), lastComment.getBody());
        assertEquals(USER_ADMIN, lastComment.getAuthor());
        assertEquals(USER_ADMIN, lastComment.getUpdateAuthor());
        assertEquals(lastComment.getCreationDate(), lastComment.getUpdateDate());
        assertTrue(lastComment.getCreationDate().isAfter(now) || lastComment.getCreationDate().isEqual(now));
        assertEquals(comment.getVisibility(), lastComment.getVisibility());
    }

    @Test
    public void testVoteUnvote() {
        final Issue issue1 = client.getIssueClient().getIssue("TST-1", pm);
        assertFalse(issue1.getVotes().hasVoted());
        assertEquals(1, issue1.getVotes().getVotes()); // the other user has voted
        final String expectedMessage = isJira5xOrNewer() // JIRA 5.0 comes without Polish translation OOB
                ? "You cannot vote for an issue you have reported."
                : "Nie mo\u017cesz g\u0142osowa\u0107 na zadanie kt\u00f3re utworzy\u0142e\u015b.";

        // I hope that such Polish special characters (for better testing local specific behaviour of REST
        assertErrorCode(Response.Status.NOT_FOUND, expectedMessage, new Runnable() {
            @Override
            public void run() {
                client.getIssueClient().vote(issue1.getVotesUri(), pm);
            }
        });

        final String issueKey = "TST-7";
        Issue issue = client.getIssueClient().getIssue(issueKey, pm);
        assertFalse(issue.getVotes().hasVoted());
        assertEquals(0, issue.getVotes().getVotes());

        client.getIssueClient().vote(issue.getVotesUri(), pm);
        issue = client.getIssueClient().getIssue(issueKey, pm);
        assertTrue(issue.getVotes().hasVoted());
        assertEquals(1, issue.getVotes().getVotes());

        client.getIssueClient().unvote(issue.getVotesUri(), pm);
        issue = client.getIssueClient().getIssue(issueKey, pm);
        assertFalse(issue.getVotes().hasVoted());
        assertEquals(0, issue.getVotes().getVotes());

        setUser2();
        issue = client.getIssueClient().getIssue(issueKey, pm);
        assertFalse(issue.getVotes().hasVoted());
        assertEquals(0, issue.getVotes().getVotes());
        final Issue finalIssue = issue;
        assertErrorCode(Response.Status.NOT_FOUND,
                "Cannot remove a vote for an issue that the user has not already voted for.", new Runnable() {
                    @Override
                    public void run() {
                        client.getIssueClient().unvote(finalIssue.getVotesUri(), pm);
                    }
                });

        issue = client.getIssueClient().getIssue(issueKey, pm);
        assertFalse(issue.getVotes().hasVoted());
        assertEquals(0, issue.getVotes().getVotes());
        client.getIssueClient().vote(issue.getVotesUri(), pm);
        issue = client.getIssueClient().getIssue(issueKey, pm);
        assertTrue(issue.getVotes().hasVoted());
        assertEquals(1, issue.getVotes().getVotes());

        setClient(ADMIN_USERNAME, ADMIN_PASSWORD);
        client.getIssueClient().vote(issue.getVotesUri(), pm);
        issue = client.getIssueClient().getIssue(issueKey, pm);
        assertTrue(issue.getVotes().hasVoted());
        assertEquals(2, issue.getVotes().getVotes());
    }

    @Test
    public void testWatchUnwatch() {
        final IssueRestClient issueClient = client.getIssueClient();
        final Issue issue1 = issueClient.getIssue("TST-1", pm);

        Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(),
                Matchers.not(IterableMatcher.contains(USER_ADMIN)));

        issueClient.watch(issue1.getWatchers().getSelf(), pm);
        Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(),
                IterableMatcher.contains(USER_ADMIN));

        issueClient.unwatch(issue1.getWatchers().getSelf(), pm);
        Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(),
                Matchers.not(IterableMatcher.contains(USER_ADMIN)));

        Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(),
                IterableMatcher.contains(USER1));
        issueClient.removeWatcher(issue1.getWatchers().getSelf(), USER1.getName(), pm);
        Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(),
                Matchers.not(IterableMatcher.contains(USER1)));
        issueClient.addWatcher(issue1.getWatchers().getSelf(), USER1.getName(), pm);
        Assert.assertThat(issueClient.getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(),
                IterableMatcher.contains(USER1));
    }

    @Test
    public void testRemoveWatcherUnauthorized() {
        final IssueRestClient issueClient = client.getIssueClient();
        final Issue issue1 = issueClient.getIssue("TST-1", pm);
        issueClient.watch(issue1.getWatchers().getSelf(), pm);

        setUser1();
        final IssueRestClient issueClient2 = client.getIssueClient();
        assertErrorCode(Response.Status.UNAUTHORIZED,
                "User 'wseliga' is not allowed to remove watchers from issue 'TST-1'", new Runnable() {
                    @Override
                    public void run() {
                        issueClient2.removeWatcher(issue1.getWatchers().getSelf(), ADMIN_USERNAME, pm);
                    }
                });
    }

    @Test
    public void testWatchAlreadyWatched() {
        setUser1();
        final IssueRestClient issueClient = client.getIssueClient();
        final Issue issue = issueClient.getIssue("TST-1", pm);
        Assert.assertThat(client.getIssueClient().getWatchers(issue.getWatchers().getSelf(), pm).getUsers(),
                IterableMatcher.contains(USER1));
        // JIRA allows to watch already watched issue by you - such action effectively has no effect
        issueClient.watch(issue.getWatchers().getSelf(), pm);
        Assert.assertThat(client.getIssueClient().getWatchers(issue.getWatchers().getSelf(), pm).getUsers(),
                IterableMatcher.contains(USER1));
    }

    @Test
    public void testAddWatcherUnauthorized() {
        final IssueRestClient issueClient = client.getIssueClient();
        final Issue issue1 = issueClient.getIssue("TST-1", pm);
        issueClient.addWatcher(issue1.getWatchers().getSelf(), USER1_USERNAME, pm);
        assertThat(client.getIssueClient().getWatchers(issue1.getWatchers().getSelf(), pm).getUsers(),
                IterableMatcher.contains(USER1));

        setUser1();
        assertTrue(client.getIssueClient().getIssue("TST-1", pm).getWatchers().isWatching());
        String expectedErrorMsg = isJraDev3516Fixed()
                ? ("User '" + USER1_USERNAME + "' is not allowed to add watchers to issue 'TST-1'")
                : null;
        assertErrorCode(Response.Status.UNAUTHORIZED, expectedErrorMsg, new Runnable() {
            @Override
            public void run() {
                client.getIssueClient().addWatcher(issue1.getWatchers().getSelf(), ADMIN_USERNAME, pm);
            }
        });
    }

    private boolean isJraDev3516Fixed() {
        return client.getMetadataClient().getServerInfo(pm).getBuildNumber() >= ServerVersionConstants.BN_JIRA_4_3;
    }

    //@Test restore when JRADEV-3666 is fixed (I don't want to pollute JRJC integration test results)
    public void xtestAddWatcherWhoDoesNotHaveViewIssuePermissions() {
        final IssueRestClient issueClient = client.getIssueClient();
        final Issue issue1 = issueClient.getIssue("RST-1", pm);
        assertErrorCode(Response.Status.BAD_REQUEST, "The user \"" + USER2_USERNAME
                + "\" does not have permission to view this issue. This user will not be added to the watch list.",
                new Runnable() {
                    @Override
                    public void run() {
                        issueClient.addWatcher(issue1.getWatchers().getSelf(), USER2_USERNAME, pm);
                    }
                });

    }

    @Test
    public void testLinkIssuesWithRoleLevel() {
        testLinkIssuesImpl(Comment.createWithRoleLevel("A comment about linking", "Administrators"));
    }

    @Test
    public void testLinkIssuesWithGroupLevel() {
        testLinkIssuesImpl(Comment.createWithGroupLevel("A comment about linking", "jira-administrators"));
    }

    @Test
    public void testLinkIssuesWithSimpleComment() {
        testLinkIssuesImpl(Comment.valueOf("A comment about linking"));
    }

    @Test
    public void testLinkIssuesWithoutComment() {
        testLinkIssuesImpl(null);
    }

    @Test
    public void testLinkIssuesWithInvalidParams() {
        if (!doesJiraSupportRestIssueLinking()) {
            return;
        }
        assertErrorCode(Response.Status.NOT_FOUND,
                IntegrationTestUtil.TESTING_JIRA_5_OR_NEWER ? "Issue Does Not Exist"
                        : "The issue no longer exists.",
                new Runnable() {
                    @Override
                    public void run() {
                        client.getIssueClient()
                                .linkIssue(new LinkIssuesInput("TST-7", "FAKEKEY-1", "Duplicate", null), pm);
                    }
                });

        assertErrorCode(Response.Status.NOT_FOUND, "No issue link type with name 'NonExistingLinkType' found.",
                new Runnable() {
                    @Override
                    public void run() {
                        client.getIssueClient()
                                .linkIssue(new LinkIssuesInput("TST-7", "TST-6", "NonExistingLinkType", null), pm);
                    }
                });

        setUser1();
        final String optionalDot = isJira5xOrNewer() ? "." : "";
        assertErrorCode(Response.Status.NOT_FOUND,
                "You do not have the permission to see the specified issue" + optionalDot, new Runnable() {
                    @Override
                    public void run() {
                        client.getIssueClient().linkIssue(new LinkIssuesInput("TST-7", "RST-1", "Duplicate", null),
                                pm);
                    }
                });
        assertErrorCode(Response.Status.BAD_REQUEST,
                "Failed to create comment for issue 'TST-6'\nYou are currently not a member of the project role: Administrators.",
                new Runnable() {
                    @Override
                    public void run() {
                        client.getIssueClient().linkIssue(new LinkIssuesInput("TST-7", "TST-6", "Duplicate",
                                Comment.createWithRoleLevel("my body", "Administrators")), pm);
                    }
                });
        assertErrorCode(Response.Status.BAD_REQUEST,
                "You are currently not a member of the group: jira-administrators.", new Runnable() {
                    @Override
                    public void run() {
                        client.getIssueClient().linkIssue(new LinkIssuesInput("TST-7", "TST-6", "Duplicate",
                                Comment.createWithGroupLevel("my body", "jira-administrators")), pm);
                    }
                });
        assertErrorCode(Response.Status.BAD_REQUEST, "Group: somefakegroup does not exist.", new Runnable() {
            @Override
            public void run() {
                client.getIssueClient().linkIssue(new LinkIssuesInput("TST-7", "TST-6", "Duplicate",
                        Comment.createWithGroupLevel("my body", "somefakegroup")), pm);
            }
        });

        setUser2();
        assertErrorCode(Response.Status.UNAUTHORIZED, "No Link Issue Permission for issue 'TST-7'", new Runnable() {
            @Override
            public void run() {
                client.getIssueClient().linkIssue(new LinkIssuesInput("TST-7", "TST-6", "Duplicate", null), pm);
            }
        });

    }

    private void testLinkIssuesImpl(@Nullable Comment commentInput) {
        if (!doesJiraSupportRestIssueLinking()) {
            return;
        }

        final IssueRestClient issueClient = client.getIssueClient();
        final Issue originalIssue = issueClient.getIssue("TST-7", pm);
        int origNumComments = Iterables.size(originalIssue.getComments());
        assertFalse(originalIssue.getIssueLinks().iterator().hasNext());

        issueClient.linkIssue(new LinkIssuesInput("TST-7", "TST-6", "Duplicate", commentInput), pm);

        final Issue linkedIssue = issueClient.getIssue("TST-7", pm);
        assertEquals(1, Iterables.size(linkedIssue.getIssueLinks()));
        final IssueLink addedLink = linkedIssue.getIssueLinks().iterator().next();
        assertEquals("Duplicate", addedLink.getIssueLinkType().getName());
        assertEquals("TST-6", addedLink.getTargetIssueKey());
        assertEquals(IssueLinkType.Direction.OUTBOUND, addedLink.getIssueLinkType().getDirection());

        final int expectedNumComments = commentInput != null ? origNumComments + 1 : origNumComments;
        assertEquals(expectedNumComments, Iterables.size(linkedIssue.getComments()));
        if (commentInput != null) {
            final Comment comment = linkedIssue.getComments().iterator().next();
            assertEquals(commentInput.getBody(), comment.getBody());
            assertEquals(IntegrationTestUtil.USER_ADMIN, comment.getAuthor());
            assertEquals(commentInput.getVisibility(), comment.getVisibility());
        } else {
            assertFalse(linkedIssue.getComments().iterator().hasNext());
        }

        final Issue targetIssue = issueClient.getIssue("TST-6", pm);
        final IssueLink targetLink = targetIssue.getIssueLinks().iterator().next();
        assertEquals(IssueLinkType.Direction.INBOUND, targetLink.getIssueLinkType().getDirection());
        assertEquals("Duplicate", targetLink.getIssueLinkType().getName());
    }

    private boolean doesJiraSupportAddingAttachment() {
        return client.getMetadataClient().getServerInfo(pm).getBuildNumber() >= ServerVersionConstants.BN_JIRA_4_3;
    }

    private boolean doesJiraServeCorrectlyErrorMessagesForBadRequestWhileTransitioningIssue() {
        return client.getMetadataClient().getServerInfo(pm).getBuildNumber() >= ServerVersionConstants.BN_JIRA_4_3;
    }

    @Test
    public void testAddAttachment() throws IOException {
        if (!doesJiraSupportAddingAttachment()) {
            return;
        }
        final IssueRestClient issueClient = client.getIssueClient();
        final Issue issue = issueClient.getIssue("TST-3", pm);
        assertFalse(issue.getAttachments().iterator().hasNext());

        String str = "Wojtek";
        final String filename1 = "my-test-file";
        issueClient.addAttachment(pm, issue.getAttachmentsUri(), new ByteArrayInputStream(str.getBytes("UTF-8")),
                filename1);
        final String filename2 = "my-picture.png";
        issueClient.addAttachment(pm, issue.getAttachmentsUri(),
                JerseyIssueRestClientTest.class.getResourceAsStream("/attachment-test/transparent-png.png"),
                filename2);

        final Issue issueWithAttachments = issueClient.getIssue("TST-3", pm);
        final Iterable<Attachment> attachments = issueWithAttachments.getAttachments();
        assertEquals(2, Iterables.size(attachments));
        final Iterable<String> attachmentsNames = Iterables.transform(attachments,
                new Function<Attachment, String>() {
                    @Override
                    public String apply(@Nullable Attachment from) {
                        return from.getFilename();
                    }
                });
        assertThat(attachmentsNames, IterableMatcher.hasOnlyElements(filename1, filename2));
        final Attachment pictureAttachment = Iterables.find(attachments, new Predicate<Attachment>() {
            @Override
            public boolean apply(@Nullable Attachment input) {
                return filename2.equals(input.getFilename());
            }
        });

        // let's download it now and compare it's binary content

        assertTrue(IOUtils.contentEquals(
                JerseyIssueRestClientTest.class.getResourceAsStream("/attachment-test/transparent-png.png"),
                issueClient.getAttachment(pm, pictureAttachment.getContentUri())));
    }

    @Test
    public void testAddAttachments() throws IOException {
        if (!doesJiraSupportAddingAttachment()) {
            return;
        }
        final IssueRestClient issueClient = client.getIssueClient();
        final Issue issue = issueClient.getIssue("TST-4", pm);
        assertFalse(issue.getAttachments().iterator().hasNext());

        final AttachmentInput[] attachmentInputs = new AttachmentInput[3];
        for (int i = 1; i <= 3; i++) {
            attachmentInputs[i - 1] = new AttachmentInput("my-test-file-" + i + ".txt",
                    new ByteArrayInputStream(("content-of-the-file-" + i).getBytes("UTF-8")));
        }
        issueClient.addAttachments(pm, issue.getAttachmentsUri(), attachmentInputs);

        final Issue issueWithAttachments = issueClient.getIssue("TST-4", pm);
        final Iterable<Attachment> attachments = issueWithAttachments.getAttachments();
        assertEquals(3, Iterables.size(attachments));
        Pattern pattern = Pattern.compile("my-test-file-(\\d)\\.txt");
        for (Attachment attachment : attachments) {
            assertTrue(pattern.matcher(attachment.getFilename()).matches());
            final Matcher matcher = pattern.matcher(attachment.getFilename());
            matcher.find();
            final String interfix = matcher.group(1);
            assertTrue(IOUtils.contentEquals(
                    new ByteArrayInputStream(("content-of-the-file-" + interfix).getBytes("UTF-8")),
                    issueClient.getAttachment(pm, attachment.getContentUri())));

        }
    }

    @Test
    public void testAddFileAttachments() throws IOException {
        if (!doesJiraSupportAddingAttachment()) {
            return;
        }
        final IssueRestClient issueClient = client.getIssueClient();
        final Issue issue = issueClient.getIssue("TST-5", pm);
        assertFalse(issue.getAttachments().iterator().hasNext());

        final File tempFile = File.createTempFile("jim-integration-test", ".txt");
        tempFile.deleteOnExit();
        FileWriter writer = new FileWriter(tempFile);
        writer.write("This is the content of my file which I am going to upload to JIRA for testing.");
        writer.close();
        issueClient.addAttachments(pm, issue.getAttachmentsUri(), tempFile);

        final Issue issueWithAttachments = issueClient.getIssue("TST-5", pm);
        final Iterable<Attachment> attachments = issueWithAttachments.getAttachments();
        assertEquals(1, Iterables.size(attachments));
        assertTrue(IOUtils.contentEquals(new FileInputStream(tempFile),
                issueClient.getAttachment(pm, attachments.iterator().next().getContentUri())));
    }

    @Test
    public void testFetchingUnassignedIssue() {
        administration.generalConfiguration().setAllowUnassignedIssues(true);
        assertEquals(IntegrationTestUtil.USER_ADMIN, client.getIssueClient().getIssue("TST-5", pm).getAssignee());

        navigation.userProfile().changeUserLanguage("angielski (UK)");
        navigation.issue().unassignIssue("TST-5", "unassigning issue");
        // this single line does instead of 2 above - func test suck with non-English locale
        // but it does not work yet with JIRA 5.0-resthack...
        //navigation.issue().assignIssue("TST-5", "unassigning issue", "Nieprzydzielone");

        assertNull(client.getIssueClient().getIssue("TST-5", pm).getAssignee());
    }

    @Test
    public void testFetchingIssueWithAnonymousComment() {
        navigation.userProfile().changeUserLanguage("angielski (UK)");
        administration.permissionSchemes().scheme("Anonymous Permission Scheme").grantPermissionToGroup(15, "");
        assertEquals(IntegrationTestUtil.USER_ADMIN, client.getIssueClient().getIssue("TST-5", pm).getAssignee());
        navigation.logout();
        navigation.issue().addComment("ANNON-1", "my nice comment");
        final Issue issue = client.getIssueClient().getIssue("ANNON-1", pm);
        assertEquals(1, Iterables.size(issue.getComments()));
        final Comment comment = issue.getComments().iterator().next();
        assertEquals("my nice comment", comment.getBody());
        assertNull(comment.getAuthor());
        assertNull(comment.getUpdateAuthor());

    }

}