The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl

use strict;
use warnings;

use Test::More;
use Test::Exception;
use File::Temp;
use File::Spec;

BEGIN {
    use_ok('JIRA::Client::Automated');
}

# Set JIRA access information in ENV vars or pass it to the test.
my $jira_server   = $ENV{JIRA_CLIENT_AUTOMATED_URL}      || $ARGV[0];
my $jira_project  = $ENV{JIRA_CLIENT_AUTOMATED_PROJECT}  || $ARGV[1];
my $jira_user     = $ENV{JIRA_CLIENT_AUTOMATED_USER}     || $ARGV[2];
my $jira_password = $ENV{JIRA_CLIENT_AUTOMATED_PASSWORD} || $ARGV[3];

SKIP: {

    my $skip_text = <<END_SKIP_TEXT;
You must provide a URL for a JIRA server, project name, username and password in the appropriate environment variables to run this test.
For example:
setenv JIRA_CLIENT_AUTOMATED_URL 'https://you.atlassian.net/'
setenv JIRA_CLIENT_AUTOMATED_PROJECT TEST
setenv JIRA_CLIENT_AUTOMATED_USER you
setenv JIRA_CLIENT_AUTOMATED_PASSWORD '******'
END_SKIP_TEXT

    skip $skip_text, 1 if (!($jira_server && $jira_project && $jira_user && $jira_password));

    my $JCA = 'JIRA::Client::Automated';
    my ($jira, $issue, $key, @issues, $link_types);

    # Create new JCA object
    ok($jira = JIRA::Client::Automated->new($jira_server, $jira_user, $jira_password), 'new');
    isa_ok($jira, $JCA);

    # --- read-only tests first

    # check search that returns no matches
    @issues = $jira->all_search_results('createdDate = "1971-01-01"', 10);
    is @issues, 0, 'all_search_results with no results';
    
    @issues = $jira->all_search_results('KEY = NONESUCH-999999', 10);
    is @issues, 0, 'all_search_results with invalid key';

    # save link_types for later testing
    ok($link_types = $jira->get_link_types(), 'get_link_types');
    cmp_ok( (grep { $_->{name} eq 'Blocks' } @$link_types), '>=', 1, 'has blocks link type');
    
    # --- read-only tests first

    # Create an issue
    $issue = $jira->create_issue(
        $jira_project, 'Bug',
        "$JCA Test Script",
        "Created by $JCA Test Script automatically.",
        { labels => ["Commentary"] });
    ok($issue, 'create_issue');
    isa_ok($issue, 'HASH');
    ok($key   = $issue->{key},          'create_issue key');
    ok($issue = $jira->get_issue($key), 'get_issue');
    is($issue->{fields}{summary},     "$JCA Test Script",                           'create_issue summary');
    is($issue->{fields}{description}, "Created by $JCA Test Script automatically.", 'create_issue description');
    is($issue->{fields}{labels}[0],   "Commentary",                                 'create_issue labels');

    # Comment on an issue
    my ($comments);
    ok($jira->create_comment($key, "Comment from $JCA Test Script."), 'create_comment');
    ok($comments = $jira->get_issue_comments($key), 'get_issue_comments');
    is($comments->[0]{body}, "Comment from $JCA Test Script.", 'create_comment worked');

    # Update an issue
    ok($jira->update_issue($key, { summary => "$JCA updated" }), 'update_issue');
    ok($issue = $jira->get_issue($key), 'get_issue to see update');
    is($issue->{fields}{summary}, "$JCA updated", 'update_issue summary');

    # Label an issue
    ok($jira->add_issue_labels($key, 'testing'), 'add_issue_labels');
    ok($issue = $jira->get_issue($key), 'get_issue to see add_issue_labels');
    is(grep(m/^testing$/, @{$issue->{fields}{labels}}), 1, 'add_issue_labels worked');
    ok($jira->remove_issue_labels($key, 'testing'), 'remove_issue_labels');
    ok($issue = $jira->get_issue($key), 'get_issue to see remove_issue_labels');
    is(grep(m/^testing$/, @{$issue->{fields}{labels}}), 0, 'remove _issue_labels worked');

    # Attach a file to an issue
    my $tmp = File::Temp->new();
    print $tmp "Attach this file to $JCA test issue $key.\n";
    close $tmp;
    my $filepath = $tmp->filename();
    my ($volume, $directories, $filename) = File::Spec->splitpath($filepath);

    ok($jira->attach_file_to_issue($key, $tmp->filename()), 'attach_file_to_issue');
    ok($issue = $jira->get_issue($key), 'get_issue to see attachment');
    is($issue->{fields}{attachment}[0]{filename}, $filename, 'attach_file_to_issue attachment');
    undef $tmp; # File::Temp unlinks the file when it goes out of scope

    # NOTE: these user-based tests may not do much, if your system auto-assigns
    # issues to the creator, but they are at least testing the API calls. 
    # add watcher
    my ($watchers);
    is($jira->add_issue_watchers($key, $jira_user), undef, 'add_issue_watchers');
    ok($watchers = $jira->get_issue_watchers($key), 'get_issue_watchers');
    is($watchers->[0]{name}, $jira_user, 'added watcher');

    # assign issue
    ok($jira->assign_issue($key, $jira_user), 'assign_issue');
    ok($issue = $jira->get_issue($key), 'get_issue to see assignee');
    is($issue->{fields}{assignee}{name}, $jira_user, 'assigned issue');

    # Transition tests
    throws_ok {
        $jira->transition_issue($key, 'NoneSuch Foo');
    }
    qr/has no transition.*NoneSuch Foo/, 'transition_issue with unknown name';
    throws_ok {
        $jira->transition_issue($key, ['NoneSuch Bar', 'NoneSuch Baz']);
    }
    qr/has no transition.*NoneSuch Bar.*NoneSuch Baz/, 'transition_issue with unknown names';

    # Transition an issue through its workflow
    my $transition_alternatives = ['Start Progress', 'Add to backlog', 'Open'];
    my $prev_status_name = $issue->{fields}{status}{name};
    ok($jira->transition_issue($key, $transition_alternatives), 'transition_issue');
    ok($issue = $jira->get_issue($key), 'get_issue to see transition');
    isnt($issue->{fields}{status}{name},
        $prev_status_name, "transition_issue status (now $issue->{fields}{status}{name})");

    # Search for issues
    # complicated queries work too:
    #    my $jql
    #      = 'project = '
    #      . $self->{_jira_project}
    #      . ' AND resolution in (Fixed, "Won\'t Fix", Duplicate, Incomplete, "Cannot Reproduce") '
    #      . 'AND issuetype = "Broken Crawler" '
    #      . 'AND status in (Verify, "Copy Crawler to Polyvore") '
    #      . 'AND reporter in (pv_eng) '
    #      . 'ORDER BY createdDate DESC';
    my $jql = "KEY = $key";
    ok(@issues = $jira->all_search_results($jql, 10), 'all_search_results');
    is($issues[0]->{key}, $key, 'all_search_results found issue');

    # Create a sub-task
    my ($subtask, $sub_key);
    ok( $subtask = $jira->create_subtask(
            $jira_project,
            "$JCA Test Subtask",
            "Created by $JCA Test Script automatically.",
            $issue->{key}
        ),
        'create_subtask'
    );
    isa_ok($subtask, 'HASH');
    ok($sub_key = $subtask->{key},            'create_subtask key');
    ok($subtask = $jira->get_issue($sub_key), 'get_issue subtask');
    is($subtask->{fields}{summary}, "$JCA Test Subtask", 'create_subtask summary');
    is($subtask->{fields}{description}, "Created by $JCA Test Script automatically.", 'create_subtask description');
    ok($jira->delete_issue($sub_key), 'delete_issue subtask');

    ok( $subtask = $jira->create_subtask(
            $jira_project,                                "$JCA Test Subtask",
            "Created by $JCA Test Script automatically.", $issue->{key},
            'Sub-task'
        ),
        'create_subtask with type'
    );
    isa_ok($subtask, 'HASH');
    ok($sub_key = $subtask->{key},            'create_subtask with type key');
    ok($subtask = $jira->get_issue($sub_key), 'get_issue subtask with type');

    # Link two issues
    is($jira->link_issues($issue->{key}, $subtask->{key}, $link_types->[0]->{name}), undef, 'link_issues');
    ok($subtask = $jira->get_issue($sub_key), 'get_issue subtask to check link worked');
    my $issuelink = $subtask->{fields}{issuelinks}[0];
    is($issuelink->{type}{name}, $link_types->[0]->{name}, 'created correct type of link');
    is($issuelink->{outwardIssue}{key}, $issue->{key}, 'linked to correct issue');

    # Delete sub-task
    ok($jira->delete_issue($sub_key), 'delete_issue subtask');

    # Close an issue
    ok($jira->close_issue($key, 'Fixed', "Closed by $JCA Test Script"), 'close_issue');
    ok($issue = $jira->get_issue($key), 'get_issue to see closed');
    is($issue->{fields}{status}{name}, 'Closed', 'close_issue status');

    # Delete our test issue
    # You wouldn't want to do this in production; they're handy to keep around as documentation
    # May fail with 403 Forbidden
    ok($jira->delete_issue($key), 'delete_issue');
    throws_ok { @issues = $jira->all_search_results($jql, 10) } qr/does not exist/, 'all_search_results after delete';
}

done_testing()