diff options
Diffstat (limited to 'rt/t/ticket')
24 files changed, 3433 insertions, 0 deletions
diff --git a/rt/t/ticket/action_linear_escalate.t b/rt/t/ticket/action_linear_escalate.t new file mode 100644 index 000000000..38cd47ded --- /dev/null +++ b/rt/t/ticket/action_linear_escalate.t @@ -0,0 +1,100 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT; +use RT::Test tests => 17; + +my ($id, $msg); +my $RecordTransaction; +my $UpdateLastUpdated; + + +use_ok('RT::Action::LinearEscalate'); + +my $q = RT::Test->load_or_create_queue( Name => 'Regression' ); +ok $q && $q->id, 'loaded or created queue'; + +# rt-cron-tool uses Gecos name to get rt user, so we'd better create one +my $gecos = RT::Test->load_or_create_user( + Name => 'gecos', + Password => 'password', + Gecos => ($^O eq 'MSWin32') ? Win32::LoginName() : (getpwuid($<))[0], +); +ok $gecos && $gecos->id, 'loaded or created gecos user'; + +# get rid of all right permissions +$gecos->PrincipalObj->GrantRight( Right => 'SuperUser' ); + + +my $user = RT::Test->load_or_create_user( + Name => 'user', Password => 'password', +); +ok $user && $user->id, 'loaded or created user'; + +$user->PrincipalObj->GrantRight( Right => 'SuperUser' ); +my $current_user = RT::CurrentUser->new($RT::SystemUser); +($id, $msg) = $current_user->Load($user->id); +ok( $id, "Got current user? $msg" ); + +#defaults +$RecordTransaction = 0; +$UpdateLastUpdated = 1; +my $ticket2 = create_ticket_as_ok($current_user); +escalate_ticket_ok($ticket2); +ok( $ticket2->LastUpdatedBy != $user->id, "Set LastUpdated" ); +ok( $ticket2->Transactions->Last->Type =~ /Create/i, "Did not record a transaction" ); + +$RecordTransaction = 1; +$UpdateLastUpdated = 1; +my $ticket1 = create_ticket_as_ok($current_user); +escalate_ticket_ok($ticket1); +ok( $ticket1->LastUpdatedBy != $user->id, "Set LastUpdated" ); +ok( $ticket1->Transactions->Last->Type !~ /Create/i, "Recorded a transaction" ); + +$RecordTransaction = 0; +$UpdateLastUpdated = 0; +my $ticket3 = create_ticket_as_ok($current_user); +escalate_ticket_ok($ticket3); +ok( $ticket3->LastUpdatedBy == $user->id, "Did not set LastUpdated" ); +ok( $ticket3->Transactions->Last->Type =~ /Create/i, "Did not record a transaction" ); + +1; + + +sub create_ticket_as_ok { + my $user = shift; + + my $created = RT::Date->new($RT::SystemUser); + $created->Unix(time() - ( 7 * 24 * 60**2 )); + my $due = RT::Date->new($RT::SystemUser); + $due->Unix(time() + ( 7 * 24 * 60**2 )); + + my $ticket = RT::Ticket->new($user); + ($id, $msg) = $ticket->Create( Queue => $q->id, + Subject => "Escalation test", + Priority => 0, + InitialPriority => 0, + FinalPriority => 50, + ); + ok($id, "Created ticket? ".$id); + $ticket->__Set( Field => 'Created', + Value => $created->ISO, + ); + $ticket->__Set( Field => 'Due', + Value => $due->ISO, + ); + + return $ticket; +} + +sub escalate_ticket_ok { + my $ticket = shift; + my $id = $ticket->id; + print "$RT::BinPath/rt-crontool --search RT::Search::FromSQL --search-arg \"id = @{[$id]}\" --action RT::Action::LinearEscalate --action-arg \"RecordTransaction:$RecordTransaction; UpdateLastUpdated:$UpdateLastUpdated\"\n"; + print STDERR `$RT::BinPath/rt-crontool --search RT::Search::FromSQL --search-arg "id = @{[$id]}" --action RT::Action::LinearEscalate --action-arg "RecordTransaction:$RecordTransaction; UpdateLastUpdated:$UpdateLastUpdated"`; + + $ticket->Load($id); # reload, because otherwise we get the cached value + ok( $ticket->Priority != 0, "Escalated ticket" ); +} diff --git a/rt/t/ticket/add-watchers.t b/rt/t/ticket/add-watchers.t new file mode 100644 index 000000000..ae993a936 --- /dev/null +++ b/rt/t/ticket/add-watchers.t @@ -0,0 +1,167 @@ +#!/usr/bin/perl -w +# BEGIN BPS TAGGED BLOCK {{{ +# +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC +# <jesse.com> +# +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: +# +# This work is made available to you under the terms of Version 2 of +# the GNU General Public License. A copy of that license should have +# been provided with this software, but in any event can be snarfed +# from www.gnu.org. +# +# This work is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) +# +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# END BPS TAGGED BLOCK }}} + +use RT::Test tests => 32; + +use strict; +use warnings; +no warnings 'once'; + +use RT::Queue; +use RT::User; +use RT::Group; +use RT::Ticket; +use RT::CurrentUser; + + +# clear all global right +my $acl = RT::ACL->new($RT::SystemUser); +$acl->Limit( FIELD => 'RightName', OPERATOR => '!=', VALUE => 'SuperUser' ); +$acl->LimitToObject( $RT::System ); +while( my $ace = $acl->Next ) { + $ace->Delete; +} + +# create new queue to be sure we do not mess with rights +my $queue = RT::Queue->new($RT::SystemUser); +my ($queue_id) = $queue->Create( Name => 'watcher tests '.$$); +ok( $queue_id, 'queue created for watcher tests' ); + +# new privileged user to check rights +my $user = RT::User->new( $RT::SystemUser ); +my ($user_id) = $user->Create( Name => 'watcher'.$$, + EmailAddress => "watcher$$".'@localhost', + Privileged => 1, + Password => 'qwe123', + ); +my $cu= RT::CurrentUser->new($user); + +# make sure user can see tickets in the queue +my $principal = $user->PrincipalObj; +ok( $principal, "principal loaded" ); +$principal->GrantRight( Right => 'ShowTicket', Object => $queue ); +$principal->GrantRight( Right => 'SeeQueue' , Object => $queue ); + +ok( $user->HasRight( Right => 'SeeQueue', Object => $queue ), "user can see queue" ); +ok( $user->HasRight( Right => 'ShowTicket', Object => $queue ), "user can show queue tickets" ); +ok( !$user->HasRight( Right => 'ModifyTicket', Object => $queue ), "user can't modify queue tickets" ); +ok( !$user->HasRight( Right => 'Watch', Object => $queue ), "user can't watch queue tickets" ); + +my $ticket = RT::Ticket->new( $RT::SystemUser ); +my ($rv, $msg) = $ticket->Create( Subject => 'watcher tests', Queue => $queue->Name ); +ok( $ticket->id, "ticket created" ); + +my $ticket2 = RT::Ticket->new( $cu ); +$ticket2->Load( $ticket->id ); +ok( $ticket2->Subject, "ticket load by user" ); + +# user can add self to ticket only after getting Watch right +($rv, $msg) = $ticket2->AddWatcher( Type => 'Cc', PrincipalId => $user->PrincipalId ); +ok( !$rv, "user can't add self as Cc" ); +($rv, $msg) = $ticket2->AddWatcher( Type => 'Requestor', PrincipalId => $user->PrincipalId ); +ok( !$rv, "user can't add self as Requestor" ); +$principal->GrantRight( Right => 'Watch' , Object => $queue ); +ok( $user->HasRight( Right => 'Watch', Object => $queue ), "user can watch queue tickets" ); +($rv, $msg) = $ticket2->AddWatcher( Type => 'Cc', PrincipalId => $user->PrincipalId ); +ok( $rv, "user can add self as Cc by PrincipalId" ); +($rv, $msg) = $ticket2->AddWatcher( Type => 'Requestor', PrincipalId => $user->PrincipalId ); +ok( $rv, "user can add self as Requestor by PrincipalId" ); + +# remove user and try adding with Email address +($rv, $msg) = $ticket->DeleteWatcher( Type => 'Cc', PrincipalId => $user->PrincipalId ); +ok( $rv, "watcher removed by PrincipalId" ); +($rv, $msg) = $ticket->DeleteWatcher( Type => 'Requestor', Email => $user->EmailAddress ); +ok( $rv, "watcher removed by Email" ); + +($rv, $msg) = $ticket2->AddWatcher( Type => 'Cc', Email => $user->EmailAddress ); +ok( $rv, "user can add self as Cc by Email" ); +($rv, $msg) = $ticket2->AddWatcher( Type => 'Requestor', Email => $user->EmailAddress ); +ok( $rv, "user can add self as Requestor by Email" ); + +# remove user and try adding by username +# This worked in 3.6 and is a regression in 3.8 +($rv, $msg) = $ticket->DeleteWatcher( Type => 'Cc', Email => $user->EmailAddress ); +ok( $rv, "watcher removed by Email" ); +($rv, $msg) = $ticket->DeleteWatcher( Type => 'Requestor', Email => $user->EmailAddress ); +ok( $rv, "watcher removed by Email" ); + +($rv, $msg) = $ticket2->AddWatcher( Type => 'Cc', Email => $user->Name ); +ok( $rv, "user can add self as Cc by username" ); +($rv, $msg) = $ticket2->AddWatcher( Type => 'Requestor', Email => $user->Name ); +ok( $rv, "user can add self as Requestor by username" ); + +# Queue watcher tests +$principal->RevokeRight( Right => 'Watch' , Object => $queue ); +ok( !$user->HasRight( Right => 'Watch', Object => $queue ), "user queue watch right revoked" ); + +my $queue2 = RT::Queue->new( $cu ); +($rv, $msg) = $queue2->Load( $queue->id ); +ok( $rv, "user loaded queue" ); + +# user can add self to queue only after getting Watch right +($rv, $msg) = $queue2->AddWatcher( Type => 'Cc', PrincipalId => $user->PrincipalId ); +ok( !$rv, "user can't add self as Cc" ); +($rv, $msg) = $queue2->AddWatcher( Type => 'Requestor', PrincipalId => $user->PrincipalId ); +ok( !$rv, "user can't add self as Requestor" ); +$principal->GrantRight( Right => 'Watch' , Object => $queue ); +ok( $user->HasRight( Right => 'Watch', Object => $queue ), "user can watch queue queues" ); +($rv, $msg) = $queue2->AddWatcher( Type => 'Cc', PrincipalId => $user->PrincipalId ); +ok( $rv, "user can add self as Cc by PrincipalId" ); +($rv, $msg) = $queue2->AddWatcher( Type => 'Requestor', PrincipalId => $user->PrincipalId ); +ok( $rv, "user can add self as Requestor by PrincipalId" ); + +# remove user and try adding with Email address +($rv, $msg) = $queue->DeleteWatcher( Type => 'Cc', PrincipalId => $user->PrincipalId ); +ok( $rv, "watcher removed by PrincipalId" ); +($rv, $msg) = $queue->DeleteWatcher( Type => 'Requestor', Email => $user->EmailAddress ); +ok( $rv, "watcher removed by Email" ); + +($rv, $msg) = $queue2->AddWatcher( Type => 'Cc', Email => $user->EmailAddress ); +ok( $rv, "user can add self as Cc by Email" ); +($rv, $msg) = $queue2->AddWatcher( Type => 'Requestor', Email => $user->EmailAddress ); +ok( $rv, "user can add self as Requestor by Email" ); + diff --git a/rt/t/ticket/badlinks.t b/rt/t/ticket/badlinks.t new file mode 100644 index 000000000..408e6b67c --- /dev/null +++ b/rt/t/ticket/badlinks.t @@ -0,0 +1,38 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use RT::Test tests => 12; + +my ($baseurl, $m) = RT::Test->started_ok; +ok($m->login, "Logged in"); + +my $queue = RT::Test->load_or_create_queue(Name => 'General'); +ok($queue->Id, "loaded the General queue"); + +my $ticket = RT::Ticket->new($RT::SystemUser); +my ($tid, $txn, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test links', + ); +ok $tid, 'created a ticket #'. $tid or diag "error: $msg"; + +$m->goto_ticket($tid); + +$m->follow_link_ok( { text => 'Links' }, "Followed link to Links" ); + +ok $m->form_with_fields("$tid-DependsOn"), "found the form"; +my $not_a_ticket_url = "http://example.com/path/to/nowhere"; +$m->field("$tid-DependsOn", $not_a_ticket_url); +$m->field("DependsOn-$tid", $not_a_ticket_url); +$m->field("$tid-MemberOf", $not_a_ticket_url); +$m->field("MemberOf-$tid", $not_a_ticket_url); +$m->field("$tid-RefersTo", $not_a_ticket_url); +$m->field("RefersTo-$tid", $not_a_ticket_url); +$m->submit; + +foreach my $type ("depends on", "member of", "refers to") { + $m->content_like(qr/$type.+$not_a_ticket_url/,"base for $type"); + $m->content_like(qr/$not_a_ticket_url.+$type/,"target for $type"); +} + +$m->goto_ticket($tid); diff --git a/rt/t/ticket/batch-upload-csv.t b/rt/t/ticket/batch-upload-csv.t new file mode 100644 index 000000000..41dc78696 --- /dev/null +++ b/rt/t/ticket/batch-upload-csv.t @@ -0,0 +1,48 @@ +#!/usr/bin/perl -w +use strict; use warnings; + +use RT::Test tests => 12; +use_ok('RT'); + +use_ok('RT::Action::CreateTickets'); + +my $QUEUE = 'uploadtest-'.$$; + +my $queue_obj = RT::Queue->new($RT::SystemUser); +$queue_obj->Create(Name => $QUEUE); + +my $cf = RT::CustomField->new($RT::SystemUser); +my ($val,$msg) = $cf->Create(Name => 'Work Package-'.$$, Type => 'Freeform', LookupType => RT::Ticket->CustomFieldLookupType, MaxValues => 1); +ok($cf->id); +ok($val,$msg); +($val, $msg) = $cf->AddToObject($queue_obj); +ok($val,$msg); +ok($queue_obj->TicketCustomFields()->Count, "We have a custom field, at least"); + + +my $data = <<EOF; +id,Queue,Subject,Status,Requestor,@{[$cf->Name]} +create-1,$QUEUE,hi,new,root,2.0 +create-2,$QUEUE,hello,new,root,3.0 +EOF + +my $action = RT::Action::CreateTickets->new(CurrentUser => RT::CurrentUser->new('root')); +ok ($action->CurrentUser->id , "WE have a current user"); + +$action->Parse(Content => $data); +my @results = $action->CreateByTemplate(); + +my $tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL ("Queue = '". $QUEUE."'"); +$tix->OrderBy( FIELD => 'id', ORDER => 'ASC' ); +is($tix->Count, 2, '2 tickets'); + +my $first = $tix->First(); + +is($first->Subject(), 'hi'); +is($first->FirstCustomFieldValue($cf->id), '2.0'); + +my $second = $tix->Next; +is($second->Subject(), 'hello'); +is($second->FirstCustomFieldValue($cf->id), '3.0'); +1; diff --git a/rt/t/ticket/cfsort-freeform-multiple.t b/rt/t/ticket/cfsort-freeform-multiple.t new file mode 100644 index 000000000..f8f5950ef --- /dev/null +++ b/rt/t/ticket/cfsort-freeform-multiple.t @@ -0,0 +1,137 @@ +#!/usr/bin/perl + +use RT::Test tests => 24; + +use strict; +use warnings; + +use RT::Tickets; +use RT::Queue; +use RT::CustomField; + +# Test Sorting by custom fields. + +diag "Create a queue to test with." if $ENV{TEST_VERBOSE}; +my $queue_name = "CFSortQueue-$$"; +my $queue; +{ + $queue = RT::Queue->new( $RT::SystemUser ); + my ($ret, $msg) = $queue->Create( + Name => $queue_name, + Description => 'queue for custom field sort testing' + ); + ok($ret, "$queue_name - test queue creation. $msg"); +} + +diag "create a CF\n" if $ENV{TEST_VERBOSE}; +my $cf_name = "Order$$"; +my $cf; +{ + $cf = RT::CustomField->new( $RT::SystemUser ); + my ($ret, $msg) = $cf->Create( + Name => $cf_name, + Queue => $queue->id, + Type => 'FreeformMultiple', + ); + ok($ret, "Custom Field Order created"); +} + +my ($total, @data, @tickets, @test) = (0, ()); + +sub add_tix_from_data { + my @res = (); + @data = sort { rand(100) <=> rand(100) } @data; + while (@data) { + my $t = RT::Ticket->new($RT::SystemUser); + my %args = %{ shift(@data) }; + my @values = (); + if ( exists $args{'CF'} && ref $args{'CF'} ) { + @values = @{ delete $args{'CF'} }; + } elsif ( exists $args{'CF'} ) { + @values = (delete $args{'CF'}); + } + $args{ 'CustomField-'. $cf->id } = \@values + if @values; + my $subject = join(",", sort @values) || '-'; + my ( $id, undef $msg ) = $t->Create( + %args, + Queue => $queue->id, + Subject => $subject, + ); + ok( $id, "ticket created" ) or diag("error: $msg"); + push @res, $t; + $total++; + } + return @res; +} + +sub run_tests { + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + foreach my $test ( @test ) { + my $query = join " AND ", map "( $_ )", grep defined && length, + $query_prefix, $test->{'Query'}; + + foreach my $order (qw(ASC DESC)) { + my $error = 0; + my $tix = RT::Tickets->new( $RT::SystemUser ); + $tix->FromSQL( $query ); + $tix->OrderBy( FIELD => $test->{'Order'}, ORDER => $order ); + + ok($tix->Count, "found ticket(s)") + or $error = 1; + + my ($order_ok, $last) = (1, $order eq 'ASC'? '-': 'zzzzzz'); + my $last_id = $tix->Last->id; + while ( my $t = $tix->Next ) { + my $tmp; + next if $t->id == $last_id and $t->Subject eq "-"; # Nulls are allowed to come last, in Pg + + if ( $order eq 'ASC' ) { + $tmp = ((split( /,/, $last))[0] cmp (split( /,/, $t->Subject))[0]); + } else { + $tmp = -((split( /,/, $last))[-1] cmp (split( /,/, $t->Subject))[-1]); + } + if ( $tmp > 0 ) { + $order_ok = 0; last; + } + $last = $t->Subject; + } + + ok( $order_ok, "$order order of tickets is good" ) + or $error = 1; + + if ( $error ) { + diag "Wrong SQL query:". $tix->BuildSelectQuery; + $tix->GotoFirstItem; + while ( my $t = $tix->Next ) { + diag sprintf "%02d - %s", $t->id, $t->Subject; + } + } + } + } +} + +@data = ( + { }, + { CF => ['b', 'd'] }, + { CF => ['a', 'c'] }, +); +@tickets = add_tix_from_data(); +@test = ( + { Order => "CF.{$cf_name}" }, + { Order => "CF.$queue_name.{$cf_name}" }, +); +run_tests(); + +@data = ( + { CF => ['m', 'a'] }, + { CF => ['m'] }, + { CF => ['m', 'o'] }, +); +@tickets = add_tix_from_data(); +@test = ( + { Order => "CF.{$cf_name}", Query => "CF.{$cf_name} = 'm'" }, + { Order => "CF.$queue_name.{$cf_name}", Query => "CF.{$cf_name} = 'm'" }, +); +run_tests(); + diff --git a/rt/t/ticket/cfsort-freeform-single.t b/rt/t/ticket/cfsort-freeform-single.t new file mode 100644 index 000000000..f1f506bea --- /dev/null +++ b/rt/t/ticket/cfsort-freeform-single.t @@ -0,0 +1,191 @@ +#!/usr/bin/perl + +use RT::Test tests => 57; + +use strict; +use warnings; + +use RT::Tickets; +use RT::Queue; +use RT::CustomField; + +# Test Sorting by FreeformSingle custom field. + +diag "Create a queue to test with." if $ENV{TEST_VERBOSE}; +my $queue_name = "CFSortQueue-$$"; +my $queue; +{ + $queue = RT::Queue->new( $RT::SystemUser ); + my ($ret, $msg) = $queue->Create( + Name => $queue_name, + Description => 'queue for custom field sort testing' + ); + ok($ret, "$queue test queue creation. $msg"); +} + +# CFs for testing, later we create another one +my %CF; +my $cf_name; + +diag "create a CF\n" if $ENV{TEST_VERBOSE}; +{ + $cf_name = $CF{'CF'}{'name'} = "Order$$"; + $CF{'CF'}{'obj'} = RT::CustomField->new( $RT::SystemUser ); + my ($ret, $msg) = $CF{'CF'}{'obj'}->Create( + Name => $CF{'CF'}{'name'}, + Queue => $queue->id, + Type => 'FreeformSingle', + ); + ok($ret, "Custom Field $CF{'CF'}{'name'} created"); +} + +my ($total, @data, @tickets, @test) = (0, ()); + +sub add_tix_from_data { + my @res = (); + @data = sort { rand(100) <=> rand(100) } @data; + while (@data) { + my $t = RT::Ticket->new($RT::SystemUser); + my %args = %{ shift(@data) }; + + my $subject = '-'; + foreach my $e ( grep exists $CF{$_} && defined $CF{$_}, keys %args ) { + my @values = (); + if ( ref $args{ $e } ) { + @values = @{ delete $args{ $e } }; + } else { + @values = (delete $args{ $e }); + } + $args{ 'CustomField-'. $CF{ $e }{'obj'}->id } = \@values + if @values; + $subject = join(",", sort @values) || '-' + if $e eq 'CF'; + } + + my ( $id, undef $msg ) = $t->Create( + %args, + Queue => $queue->id, + Subject => $subject, + ); + ok( $id, "ticket created" ) or diag("error: $msg"); + push @res, $t; + $total++; + } + return @res; +} + +sub run_tests { + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + foreach my $test ( @test ) { + my $query = join " AND ", map "( $_ )", grep defined && length, + $query_prefix, $test->{'Query'}; + + foreach my $order (qw(ASC DESC)) { + my $error = 0; + my $tix = RT::Tickets->new( $RT::SystemUser ); + $tix->FromSQL( $query ); + $tix->OrderBy( FIELD => $test->{'Order'}, ORDER => $order ); + + ok($tix->Count, "found ticket(s)") + or $error = 1; + + my ($order_ok, $last) = (1, $order eq 'ASC'? '-': 'zzzzzz'); + my $last_id = $tix->Last->id; + while ( my $t = $tix->Next ) { + my $tmp; + next if $t->id == $last_id and $t->Subject eq "-"; # Nulls are allowed to come last, in Pg + + if ( $order eq 'ASC' ) { + $tmp = ((split( /,/, $last))[0] cmp (split( /,/, $t->Subject))[0]); + } else { + $tmp = -((split( /,/, $last))[-1] cmp (split( /,/, $t->Subject))[-1]); + } + if ( $tmp > 0 ) { + $order_ok = 0; last; + } + $last = $t->Subject; + } + + ok( $order_ok, "$order order of tickets is good" ) + or $error = 1; + + if ( $error ) { + diag "Wrong SQL query:". $tix->BuildSelectQuery; + $tix->GotoFirstItem; + while ( my $t = $tix->Next ) { + diag sprintf "%02d - %s", $t->id, $t->Subject; + } + } + } + } +} + +@data = ( + { }, + { CF => 'a' }, + { CF => 'b' }, +); +@tickets = add_tix_from_data(); +@test = ( + { Order => "CF.{$cf_name}" }, + { Order => "CF.$queue_name.{$cf_name}" }, +); +run_tests(); + +@data = ( + { }, + { CF => 'aa' }, + { CF => 'ab' }, +); +@tickets = add_tix_from_data(); +@test = ( + { Query => "CF.{$cf_name} LIKE 'a'", Order => "CF.{$cf_name}" }, + { Query => "CF.{$cf_name} LIKE 'a'", Order => "CF.$queue_name.{$cf_name}" }, +); +run_tests(); + +@data = ( + { Subject => '-', }, + { Subject => 'a', CF => 'a' }, + { Subject => 'b', CF => 'b' }, + { Subject => 'c', CF => 'c' }, +); +@tickets = add_tix_from_data(); +@test = ( + { Query => "CF.{$cf_name} != 'c'", Order => "CF.{$cf_name}" }, + { Query => "CF.{$cf_name} != 'c'", Order => "CF.$queue_name.{$cf_name}" }, +); +run_tests(); + + + +diag "create another CF\n" if $ENV{TEST_VERBOSE}; +{ + $CF{'AnotherCF'}{'name'} = "OrderAnother$$"; + $CF{'AnotherCF'}{'obj'} = RT::CustomField->new( $RT::SystemUser ); + my ($ret, $msg) = $CF{'AnotherCF'}{'obj'}->Create( + Name => $CF{'AnotherCF'}{'name'}, + Queue => $queue->id, + Type => 'FreeformSingle', + ); + ok($ret, "Custom Field $CF{'AnotherCF'}{'name'} created"); +} + +# test that order is not affect by other fields (had such problem) +@data = ( + { Subject => '-', }, + { Subject => 'a', CF => 'a', AnotherCF => 'za' }, + { Subject => 'b', CF => 'b', AnotherCF => 'ya' }, + { Subject => 'c', CF => 'c', AnotherCF => 'xa' }, +); +@tickets = add_tix_from_data(); +@test = ( + { Order => "CF.{$cf_name}" }, + { Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.{$cf_name} != 'c'", Order => "CF.{$cf_name}" }, + { Query => "CF.{$cf_name} != 'c'", Order => "CF.$queue_name.{$cf_name}" }, +); +run_tests(); + + + diff --git a/rt/t/ticket/deferred_owner.t b/rt/t/ticket/deferred_owner.t new file mode 100644 index 000000000..40172caf9 --- /dev/null +++ b/rt/t/ticket/deferred_owner.t @@ -0,0 +1,120 @@ + +use strict; +use warnings; + +use RT::Test tests => 18; +use_ok('RT'); +use_ok('RT::Ticket'); +use Test::Warn; + + +my $tester = RT::Test->load_or_create_user( + EmailAddress => 'tester@localhost', +); +ok $tester && $tester->id, 'loaded or created user'; + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok $queue && $queue->id, 'loaded or created queue'; + +my $owner_role_group = RT::Group->new( $RT::SystemUser ); +$owner_role_group->LoadQueueRoleGroup( Type => 'Owner', Queue => $queue->id ); +ok $owner_role_group->id, 'loaded owners role group of the queue'; + +diag "check that deffering owner doesn't regress" if $ENV{'TEST_VERBOSE'}; +{ + RT::Test->set_rights( + { Principal => $tester->PrincipalObj, + Right => [qw(SeeQueue ShowTicket CreateTicket OwnTicket)], + }, + { Principal => $owner_role_group->PrincipalObj, + Object => $queue, + Right => [qw(ModifyTicket)], + }, + ); + my $ticket = RT::Ticket->new( $tester ); + # tester is owner, owner has right to modify owned tickets, + # this right is required to set somebody as AdminCc + my ($tid, $txn_id, $msg) = $ticket->Create( + Queue => $queue->id, + Owner => $tester->id, + AdminCc => 'root@localhost', + ); + diag $msg if $msg && $ENV{'TEST_VERBOSE'}; + ok $tid, "created a ticket"; + is $ticket->Owner, $tester->id, 'correct owner'; + like $ticket->AdminCcAddresses, qr/root\@localhost/, 'root is there'; +} + +diag "check that previous trick doesn't work without sufficient rights" + if $ENV{'TEST_VERBOSE'}; +{ + RT::Test->set_rights( + { Principal => $tester->PrincipalObj, + Right => [qw(SeeQueue ShowTicket CreateTicket OwnTicket)], + }, + ); + my $ticket = RT::Ticket->new( $tester ); + # tester is owner, owner has right to modify owned tickets, + # this right is required to set somebody as AdminCc + my ($tid, $txn_id, $msg) = $ticket->Create( + Queue => $queue->id, + Owner => $tester->id, + AdminCc => 'root@localhost', + ); + diag $msg if $msg && $ENV{'TEST_VERBOSE'}; + ok $tid, "created a ticket"; + is $ticket->Owner, $tester->id, 'correct owner'; + unlike $ticket->AdminCcAddresses, qr/root\@localhost/, 'root is there'; +} + +diag "check that deffering owner really works" if $ENV{'TEST_VERBOSE'}; +{ + RT::Test->set_rights( + { Principal => $tester->PrincipalObj, + Right => [qw(SeeQueue ShowTicket CreateTicket)], + }, + { Principal => $queue->Cc->PrincipalObj, + Object => $queue, + Right => [qw(OwnTicket TakeTicket)], + }, + ); + my $ticket = RT::Ticket->new( $tester ); + # set tester as Cc, Cc role group has right to own and take tickets + my ($tid, $txn_id, $msg) = $ticket->Create( + Queue => $queue->id, + Owner => $tester->id, + Cc => 'tester@localhost', + ); + diag $msg if $msg && $ENV{'TEST_VERBOSE'}; + ok $tid, "created a ticket"; + like $ticket->CcAddresses, qr/tester\@localhost/, 'tester is in the cc list'; + is $ticket->Owner, $tester->id, 'tester is also owner'; +} + +diag "check that deffering doesn't work without correct rights" if $ENV{'TEST_VERBOSE'}; +{ + RT::Test->set_rights( + { Principal => $tester->PrincipalObj, + Right => [qw(SeeQueue ShowTicket CreateTicket)], + }, + ); + + my $ticket = RT::Ticket->new( $tester ); + # set tester as Cc, Cc role group has right to own and take tickets + my ($tid, $txn_id, $msg); + warning_like { + ($tid, $txn_id, $msg) = $ticket->Create( + Queue => $queue->id, + Owner => $tester->id, + Cc => 'tester@localhost', + ); + } qr/User .* was proposed as a ticket owner but has no rights to own tickets in General/; + + diag $msg if $msg && $ENV{'TEST_VERBOSE'}; + ok $tid, "created a ticket"; + like $ticket->CcAddresses, qr/tester\@localhost/, 'tester is in the cc list'; + isnt $ticket->Owner, $tester->id, 'tester is also owner'; +} + + + diff --git a/rt/t/ticket/link_search.t b/rt/t/ticket/link_search.t new file mode 100644 index 000000000..1bf7dc6dc --- /dev/null +++ b/rt/t/ticket/link_search.t @@ -0,0 +1,246 @@ +#!/usr/bin/perl -w + +use strict; +use RT; + +# Load the config file +use RT::Test tests => 63; + +#Connect to the database and get RT::SystemUser and RT::Nobody loaded + + +#Get the current user all loaded +my $CurrentUser = $RT::SystemUser; + +my $queue = new RT::Queue($CurrentUser); +$queue->Load('General') || Abort(loc("Queue could not be loaded.")); + +my $child_ticket = new RT::Ticket( $CurrentUser ); +my ($childid) = $child_ticket->Create( + Subject => 'test child', + Queue => $queue->Id, +); +ok($childid, "We created a child ticket"); + +my $parent_ticket = new RT::Ticket( $CurrentUser ); +my ($parentid) = $parent_ticket->Create( + Subject => 'test parent', + Children => [ $childid ], + Queue => $queue->Id, +); +ok($parentid, "We created a parent ticket"); + + +my $Collection = RT::Tickets->new($CurrentUser); +$Collection->LimitMemberOf( $parentid ); +is($Collection->Count,1, "We found only one result"); +ok($Collection->First); +is($Collection->First->id, $childid, "We found the collection of all children of $parentid with Limit"); + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL("MemberOf = $parentid"); +is($Collection->Count, 1, "We found only one result"); +ok($Collection->First); +is($Collection->First->id, $childid, "We found the collection of all children of $parentid with TicketSQL"); + + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->LimitHasMember ($childid); +is($Collection->Count,1, "We found only one result"); +ok($Collection->First); +is($Collection->First->id, $parentid, "We found the collection of all parents of $childid with Limit"); + + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL("HasMember = $childid"); +is($Collection->Count,1, "We found only one result"); +ok($Collection->First); +is($Collection->First->id, $parentid, "We found the collection of all parents of $childid with TicketSQL"); + + +# Now we find a collection of all the tickets which have no members. they should have no children. +$Collection = RT::Tickets->new($CurrentUser); +$Collection->LimitHasMember(''); +# must contain child; must not contain parent +my %has; +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( $has{$childid}, "The collection has our child - $childid"); +ok( !$has{$parentid}, "The collection doesn't have our parent - $parentid"); + + +# Now we find a collection of all the tickets which are not members of anything. they should have no parents. +$Collection = RT::Tickets->new($CurrentUser); +$Collection->LimitMemberOf(''); +# must contain parent; must not contain child +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok ($has{$parentid} , "The collection has our parent - $parentid"); +ok( !$has{$childid}, "The collection doesn't have our child - $childid"); + + +# Do it all over with TicketSQL +# + + + +# Now we find a collection of all the tickets which have no members. they should have no children. +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL ("HasMember IS NULL"); +# must contain parent; must not contain child +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( !$has{$parentid}, "The collection doesn't have our parent - $parentid"); +ok( $has{$childid}, "The collection has our child - $childid"); + + +# Now we find a collection of all the tickets which have no members. they should have no children. +# Alternate syntax +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL("HasMember = ''"); +# must contain parent; must not contain child +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( !$has{$parentid}, "The collection doesn't have our parent - $parentid"); +ok( $has{$childid}, "The collection has our child - $childid"); + + +# Now we find a collection of all the tickets which are not members of anything. they should have no parents. +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL("MemberOf IS NULL"); +# must not contain parent; must contain parent +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( $has{$parentid}, "The collection has our parent - $parentid"); +ok( !$has{$childid}, "The collection doesn't have our child - $childid"); + + +# Now we find a collection of all the tickets which are not members of anything. they should have no parents. +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL("MemberOf = ''"); +# must not contain parent; must contain parent +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( $has{$parentid}, "The collection has our parent - $parentid"); +ok( !$has{$childid}, "The collection doesn't have our child - $childid"); + + +# Now we find a collection of all the tickets which are not members of the parent ticket +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL("MemberOf != $parentid"); +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( $has{$parentid}, "The collection has our parent - $parentid"); +ok( !$has{$childid}, "The collection doesn't have our child - $childid"); + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->LimitMemberOf($parentid, OPERATOR => '!='); +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( $has{$parentid}, "The collection has our parent - $parentid"); +ok( !$has{$childid}, "The collection doesn't have our child - $childid"); + +my $grand_child_ticket = new RT::Ticket( $CurrentUser ); +my ($grand_childid) = $child_ticket->Create( + Subject => 'test child', + Queue => $queue->Id, + MemberOf => $childid, +); +ok($childid, "We created a grand child ticket"); + +my $unlinked_ticket = new RT::Ticket( $CurrentUser ); +my ($unlinked_id) = $child_ticket->Create( + Subject => 'test unlinked', + Queue => $queue->Id, +); +ok($unlinked_id, "We created a grand child ticket"); + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL( "LinkedTo = $childid" ); +is($Collection->Count,1, "We found only one result"); +ok($Collection->First); +is($Collection->First->id, $grand_childid, "We found all tickets linked to ticket #$childid"); + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL( "LinkedFrom = $childid" ); +is($Collection->Count,1, "We found only one result"); +ok($Collection->First); +is($Collection->First->id, $parentid, "We found all tickets linked from ticket #$childid"); + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL( "LinkedTo IS NULL" ); +ok($Collection->Count, "Result is set is not empty"); +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( $has{$parentid}, "parent is in collection"); +ok( $has{$unlinked_id}, "unlinked is in collection"); +ok( !$has{$childid}, "child is NOT in collection"); +ok( !$has{$grand_childid}, "grand child too is not in collection"); + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL( "LinkedTo IS NOT NULL" ); +ok($Collection->Count, "Result set is not empty"); +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( !$has{$parentid}, "The collection has no our parent - $parentid"); +ok( !$has{$unlinked_id}, "unlinked is not in collection"); +ok( $has{$childid}, "The collection have our child - $childid"); +ok( $has{$grand_childid}, "The collection have our grand child - $grand_childid"); + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL( "LinkedFrom IS NULL" ); +ok($Collection->Count, "Result is set is not empty"); +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( !$has{$parentid}, "parent is NOT in collection"); +ok( !$has{$childid}, "child is NOT in collection"); +ok( $has{$grand_childid}, "grand child is in collection"); +ok( $has{$unlinked_id}, "unlinked is in collection"); + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL( "LinkedFrom IS NOT NULL" ); +ok($Collection->Count, "Result set is not empty"); +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( $has{$parentid}, "The collection has our parent - $parentid"); +ok( $has{$childid}, "The collection have our child - $childid"); +ok( !$has{$grand_childid}, "The collection have no our grand child - $grand_childid"); +ok( !$has{$unlinked_id}, "unlinked is not in collection"); + +$Collection = RT::Tickets->new($CurrentUser); +$Collection->FromSQL( "Linked = $childid" ); +is($Collection->Count, 2, "We found two tickets: parent and child"); +%has = (); +while (my $t = $Collection->Next) { + ++$has{$t->id}; +} +ok( !$has{$childid}, "Ticket is not linked to itself"); +ok( $has{$parentid}, "The collection has our parent"); +ok( $has{$grand_childid}, "The collection have our child"); +ok( !$has{$unlinked_id}, "unlinked is not in collection"); + + +1; diff --git a/rt/t/ticket/linking.t b/rt/t/ticket/linking.t new file mode 100644 index 000000000..2ea3d58da --- /dev/null +++ b/rt/t/ticket/linking.t @@ -0,0 +1,385 @@ + +use strict; +use warnings; + +use RT::Test tests => '101'; +use_ok('RT'); +use_ok('RT::Ticket'); +use_ok('RT::ScripConditions'); +use_ok('RT::ScripActions'); +use_ok('RT::Template'); +use_ok('RT::Scrips'); +use_ok('RT::Scrip'); + + +use File::Temp qw/tempfile/; +my ($fh, $filename) = tempfile( UNLINK => 1, SUFFIX => '.rt'); +my $link_scrips_orig = RT->Config->Get( 'LinkTransactionsRun1Scrip' ); +RT->Config->Set( 'LinkTransactionsRun1Scrip', 1 ); + +my $link_acl_checks_orig = RT->Config->Get( 'StrictLinkACL' ); +RT->Config->Set( 'StrictLinkACL', 1); + +my $condition = RT::ScripCondition->new( $RT::SystemUser ); +$condition->Load('User Defined'); +ok($condition->id); +my $action = RT::ScripAction->new( $RT::SystemUser ); +$action->Load('User Defined'); +ok($action->id); +my $template = RT::Template->new( $RT::SystemUser ); +$template->Load('Blank'); +ok($template->id); + +my $q1 = RT::Queue->new($RT::SystemUser); +my ($id,$msg) = $q1->Create(Name => "LinkTest1.$$"); +ok ($id,$msg); +my $q2 = RT::Queue->new($RT::SystemUser); +($id,$msg) = $q2->Create(Name => "LinkTest2.$$"); +ok ($id,$msg); + +my $commit_code = <<END; +open my \$file, "<$filename" or die "couldn't open $filename"; +my \$data = <\$file>; +chomp \$data; +\$data += 0; +close \$file; +\$RT::Logger->debug("Data is \$data"); + +open \$file, ">$filename" or die "couldn't open $filename"; +if (\$self->TransactionObj->Type eq 'AddLink') { + \$RT::Logger->debug("AddLink"); + print \$file \$data+1, "\n"; +} +elsif (\$self->TransactionObj->Type eq 'DeleteLink') { + \$RT::Logger->debug("DeleteLink"); + print \$file \$data-1, "\n"; +} +else { + \$RT::Logger->error("THIS SHOULDN'T HAPPEN"); + print \$file "666\n"; +} +close \$file; +1; +END + +my $Scrips = RT::Scrips->new( $RT::SystemUser ); +$Scrips->UnLimit; +while ( my $Scrip = $Scrips->Next ) { + $Scrip->Delete if $Scrip->Description and $Scrip->Description =~ /Add or Delete Link \d+/; +} + + +my $scrip = RT::Scrip->new($RT::SystemUser); +($id,$msg) = $scrip->Create( Description => "Add or Delete Link $$", + ScripCondition => $condition->id, + ScripAction => $action->id, + Template => $template->id, + Stage => 'TransactionCreate', + Queue => 0, + CustomIsApplicableCode => '$self->TransactionObj->Type =~ /(Add|Delete)Link/;', + CustomPrepareCode => '1;', + CustomCommitCode => $commit_code, + ); +ok($id, "Scrip created"); + +my $u1 = RT::User->new($RT::SystemUser); +($id,$msg) = $u1->Create(Name => "LinkTestUser.$$"); +ok ($id,$msg); + +# grant ShowTicket right to allow count transactions +($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q1, Right => 'ShowTicket'); +ok ($id,$msg); +($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q2, Right => 'ShowTicket'); +ok ($id,$msg); +($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q1, Right => 'CreateTicket'); +ok ($id,$msg); + +my $creator = RT::CurrentUser->new($u1->id); + +diag('Create tickets without rights to link') if $ENV{'TEST_VERBOSE'}; +{ + # on q2 we have no rights, yet + my $parent = RT::Ticket->new( $RT::SystemUser ); + my ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id ); + ok($id,$msg); + my $child = RT::Ticket->new( $creator ); + ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id, MemberOf => $parent->id ); + ok($id,$msg); + $child->CurrentUser( $RT::SystemUser ); + is($child->_Links('Base')->Count, 0, 'link was not created, no permissions'); + is($child->_Links('Target')->Count, 0, 'link was not create, no permissions'); +} + +diag('Create tickets with rights checks on one end of a link') if $ENV{'TEST_VERBOSE'}; +{ + # on q2 we have no rights, but use checking one only on thing + RT->Config->Set( StrictLinkACL => 0 ); + my $parent = RT::Ticket->new( $RT::SystemUser ); + my ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id ); + ok($id,$msg); + my $child = RT::Ticket->new( $creator ); + ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id, MemberOf => $parent->id ); + ok($id,$msg); + $child->CurrentUser( $RT::SystemUser ); + is($child->_Links('Base')->Count, 1, 'link was created'); + is($child->_Links('Target')->Count, 0, 'link was created only one'); + # no scrip run on second ticket accroding to config option + is(link_count($filename), undef, "scrips ok"); + RT->Config->Set( StrictLinkACL => 1 ); +} + +($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q1, Right => 'ModifyTicket'); +ok ($id,$msg); + +diag('try to add link without rights') if $ENV{'TEST_VERBOSE'}; +{ + # on q2 we have no rights, yet + my $parent = RT::Ticket->new( $RT::SystemUser ); + my ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id ); + ok($id,$msg); + my $child = RT::Ticket->new( $creator ); + ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id ); + ok($id,$msg); + ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id); + ok(!$id, $msg); + is(link_count($filename), undef, "scrips ok"); + $child->CurrentUser( $RT::SystemUser ); + is($child->_Links('Base')->Count, 0, 'link was not created, no permissions'); + is($child->_Links('Target')->Count, 0, 'link was not create, no permissions'); +} + +diag('add link with rights only on base') if $ENV{'TEST_VERBOSE'}; +{ + # on q2 we have no rights, but use checking one only on thing + RT->Config->Set( StrictLinkACL => 0 ); + my $parent = RT::Ticket->new( $RT::SystemUser ); + my ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id ); + ok($id,$msg); + my $child = RT::Ticket->new( $creator ); + ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id ); + ok($id,$msg); + ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id); + ok($id, $msg); + is(link_count($filename), 1, "scrips ok"); + $child->CurrentUser( $RT::SystemUser ); + is($child->_Links('Base')->Count, 1, 'link was created'); + is($child->_Links('Target')->Count, 0, 'link was created only one'); + $child->CurrentUser( $creator ); + + # turn off feature and try to delete link, we should fail + RT->Config->Set( StrictLinkACL => 1 ); + ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id); + ok(!$id, $msg); + is(link_count($filename), 1, "scrips ok"); + $child->CurrentUser( $RT::SystemUser ); + $child->_Links('Base')->_DoCount; + is($child->_Links('Base')->Count, 1, 'link was not deleted'); + $child->CurrentUser( $creator ); + + # try to delete link, we should success as feature is active + RT->Config->Set( StrictLinkACL => 0 ); + ($id, $msg) = $child->DeleteLink(Type => 'MemberOf', Target => $parent->id); + ok($id, $msg); + is(link_count($filename), 0, "scrips ok"); + $child->CurrentUser( $RT::SystemUser ); + $child->_Links('Base')->_DoCount; + is($child->_Links('Base')->Count, 0, 'link was deleted'); + RT->Config->Set( StrictLinkACL => 1 ); +} + +my $tid; +my $ticket = RT::Ticket->new( $creator); +ok($ticket->isa('RT::Ticket')); +($id,$tid, $msg) = $ticket->Create(Subject => 'Link test 1', Queue => $q1->id); +ok ($id,$msg); + +diag('try link to itself') if $ENV{'TEST_VERBOSE'}; +{ + my ($id, $msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket->id); + ok(!$id, $msg); + is(link_count($filename), 0, "scrips ok"); +} + +my $ticket2 = RT::Ticket->new($RT::SystemUser); +($id, $tid, $msg) = $ticket2->Create(Subject => 'Link test 2', Queue => $q2->id); +ok ($id, $msg); +($id,$msg) =$ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id); +ok(!$id,$msg); +is(link_count($filename), 0, "scrips ok"); + +($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q2, Right => 'CreateTicket'); +ok ($id,$msg); +($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q2, Right => 'ModifyTicket'); +ok ($id,$msg); +($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id); +ok($id,$msg); +is(link_count($filename), 1, "scrips ok"); +($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => -1); +ok(!$id,$msg); +($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id); +ok($id,$msg); +is(link_count($filename), 1, "scrips ok"); + +my $transactions = $ticket2->Transactions; +$transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' ); +is( $transactions->Count, 1, "Transaction found in other ticket" ); +is( $transactions->First->Field , 'ReferredToBy'); +is( $transactions->First->NewValue , $ticket->URI ); + +($id,$msg) = $ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id); +ok($id,$msg); +is(link_count($filename), 0, "scrips ok"); +$transactions = $ticket2->Transactions; +$transactions->Limit( FIELD => 'Type', VALUE => 'DeleteLink' ); +is( $transactions->Count, 1, "Transaction found in other ticket" ); +is( $transactions->First->Field , 'ReferredToBy'); +is( $transactions->First->OldValue , $ticket->URI ); + +RT->Config->Set( LinkTransactionsRun1Scrip => 0 ); + +($id,$msg) =$ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id); +ok($id,$msg); +is(link_count($filename), 2, "scrips ok"); +($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id); +ok($id,$msg); +is(link_count($filename), 0, "scrips ok"); + +# tests for silent behaviour +($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id, Silent => 1); +ok($id,$msg); +is(link_count($filename), 0, "scrips ok"); +{ + my $transactions = $ticket->Transactions; + $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' ); + is( $transactions->Count, 2, "Still two txns on the base" ); + + $transactions = $ticket2->Transactions; + $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' ); + is( $transactions->Count, 2, "Still two txns on the target" ); + +} +($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id, Silent => 1); +ok($id,$msg); +is(link_count($filename), 0, "scrips ok"); + +($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id, SilentBase => 1); +ok($id,$msg); +is(link_count($filename), 1, "scrips ok"); +{ + my $transactions = $ticket->Transactions; + $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' ); + is( $transactions->Count, 2, "still five txn on the base" ); + + $transactions = $ticket2->Transactions; + $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' ); + is( $transactions->Count, 3, "+1 txn on the target" ); + +} +($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id, SilentBase => 1); +ok($id,$msg); +is(link_count($filename), 0, "scrips ok"); + +($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id, SilentTarget => 1); +ok($id,$msg); +is(link_count($filename), 1, "scrips ok"); +{ + my $transactions = $ticket->Transactions; + $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' ); + is( $transactions->Count, 3, "+1 txn on the base" ); + + $transactions = $ticket2->Transactions; + $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' ); + is( $transactions->Count, 3, "three txns on the target" ); +} +($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id, SilentTarget => 1); +ok($id,$msg); +is(link_count($filename), 0, "scrips ok"); + + +# restore +RT->Config->Set( LinkTransactionsRun1Scrip => $link_scrips_orig ); +RT->Config->Set( StrictLinkACL => $link_acl_checks_orig ); + +{ + my $Scrips = RT::Scrips->new( $RT::SystemUser ); + $Scrips->Limit( FIELD => 'Description', OPERATOR => 'STARTSWITH', VALUE => 'Add or Delete Link '); + while ( my $s = $Scrips->Next ) { $s->Delete }; +} + + +my $link = RT::Link->new( $RT::SystemUser ); +($id,$msg) = $link->Create( Base => $ticket->URI, Target => $ticket2->URI, Type => 'MyLinkType' ); +ok($id, $msg); +ok($link->LocalBase == $ticket->id, "LocalBase set correctly"); +ok($link->LocalTarget == $ticket2->id, "LocalTarget set correctly"); + +{ + no warnings 'once'; + *RT::NotTicket::Id = sub { return $$ }; + *RT::NotTicket::id = \&RT::NotTicket::Id; +} + +{ + package RT::URI::not_ticket; + use RT::URI::base; + use vars qw(@ISA); + @ISA = qw/RT::URI::base/; + sub IsLocal { 1; } + sub Object { return bless {}, 'RT::NotTicket'; } +} + +my $orig_getresolver = \&RT::URI::_GetResolver; +{ + no warnings 'redefine'; + *RT::URI::_GetResolver = sub { + my $self = shift; + my $scheme = shift; + + $scheme =~ s/(\.|-)/_/g; + my $resolver; + my $module = "RT::URI::$scheme"; + $resolver = $module->new($self->CurrentUser); + + if ($resolver) { + $self->{'resolver'} = $resolver; + } else { + $self->{'resolver'} = RT::URI::base->new($self->CurrentUser); + } + }; +} + +($id,$msg) = $link->Create( Base => "not_ticket::$RT::Organization/notticket/$$", Target => $ticket2->URI, Type => 'MyLinkType' ); +ok($id, $msg); +ok($link->LocalBase == 0, "LocalBase set correctly"); +ok($link->LocalTarget == $ticket2->id, "LocalTarget set correctly"); + +($id,$msg) = $link->Create( Target => "not_ticket::$RT::Organization/notticket/$$", Base => $ticket->URI, Type => 'MyLinkType' ); +ok($id, $msg); +ok($link->LocalTarget == 0, "LocalTarget set correctly"); +ok($link->LocalBase == $ticket->id, "LocalBase set correctly"); + +($id,$msg) = $link->Create( + Target => "not_ticket::$RT::Organization/notticket/1$$", + Base => "not_ticket::$RT::Organization/notticket/$$", + Type => 'MyLinkType' ); + +ok($id, $msg); +ok($link->LocalTarget == 0, "LocalTarget set correctly"); +ok($link->LocalBase == 0, "LocalBase set correctly"); + +# restore _GetResolver +{ + no warnings 'redefine'; + *RT::URI::_GetResolver = $orig_getresolver; +} + +sub link_count { + my $file = shift; + open my $fh, "<$file" or die "couldn't open $file"; + my $data = <$fh>; + close $fh; + + return undef unless $data; + chomp $data; + return $data + 0; +} diff --git a/rt/t/ticket/merge.t b/rt/t/ticket/merge.t new file mode 100644 index 000000000..a714cb6cc --- /dev/null +++ b/rt/t/ticket/merge.t @@ -0,0 +1,92 @@ +#!/usr/bin/perl + +use strict; +use warnings; + + +use RT; +use RT::Test tests => '17'; + + +# validate that when merging two tickets, the comments from both tickets +# are integrated into the new ticket +{ + my $queue = RT::Queue->new($RT::SystemUser); + my ($id,$msg) = $queue->Create(Name => 'MergeTest-'.rand(25)); + ok ($id,$msg); + + my $t1 = RT::Ticket->new($RT::SystemUser); + my ($tid,$transid, $t1msg) =$t1->Create ( Queue => $queue->Name, Subject => 'Merge test. orig'); + ok ($tid, $t1msg); + ($id, $msg) = $t1->Comment(Content => 'This is a Comment on the original'); + ok($id,$msg); + + my $txns = $t1->Transactions; + my $Comments = 0; + while (my $txn = $txns->Next) { + $Comments++ if ($txn->Type eq 'Comment'); + } + is($Comments,1, "our first ticket has only one Comment"); + + my $t2 = RT::Ticket->new($RT::SystemUser); + my ($t2id,$t2transid, $t2msg) =$t2->Create ( Queue => $queue->Name, Subject => 'Merge test. duplicate'); + ok ($t2id, $t2msg); + + + + ($id, $msg) = $t2->Comment(Content => 'This is a commet on the duplicate'); + ok($id,$msg); + + + $txns = $t2->Transactions; + $Comments = 0; + while (my $txn = $txns->Next) { + $Comments++ if ($txn->Type eq 'Comment'); + } + is($Comments,1, "our second ticket has only one Comment"); + + ($id, $msg) = $t1->Comment(Content => 'This is a second Comment on the original'); + ok($id,$msg); + + $txns = $t1->Transactions; + $Comments = 0; + while (my $txn = $txns->Next) { + $Comments++ if ($txn->Type eq 'Comment'); + } + is($Comments,2, "our first ticket now has two Comments"); + + ($id,$msg) = $t2->MergeInto($t1->id); + + ok($id,$msg); + $txns = $t1->Transactions; + $Comments = 0; + while (my $txn = $txns->Next) { + $Comments++ if ($txn->Type eq 'Comment'); + } + is($Comments,3, "our first ticket now has three Comments - we merged safely"); +} + +# when you try to merge duplicate links on postgres, eveyrything goes to hell due to referential integrity constraints. +{ + my $t = RT::Ticket->new($RT::SystemUser); + $t->Create(Subject => 'Main', Queue => 'general'); + + ok ($t->id); + my $t2 = RT::Ticket->new($RT::SystemUser); + $t2->Create(Subject => 'Second', Queue => 'general'); + ok ($t2->id); + + my $t3 = RT::Ticket->new($RT::SystemUser); + $t3->Create(Subject => 'Third', Queue => 'general'); + + ok ($t3->id); + + my ($id,$val); + ($id,$val) = $t->AddLink(Type => 'DependsOn', Target => $t3->id); + ok($id,$val); + ($id,$val) = $t2->AddLink(Type => 'DependsOn', Target => $t3->id); + ok($id,$val); + + ($id,$val) = $t->MergeInto($t2->id); + ok($id,$val); +} diff --git a/rt/t/ticket/quicksearch.t b/rt/t/ticket/quicksearch.t new file mode 100644 index 000000000..9ab9f21e4 --- /dev/null +++ b/rt/t/ticket/quicksearch.t @@ -0,0 +1,41 @@ + +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT::Test tests => 10; +use_ok('RT'); + + +my $q = RT::Queue->new($RT::SystemUser); +my $queue = 'SearchTests-'.$$; +$q->Create(Name => $queue); +ok ($q->id, "Created the queue"); + +my $t1 = RT::Ticket->new($RT::SystemUser); +my ( $id, undef, $msg ) = $t1->Create( + Queue => $q->id, + Subject => 'SearchTest1', + Requestor => ['search2@example.com'], +); +ok( $id, $msg ); + +use_ok("RT::Search::Googleish"); + +my $active_statuses = join( " OR ", map "Status = '$_'", RT::Queue->ActiveStatusArray()); + +my $tickets = RT::Tickets->new($RT::SystemUser); +my $quick = RT::Search::Googleish->new(Argument => "", + TicketsObj => $tickets); +my @tests = ( + "General new open root" => "( Owner = 'root' ) AND ( Queue = 'General' ) AND ( Status = 'new' OR Status = 'open' )", + "fulltext:jesse" => "( Content LIKE 'jesse' ) AND ( $active_statuses )", + $queue => "( Queue = '$queue' ) AND ( $active_statuses )", + "root $queue" => "( Owner = 'root' ) AND ( Queue = '$queue' ) AND ( $active_statuses )", + "notauser $queue" => "( Queue = '$queue' ) AND ( $active_statuses ) AND ( Subject LIKE 'notauser' )", + "notauser $queue root" => "( Owner = 'root' ) AND ( Queue = '$queue' ) AND ( $active_statuses ) AND ( Subject LIKE 'notauser' )"); + +while (my ($from, $to) = splice @tests, 0, 2) { + is($quick->QueryToSQL($from), $to, "<$from> -> <$to>"); +} diff --git a/rt/t/ticket/requestor-order.t b/rt/t/ticket/requestor-order.t new file mode 100644 index 000000000..4539fbdc6 --- /dev/null +++ b/rt/t/ticket/requestor-order.t @@ -0,0 +1,142 @@ +#!/usr/bin/perl -w +use strict; use warnings; + +use RT::Test tests => 58; +use_ok('RT'); + +use RT::Ticket; + +my $q = RT::Queue->new($RT::SystemUser); +my $queue = 'SearchTests-'.rand(200); +$q->Create(Name => $queue); + +my @requestors = ( ('bravo@example.com') x 6, ('alpha@example.com') x 6, + ('delta@example.com') x 6, ('charlie@example.com') x 6, + (undef) x 6); +my @subjects = ("first test", "second test", "third test", "fourth test", "fifth test") x 6; +while (@requestors) { + my $t = RT::Ticket->new($RT::SystemUser); + my ( $id, undef $msg ) = $t->Create( + Queue => $q->id, + Subject => shift @subjects, + Requestor => [ shift @requestors ] + ); + ok( $id, $msg ); +} + +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue'"); + is($tix->Count, 30, "found thirty tickets"); +} + +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND requestor = 'alpha\@example.com'"); + $tix->OrderByCols({ FIELD => "Subject" }); + my @subjects; + while (my $t = $tix->Next) { push @subjects, $t->Subject; } + is(@subjects, 6, "found six tickets"); + is_deeply( \@subjects, [ sort @subjects ], "Subjects are sorted"); +} + +sub check_emails_order +{ + my ($tix,$count,$order) = (@_); + my @mails; + while (my $t = $tix->Next) { push @mails, $t->RequestorAddresses; } + is(@mails, $count, "found $count tickets for ". $tix->Query); + my @required_order; + if( $order =~ /asc/i ) { + @required_order = sort { $a? ($b? ($a cmp $b) : -1) : 1} @mails; + } else { + @required_order = sort { $a? ($b? ($b cmp $a) : -1) : 1} @mails; + } + foreach( reverse splice @mails ) { + if( $_ ) { unshift @mails, $_ } + else { push @mails, $_ } + } + is_deeply( \@mails, \@required_order, "Addresses are sorted"); +} + +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND subject = 'first test' AND Requestor.EmailAddress LIKE 'example.com'"); + $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" }); + check_emails_order($tix, 5, 'ASC'); + $tix->OrderByCols({ FIELD => "Requestor.EmailAddress", ORDER => 'DESC' }); + check_emails_order($tix, 5, 'DESC'); +} + +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND Subject = 'first test'"); + $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" }); + check_emails_order($tix, 6, 'ASC'); + $tix->OrderByCols({ FIELD => "Requestor.EmailAddress", ORDER => 'DESC' }); + check_emails_order($tix, 6, 'DESC'); +} + + +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND Subject = 'first test'"); + $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" }); + check_emails_order($tix, 6, 'ASC'); + $tix->OrderByCols({ FIELD => "Requestor.EmailAddress", ORDER => 'DESC' }); + check_emails_order($tix, 6, 'DESC'); +} + +{ + # create ticket with group as member of the requestors group + my $t = RT::Ticket->new($RT::SystemUser); + my ( $id, $msg ) = $t->Create( + Queue => $q->id, + Subject => "first test", + Requestor => 'badaboom@example.com', + ); + ok( $id, "ticket created" ) or diag( "error: $msg" ); + + my $g = RT::Group->new($RT::SystemUser); + + my ($gid); + ($gid, $msg) = $g->CreateUserDefinedGroup(Name => '20-sort-by-requestor.t-'.rand(200)); + ok($gid, "created group") or diag("error: $msg"); + + ($id, $msg) = $t->Requestors->AddMember( $gid ); + ok($id, "added group to requestors group") or diag("error: $msg"); +} + + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND Subject = 'first test'"); + + $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" }); + check_emails_order($tix, 7, 'ASC'); + + $tix->OrderByCols({ FIELD => "Requestor.EmailAddress", ORDER => 'DESC' }); + check_emails_order($tix, 7, 'DESC'); + +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue'"); + $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" }); + $tix->RowsPerPage(30); + my @mails; + while (my $t = $tix->Next) { push @mails, $t->RequestorAddresses; } + is(@mails, 30, "found thirty tickets"); + is_deeply( [grep {$_} @mails], [ sort grep {$_} @mails ], "Paging works (exclude nulls, which are db-dependant)"); +} + +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue'"); + $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" }); + $tix->RowsPerPage(30); + my @mails; + while (my $t = $tix->Next) { push @mails, $t->RequestorAddresses; } + is(@mails, 30, "found thirty tickets"); + is_deeply( [grep {$_} @mails], [ sort grep {$_} @mails ], "Paging works (exclude nulls, which are db-dependant)"); +} +RT::Test->mailsent_ok(25); + +# vim:ft=perl: diff --git a/rt/t/ticket/scrips_batch.t b/rt/t/ticket/scrips_batch.t new file mode 100644 index 000000000..f558d3bd4 --- /dev/null +++ b/rt/t/ticket/scrips_batch.t @@ -0,0 +1,100 @@ + +use strict; +use warnings; + +use RT::Test tests => '19'; +use_ok('RT'); +use_ok('RT::Ticket'); + +my $queue = RT::Test->load_or_create_queue( Name => 'Regression' ); +ok $queue && $queue->id, 'loaded or created queue'; + +RT->Config->Set( UseTransactionBatch => 1 ); + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; + +my $sid; +{ + $m->follow_link_ok( { text => 'Configuration' } ); + $m->follow_link_ok( { text => 'Queues' } ); + $m->follow_link_ok( { text => $queue->Name } ); + $m->follow_link_ok( { text => 'Scrips' } ); + $m->follow_link_ok( { text => 'New scrip' } ); + $m->form_number(3); + $m->field('Scrip-new-Description' => 'test'); + $m->select('Scrip-new-ScripCondition' => 'On Transaction'); + $m->select('Scrip-new-ScripAction' => 'User Defined'); + $m->select('Scrip-new-Template' => 'Global template: Blank'); + $m->select('Scrip-new-Stage' => 'TransactionBatch'); + $m->field('Scrip-new-CustomPrepareCode' => 'return 1;'); + $m->field('Scrip-new-CustomCommitCode' => 'return 1;'); + $m->submit; + $m->content_like( qr/Scrip Created/ ); + + ($sid) = ($m->content =~ /Scrip\s*#(\d+)/); + + my $form = $m->form_number(3); + is $m->value("Scrip-$sid-Description"), 'test', 'correct description'; + is value_name($form, "Scrip-$sid-ScripCondition"), 'On Transaction', 'correct condition'; + is value_name($form, "Scrip-$sid-ScripAction"), 'User Defined', 'correct action'; + is value_name($form, "Scrip-$sid-Template"), 'Global template: Blank', 'correct template'; + is value_name($form, "Scrip-$sid-Stage"), 'TransactionBatch', 'correct stage'; + + use File::Temp qw(tempfile); + my ($tmp_fh, $tmp_fn) = tempfile(); + + my $code = <<END; +open my \$fh, '>', '$tmp_fn' or die "Couldn't open '$tmp_fn':\$!"; + +my \$batch = \$self->TicketObj->TransactionBatch; +unless ( \$batch && \@\$batch ) { + print \$fh "no batch\n"; + return 1; +} +foreach my \$txn ( \@\$batch ) { + print \$fh \$txn->Type ."\n"; +} +return 1; +END + + $m->field( "Scrip-$sid-CustomCommitCode" => $code ); + $m->submit; + + $m->goto_create_ticket( $queue ); + $m->form_number(3); + $m->submit; + + is_deeply parse_handle($tmp_fh), ['Create'], 'Create'; + + $m->follow_link_ok( { text => 'Resolve' } ); + $m->form_number(3); + $m->field( "UpdateContent" => 'resolve it' ); + $m->click('SubmitTicket'); + + is_deeply parse_handle($tmp_fh), ['Comment', 'Status'], 'Comment + Resolve'; +} + +sub value_name { + my $form = shift; + my $field = shift; + + my $input = $form->find_input( $field ); + + my @names = $input->value_names; + my @values = $input->possible_values; + for ( my $i = 0; $i < @values; $i++ ) { + return $names[ $i ] if $values[ $i ] eq $input->value; + } + return undef; +} + +sub parse_handle { + my $fh = shift; + seek $fh, 0, 0; + my @lines = <$fh>; + foreach ( @lines ) { s/^\s+//gms; s/\s+$//gms } + truncate $fh, 0; + return \@lines; +} + diff --git a/rt/t/ticket/search.t b/rt/t/ticket/search.t new file mode 100644 index 000000000..9cec4f753 --- /dev/null +++ b/rt/t/ticket/search.t @@ -0,0 +1,278 @@ +#!/opt/perl/bin/perl -w + +# tests relating to searching. Especially around custom fields, and +# corner cases. + +use strict; +use warnings; + +use RT::Test tests => 43; + +# setup the queue + +my $q = RT::Queue->new($RT::SystemUser); +my $queue = 'SearchTests-'.$$; +$q->Create(Name => $queue); +ok ($q->id, "Created the queue"); + + +# and setup the CFs +# we believe the Type shouldn't matter. + +my $cf = RT::CustomField->new($RT::SystemUser); +$cf->Create(Name => 'SearchTest', Type => 'Freeform', MaxValues => 0, Queue => $q->id); +ok($cf->id, "Created the SearchTest CF"); +my $cflabel = "CustomField-".$cf->id; + +my $cf2 = RT::CustomField->new($RT::SystemUser); +$cf2->Create(Name => 'SearchTest2', Type => 'Freeform', MaxValues => 0, Queue => $q->id); +ok($cf2->id, "Created the SearchTest2 CF"); +my $cflabel2 = "CustomField-".$cf2->id; + +my $cf3 = RT::CustomField->new($RT::SystemUser); +$cf3->Create(Name => 'SearchTest3', Type => 'Freeform', MaxValues => 0, Queue => $q->id); +ok($cf3->id, "Created the SearchTest3 CF"); +my $cflabel3 = "CustomField-".$cf3->id; + + +# There was a bug involving a missing join to ObjectCustomFields that +# caused spurious results on negative searches if another custom field +# with the same name existed on a different queue. Hence, we make +# duplicate CFs on a different queue here +my $dup = RT::Queue->new($RT::SystemUser); +$dup->Create(Name => $queue . "-Copy"); +ok ($dup->id, "Created the duplicate queue"); +my $dupcf = RT::CustomField->new($RT::SystemUser); +$dupcf->Create(Name => 'SearchTest', Type => 'Freeform', MaxValues => 0, Queue => $dup->id); +ok($dupcf->id, "Created the duplicate SearchTest CF"); +$dupcf = RT::CustomField->new($RT::SystemUser); +$dupcf->Create(Name => 'SearchTest2', Type => 'Freeform', MaxValues => 0, Queue => $dup->id); +ok($dupcf->id, "Created the SearchTest2 CF"); +$dupcf = RT::CustomField->new($RT::SystemUser); +$dupcf->Create(Name => 'SearchTest3', Type => 'Freeform', MaxValues => 0, Queue => $dup->id); +ok($dupcf->id, "Created the SearchTest3 CF"); + + +# setup some tickets +# we'll need a small pile of them, to test various combinations and nulls. +# there's probably a way to think harder and do this with fewer + + +my $t1 = RT::Ticket->new($RT::SystemUser); +my ( $id, undef $msg ) = $t1->Create( + Queue => $q->id, + Subject => 'SearchTest1', + Requestor => ['search1@example.com'], + $cflabel => 'foo1', + $cflabel2 => 'bar1', + $cflabel3 => 'qux1', +); +ok( $id, $msg ); + + +my $t2 = RT::Ticket->new($RT::SystemUser); +( $id, undef, $msg ) = $t2->Create( + Queue => $q->id, + Subject => 'SearchTest2', + Requestor => ['search2@example.com'], +# $cflabel => 'foo2', + $cflabel2 => 'bar2', + $cflabel3 => 'qux2', +); +ok( $id, $msg ); + +my $t3 = RT::Ticket->new($RT::SystemUser); +( $id, undef, $msg ) = $t3->Create( + Queue => $q->id, + Subject => 'SearchTest3', + Requestor => ['search3@example.com'], + $cflabel => 'foo3', +# $cflabel2 => 'bar3', + $cflabel3 => 'qux3', +); +ok( $id, $msg ); + +my $t4 = RT::Ticket->new($RT::SystemUser); +( $id, undef, $msg ) = $t4->Create( + Queue => $q->id, + Subject => 'SearchTest4', + Requestor => ['search4@example.com'], + $cflabel => 'foo4', + $cflabel2 => 'bar4', +# $cflabel3 => 'qux4', +); +ok( $id, $msg ); + +my $t5 = RT::Ticket->new($RT::SystemUser); +( $id, undef, $msg ) = $t5->Create( + Queue => $q->id, +# Subject => 'SearchTest5', + Requestor => ['search5@example.com'], + $cflabel => 'foo5', + $cflabel2 => 'bar5', + $cflabel3 => 'qux5', +); +ok( $id, $msg ); + +my $t6 = RT::Ticket->new($RT::SystemUser); +( $id, undef, $msg ) = $t6->Create( + Queue => $q->id, + Subject => 'SearchTest6', +# Requestor => ['search6@example.com'], + $cflabel => 'foo6', + $cflabel2 => 'bar6', + $cflabel3 => 'qux6', +); +ok( $id, $msg ); + +my $t7 = RT::Ticket->new($RT::SystemUser); +( $id, undef, $msg ) = $t7->Create( + Queue => $q->id, + Subject => 'SearchTest7', + Requestor => ['search7@example.com'], +# $cflabel => 'foo7', +# $cflabel2 => 'bar7', + $cflabel3 => 'qux7', +); +ok( $id, $msg ); + +# we have tickets. start searching +my $tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue'"); +is($tix->Count, 7, "found all the tickets") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + + +# very simple searches. both CF and normal + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND CF.SearchTest = 'foo1'"); +is($tix->Count, 1, "matched identical subject") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND CF.SearchTest LIKE 'foo1'"); +is($tix->Count, 1, "matched LIKE subject") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND CF.SearchTest = 'foo'"); +is($tix->Count, 0, "IS a regexp match") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND CF.SearchTest LIKE 'foo'"); +is($tix->Count, 5, "matched LIKE subject") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND CF.SearchTest IS NULL"); +is($tix->Count, 2, "IS null CF") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND Requestors LIKE 'search1'"); +is($tix->Count, 1, "LIKE requestor") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND Requestors = 'search1\@example.com'"); +is($tix->Count, 1, "IS requestor") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND Requestors LIKE 'search'"); +is($tix->Count, 6, "LIKE requestor") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND Requestors IS NULL"); +is($tix->Count, 1, "Search for no requestor") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND Subject = 'SearchTest1'"); +is($tix->Count, 1, "IS subject") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND Subject LIKE 'SearchTest1'"); +is($tix->Count, 1, "LIKE subject") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND Subject = ''"); +is($tix->Count, 1, "found one ticket") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND Subject LIKE 'SearchTest'"); +is($tix->Count, 6, "found two ticket") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND Subject LIKE 'qwerty'"); +is($tix->Count, 0, "found zero ticket") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + + + + +# various combinations + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.SearchTest LIKE 'foo' AND CF.SearchTest2 LIKE 'bar1'"); +is($tix->Count, 1, "LIKE cf and LIKE cf"); + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.SearchTest = 'foo1' AND CF.SearchTest2 = 'bar1'"); +is($tix->Count, 1, "is cf and is cf"); + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.SearchTest = 'foo' AND CF.SearchTest2 LIKE 'bar1'"); +is($tix->Count, 0, "is cf and like cf"); + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.SearchTest LIKE 'foo' AND CF.SearchTest2 LIKE 'bar' AND CF.SearchTest3 LIKE 'qux'"); +is($tix->Count, 3, "like cf and like cf and like cf"); + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.SearchTest LIKE 'foo' AND CF.SearchTest2 LIKE 'bar' AND CF.SearchTest3 LIKE 'qux6'"); +is($tix->Count, 1, "like cf and like cf and is cf"); + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.SearchTest LIKE 'foo' AND Subject LIKE 'SearchTest'"); +is($tix->Count, 4, "like cf and like subject"); + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.SearchTest IS NULL AND CF.SearchTest2 = 'bar2'"); +is($tix->Count, 1, "null cf and is cf"); + + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND CF.SearchTest IS NULL AND CF.SearchTest2 IS NULL"); +is($tix->Count, 1, "null cf and null cf"); + +# tests with the same CF listed twice + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.{SearchTest} = 'foo1'"); +is($tix->Count, 1, "is cf.{name} format"); + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.SearchTest = 'foo1' OR CF.SearchTest = 'foo3'"); +is($tix->Count, 2, "is cf1 or is cf1"); + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.SearchTest = 'foo1' OR CF.SearchTest IS NULL"); +is($tix->Count, 3, "is cf1 or null cf1"); + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("(CF.SearchTest = 'foo1' OR CF.SearchTest = 'foo3') AND (CF.SearchTest2 = 'bar1' OR CF.SearchTest2 = 'bar2')"); +is($tix->Count, 1, "(is cf1 or is cf1) and (is cf2 or is cf2)"); + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("CF.SearchTest = 'foo1' OR CF.SearchTest = 'foo3' OR CF.SearchTest2 = 'bar1' OR CF.SearchTest2 = 'bar2'"); +is($tix->Count, 3, "is cf1 or is cf1 or is cf2 or is cf2"); + diff --git a/rt/t/ticket/search_by_cf_freeform_multiple.t b/rt/t/ticket/search_by_cf_freeform_multiple.t new file mode 100644 index 000000000..be5130651 --- /dev/null +++ b/rt/t/ticket/search_by_cf_freeform_multiple.t @@ -0,0 +1,153 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT::Test tests => 105; +use RT::Ticket; + +my $q = RT::Test->load_or_create_queue( Name => 'Regression' ); +ok $q && $q->id, 'loaded or created queue'; +my $queue = $q->Name; + +diag "create a CF\n" if $ENV{TEST_VERBOSE}; +my ($cf_name, $cf_id, $cf) = ("Test", 0, undef); +{ + $cf = RT::CustomField->new( $RT::SystemUser ); + my ($ret, $msg) = $cf->Create( + Name => $cf_name, + Queue => $q->id, + Type => 'FreeformMultiple', + ); + ok($ret, "Custom Field Order created"); + $cf_id = $cf->id; +} + +my ($total, @data, @tickets, %test) = (0, ()); + +sub add_tix_from_data { + my @res = (); + while (@data) { + my %args = %{ shift(@data) }; + my @cf_value = $args{'Subject'} ne '-'? (split /(?=.)/, $args{'Subject'}) : (); + diag "vals: ". join ', ', @cf_value; + my $t = RT::Ticket->new($RT::SystemUser); + my ( $id, undef $msg ) = $t->Create( + Queue => $q->id, + %args, + "CustomField-$cf_id" => \@cf_value, + ); + ok( $id, "ticket created" ) or diag("error: $msg"); + + my $got = join ',', sort do { + my $vals = $t->CustomFieldValues( $cf_name ); + my @tmp; + while (my $v = $vals->Next ) { push @tmp, $v->Content } + @tmp; + }; + + is( $got, join( ',', sort @cf_value), 'correct CF values' ); + push @res, $t; + $total++; + } + return @res; +} + +sub run_tests { + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + foreach my $key ( sort keys %test ) { + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL( "( $query_prefix ) AND ( $key )" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %{ $test{$key} }; + is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1; + + my $good_tickets = ($tix->Count == $count); + while ( my $ticket = $tix->Next ) { + next if $test{$key}->{ $ticket->Subject }; + diag $ticket->Subject ." ticket has been found when it's not expected"; + $good_tickets = 0; + } + ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1; + + diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error; + } +} + +@data = ( + { Subject => '-' }, + { Subject => 'x' }, + { Subject => 'y' }, + { Subject => 'z' }, + { Subject => 'xy' }, + { Subject => 'xz' }, + { Subject => 'yz' }, +); +%test = ( + "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, + "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, + "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, + "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, + + "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, + "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, + "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, + "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, + + "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + + "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 }, + "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 }, + "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 }, + "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 }, + + "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 }, + "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 }, + "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 }, + "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, +); +@tickets = add_tix_from_data(); +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue'"); + is($tix->Count, $total, "found $total tickets"); +} +run_tests(); + +exit 0; diff --git a/rt/t/ticket/search_by_cf_freeform_single.t b/rt/t/ticket/search_by_cf_freeform_single.t new file mode 100644 index 000000000..d5ff7ec0d --- /dev/null +++ b/rt/t/ticket/search_by_cf_freeform_single.t @@ -0,0 +1,142 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT::Test tests => 99; +use RT::Ticket; + +my $q = RT::Test->load_or_create_queue( Name => 'Regression' ); +ok $q && $q->id, 'loaded or created queue'; +my $queue = $q->Name; + +diag "create a CF\n" if $ENV{TEST_VERBOSE}; +my ($cf_name, $cf_id, $cf) = ("Test", 0, undef); +{ + $cf = RT::CustomField->new( $RT::SystemUser ); + my ($ret, $msg) = $cf->Create( + Name => $cf_name, + Queue => $q->id, + Type => 'FreeformSingle', + ); + ok($ret, "Custom Field Order created"); + $cf_id = $cf->id; +} + +my ($total, @data, @tickets, %test) = (0, ()); + +sub add_tix_from_data { + my @res = (); + while (@data) { + my %args = %{ shift(@data) }; + my $cf_value = $args{'Subject'} ne '-'? $args{'Subject'} : undef; + my $t = RT::Ticket->new($RT::SystemUser); + my ( $id, undef $msg ) = $t->Create( + Queue => $q->id, + %args, + "CustomField-$cf_id" => $cf_value, + ); + ok( $id, "ticket created" ) or diag("error: $msg"); + is( $t->FirstCustomFieldValue( $cf_name ), $cf_value, 'correct value' ); + push @res, $t; + $total++; + } + return @res; +} + +sub run_tests { + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + foreach my $key ( sort keys %test ) { + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL( "( $query_prefix ) AND ( $key )" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %{ $test{$key} }; + is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1; + + my $good_tickets = ($tix->Count == $count); + while ( my $ticket = $tix->Next ) { + next if $test{$key}->{ $ticket->Subject }; + diag $ticket->Subject ." ticket has been found when it's not expected"; + $good_tickets = 0; + } + ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1; + + diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error; + } +} + +@data = ( + { Subject => '-' }, + { Subject => 'x' }, + { Subject => 'y' }, + { Subject => 'z' }, +); +%test = ( + "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 }, + "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 }, + "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 }, + "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 }, + + "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, + "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, + "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, + "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, + + "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 }, + "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 }, + "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 }, + "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 }, + + "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 }, + "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 }, + "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 }, + "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 }, + + "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 }, + "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 }, + "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 }, + "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, + +); +@tickets = add_tix_from_data(); +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue'"); + is($tix->Count, $total, "found $total tickets"); +} +run_tests(); + +exit 0; diff --git a/rt/t/ticket/search_by_links.t b/rt/t/ticket/search_by_links.t new file mode 100644 index 000000000..a8e955c8b --- /dev/null +++ b/rt/t/ticket/search_by_links.t @@ -0,0 +1,132 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT::Test tests => 80; +use RT::Ticket; + +my $q = RT::Test->load_or_create_queue( Name => 'Regression' ); +ok $q && $q->id, 'loaded or created queue'; + +my ($total, @data, @tickets, %test) = (0, ()); + +sub add_tix_from_data { + my @res = (); + while (@data) { + my $t = RT::Ticket->new($RT::SystemUser); + my %args = %{ shift(@data) }; + $args{$_} = $res[ $args{$_} ]->id foreach grep $args{$_}, keys %RT::Ticket::LINKTYPEMAP; + my ( $id, undef $msg ) = $t->Create( + Queue => $q->id, + %args, + ); + ok( $id, "ticket created" ) or diag("error: $msg"); + push @res, $t; + $total++; + } + return @res; +} + +sub run_tests { + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + foreach my $key ( sort keys %test ) { + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL( "( $query_prefix ) AND ( $key )" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %{ $test{$key} }; + is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1; + + my $good_tickets = 1; + while ( my $ticket = $tix->Next ) { + next if $test{$key}->{ $ticket->Subject }; + diag $ticket->Subject ." ticket has been found when it's not expected"; + $good_tickets = 0; + } + ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1; + + diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error; + } +} + +# simple set with "no links", "parent and child" +@data = ( + { Subject => '-', }, + { Subject => 'p', }, + { Subject => 'c', MemberOf => -1 }, +); +@tickets = add_tix_from_data(); +%test = ( + 'Linked IS NOT NULL' => { '-' => 0, c => 1, p => 1 }, + 'Linked IS NULL' => { '-' => 1, c => 0, p => 0 }, + 'LinkedTo IS NOT NULL' => { '-' => 0, c => 1, p => 0 }, + 'LinkedTo IS NULL' => { '-' => 1, c => 0, p => 1 }, + 'LinkedFrom IS NOT NULL' => { '-' => 0, c => 0, p => 1 }, + 'LinkedFrom IS NULL' => { '-' => 1, c => 1, p => 0 }, + + 'HasMember IS NOT NULL' => { '-' => 0, c => 0, p => 1 }, + 'HasMember IS NULL' => { '-' => 1, c => 1, p => 0 }, + 'MemberOf IS NOT NULL' => { '-' => 0, c => 1, p => 0 }, + 'MemberOf IS NULL' => { '-' => 1, c => 0, p => 1 }, + + 'RefersTo IS NOT NULL' => { '-' => 0, c => 0, p => 0 }, + 'RefersTo IS NULL' => { '-' => 1, c => 1, p => 1 }, + + 'Linked = '. $tickets[0]->id => { '-' => 0, c => 0, p => 0 }, + 'Linked != '. $tickets[0]->id => { '-' => 1, c => 1, p => 1 }, + + 'MemberOf = '. $tickets[1]->id => { '-' => 0, c => 1, p => 0 }, + 'MemberOf != '. $tickets[1]->id => { '-' => 1, c => 0, p => 1 }, +); +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '". $q->id ."'"); + is($tix->Count, $total, "found $total tickets"); +} +run_tests(); + +# another set with tests of combinations searches +@data = ( + { Subject => '-', }, + { Subject => 'p', }, + { Subject => 'rp', RefersTo => -1 }, + { Subject => 'c', MemberOf => -2 }, + { Subject => 'rc1', RefersTo => -1 }, + { Subject => 'rc2', RefersTo => -2 }, +); +@tickets = add_tix_from_data(); +my $pid = $tickets[1]->id; +%test = ( + 'RefersTo IS NOT NULL' => { '-' => 0, c => 0, p => 0, rp => 1, rc1 => 1, rc2 => 1 }, + 'RefersTo IS NULL' => { '-' => 1, c => 1, p => 1, rp => 0, rc1 => 0, rc2 => 0 }, + + 'RefersTo IS NOT NULL AND MemberOf IS NOT NULL' => { '-' => 0, c => 0, p => 0, rp => 0, rc1 => 0, rc2 => 0 }, + 'RefersTo IS NOT NULL AND MemberOf IS NULL' => { '-' => 0, c => 0, p => 0, rp => 1, rc1 => 1, rc2 => 1 }, + 'RefersTo IS NULL AND MemberOf IS NOT NULL' => { '-' => 0, c => 1, p => 0, rp => 0, rc1 => 0, rc2 => 0 }, + 'RefersTo IS NULL AND MemberOf IS NULL' => { '-' => 1, c => 0, p => 1, rp => 0, rc1 => 0, rc2 => 0 }, + + 'RefersTo IS NOT NULL OR MemberOf IS NOT NULL' => { '-' => 0, c => 1, p => 0, rp => 1, rc1 => 1, rc2 => 1 }, + 'RefersTo IS NOT NULL OR MemberOf IS NULL' => { '-' => 1, c => 0, p => 1, rp => 1, rc1 => 1, rc2 => 1 }, + 'RefersTo IS NULL OR MemberOf IS NOT NULL' => { '-' => 1, c => 1, p => 1, rp => 0, rc1 => 0, rc2 => 0 }, + 'RefersTo IS NULL OR MemberOf IS NULL' => { '-' => 1, c => 1, p => 1, rp => 1, rc1 => 1, rc2 => 1 }, + + "RefersTo = $pid AND MemberOf = $pid" => { '-' => 0, c => 0, p => 0, rp => 0, rc1 => 0, rc2 => 0 }, + "RefersTo = $pid AND MemberOf != $pid" => { '-' => 0, c => 0, p => 0, rp => 1, rc1 => 0, rc2 => 0 }, + "RefersTo != $pid AND MemberOf = $pid" => { '-' => 0, c => 1, p => 0, rp => 0, rc1 => 0, rc2 => 0 }, + "RefersTo != $pid AND MemberOf != $pid" => { '-' => 1, c => 0, p => 1, rp => 0, rc1 => 1, rc2 => 1 }, + + "RefersTo = $pid OR MemberOf = $pid" => { '-' => 0, c => 1, p => 0, rp => 1, rc1 => 0, rc2 => 0 }, + "RefersTo = $pid OR MemberOf != $pid" => { '-' => 1, c => 0, p => 1, rp => 1, rc1 => 1, rc2 => 1 }, + "RefersTo != $pid OR MemberOf = $pid" => { '-' => 1, c => 1, p => 1, rp => 0, rc1 => 1, rc2 => 1 }, + "RefersTo != $pid OR MemberOf != $pid" => { '-' => 1, c => 1, p => 1, rp => 1, rc1 => 1, rc2 => 1 }, +); +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '". $q->id ."'"); + is($tix->Count, $total, "found $total tickets"); +} +run_tests(); + diff --git a/rt/t/ticket/search_by_txn.t b/rt/t/ticket/search_by_txn.t new file mode 100644 index 000000000..1be6916ef --- /dev/null +++ b/rt/t/ticket/search_by_txn.t @@ -0,0 +1,35 @@ +#!/usr/bin/perl + +use warnings; +use strict; + + +BEGIN{ $ENV{'TZ'} = 'GMT'}; + +use RT::Test tests => 10; + +my $SUBJECT = "Search test - ".$$; + +use_ok('RT::Tickets'); +my $tix = RT::Tickets->new($RT::SystemUser); +can_ok($tix, 'FromSQL'); +$tix->FromSQL('Updated = "2005-08-05" AND Subject = "$SUBJECT"'); + +ok(! $tix->Count, "Searching for tickets updated on a random date finds nothing" . $tix->Count); + +my $ticket = RT::Ticket->new($RT::SystemUser); +$ticket->Create(Queue => 'General', Subject => $SUBJECT); +ok ($ticket->id, "We created a ticket"); +my ($id, $txnid, $txnobj) = $ticket->Comment( Content => 'A comment that happend on 2004-01-01'); + +isa_ok($txnobj, 'RT::Transaction'); + +ok($txnobj->CreatedObj->ISO); +my ( $sid,$smsg) = $txnobj->__Set(Field => 'Created', Value => '2005-08-05 20:00:56'); +ok($sid,$smsg); +is($txnobj->Created,'2005-08-05 20:00:56'); +is($txnobj->CreatedObj->ISO,'2005-08-05 20:00:56'); + +$tix->FromSQL(qq{Updated = "2005-08-05" AND Subject = "$SUBJECT"}); +is( $tix->Count, 1); + diff --git a/rt/t/ticket/search_by_watcher.t b/rt/t/ticket/search_by_watcher.t new file mode 100644 index 000000000..9d94432d2 --- /dev/null +++ b/rt/t/ticket/search_by_watcher.t @@ -0,0 +1,280 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT::Test tests => 119; +use RT::Ticket; + +my $q = RT::Test->load_or_create_queue( Name => 'Regression' ); +ok $q && $q->id, 'loaded or created queue'; +my $queue = $q->Name; + +my ($total, @data, @tickets, %test) = (0, ()); + +sub add_tix_from_data { + my @res = (); + while (@data) { + my $t = RT::Ticket->new($RT::SystemUser); + my ( $id, undef $msg ) = $t->Create( + Queue => $q->id, + %{ shift(@data) }, + ); + ok( $id, "ticket created" ) or diag("error: $msg"); + push @res, $t; + $total++; + } + return @res; +} + +sub run_tests { + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + foreach my $key ( sort keys %test ) { + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL( "( $query_prefix ) AND ( $key )" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %{ $test{$key} }; + is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1; + + my $good_tickets = ($tix->Count == $count); + while ( my $ticket = $tix->Next ) { + next if $test{$key}->{ $ticket->Subject }; + diag $ticket->Subject ." ticket has been found when it's not expected"; + $good_tickets = 0; + } + ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1; + + diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error; + } +} + +@data = ( + { Subject => 'xy', Requestor => ['x@example.com', 'y@example.com'] }, + { Subject => 'x', Requestor => 'x@example.com' }, + { Subject => 'y', Requestor => 'y@example.com' }, + { Subject => '-', }, + { Subject => 'z', Requestor => 'z@example.com' }, +); +%test = ( + 'Requestor = "x@example.com"' => { xy => 1, x => 1, y => 0, '-' => 0, z => 0 }, + 'Requestor != "x@example.com"' => { xy => 0, x => 0, y => 1, '-' => 1, z => 1 }, + + 'Requestor = "y@example.com"' => { xy => 1, x => 0, y => 1, '-' => 0, z => 0 }, + 'Requestor != "y@example.com"' => { xy => 0, x => 1, y => 0, '-' => 1, z => 1 }, + + 'Requestor LIKE "@example.com"' => { xy => 1, x => 1, y => 1, '-' => 0, z => 1 }, + 'Requestor NOT LIKE "@example.com"' => { xy => 0, x => 0, y => 0, '-' => 1, z => 0 }, + + 'Requestor IS NULL' => { xy => 0, x => 0, y => 0, '-' => 1, z => 0 }, + 'Requestor IS NOT NULL' => { xy => 1, x => 1, y => 1, '-' => 0, z => 1 }, + +# this test is a todo, we run it later +# 'Requestor = "x@example.com" AND Requestor = "y@example.com"' => { xy => 1, x => 0, y => 0, '-' => 0, z => 0 }, + 'Requestor = "x@example.com" OR Requestor = "y@example.com"' => { xy => 1, x => 1, y => 1, '-' => 0, z => 0 }, + + 'Requestor != "x@example.com" AND Requestor != "y@example.com"' => { xy => 0, x => 0, y => 0, '-' => 1, z => 1 }, + 'Requestor != "x@example.com" OR Requestor != "y@example.com"' => { xy => 0, x => 1, y => 1, '-' => 1, z => 1 }, + + 'Requestor = "x@example.com" AND Requestor != "y@example.com"' => { xy => 0, x => 1, y => 0, '-' => 0, z => 0 }, + 'Requestor = "x@example.com" OR Requestor != "y@example.com"' => { xy => 1, x => 1, y => 0, '-' => 1, z => 1 }, + + 'Requestor != "x@example.com" AND Requestor = "y@example.com"' => { xy => 0, x => 0, y => 1, '-' => 0, z => 0 }, + 'Requestor != "x@example.com" OR Requestor = "y@example.com"' => { xy => 1, x => 0, y => 1, '-' => 1, z => 1 }, +); +@tickets = add_tix_from_data(); +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue'"); + is($tix->Count, $total, "found $total tickets"); +} +run_tests(); + +# mixing searches by watchers with other conditions +# http://rt3.fsck.com/Ticket/Display.html?id=9322 +%test = ( + 'Subject LIKE "x" AND Requestor = "y@example.com"' => + { xy => 1, x => 0, y => 0, '-' => 0, z => 0 }, + 'Subject NOT LIKE "x" AND Requestor = "y@example.com"' => + { xy => 0, x => 0, y => 1, '-' => 0, z => 0 }, + 'Subject LIKE "x" AND Requestor != "y@example.com"' => + { xy => 0, x => 1, y => 0, '-' => 0, z => 0 }, + 'Subject NOT LIKE "x" AND Requestor != "y@example.com"' => + { xy => 0, x => 0, y => 0, '-' => 1, z => 1 }, + + 'Subject LIKE "x" OR Requestor = "y@example.com"' => + { xy => 1, x => 1, y => 1, '-' => 0, z => 0 }, + 'Subject NOT LIKE "x" OR Requestor = "y@example.com"' => + { xy => 1, x => 0, y => 1, '-' => 1, z => 1 }, + 'Subject LIKE "x" OR Requestor != "y@example.com"' => + { xy => 1, x => 1, y => 0, '-' => 1, z => 1 }, + 'Subject NOT LIKE "x" OR Requestor != "y@example.com"' => + { xy => 0, x => 1, y => 1, '-' => 1, z => 1 }, + +# group of cases when user doesn't exist in DB at all + 'Subject LIKE "x" AND Requestor = "not-exist@example.com"' => + { xy => 0, x => 0, y => 0, '-' => 0, z => 0 }, + 'Subject NOT LIKE "x" AND Requestor = "not-exist@example.com"' => + { xy => 0, x => 0, y => 0, '-' => 0, z => 0 }, + 'Subject LIKE "x" AND Requestor != "not-exist@example.com"' => + { xy => 1, x => 1, y => 0, '-' => 0, z => 0 }, + 'Subject NOT LIKE "x" AND Requestor != "not-exist@example.com"' => + { xy => 0, x => 0, y => 1, '-' => 1, z => 1 }, +# 'Subject LIKE "x" OR Requestor = "not-exist@example.com"' => +# { xy => 1, x => 1, y => 0, '-' => 0, z => 0 }, +# 'Subject NOT LIKE "x" OR Requestor = "not-exist@example.com"' => +# { xy => 0, x => 0, y => 1, '-' => 1, z => 1 }, + 'Subject LIKE "x" OR Requestor != "not-exist@example.com"' => + { xy => 1, x => 1, y => 1, '-' => 1, z => 1 }, + 'Subject NOT LIKE "x" OR Requestor != "not-exist@example.com"' => + { xy => 1, x => 1, y => 1, '-' => 1, z => 1 }, + + 'Subject LIKE "z" AND (Requestor = "x@example.com" OR Requestor = "y@example.com")' => + { xy => 0, x => 0, y => 0, '-' => 0, z => 0 }, + 'Subject NOT LIKE "z" AND (Requestor = "x@example.com" OR Requestor = "y@example.com")' => + { xy => 1, x => 1, y => 1, '-' => 0, z => 0 }, + 'Subject LIKE "z" OR (Requestor = "x@example.com" OR Requestor = "y@example.com")' => + { xy => 1, x => 1, y => 1, '-' => 0, z => 1 }, + 'Subject NOT LIKE "z" OR (Requestor = "x@example.com" OR Requestor = "y@example.com")' => + { xy => 1, x => 1, y => 1, '-' => 1, z => 0 }, +); +run_tests(); + +TODO: { + local $TODO = "we can't generate this query yet"; + %test = ( + 'Requestor = "x@example.com" AND Requestor = "y@example.com"' + => { xy => 1, x => 0, y => 0, '-' => 0, z => 0 }, + 'Subject LIKE "x" OR Requestor = "not-exist@example.com"' => + { xy => 1, x => 1, y => 0, '-' => 0, z => 0 }, + 'Subject NOT LIKE "x" OR Requestor = "not-exist@example.com"' => + { xy => 0, x => 0, y => 1, '-' => 1, z => 1 }, + ); + run_tests(); +} + +@data = ( + { Subject => 'xy', Cc => ['x@example.com'], Requestor => [ 'y@example.com' ] }, + { Subject => 'x-', Cc => ['x@example.com'], Requestor => [] }, + { Subject => '-y', Cc => [], Requestor => [ 'y@example.com' ] }, + { Subject => '-', }, + { Subject => 'zz', Cc => ['z@example.com'], Requestor => [ 'z@example.com' ] }, + { Subject => 'z-', Cc => ['z@example.com'], Requestor => [] }, + { Subject => '-z', Cc => [], Requestor => [ 'z@example.com' ] }, +); +%test = ( + 'Cc = "x@example.com" AND Requestor = "y@example.com"' => + { xy => 1, 'x-' => 0, '-y' => 0, '-' => 0, zz => 0, 'z-' => 0, '-z' => 0 }, + 'Cc = "x@example.com" OR Requestor = "y@example.com"' => + { xy => 1, 'x-' => 1, '-y' => 1, '-' => 0, zz => 0, 'z-' => 0, '-z' => 0 }, + + 'Cc != "x@example.com" AND Requestor = "y@example.com"' => + { xy => 0, 'x-' => 0, '-y' => 1, '-' => 0, zz => 0, 'z-' => 0, '-z' => 0 }, + 'Cc != "x@example.com" OR Requestor = "y@example.com"' => + { xy => 1, 'x-' => 0, '-y' => 1, '-' => 1, zz => 1, 'z-' => 1, '-z' => 1 }, + + 'Cc IS NULL AND Requestor = "y@example.com"' => + { xy => 0, 'x-' => 0, '-y' => 1, '-' => 0, zz => 0, 'z-' => 0, '-z' => 0 }, + 'Cc IS NULL OR Requestor = "y@example.com"' => + { xy => 1, 'x-' => 0, '-y' => 1, '-' => 1, zz => 0, 'z-' => 0, '-z' => 1 }, + + 'Cc IS NOT NULL AND Requestor = "y@example.com"' => + { xy => 1, 'x-' => 0, '-y' => 0, '-' => 0, zz => 0, 'z-' => 0, '-z' => 0 }, + 'Cc IS NOT NULL OR Requestor = "y@example.com"' => + { xy => 1, 'x-' => 1, '-y' => 1, '-' => 0, zz => 1, 'z-' => 1, '-z' => 0 }, +); +@tickets = add_tix_from_data(); +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue'"); + is($tix->Count, $total, "found $total tickets"); +} +run_tests(); + + +# owner is special watcher because reference is duplicated in two places, +# owner was an ENUM field now it's WATCHERFIELD, but should support old +# style ENUM searches for backward compatibility +my $nobody = RT::Nobody(); +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND Owner = '". $nobody->id ."'"); + ok($tix->Count, "found ticket(s)"); +} +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND Owner = '". $nobody->Name ."'"); + ok($tix->Count, "found ticket(s)"); +} +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND Owner != '". $nobody->id ."'"); + is($tix->Count, 0, "found ticket(s)"); +} +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND Owner != '". $nobody->Name ."'"); + is($tix->Count, 0, "found ticket(s)"); +} + +{ + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND Owner.Name LIKE 'nob'"); + ok($tix->Count, "found ticket(s)"); +} + +{ + # create ticket and force type to not a 'ticket' value + # bug #6898@rt3.fsck.com + # and http://marc.theaimsgroup.com/?l=rt-devel&m=112662934627236&w=2 + @data = ( { Subject => 'not a ticket' } ); + my($t) = add_tix_from_data(); + $t->_Set( Field => 'Type', + Value => 'not a ticket', + CheckACL => 0, + RecordTransaction => 0, + ); + $total--; + + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND Owner = 'Nobody'"); + is($tix->Count, $total, "found ticket(s)"); +} + +{ + my $everyone = RT::Group->new( $RT::SystemUser ); + $everyone->LoadSystemInternalGroup('Everyone'); + ok($everyone->id, "loaded 'everyone' group"); + my($id, $msg) = $everyone->PrincipalObj->GrantRight( Right => 'OwnTicket', + Object => $q + ); + ok($id, "granted OwnTicket right to Everyone on '$queue'") or diag("error: $msg"); + + my $u = RT::User->new( $RT::SystemUser ); + $u->LoadOrCreateByEmail('alpha@example.com'); + ok($u->id, "loaded user"); + @data = ( { Subject => '4', Owner => $u->id } ); + my($t) = add_tix_from_data(); + is( $t->Owner, $u->id, "created ticket with custom owner" ); + my $u_alpha_id = $u->id; + + $u = RT::User->new( $RT::SystemUser ); + $u->LoadOrCreateByEmail('bravo@example.com'); + ok($u->id, "loaded user"); + @data = ( { Subject => '5', Owner => $u->id } ); + ($t) = add_tix_from_data(); + is( $t->Owner, $u->id, "created ticket with custom owner" ); + my $u_bravo_id = $u->id; + + my $tix = RT::Tickets->new($RT::SystemUser); + $tix->FromSQL("Queue = '$queue' AND + ( Owner = '$u_alpha_id' OR + Owner = '$u_bravo_id' )" + ); + is($tix->Count, 2, "found ticket(s)"); +} + + +exit(0) diff --git a/rt/t/ticket/search_long_cf_values.t b/rt/t/ticket/search_long_cf_values.t new file mode 100644 index 000000000..f9cc7b5a2 --- /dev/null +++ b/rt/t/ticket/search_long_cf_values.t @@ -0,0 +1,79 @@ +#!/opt/perl/bin/perl -w + +# tests relating to searching. Especially around custom fields with long values +# (> 255 chars) + +use strict; +use warnings; + +use RT::Test tests => 10; + +# setup the queue + +my $q = RT::Queue->new($RT::SystemUser); +my $queue = 'SearchTests-'.$$; +$q->Create(Name => $queue); +ok ($q->id, "Created the queue"); + + +# setup the CF +my $cf = RT::CustomField->new($RT::SystemUser); +$cf->Create(Name => 'SearchTest', Type => 'Freeform', MaxValues => 0, Queue => $q->id); +ok($cf->id, "Created the SearchTest CF"); +my $cflabel = "CustomField-".$cf->id; + +# setup some tickets +my $t1 = RT::Ticket->new($RT::SystemUser); +my ( $id, undef $msg ) = $t1->Create( + Queue => $q->id, + Subject => 'SearchTest1', + Requestor => ['search@example.com'], + $cflabel => 'foo', +); +ok( $id, $msg ); + + +my $t2 = RT::Ticket->new($RT::SystemUser); +( $id, undef, $msg ) = $t2->Create( + Queue => $q->id, + Subject => 'SearchTest2', + Requestor => ['searchlong@example.com'], + $cflabel => 'bar' x 150, +); +ok( $id, $msg ); + +my $t3 = RT::Ticket->new($RT::SystemUser); +( $id, undef, $msg ) = $t3->Create( + Queue => $q->id, + Subject => 'SearchTest3', + Requestor => ['searchlong@example.com'], + $cflabel => 'bar', +); +ok( $id, $msg ); + +# we have tickets. start searching +my $tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND CF.SearchTest LIKE 'foo'"); +is($tix->Count, 1, "matched short string foo") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND CF.SearchTest LIKE 'bar'"); +is($tix->Count, 2, "matched long+short string bar") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND ( CF.SearchTest LIKE 'foo' OR CF.SearchTest LIKE 'bar' )"); +is($tix->Count, 3, "matched short string foo or long+short string bar") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND CF.SearchTest NOT LIKE 'foo' AND CF.SearchTest LIKE 'bar'"); +is($tix->Count, 2, "not matched short string foo and matched long+short string bar") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + +$tix = RT::Tickets->new($RT::SystemUser); +$tix->FromSQL("Queue = '$queue' AND CF.SearchTest LIKE 'foo' AND CF.SearchTest NOT LIKE 'bar'"); +is($tix->Count, 1, "matched short string foo and not matched long+short string bar") + or diag "wrong results from SQL:\n". $tix->BuildSelectCountQuery; + diff --git a/rt/t/ticket/sort-by-custom-ownership.t b/rt/t/ticket/sort-by-custom-ownership.t new file mode 100644 index 000000000..9739c5aec --- /dev/null +++ b/rt/t/ticket/sort-by-custom-ownership.t @@ -0,0 +1,103 @@ +#!/usr/bin/perl + +use RT; +use RT::Test tests => 7; + + +use strict; +use warnings; + +use RT::Tickets; +use RT::Queue; +use RT::CustomField; + +my($ret,$msg); + +# Test Paw Sort + + + +# ---- Create a queue to test with. +my $queue = "PAWSortQueue-$$"; +my $queue_obj = RT::Queue->new($RT::SystemUser); +($ret, $msg) = $queue_obj->Create(Name => $queue, + Description => 'queue for custom field sort testing'); +ok($ret, "$queue test queue creation. $msg"); + + +# ---- Create some users + +my $me = RT::User->new($RT::SystemUser); +($ret, $msg) = $me->Create(Name => "Me$$", EmailAddress => $$.'create-me-1@example.com'); +($ret, $msg) = $me->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'OwnTicket'); +($ret, $msg) = $me->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'SeeQueue'); +($ret, $msg) = $me->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'ShowTicket'); +my $you = RT::User->new($RT::SystemUser); +($ret, $msg) = $you->Create(Name => "You$$", EmailAddress => $$.'create-you-1@example.com'); +($ret, $msg) = $you->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'OwnTicket'); +($ret, $msg) = $you->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'SeeQueue'); +($ret, $msg) = $you->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'ShowTicket'); + +my $nobody = RT::User->new($RT::SystemUser); +$nobody->Load('nobody'); + + +# ----- Create some tickets to test with. Assign them some values to +# make it easy to sort with. + +my @tickets = ( + [qw[1 10], $me], + [qw[2 20], $me], + [qw[3 20], $you], + [qw[4 30], $you], + [qw[5 5], $nobody], + [qw[6 55], $nobody], + ); +for (@tickets) { + my $t = RT::Ticket->new($RT::SystemUser); + $t->Create( Queue => $queue_obj->Id, + Subject => $_->[0], + Owner => $_->[2]->Id, + Priority => $_->[1], + ); +} + +sub check_order { + my ($tx, @order) = @_; + my @results; + while (my $t = $tx->Next) { + push @results, $t->Subject; + } + my $results = join (" ",@results); + my $order = join(" ",@order); + is( $results, $order ); +} + + +# The real tests start here + +my $cme = new RT::CurrentUser( $me ); +my $metx = new RT::Tickets( $cme ); +# Make sure we can sort in both directions on a queue specific field. +$metx->FromSQL(qq[queue="$queue"] ); +$metx->OrderBy( FIELD => "Custom.Ownership", ORDER => 'ASC' ); +is($metx->Count,6); +check_order( $metx, qw[2 1 6 5 4 3]); + +$metx->OrderBy( FIELD => "Custom.Ownership", ORDER => 'DESC' ); +is($metx->Count,6); +check_order( $metx, reverse qw[2 1 6 5 4 3]); + + + +my $cyou = new RT::CurrentUser( $you ); +my $youtx = new RT::Tickets( $cyou ); +# Make sure we can sort in both directions on a queue specific field. +$youtx->FromSQL(qq[queue="$queue"] ); +$youtx->OrderBy( FIELD => "Custom.Ownership", ORDER => 'ASC' ); +is($youtx->Count,6); +check_order( $youtx, qw[4 3 6 5 2 1]); + +__END__ + + diff --git a/rt/t/ticket/sort-by-queue.t b/rt/t/ticket/sort-by-queue.t new file mode 100644 index 000000000..df6e1ad0f --- /dev/null +++ b/rt/t/ticket/sort-by-queue.t @@ -0,0 +1,100 @@ +#!/usr/bin/perl + +use RT::Test tests => 8; + +use strict; +use warnings; + +use RT::Tickets; +use RT::Queue; +use RT::CustomField; + +######################################################### +# Test sorting by Queue, we sort by its name +######################################################### + + +diag "Create queues to test with." if $ENV{TEST_VERBOSE}; +my @qids; +my @queues; +# create them in reverse order to avoid false positives +foreach my $name ( qw(sort-by-queue-Z sort-by-queue-A) ) { + my $queue = RT::Queue->new( $RT::SystemUser ); + my ($ret, $msg) = $queue->Create( + Name => $name ."-$$", + Description => 'queue to test sorting by queue' + ); + ok($ret, "test queue creation. $msg"); + push @queues, $queue; + push @qids, $queue->id; +} + +my ($total, @data, @tickets, @test) = (0, ()); + +sub add_tix_from_data { + my @res = (); + @data = sort { rand(100) <=> rand(100) } @data; + while (@data) { + my $t = RT::Ticket->new($RT::SystemUser); + my %args = %{ shift(@data) }; + my ( $id, undef, $msg ) = $t->Create( %args ); + ok( $id, "ticket created" ) or diag("error: $msg"); + push @res, $t; + $total++; + } + return @res; +} + +sub run_tests { + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + foreach my $test ( @test ) { + my $query = join " AND ", map "( $_ )", grep defined && length, + $query_prefix, $test->{'Query'}; + + foreach my $order (qw(ASC DESC)) { + my $error = 0; + my $tix = RT::Tickets->new( $RT::SystemUser ); + $tix->FromSQL( $query ); + $tix->OrderBy( FIELD => $test->{'Order'}, ORDER => $order ); + + ok($tix->Count, "found ticket(s)") + or $error = 1; + + my ($order_ok, $last) = (1, $order eq 'ASC'? '-': 'zzzzzz'); + while ( my $t = $tix->Next ) { + my $tmp; + if ( $order eq 'ASC' ) { + $tmp = ((split( /,/, $last))[0] cmp (split( /,/, $t->Subject))[0]); + } else { + $tmp = -((split( /,/, $last))[-1] cmp (split( /,/, $t->Subject))[-1]); + } + if ( $tmp > 0 ) { + $order_ok = 0; last; + } + $last = $t->Subject; + } + + ok( $order_ok, "$order order of tickets is good" ) + or $error = 1; + + if ( $error ) { + diag "Wrong SQL query:". $tix->BuildSelectQuery; + $tix->GotoFirstItem; + while ( my $t = $tix->Next ) { + diag sprintf "%02d - %s", $t->id, $t->Subject; + } + } + } + } +} + +@data = ( + { Queue => $qids[0], Subject => 'z' }, + { Queue => $qids[1], Subject => 'a' }, +); +@tickets = add_tix_from_data(); +@test = ( + { Order => "Queue" }, +); +run_tests(); + diff --git a/rt/t/ticket/sort-by-user.t b/rt/t/ticket/sort-by-user.t new file mode 100644 index 000000000..f9b1602f1 --- /dev/null +++ b/rt/t/ticket/sort-by-user.t @@ -0,0 +1,152 @@ +#!/usr/bin/perl + +use RT::Test tests => 32; + +use strict; +use warnings; + +use RT::Tickets; +use RT::Queue; +use RT::CustomField; + +######################################################### +# Test sorting by Owner, Creator and LastUpdatedBy +# we sort by user name +######################################################### + +diag "Create a queue to test with." if $ENV{TEST_VERBOSE}; +my $queue_name = "OwnerSortQueue$$"; +my $queue; +{ + $queue = RT::Queue->new( $RT::SystemUser ); + my ($ret, $msg) = $queue->Create( + Name => $queue_name, + Description => 'queue for custom field sort testing' + ); + ok($ret, "$queue test queue creation. $msg"); +} + +my @uids; +my @users; +# create them in reverse order to avoid false positives +foreach my $u (qw(Z A)) { + my $name = $u ."-user-to-test-ordering-$$"; + my $user = RT::User->new( $RT::SystemUser ); + my ($uid) = $user->Create( + Name => $name, + Privileged => 1, + ); + ok $uid, "created user #$uid"; + + my ($status, $msg) = $user->PrincipalObj->GrantRight( Right => 'OwnTicket', Object => $queue ); + ok $status, "granted right"; + ($status, $msg) = $user->PrincipalObj->GrantRight( Right => 'CreateTicket', Object => $queue ); + ok $status, "granted right"; + + push @users, $user; + push @uids, $user->id; +} + +my ($total, @data, @tickets, @test) = (0, ()); + +sub add_tix_from_data { + my @res = (); + @data = sort { rand(100) <=> rand(100) } @data; + while (@data) { + my $t = RT::Ticket->new($RT::SystemUser); + my %args = %{ shift(@data) }; + + my ( $id, undef, $msg ) = $t->Create( %args, Queue => $queue->id ); + if ( $args{'Owner'} ) { + is $t->Owner, $args{'Owner'}, "owner is correct"; + } + if ( $args{'Creator'} ) { + is $t->Creator, $args{'Creator'}, "creator is correct"; + } + # hackish, but simpler + if ( $args{'LastUpdatedBy'} ) { + $t->__Set( Field => 'LastUpdatedBy', Value => $args{'LastUpdatedBy'} ); + } + ok( $id, "ticket created" ) or diag("error: $msg"); + push @res, $t; + $total++; + } + return @res; +} + +sub run_tests { + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + foreach my $test ( @test ) { + my $query = join " AND ", map "( $_ )", grep defined && length, + $query_prefix, $test->{'Query'}; + + foreach my $order (qw(ASC DESC)) { + my $error = 0; + my $tix = RT::Tickets->new( $RT::SystemUser ); + $tix->FromSQL( $query ); + $tix->OrderBy( FIELD => $test->{'Order'}, ORDER => $order ); + + ok($tix->Count, "found ticket(s)") + or $error = 1; + + my ($order_ok, $last) = (1, $order eq 'ASC'? '-': 'zzzzzz'); + while ( my $t = $tix->Next ) { + my $tmp; + if ( $order eq 'ASC' ) { + $tmp = ((split( /,/, $last))[0] cmp (split( /,/, $t->Subject))[0]); + } else { + $tmp = -((split( /,/, $last))[-1] cmp (split( /,/, $t->Subject))[-1]); + } + if ( $tmp > 0 ) { + $order_ok = 0; last; + } + $last = $t->Subject; + } + + ok( $order_ok, "$order order of tickets is good" ) + or $error = 1; + + if ( $error ) { + diag "Wrong SQL query:". $tix->BuildSelectQuery; + $tix->GotoFirstItem; + while ( my $t = $tix->Next ) { + diag sprintf "%02d - %s", $t->id, $t->Subject; + } + } + } + } +} + +@data = ( + { Subject => 'Nobody' }, + { Subject => 'Z', Owner => $uids[0] }, + { Subject => 'A', Owner => $uids[1] }, +); +@tickets = add_tix_from_data(); +@test = ( + { Order => "Owner" }, +); +run_tests(); + +@data = ( + { Subject => 'RT' }, + { Subject => 'Z', Creator => $uids[0] }, + { Subject => 'A', Creator => $uids[1] }, +); +@tickets = add_tix_from_data(); +@test = ( + { Order => "Creator" }, +); +run_tests(); + +@data = ( + { Subject => 'RT' }, + { Subject => 'Z', LastUpdatedBy => $uids[0] }, + { Subject => 'A', LastUpdatedBy => $uids[1] }, +); +@tickets = add_tix_from_data(); +@test = ( + { Order => "LastUpdatedBy" }, +); +run_tests(); + diff --git a/rt/t/ticket/sort_by_cf.t b/rt/t/ticket/sort_by_cf.t new file mode 100644 index 000000000..69274add9 --- /dev/null +++ b/rt/t/ticket/sort_by_cf.t @@ -0,0 +1,172 @@ +#!/usr/bin/perl + +use RT::Test tests => 21; +RT::Init(); + +use strict; +use warnings; + +use RT::Tickets; +use RT::Queue; +use RT::CustomField; + +my($ret,$msg); + + +# Test Sorting by custom fields. +# TODO: it's hard to read this file, conver to new style, +# for example look at 23cfsort-freeform-single.t + +# ---- Create a queue to test with. +my $queue = "CFSortQueue-$$"; +my $queue_obj = RT::Queue->new( $RT::SystemUser ); +($ret, $msg) = $queue_obj->Create( + Name => $queue, + Description => 'queue for custom field sort testing' +); +ok($ret, "$queue test queue creation. $msg"); + +# ---- Create some custom fields. We're not currently using all of +# them to test with, but the more the merrier. +my $cfO = RT::CustomField->new($RT::SystemUser); +my $cfA = RT::CustomField->new($RT::SystemUser); +my $cfB = RT::CustomField->new($RT::SystemUser); +my $cfC = RT::CustomField->new($RT::SystemUser); + +($ret, $msg) = $cfO->Create( Name => 'Order', + Queue => 0, + SortOrder => 1, + Description => q{Something to compare results for, since we can't guarantee ticket ID}, + Type=> 'FreeformSingle'); +ok($ret, "Custom Field Order created"); + +($ret, $msg) = $cfA->Create( Name => 'Alpha', + Queue => $queue_obj->id, + SortOrder => 1, + Description => 'A Testing custom field', + Type=> 'FreeformSingle'); +ok($ret, "Custom Field Alpha created"); + +($ret, $msg) = $cfB->Create( Name => 'Beta', + Queue => $queue_obj->id, + Description => 'A Testing custom field', + Type=> 'FreeformSingle'); +ok($ret, "Custom Field Beta created"); + +($ret, $msg) = $cfC->Create( Name => 'Charlie', + Queue => $queue_obj->id, + Description => 'A Testing custom field', + Type=> 'FreeformSingle'); +ok($ret, "Custom Field Charlie created"); + +# ----- Create some tickets to test with. Assign them some values to +# make it easy to sort with. +my $t1 = RT::Ticket->new($RT::SystemUser); +$t1->Create( Queue => $queue_obj->Id, + Subject => 'One', + ); +$t1->AddCustomFieldValue(Field => $cfO->Id, Value => '1'); +$t1->AddCustomFieldValue(Field => $cfA->Id, Value => '2'); +$t1->AddCustomFieldValue(Field => $cfB->Id, Value => '1'); +$t1->AddCustomFieldValue(Field => $cfC->Id, Value => 'BBB'); + +my $t2 = RT::Ticket->new($RT::SystemUser); +$t2->Create( Queue => $queue_obj->Id, + Subject => 'Two', + ); +$t2->AddCustomFieldValue(Field => $cfO->Id, Value => '2'); +$t2->AddCustomFieldValue(Field => $cfA->Id, Value => '1'); +$t2->AddCustomFieldValue(Field => $cfB->Id, Value => '2'); +$t2->AddCustomFieldValue(Field => $cfC->Id, Value => 'AAA'); + +# helper +sub check_order { + my ($tx, @order) = @_; + my @results; + while (my $t = $tx->Next) { + push @results, $t->CustomFieldValues($cfO->Id)->First->Content; + } + my $results = join (" ",@results); + my $order = join(" ",@order); + @_ = ($results, $order , "Ordered correctly: $order"); + goto \&is; +} + +# The real tests start here +my $tx = new RT::Tickets( $RT::SystemUser ); + + +# Make sure we can sort in both directions on a queue specific field. +$tx->FromSQL(qq[queue="$queue"] ); +$tx->OrderBy( FIELD => "CF.${queue}.{Charlie}", ORDER => 'DES' ); +is($tx->Count,2 ,"We found 2 tickets when looking for cf charlie"); +check_order( $tx, 1, 2); + +$tx = new RT::Tickets( $RT::SystemUser ); +$tx->FromSQL(qq[queue="$queue"] ); +$tx->OrderBy( FIELD => "CF.${queue}.{Charlie}", ORDER => 'ASC' ); +is($tx->Count,2, "We found two tickets when sorting by cf charlie without limiting to it" ); +check_order( $tx, 2, 1); + +# When ordering by _global_ CustomFields, if more than one queue has a +# CF named Charlie, things will go bad. So, these results are uniqued +# in Tickets_Overlay. +$tx = new RT::Tickets( $RT::SystemUser ); +$tx->FromSQL(qq[queue="$queue"] ); +$tx->OrderBy( FIELD => "CF.{Charlie}", ORDER => 'DESC' ); +is($tx->Count,2); +check_order( $tx, 1, 2); + +$tx = new RT::Tickets( $RT::SystemUser ); +$tx->FromSQL(qq[queue="$queue"] ); +$tx->OrderBy( FIELD => "CF.{Charlie}", ORDER => 'ASC' ); +is($tx->Count,2); +check_order( $tx, 2, 1); + +# Add a new ticket, to test sorting on multiple columns. +my $t3 = RT::Ticket->new($RT::SystemUser); +$t3->Create( Queue => $queue_obj->Id, + Subject => 'Three', + ); +$t3->AddCustomFieldValue(Field => $cfO->Id, Value => '3'); +$t3->AddCustomFieldValue(Field => $cfA->Id, Value => '3'); +$t3->AddCustomFieldValue(Field => $cfB->Id, Value => '2'); +$t3->AddCustomFieldValue(Field => $cfC->Id, Value => 'AAA'); + +$tx = new RT::Tickets( $RT::SystemUser ); +$tx->FromSQL(qq[queue="$queue"] ); +$tx->OrderByCols( + { FIELD => "CF.${queue}.{Charlie}", ORDER => 'ASC' }, + { FIELD => "CF.${queue}.{Alpha}", ORDER => 'DES' }, +); +is($tx->Count,3); +check_order( $tx, 3, 2, 1); + +$tx = new RT::Tickets( $RT::SystemUser ); +$tx->FromSQL(qq[queue="$queue"] ); +$tx->OrderByCols( + { FIELD => "CF.${queue}.{Charlie}", ORDER => 'DES' }, + { FIELD => "CF.${queue}.{Alpha}", ORDER => 'ASC' }, +); +is($tx->Count,3); +check_order( $tx, 1, 2, 3); + +# Reverse the order of the secondary column, which changes the order +# of the first two tickets. +$tx = new RT::Tickets( $RT::SystemUser ); +$tx->FromSQL(qq[queue="$queue"] ); +$tx->OrderByCols( + { FIELD => "CF.${queue}.{Charlie}", ORDER => 'ASC' }, + { FIELD => "CF.${queue}.{Alpha}", ORDER => 'ASC' }, +); +is($tx->Count,3); +check_order( $tx, 2, 3, 1); + +$tx = new RT::Tickets( $RT::SystemUser ); +$tx->FromSQL(qq[queue="$queue"] ); +$tx->OrderByCols( + { FIELD => "CF.${queue}.{Charlie}", ORDER => 'DES' }, + { FIELD => "CF.${queue}.{Alpha}", ORDER => 'DES' }, +); +is($tx->Count,3); +check_order( $tx, 1, 3, 2); |