# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.

########################################
# Test for xmlrpc call to Bug.search() #
########################################

use 5.14.0;
use strict;
use warnings;

use FindBin qw($RealBin);
use lib "$RealBin/../lib";

use QA::Util;
use QA::Tests qw(PRIVATE_BUG_USER);
use DateTime;
use List::MoreUtils qw(uniq);
use Test::More;
use Data::Dumper;

my ($config, @clients) = get_rpc_clients();
plan tests => $config->{test_extensions} ? 531 : 522;

my ($public_bug, $private_bug) = $clients[0]->bz_create_test_bugs('private');

# Add aliases to both bugs
$public_bug->{alias}  = random_string(40);
$private_bug->{alias} = random_string(40);
my $alias_tests = [
    { user => 'editbugs',
      args => { ids => [ $public_bug->{id} ], alias => $public_bug->{alias} },
      test => 'Add alias to public bug' },
    { user => PRIVATE_BUG_USER,
      args => { ids => [ $private_bug->{id} ],
                cc  => { add => [ $config->{'editbugs_user_login'} ] } },
      test => 'Add editusers to cc of private bug' },
    { user => 'editbugs',
      args => { ids => [ $private_bug->{id} ], alias => $private_bug->{alias} },
      test => 'Add alias to private bug' },
    { user => PRIVATE_BUG_USER,
      args => { ids => [ $private_bug->{id} ],
                cc  => { remove => [ $config->{'editbugs_user_login'} ] } },
      test => 'Remove editusers from cc of private bug' },
];
$clients[0]->bz_run_tests(tests => $alias_tests, method => 'Bug.update');

my @tests;
foreach my $field (keys %$public_bug) {
    next if ($field eq 'cc' or $field eq 'description');
    my $test = { args => { $field => $public_bug->{$field} },
                 test => "Search by $field" };
    if ( grep($_ eq $field, qw(alias whiteboard summary)) ) {
        $test->{exactly} = 1; $test->{bugs} = 1;
    }
    push(@tests, $test);
}

push(@tests, (
    { args  => { offset => 1 },
      test  => "Offset without limit fails",
      error => 'requires a limit argument',
    },

    { args => { alias => $private_bug->{alias} },
      test => 'Logged-out cannot find a private_bug by alias',
      bugs => 0,
    },

    { args => { creation_time => '19700101T00:00:00' },
      test => 'Get all bugs by creation time',
    },
    { args => { creation_time => '20380101T00:00:00' },
      test => 'Get no bugs, by creation time',
      bugs => 0,
    },
    { args => { last_change_time => '19700101T00:00:00' },
      test => 'Get all bugs by last_change_time',
    },
    { args => { last_change_time => '20380101T00:00:00' },
      test => 'Get no bugs by last_change_time',
      bugs => 0,
    },

    { args => { reporter => $config->{editbugs_user_login} },
      test => 'Search by reporter',
    },
    { args => { resolution => '' },
      test => 'Search for empty resolution',
    },
    { args => { resolution => 'NO_SUCH_RESOLUTION' },
      test => 'Search for invalid resolution',
      bugs => 0,
    },
    { args => { summary => substr($public_bug->{summary}, 0, 50) },
      test => 'Search by partial summary',
      bugs => 1, exactly => 1
    },
    { args => { summary => random_string() . ' ' . random_string() },
      test => 'Summary search that returns no results',
      bugs => 0,
    },
    { args => { summary => [split(/\s/, $public_bug->{summary})] },
      test => 'Summary search using multiple terms',
    },

    { args => { whiteboard => substr($public_bug->{whiteboard}, 0, 50) },
      test => 'Search by partial whiteboard',
      bugs => 1, exactly => 1,
    },
    { args => { whiteboard => random_string(100) },
      test => 'Whiteboard search that returns no results',
      bugs => 0,
    },
    { args => { whiteboard => [split(/\s/, $public_bug->{whiteboard})] },
      test => 'Whiteboard search using multiple terms',
      bugs => 1, exactly => 1,
    },

    { args => { product => $public_bug->{product},
                component => $public_bug->{component},
                last_change_time => '19700101T00:00:00' },
      test => 'Search by multiple arguments',
    },

    # Logged-in user who can see private bugs
    { user => PRIVATE_BUG_USER,
      args => { alias => [$public_bug->{alias}, $private_bug->{alias}] },
      test => 'Search using two aliases (including one private)',
      bugs => 2, exactly => 1,
    },
    { user => PRIVATE_BUG_USER,
      args => { product => [$public_bug->{product}, $private_bug->{product}],
                limit => 1 },
      test => 'Limit 1',
      bugs => 1, exactly => 1,
    },
    { user => PRIVATE_BUG_USER,
      args => { product => [$public_bug->{product}, $private_bug->{product}],
                limit => 1, offset => 1 },
      test => 'Limit 1 Offset 1',
      bugs => 1, exactly => 1,
    },

    # include_fields ane exclude_fields
    { args => { id => $public_bug->{id},
                include_fields => ['id', 'alias', 'summary', 'groups'] },
      test => 'include_fields',
    },
    { args => { id => $public_bug->{id},
                exclude_fields => ['assigned_to', 'cf_qa_status'] },
      test => 'exclude_fields' },
    { args => { id => $public_bug->{id},
                include_fields => ['id', 'alias', 'summary', 'groups'],
                exclude_fields => ['summary'] },
      test => 'exclude_fields overrides include_fields' },
));

push(@tests,
    { args => { votes => 1 },
      test => 'Search by votes',
      bugs => -1, # We don't care how many it returns, for now.
    }) if $config->{test_extensions};

sub post_success {
    my ($call, $t) = @_;
    my $bugs = $call->result->{bugs};

    my $expected_count = $t->{bugs};
    $expected_count = 1 if !defined $expected_count;
    if ($expected_count) {
        my $operator = $t->{exactly} ? '==' : '>=';
        cmp_ok(scalar @$bugs, $operator, $expected_count,
               'The right number of bugs are returned');
        unless ($t->{user} and $t->{user} eq PRIVATE_BUG_USER) {
            ok(!grep($_->{alias} && $_->{alias} eq $private_bug->{alias}, @$bugs),
               'Result does not contain the private bug');
        }

        my @include = @{ $t->{args}->{include_fields} || [] };
        my @exclude = @{ $t->{args}->{exclude_fields} || [] };
        if (@include or @exclude) {
            my @check_fields = uniq (keys %$public_bug, @include);
            foreach my $field (sort @check_fields) {
                next if $field eq 'description';
                if ((@include and !grep { $_ eq $field } @include )
                    or (@exclude and grep { $_ eq $field } @exclude))
                {
                    ok(!exists $bugs->[0]->{$field}, "$field is not included")
                      or diag Dumper($bugs);
                }
                else {
                    ok(exists $bugs->[0]->{$field}, "$field is included");
                }
            }
        }

    }
    else {
        is(scalar @$bugs, 0, 'No bugs returned');
    }
}

foreach my $rpc (@clients) {
    $rpc->bz_run_tests(tests => \@tests,
                       method => 'Bug.search', post_success => \&post_success);
}
