diff options
Diffstat (limited to 'rt/t')
212 files changed, 13590 insertions, 1664 deletions
diff --git a/rt/t/api/action-createtickets.t b/rt/t/api/action-createtickets.t index 69ceb8d4d..c37e2ed12 100644 --- a/rt/t/api/action-createtickets.t +++ b/rt/t/api/action-createtickets.t @@ -2,7 +2,7 @@ use strict; use warnings; use RT; -use RT::Test tests => 49; +use RT::Test tests => 54; { @@ -14,10 +14,33 @@ use_ok('RT::ScripAction'); use_ok('RT::ScripCondition'); use_ok('RT::Ticket'); -my $approvalsq = RT::Queue->new($RT::SystemUser); + +use_ok('RT::CustomField'); + +my $global_cf = RT::CustomField->new($RT::SystemUser); +my ($id, $msg)= $global_cf->Create( Name => 'GlobalCF', + Queue => '0', + SortOrder => '1', + Description => 'A Testing custom field', + Type=> 'SelectSingle'); +ok($id, 'Global custom field correctly created'); + + +my $approvalsq = RT::Queue->new(RT->SystemUser); $approvalsq->Create(Name => 'Approvals'); ok ($approvalsq->Id, "Created Approvals test queue"); +my $queue_cf = RT::CustomField->new($RT::SystemUser); +($id) = $queue_cf->Create( + Name => 'QueueCF', + Queue => $approvalsq->Id, + SortOrder => 2, + Description => 'A testing queue-specific custom field', + Type => 'SelectSingle', +); +ok($id, 'Queue-specific custom field correctly created'); + + my $approvals = '===Create-Ticket: approval @@ -26,6 +49,8 @@ Type: approval AdminCc: {join ("\nAdminCc: ",@admins) } Depended-On-By: {$Tickets{"TOP"}->Id} Refers-To: TOP +CustomField-GlobalCF: A Value +CustomField-QueueCF: Another Value Subject: Approval for ticket: {$Tickets{"TOP"}->Id} - {$Tickets{"TOP"}->Subject} Due: {time + 86400} Content-Type: text/plain @@ -45,16 +70,16 @@ ENDOFCONTENT like ($approvals , qr/Content/, "Read in the approvals template"); -my $apptemp = RT::Template->new($RT::SystemUser); +my $apptemp = RT::Template->new(RT->SystemUser); $apptemp->Create( Content => $approvals, Name => "Approvals", Queue => "0"); ok ($apptemp->Id); -my $q = RT::Queue->new($RT::SystemUser); +my $q = RT::Queue->new(RT->SystemUser); $q->Create(Name => 'WorkflowTest'); ok ($q->Id, "Created workflow test queue"); -my $scrip = RT::Scrip->new($RT::SystemUser); +my $scrip = RT::Scrip->new(RT->SystemUser); my ($sval, $smsg) =$scrip->Create( ScripCondition => 'On Transaction', ScripAction => 'Create Tickets', Template => 'Approvals', @@ -65,7 +90,7 @@ ok ($scrip->TemplateObj->Id, "Created the scrip template"); ok ($scrip->ConditionObj->Id, "Created the scrip condition"); ok ($scrip->ActionObj->Id, "Created the scrip action"); -my $t = RT::Ticket->new($RT::SystemUser); +my $t = RT::Ticket->new(RT->SystemUser); my($tid, $ttrans, $tmsg) = $t->Create(Subject => "Sample workflow test", Owner => "root", Queue => $q->Id); @@ -76,11 +101,15 @@ my $deps = $t->DependsOn; is ($deps->Count, 1, "The ticket we created depends on one other ticket"); my $dependson= $deps->First->TargetObj; ok ($dependson->Id, "It depends on a real ticket"); +is ($dependson->FirstCustomFieldValue('GlobalCF'), 'A Value', + 'global custom field was set'); +is ($dependson->FirstCustomFieldValue('QueueCF'), 'Another Value', + 'queue custom field was set'); unlike ($dependson->Subject, qr/{/, "The subject doesn't have braces in it. that means we're interpreting expressions"); is ($t->ReferredToBy->Count,1, "It's only referred to by one other ticket"); is ($t->ReferredToBy->First->BaseObj->Id,$t->DependsOn->First->TargetObj->Id, "The same ticket that depends on it refers to it."); use RT::Action::CreateTickets; -my $action = RT::Action::CreateTickets->new( CurrentUser => $RT::SystemUser); +my $action = RT::Action::CreateTickets->new( CurrentUser => RT->SystemUser); # comma-delimited templates my $commas = <<"EOF"; @@ -237,4 +266,3 @@ foreach my $id ( sort keys %expected ) { } -1; diff --git a/rt/t/api/attachment.t b/rt/t/api/attachment.t index 282d2a3de..8b7cb608b 100644 --- a/rt/t/api/attachment.t +++ b/rt/t/api/attachment.t @@ -65,5 +65,3 @@ is ($#headers, 2, "testing a bunch of singline multiple headers" ); 'body of ContentAsMIME is original' ); } - -1; diff --git a/rt/t/api/attachment_filename.t b/rt/t/api/attachment_filename.t index 2eced0127..bcbfe0057 100644 --- a/rt/t/api/attachment_filename.t +++ b/rt/t/api/attachment_filename.t @@ -1,6 +1,6 @@ use RT::Test tests => 5; use MIME::Entity; -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); my $mime = MIME::Entity->build( From => 'test@example.com', Type => 'text/html', @@ -8,32 +8,32 @@ my $mime = MIME::Entity->build( ); $mime->attach( - Path => 'share/html/NoAuth/images/bplogo.gif', - Type => 'image/gif', + Path => 'share/html/NoAuth/images/bpslogo.png', + Type => 'image/png', ); $mime->attach( - Path => 'share/html/NoAuth/images/bplogo.gif', - Type => 'image/gif', - Filename => 'bplogo.gif', + Path => 'share/html/NoAuth/images/bpslogo.png', + Type => 'image/png', + Filename => 'bpslogo.png', ); $mime->attach( - Path => 'share/html/NoAuth/images/bplogo.gif', - Filename => 'images/bplogo.gif', - Type => 'image/gif', + Path => 'share/html/NoAuth/images/bpslogo.png', + Filename => 'images/bpslogo.png', + Type => 'image/png', ); my $id = $ticket->Create( MIMEObj => $mime, Queue => 'General' ); ok( $id, "created ticket $id" ); -my $atts = RT::Attachments->new( $RT::SystemUser ); -$atts->Limit( FIELD => 'ContentType', VALUE => 'image/gif' ); -is( $atts->Count, 3, 'got 3 gif files' ); +my $atts = RT::Attachments->new( RT->SystemUser ); +$atts->Limit( FIELD => 'ContentType', VALUE => 'image/png' ); +is( $atts->Count, 3, 'got 3 png files' ); # no matter if mime's filename include path or not, # we should throw away the path all the time. while ( my $att = $atts->Next ) { - is( $att->Filename, 'bplogo.gif', "attachment's filename" ); + is( $att->Filename, 'bpslogo.png', "attachment's filename" ); } diff --git a/rt/t/api/attribute-tests.t b/rt/t/api/attribute-tests.t index 90c3ddb7e..8489f1a56 100644 --- a/rt/t/api/attribute-tests.t +++ b/rt/t/api/attribute-tests.t @@ -1,7 +1,7 @@ use strict; use warnings; use RT; -use RT::Test tests => 34; +use RT::Test nodata => 1, tests => 34; @@ -11,7 +11,7 @@ my $attribute = "squelch-$runid"; ok(require RT::Attributes); -my $user = RT::User->new($RT::SystemUser); +my $user = RT::User->new(RT->SystemUser); ok (UNIVERSAL::isa($user, 'RT::User')); my ($id,$msg) = $user->Create(Name => 'attrtest-'.$runid); ok ($id, $msg); @@ -81,6 +81,3 @@ ok(1, $attr->BuildSelectQuery); @names = $attr->Names; is("@names", "TestAttr"); - - -1; diff --git a/rt/t/api/attribute.t b/rt/t/api/attribute.t index cb2626ad8..417f01528 100644 --- a/rt/t/api/attribute.t +++ b/rt/t/api/attribute.t @@ -2,15 +2,15 @@ use strict; use warnings; use RT; -use RT::Test tests => 7; +use RT::Test nodata => 1, tests => 7; { -my $user = $RT::SystemUser; +my $user = RT->SystemUser; my ($id, $msg) = $user->AddAttribute(Name => 'SavedSearch', Content => { Query => 'Foo'} ); ok ($id, $msg); -my $attr = RT::Attribute->new($RT::SystemUser); +my $attr = RT::Attribute->new(RT->SystemUser); $attr->Load($id); is($attr->Name , 'SavedSearch'); $attr->SetSubValues( Format => 'baz'); @@ -28,15 +28,14 @@ is ($format, undef); $attr->SetSubValues(Format => 'This is a format'); -my $attr2 = RT::Attribute->new($RT::SystemUser); +my $attr2 = RT::Attribute->new(RT->SystemUser); $attr2->Load($id); is ($attr2->SubValue('Format'), 'This is a format'); $attr2->Delete; -my $attr3 = RT::Attribute->new($RT::SystemUser); +my $attr3 = RT::Attribute->new(RT->SystemUser); ($id) = $attr3->Load($id); is ($id, 0); } -1; diff --git a/rt/t/api/bookmarks.t b/rt/t/api/bookmarks.t new file mode 100644 index 000000000..65b194526 --- /dev/null +++ b/rt/t/api/bookmarks.t @@ -0,0 +1,24 @@ +use strict; +use warnings; +use RT::Test tests => 36; + +my ( $url, $m ) = RT::Test->started_ok; +my $root = RT::Test->load_or_create_user( Name => 'root' ); + +my @tickets = RT::Test->create_tickets( { }, map { { Subject => "Test $_" } } ( 1 .. 9 ) ); + +# 4.2 gives us $user->ToggleBookmark which is nicer +$root->SetAttribute( Name => 'Bookmarks', Content => { map { $_ => 1 } (3,6,9) } ); + +my $cu = RT::CurrentUser->new($root); +my $bookmarks = RT::Tickets->new($cu); +for my $search ( "Queue = 'General' AND id = '__Bookmarked__'", + "id = '__Bookmarked__' AND Queue = 'General'", + "id > 0 AND id = '__Bookmarked__'", + "id = '__Bookmarked__' AND id > 0", + "id = 3 OR id = '__Bookmarked__'", + "id = '__Bookmarked__' OR id = 3", + ) { + $bookmarks->FromSQL($search); + is($bookmarks->Count,3,"Found my 3 bookmarks for [$search]"); +} diff --git a/rt/t/api/canonical_charset.t b/rt/t/api/canonical_charset.t new file mode 100644 index 000000000..a426d89b6 --- /dev/null +++ b/rt/t/api/canonical_charset.t @@ -0,0 +1,30 @@ +use warnings; +use strict; + +use RT::Test nodata => 1, tests => 11; +use RT::I18N; +use Encode; + +my %map = ( + 'euc-cn' => 'gbk', + 'gb-2312' => 'gbk', + gb2312 => 'gbk', + utf8 => 'utf-8', + 'utf-8' => 'utf-8', +); + +for my $charset ( keys %map ) { + is( RT::I18N::_CanonicalizeCharset($charset), + $map{$charset}, "$charset => $map{$charset}" ); + is( RT::I18N::_CanonicalizeCharset( uc $charset ), + $map{$charset}, uc( $charset ) . " => $map{$charset}" ); +} + +my $mime = MIME::Entity->build( + Type => 'text/plain; charset=gb2312', + Data => [encode('gbk', decode_utf8("法新社倫敦11日電"))], +); + +RT::I18N::SetMIMEEntityToUTF8($mime); +is( $mime->stringify_body, '法新社倫敦11日電', 'gb2312 => gbk in mail' ); + diff --git a/rt/t/api/cf_render_type.t b/rt/t/api/cf_render_type.t new file mode 100644 index 000000000..ac090495e --- /dev/null +++ b/rt/t/api/cf_render_type.t @@ -0,0 +1,49 @@ +use strict; +use warnings; + +use RT::Test tests => 13; + +my $cf = RT::CustomField->new($RT::SystemUser); +my ( $id, $ret, $msg ); + +diag "single select"; +( $id, $msg ) = $cf->Create( + Name => 'single_select', + Type => 'Select', + MaxValues => '1', + Queue => 0, +); +ok( $id, $msg ); + +is( $cf->RenderType, 'Select box', 'default render type is Select box' ); +( $ret, $msg ) = $cf->SetRenderType('Dropdown'); +ok( $ret, 'changed to Dropdown' ); +is( $cf->RenderType, 'Dropdown', 'render type is indeed updated' ); + +( $ret, $msg ) = $cf->SetRenderType('List'); +ok( $ret, 'changed to List' ); +is( $cf->RenderType, 'List', 'render type is indeed updated' ); + +( $ret, $msg ) = $cf->SetRenderType('fakeone'); +ok( !$ret, 'failed to set an invalid render type' ); +is( $cf->RenderType, 'List', 'render type is still List' ); + +diag "multiple select"; +( $id, $msg ) = $cf->Create( + Name => 'multiple_select', + Type => 'Select', + MaxValues => '0', + Queue => 0, + RenderType => 'List', +); + +is( $cf->RenderType, 'List', 'set render type to List' ); +( $ret, $msg ) = $cf->SetRenderType('Dropdown'); +ok( !$ret, 'Dropdown is invalid for multiple select' ); + +is( $cf->RenderType, 'List', 'render type is still List' ); + +( $ret, $msg ) = $cf->SetRenderType('Select box'); +ok( $ret, 'changed to Select box' ); +is( $cf->RenderType, 'Select box', 'render type is indeed updated' ); + diff --git a/rt/t/api/condition-ownerchange.t b/rt/t/api/condition-ownerchange.t index 4c4c49b29..2cfef7422 100644 --- a/rt/t/api/condition-ownerchange.t +++ b/rt/t/api/condition-ownerchange.t @@ -7,12 +7,12 @@ use RT::Test tests => 11; { -my $q = RT::Queue->new($RT::SystemUser); +my $q = RT::Queue->new(RT->SystemUser); $q->Create(Name =>'ownerChangeTest'); ok($q->Id, "Created a scriptest queue"); -my $s1 = RT::Scrip->new($RT::SystemUser); +my $s1 = RT::Scrip->new(RT->SystemUser); my ($val, $msg) =$s1->Create( Queue => $q->Id, ScripAction => 'User Defined', ScripCondition => 'On Owner Change', @@ -26,7 +26,7 @@ my ($val, $msg) =$s1->Create( Queue => $q->Id, ); ok($val,$msg); -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); my ($tv,$ttv,$tm) = $ticket->Create(Queue => $q->Id, Subject => "hair on fire", InitialPriority => '20' @@ -48,4 +48,3 @@ is ($ticket->Priority , '24', "Ticket priority is set right"); } -1; diff --git a/rt/t/api/condition-reject.t b/rt/t/api/condition-reject.t index 96789509d..c2ec8cdda 100644 --- a/rt/t/api/condition-reject.t +++ b/rt/t/api/condition-reject.t @@ -10,12 +10,12 @@ use RT::Test tests => 7; { -my $q = RT::Queue->new($RT::SystemUser); +my $q = RT::Queue->new(RT->SystemUser); $q->Create(Name =>'rejectTest'); ok($q->Id, "Created a scriptest queue"); -my $s1 = RT::Scrip->new($RT::SystemUser); +my $s1 = RT::Scrip->new(RT->SystemUser); my ($val, $msg) =$s1->Create( Queue => $q->Id, ScripAction => 'User Defined', ScripCondition => 'On reject', @@ -29,7 +29,7 @@ my ($val, $msg) =$s1->Create( Queue => $q->Id, ); ok($val,$msg); -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); my ($tv,$ttv,$tm) = $ticket->Create(Queue => $q->Id, Subject => "hair on fire", InitialPriority => '20' @@ -42,4 +42,3 @@ is ($ticket->Priority , '21', "Condition is false, scrip skipped"); } -1; diff --git a/rt/t/api/config.t b/rt/t/api/config.t new file mode 100644 index 000000000..a986c3c4f --- /dev/null +++ b/rt/t/api/config.t @@ -0,0 +1,33 @@ +use strict; +use warnings; +use RT; +use RT::Test nodb => 1, tests => 9; + +ok( + RT::Config->AddOption( + Name => 'foo', + Section => 'bar', + ), + 'added option foo' +); + +my $meta = RT::Config->Meta('foo'); +is( $meta->{Section}, 'bar', 'Section is bar' ); +is( $meta->{Widget}, '/Widgets/Form/String', 'default Widget is string' ); +is_deeply( $meta->{WidgetArguments}, + {},, 'default WidgetArguments is empty hashref' ); + +ok( + RT::Config->UpdateOption( + Name => 'foo', + Section => 'baz', + Widget => '/Widgets/Form/Boolean', + ), + 'updated option foo to section baz' +); +is( $meta->{Section}, 'baz', 'section is updated to baz' ); +is( $meta->{Widget}, '/Widgets/Form/Boolean', 'widget is updated to boolean' ); + +ok( RT::Config->DeleteOption( Name => 'foo' ), 'removed option foo' ); +is( RT::Config->Meta('foo'), undef, 'foo is indeed deleted' ); + diff --git a/rt/t/api/cron.t b/rt/t/api/cron.t new file mode 100644 index 000000000..6f9d7f644 --- /dev/null +++ b/rt/t/api/cron.t @@ -0,0 +1,89 @@ +#!/usr/bin/perl -w + +use strict; + +use RT; +use RT::Test nodata => 1, tests => 18; + + +### Set up some testing data. Test the testing data because why not? + +# Create a user with rights, a queue, and some tickets. +my $user_obj = RT::User->new(RT->SystemUser); +my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('tara@example.com'); +ok($ret, 'record test user creation'); +$user_obj->SetName('tara'); +$user_obj->PrincipalObj->GrantRight(Right => 'SuperUser'); +my $CurrentUser = RT::CurrentUser->new('tara'); + +# Create our template, which will be used for tests of RT::Action::Record*. + +my $template_content = 'RT-Send-Cc: tla@example.com +RT-Send-Bcc: jesse@example.com + +This is a content string with no content.'; + +my $template_obj = RT::Template->new($CurrentUser); +$template_obj->Create(Queue => '0', + Name => 'recordtest', + Description => 'testing Record actions', + Content => $template_content, + ); + +# Create a queue and some tickets. + +my $queue_obj = RT::Queue->new($CurrentUser); +($ret, $msg) = $queue_obj->Create(Name => 'recordtest', Description => 'queue for Action::Record testing'); +ok($ret, 'record test queue creation'); + +my $ticket1 = RT::Ticket->new($CurrentUser); +my ($id, $tobj, $msg2) = $ticket1->Create(Queue => $queue_obj, + Requestor => ['tara@example.com'], + Subject => 'bork bork bork', + Priority => 22, + ); +ok($id, 'record test ticket creation 1'); +my $ticket2 = RT::Ticket->new($CurrentUser); +($id, $tobj, $msg2) = $ticket2->Create(Queue => $queue_obj, + Requestor => ['root@localhost'], + Subject => 'hurdy gurdy' + ); +ok($id, 'record test ticket creation 2'); + + +### OK. Have data, will travel. + +# First test the search. + +ok(require RT::Search::FromSQL, "Search::FromSQL loaded"); +my $ticketsqlstr = "Requestor.EmailAddress = '" . $CurrentUser->EmailAddress . + "' AND Priority > '20'"; +my $search = RT::Search::FromSQL->new(Argument => $ticketsqlstr, TicketsObj => RT::Tickets->new($CurrentUser), + ); +is(ref($search), 'RT::Search::FromSQL', "search created"); +ok($search->Prepare(), "fromsql search run"); +my $counter = 0; +while(my $t = $search->TicketsObj->Next() ) { + is($t->Id(), $ticket1->Id(), "fromsql search results 1"); + $counter++; +} +is ($counter, 1, "fromsql search results 2"); + +# Right. Now test the actions. + +ok(require RT::Action::RecordComment); +ok(require RT::Action::RecordCorrespondence); + +my ($comment_act, $correspond_act); +ok($comment_act = RT::Action::RecordComment->new(TicketObj => $ticket1, TemplateObj => $template_obj, CurrentUser => $CurrentUser), "RecordComment created"); +ok($correspond_act = RT::Action::RecordCorrespondence->new(TicketObj => $ticket2, TemplateObj => $template_obj, CurrentUser => $CurrentUser), "RecordCorrespondence created"); +ok($comment_act->Prepare(), "Comment prepared"); +ok($correspond_act->Prepare(), "Correspond prepared"); +ok($comment_act->Commit(), "Comment committed"); +ok($correspond_act->Commit(), "Correspondence committed"); + +# Now test for loop suppression. +my ($trans, $desc, $transaction) = $ticket2->Comment(MIMEObj => $template_obj->MIMEObj); +my $bogus_action = RT::Action::RecordComment->new(TicketObj => $ticket1, TemplateObj => $template_obj, TransactionObj => $transaction, CurrentUser => $CurrentUser); +ok(!$bogus_action->Prepare(), "Comment aborted to prevent loop"); + diff --git a/rt/t/api/currentuser.t b/rt/t/api/currentuser.t index c15804824..f54074af9 100644 --- a/rt/t/api/currentuser.t +++ b/rt/t/api/currentuser.t @@ -2,7 +2,7 @@ use strict; use warnings; use RT; -use RT::Test tests => 8; +use RT::Test noinitialdata => 1, tests => 8; { @@ -29,4 +29,3 @@ SKIP: { } -1; diff --git a/rt/t/api/customfield.t b/rt/t/api/customfield.t index 44319c47f..6be50bb3a 100644 --- a/rt/t/api/customfield.t +++ b/rt/t/api/customfield.t @@ -2,14 +2,14 @@ use strict; use warnings; use RT; -use RT::Test tests => 29; +use RT::Test nodata => 1, tests => 29; use Test::Warn; { use_ok('RT::CustomField'); -ok(my $cf = RT::CustomField->new($RT::SystemUser)); +ok(my $cf = RT::CustomField->new(RT->SystemUser)); ok(my ($id, $msg)= $cf->Create( Name => 'TestingCF', Queue => '0', SortOrder => '1', @@ -28,7 +28,7 @@ ok(!$cf->SingleValue ); ok(my ($bogus_val, $bogus_msg) = $cf->SetType('BogusType') , "Trying to set a custom field's type to a bogus type"); is($bogus_val , 0, "Unable to set a custom field's type to a bogus type"); -ok(my $bad_cf = RT::CustomField->new($RT::SystemUser)); +ok(my $bad_cf = RT::CustomField->new(RT->SystemUser)); ok(my ($bad_id, $bad_msg)= $cf->Create( Name => 'TestingCF-bad', Queue => '0', SortOrder => '1', @@ -41,7 +41,7 @@ is($bad_id , 0, 'Global custom field correctly decided to not create a cf with a { -ok(my $cf = RT::CustomField->new($RT::SystemUser)); +ok(my $cf = RT::CustomField->new(RT->SystemUser)); $cf->Load(1); is($cf->Id , 1); ok(my ($val,$msg) = $cf->AddValue(Name => 'foo' , Description => 'TestCFValue', SortOrder => '6')); @@ -54,7 +54,7 @@ ok ($delval,"Deleting a cf value: $delmsg"); { -ok(my $cf = RT::CustomField->new($RT::SystemUser)); +ok(my $cf = RT::CustomField->new(RT->SystemUser)); warning_like { ok($cf->ValidateType('SelectSingle')); @@ -71,4 +71,3 @@ ok(!$cf->ValidateType('SelectFooMultiple')); } -1; diff --git a/rt/t/api/date.t b/rt/t/api/date.t index bc1446f50..9756e51c4 100644 --- a/rt/t/api/date.t +++ b/rt/t/api/date.t @@ -1,47 +1,31 @@ #!/usr/bin/perl use Test::MockTime qw(set_fixed_time restore_time); - -use Test::More; -my $tests; - -my $localized_datetime_tests; -BEGIN { - $tests = 167; - $localized_datetime_tests = - eval { require DateTime; 1; } && eval { require DateTime::Locale; 1; } && - DateTime->can('format_cldr') && DateTime::Locale::root->can('date_format_full'); - - if ($localized_datetime_tests) { - - # Include RT::Date::LocalizedDateTime tests - $tests += 7; - } -} +use DateTime; use warnings; use strict; -use RT::Test tests => $tests; +use RT::Test tests => 172; use RT::User; use Test::Warn; use_ok('RT::Date'); { - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); isa_ok($date, 'RT::Date', "constructor returned RT::Date oject"); - $date = $date->new($RT::SystemUser); + $date = $date->new(RT->SystemUser); isa_ok($date, 'RT::Date', "constructor returned RT::Date oject"); } { # set timezone in all places to UTC - $RT::SystemUser->UserObj->__Set(Field => 'Timezone', Value => 'UTC') - if $RT::SystemUser->UserObj->Timezone; + RT->SystemUser->UserObj->__Set(Field => 'Timezone', Value => 'UTC') + if RT->SystemUser->UserObj->Timezone; RT->Config->Set( Timezone => 'UTC' ); } my $current_user; { - my $user = RT::User->new($RT::SystemUser); + my $user = RT::User->new(RT->SystemUser); my($uid, $msg) = $user->Create( Name => "date_api". rand(200), Lang => 'en', @@ -53,7 +37,6 @@ my $current_user; { my $date = RT::Date->new( $current_user ); - is($date->Timezone, 'UTC', "dropped all timzones to UTC"); is($date->Timezone('user'), 'UTC', "dropped all timzones to UTC"); is($date->Timezone('server'), 'UTC', "dropped all timzones to UTC"); is($date->Timezone('unknown'), 'UTC', "with wrong context returns UTC"); @@ -65,7 +48,6 @@ my $current_user; is($date->Timezone('user'), 'Europe/Moscow', "in user context returns user's timezone"); - is($date->Timezone, 'UTC', "the deafult value is always UTC"); is($date->Timezone('server'), 'UTC', "wasn't changed"); RT->Config->Set( Timezone => 'Africa/Ouagadougou' ); @@ -75,7 +57,6 @@ my $current_user; is($date->Timezone('user'), 'Europe/Moscow', "in user context still returns user's timezone"); - is($date->Timezone, 'UTC', "the deafult value is always UTC"); $current_user->UserObj->__Set( Field => 'Timezone', Value => ''); is_empty($current_user->UserObj->Timezone, @@ -83,7 +64,6 @@ my $current_user; is($date->Timezone('user'), 'Africa/Ouagadougou', "in user context returns timezone of the server if user's one is not defined"); - is($date->Timezone, 'UTC', "the deafult value is always UTC"); RT->Config->Set( Timezone => 'GMT' ); is($date->Timezone('server'), @@ -91,7 +71,6 @@ my $current_user; "timezone is GMT which one is alias for UTC"); RT->Config->Set( Timezone => '' ); - is($date->Timezone, 'UTC', "dropped all timzones to UTC"); is($date->Timezone('user'), 'UTC', "user's and server's timzones are not defined, so UTC"); @@ -103,7 +82,7 @@ my $current_user; } { - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); is($date->Unix, 0, "new date returns 0 in Unix format"); is($date->Get, '1970-01-01 00:00:00', "default is ISO format"); is($date->Get(Format =>'SomeBadFormat'), @@ -117,7 +96,7 @@ my $current_user; "RFC2822 format with defaults"); is($date->Get(Format =>'LocalizedDateTime'), 'Thu, Jan 1, 1970 12:00:00 AM', - "LocalizedDateTime format with defaults") if ( $localized_datetime_tests ); + "LocalizedDateTime format with defaults"); is($date->ISO(Time => 0), '1970-01-01', @@ -130,7 +109,7 @@ my $current_user; "RFC2822 format without time part"); is($date->LocalizedDateTime(Time => 0), 'Thu, Jan 1, 1970', - "LocalizedDateTime format without time part") if ( $localized_datetime_tests ); + "LocalizedDateTime format without time part"); is($date->ISO(Date => 0), '00:00:00', @@ -143,7 +122,7 @@ my $current_user; "RFC2822 format without date part"); is($date->LocalizedDateTime(Date => 0), '12:00:00 AM', - "LocalizedDateTime format without date part") if ( $localized_datetime_tests ); + "LocalizedDateTime format without date part"); is($date->ISO(Date => 0, Seconds => 0), '00:00', @@ -164,16 +143,16 @@ my $current_user; is($date->LocalizedDateTime(AbbrDay => 0), 'Thursday, Jan 1, 1970 12:00:00 AM', - "LocalizedDateTime format without abbreviation of day") if ( $localized_datetime_tests ); + "LocalizedDateTime format without abbreviation of day"); is($date->LocalizedDateTime(AbbrMonth => 0), 'Thu, January 1, 1970 12:00:00 AM', - "LocalizedDateTime format without abbreviation of month") if ( $localized_datetime_tests ); + "LocalizedDateTime format without abbreviation of month"); is($date->LocalizedDateTime(DateFormat => 'date_format_short'), '1/1/70 12:00:00 AM', - "LocalizedDateTime format with non default DateFormat") if ( $localized_datetime_tests ); + "LocalizedDateTime format with non default DateFormat"); is($date->LocalizedDateTime(TimeFormat => 'time_format_short'), 'Thu, Jan 1, 1970 12:00 AM', - "LocalizedDateTime format with non default TimeFormat") if ( $localized_datetime_tests ); + "LocalizedDateTime format with non default TimeFormat"); is($date->Date, '1970-01-01', @@ -256,14 +235,14 @@ my $current_user; warning_like { # bad format - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); $date->Set( Format => 'bad' ); is($date->Unix, 0, "bad format"); -} qr'Unknown Date format: bad'; +} qr{Unknown Date format: bad}; { # setting value via Unix method - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); $date->Unix(1); is($date->ISO, '1970-01-01 00:00:01', "correct value"); @@ -280,7 +259,7 @@ warning_like my $year = (localtime(time))[5] + 1900; { # set+ISO format - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); warning_like { $date->Set(Format => 'ISO', Value => 'weird date'); } qr/Couldn't parse date 'weird date' as a ISO format/; @@ -324,7 +303,7 @@ my $year = (localtime(time))[5] + 1900; } { # set+datemanip format(Time::ParseDate) - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); $date->Set(Format => 'unknown', Value => 'weird date'); is($date->Unix, 0, "date was wrong"); @@ -343,7 +322,7 @@ my $year = (localtime(time))[5] + 1900; } { # set+unknown format(Time::ParseDate) - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); $date->Set(Format => 'unknown', Value => 'weird date'); is($date->Unix, 0, "date was wrong"); @@ -380,7 +359,7 @@ my $year = (localtime(time))[5] + 1900; } { # SetToMidnight - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); RT->Config->Set( Timezone => 'Europe/Moscow' ); $date->Set(Format => 'ISO', Value => '2005-11-28 15:10:00'); @@ -414,7 +393,7 @@ my $year = (localtime(time))[5] + 1900; } { # SetToNow - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); my $time = time; $date->SetToNow; ok($date->Unix >= $time, 'close enough'); @@ -422,7 +401,7 @@ my $year = (localtime(time))[5] + 1900; } { - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); $date->Unix(0); $date->AddSeconds; @@ -484,7 +463,7 @@ my $year = (localtime(time))[5] + 1900; } { # DurationAsString - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); is($date->DurationAsString(1), '1 sec', '1 sec'); is($date->DurationAsString(59), '59 sec', '59 sec'); @@ -505,7 +484,7 @@ my $year = (localtime(time))[5] + 1900; } { # DiffAsString - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); is($date->DiffAsString(1), '', 'no diff, wrong input'); is($date->DiffAsString(-1), '', 'no diff, wrong input'); is($date->DiffAsString('qwe'), '', 'no diff, wrong input'); @@ -516,14 +495,14 @@ my $year = (localtime(time))[5] + 1900; is($date->DiffAsString(3), '1 sec ago', 'diff: 1 sec ago'); is($date->DiffAsString(1), '1 sec', 'diff: 1 sec'); - my $ndate = RT::Date->new($RT::SystemUser); + my $ndate = RT::Date->new(RT->SystemUser); is($date->DiffAsString($ndate), '', 'no diff, wrong input'); $ndate->Unix(3); is($date->DiffAsString($ndate), '1 sec ago', 'diff: 1 sec ago'); } { # Diff - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); $date->SetToNow; my $diff = $date->Diff; ok($diff <= 0, 'close enought'); @@ -531,14 +510,14 @@ my $year = (localtime(time))[5] + 1900; } { # AgeAsString - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); $date->SetToNow; my $diff = $date->AgeAsString; like($diff, qr/^(0 sec|[1-5] sec ago)$/, 'close enought'); } { # GetWeekday - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); is($date->GetWeekday(7), '', '7 and greater are invalid'); is($date->GetWeekday(6), 'Sat', '6 is Saturday'); is($date->GetWeekday(0), 'Sun', '0 is Sunday'); @@ -548,7 +527,7 @@ my $year = (localtime(time))[5] + 1900; } { # GetMonth - my $date = RT::Date->new($RT::SystemUser); + my $date = RT::Date->new(RT->SystemUser); is($date->GetMonth(12), '', '12 and greater are invalid'); is($date->GetMonth(11), 'Dec', '11 is December'); is($date->GetMonth(0), 'Jan', '0 is January'); @@ -557,8 +536,19 @@ my $year = (localtime(time))[5] + 1900; is($date->GetMonth(-13), '', '-13 and lesser are invalid'); } +{ + # set unknown format: edge cases + my $date = RT::Date->new(RT->SystemUser); + $date->Set( Value => 0, Format => 'unknown' ); + is( $date->Unix(), 0, "unix is 0 with Value => 0, Format => 'unknown'" ); + + $date->Set( Value => '', Format => 'unknown' ); + is( $date->Unix(), 0, "unix is 0 with Value => '', Format => 'unknown'" ); + + $date->Set( Value => ' ', Format => 'unknown' ); + is( $date->Unix(), 0, "unix is 0 with Value => ' ', Format => 'unknown'" ); +} + #TODO: AsString #TODO: RFC2822, W3CDTF with Timezones -exit(0); - diff --git a/rt/t/api/emailparser.t b/rt/t/api/emailparser.t index 940c26fde..790314603 100644 --- a/rt/t/api/emailparser.t +++ b/rt/t/api/emailparser.t @@ -2,7 +2,7 @@ use strict; use warnings; -use RT::Test tests => 4; +use RT::Test nodb => 1, tests => 10; RT->Config->Set( RTAddressRegexp => qr/^rt\@example.com$/i ); @@ -14,6 +14,23 @@ is(RT::EmailParser::IsRTAddress("","frt\@example.com"),undef, "Regexp didn't mat my @before = ("rt\@example.com", "frt\@example.com"); my @after = ("frt\@example.com"); -ok(eq_array(RT::EmailParser::CullRTAddresses("",@before),@after), "CullRTAddresses only culls RT addresses"); +ok(eq_array(RT::EmailParser->CullRTAddresses(@before),@after), "CullRTAddresses only culls RT addresses"); + +{ + require RT::Interface::Email; + my ( $addr, $name ) = + RT::Interface::Email::ParseAddressFromHeader('foo@example.com'); + is( $addr, 'foo@example.com', 'addr for foo@example.com' ); + is( $name, undef, 'no name for foo@example.com' ); + + ( $addr, $name ) = + RT::Interface::Email::ParseAddressFromHeader('Foo <foo@example.com>'); + is( $addr, 'foo@example.com', 'addr for Foo <foo@example.com>' ); + is( $name, 'Foo', 'name for Foo <foo@example.com>' ); + + ( $addr, $name ) = + RT::Interface::Email::ParseAddressFromHeader('foo@example.com (Comment)'); + is( $addr, 'foo@example.com', 'addr for foo@example.com (Comment)' ); + is( $name, undef, 'no name for foo@example.com (Comment)' ); +} -1; diff --git a/rt/t/api/execute-code.t b/rt/t/api/execute-code.t new file mode 100644 index 000000000..7f72be90a --- /dev/null +++ b/rt/t/api/execute-code.t @@ -0,0 +1,108 @@ +use strict; +use warnings; +use RT::Test tests => 17; + +my $ticket = RT::Ticket->new(RT->SystemUser); +ok( + $ticket->Create( + Subject => 'blue lines', + Queue => 'General', + ) +); + +my $attacker = RT::User->new(RT->SystemUser); +ok( + $attacker->Create( + Name => 'attacker', + Password => 'foobar', + Privileged => 1, + ) +); + +my $template_as_attacker = RT::Template->new($attacker); + +# can't create templates without ModifyTemplate +my ($ok, $msg) = $template_as_attacker->Create( + Name => 'Harmless, honest!', + Content => "\nhello ;)", + Type => 'Perl', +); +ok(!$ok, 'permission to create denied'); + + +# permit modifying templates but they must be simple +$attacker->PrincipalObj->GrantRight(Right => 'ShowTemplate', Object => $RT::System); +$attacker->PrincipalObj->GrantRight(Right => 'ModifyTemplate', Object => $RT::System); + +($ok, $msg) = $template_as_attacker->Create( + Name => 'Harmless, honest!', + Content => "\nhello ;)", + Type => 'Perl', +); +ok(!$ok, 'permission to create denied'); + + +($ok, $msg) = $template_as_attacker->Create( + Name => 'Harmless, honest!', + Content => "\nhello ;)", + Type => 'Simple', +); +ok($ok, 'created template now that we have ModifyTemplate'); + +($ok, $msg) = $template_as_attacker->SetType('Perl'); +ok(!$ok, 'permission to update type to Perl denied'); + +my $template_as_root = RT::Template->new(RT->SystemUser); +$template_as_root->Load('Harmless, honest!'); +is($template_as_root->Content, "\nhello ;)"); +is($template_as_root->Type, 'Simple'); + +$template_as_root->Parse(TicketObj => $ticket); +is($template_as_root->MIMEObj->stringify_body, "hello ;)"); + + +# update the content to include code (even though Simple won't parse it) + +($ok, $msg) = $template_as_attacker->SetContent("\nYou are { (my \$message = 'bjarq') =~ tr/a-z/n-za-m/; \$message }!"); +ok($ok, 'updating Content permitted since the template is Simple'); + +$template_as_root = RT::Template->new(RT->SystemUser); +$template_as_root->Load('Harmless, honest!'); + +is($template_as_root->Content, "\nYou are { (my \$message = 'bjarq') =~ tr/a-z/n-za-m/; \$message }!"); +is($template_as_root->Type, 'Simple'); + +$template_as_root->Parse(TicketObj => $ticket); +is($template_as_root->MIMEObj->stringify_body, "You are { (my \$message = 'bjarq') =~ tr/a-z/n-za-m/; \$message }!"); + + +# try again, why not +($ok, $msg) = $template_as_attacker->SetType('Perl'); +ok(!$ok, 'permission to update type to Perl denied'); + + +# now root will change the template to genuine code +$template_as_root = RT::Template->new(RT->SystemUser); +$template_as_root->Load('Harmless, honest!'); +$template_as_root->SetType('Perl'); +$template_as_root->SetContent("\n{ scalar reverse \$Ticket->Subject }"); + +$template_as_root->Parse(TicketObj => $ticket); +is($template_as_root->MIMEObj->stringify_body, "senil eulb"); + + +# see if we can update anything +$template_as_attacker = RT::Template->new($attacker); +$template_as_attacker->Load('Harmless, honest!'); + +($ok, $msg) = $template_as_attacker->SetContent("\nYou are { (my \$message = 'bjarq') =~ tr/a-z/n-za-m/; \$message }!"); +ok(!$ok, 'updating Content forbidden since the template is Perl'); + +# try again just to be absolutely sure it doesn't work +$template_as_root = RT::Template->new(RT->SystemUser); +$template_as_root->Load('Harmless, honest!'); +$template_as_root->SetType('Perl'); +$template_as_root->SetContent("\n{ scalar reverse \$Ticket->Subject }"); + +$template_as_root->Parse(TicketObj => $ticket); +is($template_as_root->MIMEObj->stringify_body, "senil eulb"); diff --git a/rt/t/api/group-rights.t b/rt/t/api/group-rights.t new file mode 100644 index 000000000..0494c286e --- /dev/null +++ b/rt/t/api/group-rights.t @@ -0,0 +1,137 @@ +use strict; +use warnings; +use RT::Test nodata => 1, tests => 114; + +RT::Group->AddRights( + 'RTxGroupRight' => 'Just a right for testing rights', +); + +# this company is split into two halves, the hackers and the non-hackers +# herbert is a hacker but eric is not. +my $herbert = RT::User->new(RT->SystemUser); +my ($ok, $msg) = $herbert->Create(Name => 'herbert'); +ok($ok, $msg); + +my $eric = RT::User->new(RT->SystemUser); +($ok, $msg) = $eric->Create(Name => 'eric'); +ok($ok, $msg); + +my $hackers = RT::Group->new(RT->SystemUser); +($ok, $msg) = $hackers->CreateUserDefinedGroup(Name => 'Hackers'); +ok($ok, $msg); + +my $employees = RT::Group->new(RT->SystemUser); +($ok, $msg) = $employees->CreateUserDefinedGroup(Name => 'Employees'); +ok($ok, $msg); + +($ok, $msg) = $employees->AddMember($hackers->PrincipalId); +ok($ok, $msg); + +($ok, $msg) = $hackers->AddMember($herbert->PrincipalId); +ok($ok, $msg); + +($ok, $msg) = $employees->AddMember($eric->PrincipalId); +ok($ok, $msg); + +ok($employees->HasMemberRecursively($hackers->PrincipalId), 'employees has member hackers'); +ok($employees->HasMemberRecursively($herbert->PrincipalId), 'employees has member herbert'); +ok($employees->HasMemberRecursively($eric->PrincipalId), 'employees has member eric'); + +ok($hackers->HasMemberRecursively($herbert->PrincipalId), 'hackers has member herbert'); +ok(!$hackers->HasMemberRecursively($eric->PrincipalId), 'hackers does not have member eric'); + +# There's also a separate group, "Other", which both are a member of. +my $other = RT::Group->new(RT->SystemUser); +($ok, $msg) = $other->CreateUserDefinedGroup(Name => 'Other'); +ok($ok, $msg); +($ok, $msg) = $other->AddMember($eric->PrincipalId); +ok($ok, $msg); +($ok, $msg) = $other->AddMember($herbert->PrincipalId); +ok($ok, $msg); + + +# Everyone can SeeGroup on all three groups +my $everyone = RT::Group->new( RT->SystemUser ); +($ok, $msg) = $everyone->LoadSystemInternalGroup( 'Everyone' ); +ok($ok, $msg); +$everyone->PrincipalObj->GrantRight(Right => 'SeeGroup', Object => $employees); +$everyone->PrincipalObj->GrantRight(Right => 'SeeGroup', Object => $hackers); +$everyone->PrincipalObj->GrantRight(Right => 'SeeGroup', Object => $other); + +sub CheckRights { + my $cu = shift; + my %groups = (Employees => 0, Hackers => 0, Other => 0, @_); + my $name = $cu->Name; + + my $groups = RT::Groups->new(RT::CurrentUser->new($cu)); + $groups->LimitToUserDefinedGroups; + $groups->ForWhichCurrentUserHasRight(Right => 'RTxGroupRight'); + my %has_right = map { ($_->Name => 1) } @{ $groups->ItemsArrayRef }; + + local $Test::Builder::Level = $Test::Builder::Level + 1; + for my $groupname (sort keys %groups) { + my $g = RT::Group->new(RT::CurrentUser->new($cu)); + $g->LoadUserDefinedGroup($groupname); + if ($groups{$groupname}) { + ok( $g->CurrentUserHasRight("RTxGroupRight"), "$name has right on $groupname (direct query)" ); + ok( delete $has_right{$groupname}, "..and also in ForWhichCurrentUserHasRight"); + } else { + ok( !$g->CurrentUserHasRight("RTxGroupRight"), "$name doesn't have right on $groupname (direct query)" ); + ok( !delete $has_right{$groupname}, "..and also not in ForWhichCurrentUserHasRight"); + } + } + ok(not(keys %has_right), "ForWhichCurrentUserHasRight has no extra groups"); +} + +# Neither should have it on any group yet +CheckRights($eric); +CheckRights($herbert); + + +# Grant it to employees, on employees. Both Herbert and Eric will have +# it on employees, though Herbert gets it by way of hackers. Neither +# will have it on hackers, because the target does not recurse. +$employees->PrincipalObj->GrantRight( Right => 'RTxGroupRight', Object => $employees); +CheckRights($eric, Employees => 1); +CheckRights($herbert, Employees => 1); + + +# Grant it to employees, on hackers. This means both Eric and Herbert +# will have the right on hackers, but not on employees. +$employees->PrincipalObj->RevokeRight(Right => 'RTxGroupRight', Object => $employees); +$employees->PrincipalObj->GrantRight( Right => 'RTxGroupRight', Object => $hackers); +CheckRights($eric, Hackers => 1); +CheckRights($herbert, Hackers => 1); + + +# Grant it to hackers, on employees. Eric will have it nowhere, and +# Herbert will have it on employees. Note that the target of the right +# itself does _not_ recurse down, so Herbert will not have it on +# hackers. +$employees->PrincipalObj->RevokeRight(Right => 'RTxGroupRight', Object => $hackers); +$hackers->PrincipalObj->GrantRight( Right => 'RTxGroupRight', Object => $employees); +CheckRights($eric); +CheckRights($herbert, Employees => 1); + + +# Grant it globally to hackers; herbert will see the right on all +# employees, hackers, and other. +$hackers->PrincipalObj->RevokeRight( Right => 'RTxGroupRight', Object => $employees); +$hackers->PrincipalObj->GrantRight( Right => 'RTxGroupRight', Object => RT->System); +CheckRights($eric); +CheckRights($herbert, Employees => 1, Hackers => 1, Other => 1 ); + + +# Grant it globally to employees; both eric and herbert will see the +# right on all employees, hackers, and other. +$hackers->PrincipalObj->RevokeRight( Right => 'RTxGroupRight', Object => RT->System); +$employees->PrincipalObj->GrantRight( Right => 'RTxGroupRight', Object => RT->System); +CheckRights($eric, Employees => 1, Hackers => 1, Other => 1 ); +CheckRights($herbert, Employees => 1, Hackers => 1, Other => 1 ); + + +# Disable the employees group. Neither eric nor herbert will see the +# right anywhere. +$employees->SetDisabled(1); +CheckRights($eric); +CheckRights($herbert); diff --git a/rt/t/api/group.t b/rt/t/api/group.t index 551d4f1a0..3ce3da999 100644 --- a/rt/t/api/group.t +++ b/rt/t/api/group.t @@ -2,20 +2,19 @@ use strict; use warnings; use RT; -use RT::Test tests => 38; +use RT::Test nodata => 1, tests => 38; { -# {{{ Tests ok (require RT::Group); -ok (my $group = RT::Group->new($RT::SystemUser), "instantiated a group object"); +ok (my $group = RT::Group->new(RT->SystemUser), "instantiated a group object"); ok (my ($id, $msg) = $group->CreateUserDefinedGroup( Name => 'TestGroup', Description => 'A test group', ), 'Created a new group'); isnt ($id , 0, "Group id is $id"); is ($group->Name , 'TestGroup', "The group's name is 'TestGroup'"); -my $ng = RT::Group->new($RT::SystemUser); +my $ng = RT::Group->new(RT->SystemUser); ok($ng->LoadUserDefinedGroup('TestGroup'), "Loaded testgroup"); is($ng->id , $group->id, "Loaded the right group"); @@ -30,7 +29,7 @@ ok($id, $msg); # Group 1 now has members 1, 2 ,3 -my $group_2 = RT::Group->new($RT::SystemUser); +my $group_2 = RT::Group->new(RT->SystemUser); ok (my ($id_2, $msg_2) = $group_2->CreateUserDefinedGroup( Name => 'TestGroup2', Description => 'A second test group'), , 'Created a new group'); isnt ($id_2 , 0, "Created group 2 ok- $msg_2 "); ok (($id,$msg) = $group_2->AddMember($ng->PrincipalId), "Made TestGroup a member of testgroup2"); @@ -40,7 +39,7 @@ ok($id, $msg); # Group 2 how has 1, g1->{1, 2,3} -my $group_3 = RT::Group->new($RT::SystemUser); +my $group_3 = RT::Group->new(RT->SystemUser); ok (my ($id_3, $msg_3) = $group_3->CreateUserDefinedGroup( Name => 'TestGroup3', Description => 'A second test group'), 'Created a new group'); isnt ($id_3 , 0, "Created group 3 ok - $msg_3"); ok (($id,$msg) =$group_3->AddMember($group_2->PrincipalId), "Made TestGroup a member of testgroup2"); @@ -48,10 +47,10 @@ ok($id, $msg); # g3 now has g2->{1, g1->{1,2,3}} -my $principal_1 = RT::Principal->new($RT::SystemUser); +my $principal_1 = RT::Principal->new(RT->SystemUser); $principal_1->Load('1'); -my $principal_2 = RT::Principal->new($RT::SystemUser); +my $principal_2 = RT::Principal->new(RT->SystemUser); $principal_2->Load('2'); ok (($id,$msg) = $group_3->AddMember('1' ), "Added member RT_System to the group TestGroup2"); @@ -81,14 +80,13 @@ is($group_2->HasMemberRecursively($principal_2), undef, "group 2 doesn't have me is($ng->HasMember($principal_2), undef, "group 1 doesn't have member 2"); is($group_3->HasMemberRecursively($principal_2), undef, "group 3 has member 2 recursively"); -# }}} } { -ok(my $u = RT::Group->new($RT::SystemUser)); +ok(my $u = RT::Group->new(RT->SystemUser)); ok($u->Load(4), "Loaded the first user"); is($u->PrincipalObj->ObjectId , 4, "user 4 is the fourth principal"); is($u->PrincipalObj->PrincipalType , 'Group' , "Principal 4 is a group"); @@ -96,4 +94,3 @@ is($u->PrincipalObj->PrincipalType , 'Group' , "Principal 4 is a group"); } -1; diff --git a/rt/t/api/groups.t b/rt/t/api/groups.t index 995c844ba..d2dc126dc 100644 --- a/rt/t/api/groups.t +++ b/rt/t/api/groups.t @@ -1,87 +1,73 @@ - use strict; use warnings; -use RT; -use RT::Test tests => 28; +use RT::Test nodata => 1, tests => 27; +RT::Group->AddRights( + 'RTxGroupRight' => 'Just a right for testing rights', +); { - -ok (require RT::Groups); - - + my $g = RT::Group->new(RT->SystemUser); + my ($id, $msg) = $g->CreateUserDefinedGroup(Name => 'GroupsNotEqualTest'); + ok ($id, "created group #". $g->id) or diag("error: $msg"); + + my $groups = RT::Groups->new(RT->SystemUser); + $groups->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $g->id ); + $groups->LimitToUserDefinedGroups(); + my $bug = grep $_->id == $g->id, @{$groups->ItemsArrayRef}; + ok (!$bug, "didn't find group"); } -{ - -# next had bugs -# Groups->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => xx ); -my $g = RT::Group->new($RT::SystemUser); -my ($id, $msg) = $g->CreateUserDefinedGroup(Name => 'GroupsNotEqualTest'); -ok ($id, "created group #". $g->id) or diag("error: $msg"); - -my $groups = RT::Groups->new($RT::SystemUser); -$groups->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $g->id ); -$groups->LimitToUserDefinedGroups(); -my $bug = grep $_->id == $g->id, @{$groups->ItemsArrayRef}; -ok (!$bug, "didn't find group"); - - -} { - -my $u = RT::User->new($RT::SystemUser); -my ($id, $msg) = $u->Create( Name => 'Membertests'. $$ ); -ok ($id, 'created user') or diag "error: $msg"; - -my $g = RT::Group->new($RT::SystemUser); -($id, $msg) = $g->CreateUserDefinedGroup(Name => 'Membertests'); -ok ($id, $msg); - -my ($aid, $amsg) =$g->AddMember($u->id); -ok ($aid, $amsg); -ok($g->HasMember($u->PrincipalObj),"G has member u"); - -my $groups = RT::Groups->new($RT::SystemUser); -$groups->LimitToUserDefinedGroups(); -$groups->WithMember(PrincipalId => $u->id); -is ($groups->Count , 1,"found the 1 group - " . $groups->Count); -is ($groups->First->Id , $g->Id, "it's the right one"); - - + my $u = RT::User->new(RT->SystemUser); + my ($id, $msg) = $u->Create( Name => 'Membertests'. $$ ); + ok ($id, 'created user') or diag "error: $msg"; + + my $g = RT::Group->new(RT->SystemUser); + ($id, $msg) = $g->CreateUserDefinedGroup(Name => 'Membertests'); + ok ($id, $msg); + + my ($aid, $amsg) =$g->AddMember($u->id); + ok ($aid, $amsg); + ok($g->HasMember($u->PrincipalObj),"G has member u"); + + my $groups = RT::Groups->new(RT->SystemUser); + $groups->LimitToUserDefinedGroups(); + $groups->WithMember(PrincipalId => $u->id); + is ($groups->Count , 1,"found the 1 group - " . $groups->Count); + is ($groups->First->Id , $g->Id, "it's the right one"); } -{ - no warnings qw/redefine once/; +no warnings qw/redefine once/; -my $q = RT::Queue->new($RT::SystemUser); +my $q = RT::Queue->new(RT->SystemUser); my ($id, $msg) =$q->Create( Name => 'GlobalACLTest'); ok ($id, $msg); -my $testuser = RT::User->new($RT::SystemUser); +my $testuser = RT::User->new(RT->SystemUser); ($id,$msg) = $testuser->Create(Name => 'JustAnAdminCc'); ok ($id,$msg); -my $global_admin_cc = RT::Group->new($RT::SystemUser); +my $global_admin_cc = RT::Group->new(RT->SystemUser); $global_admin_cc->LoadSystemRoleGroup('AdminCc'); ok($global_admin_cc->id, "Found the global admincc group"); -my $groups = RT::Groups->new($RT::SystemUser); +my $groups = RT::Groups->new(RT->SystemUser); $groups->WithRight(Right => 'OwnTicket', Object => $q); is($groups->Count, 1); -($id, $msg) = $global_admin_cc->PrincipalObj->GrantRight(Right =>'OwnTicket', Object=> $RT::System); +($id, $msg) = $global_admin_cc->PrincipalObj->GrantRight(Right =>'OwnTicket', Object=> RT->System); ok ($id,$msg); ok (!$testuser->HasRight(Object => $q, Right => 'OwnTicket') , "The test user does not have the right to own tickets in the test queue"); ($id, $msg) = $q->AddWatcher(Type => 'AdminCc', PrincipalId => $testuser->id); ok($id,$msg); ok ($testuser->HasRight(Object => $q, Right => 'OwnTicket') , "The test user does have the right to own tickets now. thank god."); -$groups = RT::Groups->new($RT::SystemUser); +$groups = RT::Groups->new(RT->SystemUser); $groups->WithRight(Right => 'OwnTicket', Object => $q); ok ($id,$msg); is($groups->Count, 3); -my $RTxGroup = RT::Group->new($RT::SystemUser); +my $RTxGroup = RT::Group->new(RT->SystemUser); ($id, $msg) = $RTxGroup->CreateUserDefinedGroup( Name => 'RTxGroup', Description => "RTx extension group"); ok ($id,$msg); is ($RTxGroup->id, $id, "group loaded"); @@ -90,7 +76,7 @@ my $RTxSysObj = {}; bless $RTxSysObj, 'RTx::System'; *RTx::System::Id = sub { 1; }; *RTx::System::id = *RTx::System::Id; -my $ace = RT::Record->new($RT::SystemUser); +my $ace = RT::Record->new(RT->SystemUser); $ace->Table('ACL'); $ace->_BuildTableAttributes unless ($RT::Record::_TABLE_ATTR->{ref($ace)}); ($id, $msg) = $ace->Create( PrincipalId => $RTxGroup->id, PrincipalType => 'Group', RightName => 'RTxGroupRight', ObjectType => 'RTx::System', ObjectId => 1); @@ -101,19 +87,19 @@ bless $RTxObj, 'RTx::System::Record'; *RTx::System::Record::Id = sub { 4; }; *RTx::System::Record::id = *RTx::System::Record::Id; -$groups = RT::Groups->new($RT::SystemUser); +$groups = RT::Groups->new(RT->SystemUser); $groups->WithRight(Right => 'RTxGroupRight', Object => $RTxSysObj); is($groups->Count, 1, "RTxGroupRight found for RTxSysObj"); -$groups = RT::Groups->new($RT::SystemUser); +$groups = RT::Groups->new(RT->SystemUser); $groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj); is($groups->Count, 0, "RTxGroupRight not found for RTxObj"); -$groups = RT::Groups->new($RT::SystemUser); +$groups = RT::Groups->new(RT->SystemUser); $groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj, EquivObjects => [ $RTxSysObj ]); is($groups->Count, 1, "RTxGroupRight found for RTxObj using EquivObjects"); -$ace = RT::Record->new($RT::SystemUser); +$ace = RT::Record->new(RT->SystemUser); $ace->Table('ACL'); $ace->_BuildTableAttributes unless ($RT::Record::_TABLE_ATTR->{ref($ace)}); ($id, $msg) = $ace->Create( PrincipalId => $RTxGroup->id, PrincipalType => 'Group', RightName => 'RTxGroupRight', ObjectType => 'RTx::System::Record', ObjectId => 5 ); @@ -123,17 +109,10 @@ my $RTxObj2 = {}; bless $RTxObj2, 'RTx::System::Record'; *RTx::System::Record::Id = sub { 5; }; -$groups = RT::Groups->new($RT::SystemUser); +$groups = RT::Groups->new(RT->SystemUser); $groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj2); is($groups->Count, 1, "RTxGroupRight found for RTxObj2"); -$groups = RT::Groups->new($RT::SystemUser); +$groups = RT::Groups->new(RT->SystemUser); $groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj2, EquivObjects => [ $RTxSysObj ]); is($groups->Count, 1, "RTxGroupRight found for RTxObj2"); - - - - -} - -1; diff --git a/rt/t/api/has_rights.t b/rt/t/api/has_rights.t new file mode 100644 index 000000000..990fc0185 --- /dev/null +++ b/rt/t/api/has_rights.t @@ -0,0 +1,43 @@ +use RT::Test nodata => 1, tests => 9; + +use strict; +use warnings; + +my $queue = RT::Test->load_or_create_queue( Name => 'A' ); +ok $queue && $queue->id, 'loaded or created queue_a'; +my $qid = $queue->id; + +my $user = RT::Test->load_or_create_user( + Name => 'user', + Password => 'password', + EmailAddress => 'test@example.com', +); +ok $user && $user->id, 'loaded or created user'; + +{ + cleanup(); + RT::Test->set_rights( + { Principal => 'Everyone', Right => [qw(SeeQueue)] }, + { Principal => 'Cc', Right => [qw(ShowTicket)] }, + ); + my ($t) = RT::Test->create_tickets( + { Queue => $queue->id }, + { }, + ); + my $rights = $user->PrincipalObj->HasRights( Object => $t ); + is_deeply( $rights, { SeeQueue => 1 }, 'got it' ); + + ($t) = RT::Test->create_tickets( + { Queue => $queue->id }, + { Cc => $user->EmailAddress }, + ); + ok($t->Cc->HasMember( $user->id ), 'user is cc'); + $rights = $user->PrincipalObj->HasRights( Object => $t ); + is_deeply( $rights, { SeeQueue => 1, ShowTicket => 1 }, 'got it' ) +} + +sub cleanup { + RT::Test->delete_tickets( "Queue = $qid" ); + RT::Test->delete_queue_watchers( $queue ); +}; + diff --git a/rt/t/api/i18n.t b/rt/t/api/i18n.t index 17d71b761..831532b90 100644 --- a/rt/t/api/i18n.t +++ b/rt/t/api/i18n.t @@ -2,7 +2,7 @@ use strict; use warnings; use RT; -use RT::Test tests => 9; +use RT::Test nodb => 1, tests => 9; { @@ -27,4 +27,3 @@ is($en->encoding , 'utf-8', "The encoding ".$en->encoding." is 'utf-8'"); } -1; diff --git a/rt/t/api/i18n_guess.t b/rt/t/api/i18n_guess.t new file mode 100644 index 000000000..139ec1acd --- /dev/null +++ b/rt/t/api/i18n_guess.t @@ -0,0 +1,71 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 16; + +use Encode qw(encode); + +use constant HAS_ENCODE_GUESS => do { local $@; eval { require Encode::Guess; 1 } }; +use constant HAS_ENCODE_DETECT => do { local $@; eval { require Encode::Detect::Detector; 1 } }; + +my $string = "\x{442}\x{435}\x{441}\x{442} \x{43f}\x{43e}\x{434}\x{434}\x{435}\x{440}\x{436}\x{43a}\x{430}"; + +sub guess { + local $Test::Builder::Level = $Test::Builder::Level + 1; + is( RT::I18N::_GuessCharset( Encode::encode($_[0], $_[1]) ), $_[2] || $_[0], "$_[0] guesses as @{[$_[2]||$_[0]]}" ); +} + +RT->Config->Set(EmailInputEncodings => qw(*)); +SKIP: { + skip "No Encode::Detect", 3 unless HAS_ENCODE_DETECT; + guess('utf-8', $string); + guess('cp1251', $string); + guess('koi8-r', $string); +} + +RT->Config->Set(EmailInputEncodings => qw(UTF-8 cp1251 koi8-r)); +SKIP: { + skip "No Encode::Guess", 4 unless HAS_ENCODE_GUESS; + guess('utf-8', $string); + guess('cp1251', $string); + guess('windows-1251', $string, 'cp1251'); + { + local $TODO = "Encode::Guess can't distinguish cp1251 from koi8-r"; + guess('koi8-r', $string); + } +} + +RT->Config->Set(EmailInputEncodings => qw(UTF-8 koi8-r cp1251)); +SKIP: { + skip "No Encode::Guess", 3 unless HAS_ENCODE_GUESS; + guess('utf-8', $string); + guess('koi8-r', $string); + { + local $TODO = "Encode::Guess can't distinguish cp1251 from koi8-r"; + guess('cp1251', $string); + } +} + +# windows-1251 is an alias for cp1251, post load check cleanups array for us +RT->Config->Set(EmailInputEncodings => qw(UTF-8 windows-1251 koi8-r)); +RT->Config->PostLoadCheck; +SKIP: { + skip "No Encode::Guess", 3 unless HAS_ENCODE_GUESS; + guess('utf-8', $string); + guess('cp1251', $string); + { + local $TODO = "Encode::Guess can't distinguish cp1251 from koi8-r"; + guess('koi8-r', $string); + } +} + +RT->Config->Set(EmailInputEncodings => qw(* UTF-8 cp1251 koi8-r)); +SKIP: { + skip "No Encode::Detect", 3 unless HAS_ENCODE_DETECT; + guess('utf-8', $string); + guess('cp1251', $string); + guess('koi8-r', $string); +} + diff --git a/rt/t/api/link.t b/rt/t/api/link.t index eac9ae29a..a9e54a716 100644 --- a/rt/t/api/link.t +++ b/rt/t/api/link.t @@ -1,13 +1,12 @@ - use strict; use warnings; -use RT::Test tests => 77; +use RT::Test nodata => 1, tests => 84; use RT::Test::Web; +use Test::Warn; use RT::Link; -my $link = RT::Link->new($RT::SystemUser); - +my $link = RT::Link->new(RT->SystemUser); ok (ref $link); isa_ok( $link, 'RT::Link'); @@ -18,29 +17,37 @@ isa_ok( $link, 'DBIx::SearchBuilder::Record'); my $queue = RT::Test->load_or_create_queue(Name => 'General'); ok($queue->Id, "loaded the General queue"); -my $parent = RT::Ticket->new($RT::SystemUser); +my $parent = RT::Ticket->new(RT->SystemUser); my ($pid, undef, $msg) = $parent->Create( Queue => $queue->id, Subject => 'parent', ); ok $pid, 'created a ticket #'. $pid or diag "error: $msg"; -my $child = RT::Ticket->new($RT::SystemUser); -my ($cid, undef, $msg) = $child->Create( +my $child = RT::Ticket->new(RT->SystemUser); +((my $cid), undef, $msg) = $child->Create( Queue => $queue->id, Subject => 'child', ); ok $cid, 'created a ticket #'. $cid or diag "error: $msg"; { + my ($status, $msg); clean_links(); - my ($status, $msg) = $parent->AddLink; + + warning_like { + ($status, $msg) = $parent->AddLink; + } qr/Base or Target must be specified/, "warned about linking a ticket to itself"; ok(!$status, "didn't create a link: $msg"); - ($status, $msg) = $parent->AddLink( Base => $parent->id ); + warning_like { + ($status, $msg) = $parent->AddLink( Base => $parent->id ); + } qr/Can't link a ticket to itself/, "warned about linking a ticket to itself"; ok(!$status, "didn't create a link: $msg"); - ($status, $msg) = $parent->AddLink( Base => $parent->id, Type => 'HasMember' ); + warning_like { + ($status, $msg) = $parent->AddLink( Base => $parent->id, Type => 'HasMember' ); + } qr/Can't link a ticket to itself/, "warned about linking a ticket to itself"; ok(!$status, "didn't create a link: $msg"); } @@ -197,8 +204,39 @@ ok $cid, 'created a ticket #'. $cid or diag "error: $msg"; ; } +{ + clean_links(); + $child->SetStatus('deleted'); + + my ($status, $msg) = $parent->AddLink( + Type => 'MemberOf', Base => $child->id, + ); + ok(!$status, "can't link to deleted ticket: $msg"); + + $child->SetStatus('new'); + ($status, $msg) = $parent->AddLink( + Type => 'MemberOf', Base => $child->id, + ); + ok($status, "created a link: $msg"); + + $child->SetStatus('deleted'); + my $children = $parent->Members; + $children->RedoSearch; + + my $total = 0; + $total++ while $children->Next; + is( $total, 0, 'Next skips deleted tickets' ); + + is( @{ $children->ItemsArrayRef }, + 0, 'ItemsArrayRef skips deleted tickets' ); + + # back to active status + $child->SetStatus('new'); +} + sub clean_links { - my $links = RT::Links->new( $RT::SystemUser ); + my $links = RT::Links->new( RT->SystemUser ); + $links->UnLimit; while ( my $link = $links->Next ) { my ($status, $msg) = $link->Delete; $RT::Logger->error("Couldn't delete a link: $msg") @@ -206,4 +244,3 @@ sub clean_links { } } -1; diff --git a/rt/t/api/password-types.t b/rt/t/api/password-types.t index 267a6ede4..5f253d51e 100644 --- a/rt/t/api/password-types.t +++ b/rt/t/api/password-types.t @@ -5,27 +5,37 @@ use warnings; use RT::Test; use Digest::MD5; +my $default = "sha512"; + my $root = RT::User->new(RT->SystemUser); $root->Load("root"); -# Salted truncated SHA-256 +# Salted SHA-512 (default) my $old = $root->__Value("Password"); -is(length($old), 40, "Stored as truncated salted SHA-256"); +like($old, qr/^\!$default\!/, "Stored as salted $default"); ok($root->IsPassword("password")); is($root->__Value("Password"), $old, "Unchanged after password check"); # Crypt $root->_Set( Field => "Password", Value => crypt("something", "salt")); ok($root->IsPassword("something"), "crypt()ed password works"); -is(length($root->__Value("Password")), 40, "And is now upgraded to truncated salted SHA-256"); +like($root->__Value("Password"), qr/^\!$default\!/, "And is now upgraded to salted $default"); # MD5, hex $root->_Set( Field => "Password", Value => Digest::MD5::md5_hex("changed")); ok($root->IsPassword("changed"), "Unsalted MD5 hex works"); -is(length($root->__Value("Password")), 40, "And is now upgraded to truncated salted SHA-256"); +like($root->__Value("Password"), qr/^\!$default\!/, "And is now upgraded to salted $default"); # MD5, base64 $root->_Set( Field => "Password", Value => Digest::MD5::md5_base64("new")); ok($root->IsPassword("new"), "Unsalted MD5 base64 works"); -is(length($root->__Value("Password")), 40, "And is now upgraded to truncated salted SHA-256"); +like($root->__Value("Password"), qr/^\!$default\!/, "And is now upgraded to salted $default"); +# Salted truncated SHA-256 +my $trunc = MIME::Base64::encode_base64( + "salt" . substr(Digest::SHA::sha256("salt".Digest::MD5::md5("secret")),0,26), + "" +); +$root->_Set( Field => "Password", Value => $trunc); +ok($root->IsPassword("secret"), "Unsalted MD5 base64 works"); +like($root->__Value("Password"), qr/^\!$default\!/, "And is now upgraded to salted $default"); diff --git a/rt/t/api/queue.t b/rt/t/api/queue.t index 44d5cafce..07b8ed479 100644 --- a/rt/t/api/queue.t +++ b/rt/t/api/queue.t @@ -2,7 +2,7 @@ use strict; use warnings; use RT; -use RT::Test tests => 24; +use RT::Test nodata => 1, tests => 24; { @@ -14,7 +14,7 @@ use RT::Queue; { -my $q = RT::Queue->new($RT::SystemUser); +my $q = RT::Queue->new(RT->SystemUser); is($q->IsValidStatus('new'), 1, 'New is a valid status'); is($q->IsValidStatus('f00'), 0, 'f00 is not a valid status'); @@ -23,7 +23,7 @@ is($q->IsValidStatus('f00'), 0, 'f00 is not a valid status'); { -my $q = RT::Queue->new($RT::SystemUser); +my $q = RT::Queue->new(RT->SystemUser); is($q->IsActiveStatus('new'), 1, 'New is a Active status'); is($q->IsActiveStatus('rejected'), 0, 'Rejected is an inactive status'); is($q->IsActiveStatus('f00'), 0, 'f00 is not a Active status'); @@ -33,7 +33,7 @@ is($q->IsActiveStatus('f00'), 0, 'f00 is not a Active status'); { -my $q = RT::Queue->new($RT::SystemUser); +my $q = RT::Queue->new(RT->SystemUser); is($q->IsInactiveStatus('new'), 0, 'New is a Active status'); is($q->IsInactiveStatus('rejected'), 1, 'rejeected is an Inactive status'); is($q->IsInactiveStatus('f00'), 0, 'f00 is not a Active status'); @@ -43,7 +43,7 @@ is($q->IsInactiveStatus('f00'), 0, 'f00 is not a Active status'); { -my $queue = RT::Queue->new($RT::SystemUser); +my $queue = RT::Queue->new(RT->SystemUser); my ($id, $val) = $queue->Create( Name => 'Test1'); ok($id, $val); @@ -55,10 +55,10 @@ ok(!$id, $val); { -my $Queue = RT::Queue->new($RT::SystemUser); +my $Queue = RT::Queue->new(RT->SystemUser); my ($id, $msg) = $Queue->Create(Name => "Foo"); ok ($id, "Foo $id was created"); -ok(my $group = RT::Group->new($RT::SystemUser)); +ok(my $group = RT::Group->new(RT->SystemUser)); ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Requestor')); ok ($group->Id, "Found the requestors object for this Queue"); @@ -67,7 +67,7 @@ ok ($group->Id, "Found the requestors object for this Queue"); ok ($status, "Added bob at fsck.com as a requestor") or diag "error: $msg"; } -ok(my $bob = RT::User->new($RT::SystemUser), "Creating a bob rt::user"); +ok(my $bob = RT::User->new(RT->SystemUser), "Creating a bob rt::user"); $bob->LoadByEmail('bob@fsck.com'); ok($bob->Id, "Found the bob rt user"); ok ($Queue->IsWatcher(Type => 'Cc', PrincipalId => $bob->PrincipalId), "The Queue actually has bob at fsck.com as a requestor"); @@ -79,14 +79,13 @@ ok ($Queue->IsWatcher(Type => 'Cc', PrincipalId => $bob->PrincipalId), "The Queu "The Queue no longer has bob at fsck.com as a requestor"); } -$group = RT::Group->new($RT::SystemUser); +$group = RT::Group->new(RT->SystemUser); ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc')); ok ($group->Id, "Found the cc object for this Queue"); -$group = RT::Group->new($RT::SystemUser); +$group = RT::Group->new(RT->SystemUser); ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'AdminCc')); ok ($group->Id, "Found the AdminCc object for this Queue"); } -1; diff --git a/rt/t/api/record.t b/rt/t/api/record.t index 6bf1af81e..4b6b0b89c 100644 --- a/rt/t/api/record.t +++ b/rt/t/api/record.t @@ -14,8 +14,8 @@ ok (require RT::Record); { -my $ticket = RT::Ticket->new($RT::SystemUser); -my $group = RT::Group->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); +my $group = RT::Group->new(RT->SystemUser); is($ticket->ObjectTypeStr, 'Ticket', "Ticket returns correct typestring"); is($group->ObjectTypeStr, 'Group', "Group returns correct typestring"); @@ -24,14 +24,14 @@ is($group->ObjectTypeStr, 'Group', "Group returns correct typestring"); { -my $t1 = RT::Ticket->new($RT::SystemUser); +my $t1 = RT::Ticket->new(RT->SystemUser); my ($id, $trans, $msg) = $t1->Create(Subject => 'DepTest1', Queue => 'general'); ok($id, "Created dep test 1 - $msg"); -my $t2 = RT::Ticket->new($RT::SystemUser); +my $t2 = RT::Ticket->new(RT->SystemUser); (my $id2, $trans, my $msg2) = $t2->Create(Subject => 'DepTest2', Queue => 'general'); ok($id2, "Created dep test 2 - $msg2"); -my $t3 = RT::Ticket->new($RT::SystemUser); +my $t3 = RT::Ticket->new(RT->SystemUser); (my $id3, $trans, my $msg3) = $t3->Create(Subject => 'DepTest3', Queue => 'general', Type => 'approval'); ok($id3, "Created dep test 3 - $msg3"); my ($addid, $addmsg); @@ -40,7 +40,7 @@ ok ($addid, $addmsg); ok (($addid, $addmsg) =$t1->AddLink( Type => 'DependsOn', Target => $t3->id)); ok ($addid, $addmsg); -my $link = RT::Link->new($RT::SystemUser); +my $link = RT::Link->new(RT->SystemUser); (my $rv, $msg) = $link->Load($addid); ok ($rv, $msg); is ($link->LocalTarget , $t3->id, "Link LocalTarget is correct"); @@ -52,19 +52,18 @@ ok ($t1->HasUnresolvedDependencies( Type => 'approval' ), "Ticket ".$t1->Id." ha ok (!$t2->HasUnresolvedDependencies, "Ticket ".$t2->Id." has no unresolved deps"); ; -my ($rid, $rmsg)= $t1->Resolve(); +my ($rid, $rmsg)= $t1->SetStatus('resolved'); ok(!$rid, $rmsg); -my ($rid2, $rmsg2) = $t2->Resolve(); +my ($rid2, $rmsg2) = $t2->SetStatus('resolved'); ok ($rid2, $rmsg2); -($rid, $rmsg)= $t1->Resolve(); +($rid, $rmsg)= $t1->SetStatus('resolved'); ok(!$rid, $rmsg); -my ($rid3,$rmsg3) = $t3->Resolve; +my ($rid3,$rmsg3) = $t3->SetStatus('resolved'); ok ($rid3,$rmsg3); -($rid, $rmsg)= $t1->Resolve(); +($rid, $rmsg)= $t1->SetStatus('resolved'); ok($rid, $rmsg); } -1; diff --git a/rt/t/api/reminders.t b/rt/t/api/reminders.t index fd1c6a69f..b035fa8de 100644 --- a/rt/t/api/reminders.t +++ b/rt/t/api/reminders.t @@ -10,7 +10,7 @@ use RT::Test tests => 20; # Create test queues use_ok ('RT::Queue'); -ok(my $testqueue = RT::Queue->new($RT::SystemUser), 'Instantiate RT::Queue'); +ok(my $testqueue = RT::Queue->new(RT->SystemUser), 'Instantiate RT::Queue'); ok($testqueue->Create( Name => 'reminders tests'), 'Create new queue: reminders tests'); isnt($testqueue->Id , 0, 'Success creating queue'); @@ -20,10 +20,10 @@ isnt($testqueue->Id , 0, 'Success creating queue'); # Create test ticket use_ok('RT::Ticket'); -my $u = RT::User->new($RT::SystemUser); +my $u = RT::User->new(RT->SystemUser); $u->Load("root"); ok ($u->Id, "Found the root user"); -ok(my $t = RT::Ticket->new($RT::SystemUser), 'Instantiate RT::Ticket'); +ok(my $t = RT::Ticket->new(RT->SystemUser), 'Instantiate RT::Ticket'); ok(my ($id, $msg) = $t->Create( Queue => $testqueue->Id, Subject => 'Testing', Owner => $u->Id @@ -31,7 +31,7 @@ ok(my ($id, $msg) = $t->Create( Queue => $testqueue->Id, isnt($id , 0, 'Success creating ticket'); # Add reminder -my $due_obj = RT::Date->new( $RT::SystemUser ); +my $due_obj = RT::Date->new( RT->SystemUser ); $due_obj->SetToNow; ok(my ( $add_id, $add_msg, $txnid ) = $t->Reminders->Add( Subject => 'TestReminder', @@ -85,4 +85,4 @@ while ( my $reminder = $reminders->Next ) { is($r_resolved, 1, 'Reminder resolved'); } -1; + diff --git a/rt/t/api/rights.t b/rt/t/api/rights.t index a38bcea0c..a1795ca0a 100644 --- a/rt/t/api/rights.t +++ b/rt/t/api/rights.t @@ -47,14 +47,14 @@ # # END BPS TAGGED BLOCK }}} -use RT::Test tests => 30; +use RT::Test nodata => 1, tests => 30; use strict; use warnings; # clear all global right { - my $acl = RT::ACL->new($RT::SystemUser); + my $acl = RT::ACL->new(RT->SystemUser); $acl->Limit( FIELD => 'RightName', OPERATOR => '!=', VALUE => 'SuperUser' ); $acl->LimitToObject( $RT::System ); while( my $ace = $acl->Next ) { @@ -81,11 +81,11 @@ ok $user && $user->id, 'loaded or created user'; } { - my $group = RT::Group->new( $RT::SystemUser ); + my $group = RT::Group->new( RT->SystemUser ); ok( $group->LoadQueueRoleGroup( Queue => $queue->id, Type=> 'Owner' ), "load queue owners role group" ); - my $ace = RT::ACE->new( $RT::SystemUser ); + my $ace = RT::ACE->new( RT->SystemUser ); my ($ace_id, $msg) = $group->PrincipalObj->GrantRight( Right => 'ReplyToTicket', Object => $queue ); @@ -101,10 +101,10 @@ ok $user && $user->id, 'loaded or created user'; my $ticket; { # new ticket - $ticket = RT::Ticket->new($RT::SystemUser); + $ticket = RT::Ticket->new(RT->SystemUser); my ($ticket_id) = $ticket->Create( Queue => $queue->id, Subject => 'test'); ok( $ticket_id, 'new ticket created' ); - is( $ticket->Owner, $RT::Nobody->Id, 'owner of the new ticket is nobody' ); + is( $ticket->Owner, RT->Nobody->Id, 'owner of the new ticket is nobody' ); ok( !$user->HasRight( Right => 'OwnTicket', Object => $ticket ), "user can't reply to ticket" @@ -136,11 +136,11 @@ my $ticket; { # Testing of EquivObjects - my $group = RT::Group->new( $RT::SystemUser ); + my $group = RT::Group->new( RT->SystemUser ); ok( $group->LoadQueueRoleGroup( Queue => $queue->id, Type=> 'AdminCc' ), "load queue AdminCc role group" ); - my $ace = RT::ACE->new( $RT::SystemUser ); + my $ace = RT::ACE->new( RT->SystemUser ); my ($ace_id, $msg) = $group->PrincipalObj->GrantRight( Right => 'ModifyTicket', Object => $queue ); @@ -165,7 +165,7 @@ my $ticket; my $ticket2; { - $ticket2 = RT::Ticket->new($RT::SystemUser); + $ticket2 = RT::Ticket->new(RT->SystemUser); my ($id) = $ticket2->Create( Queue => $queue->id, Subject => 'test2'); ok( $id, 'new ticket created' ); ok( !$user->HasRight( Right => 'ModifyTicket', Object => $ticket2 ), diff --git a/rt/t/api/rights_show_ticket.t b/rt/t/api/rights_show_ticket.t index 3e1d0740f..62f62c422 100644 --- a/rt/t/api/rights_show_ticket.t +++ b/rt/t/api/rights_show_ticket.t @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -use RT::Test tests => 264; +use RT::Test nodata => 1, tests => 264; use strict; use warnings; @@ -228,7 +228,7 @@ sub create_tickets_set{ my @res; foreach my $q ($queue_a, $queue_b) { foreach my $n (1 .. 2) { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); my ($tid) = $ticket->Create( Queue => $q->id, Subject => $q->Name .' - '. $n ); @@ -239,24 +239,8 @@ sub create_tickets_set{ return @res; } -sub cleanup { delete_tickets(); delete_watchers() }; - -sub delete_tickets { - my $tickets = RT::Tickets->new( $RT::SystemUser ); - $tickets->FromSQL( "Queue = $qa_id OR Queue = $qb_id" ); - while ( my $ticket = $tickets->Next ) { - $ticket->Delete; - } -} - -sub delete_watchers { - foreach my $q ($queue_a, $queue_b) { - foreach my $u ($user_a, $user_b) { - foreach my $t (qw(Cc AdminCc) ) { - $q->DeleteWatcher( Type => $t, PrincipalId => $u->id ) - if $q->IsWatcher( Type => $t, PrincipalId => $u->id ); - } - } - } -} +sub cleanup { + RT::Test->delete_tickets( "Queue = $qa_id OR Queue = $qb_id" ); + RT::Test->delete_queue_watchers( $queue_a, $queue_b ); +}; diff --git a/rt/t/api/rt.t b/rt/t/api/rt.t index 3c06b5848..51c776250 100644 --- a/rt/t/api/rt.t +++ b/rt/t/api/rt.t @@ -2,17 +2,16 @@ use strict; use warnings; use RT; -use RT::Test tests => 4; +use RT::Test nodata => 1, tests => 4; { -is ($RT::Nobody->Name() , 'Nobody', "Nobody is nobody"); -isnt ($RT::Nobody->Name() , 'root', "Nobody isn't named root"); -is ($RT::SystemUser->Name() , 'RT_System', "The system user is RT_System"); -isnt ($RT::SystemUser->Name() , 'noname', "The system user isn't noname"); +is (RT->Nobody->Name() , 'Nobody', "Nobody is nobody"); +isnt (RT->Nobody->Name() , 'root', "Nobody isn't named root"); +is (RT->SystemUser->Name() , 'RT_System', "The system user is RT_System"); +isnt (RT->SystemUser->Name() , 'noname', "The system user isn't noname"); } -1; diff --git a/rt/t/api/rtname.t b/rt/t/api/rtname.t new file mode 100644 index 000000000..ef6092bb2 --- /dev/null +++ b/rt/t/api/rtname.t @@ -0,0 +1,34 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use RT::Test nodata => 1, tests => 9; + +use RT::Interface::Email; + +# normal use case, regexp set to rtname +RT->Config->Set( rtname => "site" ); +RT->Config->Set( EmailSubjectTagRegex => qr/site/ ); +RT->Config->Set( rtname => undef ); +is(RT::Interface::Email::ParseTicketId("[site #123] test"), 123); +is(RT::Interface::Email::ParseTicketId("[othersite #123] test"), undef); + +# oops usecase, where the regexp is scragged +RT->Config->Set( rtname => "site" ); +RT->Config->Set( EmailSubjectTagRegex => undef ); +is(RT::Interface::Email::ParseTicketId("[site #123] test"), 123); +is(RT::Interface::Email::ParseTicketId("[othersite #123] test"), undef); + +# set to a simple regexp. NOTE: we no longer match "site" +RT->Config->Set( rtname => "site"); +RT->Config->Set( EmailSubjectTagRegex => qr/newsite/); +is(RT::Interface::Email::ParseTicketId("[site #123] test"), undef); +is(RT::Interface::Email::ParseTicketId("[newsite #123] test"), 123); + +# set to a more complex regexp +RT->Config->Set( rtname => "site" ); +RT->Config->Set( EmailSubjectTagRegex => qr/newsite|site/ ); +is(RT::Interface::Email::ParseTicketId("[site #123] test"), 123); +is(RT::Interface::Email::ParseTicketId("[newsite #123] test"), 123); +is(RT::Interface::Email::ParseTicketId("[othersite #123] test"), undef); + diff --git a/rt/t/api/safe-run-child-util.t b/rt/t/api/safe-run-child-util.t new file mode 100644 index 000000000..b29e97177 --- /dev/null +++ b/rt/t/api/safe-run-child-util.t @@ -0,0 +1,201 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 35; +use Test::Warn; + +use RT::Util qw(safe_run_child); +use POSIX qw//; + +is_handle_ok(); + +{ + my $res = safe_run_child { return 1 }; + is $res, 1, "correct return value"; + is_handle_ok(); +} + +# test context +{ + my $context; + my $sub = sub { + if ( wantarray ) { + $context = 'array'; return 1, 2, 3; + } elsif ( defined wantarray ) { + $context = 'scalar'; return 'foo'; + } elsif ( !wantarray ) { + $context = 'void'; return; + } + }; + is_deeply [ safe_run_child { $sub->(@_) } ], [1, 2, 3]; + is $context, 'array'; + is_handle_ok(); + + is scalar safe_run_child {$sub->(@_)}, 'foo'; + is $context, 'scalar'; + is_handle_ok(); + + safe_run_child {$sub->(@_)}; + is $context, 'void'; + is_handle_ok(); +} + +# fork+child returns +{ + my $res = safe_run_child { + if (fork) { wait; return 'parent' } + + open my $fh, '>', RT::Test->temp_directory .'/tttt'; + print $fh "child"; + close $fh; + + return 'child'; + }; + is $res, 'parent', "correct return value"; + is( RT::Test->file_content([RT::Test->temp_directory, 'tttt'], unlink => 1 ), + 'child', + 'correct file content', + ); + is_handle_ok(); +} + +# fork+child dies +{ + warning_like { + my $res = safe_run_child { + if (fork) { wait; return 'parent' } + + open my $fh, '>', RT::Test->temp_directory .'/tttt'; + print $fh "child"; + close $fh; + + die 'child'; + }; + is $res, 'parent', "correct return value"; + is( RT::Test->file_content([RT::Test->temp_directory, 'tttt'], unlink => 1 ), + 'child', + 'correct file content', + ); + } qr/System Error: child/; + is_handle_ok(); +} + +# fork+child exits +{ + my $res = safe_run_child { + if (fork) { wait; return 'parent' } + + open my $fh, '>', RT::Test->temp_directory .'/tttt'; + print $fh "child"; + close $fh; + + exit 0; + }; + is $res, 'parent', "correct return value"; + is( RT::Test->file_content([RT::Test->temp_directory, 'tttt'], unlink => 1 ), + 'child', + 'correct file content', + ); + is_handle_ok(); +} + +# parent dies +{ + my $res = eval { safe_run_child { die 'parent'; } }; + is $res, undef, "correct return value"; + like $@, qr'System Error: parent', "correct error message value"; + is_handle_ok(); +} + +# fork+exec +{ + my $script = RT::Test->temp_directory .'/true.pl'; + open my $fh, '>', $script; + print $fh <<END; +#!$^X + +open my \$fh, '>', '$script.res'; +print \$fh "child"; +close \$fh; + +exit 0; +END + close $fh; + chmod 0777, $script; + + my $res = safe_run_child { + if (fork) { wait; return 'parent' } + exec $script; + }; + is $res, 'parent', "correct return value"; + is( RT::Test->file_content([$script .'.res'], unlink => 1 ), + 'child', + 'correct file content', + ); + is_handle_ok(); +} + +# fork+parent that doesn't wait() +{ + require Time::HiRes; + my $start = Time::HiRes::time(); + my $pid; + + # Set up a poor man's semaphore + my $all_set = 0; + $SIG{USR1} = sub {$all_set++}; + + my $res = safe_run_child { + if ($pid = fork) { return 'parent' } + + open my $fh, '>', RT::Test->temp_directory .'/first'; + print $fh "child"; + close $fh; + # Signal that the first file is now all set; we need to do this + # to avoid a race condition + kill POSIX::SIGUSR1(), getppid(); + + sleep 5; + + open $fh, '>', RT::Test->temp_directory .'/second'; + print $fh "child"; + close $fh; + + exit 0; + }; + ok( Time::HiRes::time() - $start < 5, "Didn't wait until child finished" ); + + # Wait for up to 3 seconds to get signaled that the child has made + # the file (the USR1 will break out of the sleep()). This _should_ + # be immediate, but there's a race between the parent and child + # here, since there's no wait()'ing. There's still a tiny race + # where the signal could come in betwene the $all_set check and the + # sleep, but that just means we sleep for 3 seconds uselessly. + sleep 3 unless $all_set; + + is $res, 'parent', "correct return value"; + is( RT::Test->file_content([RT::Test->temp_directory, 'first'], unlink => 1 ), + 'child', + 'correct file content', + ); + ok( not(-f RT::Test->temp_directory.'/second'), "Second file does not exist yet"); + is_handle_ok(); + + ok(waitpid($pid,0), "Waited until child finished to reap"); + is( RT::Test->file_content([RT::Test->temp_directory, 'second'], unlink => 1 ), + 'child', + 'correct file content', + ); + is_handle_ok(); +} + +sub is_handle_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $test = $RT::Handle->dbh->selectall_arrayref( + "SELECT id FROM Users WHERE Name = 'Nobody'" + ); + ok $test && $test->[0][0], "selected, DB is there"; +} + diff --git a/rt/t/api/savedsearch.t b/rt/t/api/savedsearch.t new file mode 100644 index 000000000..0aa67eeda --- /dev/null +++ b/rt/t/api/savedsearch.t @@ -0,0 +1,181 @@ +use strict; +use warnings; +BEGIN { $ENV{'LANG'} = 'C' } + +use RT::Test tests => 27; + +use_ok('RT::SavedSearch'); +use_ok('RT::SavedSearches'); + +use Test::Warn; + +# Set up some infrastructure. These calls are tested elsewhere. + +my $searchuser = RT::User->new(RT->SystemUser); +my ($ret, $msg) = $searchuser->Create(Name => 'searchuser'.$$, + Privileged => 1, + EmailAddress => "searchuser\@p$$.example.com", + RealName => 'Search user'); +ok($ret, "created searchuser: $msg"); +$searchuser->PrincipalObj->GrantRight(Right => 'LoadSavedSearch'); +$searchuser->PrincipalObj->GrantRight(Right => 'CreateSavedSearch'); +$searchuser->PrincipalObj->GrantRight(Right => 'ModifySelf'); + +# This is the group whose searches searchuser should be able to see. +my $ingroup = RT::Group->new(RT->SystemUser); +$ingroup->CreateUserDefinedGroup(Name => 'searchgroup1'.$$); +$ingroup->AddMember($searchuser->Id); +$searchuser->PrincipalObj->GrantRight(Right => 'EditSavedSearches', + Object => $ingroup); +$searchuser->PrincipalObj->GrantRight(Right => 'ShowSavedSearches', + Object => $ingroup); + +# This is the group whose searches searchuser should not be able to see. +my $outgroup = RT::Group->new(RT->SystemUser); +$outgroup->CreateUserDefinedGroup(Name => 'searchgroup2'.$$); +$outgroup->AddMember(RT->SystemUser->Id); + +my $queue = RT::Queue->new(RT->SystemUser); +$queue->Create(Name => 'SearchQueue'.$$); +$searchuser->PrincipalObj->GrantRight(Right => 'SeeQueue', Object => $queue); +$searchuser->PrincipalObj->GrantRight(Right => 'ShowTicket', Object => $queue); +$searchuser->PrincipalObj->GrantRight(Right => 'OwnTicket', Object => $queue); + + +my $ticket = RT::Ticket->new(RT->SystemUser); +$ticket->Create(Queue => $queue->Id, + Requestor => [ $searchuser->Name ], + Owner => $searchuser, + Subject => 'saved search test'); + + +# Now start the search madness. +my $curruser = RT::CurrentUser->new($searchuser); +my $format = '\' <b><a href="/Ticket/Display.html?id=__id__">__id__</a></b>/TITLE:#\', +\'<b><a href="/Ticket/Display.html?id=__id__">__Subject__</a></b>/TITLE:Subject\', +\'__Status__\', +\'__QueueName__\', +\'__OwnerName__\', +\'__Priority__\', +\'__NEWLINE__\', +\'\', +\'<small>__Requestors__</small>\', +\'<small>__CreatedRelative__</small>\', +\'<small>__ToldRelative__</small>\', +\'<small>__LastUpdatedRelative__</small>\', +\'<small>__TimeLeft__</small>\''; + +my $mysearch = RT::SavedSearch->new($curruser); +($ret, $msg) = $mysearch->Save(Privacy => 'RT::User-' . $searchuser->Id, + Type => 'Ticket', + Name => 'owned by me', + SearchParams => {'Format' => $format, + 'Query' => "Owner = '" + . $searchuser->Name + . "'"}); +ok($ret, "mysearch was created"); + + +my $groupsearch = RT::SavedSearch->new($curruser); +($ret, $msg) = $groupsearch->Save(Privacy => 'RT::Group-' . $ingroup->Id, + Type => 'Ticket', + Name => 'search queue', + SearchParams => {'Format' => $format, + 'Query' => "Queue = '" + . $queue->Name . "'"}); +ok($ret, "groupsearch was created"); + +my $othersearch = RT::SavedSearch->new($curruser); +($ret, $msg) = $othersearch->Save(Privacy => 'RT::Group-' . $outgroup->Id, + Type => 'Ticket', + Name => 'searchuser requested', + SearchParams => {'Format' => $format, + 'Query' => + "Requestor.Name LIKE 'search'"}); +ok(!$ret, "othersearch NOT created"); +like($msg, qr/Failed to load object for/, "...for the right reason"); + +$othersearch = RT::SavedSearch->new(RT->SystemUser); +($ret, $msg) = $othersearch->Save(Privacy => 'RT::Group-' . $outgroup->Id, + Type => 'Ticket', + Name => 'searchuser requested', + SearchParams => {'Format' => $format, + 'Query' => + "Requestor.Name LIKE 'search'"}); +ok($ret, "othersearch created by systemuser"); + +# Now try to load some searches. + +# This should work. +my $loadedsearch1 = RT::SavedSearch->new($curruser); +$loadedsearch1->Load('RT::User-'.$curruser->Id, $mysearch->Id); +is($loadedsearch1->Id, $mysearch->Id, "Loaded mysearch"); +like($loadedsearch1->GetParameter('Query'), qr/Owner/, + "Retrieved query of mysearch"); +# Check through the other accessor methods. +is($loadedsearch1->Privacy, 'RT::User-' . $curruser->Id, + "Privacy of mysearch correct"); +is($loadedsearch1->Name, 'owned by me', "Name of mysearch correct"); +is($loadedsearch1->Type, 'Ticket', "Type of mysearch correct"); + +# See if it can be used to search for tickets. +my $tickets = RT::Tickets->new($curruser); +$tickets->FromSQL($loadedsearch1->GetParameter('Query')); +is($tickets->Count, 1, "Found a ticket"); + +# This should fail -- wrong object. +# my $loadedsearch2 = RT::SavedSearch->new($curruser); +# $loadedsearch2->Load('RT::User-'.$curruser->Id, $groupsearch->Id); +# isnt($loadedsearch2->Id, $othersearch->Id, "Didn't load groupsearch as mine"); +# ...but this should succeed. +my $loadedsearch3 = RT::SavedSearch->new($curruser); +$loadedsearch3->Load('RT::Group-'.$ingroup->Id, $groupsearch->Id); +is($loadedsearch3->Id, $groupsearch->Id, "Loaded groupsearch"); +like($loadedsearch3->GetParameter('Query'), qr/Queue/, + "Retrieved query of groupsearch"); +# Can it get tickets? +$tickets = RT::Tickets->new($curruser); +$tickets->FromSQL($loadedsearch3->GetParameter('Query')); +is($tickets->Count, 1, "Found a ticket"); + +# This should fail -- no permission. +my $loadedsearch4 = RT::SavedSearch->new($curruser); + +warning_like { + $loadedsearch4->Load($othersearch->Privacy, $othersearch->Id); +} qr/Could not load object RT::Group-\d+ when loading search/; + +isnt($loadedsearch4->Id, $othersearch->Id, "Did not load othersearch"); + +# Try to update an existing search. +$loadedsearch1->Update( SearchParams => {'Format' => $format, + 'Query' => "Queue = '" . $queue->Name . "'" } ); +like($loadedsearch1->GetParameter('Query'), qr/Queue/, + "Updated mysearch parameter"); +is($loadedsearch1->Type, 'Ticket', "mysearch is still for tickets"); +is($loadedsearch1->Privacy, 'RT::User-'.$curruser->Id, + "mysearch still belongs to searchuser"); +like($mysearch->GetParameter('Query'), qr/Queue/, "other mysearch object updated"); + + +## Right ho. Test the pseudo-collection object. + +my $genericsearch = RT::SavedSearch->new($curruser); +$genericsearch->Save(Name => 'generic search', + Type => 'all', + SearchParams => {'Query' => "Queue = 'General'"}); + +my $ticketsearches = RT::SavedSearches->new($curruser); +$ticketsearches->LimitToPrivacy('RT::User-'.$curruser->Id, 'Ticket'); +is($ticketsearches->Count, 1, "Found searchuser's ticket searches"); + +my $allsearches = RT::SavedSearches->new($curruser); +$allsearches->LimitToPrivacy('RT::User-'.$curruser->Id); +is($allsearches->Count, 2, "Found all searchuser's searches"); + +# Delete a search. +($ret, $msg) = $genericsearch->Delete; +ok($ret, "Deleted genericsearch"); +$allsearches->LimitToPrivacy('RT::User-'.$curruser->Id); +is($allsearches->Count, 1, "Found all searchuser's searches after deletion"); + diff --git a/rt/t/api/scrip.t b/rt/t/api/scrip.t index 9d97e7344..eb543476b 100644 --- a/rt/t/api/scrip.t +++ b/rt/t/api/scrip.t @@ -10,11 +10,11 @@ use RT::Test tests => 25; ok (require RT::Scrip); -my $q = RT::Queue->new($RT::SystemUser); +my $q = RT::Queue->new(RT->SystemUser); $q->Create(Name => 'ScripTest'); ok($q->Id, "Created a scriptest queue"); -my $s1 = RT::Scrip->new($RT::SystemUser); +my $s1 = RT::Scrip->new(RT->SystemUser); my ($val, $msg) =$s1->Create( Queue => $q->Id, ScripAction => 'User Defined', ScripCondition => 'User Defined', @@ -25,7 +25,7 @@ my ($val, $msg) =$s1->Create( Queue => $q->Id, ); ok($val,$msg); -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); my ($tv,$ttv,$tm) = $ticket->Create(Queue => $q->Id, Subject => "hair on fire", ); @@ -34,7 +34,7 @@ ok($tv, $tm); is ($ticket->Priority , '87', "Ticket priority is set right"); -my $ticket2 = RT::Ticket->new($RT::SystemUser); +my $ticket2 = RT::Ticket->new(RT->SystemUser); my ($t2v,$t2tv,$t2m) = $ticket2->Create(Queue => $q->Id, Subject => "hair in water", ); @@ -117,5 +117,3 @@ isnt ($ticket2->Priority , '87', "Ticket priority is set right"); ok( $scrip->Delete, 'delete the scrip' ); } - -1; diff --git a/rt/t/api/scrip_order.t b/rt/t/api/scrip_order.t index 9738db9bc..22d3f21d1 100644 --- a/rt/t/api/scrip_order.t +++ b/rt/t/api/scrip_order.t @@ -6,14 +6,13 @@ use RT; use RT::Test tests => 7; -# {{{ test scrip ordering based on description -my $scrip_queue = RT::Queue->new($RT::SystemUser); +my $scrip_queue = RT::Queue->new(RT->SystemUser); my ($queue_id, $msg) = $scrip_queue->Create( Name => "ScripOrdering-$$", Description => 'Test scrip ordering by description' ); ok($queue_id, "Created scrip-ordering test queue? ".$msg); -my $priority_ten_scrip = RT::Scrip->new($RT::SystemUser); +my $priority_ten_scrip = RT::Scrip->new(RT->SystemUser); (my $id, $msg) = $priority_ten_scrip->Create( Description => "10 set priority $$", Queue => $queue_id, @@ -26,7 +25,7 @@ my $priority_ten_scrip = RT::Scrip->new($RT::SystemUser); ); ok($id, "Created priority-10 scrip? ".$msg); -my $priority_five_scrip = RT::Scrip->new($RT::SystemUser); +my $priority_five_scrip = RT::Scrip->new(RT->SystemUser); ($id, $msg) = $priority_ten_scrip->Create( Description => "05 set priority $$", Queue => $queue_id, @@ -39,7 +38,7 @@ my $priority_five_scrip = RT::Scrip->new($RT::SystemUser); ); ok($id, "Created priority-5 scrip? ".$msg); -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); ($id, $msg) = $ticket->Create( Queue => $queue_id, Requestor => 'order@example.com', @@ -51,6 +50,4 @@ isnt($ticket->Priority , 0, "Ticket shouldn't be priority 0"); isnt($ticket->Priority , 5, "Ticket shouldn't be priority 5"); is ($ticket->Priority , 10, "Ticket should be priority 10"); -# }}} -1; diff --git a/rt/t/api/searchbuilder.t b/rt/t/api/searchbuilder.t index cb118906c..8562bfc2b 100644 --- a/rt/t/api/searchbuilder.t +++ b/rt/t/api/searchbuilder.t @@ -15,7 +15,7 @@ ok (require RT::SearchBuilder); { use_ok('RT::Queues'); -ok(my $queues = RT::Queues->new($RT::SystemUser), 'Created a queues object'); +ok(my $queues = RT::Queues->new(RT->SystemUser), 'Created a queues object'); ok( $queues->UnLimit(),'Unlimited the result set of the queues object'); my $items = $queues->ItemsArrayRef(); my @items = @{$items}; @@ -37,4 +37,3 @@ is_deeply(\@items_ids, \@sorted_ids, "ItemsArrayRef sorts alphabetically by name } -1; diff --git a/rt/t/api/squish.t b/rt/t/api/squish.t new file mode 100644 index 000000000..59615368f --- /dev/null +++ b/rt/t/api/squish.t @@ -0,0 +1,16 @@ +use strict; +use warnings; +use RT; +use RT::Test nodb => 1, tests => 7; + +use RT::Squish; + +my $squish = RT::Squish->new(); +for my $method ( qw/Content ModifiedTime ModifiedTimeString Key/ ) { + can_ok($squish, $method); +} +like( $squish->Key, qr/[a-f0-9]{32}/, 'Key is like md5' ); +ok( (time()-$squish->ModifiedTime) <= 2, 'ModifiedTime' ); + +use RT::Squish::CSS; +can_ok('RT::Squish::CSS', 'Style'); diff --git a/rt/t/api/system.t b/rt/t/api/system.t index 3077115c7..f1100d332 100644 --- a/rt/t/api/system.t +++ b/rt/t/api/system.t @@ -2,12 +2,12 @@ use strict; use warnings; use RT; -use RT::Test tests => 7; +use RT::Test nodata => 1, tests => 7; { -my $s = RT::System->new($RT::SystemUser); +my $s = RT::System->new(RT->SystemUser); my $rights = $s->AvailableRights; ok ($rights, "Rights defined"); ok ($rights->{'AdminUsers'},"AdminUsers right found"); @@ -30,4 +30,3 @@ is ($sys->id, 1); } -1; diff --git a/rt/t/api/template-insert.t b/rt/t/api/template-insert.t index 47bbd790c..1bf5fc390 100644 --- a/rt/t/api/template-insert.t +++ b/rt/t/api/template-insert.t @@ -12,7 +12,7 @@ use RT::Test tests => 7; # This tiny little test script triggers an interaction bug between DBD::Oracle 1.16, SB 1.15 and RT 3.4 use_ok('RT::Template'); -my $template = RT::Template->new($RT::SystemUser); +my $template = RT::Template->new(RT->SystemUser); isa_ok($template, 'RT::Template'); my ($val,$msg) = $template->Create(Queue => 1, diff --git a/rt/t/api/template-simple.t b/rt/t/api/template-simple.t new file mode 100644 index 000000000..bbdebb31f --- /dev/null +++ b/rt/t/api/template-simple.t @@ -0,0 +1,275 @@ +use strict; +use warnings; +use RT; +use RT::Test tests => 231; +use Test::Warn; + +my $queue = RT::Queue->new(RT->SystemUser); +$queue->Load("General"); + +my $ticket_cf = RT::CustomField->new(RT->SystemUser); +$ticket_cf->Create( + Name => 'Department', + Queue => '0', + Type => 'FreeformSingle', +); + +my $txn_cf = RT::CustomField->new(RT->SystemUser); +$txn_cf->Create( + Name => 'Category', + LookupType => RT::Transaction->CustomFieldLookupType, + Type => 'FreeformSingle', +); +$txn_cf->AddToObject($queue); + +my $ticket = RT::Ticket->new(RT->SystemUser); +my ($id, $msg) = $ticket->Create( + Subject => "template testing", + Queue => "General", + Owner => 'root@localhost', + Requestor => ["dom\@example.com"], + "CustomField-" . $txn_cf->id => "Special", +); +ok($id, "Created ticket: $msg"); +my $txn = $ticket->Transactions->First; + +$ticket->AddCustomFieldValue( + Field => 'Department', + Value => 'Coolio', +); + +TemplateTest( + Content => "\ntest", + PerlOutput => "test", + SimpleOutput => "test", +); + +TemplateTest( + Content => "\ntest { 5 * 5 }", + PerlOutput => "test 25", + SimpleOutput => "test { 5 * 5 }", +); + +TemplateTest( + Content => "\ntest { \$Requestor }", + PerlOutput => "test dom\@example.com", + SimpleOutput => "test dom\@example.com", +); + +TemplateTest( + Content => "\ntest { \$TicketSubject }", + PerlOutput => "test ", + SimpleOutput => "test template testing", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketQueueId }", + Output => "test 1", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketQueueName }", + Output => "test General", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketOwnerId }", + Output => "test 12", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketOwnerName }", + Output => "test root", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketOwnerEmailAddress }", + Output => "test root\@localhost", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketStatus }", + Output => "test new", +); + +SimpleTemplateTest( + Content => "\ntest #{ \$TicketId }", + Output => "test #" . $ticket->id, +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketCFDepartment }", + Output => "test Coolio", +); + +SimpleTemplateTest( + Content => "\ntest #{ \$TransactionId }", + Output => "test #" . $txn->id, +); + +SimpleTemplateTest( + Content => "\ntest { \$TransactionType }", + Output => "test Create", +); + +SimpleTemplateTest( + Content => "\ntest { \$TransactionCFCategory }", + Output => "test Special", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketDelete }", + Output => "test { \$TicketDelete }", +); + +SimpleTemplateTest( + Content => "\ntest { \$Nonexistent }", + Output => "test { \$Nonexistent }", +); + +warning_like { + TemplateTest( + Content => "\ntest { \$Ticket->Nonexistent }", + PerlOutput => undef, + SimpleOutput => "test { \$Ticket->Nonexistent }", + ); +} qr/RT::Ticket::Nonexistent Unimplemented/; + +warning_like { + TemplateTest( + Content => "\ntest { \$Nonexistent->Nonexistent }", + PerlOutput => undef, + SimpleOutput => "test { \$Nonexistent->Nonexistent }", + ); +} qr/Can't call method "Nonexistent" on an undefined value/; + +TemplateTest( + Content => "\ntest { \$Ticket->OwnerObj->Name }", + PerlOutput => "test root", + SimpleOutput => "test { \$Ticket->OwnerObj->Name }", +); + +warning_like { + TemplateTest( + Content => "\ntest { *!( }", + SyntaxError => 1, + PerlOutput => undef, + SimpleOutput => "test { *!( }", + ); +} qr/Template parsing error: syntax error/; + +TemplateTest( + Content => "\ntest { \$rtname ", + SyntaxError => 1, + PerlOutput => undef, + SimpleOutput => undef, +); + +is($ticket->Status, 'new', "test setup"); +SimpleTemplateTest( + Content => "\ntest { \$Ticket->SetStatus('resolved') }", + Output => "test { \$Ticket->SetStatus('resolved') }", +); +is($ticket->Status, 'new', "simple templates can't call ->SetStatus"); + +# Make sure changing the template's type works +my $template = RT::Template->new(RT->SystemUser); +$template->Create( + Name => "type chameleon", + Type => "Perl", + Content => "\ntest { 10 * 7 }", +); +ok($id = $template->id, "Created template"); +$template->Parse; +is($template->MIMEObj->stringify_body, "test 70", "Perl output"); + +$template = RT::Template->new(RT->SystemUser); +$template->Load($id); +is($template->Name, "type chameleon"); + +$template->SetType('Simple'); +$template->Parse; +is($template->MIMEObj->stringify_body, "test { 10 * 7 }", "Simple output"); + +$template = RT::Template->new(RT->SystemUser); +$template->Load($id); +is($template->Name, "type chameleon"); + +$template->SetType('Perl'); +$template->Parse; +is($template->MIMEObj->stringify_body, "test 70", "Perl output"); + +undef $ticket; + +my $counter = 0; +sub IndividualTemplateTest { + local $Test::Builder::Level = $Test::Builder::Level + 1; + + my %args = ( + Name => "Test-" . ++$counter, + Type => "Perl", + @_, + ); + + my $t = RT::Template->new(RT->SystemUser); + $t->Create( + Name => $args{Name}, + Type => $args{Type}, + Content => $args{Content}, + ); + + ok($t->id, "Created $args{Type} template"); + is($t->Name, $args{Name}, "$args{Type} template name"); + is($t->Content, $args{Content}, "$args{Type} content"); + is($t->Type, $args{Type}, "template type"); + + # this should never blow up! + my ($ok, $msg) = $t->CompileCheck; + + # we don't need to syntax check simple templates since if you mess them up + # it's safe to just use the input directly as the template's output + if ($args{SyntaxError} && $args{Type} eq 'Perl') { + ok(!$ok, "got a syntax error"); + } + else { + ok($ok, $msg); + } + + ($ok, $msg) = $t->Parse( + TicketObj => $ticket, + TransactionObj => $txn, + ); + if (defined $args{Output}) { + ok($ok, $msg); + is($t->MIMEObj->stringify_body, $args{Output}, "$args{Type} template's output"); + } + else { + ok(!$ok, "expected a failure"); + } +} + +sub TemplateTest { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my %args = @_; + + for my $type ('Perl', 'Simple') { + next if $args{"Skip$type"}; + + IndividualTemplateTest( + %args, + Type => $type, + Output => $args{$type . 'Output'}, + ); + } +} + +sub SimpleTemplateTest { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my %args = @_; + + IndividualTemplateTest( + %args, + Type => 'Simple', + ); +} + diff --git a/rt/t/api/template.t b/rt/t/api/template.t index 1612b8ffd..2fadede38 100644 --- a/rt/t/api/template.t +++ b/rt/t/api/template.t @@ -14,13 +14,12 @@ ok(require RT::Template); { -my $t = RT::Template->new($RT::SystemUser); +my $t = RT::Template->new(RT->SystemUser); $t->Create(Name => "Foo", Queue => 1); -my $t2 = RT::Template->new($RT::Nobody); +my $t2 = RT::Template->new(RT->Nobody); $t2->Load($t->Id); ok($t2->QueueObj->id, "Got the template's queue objet"); } -1; diff --git a/rt/t/api/ticket.t b/rt/t/api/ticket.t index 2ca0997bd..92c8a85df 100644 --- a/rt/t/api/ticket.t +++ b/rt/t/api/ticket.t @@ -8,11 +8,11 @@ use RT::Test tests => 87; { use_ok ('RT::Queue'); -ok(my $testqueue = RT::Queue->new($RT::SystemUser)); +ok(my $testqueue = RT::Queue->new(RT->SystemUser)); ok($testqueue->Create( Name => 'ticket tests')); isnt($testqueue->Id , 0); use_ok('RT::CustomField'); -ok(my $testcf = RT::CustomField->new($RT::SystemUser)); +ok(my $testcf = RT::CustomField->new(RT->SystemUser)); my ($ret, $cmsg) = $testcf->Create( Name => 'selectmulti', Queue => $testqueue->id, Type => 'SelectMultiple'); @@ -34,10 +34,10 @@ is($testcf->Values->Count , 3); use_ok('RT::Ticket'); -my $u = RT::User->new($RT::SystemUser); +my $u = RT::User->new(RT->SystemUser); $u->Load("root"); ok ($u->Id, "Found the root user"); -ok(my $t = RT::Ticket->new($RT::SystemUser)); +ok(my $t = RT::Ticket->new(RT->SystemUser)); ok(my ($id, $msg) = $t->Create( Queue => $testqueue->Id, Subject => 'Testing', Owner => $u->Id @@ -56,13 +56,13 @@ ok(my ($cfdv, $cfdm) = $t->DeleteCustomFieldValue(Field => $testcf->Id, isnt ($cfdv , 0, "Deleted a custom field value: $cfdm"); is($t->CustomFieldValues($testcf->Id)->Count , 0); -ok(my $t2 = RT::Ticket->new($RT::SystemUser)); +ok(my $t2 = RT::Ticket->new(RT->SystemUser)); ok($t2->Load($id)); is($t2->Subject, 'Testing'); is($t2->QueueObj->Id, $testqueue->id); is($t2->OwnerObj->Id, $u->Id); -my $t3 = RT::Ticket->new($RT::SystemUser); +my $t3 = RT::Ticket->new(RT->SystemUser); my ($id3, $msg3) = $t3->Create( Queue => $testqueue->Id, Subject => 'Testing', Owner => $u->Id); @@ -93,7 +93,7 @@ ok(require RT::Ticket, "Loading the RT::Ticket library"); { -my $t = RT::Ticket->new($RT::SystemUser); +my $t = RT::Ticket->new(RT->SystemUser); ok( $t->Create(Queue => 'General', Due => '2002-05-21 00:00:00', ReferredToBy => 'http://www.cpan.org', RefersTo => 'http://fsck.com', Subject => 'This is a subject'), "Ticket Created"); @@ -107,19 +107,19 @@ is ($t->ResolvedObj->Unix, 0, "It hasn't been resolved - ". $t->ResolvedObj->Uni { -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); my ($id, $msg) = $ticket->Create(Subject => "Foo", - Owner => $RT::SystemUser->Id, + Owner => RT->SystemUser->Id, Status => 'open', Requestor => ['jesse@example.com'], Queue => '1' ); ok ($id, "Ticket $id was created"); -ok(my $group = RT::Group->new($RT::SystemUser)); +ok(my $group = RT::Group->new(RT->SystemUser)); ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Requestor')); ok ($group->Id, "Found the requestors object for this ticket"); -ok(my $jesse = RT::User->new($RT::SystemUser), "Creating a jesse rt::user"); +ok(my $jesse = RT::User->new(RT->SystemUser), "Creating a jesse rt::user"); $jesse->LoadByEmail('jesse@example.com'); ok($jesse->Id, "Found the jesse rt user"); @@ -127,7 +127,7 @@ ok($jesse->Id, "Found the jesse rt user"); ok ($ticket->IsWatcher(Type => 'Requestor', PrincipalId => $jesse->PrincipalId), "The ticket actually has jesse at fsck.com as a requestor"); ok (my ($add_id, $add_msg) = $ticket->AddWatcher(Type => 'Requestor', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor"); ok ($add_id, "Add succeeded: ($add_msg)"); -ok(my $bob = RT::User->new($RT::SystemUser), "Creating a bob rt::user"); +ok(my $bob = RT::User->new(RT->SystemUser), "Creating a bob rt::user"); $bob->LoadByEmail('bob@fsck.com'); ok($bob->Id, "Found the bob rt user"); ok ($ticket->IsWatcher(Type => 'Requestor', PrincipalId => $bob->PrincipalId), "The ticket actually has bob at fsck.com as a requestor"); @@ -135,23 +135,23 @@ ok ( ($add_id, $add_msg) = $ticket->DeleteWatcher(Type =>'Requestor', Email => ' ok (!$ticket->IsWatcher(Type => 'Requestor', PrincipalId => $bob->PrincipalId), "The ticket no longer has bob at fsck.com as a requestor"); -$group = RT::Group->new($RT::SystemUser); +$group = RT::Group->new(RT->SystemUser); ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Cc')); ok ($group->Id, "Found the cc object for this ticket"); -$group = RT::Group->new($RT::SystemUser); +$group = RT::Group->new(RT->SystemUser); ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'AdminCc')); ok ($group->Id, "Found the AdminCc object for this ticket"); -$group = RT::Group->new($RT::SystemUser); +$group = RT::Group->new(RT->SystemUser); ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Owner')); ok ($group->Id, "Found the Owner object for this ticket"); -ok($group->HasMember($RT::SystemUser->UserObj->PrincipalObj), "the owner group has the member 'RT_System'"); +ok($group->HasMember(RT->SystemUser->UserObj->PrincipalObj), "the owner group has the member 'RT_System'"); } { -my $t = RT::Ticket->new($RT::SystemUser); +my $t = RT::Ticket->new(RT->SystemUser); ok($t->Create(Queue => 'general', Subject => 'SquelchTest', SquelchMailTo => 'nobody@example.com')); my @returned = $t->SquelchMailTo(); @@ -189,15 +189,15 @@ is($#returned, -1, "The ticket has no squelched recipients". join(',',@returned) { -my $t1 = RT::Ticket->new($RT::SystemUser); +my $t1 = RT::Ticket->new(RT->SystemUser); $t1->Create ( Subject => 'Merge test 1', Queue => 'general', Requestor => 'merge1@example.com'); my $t1id = $t1->id; -my $t2 = RT::Ticket->new($RT::SystemUser); +my $t2 = RT::Ticket->new(RT->SystemUser); $t2->Create ( Subject => 'Merge test 2', Queue => 'general', Requestor => 'merge2@example.com'); my $t2id = $t2->id; my ($msg, $val) = $t1->MergeInto($t2->id); ok ($msg,$val); -$t1 = RT::Ticket->new($RT::SystemUser); +$t1 = RT::Ticket->new(RT->SystemUser); is ($t1->id, undef, "ok. we've got a blank ticket1"); $t1->Load($t1id); @@ -211,16 +211,16 @@ is ($t1->Requestors->MembersObj->Count, 2); { -my $root = RT::User->new($RT::SystemUser); +my $root = RT::User->new(RT->SystemUser); $root->Load('root'); ok ($root->Id, "Loaded the root user"); -my $t = RT::Ticket->new($RT::SystemUser); +my $t = RT::Ticket->new(RT->SystemUser); $t->Load(1); $t->SetOwner('root'); is ($t->OwnerObj->Name, 'root' , "Root owns the ticket"); $t->Steal(); -is ($t->OwnerObj->id, $RT::SystemUser->id , "SystemUser owns the ticket"); -my $txns = RT::Transactions->new($RT::SystemUser); +is ($t->OwnerObj->id, RT->SystemUser->id , "SystemUser owns the ticket"); +my $txns = RT::Transactions->new(RT->SystemUser); $txns->OrderBy(FIELD => 'id', ORDER => 'DESC'); $txns->Limit(FIELD => 'ObjectId', VALUE => '1'); $txns->Limit(FIELD => 'ObjectType', VALUE => 'RT::Ticket'); @@ -228,14 +228,14 @@ $txns->Limit(FIELD => 'Type', OPERATOR => '!=', VALUE => 'EmailRecord'); my $steal = $txns->First; is($steal->OldValue , $root->Id , "Stolen from root"); -is($steal->NewValue , $RT::SystemUser->Id , "Stolen by the systemuser"); +is($steal->NewValue , RT->SystemUser->Id , "Stolen by the systemuser"); } { -my $tt = RT::Ticket->new($RT::SystemUser); +my $tt = RT::Ticket->new(RT->SystemUser); my ($id, $tid, $msg)= $tt->Create(Queue => 'general', Subject => 'test'); ok($id, $msg); @@ -254,4 +254,3 @@ ok(!$id,$msg); } -1; diff --git a/rt/t/api/tickets.t b/rt/t/api/tickets.t index 9148a8899..cabb00e50 100644 --- a/rt/t/api/tickets.t +++ b/rt/t/api/tickets.t @@ -8,7 +8,7 @@ use RT::Test tests => 16; { ok (require RT::Tickets); -ok( my $testtickets = RT::Tickets->new( $RT::SystemUser ) ); +ok( my $testtickets = RT::Tickets->new( RT->SystemUser ) ); ok( $testtickets->LimitStatus( VALUE => 'deleted' ) ); # Should be zero until 'allow_deleted_search' is( $testtickets->Count , 0 ); @@ -22,45 +22,45 @@ is( $testtickets->Count , 0 ); # by requestor name. my ($id,$msg); -my $u1 = RT::User->new($RT::SystemUser); +my $u1 = RT::User->new(RT->SystemUser); ($id, $msg) = $u1->Create( Name => 'RequestorTestOne', EmailAddress => 'rqtest1@example.com'); ok ($id,$msg); -my $u2 = RT::User->new($RT::SystemUser); +my $u2 = RT::User->new(RT->SystemUser); ($id, $msg) = $u2->Create( Name => 'RequestorTestTwo', EmailAddress => 'rqtest2@example.com'); ok ($id,$msg); -my $t1 = RT::Ticket->new($RT::SystemUser); +my $t1 = RT::Ticket->new(RT->SystemUser); my ($trans); ($id,$trans,$msg) =$t1->Create (Queue => 'general', Subject => 'Requestor test one', Requestor => [$u1->EmailAddress]); ok ($id, $msg); -my $t2 = RT::Ticket->new($RT::SystemUser); +my $t2 = RT::Ticket->new(RT->SystemUser); ($id,$trans,$msg) =$t2->Create (Queue => 'general', Subject => 'Requestor test one', Requestor => [$u2->EmailAddress]); ok ($id, $msg); -my $t3 = RT::Ticket->new($RT::SystemUser); +my $t3 = RT::Ticket->new(RT->SystemUser); ($id,$trans,$msg) =$t3->Create (Queue => 'general', Subject => 'Requestor test one', Requestor => [$u2->EmailAddress, $u1->EmailAddress]); ok ($id, $msg); -my $tix1 = RT::Tickets->new($RT::SystemUser); +my $tix1 = RT::Tickets->new(RT->SystemUser); $tix1->FromSQL('Requestor.EmailAddress LIKE "rqtest1" OR Requestor.EmailAddress LIKE "rqtest2"'); is ($tix1->Count, 3); -my $tix2 = RT::Tickets->new($RT::SystemUser); +my $tix2 = RT::Tickets->new(RT->SystemUser); $tix2->FromSQL('Requestor.Name LIKE "TestOne" OR Requestor.Name LIKE "TestTwo"'); is ($tix2->Count, 3); -my $tix3 = RT::Tickets->new($RT::SystemUser); +my $tix3 = RT::Tickets->new(RT->SystemUser); $tix3->FromSQL('Requestor.EmailAddress LIKE "rqtest1"'); is ($tix3->Count, 2); -my $tix4 = RT::Tickets->new($RT::SystemUser); +my $tix4 = RT::Tickets->new(RT->SystemUser); $tix4->FromSQL('Requestor.Name LIKE "TestOne" '); is ($tix4->Count, 2); @@ -69,12 +69,12 @@ is ($tix4->Count, 2); # There's no way to differentiate "one requestor name that matches foo and bar" # and "two requestors, one matching foo and one matching bar" -# my $tix5 = RT::Tickets->new($RT::SystemUser); +# my $tix5 = RT::Tickets->new(RT->SystemUser); # $tix5->FromSQL('Requestor.Name LIKE "TestOne" AND Requestor.Name LIKE "TestTwo"'); # # is ($tix5->Count, 1); # -# my $tix6 = RT::Tickets->new($RT::SystemUser); +# my $tix6 = RT::Tickets->new(RT->SystemUser); # $tix6->FromSQL('Requestor.EmailAddress LIKE "rqtest1" AND Requestor.EmailAddress LIKE "rqtest2"'); # # is ($tix6->Count, 1); @@ -85,7 +85,7 @@ is ($tix4->Count, 2); { -my $t1 = RT::Ticket->new($RT::SystemUser); +my $t1 = RT::Ticket->new(RT->SystemUser); $t1->Create(Queue => 'general', Subject => "LimitWatchers test", Requestors => \['requestor1@example.com']); @@ -94,11 +94,10 @@ $t1->Create(Queue => 'general', Subject => "LimitWatchers test", Requestors => \ { # We assume that we've got some tickets hanging around from before. -ok( my $unlimittickets = RT::Tickets->new( $RT::SystemUser ) ); +ok( my $unlimittickets = RT::Tickets->new( RT->SystemUser ) ); ok( $unlimittickets->UnLimit ); ok( $unlimittickets->Count > 0, "UnLimited tickets object should return tickets" ); } -1; diff --git a/rt/t/api/tickets_overlay_sql.t b/rt/t/api/tickets_overlay_sql.t index 5bc614077..9f91111df 100644 --- a/rt/t/api/tickets_overlay_sql.t +++ b/rt/t/api/tickets_overlay_sql.t @@ -1,13 +1,9 @@ - -use RT; -use RT::Test tests => 19; - -{ - -use RT::Tickets; use strict; +use warnings; +use RT::Test tests => 20, config => 'Set( %FullTextSearch, Enable => 1 );'; +use Test::Warn; -my $tix = RT::Tickets->new($RT::SystemUser); +my $tix = RT::Tickets->new(RT->SystemUser); { my $query = "Status = 'open'"; my ($status, $msg) = $tix->FromSQL($query); @@ -18,7 +14,7 @@ my $tix = RT::Tickets->new($RT::SystemUser); my (@created,%created); my $string = 'subject/content SQL test'; { - my $t = RT::Ticket->new($RT::SystemUser); + my $t = RT::Ticket->new(RT->SystemUser); ok( $t->Create(Queue => 'General', Subject => $string), "Ticket Created"); $created{ $t->Id }++; push @created, $t->Id; } @@ -30,7 +26,7 @@ my $string = 'subject/content SQL test'; Data => [ $string ], ); - my $t = RT::Ticket->new($RT::SystemUser); + my $t = RT::Ticket->new(RT->SystemUser); ok( $t->Create( Queue => 'General', Requestor => 'jesse@example.com', Subject => 'another ticket', @@ -74,7 +70,7 @@ diag "Make sure we don't barf on invalid input for IS / IS NOT"; unlike $tix->BuildSelectQuery, qr/foobar/, "didn't find foobar in the select"; like $tix->BuildSelectQuery, qr/Subject IS NULL/, "found right clause"; - my ($status, $msg) = $tix->FromSQL("Subject IS NOT 'foobar'"); + ($status, $msg) = $tix->FromSQL("Subject IS NOT 'foobar'"); ok ($status, "valid query") or diag("error: $msg"); is $tix->Count, 2, "found two tickets"; unlike $tix->BuildSelectQuery, qr/foobar/, "didn't find foobar in the select"; @@ -82,15 +78,16 @@ diag "Make sure we don't barf on invalid input for IS / IS NOT"; } { - my ($status, $msg) = $tix->FromSQL("Requestor.Signature LIKE 'foo'"); - ok (!$status, "invalid query - Signature not valid") or diag("error: $msg"); + my ($status, $msg); - my ($status, $msg) = $tix->FromSQL("Requestor.EmailAddress LIKE 'jesse'"); + warning_like { + ($status, $msg) = $tix->FromSQL("Requestor.Signature LIKE 'foo'"); + } qr/Invalid watcher subfield: 'Signature'/; + ok(!$status, "invalid query - Signature not valid") or diag("error: $msg"); + + ($status, $msg) = $tix->FromSQL("Requestor.EmailAddress LIKE 'jesse'"); ok ($status, "valid query") or diag("error: $msg"); is $tix->Count, 1, "found one ticket"; like $tix->First->Subject, qr/another ticket/, "found the right ticket"; } -} - -1; diff --git a/rt/t/api/txn_content.t b/rt/t/api/txn_content.t index 0f5d78ca9..392b6a73b 100644 --- a/rt/t/api/txn_content.t +++ b/rt/t/api/txn_content.t @@ -3,7 +3,7 @@ use strict; use RT::Test tests => 3; use MIME::Entity; -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); my $mime = MIME::Entity->build( From => 'test@example.com', Type => 'text/html', diff --git a/rt/t/api/uri-fsck_com_rt.t b/rt/t/api/uri-fsck_com_rt.t index d62e58022..18bee7db2 100644 --- a/rt/t/api/uri-fsck_com_rt.t +++ b/rt/t/api/uri-fsck_com_rt.t @@ -4,9 +4,9 @@ use RT; use RT::Test tests => 8; use_ok("RT::URI::fsck_com_rt"); -my $uri = RT::URI::fsck_com_rt->new($RT::SystemUser); +my $uri = RT::URI::fsck_com_rt->new(RT->SystemUser); -my $t1 = RT::Ticket->new($RT::SystemUser); +my $t1 = RT::Ticket->new(RT->SystemUser); my ($id,$trans,$msg) =$t1->Create (Queue => 'general', Subject => 'Requestor test one', ); ok ($id, $msg); @@ -20,9 +20,8 @@ ok ($uri->isa('RT::Base'), "It's an RT::Base"); is ($uri->LocalURIPrefix , 'fsck.com-rt://'.RT->Config->Get('Organization')); -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); $ticket->Load(1); $uri = RT::URI::fsck_com_rt->new($ticket->CurrentUser); is($uri->LocalURIPrefix. "/ticket/1" , $uri->URIForObject($ticket)); -1; diff --git a/rt/t/api/uri-t.t b/rt/t/api/uri-t.t index 4695629bb..61b3e1761 100644 --- a/rt/t/api/uri-t.t +++ b/rt/t/api/uri-t.t @@ -3,12 +3,12 @@ use warnings; use RT; use RT::Test tests => 6; -my $t1 = RT::Ticket->new($RT::SystemUser); +my $t1 = RT::Ticket->new(RT->SystemUser); my ($id,$trans,$msg) =$t1->Create (Queue => 'general', Subject => 'Requestor test one', ); ok ($id, $msg); use_ok("RT::URI::t"); -my $uri = RT::URI::t->new($RT::SystemUser); +my $uri = RT::URI::t->new(RT->SystemUser); ok(ref($uri), "URI object exists"); my $uristr = "t:1"; @@ -18,4 +18,3 @@ is($uri->Object->Id, 1, "Object loaded has correct ID"); is($uri->URI, 'fsck.com-rt://'.RT->Config->Get('Organization').'/ticket/1', "URI object has correct URI string"); -1; diff --git a/rt/t/api/user.t b/rt/t/api/user.t index 25cf74773..e6b891f73 100644 --- a/rt/t/api/user.t +++ b/rt/t/api/user.t @@ -2,7 +2,7 @@ use strict; use warnings; use RT; -use RT::Test tests => 108; +use RT::Test tests => 111; { @@ -16,38 +16,38 @@ ok(require RT::User); # Make sure we can create a user -my $u1 = RT::User->new($RT::SystemUser); +my $u1 = RT::User->new(RT->SystemUser); is(ref($u1), 'RT::User'); my ($id, $msg) = $u1->Create(Name => 'CreateTest1'.$$, EmailAddress => $$.'create-test-1@example.com'); ok ($id, "Creating user CreateTest1 - " . $msg ); # Make sure we can't create a second user with the same name -my $u2 = RT::User->new($RT::SystemUser); +my $u2 = RT::User->new(RT->SystemUser); ($id, $msg) = $u2->Create(Name => 'CreateTest1'.$$, EmailAddress => $$.'create-test-2@example.com'); ok (!$id, $msg); # Make sure we can't create a second user with the same EmailAddress address -my $u3 = RT::User->new($RT::SystemUser); +my $u3 = RT::User->new(RT->SystemUser); ($id, $msg) = $u3->Create(Name => 'CreateTest2'.$$, EmailAddress => $$.'create-test-1@example.com'); ok (!$id, $msg); # Make sure we can create a user with no EmailAddress address -my $u4 = RT::User->new($RT::SystemUser); +my $u4 = RT::User->new(RT->SystemUser); ($id, $msg) = $u4->Create(Name => 'CreateTest3'.$$); ok ($id, $msg); # make sure we can create a second user with no EmailAddress address -my $u5 = RT::User->new($RT::SystemUser); +my $u5 = RT::User->new(RT->SystemUser); ($id, $msg) = $u5->Create(Name => 'CreateTest4'.$$); ok ($id, $msg); # make sure we can create a user with a blank EmailAddress address -my $u6 = RT::User->new($RT::SystemUser); +my $u6 = RT::User->new(RT->SystemUser); ($id, $msg) = $u6->Create(Name => 'CreateTest6'.$$, EmailAddress => ''); ok ($id, $msg); # make sure we can create a second user with a blankEmailAddress address -my $u7 = RT::User->new($RT::SystemUser); +my $u7 = RT::User->new(RT->SystemUser); ($id, $msg) = $u7->Create(Name => 'CreateTest7'.$$, EmailAddress => ''); ok ($id, $msg); @@ -59,19 +59,27 @@ ok ($id, $msg); ok ($id, $msg); is_empty ($u7->EmailAddress); +# back to something, so we can set undef next successfully +($id,$msg) = $u7->SetEmailAddress('foo@bar'.$$); +ok ($id, $msg); + +($id,$msg) = $u7->SetEmailAddress(undef); +ok ($id, $msg); +is_empty ($u7->EmailAddress); + RT->Config->Set('ValidateUserEmailAddresses' => 1); # Make sur we can't create a user with multiple email adresses separated by comma -my $u8 = RT::User->new($RT::SystemUser); +my $u8 = RT::User->new(RT->SystemUser); ($id, $msg) = $u8->Create(Name => 'CreateTest8'.$$, EmailAddress => $$.'create-test-81@example.com, '.$$.'create-test-82@example.com'); ok (!$id, $msg); # Make sur we can't create a user with multiple email adresses separated by space -my $u9 = RT::User->new($RT::SystemUser); +my $u9 = RT::User->new(RT->SystemUser); ($id, $msg) = $u9->Create(Name => 'CreateTest9'.$$, EmailAddress => $$.'create-test-91@example.com '.$$.'create-test-92@example.com'); ok (!$id, $msg); # Make sur we can't create a user with invalid email address -my $u10 = RT::User->new($RT::SystemUser); +my $u10 = RT::User->new(RT->SystemUser); ($id, $msg) = $u10->Create(Name => 'CreateTest10'.$$, EmailAddress => $$.'create-test10}@[.com'); ok (!$id, $msg); RT->Config->Set('ValidateUserEmailAddresses' => undef); @@ -81,7 +89,7 @@ RT->Config->Set('ValidateUserEmailAddresses' => undef); { -ok(my $user = RT::User->new($RT::SystemUser)); +ok(my $user = RT::User->new(RT->SystemUser)); ok($user->Load('root'), "Loaded user 'root'"); ok($user->Privileged, "User 'root' is privileged"); ok(my ($v,$m) = $user->SetPrivileged(0)); @@ -96,7 +104,7 @@ ok($user->Privileged, "User 'root' is privileged again"); { -ok(my $u = RT::User->new($RT::SystemUser)); +ok(my $u = RT::User->new(RT->SystemUser)); ok($u->Load(1), "Loaded the first user"); is($u->PrincipalObj->ObjectId , 1, "user 1 is the first principal"); is($u->PrincipalObj->PrincipalType, 'User' , "Principal 1 is a user, not a group"); @@ -106,7 +114,7 @@ is($u->PrincipalObj->PrincipalType, 'User' , "Principal 1 is a user, not a group { -my $root = RT::User->new($RT::SystemUser); +my $root = RT::User->new(RT->SystemUser); $root->Load('root'); ok($root->Id, "Found the root user"); my $rootq = RT::Queue->new($root); @@ -115,7 +123,7 @@ ok($rootq->Id, "Loaded the first queue"); ok ($rootq->CurrentUser->HasRight(Right=> 'CreateTicket', Object => $rootq), "Root can create tickets"); -my $new_user = RT::User->new($RT::SystemUser); +my $new_user = RT::User->new(RT->SystemUser); my ($id, $msg) = $new_user->Create(Name => 'ACLTest'.$$); ok ($id, "Created a new user for acl test $msg"); @@ -140,13 +148,13 @@ ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user # Create a ticket in the queue -my $new_tick = RT::Ticket->new($RT::SystemUser); +my $new_tick = RT::Ticket->new(RT->SystemUser); my ($tickid, $tickmsg) = $new_tick->Create(Subject=> 'ACL Test', Queue => 'General'); ok($tickid, "Created ticket: $tickid"); # Make sure the user doesn't have the right to modify tickets in the queue ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership"); # Create a new group -my $group = RT::Group->new($RT::SystemUser); +my $group = RT::Group->new(RT->SystemUser); $group->CreateUserDefinedGroup(Name => 'ACLTest'.$$); ok($group->Id, "Created a new group Ok"); # Grant a group the right to modify tickets in a queue @@ -166,12 +174,12 @@ ok ($did,"Deleted the group member: $dmsg"); ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership"); -my $q_as_system = RT::Queue->new($RT::SystemUser); +my $q_as_system = RT::Queue->new(RT->SystemUser); $q_as_system->Load(1); ok($q_as_system->Id, "Loaded the first queue"); # Create a ticket in the queue -my $new_tick2 = RT::Ticket->new($RT::SystemUser); +my $new_tick2 = RT::Ticket->new(RT->SystemUser); (my $tick2id, $tickmsg) = $new_tick2->Create(Subject=> 'ACL Test 2', Queue =>$q_as_system->Id); ok($tick2id, "Created ticket: $tick2id"); is($new_tick2->QueueObj->id, $q_as_system->Id, "Created a new ticket in queue 1"); @@ -181,7 +189,7 @@ is($new_tick2->QueueObj->id, $q_as_system->Id, "Created a new ticket in queue 1" ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership"); # Create a subgroup -my $subgroup = RT::Group->new($RT::SystemUser); +my $subgroup = RT::Group->new(RT->SystemUser); $subgroup->CreateUserDefinedGroup(Name => 'Subgrouptest'.$$); ok($subgroup->Id, "Created a new group ".$subgroup->Id."Ok"); #Add the subgroup as a subgroup of the group @@ -210,7 +218,6 @@ ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User ok ($id,$msg); ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket without group membership"); -# }}} my ($usrid, $usrmsg) = $subgroup->DeleteMember($new_user->PrincipalId); @@ -222,7 +229,6 @@ ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User ok(($gv,$gm) = $group->PrincipalObj->RevokeRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets"); ok($gv,"revoke succeeed - $gm"); -# {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _queue_ level # Grant queue admin cc the right to modify ticket in the queue ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $q_as_system, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets"); @@ -240,9 +246,7 @@ ok (my ($del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', Prin # Make sure the user doesn't have the right to modify tickets in the queue ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership"); -# }}} -# {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level # Add the user as a ticket admincc ok (my( $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc"); @@ -262,11 +266,9 @@ ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $q_as_system, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets"); ok($rqv, "Revoked the right successfully - $rqm"); -# }}} -# {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _system_ level # Before we start Make sure the user does not have the right to modify tickets in the queue ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without it being granted"); @@ -294,9 +296,7 @@ ok (($del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', Princip ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership"); ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can't modify tickets in the queue without group membership"); -# }}} -# {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc"); ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc"); @@ -322,7 +322,6 @@ ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket ok(($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $RT::System, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets"); ok($rqv, "Revoked the right successfully - $rqm"); -# }}} @@ -336,4 +335,3 @@ ok($rqv, "Revoked the right successfully - $rqm"); } -1; diff --git a/rt/t/api/users.t b/rt/t/api/users.t index d1ff174e1..1f3a48770 100644 --- a/rt/t/api/users.t +++ b/rt/t/api/users.t @@ -1,37 +1,31 @@ - use strict; use warnings; -use RT; -use RT::Test tests => 11; - - -{ +use RT::Test tests => 10; -ok(require RT::Users); - - -} +RT::System->AddRights( + 'RTxUserRight' => 'Just a right for testing rights', +); { no warnings qw(redefine once); -ok(my $users = RT::Users->new($RT::SystemUser)); -$users->WhoHaveRight(Object =>$RT::System, Right =>'SuperUser'); +ok(my $users = RT::Users->new(RT->SystemUser)); +$users->WhoHaveRight(Object => RT->System, Right =>'SuperUser'); is($users->Count , 1, "There is one privileged superuser - Found ". $users->Count ); # TODO: this wants more testing -my $RTxUser = RT::User->new($RT::SystemUser); +my $RTxUser = RT::User->new(RT->SystemUser); my ($id, $msg) = $RTxUser->Create( Name => 'RTxUser', Comments => "RTx extension user", Privileged => 1); ok ($id,$msg); -my $group = RT::Group->new($RT::SystemUser); +my $group = RT::Group->new(RT->SystemUser); $group->LoadACLEquivalenceGroup($RTxUser->PrincipalObj); my $RTxSysObj = {}; bless $RTxSysObj, 'RTx::System'; *RTx::System::Id = sub { 1; }; *RTx::System::id = *RTx::System::Id; -my $ace = RT::Record->new($RT::SystemUser); +my $ace = RT::Record->new(RT->SystemUser); $ace->Table('ACL'); $ace->_BuildTableAttributes unless ($RT::Record::_TABLE_ATTR->{ref($ace)}); ($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System', ObjectId => 1 ); @@ -42,19 +36,19 @@ bless $RTxObj, 'RTx::System::Record'; *RTx::System::Record::Id = sub { 4; }; *RTx::System::Record::id = *RTx::System::Record::Id; -$users = RT::Users->new($RT::SystemUser); +$users = RT::Users->new(RT->SystemUser); $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxSysObj); is($users->Count, 1, "RTxUserRight found for RTxSysObj"); -$users = RT::Users->new($RT::SystemUser); +$users = RT::Users->new(RT->SystemUser); $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj); is($users->Count, 0, "RTxUserRight not found for RTxObj"); -$users = RT::Users->new($RT::SystemUser); +$users = RT::Users->new(RT->SystemUser); $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj, EquivObjects => [ $RTxSysObj ]); is($users->Count, 1, "RTxUserRight found for RTxObj using EquivObjects"); -$ace = RT::Record->new($RT::SystemUser); +$ace = RT::Record->new(RT->SystemUser); $ace->Table('ACL'); $ace->_BuildTableAttributes unless ($RT::Record::_TABLE_ATTR->{ref($ace)}); ($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System::Record', ObjectId => 5 ); @@ -65,11 +59,11 @@ bless $RTxObj2, 'RTx::System::Record'; *RTx::System::Record::Id = sub { 5; }; *RTx::System::Record::id = sub { 5; }; -$users = RT::Users->new($RT::SystemUser); +$users = RT::Users->new(RT->SystemUser); $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2); is($users->Count, 1, "RTxUserRight found for RTxObj2"); -$users = RT::Users->new($RT::SystemUser); +$users = RT::Users->new(RT->SystemUser); $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2, EquivObjects => [ $RTxSysObj ]); is($users->Count, 1, "RTxUserRight found for RTxObj2"); @@ -77,4 +71,3 @@ is($users->Count, 1, "RTxUserRight found for RTxObj2"); } -1; diff --git a/rt/t/api/versions_sorter.t b/rt/t/api/versions_sorter.t new file mode 100644 index 000000000..b2ec547a0 --- /dev/null +++ b/rt/t/api/versions_sorter.t @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w + +use RT::Test nodata => 1, tests => 3; + +use strict; +use warnings; + +sub is_right_sorting { + my @order = @_; + my @tmp = sort { int(rand(3)) - 1 } @order; + + is_deeply( + [ sort RT::Handle::cmp_version @tmp ], + \@order, + 'test sorting of ('. join(' ', @tmp) .')' + ); +} + +is_right_sorting(qw(1 2 3)); +is_right_sorting(qw(1.1 1.2 1.3 2.0 2.1)); +is_right_sorting(qw(4.0.0a1 4.0.0alpha2 4.0.0b1 4.0.0beta2 4.0.0pre1 4.0.0pre2 4.0.0rc1 4.0.0rc2 4.0.0)); + diff --git a/rt/t/api/web-config.t b/rt/t/api/web-config.t new file mode 100644 index 000000000..fb2b36242 --- /dev/null +++ b/rt/t/api/web-config.t @@ -0,0 +1,163 @@ +use strict; +use warnings; +use RT; +use RT::Test nodb => 1, tests => 89; + +sub no_warnings_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + + my $option = shift; + my $value = shift; + my $name = shift; + + is(warnings_from($option => $value), 0, $name); +} + +sub one_warning_like { + local $Test::Builder::Level = $Test::Builder::Level + 1; + + my $option = shift; + my $value = shift; + my $regex = shift; + my $name = shift; + + my @w = warnings_from($option => $value); + is(@w, 1); + like($w[0], $regex, $name); +} + + +sub warnings_from { + my $option = shift; + my $value = shift; + + my @warnings; + local $SIG{__WARN__} = sub { + push @warnings, $_[0]; + }; + + RT->Config->Set($option => $value); + RT->Config->PostLoadCheck; + + return @warnings; +} + +# WebPath +no_warnings_ok(WebPath => ''); +no_warnings_ok(WebPath => '/foo'); +no_warnings_ok(WebPath => '/foo/bar'); + +one_warning_like(WebPath => '/foo/', qr/The WebPath config option requires no trailing slash/); + +one_warning_like(WebPath => 'foo', qr/The WebPath config option requires a leading slash/); + +my @w = warnings_from(WebPath => 'foo/'); +is(@w, 2); +like($w[0], qr/The WebPath config option requires no trailing slash/); +like($w[1], qr/The WebPath config option requires a leading slash/); + +one_warning_like(WebPath => '/foo/bar/', qr/The WebPath config option requires no trailing slash/); + +one_warning_like(WebPath => 'foo/bar', qr/The WebPath config option requires a leading slash/); + +@w = warnings_from(WebPath => 'foo/bar/'); +is(@w, 2); +like($w[0], qr/The WebPath config option requires no trailing slash/); +like($w[1], qr/The WebPath config option requires a leading slash/); + +one_warning_like(WebPath => '/', qr{For the WebPath config option, use the empty string instead of /}); + +# reinstate a valid WebPath for other tests +no_warnings_ok(WebPath => '/rt'); + +# WebDomain +no_warnings_ok(WebDomain => 'example.com'); +no_warnings_ok(WebDomain => 'rt.example.com'); +no_warnings_ok(WebDomain => 'localhost'); + +one_warning_like(WebDomain => '', qr{You must set the WebDomain config option}); + +one_warning_like(WebDomain => 'http://rt.example.com', qr{The WebDomain config option must not contain a scheme \(http://\)}); + +one_warning_like(WebDomain => 'https://rt.example.com', qr{The WebDomain config option must not contain a scheme \(https://\)}); + +one_warning_like(WebDomain => 'rt.example.com/path', qr{The WebDomain config option must not contain a path \(/path\)}); + +one_warning_like(WebDomain => 'rt.example.com/path/more', qr{The WebDomain config option must not contain a path \(/path/more\)}); + +one_warning_like(WebDomain => 'rt.example.com:80', qr{The WebDomain config option must not contain a port \(80\)}); + +# reinstate a valid WebDomain for other tests +no_warnings_ok(WebDomain => 'rt.example.com'); + +# WebPort +no_warnings_ok(WebDomain => 80); +no_warnings_ok(WebDomain => 443); +no_warnings_ok(WebDomain => 8888); + +one_warning_like(WebPort => '', qr{You must set the WebPort config option}); + +one_warning_like(WebPort => 3.14, qr{The WebPort config option must be an integer}); + +one_warning_like(WebPort => 'wha?', qr{The WebPort config option must be an integer}); + +# reinstate a valid WebDomain for other tests +no_warnings_ok(WebPort => 443); + +# WebBaseURL +no_warnings_ok(WebBaseURL => 'http://rt.example.com'); +no_warnings_ok(WebBaseURL => 'HTTP://rt.example.com', 'uppercase scheme is okay'); +no_warnings_ok(WebBaseURL => 'http://rt.example.com:8888', 'nonstandard port is okay'); +no_warnings_ok(WebBaseURL => 'https://rt.example.com:8888', 'nonstandard port with https is okay'); + +one_warning_like(WebBaseURL => '', qr{You must set the WebBaseURL config option}); + +one_warning_like(WebBaseURL => 'rt.example.com', qr{The WebBaseURL config option must contain a scheme}); + +one_warning_like(WebBaseURL => 'xtp://rt.example.com', qr{The WebBaseURL config option must contain a scheme \(http or https\)}); + +one_warning_like(WebBaseURL => 'http://rt.example.com/', qr{The WebBaseURL config option requires no trailing slash}); + +one_warning_like(WebBaseURL => 'http://rt.example.com/rt', qr{The WebBaseURL config option must not contain a path \(/rt\)}); + +@w = warnings_from(WebBaseURL => 'http://rt.example.com/rt/'); +is(@w, 2); +like($w[0], qr{The WebBaseURL config option requires no trailing slash}); +like($w[1], qr{The WebBaseURL config option must not contain a path \(/rt/\)}); + +one_warning_like(WebBaseURL => 'http://rt.example.com/rt/ir', qr{The WebBaseURL config option must not contain a path \(/rt/ir\)}); + +@w = warnings_from(WebBaseURL => 'http://rt.example.com/rt/ir/'); +is(@w, 2); +like($w[0], qr{The WebBaseURL config option requires no trailing slash}); +like($w[1], qr{The WebBaseURL config option must not contain a path \(/rt/ir/\)}); + +# reinstate a valid WebBaseURL for other tests +no_warnings_ok(WebBaseURL => 'http://rt.example.com'); + +# WebURL +no_warnings_ok(WebURL => 'http://rt.example.com/'); +no_warnings_ok(WebURL => 'HTTP://rt.example.com/', 'uppercase scheme is okay'); +no_warnings_ok(WebURL => 'http://example.com/rt/'); +no_warnings_ok(WebURL => 'http://example.com/rt/ir/'); +no_warnings_ok(WebURL => 'http://rt.example.com:8888/', 'nonstandard port is okay'); +no_warnings_ok(WebURL => 'https://rt.example.com:8888/', 'nonstandard port with https is okay'); + +one_warning_like(WebURL => '', qr{You must set the WebURL config option}); + +@w = warnings_from(WebURL => 'rt.example.com'); +is(@w, 2); +like($w[0], qr{The WebURL config option must contain a scheme}); +like($w[1], qr{The WebURL config option requires a trailing slash}); + +one_warning_like(WebURL => 'http://rt.example.com', qr{The WebURL config option requires a trailing slash}); + +one_warning_like(WebURL => 'xtp://example.com/rt/', qr{The WebURL config option must contain a scheme \(http or https\)}); + +one_warning_like(WebURL => 'http://rt.example.com/rt', qr{The WebURL config option requires a trailing slash}); + +one_warning_like(WebURL => 'http://rt.example.com/rt/ir', qr{The WebURL config option requires a trailing slash}); + +# reinstate a valid WebURL for other tests +no_warnings_ok(WebURL => 'http://rt.example.com/rt/'); + diff --git a/rt/t/articles/article.t b/rt/t/articles/article.t new file mode 100644 index 000000000..33becb023 --- /dev/null +++ b/rt/t/articles/article.t @@ -0,0 +1,230 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 67; + +use_ok 'RT::Articles'; +use_ok 'RT::Classes'; +use_ok 'RT::Class'; + +my $CLASS = 'ArticleTest-'.$$; + +my $user = RT::CurrentUser->new('root'); + +my $class = RT::Class->new($user); + + +my ($id, $msg) = $class->Create(Name =>$CLASS); +ok ($id, $msg); + + + +my $article = RT::Article->new($user); +ok (UNIVERSAL::isa($article, 'RT::Article')); +ok (UNIVERSAL::isa($article, 'RT::Record')); +ok (UNIVERSAL::isa($article, 'RT::Record')); +ok (UNIVERSAL::isa($article, 'DBIx::SearchBuilder::Record') , "It's a searchbuilder record!"); + + +($id, $msg) = $article->Create( Class => $CLASS, Summary => $CLASS); +ok ($id, $msg); +$article->Load($id); +is ($article->Summary, $CLASS, "The summary is set correct"); +my $at = RT::Article->new($RT::SystemUser); +$at->Load($id); +is ($at->id , $id); +is ($at->Summary, $article->Summary); + + + + +my $a1 = RT::Article->new($RT::SystemUser); + ($id, $msg) = $a1->Create(Class => $class->id, Name => 'ValidateNameTest'.$$); +ok ($id, $msg); + + + +my $a2 = RT::Article->new($RT::SystemUser); +($id, $msg) = $a2->Create(Class => $class->id, Name => 'ValidateNameTest'.$$); +ok (!$id, $msg); + +my $a3 = RT::Article->new($RT::SystemUser); +($id, $msg) = $a3->Create(Class => $class->id, Name => 'ValidateNameTest2'.$$); +ok ($id, $msg); +($id, $msg) =$a3->SetName('ValidateNameTest'.$$); + +ok (!$id, $msg); + +($id, $msg) =$a3->SetName('ValidateNametest2'.$$); + +ok ($id, $msg); + + + + + +my $newart = RT::Article->new($RT::SystemUser); +$newart->Create(Name => 'DeleteTest'.$$, Class => '1'); +$id = $newart->Id; + +ok($id, "New article has an id"); + + + $article = RT::Article->new($RT::SystemUser); +$article->Load($id); +ok ($article->Id, "Found the article"); +my $val; + ($val, $msg) = $article->Delete(); +ok ($val, "Article Deleted: $msg"); + + $a2 = RT::Article->new($RT::SystemUser); +$a2->Load($id); +ok (!$a2->Id, "Did not find the article"); + +# NOT OK +#$RT::Handle->SimpleQuery("DELETE FROM Links"); + +my $article_a = RT::Article->new($RT::SystemUser); +($id, $msg) = $article_a->Create( Class => $CLASS, Summary => "ArticleTestlink1".$$); +ok($id,$msg); + +my $article_b = RT::Article->new($RT::SystemUser); +($id, $msg) = $article_b->Create( Class => $CLASS, Summary => "ArticleTestlink2".$$); +ok($id,$msg); + +# Create a link between two articles +($id, $msg) = $article_a->AddLink( Type => 'RefersTo', Target => $article_b->URI); +ok($id,$msg); + +# Make sure that Article Bs "ReferredToBy" links object refers to to this article +my $refers_to_b = $article_b->ReferredToBy; +is($refers_to_b->Count, 1, "Found one thing referring to b"); +my $first = $refers_to_b->First; +ok ($first->isa('RT::Link'), "IT's an RT link - ref ".ref($first) ); +is($first->TargetObj->Id, $article_b->Id, "Its target is B"); + +ok($refers_to_b->First->BaseObj->isa('RT::Article'), "Yep. its an article"); + + +# Make sure that Article A's "RefersTo" links object refers to this article" +my $referred_To_by_a = $article_a->RefersTo; +is($referred_To_by_a->Count, 1, "Found one thing referring to b ".$referred_To_by_a->Count. "-".$referred_To_by_a->First->id . " - ".$referred_To_by_a->Last->id); + $first = $referred_To_by_a->First; +ok ($first->isa('RT::Link'), "IT's an RT link - ref ".ref($first) ); +is ($first->TargetObj->Id, $article_b->Id, "Its target is B - " . $first->TargetObj->Id); +is ($first->BaseObj->Id, $article_a->Id, "Its base is A"); + +ok($referred_To_by_a->First->BaseObj->isa('RT::Article'), "Yep. its an article"); + +# Delete the link +($id, $msg) = $article_a->DeleteLink(Type => 'RefersTo', Target => $article_b->URI); +ok($id,$msg); + + +# Create an Article A RefersTo Ticket 1 from the Articles side +use RT::Ticket; + + +my $tick = RT::Ticket->new($RT::SystemUser); +$tick->Create(Subject => "Article link test ", Queue => 'General'); +$tick->Load($tick->Id); +ok ($tick->Id, "Found ticket ".$tick->id); +($id, $msg) = $article_a->AddLink(Type => 'RefersTo', Target => $tick->URI); +ok($id,$msg); + +# Find all tickets whhich refer to Article A + +use RT::Tickets; +use RT::Links; + +my $tix = RT::Tickets->new($RT::SystemUser); +ok ($tix, "Got an RT::Tickets object"); +ok ($tix->LimitReferredToBy($article_a->URI)); +is ($tix->Count, 1, "Found one ticket linked to that article"); +is ($tix->First->Id, $tick->id, "It's even the right one"); + + + +# Find all articles which refer to Ticket 1 +use RT::Articles; + +my $articles = RT::Articles->new($RT::SystemUser); +ok($articles->isa('RT::Articles'), "Created an article collection"); +ok($articles->isa('RT::SearchBuilder'), "Created an article collection"); +ok($articles->isa('DBIx::SearchBuilder'), "Created an article collection"); +ok($tick->URI, "The ticket does still have a URI"); +$articles->LimitRefersTo($tick->URI); + +is($articles->Count(), 1); +is ($articles->First->Id, $article_a->Id); +is ($articles->First->URI, $article_a->URI); + + + +# Find all things which refer to ticket 1 using the RT API. + +my $tix2 = RT::Links->new($RT::SystemUser); +ok ($tix2->isa('RT::Links')); +ok($tix2->LimitRefersTo($tick->URI)); +is ($tix2->Count, 1); +is ($tix2->First->BaseObj->URI ,$article_a->URI); + + + +# Delete the link from the RT side. +my $t2 = RT::Ticket->new($RT::SystemUser); +$t2->Load($tick->Id); +($id, $msg)= $t2->DeleteLink( Base => $article_a->URI, Type => 'RefersTo'); +ok ($id, $msg . " - $id - $msg"); + +# it is actually deleted +my $tix3 = RT::Links->new($RT::SystemUser); +$tix3->LimitReferredToBy($tick->URI); +is ($tix3->Count, 0); + +# Recreate the link from teh RT site +($id, $msg) = $t2->AddLink( Base => $article_a->URI, Type => 'RefersTo'); +ok ($id, $msg); + +# Find all tickets whhich refer to Article A + +# Find all articles which refer to Ticket 1 + + + + +my $art = RT::Article->new($RT::SystemUser); +($id, $msg) = $art->Create (Class => $CLASS); +ok ($id,$msg); + +ok($art->URI); +ok($art->__Value('URI') eq $art->URI, "The uri in the db is set correctly"); + + + + + $art = RT::Article->new($RT::SystemUser); +($id, $msg) = $art->Create (Class => $CLASS); +ok ($id,$msg); + +ok($art->URIObj); +ok($art->__Value('URI') eq $art->URIObj->URI, "The uri in the db is set correctly"); + + +my $art_id = $art->id; +$art = RT::Article->new($RT::SystemUser); +$art->Load($art_id); +is ($art->Id, $art_id, "Loaded article 1"); +my $s =$art->Summary; +($val, $msg) = $art->SetSummary("testFoo"); +ok ($val, $msg); +ok ($art->Summary eq 'testFoo', "The Summary was set to foo"); +my $t = $art->Transactions(); +my $trans = $t->Last; +ok ($trans->Type eq 'Set', "It's a Set transaction"); +ok ($trans->Field eq 'Summary', "it is about setting the Summary"); +is ($trans->NewValue , 'testFoo', "The new content is 'foo'"); +is ($trans->OldValue,$s, "the old value was preserved"); + diff --git a/rt/t/articles/articles.t b/rt/t/articles/articles.t new file mode 100644 index 000000000..c6fe65106 --- /dev/null +++ b/rt/t/articles/articles.t @@ -0,0 +1,137 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 29; + +use_ok 'RT::Articles'; +use_ok 'RT::Classes'; +use_ok 'RT::Class'; + +my $class = RT::Class->new($RT::SystemUser); +my ( $id, $msg ) = $class->Create( Name => 'CollectionTest-' . $$ ); +ok( $id, $msg ); + +# Add a custom field to our class +use_ok('RT::CustomField'); +my $cf = RT::CustomField->new($RT::SystemUser); +isa_ok($cf, 'RT::CustomField'); + +($id,$msg) = $cf->Create( Name => 'Articles::Sample-'.$$, + Description => 'Test text cf', + LookupType => RT::Article->CustomFieldLookupType, + Type => 'Freeform' + ); + + + +ok($id,$msg); + + +($id,$msg) = $cf->AddToObject($class); +ok ($id,$msg); + + + +my $art = RT::Article->new($RT::SystemUser); +( $id, $msg ) = $art->Create( + Class => $class->id, + Name => 'Collection-1-' . $$, + Summary => 'Coll-1-' . $$, + 'CustomField-'.$cf->Name => 'Test-'.$$ +); + +ok( $id, $msg ); + + + + + + +my $arts = RT::Articles->new($RT::SystemUser); +$arts->LimitName( VALUE => 'Collection-1-' . $$ . 'fake' ); +is( $arts->Count, 0, + "Found no artlcles with names matching something that is not there" ); + +my $arts2 = RT::Articles->new($RT::SystemUser); +$arts2->LimitName( VALUE => 'Collection-1-' . $$ ); +is( $arts2->Count, 1, 'Found one with names matching the word "test"' ); + +$arts = RT::Articles->new($RT::SystemUser); +$arts->LimitSummary( VALUE => 'Coll-1-' . $$ . 'fake' ); +is( $arts->Count, 0, + 'Found no artlcles with summarys matching something that is not there' ); + +$arts2 = RT::Articles->new($RT::SystemUser); +$arts2->LimitSummary( VALUE => 'Coll-1-' . $$ ); +is( $arts2->Count, 1, 'Found one with summarys matching the word "Coll-1"' ); + +my $new_art = RT::Article->new($RT::SystemUser); +( $id, $msg ) = $new_art->Create( + Class => $class->id, + Name => 'CFSearchTest1' . $$, + 'CustomField-'.$cf->Name => 'testing' . $$ +); + +ok( $id, $msg . " Created a second testable article" ); + + +$arts = RT::Articles->new($RT::SystemUser); +$arts->LimitCustomField( OPERATOR => 'LIKE', VALUE => "esting".$$ ); +is( $arts->Count, 1, "Found 1 cf values matching 'esting" . $$ . "' for an unspecified field"); + +$arts = RT::Articles->new($RT::SystemUser); +$arts->LimitCustomField( OPERATOR => '=', VALUE => "esting".$$ ); +is( $arts->Count, 0, "Found 0 cf values EXACTLY matching 'esting" . $$ . "' for an unspecified field"); + +$arts = RT::Articles->new($RT::SystemUser); +$arts->LimitCustomField( OPERATOR => '=', VALUE => "testing".$$ ); +is( $arts->Count, 1, "Found 0 cf values EXACTLY matching 'testing" . $$ . "' for an unspecified field"); + + +$arts = RT::Articles->new($RT::SystemUser); +$arts->LimitCustomField( OPERATOR => 'LIKE', VALUE => $$ ); +is( $arts->Count, 2, "Found 1 cf values matching '" . $$ . "' for an unspecified field"); + + +# Test searching on named custom fields +$arts = RT::Articles->new($RT::SystemUser); +$arts->LimitCustomField( OPERATOR => 'LIKE', VALUE => $$, FIELD => $cf->Name ); +is( $arts->Count, 2, "Found 1 Article with cf values matching '".$$."' for CF named " .$cf->Name); + +$arts = RT::Articles->new($RT::SystemUser); +$arts->LimitCustomField( OPERATOR => 'LIKE', VALUE => $$, FIELD => 'NO-SUCH-CF' ); +is( $arts->Count,0, "Found no cf values matching '".$$."' for CF 'NO-SUCH-CF' " ); + +$arts = RT::Articles->new($RT::SystemUser); +$arts->Limit(FIELD =>'Class', VALUE => $class->id); + +$arts->LimitCustomField( + OPERATOR => 'NOT LIKE', + VALUE => 'blah', + FIELD => $cf->id +); +is( + $arts->Count ,2, + "Found 1 articles with custom field values not matching blah"); + +$arts = RT::Articles->new($RT::SystemUser); +$arts->Limit(FIELD =>'Class', VALUE => $class->id); +$arts->LimitCustomField( OPERATOR => 'NOT LIKE', VALUE => 'est', FIELD => $cf->id ); +is( $arts->Count , 0, "Found 0 cf values not matching 'est' for CF ".$cf->id. " " . join(',', map {$_->id} @{$arts->ItemsArrayRef})); +$arts = RT::Articles->new($RT::SystemUser); +$arts->Limit(FIELD =>'Class', VALUE => $class->id); +$arts->LimitCustomField( OPERATOR => 'NOT LIKE', VALUE => 'BOGUS', FIELD => $cf->id ); +is( $arts->Count , 2, "Found 2 articles not matching 'BOGUS' for CF ".$cf->id); + +my $ac = RT::Articles->new($RT::SystemUser); +ok( $ac->isa('RT::Articles') ); +ok( $ac->isa('DBIx::SearchBuilder') ); +ok( $ac->LimitRefersTo('http://dead.link') ); +is( $ac->Count, 0 ); + +$ac = RT::Articles->new($RT::SystemUser); +ok( $ac->LimitReferredToBy('http://dead.link') ); +is( $ac->Count, 0 ); + diff --git a/rt/t/articles/basic-api.t b/rt/t/articles/basic-api.t new file mode 100644 index 000000000..f9f9f89f4 --- /dev/null +++ b/rt/t/articles/basic-api.t @@ -0,0 +1,114 @@ +#!/usr/bin/perl + +use warnings; +use strict; + +use RT::Test tests => 37; + +use_ok('RT::Class'); + +my $class = RT::Class->new($RT::SystemUser); +isa_ok($class, 'RT::Class'); +isa_ok($class, 'RT::Record'); +isa_ok($class, 'RT::Record'); + + +my $name = 'test-'.$$; +my ($id,$msg) = $class->Create( Name =>$name, Description => 'Test class'); +ok($id,$msg); +is ($class->Name, $name); +is ($class->Description, 'Test class'); + + + +# Test custom fields. + +can_ok($class, 'CustomFields'); +can_ok($class, 'AddCustomFieldValue'); +can_ok($class, 'DeleteCustomFieldValue'); +can_ok($class, 'FirstCustomFieldValue'); +can_ok($class, 'CustomFieldValues'); +can_ok($class, 'CurrentUserHasRight'); + + +# Add a custom field to our class +my $cf = RT::CustomField->new($RT::SystemUser); +isa_ok($cf, 'RT::CustomField'); + +($id,$msg) = $cf->Create( Name => 'Articles::Sample-'.$$, + Description => 'Test text cf', + LookupType => RT::Article->CustomFieldLookupType, + Type => 'Text' + ); + + + +ok($id,$msg); + + +($id,$msg) = $cf->AddToObject($class); +ok ($id,$msg); + + +# Does our class have a custom field? + +my $cfs = $class->ArticleCustomFields; +isa_ok($cfs, 'RT::CustomFields'); +is($cfs->Count, 1, "We only have one custom field"); +my $found_cf = $cfs->First; +is ($cf->id, $found_cf->id, "it's the right one"); + +($id,$msg) = $cf->RemoveFromObject($class); + +is($class->ArticleCustomFields->Count, 0, "All gone!"); + +# Put it back. we want to go forward. + +($id,$msg) = $cf->AddToObject($class); +ok ($id,$msg); + + + + +use_ok('RT::Article'); + +my $art = RT::Article->new($RT::SystemUser); +($id,$msg) =$art->Create(Class => $class->id, + Name => 'Sample'.$$, + Description => 'A sample article'); + +ok($id,"Created article ".$id." - " .$msg); + +# make sure there is one transaction. + +my $txns = $art->Transactions; + +is($txns->Count, 1, "One txn"); +my $txn = $txns->First; +is ($txn->ObjectType, 'RT::Article'); +is ($txn->ObjectId , $id , "It's the right article"); +is ($txn->Type, 'Create', "It's a create!"); + + +my $art_cfs = $art->CustomFields(); +is ($art_cfs->Count, 1, "It has a custom field"); +my $values = $art->CustomFieldValues($art_cfs->First); +is ($values->Count, 0); + +$art->AddCustomFieldValue(Field => $cf->id, Value => 'Testing'); +$values = $art->CustomFieldValues($art_cfs->First->id); + +# We added the custom field +is ($values->Count, 1); +is ($values->First->Content, 'Testing', "We added the CF"); + +is ($art->Transactions->Count,2, "We added a second transaction for the custom field addition"); +my $txn2 = $art->Transactions->Last; +is ($txn2->ObjectId, $art->id); +is ($txn2->id, ($txn->id +1)); +is ($txn2->Type, 'CustomField'); +is($txn2->NewValue, 'Testing'); +ok (!$txn2->OldValue, "It had no old value"); + +1; + diff --git a/rt/t/articles/cfsearch.t b/rt/t/articles/cfsearch.t new file mode 100644 index 000000000..49420e581 --- /dev/null +++ b/rt/t/articles/cfsearch.t @@ -0,0 +1,94 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use RT::Test tests => 11; + +my $suffix = '-'. $$; + +use_ok 'RT::Class'; +use_ok 'RT::Article'; +use_ok 'RT::CustomField'; + +my $classname = 'TestClass'; +my $class = RT::Class->new( $RT::SystemUser ); +{ + $class->Load( $classname ); + unless ( $class->Id ) { + my ($id, $msg) = $class->Create( + Name => $classname, + Description => 'class for cf tests', + ); + ok $id, "created class '$classname' #$id" + or diag "error: $msg"; + } else { + ok 1, "class '$classname' exists"; + } +} + +# create cf +my $cfname = 'TestCF'. $suffix; +my $cf = RT::CustomField->new( $RT::SystemUser ); +{ + my ($id, $msg) = $cf->Create( + Name => $cfname, + LookupType => 'RT::Class-RT::Article', + Type => 'Select', MaxValues => 1, + Description => 'singleselect cf for tests', + ); + ok $id, "created cf '$cfname' #$id" + or diag "error: $msg"; +} + +# attach cf to class +{ + my ($status, $msg) = $cf->AddToObject( $class ); + ok $status, "attached the cf to the class" + or diag "error: $msg"; +} + +# create two cf-values +{ + my ($status, $msg) = $cf->AddValue( Name => 'Value1' ); + ok $status, "added a value to the cf" or diag "error: $msg"; + + ($status, $msg) = $cf->AddValue( Name => 'Value2' ); + ok $status, "added a value to the cf" or diag "error: $msg"; +} + +my $article1name = 'TestArticle1'.$suffix; +my $article1 = RT::Article->new($RT::SystemUser); +$article1->Create( Name => $article1name, Summary => 'Test', Class => $class->Id); +$article1->AddCustomFieldValue(Field => $cf->Id, Value => 'Value1'); + +my $article2name = 'TestArticle2'.$suffix; +my $article2 = RT::Article->new($RT::SystemUser); +$article2->Create( Name => $article2name, Summary => 'Test', Class => $class->Id); +$article2->AddCustomFieldValue(Field => $cf->Id, Value => 'Value2'); + +# search for articles containing 1st value +{ + my $articles = RT::Articles->new( $RT::SystemUser ); + $articles->UnLimit; + $articles->Limit( FIELD => "Class", SUBCLAUSE => 'ClassMatch', VALUE => $class->Id); + $articles->LimitCustomField( FIELD => $cf->Id, VALUE => 'Value1' ); + is $articles->Count, 1, 'found correct number of articles'; +} + +{ + my $articles = RT::Articles->new($RT::SystemUser); + $articles->UnLimit; + $articles->Limit( FIELD => "Class", SUBCLAUSE => 'ClassMatch', VALUE => $class->Id); + $articles->LimitCustomField( FIELD => $cf, VALUE => 'Value1' ); + is $articles->Count, 1, 'found correct number of articles'; +} + +{ + my $articles = RT::Articles->new($RT::SystemUser); + $articles->UnLimit( ); + $articles->Limit( FIELD => "Class", SUBCLAUSE => 'ClassMatch', VALUE => $class->Id); + $articles->LimitCustomField( FIELD => $cf->Name, VALUE => 'Value1' ); + is $articles->Count, 1, 'found correct number of articles'; +} + diff --git a/rt/t/articles/class.t b/rt/t/articles/class.t new file mode 100644 index 000000000..84d6e23be --- /dev/null +++ b/rt/t/articles/class.t @@ -0,0 +1,76 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 24; + +use_ok 'RT::Articles'; +use_ok 'RT::Classes'; +use_ok 'RT::Class'; + +my $root = RT::CurrentUser->new('root'); +ok ($root->Id, "Loaded root"); +my $cl = RT::Class->new($root); +ok (UNIVERSAL::isa($cl, 'RT::Class'), "the new class is a class"); + +my ($id, $msg) = $cl->Create(Name => 'Test-'.$$, Description => 'A test class'); + +ok ($id, $msg); + +# no duplicate class names should be allowed +($id, $msg) = $cl->Create(Name => 'Test-'.$$, Description => 'A test class'); + +ok (!$id, $msg); + +#class name should be required + +($id, $msg) = $cl->Create(Name => '', Description => 'A test class'); + +ok (!$id, $msg); + + + +$cl->Load('Test-'.$$); +ok($cl->id, "Loaded the class we want"); + + + +# Create a new user. make sure they can't create a class + +my $u= RT::User->new(RT->SystemUser); +$u->Create(Name => "ArticlesTest".time, Privileged => 1); +ok ($u->Id, "Created a new user"); + +# Make sure you can't create a group with no acls +$cl = RT::Class->new($u); +ok (UNIVERSAL::isa($cl, 'RT::Class'), "the new class is a class"); + +($id, $msg) = $cl->Create(Name => 'Test-nobody'.$$, Description => 'A test class'); + + +ok (!$id, $msg. "- Can not create classes as a random new user - " .$u->Id); +$u->PrincipalObj->GrantRight(Right =>'AdminClass', Object => RT->System); +($id, $msg) = $cl->Create(Name => 'Test-nobody-'.$$, Description => 'A test class'); + +ok ($id, $msg. "- Can create classes as a random new user after ACL grant"); + +# now check the Web UI + +my ($url, $m) = RT::Test->started_ok; +ok($m->login); +$m->get_ok("$url/Admin/Articles/Classes/Modify.html?Create=1"); +$m->content_contains('Create a Class', 'found title'); +$m->submit_form_ok({ + form_number => 3, + fields => { Name => 'Test Redirect' }, +}); +$m->content_contains('Object created', 'found results'); +$m->content_contains('Modify the Class Test Redirect', 'found title'); +$m->form_number(3); +$m->untick( 'Include-Name', 1 ); +$m->field( 'Description', 'Test Description' ); +$m->submit(); +$m->content_like(qr/Description changed from.*no value.*to .*Test Description/,'description changed'); +$m->form_number(3); +is($m->current_form->find_input('Include-Name')->value,undef,'Disabled Including Names for this Class'); diff --git a/rt/t/articles/interface.t b/rt/t/articles/interface.t new file mode 100644 index 000000000..d04be8b88 --- /dev/null +++ b/rt/t/articles/interface.t @@ -0,0 +1,221 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 53; + +use RT::CustomField; +use RT::EmailParser; +use RT::Queue; +use RT::Ticket; +use_ok 'RT::Class'; +use_ok 'RT::Topic'; +use_ok 'RT::Article'; + +my ($url, $m) = RT::Test->started_ok; + +# Variables to test return values +my ($ret, $msg); + +# Create a test class +my $class = RT::Class->new($RT::SystemUser); +($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$, + 'Description' => 'A general-purpose test class'); +ok($ret, "Test class created"); +my $class2 = RT::Class->new($RT::SystemUser); +($ret, $msg) = $class2->Create('Name' => 'tlaTestClass2-'.$$, + 'Description' => 'Another general-purpose test class'); +ok($ret, "Test class 2 created"); + + +# Create a hierarchy of test topics +my $topic1 = RT::Topic->new($RT::SystemUser); +my $topic11 = RT::Topic->new($RT::SystemUser); +my $topic12 = RT::Topic->new($RT::SystemUser); +my $topic2 = RT::Topic->new($RT::SystemUser); +my $topic_class2= RT::Topic->new($RT::SystemUser); +my $gtopic = RT::Topic->new($RT::SystemUser); +($ret, $msg) = $topic1->Create('Parent' => 0, + 'Name' => 'tlaTestTopic1-'.$$, + 'ObjectType' => 'RT::Class', + 'ObjectId' => $class->Id); +ok($ret, "Topic 1 created"); +($ret, $msg) = $topic11->Create('Parent' => $topic1->Id, + 'Name' => 'tlaTestTopic1.1-'.$$, + 'ObjectType' => 'RT::Class', + 'ObjectId' => $class->Id); +ok($ret, "Topic 1.1 created"); +($ret, $msg) = $topic12->Create('Parent' => $topic1->Id, + 'Name' => 'tlaTestTopic1.2-'.$$, + 'ObjectType' => 'RT::Class', + 'ObjectId' => $class->Id); +ok($ret, "Topic 1.2 created"); +($ret, $msg) = $topic2->Create('Parent' => 0, + 'Name' => 'tlaTestTopic2-'.$$, + 'ObjectType' => 'RT::Class', + 'ObjectId' => $class->Id); +ok($ret, "Topic 2 created"); +($ret, $msg) = $topic_class2->Create('Parent' => 0, + 'Name' => 'tlaTestTopicClass2-'.$$, + 'ObjectType' => 'RT::Class', + 'ObjectId' => $class2->Id); +ok($ret, "Topic Class2 created"); +($ret, $msg) = $gtopic->Create('Parent' => 0, + 'Name' => 'tlaTestTopicGlobal-'.$$, + 'ObjectType' => 'RT::System', + 'ObjectId' => $RT::System->Id ); +ok($ret, "Global Topic created"); + +# Create some article custom fields + +my $questionCF = RT::CustomField->new($RT::SystemUser); +my $answerCF = RT::CustomField->new($RT::SystemUser); +($ret, $msg) = $questionCF->Create('Name' => 'Question-'.$$, + 'Type' => 'Text', + 'MaxValues' => 1, + 'LookupType' => 'RT::Class-RT::Article', + 'Description' => 'The question to be answered', + 'Disabled' => 0); +ok($ret, "Question CF created: $msg"); +($ret, $msg) = $answerCF->Create('Name' => 'Answer-'.$$, + 'Type' => 'Text', + 'MaxValues' => 1, + 'LookupType' => 'RT::Class-RT::Article', + 'Description' => 'The answer to the question', + 'Disabled' => 0); +ok($ret, "Answer CF created: $msg"); + +# Attach the custom fields to our class +($ret, $msg) = $questionCF->AddToObject($class); +ok($ret, "Question CF added to class: $msg"); +($ret, $msg) = $answerCF->AddToObject($class); +ok($ret, "Answer CF added to class: $msg"); +my ($qid, $aid) = ($questionCF->Id, $answerCF->Id); + +my %cvals = ('article1q' => 'Some question about swallows', + 'article1a' => 'Some answer about Europe and Africa', + 'article2q' => 'Another question about Monty Python', + 'article2a' => 'Romani ite domum', + 'article3q' => 'Why should I eat my supper?', + 'article3a' => 'There are starving children in Africa', + 'article4q' => 'What did Brian originally write?', + 'article4a' => 'Romanes eunt domus'); + +# Create an article or two with our custom field values. + +my $article1 = RT::Article->new($RT::SystemUser); +my $article2 = RT::Article->new($RT::SystemUser); +my $article3 = RT::Article->new($RT::SystemUser); +my $article4 = RT::Article->new($RT::SystemUser); +($ret, $msg) = $article1->Create(Name => 'First article '.$$, + Summary => 'blah blah 1', + Class => $class->Id, + Topics => [$topic1->Id], + "CustomField-$qid" => $cvals{'article1q'}, + "CustomField-$aid" => $cvals{'article1a'}, + ); +ok($ret, "article 1 created"); +($ret, $msg) = $article2->Create(Name => 'Second article '.$$, + Summary => 'foo bar 2', + Class => $class->Id, + Topics => [$topic11->Id], + "CustomField-$qid" => $cvals{'article2q'}, + "CustomField-$aid" => $cvals{'article2a'}, + ); +ok($ret, "article 2 created"); +($ret, $msg) = $article3->Create(Name => 'Third article '.$$, + Summary => 'ping pong 3', + Class => $class->Id, + Topics => [$topic12->Id], + "CustomField-$qid" => $cvals{'article3q'}, + "CustomField-$aid" => $cvals{'article3a'}, + ); +ok($ret, "article 3 created"); +($ret, $msg) = $article4->Create(Name => 'Fourth article '.$$, + Summary => 'hoi polloi 4', + Class => $class->Id, + Topics => [$topic2->Id], + "CustomField-$qid" => $cvals{'article4q'}, + "CustomField-$aid" => $cvals{'article4a'}, + ); +ok($ret, "article 4 created"); + +# Create a ticket. +my $parser = RT::EmailParser->new(); +$parser->ParseMIMEEntityFromScalar('From: root@localhost +To: rt@example.com +Subject: test ticket for articles + +This is some form of new request. +May as well say something about Africa.'); + +my $ticket = RT::Ticket->new($RT::SystemUser); +my $obj; +($ret, $obj, $msg) = $ticket->Create(Queue => 'General', + Subject => 'test ticket for articles '.$$, + MIMEObj => $parser->Entity); +ok($ret, "Test ticket for articles created: $msg"); + + +#### Right. That's our data. Now begin the real testing. + +isa_ok($m, 'Test::WWW::Mechanize'); +ok($m->login, 'logged in'); +$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! }, + 'UI -> Articles' ); + +$m->content_contains($article3->Name); +$m->follow_link_ok( {text => $article3->Name}, 'Articles -> '. $article3->Name ); +$m->title_is("Article #" . $article3->Id . ": " . $article3->Name); +$m->follow_link_ok( { text => 'Modify'}, 'Article -> Modify' ); + +{ +$m->content_like(qr/Refers to/, "found links edit box"); +my $ticket_id = $ticket->Id; +my $turi = "t:$ticket_id"; +my $a1uri = 'a:'.$article1->Id; +$m->submit_form(form_name => 'EditArticle', + fields => { $article3->Id.'-RefersTo' => $turi, + 'RefersTo-'.$article3->Id => $a1uri } + ); + +$m->content_like(qr/Ticket.*$ticket_id/, "Ticket linkto was created"); +$m->content_like(qr/URI.*$a1uri/, "Article linkfrom was created"); +} + +# Now try to extract an article from a link. +$m->get_ok($url."/Ticket/Display.html?id=".$ticket->Id, + "Loaded ticket display"); +$m->content_like(qr/Extract Article/, "Article extraction link shows up"); +$m->follow_link_ok( { text => 'Extract Article' }, '-> Extract Article' ); +$m->content_contains($class->Name); +$m->follow_link_ok( { text => $class->Name }, 'Extract Article -> '. $class->Name ); +$m->content_like(qr/Select topics for this article/i, 'selecting topic'); +$m->form_number(3); +$m->set_visible([option => $topic1->Name]); +$m->submit; +$m->form_number(3); +$m->set_visible([option => $answerCF->Name]); +$m->click(); +$m->title_like(qr/Create a new article/, "got edit page from extraction"); +$m->submit_form(form_name => 'EditArticle'); +$m->title_like(qr/Modify article/); +$m->follow_link_ok( { text => 'Display' }, '-> Display' ); +$m->content_like(qr/Africa/, "Article content exist"); +$m->content_contains($ticket->Subject, + "Article references originating ticket"); + +diag("Test creating a ticket in Class2 and make sure we don't see Class1 Topics") if $ENV{TEST_VERBOSE}; +{ +$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! }, + 'UI -> Articles' ); +$m->follow_link_ok( {text => 'New Article' }, 'Articles -> New Article' ); +$m->follow_link_ok( {text => 'in class '.$class2->Name }, 'New Article -> in class '.$class2->Name ); +$m->content_lacks( $topic1->Name, "Topic1 from Class1 isn't shown" ); +$m->content_lacks( $topic11->Name, "Topic11 from Class1 isn't shown" ); +$m->content_lacks( $topic12->Name, "Topic12 from Class1 isn't shown" ); +$m->content_lacks( $topic2->Name, "Topic2 from Class1 isn't shown" ); +$m->content_contains( $gtopic->Name, "Global Topic is shown" ); +$m->content_contains( $topic_class2->Name, "Class2 topic is shown" ); +} diff --git a/rt/t/articles/queue-specific-class.t b/rt/t/articles/queue-specific-class.t new file mode 100644 index 000000000..a73d583c3 --- /dev/null +++ b/rt/t/articles/queue-specific-class.t @@ -0,0 +1,218 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 56; + +my ( $url, $m ) = RT::Test->started_ok; +$m->login; + +my %class = map { $_ => '' } qw/foo bar/; + +diag "create classes foo and bar" if $ENV{TEST_VERBOSE}; + +for my $name ( keys %class ) { + $m->get_ok( '/Admin/Articles/Classes/Modify.html?Create=1', + 'class create page' ); + + $m->submit_form( + form_number => 3, + fields => { Name => $name, HotList => 1 }, + ); + + $m->content_contains( "Modify the Class $name", + 'created class $name' ); + my ($id) = ( $m->content =~ /name="id" value="(\d+)"/ ); + ok( $id, "id of $name" ); + $class{$name} = $id; +} + +diag "create articles in foo and bar" if $ENV{TEST_VERBOSE}; + +for my $name ( keys %class ) { + $m->get_ok( '/Articles/Article/Edit.html?Class=' . $class{$name}, + 'article create page' ); + + $m->submit_form( + form_number => 3, + fields => { Name => "article $name" } + ); + + $m->content_like( qr/Article \d+ created/, "created article $name" ); +} + +diag "apply foo to queue General" if $ENV{TEST_VERBOSE}; +{ + $m->get_ok( '/Admin/Articles/Classes/Objects.html?id=' . $class{foo}, + 'apply page' ); + $m->submit_form( + form_number => 3, + fields => { 'AddClass-' . $class{foo} => 1 }, + button => 'UpdateObjs', + ); + $m->content_contains( 'Object created', 'applied foo to General' ); +} + +my $ticket_id; + +diag "create ticket in General" if $ENV{TEST_VERBOSE}; + +{ + $m->get_ok( '/Ticket/Create.html?Queue=1', 'ticket create page' ); + $m->submit_form( + form_number => 3, + fields => { 'Subject' => 'test article', Content => 'test article' }, + ); + ($ticket_id) = ( $m->content =~ /Ticket \d+ created/ ); + ok( $ticket_id, "id of ticket: $ticket_id" ); +} + +diag "update ticket to see if there is article foo" + if $ENV{TEST_VERBOSE}; + +{ + $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id, + 'ticket update page' ); + $m->content_contains( 'article foo:', 'got article foo in hotlist' ); + $m->content_lacks( 'article bar:', 'no article bar in hotlist' ); + + $m->submit_form( + form_number => 3, + fields => { 'Articles_Content' => 'article' }, + button => 'Go', + ); + $m->content_like( qr/article foo.*article foo/s, 'selected article foo' ); + $m->content_lacks( 'article bar', 'no article bar' ); + + $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id, + 'ticket update page' ); + $m->submit_form( + form_number => 3, + fields => { 'Articles-Include-Article-Named' => 'article foo' }, + button => 'Go', + ); + $m->content_like( qr/article foo.*article foo/s, 'selected article foo' ); + $m->content_lacks( 'article bar', 'no article bar' ); + + $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id, + 'ticket update page' ); + $m->submit_form( + form_number => 3, + fields => { 'Articles-Include-Article-Named' => 'articlei bar' }, + button => 'Go', + ); + $m->content_unlike( qr/article foo.*article foo/s, 'no article foo' ); + $m->content_lacks( 'article bar', 'no article bar' ); +} + +diag "apply bar to globally" if $ENV{TEST_VERBOSE}; +{ + $m->get_ok( '/Admin/Articles/Classes/Objects.html?id=' . $class{bar}, + 'apply page' ); + $m->submit_form( + form_number => 3, + fields => { 'AddClass-' . $class{bar} => 0 }, + button => 'UpdateObjs', + ); + $m->content_contains( 'Object created', 'applied bar globally' ); +} + +diag "update ticket to see if there are both article foo and bar" + if $ENV{TEST_VERBOSE}; + +{ + $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id, + 'ticket update page' ); + $m->content_contains( 'article foo:', 'got article foo in hotlist' ); + $m->content_contains( 'article bar:', 'got article bar in hotlist' ); + + $m->submit_form( + form_number => 3, + fields => { 'Articles_Content' => 'article' }, + button => 'Go', + ); + $m->content_like( qr/article foo.*article foo/s, 'selected article foo' ); + $m->content_like( qr/article bar.*article bar/s, 'selected article bar' ); + + $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id, + 'ticket update page' ); + $m->submit_form( + form_number => 3, + fields => { 'Articles-Include-Article-Named' => 'article foo' }, + button => 'Go', + ); + $m->content_like( qr/article foo.*article foo/s, 'selected article foo' ); + $m->content_unlike( qr/article bar.*article bar/s, 'no article bar' ); + + $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id, + 'ticket update page' ); + $m->submit_form( + form_number => 3, + fields => { 'Articles-Include-Article-Named' => 'article bar' }, + button => 'Go', + ); + $m->content_like( qr/article bar.*article bar/s, 'selected article bar' ); + $m->content_unlike( qr/article foo.*article foo/s, 'no article foo' ); +} + + +diag "remove both foo and bar" if $ENV{TEST_VERBOSE}; +{ + $m->get_ok( '/Admin/Articles/Classes/Objects.html?id=' . $class{foo}, + 'apply page' ); + $m->submit_form( + form_number => 3, + fields => { 'RemoveClass-' . $class{foo} => 1 }, + button => 'UpdateObjs', + ); + $m->content_contains( 'Object deleted', 'removed foo' ); + + $m->get_ok( '/Admin/Articles/Classes/Objects.html?id=' . $class{bar}, + 'apply page' ); + $m->submit_form( + form_number => 3, + fields => { 'RemoveClass-' . $class{bar} => 0 }, + button => 'UpdateObjs', + ); + $m->content_contains( 'Object deleted', 'remoked bar' ); +} + +diag "update ticket to see if there are both article foo and bar" + if $ENV{TEST_VERBOSE}; + +{ + $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id, + 'ticket update page' ); + $m->content_lacks( 'article foo:', 'no article foo in hotlist' ); + $m->content_lacks( 'article bar:', 'no article bar in hotlist' ); + + $m->submit_form( + form_number => 3, + fields => { 'Articles_Content' => 'article' }, + button => 'Go', + ); + $m->content_lacks( 'article foo', 'no article foo' ); + $m->content_lacks( 'article bar', 'no article bar' ); + + $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id, + 'ticket update page' ); + $m->submit_form( + form_number => 3, + fields => { 'Articles-Include-Article-Named' => 'article foo' }, + button => 'Go', + ); + $m->content_lacks( 'article foo', 'no article foo' ); + $m->content_lacks( 'article bar', 'no article bar' ); + + $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id, + 'ticket update page' ); + $m->submit_form( + form_number => 3, + fields => { 'Articles-Include-Article-Named' => 'article bar' }, + button => 'Go', + ); + $m->content_lacks( 'article foo', 'no article foo' ); + $m->content_lacks( 'article bar', 'no article bar' ); +} + diff --git a/rt/t/articles/search-interface.t b/rt/t/articles/search-interface.t new file mode 100644 index 000000000..eb3a4f763 --- /dev/null +++ b/rt/t/articles/search-interface.t @@ -0,0 +1,113 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 23; + +use RT::CustomField; +use RT::Queue; +use RT::Ticket; +use_ok 'RT::Class'; +use_ok 'RT::Topic'; +use_ok 'RT::Article'; + +my ($url, $m) = RT::Test->started_ok; + +# Variables to test return values +my ($ret, $msg); + +# Create a test class +my $class = RT::Class->new($RT::SystemUser); +($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$, + 'Description' => 'A general-purpose test class'); +ok($ret, "Test class created"); + + +my $questionCF = RT::CustomField->new($RT::SystemUser); +my $answerCF = RT::CustomField->new($RT::SystemUser); +my $ticketCF = RT::CustomField->new($RT::SystemUser); +($ret, $msg) = $questionCF->Create('Name' => 'Question-'.$$, + 'Type' => 'Text', + 'MaxValues' => 1, + 'LookupType' => 'RT::Class-RT::Article', + 'Description' => 'The question to be answered', + 'Disabled' => 0); +ok($ret, "Question CF created: $msg"); +($ret, $msg) = $answerCF->Create('Name' => 'Answer-'.$$, + 'Type' => 'Text', + 'MaxValues' => 1, + 'LookupType' => 'RT::Class-RT::Article', + 'Description' => 'The answer to the question', + 'Disabled' => 0); +ok($ret, "Answer CF created: $msg"); + +($ret, $msg) = $ticketCF->Create('Name' => 'Class', + 'Type' => 'Text', + 'MaxValues' => 1, + 'LookupType' => 'RT::Queue-RT::Ticket', + 'Disabled' => 0); +ok($ret, "Ticket CF 'Class' created: $msg"); + +# Attach the custom fields to our class +($ret, $msg) = $questionCF->AddToObject($class); +ok($ret, "Question CF added to class: $msg"); +($ret, $msg) = $answerCF->AddToObject($class); +ok($ret, "Answer CF added to class: $msg"); +my ($qid, $aid) = ($questionCF->Id, $answerCF->Id); + +my $global_queue = RT::Queue->new($RT::SystemUser); +($ret, $msg) = $ticketCF->AddToObject($global_queue); +ok($ret, "Ticket CF added globally: $msg"); + +my %cvals = ('article1q' => 'Some question about swallows', + 'article1a' => 'Some answer about Europe and Africa', + 'article2q' => 'Another question about Monty Python', + 'article2a' => 'Romani ite domum', + 'article3q' => 'Why should I eat my supper?', + 'article3a' => 'There are starving children in Africa', + 'article4q' => 'What did Brian originally write?', + 'article4a' => 'Romanes eunt domus'); + +# Create an article or two with our custom field values. + +my $article1 = RT::Article->new($RT::SystemUser); +my $article2 = RT::Article->new($RT::SystemUser); +my $article3 = RT::Article->new($RT::SystemUser); +my $article4 = RT::Article->new($RT::SystemUser); +($ret, $msg) = $article1->Create(Name => 'First article '.$$, + Summary => 'blah blah 1', + Class => $class->Id, + "CustomField-$qid" => $cvals{'article1q'}, + "CustomField-$aid" => $cvals{'article1a'}, + ); +ok($ret, "article 1 created"); +($ret, $msg) = $article2->Create(Name => 'Second article '.$$, + Summary => 'foo bar 2', + Class => $class->Id, + "CustomField-$qid" => $cvals{'article2q'}, + "CustomField-$aid" => $cvals{'article2a'}, + ); +ok($ret, "article 2 created"); +($ret, $msg) = $article3->Create(Name => 'Third article '.$$, + Summary => 'ping pong 3', + Class => $class->Id, + "CustomField-$qid" => $cvals{'article3q'}, + "CustomField-$aid" => $cvals{'article3a'}, + ); +ok($ret, "article 3 created"); +($ret, $msg) = $article4->Create(Name => 'Fourth article '.$$, + Summary => 'hoi polloi 4', + Class => $class->Id, + "CustomField-$qid" => $cvals{'article4q'}, + "CustomField-$aid" => $cvals{'article4a'}, + ); +ok($ret, "article 4 created"); + +isa_ok($m, 'Test::WWW::Mechanize'); +ok($m->login, 'logged in'); +$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! }, + 'UI -> Articles' ); +$m->follow_link_ok( {text => 'Search'}, 'Articles -> Search'); +$m->follow_link_ok( {text => 'in class '.$class->Name}, 'Articles in class '.$class->Name); +$m->content_contains($article1->Name); diff --git a/rt/t/articles/upload-customfields.t b/rt/t/articles/upload-customfields.t new file mode 100644 index 000000000..912c23ded --- /dev/null +++ b/rt/t/articles/upload-customfields.t @@ -0,0 +1,90 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 20; + +use RT; +my $logo; +BEGIN { + $logo = + -e $RT::MasonComponentRoot . '/NoAuth/images/bpslogo.png' + ? 'bpslogo.png' + : 'bplogo.gif'; +} + +use constant ImageFile => $RT::MasonComponentRoot . "/NoAuth/images/$logo"; + +use constant ImageFileContent => do { + local $/; + open my $fh, '<', ImageFile or die ImageFile.$!; + binmode($fh); + scalar <$fh>; +}; + +use RT::Class; +my $class = RT::Class->new($RT::SystemUser); +my ($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$, + 'Description' => 'A general-purpose test class'); +ok($ret, "Test class created"); + + +my ($url, $m) = RT::Test->started_ok; +isa_ok($m, 'Test::WWW::Mechanize'); +ok($m->login, 'logged in'); +$m->follow_link_ok( { text => 'Configuration' } ); +$m->title_is(q/RT Administration/, 'admin screen'); +$m->follow_link_ok( { text => 'Custom Fields' } ); +$m->title_is(q/Select a Custom Field/, 'admin-cf screen'); +$m->follow_link_ok( { text => 'Create', url_regex => qr{^/Admin/CustomFields/} } ); +$m->submit_form( + form_name => "ModifyCustomField", + fields => { + TypeComposite => 'Image-0', + LookupType => 'RT::Class-RT::Article', + Name => 'img'.$$, + Description => 'img', + }, +); +$m->title_is(qq/Editing CustomField img$$/, 'admin-cf created'); +$m->follow_link( text => 'Applies to' ); +$m->title_is(qq/Modify associated objects for img$$/, 'pick cf screenadmin screen'); +$m->form_number(3); + +my $tcf = (grep { /AddCustomField-/ } map { $_->name } $m->current_form->inputs )[0]; +$m->tick( $tcf, 0 ); # Associate the new CF with this queue +$m->click('UpdateObjs'); +$m->content_like( qr/Object created/, 'TCF added to the queue' ); + +$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! }, + 'UI -> Articles' ); +$m->follow_link( text => 'New Article'); + +$m->follow_link( url_regex => qr/Edit.html\?Class=1/ ); +$m->title_is(qq/Create a new article/); + +$m->content_like(qr/Upload multiple images/, 'has a upload image field'); + +$tcf =~ /(\d+)$/ or die "Hey this is impossible dude"; +my $upload_field = "Object-RT::Article--CustomField-$1-Upload"; + +diag("Uploading an image to $upload_field") if $ENV{TEST_VERBOSE}; + +$m->submit_form( + form_name => "EditArticle", + fields => { + $upload_field => ImageFile, + Name => 'Image Test '.$$, + Summary => 'testing img cf creation', + }, +); + +$m->content_like(qr/Article \d+ created/, "an article was created succesfully"); + +my $id = $1 if $m->content =~ /Article (\d+) created/; + +$m->title_like(qr/Modify article #$id/, "Editing article $id"); + +$m->follow_link( text => $logo ); +$m->content_is(ImageFileContent, "it links to the uploaded image"); diff --git a/rt/t/articles/uri-a.t b/rt/t/articles/uri-a.t new file mode 100644 index 000000000..82d0f1b01 --- /dev/null +++ b/rt/t/articles/uri-a.t @@ -0,0 +1,28 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 7; + +use_ok("RT::URI::a"); +my $uri = RT::URI::a->new($RT::SystemUser); +ok(ref($uri), "URI object exists"); + +my $class = RT::Class->new( $RT::SystemUser ); +$class->Create( Name => 'URItest - '. $$ ); +ok $class->id, 'created a class'; +my $article = RT::Article->new( $RT::SystemUser ); +my ($id, $msg) = $article->Create( + Name => 'Testing URI parsing - '. $$, + Summary => 'In which this should load', + Class => $class->Id +); +ok($id,$msg); + +my $uristr = "a:" . $article->Id; +$uri->ParseURI($uristr); +is(ref($uri->Object), "RT::Article", "Object loaded is an article"); +is($uri->Object->Id, $article->Id, "Object loaded has correct ID"); +is($article->URI, 'fsck.com-article://example.com/article/'.$article->Id, + "URI object has correct URI string"); diff --git a/rt/t/articles/uri-articles.t b/rt/t/articles/uri-articles.t new file mode 100644 index 000000000..4124b1942 --- /dev/null +++ b/rt/t/articles/uri-articles.t @@ -0,0 +1,31 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 9; + +use_ok "RT::URI::fsck_com_article"; +my $uri = RT::URI::fsck_com_article->new( $RT::SystemUser ); + +ok $uri; +isa_ok $uri, 'RT::URI::fsck_com_article'; +isa_ok $uri, 'RT::URI::base'; +isa_ok $uri, 'RT::Base'; + +is $uri->LocalURIPrefix, 'fsck.com-article://example.com/article/'; + +my $class = RT::Class->new( $RT::SystemUser ); +$class->Create( Name => 'URItest - '. $$ ); +ok $class->id, 'created a class'; +my $article = RT::Article->new( $RT::SystemUser ); +my ($id, $msg) = $article->Create( + Name => 'Testing URI parsing - '. $$, + Summary => 'In which this should load', + Class => $class->Id +); +ok($id,$msg); + +$uri = RT::URI::fsck_com_article->new( $article->CurrentUser ); +is $uri->LocalURIPrefix . $article->id, $uri->URIForObject( $article ); + diff --git a/rt/t/customfields/access_via_queue.t b/rt/t/customfields/access_via_queue.t index c291860ea..690e177df 100644 --- a/rt/t/customfields/access_via_queue.t +++ b/rt/t/customfields/access_via_queue.t @@ -3,7 +3,7 @@ use strict; use warnings; -use RT::Test tests => 35; +use RT::Test nodata => 1, tests => 37; use RT::Ticket; use RT::CustomField; @@ -11,11 +11,11 @@ my $queue_name = "CFSortQueue-$$"; my $queue = RT::Test->load_or_create_queue( Name => $queue_name ); ok($queue && $queue->id, "$queue_name - test queue creation"); -diag "create a CF\n" if $ENV{TEST_VERBOSE}; +diag "create a CF"; my $cf_name = "Rights$$"; my $cf; { - $cf = RT::CustomField->new( $RT::SystemUser ); + $cf = RT::CustomField->new( RT->SystemUser ); my ($ret, $msg) = $cf->Create( Name => $cf_name, Queue => $queue->id, @@ -98,7 +98,7 @@ ok( RT::Test->set_rights( my ($baseurl, $m) = RT::Test->started_ok; ok $m->login( tester => 'password' ), 'logged in'; -diag "check that we have no the CF on the create" if $ENV{'TEST_VERBOSE'}; +diag "check that we don't have the cf on create"; { $m->submit_form( form_name => "CreateTicketInQueue", @@ -115,46 +115,46 @@ diag "check that we have no the CF on the create" if $ENV{'TEST_VERBOSE'}; ); my ($tid) = ($m->content =~ /Ticket (\d+) created/i); ok $tid, "created a ticket succesfully"; - $m->content_unlike(qr/$cf_name/, "don't see CF"); + $m->content_lacks($cf_name, "don't see CF"); - $m->follow_link( text => 'Custom Fields' ); - $form = $m->form_number(3); + $m->follow_link( id => 'page-basics'); + $form = $m->form_name('TicketModify'); $cf_field = "Object-RT::Ticket-$tid-CustomField-". $cf->id ."-Value"; ok !$form->find_input( $cf_field ), 'no form field on the page'; } -diag "check that we see CF as Cc" if $ENV{'TEST_VERBOSE'}; +diag "check that we see CF as Cc"; { my $ticket = RT::Ticket->new( $tester ); my ($tid, $msg) = $ticket->Create( Queue => $queue, Subject => 'test', Cc => $tester->id ); ok $tid, "created ticket"; ok $m->goto_ticket( $tid ), "opened ticket"; - $m->content_like(qr/$cf_name/, "see CF"); + $m->content_contains($cf_name, "see CF"); } -diag "check that owner can see and edit CF" if $ENV{'TEST_VERBOSE'}; +diag "check that owner can see and edit CF"; { my $ticket = RT::Ticket->new( $tester ); my ($tid, $msg) = $ticket->Create( Queue => $queue, Subject => 'test', Cc => $tester->id, Owner => $tester->id ); ok $tid, "created ticket"; ok $m->goto_ticket( $tid ), "opened ticket"; - $m->content_like(qr/$cf_name/, "see CF"); + $m->content_contains($cf_name, "see CF"); - $m->follow_link( text => 'Custom Fields' ); - my $form = $m->form_number(3); + $m->follow_link( id => 'page-basics'); + my $form = $m->form_name('TicketModify'); my $cf_field = "Object-RT::Ticket-$tid-CustomField-". $cf->id ."-Value"; ok $form->find_input( $cf_field ), 'form field on the page'; $m->submit_form( - form_number => 3, + form_name => 'TicketModify', fields => { $cf_field => "changed cf", }, ); ok $m->goto_ticket( $tid ), "opened ticket"; - $m->content_like(qr/$cf_name/, "changed cf"); + $m->content_contains($cf_name, "changed cf"); } diff --git a/rt/t/customfields/api.t b/rt/t/customfields/api.t new file mode 100644 index 000000000..d739a572d --- /dev/null +++ b/rt/t/customfields/api.t @@ -0,0 +1,221 @@ +#!/usr/bin/perl + +use strict; +use warnings FATAL => 'all'; + +use RT::Test nodata => 1, tests => 139; + +# Before we get going, ditch all object_cfs; this will remove +# all custom fields systemwide; +my $object_cfs = RT::ObjectCustomFields->new(RT->SystemUser); +$object_cfs->UnLimit(); +while (my $ocf = $object_cfs->Next) { + $ocf->Delete(); +} + + +my $queue = RT::Queue->new( RT->SystemUser ); +$queue->Create( Name => 'RecordCustomFields-'.$$ ); +ok ($queue->id, "Created the queue"); + +my $queue2 = RT::Queue->new( RT->SystemUser ); +$queue2->Create( Name => 'RecordCustomFields2' ); + +my $ticket = RT::Ticket->new( RT->SystemUser ); +$ticket->Create( + Queue => $queue->Id, + Requestor => 'root@localhost', + Subject => 'RecordCustomFields1', +); + +my $cfs = $ticket->CustomFields; +is( $cfs->Count, 0 ); + +# Check that record has no any CF values yet {{{ +my $cfvs = $ticket->CustomFieldValues; +is( $cfvs->Count, 0 ); +is( $ticket->FirstCustomFieldValue, undef ); + +my $local_cf1 = RT::CustomField->new( RT->SystemUser ); +$local_cf1->Create( Name => 'RecordCustomFields1-'.$$, Type => 'SelectSingle', Queue => $queue->id ); +$local_cf1->AddValue( Name => 'RecordCustomFieldValues11' ); +$local_cf1->AddValue( Name => 'RecordCustomFieldValues12' ); + +my $local_cf2 = RT::CustomField->new( RT->SystemUser ); +$local_cf2->Create( Name => 'RecordCustomFields2-'.$$, Type => 'SelectSingle', Queue => $queue->id ); +$local_cf2->AddValue( Name => 'RecordCustomFieldValues21' ); +$local_cf2->AddValue( Name => 'RecordCustomFieldValues22' ); + +my $global_cf3 = RT::CustomField->new( RT->SystemUser ); +$global_cf3->Create( Name => 'RecordCustomFields3-'.$$, Type => 'SelectSingle', Queue => 0 ); +$global_cf3->AddValue( Name => 'RecordCustomFieldValues31' ); +$global_cf3->AddValue( Name => 'RecordCustomFieldValues32' ); + +my $local_cf4 = RT::CustomField->new( RT->SystemUser ); +$local_cf4->Create( Name => 'RecordCustomFields4', Type => 'SelectSingle', Queue => $queue2->id ); +$local_cf4->AddValue( Name => 'RecordCustomFieldValues41' ); +$local_cf4->AddValue( Name => 'RecordCustomFieldValues42' ); + + +my @custom_fields = ($local_cf1, $local_cf2, $global_cf3); + + +$cfs = $ticket->CustomFields; +is( $cfs->Count, 3 ); + +# Check that record has no any CF values yet {{{ +$cfvs = $ticket->CustomFieldValues; +is( $cfvs->Count, 0 ); +is( $ticket->FirstCustomFieldValue, undef ); + +# CF with ID -1 shouldnt exist at all +$cfvs = $ticket->CustomFieldValues( -1 ); +is( $cfvs->Count, 0 ); +is( $ticket->FirstCustomFieldValue( -1 ), undef ); + +$cfvs = $ticket->CustomFieldValues( 'SomeUnexpedCustomFieldName' ); +is( $cfvs->Count, 0 ); +is( $ticket->FirstCustomFieldValue( 'SomeUnexpedCustomFieldName' ), undef ); + +for (@custom_fields) { + $cfvs = $ticket->CustomFieldValues( $_->id ); + is( $cfvs->Count, 0 ); + + $cfvs = $ticket->CustomFieldValues( $_->Name ); + is( $cfvs->Count, 0 ); + is( $ticket->FirstCustomFieldValue( $_->id ), undef ); + is( $ticket->FirstCustomFieldValue( $_->Name ), undef ); +} + +# try to add field value with fields that do not exist {{{ +my ($status, $msg) = $ticket->AddCustomFieldValue( Field => -1 , Value => 'foo' ); +ok(!$status, "shouldn't add value" ); +($status, $msg) = $ticket->AddCustomFieldValue( Field => 'SomeUnexpedCustomFieldName' , Value => 'foo' ); +ok(!$status, "shouldn't add value" ); + +SKIP: { + + skip "TODO: We want fields that are not allowed to set unexpected values", 10; + for (@custom_fields) { + ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_ , Value => 'SomeUnexpectedCFValue' ); + ok( !$status, 'value doesn\'t exist'); + + ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_->id , Value => 'SomeUnexpectedCFValue' ); + ok( !$status, 'value doesn\'t exist'); + + ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_->Name , Value => 'SomeUnexpectedCFValue' ); + ok( !$status, 'value doesn\'t exist'); + } + + # Let check that we did not add value to be sure + # using only FirstCustomFieldValue sub because + # we checked other variants allready + for (@custom_fields) { + is( $ticket->FirstCustomFieldValue( $_->id ), undef ); + } + +} +# Add some values to our custom fields +for (@custom_fields) { + # this should be tested elsewhere + $_->AddValue( Name => 'Foo' ); + $_->AddValue( Name => 'Bar' ); +} + +my $test_add_delete_cycle = sub { + my $cb = shift; + for (@custom_fields) { + ($status, $msg) = $ticket->AddCustomFieldValue( Field => $cb->($_) , Value => 'Foo' ); + ok( $status, "message: $msg"); + } + + # does it exist? + $cfvs = $ticket->CustomFieldValues; + is( $cfvs->Count, 3, "We found all three custom fields on our ticket" ); + for (@custom_fields) { + $cfvs = $ticket->CustomFieldValues( $_->id ); + is( $cfvs->Count, 1 , "we found one custom field when searching by id"); + + $cfvs = $ticket->CustomFieldValues( $_->Name ); + is( $cfvs->Count, 1 , " We found one custom field when searching by name for " . $_->Name); + is( $ticket->FirstCustomFieldValue( $_->id ), 'Foo' , "first value by id is foo"); + is( $ticket->FirstCustomFieldValue( $_->Name ), 'Foo' , "first value by name is foo"); + } + # because our CFs are SingleValue then new value addition should override + for (@custom_fields) { + ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_ , Value => 'Bar' ); + ok( $status, "message: $msg"); + } + $cfvs = $ticket->CustomFieldValues; + is( $cfvs->Count, 3 ); + for (@custom_fields) { + $cfvs = $ticket->CustomFieldValues( $_->id ); + is( $cfvs->Count, 1 ); + + $cfvs = $ticket->CustomFieldValues( $_->Name ); + is( $cfvs->Count, 1 ); + is( $ticket->FirstCustomFieldValue( $_->id ), 'Bar' ); + is( $ticket->FirstCustomFieldValue( $_->Name ), 'Bar' ); + } + # delete it + for (@custom_fields ) { + ($status, $msg) = $ticket->DeleteCustomFieldValue( Field => $_ , Value => 'Bar' ); + ok( $status, "Deleted a custom field value 'Bar' for field ".$_->id.": $msg"); + } + $cfvs = $ticket->CustomFieldValues; + is( $cfvs->Count, 0, "The ticket (".$ticket->id.") no longer has any custom field values" ); + for (@custom_fields) { + $cfvs = $ticket->CustomFieldValues( $_->id ); + is( $cfvs->Count, 0, $ticket->id." has no values for cf ".$_->id ); + + $cfvs = $ticket->CustomFieldValues( $_->Name ); + is( $cfvs->Count, 0 , $ticket->id." has no values for cf '".$_->Name. "'" ); + is( $ticket->FirstCustomFieldValue( $_->id ), undef , "There is no first custom field value when loading by id" ); + is( $ticket->FirstCustomFieldValue( $_->Name ), undef, "There is no first custom field value when loading by Name" ); + } +}; + +# lets test cycle via CF id +$test_add_delete_cycle->( sub { return $_[0]->id } ); +# lets test cycle via CF object reference +$test_add_delete_cycle->( sub { return $_[0] } ); + +$ticket->AddCustomFieldValue( Field => $local_cf2->id , Value => 'Baz' ); +$ticket->AddCustomFieldValue( Field => $global_cf3->id , Value => 'Baz' ); +# now if we ask for cf values on RecordCustomFields4 we should not get any +$cfvs = $ticket->CustomFieldValues( 'RecordCustomFields4' ); +is( $cfvs->Count, 0, "No custom field values for non-Queue cf" ); +is( $ticket->FirstCustomFieldValue( 'RecordCustomFields4' ), undef, "No first custom field value for non-Queue cf" ); + +{ + my $cfname = $global_cf3->Name; + ($status, $msg) = $global_cf3->SetDisabled(1); + ok($status, "Disabled CF named $cfname"); + + my $load = RT::CustomField->new( RT->SystemUser ); + $load->LoadByName( Name => $cfname); + ok($load->Id, "Loaded CF named $cfname"); + is($load->Id, $global_cf3->Id, "Can load disabled CFs"); + + my $dup = RT::CustomField->new( RT->SystemUser ); + $dup->Create( Name => $cfname, Type => 'SelectSingle', Queue => 0 ); + ok($dup->Id, "Created CF with duplicate name"); + + $load->LoadByName( Name => $cfname); + is($load->Id, $dup->Id, "Loading by name gets non-disabled first"); + + $dup->SetDisabled(1); + $global_cf3->SetDisabled(0); + + $load->LoadByName( Name => $cfname); + is($load->Id, $global_cf3->Id, "Loading by name gets non-disabled first, even with order swapped"); +} + +#SKIP: { +# skip "TODO: should we add CF values to objects via CF Name?", 48; +# names are not unique + # lets test cycle via CF Name +# $test_add_delete_cycle->( sub { return $_[0]->Name } ); +#} + + diff --git a/rt/t/customfields/combo_cascade.t b/rt/t/customfields/combo_cascade.t new file mode 100644 index 000000000..28eee4527 --- /dev/null +++ b/rt/t/customfields/combo_cascade.t @@ -0,0 +1,37 @@ +#!/usr/bin/perl +use warnings; +use strict; + +use RT::Test nodata => 1, tests => 11; + +my $q = RT::Queue->new($RT::SystemUser); +works($q->Create(Name => "CF-Pattern-".$$)); + +my $cf = RT::CustomField->new($RT::SystemUser); +my @cf_args = (Name => $q->Name, Type => 'Combobox', Queue => $q->id); + +works($cf->Create(@cf_args)); + +# Set some CFVs with Category markers + +my $t = RT::Ticket->new($RT::SystemUser); +my ($id,undef,$msg) = $t->Create(Queue => $q->id, Subject => 'CF Test'); +works($id,$msg); + +sub add_works { + works( + $cf->AddValue(Name => $_[0], Description => $_[0], Category => $_[1]) + ); +}; + +add_works('value1', '1. Category A'); +add_works('value2'); +add_works('value3', '1.1. A-sub one'); +add_works('value4', '1.2. A-sub two'); +add_works('value5', ''); + +my $cfv = $cf->Values->First; +is($cfv->Category, '1. Category A'); +works($cfv->SetCategory('1. Category AAA')); +is($cfv->Category, '1. Category AAA'); + diff --git a/rt/t/customfields/date_search.t b/rt/t/customfields/date_search.t new file mode 100644 index 000000000..b425b9e36 --- /dev/null +++ b/rt/t/customfields/date_search.t @@ -0,0 +1,119 @@ +#!/usr/bin/perl + +use warnings; +use strict; + +use RT::Test nodata => 1, tests => 13; + +my $q = RT::Queue->new(RT->SystemUser); +ok( $q->Create( Name => 'DateCFTest' . $$ ), 'create queue' ); + +my $cf = RT::CustomField->new(RT->SystemUser); +ok( + $cf->Create( + Name => 'date-' . $$, + Type => 'Date', + MaxValues => 1, + LookupType => RT::Ticket->CustomFieldLookupType, + ), + 'create cf date' +); +ok( $cf->AddToObject($q), 'date cf apply to queue' ); + +my $ticket = RT::Ticket->new(RT->SystemUser); + +ok( + $ticket->Create( + Queue => $q->id, + Subject => 'Test', + 'CustomField-' . $cf->id => '2010-05-04', + ), + 'create ticket with cf set to 2010-05-04' +); + +is( $ticket->CustomFieldValues->First->Content, '2010-05-04', 'date in db is' ); + +{ + + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '=', + VALUE => '2010-05-04', + ); + is( $tickets->Count, 1, 'found the ticket with exact date: 2010-05-04' ); + +} + +{ + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '>', + VALUE => '2010-05-03', + ); + + is( $tickets->Count, 1, 'found ticket with > 2010-05-03' ); +} + +{ + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '<', + VALUE => '2010-05-05', + ); + + is( $tickets->Count, 1, 'found ticket with < 2010-05-05' ); +} + +{ + + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '=', + VALUE => '2010-05-05', + ); + + is( $tickets->Count, 0, 'did not find the ticket with = 2010-05-05' ); +} + +{ + + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '<', + VALUE => '2010-05-03', + ); + + is( $tickets->Count, 0, 'did not find the ticket with < 2010-05-03' ); +} + +{ + + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '>', + VALUE => '2010-05-05', + ); + + is( $tickets->Count, 0, 'did not find the ticket with > 2010-05-05' ); +} + +$ticket = RT::Ticket->new(RT->SystemUser); + +ok( + $ticket->Create( + Queue => $q->id, + Subject => 'Test', + 'CustomField-' . $cf->id => '2010-05-04 11:34:56', + ), + 'create ticket with cf set to 2010-05-04 11:34:56' +); + +is( $ticket->CustomFieldValues->First->Content, + '2010-05-04', 'date in db only has date' ); + diff --git a/rt/t/customfields/datetime_search.t b/rt/t/customfields/datetime_search.t new file mode 100644 index 000000000..11fe3bc57 --- /dev/null +++ b/rt/t/customfields/datetime_search.t @@ -0,0 +1,141 @@ +#!/usr/bin/perl + +use warnings; +use strict; + +use RT::Test nodata => 1, tests => 14; +RT->Config->Set( 'Timezone' => 'EST5EDT' ); # -04:00 + +my $q = RT::Queue->new(RT->SystemUser); +ok( $q->Create( Name => 'DateTimeCFTest' . $$ ), 'create queue' ); + +my $cf = RT::CustomField->new(RT->SystemUser); +ok( + $cf->Create( + Name => 'datetime-' . $$, + Type => 'DateTime', + MaxValues => 1, + LookupType => RT::Ticket->CustomFieldLookupType, + ), + 'create cf datetime' +); +ok( $cf->AddToObject($q), 'date cf apply to queue' ); + +my $ticket = RT::Ticket->new(RT->SystemUser); + +ok( + $ticket->Create( + Queue => $q->id, + Subject => 'Test', + 'CustomField-' . $cf->id => '2010-05-04 07:00:00', + ), + 'create ticket with cf set to 2010-05-04 07:00:00( 2010-05-04 11:00:00 with UTC )' +); + +is( + $ticket->CustomFieldValues->First->Content, + '2010-05-04 11:00:00', + 'date in db is in timezone UTC' +); + +{ + + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '=', + VALUE => '2010-05-04 07:00:00', # this timezone is server + ); + + is( $tickets->Count, 1, 'found the ticket with exact date: 2010-05-04 07:00:00' ); +} + +{ + + # TODO according to the code, if OPERATOR is '=', it means on that day + # this will test this behavior + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '=', + VALUE => '2010-05-04', + ); + + is( $tickets->Count, 1, 'found the ticket with rough date: 2010-05-04' ); +} + +{ + + # TODO according to the code, if OPERATOR is '=', it means on that day + # this will test this behavior + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '=', + VALUE => '2010-05-05', + ); + + is( $tickets->Count, 0, 'did not find the ticket with wrong datetime: 2010-05-05' ); +} + +my $tickets = RT::Tickets->new( RT->SystemUser ); +$tickets->UnLimit; +while( my $ticket = $tickets->Next ) { + $ticket->Delete(); +} + +{ + ok( + $ticket->Create( + Queue => $q->id, + Subject => 'Test', + 'CustomField-' . $cf->id => '2010-06-21 17:00:01', + ), +'create ticket with cf set to 2010-06-21 17:00:01( 2010-06-21 21:00:01 with UTC )' + ); + + my $shanghai = RT::Test->load_or_create_user( + Name => 'shanghai', + Timezone => 'Asia/Shanghai', + ); + + ok( + $shanghai->PrincipalObj->GrantRight( + Right => 'SuperUser', + Object => $RT::System, + ) + ); + + my $current_user = RT::CurrentUser->new($shanghai); + my $tickets = RT::Tickets->new($current_user); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '=', + VALUE => '2010-06-22', + ); + is( $tickets->Count, 1, 'found the ticket with rough datetime: 2010-06-22' ); + + $tickets->UnLimit; + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '>', + VALUE => '2010-06-21', + ); + is( $tickets->Count, 1, 'found the ticket with > 2010-06-21' ); + + $tickets->UnLimit; + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '<', + VALUE => '2010-06-23', + ); + is( $tickets->Count, 1, 'found the ticket with < 2010-06-23' ); + + $tickets->UnLimit; + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => '=', + VALUE => '2010-06-22 05:00:01', + ); + is( $tickets->Count, 1, 'found the ticket with = 2010-06-22 01:00:01' ); +} diff --git a/rt/t/customfields/external.t b/rt/t/customfields/external.t new file mode 100644 index 000000000..0abf6eca1 --- /dev/null +++ b/rt/t/customfields/external.t @@ -0,0 +1,56 @@ +#!/usr/bin/perl + +use warnings; +use strict; + +use RT; +use RT::Test nodata => 1, tests => 11; + +sub new (*) { + my $class = shift; + return $class->new(RT->SystemUser); +} + +use constant VALUES_CLASS => 'RT::CustomFieldValues::Groups'; +RT->Config->Set(CustomFieldValuesSources => VALUES_CLASS); + +my $q = new( RT::Queue ); +isa_ok( $q, 'RT::Queue' ); +my ($qid) = $q->Create( Name => "CF-External-". $$ ); +ok( $qid, "created queue" ); +my %arg = ( Name => $q->Name, + Type => 'Select', + Queue => $q->id, + MaxValues => 1, + ValuesClass => VALUES_CLASS ); + +my $cf = new( RT::CustomField ); +isa_ok( $cf, 'RT::CustomField' ); + +{ + my ($cfid, $msg) = $cf->Create( %arg ); + ok( $cfid, "created cf" ) or diag "error: $msg"; + is( $cf->ValuesClass, VALUES_CLASS, "right values class" ); + ok( $cf->IsExternalValues, "custom field has external values" ); +} + +{ + # create at least on group for the tests + my $group = RT::Group->new( RT->SystemUser ); + my ($ret, $msg) = $group->CreateUserDefinedGroup( Name => $q->Name ); + ok $ret, 'created group' or diag "error: $msg"; +} + +{ + my $values = $cf->Values; + isa_ok( $values, VALUES_CLASS ); + ok( $values->Count, "we have values" ); + my ($failure, $count) = (0, 0); + while( my $value = $values->Next ) { + $count++; + $failure = 1 unless $value->Name; + } + ok( !$failure, "all values have name" ); + is( $values->Count, $count, "count is correct" ); +} + diff --git a/rt/t/customfields/ip.t b/rt/t/customfields/ip.t new file mode 100644 index 000000000..f73e63fa5 --- /dev/null +++ b/rt/t/customfields/ip.t @@ -0,0 +1,285 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 73; + +my ( $baseurl, $agent ) = RT::Test->started_ok; +ok( $agent->login, 'log in' ); + +my $q = RT::Queue->new($RT::SystemUser); +$q->Load('General'); +my $ip_cf = RT::CustomField->new($RT::SystemUser); + +my ( $val, $msg ) = $ip_cf->Create( + Name => 'IP', + Type => 'IPAddress', + LookupType => 'RT::Queue-RT::Ticket' +); +ok( $val, $msg ); +my $cf_id = $val; +$ip_cf->AddToObject($q); +use_ok('RT'); + +my $cf; +diag "load and check basic properties of the IP CF" if $ENV{'TEST_VERBOSE'}; +{ + my $cfs = RT::CustomFields->new($RT::SystemUser); + $cfs->Limit( FIELD => 'Name', VALUE => 'IP' ); + is( $cfs->Count, 1, "found one CF with name 'IP'" ); + + $cf = $cfs->First; + is( $cf->Type, 'IPAddress', 'type check' ); + is( $cf->LookupType, 'RT::Queue-RT::Ticket', 'lookup type check' ); + ok( !$cf->MaxValues, "unlimited number of values" ); + ok( !$cf->Disabled, "not disabled" ); +} + +diag "check that CF applies to queue General" if $ENV{'TEST_VERBOSE'}; +{ + my $cfs = $q->TicketCustomFields; + $cfs->Limit( FIELD => 'id', VALUE => $cf->id, ENTRYAGGREGATOR => 'AND' ); + is( $cfs->Count, 1, 'field applies to queue' ); +} + +diag "create a ticket via web and set IP" if $ENV{'TEST_VERBOSE'}; +{ + my $val = '192.168.20.1'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $val, + } + ); + + $agent->content_contains( $val, "IP on the page" ); + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $val, 'correct value' ); +} + +diag "create a ticket and edit IP field using Edit page" + if $ENV{'TEST_VERBOSE'}; +{ + my $val = '172.16.0.1'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { Subject => 'test ip', } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + my $cf_field = "Object-RT::Ticket-$id-CustomField-$cf_id-Values"; + + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + + is( $agent->value($cf_field), '', 'IP is empty' ); + $agent->field( $cf_field => $val ); + $agent->click('SubmitTicket'); + + $agent->content_contains( $val, "IP on the page" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), '172.16.0.1' ); + + diag "set IP with spaces around" if $ENV{'TEST_VERBOSE'}; + $val = " 172.16.0.2 \n "; + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + is( $agent->value($cf_field), '172.16.0.1', 'IP is in input box' ); + $agent->field( $cf_field => $val ); + $agent->click('SubmitTicket'); + + $agent->content_contains( '172.16.0.2', "IP on the page" ); + + $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), + '172.16.0.2', 'correct value' ); +} + +diag "check that we parse correct IPs only" if $ENV{'TEST_VERBOSE'}; +{ + + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + for my $valid (qw/1.0.0.0 255.255.255.255/) { + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $valid, + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + is( $ticket->id, $id, 'loaded ticket' ); + + is( $ticket->FirstCustomFieldValue('IP'), + $valid, 'correct value' ); + } + + for my $invalid (qw{255.255.255.256 355.255.255.255 8.13.8/8.13.0/1.0}) { + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $invalid, + } + ); + + $agent->content_contains( 'can not be parsed as an IP address', + 'ticket fails to create' ); + } + +} + +diag "search tickets by IP" if $ENV{'TEST_VERBOSE'}; +{ + my $val = '172.16.1.1'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $val, + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + + my $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("id = $id AND CF.{IP} = '172.16.1.1'"); + ok( $tickets->Count, "found tickets" ); +} + +diag "create two tickets with different IPs and check several searches" + if $ENV{'TEST_VERBOSE'}; +{ + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => '192.168.21.10', + } + ); + + my ($id1) = $agent->content =~ /Ticket (\d+) created/; + ok( $id1, "created first ticket $id1" ); + + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => '192.168.22.10', + } + ); + + my ($id2) = $agent->content =~ /Ticket (\d+) created/; + ok( $id2, "created second ticket $id2" ); + + my $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("id = $id1 OR id = $id2"); + is( $tickets->Count, 2, "found both tickets by 'id = x OR y'" ); + + # IP + $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.10'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + '192.168.21.10', "correct value" ); + $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.22.10'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + '192.168.22.10', "correct value" ); + + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} <= '192.168.21.10'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + '192.168.21.10', "correct value" ); + $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} >= '192.168.22.10'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + '192.168.22.10', "correct value" ); + + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} > '192.168.22.10'"); + is( $tickets->Count, 0, "no tickets found" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} < '192.168.21.10'"); + is( $tickets->Count, 0, "no tickets found" ); + + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} < '192.168.22.10'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + '192.168.21.10', "correct value" ); + + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} > '192.168.21.10'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + '192.168.22.10', "correct value" ); +} + +diag "create a ticket with an IP of 10.0.0.1 and search for doesn't match '10.0.0.'." + if $ENV{'TEST_VERBOSE'}; +{ + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'local', + $cf_field => '10.0.0.1', + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created first ticket $id" ); + + my $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("id=$id AND CF.{IP} NOT LIKE '10.0.0.'"); + + SKIP: { + skip "partical ip parse causes ambiguity", 1; + is( $tickets->Count, 0, "should not have found the ticket" ); + } +} + + +diag "test the operators in search page" if $ENV{'TEST_VERBOSE'}; +{ + $agent->get_ok( $baseurl . "/Search/Build.html?Query=Queue='General'" ); + $agent->content_contains('CF.{IP}', 'got CF.{IP}'); + my $form = $agent->form_name('BuildQuery'); + my $op = $form->find_input("'CF.{IP}'Op"); + ok( $op, "found 'CF.{IP}'Op" ); + is_deeply( [ $op->possible_values ], [ '=', '!=', '<', '>' ], 'op values' ); +} diff --git a/rt/t/customfields/iprange.t b/rt/t/customfields/iprange.t new file mode 100644 index 000000000..118d23c88 --- /dev/null +++ b/rt/t/customfields/iprange.t @@ -0,0 +1,469 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 133; + +my ($baseurl, $agent) =RT::Test->started_ok; +ok( $agent->login, 'log in' ); + +my $q = RT::Queue->new($RT::SystemUser); +$q->Load('General'); +my $ip_cf = RT::CustomField->new($RT::SystemUser); + +my ($val,$msg) = $ip_cf->Create(Name => 'IP', Type =>'IPAddressRange', LookupType => 'RT::Queue-RT::Ticket'); +ok($val,$msg); +my $cf_id = $val; +$ip_cf->AddToObject($q); +use_ok('RT'); + +my $cf; +diag "load and check basic properties of the IP CF" if $ENV{'TEST_VERBOSE'}; +{ + my $cfs = RT::CustomFields->new( $RT::SystemUser ); + $cfs->Limit( FIELD => 'Name', VALUE => 'IP' ); + is( $cfs->Count, 1, "found one CF with name 'IP'" ); + + $cf = $cfs->First; + is( $cf->Type, 'IPAddressRange', 'type check' ); + is( $cf->LookupType, 'RT::Queue-RT::Ticket', 'lookup type check' ); + ok( !$cf->MaxValues, "unlimited number of values" ); + ok( !$cf->Disabled, "not disabled" ); +} + +diag "check that CF applies to queue General" if $ENV{'TEST_VERBOSE'}; +{ + my $cfs = $q->TicketCustomFields; + $cfs->Limit( FIELD => 'id', VALUE => $cf->id, ENTRYAGGREGATOR => 'AND' ); + is( $cfs->Count, 1, 'field applies to queue' ); +} + +diag "create a ticket via web and set IP" if $ENV{'TEST_VERBOSE'}; +{ + my $val = '192.168.20.1'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $val, + } + ); + + $agent->content_like( qr/\Q$val/, "IP on the page" ); + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $val, 'correct value' ); +} + +diag "create a ticket via web with CIDR" if $ENV{'TEST_VERBOSE'}; +{ + my $val = '172.16.20/31'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $val, + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), '172.16.20.0-172.16.20.1', 'correct value' ); +} + +diag "create a ticket and edit IP field using Edit page" if $ENV{'TEST_VERBOSE'}; +{ + my $val = '172.16.0.1'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { Subject => 'test ip', } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + my $cf_field = "Object-RT::Ticket-$id-CustomField-$cf_id-Values"; + + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + + like( $agent->value($cf_field), qr/^\s*$/, 'IP is empty' ); + $agent->field( $cf_field => $val ); + $agent->click('SubmitTicket'); + + $agent->content_like( qr/\Q$val/, "IP on the page" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $val, 'correct value' ); + + diag "set IP with spaces around" if $ENV{'TEST_VERBOSE'}; + $val = " 172.16.0.2 \n "; + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + like( $agent->value($cf_field), + qr/^\s*\Q172.16.0.1\E\s*$/, 'IP is in input box' ); + $agent->field( $cf_field => $val ); + $agent->click('SubmitTicket'); + + $agent->content_like( qr/\Q172.16.0.2/, "IP on the page" ); + + $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), '172.16.0.2', 'correct value' ); + + diag "replace IP with a range" if $ENV{'TEST_VERBOSE'}; + $val = '172.16.0.0-172.16.0.255'; + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + like( $agent->value($cf_field), + qr/^\s*\Q172.16.0.2\E\s*$/, 'IP is in input box' ); + $agent->field( $cf_field => $val ); + $agent->click('SubmitTicket'); + + $agent->content_like( qr/\Q$val/, "IP on the page" ); + + $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $val, 'correct value' ); + + diag "delete range, add another range using CIDR" if $ENV{'TEST_VERBOSE'}; + $val = '172.16/16'; + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + is( $agent->value($cf_field), + '172.16.0.0-172.16.0.255', 'IP is in input box' ); + $agent->field( $cf_field => $val ); + $agent->click('SubmitTicket'); + + $agent->content_like( qr/\Q$val/, "IP on the page" ); + + $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), + '172.16.0.0-172.16.255.255', 'correct value' ); +} + +diag "check that we parse correct IPs only" if $ENV{'TEST_VERBOSE'}; +{ + + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + for my $valid (qw/1.0.0.0 255.255.255.255/) { + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $valid, + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + is( $ticket->id, $id, 'loaded ticket' ); + + is( $ticket->FirstCustomFieldValue('IP'), $valid, 'correct value' ); + } + + for my $invalid (qw{255.255.255.256 355.255.255.255 8.13.8/8.13.0/1.0}) { + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $invalid, + } + ); + + $agent->content_like( qr/can not be parsed as an IP address range/, 'ticket fails to create' ); + } + +} + +diag "search tickets by IP" if $ENV{'TEST_VERBOSE'}; +{ + my $val = '172.16.1/31'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $val, + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + + my $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("id = $id AND CF.{IP} = '172.16.1.1'"); + ok( $tickets->Count, "found tickets" ); + + is( $ticket->FirstCustomFieldValue('IP'), + '172.16.1.0-172.16.1.1', 'correct value' ); +} + +diag "search tickets by IP range" if $ENV{'TEST_VERBOSE'}; +{ + my $val = '172.16.2/26'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $val, + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + + my $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("id = $id AND CF.{IP} = '172.16.2.0-172.16.2.255'"); + ok( $tickets->Count, "found tickets" ); + + is( $ticket->FirstCustomFieldValue('IP'), + '172.16.2.0-172.16.2.63', 'correct value' ); +} + +diag "create two tickets with different IPs and check several searches" if $ENV{'TEST_VERBOSE'}; +{ + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => '192.168.21.10', + } + ); + + my ($id1) = $agent->content =~ /Ticket (\d+) created/; + ok( $id1, "created first ticket $id1" ); + + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => '192.168.22.10', + } + ); + + my ($id2) = $agent->content =~ /Ticket (\d+) created/; + ok( $id2, "created second ticket $id2" ); + + my $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("id = $id1 OR id = $id2"); + is( $tickets->Count, 2, "found both tickets by 'id = x OR y'" ); + + # IP + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.10'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.21.10', "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.22.10'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.22.10', "correct value" ); + + # IP/32 - one address + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.10/32'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.21.10', "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.22.10/32'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.22.10', "correct value" ); + + # IP range + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.0-192.168.21.255'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.21.10', "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.22.0-192.168.22.255'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.22.10', "correct value" ); + + # IP range, with start IP greater than end + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.255-192.168.21.0'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.21.10', "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.22.255-192.168.22.0'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.22.10', "correct value" ); + + # CIDR/24 + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.0/24'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.21.10', "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.22.0/24'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.22.10', "correct value" ); + + # IP is not in CIDR/24 + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} != '192.168.21.0/24'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.22.10', "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} != '192.168.22.0/24'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), '192.168.21.10', "correct value" ); + + # CIDR or CIDR + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND " + ."(CF.{IP} = '192.168.21.0/24' OR CF.{IP} = '192.168.22.0/24')"); + is( $tickets->Count, 2, "found both tickets" ); +} + +diag "create two tickets with different IP ranges and check several searches" if $ENV{'TEST_VERBOSE'}; +{ + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => '192.168.21.0-192.168.21.127', + } + ); + + my ($id1) = $agent->content =~ /Ticket (\d+) created/; + ok( $id1, "created first ticket $id1" ); + + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => '192.168.21.128-192.168.21.255', + } + ); + + my ($id2) = $agent->content =~ /Ticket (\d+) created/; + ok( $id2, "created ticket $id2" ); + + my $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("id = $id1 OR id = $id2"); + is( $tickets->Count, 2, "found both tickets by 'id = x OR y'" ); + + # IP + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.0'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.64'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.127'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.128'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id2, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.191'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id2, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.255'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id2, "correct value" ); + + # IP/32 - one address + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.63/32'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.191/32'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id2, "correct value" ); + + # IP range, lower than both + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.20.0-192.168.20.255'"); + is( $tickets->Count, 0, "didn't finnd ticket" ) or diag "but found ". $tickets->First->id; + + # IP range, intersect with the first range + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.20.0-192.168.21.63'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + + # IP range, equal to the first range + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.0-192.168.21.127'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + + # IP range, lay inside the first range + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.31-192.168.21.63'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + + # IP range, intersect with the ranges + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.31-192.168.21.191'"); + is( $tickets->Count, 2, "found both tickets" ); + + # IP range, equal to range from the starting IP of the first ticket to the ending IP of the second + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.21.0-192.168.21.255'"); + is( $tickets->Count, 2, "found both tickets" ); + + # IP range, has the both ranges inside it + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168/16'"); + is( $tickets->Count, 2, "found both tickets" ); + + # IP range, greater than both + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '192.168.22/24'"); + is( $tickets->Count, 0, "didn't find ticket" ) or diag "but found ". $tickets->First->id; +} + + +diag "test the operators in search page" if $ENV{'TEST_VERBOSE'}; +{ + $agent->get_ok( $baseurl . "/Search/Build.html?Query=Queue='General'" ); + $agent->content_contains('CF.{IP}', 'got CF.{IP}'); + my $form = $agent->form_name('BuildQuery'); + my $op = $form->find_input("'CF.{IP}'Op"); + ok( $op, "found 'CF.{IP}'Op" ); + is_deeply( [ $op->possible_values ], [ '=', '!=', '<', '>' ], 'op values' ); +} + diff --git a/rt/t/customfields/iprangev6.t b/rt/t/customfields/iprangev6.t new file mode 100644 index 000000000..d823dd6e3 --- /dev/null +++ b/rt/t/customfields/iprangev6.t @@ -0,0 +1,474 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 158; + +my ($baseurl, $agent) =RT::Test->started_ok; +ok( $agent->login, 'log in' ); + +my $q = RT::Queue->new($RT::SystemUser); +$q->Load('General'); +my $ip_cf = RT::CustomField->new($RT::SystemUser); + +my ($val,$msg) = $ip_cf->Create(Name => 'IP', Type =>'IPAddressRange', LookupType => 'RT::Queue-RT::Ticket'); +ok($val,$msg); +my $cf_id = $val; +$ip_cf->AddToObject($q); +use_ok('RT'); + +my $cf; +diag "load and check basic properties of the IP CF" if $ENV{'TEST_VERBOSE'}; +{ + my $cfs = RT::CustomFields->new( $RT::SystemUser ); + $cfs->Limit( FIELD => 'Name', VALUE => 'IP' ); + is( $cfs->Count, 1, "found one CF with name 'IP'" ); + + $cf = $cfs->First; + is( $cf->Type, 'IPAddressRange', 'type check' ); + is( $cf->LookupType, 'RT::Queue-RT::Ticket', 'lookup type check' ); + ok( !$cf->MaxValues, "unlimited number of values" ); + ok( !$cf->Disabled, "not disabled" ); +} + +diag "check that CF applies to queue General" if $ENV{'TEST_VERBOSE'}; +{ + my $cfs = $q->TicketCustomFields; + $cfs->Limit( FIELD => 'id', VALUE => $cf->id, ENTRYAGGREGATOR => 'AND' ); + is( $cfs->Count, 1, 'field applies to queue' ); +} + +my %valid = ( + 'abcd:' x 7 . 'abcd' => 'abcd:' x 7 . 'abcd', + '034:' x 7 . '034' => '0034:' x 7 . '0034', + 'abcd::' => 'abcd:' . '0000:' x 6 . '0000', + '::abcd' => '0000:' x 7 . 'abcd', + 'abcd::034' => 'abcd:' . '0000:' x 6 . '0034', + 'abcd::192.168.1.1' => 'abcd:' . '0000:' x 5 . 'c0a8:0101', + '::192.168.1.1' => '0000:' x 6 . 'c0a8:0101', + '::' => '0000:' x 7 . '0000', +); + +diag "create a ticket via web and set IP" if $ENV{'TEST_VERBOSE'}; +{ + for my $ip ( keys %valid ) { + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $ip, + } + ); + + $agent->content_like( qr/$valid{$ip}/, "IP on the page" ); + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $valid{$ip}, + 'correct value' ); + } +} + +diag "create a ticket via web with CIDR" if $ENV{'TEST_VERBOSE'}; +{ + my $val = 'abcd:034::/31'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $val, + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( + $ticket->FirstCustomFieldValue('IP'), +'abcd:0034:0000:0000:0000:0000:0000:0000-abcd:0035:ffff:ffff:ffff:ffff:ffff:ffff', + 'correct value' + ); +} + +diag "create a ticket and edit IP field using Edit page" if $ENV{'TEST_VERBOSE'}; +{ + my $val = 'abcd' . ':abcd' x 7; + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { Subject => 'test ip', } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + my $cf_field = "Object-RT::Ticket-$id-CustomField-$cf_id-Values"; + + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + + is( $agent->value($cf_field), '', 'IP is empty' ); + $agent->field( $cf_field => $val ); + $agent->click('SubmitTicket'); + + $agent->content_contains( $val, "IP on the page" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $val, 'correct value' ); + + diag "set IP with spaces around" if $ENV{'TEST_VERBOSE'}; + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + is( $agent->value($cf_field), $val, 'IP is in input box' ); + $val = 'bbcd' . ':abcd' x 7; + $agent->field( $cf_field => " $val " ); + $agent->click('SubmitTicket'); + + $agent->content_contains( $val, "IP on the page" ); + + $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $val, 'correct value' ); + + diag "replace IP with a range" if $ENV{'TEST_VERBOSE'}; + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + is( $agent->value($cf_field), $val, 'IP is in input box' ); + $val = 'abcd' . ':0000' x 7 . '-' . 'abcd' . ':ffff' x 7; + $agent->field( $cf_field => 'abcd::/16' ); + $agent->click('SubmitTicket'); + + $agent->content_contains( $val, "IP on the page" ); + + $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $val, 'correct value' ); + + diag "delete range, add another range using CIDR" if $ENV{'TEST_VERBOSE'}; + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + is( $agent->value($cf_field), $val, 'IP is in input box' ); + $val = 'bb00' . ':0000' x 7 . '-' . 'bbff' . ':ffff' x 7; + $agent->field( $cf_field => $val ); + $agent->click('SubmitTicket'); + + $agent->content_contains( $val, "IP on the page" ); + + $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $val, 'correct value' ); +} + +diag "check that we parse correct IPs only" if $ENV{'TEST_VERBOSE'}; +{ + + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + my @invalid = + ( 'abcd:', 'efgh', 'abcd:' x 8 . 'abcd', 'abcd::abcd::abcd' ); + for my $invalid (@invalid) { + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $invalid, + } + ); + + $agent->content_like( qr/can not be parsed as an IP address range/, + 'ticket fails to create' ); + } + +} + +diag "search tickets by IP" if $ENV{'TEST_VERBOSE'}; +{ + my $val = 'abcd::/16'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $val, + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + + my $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("id = $id AND CF.{IP} = 'abcd::/16'"); + ok( $tickets->Count, "found tickets" ); + is( + $ticket->FirstCustomFieldValue('IP'), +'abcd:0000:0000:0000:0000:0000:0000:0000-abcd:ffff:ffff:ffff:ffff:ffff:ffff:ffff', + 'correct value' + ); +} + +diag "search tickets by IP range" if $ENV{'TEST_VERBOSE'}; +{ + my $val = 'abcd:ef00::/24'; + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $val, + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + + my $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("id = $id AND CF.{IP} = + 'abcd:ef::-abcd:efff:ffff:ffff:ffff:ffff:ffff:ffff'"); + ok( $tickets->Count, "found tickets" ); + + is( + $ticket->FirstCustomFieldValue('IP'), +'abcd:ef00:0000:0000:0000:0000:0000:0000-abcd:efff:ffff:ffff:ffff:ffff:ffff:ffff', + 'correct value' + ); +} + +diag "create two tickets with different IPs and check several searches" if $ENV{'TEST_VERBOSE'}; +{ + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + my $first_ip = 'cbcd' . ':0000' x 7; + my $second_ip = 'cbdd' . ':0000' x 7; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $first_ip, + } + ); + + my ($id1) = $agent->content =~ /Ticket (\d+) created/; + ok( $id1, "created first ticket $id1" ); + + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $second_ip, + } + ); + + my ($id2) = $agent->content =~ /Ticket (\d+) created/; + ok( $id2, "created second ticket $id2" ); + + my $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("id = $id1 OR id = $id2"); + is( $tickets->Count, 2, "found both tickets by 'id = x OR y'" ); + + # IP + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '$first_ip'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $first_ip, "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '$second_ip'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $second_ip, "correct value" ); + + # IP/32 - one address + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'cbcd::/16'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $first_ip, "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'cbdd::/16'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $second_ip, "correct value" ); + + # IP range + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL( + "(id = $id1 OR id = $id2) AND CF.{IP} = '$first_ip-cbcf::'" + ); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $first_ip, "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL( + "(id = $id1 OR id = $id2) AND CF.{IP} = '$second_ip-cbdf::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $second_ip, "correct value" ); + + # IP range, with start IP greater than end + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = + 'cbcf::-$first_ip'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $first_ip,, "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'cbdf::-$second_ip'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $second_ip, "correct value" ); + + # CIDR/12 + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'cbcd::/12'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $first_ip, "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'cbdd::/12'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $second_ip, "correct value" ); + + # IP is not in CIDR/24 + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} != 'cbcd::/12'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $second_ip,, "correct value" ); + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} != 'cbdd::/12'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), $first_ip, "correct value" ); + + # CIDR or CIDR + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND " + ."(CF.{IP} = 'cbcd::/12' OR CF.{IP} = 'cbdd::/12')"); + is( $tickets->Count, 2, "found both tickets" ); +} + +diag "create two tickets with different IP ranges and check several searches" if $ENV{'TEST_VERBOSE'}; +{ + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => 'ddcd::/16', + } + ); + + my ($id1) = $agent->content =~ /Ticket (\d+) created/; + ok( $id1, "created first ticket $id1" ); + + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => 'edcd::/16', + } + ); + + my ($id2) = $agent->content =~ /Ticket (\d+) created/; + ok( $id2, "created ticket $id2" ); + + my $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("id = $id1 OR id = $id2"); + is( $tickets->Count, 2, "found both tickets by 'id = x OR y'" ); + + # IP + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'ddcd::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'ddcd:abcd::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'ddcd:ffff::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'edcd::abcd'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id2, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'edcd::ffff'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id2, "correct value" ); + $tickets->FromSQL( +"(id = $id1 OR id = $id2) AND CF.{IP} = 'edcd:ffff:ffff:ffff:ffff:ffff:ffff:ffff'" + ); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id2, "correct value" ); + + # IP/32 - one address + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'ddcd::/32'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'edcd::/32'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id2, "correct value" ); + + # IP range, lower than both + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'abcd::/32'"); + is( $tickets->Count, 0, "didn't finnd ticket" ) or diag "but found ". $tickets->First->id; + + # IP range, intersect with the first range + $tickets->FromSQL( + "(id = $id1 OR id = $id2) AND CF.{IP} = 'ddcc::-ddcd:ab::'" + ); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + + # IP range, equal to the first range + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'ddcd::/16'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + + # IP range, lay inside the first range + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'ddcd:ab::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->id, $id1, "correct value" ); + + # IP range, intersect with the ranges + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'ddcc::-edcd:ab::'"); + is( $tickets->Count, 2, "found both tickets" ); + + # IP range, equal to range from the starting IP of the first ticket to the ending IP of the second + $tickets->FromSQL( + "(id = $id1 OR id = $id2) AND CF.{IP} = 'ddcd::-edcd:ffff:ffff:ffff:ffff:ffff:ffff:ffff'" + ); + is( $tickets->Count, 2, "found both tickets" ); + + # IP range, has the both ranges inside it + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'd000::/2'"); + is( $tickets->Count, 2, "found both tickets" ); + + # IP range, greater than both + $tickets = RT::Tickets->new( $RT::SystemUser ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'ffff::/16'"); + is( $tickets->Count, 0, "didn't find ticket" ) or diag "but found ". $tickets->First->id; +} + + diff --git a/rt/t/customfields/ipv6.t b/rt/t/customfields/ipv6.t new file mode 100644 index 000000000..09c4d30d0 --- /dev/null +++ b/rt/t/customfields/ipv6.t @@ -0,0 +1,252 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 102; + +my ( $baseurl, $agent ) = RT::Test->started_ok; +ok( $agent->login, 'log in' ); + +my $q = RT::Queue->new($RT::SystemUser); +$q->Load('General'); +my $ip_cf = RT::CustomField->new($RT::SystemUser); + +my ( $val, $msg ) = $ip_cf->Create( + Name => 'IP', + Type => 'IPAddress', + LookupType => 'RT::Queue-RT::Ticket' +); +ok( $val, $msg ); +my $cf_id = $val; +$ip_cf->AddToObject($q); +use_ok('RT'); + +my $cf; +diag "load and check basic properties of the IP CF" if $ENV{'TEST_VERBOSE'}; +{ + my $cfs = RT::CustomFields->new($RT::SystemUser); + $cfs->Limit( FIELD => 'Name', VALUE => 'IP' ); + is( $cfs->Count, 1, "found one CF with name 'IP'" ); + + $cf = $cfs->First; + is( $cf->Type, 'IPAddress', 'type check' ); + is( $cf->LookupType, 'RT::Queue-RT::Ticket', 'lookup type check' ); + ok( !$cf->MaxValues, "unlimited number of values" ); + ok( !$cf->Disabled, "not disabled" ); +} + +diag "check that CF applies to queue General" if $ENV{'TEST_VERBOSE'}; +{ + my $cfs = $q->TicketCustomFields; + $cfs->Limit( FIELD => 'id', VALUE => $cf->id, ENTRYAGGREGATOR => 'AND' ); + is( $cfs->Count, 1, 'field applies to queue' ); +} + +my %valid = ( + 'abcd:' x 7 . 'abcd' => 'abcd:' x 7 . 'abcd', + '034:' x 7 . '034' => '0034:' x 7 . '0034', + 'abcd::' => 'abcd:' . '0000:' x 6 . '0000', + '::abcd' => '0000:' x 7 . 'abcd', + 'abcd::034' => 'abcd:' . '0000:' x 6 . '0034', + 'abcd::192.168.1.1' => 'abcd:' . '0000:' x 5 . 'c0a8:0101', + '::192.168.1.1' => '0000:' x 6 . 'c0a8:0101', + '::' => '0000:' x 7 . '0000', +); + +diag "create a ticket via web and set IP" if $ENV{'TEST_VERBOSE'}; +{ + for my $ip ( keys %valid ) { + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $ip, + } + ); + + $agent->content_contains( $valid{$ip}, "IP on the page" ); + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $valid{$ip}, + 'correct value' ); + + my $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("id = $id AND CF.{IP} = '$ip'"); + ok( $tickets->Count, "found tickets" ); + } +} + +diag "create a ticket and edit IP field using Edit page" + if $ENV{'TEST_VERBOSE'}; + +{ + my $ip = 'abcd::034'; + + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { Subject => 'test ip', } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created ticket $id" ); + my $cf_field = "Object-RT::Ticket-$id-CustomField-$cf_id-Values"; + + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + + is( $agent->value($cf_field), '', 'IP is empty' ); + $agent->field( $cf_field => $valid{$ip} ); + $agent->click('SubmitTicket'); + + $agent->content_contains( $valid{$ip}, "IP on the page" ); + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + my $values = $ticket->CustomFieldValues('IP'); + is( $ticket->FirstCustomFieldValue('IP'), $valid{$ip}, 'correct value' ); + + diag "set IP with spaces around" if $ENV{'TEST_VERBOSE'}; + my $new_ip = '::3141'; + my $new_value = '0000:' x 7 . '3141'; + + $agent->follow_link_ok( { text => 'Basics', n => "1" }, + "Followed 'Basics' link" ); + $agent->form_name('TicketModify'); + is( $agent->value($cf_field), $valid{$ip}, 'IP is in input box' ); + $agent->field( $cf_field => $new_ip ); + $agent->click('SubmitTicket'); + + $agent->content_contains( $new_value, "IP on the page" ); + + $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load($id); + ok( $ticket->id, 'loaded ticket' ); + is( $ticket->FirstCustomFieldValue('IP'), $new_value, 'correct value' ); +} + +diag "check that we parse correct IPs only" if $ENV{'TEST_VERBOSE'}; +{ + + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + my @invalid = + ( 'abcd:', 'efgh', 'abcd:' x 8 . 'abcd', 'abcd::abcd::abcd' ); + for my $invalid (@invalid) { + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => $invalid, + } + ); + + $agent->content_contains( 'can not be parsed as an IP address', + 'ticket fails to create' ); + } +} + +diag "create two tickets with different IPs and check several searches" + if $ENV{'TEST_VERBOSE'}; +{ + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => 'abcd::', + } + ); + + my ($id1) = $agent->content =~ /Ticket (\d+) created/; + ok( $id1, "created first ticket $id1" ); + + ok $agent->goto_create_ticket($q), "go to create ticket"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test ip', + $cf_field => 'bbcd::', + } + ); + + my ($id2) = $agent->content =~ /Ticket (\d+) created/; + ok( $id2, "created second ticket $id2" ); + + my $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("id = $id1 OR id = $id2"); + is( $tickets->Count, 2, "found both tickets by 'id = x OR y'" ); + + # IP + $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'abcd::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + 'abcd' . ':0000' x 7, "correct value" ); + $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = 'bbcd::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + 'bbcd' . ':0000' x 7, "correct value" ); + + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} <= 'abcd::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + 'abcd' . ':0000' x 7, "correct value" ); + $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} >= 'bbcd::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + 'bbcd' . ':0000' x 7, "correct value" ); + + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} > 'bbcd::'"); + is( $tickets->Count, 0, "no tickets found" ); + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} < 'abcd::'"); + is( $tickets->Count, 0, "no tickets found" ); + + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} < 'bbcd::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + 'abcd' . ':0000' x 7, "correct value" ); + + $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} > 'abcd::'"); + is( $tickets->Count, 1, "found one ticket" ); + is( $tickets->First->FirstCustomFieldValue('IP'), + 'bbcd' . ':0000' x 7, "correct value" ); +} + +diag "create a ticket with an IP of abcd:23:: and search for doesn't match 'abcd:23'." + if $ENV{'TEST_VERBOSE'}; +{ + ok $agent->goto_create_ticket($q), "go to create ticket"; + my $cf_field = "Object-RT::Ticket--CustomField-$cf_id-Values"; + $agent->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'local', + $cf_field => 'abcd:23::', + } + ); + + my ($id) = $agent->content =~ /Ticket (\d+) created/; + ok( $id, "created first ticket $id" ); + + my $tickets = RT::Tickets->new($RT::SystemUser); + $tickets->FromSQL("id=$id AND CF.{IP} NOT LIKE 'abcd:23'"); + + SKIP: { + skip "partical ip parse can causes ambiguity", 1; + is( $tickets->Count, 0, "should not have found the ticket" ); + } +} + diff --git a/rt/t/customfields/pattern.t b/rt/t/customfields/pattern.t new file mode 100644 index 000000000..7d1090fa7 --- /dev/null +++ b/rt/t/customfields/pattern.t @@ -0,0 +1,44 @@ +#!/usr/bin/perl +use warnings; +use strict; + +use RT; +use RT::Test nodata => 1, tests => 17; + +my $q = RT::Queue->new($RT::SystemUser); +works($q->Create(Name => "CF-Pattern-".$$)); + +my $cf = RT::CustomField->new($RT::SystemUser); +my @cf_args = (Name => $q->Name, Type => 'Freeform', Queue => $q->id, MaxValues => 1); + +fails($cf->Create(@cf_args, Pattern => ')))bad!regex(((')); +works($cf->Create(@cf_args, Pattern => 'good regex')); + +my $t = RT::Ticket->new($RT::SystemUser); +my ($id,undef,$msg) = $t->Create(Queue => $q->id, Subject => 'CF Test'); +works($id,$msg); + +# OK, I'm thoroughly brain washed by HOP at this point now... +sub cnt { $t->CustomFieldValues($cf->id)->Count }; +sub add { $t->AddCustomFieldValue(Field => $cf->id, Value => $_[0]) }; +sub del { $t->DeleteCustomFieldValue(Field => $cf->id, Value => $_[0]) }; + +is(cnt(), 0, "No values yet"); +fails(add('not going to match')); +is(cnt(), 0, "No values yet"); +works(add('here is a good regex')); +is(cnt(), 1, "Value filled"); +fails(del('here is a good regex')); +is(cnt(), 1, "Single CF - Value _not_ deleted"); + +$cf->SetMaxValues(0); # Unlimited MaxValues + +works(del('here is a good regex')); +is(cnt(), 0, "Multiple CF - Value deleted"); + +fails($cf->SetPattern('(?{ "insert evil code here" })')); +works($cf->SetPattern('(?!)')); # reject everything +fails(add('')); +fails(add('...')); + +undef $t; diff --git a/rt/t/customfields/single_values.t b/rt/t/customfields/single_values.t new file mode 100644 index 000000000..15a00163c --- /dev/null +++ b/rt/t/customfields/single_values.t @@ -0,0 +1,37 @@ +#!/usr/bin/perl +use warnings; +use strict; + +use RT; +use RT::Test nodata => 1, tests => 8; + + + +my $q = RT::Queue->new(RT->SystemUser); +my ($id,$msg) =$q->Create(Name => "CF-Single-".$$); +ok($id,$msg); + +my $cf = RT::CustomField->new(RT->SystemUser); +($id,$msg) = $cf->Create(Name => 'Single-'.$$, Type => 'Select', MaxValues => '1', Queue => $q->id); +ok($id,$msg); + + +($id,$msg) =$cf->AddValue(Name => 'First'); +ok($id,$msg); + +($id,$msg) =$cf->AddValue(Name => 'Second'); +ok($id,$msg); + + +my $t = RT::Ticket->new(RT->SystemUser); +($id,undef,$msg) = $t->Create(Queue => $q->id, + Subject => 'CF Test'); + +ok($id,$msg); +is($t->CustomFieldValues($cf->id)->Count, 0, "No values yet"); +$t->AddCustomFieldValue(Field => $cf->id, Value => 'First'); +is($t->CustomFieldValues($cf->id)->Count, 1, "One now"); + +$t->AddCustomFieldValue(Field => $cf->id, Value => 'Second'); +is($t->CustomFieldValues($cf->id)->Count, 1, "Still one"); + diff --git a/rt/t/customfields/sort_order.t b/rt/t/customfields/sort_order.t index c5c808ceb..2453a7cc8 100644 --- a/rt/t/customfields/sort_order.t +++ b/rt/t/customfields/sort_order.t @@ -3,7 +3,7 @@ use strict; use warnings; -use RT::Test tests => 18; +use RT::Test tests => 20; use RT::Ticket; use RT::CustomField; @@ -11,10 +11,10 @@ my $queue_name = "CFSortQueue-$$"; my $queue = RT::Test->load_or_create_queue( Name => $queue_name ); ok($queue && $queue->id, "$queue_name - test queue creation"); -diag "create multiple CFs: B, A and C" if $ENV{TEST_VERBOSE}; +diag "create multiple CFs: B, A and C"; my @cfs = (); { - my $cf = RT::CustomField->new( $RT::SystemUser ); + my $cf = RT::CustomField->new( RT->SystemUser ); my ($ret, $msg) = $cf->Create( Name => "CF B", Queue => $queue->id, @@ -24,7 +24,7 @@ my @cfs = (); push @cfs, $cf; } { - my $cf = RT::CustomField->new( $RT::SystemUser ); + my $cf = RT::CustomField->new( RT->SystemUser ); my ($ret, $msg) = $cf->Create( Name => "CF A", Queue => $queue->id, @@ -34,7 +34,7 @@ my @cfs = (); push @cfs, $cf; } { - my $cf = RT::CustomField->new( $RT::SystemUser ); + my $cf = RT::CustomField->new( RT->SystemUser ); my ($ret, $msg) = $cf->Create( Name => "CF C", Queue => $queue->id, @@ -47,12 +47,11 @@ my @cfs = (); my ($baseurl, $m) = RT::Test->started_ok; ok $m->login( root => 'password' ), 'logged in'; -diag "reorder CFs: C, A and B" if $ENV{TEST_VERBOSE}; +diag "reorder CFs: C, A and B"; { $m->get( '/Admin/Queues/' ); $m->follow_link_ok( {text => $queue->id} ); - $m->follow_link_ok( {text => 'Ticket Custom Fields'} ); - + $m->follow_link_ok( {id => 'page-ticket-custom-fields'} ); my @tmp = ($m->content =~ /(CF [ABC])/g); is_deeply(\@tmp, ['CF B', 'CF A', 'CF C']); @@ -64,7 +63,7 @@ diag "reorder CFs: C, A and B" if $ENV{TEST_VERBOSE}; is_deeply(\@tmp, ['CF C', 'CF A', 'CF B']); } -diag "check ticket create, display and edit pages" if $ENV{TEST_VERBOSE}; +diag "check ticket create, display and edit pages"; { $m->submit_form( form_name => "CreateTicketInQueue", @@ -83,8 +82,7 @@ diag "check ticket create, display and edit pages" if $ENV{TEST_VERBOSE}; @tmp = ($m->content =~ /(CF [ABC])/g); is_deeply(\@tmp, ['CF C', 'CF A', 'CF B']); - - $m->follow_link_ok( {text => 'Custom Fields'} ); + $m->follow_link_ok( {id => 'page-basics'}); @tmp = ($m->content =~ /(CF [ABC])/g); is_deeply(\@tmp, ['CF C', 'CF A', 'CF B']); diff --git a/rt/t/customfields/transaction.t b/rt/t/customfields/transaction.t new file mode 100644 index 000000000..22f8459eb --- /dev/null +++ b/rt/t/customfields/transaction.t @@ -0,0 +1,60 @@ +#!/usr/bin/perl + +use warnings; +use strict; +use Data::Dumper; + +use RT::Test nodata => 1, tests => 14; +use_ok('RT'); +use_ok('RT::Transactions'); + + +my $q = RT::Queue->new(RT->SystemUser); +my ($id,$msg) = $q->Create( Name => 'TxnCFTest'.$$); +ok($id,$msg); + +my $cf = RT::CustomField->new(RT->SystemUser); +($id,$msg) = $cf->Create(Name => 'Txnfreeform-'.$$, Type => 'Freeform', MaxValues => '0', LookupType => RT::Transaction->CustomFieldLookupType ); + +ok($id,$msg); + +($id,$msg) = $cf->AddToObject($q); + +ok($id,$msg); + + +my $ticket = RT::Ticket->new(RT->SystemUser); + +my $transid; +($id,$transid, $msg) = $ticket->Create(Queue => $q->id, + Subject => 'TxnCF test', + ); +ok($id,$msg); + +my $trans = RT::Transaction->new(RT->SystemUser); +$trans->Load($transid); + +is($trans->ObjectId,$id); +is ($trans->ObjectType, 'RT::Ticket'); +is ($trans->Type, 'Create'); +my $txncfs = $trans->CustomFields; +is ($txncfs->Count, 1, "We have one custom field"); +my $txn_cf = $txncfs->First; +is ($txn_cf->id, $cf->id, "It's the right custom field"); +my $values = $trans->CustomFieldValues($txn_cf->id); +is ($values->Count, 0, "It has no values"); + +# Old API +my %cf_updates = ( 'CustomField-'.$cf->id => 'Testing'); +$trans->UpdateCustomFields( ARGSRef => \%cf_updates); + + $values = $trans->CustomFieldValues($txn_cf->id); +is ($values->Count, 1, "It has one value"); + +# New API + +$trans->UpdateCustomFields( 'CustomField-'.$cf->id => 'Test two'); + $values = $trans->CustomFieldValues($txn_cf->id); +is ($values->Count, 2, "it has two values"); + +# TODO ok(0, "Should updating custom field values remove old values?"); diff --git a/rt/t/data/configs/passwords b/rt/t/data/configs/passwords new file mode 100644 index 000000000..2ea72d166 --- /dev/null +++ b/rt/t/data/configs/passwords @@ -0,0 +1,2 @@ +# root / password +root:8NbrT44Shvnco diff --git a/rt/t/fts/indexed_mysql.t b/rt/t/fts/indexed_mysql.t new file mode 100644 index 000000000..8966f1cd4 --- /dev/null +++ b/rt/t/fts/indexed_mysql.t @@ -0,0 +1,134 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT::Test tests => undef; +plan skip_all => 'Not mysql' unless RT->Config->Get('DatabaseType') eq 'mysql'; +plan skip_all => "No SphinxSE in mysql" unless $RT::Handle->CheckSphinxSE; + +my %sphinx; +$sphinx{'searchd'} = RT::Test->find_executable('searchd'); +$sphinx{'indexer'} = RT::Test->find_executable('indexer'); + +plan skip_all => "No searchd and indexer under PATH" + unless $sphinx{'searchd'} && $sphinx{'indexer'}; + +plan tests => 15; + +RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Table => 'AttachmentsIndex', MaxMatches => 1000 ); + +setup_indexing(); + +my $q = RT::Test->load_or_create_queue( Name => 'General' ); +ok $q && $q->id, 'loaded or created queue'; +my $queue = $q->Name; + +sub setup_indexing { + # Since we're not running a webserver in this test, use the + # known-safe port we determined at test setup + my $port = $RT::Test::port; + my ($exit_code, $output) = RT::Test->run_and_capture( + 'no-ask' => 1, + command => $RT::SbinPath .'/rt-setup-fulltext-index', + dba => $ENV{'RT_DBA_USER'}, + 'dba-password' => $ENV{'RT_DBA_PASSWORD'}, + url => "sphinx://localhost:$port/rt", + ); + ok(!$exit_code, "setted up index"); + diag "output: $output" if $ENV{'TEST_VERBOSE'}; + + my $tmp = $sphinx{'directory'} = File::Spec->catdir( RT::Test->temp_directory, 'sphinx' ); + mkdir $tmp; + + my $sphinx_conf = $output; + $sphinx_conf =~ s/.*?source rt {/source rt {/ms; + $sphinx_conf =~ s{\Q$RT::VarPath\E/sphinx/}{$tmp/}g; + + $sphinx{'config'} = File::Spec->catfile( $tmp, 'sphinx.conf' ); + { + open my $fh, ">", $sphinx{'config'}; + print $fh $sphinx_conf; + close $fh; + } + + sync_index(); + + { + my ($exit_code, $output) = RT::Test->run_and_capture( + command => $sphinx{'searchd'}, + config => $sphinx{'config'}, + ); + ok(!$exit_code, "setted up index") or diag "output: $output"; + $sphinx{'started'} = 1 if !$exit_code; + } +} + +sub sync_index { + local $SIG{'CHLD'} = 'DEFAULT'; + local $SIG{'PIPE'} = 'DEFAULT'; + open my $fh, '-|', $sphinx{'indexer'}, '--all', + '--config' => $sphinx{'config'}, + $sphinx{'started'}? ('--rotate') : (), + ; + my $output = <$fh>; + close $fh; + my $exit_code = $?>>8; + ok(!$exit_code, "indexed") or diag "output: $output"; + + # We may need to wait a second for searchd to pick up the changes + sleep 1; +} + +sub run_tests { + my @test = @_; + while ( my ($query, $checks) = splice @test, 0, 2 ) { + run_test( $query, %$checks ); + } +} + +my @tickets; +sub run_test { + my ($query, %checks) = @_; + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + + my $tix = RT::Tickets->new(RT->SystemUser); + $tix->FromSQL( "( $query_prefix ) AND ( $query )" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %checks; + is($tix->Count, $count, "found correct number of ticket(s) by '$query'") or $error = 1; + + my $good_tickets = ($tix->Count == $count); + while ( my $ticket = $tix->Next ) { + next if $checks{ $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 '$query'" ) or $error = 1; + + diag "Wrong SQL query for '$query':". $tix->BuildSelectQuery if $error; +} + +@tickets = RT::Test->create_tickets( + { Queue => $q->id }, + { Subject => 'book', Content => 'book' }, + { Subject => 'bar', Content => 'bar' }, +); +sync_index(); + +run_tests( + "Content LIKE 'book'" => { book => 1, bar => 0 }, + "Content LIKE 'bar'" => { book => 0, bar => 1 }, +); + +END { + my $Test = RT::Test->builder; + return if $Test->{Original_Pid} != $$; + return unless $sphinx{'started'}; + + my $pid = int RT::Test->file_content([$sphinx{'directory'}, 'searchd.pid']); + kill TERM => $pid if $pid; +} diff --git a/rt/t/fts/indexed_oracle.t b/rt/t/fts/indexed_oracle.t new file mode 100644 index 000000000..5d71712d0 --- /dev/null +++ b/rt/t/fts/indexed_oracle.t @@ -0,0 +1,82 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT::Test tests => undef; +plan skip_all => 'Not Oracle' unless RT->Config->Get('DatabaseType') eq 'Oracle'; +plan tests => 13; + +RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1 ); + +setup_indexing(); + +my $q = RT::Test->load_or_create_queue( Name => 'General' ); +ok $q && $q->id, 'loaded or created queue'; +my $queue = $q->Name; + +sub setup_indexing { + my %args = ( + 'no-ask' => 1, + command => $RT::SbinPath .'/rt-setup-fulltext-index', + dba => $ENV{'RT_DBA_USER'}, + 'dba-password' => $ENV{'RT_DBA_PASSWORD'}, + ); + my ($exit_code, $output) = RT::Test->run_and_capture( %args ); + ok(!$exit_code, "setted up index") or diag "output: $output"; +} + +sub sync_index { + my %args = ( + command => $RT::SbinPath .'/rt-fulltext-indexer', + ); + my ($exit_code, $output) = RT::Test->run_and_capture( %args ); + ok(!$exit_code, "synced the index") or diag "output: $output"; +} + +sub run_tests { + my @test = @_; + while ( my ($query, $checks) = splice @test, 0, 2 ) { + run_test( $query, %$checks ); + } +} + +my @tickets; +sub run_test { + my ($query, %checks) = @_; + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + + my $tix = RT::Tickets->new(RT->SystemUser); + $tix->FromSQL( "( $query_prefix ) AND ( $query )" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %checks; + is($tix->Count, $count, "found correct number of ticket(s) by '$query'") or $error = 1; + + my $good_tickets = ($tix->Count == $count); + while ( my $ticket = $tix->Next ) { + next if $checks{ $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 '$query'" ) or $error = 1; + + diag "Wrong SQL query for '$query':". $tix->BuildSelectQuery if $error; +} + +@tickets = RT::Test->create_tickets( + { Queue => $q->id }, + { Subject => 'book', Content => 'book' }, + { Subject => 'bar', Content => 'bar' }, +); +sync_index(); + +run_tests( + "Content LIKE 'book'" => { book => 1, bar => 0 }, + "Content LIKE 'bar'" => { book => 0, bar => 1 }, +); + +@tickets = (); + diff --git a/rt/t/fts/indexed_pg.t b/rt/t/fts/indexed_pg.t new file mode 100644 index 000000000..c437c1f4f --- /dev/null +++ b/rt/t/fts/indexed_pg.t @@ -0,0 +1,119 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT::Test tests => undef; +plan skip_all => 'Not Pg' unless RT->Config->Get('DatabaseType') eq 'Pg'; + +my ($major, $minor) = $RT::Handle->dbh->get_info(18) =~ /^0*(\d+)\.0*(\d+)/; +plan skip_all => "Need Pg 8.2 or higher; we have $major.$minor" + if "$major.$minor" < 8.2; + +plan tests => 36; + +RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Column => 'ContentIndex', Table => 'Attachments' ); + +setup_indexing(); + +my $q = RT::Test->load_or_create_queue( Name => 'General' ); +ok $q && $q->id, 'loaded or created queue'; +my $queue = $q->Name; + +sub setup_indexing { + my %args = ( + 'no-ask' => 1, + command => $RT::SbinPath .'/rt-setup-fulltext-index', + dba => $ENV{'RT_DBA_USER'}, + 'dba-password' => $ENV{'RT_DBA_PASSWORD'}, + ); + my ($exit_code, $output) = RT::Test->run_and_capture( %args ); + ok(!$exit_code, "setted up index") or diag "output: $output"; +} + +sub sync_index { + my %args = ( + command => $RT::SbinPath .'/rt-fulltext-indexer', + ); + my ($exit_code, $output) = RT::Test->run_and_capture( %args ); + ok(!$exit_code, "setted up index") or diag "output: $output"; +} + +sub run_tests { + my @test = @_; + while ( my ($query, $checks) = splice @test, 0, 2 ) { + run_test( $query, %$checks ); + } +} + +my @tickets; +sub run_test { + my ($query, %checks) = @_; + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + + my $tix = RT::Tickets->new(RT->SystemUser); + $tix->FromSQL( "( $query_prefix ) AND ( $query )" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %checks; + is($tix->Count, $count, "found correct number of ticket(s) by '$query'") or $error = 1; + + my $good_tickets = ($tix->Count == $count); + while ( my $ticket = $tix->Next ) { + next if $checks{ $ticket->id }; + diag $ticket->Subject ." ticket has been found when it's not expected"; + $good_tickets = 0; + } + ok( $good_tickets, "all tickets are good with '$query'" ) or $error = 1; + + diag "Wrong SQL query for '$query':". $tix->BuildSelectQuery if $error; +} + +@tickets = RT::Test->create_tickets( + { Queue => $q->id }, + { Subject => 'fts test 1', Content => 'book' }, + { Subject => 'fts test 2', Content => 'bars' }, +); +sync_index(); + +my $book = $tickets[0]; +my $bars = $tickets[1]; + +run_tests( + "Content LIKE 'book'" => { $book->id => 1, $bars->id => 0 }, + "Content LIKE 'bars'" => { $book->id => 0, $bars->id => 1 }, + + # make sure that Pg stemming works + "Content LIKE 'books'" => { $book->id => 1, $bars->id => 0 }, + "Content LIKE 'bar'" => { $book->id => 0, $bars->id => 1 }, + + # no matches + "Content LIKE 'baby'" => { $book->id => 0, $bars->id => 0 }, + "Content LIKE 'pubs'" => { $book->id => 0, $bars->id => 0 }, +); + +# Test the "ts_vector too long" skip +my $content = ""; +$content .= "$_\n" for 1..200_000; +@tickets = RT::Test->create_tickets( + { Queue => $q->id }, + { Subject => 'Short content', Content => '50' }, + { Subject => 'Long content', Content => $content }, + { Subject => 'More short', Content => '50' }, +); + +my ($exit_code, $output) = RT::Test->run_and_capture( + command => $RT::SbinPath .'/rt-fulltext-indexer' +); +like($output, qr/string is too long for tsvector/, "Got a warning for the ticket"); +ok(!$exit_code, "set up index"); + +# The long content is skipped entirely +run_tests( + "Content LIKE '1'" => { $tickets[0]->id => 0, $tickets[1]->id => 0, $tickets[2]->id => 0 }, + "Content LIKE '50'" => { $tickets[0]->id => 1, $tickets[1]->id => 0, $tickets[2]->id => 1 }, +); + +@tickets = (); diff --git a/rt/t/fts/not_indexed.t b/rt/t/fts/not_indexed.t new file mode 100644 index 000000000..0a1abd081 --- /dev/null +++ b/rt/t/fts/not_indexed.t @@ -0,0 +1,61 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT::Test tests => 20; + +RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 0 ); + +my $q = RT::Test->load_or_create_queue( Name => 'General' ); +ok $q && $q->id, 'loaded or created queue'; +my $queue = $q->Name; + +sub run_tests { + my @test = @_; + while ( my ($query, $checks) = splice @test, 0, 2 ) { + run_test( $query, %$checks ); + } +} + +my @tickets; +sub run_test { + my ($query, %checks) = @_; + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + + my $tix = RT::Tickets->new(RT->SystemUser); + $tix->FromSQL( "( $query_prefix ) AND ( $query )" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %checks; + is($tix->Count, $count, "found correct number of ticket(s) by '$query'") or $error = 1; + + my $good_tickets = ($tix->Count == $count); + while ( my $ticket = $tix->Next ) { + next if $checks{ $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 '$query'" ) or $error = 1; + + diag "Wrong SQL query for '$query':". $tix->BuildSelectQuery if $error; +} + +@tickets = RT::Test->create_tickets( + { Queue => $q->id }, + { Subject => 'book', Content => 'book' }, + { Subject => 'bar', Content => 'bar' }, + { Subject => 'no content', Content => undef }, +); + +run_tests( + "Content LIKE 'book'" => { book => 1, bar => 0 }, + "Content LIKE 'bar'" => { book => 0, bar => 1 }, + "(Content LIKE 'baz' OR Subject LIKE 'con')" => { 'no content' => 1 }, + "(Content LIKE 'bar' OR Subject LIKE 'con')" => { 'no content' => 1, bar => 1 }, + "(Content LIKE 'bar' OR Subject LIKE 'missing')" => { bar => 1 }, +); + + diff --git a/rt/t/i18n/caching.t b/rt/t/i18n/caching.t new file mode 100644 index 000000000..ebb29423f --- /dev/null +++ b/rt/t/i18n/caching.t @@ -0,0 +1,33 @@ +#!/usr/bin/perl -w +use strict; +use warnings; + +use RT::Test; + +{ + my $french = RT::User->new(RT->SystemUser); + $french->LoadOrCreateByEmail('french@example.com'); + $french->SetName('french'); + $french->SetLang('fr'); + $french->SetPrivileged(1); + $french->SetPassword('password'); + $french->PrincipalObj->GrantRight(Right => 'SuperUser'); +} + + +my ($baseurl, $m) = RT::Test->started_ok; +$m->login( root => "password" ); +$m->get_ok('/Prefs/Other.html'); +$m->content_lacks('Ne pas','Lacks translated french'); +$m->get_ok( "/NoAuth/Logout.html" ); + +$m->login( french => "password" ); +$m->get_ok('/Prefs/Other.html'); +$m->content_contains('Ne pas','Has translated french'); +$m->get_ok( "/NoAuth/Logout.html" ); # ->logout fails because it's translated + +$m->login( root => "password" ); +$m->get_ok('/Prefs/Other.html'); +$m->content_lacks('Ne pas','Lacks translated french'); + +undef $m; diff --git a/rt/t/i18n/footer.t b/rt/t/i18n/footer.t new file mode 100644 index 000000000..e0d09058c --- /dev/null +++ b/rt/t/i18n/footer.t @@ -0,0 +1,29 @@ +#!/usr/bin/perl -w +use strict; +use warnings; + +use RT::Test; + +{ + my $chinese = RT::User->new(RT->SystemUser); + $chinese->LoadOrCreateByEmail('chinese@example.com'); + $chinese->SetName('chinese'); + $chinese->SetLang('zh_tw'); + $chinese->SetPrivileged(1); + $chinese->SetPassword('password'); + $chinese->PrincipalObj->GrantRight(Right => 'SuperUser'); +} + +my ($baseurl, $m) = RT::Test->started_ok; +$m->login( root => "password" ); +$m->content_contains('Copyright','Has english coypright'); +$m->get_ok( "/NoAuth/Logout.html" ); + +$m->login( chinese => "password" ); +$m->content_lacks('Copyright','Lacks english copyright'); +$m->get_ok( "/NoAuth/Logout.html" ); # ->logout fails because it's translated + +$m->login( root => "password" ); +$m->content_contains('Copyright','Still has english copyright'); + +undef $m; diff --git a/rt/t/lifecycles/basics.t b/rt/t/lifecycles/basics.t new file mode 100644 index 000000000..40e239186 --- /dev/null +++ b/rt/t/lifecycles/basics.t @@ -0,0 +1,247 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Data::Dumper; + +BEGIN {require 't/lifecycles/utils.pl'}; + +my $general = RT::Test->load_or_create_queue( + Name => 'General', +); +ok $general && $general->id, 'loaded or created a queue'; + +my $tstatus = sub { + DBIx::SearchBuilder::Record::Cachable->FlushCache; + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load( $_[0] ); + return $ticket->Status; +}; + +diag "check basic API"; +{ + my $schema = $general->Lifecycle; + isa_ok($schema, 'RT::Lifecycle'); + is $schema->Name, 'default', "it's a default schema"; + is_deeply [$schema->Valid], + [qw(new open stalled resolved rejected deleted)], + 'this is the default set from our config file'; + + foreach my $s ( qw(new open stalled resolved rejected deleted) ) { + ok $schema->IsValid($s), "valid"; + } + ok !$schema->IsValid(), 'invalid'; + ok !$schema->IsValid(''), 'invalid'; + ok !$schema->IsValid(undef), 'invalid'; + ok !$schema->IsValid('foo'), 'invalid'; + + is_deeply [$schema->Initial], ['new'], 'initial set'; + ok $schema->IsInitial('new'), "initial"; + ok !$schema->IsInitial('open'), "not initial"; + ok !$schema->IsInitial, "not initial"; + ok !$schema->IsInitial(''), "not initial"; + ok !$schema->IsInitial(undef), "not initial"; + ok !$schema->IsInitial('foo'), "not initial"; + + is_deeply [$schema->Active], [qw(open stalled)], 'active set'; + ok( $schema->IsActive($_), "active" ) + foreach qw(open stalled); + ok !$schema->IsActive('new'), "not active"; + ok !$schema->IsActive, "not active"; + ok !$schema->IsActive(''), "not active"; + ok !$schema->IsActive(undef), "not active"; + ok !$schema->IsActive('foo'), "not active"; + + is_deeply [$schema->Inactive], [qw(resolved rejected deleted)], 'inactive set'; + ok( $schema->IsInactive($_), "inactive" ) + foreach qw(resolved rejected deleted); + ok !$schema->IsInactive('new'), "not inactive"; + ok !$schema->IsInactive, "not inactive"; + ok !$schema->IsInactive(''), "not inactive"; + ok !$schema->IsInactive(undef), "not inactive"; + ok !$schema->IsInactive('foo'), "not inactive"; + + is_deeply [$schema->Transitions('')], [qw(new open resolved)], 'on create transitions'; + ok $schema->IsTransition('' => $_), 'good transition' + foreach qw(new open resolved); +} + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +diag "check status input on create"; +{ + $m->goto_create_ticket( $general ); + + my $form = $m->form_name('TicketCreate'); + ok my $input = $form->find_input('Status'), 'found status selector'; + + my @form_values = $input->possible_values; + ok scalar @form_values, 'some options in the UI'; + + my $valid = 1; + foreach ( @form_values ) { + next if $general->Lifecycle->IsValid($_); + $valid = 0; + diag("$_ doesn't appear to be a valid status, but it was in the form"); + } + + + ok $valid, 'all statuses in the form are valid'; +} + +diag "create a ticket"; +my $tid; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + ($tid) = $ticket->Create( Queue => $general->id, Subject => 'test' ); + ok $tid, "created a ticket #$tid"; + is $ticket->Status, 'new', 'correct status'; +} + +diag "new ->(open it)-> open"; +{ + ok $m->goto_ticket( $tid ), 'opened a ticket'; + $m->check_links( + has => ['Open It', 'Resolve', 'Reject', 'Delete'], + has_no => ['Stall', 'Re-open', 'Undelete'], + ); + + $m->follow_link_ok({text => 'Open It'}); + $m->form_name('TicketUpdate'); + $m->click('SubmitTicket'); + + is $tstatus->($tid), 'open', 'changed status'; +} + +diag "open ->(stall)-> stalled"; +{ + is $tstatus->($tid), 'open', 'ticket is open'; + + ok $m->goto_ticket( $tid ), 'opened a ticket'; + + $m->check_links( + has => ['Stall', 'Resolve', 'Reject'], + has_no => ['Open It', 'Delete', 'Re-open', 'Undelete'], + ); + + $m->follow_link_ok({text => 'Stall'}); + $m->form_name('TicketUpdate'); + $m->click('SubmitTicket'); + + is $tstatus->($tid), 'stalled', 'changed status'; +} + +diag "stall ->(open it)-> open"; +{ + is $tstatus->($tid), 'stalled', 'ticket is stalled'; + + ok $m->goto_ticket( $tid ), 'opened a ticket'; + $m->check_links( + has => ['Open It'], + has_no => ['Delete', 'Re-open', 'Undelete', 'Stall', 'Resolve', 'Reject'], + ); + + $m->follow_link_ok({text => 'Open It'}); + + is $tstatus->($tid), 'open', 'changed status'; +} + +diag "open -> deleted, only via modify"; +{ + is $tstatus->($tid), 'open', 'ticket is open'; + + $m->get_ok( '/Ticket/Modify.html?id='. $tid ); + my $form = $m->form_name('TicketModify'); + ok my $input = $form->find_input('Status'), 'found status selector'; + + my @form_values = $input->possible_values; + ok scalar @form_values, 'some options in the UI'; + + ok grep($_ eq 'deleted', @form_values), "has deleted"; + + $m->select( Status => 'deleted' ); + $m->submit; + + is $tstatus->($tid), 'deleted', 'deleted ticket'; +} + +diag "deleted -> X via modify, only open is available"; +{ + is $tstatus->($tid), 'deleted', 'ticket is deleted'; + + $m->get_ok( '/Ticket/Modify.html?id='. $tid ); + my $form = $m->form_name('TicketModify'); + ok my $input = $form->find_input('Status'), 'found status selector'; + + my @form_values = $input->possible_values; + ok scalar @form_values, 'some options in the UI'; + + is join('-', @form_values), '-open', 'only open and default available'; +} + +diag "check illegal values and transitions"; +{ + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'illegal', + ); + ok !$id, 'have not created a ticket'; + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'new', + ); + ok $id, 'created a ticket'; + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'new', + ); + ok $id, 'created a ticket'; + + (my $status, $msg) = $ticket->SetStatus( 'illeagal' ); + ok !$status, "couldn't set illeagal status"; + is $ticket->Status, 'new', 'status is steal the same'; + + ($status, $msg) = $ticket->SetStatus( 'stalled' ); + ok !$status, "couldn't set status, transition is illeagal"; + is $ticket->Status, 'new', 'status is steal the same'; + } +} + +diag "'!inactive -> inactive' actions are shown even if ticket has unresolved dependencies"; +{ + my $child_ticket = RT::Test->create_ticket( + Queue => $general->id, + Subject => 'child', + ); + my $cid = $child_ticket->id; + my $parent_ticket = RT::Test->create_ticket( + Queue => $general->id, + Subject => 'parent', + DependsOn => $child_ticket->id, + ); + my $pid = $parent_ticket->id; + + ok $m->goto_ticket( $pid ), 'opened a ticket'; + $m->check_links( + has => ['Open It', 'Resolve', 'Reject', 'Delete' ], + has_no => ['Stall', 'Re-open', 'Undelete', ], + ); + ok $m->goto_ticket( $cid ), 'opened a ticket'; + $m->check_links( + has => ['Open It', 'Resolve', 'Reject', 'Delete'], + has_no => ['Stall', 'Re-open', 'Undelete'], + ); +} + diff --git a/rt/t/lifecycles/dates.t b/rt/t/lifecycles/dates.t new file mode 100644 index 000000000..d8a27f613 --- /dev/null +++ b/rt/t/lifecycles/dates.t @@ -0,0 +1,317 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Data::Dumper; + +BEGIN {require 't/lifecycles/utils.pl'}; + +my $general = RT::Test->load_or_create_queue( + Name => 'General', +); +ok $general && $general->id, 'loaded or created a queue'; + +my $delivery = RT::Test->load_or_create_queue( + Name => 'delivery', + Lifecycle => 'delivery', +); +ok $delivery && $delivery->id, 'loaded or created a queue'; + +my $tstatus = sub { + DBIx::SearchBuilder::Record::Cachable->FlushCache; + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load( $_[0] ); + return $ticket->Status; +}; + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +diag "check basic API"; +{ + my $schema = $general->Lifecycle; + isa_ok($schema, 'RT::Lifecycle'); + is $schema->Name, 'default', "it's a default schema"; + + $schema = $delivery->Lifecycle; + isa_ok($schema, 'RT::Lifecycle'); + is $schema->Name, 'delivery', "it's a delivery schema"; +} + +diag "dates on create for default schema"; +{ + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'new', + ); + ok $id, 'created a ticket'; + ok $ticket->StartedObj->Unix <= 0, 'started is not set'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'open', + ); + ok $id, 'created a ticket'; + ok $ticket->StartedObj->Unix > 0, 'started is set'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'resolved', + ); + ok $id, 'created a ticket'; + ok $ticket->StartedObj->Unix > 0, 'started is set'; + ok $ticket->ResolvedObj->Unix > 0, 'resolved is set'; + } + + my $test_date = '2008-11-28 12:00:00'; + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'new', + Started => $test_date, + Resolved => $test_date, + ); + ok $id, 'created a ticket'; + is $ticket->StartedObj->ISO, $test_date, 'started is set'; + is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set'; + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'open', + Started => $test_date, + Resolved => $test_date, + ); + ok $id, 'created a ticket'; + is $ticket->StartedObj->ISO, $test_date, 'started is set'; + is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set'; + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'resolved', + Started => $test_date, + Resolved => $test_date, + ); + ok $id, 'created a ticket'; + is $ticket->StartedObj->ISO, $test_date, 'started is set'; + is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set'; + } +} + +diag "dates on create for delivery schema"; +{ + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $delivery->id, + Subject => 'test', + Status => 'ordered', + ); + ok $id, 'created a ticket'; + is $ticket->StartedObj->Unix , 0, 'started is not set'; + is $ticket->ResolvedObj->Unix, 0, 'resolved is not set'; + + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $txn, $msg) = $ticket->Create( + Queue => $delivery->id, + Subject => 'test', + ); + ok $id, 'created a ticket'; + diag($msg); + is $ticket->Status, 'ordered', "Status is ordered"; + my ($statusval,$statusmsg) = $ticket->SetStatus('on way'); + ok($statusval,$statusmsg); + ok $ticket->StartedObj->Unix > 0, 'started is set to ' .$ticket->StartedObj->AsString ; + is $ticket->ResolvedObj->Unix, 0, 'resolved is not set'; + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $delivery->id, + Subject => 'test', + ); + ok $id, 'created a ticket'; + + my ($statusval,$statusmsg) = $ticket->SetStatus('on way'); + ok($statusval,$statusmsg); + + ($statusval,$statusmsg) = $ticket->SetStatus('delivered'); + ok($statusval,$statusmsg); + + ok $ticket->StartedObj->Unix > 0, 'started is set'; + ok $ticket->ResolvedObj->Unix > 0, 'resolved is set'; + } + + my $test_date = '2008-11-28 12:00:00'; + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $statusmsg) = $ticket->Create( + Queue => $delivery->id, + Subject => 'test', + Status => 'ordered', + Started => $test_date, + Resolved => $test_date, + ); + ok $id, 'created a ticket'; + is $ticket->StartedObj->ISO, $test_date, 'started is set'; + is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set'; + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $delivery->id, + Subject => 'test', + Status => 'ordered', + Started => $test_date, + Resolved => $test_date, + ); + ok $id, 'created a ticket'; + my ($statusval,$statusmsg) = $ticket->SetStatus('on way'); + ok($statusval,$statusmsg); + is $ticket->StartedObj->ISO, $test_date, 'started is set'; + is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set'; + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $delivery->id, + Subject => 'test', + Started => $test_date, + Resolved => $test_date, + ); + ok $id, 'created a ticket'; + my ($statusval,$statusmsg) = $ticket->SetStatus('on way'); + ok($statusval,$statusmsg); + ($statusval,$statusmsg) = $ticket->SetStatus('delivered'); + ok($statusval,$statusmsg); + is $ticket->StartedObj->ISO, $test_date, 'started is set'; + TODO: { + local $TODO = "we should decide if we set resolved repeatedly"; + is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set'; + }; + } +} + +diag "dates on status change for default schema"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'new', + ); + ok $id, 'created a ticket'; + ok $ticket->StartedObj->Unix <= 0, 'started is not set'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + + (my $status, $msg) = $ticket->SetStatus('open'); + ok $status, 'changed status' or diag "error: $msg"; + ok $ticket->StartedObj->Unix > 0, 'started is set'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + + my $started = $ticket->StartedObj->Unix; + + ($status, $msg) = $ticket->SetStatus('stalled'); + ok $status, 'changed status' or diag "error: $msg"; + is $ticket->StartedObj->Unix, $started, 'started is set and the same'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + + ($status, $msg) = $ticket->SetStatus('open'); + ok $status, 'changed status' or diag "error: $msg"; + is $ticket->StartedObj->Unix, $started, 'started is set and the same'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + + ($status, $msg) = $ticket->SetStatus('resolved'); + ok $status, 'changed status' or diag "error: $msg"; + is $ticket->StartedObj->Unix, $started, 'started is set and the same'; + ok $ticket->ResolvedObj->Unix > 0, 'resolved is set'; +} + +diag "dates on status change for delivery schema"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $delivery->id, + Subject => 'test', + Status => 'ordered', + ); + ok $id, 'created a ticket'; + ok $ticket->StartedObj->Unix <= 0, 'started is not set'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + + (my $status, $msg) = $ticket->SetStatus('delayed'); + ok $status, 'changed status' or diag "error: $msg"; + ok $ticket->StartedObj->Unix > 0, 'started is set'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + + my $started = $ticket->StartedObj->Unix; + + ($status, $msg) = $ticket->SetStatus('on way'); + ok $status, 'changed status' or diag "error: $msg"; + is $ticket->StartedObj->Unix, $started, 'started is set and the same'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + + ($status, $msg) = $ticket->SetStatus('delivered'); + ok $status, 'changed status' or diag "error: $msg"; + is $ticket->StartedObj->Unix, $started, 'started is set and the same'; + ok $ticket->ResolvedObj->Unix > 0, 'resolved is set'; +} + +diag "add partial map between general->delivery"; +{ + my $schemas = RT->Config->Get('Lifecycles'); + $schemas->{'__maps__'} = { + 'default -> delivery' => { + new => 'on way', + }, + 'delivery -> default' => { + 'on way' => 'resolved', + }, + }; + RT::Lifecycle->FillCache; +} + +diag "check date changes on moving a ticket"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'new', + ); + ok $id, 'created a ticket'; + ok $ticket->StartedObj->Unix <= 0, 'started is not set'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + + (my $status, $msg) = $ticket->SetQueue( $delivery->id ); + ok $status, "moved ticket between queues with different schemas"; + is $ticket->Status, 'on way', 'status has been changed'; + ok $ticket->StartedObj->Unix > 0, 'started is set'; + ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + + ($status, $msg) = $ticket->SetQueue( $general->id ); + ok $status, "moved ticket between queues with different schemas"; + is $ticket->Status, 'resolved', 'status has been changed'; + ok $ticket->StartedObj->Unix > 0, 'started is set'; + ok $ticket->ResolvedObj->Unix > 0, 'resolved is set'; +} diff --git a/rt/t/lifecycles/moving.t b/rt/t/lifecycles/moving.t new file mode 100644 index 000000000..6e0d64bbf --- /dev/null +++ b/rt/t/lifecycles/moving.t @@ -0,0 +1,97 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Data::Dumper; + +BEGIN {require 't/lifecycles/utils.pl'}; + +my $general = RT::Test->load_or_create_queue( + Name => 'General', +); +ok $general && $general->id, 'loaded or created a queue'; + +my $delivery = RT::Test->load_or_create_queue( + Name => 'delivery', + Lifecycle => 'delivery', +); +ok $delivery && $delivery->id, 'loaded or created a queue'; + +my $tstatus = sub { + DBIx::SearchBuilder::Record::Cachable->FlushCache; + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load( $_[0] ); + return $ticket->Status; +}; + +diag "check moving without a map"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'new', + ); + ok $id, 'created a ticket'; + (my $status, $msg) = $ticket->SetQueue( $delivery->id ); + ok !$status, "couldn't change queue when there is no maps between schemas"; + is $ticket->Queue, $general->id, 'queue is steal the same'; + is $ticket->Status, 'new', 'status is steal the same'; +} + +diag "add partial map"; +{ + my $schemas = RT->Config->Get('Lifecycles'); + $schemas->{'__maps__'} = { + 'default -> delivery' => { + new => 'ordered', + }, + }; + RT::Lifecycle->FillCache; +} + +diag "check moving with a partial map"; +{ + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'new', + ); + ok $id, 'created a ticket'; + (my $status, $msg) = $ticket->SetQueue( $delivery->id ); + ok $status, "moved ticket between queues with different schemas"; + is $ticket->Queue, $delivery->id, 'queue has been changed' + or diag "error: $msg"; + is $ticket->Status, 'ordered', 'status has been changed'; + } + { + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $general->id, + Subject => 'test', + Status => 'open', + ); + ok $id, 'created a ticket'; + (my $status, $msg) = $ticket->SetQueue( $delivery->id ); + ok !$status, "couldn't change queue when map is not complete"; + is $ticket->Queue, $general->id, 'queue is steal the same'; + is $ticket->Status, 'open', 'status is steal the same'; + } +} + +diag "one way map doesn't work backwards"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $msg) = $ticket->Create( + Queue => $delivery->id, + Subject => 'test', + Status => 'ordered', + ); + ok $id, 'created a ticket'; + (my $status, $msg) = $ticket->SetQueue( $general->id ); + ok !$status, "couldn't change queue when there is no maps between schemas"; + is $ticket->Queue, $delivery->id, 'queue is steal the same'; + is $ticket->Status, 'ordered', 'status is steal the same'; +} diff --git a/rt/t/lifecycles/unresolved-deps.t b/rt/t/lifecycles/unresolved-deps.t new file mode 100644 index 000000000..aff9a1a56 --- /dev/null +++ b/rt/t/lifecycles/unresolved-deps.t @@ -0,0 +1,45 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Data::Dumper; + +use Test::More tests => 15; +BEGIN {require 't/lifecycles/utils.pl'}; + +my $general = RT::Test->load_or_create_queue( + Name => 'General', +); +ok $general && $general->id, 'loaded or created a queue'; + +# different value tested in basics +RT->Config->Set('HideResolveActionsWithDependencies' => 1); + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +{ + my $child_ticket = RT::Test->create_ticket( + Queue => $general->id, + Subject => 'child', + ); + my $cid = $child_ticket->id; + my $parent_ticket = RT::Test->create_ticket( + Queue => $general->id, + Subject => 'parent', + DependsOn => $child_ticket->id, + ); + my $pid = $parent_ticket->id; + + ok $m->goto_ticket( $pid ), 'opened a ticket'; + $m->check_links( + has => ['Open It'], + has_no => ['Stall', 'Re-open', 'Undelete', 'Resolve', 'Reject', 'Delete'], + ); + ok $m->goto_ticket( $cid ), 'opened a ticket'; + $m->check_links( + has => ['Open It', 'Resolve', 'Reject', 'Delete'], + has_no => ['Stall', 'Re-open', 'Undelete'], + ); +} + diff --git a/rt/t/lifecycles/utils.pl b/rt/t/lifecycles/utils.pl new file mode 100644 index 000000000..6fb229390 --- /dev/null +++ b/rt/t/lifecycles/utils.pl @@ -0,0 +1,73 @@ +#!/usr/bin/perl + +use strict; +use warnings; + + +my $config; +BEGIN { +$config = <<END; +Set(\%Lifecycles, + default => { + initial => [qw(new)], + active => [qw(open stalled)], + inactive => [qw(resolved rejected deleted)], + defaults => { + on_create => 'new', + on_merge => 'resolved', + }, + transitions => { + '' => [qw(new open resolved)], + new => [qw(open resolved rejected deleted)], + open => [qw(stalled resolved rejected deleted)], + stalled => [qw(open)], + resolved => [qw(open)], + rejected => [qw(open)], + deleted => [qw(open)], + }, + actions => { + 'new -> open' => {label => 'Open It', update => 'Respond'}, + 'new -> resolved' => {label => 'Resolve', update => 'Comment'}, + 'new -> rejected' => {label => 'Reject', update => 'Respond'}, + 'new -> deleted' => {label => 'Delete', update => ''}, + + 'open -> stalled' => {label => 'Stall', update => 'Comment'}, + 'open -> resolved' => {label => 'Resolve', update => 'Comment'}, + 'open -> rejected' => {label => 'Reject', update => 'Respond'}, + + 'stalled -> open' => {label => 'Open It', update => ''}, + 'resolved -> open' => {label => 'Re-open', update => 'Comment'}, + 'rejected -> open' => {label => 'Re-open', update => 'Comment'}, + 'deleted -> open' => {label => 'Undelete', update => ''}, + }, + }, + delivery => { + initial => ['ordered'], + active => ['on way', 'delayed'], + inactive => ['delivered'], + defaults => { + on_create => 'ordered', + on_merge => 'delivered', + }, + transitions => { + '' => ['ordered'], + ordered => ['on way', 'delayed'], + 'on way' => ['delivered'], + delayed => ['on way'], + delivered => [], + }, + actions => { + 'ordered -> on way' => {label => 'Put On Way', update => 'Respond'}, + 'ordered -> delayed' => {label => 'Delay', update => 'Respond'}, + + 'on way -> delivered' => {label => 'Done', update => 'Respond'}, + 'delayed -> on way' => {label => 'Put On Way', update => 'Respond'}, + }, + }, +); +END +} + +use RT::Test config => $config; + +1; diff --git a/rt/t/mail/bounce.t b/rt/t/mail/bounce.t new file mode 100644 index 000000000..703e86d67 --- /dev/null +++ b/rt/t/mail/bounce.t @@ -0,0 +1,42 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +RT->Config->Set( MailCommand => 'sendmailpipe' ); +RT->Config->Set( SetOutgoingMailFrom => 1 ); +RT->Config->Set( OverrideOutgoingMailFrom => { Default => 'queue@example.invalid' } ); + +# Ensure that the fake sendmail knows where to write to +$ENV{RT_MAILLOGFILE} = RT::Test->temp_directory . "/sendmailpipe.log"; +my $fake = File::Spec->rel2abs( File::Spec->catfile( + 't', 'mail', 'fake-sendmail' ) ); +RT->Config->Set( SendmailPath => $fake); + +my $message = <<EOM; +From: doesnotexist\@willbounce.invalid +Subject: This is a test of new ticket creation + +Bounce bounce bounce +EOM + +{ + # by default, MailError wants to crit or error the email message + # out to Screen, which scribbles all over the test output + no warnings 'redefine'; + my $orig_mail_error = RT::Interface::Email->can('MailError'); + local *RT::Interface::Email::MailError = sub { $orig_mail_error->( @_, LogLevel => undef ) }; + RT::Test->send_via_mailgate($message); +} + + +open(LOG, "<", $ENV{RT_MAILLOGFILE}) or die "Can't open log file: $!"; +my $fcount; +while (my $line = <LOG>) { + $fcount++ if $line =~ /^-f/; +} +close(LOG); +# RT_MAILLOGFILE will contain all the command line flags if you need them +is($fcount,1,"Only one -f specified to sendmail command"); + +done_testing; diff --git a/rt/t/mail/charsets-outgoing.t b/rt/t/mail/charsets-outgoing.t index e3f13fb6c..e17dd983d 100644 --- a/rt/t/mail/charsets-outgoing.t +++ b/rt/t/mail/charsets-outgoing.t @@ -18,8 +18,6 @@ my %string = ( }, ); -RT::Test->set_mail_catcher; - my $queue = RT::Test->load_or_create_queue( Name => 'Regression', CorrespondAddress => 'rt-recipient@example.com', @@ -27,15 +25,15 @@ my $queue = RT::Test->load_or_create_queue( ); ok $queue && $queue->id, 'loaded or created queue'; -diag "make sure queue has no subject tag" if $ENV{'TEST_VERBOSE'}; +diag "make sure queue has no subject tag"; { my ($status, $msg) = $queue->SetSubjectTag( undef ); ok $status, "set subject tag for the queue" or diag "error: $msg"; } -diag "set intial simple autoreply template" if $ENV{'TEST_VERBOSE'}; +diag "set intial simple autoreply template"; { - my $template = RT::Template->new( $RT::SystemUser ); + my $template = RT::Template->new( RT->SystemUser ); $template->Load('Autoreply'); ok $template->id, "loaded autoreply tempalte"; @@ -49,9 +47,9 @@ diag "set intial simple autoreply template" if $ENV{'TEST_VERBOSE'}; or diag "error: $msg"; } -diag "basic test of autoreply" if $ENV{'TEST_VERBOSE'}; +diag "basic test of autoreply"; { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Create( Queue => $queue->id, Subject => 'test', @@ -61,10 +59,9 @@ diag "basic test of autoreply" if $ENV{'TEST_VERBOSE'}; ok @mails, "got some outgoing emails"; } -diag "non-ascii Subject with ascii prefix set in the template" - if $ENV{'TEST_VERBOSE'}; +diag "non-ascii Subject with ascii prefix set in the template"; foreach my $set ( 'ru', 'latin1' ) { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Create( Queue => $queue->id, Subject => $string{$set}{test}, @@ -85,15 +82,15 @@ foreach my $set ( 'ru', 'latin1' ) { foreach my $tag_set ( 'ru', 'latin1' ) { -diag "set non-ascii subject tag for the queue" if $ENV{'TEST_VERBOSE'}; +diag "set non-ascii subject tag for the queue"; { my ($status, $msg) = $queue->SetSubjectTag( $string{$tag_set}{support} ); ok $status, "set subject tag for the queue" or diag "error: $msg"; } -diag "ascii subject with non-ascii subject tag" if $ENV{'TEST_VERBOSE'}; +diag "ascii subject with non-ascii subject tag"; { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Create( Queue => $queue->id, Subject => 'test', @@ -112,9 +109,9 @@ diag "ascii subject with non-ascii subject tag" if $ENV{'TEST_VERBOSE'}; ok $status, "all mails have correct data"; } -diag "non-ascii subject with non-ascii subject tag" if $ENV{'TEST_VERBOSE'}; +diag "non-ascii subject with non-ascii subject tag"; foreach my $set ( 'ru', 'latin1' ) { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Create( Queue => $queue->id, Subject => $string{$set}{test}, @@ -137,7 +134,7 @@ foreach my $set ( 'ru', 'latin1' ) { } # subject tag -diag "return back the empty subject tag" if $ENV{'TEST_VERBOSE'}; +diag "return back the empty subject tag"; { my ($status, $msg) = $queue->SetSubjectTag( undef ); ok $status, "set subject tag for the queue" or diag "error: $msg"; @@ -146,9 +143,9 @@ diag "return back the empty subject tag" if $ENV{'TEST_VERBOSE'}; foreach my $prefix_set ( 'ru', 'latin1' ) { -diag "add non-ascii subject prefix in the autoreply template" if $ENV{'TEST_VERBOSE'}; +diag "add non-ascii subject prefix in the autoreply template"; { - my $template = RT::Template->new( $RT::SystemUser ); + my $template = RT::Template->new( RT->SystemUser ); $template->Load('Autoreply'); ok $template->id, "loaded autoreply tempalte"; @@ -161,9 +158,9 @@ diag "add non-ascii subject prefix in the autoreply template" if $ENV{'TEST_VERB ok $status, "changed content of the template" or diag "error: $msg"; } -diag "ascii subject with non-ascii subject prefix in template" if $ENV{'TEST_VERBOSE'}; +diag "ascii subject with non-ascii subject prefix in template"; { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Create( Queue => $queue->id, Subject => 'test', @@ -182,10 +179,9 @@ diag "ascii subject with non-ascii subject prefix in template" if $ENV{'TEST_VER ok $status, "all mails have correct data"; } -diag "non-ascii subject with non-ascii subject prefix in template" - if $ENV{'TEST_VERBOSE'}; +diag "non-ascii subject with non-ascii subject prefix in template"; foreach my $set ( 'ru', 'latin1' ) { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Create( Queue => $queue->id, Subject => $string{$set}{test}, @@ -207,16 +203,15 @@ foreach my $set ( 'ru', 'latin1' ) { } foreach my $tag_set ( 'ru', 'latin1' ) { -diag "set non-ascii subject tag for the queue" if $ENV{'TEST_VERBOSE'}; +diag "set non-ascii subject tag for the queue"; { my ($status, $msg) = $queue->SetSubjectTag( $string{$tag_set}{support} ); ok $status, "set subject tag for the queue" or diag "error: $msg"; } -diag "non-ascii subject, non-ascii prefix in template and non-ascii tag" - if $ENV{'TEST_VERBOSE'}; +diag "non-ascii subject, non-ascii prefix in template and non-ascii tag"; foreach my $set ( 'ru', 'latin1' ) { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Create( Queue => $queue->id, Subject => $string{$set}{test}, @@ -241,7 +236,7 @@ foreach my $set ( 'ru', 'latin1' ) { } # subject tag -diag "flush subject tag of the queue" if $ENV{'TEST_VERBOSE'}; +diag "flush subject tag of the queue"; { my ($status, $msg) = $queue->SetSubjectTag( undef ); ok $status, "set subject tag for the queue" or diag "error: $msg"; @@ -250,11 +245,11 @@ diag "flush subject tag of the queue" if $ENV{'TEST_VERBOSE'}; } # prefix set -diag "don't change subject via template" if $ENV{'TEST_VERBOSE'}; +diag "don't change subject via template"; # clean DB has autoreply that always changes subject in template, # we should test situation when subject is not changed from template { - my $template = RT::Template->new( $RT::SystemUser ); + my $template = RT::Template->new( RT->SystemUser ); $template->Load('Autoreply'); ok $template->id, "loaded autoreply tempalte"; @@ -267,9 +262,9 @@ diag "don't change subject via template" if $ENV{'TEST_VERBOSE'}; ok $status, "changed content of the template" or diag "error: $msg"; } -diag "non-ascii Subject without changes in template" if $ENV{'TEST_VERBOSE'}; +diag "non-ascii Subject without changes in template"; foreach my $set ( 'ru', 'latin1' ) { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Create( Queue => $queue->id, Subject => $string{$set}{test}, @@ -289,16 +284,15 @@ foreach my $set ( 'ru', 'latin1' ) { } foreach my $tag_set ( 'ru', 'latin1' ) { -diag "set non-ascii subject tag for the queue" if $ENV{'TEST_VERBOSE'}; +diag "set non-ascii subject tag for the queue"; { my ($status, $msg) = $queue->SetSubjectTag( $string{$tag_set}{support} ); ok $status, "set subject tag for the queue" or diag "error: $msg"; } -diag "non-ascii Subject without changes in template and with non-ascii subject tag" - if $ENV{'TEST_VERBOSE'}; +diag "non-ascii Subject without changes in template and with non-ascii subject tag"; foreach my $set ( 'ru', 'latin1' ) { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Create( Queue => $queue->id, Subject => $string{$set}{test}, @@ -320,3 +314,4 @@ foreach my $set ( 'ru', 'latin1' ) { } } # subject tag set + diff --git a/rt/t/mail/crypt-gnupg.t b/rt/t/mail/crypt-gnupg.t index cc52dd631..c0a875644 100644 --- a/rt/t/mail/crypt-gnupg.t +++ b/rt/t/mail/crypt-gnupg.t @@ -3,35 +3,20 @@ use strict; use warnings; -use RT::Test nodata => 1, tests => 92; -plan skip_all => 'GnuPG required.' - unless eval 'use GnuPG::Interface; 1'; -plan skip_all => 'gpg executable is required.' - unless RT::Test->find_executable('gpg'); - - -use File::Spec (); -use Cwd; - -my $homedir = RT::Test::get_abs_relocatable_dir(File::Spec->updir(), - qw(data gnupg keyrings) ); +my $homedir; +BEGIN { + require RT::Test; + $homedir = + RT::Test::get_abs_relocatable_dir( File::Spec->updir(), + qw/data gnupg keyrings/ ); +} -mkdir $homedir; +use RT::Test::GnuPG tests => 96, gnupg_options => { homedir => $homedir }; +use Test::Warn; -use_ok('RT::Crypt::GnuPG'); use_ok('MIME::Entity'); -RT->Config->Set( 'GnuPG', - Enable => 1, - OutgoingMessagesFormat => 'RFC' ); - -RT->Config->Set( 'GnuPGOptions', - homedir => $homedir, - 'no-permission-warning' => undef, -); - - -diag 'only signing. correct passphrase' if $ENV{'TEST_VERBOSE'}; +diag 'only signing. correct passphrase'; { my $entity = MIME::Entity->build( From => 'rt@example.com', @@ -67,14 +52,21 @@ diag 'only signing. correct passphrase' if $ENV{'TEST_VERBOSE'}; is( $status[0]->{'Trust'}, 'ULTIMATE', 'have trust value'); } -diag 'only signing. missing passphrase' if $ENV{'TEST_VERBOSE'}; +diag 'only signing. missing passphrase'; { my $entity = MIME::Entity->build( From => 'rt@example.com', Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => '' ); + my %res; + warning_like { + %res = RT::Crypt::GnuPG::SignEncrypt( + Entity => $entity, + Encrypt => 0, + Passphrase => '' + ); + } qr/can't query passphrase in batch mode/; ok( $res{'exit_code'}, "couldn't sign without passphrase"); ok( $res{'error'} || $res{'logger'}, "error is here" ); @@ -84,14 +76,23 @@ diag 'only signing. missing passphrase' if $ENV{'TEST_VERBOSE'}; is( $status[0]->{'Status'}, 'MISSING', 'missing passphrase'); } -diag 'only signing. wrong passphrase' if $ENV{'TEST_VERBOSE'}; +diag 'only signing. wrong passphrase'; { my $entity = MIME::Entity->build( From => 'rt@example.com', Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'wrong' ); + + my %res; + warning_like { + %res = RT::Crypt::GnuPG::SignEncrypt( + Entity => $entity, + Encrypt => 0, + Passphrase => 'wrong', + ); + } qr/bad passphrase/; + ok( $res{'exit_code'}, "couldn't sign with bad passphrase"); ok( $res{'error'} || $res{'logger'}, "error is here" ); @@ -101,7 +102,7 @@ diag 'only signing. wrong passphrase' if $ENV{'TEST_VERBOSE'}; is( $status[0]->{'Status'}, 'BAD', 'wrong passphrase'); } -diag 'encryption only' if $ENV{'TEST_VERBOSE'}; +diag 'encryption only'; { my $entity = MIME::Entity->build( From => 'rt@example.com', @@ -127,7 +128,7 @@ diag 'encryption only' if $ENV{'TEST_VERBOSE'}; is( $parts[0]->{'Top'}, $entity, "it's the same entity" ); } -diag 'encryption only, bad recipient' if $ENV{'TEST_VERBOSE'}; +diag 'encryption only, bad recipient'; { my $entity = MIME::Entity->build( From => 'rt@example.com', @@ -135,7 +136,15 @@ diag 'encryption only, bad recipient' if $ENV{'TEST_VERBOSE'}; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + + my %res; + warning_like { + %res = RT::Crypt::GnuPG::SignEncrypt( + Entity => $entity, + Sign => 0, + ); + } qr/public key not found/; + ok( $res{'exit_code'}, 'no way to encrypt without keys of recipients'); ok( $res{'logger'}, "errors are in logger" ); @@ -144,7 +153,7 @@ diag 'encryption only, bad recipient' if $ENV{'TEST_VERBOSE'}; is( $status[0]->{'Keyword'}, 'INV_RECP', 'invalid recipient'); } -diag 'encryption and signing with combined method' if $ENV{'TEST_VERBOSE'}; +diag 'encryption and signing with combined method'; { my $entity = MIME::Entity->build( From => 'rt@example.com', @@ -174,7 +183,7 @@ diag 'encryption and signing with combined method' if $ENV{'TEST_VERBOSE'}; is( $parts[0]->{'Top'}, $entity, "it's the same entity" ); } -diag 'encryption and signing with cascading, sign on encrypted' if $ENV{'TEST_VERBOSE'}; +diag 'encryption and signing with cascading, sign on encrypted'; { my $entity = MIME::Entity->build( From => 'rt@example.com', @@ -196,7 +205,7 @@ diag 'encryption and signing with cascading, sign on encrypted' if $ENV{'TEST_VE is( $parts[0]->{'Top'}, $entity, "it's the same entity" ); } -diag 'find signed/encrypted part deep inside' if $ENV{'TEST_VERBOSE'}; +diag 'find signed/encrypted part deep inside'; { my $entity = MIME::Entity->build( From => 'rt@example.com', @@ -219,7 +228,7 @@ diag 'find signed/encrypted part deep inside' if $ENV{'TEST_VERBOSE'}; is( $parts[0]->{'Top'}, $entity->parts(0), "it's the same entity" ); } -diag 'wrong signed/encrypted parts: no protocol' if $ENV{'TEST_VERBOSE'}; +diag 'wrong signed/encrypted parts: no protocol'; { my $entity = MIME::Entity->build( From => 'rt@example.com', @@ -227,15 +236,24 @@ diag 'wrong signed/encrypted parts: no protocol' if $ENV{'TEST_VERBOSE'}; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + + my %res = RT::Crypt::GnuPG::SignEncrypt( + Entity => $entity, + Sign => 0, + ); + ok( !$res{'exit_code'}, 'success' ); $entity->head->mime_attr( 'Content-Type.protocol' => undef ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts; + warning_like { + @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + } qr{Entity is 'multipart/encrypted', but has no protocol defined. Skipped}; + is( scalar @parts, 0, 'no protected parts' ); } -diag 'wrong signed/encrypted parts: not enought parts' if $ENV{'TEST_VERBOSE'}; +diag 'wrong signed/encrypted parts: not enought parts'; { my $entity = MIME::Entity->build( From => 'rt@example.com', @@ -243,15 +261,23 @@ diag 'wrong signed/encrypted parts: not enought parts' if $ENV{'TEST_VERBOSE'}; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + + my %res = RT::Crypt::GnuPG::SignEncrypt( + Entity => $entity, + Sign => 0, + ); + ok( !$res{'exit_code'}, 'success' ); $entity->parts([ $entity->parts(0) ]); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts; + warning_like { + @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + } qr/Encrypted or signed entity must has two subparts. Skipped/; is( scalar @parts, 0, 'no protected parts' ); } -diag 'wrong signed/encrypted parts: wrong proto' if $ENV{'TEST_VERBOSE'}; +diag 'wrong signed/encrypted parts: wrong proto'; { my $entity = MIME::Entity->build( From => 'rt@example.com', @@ -267,7 +293,7 @@ diag 'wrong signed/encrypted parts: wrong proto' if $ENV{'TEST_VERBOSE'}; is( scalar @parts, 0, 'no protected parts' ); } -diag 'wrong signed/encrypted parts: wrong proto' if $ENV{'TEST_VERBOSE'}; +diag 'wrong signed/encrypted parts: wrong proto'; { my $entity = MIME::Entity->build( From => 'rt@example.com', @@ -283,7 +309,7 @@ diag 'wrong signed/encrypted parts: wrong proto' if $ENV{'TEST_VERBOSE'}; is( scalar @parts, 0, 'no protected parts' ); } -diag 'verify inline and in attachment signatures' if $ENV{'TEST_VERBOSE'}; +diag 'verify inline and in attachment signatures'; { open( my $fh, '<', "$homedir/signed_old_style_with_attachment.eml" ) or die $!; my $parser = new MIME::Parser; diff --git a/rt/t/mail/dashboards.t b/rt/t/mail/dashboards.t new file mode 100644 index 000000000..7a7a54ce6 --- /dev/null +++ b/rt/t/mail/dashboards.t @@ -0,0 +1,397 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use RT::Test tests => 187; +use Test::Warn; +use RT::Dashboard::Mailer; + +my ($baseurl, $m) = RT::Test->started_ok; +ok($m->login, 'logged in'); + +sub create_dashboard { + my ($baseurl, $m) = @_; + local $Test::Builder::Level = $Test::Builder::Level + 1; + $m->get_ok($baseurl . '/Dashboards/Modify.html?Create=1'); + $m->form_name('ModifyDashboard'); + $m->field('Name' => 'Testing!'); + $m->click_button(value => 'Create'); + $m->title_is('Modify the dashboard Testing!'); + + $m->follow_link_ok({text => 'Content'}); + $m->title_is('Modify the content of dashboard Testing!'); + + my $form = $m->form_name('Dashboard-Searches-body'); + my @input = $form->find_input('Searches-body-Available'); + my ($dashboards_component) = + map { ( $_->possible_values )[1] } + grep { ( $_->value_names )[1] =~ /Dashboards/ } @input; + $form->value('Searches-body-Available' => $dashboards_component ); + $m->click_button(name => 'add'); + $m->content_contains('Dashboard updated'); + + $m->follow_link_ok({text => 'Show'}); + $m->title_is('Testing! Dashboard'); + $m->content_contains('My dashboards'); + $m->content_like(qr{<a href="/Dashboards/\d+/Testing!">Testing!</a>}); + +} + +sub create_subscription { + my ($baseurl, $m, %fields) = @_; + local $Test::Builder::Level = $Test::Builder::Level + 1; + + # create a subscription + $m->follow_link_ok({text => 'Subscription'}); + $m->title_is('Subscribe to dashboard Testing!'); + $m->form_name('SubscribeDashboard'); + $m->set_fields(%fields); + $m->click_button(name => 'Save'); + $m->content_contains("Subscribed to dashboard Testing!"); +} + +sub get_dash_sub_ids { + my $user = RT::User->new(RT->SystemUser); + $user->Load('root'); + ok($user->Id, 'loaded user'); + my ($subscription) = $user->Attributes->Named('Subscription'); + my $subscription_id = $subscription->Id; + ok($subscription_id, 'loaded subscription'); + my $dashboard_id = $subscription->SubValue('DashboardId'); + ok($dashboard_id, 'got dashboard id'); + + + return ($dashboard_id, $subscription_id); +} + +# first, create and populate a dashboard +create_dashboard($baseurl, $m); + +# now test the mailer + +# without a subscription.. +RT::Dashboard::Mailer->MailDashboards(); + +my @mails = RT::Test->fetch_caught_mails; +is @mails, 0, 'no mail yet'; + +RT::Dashboard::Mailer->MailDashboards( + All => 1, +); + +@mails = RT::Test->fetch_caught_mails; +is @mails, 0, "no mail yet since there's no subscription"; + +create_subscription($baseurl, $m, + Frequency => 'daily', + Hour => '06:00', +); + +my ($dashboard_id, $subscription_id) = get_dash_sub_ids(); + +sub produces_dashboard_mail_ok { # {{{ + my %args = @_; + my $subject = delete $args{Subject}; + + local $Test::Builder::Level = $Test::Builder::Level + 1; + + RT::Dashboard::Mailer->MailDashboards(%args); + + my @mails = RT::Test->fetch_caught_mails; + is @mails, 1, "got a dashboard mail"; + + my $mail = parse_mail( $mails[0] ); + is($mail->head->get('Subject'), $subject); + is($mail->head->get('From'), "root\n"); + is($mail->head->get('X-RT-Dashboard-Id'), "$dashboard_id\n"); + is($mail->head->get('X-RT-Dashboard-Subscription-Id'), "$subscription_id\n"); + + SKIP: { + skip 'Weird MIME failure', 2; + my $body = $mail->stringify_body; + like($body, qr{My dashboards}); + like($body, qr{<a href="http://[^/]+/Dashboards/\d+/Testing!">Testing!</a>}); + }; +} # }}} + +sub produces_no_dashboard_mail_ok { # {{{ + my %args = @_; + my $name = delete $args{Name}; + + local $Test::Builder::Level = $Test::Builder::Level + 1; + + RT::Dashboard::Mailer->MailDashboards(%args); + + @mails = RT::Test->fetch_caught_mails; + is @mails, 0, $name; +} # }}} + +sub delete_dashboard { # {{{ + my $dashboard_id = shift; + # delete the dashboard and make sure we get exactly one subscription failure + # notice + my $dashboard = RT::Dashboard->new(RT::CurrentUser->new('root')); + my ($ok, $msg) = $dashboard->LoadById($dashboard_id); + ok($ok, $msg); + + ($ok, $msg) = $dashboard->Delete; + ok($ok, $msg); +} # }}} + +sub delete_subscriptions { # {{{ + my $subscription_id = shift; + # delete the dashboard and make sure we get exactly one subscription failure + # notice + my $user = RT::User->new(RT->SystemUser); + $user->Load('root'); + for my $subscription ($user->Attributes->Named('Subscription')) { + $subscription->Delete; + } +} # }}} + +my $good_time = 1290423660; # 6:01 EST on a monday +my $bad_time = 1290427260; # 7:01 EST on a monday + +my $expected_subject = "[example.com] Daily Dashboard: Testing!\n"; + +produces_dashboard_mail_ok( + Time => $good_time, + Subject => $expected_subject, +); + +produces_dashboard_mail_ok( + All => 1, + Subject => $expected_subject, +); + +produces_dashboard_mail_ok( + All => 1, + Time => $good_time, + Subject => $expected_subject, +); + +produces_dashboard_mail_ok( + All => 1, + Time => $bad_time, + Subject => $expected_subject, +); + + +produces_no_dashboard_mail_ok( + Name => "no dashboard mail it's a dry run", + All => 1, + DryRun => 1, +); + +produces_no_dashboard_mail_ok( + Name => "no dashboard mail it's a dry run", + Time => $good_time, + DryRun => 1, +); + +produces_no_dashboard_mail_ok( + Name => "no mail because it's the wrong time", + Time => $bad_time, +); + +@mails = RT::Test->fetch_caught_mails; +is(@mails, 0, "no mail leftover"); + + +$m->no_warnings_ok; +RT::Test->stop_server; +RT->Config->Set('DashboardSubject' => 'a %s b %s c'); +RT->Config->Set('DashboardAddress' => 'dashboard@example.com'); +RT->Config->Set('EmailDashboardRemove' => (qr/My dashboards/, "Testing!")); +($baseurl, $m) = RT::Test->started_ok; + +RT::Dashboard::Mailer->MailDashboards(All => 1); +@mails = RT::Test->fetch_caught_mails; +is(@mails, 1, "one mail"); +my $mail = parse_mail($mails[0]); +is($mail->head->get('Subject'), "[example.com] a Daily b Testing! c\n"); +is($mail->head->get('From'), "dashboard\@example.com\n"); +is($mail->head->get('X-RT-Dashboard-Id'), "$dashboard_id\n"); +is($mail->head->get('X-RT-Dashboard-Subscription-Id'), "$subscription_id\n"); + +SKIP: { + skip 'Weird MIME failure', 2; + my $body = $mail->stringify_body; + unlike($body, qr{My dashboards}); + unlike($body, qr{Testing!}); +}; + +delete_dashboard($dashboard_id); + +warning_like { + RT::Dashboard::Mailer->MailDashboards(All => 1); +} qr/Unable to load dashboard $dashboard_id of subscription $subscription_id for user root/; + +@mails = RT::Test->fetch_caught_mails; +is(@mails, 1, "one mail for subscription failure"); +$mail = parse_mail($mails[0]); +is($mail->head->get('Subject'), "[example.com] Missing dashboard!\n"); +is($mail->head->get('From'), "dashboard\@example.com\n"); +is($mail->head->get('X-RT-Dashboard-Id'), "$dashboard_id\n"); +is($mail->head->get('X-RT-Dashboard-Subscription-Id'), "$subscription_id\n"); + +RT::Dashboard::Mailer->MailDashboards(All => 1); +@mails = RT::Test->fetch_caught_mails; +is(@mails, 0, "no mail because the subscription notice happens only once"); + +RT::Test->stop_server; +RT::Test->clean_caught_mails; +RT->Config->Set('EmailDashboardRemove' => ()); +RT->Config->Set('DashboardAddress' => 'root'); +($baseurl, $m) = RT::Test->started_ok; +$m->login; +create_dashboard($baseurl, $m); +create_subscription($baseurl, $m, + Frequency => 'weekly', + Hour => '06:00', +); + +($dashboard_id, $subscription_id) = get_dash_sub_ids(); + +# bump $bad_time to Tuesday +$bad_time = $good_time + 86400; + +produces_dashboard_mail_ok( + Time => $good_time, + Subject => "[example.com] a Weekly b Testing! c\n", +); + +produces_no_dashboard_mail_ok( + Name => "no mail because it's the wrong time", + Time => $bad_time, +); + +@mails = RT::Test->fetch_caught_mails; +is(@mails, 0, "no mail leftover"); + +$m->no_warnings_ok; +RT::Test->stop_server; +RT->Config->Set('DashboardSubject' => 'a %s b %s c'); +RT->Config->Set('DashboardAddress' => 'dashboard@example.com'); +RT->Config->Set('EmailDashboardRemove' => (qr/My dashboards/, "Testing!")); +($baseurl, $m) = RT::Test->started_ok; + +delete_dashboard($dashboard_id); +delete_subscriptions(); + +RT::Test->clean_caught_mails; + +RT::Test->stop_server; + +RT->Config->Set('EmailDashboardRemove' => ()); +RT->Config->Set('DashboardAddress' => 'root'); +($baseurl, $m) = RT::Test->started_ok; +$m->login; +create_dashboard($baseurl, $m); +create_subscription($baseurl, $m, + Frequency => 'm-f', + Hour => '06:00', +); + +($dashboard_id, $subscription_id) = get_dash_sub_ids(); + +# bump $bad_time back to Sunday +$bad_time = $good_time - 86400; + +produces_dashboard_mail_ok( + Time => $good_time, + Subject => "[example.com] a Weekday b Testing! c\n", +); + +produces_no_dashboard_mail_ok( + Name => "no mail because it's the wrong time", + Time => $bad_time, +); + +produces_no_dashboard_mail_ok( + Name => "no mail because it's the wrong time", + Time => $bad_time - 86400, # saturday +); + +produces_dashboard_mail_ok( + Time => $bad_time - 86400 * 2, # friday + Subject => "[example.com] a Weekday b Testing! c\n", +); + + +@mails = RT::Test->fetch_caught_mails; +is(@mails, 0, "no mail leftover"); + +$m->no_warnings_ok; +RT::Test->stop_server; +RT->Config->Set('DashboardSubject' => 'a %s b %s c'); +RT->Config->Set('DashboardAddress' => 'dashboard@example.com'); +RT->Config->Set('EmailDashboardRemove' => (qr/My dashboards/, "Testing!")); +($baseurl, $m) = RT::Test->started_ok; + +delete_dashboard($dashboard_id); +delete_subscriptions(); + +RT::Test->clean_caught_mails; + +RT::Test->stop_server; + +RT->Config->Set('EmailDashboardRemove' => ()); +RT->Config->Set('DashboardAddress' => 'root'); +($baseurl, $m) = RT::Test->started_ok; +$m->login; +create_dashboard($baseurl, $m); +create_subscription($baseurl, $m, + Frequency => 'monthly', + Hour => '06:00', +); + +($dashboard_id, $subscription_id) = get_dash_sub_ids(); + +$good_time = 1291201200; # dec 1 +$bad_time = $good_time - 86400; # day before (i.e. different month) + +produces_dashboard_mail_ok( + Time => $good_time, + Subject => "[example.com] a Monthly b Testing! c\n", +); + +produces_no_dashboard_mail_ok( + Name => "no mail because it's the wrong time", + Time => $bad_time, +); + + +@mails = RT::Test->fetch_caught_mails; +is(@mails, 0, "no mail leftover"); + +$m->no_warnings_ok; +RT::Test->stop_server; +RT->Config->Set('DashboardSubject' => 'a %s b %s c'); +RT->Config->Set('DashboardAddress' => 'dashboard@example.com'); +RT->Config->Set('EmailDashboardRemove' => (qr/My dashboards/, "Testing!")); +($baseurl, $m) = RT::Test->started_ok; + +delete_dashboard($dashboard_id); +delete_subscriptions(); + +RT::Test->clean_caught_mails; + +RT::Test->stop_server; + +RT->Config->Set('EmailDashboardRemove' => ()); +RT->Config->Set('DashboardAddress' => 'root'); +($baseurl, $m) = RT::Test->started_ok; +$m->login; +create_dashboard($baseurl, $m); +create_subscription($baseurl, $m, + Frequency => 'never', +); + +($dashboard_id, $subscription_id) = get_dash_sub_ids(); + +produces_no_dashboard_mail_ok( + Name => "mail should never get sent", + Time => $bad_time, +); + diff --git a/rt/t/mail/digest-attributes.t b/rt/t/mail/digest-attributes.t new file mode 100644 index 000000000..5b4560621 --- /dev/null +++ b/rt/t/mail/digest-attributes.t @@ -0,0 +1,168 @@ +#!/usr/bin/perl -w + +use warnings; +use strict; +use RT; +use RT::Test tests => 31; +my @users = qw/ emailnormal@example.com emaildaily@example.com emailweekly@example.com emailsusp@example.com /; + +my( $ret, $msg ); +my $user_n = RT::User->new( RT->SystemUser ); +( $ret, $msg ) = $user_n->LoadOrCreateByEmail( $users[0] ); +ok( $ret, "user with default email prefs created: $msg" ); +$user_n->SetPrivileged( 1 ); + +my $user_d = RT::User->new( RT->SystemUser ); +( $ret, $msg ) = $user_d->LoadOrCreateByEmail( $users[1] ); +ok( $ret, "user with daily digest email prefs created: $msg" ); +# Set a username & password for testing the interface. +$user_d->SetPrivileged( 1 ); +$user_d->SetPreferences($RT::System => { %{ $user_d->Preferences( $RT::System ) || {}}, EmailFrequency => 'Daily digest'}); + + + +my $user_w = RT::User->new( RT->SystemUser ); +( $ret, $msg ) = $user_w->LoadOrCreateByEmail( $users[2] ); +ok( $ret, "user with weekly digest email prefs created: $msg" ); +$user_w->SetPrivileged( 1 ); +$user_w->SetPreferences($RT::System => { %{ $user_w->Preferences( $RT::System ) || {}}, EmailFrequency => 'Weekly digest'}); + +my $user_s = RT::User->new( RT->SystemUser ); +( $ret, $msg ) = $user_s->LoadOrCreateByEmail( $users[3] ); +ok( $ret, "user with suspended email prefs created: $msg" ); +$user_s->SetPreferences($RT::System => { %{ $user_s->Preferences( $RT::System ) || {}}, EmailFrequency => 'Suspended'}); +$user_s->SetPrivileged( 1 ); + + +is(RT::Config->Get('EmailFrequency' => $user_s), 'Suspended'); + +# Make a testing queue for ourselves. +my $testq = RT::Queue->new( RT->SystemUser ); +if( $testq->ValidateName( 'EmailDigest-testqueue' ) ) { + ( $ret, $msg ) = $testq->Create( Name => 'EmailDigest-testqueue' ); + ok( $ret, "Our test queue is created: $msg" ); +} else { + $testq->Load( 'EmailDigest-testqueue' ); + ok( $testq->id, "Our test queue is loaded" ); +} + +# Allow anyone to open a ticket on the test queue. +my $everyone = RT::Group->new( RT->SystemUser ); +( $ret, $msg ) = $everyone->LoadSystemInternalGroup( 'Everyone' ); +ok( $ret, "Loaded 'everyone' group: $msg" ); + +( $ret, $msg ) = $everyone->PrincipalObj->GrantRight( Right => 'CreateTicket', + Object => $testq ); +ok( $ret || $msg =~ /already has/, "Granted everyone CreateTicket on testq: $msg" ); + +# Make user_d an admincc for the queue. +( $ret, $msg ) = $user_d->PrincipalObj->GrantRight( Right => 'AdminQueue', + Object => $testq ); +ok( $ret || $msg =~ /already has/, "Granted dduser AdminQueue on testq: $msg" ); +( $ret, $msg ) = $testq->AddWatcher( Type => 'AdminCc', + PrincipalId => $user_d->PrincipalObj->id ); +ok( $ret || $msg =~ /already/, "dduser added as a queue watcher: $msg" ); + +# Give the others queue rights. +( $ret, $msg ) = $user_n->PrincipalObj->GrantRight( Right => 'AdminQueue', + Object => $testq ); +ok( $ret || $msg =~ /already has/, "Granted emailnormal right on testq: $msg" ); +( $ret, $msg ) = $user_w->PrincipalObj->GrantRight( Right => 'AdminQueue', + Object => $testq ); +ok( $ret || $msg =~ /already has/, "Granted emailweekly right on testq: $msg" ); +( $ret, $msg ) = $user_s->PrincipalObj->GrantRight( Right => 'AdminQueue', + Object => $testq ); +ok( $ret || $msg =~ /already has/, "Granted emailsusp right on testq: $msg" ); + +# Create a ticket with To: Cc: Bcc: fields using our four users. +my $id; +my $ticket = RT::Ticket->new( RT->SystemUser ); +( $id, $ret, $msg ) = $ticket->Create( Queue => $testq->Name, + Requestor => [ $user_w->Name ], + Subject => 'Test ticket for RT::Extension::EmailDigest', + ); +ok( $ret, "Ticket $id created: $msg" ); + +# Make the other users ticket watchers. +( $ret, $msg ) = $ticket->AddWatcher( Type => 'Cc', + PrincipalId => $user_n->PrincipalObj->id ); +ok( $ret, "Added user_n as a ticket watcher: $msg" ); +( $ret, $msg ) = $ticket->AddWatcher( Type => 'Cc', + PrincipalId => $user_s->PrincipalObj->id ); +ok( $ret, "Added user_s as a ticket watcher: $msg" ); + +my $obj; +($id, $msg, $obj ) = $ticket->Correspond( + Content => "This is a ticket response for CC action" ); +ok( $ret, "Transaction created: $msg" ); + +# Get the deferred notifications that should result. Should be two for +# email daily, and one apiece for emailweekly and emailsusp. +my @notifications; + +my $txns = RT::Transactions->new( RT->SystemUser ); +$txns->LimitToTicket( $ticket->id ); +my( $c_daily, $c_weekly, $c_susp ) = ( 0, 0, 0 ); +while( my $txn = $txns->Next ) { + my @daily_rcpt = $txn->DeferredRecipients( 'daily' ); + my @weekly_rcpt = $txn->DeferredRecipients('weekly' ); + my @susp_rcpt = $txn->DeferredRecipients( 'susp' ); + + $c_daily++ if @daily_rcpt; + $c_weekly++ if @weekly_rcpt; + $c_susp++ if @susp_rcpt; + + # If the transaction has content... + if( $txn->ContentObj ) { + # ...none of the deferred folk should be in the header. + my $headerstr = $txn->ContentObj->Headers; + foreach my $rcpt( @daily_rcpt, @weekly_rcpt, @susp_rcpt ) { + ok( $headerstr !~ /$rcpt/, "Deferred recipient $rcpt not found in header" ); + } + } +} + +# Finally, check to see that we got the correct number of each sort of +# deferred recipient. +is( $c_daily, 2, "correct number of daily-sent messages" ); +is( $c_weekly, 2, "correct number of weekly-sent messages" ); +is( $c_susp, 1, "correct number of suspended messages" ); + + + + + +# Now let's actually run the daily and weekly digest tool to make sure we generate those + +# the first time get the content +email_digest_like( '--mode daily --print', qr/in the last day/ ); +# The second time run it for real so we make sure that we get RT to mark the txn as sent +email_digest_like( '--mode daily', qr/maildaily\@/ ); +# now we should have nothing to do, so no content. +email_digest_like( '--mode daily --print', '' ); + +# the first time get the content +email_digest_like( '--mode weekly --print', qr/in the last seven days/ ); +# The second time run it for real so we make sure that we get RT to mark the txn as sent +email_digest_like( '--mode weekly', qr/mailweekly\@/ ); +# now we should have nothing to do, so no content. +email_digest_like( '--mode weekly --print', '' ); + +sub email_digest_like { + my $arg = shift; + my $pattern = shift; + + local $Test::Builder::Level = $Test::Builder::Level + 1; + + my $perl = $^X . ' ' . join ' ', map { "-I$_" } @INC; + open my $digester, "-|", "$perl $RT::SbinPath/rt-email-digest $arg"; + my @results = <$digester>; + my $content = join '', @results; + if ( ref $pattern && ref $pattern eq 'Regexp' ) { + like($content, $pattern); + } + else { + is( $content, $pattern ); + } + close $digester; +} diff --git a/rt/t/mail/disposition-outgoing.t b/rt/t/mail/disposition-outgoing.t new file mode 100644 index 000000000..06295a09c --- /dev/null +++ b/rt/t/mail/disposition-outgoing.t @@ -0,0 +1,69 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok $queue->id, 'loaded queue'; + +my ($ok, $msg) = $queue->AddWatcher( + Type => 'AdminCc', + Email => 'test@example.com', +); +ok $ok, $msg; + +my $mail = <<'.'; +From: root@localhost +Subject: I like inline dispositions and I cannot lie +Content-type: multipart/related; boundary="foo" + +--foo +Content-type: text/plain; charset="UTF-8" + +ho hum just some text + +--foo +Content-type: text/x-patch; name="filename.patch" +Content-disposition: inline; filename="filename.patch" + +a fake patch + +--foo +. + +# inline +{ + my $rt = send_and_receive($mail); + like $rt, qr/Content-Disposition:\s*inline.+?filename\.patch/is, 'found inline disposition'; +} + +# attachment +{ + $mail =~ s/(?<=Content-disposition: )inline/attachment/i; + + my $rt = send_and_receive($mail); + like $rt, qr/Content-Disposition:\s*attachment.+?filename\.patch/is, 'found attachment disposition'; +} + +# no disposition +{ + $mail =~ s/^Content-disposition: .+?\n(?=\n)//ism; + + my $rt = send_and_receive($mail); + like $rt, qr/Content-Disposition:\s*inline.+?filename\.patch/is, 'found default (inline) disposition'; +} + +sub send_and_receive { + my $mail = shift; + my ($stat, $id) = RT::Test->send_via_mailgate($mail); + is( $stat >> 8, 0, "The mail gateway exited normally" ); + ok( $id, "created ticket" ); + + my @mails = RT::Test->fetch_caught_mails; + is @mails, 2, "got 2 outgoing emails"; + + # first is autoreply + pop @mails; +} + +done_testing; diff --git a/rt/t/mail/extractsubjecttag.t b/rt/t/mail/extractsubjecttag.t index fcaba8c98..e76da6f82 100644 --- a/rt/t/mail/extractsubjecttag.t +++ b/rt/t/mail/extractsubjecttag.t @@ -3,11 +3,7 @@ use strict; use warnings; use utf8; -use RT::Test tests => 14; - - -my ($baseurl, $m) = RT::Test->started_ok; -RT::Test->set_mail_catcher; +use RT::Test tests => 13; my $queue = RT::Test->load_or_create_queue( Name => 'Regression', @@ -17,7 +13,7 @@ my $queue = RT::Test->load_or_create_queue( my $subject_tag = 'Windows/Servers-Desktops'; ok $queue && $queue->id, 'loaded or created queue'; -diag "Set Subject Tag" if $ENV{'TEST_VERBOSE'}; +diag "Set Subject Tag"; { is(RT->System->SubjectTag($queue), undef, 'No Subject Tag yet'); my ($status, $msg) = $queue->SetSubjectTag( $subject_tag ); @@ -25,8 +21,8 @@ diag "Set Subject Tag" if $ENV{'TEST_VERBOSE'}; is(RT->System->SubjectTag($queue), $subject_tag, "Set Subject Tag to $subject_tag"); } -my $original_ticket = RT::Ticket->new( $RT::SystemUser ); -diag "Create a ticket and make sure it has the subject tag" if $ENV{'TEST_VERBOSE'}; +my $original_ticket = RT::Ticket->new( RT->SystemUser ); +diag "Create a ticket and make sure it has the subject tag"; { $original_ticket->Create( Queue => $queue->id, @@ -47,7 +43,7 @@ diag "Create a ticket and make sure it has the subject tag" if $ENV{'TEST_VERBOS } -diag "Test that a reply with a Subject Tag doesn't change the subject" if $ENV{'TEST_VERBOSE'}; +diag "Test that a reply with a Subject Tag doesn't change the subject"; { my $ticketid = $original_ticket->Id; my $text = <<EOF; @@ -61,13 +57,13 @@ EOF is ($status >> 8, 0, "The mail gateway exited normally"); is ($id, $ticketid, "Replied to ticket $id correctly"); - my $freshticket = RT::Ticket->new( $RT::SystemUser ); + my $freshticket = RT::Ticket->new( RT->SystemUser ); $freshticket->LoadById($id); is($original_ticket->Subject,$freshticket->Subject,'Stripped Queue Subject Tag correctly'); } -diag "Test that a reply with another RT's subject tag changes the subject" if $ENV{'TEST_VERBOSE'}; +diag "Test that a reply with another RT's subject tag changes the subject"; { my $ticketid = $original_ticket->Id; my $text = <<EOF; @@ -77,14 +73,14 @@ Subject: [$subject_tag #$ticketid] [remote-rt-system #79] test reply with subject tag and remote rt subject tag EOF - diag($text); my ($status, $id) = RT::Test->send_via_mailgate($text, queue => $queue->Name); is ($status >> 8, 0, "The mail gateway exited normally"); is ($id, $ticketid, "Replied to ticket $id correctly"); - my $freshticket = RT::Ticket->new( $RT::SystemUser ); + my $freshticket = RT::Ticket->new( RT->SystemUser ); $freshticket->LoadById($id); like($freshticket->Subject,qr/\[remote-rt-system #79\]/,"Kept remote rt's subject tag"); unlike($freshticket->Subject,qr/\[\Q$subject_tag\E #$ticketid\]/,'Stripped Queue Subject Tag correctly'); } + diff --git a/rt/t/mail/fake-sendmail b/rt/t/mail/fake-sendmail new file mode 100644 index 000000000..44c237746 --- /dev/null +++ b/rt/t/mail/fake-sendmail @@ -0,0 +1,27 @@ +#!/usr/bin/env perl + +# captures command line arguments so you can validate +# what is being generated in sendmailpipe + +use strict; +use warnings; + +die "No \$RT_MAILLOGFILE set in environment" + unless $ENV{RT_MAILLOGFILE}; +open LOG, ">", $ENV{RT_MAILLOGFILE} + or die "Can't write to $ENV{RT_MAILLOGFILE}: $!"; + +my $needs_newline; +for (@ARGV) { + if (/^-/) { + print LOG "\n" if $needs_newline++; + print LOG $_; + } else { + print LOG " $_"; + } +} +print LOG "\n"; + +1 while $_ = <STDIN>; + +exit 0; diff --git a/rt/t/mail/gateway.t b/rt/t/mail/gateway.t index d57b063a2..9f0e669a3 100644 --- a/rt/t/mail/gateway.t +++ b/rt/t/mail/gateway.t @@ -57,7 +57,7 @@ use strict; use warnings; -use RT::Test config => 'Set( $UnsafeEmailCommands, 1);', tests => 159; +use RT::Test config => 'Set( $UnsafeEmailCommands, 1);', tests => 221, actual_server => 1; my ($baseurl, $m) = RT::Test->started_ok; use RT::Tickets; @@ -70,7 +70,7 @@ use LWP::UserAgent; my $url = $m->rt_base_url; -diag "Make sure that when we call the mailgate without URL, it fails" if $ENV{'TEST_VERBOSE'}; +diag "Make sure that when we call the mailgate without URL, it fails"; { my $text = <<EOF; From: root\@localhost @@ -79,12 +79,13 @@ Subject: This is a test of new ticket creation Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text, url => undef); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text, url => undef); is ($status >> 8, 1, "The mail gateway exited with a failure"); ok (!$id, "No ticket id") or diag "by mistake ticket #$id"; + $m->no_warnings_ok; } -diag "Make sure that when we call the mailgate with wrong URL, it tempfails" if $ENV{'TEST_VERBOSE'}; +diag "Make sure that when we call the mailgate with wrong URL, it tempfails"; { my $text = <<EOF; From: root\@localhost @@ -93,15 +94,16 @@ Subject: This is a test of new ticket creation Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text, url => 'http://this.test.for.non-connection.is.expected.to.generate.an.error'); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text, url => 'http://this.test.for.non-connection.is.expected.to.generate.an.error'); is ($status >> 8, 75, "The mail gateway exited with a failure"); ok (!$id, "No ticket id"); + $m->no_warnings_ok; } my $everyone_group; -diag "revoke rights tests depend on" if $ENV{'TEST_VERBOSE'}; +diag "revoke rights tests depend on"; { - $everyone_group = RT::Group->new( $RT::SystemUser ); + $everyone_group = RT::Group->new( RT->SystemUser ); $everyone_group->LoadSystemInternalGroup( 'Everyone' ); ok ($everyone_group->Id, "Found group 'everyone'"); @@ -110,7 +112,7 @@ diag "revoke rights tests depend on" if $ENV{'TEST_VERBOSE'}; } } -diag "Test new ticket creation by root who is privileged and superuser" if $ENV{'TEST_VERBOSE'}; +diag "Test new ticket creation by root who is privileged and superuser"; { my $text = <<EOF; From: root\@localhost @@ -121,7 +123,7 @@ Blah! Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text); is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "Created ticket"); @@ -129,9 +131,10 @@ EOF isa_ok ($tick, 'RT::Ticket'); is ($tick->Id, $id, "correct ticket id"); is ($tick->Subject , 'This is a test of new ticket creation', "Created the ticket"); + $m->no_warnings_ok; } -diag "Test the 'X-RT-Mail-Extension' field in the header of a ticket" if $ENV{'TEST_VERBOSE'}; +diag "Test the 'X-RT-Mail-Extension' field in the header of a ticket"; { my $text = <<EOF; From: root\@localhost @@ -141,7 +144,7 @@ Blah! Foob! EOF local $ENV{'EXTENSION'} = "bad value with\nnewlines\n"; - my ($status, $id) = RT::Test->send_via_mailgate($text); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text); is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "Created ticket #$id"); @@ -165,9 +168,10 @@ EOF "bad value with newlines", 'header is in place, without trailing newline char' ); + $m->no_warnings_ok; } -diag "Make sure that not standard --extension is passed" if $ENV{'TEST_VERBOSE'}; +diag "Make sure that not standard --extension is passed"; { my $text = <<EOF; From: root\@localhost @@ -176,7 +180,7 @@ Subject: This is a test of new ticket creation Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text, extension => 'some-extension-arg' ); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text, extension => 'some-extension-arg' ); is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "Created ticket #$id"); @@ -198,9 +202,10 @@ EOF 'some-extension-arg', 'header is in place' ); + $m->no_warnings_ok; } -diag "Test new ticket creation without --action argument" if $ENV{'TEST_VERBOSE'}; +diag "Test new ticket creation without --action argument"; { my $text = <<EOF; From: root\@localhost @@ -210,7 +215,7 @@ Subject: using mailgate without --action arg Blah! Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text, extension => 'some-extension-arg' ); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text, extension => 'some-extension-arg' ); is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "Created ticket #$id"); @@ -218,9 +223,10 @@ EOF isa_ok ($tick, 'RT::Ticket'); is ($tick->Id, $id, "correct ticket id"); is ($tick->Subject, 'using mailgate without --action arg', "using mailgate without --action arg"); + $m->no_warnings_ok; } -diag "This is a test of new ticket creation as an unknown user" if $ENV{'TEST_VERBOSE'}; +diag "This is a test of new ticket creation as an unknown user"; { my $text = <<EOF; From: doesnotexist\@@{[RT->Config->Get('rtname')]} @@ -230,7 +236,7 @@ Subject: This is a test of new ticket creation as an unknown user Blah! Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text); is ($status >> 8, 0, "The mail gateway exited normally"); ok (!$id, "no ticket created"); @@ -239,12 +245,19 @@ EOF ok ($tick->Id, "found ticket ".$tick->Id); isnt ($tick->Subject , 'This is a test of new ticket creation as an unknown user', "failed to create the new ticket from an unprivileged account"); - my $u = RT::User->new($RT::SystemUser); + my $u = RT::User->new(RT->SystemUser); $u->Load("doesnotexist\@@{[RT->Config->Get('rtname')]}"); ok( !$u->Id, "user does not exist and was not created by failed ticket submission"); + + $m->next_warning_like(qr/RT's configuration does not allow\s+for the creation of a new user for this email/); + $m->next_warning_like(qr/RT could not load a valid user/); + TODO: { + local $TODO = "we're a bit noisy for this warning case"; + $m->no_leftover_warnings_ok; + } } -diag "grant everybody with CreateTicket right" if $ENV{'TEST_VERBOSE'}; +diag "grant everybody with CreateTicket right"; { ok( RT::Test->set_rights( { Principal => $everyone_group->PrincipalObj, @@ -254,7 +267,7 @@ diag "grant everybody with CreateTicket right" if $ENV{'TEST_VERBOSE'}; } my $ticket_id; -diag "now everybody can create tickets. can a random unkown user create tickets?" if $ENV{'TEST_VERBOSE'}; +diag "now everybody can create tickets. can a random unkown user create tickets?"; { my $text = <<EOF; From: doesnotexist\@@{[RT->Config->Get('rtname')]} @@ -264,7 +277,7 @@ Subject: This is a test of new ticket creation as an unknown user Blah! Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text); is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "ticket created"); @@ -274,13 +287,14 @@ EOF is ($tick->Id, $id, "correct ticket id"); is ($tick->Subject , 'This is a test of new ticket creation as an unknown user', "failed to create the new ticket from an unprivileged account"); - my $u = RT::User->new( $RT::SystemUser ); + my $u = RT::User->new( RT->SystemUser ); $u->Load( "doesnotexist\@@{[RT->Config->Get('rtname')]}" ); ok ($u->Id, "user does not exist and was created by ticket submission"); $ticket_id = $id; + $m->no_warnings_ok; } -diag "can another random reply to a ticket without being granted privs? answer should be no." if $ENV{'TEST_VERBOSE'}; +diag "can another random reply to a ticket without being granted privs? answer should be no."; { my $text = <<EOF; From: doesnotexist-2\@@{[RT->Config->Get('rtname')]} @@ -290,16 +304,21 @@ Subject: [@{[RT->Config->Get('rtname')]} #$ticket_id] This is a test of a reply Blah! (Should not work.) Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text); is ($status >> 8, 0, "The mail gateway exited normally"); ok (!$id, "no way to reply to the ticket"); - my $u = RT::User->new($RT::SystemUser); + my $u = RT::User->new(RT->SystemUser); $u->Load('doesnotexist-2@'.RT->Config->Get('rtname')); ok( !$u->Id, " user does not exist and was not created by ticket correspondence submission"); + $m->next_warning_like(qr/RT's configuration does not allow\s+for the creation of a new user for this email \(doesnotexist-2\@example\.com\)/); + TODO: { + local $TODO = "we're a bit noisy for this warning case"; + $m->no_leftover_warnings_ok; + } } -diag "grant everyone 'ReplyToTicket' right" if $ENV{'TEST_VERBOSE'}; +diag "grant everyone 'ReplyToTicket' right"; { ok( RT::Test->set_rights( { Principal => $everyone_group->PrincipalObj, @@ -308,7 +327,7 @@ diag "grant everyone 'ReplyToTicket' right" if $ENV{'TEST_VERBOSE'}; ), "Granted everybody the right to reply to tickets" ); } -diag "can another random reply to a ticket after being granted privs? answer should be yes" if $ENV{'TEST_VERBOSE'}; +diag "can another random reply to a ticket after being granted privs? answer should be yes"; { my $text = <<EOF; From: doesnotexist-2\@@{[RT->Config->Get('rtname')]} @@ -318,16 +337,17 @@ Subject: [@{[RT->Config->Get('rtname')]} #$ticket_id] This is a test of a reply Blah! Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text); is ($status >> 8, 0, "The mail gateway exited normally"); is ($id, $ticket_id, "replied to the ticket"); - my $u = RT::User->new($RT::SystemUser); + my $u = RT::User->new(RT->SystemUser); $u->Load('doesnotexist-2@'.RT->Config->Get('rtname')); ok ($u->Id, "user exists and was created by ticket correspondence submission"); + $m->no_warnings_ok; } -diag "add a reply to the ticket using '--extension ticket' feature" if $ENV{'TEST_VERBOSE'}; +diag "add a reply to the ticket using '--extension ticket' feature"; { my $text = <<EOF; From: doesnotexist-2\@@{[RT->Config->Get('rtname')]} @@ -338,7 +358,7 @@ Blah! Foob! EOF local $ENV{'EXTENSION'} = $ticket_id; - my ($status, $id) = RT::Test->send_via_mailgate($text, extension => 'ticket'); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text, extension => 'ticket'); is ($status >> 8, 0, "The mail gateway exited normally"); is ($id, $ticket_id, "replied to the ticket"); @@ -357,9 +377,10 @@ EOF my $attachment = $txn->Attachments->First; isa_ok ($attachment, 'RT::Attachment'); is ($attachment->GetHeader('X-RT-Mail-Extension'), $id, 'header is in place'); + $m->no_warnings_ok; } -diag "can another random comment on a ticket without being granted privs? answer should be no" if $ENV{'TEST_VERBOSE'}; +diag "can another random comment on a ticket without being granted privs? answer should be no"; { my $text = <<EOF; From: doesnotexist-3\@@{[RT->Config->Get('rtname')]} @@ -369,17 +390,22 @@ Subject: [@{[RT->Config->Get('rtname')]} #$ticket_id] This is a test of a commen Blah! (Should not work.) Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text, action => 'comment'); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text, action => 'comment'); is ($status >> 8, 0, "The mail gateway exited normally"); ok (!$id, "no way to comment on the ticket"); - my $u = RT::User->new($RT::SystemUser); + my $u = RT::User->new(RT->SystemUser); $u->Load('doesnotexist-3@'.RT->Config->Get('rtname')); ok( !$u->Id, " user does not exist and was not created by ticket comment submission"); + $m->next_warning_like(qr/RT's configuration does not allow\s+for the creation of a new user for this email \(doesnotexist-3\@example\.com\)/); + TODO: { + local $TODO = "we're a bit noisy for this warning case"; + $m->no_leftover_warnings_ok; + } } -diag "grant everyone 'CommentOnTicket' right" if $ENV{'TEST_VERBOSE'}; +diag "grant everyone 'CommentOnTicket' right"; { ok( RT::Test->set_rights( { Principal => $everyone_group->PrincipalObj, @@ -388,7 +414,7 @@ diag "grant everyone 'CommentOnTicket' right" if $ENV{'TEST_VERBOSE'}; ), "Granted everybody the right to comment on tickets"); } -diag "can another random reply to a ticket after being granted privs? answer should be yes" if $ENV{'TEST_VERBOSE'}; +diag "can another random reply to a ticket after being granted privs? answer should be yes"; { my $text = <<EOF; From: doesnotexist-3\@@{[RT->Config->Get('rtname')]} @@ -398,16 +424,17 @@ Subject: [@{[RT->Config->Get('rtname')]} #$ticket_id] This is a test of a commen Blah! Foob! EOF - my ($status, $id) = RT::Test->send_via_mailgate($text, action => 'comment'); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text, action => 'comment'); is ($status >> 8, 0, "The mail gateway exited normally"); is ($id, $ticket_id, "replied to the ticket"); - my $u = RT::User->new($RT::SystemUser); + my $u = RT::User->new(RT->SystemUser); $u->Load('doesnotexist-3@'.RT->Config->Get('rtname')); ok ($u->Id, " user exists and was created by ticket comment submission"); + $m->no_warnings_ok; } -diag "add comment to the ticket using '--extension action' feature" if $ENV{'TEST_VERBOSE'}; +diag "add comment to the ticket using '--extension action' feature"; { my $text = <<EOF; From: doesnotexist-3\@@{[RT->Config->Get('rtname')]} @@ -418,7 +445,7 @@ Blah! Foob! EOF local $ENV{'EXTENSION'} = 'comment'; - my ($status, $id) = RT::Test->send_via_mailgate($text, extension => 'action'); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text, extension => 'action'); is ($status >> 8, 0, "The mail gateway exited normally"); is ($id, $ticket_id, "added comment to the ticket"); @@ -442,12 +469,13 @@ EOF my $attachment = $txn->Attachments->First; isa_ok ($attachment, 'RT::Attachment'); is ($attachment->GetHeader('X-RT-Mail-Extension'), 'comment', 'header is in place'); + $m->no_warnings_ok; } -diag "Testing preservation of binary attachments" if $ENV{'TEST_VERBOSE'}; +diag "Testing preservation of binary attachments"; { # Get a binary blob (Best Practical logo) - my $LOGO_FILE = $RT::MasonComponentRoot .'/NoAuth/images/bplogo.gif'; + my $LOGO_FILE = $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png'; # Create a mime entity with an attachment my $entity = MIME::Entity->build( @@ -459,11 +487,11 @@ diag "Testing preservation of binary attachments" if $ENV{'TEST_VERBOSE'}; $entity->attach( Path => $LOGO_FILE, - Type => 'image/gif', + Type => 'image/png', Encoding => 'base64', ); # Create a ticket with a binary attachment - my ($status, $id) = RT::Test->send_via_mailgate($entity); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($entity); is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "created ticket"); @@ -475,11 +503,11 @@ diag "Testing preservation of binary attachments" if $ENV{'TEST_VERBOSE'}; my $file = `cat $LOGO_FILE`; ok ($file, "Read in the logo image"); - diag "for the raw file the md5 hex is ". Digest::MD5::md5_hex($file) if $ENV{'TEST_VERBOSE'}; + diag "for the raw file the md5 hex is ". Digest::MD5::md5_hex($file); # Verify that the binary attachment is valid in the database - my $attachments = RT::Attachments->new($RT::SystemUser); - $attachments->Limit(FIELD => 'ContentType', VALUE => 'image/gif'); + my $attachments = RT::Attachments->new(RT->SystemUser); + $attachments->Limit(FIELD => 'ContentType', VALUE => 'image/png'); my $txn_alias = $attachments->Join( ALIAS1 => 'main', FIELD1 => 'TransactionId', @@ -488,25 +516,27 @@ diag "Testing preservation of binary attachments" if $ENV{'TEST_VERBOSE'}; ); $attachments->Limit( ALIAS => $txn_alias, FIELD => 'ObjectType', VALUE => 'RT::Ticket' ); $attachments->Limit( ALIAS => $txn_alias, FIELD => 'ObjectId', VALUE => $id ); - is ($attachments->Count, 1, 'Found only one gif attached to the ticket'); + is ($attachments->Count, 1, 'Found only one png attached to the ticket'); my $attachment = $attachments->First; ok ($attachment->Id, 'loaded attachment object'); my $acontent = $attachment->Content; - diag "coming from the database, md5 hex is ".Digest::MD5::md5_hex($acontent) if $ENV{'TEST_VERBOSE'}; + diag "coming from the database, md5 hex is ".Digest::MD5::md5_hex($acontent); is ($acontent, $file, 'The attachment isn\'t screwed up in the database.'); # Grab the binary attachment via the web ui my $ua = new LWP::UserAgent; my $full_url = "$url/Ticket/Attachment/". $attachment->TransactionId - ."/". $attachment->id. "/bplogo.gif?&user=root&pass=password"; + ."/". $attachment->id. "/bpslogo.png?&user=root&pass=password"; my $r = $ua->get( $full_url ); # Verify that the downloaded attachment is the same as what we uploaded. is ($file, $r->content, 'The attachment isn\'t screwed up in download'); + + $m->no_warnings_ok; } -diag "Simple I18N testing" if $ENV{'TEST_VERBOSE'}; +diag "Simple I18N testing"; { my $text = <<EOF; From: root\@localhost @@ -519,7 +549,7 @@ Content-Type: text/plain; charset="utf-8" \303\241\303\251\303\255\303\263\303\272 bye EOF - my ($status, $id) = RT::Test->send_via_mailgate($text); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text); is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "created ticket"); @@ -540,9 +570,11 @@ EOF $tick->Transactions->First->Content =~ /$unistring/i, $tick->Id." appears to be unicode ". $tick->Transactions->First->Attachments->First->Id ); + + $m->no_warnings_ok; } -diag "supposedly I18N fails on the second message sent in." if $ENV{'TEST_VERBOSE'}; +diag "supposedly I18N fails on the second message sent in."; { my $text = <<EOF; From: root\@localhost @@ -555,7 +587,7 @@ Content-Type: text/plain; charset="utf-8" \303\241\303\251\303\255\303\263\303\272 bye EOF - my ($status, $id) = RT::Test->send_via_mailgate($text); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text); is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "created ticket"); @@ -572,9 +604,11 @@ EOF $tick->Transactions->First->Content =~ $unistring, "It appears to be unicode - ". $tick->Transactions->First->Content ); + + $m->no_warnings_ok; } -diag "check that mailgate doesn't suffer from empty Reply-To:" if $ENV{'TEST_VERBOSE'}; +diag "check that mailgate doesn't suffer from empty Reply-To:"; { my $text = <<EOF; From: root\@localhost @@ -585,7 +619,7 @@ Content-Type: text/plain; charset="utf-8" test EOF - my ($status, $id) = RT::Test->send_via_mailgate($text); + my ($status, $id) = RT::Test->send_via_mailgate_and_http($text); is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "created ticket"); @@ -595,6 +629,8 @@ EOF is ($tick->Id, $id, "correct ticket"); like $tick->RequestorAddresses, qr/root\@localhost/, 'correct requestor'; + + $m->no_warnings_ok; } @@ -607,18 +643,17 @@ skip "Advanced mailgate actions require an unsafe configuration", 47 # create new queue to be shure we don't mess with rights use RT::Queue; -my $queue = RT::Queue->new($RT::SystemUser); +my $queue = RT::Queue->new(RT->SystemUser); my ($qid) = $queue->Create( Name => 'ext-mailgate'); ok( $qid, 'queue created for ext-mailgate tests' ); -# {{{ Check take and resolve actions # create ticket that is owned by nobody use RT::Ticket; -my $tick = RT::Ticket->new($RT::SystemUser); +my $tick = RT::Ticket->new(RT->SystemUser); my ($id) = $tick->Create( Queue => 'ext-mailgate', Subject => 'test'); ok( $id, 'new ticket created' ); -is( $tick->Owner, $RT::Nobody->Id, 'owner of the new ticket is nobody' ); +is( $tick->Owner, RT->Nobody->Id, 'owner of the new ticket is nobody' ); $! = 0; ok(open(MAIL, '|-', "$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action take"), "Opened the mailgate - $!"); @@ -630,7 +665,7 @@ EOF close (MAIL); is ($? >> 8, 0, "The mail gateway exited normally"); -$tick = RT::Ticket->new($RT::SystemUser); +$tick = RT::Ticket->new(RT->SystemUser); $tick->Load( $id ); is( $tick->Id, $id, 'load correct ticket'); is( $tick->OwnerObj->EmailAddress, 'root@localhost', 'successfuly take ticket via email'); @@ -639,9 +674,11 @@ is( $tick->OwnerObj->EmailAddress, 'root@localhost', 'successfuly take ticket vi is( $tick->Transactions->Count, 2, 'no superfluous transactions'); my $status; -($status, $msg) = $tick->SetOwner( $RT::Nobody->Id, 'Force' ); +($status, $msg) = $tick->SetOwner( RT->Nobody->Id, 'Force' ); ok( $status, 'successfuly changed owner: '. ($msg||'') ); -is( $tick->Owner, $RT::Nobody->Id, 'set owner back to nobody'); +is( $tick->Owner, RT->Nobody->Id, 'set owner back to nobody'); + +$m->no_warnings_ok; $! = 0; @@ -657,7 +694,7 @@ is ($? >> 8, 0, "The mail gateway exited normally"); DBIx::SearchBuilder::Record::Cachable->FlushCache; -$tick = RT::Ticket->new($RT::SystemUser); +$tick = RT::Ticket->new(RT->SystemUser); $tick->Load( $id ); is( $tick->Id, $id, "load correct ticket #$id"); is( $tick->OwnerObj->EmailAddress, 'root@localhost', 'successfuly take ticket via email'); @@ -668,6 +705,9 @@ $txns->OrderBy( FIELD => 'id', ORDER => 'DESC' ); is( $tick->Transactions->Count, 6, 'no superfluous transactions'); is( $txns->First->Subject, "[$RT::rtname \#$id] correspondence", 'successfuly add correspond within take via email' ); +$m->no_warnings_ok; + + $! = 0; ok(open(MAIL, '|-', "$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action resolve"), "Opened the mailgate - $!"); print MAIL <<EOF; @@ -680,14 +720,14 @@ is ($? >> 8, 0, "The mail gateway exited normally"); DBIx::SearchBuilder::Record::Cachable->FlushCache; -$tick = RT::Ticket->new($RT::SystemUser); +$tick = RT::Ticket->new(RT->SystemUser); $tick->Load( $id ); is( $tick->Id, $id, 'load correct ticket'); is( $tick->Status, 'resolved', 'successfuly resolved ticket via email'); is( $tick->Transactions->Count, 7, 'no superfluous transactions'); use RT::User; -my $user = RT::User->new( $RT::SystemUser ); +my $user = RT::User->new( RT->SystemUser ); my ($uid) = $user->Create( Name => 'ext-mailgate', EmailAddress => 'ext-mailgate@localhost', Privileged => 1, @@ -696,12 +736,14 @@ my ($uid) = $user->Create( Name => 'ext-mailgate', ok( $uid, 'user created for ext-mailgate tests' ); ok( !$user->HasRight( Right => 'OwnTicket', Object => $queue ), "User can't own ticket" ); -$tick = RT::Ticket->new($RT::SystemUser); +$tick = RT::Ticket->new(RT->SystemUser); ($id) = $tick->Create( Queue => $qid, Subject => 'test' ); ok( $id, 'create new ticket' ); my $rtname = RT->Config->Get('rtname'); +$m->no_warnings_ok; + $! = 0; ok(open(MAIL, '|-', "$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action take"), "Opened the mailgate - $!"); print MAIL <<EOF; @@ -720,6 +762,10 @@ ok( $status, "successfuly granted right: $msg" ); my $ace_id = $status; ok( $user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "User can reply to ticket" ); +$m->next_warning_like(qr/Permission Denied/); +$m->next_warning_like(qr/Could not record email: Ticket not taken/); +$m->no_leftover_warnings_ok; + $! = 0; ok(open(MAIL, '|-', "$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action correspond-take"), "Opened the mailgate - $!"); print MAIL <<EOF; @@ -735,6 +781,10 @@ DBIx::SearchBuilder::Record::Cachable->FlushCache; cmp_ok( $tick->Owner, '!=', $user->id, "we didn't change owner" ); is( $tick->Transactions->Count, 3, "one transactions added" ); +$m->next_warning_like(qr/Permission Denied/); +$m->next_warning_like(qr/Could not record email: Ticket not taken/); +$m->no_leftover_warnings_ok; + $! = 0; ok(open(MAIL, '|-', "$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action take-correspond"), "Opened the mailgate - $!"); print MAIL <<EOF; @@ -750,12 +800,16 @@ DBIx::SearchBuilder::Record::Cachable->FlushCache; cmp_ok( $tick->Owner, '!=', $user->id, "we didn't change owner" ); is( $tick->Transactions->Count, 3, "no transactions added, user can't take ticket first" ); +$m->next_warning_like(qr/Permission Denied/); +$m->next_warning_like(qr/Could not record email: Ticket not taken/); +$m->no_leftover_warnings_ok; + # revoke ReplyToTicket right use RT::ACE; -my $ace = RT::ACE->new($RT::SystemUser); +my $ace = RT::ACE->new(RT->SystemUser); $ace->Load( $ace_id ); $ace->Delete; -my $acl = RT::ACL->new($RT::SystemUser); +my $acl = RT::ACL->new(RT->SystemUser); $acl->Limit( FIELD => 'RightName', VALUE => 'ReplyToTicket' ); $acl->LimitToObject( $RT::System ); while( my $ace = $acl->Next ) { @@ -765,9 +819,9 @@ while( my $ace = $acl->Next ) { ok( !$user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "User can't reply to ticket any more" ); -my $group = RT::Group->new( $RT::SystemUser ); +my $group = RT::Group->new( RT->SystemUser ); ok( $group->LoadQueueRoleGroup( Queue => $qid, Type=> 'Owner' ), "load queue owners role group" ); -$ace = RT::ACE->new( $RT::SystemUser ); +$ace = RT::ACE->new( RT->SystemUser ); ($ace_id, $msg) = $group->PrincipalObj->GrantRight( Right => 'ReplyToTicket', Object => $queue ); ok( $ace_id, "Granted queue owners role group with ReplyToTicket right" ); @@ -793,10 +847,7 @@ is( $tick->Owner, $user->id, "we changed owner" ); ok( $user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "owner can reply to ticket" ); is( $tick->Transactions->Count, 5, "transactions added" ); +$m->no_warnings_ok; -# }}} }; - -1; - diff --git a/rt/t/mail/gnupg-bad.t b/rt/t/mail/gnupg-bad.t index 2d8e03575..c9b28c902 100644 --- a/rt/t/mail/gnupg-bad.t +++ b/rt/t/mail/gnupg-bad.t @@ -2,57 +2,43 @@ use strict; use warnings; -use RT::Test tests => 6; - -plan skip_all => 'GnuPG required.' - unless eval 'use GnuPG::Interface; 1'; -plan skip_all => 'gpg executable is required.' - unless RT::Test->find_executable('gpg'); - - -use Cwd 'getcwd'; - -my $homedir = RT::Test::get_abs_relocatable_dir(File::Spec->updir(), - qw(data gnupg keyrings)); - -RT->Config->Set( 'GnuPG', - Enable => 1, - OutgoingMessagesFormat => 'RFC' ); - -RT->Config->Set( 'GnuPGOptions', - homedir => $homedir, - passphrase => 'test', - 'no-permission-warning' => undef); +use RT::Test::GnuPG + tests => 7, + gnupg_options => { + passphrase => 'rt-test', + homedir => RT::Test::get_abs_relocatable_dir( + File::Spec->updir(), qw/data gnupg keyrings/ + ), + }; RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' ); my ($baseurl, $m) = RT::Test->started_ok; -$m->get( $baseurl."?user=root;pass=password" ); -$m->content_like(qr/Logout/, 'we did log in'); +$m->login; $m->get( $baseurl.'/Admin/Queues/'); $m->follow_link_ok( {text => 'General'} ); $m->submit_form( form_number => 3, fields => { CorrespondAddress => 'rt@example.com' } ); $m->content_like(qr/rt\@example.com.* - never/, 'has key info.'); -ok(my $user = RT::User->new($RT::SystemUser)); +ok(my $user = RT::User->new(RT->SystemUser)); ok($user->Load('root'), "Loaded user 'root'"); $user->SetEmailAddress('rt@example.com'); if (0) { # XXX: need to generate these mails - diag "no signature" if $ENV{TEST_VERBOSE}; - diag "no encryption on encrypted queue" if $ENV{TEST_VERBOSE}; - diag "mismatched signature" if $ENV{TEST_VERBOSE}; - diag "unknown public key" if $ENV{TEST_VERBOSE}; - diag "unknown private key" if $ENV{TEST_VERBOSE}; - diag "signer != sender" if $ENV{TEST_VERBOSE}; - diag "encryption to user whose pubkey is not signed" if $ENV{TEST_VERBOSE}; - diag "no encryption of attachment on encrypted queue" if $ENV{TEST_VERBOSE}; - diag "no signature of attachment" if $ENV{TEST_VERBOSE}; - diag "revoked key" if $ENV{TEST_VERBOSE}; - diag "expired key" if $ENV{TEST_VERBOSE}; - diag "unknown algorithm" if $ENV{TEST_VERBOSE}; + diag "no signature"; + diag "no encryption on encrypted queue"; + diag "mismatched signature"; + diag "unknown public key"; + diag "unknown private key"; + diag "signer != sender"; + diag "encryption to user whose pubkey is not signed"; + diag "no encryption of attachment on encrypted queue"; + diag "no signature of attachment"; + diag "revoked key"; + diag "expired key"; + diag "unknown algorithm"; } diff --git a/rt/t/mail/gnupg-incoming.t b/rt/t/mail/gnupg-incoming.t index 230aa9c58..e591add6c 100644 --- a/rt/t/mail/gnupg-incoming.t +++ b/rt/t/mail/gnupg-incoming.t @@ -2,35 +2,25 @@ use strict; use warnings; -use RT::Test tests => 39; - -plan skip_all => 'GnuPG required.' - unless eval 'use GnuPG::Interface; 1'; -plan skip_all => 'gpg executable is required.' - unless RT::Test->find_executable('gpg'); +my $homedir; +BEGIN { + require RT::Test; + $homedir = + RT::Test::get_abs_relocatable_dir( File::Spec->updir(), + qw/data gnupg keyrings/ ); +} +use RT::Test::GnuPG + tests => 41, + actual_server => 1, + gnupg_options => { + passphrase => 'rt-test', + homedir => $homedir, + }; -use File::Temp; -use Cwd 'getcwd'; use String::ShellQuote 'shell_quote'; use IPC::Run3 'run3'; -my $homedir = RT::Test::get_abs_relocatable_dir(File::Spec->updir(), - qw(data gnupg keyrings)); - -# catch any outgoing emails -RT::Test->set_mail_catcher; - -RT->Config->Set( 'GnuPG', - Enable => 1, - OutgoingMessagesFormat => 'RFC' ); - -RT->Config->Set( 'GnuPGOptions', - homedir => $homedir, - 'no-permission-warning' => undef); - -RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' ); - my ($baseurl, $m) = RT::Test->started_ok; # configure key for General queue @@ -41,7 +31,7 @@ $m->submit_form( form_number => 3, fields => { CorrespondAddress => 'general@example.com' } ); $m->content_like(qr/general\@example.com.* - never/, 'has key info.'); -ok(my $user = RT::User->new($RT::SystemUser)); +ok(my $user = RT::User->new(RT->SystemUser)); ok($user->Load('root'), "Loaded user 'root'"); $user->SetEmailAddress('recipient@example.com'); @@ -71,7 +61,7 @@ RT::Test->close_mailgate_ok($mail); qr/^X-RT-Incoming-Encryption: Not encrypted/m, 'recorded incoming mail that is not encrypted' ); - like( $txn->Attachments->First->Content, qr'Blah'); + like( $txn->Attachments->First->Content, qr/Blah/); } # test for signed mail @@ -79,7 +69,7 @@ my $buf = ''; run3( shell_quote( - qw(gpg --armor --sign), + qw(gpg --batch --no-tty --armor --sign), '--default-key' => 'recipient@example.com', '--homedir' => $homedir, '--passphrase' => 'recipient', @@ -113,7 +103,7 @@ RT::Test->close_mailgate_ok($mail); 'recorded incoming mail that is encrypted' ); # test for some kind of PGP-Signed-By: Header - like( $attach->Content, qr'fnord'); + like( $attach->Content, qr/fnord/); } # test for clear-signed mail @@ -121,7 +111,7 @@ $buf = ''; run3( shell_quote( - qw(gpg --armor --sign --clearsign), + qw(gpg --batch --no-tty --armor --sign --clearsign), '--default-key' => 'recipient@example.com', '--homedir' => $homedir, '--passphrase' => 'recipient', @@ -154,7 +144,7 @@ RT::Test->close_mailgate_ok($mail); 'recorded incoming mail that is encrypted' ); # test for some kind of PGP-Signed-By: Header - like( $attach->Content, qr'clearfnord'); + like( $attach->Content, qr/clearfnord/); } # test for signed and encrypted mail @@ -162,7 +152,7 @@ $buf = ''; run3( shell_quote( - qw(gpg --encrypt --armor --sign), + qw(gpg --batch --no-tty --encrypt --armor --sign), '--recipient' => 'general@example.com', '--default-key' => 'recipient@example.com', '--homedir' => $homedir, @@ -200,7 +190,7 @@ RT::Test->close_mailgate_ok($mail); 'PGP', 'recorded incoming mail that is encrypted' ); - like( $attach->Content, qr'orz'); + like( $attach->Content, qr/orz/); is( $orig->GetHeader('Content-Type'), 'application/x-rt-original-message'); ok(index($orig->Content, $buf) != -1, 'found original msg'); @@ -211,7 +201,7 @@ $buf = ''; run3( shell_quote( - qw(gpg --armor --sign), + qw(gpg --batch --no-tty --armor --sign), '--default-key' => 'rt@example.com', '--homedir' => $homedir, '--passphrase' => 'test', @@ -247,7 +237,7 @@ $buf = ''; run3( shell_quote( - qw(gpg --armor --encrypt), + qw(gpg --batch --no-tty --armor --encrypt), '--recipient' => 'random@localhost', '--homedir' => $homedir, ), @@ -274,7 +264,7 @@ RT::Test->close_mailgate_ok($mail); TODO: { local $TODO = "this test requires keys associated with queues"; - unlike( $attach->Content, qr'should not be there either'); + unlike( $attach->Content, qr/should not be there either/); } } @@ -284,7 +274,7 @@ $buf = ''; run3( shell_quote( - qw(gpg --armor --encrypt), + qw(gpg --batch --no-tty --armor --encrypt), '--recipient' => 'rt@example.com', '--homedir' => $homedir, ), @@ -314,6 +304,6 @@ is(@mail, 1, 'caught outgoing mail.'); my $tick = RT::Test->last_ticket; my $txn = $tick->Transactions->First; my ($msg, $attach) = @{$txn->Attachments->ItemsArrayRef}; - unlike( ($attach ? $attach->Content : ''), qr'really should not be there either'); + unlike( ($attach ? $attach->Content : ''), qr/really should not be there either/); } diff --git a/rt/t/mail/gnupg-outgoing-encrypted.t b/rt/t/mail/gnupg-outgoing-encrypted.t new file mode 100644 index 000000000..4f2a28f55 --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-encrypted.t @@ -0,0 +1,27 @@ +#!/usr/bin/perl -w +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 103, + gnupg_options => { + passphrase => 'rt-test', + 'trust-model' => 'always', + }; + +RT::Test->import_gnupg_key('rt-recipient@example.com'); +RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' ); + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', + Encrypt => 1, +); +ok $queue && $queue->id, 'loaded or created queue'; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +create_and_test_outgoing_emails( $queue, $m ); + diff --git a/rt/t/mail/gnupg-outgoing-plain.t b/rt/t/mail/gnupg-outgoing-plain.t new file mode 100644 index 000000000..ee9a8ac81 --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-plain.t @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 103, + gnupg_options => { + passphrase => 'rt-test', + 'trust-model' => 'always', + }; + +RT::Test->import_gnupg_key('rt-recipient@example.com'); +RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' ); + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +create_and_test_outgoing_emails( $queue, $m ); diff --git a/rt/t/mail/gnupg-outgoing-signed.t b/rt/t/mail/gnupg-outgoing-signed.t new file mode 100644 index 000000000..2ea20a2d7 --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-signed.t @@ -0,0 +1,27 @@ +#!/usr/bin/perl -w +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 103, + gnupg_options => { + passphrase => 'rt-test', + 'trust-model' => 'always', + }; + +RT::Test->import_gnupg_key('rt-recipient@example.com'); +RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' ); + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', + Sign => 1, +); +ok $queue && $queue->id, 'loaded or created queue'; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +create_and_test_outgoing_emails( $queue, $m ); + diff --git a/rt/t/mail/gnupg-outgoing-signed_encrypted.t b/rt/t/mail/gnupg-outgoing-signed_encrypted.t new file mode 100644 index 000000000..0b82cf1ca --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-signed_encrypted.t @@ -0,0 +1,28 @@ +#!/usr/bin/perl -w +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 103, + gnupg_options => { + passphrase => 'rt-test', + 'trust-model' => 'always', + }; + +RT::Test->import_gnupg_key('rt-recipient@example.com'); +RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' ); + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', + Sign => 1, + Encrypt => 1, +); +ok $queue && $queue->id, 'loaded or created queue'; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +create_and_test_outgoing_emails( $queue, $m ); + diff --git a/rt/t/mail/gnupg-realmail.t b/rt/t/mail/gnupg-realmail.t index 198402b23..7d1dbcab5 100644 --- a/rt/t/mail/gnupg-realmail.t +++ b/rt/t/mail/gnupg-realmail.t @@ -2,32 +2,13 @@ use strict; use warnings; -use RT::Test tests => 196; - -plan skip_all => 'GnuPG required.' - unless eval 'use GnuPG::Interface; 1'; -plan skip_all => 'gpg executable is required.' - unless RT::Test->find_executable('gpg'); - +use RT::Test::GnuPG tests => 198, gnupg_options => { passphrase => 'rt-test' }; use Digest::MD5 qw(md5_hex); -use File::Temp qw(tempdir); -my $homedir = tempdir( CLEANUP => 1 ); - -RT->Config->Set( 'GnuPG', - Enable => 1, - OutgoingMessagesFormat => 'RFC' ); - -RT->Config->Set( 'GnuPGOptions', - homedir => $homedir, - passphrase => 'rt-test', - 'no-permission-warning' => undef); - -RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' ); - RT::Test->import_gnupg_key('rt-recipient@example.com'); RT::Test->import_gnupg_key('rt-test@example.com', 'public'); +RT::Test->trust_gnupg_key('rt-test@example.com'); my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'we did log in'; @@ -37,17 +18,12 @@ $m->submit_form( form_number => 3, fields => { CorrespondAddress => 'rt-recipient@example.com' } ); $m->content_like(qr/rt-recipient\@example.com.* - never/, 'has key info.'); -RT::Test->set_rights( - Principal => 'Everyone', - Right => ['CreateTicket'], -); - my $eid = 0; for my $usage (qw/signed encrypted signed&encrypted/) { for my $format (qw/MIME inline/) { for my $attachment (qw/plain text-attachment binary-attachment/) { ++$eid; - diag "Email $eid: $usage, $attachment email with $format format" if $ENV{TEST_VERBOSE}; + diag "Email $eid: $usage, $attachment email with $format format"; eval { email_ok($eid, $usage, $format, $attachment) }; } } @@ -57,13 +33,13 @@ $eid = 18; { my ($usage, $format, $attachment) = ('signed', 'inline', 'plain'); ++$eid; - diag "Email $eid: $usage, $attachment email with $format format" if $ENV{TEST_VERBOSE}; + diag "Email $eid: $usage, $attachment email with $format format"; eval { email_ok($eid, $usage, $format, $attachment) }; } sub email_ok { my ($eid, $usage, $format, $attachment) = @_; - diag "email_ok $eid: $usage, $format, $attachment" if $ENV{'TEST_VERBOSE'}; + diag "email_ok $eid: $usage, $format, $attachment"; my $emaildatadir = RT::Test::get_relocatable_dir(File::Spec->updir(), qw(data gnupg emails)); @@ -74,7 +50,7 @@ sub email_ok { is ($status >> 8, 0, "$eid: The mail gateway exited normally"); ok ($id, "$eid: got id of a newly created ticket - $id"); - my $tick = RT::Ticket->new( $RT::SystemUser ); + my $tick = RT::Ticket->new( RT->SystemUser ); $tick->Load( $id ); ok ($tick->id, "$eid: loaded ticket #$id"); diff --git a/rt/t/mail/gnupg-reverification.t b/rt/t/mail/gnupg-reverification.t index f116d9380..96a37a080 100644 --- a/rt/t/mail/gnupg-reverification.t +++ b/rt/t/mail/gnupg-reverification.t @@ -2,43 +2,16 @@ use strict; use warnings; -use RT::Test tests => 120; +use RT::Test::GnuPG tests => 216, gnupg_options => { passphrase => 'rt-test' }; -plan skip_all => 'GnuPG required.' - unless eval 'use GnuPG::Interface; 1'; -plan skip_all => 'gpg executable is required.' - unless RT::Test->find_executable('gpg'); - - -use File::Temp qw(tempdir); -my $homedir = tempdir( CLEANUP => 1 ); - -RT->Config->Set( 'GnuPG', - Enable => 1, - OutgoingMessagesFormat => 'RFC' ); - -RT->Config->Set( 'GnuPGOptions', - homedir => $homedir, - passphrase => 'rt-test', - 'no-permission-warning' => undef); - -RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' ); - - -diag "load Everyone group" if $ENV{'TEST_VERBOSE'}; +diag "load Everyone group"; my $everyone; { - $everyone = RT::Group->new( $RT::SystemUser ); + $everyone = RT::Group->new( RT->SystemUser ); $everyone->LoadSystemInternalGroup('Everyone'); ok $everyone->id, "loaded 'everyone' group"; } -RT::Test->set_rights( - Principal => $everyone, - Right => ['CreateTicket'], -); - - my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'we get log in'; @@ -50,7 +23,7 @@ my $emaildatadir = RT::Test::get_relocatable_dir(File::Spec->updir(), qw(data gnupg emails)); my @files = glob("$emaildatadir/*-signed-*"); foreach my $file ( @files ) { - diag "testing $file" if $ENV{'TEST_VERBOSE'}; + diag "testing $file"; my ($eid) = ($file =~ m{(\d+)[^/\\]+$}); ok $eid, 'figured id of a file'; @@ -58,35 +31,61 @@ foreach my $file ( @files ) { my $email_content = RT::Test->file_content( $file ); ok $email_content, "$eid: got content of email"; - my ($status, $id) = RT::Test->send_via_mailgate( $email_content ); + my $warnings; + my ($status, $id); + + { + # We don't use Test::Warn here because we get multi-line + # warnings, which Test::Warn only records the first line of. + local $SIG{__WARN__} = sub { + $warnings .= "@_"; + }; + + ($status, $id) = RT::Test->send_via_mailgate( $email_content ); + } + is $status >> 8, 0, "$eid: the mail gateway exited normally"; ok $id, "$eid: got id of a newly created ticket - $id"; - my $ticket = RT::Ticket->new( $RT::SystemUser ); + like($warnings, qr/Had a problem during decrypting and verifying/); + like($warnings, qr/public key not found/); + + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $id ); ok $ticket->id, "$eid: loaded ticket #$id"; is $ticket->Subject, "Test Email ID:$eid", "$eid: correct subject"; $m->goto_ticket( $id ); - $m->content_like( - qr/Not possible to check the signature, the reason is missing public key/is, + $m->content_contains( + "Not possible to check the signature, the reason is missing public key", "$eid: signature is not verified", ); $m->content_like(qr/This is .*ID:$eid/ims, "$eid: content is there and message is decrypted"); + $m->next_warning_like(qr/public key not found/); + + # some mails contain multiple signatures + if ($eid == 5 || $eid == 17 || $eid == 18) { + $m->next_warning_like(qr/public key not found/); + } + + $m->no_leftover_warnings_ok; + push @ticket_ids, $id; } -diag "import key into keyring" if $ENV{'TEST_VERBOSE'}; +diag "import key into keyring"; RT::Test->import_gnupg_key('rt-test@example.com', 'public'); foreach my $id ( @ticket_ids ) { - diag "testing ticket #$id" if $ENV{'TEST_VERBOSE'}; + diag "testing ticket #$id"; $m->goto_ticket( $id ); - $m->content_like( - qr/The signature is good/is, + $m->content_contains( + "The signature is good", "signature is re-verified and now good", ); + + $m->no_warnings_ok; } diff --git a/rt/t/mail/gnupg-special.t b/rt/t/mail/gnupg-special.t index 6a31ef131..7dd63478a 100644 --- a/rt/t/mail/gnupg-special.t +++ b/rt/t/mail/gnupg-special.t @@ -2,32 +2,10 @@ use strict; use warnings; -use RT::Test tests => 23; - -plan skip_all => 'GnuPG required.' - unless eval 'use GnuPG::Interface; 1'; -plan skip_all => 'gpg executable is required.' - unless RT::Test->find_executable('gpg'); +use RT::Test::GnuPG tests => 25, gnupg_options => { passphrase => 'rt-test' }; use Digest::MD5 qw(md5_hex); -use File::Temp qw(tempdir); -my $homedir = tempdir( CLEANUP => 1 ); - -# catch any outgoing emails -RT::Test->set_mail_catcher; - -RT->Config->Set( 'GnuPG', - Enable => 1, - OutgoingMessagesFormat => 'RFC' ); - -RT->Config->Set( 'GnuPGOptions', - homedir => $homedir, - 'passphrase' => 'rt-test', - 'no-permission-warning' => undef); - -RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' ); - RT::Test->import_gnupg_key('rt-recipient@example.com'); RT::Test->import_gnupg_key('rt-test@example.com', 'public'); @@ -46,19 +24,14 @@ ok( $m->login, 'we did log in' ); $m->content_like(qr/rt-recipient\@example.com.* - never/, 'has key info.'); } -ok(my $user = RT::User->new($RT::SystemUser)); +ok(my $user = RT::User->new(RT->SystemUser)); ok($user->Load('root'), "Loaded user 'root'"); $user->SetEmailAddress('recipient@example.com'); -RT::Test->set_rights( - Principal => 'Everyone', - Right => ['CreateTicket'], -); - { my $id = send_via_mailgate('quoted_inline_signature.txt'); - my $tick = RT::Ticket->new( $RT::SystemUser ); + my $tick = RT::Ticket->new( RT->SystemUser ); $tick->Load( $id ); ok ($tick->id, "loaded ticket #$id"); diff --git a/rt/t/mail/mime_decoding.t b/rt/t/mail/mime_decoding.t index 8257aee80..b02f9795f 100644 --- a/rt/t/mail/mime_decoding.t +++ b/rt/t/mail/mime_decoding.t @@ -1,11 +1,11 @@ #!/usr/bin/perl use strict; use warnings; -use RT::Test nodata => 1, tests => 6; +use RT::Test nodb => 1, tests => 8; use_ok('RT::I18N'); -diag q{'=' char in a leading part before an encoded part} if $ENV{TEST_VERBOSE}; +diag q{'=' char in a leading part before an encoded part}; { my $str = 'key="plain"; key="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="'; is( @@ -15,8 +15,7 @@ diag q{'=' char in a leading part before an encoded part} if $ENV{TEST_VERBOSE}; ); } -diag q{not compliant with standards, but MUAs send such field when attachment has non-ascii in name} - if $ENV{TEST_VERBOSE}; +diag q{not compliant with standards, but MUAs send such field when attachment has non-ascii in name}; { my $str = 'attachment; filename="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="'; is( @@ -26,7 +25,7 @@ diag q{not compliant with standards, but MUAs send such field when attachment ha ); } -diag q{'=' char in a trailing part after an encoded part} if $ENV{TEST_VERBOSE}; +diag q{'=' char in a trailing part after an encoded part}; { my $str = 'attachment; filename="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="; some_prop="value"'; is( @@ -36,7 +35,7 @@ diag q{'=' char in a trailing part after an encoded part} if $ENV{TEST_VERBOSE}; ); } -diag q{regression test for #5248 from rt3.fsck.com} if $ENV{TEST_VERBOSE}; +diag q{regression test for #5248 from rt3.fsck.com}; { my $str = qq{Subject: =?ISO-8859-1?Q?Re=3A_=5BXXXXXX=23269=5D_=5BComment=5D_Frag?=} . qq{\n =?ISO-8859-1?Q?e_zu_XXXXXX--xxxxxx_/_Xxxxx=FCxxxxxxxxxx?=}; @@ -47,7 +46,7 @@ diag q{regression test for #5248 from rt3.fsck.com} if $ENV{TEST_VERBOSE}; ); } -diag q{newline and encoded file name} if $ENV{TEST_VERBOSE}; +diag q{newline and encoded file name}; { my $str = qq{application/vnd.ms-powerpoint;\n\tname="=?ISO-8859-1?Q?Main_presentation.ppt?="}; is( @@ -57,3 +56,26 @@ diag q{newline and encoded file name} if $ENV{TEST_VERBOSE}; ); } +diag q{rfc2231}; +{ + my $str = +"filename*=ISO-8859-1''%74%E9%73%74%2E%74%78%74 filename*=ISO-8859-1''%74%E9%73%74%2E%74%78%74"; + is( + RT::I18N::DecodeMIMEWordsToEncoding( $str, 'utf-8' ), + 'filename=tést.txt filename=tést.txt', + 'right decodig' + ); +} + +diag q{canonicalize mime word encodings like gb2312}; +{ + my $str = qq{Subject: =?gb2312?B?1NrKwL3nuPe12Lmy09CzrN9eX1NpbXBsaWZpZWRfQ05fR0IyMzEyYQ==?= + =?gb2312?B?dHRhY2hlbWVudCB0ZXN0IGluIENOIHNpbXBsaWZpZWQ=?=}; + + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + qq{Subject: 在世界各地共有超過_Simplified_CN_GB2312attachement test in CN simplified}, + "right decoding" + ); +} + diff --git a/rt/t/mail/multipart.t b/rt/t/mail/multipart.t index dc97b266f..a68710a75 100644 --- a/rt/t/mail/multipart.t +++ b/rt/t/mail/multipart.t @@ -46,19 +46,11 @@ # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} - -=head1 NAME - -rt-mailgate - Mail interface to RT3. - -=cut - use strict; use warnings; -use RT::Test tests => 5; +use RT::Test tests => 4; use RT::Test::Email; -my ($baseurl, $m) = RT::Test->started_ok; my $queue = RT::Test->load_or_create_queue( Name => 'General' ); my $user = RT::Test->load_or_create_user( Name => 'bob', EmailAddress => 'bob@example.com' ); diff --git a/rt/t/mail/one-time-recipients.t b/rt/t/mail/one-time-recipients.t new file mode 100644 index 000000000..985f95562 --- /dev/null +++ b/rt/t/mail/one-time-recipients.t @@ -0,0 +1,209 @@ +#!/usr/bin/perl +use strict; +use warnings; +use utf8; + +use RT::Test tests => 38; + +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user = RT::Test->load_or_create_user( + Name => 'root', + EmailAddress => 'root@localhost', +); +ok $user && $user->id, 'loaded or created user'; + +diag "Reply to ticket with actor as one time cc"; +{ + my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) ); + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'root@localhost', + ); + ok $status, "created ticket"; + + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $to = $entity->head->get('To'); + $to =~ s/^\s+|\s+$//; + is $to, 'root@localhost', 'got mail' + } + + RT->Config->Set( NotifyActor => 1 ); + ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + + @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $to = $entity->head->get('To'); + $to =~ s/^\s+|\s+$//; + is $to, 'root@localhost', 'got mail' + } + + RT->Config->Set( NotifyActor => 0 ); + ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + + @mails = RT::Test->fetch_caught_mails; + ok !@mails, "no mail - don't notify actor"; + + ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'root@localhost', + ); + ok $status, "replied to a ticket"; + + @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $to = $entity->head->get('Cc'); + $to =~ s/^\s+|\s+$//; + is $to, 'root@localhost', 'got mail' + } +} + +diag "Reply to ticket with requestor squelched"; +{ + my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) ); + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'test@localhost', + ); + ok $status, "created ticket"; + + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $to = $entity->head->get('To'); + $to =~ s/^\s+|\s+$//; + is $to, 'test@localhost', 'got mail' + } + + ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + + @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $to = $entity->head->get('To'); + $to =~ s/^\s+|\s+$//; + is $to, 'test@localhost', 'got mail' + } + + $ticket->SquelchMailTo('test@localhost'); + ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + + @mails = RT::Test->fetch_caught_mails; + ok !@mails, "no mail - squelched"; + + ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'test@localhost', + ); + ok $status, "replied to a ticket"; + + @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $to = $entity->head->get('Cc'); + $to =~ s/^\s+|\s+$//; + is $to, 'test@localhost', 'got mail' + } +} + +diag "Reply to ticket with requestor squelched"; +{ + my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) ); + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'test@localhost', + ); + ok $status, "created ticket"; + + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $to = $entity->head->get('To'); + $to =~ s/^\s+|\s+$//; + is $to, 'test@localhost', 'got mail' + } + + ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + + @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $to = $entity->head->get('To'); + $to =~ s/^\s+|\s+$//; + is $to, 'test@localhost', 'got mail' + } + + ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + SquelchMailTo => ['test@localhost'], + ); + ok $status, "replied to a ticket"; + + @mails = RT::Test->fetch_caught_mails; + ok !@mails, "no mail - squelched"; + + ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + + @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $to = $entity->head->get('To'); + $to =~ s/^\s+|\s+$//; + is $to, 'test@localhost', 'got mail' + } + + ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'test@localhost', + SquelchMailTo => ['test@localhost'], + ); + ok $status, "replied to a ticket"; + + @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $to = $entity->head->get('Cc'); + $to =~ s/^\s+|\s+$//; + is $to, 'test@localhost', 'got mail' + } +} diff --git a/rt/t/mail/outlook.t b/rt/t/mail/outlook.t index 15bfa21bc..00bbc9445 100644 --- a/rt/t/mail/outlook.t +++ b/rt/t/mail/outlook.t @@ -56,11 +56,11 @@ rt-mailgate - Mail interface to RT3. use strict; use warnings; -use RT::Test tests => 43; -my ($baseurl, $m) = RT::Test->started_ok; +use RT::Test tests => 42; + # 12.0 is outlook 2007, 14.0 is 2010 for my $mailer ( 'Microsoft Office Outlook 12.0', 'Microsoft Outlook 14.0' ) { - diag "Test mail with multipart/alternative" if $ENV{'TEST_VERBOSE'}; + diag "Test mail with multipart/alternative"; { my $text = <<EOF; From: root\@localhost @@ -106,8 +106,7 @@ EOF $mailer . ' with multipart/alternative, \n\n are replaced' ); } - diag "Test mail with multipart/mixed, with multipart/alternative in it" - if $ENV{'TEST_VERBOSE'}; + diag "Test mail with multipart/mixed, with multipart/alternative in it"; { my $text = <<EOF; From: root\@localhost @@ -167,8 +166,7 @@ EOF $mailer . ' with multipart/multipart, \n\n are replaced' ); } - diag "Test mail with with outlook, but the content type is text/plain" - if $ENV{'TEST_VERBOSE'}; + diag "Test mail with with outlook, but the content type is text/plain"; { my $text = <<EOF; From: root\@localhost @@ -203,8 +201,7 @@ EOF } } -diag "Test mail with with multipart/alternative but x-mailer is not outlook " - if $ENV{'TEST_VERBOSE'}; +diag "Test mail with with multipart/alternative but x-mailer is not outlook "; { my $text = <<EOF; From: root\@localhost diff --git a/rt/t/mail/rfc822-attachment.t b/rt/t/mail/rfc822-attachment.t new file mode 100644 index 000000000..f498ec55a --- /dev/null +++ b/rt/t/mail/rfc822-attachment.t @@ -0,0 +1,137 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +use MIME::Entity; + +diag "simple rfc822 attachment"; +{ + + my $top = MIME::Entity->build( + From => 'root@localhost', + To => 'rt@localhost', + Subject => 'this is top', + Data => ['top mail'], + ); + + my $rfc822 = MIME::Entity->build( + From => 'foo@localhost', + To => 'bar@localhost', + Subject => 'rfc822', + Data => ['rfc822 attachment'], + 'X-Brokenness' => 'high', + ); + + $top->attach( + Data => $rfc822->stringify, + Type => 'message/rfc822', + ); + + my $parsed = content_as_mime($top); + + for my $mime ($top, $parsed) { + diag "testing mail"; + is $mime->parts, 2, 'two mime parts'; + + like $mime->head->get('Subject'), qr/this is top/, 'top subject'; + like $mime->head->get('From'), qr/root\@localhost/, 'top From'; + like $mime->parts(0)->bodyhandle->as_string, qr/top mail/, 'content of top'; + + my $attach = $mime->parts(1); + my $body = $attach->bodyhandle->as_string; + + like $attach->head->mime_type, qr/message\/rfc822/, 'attach of type message/rfc822'; + like $body, qr/rfc822 attachment/, 'attach content'; + + headers_like( + $attach, + Subject => 'rfc822', + From => 'foo@localhost', + 'X-Brokenness' => 'high', + ); + } +} + +diag "multipart rfc822 attachment"; +{ + + my $top = MIME::Entity->build( + From => 'root@localhost', + To => 'rt@localhost', + Subject => 'this is top', + Data => ['top mail'], + ); + + my $rfc822 = MIME::Entity->build( + From => 'foo@localhost', + To => 'bar@localhost', + Subject => 'rfc822', + Data => ['rfc822 attachment'], + 'X-Brokenness' => 'high', + ); + + $rfc822->attach( + Data => '<b>attachment of rfc822 attachment</b>', + Type => 'text/html', + ); + + $top->attach( + Data => $rfc822->stringify, + Type => 'message/rfc822', + ); + + my $parsed = content_as_mime($top); + + for my $mime ($top, $parsed) { + diag "testing mail"; + is $mime->parts, 2, 'two mime parts'; + + like $mime->head->get('Subject'), qr/this is top/, 'top subject'; + like $mime->head->get('From'), qr/root\@localhost/, 'top From'; + like $mime->parts(0)->bodyhandle->as_string, qr/top mail/, 'content of top'; + + my $attach = $mime->parts(1); + my $body = $attach->bodyhandle->as_string; + + like $attach->head->mime_type, qr/message\/rfc822/, 'attach of type message/rfc822'; + like $body, qr/rfc822 attachment/, 'attach content'; + like $body, qr/attachment of rfc822 attachment/, 'attach content'; + + headers_like( + $attach, + Subject => 'rfc822', + From => 'foo@localhost', + 'X-Brokenness' => 'high', + 'Content-Type' => 'text/plain', + 'Content-type' => 'text/html', + ); + } +} + +sub content_as_mime { + my $entity = shift; + my ( $status, $id ) = RT::Test->send_via_mailgate($entity); + is( $status >> 8, 0, "The mail gateway exited normally" ); + ok( $id, "created ticket" ); + # We can't simply use Txn->ContentAsMIME since that is wrapped in a + # message/rfc822 entity + return RT::Test->last_ticket->Transactions->First->Attachments->First->ContentAsMIME(Children => 1); +} + +sub headers_like { + my $attach = shift; + my %header = (@_); + my $body = $attach->bodyhandle->as_string; + for my $name (keys %header) { + if (lc $name eq 'content-type') { + like $attach->head->get($name), qr/message\/rfc822/, "attach $name message/rfc822, not from a subpart"; + } else { + is $attach->head->get($name), undef, "attach $name not in part header"; + } + like $body, qr/$name: $header{$name}/i, "attach $name in part body"; + } +} + +done_testing; + diff --git a/rt/t/mail/sendmail.t b/rt/t/mail/sendmail.t index 1f97bbb9f..bb5d2db80 100644 --- a/rt/t/mail/sendmail.t +++ b/rt/t/mail/sendmail.t @@ -3,7 +3,7 @@ use strict; use File::Spec (); -use RT::Test tests => 137; +use RT::Test tests => 141; use RT::EmailParser; use RT::Tickets; @@ -13,7 +13,7 @@ my @_outgoing_messages; my @scrips_fired; #We're not testing acls here. -my $everyone = RT::Group->new($RT::SystemUser); +my $everyone = RT::Group->new(RT->SystemUser); $everyone->LoadSystemInternalGroup('Everyone'); $everyone->PrincipalObj->GrantRight( Right =>'SuperUser' ); @@ -22,13 +22,13 @@ is (__PACKAGE__, 'main', "We're operating in the main package"); { no warnings qw/redefine/; - sub RT::Action::SendEmail::SendMessage { + *RT::Action::SendEmail::SendMessage = sub { my $self = shift; my $MIME = shift; main::_fired_scrip($self->ScripObj); main::is(ref($MIME) , 'MIME::Entity', "hey, look. it's a mime entity"); - } + }; } # some utils @@ -52,7 +52,7 @@ use RT::Interface::Email; my %args = (message => $content, queue => 1, action => 'correspond'); my ($status, $msg) = RT::Interface::Email::Gateway(\%args); ok($status, "successfuly used Email::Gateway interface") or diag("error: $msg"); -my $tickets = RT::Tickets->new($RT::SystemUser); +my $tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); my $tick= $tickets->First(); @@ -79,10 +79,10 @@ Foob!'); use Data::Dumper; -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); my ($id, undef, $create_msg ) = $ticket->Create(Requestor => ['root@localhost'], Queue => 'general', Subject => 'I18NTest', MIMEObj => $parser->Entity); ok ($id,$create_msg); -$tickets = RT::Tickets->new($RT::SystemUser); +$tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick = $tickets->First(); @@ -115,7 +115,7 @@ use RT::Interface::Email; %args = (message => $content, queue => 1, action => 'correspond'); RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new($RT::SystemUser); + $tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick = $tickets->First(); @@ -157,7 +157,7 @@ use RT::Interface::Email; %args = (message => $content, queue => 1, action => 'correspond'); RT::Interface::Email::Gateway(\%args); -$tickets = RT::Tickets->new($RT::SystemUser); +$tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick = $tickets->First(); @@ -192,8 +192,7 @@ sub _fired_scrip { sub utf8_redef_sendmessage { no warnings qw/redefine/; - eval ' - sub RT::Action::SendEmail::SendMessage { + *RT::Action::SendEmail::SendMessage = sub { my $self = shift; my $MIME = shift; @@ -201,26 +200,25 @@ sub utf8_redef_sendmessage { ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name); main::_fired_scrip($self->ScripObj); $MIME->make_singlepart; - main::is( ref($MIME) , \'MIME::Entity\', - "hey, look. it\'s a mime entity" ); - main::is( ref( $MIME->head ) , \'MIME::Head\', + main::is( ref($MIME) , 'MIME::Entity', + "hey, look. it's a mime entity" ); + main::is( ref( $MIME->head ) , 'MIME::Head', "its mime header is a mime header. yay" ); - main::like( $MIME->head->get(\'Content-Type\') , qr/utf-8/, + main::like( $MIME->head->get('Content-Type') , qr/utf-8/, "Its content type is utf-8" ); my $message_as_string = $MIME->bodyhandle->as_string(); use Encode; $message_as_string = Encode::decode_utf8($message_as_string); main::like( $message_as_string , qr/H\x{e5}vard/, -"The message\'s content contains havard\'s name. this will fail if it\'s not utf8 out"); +"The message's content contains havard's name. this will fail if it's not utf8 out"); - }'; + }; } sub iso8859_redef_sendmessage { no warnings qw/redefine/; - eval ' - sub RT::Action::SendEmail::SendMessage { + *RT::Action::SendEmail::SendMessage = sub { my $self = shift; my $MIME = shift; @@ -228,22 +226,20 @@ sub iso8859_redef_sendmessage { ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name); main::_fired_scrip($self->ScripObj); $MIME->make_singlepart; - main::is( ref($MIME) , \'MIME::Entity\', - "hey, look. it\'s a mime entity" ); - main::is( ref( $MIME->head ) , \'MIME::Head\', + main::is( ref($MIME) , 'MIME::Entity', + "hey, look. it's a mime entity" ); + main::is( ref( $MIME->head ) , 'MIME::Head', "its mime header is a mime header. yay" ); - main::like( $MIME->head->get(\'Content-Type\') , qr/iso-8859-1/, + main::like( $MIME->head->get('Content-Type') , qr/iso-8859-1/, "Its content type is iso-8859-1 - " . $MIME->head->get("Content-Type") ); my $message_as_string = $MIME->bodyhandle->as_string(); use Encode; $message_as_string = Encode::decode("iso-8859-1",$message_as_string); main::like( - $message_as_string , qr/H\x{e5}vard/, "The message\'s content contains havard\'s name. this will fail if it\'s not utf8 out"); - - }'; + $message_as_string , qr/H\x{e5}vard/, "The message's content contains havard's name. this will fail if it's not utf8 out"); + }; } -# {{{ test a multipart alternative containing a text-html part with an umlaut my $alt_umlaut_email = RT::Test::get_relocatable_file( 'multipart-alternative-with-umlaut', (File::Spec->updir(), 'data', 'emails')); @@ -260,7 +256,7 @@ $parser->ParseMIMEEntityFromScalar($content); %args = (message => $content, queue => 1, action => 'correspond'); RT::Interface::Email::Gateway(\%args); # TODO: following 5 lines should replaced by get_latest_ticket_ok() - $tickets = RT::Tickets->new($RT::SystemUser); + $tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick = $tickets->First(); @@ -272,9 +268,7 @@ $parser->ParseMIMEEntityFromScalar($content); } -# }}} -# {{{ test a text-html message with an umlaut my $text_html_email = RT::Test::get_relocatable_file('text-html-with-umlaut', (File::Spec->updir(), 'data', 'emails')); $content = RT::Test->file_content($text_html_email); @@ -287,7 +281,7 @@ $parser->ParseMIMEEntityFromScalar($content); %args = (message => $content, queue => 1, action => 'correspond'); RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new($RT::SystemUser); + $tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick = $tickets->First(); @@ -299,32 +293,54 @@ is (count_attachs($tick), 1 , "Has one attachment, presumably a text-html and a sub text_html_redef_sendmessage { no warnings qw/redefine/; - eval 'sub RT::Action::SendEmail::SendMessage { - my $self = shift; - my $MIME = shift; - return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" ); - is ($MIME->parts, 0, "generated correspondence mime entity - does not have parts"); - is ($MIME->head->mime_type , "text/plain", "The mime type is a plain"); - }'; + *RT::Action::SendEmail::SendMessage = sub { + my $self = shift; + my $MIME = shift; + return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" ); + is ($MIME->parts, 0, "generated correspondence mime entity + does not have parts"); + is ($MIME->head->mime_type , "text/plain", "The mime type is a plain"); + }; } -# }}} -# {{{ test a text-html message with russian characters my $russian_email = RT::Test::get_relocatable_file('text-html-in-russian', (File::Spec->updir(), 'data', 'emails')); $content = RT::Test->file_content($russian_email); $parser->ParseMIMEEntityFromScalar($content); - # be as much like the mail gateway as possible. &text_html_redef_sendmessage; %args = (message => $content, queue => 1, action => 'correspond'); - RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new($RT::SystemUser); + +{ + +my @warnings; +local $SIG{__WARN__} = sub { + push @warnings, "@_"; +}; + +RT::Interface::Email::Gateway(\%args); + +TODO: { + local $TODO = +'need a better approach of encoding converter, should be fixed in 4.2'; +ok( @warnings == 1 || @warnings == 2, "1 or 2 warnings are ok" ); +ok( @warnings == 1 || ( @warnings == 2 && $warnings[1] eq $warnings[0] ), + 'if there are 2 warnings, they should be same' ); + +like( + $warnings[0], + qr/\QEncoding error: "\x{041f}" does not map to iso-8859-1/, +"The badly formed Russian spam we have isn't actually well-formed UTF8, which makes Encode (correctly) warn", +); + +} +} + + $tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick = $tickets->First(); @@ -334,9 +350,7 @@ like (first_attach($tick)->ContentType , qr/text\/html/, "We recorded the conten is (count_attachs($tick) ,1 , "Has one attachment, presumably a text-html and a multipart alternative"); -# }}} -# {{{ test a message containing a russian subject and NO content type RT->Config->Set( EmailInputEncodings => 'koi8-r', RT->Config->Get('EmailInputEncodings') ); RT->Config->Set( EmailOutputEncoding => 'koi8-r' ); @@ -351,7 +365,7 @@ $parser->ParseMIMEEntityFromScalar($content); &text_plain_russian_redef_sendmessage; %args = (message => $content, queue => 1, action => 'correspond'); RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new($RT::SystemUser); + $tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick= $tickets->First(); @@ -362,26 +376,23 @@ is (count_attachs($tick) ,1 , "Has one attachment, presumably a text-plain"); is ($tick->Subject, "\x{442}\x{435}\x{441}\x{442} \x{442}\x{435}\x{441}\x{442}", "Recorded the subject right"); sub text_plain_russian_redef_sendmessage { no warnings qw/redefine/; - eval 'sub RT::Action::SendEmail::SendMessage { - my $self = shift; - my $MIME = shift; - return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" ); - is ($MIME->head->mime_type , "text/plain", "The only part is text/plain "); - my $subject = $MIME->head->get("subject"); - chomp($subject); - #is( $subject , /^=\?KOI8-R\?B\?W2V4YW1wbGUuY39tICM3XSDUxdPUINTF09Q=\?=/ , "The $subject is encoded correctly"); - }; - '; + *RT::Action::SendEmail::SendMessage = sub { + my $self = shift; + my $MIME = shift; + return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" ); + is ($MIME->head->mime_type , "text/plain", "The only part is text/plain "); + my $subject = $MIME->head->get("subject"); + chomp($subject); + #is( $subject , /^=\?KOI8-R\?B\?W2V4YW1wbGUuY39tICM3XSDUxdPUINTF09Q=\?=/ , "The $subject is encoded correctly"); + }; } my @input_encodings = RT->Config->Get( 'EmailInputEncodings' ); shift @input_encodings; RT->Config->Set(EmailInputEncodings => @input_encodings ); RT->Config->Set(EmailOutputEncoding => 'utf-8'); -# }}} -# {{{ test a message containing a nested RFC 822 message my $nested_rfc822_email = RT::Test::get_relocatable_file('nested-rfc-822', (File::Spec->updir(), 'data', 'emails')); @@ -395,7 +406,7 @@ $parser->ParseMIMEEntityFromScalar($content); &text_plain_nested_redef_sendmessage; %args = (message => $content, queue => 1, action => 'correspond'); RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new($RT::SystemUser); + $tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick= $tickets->First(); @@ -405,24 +416,28 @@ like (first_attach($tick)->ContentType , qr/multipart\/mixed/, "We recorded the is (count_attachs($tick) , 5 , "Has one attachment, presumably a text-plain and a message RFC 822 and another plain"); sub text_plain_nested_redef_sendmessage { no warnings qw/redefine/; - eval 'sub RT::Action::SendEmail::SendMessage { - my $self = shift; - my $MIME = shift; - return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" ); - is ($MIME->head->mime_type , "multipart/mixed", "It is a mixed multipart"); - my $subject = $MIME->head->get("subject"); - $subject = MIME::Base64::decode_base64( $subject); - chomp($subject); - # TODO, why does this test fail - #ok($subject =~ qr{Niv\x{e5}er}, "The subject matches the word - $subject"); - 1; - }'; + *RT::Action::SendEmail::SendMessage = sub { + my $self = shift; + my $MIME = shift; + + return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" ); + + is ($MIME->head->mime_type , "multipart/mixed", "It is a mixed multipart"); + + use MIME::Words qw(:all); + my $encoded_subject = $MIME->head->get("subject"); + my $subject = decode_mimewords($encoded_subject); + + # MIME::Words isn't actually UTF8-safe. There go 4 hours I'll never get back. + utf8::decode($subject); + like($subject, qr/Niv\x{e5}er/, "The subject matches the word - $subject"); + + 1; + }; } -# }}} -# {{{ test a multipart alternative containing a uuencoded mesage generated by lotus notes my $uuencoded_email = RT::Test::get_relocatable_file('notes-uuencoded', (File::Spec->updir(), 'data', 'emails')); @@ -437,7 +452,7 @@ $parser->ParseMIMEEntityFromScalar($content); local *RT::Action::SendEmail::SendMessage = sub { return 1}; %args = (message => $content, queue => 1, action => 'correspond'); RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new($RT::SystemUser); + $tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick= $tickets->First(); @@ -447,9 +462,7 @@ $parser->ParseMIMEEntityFromScalar($content); is (count_attachs($tick) , 3 , "Has three attachments"); } -# }}} -# {{{ test a multipart that crashes the file-based mime-parser works my $crashes_file_based_parser_email = RT::Test::get_relocatable_file( 'crashes-file-based-parser', (File::Spec->updir(), 'data', 'emails')); @@ -464,7 +477,7 @@ no warnings qw/redefine/; local *RT::Action::SendEmail::SendMessage = sub { return 1}; %args = (message => $content, queue => 1, action => 'correspond'); RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new($RT::SystemUser); + $tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick= $tickets->First(); @@ -476,9 +489,7 @@ is (count_attachs($tick) , 5 , "Has three attachments"); -# }}} -# {{{ test a multi-line RT-Send-CC header my $rt_send_cc_email = RT::Test::get_relocatable_file('rt-send-cc', (File::Spec->updir(), 'data', 'emails')); @@ -490,7 +501,7 @@ $parser->ParseMIMEEntityFromScalar($content); %args = (message => $content, queue => 1, action => 'correspond'); RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new($RT::SystemUser); + $tickets = RT::Tickets->new(RT->SystemUser); $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); $tick= $tickets->First(); @@ -503,9 +514,8 @@ like ($cc , qr/test3/, "Found test 3"); like ($cc , qr/test4/, "Found test 4"); like ($cc , qr/test5/, "Found test 5"); -# }}} -diag q{regression test for #5248 from rt3.fsck.com} if $ENV{TEST_VERBOSE}; +diag q{regression test for #5248 from rt3.fsck.com}; { my $subject_folding_email = RT::Test::get_relocatable_file( 'subject-with-folding-ws', (File::Spec->updir(), 'data', 'emails')); @@ -518,7 +528,7 @@ diag q{regression test for #5248 from rt3.fsck.com} if $ENV{TEST_VERBOSE}; is ($ticket->Subject, 'test', 'correct subject'); } -diag q{regression test for #5248 from rt3.fsck.com} if $ENV{TEST_VERBOSE}; +diag q{regression test for #5248 from rt3.fsck.com}; { my $long_subject_email = RT::Test::get_relocatable_file('very-long-subject', (File::Spec->updir(), 'data', 'emails')); @@ -535,4 +545,3 @@ diag q{regression test for #5248 from rt3.fsck.com} if $ENV{TEST_VERBOSE}; # Don't taint the environment $everyone->PrincipalObj->RevokeRight(Right =>'SuperUser'); -1; diff --git a/rt/t/mail/threading.t b/rt/t/mail/threading.t new file mode 100644 index 000000000..7112ecf07 --- /dev/null +++ b/rt/t/mail/threading.t @@ -0,0 +1,90 @@ +#!/usr/bin/perl +use strict; +use warnings; +use utf8; + +use RT::Test tests => 22; +RT->Config->Set( NotifyActor => 1 ); + +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user = RT::Test->load_or_create_user( + Name => 'root', + EmailAddress => 'root@localhost', +); +ok $user && $user->id, 'loaded or created user'; + +{ + my $mail = <<EOF; +From: root\@localhost +Subject: a ticket +Message-ID: <some-message-id> + +Foob! +EOF + my ($status, $id) = RT::Test->send_via_mailgate($mail); + ok $id, "created a ticket"; + + my @mail = RT::Test->fetch_caught_mails; + is scalar @mail, 1, "autoreply"; + like $mail[0], qr{^In-Reply-To:\s*<some-message-id>$}mi; + like $mail[0], qr{^References:\s*<RT-Ticket-\Q$id\E\@example\.com>}mi; + + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load( $id ); + ok $ticket->id, "loaded ticket"; + + ($status, my ($msg)) = $ticket->Correspond( Content => 'boo' ); + ok $status, "replied to the ticket"; + + @mail = RT::Test->fetch_caught_mails; + is scalar @mail, 1, "reply"; + like $mail[0], qr{^References:\s*<RT-Ticket-\Q$id\E\@example\.com>$}mi, + "no context, so only pseudo header is referenced"; +} + +{ + my ($ticket) = RT::Test->create_ticket( + Queue => $queue->id, + Requestor => $user->EmailAddress + ); + my $id = $ticket->id; + ok $id, "created a ticket"; + + my @mail = RT::Test->fetch_caught_mails; + is scalar @mail, 1, "autoreply"; + like $mail[0], qr{^References:\s*<RT-Ticket-\Q$id\E\@example\.com>}mi; +} + +{ + my $scrip = RT::Scrip->new(RT->SystemUser); + my ($status, $msg) = $scrip->Create( + Description => "Notify requestor on status change", + ScripCondition => 'On Status Change', + ScripAction => 'Notify Requestors', + Template => 'Transaction', + Stage => 'TransactionCreate', + Queue => 0, + ); + ok($status, "Scrip created"); + + my ($ticket) = RT::Test->create_ticket( + Queue => $queue->id, + Requestor => $user->EmailAddress, + ); + my $id = $ticket->id; + ok $id, "created a ticket"; + + RT::Test->fetch_caught_mails; + ($status, $msg) = $ticket->SetStatus('open'); + ok $status, "changed status"; + + my @mail = RT::Test->fetch_caught_mails; + is scalar @mail, 1, "status change notification"; + like $mail[0], qr{^References:\s*<RT-Ticket-\Q$id\E\@example\.com>}mi; +} diff --git a/rt/t/mail/verp.t b/rt/t/mail/verp.t index 79ede90ab..ed3af6a7b 100644 --- a/rt/t/mail/verp.t +++ b/rt/t/mail/verp.t @@ -1,7 +1,7 @@ #!/usr/bin/perl -w use strict; -use RT::Test tests => 1; +use RT::Test nodb => 1, tests => 1; TODO: { todo_skip "No tests written for VERP yet", 1; ok(1,"a test to skip"); diff --git a/rt/t/mail/wrong_mime_charset.t b/rt/t/mail/wrong_mime_charset.t index 71a574f26..511a7e61d 100644 --- a/rt/t/mail/wrong_mime_charset.t +++ b/rt/t/mail/wrong_mime_charset.t @@ -1,7 +1,7 @@ #!/usr/bin/perl use strict; use warnings; -use RT::Test nodata => 1, tests => 3; +use RT::Test nodb => 1, tests => 6; use_ok('RT::I18N'); use utf8; @@ -16,12 +16,31 @@ my $mime = MIME::Entity->build( # set the wrong charset mime in purpose $mime->head->mime_attr( "Content-Type.charset" => 'utf8' ); +my @warnings; +local $SIG{__WARN__} = sub { + push @warnings, "@_"; +}; + RT::I18N::SetMIMEEntityToEncoding( $mime, 'iso-8859-1' ); TODO: { local $TODO = 'need a better approach of encoding converter, should be fixed in 4.2'; +# this is a weird behavior for different perl versions, 5.12 warns twice, +# which is correct since we do the encoding thing twice, for Subject +# and Data respectively. +# but 5.8 and 5.10 warns only once. +ok( @warnings == 1 || @warnings == 2, "1 or 2 warnings are ok" ); +ok( @warnings == 1 || ( @warnings == 2 && $warnings[1] eq $warnings[0] ), + 'if there are 2 warnings, they should be same' ); + +like( + $warnings[0], + qr/\QEncoding error: "\x{fffd}" does not map to iso-8859-1/, +"We can't encode something into the wrong encoding without Encode complaining" +); + my $subject = decode( 'iso-8859-1', $mime->head->get('Subject') ); chomp $subject; is( $subject, $test_string, 'subject is set to iso-8859-1' ); diff --git a/rt/t/ticket/clicky.t b/rt/t/ticket/clicky.t new file mode 100644 index 000000000..3a7ec8241 --- /dev/null +++ b/rt/t/ticket/clicky.t @@ -0,0 +1,142 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Test::More; +use RT::Test tests => 20; + +my $plain = MIME::Entity->build( + Subject => 'plain mime', + Type => 'text/plain', + Data => <<END, +If you have some problems with RT you could find help +on http://wiki.bestpractical.com or subscribe to +the rt-users\@lists.bestpractical.com. + +to test anchor: +https://wiki.bestpractical.com/test#anchor +-- +Best regards. BestPractical Team. +END +); + +my $html = MIME::Entity->build( + Type => 'text/html', + Subject => 'html mime', + Data => <<END, +If you have some problems with RT you could find help +on <a href="http://wiki.bestpractical.com">wiki</a> +or find known bugs on http://rt3.fsck.com + +to test anchor: +https://wiki.bestpractical.com/test#anchor +-- +Best regards. BestPractical Team. +END +); + + +my $ticket = RT::Ticket->new( RT->SystemUser ); + +my ($plain_id) = $ticket->Create( + Subject => 'test', + Queue => 'General', + MIMEObj => $plain, +); +ok($plain_id, "We created a ticket #$plain_id"); + +my ($html_id) = $ticket->Create( + Subject => 'test', + Queue => 'General', + MIMEObj => $html, +); +ok($html_id, "We created a ticket #$html_id"); + +diag 'test no clicky'; +{ + RT->Config->Set( 'Active_MakeClicky' => () ); + my ( $baseurl, $m ) = RT::Test->started_ok; + ok $m->login, 'logged in'; + $m->goto_ticket($plain_id); + + my @links = $m->find_link( + tag => 'a', + url => 'http://wiki.bestpractical.com', + ); + ok( @links == 0, 'no clicky link found with plain message' ); + + @links = $m->find_link( + tag => 'a', + url => 'http://rt3.fsck.com', + ); + ok( @links == 0, 'no extra clicky link found with html message' ); +} + +diag 'test httpurl'; +{ + RT::Test->stop_server; + RT->Config->Set( 'Active_MakeClicky' => qw/httpurl/ ); + my ( $baseurl, $m ) = RT::Test->started_ok; + ok $m->login, 'logged in'; + $m->goto_ticket($plain_id); + + my @links = $m->find_link( + tag => 'a', + url => 'http://wiki.bestpractical.com', + text => 'Open URL', + ); + ok( scalar @links, 'found clicky link' ); + + @links = $m->find_link( + tag => 'a', + url => 'https://wiki.bestpractical.com/test#anchor', + text => 'Open URL', + ); + ok( scalar @links, 'found clicky link with anchor' ); + + $m->goto_ticket($html_id); + @links = $m->find_link( + tag => 'a', + url => 'http://wiki.bestpractical.com', + text => 'Open URL', + ); + ok( @links == 0, 'not make clicky links clicky twice' ); + + @links = $m->find_link( + tag => 'a', + url => 'http://rt3.fsck.com', + text => 'Open URL', + ); + ok( scalar @links, 'found clicky link' ); + + @links = $m->find_link( + tag => 'a', + url => 'https://wiki.bestpractical.com/test#anchor', + text => 'Open URL', + ); + ok( scalar @links, 'found clicky link with anchor' ); +} + +diag 'test httpurl_overwrite'; +{ + RT::Test->stop_server; + RT->Config->Set( 'Active_MakeClicky' => 'httpurl_overwrite' ); + my ( $baseurl, $m ) = RT::Test->started_ok; + ok $m->login, 'logged in'; + ok $m->goto_ticket($plain_id), 'opened diplay page of the ticket'; + + my @links = $m->find_link( + tag => 'a', + url => 'http://wiki.bestpractical.com', + text => 'http://wiki.bestpractical.com', + ); + ok( scalar @links, 'found clicky link' ); + + @links = $m->find_link( + tag => 'a', + url => 'https://wiki.bestpractical.com/test#anchor', + text => 'https://wiki.bestpractical.com/test#anchor', + ); + ok( scalar @links, 'found clicky link with anchor' ); +} + diff --git a/rt/t/ticket/googleish_search.t b/rt/t/ticket/googleish_search.t new file mode 100644 index 000000000..aa33257f1 --- /dev/null +++ b/rt/t/ticket/googleish_search.t @@ -0,0 +1,43 @@ + +#!/usr/bin/perl -w + +use strict; +use warnings; + +use RT::Test tests => 12; +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' )", + "General" => "( Queue = 'General' ) AND ( $active_statuses )", + "General any" => "( Queue = 'General' )", + "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/web/admin_groups.t b/rt/t/web/admin_groups.t new file mode 100644 index 000000000..783b5ad44 --- /dev/null +++ b/rt/t/web/admin_groups.t @@ -0,0 +1,59 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use RT::Test tests => 26; + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login(), 'logged in' ); + +{ + diag "test creating a group" if $ENV{TEST_VERBOSE}; + $m->get_ok( $url . '/Admin/Groups/Modify.html?Create=1' ); + $m->content_contains('Create a new group', 'found title'); + $m->submit_form_ok({ + form_number => 3, + fields => { Name => 'test group' }, + }); + $m->content_contains('Group created', 'found results'); + $m->content_contains('Modify the group test group', 'found title'); +} + +{ + diag "test creating another group" if $ENV{TEST_VERBOSE}; + $m->get_ok( $url . '/Admin/Groups/Modify.html?Create=1' ); + $m->content_contains('Create a new group', 'found title'); + $m->submit_form_ok({ + form_number => 3, + fields => { Name => 'test group2' }, + }); + $m->content_contains('Group created', 'found results'); + $m->content_contains('Modify the group test group2', 'found title'); +} + +{ + diag "test creating an overlapping group" if $ENV{TEST_VERBOSE}; + $m->get_ok( $url . '/Admin/Groups/Modify.html?Create=1' ); + $m->content_contains('Create a new group', 'found title'); + $m->submit_form_ok({ + form_number => 3, + fields => { Name => 'test group' }, + }); + $m->content_contains('Group could not be created', 'found results'); + $m->content_like(qr/Group name .+? is already in use/, 'found message'); +} + +{ + diag "test updating a group name to overlap" if $ENV{TEST_VERBOSE}; + $m->get_ok( $url . '/Admin/Groups/' ); + $m->follow_link_ok({text => 'test group2'}, 'found title'); + $m->content_contains('Modify the group test group2'); + $m->submit_form_ok({ + form_number => 3, + fields => { Name => 'test group' }, + }); + $m->content_lacks('Name changed', "name not changed"); + $m->content_contains('Illegal value for Name', 'found error message'); + $m->content_contains('test group', 'did not find new name'); +} + diff --git a/rt/t/web/admin_user.t b/rt/t/web/admin_user.t new file mode 100644 index 000000000..fde075f4d --- /dev/null +++ b/rt/t/web/admin_user.t @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 7; + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login(), 'logged in' ); + +my $root = RT::User->new( $RT::SystemUser ); +$root->Load('root'); +ok( $root->id, 'loaded root' ); + + +diag "test the history page" if $ENV{TEST_VERBOSE}; +$m->get_ok( $url . '/Admin/Users/History.html?id=' . $root->id ); +$m->content_contains('User created', 'has User created entry'); + +# TODO more /Admin/Users tests + diff --git a/rt/t/web/articles-links.t b/rt/t/web/articles-links.t new file mode 100644 index 000000000..713dc05e4 --- /dev/null +++ b/rt/t/web/articles-links.t @@ -0,0 +1,52 @@ +use strict; +use warnings; + +use RT::Test tests => 18; + +RT->Config->Set( MasonLocalComponentRoot => RT::Test::get_abs_relocatable_dir('html') ); + +my ($baseurl, $m) = RT::Test->started_ok; + +my $queue = RT::Queue->new(RT->SystemUser); +$queue->Load('General'); + +my $class = RT::Class->new(RT->SystemUser); +my ($ok, $msg) = $class->Create(Name => "issues"); +ok($ok, "created class: $msg"); + +($ok, $msg) = $class->AddToObject($queue); +ok($ok, "applied class to General: $msg"); + +my $article = RT::Article->new(RT->SystemUser); +($ok, $msg) = $article->Create(Name => "instance of ticket #17421", Class => $class->id); +ok($ok, "created article: $msg"); + +ok($m->login, "logged in"); + +my $ticket = RT::Test->create_ticket(Queue => $queue->Id, Subject => 'oh wow! an AUTOLOAD bug'); + +$m->goto_ticket($ticket->id); +$m->follow_link_ok({text => 'Reply'}); + +$m->form_name('TicketUpdate'); +$m->field('Articles-Include-Article-Named' => $article->Name); +$m->submit; + +$m->content_contains('instance of ticket #17421', 'got the name of the article in the ticket'); + +# delete RT::Article's Name method on the server so we'll need to AUTOLOAD it +my $clone = $m->clone; +$clone->get_ok('/delete-article-name-method.html'); +like($clone->content, qr/{deleted}/); + +$m->form_name('TicketUpdate'); +$m->click('SubmitTicket'); + +$m->follow_link_ok({text => 'Links'}); + +$m->text_contains('Article ' . $article->id . ': instance of ticket #17421', 'Article appears with its name in the links table'); + +my $refers_to = $ticket->RefersTo; +is($refers_to->Count, 1, 'the ticket has a refers-to link'); +is($refers_to->First->TargetURI->URI, 'fsck.com-article://example.com/article/' . $article->Id, 'when we included the article it created a refers-to'); + diff --git a/rt/t/web/attachment-with-name-0.t b/rt/t/web/attachment-with-name-0.t new file mode 100644 index 000000000..12a8dd548 --- /dev/null +++ b/rt/t/web/attachment-with-name-0.t @@ -0,0 +1,23 @@ +use strict; +use warnings; + +use RT::Test tests => 8; +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; + +use File::Spec; + +my $file = File::Spec->catfile( RT::Test->temp_directory, 0 ); +open my $fh, '>', $file or die $!; +print $fh 'foobar'; +close $fh; + +$m->get_ok( '/Ticket/Create.html?Queue=1' ); + +$m->submit_form( + form_number => 3, + fields => { Subject => 'test att 0', Content => 'test', Attach => $file }, +); +$m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); +$m->follow_link_ok( { text => 'Download 0' } ); +$m->content_contains( 'foobar', 'file content' ); diff --git a/rt/t/web/attachment_encoding.t b/rt/t/web/attachment_encoding.t index 9ba567746..bfbf24590 100644 --- a/rt/t/web/attachment_encoding.t +++ b/rt/t/web/attachment_encoding.t @@ -3,13 +3,11 @@ use strict; use warnings; -use RT::Test tests => 28; +use RT::Test tests => 32; use Encode; my ( $baseurl, $m ) = RT::Test->started_ok; ok $m->login, 'logged in as root'; -$RT::Test::SKIP_REQUEST_WORK_AROUND = 1; - use utf8; use File::Spec; @@ -19,7 +17,7 @@ diag 'test without attachments' if $ENV{TEST_VERBOSE}; { $m->get_ok( $baseurl . '/Ticket/Create.html?Queue=1' ); - $m->form_number(3); + $m->form_name('TicketModify'); $m->submit_form( form_number => 3, fields => { Subject => '标题', Content => '测试' }, @@ -47,7 +45,7 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; { my $file = - File::Spec->catfile( File::Spec->tmpdir, 'rt_attachemnt_abcde.txt' ); + File::Spec->catfile( RT::Test->temp_directory, encode_utf8 '附件.txt' ); open( my $fh, '>', $file ) or die $!; binmode $fh, ':utf8'; print $fh '附件'; @@ -55,12 +53,14 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; $m->get_ok( $baseurl . '/Ticket/Create.html?Queue=1' ); - $m->form_number(3); + $m->form_name('TicketModify'); $m->submit_form( form_number => 3, fields => { Subject => '标题', Content => '测试', Attach => $file }, ); $m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); + $m->content_contains( '附件.txt', 'attached filename' ); + $m->content_lacks( encode_utf8 '附件.txt', 'no double encoded attached filename' ); $m->follow_link_ok( { text => 'with headers' }, '-> /Ticket/Attachment/WithHeaders/...' ); @@ -82,7 +82,7 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; $m->back; $m->back; - $m->follow_link_ok( { text_regex => qr/by Enoch Root/ }, + $m->follow_link_ok( { text => 'Download 附件.txt' }, '-> /Ticket/Attachment/...' ); $m->content_contains( '附件', 'has content 附件' ); @@ -100,4 +100,3 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; unlink $file; } - diff --git a/rt/t/web/attachments.t b/rt/t/web/attachments.t index e827b2f02..8c75f6caf 100644 --- a/rt/t/web/attachments.t +++ b/rt/t/web/attachments.t @@ -1,15 +1,15 @@ #!/usr/bin/perl -w use strict; -use RT::Test tests => 14; +use RT::Test tests => 25; -use constant LogoFile => $RT::MasonComponentRoot .'/NoAuth/images/bplogo.gif'; +use constant LogoFile => $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png'; use constant FaviconFile => $RT::MasonComponentRoot .'/NoAuth/images/favicon.png'; my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'logged in'; -my $queue = RT::Queue->new($RT::Nobody); +my $queue = RT::Queue->new(RT->Nobody); my $qid = $queue->Load('General'); ok( $qid, "Loaded General queue" ); @@ -17,7 +17,7 @@ $m->form_name('CreateTicketInQueue'); $m->field('Queue', $qid); $m->submit; is($m->status, 200, "request successful"); -$m->content_like(qr/Create a new ticket/, 'ticket create page'); +$m->content_contains("Create a new ticket", 'ticket create page'); $m->form_name('TicketCreate'); $m->field('Subject', 'Attachments test'); @@ -26,9 +26,9 @@ $m->field('Content', 'Some content'); $m->submit; is($m->status, 200, "request successful"); -$m->content_like(qr/Attachments test/, 'we have subject on the page'); -$m->content_like(qr/Some content/, 'and content'); -$m->content_like(qr/Download bplogo\.gif/, 'page has file name'); +$m->content_contains('Attachments test', 'we have subject on the page'); +$m->content_contains('Some content', 'and content'); +$m->content_contains('Download bpslogo.png', 'page has file name'); $m->follow_link_ok({text => 'Reply'}, "reply to the ticket"); $m->form_name('TicketUpdate'); @@ -42,6 +42,34 @@ $m->field('UpdateContent', 'Message'); $m->click('SubmitTicket'); is($m->status, 200, "request successful"); -$m->content_like(qr/Download bplogo\.gif/, 'page has file name'); -$m->content_like(qr/Download favicon\.png/, 'page has file name'); +$m->content_contains('Download bpslogo.png', 'page has file name'); +$m->content_contains('Download favicon.png', 'page has file name'); + +diag "test mobile ui"; +$m->get_ok( $baseurl . '/m/ticket/create?Queue=' . $qid ); + +$m->form_name('TicketCreate'); +$m->field('Subject', 'Attachments test'); +$m->field('Attach', LogoFile); +$m->field('Content', 'Some content'); +$m->submit; +is($m->status, 200, "request successful"); + +$m->content_contains('Attachments test', 'we have subject on the page'); +$m->content_contains('bpslogo.png', 'page has file name'); + +$m->follow_link_ok({text => 'Reply'}, "reply to the ticket"); +$m->form_name('TicketUpdate'); +$m->field('Attach', LogoFile); +$m->click('AddMoreAttach'); +is($m->status, 200, "request successful"); + +$m->form_name('TicketUpdate'); +$m->field('Attach', FaviconFile); +$m->field('UpdateContent', 'Message'); +$m->click('SubmitTicket'); +is($m->status, 200, "request successful"); + +$m->content_contains('bpslogo.png', 'page has file name'); +$m->content_contains('favicon.png', 'page has file name'); diff --git a/rt/t/web/basic.t b/rt/t/web/basic.t index 3f94e732f..31e28c406 100644 --- a/rt/t/web/basic.t +++ b/rt/t/web/basic.t @@ -4,37 +4,35 @@ use strict; use warnings; use Encode; -use RT::Test tests => 21; -$RT::Test::SKIP_REQUEST_WORK_AROUND = 1; +use RT::Test tests => 23; my ($baseurl, $agent) = RT::Test->started_ok; my $url = $agent->rt_base_url; -diag $url if $ENV{TEST_VERBOSE}; # get the top page { $agent->get($url); - is ($agent->{'status'}, 200, "Loaded a page"); + is ($agent->status, 200, "Loaded a page"); } # test a login { $agent->login('root' => 'password'); # the field isn't named, so we have to click link 0 - is( $agent->{'status'}, 200, "Fetched the page ok"); - ok( $agent->{'content'} =~ /Logout/i, "Found a logout link"); + is( $agent->status, 200, "Fetched the page ok"); + $agent->content_contains("Logout", "Found a logout link"); } { - $agent->get($url."Ticket/Create.html?Queue=1"); - is ($agent->{'status'}, 200, "Loaded Create.html"); - $agent->form_number(3); + $agent->goto_create_ticket(1); + is ($agent->status, 200, "Loaded Create.html"); + $agent->form_name('TicketCreate'); my $string = Encode::decode_utf8("I18N Web Testing æøå"); $agent->field('Subject' => "Ticket with utf8 body"); $agent->field('Content' => $string); ok($agent->submit, "Created new ticket with $string as Content"); - $agent->content_like( qr{$string} , "Found the content"); + $agent->content_contains($string, "Found the content"); ok($agent->{redirected_uri}, "Did redirection"); { @@ -48,15 +46,15 @@ diag $url if $ENV{TEST_VERBOSE}; } { - $agent->get($url."Ticket/Create.html?Queue=1"); - is ($agent->{'status'}, 200, "Loaded Create.html"); - $agent->form_number(3); + $agent->goto_create_ticket(1); + is ($agent->status, 200, "Loaded Create.html"); + $agent->form_name('TicketCreate'); my $string = Encode::decode_utf8("I18N Web Testing æøå"); $agent->field('Subject' => $string); $agent->field('Content' => "Ticket with utf8 subject"); ok($agent->submit, "Created new ticket with $string as Content"); - $agent->content_like( qr{$string} , "Found the content"); + $agent->content_contains($string, "Found the content"); ok($agent->{redirected_uri}, "Did redirection"); { @@ -71,14 +69,13 @@ diag $url if $ENV{TEST_VERBOSE}; # Update time worked in hours { $agent->follow_link( text_regex => qr/Basics/ ); - $agent->submit_form( form_number => 3, + $agent->submit_form( form_name => 'TicketModify', fields => { TimeWorked => 5, 'TimeWorked-TimeUnits' => "hours" } ); - like ($agent->{'content'}, qr/to '300'/, "5 hours is 300 minutes"); + $agent->content_contains("to '300'", "5 hours is 300 minutes"); } -# {{{ test an image TODO: { todo_skip("Need to handle mason trying to compile images",1); @@ -94,9 +91,7 @@ is( "got a file of the correct size ($file)", ); } -# }}} -# {{{ Query Builder tests # # XXX: hey-ho, we have these tests in t/web/query-builder # TODO: move everything about QB there @@ -135,5 +130,3 @@ $query =~ s/\s+/ /g; is ($query, "Subject LIKE 'aaa' AND Subject LIKE 'bbb'"); - -1; diff --git a/rt/t/web/case-sensitivity.t b/rt/t/web/case-sensitivity.t new file mode 100644 index 000000000..276b7615a --- /dev/null +++ b/rt/t/web/case-sensitivity.t @@ -0,0 +1,85 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 18; + +my $q = RT::Test->load_or_create_queue( Name => 'General' ); +ok $q && $q->id, 'loaded or created queue'; + +my ($root, $root_id); +{ + $root = RT::User->new( RT->SystemUser ); + $root->Load('root'); + ok $root_id = $root->id, 'found root'; +} + +my ($baseurl, $m) = RT::Test->started_ok; +$m->login; + +# test users auto completer +{ + $m->get_ok('/Helpers/Autocomplete/Users?term=eNo'); + require JSON; + is_deeply( + JSON::from_json( $m->content ), + [{"value" => "root\@localhost","label" => "Enoch Root"}] + ); +} + +# test ticket's People page +{ + my $ticket = RT::Test->create_ticket( Queue => $q->id ); + ok $ticket && $ticket->id, "created ticket"; + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok( {text => 'People'} ); + $m->form_number(3); + $m->select( UserField => 'RealName' ); + $m->field( UserString => 'eNo' ); + $m->click('OnlySearchForPeople'); + + my $form = $m->form_number(3); + my $input = $form->find_input('Ticket-AddWatcher-Principal-'. $root->id ); + ok $input, 'input is there'; +} + +# test users' admin UI +{ + $m->get_ok('/Admin/Users/'); + + $m->form_number(4); + $m->select( UserField => 'RealName' ); + $m->field( UserString => 'eNo' ); + $m->submit; + + like $m->uri, qr{\QAdmin/Users/Modify.html?id=$root_id\E}; +} + +# create a cf for testing +my $cf; +{ + $cf = RT::CustomField->new(RT->SystemUser); + my ($id,$msg) = $cf->Create( + Name => 'Test', + Type => 'Select', + MaxValues => '1', + Queue => $q->id, + ); + ok($id,$msg); + + ($id,$msg) = $cf->AddValue(Name => 'Enoch', Description => 'Root'); + ok($id,$msg); +} + +# test custom field values auto completer +{ + $m->get_ok('/Helpers/Autocomplete/CustomFieldValues?term=eNo&Object---CustomField-'. $cf->id .'-Value'); + require JSON; + is_deeply( + JSON::from_json( $m->content ), + [{"value" => "Enoch","label" => "Enoch (Root)"}] + ); +} + diff --git a/rt/t/web/cf_access.t b/rt/t/web/cf_access.t index 183597b4a..73b7765c1 100644 --- a/rt/t/web/cf_access.t +++ b/rt/t/web/cf_access.t @@ -1,23 +1,54 @@ #!/usr/bin/perl -w use strict; -use RT::Test tests => 26; -$RT::Test::SKIP_REQUEST_WORK_AROUND = 1; +use RT::Test tests => 32; my ($baseurl, $m) = RT::Test->started_ok; -use constant ImageFile => $RT::MasonComponentRoot .'/NoAuth/images/bplogo.gif'; +use constant ImageFile => $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png'; use constant ImageFileContent => RT::Test->file_content(ImageFile); ok $m->login, 'logged in'; -diag "Create a CF" if $ENV{'TEST_VERBOSE'}; +diag "Create a CF"; { - $m->follow_link( text => 'Configuration' ); - $m->title_is(q/RT Administration/, 'admin screen'); - $m->follow_link( text => 'Custom Fields' ); - $m->title_is(q/Select a Custom Field/, 'admin-cf screen'); - $m->follow_link( text => 'Create' ); + $m->follow_link( id => 'tools-config-custom-fields-create'); + + # Test form validation + $m->submit_form( + form_name => "ModifyCustomField", + fields => { + TypeComposite => 'Image-0', + LookupType => 'RT::Queue-RT::Ticket', + Name => '', + Description => 'img', + }, + ); + $m->text_contains('Invalid value for Name'); + + $m->submit_form( + form_name => "ModifyCustomField", + fields => { + TypeComposite => 'Image-0', + LookupType => 'RT::Queue-RT::Ticket', + Name => '0', + Description => 'img', + }, + ); + $m->text_contains('Invalid value for Name'); + + $m->submit_form( + form_name => "ModifyCustomField", + fields => { + TypeComposite => 'Image-0', + LookupType => 'RT::Queue-RT::Ticket', + Name => '1', + Description => 'img', + }, + ); + $m->text_contains('Invalid value for Name'); + + # The real submission $m->submit_form( form_name => "ModifyCustomField", fields => { @@ -27,19 +58,47 @@ diag "Create a CF" if $ENV{'TEST_VERBOSE'}; Description => 'img', }, ); + $m->text_contains('Object created'); + + # Validation on update + $m->form_name("ModifyCustomField"); + $m->set_fields( + TypeComposite => 'Image-0', + LookupType => 'RT::Queue-RT::Ticket', + Name => '', + Description => 'img', + ); + $m->click('Update'); + $m->text_contains('Illegal value for Name'); + $m->form_name("ModifyCustomField"); + $m->set_fields( + TypeComposite => 'Image-0', + LookupType => 'RT::Queue-RT::Ticket', + Name => '0', + Description => 'img', + ); + $m->click('Update'); + $m->text_contains('Illegal value for Name'); + $m->form_name("ModifyCustomField"); + $m->set_fields( + TypeComposite => 'Image-0', + LookupType => 'RT::Queue-RT::Ticket', + Name => '1', + Description => 'img', + ); + $m->click('Update'); + $m->text_contains('Illegal value for Name'); } -diag "apply the CF to General queue" if $ENV{'TEST_VERBOSE'}; +diag "apply the CF to General queue"; my ( $cf, $cfid, $tid ); { - $m->title_is(q/Created CustomField img/, 'admin-cf created'); - $m->follow_link( text => 'Queues' ); - $m->title_is(q/Admin queues/, 'admin-queues screen'); + $m->title_is(q/Editing CustomField img/, 'admin-cf created'); + $m->follow_link( id => 'tools-config-queues'); $m->follow_link( text => 'General' ); - $m->title_is(q/Editing Configuration for queue General/, 'admin-queue: general'); - $m->follow_link( text => 'Ticket Custom Fields' ); - - $m->title_is(q/Edit Custom Fields for General/, 'admin-queue: general cfid'); + $m->title_is(q/Configuration for queue General/, 'admin-queue: general'); + $m->follow_link( id => 'page-ticket-custom-fields'); + $m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid'); $m->form_name('EditCustomFields'); # Sort by numeric IDs in names @@ -52,7 +111,7 @@ my ( $cf, $cfid, $tid ); $m->tick( AddCustomField => $_ => 0 ) for @names; # ...and not any other. ;-) $m->click('UpdateCFs'); - $m->content_like( qr/Object created/, 'TCF added to the queue' ); + $m->content_contains('Object created', 'TCF added to the queue' ); } my $tester = RT::Test->load_or_create_user( Name => 'tester', Password => '123456' ); @@ -61,17 +120,16 @@ RT::Test->set_rights( Right => [qw(SeeQueue ShowTicket CreateTicket)], }, ); -ok $m->login( $tester->Name, 123456), 'logged in'; +ok $m->login( $tester->Name, 123456, logout => 1), 'logged in'; diag "check that we have no the CF on the create" - ." ticket page when user has no SeeCustomField right" - if $ENV{'TEST_VERBOSE'}; + ." ticket page when user has no SeeCustomField right"; { $m->submit_form( form_name => "CreateTicketInQueue", fields => { Queue => 'General' }, ); - $m->content_unlike(qr/Upload multiple images/, 'has no upload image field'); + $m->content_lacks('Upload multiple images', 'has no upload image field'); my $form = $m->form_name("TicketCreate"); my $upload_field = "Object-RT::Ticket--CustomField-$cfid-Upload"; @@ -83,9 +141,9 @@ diag "check that we have no the CF on the create" ); $m->content_like(qr/Ticket \d+ created/, "a ticket is created succesfully"); - $m->content_unlike(qr/img:/, 'has no img field on the page'); + $m->content_lacks('img:', 'has no img field on the page'); $m->follow_link( text => 'Custom Fields'); - $m->content_unlike(qr/Upload multiple images/, 'has no upload image field'); + $m->content_lacks('Upload multiple images', 'has no upload image field'); } RT::Test->set_rights( @@ -95,14 +153,13 @@ RT::Test->set_rights( ); diag "check that we have no the CF on the create" - ." ticket page when user has no ModifyCustomField right" - if $ENV{'TEST_VERBOSE'}; + ." ticket page when user has no ModifyCustomField right"; { $m->submit_form( form_name => "CreateTicketInQueue", fields => { Queue => 'General' }, ); - $m->content_unlike(qr/Upload multiple images/, 'has no upload image field'); + $m->content_lacks('Upload multiple images', 'has no upload image field'); my $form = $m->form_name("TicketCreate"); my $upload_field = "Object-RT::Ticket--CustomField-$cfid-Upload"; @@ -115,9 +172,9 @@ diag "check that we have no the CF on the create" $tid = $1 if $m->content =~ /Ticket (\d+) created/i; ok $tid, "a ticket is created succesfully"; - $m->follow_link( text => 'Custom Fields' ); - $m->content_unlike(qr/Upload multiple images/, 'has no upload image field'); - $form = $m->form_number(3); + $m->follow_link( id => 'page-basics'); + $m->content_lacks('Upload multiple images', 'has no upload image field'); + $form = $m->form_name('TicketModify'); $upload_field = "Object-RT::Ticket-$tid-CustomField-$cfid-Upload"; ok !$form->find_input( $upload_field ), 'no form field on the page'; } @@ -128,13 +185,13 @@ RT::Test->set_rights( }, ); -diag "create a ticket with an image" if $ENV{'TEST_VERBOSE'}; +diag "create a ticket with an image"; { $m->submit_form( form_name => "CreateTicketInQueue", fields => { Queue => 'General' }, ); - $m->content_like(qr/Upload multiple images/, 'has a upload image field'); + $m->content_contains('Upload multiple images', 'has a upload image field'); $cf =~ /(\d+)$/ or die "Hey this is impossible dude"; my $upload_field = "Object-RT::Ticket--CustomField-$1-Upload"; @@ -153,14 +210,12 @@ diag "create a ticket with an image" if $ENV{'TEST_VERBOSE'}; $m->title_like(qr/testing img cf creation/, "its title is the Subject"); - $m->follow_link( text => 'bplogo.gif' ); + $m->follow_link( text => 'bpslogo.png' ); $m->content_is(ImageFileContent, "it links to the uploaded image"); } $m->get( $m->rt_base_url ); -$m->follow_link( text => 'Tickets' ); -$m->follow_link( text => 'New Query' ); - +$m->follow_link( id => 'search-new'); $m->title_is(q/Query Builder/, 'Query building'); $m->submit_form( form_name => "BuildQuery", @@ -182,7 +237,7 @@ $m->click('AddCol'); $m->form_name('BuildQuery'); $m->click('DoSearch'); -$m->follow_link( text_regex => qr/bplogo\.gif/ ); +$m->follow_link( text_regex => qr/bpslogo\.png/ ); $m->content_is(ImageFileContent, "it links to the uploaded image"); __END__ diff --git a/rt/t/web/cf_date.t b/rt/t/web/cf_date.t new file mode 100644 index 000000000..887aa4220 --- /dev/null +++ b/rt/t/web/cf_date.t @@ -0,0 +1,187 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 35; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; +my $root = RT::User->new(RT->SystemUser); +ok( $root->Load('root'), 'load root user' ); + +my $cf_name = 'test cf date'; + +my $cfid; +diag "Create a CF"; +{ + $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->submit_form( + form_name => "ModifyCustomField", + fields => { + Name => $cf_name, + TypeComposite => 'Date-1', + LookupType => 'RT::Queue-RT::Ticket', + }, + ); + $m->content_contains('Object created', 'created CF sucessfully' ); + $cfid = $m->form_name('ModifyCustomField')->value('id'); + ok $cfid, "found id of the CF in the form, it's #$cfid"; +} + +diag "apply the CF to General queue"; +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok $queue && $queue->id, 'loaded or created queue'; + +{ + $m->follow_link( id => 'tools-config-queues-select'); + $m->title_is( q/Admin queues/, 'admin-queues screen' ); + $m->follow_link( text => 'General' ); + $m->title_is( q/Configuration for queue General/, + 'admin-queue: general' ); + $m->follow_link( text => 'Ticket Custom Fields' ); + $m->title_is( q/Custom Fields for queue General/, + 'admin-queue: general cfid' ); + + $m->form_name('EditCustomFields'); + $m->tick( "AddCustomField" => $cfid ); + $m->click('UpdateCFs'); + + $m->content_contains('Object created', 'TCF added to the queue' ); +} + +diag 'check valid inputs with various timezones in ticket create page'; +{ + my ( $ticket, $id ); + + $m->submit_form( + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, + ); + $m->content_contains('Select date', 'has cf field' ); + + $m->submit_form( + form_name => "TicketCreate", + fields => { + Subject => 'test 2010-05-04', + Content => 'test', + "Object-RT::Ticket--CustomField-$cfid-Values" => '2010-05-04', + }, + ); + ok( ($id) = $m->content =~ /Ticket (\d+) created/, "created ticket $id" ); + + $ticket = RT::Ticket->new(RT->SystemUser); + $ticket->Load($id); + is( $ticket->CustomFieldValues($cfid)->First->Content, + '2010-05-04', 'date in db' ); + + $m->content_contains('test cf date:', 'has no cf date field on the page' ); + $m->content_contains('Tue May 04 2010', + 'has cf date value on the page' ); +} + +diag 'check search build page'; +{ + $m->get_ok( $baseurl . '/Search/Build.html?Query=Queue=1' ); + + $m->form_name('BuildQuery'); + my ($cf_op) = + $m->find_all_inputs( type => 'option', name_regex => qr/test cf date/ ); + is_deeply( + [ $cf_op->possible_values ], + [ '<', '=', '>' ], + 'right oprators' + ); + + my ($cf_field) = + $m->find_all_inputs( type => 'text', name_regex => qr/test cf date/ ); + $m->submit_form( + fields => { + $cf_op->name => '=', + $cf_field->name => '2010-05-04' + }, + button => 'DoSearch', + ); + + $m->content_contains( 'Found 1 ticket', 'Found 1 ticket' ); + $m->content_contains( '2010-05-04', 'got the right ticket' ); + $m->content_lacks( '2010-05-06', 'did not get the wrong ticket' ); + + $m->get_ok( $baseurl . '/Search/Build.html?Query=Queue=1' ); + $m->form_name('BuildQuery'); + $m->submit_form( + fields => { + $cf_op->name => '<', + $cf_field->name => '2010-05-05' + }, + button => 'DoSearch', + ); + $m->content_contains( 'Found 1 ticket', 'Found 1 ticket' ); + + $m->get_ok( $baseurl . '/Search/Build.html?Query=Queue=1' ); + $m->form_name('BuildQuery'); + $m->submit_form( + fields => { + $cf_op->name => '>', + $cf_field->name => '2010-05-03', + }, + button => 'DoSearch', + ); + $m->content_contains( 'Found 1 ticket', 'Found 1 ticket' ); + + $m->get_ok( $baseurl . '/Search/Build.html?Query=Queue=1' ); + $m->form_name('BuildQuery'); + $m->submit_form( + fields => { + $cf_op->name => '=', + $cf_field->name => '2010-05-05', + }, + button => 'DoSearch', + ); + $m->content_contains( 'Found 0 tickets', 'Found 0 tickets' ); + + $m->get_ok( $baseurl . '/Search/Build.html?Query=Queue=1' ); + $m->form_name('BuildQuery'); + $m->submit_form( + fields => { + $cf_op->name => '<', + $cf_field->name => '2010-05-03', + }, + button => 'DoSearch', + ); + $m->content_contains( 'Found 0 tickets', 'Found 0 tickets' ); + + $m->get_ok( $baseurl . '/Search/Build.html?Query=Queue=1' ); + $m->form_name('BuildQuery'); + $m->submit_form( + fields => { + $cf_op->name => '>', + $cf_field->name => '2010-05-05', + }, + button => 'DoSearch', + ); + $m->content_contains( 'Found 0 tickets', 'Found 0 tickets' ); +} + +diag 'check invalid inputs'; +{ + $m->submit_form( + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, + ); + my $form = $m->form_name("TicketCreate"); + + $m->submit_form( + form_name => "TicketCreate", + fields => { + Subject => 'test', + Content => 'test', + "Object-RT::Ticket--CustomField-$cfid-Values" => 'foodate', + }, + ); + $m->content_like( qr/Ticket \d+ created/, + "a ticket is created succesfully" ); + + $m->content_contains('test cf date:', 'has no cf date field on the page' ); + $m->content_lacks('foodate', 'invalid dates not set' ); +} diff --git a/rt/t/web/cf_datetime.t b/rt/t/web/cf_datetime.t new file mode 100644 index 000000000..9781c5e2e --- /dev/null +++ b/rt/t/web/cf_datetime.t @@ -0,0 +1,235 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 51; + +RT->Config->Set( 'Timezone' => 'EST5EDT' ); # -04:00 +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; +my $root = RT::User->new( RT->SystemUser ); +ok( $root->Load('root'), 'load root user' ); + +my $cf_name = 'test cf datetime'; + +my $why; + +if ( ( $ENV{RT_TEST_WEB_HANDLER} || '' ) =~ /^apache(\+mod_perl)?$/ + && RT::Test::Apache->apache_mpm_type =~ /^(?:worker|event)$/ ) +{ + $why = +'localizing $ENV{TZ} does *not* work with mod_perl+mpm_event or mod_perl+mpm_worker'; +} + +my $cfid; +diag "Create a CF"; +{ + $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->submit_form( + form_name => "ModifyCustomField", + fields => { + Name => $cf_name, + TypeComposite => 'DateTime-1', + LookupType => 'RT::Queue-RT::Ticket', + }, + ); + $m->content_contains('Object created', 'created CF sucessfully' ); + $cfid = $m->form_name('ModifyCustomField')->value('id'); + ok $cfid, "found id of the CF in the form, it's #$cfid"; +} + +diag "apply the CF to General queue"; +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok $queue && $queue->id, 'loaded or created queue'; + +{ + $m->follow_link( text => 'Queues' ); + $m->title_is(q/Admin queues/, 'admin-queues screen'); + $m->follow_link( text => 'General' ); + $m->title_is(q/Configuration for queue General/, 'admin-queue: general'); + $m->follow_link( text => 'Ticket Custom Fields' ); + $m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid'); + + $m->form_name('EditCustomFields'); + $m->tick( "AddCustomField" => $cfid ); + $m->click('UpdateCFs'); + + $m->content_contains('Object created', 'TCF added to the queue' ); +} + +diag 'check valid inputs with various timezones in ticket create page'; +{ + my ( $ticket, $id ); + + $m->submit_form( + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, + ); + $m->content_contains('Select datetime', 'has cf field'); + + $m->submit_form( + form_name => "TicketCreate", + fields => { + Subject => 'test 2010-05-04 13:00:01', + Content => 'test', + "Object-RT::Ticket--CustomField-$cfid-Values" => '2010-05-04 13:00:01', + }, + ); + ok( ($id) = $m->content =~ /Ticket (\d+) created/, + "created ticket $id" ); + + $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load($id); + TODO: { + local $TODO = $why; + is( + $ticket->CustomFieldValues($cfid)->First->Content, + '2010-05-04 17:00:01', + 'date in db is in UTC' + ); + } + + $m->content_contains('test cf datetime:', 'has cf datetime field on the page'); + $m->content_contains('Tue May 04 13:00:01 2010', 'has cf datetime value on the page'); + + $root->SetTimezone( 'Asia/Shanghai' ); + # interesting that $m->reload doesn't work + $m->get_ok( $m->uri ); + + TODO: { + local $TODO = $why; + $m->content_contains( 'Wed May 05 01:00:01 2010', + 'cf datetime value respects user timezone' ); + } + + $m->submit_form( + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, + ); + $m->submit_form( + form_name => "TicketCreate", + fields => { + Subject => 'test 2010-05-06 07:00:01', + Content => 'test', + "Object-RT::Ticket--CustomField-$cfid-Values" => '2010-05-06 07:00:01', + }, + ); + ok( ($id) = $m->content =~ /Ticket (\d+) created/, + "created ticket $id" ); + $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load($id); + TODO: { + local $TODO = $why; + is( + $ticket->CustomFieldValues($cfid)->First->Content, + '2010-05-05 23:00:01', + 'date in db is in UTC' + ); + } + + $m->content_contains('test cf datetime:', 'has cf datetime field on the page'); + $m->content_contains( 'Thu May 06 07:00:01 2010', + 'cf datetime input respects user timezone' ); + $root->SetTimezone( 'EST5EDT' ); # back to -04:00 + $m->get_ok( $m->uri ); + + TODO: { + local $TODO = $why; + $m->content_contains( 'Wed May 05 19:00:01 2010', + 'cf datetime value respects user timezone' ); + } +} + + +diag 'check search build page'; +{ + $m->get_ok( $baseurl . '/Search/Build.html?Query=Queue=1' ); + + $m->form_name('BuildQuery'); + my ($cf_op) = + $m->find_all_inputs( type => 'option', name_regex => qr/test cf datetime/ ); + is_deeply( + [ $cf_op->possible_values ], + [ '<', '=', '>' ], + 'right oprators' + ); + + my ($cf_field) = + $m->find_all_inputs( type => 'text', name_regex => qr/test cf datetime/ ); + + is_results_number( { $cf_op->name => '=', $cf_field->name => '2010-05-04', }, 1 ); + $m->content_contains( '2010-05-04', 'got the right ticket' ); + $m->content_lacks( '2010-05-06', 'did not get the wrong ticket' ); + + my $shanghai = RT::Test->load_or_create_user( + Name => 'shanghai', + Password => 'password', + Timezone => 'Asia/Shanghai', + ); + ok( $shanghai->PrincipalObj->GrantRight( + Right => 'SuperUser', + Object => $RT::System, + )); + $m->login( 'shanghai', 'password', logout => 1 ); + + is_results_number( { $cf_op->name => '<', $cf_field->name => '2010-05-07', }, 2 ); + is_results_number( { $cf_op->name => '>', $cf_field->name => '2010-05-04', }, 2 ); + + TODO: { + local $TODO = $why; + is_results_number( { $cf_op->name => '=', $cf_field->name => '2010-05-05', }, 1 ); + is_results_number( { $cf_op->name => '=', $cf_field->name => '2010-05-05 01:00:01', }, 1 ); + } + + is_results_number( + { $cf_op->name => '=', $cf_field->name => '2010-05-05 02:00:01', }, 0 ); + + is_results_number( { $cf_op->name => '=', $cf_field->name => '2010-05-06', }, 1 ); + is_results_number( { $cf_op->name => '=', $cf_field->name => '2010-05-06 07:00:01', }, 1 ); + is_results_number( { $cf_op->name => '=', $cf_field->name => '2010-05-06 08:00:01', }, 0 ); +} + +diag 'check invalid inputs'; +{ + $m->submit_form( + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, + ); + my $form = $m->form_name("TicketCreate"); + + $m->submit_form( + form_name => "TicketCreate", + fields => { + Subject => 'test', + Content => 'test', + "Object-RT::Ticket--CustomField-$cfid-Values" => 'foodate', + }, + ); + $m->content_like(qr/Ticket \d+ created/, "a ticket is created succesfully"); + + $m->content_contains('test cf datetime:', 'has cf datetime field on the page'); + $m->content_lacks('foodate', 'invalid dates not set'); +} + +sub is_results_number { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $fields = shift; + my $number = shift; + my $operator = shift; + my $value = shift; + { + local $TODO; + $m->get_ok( $baseurl . '/Search/Build.html?Query=Queue=1' ); + } + $m->form_name('BuildQuery'); + $m->submit_form( + fields => $fields, + button => 'DoSearch', + ); + $m->content_contains( "Found $number ticket", "Found $number ticket" ); +} + +# to make $m->DESTROY happy +undef $m; + diff --git a/rt/t/web/cf_onqueue.t b/rt/t/web/cf_onqueue.t index bed4dd188..422eeff60 100644 --- a/rt/t/web/cf_onqueue.t +++ b/rt/t/web/cf_onqueue.t @@ -6,13 +6,9 @@ my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'logged in'; -diag "Create a queue CF" if $ENV{'TEST_VERBOSE'}; +diag "Create a queue CF"; { - $m->follow_link( text => 'Configuration' ); - $m->title_is(q/RT Administration/, 'admin screen'); - $m->follow_link( text => 'Custom Fields' ); - $m->title_is(q/Select a Custom Field/, 'admin-cf screen'); - $m->follow_link( text => 'Create' ); + $m->follow_link( id => 'tools-config-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { @@ -22,10 +18,10 @@ diag "Create a queue CF" if $ENV{'TEST_VERBOSE'}; Description => 'QueueCFTest', }, ); - $m->content_like( qr/Object created/, 'CF QueueCFTest created' ); + $m->content_contains('Object created', 'CF QueueCFTest created' ); } -diag "Apply the new CF globally" if $ENV{'TEST_VERBOSE'}; +diag "Apply the new CF globally"; { $m->follow_link( text => 'Global' ); $m->title_is(q!Admin/Global configuration!, 'global configuration screen'); @@ -33,22 +29,22 @@ diag "Apply the new CF globally" if $ENV{'TEST_VERBOSE'}; $m->title_is(q/Global custom field configuration/, 'global custom field configuration screen'); $m->follow_link( url => 'Queues.html' ); $m->title_is(q/Edit Custom Fields for all queues/, 'global custom field for all queues configuration screen'); - $m->content_like( qr/QueueCFTest/, 'CF QueueCFTest displayed on page' ); + $m->content_contains('QueueCFTest', 'CF QueueCFTest displayed on page' ); $m->form_name('EditCustomFields'); $m->tick( AddCustomField => 1 ); $m->click('UpdateCFs'); - $m->content_like( qr/Object created/, 'CF QueueCFTest enabled globally' ); + $m->content_contains('Object created', 'CF QueueCFTest enabled globally' ); } -diag "Edit the CF value for default queue" if $ENV{'TEST_VERBOSE'}; +diag "Edit the CF value for default queue"; { $m->follow_link( url => '/Admin/Queues/' ); $m->title_is(q/Admin queues/, 'queues configuration screen'); $m->follow_link( text => "1" ); - $m->title_is(q/Editing Configuration for queue General/, 'default queue configuration screen'); - $m->content_like( qr/QueueCFTest/, 'CF QueueCFTest displayed on default queue' ); + $m->title_is(q/Configuration for queue General/, 'default queue configuration screen'); + $m->content_contains('QueueCFTest', 'CF QueueCFTest displayed on default queue' ); $m->submit_form( form_number => 3, # The following doesn't want to works :( @@ -57,7 +53,7 @@ diag "Edit the CF value for default queue" if $ENV{'TEST_VERBOSE'}; 'Object-RT::Queue-1-CustomField-1-Value' => 'QueueCFTest content', }, ); - $m->content_like( qr/QueueCFTest QueueCFTest content added/, 'Content filed in CF QueueCFTest for default queue' ); + $m->content_contains('QueueCFTest QueueCFTest content added', 'Content filed in CF QueueCFTest for default queue' ); } diff --git a/rt/t/web/cf_render_type.t b/rt/t/web/cf_render_type.t new file mode 100644 index 000000000..8d8efa897 --- /dev/null +++ b/rt/t/web/cf_render_type.t @@ -0,0 +1,50 @@ +use strict; +use warnings; + +use RT::Test tests => 8; + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; + +my $cf_name = 'test render type'; + +my $cfid; +diag "Create a CF"; +{ + $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->submit_form( + form_name => "ModifyCustomField", + fields => { + Name => $cf_name, + TypeComposite => 'Freeform-1', + LookupType => 'RT::Queue-RT::Ticket', + }, + ); + $m->content_contains('Object created', 'created Freeform-1' ); + $cfid = $m->form_name('ModifyCustomField')->value('id'); + ok $cfid, "found id of the CF in the form, it's #$cfid"; +} + +diag "change to Select type"; +{ + $m->submit_form( + form_name => "ModifyCustomField", + fields => { TypeComposite => 'Select-1', }, + button => 'Update', + ); + $m->content_contains( + "Type changed from 'Enter one value' to 'Select one value'", + 'changed to Select-1' ); +} + +diag "let's save it again"; +{ + $m->submit_form( + form_name => "ModifyCustomField", + button => 'Update', + ); + $m->content_lacks( "Render Type changed from '1' to 'Select box'", + 'no buggy RenderType change msg' ); +} + + diff --git a/rt/t/web/cf_select_one.t b/rt/t/web/cf_select_one.t index 26c1fcf65..15fe416b4 100644 --- a/rt/t/web/cf_select_one.t +++ b/rt/t/web/cf_select_one.t @@ -3,7 +3,7 @@ use strict; use warnings; -use RT::Test tests => 46; +use RT::Test tests => 45; my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'logged in as root'; @@ -11,13 +11,9 @@ ok $m->login, 'logged in as root'; my $cf_name = 'test select one value'; my $cfid; -diag "Create a CF" if $ENV{'TEST_VERBOSE'}; +diag "Create a CF"; { - $m->follow_link( text => 'Configuration' ); - $m->title_is(q/RT Administration/, 'admin screen'); - $m->follow_link( text => 'Custom Fields' ); - $m->title_is(q/Select a Custom Field/, 'admin-cf screen'); - $m->follow_link( text => 'Create' ); + $m->follow_link( id => 'tools-config-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { @@ -26,12 +22,12 @@ diag "Create a CF" if $ENV{'TEST_VERBOSE'}; LookupType => 'RT::Queue-RT::Ticket', }, ); - $m->content_like( qr/Object created/, 'created CF sucessfully' ); + $m->content_contains('Object created', 'created CF sucessfully' ); $cfid = $m->form_name('ModifyCustomField')->value('id'); ok $cfid, "found id of the CF in the form, it's #$cfid"; } -diag "add 'qwe', 'ASD', '0' and ' foo ' as values to the CF" if $ENV{'TEST_VERBOSE'}; +diag "add 'qwe', 'ASD', '0' and ' foo ' as values to the CF"; { foreach my $value(qw(qwe ASD 0), 'foo ') { $m->submit_form( @@ -41,38 +37,36 @@ diag "add 'qwe', 'ASD', '0' and ' foo ' as values to the CF" if $ENV{'TEST_VERBO }, button => 'Update', ); - $m->content_like( qr/Object created/, 'added a value to the CF' ); # or diag $m->content; + $m->content_contains('Object created', 'added a value to the CF' ); # or diag $m->content; my $v = $value; $v =~ s/^\s+$//; $v =~ s/\s+$//; - $m->content_like( qr/value="$v"/, 'the added value is right' ); + $m->content_contains("value=\"$v\"", 'the added value is right' ); } } my $queue = RT::Test->load_or_create_queue( Name => 'General' ); ok $queue && $queue->id, 'loaded or created queue'; -diag "apply the CF to General queue" if $ENV{'TEST_VERBOSE'}; +diag "apply the CF to General queue"; { - $m->follow_link( text => 'Queues' ); - $m->title_is(q/Admin queues/, 'admin-queues screen'); + $m->follow_link( id => 'tools-config-queues'); $m->follow_link( text => 'General' ); - $m->title_is(q/Editing Configuration for queue General/, 'admin-queue: general'); - $m->follow_link( text => 'Ticket Custom Fields' ); - $m->title_is(q/Edit Custom Fields for General/, 'admin-queue: general cfid'); + $m->title_is(q/Configuration for queue General/, 'admin-queue: general'); + $m->follow_link( id => 'page-ticket-custom-fields'); + $m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid'); $m->form_name('EditCustomFields'); $m->tick( "AddCustomField" => $cfid ); $m->click('UpdateCFs'); - $m->content_like( qr/Object created/, 'TCF added to the queue' ); + $m->content_contains('Object created', 'TCF added to the queue' ); } my $tid; -diag "create a ticket using API with 'asd'(not 'ASD') as value of the CF" - if $ENV{'TEST_VERBOSE'}; +diag "create a ticket using API with 'asd'(not 'ASD') as value of the CF"; { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); my ($txnid, $msg); ($tid, $txnid, $msg) = $ticket->Create( Subject => 'test', @@ -80,7 +74,7 @@ diag "create a ticket using API with 'asd'(not 'ASD') as value of the CF" "CustomField-$cfid" => 'asd', ); ok $tid, "created ticket"; - diag $msg if $msg && $ENV{'TEST_VERBOSE'}; + diag $msg if $msg; # we use lc as we really don't care about case # so if later we'll add canonicalization of value @@ -89,72 +83,69 @@ diag "create a ticket using API with 'asd'(not 'ASD') as value of the CF" 'asd', 'assigned value of the CF'; } -diag "check that values of the CF are case insensetive(asd vs. ASD)" - if $ENV{'TEST_VERBOSE'}; +diag "check that values of the CF are case insensetive(asd vs. ASD)"; { ok $m->goto_ticket( $tid ), "opened ticket's page"; - $m->follow_link( text => 'Custom Fields' ); + $m->follow_link( id => 'page-basics'); $m->title_like(qr/Modify ticket/i, 'modify ticket'); - $m->content_like(qr/\Q$cf_name/, 'CF on the page'); + $m->content_contains($cf_name, 'CF on the page'); - my $value = $m->form_number(3)->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); + my $value = $m->form_name('TicketModify')->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); is lc $value, 'asd', 'correct value is selected'; $m->submit; $m->content_unlike(qr/\Q$cf_name\E.*?changed/mi, 'field is not changed'); - $value = $m->form_number(3)->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); + $value = $m->form_name('TicketModify')->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); is lc $value, 'asd', 'the same value is still selected'; - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $tid ); ok $ticket->id, 'loaded the ticket'; is lc $ticket->FirstCustomFieldValue( $cf_name ), 'asd', 'value is still the same'; } -diag "check that 0 is ok value of the CF" - if $ENV{'TEST_VERBOSE'}; +diag "check that 0 is ok value of the CF"; { ok $m->goto_ticket( $tid ), "opened ticket's page"; - $m->follow_link( text => 'Custom Fields' ); + $m->follow_link( id => 'page-basics'); $m->title_like(qr/Modify ticket/i, 'modify ticket'); - $m->content_like(qr/\Q$cf_name/, 'CF on the page'); + $m->content_contains($cf_name, 'CF on the page'); - my $value = $m->form_number(3)->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); + my $value = $m->form_name('TicketModify')->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); is lc $value, 'asd', 'correct value is selected'; $m->select("Object-RT::Ticket-$tid-CustomField-$cfid-Values" => 0 ); $m->submit; $m->content_like(qr/\Q$cf_name\E.*?changed/mi, 'field is changed'); - $m->content_unlike(qr/0 is no longer a value for custom field/mi, 'no bad message in results'); + $m->content_lacks('0 is no longer a value for custom field', 'no bad message in results'); - $value = $m->form_number(3)->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); + $value = $m->form_name('TicketModify')->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); is lc $value, '0', 'new value is selected'; - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $tid ); ok $ticket->id, 'loaded the ticket'; is lc $ticket->FirstCustomFieldValue( $cf_name ), '0', 'API returns correct value'; } -diag "check that we can set empty value when the current is 0" - if $ENV{'TEST_VERBOSE'}; +diag "check that we can set empty value when the current is 0"; { ok $m->goto_ticket( $tid ), "opened ticket's page"; - $m->follow_link( text => 'Custom Fields' ); + $m->follow_link( id => 'page-basics'); $m->title_like(qr/Modify ticket/i, 'modify ticket'); - $m->content_like(qr/\Q$cf_name/, 'CF on the page'); + $m->content_contains($cf_name, 'CF on the page'); - my $value = $m->form_number(3)->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); + my $value = $m->form_name('TicketModify')->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); is lc $value, '0', 'correct value is selected'; $m->select("Object-RT::Ticket-$tid-CustomField-$cfid-Values" => '' ); $m->submit; - $m->content_like(qr/0 is no longer a value for custom field/mi, '0 is no longer a value'); + $m->content_contains('0 is no longer a value for custom field', '0 is no longer a value'); - $value = $m->form_number(3)->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); + $value = $m->form_name('TicketModify')->value("Object-RT::Ticket-$tid-CustomField-$cfid-Values"); is $value, '', '(no value) is selected'; - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $tid ); ok $ticket->id, 'loaded the ticket'; is $ticket->FirstCustomFieldValue( $cf_name ), diff --git a/rt/t/web/charting.t b/rt/t/web/charting.t index 7c11f9c92..32d95d99b 100644 --- a/rt/t/web/charting.t +++ b/rt/t/web/charting.t @@ -1,7 +1,16 @@ use strict; use warnings; -use RT::Test no_plan => 1; +BEGIN { + require RT::Test; + + if (eval { require GD; 1 }) { + RT::Test->import(plan => 'no_plan'); + } + else { + RT::Test->import(skip_all => 'GD required.'); + } +} for my $n (1..7) { my $ticket = RT::Ticket->new( RT->SystemUser ); @@ -26,8 +35,8 @@ ok( $m->login, "Logged in" ); # Test that defaults work $m->get_ok( "/Search/Chart.html?Query=id>0" ); -$m->content_like(qr{<th[^>]*>\s*Queue\s*</th>\s*<th[^>]*>\s*Tickets\s*</th>}, "Grouped by queue"); -$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*7}, "Found results in table"); +$m->content_like(qr{<th[^>]*>Queue\s*</th>\s*<th[^>]*>Tickets\s*</th>}, "Grouped by queue"); +$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found results in table"); $m->content_like(qr{<img src="/Search/Chart\?}, "Found image"); $m->get_ok( "/Search/Chart?Query=id>0" ); @@ -37,8 +46,8 @@ ok( length($m->content), "Has content" ); # Group by Queue $m->get_ok( "/Search/Chart.html?Query=id>0&PrimaryGroupBy=Queue" ); -$m->content_like(qr{<th[^>]*>\s*Queue\s*</th>\s*<th[^>]*>\s*Tickets\s*</th>}, "Grouped by queue"); -$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*7}, "Found results in table"); +$m->content_like(qr{<th[^>]*>Queue\s*</th>\s*<th[^>]*>Tickets\s*</th>}, "Grouped by queue"); +$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found results in table"); $m->content_like(qr{<img src="/Search/Chart\?}, "Found image"); $m->get_ok( "/Search/Chart?Query=id>0&PrimaryGroupBy=Queue" ); @@ -48,9 +57,9 @@ ok( length($m->content), "Has content" ); # Group by Requestor email $m->get_ok( "/Search/Chart.html?Query=id>0&PrimaryGroupBy=Requestor.EmailAddress" ); -$m->content_like(qr{<th[^>]*>\s*Requestor\.EmailAddress\s*</th>\s*<th[^>]*>\s*Tickets\s*</th>}, +$m->content_like(qr{<th[^>]*>Requestor\.EmailAddress\s*</th>\s*<th[^>]*>Tickets\s*</th>}, "Grouped by requestor"); -$m->content_like(qr{root0\@localhost</a>\s*</td>\s*<td[^>]*>\s*3}, "Found results in table"); +$m->content_like(qr{root0\@localhost</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>3</a>}, "Found results in table"); $m->content_like(qr{<img src="/Search/Chart\?}, "Found image"); $m->get_ok( "/Search/Chart?Query=id>0&PrimaryGroupBy=Requestor.Email" ); @@ -60,7 +69,7 @@ ok( length($m->content), "Has content" ); # Group by Requestor phone -- which is bogus, and falls back to queue $m->get_ok( "/Search/Chart.html?Query=id>0&PrimaryGroupBy=Requestor.Phone" ); -$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*7}, +$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found queue results in table, as a default"); $m->content_like(qr{<img src="/Search/Chart\?}, "Found image"); diff --git a/rt/t/web/class_create.t b/rt/t/web/class_create.t new file mode 100644 index 000000000..cec41a8aa --- /dev/null +++ b/rt/t/web/class_create.t @@ -0,0 +1,75 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 13; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; +my $root = RT::User->new(RT->SystemUser); +ok( $root->Load('root'), 'load root user' ); + +my $class_name = 'test class'; + +my $class_id; +diag "Create a class"; +{ + $m->follow_link( id => 'tools-config-articles-classes-create'); + + # Test class form validation + $m->submit_form( + form_name => 'ModifyClass', + fields => { + Name => '', + }, + ); + $m->text_contains('Invalid value for Name'); + $m->submit_form( + form_name => 'ModifyClass', + fields => { + Name => '0', + }, + ); + $m->text_contains('Invalid value for Name'); + $m->submit_form( + form_name => 'ModifyClass', + fields => { + Name => '1', + }, + ); + $m->text_contains('Invalid value for Name'); + $m->submit_form( + form_name => 'ModifyClass', + fields => { + Name => $class_name, + }, + ); + $m->content_contains('Object created', 'created class sucessfully' ); + + # Test validation on updae + $m->form_name('ModifyClass'); + $m->set_fields( + Name => '', + ); + $m->click_button(value => 'Save Changes'); + $m->text_contains('Illegal value for Name'); + + $m->form_name('ModifyClass'); + $m->set_fields( + Name => '0', + ); + $m->click_button(value => 'Save Changes'); + $m->text_contains('Illegal value for Name'); + + $m->form_name('ModifyClass'); + $m->set_fields( + Name => '1', + ); + $m->click_button(value => 'Save Changes'); + $m->text_contains('Illegal value for Name'); + + $class_id = $m->form_name('ModifyClass')->value('id'); + ok $class_id, "found id of the class in the form, it's #$class_id"; +} + diff --git a/rt/t/web/clickjacking-preventions.t b/rt/t/web/clickjacking-preventions.t new file mode 100644 index 000000000..dde82952b --- /dev/null +++ b/rt/t/web/clickjacking-preventions.t @@ -0,0 +1,30 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use RT::Test tests => 11; + +my ($url, $m); + +# Enabled by default +{ + ok(RT->Config->Get('Framebusting'), "Framebusting enabled by default"); + + ($url, $m) = RT::Test->started_ok; + $m->get_ok($url); + $m->content_contains('if (window.top !== window.self) {', "Found the framekiller javascript"); + is $m->response->header('X-Frame-Options'), 'DENY', "X-Frame-Options is set to DENY"; + + RT::Test->stop_server; +} + +# Disabled +{ + RT->Config->Set('Framebusting', 0); + + ($url, $m) = RT::Test->started_ok; + $m->get_ok($url); + $m->content_lacks('if (window.top !== window.self) {', "Didn't find the framekiller javascript"); + is $m->response->header('X-Frame-Options'), undef, "X-Frame-Options is not present"; +} + diff --git a/rt/t/web/command_line.t b/rt/t/web/command_line.t index 884b064e6..1fed8e69e 100644 --- a/rt/t/web/command_line.t +++ b/rt/t/web/command_line.t @@ -3,7 +3,7 @@ use strict; use File::Spec (); use Test::Expect; -use RT::Test tests => 295; +use RT::Test tests => 303, actual_server => 1; my ($baseurl, $m) = RT::Test->started_ok; use RT::User; @@ -11,7 +11,6 @@ use RT::Queue; my $rt_tool_path = "$RT::BinPath/rt"; -# {{{ test configuration options # config directives: # (in $CWD/.rtrc) @@ -38,14 +37,13 @@ $ENV{'RTSERVER'} =RT->Config->Get('WebBaseURL') ; $ENV{'RTDEBUG'} = '1'; # - RTCONFIG Specifies a name other than ".rtrc" for the # configuration file. +$ENV{'RTCONFIG'} = '/dev/null'; # # - RTQUERY Default RT Query for rt list # - RTORDERBY Default order for rt list -# }}} -# {{{ test ticket manipulation # create a ticket expect_run( @@ -54,6 +52,7 @@ expect_run( quit => 'quit', ); expect_send(q{create -t ticket set subject='new ticket' add cc=foo@example.com}, "Creating a ticket..."); + expect_like(qr/Ticket \d+ created/, "Created the ticket"); expect_handle->before() =~ /Ticket (\d+) created/; my $ticket_id = $1; @@ -65,7 +64,6 @@ expect_like(qr/Ticket \d+ created/, "Created the ticket"); expect_send(q{rt create -t ticket set subject='rt ticket'}, "Creating a ticket with 'rt create'..."); expect_like(qr/Ticket \d+ created/, "Created the ticket"); -# {{{ test queue manipulation # creating queues expect_send("create -t queue set Name='NewQueue$$'", 'Creating a queue...'); @@ -85,19 +83,18 @@ TODO: { expect_like(qr/$queue_id: EditedQueue$$/, 'Found the queue'); } -# }}} # Set up a custom field for editing tests -my $cf = RT::CustomField->new($RT::SystemUser); +my $cf = RT::CustomField->new(RT->SystemUser); my ($val,$msg) = $cf->Create(Name => 'MyCF'.$$, Type => 'FreeformSingle', Queue => $queue_id); ok($val,$msg); -my $othercf = RT::CustomField->new($RT::SystemUser); +my $othercf = RT::CustomField->new(RT->SystemUser); ($val,$msg) = $othercf->Create(Name => 'My CF'.$$, Type => 'FreeformSingle', Queue => $queue_id); ok($val,$msg); -my $multiple_cf = RT::CustomField->new($RT::SystemUser); +my $multiple_cf = RT::CustomField->new(RT->SystemUser); ($val,$msg) = $multiple_cf->Create(Name => 'MultipleCF'.$$, Type => 'FreeformMultiple', Queue => $queue_id); ok($val,$msg); @@ -118,7 +115,7 @@ ok($val,$msg); # text attachment check_attachment($test_email); # binary attachment - check_attachment($RT::MasonComponentRoot.'/NoAuth/images/bplogo.gif'); + check_attachment($RT::MasonComponentRoot.'/NoAuth/images/bpslogo.png'); # change a ticket's Owner expect_send("edit ticket/$ticket_id set owner=root", 'Changing owner...'); @@ -130,6 +127,11 @@ expect_send("edit ticket/$ticket_id set requestors=foo\@example.com", 'Changing expect_like(qr/Ticket $ticket_id updated/, 'Changed Requestor'); expect_send("show ticket/$ticket_id -f requestors", 'Verifying change...'); expect_like(qr/Requestors: foo\@example.com/, 'Verified change'); +# set multiple Requestors +expect_send("edit ticket/$ticket_id set requestors=foo\@example.com,bar\@example.com", 'Changing Requestor...'); +expect_like(qr/Ticket $ticket_id updated/, 'Changed Requestor'); +expect_send("show ticket/$ticket_id -f requestors", 'Verifying change...'); +expect_like(qr/Requestors: bar\@example.com, foo\@example.com/, 'Verified change'); # change a ticket's Cc expect_send("edit ticket/$ticket_id set cc=bar\@example.com", 'Changing Cc...'); expect_like(qr/Ticket $ticket_id updated/, 'Changed Cc'); @@ -269,17 +271,19 @@ expect_send("show ticket/$ticket_id -f status", 'Verifying change...'); expect_like(qr/Status: resolved/, 'Verified change'); # try to set status to an illegal value expect_send("edit ticket/$ticket_id set status=quux", 'Changing status to an illegal value...'); -expect_like(qr/illegal value/i, 'Errored out'); +expect_like(qr/isn't a valid status/i, 'Errored out'); expect_send("show ticket/$ticket_id -f status", 'Verifying lack of change...'); expect_like(qr/Status: resolved/, 'Verified change'); -# }}} -# {{{ display # show ticket list expect_send("ls -s -t ticket -o +id \"Status='resolved'\"", 'Listing resolved tickets...'); expect_like(qr/$ticket_id: new ticket/, 'Found our ticket'); + +expect_send("ls -s -t ticket -f Requestors $ticket_id", 'getting Requestors'); +expect_like(qr/$ticket_id\s+bar\@example.com,\s+foo\@example.com/, 'got Requestors'); + # show ticket list verbosely expect_send("ls -l -t ticket -o +id \"Status='resolved'\"", 'Listing resolved tickets verbosely...'); expect_like(qr/id: ticket\/$ticket_id/, 'Found our ticket'); @@ -307,9 +311,7 @@ ok($attachment_id, "Got attachment id=$attachment_id $attachment_type"); expect_send("show -s ticket/$ticket_id/attachments/$attachment_id", "Showing attachment $attachment_id..."); expect_like(qr/ContentType: $attachment_type/, 'Got the attachment'); -# }}} -# {{{ test user manipulation # creating users expect_send("create -t user set Name='NewUser$$' EmailAddress='fbar$$\@example.com'", 'Creating a user...'); @@ -329,9 +331,7 @@ TODO: { expect_like(qr/$user_id: EditedUser$$/, 'Found the user'); } -# }}} -# {{{ test group manipulation TODO: { todo_skip "Group manipulation doesn't work right now", 8; @@ -354,11 +354,9 @@ TODO: { } } -# }}} TODO: { todo_skip "Custom field manipulation not yet implemented", 8; -# {{{ test custom field manipulation # creating custom fields expect_send("create -t custom_field set Name='NewCF$$'", 'Creating a custom field...'); @@ -379,9 +377,7 @@ TODO: { } } -# }}} -# {{{ test merging tickets expect_send("create -t ticket set subject='CLIMergeTest1-$$'", 'Creating first ticket to merge...'); expect_like(qr/Ticket \d+ created/, 'Created first ticket'); expect_handle->before() =~ /Ticket (\d+) created/; @@ -395,30 +391,23 @@ ok($merge_ticket_B, "Got second ticket to merge id=$merge_ticket_B"); expect_send("merge $merge_ticket_B $merge_ticket_A", 'Merging the tickets...'); expect_like(qr/Merge completed/, 'Merged the tickets'); -TODO: { - local $TODO = "we generate a spurious warning here"; - $m->no_warnings_ok; -} - expect_send("show ticket/$merge_ticket_A/history", 'Checking merge on first ticket'); expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in first ticket'); expect_send("show ticket/$merge_ticket_B/history", 'Checking merge on second ticket'); expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in second ticket'); -# }}} -# {{{ test taking/stealing tickets { # create a user; give them privileges to take and steal ### TODO: implement 'grant' in the CLI tool; use that here instead. ### this breaks the abstraction barrier, like, a lot. - my $steal_user = RT::User->new($RT::SystemUser); + my $steal_user = RT::User->new(RT->SystemUser); my ($steal_user_id, $msg) = $steal_user->Create( Name => "fooser$$", EmailAddress => "fooser$$\@localhost", Privileged => 1, Password => 'foobar', ); ok($steal_user_id, "Created the user? $msg"); - my $steal_queue = RT::Queue->new($RT::SystemUser); + my $steal_queue = RT::Queue->new(RT->SystemUser); my $steal_queue_id; ($steal_queue_id, $msg) = $steal_queue->Create( Name => "Steal$$" ); ok($steal_queue_id, "Got the queue? $msg"); @@ -476,9 +465,7 @@ expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in expect_send("steal $steal_ticket_id", 'root steals the ticket back...'); expect_like(qr/Owner changed from fooser$$ to root/, '...and succeeds.'); } -# }}} -# {{{ test ticket linking my @link_relns = ( 'DependsOn', 'DependedOnBy', 'RefersTo', 'ReferredToBy', 'MemberOf', 'HasMember', ); my %display_relns = map { $_ => $_ } @link_relns; @@ -502,7 +489,6 @@ expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in #expect_unlike(qr/\Q$reln: \E[\w\d\.]+\Q://\E[w\d\.]+\/ticket\/$link2_id/, "Removed link $reln"); } -# }}} expect_quit(); # We need to do this ourselves, so that we quit # *before* we tear down the webserver. @@ -536,9 +522,16 @@ sub check_attachment { my $attachment_content = do { local($/); <$fh> }; close $fh; chomp $attachment_content; - expect_is($attachment_content,"Attachment contains original text"); + TODO: { + local $TODO = "Binary PNG content is getting mangled somewhere along the way" + if $attachment_path =~ /\.png$/; + expect_is($attachment_content,"Attachment contains original text"); + } } +# you may encounter warning like Use of uninitialized value $ampm +# ... in Time::ParseDate +my @warnings = grep { $_ !~ /\$ampm/ } $m->get_warnings; +is( scalar @warnings, 0, 'no extra warnings' ); - -1; +1; # needed to avoid a weird exit value from expect_quit diff --git a/rt/t/web/command_line_with_unknown_field.t b/rt/t/web/command_line_with_unknown_field.t index 9a7ec7acd..736be4d1c 100644 --- a/rt/t/web/command_line_with_unknown_field.t +++ b/rt/t/web/command_line_with_unknown_field.t @@ -3,7 +3,7 @@ use strict; use File::Spec (); use Test::Expect; -use RT::Test tests => 10; +use RT::Test tests => 14, actual_server => 1; my ($baseurl, $m) = RT::Test->started_ok; my $rt_tool_path = "$RT::BinPath/rt"; @@ -12,6 +12,7 @@ $ENV{'RTPASSWD'} = 'password'; $RT::Logger->debug("Connecting to server at ".RT->Config->Get('WebBaseURL')); $ENV{'RTSERVER'} =RT->Config->Get('WebBaseURL') ; $ENV{'RTDEBUG'} = '1'; +$ENV{'RTCONFIG'} = '/dev/null'; expect_run( command => "$rt_tool_path shell", @@ -19,6 +20,7 @@ expect_run( quit => 'quit', ); expect_send(q{create -t ticket set subject='new ticket' add cc=foo@example.com}, "Creating a ticket..."); + expect_like(qr/Ticket \d+ created/, "Created the ticket"); expect_handle->before() =~ /Ticket (\d+) created/; my $ticket_id = $1; @@ -32,3 +34,10 @@ expect_like(qr/homer: Unknown field/, 'homer is unknown field'); expect_like(qr/homer: simpson/, 'the value we set for homer is shown too'); expect_quit(); + +# you may encounter warning like Use of uninitialized value $ampm +# ... in Time::ParseDate +my @warnings = grep { $_ !~ /\$ampm/ } $m->get_warnings; +is( scalar @warnings, 0, 'no extra warnings' ); + +1; # needed to avoid a weird exit value from expect_quit diff --git a/rt/t/web/compilation_errors.t b/rt/t/web/compilation_errors.t index 36a006890..1f82ab91f 100644 --- a/rt/t/web/compilation_errors.t +++ b/rt/t/web/compilation_errors.t @@ -7,7 +7,7 @@ BEGIN { sub wanted { -f && /\.html$/ && $_ !~ /Logout.html$/ && $File::Find::dir !~ /RichText/; } - my $tests = 4; + my $tests = 8; find( sub { wanted() and $tests += 4 }, 'share/html/' ); plan tests => $tests; } @@ -28,33 +28,37 @@ $agent->cookie_jar($cookie_jar); # get the top page my $url = $agent->rt_base_url; -diag "Base URL is '$url'" if $ENV{TEST_VERBOSE}; $agent->get($url); -is ($agent->{'status'}, 200, "Loaded a page"); - -# {{{ test a login +is($agent->status, 200, "Loaded a page"); # follow the link marked "Login" $agent->login(root => 'password'); -is($agent->{'status'}, 200, "Fetched the page ok"); -like( $agent->{'content'} , qr/Logout/i, "Found a logout link"); +is($agent->status, 200, "Fetched the page ok"); +$agent->content_contains('Logout', "Found a logout link"); + +find ( sub { wanted() and test_get($agent, $File::Find::name) } , 'share/html/'); -find ( sub { wanted() and test_get($File::Find::name) } , 'share/html/'); +TODO: { + local $TODO = "we spew *lots* of undef warnings"; + $agent->no_warnings_ok; +}; sub test_get { + my $agent = shift; my $file = shift; $file =~ s#^share/html/##; - diag( "testing $url/$file" ) if $ENV{TEST_VERBOSE}; - ok ($agent->get("$url/$file", "GET $url/$file"), "Can Get $url/$file"); - is ($agent->{'status'}, 200, "Loaded $file"); -# ok( $agent->{'content'} =~ /Logout/i, "Found a logout link on $file "); - ok( $agent->{'content'} !~ /Not logged in/i, "Still logged in for $file"); - ok( $agent->{'content'} !~ /raw error/i, "Didn't get a Mason compilation error on $file"); + diag( "testing $url/$file" ); + + $agent->get_ok("$url/$file"); + is($agent->status, 200, "Loaded $file"); + $agent->content_lacks('Not logged in', "Still logged in for $file"); + $agent->content_lacks('raw error', "Didn't get a Mason compilation error on $file") or do { + if (my ($error) = $agent->content =~ /<pre>(.*?line.*?)$/s) { + diag "$file: $error"; + } + }; } -# }}} - -1; diff --git a/rt/t/web/config_tab_right.t b/rt/t/web/config_tab_right.t index 4dc9ec082..361506c10 100644 --- a/rt/t/web/config_tab_right.t +++ b/rt/t/web/config_tab_right.t @@ -2,11 +2,11 @@ use strict; use warnings; -use RT::Test tests => 8; +use RT::Test nodata => 1, tests => 10; my ($uname, $upass, $user) = ('tester', 'tester'); { - $user = RT::User->new($RT::SystemUser); + $user = RT::User->new(RT->SystemUser); my ($status, $msg) = $user->Create( Name => $uname, Password => $upass, @@ -20,7 +20,7 @@ my ($baseurl, $m) = RT::Test->started_ok; ok $m->login($uname, $upass), "logged in"; { - $m->content_unlike(qr/Configuration/, 'no configuration'); + $m->content_lacks('Configuration', 'no configuration tab'); $m->get('/Admin/'); is $m->status, 403, 'no access to /Admin/'; } @@ -33,7 +33,7 @@ RT::Test->set_rights( { $m->get('/'); - $m->content_like(qr/Configuration/, 'configuration is there'); + $m->content_contains('Configuration', 'configuration tab is there'); $m->follow_link_ok({text => 'Configuration'}); is $m->status, 200, 'user has access to /Admin/'; diff --git a/rt/t/web/crypt-gnupg.t b/rt/t/web/crypt-gnupg.t index fb28c887c..6bdefdac7 100644 --- a/rt/t/web/crypt-gnupg.t +++ b/rt/t/web/crypt-gnupg.t @@ -1,20 +1,16 @@ #!/usr/bin/perl -w use strict; -use RT::Test tests => 94; - -plan skip_all => 'GnuPG required.' - unless eval 'use GnuPG::Interface; 1'; -plan skip_all => 'gpg executable is required.' - unless RT::Test->find_executable('gpg'); - +use RT::Test::GnuPG + tests => 102, + gnupg_options => { + passphrase => 'recipient', + 'trust-model' => 'always', +}; +use Test::Warn; use RT::Action::SendEmail; -eval 'use GnuPG::Interface; 1' or plan skip_all => 'GnuPG required.'; - -RT::Test->set_mail_catcher; - RT->Config->Set( CommentAddress => 'general@example.com'); RT->Config->Set( CorrespondAddress => 'general@example.com'); RT->Config->Set( DefaultSearchResultFormat => qq{ @@ -26,23 +22,6 @@ RT->Config->Set( DefaultSearchResultFormat => qq{ 'KR-__KeyRequestors__-K', Status}); -use File::Spec (); -use Cwd; -use File::Temp qw(tempdir); -my $homedir = tempdir( CLEANUP => 1 ); - -use_ok('RT::Crypt::GnuPG'); - -RT->Config->Set( 'GnuPG', - Enable => 1, - OutgoingMessagesFormat => 'RFC' ); - -RT->Config->Set( 'GnuPGOptions', - homedir => $homedir, - passphrase => 'recipient', - 'no-permission-warning' => undef, - 'trust-model' => 'always'); -RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' ); RT::Test->import_gnupg_key('recipient@example.com', 'public'); RT::Test->import_gnupg_key('recipient@example.com', 'secret'); @@ -51,7 +30,7 @@ RT::Test->import_gnupg_key('general@example.com', 'secret'); RT::Test->import_gnupg_key('general@example.com.2', 'public'); RT::Test->import_gnupg_key('general@example.com.2', 'secret'); -ok(my $user = RT::User->new($RT::SystemUser)); +ok(my $user = RT::User->new(RT->SystemUser)); ok($user->Load('root'), "Loaded user 'root'"); $user->SetEmailAddress('recipient@example.com'); @@ -62,11 +41,6 @@ my $queue = RT::Test->load_or_create_queue( ok $queue && $queue->id, 'loaded or created queue'; my $qid = $queue->id; -RT::Test->set_rights( - Principal => 'Everyone', - Right => ['CreateTicket', 'ShowTicket', 'SeeQueue', 'ModifyTicket'], -); - my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'logged in'; @@ -113,7 +87,7 @@ MAIL is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "got id of a newly created ticket - $id"); - my $tick = RT::Ticket->new( $RT::SystemUser ); + my $tick = RT::Ticket->new( RT->SystemUser ); $tick->Load( $id ); ok ($tick->id, "loaded ticket #$id"); @@ -181,7 +155,7 @@ MAIL is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "got id of a newly created ticket - $id"); - my $tick = RT::Ticket->new( $RT::SystemUser ); + my $tick = RT::Ticket->new( RT->SystemUser ); $tick->Load( $id ); ok ($tick->id, "loaded ticket #$id"); @@ -253,7 +227,7 @@ MAIL is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "got id of a newly created ticket - $id"); - my $tick = RT::Ticket->new( $RT::SystemUser ); + my $tick = RT::Ticket->new( RT->SystemUser ); $tick->Load( $id ); ok ($tick->id, "loaded ticket #$id"); @@ -319,7 +293,7 @@ MAIL is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "got id of a newly created ticket - $id"); - my $tick = RT::Ticket->new( $RT::SystemUser ); + my $tick = RT::Ticket->new( RT->SystemUser ); $tick->Load( $id ); ok ($tick->id, "loaded ticket #$id"); @@ -361,12 +335,12 @@ my $nokey = RT::Test->load_or_create_user(Name => 'nokey', EmailAddress => 'noke $nokey->PrincipalObj->GrantRight(Right => 'CreateTicket'); $nokey->PrincipalObj->GrantRight(Right => 'OwnTicket'); -my $tick = RT::Ticket->new( $RT::SystemUser ); +my $tick = RT::Ticket->new( RT->SystemUser ); $tick->Create(Subject => 'owner lacks pubkey', Queue => 'general', Owner => $nokey); ok(my $id = $tick->id, 'created ticket for owner-without-pubkey'); -$tick = RT::Ticket->new( $RT::SystemUser ); +$tick = RT::Ticket->new( RT->SystemUser ); $tick->Create(Subject => 'owner has pubkey', Queue => 'general', Owner => 'root'); ok($id = $tick->id, 'created ticket for owner-with-pubkey'); @@ -379,11 +353,18 @@ To: general\@example.com hello MAIL -((my $status), $id) = RT::Test->send_via_mailgate($mail); +my $status; +warning_like { + ($status, $id) = RT::Test->send_via_mailgate($mail); +} [ + qr/nokey\@example.com: skipped: public key not found/, + qr/Recipient 'nokey\@example.com' is unusable/, +]; + is ($status >> 8, 0, "The mail gateway exited normally"); ok ($id, "got id of a newly created ticket - $id"); -$tick = RT::Ticket->new( $RT::SystemUser ); +$tick = RT::Ticket->new( RT->SystemUser ); $tick->Load( $id ); ok ($tick->id, "loaded ticket #$id"); @@ -396,7 +377,7 @@ is ($tick->Subject, my $key1 = "EC1E81E7DC3DB42788FB0E4E9FA662C06DE22FC2"; my $key2 = "75E156271DCCF02DDD4A7A8CDF651FA0632C4F50"; -ok($user = RT::User->new($RT::SystemUser)); +ok($user = RT::User->new(RT->SystemUser)); ok($user->Load('root'), "Loaded user 'root'"); is($user->PreferredKey, $key1, "preferred key is set correctly"); $m->get("$baseurl/Prefs/Other.html"); @@ -407,11 +388,11 @@ like($m->content, qr/$key1/, "first key shows up in preferences"); like($m->content, qr/$key2/, "second key shows up in preferences"); like($m->content, qr/$key1.*?$key2/s, "first key shows up before the second"); -$m->form_number(3); +$m->form_name('ModifyPreferences'); $m->select("PreferredKey" => $key2); $m->submit; -ok($user = RT::User->new($RT::SystemUser)); +ok($user = RT::User->new(RT->SystemUser)); ok($user->Load('root'), "Loaded user 'root'"); is($user->PreferredKey, $key2, "preferred key is set correctly to the new value"); @@ -423,6 +404,8 @@ like($m->content, qr/$key2/, "second key shows up in preferences"); like($m->content, qr/$key1/, "first key shows up in preferences"); like($m->content, qr/$key2.*?$key1/s, "second key (now preferred) shows up before the first"); +$m->no_warnings_ok; + # test that the new fields work $m->get("$baseurl/Search/Simple.html?q=General"); my $content = $m->content; @@ -441,6 +424,10 @@ like($content, qr/KO-nokey \(no pubkey!\)-K/, "KeyOwnerName issues no-pubkey war like($content, qr/KO-Nobody \(no pubkey!\)-K/, "KeyOwnerName issues no-pubkey warning for nobody"); like($content, qr/KR-recipient\@example.com-K/, "KeyRequestors does not issue no-pubkey warning for recipient\@example.com"); + like($content, qr/KR-general\@example.com-K/, "KeyRequestors does not issue no-pubkey warning for general\@example.com"); like($content, qr/KR-nokey\@example.com \(no pubkey!\)-K/, "KeyRequestors DOES issue no-pubkey warning for nokey\@example.com"); +$m->next_warning_like(qr/public key not found/); +$m->next_warning_like(qr/public key not found/); +$m->no_leftover_warnings_ok; diff --git a/rt/t/web/custom_frontpage.t b/rt/t/web/custom_frontpage.t index 79ea56629..43c5f6e33 100644 --- a/rt/t/web/custom_frontpage.t +++ b/rt/t/web/custom_frontpage.t @@ -1,12 +1,12 @@ #!/usr/bin/perl -w use strict; -use RT::Test tests => 7; +use RT::Test tests => 12; my ($baseurl, $m) = RT::Test->started_ok; my $url = $m->rt_base_url; -my $user_obj = RT::User->new($RT::SystemUser); +my $user_obj = RT::User->new(RT->SystemUser); my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('customer@example.com'); ok($ret, 'ACL test user creation'); $user_obj->SetName('customer'); @@ -29,9 +29,9 @@ $m->field ( "SavedSearchDescription" => 'stupid tickets'); $m->click_button (name => 'SavedSearchSave'); $m->get ( $url.'Prefs/MyRT.html' ); -$m->content_like (qr/stupid tickets/, 'saved search listed in rt at a glance items'); +$m->content_contains('stupid tickets', 'saved search listed in rt at a glance items'); -ok $m->login, 'we did log in as root'; +ok $m->login('root', 'password', logout => 1), 'we did log in as root'; $m->get ( $url.'Prefs/MyRT.html' ); $m->form_name ('SelectionBox-body'); @@ -58,4 +58,33 @@ $m->click_button (name => 'movedown'); $m->form_name ('SelectionBox-body'); #$m->click_button (name => 'body-Save'); $m->get ( $url ); -$m->content_like (qr'highest priority tickets', 'adds them back'); +$m->content_contains('highest priority tickets', 'adds them back'); + + +#create a saved search with special chars +$m->get( $url . "Search/Build.html" ); +$m->form_name('BuildQuery'); +$m->field( "ValueOfAttachment" => 'stupid' ); +$m->field( "SavedSearchDescription" => 'special chars [test] [_1] ~[_1~]' ); +$m->click_button( name => 'SavedSearchSave' ); +my ($name) = $m->content =~ /value="(RT::User-\d+-SavedSearch-\d+)"/; +ok( $name, 'saved search name' ); +$m->get( $url . 'Prefs/MyRT.html' ); +$m->content_contains( 'special chars [test] [_1] ~[_1~]', + 'saved search listed in rt at a glance items' ); + +$m->get( $url . 'Prefs/MyRT.html' ); +$m->form_name('SelectionBox-body'); +$m->field( + 'body-Available' => [ + 'component-QuickCreate', + 'system-Unowned Tickets', + 'system-My Tickets', + 'saved-' . $name, + ] +); +$m->click_button( name => 'add' ); + +$m->get($url); +$m->content_like( qr/special chars \[test\] \d+ \[_1\]/, + 'special chars in titlebox' ); diff --git a/rt/t/web/custom_search.t b/rt/t/web/custom_search.t index 05cfdab60..f8fde2500 100644 --- a/rt/t/web/custom_search.t +++ b/rt/t/web/custom_search.t @@ -1,7 +1,7 @@ #!/usr/bin/perl -w use strict; -use RT::Test tests => 11; +use RT::Test tests => 13; my ($baseurl, $m) = RT::Test->started_ok; my $url = $m->rt_base_url; @@ -9,7 +9,7 @@ my $url = $m->rt_base_url; -my $t = RT::Ticket->new($RT::SystemUser); +my $t = RT::Ticket->new(RT->SystemUser); $t->Create(Subject => 'for custom search'.$$, Queue => 'general', Owner => 'root', Requestor => 'customsearch@localhost'); ok(my $id = $t->id, 'created ticket for custom search'); @@ -24,7 +24,7 @@ $m->get ( $url.'Prefs/MyRT.html' ); my $cus_hp = $m->find_link( text => "My Tickets" ); my $cus_qs = $m->find_link( text => "Quick search" ); $m->get ($cus_hp); -$m->content_like (qr'highest priority tickets'); +$m->content_contains('highest priority tickets'); # add Requestor to the fields $m->form_name ('BuildQuery'); diff --git a/rt/t/web/dashboards-basics.t b/rt/t/web/dashboards-basics.t new file mode 100644 index 000000000..1d56da5be --- /dev/null +++ b/rt/t/web/dashboards-basics.t @@ -0,0 +1,268 @@ +#!/usr/bin/perl -w +use strict; + +use RT::Test tests => 122; +my ($baseurl, $m) = RT::Test->started_ok; + +my $url = $m->rt_base_url; + +my $user_obj = RT::User->new(RT->SystemUser); +my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('customer@example.com'); +ok($ret, 'ACL test user creation'); +$user_obj->SetName('customer'); +$user_obj->SetPrivileged(1); +($ret, $msg) = $user_obj->SetPassword('customer'); +$user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf'); +my $currentuser = RT::CurrentUser->new($user_obj); + +my $onlooker = RT::User->new(RT->SystemUser); +($ret, $msg) = $onlooker->LoadOrCreateByEmail('onlooker@example.com'); +ok($ret, 'ACL test user creation'); +$onlooker->SetName('onlooker'); +$onlooker->SetPrivileged(1); +($ret, $msg) = $onlooker->SetPassword('onlooker'); + +my $queue = RT::Queue->new(RT->SystemUser); +$queue->Create(Name => 'SearchQueue'.$$); + +for my $user ($user_obj, $onlooker) { + $user->PrincipalObj->GrantRight(Right => 'ModifySelf'); + for my $right (qw/SeeQueue ShowTicket OwnTicket/) { + $user->PrincipalObj->GrantRight(Right => $right, Object => $queue); + } +} + +ok $m->login(customer => 'customer'), "logged in"; + +$m->get_ok($url."Dashboards/index.html"); +$m->content_lacks('<a href="/Dashboards/Modify.html?Create=1">New</a>', + "No 'new dashboard' link because we have no CreateOwnDashboard"); + +$m->no_warnings_ok; + +$m->get_ok($url."Dashboards/Modify.html?Create=1"); +$m->content_contains("Permission denied"); +$m->content_lacks("Save Changes"); + +$m->warning_like(qr/Permission denied/, "got a permission denied warning"); + +$user_obj->PrincipalObj->GrantRight(Right => 'ModifyOwnDashboard', Object => $RT::System); + +# Modify itself is no longer good enough, you need Create +$m->get_ok($url."Dashboards/Modify.html?Create=1"); +$m->content_contains("Permission denied"); +$m->content_lacks("Save Changes"); + +$m->warning_like(qr/Permission denied/, "got a permission denied warning"); + +$user_obj->PrincipalObj->GrantRight(Right => 'CreateOwnDashboard', Object => $RT::System); + +$m->get_ok($url."Dashboards/Modify.html?Create=1"); +$m->content_lacks("Permission denied"); +$m->content_contains("Create"); + +$m->get_ok($url."Dashboards/index.html"); +$m->content_contains("New", "'New' link because we now have ModifyOwnDashboard"); +$m->follow_link_ok({ id => 'home-dashboard_create'}); +$m->form_name('ModifyDashboard'); +$m->field("Name" => 'different dashboard'); +$m->content_lacks('Delete', "Delete button hidden because we are creating"); +$m->click_button(value => 'Create'); +$m->content_contains("Saved dashboard different dashboard"); +$user_obj->PrincipalObj->GrantRight(Right => 'SeeOwnDashboard', Object => $RT::System); +$m->get($url."Dashboards/index.html"); +$m->follow_link_ok({ text => 'different dashboard'}); +$m->content_lacks("Permission denied", "we now have SeeOwnDashboard"); +$m->content_lacks('Delete', "Delete button hidden because we lack DeleteOwnDashboard"); + +$m->get_ok($url."Dashboards/index.html"); +$m->content_contains("different dashboard", "we now have SeeOwnDashboard"); +$m->content_lacks("Permission denied"); + +$m->follow_link_ok({text => "different dashboard"}); +$m->content_contains("Basics"); +$m->content_contains("Content"); +$m->content_lacks("Subscription", "we don't have the SubscribeDashboard right"); + +$m->follow_link_ok({text => "Basics"}); +$m->content_contains("Modify the dashboard different dashboard"); + +$m->follow_link_ok({text => "Content"}); +$m->content_contains("Modify the content of dashboard different dashboard"); +my $form = $m->form_name('Dashboard-Searches-body'); +my @input = $form->find_input('Searches-body-Available'); +my ($unowned) = + map { ( $_->possible_values )[1] } + grep { ( $_->value_names )[1] =~ /Saved Search: Unowned Tickets/ } @input; +$form->value('Searches-body-Available' => $unowned ); +$m->click_button(name => 'add'); +$m->content_contains("Dashboard updated"); + +my $dashboard = RT::Dashboard->new($currentuser); +my ($id) = $m->content =~ /name="id" value="(\d+)"/; +ok($id, "got an ID, $id"); +$dashboard->LoadById($id); +is($dashboard->Name, "different dashboard"); + +is($dashboard->Privacy, 'RT::User-' . $user_obj->Id, "correct privacy"); +is($dashboard->PossibleHiddenSearches, 0, "all searches are visible"); + +my @searches = $dashboard->Searches; +is(@searches, 1, "one saved search in the dashboard"); +like($searches[0]->Name, qr/newest unowned tickets/, "correct search name"); + +$form = $m->form_name('Dashboard-Searches-body'); +@input = $form->find_input('Searches-body-Available'); +my ($my_tickets) = + map { ( $_->possible_values )[1] } + grep { ( $_->value_names )[1] =~ /Saved Search: My Tickets/ } @input; +$form->value('Searches-body-Available' => $my_tickets ); +$m->click_button(name => 'add'); +$m->content_contains("Dashboard updated"); + +RT::Record->FlushCache if RT::Record->can('FlushCache'); +$dashboard = RT::Dashboard->new($currentuser); +$dashboard->LoadById($id); + +@searches = $dashboard->Searches; +is(@searches, 2, "two saved searches in the dashboard"); +like($searches[0]->Name, qr/newest unowned tickets/, "correct existing search name"); +like($searches[1]->Name, qr/highest priority tickets I own/, "correct new search name"); + +my $ticket = RT::Ticket->new(RT->SystemUser); +$ticket->Create( + Queue => $queue->Id, + Requestor => [ $user_obj->Name ], + Owner => $user_obj, + Subject => 'dashboard test', +); + +$m->follow_link_ok({id => 'page-show'}); +$m->content_contains("50 highest priority tickets I own"); +$m->content_contains("50 newest unowned tickets"); +$m->content_unlike( qr/Bookmarked Tickets.*Bookmarked Tickets/s, + 'only dashboard queries show up' ); +$m->content_contains("dashboard test", "ticket subject"); + +$m->get_ok("/Dashboards/$id/This fragment left intentionally blank"); +$m->content_contains("50 highest priority tickets I own"); +$m->content_contains("50 newest unowned tickets"); +$m->content_unlike( qr/Bookmarked Tickets.*Bookmarked Tickets/s, + 'only dashboard queries show up' ); +$m->content_contains("dashboard test", "ticket subject"); + +$m->get_ok("/Dashboards/Subscription.html?id=$id"); +$m->form_name('SubscribeDashboard'); +$m->click_button(name => 'Save'); +$m->content_contains("Permission denied"); +$m->warning_like(qr/Unable to subscribe to dashboard.*Permission denied/, "got a permission denied warning when trying to subscribe to a dashboard"); + +$user_obj->Attributes->RedoSearch; +is($user_obj->Attributes->Named('Subscription'), 0, "no subscriptions"); + +$user_obj->PrincipalObj->GrantRight(Right => 'SubscribeDashboard', Object => $RT::System); + +$m->get_ok("/Dashboards/Modify.html?id=$id"); +$m->follow_link_ok({text => "Subscription"}); +$m->content_contains("Subscribe to dashboard different dashboard"); +$m->content_contains("Unowned Tickets"); +$m->content_contains("My Tickets"); +$m->content_unlike( qr/Bookmarked Tickets.*Bookmarked Tickets/s, + 'only dashboard queries show up' ); + +$m->form_name('SubscribeDashboard'); +$m->click_button(name => 'Save'); +$m->content_lacks("Permission denied"); +$m->content_contains("Subscribed to dashboard different dashboard"); + +$user_obj->Attributes->RedoSearch; +is($user_obj->Attributes->Named('Subscription'), 1, "we have a subscription"); + +$m->get_ok("/Dashboards/Modify.html?id=$id"); +$m->follow_link_ok({text => "Subscription"}); +$m->content_contains("Modify the subscription to dashboard different dashboard"); + +$m->get_ok("/Dashboards/Modify.html?id=$id&Delete=1"); +$m->content_contains("Permission denied", "unable to delete dashboard because we lack DeleteOwnDashboard"); + +$m->warning_like(qr/Couldn't delete dashboard.*Permission denied/, "got a permission denied warning when trying to delete the dashboard"); + +$user_obj->PrincipalObj->GrantRight(Right => 'DeleteOwnDashboard', Object => $RT::System); + +$m->get_ok("/Dashboards/Modify.html?id=$id"); +$m->content_contains('Delete', "Delete button shows because we have DeleteOwnDashboard"); + +$m->form_name('ModifyDashboard'); +$m->click_button(name => 'Delete'); +$m->content_contains("Deleted dashboard"); + +$m->get("/Dashboards/Modify.html?id=$id"); +$m->content_lacks("different dashboard", "dashboard was deleted"); +$m->content_contains("Failed to load dashboard $id"); + +$m->warning_like(qr/Failed to load dashboard.*Couldn't find row/, "the dashboard was deleted"); + +$user_obj->PrincipalObj->GrantRight(Right => "SuperUser", Object => $RT::System); + +# now test that we warn about searches others can't see +# first create a personal saved search... +$m->get_ok($url."Search/Build.html"); +$m->follow_link_ok({text => 'Advanced'}); +$m->form_with_fields('Query'); +$m->field(Query => "id > 0"); +$m->submit; + +$m->form_with_fields('SavedSearchDescription'); +$m->field(SavedSearchDescription => "personal search"); +$m->click_button(name => "SavedSearchSave"); + +# then the system-wide dashboard +$m->get_ok($url."Dashboards/Modify.html?Create=1"); + +$m->form_name('ModifyDashboard'); +$m->field("Name" => 'system dashboard'); +$m->field("Privacy" => 'RT::System-1'); +$m->content_lacks('Delete', "Delete button hidden because we are creating"); +$m->click_button(value => 'Create'); +$m->content_lacks("No permission to create dashboards"); +$m->content_contains("Saved dashboard system dashboard"); + +$m->follow_link_ok({id => 'page-content'}); + +$form = $m->form_name('Dashboard-Searches-body'); +@input = $form->find_input('Searches-body-Available'); +my ($personal) = + map { ( $_->possible_values )[1] } + grep { ( $_->value_names )[1] =~ /Saved Search: personal search/ } @input; +$form->value('Searches-body-Available' => $personal ); +$m->click_button(name => 'add'); +$m->content_contains("Dashboard updated"); + +$m->content_contains("The following queries may not be visible to all users who can see this dashboard."); + +$m->follow_link_ok({id => 'page-show'}); +$m->content_contains("personal search", "saved search shows up"); +$m->content_contains("dashboard test", "matched ticket shows up"); + +# make sure the onlooker can't see the search... +$onlooker->PrincipalObj->GrantRight(Right => 'SeeDashboard', Object => $RT::System); + +my $omech = RT::Test::Web->new; +ok $omech->login(onlooker => 'onlooker'), "logged in"; +$omech->get_ok("/Dashboards"); + +$omech->follow_link_ok({text => 'system dashboard'}); +$omech->content_lacks("personal search", "saved search doesn't show up"); +$omech->content_lacks("dashboard test", "matched ticket doesn't show up"); + +$omech->warning_like(qr/User .* tried to load container user /, "can't see other users' personal searches"); + +# make sure that navigating to dashboard pages with bad IDs throws an error +my ($bad_id) = $personal =~ /^search-(\d+)/; + +for my $page (qw/Modify Queries Render Subscription/) { + $m->get("/Dashboards/$page.html?id=$bad_id"); + $m->content_like(qr/Couldn.+t load dashboard $bad_id: Invalid object type/); + $m->warning_like(qr/Couldn't load dashboard $bad_id: Invalid object type/); +} + diff --git a/rt/t/web/dashboards-deleted-saved-search.t b/rt/t/web/dashboards-deleted-saved-search.t new file mode 100644 index 000000000..4cd7fea1f --- /dev/null +++ b/rt/t/web/dashboards-deleted-saved-search.t @@ -0,0 +1,89 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use RT::Test tests => 20; +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login, 'logged in' ); + +# create a saved search +$m->get_ok( $url . "/Search/Build.html?Query=" . 'id=1' ); + +$m->submit_form( + form_name => 'BuildQuery', + fields => { SavedSearchDescription => 'foo', }, + button => 'SavedSearchSave', +); + +my ( $search_uri, $user_id, $search_id ) = + $m->content =~ /value="(RT::User-(\d+)-SavedSearch-(\d+))"/; +$m->submit_form( + form_name => 'BuildQuery', + fields => { SavedSearchLoad => $search_uri }, + button => 'SavedSearchSave', +); + +$m->content_like( qr/name="SavedSearchDelete"\s+value="Delete"/, + 'found Delete button' ); +$m->content_like( + qr/name="SavedSearchDescription"\s+value="foo"/, + 'found Description input with the value filled' +); + +# create a dashboard with the created search + +$m->get_ok( $url . "/Dashboards/Modify.html?Create=1" ); +$m->submit_form( + form_name => 'ModifyDashboard', + fields => { Name => 'bar' }, +); + +$m->content_contains('Saved dashboard bar', 'dashboard saved' ); +my $dashboard_queries_link = $m->find_link( text_regex => qr/Content/ ); +my ( $dashboard_id ) = $dashboard_queries_link->url =~ /id=(\d+)/; + +$m->get_ok( $url . "/Dashboards/Queries.html?id=$dashboard_id" ); + +$m->content_lacks( 'value="Update"', 'no update button' ); + +$m->submit_form( + form_name => 'Dashboard-Searches-body', + fields => + { 'Searches-body-Available' => "search-$search_id-RT::User-$user_id" }, + button => 'add', +); + +$m->content_contains('Dashboard updated', 'added search foo to dashboard bar' ); + +# delete the created search + +$m->get_ok( $url . "/Search/Build.html?Query=" . 'id=1' ); +$m->submit_form( + form_name => 'BuildQuery', + fields => { SavedSearchLoad => $search_uri }, +); +$m->submit_form( + form_name => 'BuildQuery', + button => 'SavedSearchDelete', +); + +$m->content_lacks( $search_uri, 'deleted search foo' ); + +# here is what we really want to test + +$m->get_ok( $url . "/Dashboards/Queries.html?id=$dashboard_id" ); +$m->content_contains('Deleted queries', 'found deleted message' ); + +# Update button shows so we can update the deleted search easily +$m->content_contains( 'value="Update"', 'found update button' ); + +$m->submit_form( + form_name => 'Dashboard-Searches-body', + button => 'update', +); + +$m->content_lacks('Deleted queries', 'deleted message is gone' ); +$m->content_lacks( 'value="Update"', 'update button is gone too' ); + +$m->get_warnings; # we'll get a lot of warnings because the deleted search + diff --git a/rt/t/web/dashboards-groups.t b/rt/t/web/dashboards-groups.t index cbf1d6a9f..ac2a5aca0 100644 --- a/rt/t/web/dashboards-groups.t +++ b/rt/t/web/dashboards-groups.t @@ -1,13 +1,13 @@ #!/usr/bin/perl -w use strict; -use RT::Test tests => 40; +use RT::Test nodata => 1, tests => 64; my ($baseurl, $m) = RT::Test->started_ok; my $url = $m->rt_base_url; -# create user and queue {{{ -my $user_obj = RT::User->new($RT::SystemUser); +# create user and queue +my $user_obj = RT::User->new(RT->SystemUser); my ($ok, $msg) = $user_obj->LoadOrCreateByEmail('customer@example.com'); ok($ok, 'ACL test user creation'); $user_obj->SetName('customer'); @@ -16,7 +16,7 @@ $user_obj->SetPrivileged(1); $user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf'); my $currentuser = RT::CurrentUser->new($user_obj); -my $queue = RT::Queue->new($RT::SystemUser); +my $queue = RT::Queue->new(RT->SystemUser); $queue->Create(Name => 'SearchQueue'.$$); $user_obj->PrincipalObj->GrantRight(Right => $_, Object => $queue) @@ -26,13 +26,12 @@ $user_obj->PrincipalObj->GrantRight(Right => $_, Object => $queue) # are checked and not these as well $user_obj->PrincipalObj->GrantRight(Right => $_, Object => $RT::System) for qw/SubscribeDashboard CreateOwnDashboard SeeOwnDashboard ModifyOwnDashboard DeleteOwnDashboard/; -# }}} -# create and test groups (outer < inner < user) {{{ -my $inner_group = RT::Group->new($RT::SystemUser); +# create and test groups (outer < inner < user) +my $inner_group = RT::Group->new(RT->SystemUser); ($ok, $msg) = $inner_group->CreateUserDefinedGroup(Name => "inner", Description => "inner group"); ok($ok, "created inner group: $msg"); -my $outer_group = RT::Group->new($RT::SystemUser); +my $outer_group = RT::Group->new(RT->SystemUser); ($ok, $msg) = $outer_group->CreateUserDefinedGroup(Name => "outer", Description => "outer group"); ok($ok, "created outer group: $msg"); @@ -51,28 +50,36 @@ ok(!$inner_group->HasMember($outer_group->PrincipalId), "inner doesn't have oute ok($inner_group->HasMember($user_obj->PrincipalId), "inner has user"); ok(!$inner_group->HasMemberRecursively($outer_group->PrincipalId), "inner doesn't have outer, even recursively"); ok($inner_group->HasMemberRecursively($user_obj->PrincipalId), "inner has user recursively"); -# }}} ok $m->login(customer => 'customer'), "logged in"; -$m->get_ok("$url/Dashboards"); -$m->follow_link_ok({text => "New"}); +$m->follow_link_ok({ id => 'home-dashboard_create'}); $m->form_name('ModifyDashboard'); is_deeply([$m->current_form->find_input('Privacy')->possible_values], ["RT::User-" . $user_obj->Id], "the only selectable privacy is user"); $m->content_lacks('Delete', "Delete button hidden because we are creating"); $user_obj->PrincipalObj->GrantRight(Right => 'CreateGroupDashboard', Object => $inner_group); -$m->follow_link_ok({text => "New"}); +$m->follow_link_ok({ id => 'home-dashboard_create'}); $m->form_name('ModifyDashboard'); is_deeply([$m->current_form->find_input('Privacy')->possible_values], ["RT::User-" . $user_obj->Id, "RT::Group-" . $inner_group->Id], "the only selectable privacies are user and inner group (not outer group)"); -$m->field("Name" => 'inner dashboard'); +$m->field("Name" => 'broken dashboard'); $m->field("Privacy" => "RT::Group-" . $inner_group->Id); $m->content_lacks('Delete', "Delete button hidden because we are creating"); +$m->click_button(value => 'Create'); +$m->content_contains("saved", "we lack SeeGroupDashboard, so we end up back at the index."); +$user_obj->PrincipalObj->GrantRight( + Right => 'SeeGroupDashboard', + Object => $inner_group, +); +$m->follow_link_ok({ id => 'home-dashboard_create'}); +$m->form_name('ModifyDashboard'); +$m->field("Name" => 'inner dashboard'); +$m->field("Privacy" => "RT::Group-" . $inner_group->Id); $m->click_button(value => 'Create'); -$m->content_lacks("No permission to create dashboards"); +$m->content_lacks("Permission denied", "we now have SeeGroupDashboard"); $m->content_contains("Saved dashboard inner dashboard"); $m->content_lacks('Delete', "Delete button hidden because we lack DeleteDashboard"); @@ -85,18 +92,104 @@ is($dashboard->Name, "inner dashboard"); is($dashboard->Privacy, 'RT::Group-' . $inner_group->Id, "correct privacy"); is($dashboard->PossibleHiddenSearches, 0, "all searches are visible"); -$m->no_warnings_ok; - -$m->get_ok("/Dashboards/Modify.html?id=$id"); -$m->content_lacks("inner dashboard", "no SeeGroupDashboard right"); -$m->content_contains("Permission denied"); - -$m->warning_like(qr/Permission denied/, "got a permission denied warning"); -$user_obj->PrincipalObj->GrantRight(Right => 'SeeGroupDashboard', Object => $inner_group); $m->get_ok("/Dashboards/Modify.html?id=$id"); $m->content_contains("inner dashboard", "we now have SeeGroupDashboard right"); $m->content_lacks("Permission denied"); - $m->content_contains('Subscription', "Subscription link not hidden because we have SubscribeDashboard"); + +$m->get_ok("/Dashboards/index.html"); +$m->content_contains("inner dashboard", "We can see the inner dashboard from the UI"); + +$m->get_ok("/index.html"); +$m->content_contains("inner dashboard", "We can see the inner dashboard from the menu drop-down"); + +my ($group) = grep {$_->isa("RT::Group") and $_->Id == $inner_group->Id} + RT::Dashboard->new($currentuser)->_PrivacyObjects; +ok($group, "Found the group in the privacy objects list"); + +my @loading = map {ref($_)."-".$_->Id} RT::Dashboard->new($currentuser)->ObjectsForLoading; +is_deeply( + \@loading, + ["RT::User-".$user_obj->Id, "RT::Group-".$inner_group->Id], + "We can load from ourselves (SeeOwnDashboard) and a group we are with SeeGroupDashboard" +); + +# If you are granted SeeGroupDashboard globally, you can only see +# dashboards in groups you are in. +$user_obj->PrincipalObj->RevokeRight( + Right => 'SeeGroupDashboard', + Object => $inner_group, +); +$user_obj->PrincipalObj->GrantRight( + Right => 'SeeGroupDashboard', + Object => RT->System, +); +$m->get_ok("/Dashboards/index.html"); +$m->content_contains("inner dashboard", "Having SeeGroupDashboard gobally is fine for groups you are in"); +@loading = map {ref($_)."-".$_->Id} RT::Dashboard->new($currentuser)->ObjectsForLoading; +is_deeply( + \@loading, + ["RT::User-".$user_obj->Id, "RT::Group-".$inner_group->Id], + "SeeGroupDashboard globally still works for groups you are in" +); + +$inner_group->DeleteMember($user_obj->PrincipalObj->Id); +ok(!$outer_group->HasMemberRecursively($user_obj->PrincipalId), "outer no longer has user recursively"); +ok(!$inner_group->HasMemberRecursively($user_obj->PrincipalId), "inner no longer has user recursively"); +$m->get_ok("/Dashboards/index.html"); +$m->content_lacks("inner dashboard", "But global SeeGroupDashboard isn't enough for other groups"); +$m->no_warnings_ok; +@loading = map {ref($_)."-".$_->Id} RT::Dashboard->new($currentuser)->ObjectsForLoading; +is_deeply( + \@loading, + ["RT::User-".$user_obj->Id], + "We only have our SeeOwnDashboard right, as we are no longer in inner" +); + +# Similarly, if you're a SuperUser, you still only see dashboards for +# groups you belong to +$user_obj->PrincipalObj->RevokeRight( + Right => 'SeeGroupDashboard', + Object => RT->System, +); +$user_obj->PrincipalObj->GrantRight( + Right => 'SuperUser', + Object => RT->System, +); +$m->get_ok("/Dashboards/index.html"); +$m->content_lacks("inner dashboard", "Superuser can't see dashboards in groups they're not in"); +@loading = map {ref($_)."-".$_->Id} RT::Dashboard->new($currentuser)->ObjectsForLoading; +is_deeply( + \@loading, + ["RT::User-".$user_obj->Id, "RT::System-1"], + "We pick up the system-level SeeDashboard right from superuser" +); +@loading = map {ref($_)."-".$_->Id} RT::Dashboard->new($currentuser)->ObjectsForLoading(IncludeSuperuserGroups => 0); +is_deeply( + \@loading, + ["RT::User-".$user_obj->Id, "RT::System-1"], + "IncludeSuperusers only cuts out _group_ dashboard objects for loading, not user and system ones" +); + +$inner_group->AddMember($user_obj->PrincipalId); +$m->get_ok("/Dashboards/index.html"); +$m->content_contains("inner dashboard", "Superuser can see dashboards in groups they are in"); +@loading = map {ref($_)."-".$_->Id} RT::Dashboard->new($currentuser)->ObjectsForLoading; +is_deeply( + \@loading, + ["RT::User-".$user_obj->Id, "RT::Group-".$inner_group->Id, "RT::System-1"], + "Becoming a member of the group makes it a possibility" +); +@loading = map {ref($_)."-".$_->Id} RT::Dashboard->new($currentuser)->ObjectsForLoading(IncludeSuperuserGroups => 0); +is_deeply( + \@loading, + ["RT::User-".$user_obj->Id, "RT::System-1"], + "But only via superuser" +); + +$m->get_ok("/Dashboards/index.html"); +$m->content_contains("inner dashboard", "The dashboards list includes superuser rights"); +$m->get_ok("/index.html"); +$m->content_lacks("inner dashboard", "But the menu skips them"); diff --git a/rt/t/web/dashboards-permissions.t b/rt/t/web/dashboards-permissions.t index 172404289..f2e59e5cc 100644 --- a/rt/t/web/dashboards-permissions.t +++ b/rt/t/web/dashboards-permissions.t @@ -2,13 +2,13 @@ use strict; use warnings; -use RT::Test tests => 7; +use RT::Test nodata => 1, tests => 8; my ($baseurl, $m) = RT::Test->started_ok; my $url = $m->rt_base_url; # create user and queue {{{ -my $user_obj = RT::User->new($RT::SystemUser); +my $user_obj = RT::User->new(RT->SystemUser); my ($ok, $msg) = $user_obj->LoadOrCreateByEmail('customer@example.com'); ok($ok, 'ACL test user creation'); $user_obj->SetName('customer'); @@ -17,7 +17,7 @@ $user_obj->SetPrivileged(1); $user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf'); my $currentuser = RT::CurrentUser->new($user_obj); -my $queue = RT::Queue->new($RT::SystemUser); +my $queue = RT::Queue->new(RT->SystemUser); $queue->Create(Name => 'SearchQueue'.$$); $user_obj->PrincipalObj->GrantRight(Right => $_, Object => $queue) @@ -25,13 +25,11 @@ $user_obj->PrincipalObj->GrantRight(Right => $_, Object => $queue) $user_obj->PrincipalObj->GrantRight(Right => $_, Object => $RT::System) for qw/SubscribeDashboard CreateOwnDashboard SeeOwnDashboard ModifyOwnDashboard DeleteOwnDashboard/; -# }}} ok $m->login(customer => 'customer'), "logged in"; -$m->get_ok("$url/Dashboards"); -$m->follow_link_ok({text => "New"}); +$m->follow_link_ok( {id => 'home-dashboard_create'}); $m->form_name('ModifyDashboard'); is_deeply([$m->current_form->find_input('Privacy')->possible_values], ["RT::User-" . $user_obj->Id], "the only selectable privacy is user"); $m->content_lacks('Delete', "Delete button hidden because we are creating"); diff --git a/rt/t/web/dashboards-search-cache.t b/rt/t/web/dashboards-search-cache.t new file mode 100644 index 000000000..ad2a96969 --- /dev/null +++ b/rt/t/web/dashboards-search-cache.t @@ -0,0 +1,73 @@ +#!/usr/bin/perl -w +use strict; + +use RT::Test tests => 20; +my ($baseurl, $m) = RT::Test->started_ok; + +my $url = $m->rt_base_url; + +ok($m->login, 'logged in'); + +# create a search +$m->follow_link_ok({text => 'Tickets'}, 'to query builder'); +$m->form_name('BuildQuery'); + +$m->field(ValueOfid => 10 ); +$m->click('AddClause'); +$m->text_contains( 'id < 10', 'added new clause'); + +$m->form_name('BuildQuery'); +$m->field(SavedSearchDescription => 'Original Name'); +$m->click('SavedSearchSave'); + +# create a dashboard +$m->get_ok("$url/Dashboards/Modify.html?Create=1"); +$m->form_name('ModifyDashboard'); +$m->field('Name' => 'cachey dashboard'); +$m->click_button(value => 'Create'); +$m->text_contains('Saved dashboard cachey dashboard'); + +my ($dashboard_id) = $m->content =~ /name="id" value="(\d+)"/; +ok($dashboard_id, "got an ID, $dashboard_id"); + +# add the search to the dashboard +$m->follow_link_ok({text => 'Content'}); +my $form = $m->form_name('Dashboard-Searches-body'); +my @input = $form->find_input('Searches-body-Available'); +my ($search) = + map { ( $_->possible_values )[1] } + grep { ( $_->value_names )[1] =~ /Saved Search: Original Name/ } @input; +$form->value('Searches-body-Available' => $search ); +$m->click_button(name => 'add'); +$m->text_contains('Dashboard updated'); + +# subscribe to the dashboard +$m->follow_link_ok({text => 'Subscription'}); +$m->text_contains('Saved Search: Original Name'); +$m->form_name('SubscribeDashboard'); +$m->click_button(name => 'Save'); +$m->text_contains('Subscribed to dashboard cachey dashboard'); + +# rename the search +$m->follow_link_ok({text => 'Tickets'}, 'to query builder'); +$form = $m->form_name('BuildQuery'); +@input = $form->find_input('SavedSearchLoad'); +($search) = + map { ( $_->possible_values )[1] } + grep { ( $_->value_names )[1] =~ /Original Name/ } @input; +$form->value('SavedSearchLoad' => $search ); +$m->click_button(value => 'Load'); +$m->text_contains('Loaded saved search "Original Name"'); + +$m->form_name('BuildQuery'); +$m->field('SavedSearchDescription' => 'New Name'); +$m->click_button(value => 'Update'); +$m->text_contains('Updated saved search "New Name"'); + +# check subscription page again +$m->get_ok("/Dashboards/Subscription.html?id=$dashboard_id"); +TODO: { + local $TODO = 'we cache search names too aggressively'; + $m->text_contains('Saved Search: New Name'); + $m->text_unlike(qr/Saved Search: Original Name/); # t-w-m lacks text_lacks +} diff --git a/rt/t/web/gnupg-headers.t b/rt/t/web/gnupg-headers.t new file mode 100644 index 000000000..88b3ab9c2 --- /dev/null +++ b/rt/t/web/gnupg-headers.t @@ -0,0 +1,53 @@ +#!/usr/bin/perl -w +use strict; + +use RT::Test::GnuPG + tests => 15, + gnupg_options => { + passphrase => 'recipient', + 'trust-model' => 'always', + }; + +RT::Test->import_gnupg_key( 'recipient@example.com', 'public' ); +RT::Test->import_gnupg_key( 'general@example.com', 'secret' ); + +ok( my $user = RT::User->new( RT->SystemUser ) ); +ok( $user->Load('root'), "Loaded user 'root'" ); +$user->SetEmailAddress('recipient@example.com'); + +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'general@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; +my $qid = $queue->id; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +diag "test with Encrypt and Sign disabled"; + +$m->goto_create_ticket($queue); +$m->form_name('TicketCreate'); +$m->field( 'Subject', 'Signing test' ); +$m->field( 'Content', 'Some other content' ); +$m->submit; +$m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); +$m->follow_link_ok( { text => 'with headers' } ); +$m->content_contains('X-RT-Encrypt: 0'); +$m->content_contains('X-RT-Sign: 0'); + +diag "test with Encrypt and Sign enabled"; + +$m->goto_create_ticket($queue); +$m->form_name('TicketCreate'); +$m->field( 'Subject', 'Signing test' ); +$m->field( 'Content', 'Some other content' ); +$m->tick( 'Encrypt', 1 ); +$m->tick( 'Sign', 1 ); +$m->submit; +$m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); +$m->follow_link_ok( { text => 'with headers' } ); +$m->content_contains('X-RT-Encrypt: 1'); +$m->content_contains('X-RT-Sign: 1'); + diff --git a/rt/t/web/gnupg-select-keys-on-create.t b/rt/t/web/gnupg-select-keys-on-create.t index deee6b291..cf27e48a7 100644 --- a/rt/t/web/gnupg-select-keys-on-create.t +++ b/rt/t/web/gnupg-select-keys-on-create.t @@ -2,34 +2,8 @@ use strict; use warnings; -use RT::Test tests => 60; - -plan skip_all => 'GnuPG required.' - unless eval 'use GnuPG::Interface; 1'; -plan skip_all => 'gpg executable is required.' - unless RT::Test->find_executable('gpg'); - - +use RT::Test::GnuPG tests => 79, gnupg_options => { passphrase => 'rt-test' }; use RT::Action::SendEmail; -use File::Temp qw(tempdir); - -RT::Test->set_mail_catcher; - -use_ok('RT::Crypt::GnuPG'); - -RT->Config->Set( GnuPG => - Enable => 1, - OutgoingMessagesFormat => 'RFC', -); - -RT->Config->Set( GnuPGOptions => - homedir => scalar tempdir( CLEANUP => 0 ), - passphrase => 'rt-test', - 'no-permission-warning' => undef, -); -diag "GnuPG --homedir ". RT->Config->Get('GnuPGOptions')->{'homedir'} if $ENV{TEST_VERBOSE}; - -RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' ); my $queue = RT::Test->load_or_create_queue( Name => 'Regression', @@ -38,28 +12,24 @@ my $queue = RT::Test->load_or_create_queue( ); ok $queue && $queue->id, 'loaded or created queue'; -RT::Test->set_rights( - Principal => 'Everyone', - Right => ['CreateTicket', 'ShowTicket', 'SeeQueue', 'ReplyToTicket', 'ModifyTicket'], -); - my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'logged in'; -diag "check that signing doesn't work if there is no key" if $ENV{TEST_VERBOSE}; +diag "check that signing doesn't work if there is no key"; { RT::Test->clean_caught_mails; ok $m->goto_create_ticket( $queue ), "UI -> create ticket"; - $m->form_number(3); + $m->form_name('TicketCreate'); $m->tick( Sign => 1 ); $m->field( Requestors => 'rt-test@example.com' ); $m->field( Content => 'Some content' ); $m->submit; - $m->content_like( - qr/unable to sign outgoing email messages/i, + $m->content_contains( + 'unable to sign outgoing email messages', 'problems with passphrase' ); + $m->warning_like(qr/signing failed: secret key not available/); my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; @@ -72,33 +42,36 @@ diag "check that signing doesn't work if there is no key" if $ENV{TEST_VERBOSE}; is $res{'info'}[0]{'TrustTerse'}, 'ultimate', 'ultimately trusted key'; } -diag "check that things don't work if there is no key" if $ENV{TEST_VERBOSE}; +diag "check that things don't work if there is no key"; { RT::Test->clean_caught_mails; ok $m->goto_create_ticket( $queue ), "UI -> create ticket"; - $m->form_number(3); + $m->form_name('TicketCreate'); $m->tick( Encrypt => 1 ); $m->field( Requestors => 'rt-test@example.com' ); $m->field( Content => 'Some content' ); $m->submit; - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There is no key suitable for encryption/i, + $m->content_contains( + 'There is no key suitable for encryption', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketCreate'); ok !$form->find_input( 'UseKey-rt-test@example.com' ), 'no key selector'; my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; + + $m->next_warning_like(qr/public key not found/) for 1 .. 4; + $m->no_leftover_warnings_ok; } -diag "import first key of rt-test\@example.com" if $ENV{TEST_VERBOSE}; +diag "import first key of rt-test\@example.com"; my $fpr1 = ''; { RT::Test->import_gnupg_key('rt-test@example.com', 'public'); @@ -107,45 +80,47 @@ my $fpr1 = ''; $fpr1 = $res{'info'}[0]{'Fingerprint'}; } -diag "check that things still doesn't work if key is not trusted" if $ENV{TEST_VERBOSE}; +diag "check that things still doesn't work if key is not trusted"; { RT::Test->clean_caught_mails; ok $m->goto_create_ticket( $queue ), "UI -> create ticket"; - $m->form_number(3); + $m->form_name('TicketCreate'); $m->tick( Encrypt => 1 ); $m->field( Requestors => 'rt-test@example.com' ); $m->field( Content => 'Some content' ); $m->submit; - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There is one suitable key, but trust level is not set/i, + $m->content_contains( + 'There is one suitable key, but trust level is not set', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketCreate'); ok my $input = $form->find_input( 'UseKey-rt-test@example.com' ), 'found key selector'; is scalar $input->possible_values, 1, 'one option'; $m->select( 'UseKey-rt-test@example.com' => $fpr1 ); $m->submit; - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/Selected key either is not trusted/i, + $m->content_contains( + 'Selected key either is not trusted', 'problems with keys' ); my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; + + $m->no_warnings_ok; } -diag "import a second key of rt-test\@example.com" if $ENV{TEST_VERBOSE}; +diag "import a second key of rt-test\@example.com"; my $fpr2 = ''; { RT::Test->import_gnupg_key('rt-test@example.com.2', 'public'); @@ -154,42 +129,44 @@ my $fpr2 = ''; $fpr2 = $res{'info'}[2]{'Fingerprint'}; } -diag "check that things still doesn't work if two keys are not trusted" if $ENV{TEST_VERBOSE}; +diag "check that things still doesn't work if two keys are not trusted"; { RT::Test->clean_caught_mails; ok $m->goto_create_ticket( $queue ), "UI -> create ticket"; - $m->form_number(3); + $m->form_name('TicketCreate'); $m->tick( Encrypt => 1 ); $m->field( Requestors => 'rt-test@example.com' ); $m->field( Content => 'Some content' ); $m->submit; - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There are several keys suitable for encryption/i, + $m->content_contains( + 'There are several keys suitable for encryption', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketCreate'); ok my $input = $form->find_input( 'UseKey-rt-test@example.com' ), 'found key selector'; is scalar $input->possible_values, 2, 'two options'; $m->select( 'UseKey-rt-test@example.com' => $fpr1 ); $m->submit; - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/Selected key either is not trusted/i, + $m->content_contains( + 'Selected key either is not trusted', 'problems with keys' ); my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; + + $m->no_warnings_ok; } { @@ -204,26 +181,28 @@ diag "check that we see key selector even if only one key is trusted but there a RT::Test->clean_caught_mails; ok $m->goto_create_ticket( $queue ), "UI -> create ticket"; - $m->form_number(3); + $m->form_name('TicketCreate'); $m->tick( Encrypt => 1 ); $m->field( Requestors => 'rt-test@example.com' ); $m->field( Content => 'Some content' ); $m->submit; - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There are several keys suitable for encryption/i, + $m->content_contains( + 'There are several keys suitable for encryption', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketCreate'); ok my $input = $form->find_input( 'UseKey-rt-test@example.com' ), 'found key selector'; is scalar $input->possible_values, 2, 'two options'; my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; + + $m->no_warnings_ok; } diag "check that key selector works and we can select trusted key"; @@ -231,21 +210,21 @@ diag "check that key selector works and we can select trusted key"; RT::Test->clean_caught_mails; ok $m->goto_create_ticket( $queue ), "UI -> create ticket"; - $m->form_number(3); + $m->form_name('TicketCreate'); $m->tick( Encrypt => 1 ); $m->field( Requestors => 'rt-test@example.com' ); $m->field( Content => 'Some content' ); $m->submit; - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There are several keys suitable for encryption/i, + $m->content_contains( + 'There are several keys suitable for encryption', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketCreate'); ok my $input = $form->find_input( 'UseKey-rt-test@example.com' ), 'found key selector'; is scalar $input->possible_values, 2, 'two options'; @@ -256,6 +235,8 @@ diag "check that key selector works and we can select trusted key"; my @mail = RT::Test->fetch_caught_mails; ok @mail, 'there are some emails'; check_text_emails( { Encrypt => 1 }, @mail ); + + $m->no_warnings_ok; } diag "check encrypting of attachments"; @@ -263,22 +244,22 @@ diag "check encrypting of attachments"; RT::Test->clean_caught_mails; ok $m->goto_create_ticket( $queue ), "UI -> create ticket"; - $m->form_number(3); + $m->form_name('TicketCreate'); $m->tick( Encrypt => 1 ); $m->field( Requestors => 'rt-test@example.com' ); $m->field( Content => 'Some content' ); $m->field( Attach => $0 ); $m->submit; - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There are several keys suitable for encryption/i, + $m->content_contains( + 'There are several keys suitable for encryption', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketCreate'); ok my $input = $form->find_input( 'UseKey-rt-test@example.com' ), 'found key selector'; is scalar $input->possible_values, 2, 'two options'; @@ -289,37 +270,7 @@ diag "check encrypting of attachments"; my @mail = RT::Test->fetch_caught_mails; ok @mail, 'there are some emails'; check_text_emails( { Encrypt => 1, Attachment => 1 }, @mail ); -} -sub check_text_emails { - my %args = %{ shift @_ }; - my @mail = @_; - - ok scalar @mail, "got some mail"; - for my $mail (@mail) { - for my $type ('email', 'attachment') { - next if $type eq 'attachment' && !$args{'Attachment'}; - - my $content = $type eq 'email' - ? "Some content" - : "Attachment content"; - - if ( $args{'Encrypt'} ) { - unlike $mail, qr/$content/, "outgoing $type was encrypted"; - } else { - like $mail, qr/$content/, "outgoing $type was not encrypted"; - } - - next unless $type eq 'email'; - - if ( $args{'Sign'} && $args{'Encrypt'} ) { - like $mail, qr/BEGIN PGP MESSAGE/, 'outgoing email was signed'; - } elsif ( $args{'Sign'} ) { - like $mail, qr/SIGNATURE/, 'outgoing email was signed'; - } else { - unlike $mail, qr/SIGNATURE/, 'outgoing email was not signed'; - } - } - } + $m->no_warnings_ok; } diff --git a/rt/t/web/gnupg-select-keys-on-update.t b/rt/t/web/gnupg-select-keys-on-update.t index 76817ddf2..4842dcd54 100644 --- a/rt/t/web/gnupg-select-keys-on-update.t +++ b/rt/t/web/gnupg-select-keys-on-update.t @@ -2,34 +2,9 @@ use strict; use warnings; -use RT::Test tests => 68; - -plan skip_all => 'GnuPG required.' - unless eval 'use GnuPG::Interface; 1'; -plan skip_all => 'gpg executable is required.' - unless RT::Test->find_executable('gpg'); - +use RT::Test::GnuPG tests => 86, gnupg_options => { passphrase => 'rt-test' }; use RT::Action::SendEmail; -use File::Temp qw(tempdir); - -RT::Test->set_mail_catcher; - -use_ok('RT::Crypt::GnuPG'); - -RT->Config->Set( GnuPG => - Enable => 1, - OutgoingMessagesFormat => 'RFC', -); - -RT->Config->Set( GnuPGOptions => - homedir => scalar tempdir( CLEANUP => 0 ), - passphrase => 'rt-test', - 'no-permission-warning' => undef, -); -diag "GnuPG --homedir ". RT->Config->Get('GnuPGOptions')->{'homedir'} if $ENV{TEST_VERBOSE}; - -RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' ); my $queue = RT::Test->load_or_create_queue( Name => 'Regression', @@ -38,18 +13,13 @@ my $queue = RT::Test->load_or_create_queue( ); ok $queue && $queue->id, 'loaded or created queue'; -RT::Test->set_rights( - Principal => 'Everyone', - Right => ['CreateTicket', 'ShowTicket', 'SeeQueue', 'ReplyToTicket', 'ModifyTicket'], -); - my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'logged in'; my $tid; { - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); ($tid) = $ticket->Create( Subject => 'test', Queue => $queue->id, @@ -57,24 +27,27 @@ my $tid; ok $tid, 'ticket created'; } -diag "check that signing doesn't work if there is no key" if $ENV{TEST_VERBOSE}; +diag "check that signing doesn't work if there is no key"; { RT::Test->clean_caught_mails; ok $m->goto_ticket( $tid ), "UI -> ticket #$tid"; $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' ); - $m->form_number(3); + $m->form_name('TicketUpdate'); $m->tick( Sign => 1 ); $m->field( UpdateCc => 'rt-test@example.com' ); $m->field( UpdateContent => 'Some content' ); $m->click('SubmitTicket'); - $m->content_like( - qr/unable to sign outgoing email messages/i, + $m->content_contains( + 'unable to sign outgoing email messages', 'problems with passphrase' ); my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; + + $m->next_warning_like(qr/secret key not available/); + $m->no_leftover_warnings_ok; } { @@ -84,35 +57,38 @@ diag "check that signing doesn't work if there is no key" if $ENV{TEST_VERBOSE}; is $res{'info'}[0]{'TrustTerse'}, 'ultimate', 'ultimately trusted key'; } -diag "check that things don't work if there is no key" if $ENV{TEST_VERBOSE}; +diag "check that things don't work if there is no key"; { RT::Test->clean_caught_mails; ok $m->goto_ticket( $tid ), "UI -> ticket #$tid"; $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' ); - $m->form_number(3); + $m->form_name('TicketUpdate'); $m->tick( Encrypt => 1 ); $m->field( UpdateCc => 'rt-test@example.com' ); $m->field( UpdateContent => 'Some content' ); $m->click('SubmitTicket'); - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There is no key suitable for encryption/i, + $m->content_contains( + 'There is no key suitable for encryption', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketUpdate'); ok !$form->find_input( 'UseKey-rt-test@example.com' ), 'no key selector'; my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; + + $m->next_warning_like(qr/public key not found/) for 1 .. 2; + $m->no_leftover_warnings_ok; } -diag "import first key of rt-test\@example.com" if $ENV{TEST_VERBOSE}; +diag "import first key of rt-test\@example.com"; my $fpr1 = ''; { RT::Test->import_gnupg_key('rt-test@example.com', 'public'); @@ -121,46 +97,48 @@ my $fpr1 = ''; $fpr1 = $res{'info'}[0]{'Fingerprint'}; } -diag "check that things still doesn't work if key is not trusted" if $ENV{TEST_VERBOSE}; +diag "check that things still doesn't work if key is not trusted"; { RT::Test->clean_caught_mails; ok $m->goto_ticket( $tid ), "UI -> ticket #$tid"; $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' ); - $m->form_number(3); + $m->form_name('TicketUpdate'); $m->tick( Encrypt => 1 ); $m->field( UpdateCc => 'rt-test@example.com' ); $m->field( UpdateContent => 'Some content' ); $m->click('SubmitTicket'); - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There is one suitable key, but trust level is not set/i, + $m->content_contains( + 'There is one suitable key, but trust level is not set', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketUpdate'); ok my $input = $form->find_input( 'UseKey-rt-test@example.com' ), 'found key selector'; is scalar $input->possible_values, 1, 'one option'; $m->select( 'UseKey-rt-test@example.com' => $fpr1 ); $m->click('SubmitTicket'); - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/Selected key either is not trusted/i, + $m->content_contains( + 'Selected key either is not trusted', 'problems with keys' ); my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; + + $m->no_warnings_ok; } -diag "import a second key of rt-test\@example.com" if $ENV{TEST_VERBOSE}; +diag "import a second key of rt-test\@example.com"; my $fpr2 = ''; { RT::Test->import_gnupg_key('rt-test@example.com.2', 'public'); @@ -169,43 +147,45 @@ my $fpr2 = ''; $fpr2 = $res{'info'}[2]{'Fingerprint'}; } -diag "check that things still doesn't work if two keys are not trusted" if $ENV{TEST_VERBOSE}; +diag "check that things still doesn't work if two keys are not trusted"; { RT::Test->clean_caught_mails; ok $m->goto_ticket( $tid ), "UI -> ticket #$tid"; $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' ); - $m->form_number(3); + $m->form_name('TicketUpdate'); $m->tick( Encrypt => 1 ); $m->field( UpdateCc => 'rt-test@example.com' ); $m->field( UpdateContent => 'Some content' ); $m->click('SubmitTicket'); - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There are several keys suitable for encryption/i, + $m->content_contains( + 'There are several keys suitable for encryption', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketUpdate'); ok my $input = $form->find_input( 'UseKey-rt-test@example.com' ), 'found key selector'; is scalar $input->possible_values, 2, 'two options'; $m->select( 'UseKey-rt-test@example.com' => $fpr1 ); $m->click('SubmitTicket'); - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/Selected key either is not trusted/i, + $m->content_contains( + 'Selected key either is not trusted', 'problems with keys' ); my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; + + $m->no_warnings_ok; } { @@ -215,130 +195,103 @@ diag "check that things still doesn't work if two keys are not trusted" if $ENV{ is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key'; } -diag "check that we see key selector even if only one key is trusted but there are more keys" if $ENV{TEST_VERBOSE}; +diag "check that we see key selector even if only one key is trusted but there are more keys"; { RT::Test->clean_caught_mails; ok $m->goto_ticket( $tid ), "UI -> ticket #$tid"; $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' ); - $m->form_number(3); + $m->form_name('TicketUpdate'); $m->tick( Encrypt => 1 ); $m->field( UpdateCc => 'rt-test@example.com' ); $m->field( UpdateContent => 'Some content' ); $m->click('SubmitTicket'); - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There are several keys suitable for encryption/i, + $m->content_contains( + 'There are several keys suitable for encryption', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketUpdate'); ok my $input = $form->find_input( 'UseKey-rt-test@example.com' ), 'found key selector'; is scalar $input->possible_values, 2, 'two options'; my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; + + $m->no_warnings_ok; } -diag "check that key selector works and we can select trusted key" if $ENV{TEST_VERBOSE}; +diag "check that key selector works and we can select trusted key"; { RT::Test->clean_caught_mails; ok $m->goto_ticket( $tid ), "UI -> ticket #$tid"; $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' ); - $m->form_number(3); + $m->form_name('TicketUpdate'); $m->tick( Encrypt => 1 ); $m->field( UpdateCc => 'rt-test@example.com' ); $m->field( UpdateContent => 'Some content' ); $m->click('SubmitTicket'); - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There are several keys suitable for encryption/i, + $m->content_contains( + 'There are several keys suitable for encryption', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketUpdate'); ok my $input = $form->find_input( 'UseKey-rt-test@example.com' ), 'found key selector'; is scalar $input->possible_values, 2, 'two options'; $m->select( 'UseKey-rt-test@example.com' => $fpr1 ); $m->click('SubmitTicket'); - $m->content_like( qr/Message recorded/i, 'Message recorded' ); + $m->content_contains('Message recorded', 'Message recorded' ); my @mail = RT::Test->fetch_caught_mails; ok @mail, 'there are some emails'; check_text_emails( { Encrypt => 1 }, @mail ); + + $m->no_warnings_ok; } -diag "check encrypting of attachments" if $ENV{TEST_VERBOSE}; +diag "check encrypting of attachments"; { RT::Test->clean_caught_mails; ok $m->goto_ticket( $tid ), "UI -> ticket #$tid"; $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' ); - $m->form_number(3); + $m->form_name('TicketUpdate'); $m->tick( Encrypt => 1 ); $m->field( UpdateCc => 'rt-test@example.com' ); $m->field( UpdateContent => 'Some content' ); $m->field( Attach => $0 ); $m->click('SubmitTicket'); - $m->content_like( - qr/You are going to encrypt outgoing email messages/i, + $m->content_contains( + 'You are going to encrypt outgoing email messages', 'problems with keys' ); - $m->content_like( - qr/There are several keys suitable for encryption/i, + $m->content_contains( + 'There are several keys suitable for encryption', 'problems with keys' ); - my $form = $m->form_number(3); + my $form = $m->form_name('TicketUpdate'); ok my $input = $form->find_input( 'UseKey-rt-test@example.com' ), 'found key selector'; is scalar $input->possible_values, 2, 'two options'; $m->select( 'UseKey-rt-test@example.com' => $fpr1 ); $m->click('SubmitTicket'); - $m->content_like( qr/Message recorded/i, 'Message recorded' ); + $m->content_contains('Message recorded', 'Message recorded' ); my @mail = RT::Test->fetch_caught_mails; ok @mail, 'there are some emails'; check_text_emails( { Encrypt => 1, Attachment => 1 }, @mail ); -} -sub check_text_emails { - my %args = %{ shift @_ }; - my @mail = @_; - - ok scalar @mail, "got some mail"; - for my $mail (@mail) { - for my $type ('email', 'attachment') { - next if $type eq 'attachment' && !$args{'Attachment'}; - - my $content = $type eq 'email' - ? "Some content" - : "Attachment content"; - - if ( $args{'Encrypt'} ) { - unlike $mail, qr/$content/, "outgoing $type was encrypted"; - } else { - like $mail, qr/$content/, "outgoing $type was not encrypted"; - } - - next unless $type eq 'email'; - - if ( $args{'Sign'} && $args{'Encrypt'} ) { - like $mail, qr/BEGIN PGP MESSAGE/, 'outgoing email was signed'; - } elsif ( $args{'Sign'} ) { - like $mail, qr/SIGNATURE/, 'outgoing email was signed'; - } else { - unlike $mail, qr/SIGNATURE/, 'outgoing email was not signed'; - } - } - } + $m->no_warnings_ok; } - diff --git a/rt/t/web/gnupg-tickyboxes.t b/rt/t/web/gnupg-tickyboxes.t new file mode 100644 index 000000000..944539758 --- /dev/null +++ b/rt/t/web/gnupg-tickyboxes.t @@ -0,0 +1,84 @@ +#!/usr/bin/perl -w +use strict; +use warnings; + +use RT::Test::GnuPG tests => 22, gnupg_options => { passphrase => 'rt-test' }; + +use RT::Action::SendEmail; + +RT::Test->import_gnupg_key('rt-recipient@example.com'); +RT::Test->import_gnupg_key('rt-test@example.com', 'public'); + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +my @variants = ( + {}, + { Sign => 1 }, + { Encrypt => 1 }, + { Sign => 1, Encrypt => 1 }, +); + +# collect emails +my %mail = ( + plain => [], + signed => [], + encrypted => [], + signed_encrypted => [], +); + +diag "check in read-only mode that queue's props influence create/update ticket pages"; +{ + foreach my $variant ( @variants ) { + set_queue_crypt_options( $queue => %$variant ); + $m->goto_create_ticket( $queue ); + $m->form_name('TicketCreate'); + if ( $variant->{'Encrypt'} ) { + ok $m->value('Encrypt', 2), "encrypt tick box is checked"; + } else { + ok !$m->value('Encrypt', 2), "encrypt tick box is unchecked"; + } + if ( $variant->{'Sign'} ) { + ok $m->value('Sign', 2), "sign tick box is checked"; + } else { + ok !$m->value('Sign', 2), "sign tick box is unchecked"; + } + } + + # to avoid encryption/signing during create + set_queue_crypt_options($queue); + + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id) = $ticket->Create( + Subject => 'test', + Queue => $queue->id, + Requestor => 'rt-test@example.com', + ); + ok $id, 'ticket created'; + + foreach my $variant ( @variants ) { + set_queue_crypt_options( $queue => %$variant ); + $m->get( $m->rt_base_url . "/Ticket/Update.html?Action=Respond&id=$id" ); + $m->form_name('TicketUpdate'); + if ( $variant->{'Encrypt'} ) { + ok $m->value('Encrypt', 2), "encrypt tick box is checked"; + } else { + ok !$m->value('Encrypt', 2), "encrypt tick box is unchecked"; + } + if ( $variant->{'Sign'} ) { + ok $m->value('Sign', 2), "sign tick box is checked"; + } else { + ok !$m->value('Sign', 2), "sign tick box is unchecked"; + } + } +} + + + diff --git a/rt/t/web/googleish_search.t b/rt/t/web/googleish_search.t new file mode 100644 index 000000000..e2a4e9116 --- /dev/null +++ b/rt/t/web/googleish_search.t @@ -0,0 +1,219 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use RT::Test tests => 96, config => 'Set( %FullTextSearch, Enable => 1, Indexed => 0 );'; +my ($baseurl, $m) = RT::Test->started_ok; +my $url = $m->rt_base_url; + +my $queue = RT::Queue->new($RT::SystemUser); +$queue->Create( Name => 'other' ); +ok( $queue->id, 'created queue other'); + +my $two_words_queue = RT::Test->load_or_create_queue( + Name => 'Two Words', +); +ok $two_words_queue && $two_words_queue->id, 'loaded or created a queue'; + + +{ + my $tickets = RT::Tickets->new( RT->SystemUser ); + my $active = "( ".join( " OR ", map "Status = '$_'", RT::Queue->ActiveStatusArray())." )"; + my $inactive = "( ".join( " OR ", map "Status = '$_'", RT::Queue->InactiveStatusArray())." )"; + + require RT::Search::Googleish; + my $parser = RT::Search::Googleish->new( + TicketsObj => $tickets, + Argument => '', + ); + is $parser->QueryToSQL("foo"), "$active AND ( Subject LIKE 'foo' )", "correct parsing"; + is $parser->QueryToSQL("1"), "( Id = 1 )", "correct parsing"; + is $parser->QueryToSQL("#1"), "( Id = 1 )", "correct parsing"; + is $parser->QueryToSQL("'1'"), "$active AND ( Subject LIKE '1' )", "correct parsing"; + + is $parser->QueryToSQL("foo bar"), + "$active AND ( Subject LIKE 'foo' AND Subject LIKE 'bar' )", + "correct parsing"; + is $parser->QueryToSQL("'foo bar'"), + "$active AND ( Subject LIKE 'foo bar' )", + "correct parsing"; + + is $parser->QueryToSQL("'foo \\' bar'"), + "$active AND ( Subject LIKE 'foo \\' bar' )", + "correct parsing"; + is $parser->QueryToSQL('"foo \' bar"'), + "$active AND ( Subject LIKE 'foo \\' bar' )", + "correct parsing"; + is $parser->QueryToSQL('"\f\o\o"'), + "$active AND ( Subject LIKE '\\\\f\\\\o\\\\o' )", + "correct parsing"; + + is $parser->QueryToSQL("General"), "( Queue = 'General' ) AND $active", "correct parsing"; + is $parser->QueryToSQL("'Two Words'"), "$active AND ( Subject LIKE 'Two Words' )", "correct parsing"; + is $parser->QueryToSQL("queue:'Two Words'"), "( Queue = 'Two Words' ) AND $active", "correct parsing"; + is $parser->QueryToSQL("subject:'Two Words'"), "$active AND ( Subject LIKE 'Two Words' )", "correct parsing"; + + is $parser->QueryToSQL("me"), "( Owner.id = '__CurrentUser__' ) AND $active", "correct parsing"; + is $parser->QueryToSQL("'me'"), "$active AND ( Subject LIKE 'me' )", "correct parsing"; + is $parser->QueryToSQL("owner:me"), "( Owner.id = '__CurrentUser__' ) AND $active", "correct parsing"; + is $parser->QueryToSQL("owner:'me'"), "( Owner = 'me' ) AND $active", "correct parsing"; + + is $parser->QueryToSQL("resolved me"), "( Owner.id = '__CurrentUser__' ) AND ( Status = 'resolved' )", "correct parsing"; + is $parser->QueryToSQL("resolved active me"), "( Owner.id = '__CurrentUser__' ) AND ( Status = 'resolved' OR Status = 'new' OR Status = 'open' OR Status = 'stalled' )", "correct parsing"; + is $parser->QueryToSQL("status:active"), $active, "Explicit active search"; + is $parser->QueryToSQL("status:'active'"), "( Status = 'active' )", "Quoting active makes it the actual word"; + is $parser->QueryToSQL("inactive me"), "( Owner.id = '__CurrentUser__' ) AND $inactive", "correct parsing"; + + is $parser->QueryToSQL("cf.Foo:bar"), "( 'CF.{Foo}' LIKE 'bar' ) AND $active", "correct parsing of CFs"; + is $parser->QueryToSQL(q{cf."don't foo?":'bar n\\' baz'}), qq/( 'CF.{don\\'t foo?}' LIKE 'bar n\\' baz' ) AND $active/, "correct parsing of CFs with quotes"; +} + +my $ticket_found_1 = RT::Ticket->new($RT::SystemUser); +my $ticket_found_2 = RT::Ticket->new($RT::SystemUser); +my $ticket_not_found = RT::Ticket->new($RT::SystemUser); + +$ticket_found_1->Create( + Subject => 'base ticket 1'.$$, + Queue => 'general', + Owner => 'root', + Requestor => 'customsearch@localhost', + Content => 'this is base ticket 1', +); +ok( $ticket_found_1->id, 'created ticket for custom search'); + + +$ticket_found_2->Create( + Subject => 'base ticket 2'.$$, + Queue => 'general', + Owner => 'root', + Requestor => 'customsearch@localhost', + Content => 'this is base ticket 2', +); +ok( $ticket_found_2->id, 'created ticket for custom search'); + +$ticket_not_found = RT::Ticket->new($RT::SystemUser); +$ticket_not_found->Create( + Subject => 'not found subject' . $$, + Queue => 'other', + Owner => 'nobody', + Requestor => 'notfound@localhost', + Content => 'this is not found content', +); +ok( $ticket_not_found->id, 'created ticket for custom search'); + +ok($m->login, 'logged in'); + +my @queries = ( + 'base ticket', 'root', + 'customsearch@localhost', 'requestor:customsearch', + 'subject:base', 'subject:"base ticket"', + 'queue:general', 'owner:root', +); + +for my $q (@queries) { + $m->form_with_fields('q'); + $m->field( q => $q ); + $m->submit; + $m->content_contains( 'base ticket 1', 'base ticket 1 is found' ); + $m->content_contains( 'base ticket 2', 'base ticket 2 is found' ); + $m->content_lacks( 'not found subject', 'not found ticket is not found' ); +} + +$ticket_not_found->SetStatus('open'); +is( $ticket_not_found->Status, 'open', 'status of not found ticket is open' ); +@queries = qw/new status:new/; +for my $q (@queries) { + $m->form_with_fields('q'); + $m->field( q => $q ); + $m->submit; + $m->content_contains( 'base ticket 1', 'base ticket 1 is found' ); + $m->content_contains( 'base ticket 2', 'base ticket 2 is found' ); + $m->content_lacks( 'not found subject', 'not found ticket is not found' ); +} + +@queries = ( 'fulltext:"base ticket 1"', "'base ticket 1'" ); +for my $q (@queries) { + $m->form_with_fields('q'); + $m->field( q => $q ); + $m->submit; + $m->content_contains( 'base ticket 1', 'base ticket 1 is found' ); + $m->content_lacks( 'base ticket 2', 'base ticket 2 is not found' ); + $m->content_lacks( 'not found subject', 'not found ticket is not found' ); +} + +# now let's test with ' or " +for my $quote ( q{'}, q{"} ) { + my $user = RT::User->new($RT::SystemUser); + is( ref($user), 'RT::User' ); + my ( $id, $msg ) = $user->Create( + Name => qq!foo${quote}bar!, + EmailAddress => qq!foo${quote}bar$$\@example.com !, + Privileged => 1, + ); + ok ($id, "Creating user - " . $msg ); + + my ( $grantid, $grantmsg ) = + $user->PrincipalObj->GrantRight( Right => 'OwnTicket' ); + ok( $grantid, $grantmsg ); + + + + my $ticket_quote = RT::Ticket->new($RT::SystemUser); + $ticket_quote->Create( + Subject => qq!base${quote}ticket $$!, + Queue => 'general', + Owner => $user->Name, + Requestor => qq!custom${quote}search\@localhost!, + Content => qq!this is base${quote}ticket with quote inside!, + ); + ok( $ticket_quote->id, 'created ticket with quote for custom search' ); + + @queries = ( + qq!fulltext:base${quote}ticket!, + "base${quote}ticket", + "owner:foo${quote}bar", + "foo${quote}bar", + + # email doesn't allow " character + $quote eq q{'} + ? ( + "requestor:custom${quote}search\@localhost", + "custom${quote}search\@localhost", + ) + : (), + ); + for my $q (@queries) { + $m->form_with_fields('q'); + $m->field( q => $q ); + $m->submit; + my $escape_quote = $quote; + RT::Interface::Web::EscapeUTF8(\$escape_quote); + $m->content_contains( "base${escape_quote}ticket", + "base${quote}ticket is found" ); + } +} + +# Create a CF +{ + my $cf = RT::CustomField->new(RT->SystemUser); + ok( $cf->Create(Name => 'Foo', Type => 'Freeform', MaxValues => '1', Queue => 0) ); + ok $cf->Id; + + $ticket_found_1->AddCustomFieldValue( Field => 'Foo', Value => 'bar' ); + $ticket_found_2->AddCustomFieldValue( Field => 'Foo', Value => 'bar' ); + $ticket_not_found->AddCustomFieldValue( Field => 'Foo', Value => 'baz' ); + is( $ticket_found_1->FirstCustomFieldValue('Foo'), 'bar', 'cf value is ok' ); + is( $ticket_found_2->FirstCustomFieldValue('Foo'), 'bar', 'cf value is ok' ); + is( $ticket_not_found->FirstCustomFieldValue('Foo'), 'baz', 'cf value is ok' ); + + @queries = qw/cf.Foo:bar/; + for my $q (@queries) { + $m->form_with_fields('q'); + $m->field( q => $q ); + $m->submit; + $m->content_contains( 'base ticket 1', 'base ticket 1 is found' ); + $m->content_contains( 'base ticket 2', 'base ticket 2 is found' ); + $m->content_lacks( 'not found subject', 'not found ticket is not found' ); + } +} + diff --git a/rt/t/web/group_create.t b/rt/t/web/group_create.t new file mode 100644 index 000000000..26da59268 --- /dev/null +++ b/rt/t/web/group_create.t @@ -0,0 +1,75 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 13; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; +my $root = RT::User->new(RT->SystemUser); +ok( $root->Load('root'), 'load root user' ); + +my $group_name = 'test group'; + +my $group_id; +diag "Create a group"; +{ + $m->follow_link( id => 'tools-config-groups-create'); + + # Test group form validation + $m->submit_form( + form_name => 'ModifyGroup', + fields => { + Name => '', + }, + ); + $m->text_contains('Name is required'); + $m->submit_form( + form_name => 'ModifyGroup', + fields => { + Name => '0', + }, + ); + $m->text_contains('Could not create group'); + $m->submit_form( + form_name => 'ModifyGroup', + fields => { + Name => '1', + }, + ); + $m->text_contains('Could not create group'); + $m->submit_form( + form_name => 'ModifyGroup', + fields => { + Name => $group_name, + }, + ); + $m->content_contains('Group created', 'created group sucessfully' ); + + # Test validation on updae + $m->form_name('ModifyGroup'); + $m->set_fields( + Name => '', + ); + $m->click_button(value => 'Save Changes'); + $m->text_contains('Illegal value for Name'); + + $m->form_name('ModifyGroup'); + $m->set_fields( + Name => '0', + ); + $m->click_button(value => 'Save Changes'); + $m->text_contains('Illegal value for Name'); + + $m->form_name('ModifyGroup'); + $m->set_fields( + Name => '1', + ); + $m->click_button(value => 'Save Changes'); + $m->text_contains('Illegal value for Name'); + + $group_id = $m->form_name('ModifyGroup')->value('id'); + ok $group_id, "found id of the group in the form, it's #$group_id"; +} + diff --git a/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/Default b/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/Default new file mode 100644 index 000000000..90278ae49 --- /dev/null +++ b/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/Default @@ -0,0 +1,6 @@ +<%args> +$URL +</%args> +<%init> +$$URL = 'http://bestpractical.com/rt'; +</%init> diff --git a/rt/t/web/html/NoAuth/js/not-by-default.js b/rt/t/web/html/NoAuth/js/not-by-default.js new file mode 100644 index 000000000..568f670ee --- /dev/null +++ b/rt/t/web/html/NoAuth/js/not-by-default.js @@ -0,0 +1,3 @@ +function just_testing() { + alert("hi"); +} diff --git a/rt/t/web/html/delete-article-name-method.html b/rt/t/web/html/delete-article-name-method.html new file mode 100644 index 000000000..27592c1d0 --- /dev/null +++ b/rt/t/web/html/delete-article-name-method.html @@ -0,0 +1,15 @@ +<%INIT> +# This is used by t/web/articles-links.t. Since we fork off a server +# under most test configurations, and we need a way to run code in the +# middle of the test script, we have to play at shenanigans to delete +# RT::Article's Name method. Other options were a Plack::Middleware that +# eval'd code given by paths like /__perl/(.+) (is scary), a signal +# handler (fails under apache), and only running articles-links.t for +# inline tests (fails under apache and standalone) + +delete $RT::Article::{Name}; + +</%INIT> + +<% RT::Article->can('Name') ? '{exists}' : '{deleted}' %> + diff --git a/rt/t/web/html_template.t b/rt/t/web/html_template.t index a2461dc97..662a26bbd 100644 --- a/rt/t/web/html_template.t +++ b/rt/t/web/html_template.t @@ -8,22 +8,17 @@ use Encode; my ( $baseurl, $m ) = RT::Test->started_ok; ok $m->login, 'logged in as root'; -$RT::Test::SKIP_REQUEST_WORK_AROUND = 1; -RT::Test->set_mail_catcher; - use utf8; diag('make Autoreply template a html one and add utf8 chars') if $ENV{TEST_VERBOSE}; { - $m->follow_link_ok( { text => 'Configuration' }, '-> Configuration' ); - $m->follow_link_ok( { text => 'Global' }, '-> Global' ); - $m->follow_link_ok( { text => 'Templates' }, '-> Templates' ); + $m->follow_link_ok( { id => 'tools-config-global-templates' }, '-> Templates' ); $m->follow_link_ok( { text => 'Autoreply' }, '-> Autoreply' ); - $m->form_number(3); $m->submit_form( + form_name => 'ModifyTemplate', fields => { Content => <<'EOF', Subject: AutoReply: {$Ticket->Subject} @@ -46,9 +41,8 @@ diag('create a ticket to see the autoreply mail') if $ENV{TEST_VERBOSE}; { $m->get_ok( $baseurl . '/Ticket/Create.html?Queue=1' ); - $m->form_number(3); $m->submit_form( - form_number => 3, + form_name => 'TicketCreate', fields => { Subject => '标题', Content => '<h1>测试</h1>', ContentType => 'text/html' }, ); diff --git a/rt/t/web/logout.t b/rt/t/web/logout.t new file mode 100644 index 000000000..889e5fc18 --- /dev/null +++ b/rt/t/web/logout.t @@ -0,0 +1,39 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use RT::Test tests => 12; + +my ($baseurl, $agent) = RT::Test->started_ok; + +my $url = $agent->rt_base_url; +diag $url if $ENV{TEST_VERBOSE}; + +# test that logout would actually redirects to the correct URL +{ + ok $agent->login, "logged in"; + $agent->follow_link_ok({ text => 'Logout' }); + like $agent->uri, qr'/Logout\.html$', "right url"; + $agent->content_contains('<meta http-equiv="refresh" content="1;URL=/"', "found the expected meta-refresh"); +} + +# Stop server and set MasonLocalComponentRoot +RT::Test->stop_server; + +RT->Config->Set(MasonLocalComponentRoot => RT::Test::get_abs_relocatable_dir('html')); + +($baseurl, $agent) = RT::Test->started_ok; + +$url = $agent->rt_base_url; +diag $url if $ENV{TEST_VERBOSE}; + +# test that logout would actually redirects to URL from the callback +{ + ok $agent->login, "logged in"; + $agent->follow_link_ok({ text => 'Logout' }); + like $agent->uri, qr'/Logout\.html$', "right url"; + $agent->content_contains('<meta http-equiv="refresh" content="1;URL=http://bestpractical.com/rt"', "found the expected meta-refresh"); +} + + +1; diff --git a/rt/t/web/offline.t b/rt/t/web/offline.t new file mode 100644 index 000000000..b3762fc20 --- /dev/null +++ b/rt/t/web/offline.t @@ -0,0 +1,78 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use RT::Test tests => 20; + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login, 'logged in' ); + +{ + my $template = <<EOF; +===Create-Ticket: ticket1 +Queue: General +Subject: test +Status: new +EOF + my $ticket = create_ticket_offline( $m, $template ); + ok $ticket->id, 'created a ticket with offline tool'; + is $ticket->QueueObj->Name, 'General', 'correct value'; + is $ticket->Subject, 'test', 'correct value'; + is $ticket->Status, 'new', 'correct value'; +} + +{ + my $template = <<'EOF'; +===Create-Ticket: ticket1 +Queue: General +Subject: test +Status: new +Requestor: test@example.com +EOF + my $ticket = create_ticket_offline( $m, $template ); + ok $ticket->id, 'created a ticket with offline tool'; + is $ticket->RequestorAddresses, 'test@example.com', 'correct value'; +} + +{ + my $group = RT::Group->new(RT->SystemUser); + my ($id, $msg) = $group->CreateUserDefinedGroup( Name => 'test' ); + ok $id, "created a user defined group"; + + my $template = <<'EOF'; +===Create-Ticket: ticket1 +Queue: General +Subject: test +Status: new +Requestor: test@example.com +RequestorGroup: test +EOF + my $ticket = create_ticket_offline( $m, $template ); + ok $ticket->id, 'created a ticket with offline tool'; + ok grep( + { $_->MemberId eq $group->id } + @{ $ticket->Requestors->MembersObj->ItemsArrayRef } + ), 'correct value' ; + is $ticket->RequestorAddresses, 'test@example.com', 'correct value'; +} + +sub create_ticket_offline { + my ($m, $template) = @_; + + $m->get_ok( $url . '/Tools/Offline.html' ); + + $m->submit_form( + form_name => 'TicketUpdate', + fields => { string => $template }, + button => 'UpdateTickets', + ); + + my $ticket = RT::Ticket->new( RT->SystemUser ); + $m->content_like( qr/Ticket \d+ created/, 'found ticket created message' ) + or return $ticket; + + $ticket->Load( $m->content =~ /Ticket (\d+) created/ ); + return $ticket; +} + + diff --git a/rt/t/web/offline_messages_utf8.t b/rt/t/web/offline_messages_utf8.t index 9901b4555..bf9cf39fa 100644 --- a/rt/t/web/offline_messages_utf8.t +++ b/rt/t/web/offline_messages_utf8.t @@ -2,8 +2,7 @@ use strict; use warnings; -use RT::Test tests => 6; -use File::Temp qw/tempfile/; +use RT::Test tests => 8; use Encode; use RT::Ticket; diff --git a/rt/t/web/offline_utf8.t b/rt/t/web/offline_utf8.t index 2a3e64d3c..24795c0d3 100644 --- a/rt/t/web/offline_utf8.t +++ b/rt/t/web/offline_utf8.t @@ -2,11 +2,14 @@ use strict; use warnings; -use RT::Test tests => 8; -use File::Temp qw/tempfile/; +use RT::Test tests => 9; +use utf8; + use Encode; + use RT::Ticket; -my ( $fh, $file ) = tempfile; +my $file = File::Spec->catfile( RT::Test->temp_directory, 'template' ); +open my $fh, '>', $file or die $!; my $template = <<EOF; ===Create-Ticket: ticket1 Queue: General @@ -38,17 +41,17 @@ $m->submit_form( button => 'UpdateTickets', # mimic what browsers do: they seems decoded $template - fields => { string => decode( 'utf8', $template ), }, + fields => { string => $template }, ); $m->content_like( qr/Ticket \d+ created/, 'found ticket created message' ); my ( $ticket_id ) = $m->content =~ /Ticket (\d+) created/; -my $ticket = RT::Ticket->new( $RT::SystemUser ); +my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $ticket_id ); is( $ticket->Subject, '标题', 'subject in $ticket is right' ); -$m->get_ok( $url . "/Ticket/Display.html?id=$ticket_id" ); +$m->goto_ticket($ticket_id); $m->content_contains( '这是正文', 'content is right in ticket display page' ); diff --git a/rt/t/web/passthrough-jsmin b/rt/t/web/passthrough-jsmin new file mode 100644 index 000000000..3d9bf0129 --- /dev/null +++ b/rt/t/web/passthrough-jsmin @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "// passthrough-jsmin added this"; + +exec cat; diff --git a/rt/t/web/path-traversal.t b/rt/t/web/path-traversal.t index 8d2f5cc88..5d5c954a1 100644 --- a/rt/t/web/path-traversal.t +++ b/rt/t/web/path-traversal.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test tests => 20; +use RT::Test tests => 22; my ($baseurl, $agent) = RT::Test->started_ok; @@ -19,11 +19,17 @@ $agent->warning_like(qr/Invalid request.*aborting/,); $agent->get("$baseurl/NoAuth/../../../etc/RT_Config.pm"); is($agent->status, 400); -$agent->warning_like(qr/Invalid request.*aborting/,); +SKIP: { + skip "Apache rejects busting up above / for us", 2 if $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/; + $agent->warning_like(qr/Invalid request.*aborting/,); +}; $agent->get("$baseurl/NoAuth/css/web2/images/../../../../../../etc/RT_Config.pm"); is($agent->status, 400); -$agent->warning_like(qr/Invalid request.*aborting/,); +SKIP: { + skip "Apache rejects busting up above / for us", 2 if $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/; + $agent->warning_like(qr/Invalid request.*aborting/,); +}; # do not reject these URLs, even though they contain /. outside the path $agent->get("$baseurl/index.html?ignored=%2F%2E"); diff --git a/rt/t/web/private-components.t b/rt/t/web/private-components.t index 30e145f34..ceb2b34af 100644 --- a/rt/t/web/private-components.t +++ b/rt/t/web/private-components.t @@ -1,6 +1,6 @@ use strict; -use RT::Test tests => 20; +use RT::Test tests => 24; my ($baseurl, $agent) = RT::Test->started_ok; ok $agent->login, 'logged in'; @@ -18,6 +18,10 @@ $agent->get("/Widgets/TitleBox?title=private"); is($agent->status, 403); $agent->content_lacks("private"); +$agent->get("/m/_elements/header?title=private"); +is($agent->status, 403); +$agent->content_lacks("private"); + $agent->get("/autohandler"); is($agent->status, 403); $agent->content_lacks("comp called without component"); diff --git a/rt/t/web/query_builder.t b/rt/t/web/query_builder.t index 25d6ec5a3..0abbfaca8 100644 --- a/rt/t/web/query_builder.t +++ b/rt/t/web/query_builder.t @@ -5,7 +5,7 @@ use HTTP::Request::Common; use HTTP::Cookies; use LWP; use Encode; -use RT::Test tests => 50; +use RT::Test tests => 56; my $cookie_jar = HTTP::Cookies->new; my ($baseurl, $agent) = RT::Test->started_ok; @@ -22,12 +22,12 @@ ok $queue && $queue->id, 'loaded or created queue'; my $url = $agent->rt_base_url; ok $agent->login, "logged in"; -# {{{ Query Builder tests my $response = $agent->get($url."Search/Build.html"); ok $response->is_success, "Fetched ". $url ."Search/Build.html"; sub getQueryFromForm { + my $agent = shift; $agent->form_name('BuildQuery'); # This pulls out the "hidden input" query from the page my $q = $agent->current_form->find_input("Query")->value; @@ -38,117 +38,117 @@ sub getQueryFromForm { } sub selectedClauses { + my $agent = shift; my @clauses = grep { defined } map { $_->value } $agent->current_form->find_input("clauses"); return [ @clauses ]; } -diag "add the first condition" if $ENV{'TEST_VERBOSE'}; +diag "add the first condition"; { ok $agent->form_name('BuildQuery'), "found the form once"; $agent->field("ActorField", "Owner"); $agent->field("ActorOp", "="); $agent->field("ValueOfActor", "Nobody"); $agent->submit; - is getQueryFromForm, "Owner = 'Nobody'", 'correct query'; + is getQueryFromForm($agent), "Owner = 'Nobody'", 'correct query'; } -diag "set the next condition" if $ENV{'TEST_VERBOSE'}; +diag "set the next condition"; { ok($agent->form_name('BuildQuery'), "found the form again"); $agent->field("QueueOp", "!="); $agent->field("ValueOfQueue", "Regression"); $agent->submit; - is getQueryFromForm, "Owner = 'Nobody' AND Queue != 'Regression'", + is getQueryFromForm($agent), "Owner = 'Nobody' AND Queue != 'Regression'", 'correct query'; } -diag "We're going to delete the owner" if $ENV{'TEST_VERBOSE'}; +diag "We're going to delete the owner"; { $agent->select("clauses", ["0"] ); $agent->click("DeleteClause"); ok $agent->form_name('BuildQuery'), "found the form"; - is getQueryFromForm, "Queue != 'Regression'", 'correct query'; + is getQueryFromForm($agent), "Queue != 'Regression'", 'correct query'; } -diag "add a cond with OR and se number by the way" if $ENV{'TEST_VERBOSE'}; +diag "add a cond with OR and se number by the way"; { $agent->field("AndOr", "OR"); $agent->select("idOp", ">"); $agent->field("ValueOfid" => "1234"); $agent->click("AddClause"); ok $agent->form_name('BuildQuery'), "found the form again"; - is getQueryFromForm, "Queue != 'Regression' OR id > 1234", + is getQueryFromForm($agent), "Queue != 'Regression' OR id > 1234", "added something as OR, and number not quoted"; - is_deeply selectedClauses, ["1"], 'the id that we just entered is still selected'; + is_deeply selectedClauses($agent), ["1"], 'the id that we just entered is still selected'; } -diag "Move the second one up a level" if $ENV{'TEST_VERBOSE'}; +diag "Move the second one up a level"; { $agent->click("Up"); ok $agent->form_name('BuildQuery'), "found the form again"; - is getQueryFromForm, "id > 1234 OR Queue != 'Regression'", "moved up one"; - is_deeply selectedClauses, ["0"], 'the one we moved up is selected'; + is getQueryFromForm($agent), "id > 1234 OR Queue != 'Regression'", "moved up one"; + is_deeply selectedClauses($agent), ["0"], 'the one we moved up is selected'; } -diag "Move the second one right" if $ENV{'TEST_VERBOSE'}; +diag "Move the second one right"; { $agent->click("Right"); ok $agent->form_name('BuildQuery'), "found the form again"; - is getQueryFromForm, "Queue != 'Regression' OR ( id > 1234 )", + is getQueryFromForm($agent), "Queue != 'Regression' OR ( id > 1234 )", "moved over to the right (and down)"; - is_deeply selectedClauses, ["2"], 'the one we moved right is selected'; + is_deeply selectedClauses($agent), ["2"], 'the one we moved right is selected'; } -diag "Move the block up" if $ENV{'TEST_VERBOSE'}; +diag "Move the block up"; { $agent->select("clauses", ["1"]); $agent->click("Up"); ok $agent->form_name('BuildQuery'), "found the form again"; - is getQueryFromForm, "( id > 1234 ) OR Queue != 'Regression'", "moved up"; - is_deeply selectedClauses, ["0"], 'the one we moved up is selected'; + is getQueryFromForm($agent), "( id > 1234 ) OR Queue != 'Regression'", "moved up"; + is_deeply selectedClauses($agent), ["0"], 'the one we moved up is selected'; } -diag "Can not move up the top most clause" if $ENV{'TEST_VERBOSE'}; +diag "Can not move up the top most clause"; { $agent->select("clauses", ["0"]); $agent->click("Up"); ok $agent->form_name('BuildQuery'), "found the form again"; - $agent->content_like(qr/error: can\S+t move up/, "i shouldn't have been able to hit up"); - is_deeply selectedClauses, ["0"], 'the one we tried to move is selected'; + $agent->content_contains("error: can't move up", "i shouldn't have been able to hit up"); + is_deeply selectedClauses($agent), ["0"], 'the one we tried to move is selected'; } -diag "Can not move left the left most clause" if $ENV{'TEST_VERBOSE'}; +diag "Can not move left the left most clause"; { $agent->click("Left"); ok($agent->form_name('BuildQuery'), "found the form again"); - $agent->content_like(qr/error: can\S+t move left/, "i shouldn't have been able to hit left"); - is_deeply selectedClauses, ["0"], 'the one we tried to move is selected'; + $agent->content_contains("error: can't move left", "i shouldn't have been able to hit left"); + is_deeply selectedClauses($agent), ["0"], 'the one we tried to move is selected'; } -diag "Add a condition into a nested block" if $ENV{'TEST_VERBOSE'}; +diag "Add a condition into a nested block"; { $agent->select("clauses", ["1"]); $agent->select("ValueOfStatus" => "stalled"); $agent->submit; ok $agent->form_name('BuildQuery'), "found the form again"; - is_deeply selectedClauses, ["2"], 'the one we added is only selected'; - is getQueryFromForm, + is_deeply selectedClauses($agent), ["2"], 'the one we added is only selected'; + is getQueryFromForm($agent), "( id > 1234 AND Status = 'stalled' ) OR Queue != 'Regression'", "added new one"; } -diag "click advanced, enter 'C1 OR ( C2 AND C3 )', apply, aggregators should stay the same." - if $ENV{'TEST_VERBOSE'}; +diag "click advanced, enter 'C1 OR ( C2 AND C3 )', apply, aggregators should stay the same."; { my $response = $agent->get($url."Search/Edit.html"); ok( $response->is_success, "Fetched /Search/Edit.html" ); - ok($agent->form_number(3), "found the form"); + ok($agent->form_name('BuildQueryAdvanced'), "found the form"); $agent->field("Query", "Status = 'new' OR ( Status = 'open' AND Subject LIKE 'office' )"); $agent->submit; - is( getQueryFromForm, + is( getQueryFromForm($agent), "Status = 'new' OR ( Status = 'open' AND Subject LIKE 'office' )", "no aggregators change" ); @@ -194,11 +194,10 @@ diag "click advanced, enter 'C1 OR ( C2 AND C3 )', apply, aggregators should sta # - clears entire query # - clears it from the session, too -# }}} # create a custom field with nonascii name and try to add a condition { - my $cf = RT::CustomField->new( $RT::SystemUser ); + my $cf = RT::CustomField->new( RT->SystemUser ); $cf->LoadByName( Name => "\x{442}", Queue => 0 ); if ( $cf->id ) { is($cf->Type, 'Freeform', 'loaded and type is correct'); @@ -217,22 +216,21 @@ diag "click advanced, enter 'C1 OR ( C2 AND C3 )', apply, aggregators should sta ok($agent->form_name('BuildQuery'), "found the form once"); $agent->field("ValueOf'CF.{\x{442}}'", "\x{441}"); $agent->submit(); - is( getQueryFromForm, + is( getQueryFromForm($agent), "'CF.{\x{442}}' LIKE '\x{441}'", "no changes, no duplicate condition with badly encoded text" ); } -diag "input a condition, select (several conditions), click delete" - if $ENV{'TEST_VERBOSE'}; +diag "input a condition, select (several conditions), click delete"; { my $response = $agent->get( $url."Search/Edit.html" ); ok $response->is_success, "Fetched /Search/Edit.html"; - ok $agent->form_number(3), "found the form"; + ok $agent->form_name('BuildQueryAdvanced'), "found the form"; $agent->field("Query", "( Status = 'new' OR Status = 'open' )"); $agent->submit; - is( getQueryFromForm, + is( getQueryFromForm($agent), "( Status = 'new' OR Status = 'open' )", "query is the same" ); @@ -240,7 +238,7 @@ diag "input a condition, select (several conditions), click delete" $agent->field( ValueOfid => 10 ); $agent->click("DeleteClause"); - is( getQueryFromForm, + is( getQueryFromForm($agent), "id < 10", "replaced query successfuly" ); @@ -251,7 +249,7 @@ diag "send query with not quoted negative number"; my $response = $agent->get($url."Search/Build.html?Query=Priority%20>%20-2"); ok( $response->is_success, "Fetched " . $url."Search/Build.html" ); - is( getQueryFromForm, + is( getQueryFromForm($agent), "Priority > -2", "query is the same" ); @@ -261,7 +259,7 @@ diag "click advanced, enter an invalid SQL IS restriction, apply and check that { my $response = $agent->get($url."Search/Edit.html"); ok( $response->is_success, "Fetched /Search/Edit.html" ); - ok($agent->form_number(3), "found the form"); + ok($agent->form_name('BuildQueryAdvanced'), "found the form"); $agent->field("Query", "Requestor.EmailAddress IS 'FOOBAR'"); $agent->submit; is( getQueryFromForm($agent), @@ -274,7 +272,7 @@ diag "click advanced, enter an invalid SQL IS NOT restriction, apply and check t { my $response = $agent->get($url."Search/Edit.html"); ok( $response->is_success, "Fetched /Search/Edit.html" ); - ok($agent->form_number(3), "found the form"); + ok($agent->form_name('BuildQueryAdvanced'), "found the form"); $agent->field("Query", "Requestor.EmailAddress IS NOT 'FOOBAR'"); $agent->submit; is( getQueryFromForm($agent), @@ -283,3 +281,17 @@ diag "click advanced, enter an invalid SQL IS NOT restriction, apply and check t ); } +diag "click advanced, enter a valid SQL, but the field is lower cased"; +{ + my $response = $agent->get($url."Search/Edit.html"); + ok( $response->is_success, "Fetched /Search/Edit.html" ); + ok($agent->form_name('BuildQueryAdvanced'), "found the form"); + $agent->field("Query", "status = 'new'"); + $agent->submit; + $agent->content_lacks( 'Unknown field:', 'no "unknown field" warning' ); + is( getQueryFromForm($agent), + "Status = 'new'", + "field's case is corrected" + ); +} + diff --git a/rt/t/web/query_log.t b/rt/t/web/query_log.t new file mode 100644 index 000000000..e19f44dc6 --- /dev/null +++ b/rt/t/web/query_log.t @@ -0,0 +1,20 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use RT::Test tests => 9; + +RT->Config->Set(StatementLog => 1); + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +my $root = RT::User->new($RT::SystemUser); +$root->LoadByEmail('root@localhost'); + +$m->get_ok("/Admin/Tools/Queries.html"); +$m->text_contains("/index.html", "we include info about a page we hit while logging in"); +$m->text_contains("Stack:", "stack traces"); +$m->text_like(qr{share/html/autohandler:\d+}, "stack trace includes mason components"); +$m->text_contains("SELECT * FROM Principals WHERE id = '".$root->id."'", "we interpolate bind params"); + diff --git a/rt/t/web/queue_caching.t b/rt/t/web/queue_caching.t new file mode 100644 index 000000000..d90aacbdf --- /dev/null +++ b/rt/t/web/queue_caching.t @@ -0,0 +1,90 @@ +use strict; +use warnings; +use RT::Test tests => 36; + +# make an initial queue, so we have more than 1 +my $original_test_queue = new_queue("Test$$"); + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +diag("Check for 2 existing queues being visible"); +{ + check_queues($m); +} + +diag("Add a new queue, which won't show up until we fix the cache"); +{ + new_queue("New Test $$"); + check_queues($m); +} + +diag("Disable an existing queue, it should stop appearing in the list"); +{ + ok($original_test_queue->SetDisabled(1)); + check_queues($m); +} + +diag("Bring back a disabled queue"); +{ + ok($original_test_queue->SetDisabled(0)); + check_queues($m); +} + +diag("Test a user who has more limited rights Queues"); +{ + +my $user_a = RT::Test->load_or_create_user( + Name => 'user_a', Password => 'password', +); +ok $user_a && $user_a->id, 'loaded or created user'; + +ok( RT::Test->set_rights( + { Principal => $user_a, Right => [qw(SeeQueue CreateTicket)], Object => $original_test_queue }, +), 'Allow user a to see the testing queue'); + +my $a_m = RT::Test::Web->new; +ok $a_m->login('user_a', 'password'), 'logged in as user A'; + +# check that they see a single queue +check_queues($a_m,[$original_test_queue->Id]); + +ok( RT::Test->add_rights( + { Principal => $user_a, Right => [qw(SeeQueue CreateTicket)] }, +), 'add global queue viewing rights'); + +check_queues($a_m); + +} + +sub new_queue { + my $name = shift; + my $new_queue = RT::Queue->new(RT->SystemUser); + ok($new_queue->Create( Name => $name, Description => "Testing for $name queue" ), "Created queue ".$new_queue->Name); + return $new_queue; +} + +sub internal_queues { + my $internal_queues = RT::Queues->new(RT->SystemUser); + $internal_queues->Limit(FIELD => 'Disabled', VALUE => 0); + my $queuelist; + while ( my $q = $internal_queues->Next ) { + $queuelist->{$q->Id} = $q->Name; + } + return $queuelist; +} + + +# takes a WWW::Mech object and an optional arrayref of queue ids +# compares the list of ids to the dropdown of Queues for the New Ticket In form +sub check_queues { + my $browser = shift; + my $queue_list = shift; + $browser->get_ok($baseurl,"Navigated to homepage"); + ok(my $form = $browser->form_name('CreateTicketInQueue'), "Found New Ticket In form"); + ok(my $queuelist = $form->find_input('Queue','option'), "Found queue select"); + my @queues = $queuelist->possible_values; + + $queue_list = [keys %{internal_queues()}] unless $queue_list; + is_deeply([sort @queues],[sort @$queue_list], "Queue list contains the expected queues"); +} diff --git a/rt/t/web/queue_create.t b/rt/t/web/queue_create.t new file mode 100644 index 000000000..3d2cd04d0 --- /dev/null +++ b/rt/t/web/queue_create.t @@ -0,0 +1,75 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 13; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; +my $root = RT::User->new(RT->SystemUser); +ok( $root->Load('root'), 'load root user' ); + +my $queue_name = 'test queue'; + +my $queue_id; +diag "Create a queue"; +{ + $m->follow_link( id => 'tools-config-queues-create'); + + # Test queue form validation + $m->submit_form( + form_name => 'ModifyQueue', + fields => { + Name => '', + }, + ); + $m->text_contains('Queue name is required'); + $m->submit_form( + form_name => 'ModifyQueue', + fields => { + Name => '0', + }, + ); + $m->text_contains("'0' is not a valid name"); + $m->submit_form( + form_name => 'ModifyQueue', + fields => { + Name => '1', + }, + ); + $m->text_contains("'1' is not a valid name"); + $m->submit_form( + form_name => 'ModifyQueue', + fields => { + Name => $queue_name, + }, + ); + $m->content_contains('Queue created', 'created queue sucessfully' ); + + # Test validation on update + $m->form_name('ModifyQueue'); + $m->set_fields( + Name => '', + ); + $m->click_button(value => 'Save Changes'); + $m->content_contains('Illegal value for Name'); + + $m->form_name('ModifyQueue'); + $m->set_fields( + Name => '0', + ); + $m->click_button(value => 'Save Changes'); + $m->content_contains('Illegal value for Name'); + + $m->form_name('ModifyQueue'); + $m->set_fields( + Name => '1', + ); + $m->click_button(value => 'Save Changes'); + $m->content_contains('Illegal value for Name'); + + $queue_id = $m->form_name('ModifyQueue')->value('id'); + ok $queue_id, "found id of the queue in the form, it's #$queue_id"; +} + diff --git a/rt/t/web/quickcreate.t b/rt/t/web/quickcreate.t new file mode 100644 index 000000000..b257f9ddd --- /dev/null +++ b/rt/t/web/quickcreate.t @@ -0,0 +1,37 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use RT::Test tests => 11; + +RT->Config->Set('DisplayTicketAfterQuickCreate' => 0); + +my ($baseurl, $m) = RT::Test->started_ok; + +ok($m->login, 'logged in'); + +$m->form_with_fields('Subject', 'Content'); +$m->field(Subject => 'from quick create'); +$m->submit; + +$m->content_like(qr/Ticket \d+ created in queue/, 'created ticket'); +like( $m->uri, qr{^\Q$baseurl\E/(?:index\.html)?\?results=}, 'still in homepage' ); +unlike( $m->uri, qr{Ticket/Display.html}, 'not on ticket display page' ); + +$m->get_ok($baseurl . '/Prefs/Other.html'); +$m->submit_form( + form_name => 'ModifyPreferences', + fields => { 'DisplayTicketAfterQuickCreate' => 1, }, + button => 'Update', +); + +$m->content_contains( 'Preferences saved', + 'enabled DisplayTicketAfterQuickCreate' ); +$m->get($baseurl); + +$m->form_with_fields('Subject', 'Content'); +$m->field(Subject => 'from quick create'); +$m->submit; + +$m->content_like(qr/Ticket \d+ created in queue/, 'created ticket'); +like( $m->uri, qr!/Ticket/Display.html!, 'still in homepage' ); diff --git a/rt/t/web/quicksearch.t b/rt/t/web/quicksearch.t index cd9a8e76c..8cc084ce5 100644 --- a/rt/t/web/quicksearch.t +++ b/rt/t/web/quicksearch.t @@ -2,12 +2,12 @@ use strict; use warnings; -use RT::Test tests => 7; +use RT::Test tests => 9; my ($baseurl, $m) = RT::Test->started_ok; my $url = $m->rt_base_url; # merged tickets still show up in search -my $t1 = RT::Ticket->new($RT::SystemUser); +my $t1 = RT::Ticket->new(RT->SystemUser); $t1->Create( Subject => 'base ticket'.$$, Queue => 'general', @@ -22,7 +22,7 @@ $t1->Create( ); ok(my $id1 = $t1->id, 'created ticket for custom search'); -my $t2 = RT::Ticket->new($RT::SystemUser); +my $t2 = RT::Ticket->new(RT->SystemUser); $t2->Create( Subject => 'merged away'.$$, Queue => 'general', diff --git a/rt/t/web/redirect-after-login.t b/rt/t/web/redirect-after-login.t index d39bb58c8..d429d30d1 100644 --- a/rt/t/web/redirect-after-login.t +++ b/rt/t/web/redirect-after-login.t @@ -3,7 +3,9 @@ use strict; use warnings; -use RT::Test tests => 120; +use RT::Test tests => 122; + +RT->Config->Set( GnuPG => Enable => 0 ); my ($baseurl, $agent) = RT::Test->started_ok; @@ -51,11 +53,9 @@ diag $url if $ENV{TEST_VERBOSE}; ok( $agent->content =~ /Your username or password is incorrect/i, "Found the error message"); like( $agent->uri, qr{/NoAuth/Login\.html$}, "now on /NoAuth/Login.html" ); - $agent->logout(); - - # Handle the warning after we're done with the page, since this leaves us - # with a completely different $mech $agent->warning_like(qr/FAILED LOGIN for root/, "got failed login warning"); + + $agent->logout(); } # test a login from a non-front page, both with a double leading slash and without @@ -116,6 +116,7 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) { ok( $agent->content =~ /Your username or password is incorrect/i, "Found the error message"); like( $agent->uri, qr{/NoAuth/Login\.html$}, "still on /NoAuth/Login.html" ); + $agent->warning_like(qr/FAILED LOGIN for root/, "got failed login warning"); # try to login again ok($agent->current_form->find_input('user')); @@ -136,10 +137,6 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) { is( $agent->uri, $requested, "right URL" ); like( $agent->{redirected_uri}, qr{/NoAuth/Login\.html}, "We redirected from login"); $agent->logout(); - - # Handle the warning after we're done with the page, since this leaves us - # with a completely different $mech - $agent->warning_like(qr/FAILED LOGIN for root/, "got failed login warning"); } # test a login from the main page with query params @@ -163,9 +160,6 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) { ok($agent->current_form->find_input('user')); ok($agent->current_form->find_input('pass')); like($agent->current_form->action, qr{/NoAuth/Login\.html$}, "login form action is correct"); - - # Handle the warning after we're done with the page, since this leaves us - # with a completely different $mech $agent->warning_like(qr/FAILED LOGIN for root/, "got failed login warning"); } @@ -183,6 +177,7 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) { ok($agent->current_form->find_input('next')); like($agent->value('next'), qr/^[a-z0-9]{32}$/i, "next page argument is a hash"); like($agent->current_form->action, qr{/NoAuth/Login\.html$}, "login form action is correct"); + $agent->warning_like(qr/FAILED LOGIN for root/, "got failed login warning"); # Try to login again ok($agent->content =~ /username:/i); @@ -197,10 +192,6 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) { is( $agent->uri, $requested, "right URL" ); like( $agent->{redirected_uri}, qr{/NoAuth/Login\.html}, "We redirected from login"); $agent->logout(); - - # Handle the warning after we're done with the page, since this leaves us - # with a completely different $mech - $agent->warning_like(qr/FAILED LOGIN for root/, "got failed login warning"); } # test REST login response @@ -221,9 +212,6 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) { is($agent->uri, $requested, "didn't redirect to /NoAuth/Login.html for REST"); like($agent->content, qr/401 Credentials required/i, "got error status"); like($agent->content, qr/Your username or password is incorrect/, "got error message"); - - # Handle the warning after we're done with the page, since this leaves us - # with a completely different $mech $agent->warning_like(qr/FAILED LOGIN for root/, "got failed login warning"); } diff --git a/rt/t/web/redirect.t b/rt/t/web/redirect.t new file mode 100644 index 000000000..d92386bb8 --- /dev/null +++ b/rt/t/web/redirect.t @@ -0,0 +1,106 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 13; + +my $r = $HTML::Mason::Commands::r = bless {}, 'R'; +my $m = $HTML::Mason::Commands::m = bless {}, 'M'; + +set_config( + CanonicalizeRedirectURLs => 0, + WebDomain => 'localhost', + WebPort => 80, + WebPath => '', +); +is( RT->Config->Get('WebBaseURL'), 'http://localhost' ); +is( RT->Config->Get('WebURL'), 'http://localhost/' ); + +redirect_ok( + 'http://localhost/Ticket/', 'http://localhost/Ticket/', + { SERVER_NAME => 'localhost', SERVER_PORT => 80 }, +); +redirect_ok( + '/Ticket/', 'http://localhost/Ticket/', + { SERVER_NAME => 'localhost', SERVER_PORT => 80 }, +); +redirect_ok( + 'http://localhost/Ticket/', 'http://example.com/Ticket/', + { SERVER_NAME => 'example.com', SERVER_PORT => 80 }, +); + +set_config( + CanonicalizeRedirectURLs => 0, + WebDomain => 'localhost', + WebPort => 443, + WebPath => '', +); +is( RT->Config->Get('WebBaseURL'), 'https://localhost' ); +is( RT->Config->Get('WebURL'), 'https://localhost/' ); + +redirect_ok( + 'https://localhost/Ticket/', 'https://localhost/Ticket/', + { SERVER_NAME => 'localhost', SERVER_PORT => 443, HTTPS => 'on' }, +); +redirect_ok( + '/Ticket/', 'https://localhost/Ticket/', + { SERVER_NAME => 'localhost', SERVER_PORT => 443, HTTPS => 'on' }, +); +redirect_ok( + 'https://localhost/Ticket/', 'http://localhost/Ticket/', + { SERVER_NAME => 'localhost', SERVER_PORT => 80 }, +); +redirect_ok( + '/Ticket/', 'http://localhost/Ticket/', + { SERVER_NAME => 'localhost', SERVER_PORT => 80 }, +); +redirect_ok( + 'https://localhost/Ticket/', 'http://example.com/Ticket/', + { SERVER_NAME => 'example.com', SERVER_PORT => 80 }, +); +redirect_ok( + 'https://localhost/Ticket/', 'https://example.com/Ticket/', + { SERVER_NAME => 'example.com', SERVER_PORT => 443, HTTPS => 'on' }, +); + +sub set_config { + my %values = @_; + while ( my ($k, $v) = each %values ) { + RT->Config->Set( $k => $v ); + } + + unless ( $values{'WebBaseURL'} ) { + my $port = RT->Config->Get('WebPort'); + RT->Config->Set( + WebBaseURL => + ($port == 443? 'https': 'http') .'://' + . RT->Config->Get('WebDomain') + . ($port != 80 && $port != 443? ":$port" : '') + ); + } + unless ( $values{'WebURL'} ) { + RT->Config->Set( + WebURL => RT->Config->Get('WebBaseURL') . RT->Config->Get('WebPath') . "/" + ); + } +} + +sub redirect_ok { + my ($to, $expected, $env, $details) = @_; + + local %ENV = %ENV; + while ( my ($k, $v) = each %{ $env || {} } ) { + $ENV{ $k } = $v; + } + RT::Interface::Web::Redirect( $to ); + is($m->redirect, $expected, $details || "correct for '$to'"); +} + +package R; +sub status {}; + +package M; +sub redirect { $_[0]{'last'} = $_[1] if @_ > 1; return $_[0]{'last'} } +sub abort {} + diff --git a/rt/t/web/reminders.t b/rt/t/web/reminders.t new file mode 100644 index 000000000..32e130c47 --- /dev/null +++ b/rt/t/web/reminders.t @@ -0,0 +1,88 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use RT::Test tests => 35; + +my ($baseurl, $m) = RT::Test->started_ok; + +ok($m->login, 'logged in'); +my $user = RT::CurrentUser->new('root'); + +my $ticket = RT::Ticket->new($user); +$ticket->Create(Subject => 'testing reminders!', Queue => 'General'); +ok($ticket->id, 'created a ticket'); + +$m->goto_ticket($ticket->id); +$m->text_contains('New reminder:', 'can create a new reminder'); +$m->content_unlike(qr{Check box to complete}, "we don't display this text when there are no reminders"); +$m->content_unlike(qr{<th[^>]*>Reminders?</th>}, "no reminder titlebar"); + +$m->follow_link_ok({id => 'page-reminders'}); +$m->title_is("Reminders for ticket #" . $ticket->id); +$m->text_contains('New reminder:', 'can create a new reminder'); +$m->content_unlike(qr{Check box to complete}, "we don't display this text when there are no reminders"); +$m->content_unlike(qr{<th[^>]*>Reminders?</th>}, "no reminder titlebar"); + +$m->goto_ticket($ticket->id); +$m->form_name('UpdateReminders'); +$m->field( 'NewReminder-Subject' => "baby's first reminder" ); +$m->submit; + +my $reminders = RT::Reminders->new($user); +$reminders->Ticket($ticket->id); +my $col = $reminders->Collection; +is($col->Count, 1, 'got a reminder'); +my $reminder = $col->First; +is($reminder->Subject, "baby's first reminder"); +my $reminder_id = $reminder->id; +is($reminder->Status, 'new'); + +$m->text_contains('New reminder:', 'can create a new reminder'); +$m->text_contains('Check box to complete', "we DO display this text when there are reminders"); +$m->content_like(qr{<th[^>]*>Reminders?</th>}, "now we have a reminder titlebar"); +$m->text_contains("baby's first reminder", "display the reminder's subject"); + +$m->follow_link_ok({id => 'page-reminders'}); +$m->title_is("Reminders for ticket #" . $ticket->id); +$m->form_name('UpdateReminders'); +$m->field("Reminder-Subject-$reminder_id" => "changed the subject"); +$m->submit; + +DBIx::SearchBuilder::Record::Cachable->FlushCache; +$reminder = RT::Ticket->new($user); +$reminder->Load($reminder_id); +is($reminder->Subject, 'changed the subject'); +is($reminder->Status, 'new'); + +$m->goto_ticket($ticket->id); +$m->form_name('UpdateReminders'); +$m->tick("Complete-Reminder-$reminder_id" => 1); +$m->submit; + +DBIx::SearchBuilder::Record::Cachable->FlushCache; +$reminder = RT::Ticket->new($user); +$reminder->Load($reminder_id); +is($reminder->Status, 'resolved'); + +$m->text_contains('New reminder:', 'can create a new reminder'); +$m->content_unlike(qr{Check box to complete}, "we don't display this text when there are open reminders"); +$m->content_unlike(qr{<th[^>]*>Reminders?</th>}, "no reminder titlebar"); +$m->content_unlike(qr{baby's first reminder}, "we don't display resolved reminders"); + +$m->follow_link_ok({id => 'page-reminders'}); +$m->title_is("Reminders for ticket #" . $ticket->id); +$m->text_contains('New reminder:', 'can create a new reminder'); +$m->text_contains('Check box to complete', "we DO display this text when there are reminders"); +$m->content_contains("changed the subject", "display the resolved reminder's subject"); + +# make sure that when we submit the form, it doesn't accidentally reopen +# resolved reminders +$m->goto_ticket($ticket->id); +$m->form_name('UpdateReminders'); +$m->submit; + +DBIx::SearchBuilder::Record::Cachable->FlushCache; +$reminder = RT::Ticket->new($user); +$reminder->Load($reminder_id); +is($reminder->Status, 'resolved'); + diff --git a/rt/t/web/remote_user.t b/rt/t/web/remote_user.t new file mode 100644 index 000000000..edad6ef95 --- /dev/null +++ b/rt/t/web/remote_user.t @@ -0,0 +1,36 @@ +use strict; +use warnings; +use RT; +use RT::Test tests => 9; +use MIME::Base64 qw//; + +RT->Config->Set( DevelMode => 0 ); +RT->Config->Set( WebExternalAuth => 1 ); + +sub auth { + return Authorization => "Basic " . + MIME::Base64::encode( join(":", @_) ); +} + +my ( $url, $m ) = RT::Test->started_ok( basic_auth => 1 ); +$m->get($url); +is($m->status, 401, "Initial request with no creds gets 401"); + +$m->get($url, auth( root => "wrong" )); +is($m->status, 401, "Request with wrong creds gets 401"); + +$m->get($url, auth( root => "password" )); +is($m->status, 200, "Request with right creds gets 200"); + +$m->content_like( + qr{<span class="current-user">\Qroot\E</span>}i, + "Has user on the page" +); +$m->content_unlike(qr/Logout/i, "Has no logout button, no WebFallbackToInternalAuth"); + +$m->get($url); +is($m->status, 401, "Subsequent requests without credentials aren't still logged in"); + + +# Put the credentials back for the warnings check at the end +$m->default_header( auth( root => "password" )); diff --git a/rt/t/web/requestor_groups_edit_link.t b/rt/t/web/requestor_groups_edit_link.t new file mode 100644 index 000000000..9d1fefd5c --- /dev/null +++ b/rt/t/web/requestor_groups_edit_link.t @@ -0,0 +1,56 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 11; + +RT->Config->Set( ShowMoreAboutPrivilegedUsers => 1 ); + +my ( $url, $m ) = RT::Test->started_ok; +my $user_a = RT::Test->load_or_create_user( + Name => 'user_a', + Password => 'password', +); +ok( $user_a, 'created user user_a' ); +ok( + RT::Test->set_rights( + { + Principal => $user_a, + Right => [ qw/SeeQueue ShowTicket CreateTicket/ ] + }, + ), + 'set rights for user_a' +); + +my $ticket = RT::Ticket->new(RT->SystemUser); +my ($id) = $ticket->Create( + Subject => 'groups limit', + Queue => 'General', + Requestor => $user_a->id, +); +ok( $id, 'created ticket' ); + + +ok( $m->login( user_a => 'password' ), 'logged in as user_a' ); + +$m->goto_ticket($id); + +ok( + !$m->find_link( text => 'Edit' ), 'no Edit link without AdminUsers permission' +); + +ok( + RT::Test->add_rights( + { + Principal => $user_a, + Right => [ qw/AdminUsers ShowConfigTab/ ] + }, + ), + 'add AdminUsers and ShowConfigTab rights for user_a' +); + +$m->goto_ticket($id); +$m->follow_link_ok( { text => 'Edit' }, 'follow the Edit link' ); +is( $m->uri, $url . "/Admin/Users/Memberships.html?id=" . $user_a->id, 'url is right' ); + diff --git a/rt/t/web/requestor_groups_limit.t b/rt/t/web/requestor_groups_limit.t new file mode 100644 index 000000000..7987a136c --- /dev/null +++ b/rt/t/web/requestor_groups_limit.t @@ -0,0 +1,36 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 11; + +diag "set groups limit to 1"; +RT->Config->Set( ShowMoreAboutPrivilegedUsers => 1 ); +RT->Config->Set( MoreAboutRequestorGroupsLimit => 1 ); + +my $ticket = RT::Ticket->new(RT->SystemUser); +my ($id) = $ticket->Create( + Subject => 'groups limit', + Queue => 'General', + Requestor => 'root@localhost', +); +ok( $id, 'created ticket' ); + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login(), 'logged in as root' ); +$m->goto_ticket($id); +$m->content_like( qr/Everyone|Privileged/, 'got one group' ); +$m->content_unlike( qr/Everyone.*?Privileged/, 'not 2 groups' ); + +RT::Test->stop_server; + +diag "set groups limit to 2"; + +RT->Config->Set( MoreAboutRequestorGroupsLimit => 2 ); +( $url, $m ) = RT::Test->started_ok; +ok( $m->login(), 'logged in as root' ); +$m->goto_ticket($id); +$m->content_contains( 'Everyone', 'got the first group' ); +$m->content_contains( 'Privileged', 'got the second group' ); + diff --git a/rt/t/web/rest-non-ascii-subject.t b/rt/t/web/rest-non-ascii-subject.t index 371b2ffc0..d7a89af5e 100644 --- a/rt/t/web/rest-non-ascii-subject.t +++ b/rt/t/web/rest-non-ascii-subject.t @@ -2,9 +2,7 @@ # Test ticket creation with REST using non ascii subject use strict; use warnings; -use RT::Test tests => 7; - -local $RT::Test::SKIP_REQUEST_WORK_AROUND = 1; +use RT::Test tests => 9; use Encode; # \x{XX} where XX is less than 255 is not treated as unicode code point @@ -42,7 +40,7 @@ $m->post("$baseurl/REST/1.0/ticket/new", [ my ($id) = $m->content =~ /Ticket (\d+) created/; ok($id, "got ticket #$id"); -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); $ticket->Load($id); is($ticket->Id, $id, "loaded the REST-created ticket"); is($ticket->Subject, $subject, "ticket subject successfully set"); diff --git a/rt/t/web/rest-sort.t b/rt/t/web/rest-sort.t new file mode 100644 index 000000000..6b4d35169 --- /dev/null +++ b/rt/t/web/rest-sort.t @@ -0,0 +1,46 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use RT::Test tests => 25; + +my ($baseurl, $m) = RT::Test->started_ok; + +RT::Test->create_tickets( + { }, + { Subject => 'uno' }, + { Subject => 'dos' }, + { Subject => 'tres' }, +); + +ok($m->login, 'logged in'); + +sorted_tickets_ok('Subject', ['2: dos', '3: tres', '1: uno']); +sorted_tickets_ok('+Subject', ['2: dos', '3: tres', '1: uno']); +sorted_tickets_ok('-Subject', ['1: uno', '3: tres', '2: dos']); + +sorted_tickets_ok('id', ['1: uno', '2: dos', '3: tres']); +sorted_tickets_ok('+id', ['1: uno', '2: dos', '3: tres']); +sorted_tickets_ok('-id', ['3: tres', '2: dos', '1: uno']); + +undef $m; + +sub sorted_tickets_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $order = shift; + my $expected = shift; + + my $query = 'id > 0'; + + my $uri = URI->new("$baseurl/REST/1.0/search/ticket"); + $uri->query_form( + query => $query, + orderby => $order, + ); + $m->get_ok($uri); + + my @lines = split /\n/, $m->content; + shift @lines; # header + shift @lines; # empty line + + is_deeply(\@lines, $expected, "sorted results by '$order'"); +} diff --git a/rt/t/web/rest.t b/rt/t/web/rest.t index b3a7c558b..5e7194c95 100644 --- a/rt/t/web/rest.t +++ b/rt/t/web/rest.t @@ -1,7 +1,7 @@ #!/usr/bin/env perl use strict; use warnings; -use RT::Test tests => 16; +use RT::Test tests => 18; my ($baseurl, $m) = RT::Test->started_ok; @@ -46,7 +46,7 @@ $m->post("$baseurl/REST/1.0/ticket/edit", [ my ($id) = $m->content =~ /Ticket (\d+) created/; ok($id, "got ticket #$id"); -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); $ticket->Load($id); is($ticket->Id, $id, "loaded the REST-created ticket"); is($ticket->Subject, "REST interface", "subject successfully set"); diff --git a/rt/t/web/richtext-autohandler.t b/rt/t/web/richtext-autohandler.t index 56617b2fb..734426fc3 100644 --- a/rt/t/web/richtext-autohandler.t +++ b/rt/t/web/richtext-autohandler.t @@ -1,13 +1,13 @@ use strict; -use RT::Test tests => 7; +use RT::Test tests => 9; my ($baseurl, $agent) = RT::Test->started_ok; -$agent->get("$baseurl/NoAuth/RichText/FCKeditor/license.txt"); +$agent->get("$baseurl/NoAuth/RichText/ckeditor/config.js"); is($agent->status, 403); -$agent->content_lacks("It is not the purpose of this section to induce"); +$agent->content_lacks("config.disableNativeSpellChecker"); -$agent->get_ok("/NoAuth/RichText/license.txt"); -$agent->content_contains("It is not the purpose of this section to induce"); +$agent->get_ok("/NoAuth/RichText/config.js"); +$agent->content_contains("config.disableNativeSpellChecker"); $agent->warning_like(qr/Invalid request directly to the rich text editor/,); diff --git a/rt/t/web/rights.t b/rt/t/web/rights.t index b47ba99af..9a5f358e2 100644 --- a/rt/t/web/rights.t +++ b/rt/t/web/rights.t @@ -7,34 +7,32 @@ use RT::Test tests => 14; my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, "logged in"; -$m->follow_link_ok({ text => 'Configuration' }); -$m->follow_link_ok({ text => 'Global' }); -$m->follow_link_ok({ text => 'Group Rights' }); +$m->follow_link_ok({ id => 'tools-config-global-group-rights'}); sub get_rights { my $agent = shift; my $principal_id = shift; my $object = shift; - $agent->form_number(3); - my @inputs = $agent->current_form->find_input("RevokeRight-$principal_id-$object"); - my @rights = sort grep $_, map $_->possible_values, grep $_, @inputs; + $agent->form_name('ModifyGroupRights'); + my @inputs = $agent->current_form->find_input("SetRights-$principal_id-$object"); + my @rights = sort grep $_, map $_->possible_values, grep $_ && $_->value, @inputs; return @rights; }; -diag "load Everyone group" if $ENV{'TEST_VERBOSE'}; +diag "load Everyone group"; my ($everyone, $everyone_gid); { - $everyone = RT::Group->new( $RT::SystemUser ); + $everyone = RT::Group->new( RT->SystemUser ); $everyone->LoadSystemInternalGroup('Everyone'); ok($everyone_gid = $everyone->id, "loaded 'everyone' group"); } -diag "revoke all global rights from Everyone group" if $ENV{'TEST_VERBOSE'}; +diag "revoke all global rights from Everyone group"; my @has = get_rights( $m, $everyone_gid, 'RT::System-1' ); if ( @has ) { - $m->form_number(3); - $m->tick("RevokeRight-$everyone_gid-RT::System-1", $_) foreach @has; + $m->form_name('ModifyGroupRights'); + $m->untick("SetRights-$everyone_gid-RT::System-1", $_) foreach @has; $m->submit; is_deeply([get_rights( $m, $everyone_gid, 'RT::System-1' )], [], 'deleted all rights' ); @@ -42,10 +40,10 @@ if ( @has ) { ok(1, 'the group has no global rights'); } -diag "grant SuperUser right to everyone" if $ENV{'TEST_VERBOSE'}; +diag "grant SuperUser right to everyone"; { - $m->form_number(3); - $m->select("GrantRight-$everyone_gid-RT::System-1", ['SuperUser']); + $m->form_name('ModifyGroupRights'); + $m->tick("SetRights-$everyone_gid-RT::System-1", 'SuperUser'); $m->submit; $m->content_contains('Right Granted', 'got message'); @@ -54,10 +52,10 @@ diag "grant SuperUser right to everyone" if $ENV{'TEST_VERBOSE'}; is_deeply( [get_rights( $m, $everyone_gid, 'RT::System-1' )], ['SuperUser'], 'granted SuperUser right' ); } -diag "revoke the right" if $ENV{'TEST_VERBOSE'}; +diag "revoke the right"; { - $m->form_number(3); - $m->tick("RevokeRight-$everyone_gid-RT::System-1", 'SuperUser'); + $m->form_name('ModifyGroupRights'); + $m->untick("SetRights-$everyone_gid-RT::System-1", 'SuperUser'); $m->submit; $m->content_contains('Right revoked', 'got message'); @@ -67,10 +65,10 @@ diag "revoke the right" if $ENV{'TEST_VERBOSE'}; } -diag "return rights the group had in the beginning" if $ENV{'TEST_VERBOSE'}; +diag "return rights the group had in the beginning"; if ( @has ) { - $m->form_number(3); - $m->select("GrantRight-$everyone_gid-RT::System-1", \@has); + $m->form_name('ModifyGroupRights'); + $m->tick("SetRights-$everyone_gid-RT::System-1", $_) for @has; $m->submit; $m->content_contains('Right Granted', 'got message'); diff --git a/rt/t/web/rights1.t b/rt/t/web/rights1.t index c8892f2fe..24e5c3448 100644 --- a/rt/t/web/rights1.t +++ b/rt/t/web/rights1.t @@ -2,11 +2,11 @@ use strict; use HTTP::Cookies; -use RT::Test tests => 29; +use RT::Test nodata => 1, tests => 31; my ($baseurl, $agent) = RT::Test->started_ok; # Create a user with basically no rights, to start. -my $user_obj = RT::User->new($RT::SystemUser); +my $user_obj = RT::User->new(RT->SystemUser); my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('customer-'.$$.'@example.com'); ok($ret, 'ACL test user creation'); $user_obj->SetName('customer-'.$$); @@ -24,7 +24,6 @@ my $cookie_jar = HTTP::Cookies->new; $agent->cookie_jar($cookie_jar); -no warnings 'once'; # get the top page $agent->login( $user_obj->Name, 'customer'); @@ -36,13 +35,13 @@ ok(!$agent->find_link( url => "$RT::WebPath/User/Prefs.html", # Now test for their presence, one at a time. Sleep for a bit after # ACL changes, thanks to the 10s ACL cache. -my ($grantid,$grantmsg) =$user_obj->PrincipalObj->GrantRight(Right => 'ShowConfigTab', Object => $RT::System); +my ($grantid,$grantmsg) =$user_obj->PrincipalObj->GrantRight(Right => 'ShowConfigTab', Object => RT->System); ok($grantid,$grantmsg); $agent->reload; -like($agent->{'content'} , qr/Logout/i, "Reloaded page successfully"); +$agent->content_contains('Logout', "Reloaded page successfully"); ok($agent->find_link( url => "$RT::WebPath/Admin/", text => 'Configuration'), "Found config tab" ); my ($revokeid,$revokemsg) =$user_obj->PrincipalObj->RevokeRight(Right => 'ShowConfigTab'); @@ -50,41 +49,40 @@ ok ($revokeid,$revokemsg); ($grantid,$grantmsg) =$user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf'); ok ($grantid,$grantmsg); $agent->reload(); -like($agent->{'content'} , qr/Logout/i, "Reloaded page successfully"); +$agent->content_contains('Logout', "Reloaded page successfully"); ok($agent->find_link( - text => 'Preferences'), "Found prefs pane" ); + id => 'preferences-settings' ), "Found prefs pane" ); ($revokeid,$revokemsg) = $user_obj->PrincipalObj->RevokeRight(Right => 'ModifySelf'); ok ($revokeid,$revokemsg); # Good. Now load the search page and test Load/Save Search. $agent->follow_link( url => "$RT::WebPath/Search/Build.html", text => 'Tickets'); -is($agent->{'status'}, 200, "Fetched search builder page"); -ok($agent->{'content'} !~ /Load saved search/i, "No search loading box"); -ok($agent->{'content'} !~ /Saved searches/i, "No saved searches box"); +is($agent->status, 200, "Fetched search builder page"); +$agent->content_lacks("Load saved search", "No search loading box"); +$agent->content_lacks("Saved searches", "No saved searches box"); ($grantid,$grantmsg) = $user_obj->PrincipalObj->GrantRight(Right => 'LoadSavedSearch'); ok($grantid,$grantmsg); $agent->reload(); -like($agent->{'content'} , qr/Load saved search/i, "Search loading box exists"); -ok($agent->{'content'} !~ /input\s+type=['"]submit['"][^>]+name=['"]SavedSearchSave['"]/i, +$agent->content_contains("Load saved search", "Search loading box exists"); +$agent->content_unlike(qr/input\s+type=['"]submit['"][^>]+name=['"]SavedSearchSave['"]/i, "Still no saved searches box"); ($grantid,$grantmsg) =$user_obj->PrincipalObj->GrantRight(Right => 'CreateSavedSearch'); ok ($grantid,$grantmsg); $agent->reload(); -like($agent->{'content'} , qr/Load saved search/i, - "Search loading box still exists"); -like($agent->{'content'} , qr/input\s+type=['"]submit['"][^>]+name=['"]SavedSearchSave['"]/i, +$agent->content_contains("Load saved search", "Search loading box still exists"); +$agent->content_like(qr/input\s+type=['"]submit['"][^>]+name=['"]SavedSearchSave['"]/i, "Saved searches box exists"); # Create a group, and a queue, so we can test limited user visibility # via SelectOwner. -my $queue_obj = RT::Queue->new($RT::SystemUser); +my $queue_obj = RT::Queue->new(RT->SystemUser); ($ret, $msg) = $queue_obj->Create(Name => 'CustomerQueue-'.$$, Description => 'queue for SelectOwner testing'); ok($ret, "SelectOwner test queue creation. $msg"); -my $group_obj = RT::Group->new($RT::SystemUser); +my $group_obj = RT::Group->new(RT->SystemUser); ($ret, $msg) = $group_obj->CreateUserDefinedGroup(Name => 'CustomerGroup-'.$$, Description => 'group for SelectOwner testing'); ok($ret, "SelectOwner test group creation. $msg"); @@ -107,4 +105,3 @@ ok($agent->form_name('BuildQuery'), "Yep, form is still there"); my $input = $agent->current_form->find_input('ValueOfActor'); ok(grep(/customer-$$/, $input->value_names()), "Found self in the actor listing"); -1; diff --git a/rt/t/web/saved_search_chart.t b/rt/t/web/saved_search_chart.t index 105166233..f84307162 100644 --- a/rt/t/web/saved_search_chart.t +++ b/rt/t/web/saved_search_chart.t @@ -2,11 +2,12 @@ use strict; use warnings; -use RT::Test tests => 19; +use RT::Test no_plan => 1; my ( $url, $m ) = RT::Test->started_ok; use RT::Attribute; -my $search = RT::Attribute->new($RT::SystemUser); -my $ticket = RT::Ticket->new($RT::SystemUser); + +my $search = RT::Attribute->new(RT->SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); my ( $ret, $msg ) = $ticket->Create( Subject => 'base ticket' . $$, Queue => 'general', @@ -35,7 +36,7 @@ $m->submit_form( button => 'SavedSearchSave', ); -$m->content_like( qr/Chart first chart saved/, 'saved first chart' ); +$m->content_contains("Chart first chart saved", 'saved first chart' ); my ( $search_uri, $id ) = $m->content =~ /value="(RT::User-\d+-SavedSearch-(\d+))"/; $m->submit_form( @@ -64,8 +65,8 @@ $m->submit_form( button => 'SavedSearchSave', ); -$m->content_like( qr/Chart first chart updated/, 'found updated message' ); -$m->content_like( qr/id=2/, 'Query is updated' ); +$m->content_contains("Chart first chart updated", 'found updated message' ); +$m->content_contains("id=2", 'Query is updated' ); $m->content_like( qr/value="Status"\s+selected="selected"/, 'PrimaryGroupBy is updated' ); $m->content_like( qr/value="pie"\s+selected="selected"/, @@ -81,6 +82,73 @@ $m->submit_form( form_name => 'SaveSearch', button => 'SavedSearchDelete', ); -$m->content_like( qr/Chart first chart deleted/, 'found deleted message' ); +$m->content_contains("Chart first chart deleted", 'found deleted message' ); $m->content_unlike( qr/value="RT::User-\d+-SavedSearch-\d+"/, 'no saved search' ); + +for ('A' .. 'F') { + $ticket->Create( + Subject => $$ . $_, + ); +} + +for ([A => 'subject="'.$$.'A"'], [BorC => 'subject="'.$$.'B" OR subject="'.$$.'C"']) { + $m->get_ok('/Search/Edit.html'); + $m->form_name('BuildQueryAdvanced'); + $m->field('Query', $_->[1]); + $m->submit; + + # Save the search + $m->follow_link_ok({id => 'page-chart'}); + $m->form_name('SaveSearch'); + $m->field(SavedSearchDescription => $_->[0]); + $m->click_ok('SavedSearchSave'); + $m->text_contains('Chart ' . $_->[0] . ' saved.'); + +} + +$m->form_name('SaveSearch'); +my @saved_search_ids = + $m->current_form->find_input('SavedSearchLoad')->possible_values; +shift @saved_search_ids; # first value is blank + +cmp_ok(@saved_search_ids, '==', 2, 'Two saved charts were made'); + +# TODO get find_link('page-chart')->URI->params to work... +sub page_chart_link_has { + my ($m, $id, $msg) = @_; + + $Test::Builder::Level = $Test::Builder::Level + 1; + + (my $dec_id = $id) =~ s/:/%3A/g; + + my $chart_url = $m->find_link(id => 'page-chart')->url; + like( + $chart_url, qr{SavedChartSearchId=\Q$dec_id\E}, + $msg || 'Page chart link matches the pattern we expected' + ); +} + +# load the first chart +$m->field('SavedSearchLoad' => $saved_search_ids[0]); +$m->click('SavedSearchLoadSubmit'); + +page_chart_link_has($m, $saved_search_ids[0]); + +$m->form_name('SaveSearch'); +is($m->form_number(3)->value('SavedChartSearchId'), $saved_search_ids[0]); + +$m->form_name('SaveSearch'); + +# now load the second chart +$m->field('SavedSearchLoad' => $saved_search_ids[1]); +$m->click('SavedSearchLoadSubmit'); + +page_chart_link_has($m, $saved_search_ids[1]); + +is( + $m->form_number(3)->value('SavedChartSearchId'), $saved_search_ids[1], + 'Second form is seen as a hidden field' +); + +page_chart_link_has($m, $saved_search_ids[1]); diff --git a/rt/t/web/saved_search_context.t b/rt/t/web/saved_search_context.t new file mode 100644 index 000000000..fe9c51ead --- /dev/null +++ b/rt/t/web/saved_search_context.t @@ -0,0 +1,69 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use RT::Test no_plan => 1; +my ( $url, $m ) = RT::Test->started_ok; + +my $ticket = RT::Ticket->new(RT->SystemUser); +for (['x', 50], ['y', 40], ['z', 30]) { + $ticket->Create( + Subject => $_->[0], + Queue => 'general', + Owner => 'root', + Priority => $_->[1], + Requestor => 'root@localhost', + ); +} + +ok( $m->login, 'logged in' ); + +$m->get($url . '/Search/Build.html?NewQuery=1'); +$m->form_name('BuildQuery'); +$m->field(ValueOfPriority => 45); +$m->click('DoSearch'); +#RT->Logger->error($m->uri); sleep 100; +#{ open my $fh, '>', 'm.html'; print $fh $m->content; close $fh; }; die; +$m->text_contains('Found 2 tickets'); + +$m->follow_link(id => 'page-edit_search'); +$m->form_name('BuildQuery'); +$m->field(ValueOfAttachment => 'z'); +$m->click('DoSearch'); + +$m->text_contains('Found 1 ticket'); + +$m->follow_link(id => 'page-bulk'); + +$m->form_name('BulkUpdate'); +ok(!$m->value('UpdateTicket2'), "There is no Ticket #2 in the search's bulk update"); + +sub edit_search_link_has { + my ($m, $id, $msg) = @_; + + local $Test::Builder::Level = $Test::Builder::Level + 1; + + (my $dec_id = $id) =~ s/:/%3A/g; + + my $chart_url = $m->find_link(id => 'page-edit_search')->url; + like( + $chart_url, qr{SavedSearchId=\Q$dec_id\E}, + $msg || 'Search link matches the pattern we expected' + ); +} + +diag("Test search context"); +{ + $m->get_ok($url . '/Search/Build.html'); + $m->form_name('BuildQuery'); + $m->field(ValueOfPriority => 45); + $m->click('AddClause'); + $m->form_name('BuildQuery'); + $m->set_fields( + SavedSearchDescription => 'my saved search', + ); + $m->click('SavedSearchSave'); + + my $saved_search_id = $m->form_name('BuildQuery')->value('SavedSearchId'); + edit_search_link_has($m, $saved_search_id); +} diff --git a/rt/t/web/saved_search_permissions.t b/rt/t/web/saved_search_permissions.t index f91ca13c6..5cae30b28 100644 --- a/rt/t/web/saved_search_permissions.t +++ b/rt/t/web/saved_search_permissions.t @@ -2,8 +2,8 @@ use strict; use warnings; -use RT::Test tests => 10; -my $user = RT::User->new($RT::SystemUser); +use RT::Test tests => 12; +my $user = RT::User->new(RT->SystemUser); ok( $user->Create( Name => 'foo', @@ -23,7 +23,7 @@ $m->submit_form( $m->content_contains( q{name="SavedSearchDescription" value="test"}, 'saved test search' ); my ($id) = $m->content =~ /value="(RT::User-\d+-SavedSearch-\d+)"/; -ok( $m->login( 'foo', 'foobar' ), 'logged in' ); +ok( $m->login( 'foo', 'foobar', logout => 1 ), 'logged in' ); $m->get_ok( $url . "/Search/Build.html?SavedSearchLoad=$id" ); my $message = qq{Can not load saved search "$id"}; diff --git a/rt/t/web/saved_search_update.t b/rt/t/web/saved_search_update.t index 9b2724e82..dfb18d49c 100644 --- a/rt/t/web/saved_search_update.t +++ b/rt/t/web/saved_search_update.t @@ -3,7 +3,7 @@ use strict; use warnings; -use RT::Test tests => 16; +use RT::Test tests => 18; my $root = RT::User->new( $RT::SystemUser ); $root->Load('root'); diff --git a/rt/t/web/scrips.t b/rt/t/web/scrips.t new file mode 100644 index 000000000..def20eca7 --- /dev/null +++ b/rt/t/web/scrips.t @@ -0,0 +1,105 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test tests => 14; + +# TODO: +# Test the rest of the conditions. +# Test actions. +# Test templates? +# Test cleanup scripts. + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, "logged in"; + +$m->follow_link_ok({id => 'tools-config-global-scrips-create'}); + +sub prepare_code_with_value { + my $value = shift; + + # changing the ticket is an easy scrip check for a test + return + '$self->TicketObj->SetSubject(' . + '$self->TicketObj->Subject . ' . + '"|" . ' . $value . + ')'; +} + +{ + # preserve order for checking the subject string later + my @values_for_actions; + + my $conds = RT::ScripConditions->new(RT->SystemUser); + foreach my $cond_value ('On Forward', 'On Forward Ticket', 'On Forward Transaction') { + $conds->Limit( + FIELD => 'name', + VALUE => $cond_value, + ENTRYAGGREGATOR => 'OR', + ); + } + + while (my $rec = $conds->Next) { + push @values_for_actions, [$rec->Id, '"' . $rec->Name . '"']; + } + + @values_for_actions = sort { $a->[0] cmp $b->[0] } @values_for_actions; + + foreach my $data (@values_for_actions) { + my ($condition, $prepare_code_value) = @$data; + diag "Create Scrip (Cond #$condition)" if $ENV{TEST_VERBOSE}; + $m->follow_link_ok({id => 'tools-config-global-scrips-create'}); + my $prepare_code = prepare_code_with_value($prepare_code_value); + $m->form_name('ModifyScrip'); + $m->set_fields( + 'Scrip-new-ScripCondition' => $condition, + 'Scrip-new-ScripAction' => 15, # User Defined + 'Scrip-new-Template' => 1, # Blank + 'Scrip-new-CustomPrepareCode' => $prepare_code, + ); + $m->submit; + } + + my $ticket_obj = RT::Test->create_ticket( + Subject => 'subject', + Content => 'stuff', + Queue => 1, + ); + my $ticket = $ticket_obj->id; + $m->goto_ticket($ticket); + + $m->follow_link_ok( + { id => 'page-actions-forward' }, + 'follow 1st Forward to forward ticket' + ); + + diag "Forward Ticket" if $ENV{TEST_VERBOSE}; + $m->submit_form( + form_name => 'ForwardMessage', + fields => { + To => 'rt-test, rt-to@example.com', + }, + button => 'ForwardAndReturn' + ); + + $m->text_contains("#${ticket}: subject|On Forward|On Forward Ticket"); + + diag "Forward Transaction" if $ENV{TEST_VERBOSE}; + # get the first transaction on the ticket + my ($transaction) = $ticket_obj->Transactions->First->id; + $m->get( + "$baseurl/Ticket/Forward.html?id=1&QuoteTransaction=$transaction" + ); + $m->submit_form( + form_name => 'ForwardMessage', + fields => { + To => 'rt-test, rt-to@example.com', + }, + button => 'ForwardAndReturn' + ); + + $m->text_contains("#${ticket}: subject|On Forward|On Forward Ticket|On Forward|On Forward Transaction"); + + RT::Test->clean_caught_mails; +} diff --git a/rt/t/web/scrub.t b/rt/t/web/scrub.t new file mode 100644 index 000000000..6483a7500 --- /dev/null +++ b/rt/t/web/scrub.t @@ -0,0 +1,46 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use RT::Test nodb => 1, tests => 6; +use RT::Interface::Web; # This gets us HTML::Mason::Commands +use Test::LongString; + +{ + my $html = 'This is a test of <span style="color: rgb(255, 0, 0); ">color</span> and <span style="font-size: 18px; "><span style="font-family: Georgia, serif; ">font</span></span> and <em><u><strike><strong>boldness</strong></strike></u></em>.'; + is_string(scrub_html($html), $html, "CKEditor produced HTML sails through"); +} + +{ + my $html = '<p style="text-align: right; "> + And <span style="color: rgb(255, 0, 0); "><span style="font-size: 16px; "><span style="font-family: Georgia, serif; ">alignment with color</span></span></span>?</p>'; + is_string(scrub_html($html), $html, "CKEditor produced HTML sails through"); +} + +{ + my $html = 'This is a test of <span style="color: rgb(255, 0, 0); content: url(/Nasty/URL);">color</span> and <span style="font-size: 18px; "><span style="font-family: Georgia, serif; ">font</span></span> and <em><u><strike><strong>boldness</strong></strike></u></em>.'; + my $expected = 'This is a test of <span>color</span> and <span style="font-size: 18px; "><span style="font-family: Georgia, serif; ">font</span></span> and <em><u><strike><strong>boldness</strong></strike></u></em>.'; + is_string(scrub_html($html), $expected, "nasty CSS not allowed through"); +} + +{ + my $html = 'Let\'s add some <span style="color: blue; font-family: Georgia">color</span> up in <span style="color: #DEADBE">here</span>.'; + is_string(scrub_html($html), $html, "multiple props and color specs allowed"); +} + +{ + my $html = q[<span lang=EN-US style='font-family:"Century Gothic","sans-serif";'>oh hai I'm some text</span>]; + my $expected = q[<span style="font-family:"Century Gothic","sans-serif";">oh hai I'm some text</span>]; + is_string(scrub_html($html), $expected, "font lists"); +} + +{ + my $html = q[<span lang=EN-US style='font-size:7.5pt;font-family:"Century Gothic","sans-serif";color:#666666;mso-fareast-language:IT'>oh hai I'm some text</span>]; + my $expected = q[<span style="font-size:7.5pt;font-family:"Century Gothic","sans-serif";color:#666666;mso-fareast-language:IT">oh hai I'm some text</span>]; + is_string(scrub_html($html), $expected, "outlook html"); +} + +sub scrub_html { + return HTML::Mason::Commands::ScrubHTML(shift); +} + diff --git a/rt/t/web/search_bulk_update_links.t b/rt/t/web/search_bulk_update_links.t index d6bfdfd3c..7d150a699 100644 --- a/rt/t/web/search_bulk_update_links.t +++ b/rt/t/web/search_bulk_update_links.t @@ -2,7 +2,7 @@ use strict; use warnings; -use RT::Test tests => 28; +use RT::Test tests => 47; my ( $url, $m ) = RT::Test->started_ok; ok( $m->login, 'logged in' ); @@ -13,7 +13,7 @@ use RT::Ticket; my ( @link_tickets, @search_tickets ); for ( 1 .. 3 ) { - my $link_ticket = RT::Ticket->new($RT::SystemUser); + my $link_ticket = RT::Ticket->new(RT->SystemUser); my ( $ret, $msg ) = $link_ticket->Create( Subject => "link ticket $_", Queue => 'general', @@ -25,7 +25,7 @@ for ( 1 .. 3 ) { } for ( 1 .. 3 ) { - my $ticket = RT::Ticket->new($RT::SystemUser); + my $ticket = RT::Ticket->new(RT->SystemUser); my ( $ret, $msg ) = $ticket->Create( Subject => "search ticket $_", Queue => 'general', @@ -41,7 +41,7 @@ $m->get_ok( $url . "/Search/Bulk.html?Query=id=$search_tickets[0]&Rows=10" ); $m->content_contains( 'Current Links', 'has current links part' ); $m->content_lacks( 'DeleteLink--', 'no delete link stuff' ); $m->submit_form( - form_number => 3, + form_name => 'BulkUpdate', fields => { 'Ticket-DependsOn' => $link_tickets[0], 'Ticket-MemberOf' => $link_tickets[1], @@ -77,9 +77,17 @@ $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" ); $m->content_contains( 'Current Links', 'has current links part' ); $m->content_lacks( 'DeleteLink--', 'no delete link stuff' ); +$m->form_name('BulkUpdate'); +my @fields = qw/Owner AddRequestor DeleteRequestor AddCc DeleteCc AddAdminCc +DeleteAdminCc Subject Priority Queue Status Starts_Date Told_Date Due_Date +Resolved_Date UpdateSubject UpdateContent/; +for my $field ( @fields ) { + is( $m->value($field), '', "default $field is empty" ); +} + # test DependsOn, MemberOf and RefersTo $m->submit_form( - form_number => 3, + form_name => 'BulkUpdate', fields => { 'Ticket-DependsOn' => $link_tickets[0], 'Ticket-MemberOf' => $link_tickets[1], @@ -98,7 +106,7 @@ $m->content_contains( 'found refers to link' ); $m->submit_form( - form_number => 3, + form_name => 'BulkUpdate', fields => { "DeleteLink--DependsOn-fsck.com-rt://$rtname/ticket/$link_tickets[0]" => 1, @@ -114,7 +122,7 @@ $m->content_lacks( 'DeleteLink--', 'links are all deleted' ); # test DependedOnBy, Members and ReferredToBy $m->submit_form( - form_number => 3, + form_name => 'BulkUpdate', fields => { 'DependsOn-Ticket' => $link_tickets[0], 'MemberOf-Ticket' => $link_tickets[1], @@ -133,7 +141,7 @@ $m->content_contains( 'found referrd to link' ); $m->submit_form( - form_number => 3, + form_name => 'BulkUpdate', fields => { "DeleteLink-fsck.com-rt://$rtname/ticket/$link_tickets[0]-DependsOn-" => 1, diff --git a/rt/t/web/search_cf_quotes.t b/rt/t/web/search_cf_quotes.t new file mode 100644 index 000000000..360fe0dc9 --- /dev/null +++ b/rt/t/web/search_cf_quotes.t @@ -0,0 +1,53 @@ +use strict; +use warnings; + +use RT::Test tests => 24; +my ( $baseurl, $m ) = RT::Test->started_ok; + +my $cf = RT::CustomField->new($RT::SystemUser); +ok( + $cf->Create( + Name => "I'm a cf", + Type => 'Date', + LookupType => 'RT::Queue-RT::Ticket', + ) +); +ok( $cf->AddToObject( RT::Queue->new($RT::SystemUser) ) ); + +RT::Test->create_tickets( + { Queue => 'General' }, + { Subject => 'ticket foo', 'CustomField-' . $cf->id => '2011-09-15' }, + { Subject => 'ticket bar', 'CustomField-' . $cf->id => '2011-10-15' }, + { Subject => 'ticket baz' }, +); + +ok( $m->login, 'logged in' ); + +$m->get_ok('/Search/Build.html'); +$m->form_name( 'BuildQuery' ); + +my ($cf_op) = + $m->find_all_inputs( type => 'option', name_regex => qr/I'm a cf/ ); +my ($cf_field) = + $m->find_all_inputs( type => 'text', name_regex => qr/I'm a cf/ ); + +diag "search directly"; +$m->submit_form( + fields => { $cf_op->name => '<', $cf_field->name => '2011-09-30', }, + button => 'DoSearch', +); + +$m->title_is( 'Found 1 ticket', 'found only 1 ticket' ); +$m->content_contains( 'ticket foo', 'has ticket foo' ); + +diag "first add clause, then search"; +$m->get_ok('/Search/Build.html?NewQuery=1'); +$m->form_name( 'BuildQuery' ); +$m->submit_form( + fields => { $cf_op->name => '<', $cf_field->name => '2011-09-30', }, + button => 'AddClause', +); +$m->follow_link_ok( { text => 'Show Results' } ); +$m->title_is( 'Found 1 ticket', 'found only 1 ticket' ); +$m->content_contains( 'ticket foo', 'has ticket foo' ); + diff --git a/rt/t/web/search_rss.t b/rt/t/web/search_rss.t index 454dc0369..95de931c3 100644 --- a/rt/t/web/search_rss.t +++ b/rt/t/web/search_rss.t @@ -2,10 +2,10 @@ use strict; -use RT::Test tests => 36; -RT::Test->started_ok; +use RT::Test tests => 38; +my ($baseurl, $agent) = RT::Test->started_ok; -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); for ( 1 .. 5 ) { $ticket->Create( Subject => 'Ticket ' . $_, @@ -15,7 +15,6 @@ for ( 1 .. 5 ) { ); } -my $agent = RT::Test::Web->new; ok $agent->login('root', 'password'), 'logged in as root'; $agent->get_ok('/Search/Build.html'); @@ -23,7 +22,7 @@ $agent->form_name('BuildQuery'); $agent->field('idOp', '>'); $agent->field('ValueOfid', '0'); $agent->submit('DoSearch'); -$agent->follow_link_ok({text=>'Show Results'}); +$agent->follow_link_ok({id => 'page-results'}); for ( 1 .. 5 ) { $agent->content_contains('Ticket ' . $_); @@ -64,7 +63,7 @@ my $user_b = RT::Test->load_or_create_user( Name => 'user_b', Password => 'password', ); ok $user_b && $user_b->id, 'loaded or created user'; -$agent_b->login('user_b', 'password'), 'logged in as user B'; +$agent_b->login('user_b', 'password'); $agent_b->get_ok($noauth_uri); is( $agent_b->content_type, 'application/rss+xml', 'content type' ); is( $agent_b->content, $rss_content, 'content' ); diff --git a/rt/t/web/search_simple.t b/rt/t/web/search_simple.t new file mode 100644 index 000000000..1efc9a566 --- /dev/null +++ b/rt/t/web/search_simple.t @@ -0,0 +1,22 @@ +use strict; +use warnings; + +use RT::Test tests => 16; +my ( $baseurl, $m ) = RT::Test->started_ok; + +RT::Test->create_tickets( + { Queue => 'General' }, + { Subject => 'ticket foo' }, + { Subject => 'ticket bar' }, +); + +ok( $m->login, 'logged in' ); + +$m->get_ok('/Search/Simple.html'); +$m->content_lacks( 'Show Results', 'no page menu' ); +$m->get_ok('/Search/Simple.html?q=ticket foo'); +$m->content_contains( 'Show Results', "has page menu" ); +$m->title_is( 'Found 1 ticket', 'title' ); +$m->content_contains( 'ticket foo', 'has ticket foo' ); + +# TODO more simple search tests diff --git a/rt/t/web/search_tabs.t b/rt/t/web/search_tabs.t new file mode 100644 index 000000000..b3ed2cbdf --- /dev/null +++ b/rt/t/web/search_tabs.t @@ -0,0 +1,86 @@ +#!/usr/bin/perl +use warnings; +use strict; + +use RT::Test tests => 21; +my ($baseurl, $agent) = RT::Test->started_ok; + +my $ticket = RT::Ticket->new(RT->SystemUser); +for ( 1 .. 3 ) { + $ticket->Create( + Subject => 'Ticket ' . $_, + Queue => 'General', + Owner => 'root', + Requestor => 'clownman@localhost', + ); +} + +ok $agent->login('root', 'password'), 'logged in as root'; + +# [issues.bestpractical.com #16841] { +$agent->get_ok('/Search/Build.html'); + +$agent->form_name('BuildQuery'); +$agent->field('idOp', '='); +$agent->field('ValueOfid', '1'); +$agent->submit('AddClause'); + +$agent->form_name('BuildQuery'); +$agent->field('idOp', '='); +$agent->field('ValueOfid', '2'); +$agent->field('AndOr', 'OR'); +$agent->submit('AddClause'); + +$agent->follow_link_ok({id => 'page-results'}); +$agent->title_is('Found 2 tickets'); +# } + +# [issues.bestpractical.com #17237] { +$agent->follow_link_ok({text => 'New Search'}); +$agent->title_is('Query Builder'); + +$agent->form_name('BuildQuery'); +$agent->field('idOp', '='); +$agent->field('ValueOfid', '1'); +$agent->submit('AddClause'); + +$agent->form_name('BuildQuery'); +$agent->field('idOp', '='); +$agent->field('ValueOfid', '2'); +$agent->field('AndOr', 'OR'); +$agent->click_button(name => 'DoSearch'); + +$agent->title_is('Found 2 tickets'); + +$agent->follow_link_ok({id => 'page-results'}); +$agent->title_is('Found 2 tickets'); +# } + +$agent->follow_link_ok({text => 'Chart'}); +$agent->text_contains('id = 1 OR id = 2'); +$agent->form_name('SaveSearch'); +$agent->field('SavedSearchDescription' => 'this is my saved chart'); +$agent->click_button(name => 'SavedSearchSave'); + +# Confirm that we saved the chart and that it's the "current chart" +$agent->text_contains('Chart this is my saved chart saved.'); +$agent->form_name('SaveSearch'); +is($agent->value('SavedSearchDescription'), 'this is my saved chart'); + +$agent->follow_link_ok({text => 'Edit Search'}); +$agent->form_name('BuildQuery'); +$agent->field('idOp', '='); +$agent->field('ValueOfid', '3'); +$agent->field('AndOr', 'OR'); +$agent->click_button(name => 'DoSearch'); + +$agent->title_is('Found 3 tickets'); + +$agent->follow_link_ok({text => 'Chart'}); +$agent->text_contains('id = 1 OR id = 2 OR id = 3'); + +# The interesting bit: confirm that the chart we saved is still the +# "current chart" after roundtripping through search builder +$agent->form_name('SaveSearch'); +is($agent->value('SavedSearchDescription'), 'this is my saved chart'); + diff --git a/rt/t/web/self_service.t b/rt/t/web/self_service.t new file mode 100644 index 000000000..49d9e37ee --- /dev/null +++ b/rt/t/web/self_service.t @@ -0,0 +1,19 @@ +use strict; +use warnings; +use RT::Test tests => 9; + +my ($url, $m) = RT::Test->started_ok; + +my ($ticket) = + RT::Test->create_ticket( Queue => 'General', Subject => 'test subject' ); + +$m->login(); + +$m->get_ok( '/SelfService/Display.html?id=' . $ticket->id, + 'got selfservice display page' ); + +my $title = '#' . $ticket->id . ': test subject'; +$m->title_is( $title ); +$m->content_contains( "<h1>$title</h1>", "contains <h1>$title</h1>" ); + +# TODO need more SelfService tests diff --git a/rt/t/web/squish.t b/rt/t/web/squish.t new file mode 100644 index 000000000..ff43e74fb --- /dev/null +++ b/rt/t/web/squish.t @@ -0,0 +1,78 @@ +use strict; +use warnings; +use RT; +use RT::Test tests => 26; + +RT->Config->Set( DevelMode => 0 ); +RT->Config->Set( WebDefaultStylesheet => 'aileron' ); +RT->Config->Set( MasonLocalComponentRoot => RT::Test::get_abs_relocatable_dir('html') ); + +my ( $url, $m ) = RT::Test->started_ok; +$m->login; + +diag "test squished files with devel mode disabled"; + +$m->follow_link_ok( { url_regex => qr!aileron-squished-([a-f0-9]{32})\.css! }, + 'follow squished css' ); +$m->content_like( qr!/\*\* End of .*?.css \*/!, 'squished css' ); +$m->content_lacks( 'text-decoration: underline !important;', + 'no print.css by default' ); + +$m->back; +my ($js_link) = + $m->content =~ m!src="([^"]+?squished-([a-f0-9]{32})\.js)"!; +$m->get_ok( $url . $js_link, 'follow squished js' ); +$m->content_lacks('function just_testing', "no not-by-default.js"); +$m->content_contains('jQuery.noConflict', "found default js content"); + +RT::Test->stop_server; + +diag "test squished files with customized files and devel mode disabled"; +RT->AddJavaScript( 'not-by-default.js' ); +RT->AddStyleSheets( 'print.css' ); + +( $url, $m ) = RT::Test->started_ok; + +$m->login; +$m->follow_link_ok( { url_regex => qr!aileron-squished-([a-f0-9]{32})\.css! }, + 'follow squished css' ); +$m->content_like( qr!/\*\* End of .*?.css \*/!, 'squished css' ); +$m->content_contains( 'text-decoration: underline !important;', + 'has print.css' ); + +$m->back; +($js_link) = + $m->content =~ m!src="([^"]+?squished-([a-f0-9]{32})\.js)"!; +$m->get_ok( $url . $js_link, 'follow squished js' ); +$m->content_contains( 'function just_testing', "has not-by-default.js" ); +$m->content_contains('jQuery.noConflict', "found default js content"); +RT::Test->stop_server; + + +diag "Test with a trivial jsmin which is a pass-through"; +RT->Config->Set( 'JSMinPath' => RT::Test::get_abs_relocatable_dir("passthrough-jsmin")); +( $url, $m ) = RT::Test->started_ok; +$m->login; +($js_link) = + $m->content =~ m!src="([^"]+?squished-([a-f0-9]{32})\.js)"!; +$m->get_ok( $url . $js_link, 'follow squished js' ); +$m->content_contains( 'passthrough-jsmin added this', "has passthrough-added content" ); +$m->content_contains( 'function just_testing', "has not-by-default.js" ); +$m->content_contains('jQuery.noConflict', "found default js content"); +RT::Test->stop_server; + + +diag "test squished files with devel mode enabled"; +RT->Config->Set( 'DevelMode' => 1 ); +RT->AddJavaScript( 'not-by-default.js' ); +RT->AddStyleSheets( 'nottherebutwedontcare.css' ); + +( $url, $m ) = RT::Test->started_ok; +$m->login; +$m->content_unlike( qr!squished-.*?\.(js|css)!, + 'no squished link with develmode' ); + +$m->content_contains('not-by-default.js', "found extra javascript resource"); +$m->content_contains('nottherebutwedontcare.css', "found extra css resource"); +$m->content_contains('jquery_noconflict.js', "found a default js resource"); + diff --git a/rt/t/web/template.t b/rt/t/web/template.t new file mode 100644 index 000000000..40a5366dc --- /dev/null +++ b/rt/t/web/template.t @@ -0,0 +1,62 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use RT::Test tests => 19; + +my $user_a = RT::Test->load_or_create_user( + Name => 'user_a', Password => 'password', +); +ok $user_a && $user_a->id, 'loaded or created user'; + +my ($baseurl, $m) = RT::Test->started_ok; + +ok( RT::Test->set_rights( + { Principal => $user_a, Right => [qw(ShowConfigTab ShowTemplate ModifyTemplate)] }, +), 'set rights'); + +ok $m->login('user_a', 'password'), 'logged in as user A'; + +# get to the templates screen +$m->follow_link( text => 'Configuration' ); +$m->title_is(q{RT Administration}, 'admin screen'); + +$m->follow_link( text => 'Global' ); +$m->title_is(q{Admin/Global configuration}, 'global admin'); + +$m->follow_link( text => 'Templates' ); +$m->title_is(q{Modify templates which apply to all queues}, 'global templates'); + +$m->follow_link( text => 'Resolved' ); # template name +$m->title_is(q{Modify template Resolved}, 'modifying the Resolved template'); + +# now try changing Type back and forth +$m->form_name('ModifyTemplate'); +is($m->value('Type'), 'Perl'); + +$m->field(Type => 'Simple'); +$m->submit; + +$m->title_is(q{Modify template Resolved}, 'modifying the Resolved template'); +$m->form_name('ModifyTemplate'); +is($m->value('Type'), 'Simple', 'updated type to simple'); + +$m->field(Type => 'Perl'); +$m->submit; + +$m->title_is(q{Modify template Resolved}, 'modifying the Resolved template'); +$m->form_name('ModifyTemplate'); +is($m->value('Type'), 'Simple', 'need the ExecuteCode right to update Type to Perl'); +$m->content_contains('Permission Denied'); + +ok( RT::Test->add_rights( + { Principal => $user_a, Right => [qw(ExecuteCode)] }, +), 'add ExecuteCode rights'); + +$m->field(Type => 'Perl'); +$m->submit; + +$m->title_is(q{Modify template Resolved}, 'modifying the Resolved template'); +$m->form_name('ModifyTemplate'); +is($m->value('Type'), 'Perl', 'now that we have ExecuteCode we can update Type to Perl'); + diff --git a/rt/t/web/ticket-create-utf8.t b/rt/t/web/ticket-create-utf8.t index f37eeec06..8d36bd19e 100644 --- a/rt/t/web/ticket-create-utf8.t +++ b/rt/t/web/ticket-create-utf8.t @@ -3,9 +3,7 @@ use strict; use warnings; -use RT::Test tests => 41; - -$RT::Test::SKIP_REQUEST_WORK_AROUND = 1; +use RT::Test tests => 43; use Encode; @@ -32,7 +30,7 @@ ok $m->login, 'logged in'; # create a ticket with a subject only foreach my $test_str ( $ru_test, $l1_test ) { ok $m->goto_create_ticket( $q ), "go to create ticket"; - $m->form_number(3); + $m->form_name('TicketCreate'); $m->field( Subject => $test_str ); $m->submit; @@ -49,7 +47,7 @@ foreach my $test_str ( $ru_test, $l1_test ) { foreach my $test_str ( $ru_test, $l1_test ) { foreach my $support_str ( $ru_support, $l1_support ) { ok $m->goto_create_ticket( $q ), "go to create ticket"; - $m->form_number(3); + $m->form_name('TicketCreate'); $m->field( Subject => $test_str ); $m->field( Content => $support_str ); $m->submit; @@ -58,8 +56,8 @@ foreach my $test_str ( $ru_test, $l1_test ) { qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i, 'header on the page' ); - $m->content_like( - qr{\Q$support_str\E}i, + $m->content_contains( + $support_str, 'content on the page' ); @@ -72,7 +70,7 @@ foreach my $test_str ( $ru_test, $l1_test ) { foreach my $test_str ( $ru_test, $l1_test ) { foreach my $support_str ( $ru_support, $l1_support ) { ok $m->goto_create_ticket( $q ), "go to create ticket"; - $m->form_number(3); + $m->form_name('TicketCreate'); $m->field( Subject => $test_str ); $m->field( Content => $support_str ); $m->submit; @@ -81,8 +79,8 @@ foreach my $test_str ( $ru_test, $l1_test ) { qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i, 'header on the page' ); - $m->content_like( - qr{\Q$support_str\E}i, + $m->content_contains( + $support_str, 'content on the page' ); diff --git a/rt/t/web/ticket_display.t b/rt/t/web/ticket_display.t new file mode 100644 index 000000000..a9cab0cbf --- /dev/null +++ b/rt/t/web/ticket_display.t @@ -0,0 +1,63 @@ +use strict; +use warnings; + +use RT::Test tests => 18; + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); + +my $user = RT::Test->load_or_create_user( + Name => 'user', + Password => 'password', +); + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok( + RT::Test->set_rights( + { Principal => $user, Right => [qw(SeeQueue CreateTicket)] }, + ), + 'set rights' +); + +ok $m->login( 'user', 'password' ), 'logged in as user'; + +diag "test ShowTicket right"; +{ + + $m->get_ok( '/Ticket/Create.html?Queue=' . $queue->id, + 'go to ticket create page' ); + my $form = $m->form_name('TicketCreate'); + $m->submit_form( fields => { Subject => 'ticket foo' } ); + + my $ticket = RT::Test->last_ticket; + ok( $ticket->id, 'ticket is created' ); + my $id = $ticket->id; + + $m->content_lacks( "Ticket $id created", 'created ticket' ); + $m->content_contains( "No permission to view newly created ticket #$id", + 'got no permission msg' ); + $m->warning_like( qr/No permission to view newly created ticket #$id/, + 'got no permission warning' ); + + + $m->goto_ticket($id); + $m->content_contains( "No permission to view ticket", + 'got no permission msg' ); + $m->warning_like( qr/No permission to view ticket/, 'got warning' ); + $m->title_is('RT Error'); + + ok( + RT::Test->add_rights( + { Principal => $user, Right => [qw(ShowTicket)] }, + ), + 'add ShowTicket right' + ); + + $m->reload; + + $m->content_lacks( "No permission to view ticket", 'no error msg' ); + $m->title_is( "#$id: ticket foo", 'we can it' ); +} + + +# TODO more /Ticket/Display.html tests here + diff --git a/rt/t/web/ticket_forward.t b/rt/t/web/ticket_forward.t new file mode 100644 index 000000000..be06ad976 --- /dev/null +++ b/rt/t/web/ticket_forward.t @@ -0,0 +1,232 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use RT::Test tests => undef; +use File::Spec; +my $att_file = File::Spec->catfile( RT::Test->temp_directory, 'attachment' ); +open my $att_fh, '>', $att_file or die $!; +print $att_fh "this is an attachment"; +close $att_fh; +my $att_name = ( File::Spec->splitpath($att_file) )[-1]; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; + +# Create a ticket with content and an attachment +$m->get_ok( $baseurl . '/Ticket/Create.html?Queue=1' ); + +$m->submit_form( + form_name => 'TicketCreate', + fields => { + Subject => 'test forward', + Content => 'this is content', + Attach => $att_file, + }, +); +$m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); +RT::Test->clean_caught_mails; + +diag "Forward Ticket" if $ENV{TEST_VERBOSE}; +{ + $m->follow_link_ok( + { id => 'page-actions-forward' }, + 'follow 1st Forward to forward ticket' + ); + + $m->submit_form( + form_name => 'ForwardMessage', + fields => { + To => 'rt-test, rt-to@example.com', + Cc => 'rt-cc@example.com', + }, + button => 'ForwardAndReturn' + ); + $m->content_contains( 'Sent email successfully', 'sent mail msg' ); + $m->content_contains( + 'Forwarded Ticket to rt-test, rt-to@example.com, rt-cc@example.com', + 'txn msg' ); + my ($mail) = RT::Test->fetch_caught_mails; + like( $mail, qr!Subject: test forward!, 'Subject field' ); + like( $mail, qr!To: rt-test, rt-to\@example.com!, 'To field' ); + like( $mail, qr!Cc: rt-cc\@example.com!, 'Cc field' ); + like( $mail, qr!This is a forward of ticket!, 'content' ); + like( $mail, qr!this is an attachment!, 'att content' ); + like( $mail, qr!$att_name!, 'att file name' ); +} + +diag "Forward Transaction" if $ENV{TEST_VERBOSE}; +{ + $m->follow_link_ok( { text => 'Forward', n => 2 }, 'follow 2nd Forward' ); + $m->submit_form( + form_name => 'ForwardMessage', + fields => { + To => 'rt-test, rt-to@example.com', + Cc => 'rt-cc@example.com', + Bcc => 'rt-bcc@example.com' + }, + button => 'ForwardAndReturn' + ); + $m->content_contains( 'Sent email successfully', 'sent mail msg' ); + $m->content_like( +qr/Forwarded Transaction #\d+ to rt-test, rt-to\@example.com, rt-cc\@example.com, rt-bcc\@example.com/, + 'txn msg' + ); + my ($mail) = RT::Test->fetch_caught_mails; + like( $mail, qr!Subject: test forward!, 'Subject field' ); + like( $mail, qr!To: rt-test, rt-to\@example.com!, 'To field' ); + like( $mail, qr!Cc: rt-cc\@example.com!, 'Cc field' ); + like( $mail, qr!Bcc: rt-bcc\@example.com!, 'Bcc field' ); + like( $mail, qr!This is a forward of transaction!, 'content' ); + like( $mail, qr!$att_name!, 'att file name' ); + like( $mail, qr!this is an attachment!, 'att content' ); +} + +diag "Forward Ticket without content" if $ENV{TEST_VERBOSE}; +{ + my $ticket = RT::Test->create_ticket( + Subject => 'test forward without content', + Queue => 1, + ); + $m->get_ok( $baseurl . '/Ticket/Forward.html?id=' . $ticket->id ); + $m->submit_form( + form_name => 'ForwardMessage', + fields => { To => 'rt-test@example.com', }, + button => 'ForwardAndReturn' + ); + $m->content_contains( 'Sent email successfully', 'sent mail msg' ); + my ($mail) = RT::Test->fetch_caught_mails; + like( $mail, qr/Subject: Fwd: \[example\.com #\d\] test forward without content/, 'Subject field' ); + like( $mail, qr/To: rt-test\@example\.com/, 'To field' ); + like( $mail, qr/This is a forward of ticket #\d/, 'content' ); +} + +diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBOSE}; +{ + # Create a ticket without content but with a non-text/plain attachment + $m->get_ok( $baseurl . '/Ticket/Create.html?Queue=1' ); + + $m->form_name('TicketCreate'); + my $attach = $m->current_form->find_input('Attach'); + $attach->filename("awesome.patch"); + $attach->headers('Content-Type' => 'text/x-diff'); + $m->set_fields( + Subject => 'test forward, empty content but attachments', + Attach => $att_file, # from up top + ); + $m->click('AddMoreAttach'); + $m->form_name('TicketCreate'); + $attach = $m->current_form->find_input('Attach'); + $attach->filename("bpslogo.png"); + $attach->headers('Content-Type' => 'image/png'); + $m->set_fields( + Attach => RT::Test::get_relocatable_file('bpslogo.png', '..', 'data'), # an image! + ); + $m->submit; + $m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); + $m->content_like( qr/awesome\.patch/, 'uploaded patch file' ); + $m->content_like( qr/text\/x-diff/, 'uploaded patch file content type' ); + $m->content_like( qr/bpslogo\.png/, 'uploaded image file' ); + $m->content_like( qr/image\/png/, 'uploaded image file content type' ); + RT::Test->clean_caught_mails; + + $m->follow_link_ok( { text => 'Forward', n => 2 }, 'follow 2nd Forward' ); + $m->submit_form( + form_name => 'ForwardMessage', + fields => { + To => 'rt-test@example.com', + }, + button => 'ForwardAndReturn' + ); + $m->content_contains( 'Sent email successfully', 'sent mail msg' ); + $m->content_like( qr/Forwarded Transaction #\d+ to rt-test\@example\.com/, 'txn msg' ); + my ($mail) = RT::Test->fetch_caught_mails; + like( $mail, qr/Subject: test forward, empty content but attachments/, 'Subject field' ); + like( $mail, qr/To: rt-test\@example.com/, 'To field' ); + like( $mail, qr/This is a forward of transaction/, 'content' ); + like( $mail, qr/awesome\.patch/, 'att file name' ); + like( $mail, qr/this is an attachment/, 'att content' ); + like( $mail, qr/text\/x-diff/, 'att content type' ); + like( $mail, qr/bpslogo\.png/, 'att image file name' ); + like( $mail, qr/image\/png/, 'att image content type' ); +} + +diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_VERBOSE}; +{ + my $mime = MIME::Entity->build( + From => 'test@example.com', + Subject => 'attachments for everyone', + Type => 'multipart/mixed', + ); + + $mime->attach( + Path => $att_file, + Type => 'text/x-diff', + Filename => 'awesome.patch', + Disposition => 'attachment', + ); + + $mime->attach( + Path => RT::Test::get_relocatable_file('bpslogo.png', '..', 'data'), + Type => 'image/png', + Filename => 'bpslogo.png', + Encoding => 'base64', + Disposition => 'attachment', + ); + + my $ticket = RT::Test->create_ticket( + Queue => 1, + Subject => 'test forward, attachments but no "content"', + MIMEObj => $mime, + ); + + $m->get_ok( $baseurl . '/Ticket/Display.html?id=' . $ticket->Id ); + $m->content_like( qr/awesome\.patch/, 'uploaded patch file' ); + $m->content_like( qr/text\/x-diff/, 'uploaded patch file content type' ); + $m->content_like( qr/bpslogo\.png/, 'uploaded image file' ); + $m->content_like( qr/image\/png/, 'uploaded image file content type' ); + RT::Test->clean_caught_mails; + + # Forward txn + $m->follow_link_ok( { text => 'Forward', n => 2 }, 'follow 2nd Forward' ); + $m->submit_form( + form_name => 'ForwardMessage', + fields => { + To => 'rt-test@example.com', + }, + button => 'ForwardAndReturn' + ); + $m->content_contains( 'Sent email successfully', 'sent mail msg' ); + $m->content_like( qr/Forwarded Transaction #\d+ to rt-test\@example\.com/, 'txn msg' ); + + # Forward ticket + $m->follow_link_ok( { text => 'Forward', n => 1 }, 'follow 1st Forward' ); + $m->submit_form( + form_name => 'ForwardMessage', + fields => { + To => 'rt-test@example.com', + }, + button => 'ForwardAndReturn' + ); + $m->content_contains( 'Sent email successfully', 'sent mail msg' ); + $m->content_like( qr/Forwarded Ticket to rt-test\@example\.com/, 'txn msg' ); + + my ($forward_txn, $forward_ticket) = RT::Test->fetch_caught_mails; + my $tag = qr/Fwd: \[example\.com #\d+\]/; + like( $forward_txn, qr/Subject: $tag attachments for everyone/, 'Subject field is from txn' ); + like( $forward_txn, qr/This is a forward of transaction/, 'forward description' ); + like( $forward_ticket, qr/Subject: $tag test forward, attachments but no "content"/, 'Subject field is from ticket' ); + like( $forward_ticket, qr/This is a forward of ticket/, 'forward description' ); + + for my $mail ($forward_txn, $forward_ticket) { + like( $mail, qr/To: rt-test\@example.com/, 'To field' ); + like( $mail, qr/awesome\.patch/, 'att file name' ); + like( $mail, qr/this is an attachment/, 'att content' ); + like( $mail, qr/text\/x-diff/, 'att content type' ); + like( $mail, qr/bpslogo\.png/, 'att image file name' ); + like( $mail, qr/image\/png/, 'att image content type' ); + } +} + +undef $m; +done_testing; diff --git a/rt/t/web/ticket_links.t b/rt/t/web/ticket_links.t new file mode 100644 index 000000000..cb30e92f9 --- /dev/null +++ b/rt/t/web/ticket_links.t @@ -0,0 +1,110 @@ +use strict; +use warnings; +use RT::Test tests => 106; + +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 ( $deleted, $active, $inactive ) = RT::Test->create_tickets( + { Queue => 'General' }, + { Subject => 'deleted ticket', }, + { Subject => 'active ticket', }, + { Subject => 'inactive ticket', } +); + +my ( $deleted_id, $active_id, $inactive_id ) = + ( $deleted->id, $active->id, $inactive->id ); + +$deleted->SetStatus('deleted'); +is( $deleted->Status, 'deleted', "deleted $deleted_id" ); + +$inactive->SetStatus('resolved'); +is( $inactive->Status, 'resolved', 'resolved $inactive_id' ); + +for my $type ( "DependsOn", "MemberOf", "RefersTo" ) { + for my $c (qw/base target/) { + my $id; + + diag "create ticket with links of type $type $c"; + { + ok( $m->goto_create_ticket($queue), "go to create ticket" ); + $m->form_name('TicketCreate'); + $m->field( Subject => "test ticket creation with $type $c" ); + if ( $c eq 'base' ) { + $m->field( "new-$type", "$deleted_id $active_id $inactive_id" ); + } + else { + $m->field( "$type-new", "$deleted_id $active_id $inactive_id" ); + } + + $m->submit; + $m->content_like(qr/Ticket \d+ created/, 'created ticket'); + $m->content_contains("Can't link to a deleted ticket"); + $id = RT::Test->last_ticket->id; + } + + diag "add ticket links of type $type $c"; + { + my $ticket = RT::Test->create_ticket( + Queue => 'General', + Subject => "test $type $c", + ); + $id = $ticket->id; + + $m->goto_ticket($id); + $m->follow_link_ok( { text => 'Links' }, "Followed link to Links" ); + + ok( $m->form_with_fields("$id-DependsOn"), "found the form" ); + if ( $c eq 'base' ) { + $m->field( "$id-$type", "$deleted_id $active_id $inactive_id" ); + } + else { + $m->field( "$type-$id", "$deleted_id $active_id $inactive_id" ); + } + $m->submit; + $m->content_contains("Can't link to a deleted ticket"); + + if ( $c eq 'base' ) { + $m->content_like( + qr{"DeleteLink--$type-.*?ticket/$active_id"}, + "$c for $type: has active ticket", + ); + $m->content_like( + qr{"DeleteLink--$type-.*?ticket/$inactive_id"}, + "base for $type: has inactive ticket", + ); + $m->content_unlike( + qr{"DeleteLink--$type-.*?ticket/$deleted_id"}, + "base for $type: no deleted ticket", + ); + } + else { + $m->content_like( + qr{"DeleteLink-.*?ticket/$active_id-$type-"}, + "$c for $type: has active ticket", + ); + $m->content_like( + qr{"DeleteLink-.*?ticket/$inactive_id-$type-"}, + "base for $type: has inactive ticket", + ); + $m->content_unlike( + qr{"DeleteLink-.*?ticket/$deleted_id-$type-"}, + "base for $type: no deleted ticket", + ); + } + } + + $m->goto_ticket($id); + $m->content_like( qr{$active_id:.*?\[new\]}, "has active ticket", ); + $m->content_like( + qr{$inactive_id:.*?\[resolved\]}, + "has inactive ticket", + ); + $m->content_unlike( qr{$deleted_id.*?\[deleted\]}, "no deleted ticket", + ); + } +} + diff --git a/rt/t/web/ticket_modify_all.t b/rt/t/web/ticket_modify_all.t new file mode 100644 index 000000000..c9dd7e7cd --- /dev/null +++ b/rt/t/web/ticket_modify_all.t @@ -0,0 +1,44 @@ +use strict; +use warnings; + +use RT::Test tests => 15; + +my $ticket = RT::Test->create_ticket( + Subject => 'test bulk update', + Queue => 1, +); + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login, 'logged in' ); + +$m->get_ok( $url . "/Ticket/ModifyAll.html?id=" . $ticket->id ); + +$m->submit_form( + form_number => 3, + fields => { 'UpdateContent' => 'this is update content' }, + button => 'SubmitTicket', +); + +$m->content_contains("Message recorded", 'updated ticket'); +$m->content_lacks("this is update content", 'textarea is clear'); + +$m->get_ok($url . '/Ticket/Display.html?id=' . $ticket->id ); +$m->content_contains("this is update content", 'updated content in display page'); + +# NOTE http://issues.bestpractical.com/Ticket/Display.html?id=18284 +RT::Test->stop_server; +RT->Config->Set(AutocompleteOwners => 1); +($url, $m) = RT::Test->started_ok; +$m->login; + +$m->get_ok($url . '/Ticket/ModifyAll.html?id=' . $ticket->id); + +$m->form_name('TicketModifyAll'); +$m->field(Owner => 'root'); +$m->click('SubmitTicket'); + +$m->form_name('TicketModifyAll'); +is($m->value('Owner'), 'root', 'owner was successfully changed to root'); + +# XXX TODO test other parts, i.e. basic, dates, people and links + diff --git a/rt/t/web/ticket_modify_people.t b/rt/t/web/ticket_modify_people.t new file mode 100644 index 000000000..750be3f2c --- /dev/null +++ b/rt/t/web/ticket_modify_people.t @@ -0,0 +1,113 @@ +use strict; +use warnings; + +use RT::Test tests => 23; + +my $root = RT::Test->load_or_create_user( Name => 'root' ); +my $group_foo = RT::Group->new($RT::SystemUser); +my ( $ret, $msg ) = $group_foo->CreateUserDefinedGroup( + Name => 'group_foo', + Description => 'group_foo', +); +ok( $ret, 'created group_foo' ); + +my $ticket = RT::Test->create_ticket( + Subject => 'test modify people', + Queue => 'General', + Requestor => $root->id, + Cc => $group_foo->id, +); + +my $user = RT::Test->load_or_create_user( + Name => 'user', + Password => 'password', +); +ok $user && $user->id, 'loaded or created user'; + +ok( + RT::Test->set_rights( + { Principal => $user, Right => [qw(SeeQueue ShowTicket ModifyTicket)] }, + ), + 'set rights' +); + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login( 'user', 'password' ), 'logged in' ); +$m->get_ok( $url . "/Ticket/ModifyPeople.html?id=" . $ticket->id ); + +ok( + !$m->find_link( + text => 'Enoch Root', + url_regex => qr!/Admin/Users/Modify\.html!, + ), + 'no link to modify user' +); +$m->content_contains('Enoch Root', 'still has the user name' ); + +ok( + !$m->find_link( + text => 'group_foo', + url_regex => qr!/Admin/Groups/Modify\.html!, + ), + 'no link to modify group' +); + +$m->content_contains('group_foo', 'still has the group name' ); + +ok( RT::Test->add_rights( { Principal => $user, Right => ['AdminUsers'] }, ), + 'added AdminUsers right' ); +$m->reload; +ok( + !$m->find_link( + text => 'Enoch Root', + url_regex => qr!/Admin/Users/Modify\.html!, + ), + 'still no link to modify user' +); +ok( + !$m->find_link( + text => 'group_foo', + url_regex => qr!/Admin/Groups/Modify\.html!, + ), + 'still no link to modify group' +); + +ok( + RT::Test->add_rights( { Principal => $user, Right => ['ShowConfigTab'] }, ), + 'added ShowConfigTab right', +); + +$m->reload; +ok( + $m->find_link( + text => 'Enoch Root', + url_regex => qr!/Admin/Users/Modify\.html!, + ), + 'got link to modify user' +); + +ok( + !$m->find_link( + text => 'group_foo', + url_regex => qr!/Admin/Groups/Modify\.html!, + ), + 'still no link to modify group' +); + +ok( + RT::Test->add_rights( { Principal => $user, Right => ['AdminGroup'] }, ), + 'added AdminGroup right' +); + +$m->reload; +ok( + $m->find_link( + text => 'group_foo', + url_regex => qr!/Admin/Groups/Modify\.html!, + ), + 'got link to modify group' +); + + +# TODO test Add|Delete people + diff --git a/rt/t/web/ticket_owner.t b/rt/t/web/ticket_owner.t index 0bacaf1bc..4db39e61a 100644 --- a/rt/t/web/ticket_owner.t +++ b/rt/t/web/ticket_owner.t @@ -3,7 +3,7 @@ use strict; use warnings; -use RT::Test tests => 91; +use RT::Test nodata => 1, tests => 105; my $queue = RT::Test->load_or_create_queue( Name => 'Regression' ); ok $queue && $queue->id, 'loaded or created queue'; @@ -18,26 +18,25 @@ my $user_b = RT::Test->load_or_create_user( ); ok $user_b && $user_b->id, 'loaded or created user'; -RT::Test->started_ok; +my ($baseurl, $agent_a) = RT::Test->started_ok; ok( RT::Test->set_rights( { Principal => $user_a, Right => [qw(SeeQueue ShowTicket CreateTicket ReplyToTicket)] }, { Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] }, ), 'set rights'); -my $agent_a = RT::Test::Web->new; ok $agent_a->login('user_a', 'password'), 'logged in as user A'; -diag "current user has no right to own, nobody selected as owner on create" if $ENV{TEST_VERBOSE}; +diag "current user has no right to own, nobody selected as owner on create"; { $agent_a->get_ok('/', 'open home page'); $agent_a->form_name('CreateTicketInQueue'); $agent_a->select( 'Queue', $queue->id ); $agent_a->submit; - $agent_a->content_like(qr/Create a new ticket/i, 'opened create ticket page'); + $agent_a->content_contains('Create a new ticket', 'opened create ticket page'); my $form = $agent_a->form_name('TicketCreate'); - is $form->value('Owner'), $RT::Nobody->id, 'correct owner selected'; + is $form->value('Owner'), RT->Nobody->id, 'correct owner selected'; ok !grep($_ == $user_a->id, $form->find_input('Owner')->possible_values), 'user A can not own tickets'; $agent_a->submit; @@ -46,22 +45,22 @@ diag "current user has no right to own, nobody selected as owner on create" if $ my ($id) = ($agent_a->content =~ /Ticket (\d+) created in queue/); ok $id, 'found id of the ticket'; - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $id ); ok $ticket->id, 'loaded the ticket'; - is $ticket->Owner, $RT::Nobody->id, 'correct owner'; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; } -diag "user can chose owner of a new ticket" if $ENV{TEST_VERBOSE}; +diag "user can chose owner of a new ticket"; { $agent_a->get_ok('/', 'open home page'); $agent_a->form_name('CreateTicketInQueue'); $agent_a->select( 'Queue', $queue->id ); $agent_a->submit; - $agent_a->content_like(qr/Create a new ticket/i, 'opened create ticket page'); + $agent_a->content_contains('Create a new ticket', 'opened create ticket page'); my $form = $agent_a->form_name('TicketCreate'); - is $form->value('Owner'), $RT::Nobody->id, 'correct owner selected'; + is $form->value('Owner'), RT->Nobody->id, 'correct owner selected'; ok grep($_ == $user_b->id, $form->find_input('Owner')->possible_values), 'user B is listed as potential owner'; @@ -72,7 +71,7 @@ diag "user can chose owner of a new ticket" if $ENV{TEST_VERBOSE}; my ($id) = ($agent_a->content =~ /Ticket (\d+) created in queue/); ok $id, 'found id of the ticket'; - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $id ); ok $ticket->id, 'loaded the ticket'; is $ticket->Owner, $user_b->id, 'correct owner'; @@ -81,7 +80,7 @@ diag "user can chose owner of a new ticket" if $ENV{TEST_VERBOSE}; my $agent_b = RT::Test::Web->new; ok $agent_b->login('user_b', 'password'), 'logged in as user B'; -diag "user A can not change owner after create" if $ENV{TEST_VERBOSE}; +diag "user A can not change owner after create"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -95,30 +94,29 @@ diag "user A can not change owner after create" if $ENV{TEST_VERBOSE}; # try the following group of tests twice with different agents(logins) my $test_cb = sub { my $agent = shift; - $agent->goto_ticket( $id ); - $agent->follow_link_ok({text => 'Basics'}, 'Ticket -> Basics'); - my $form = $agent->form_number(3); + $agent->get("/Ticket/Modify.html?id=$id"); + my $form = $agent->form_name('TicketModify'); is $form->value('Owner'), $user_b->id, 'correct owner selected'; - $agent->select('Owner', $RT::Nobody->id); + $agent->select('Owner', RT->Nobody->id); $agent->submit; - $agent->content_like( - qr/Permission denied/i, + $agent->content_contains( + 'Permission Denied', 'no way to change owner after create if you have no rights' ); - my $ticket = RT::Ticket->new( $RT::SystemUser ); + my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $id ); ok $ticket->id, 'loaded the ticket'; is $ticket->Owner, $user_b->id, 'correct owner'; }; $test_cb->($agent_a); - diag "even owner(user B) can not change owner" if $ENV{TEST_VERBOSE}; + diag "even owner(user B) can not change owner"; $test_cb->($agent_b); } -diag "on reply correct owner is selected" if $ENV{TEST_VERBOSE}; +diag "on reply correct owner is selected"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -130,13 +128,13 @@ diag "on reply correct owner is selected" if $ENV{TEST_VERBOSE}; is $ticket->Owner, $user_b->id, 'correct owner'; $agent_a->goto_ticket( $id ); - $agent_a->follow_link_ok({text => 'Reply'}, 'Ticket -> Basics'); + $agent_a->follow_link_ok({text => 'Reply'}, 'Ticket -> Reply'); - my $form = $agent_a->form_number(3); + my $form = $agent_a->form_name('TicketUpdate'); is $form->value('Owner'), '', 'empty value selected'; $agent_a->submit; - $ticket = RT::Ticket->new( $RT::SystemUser ); + $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $id ); ok $ticket->id, 'loaded the ticket'; is $ticket->Owner, $user_b->id, 'correct owner'; @@ -147,7 +145,7 @@ ok( RT::Test->set_rights( { Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] }, ), 'set rights'); -diag "Couldn't take without coresponding right" if $ENV{TEST_VERBOSE}; +diag "Couldn't take without coresponding right"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -155,7 +153,7 @@ diag "Couldn't take without coresponding right" if $ENV{TEST_VERBOSE}; Subject => 'test', ); ok $id, 'created a ticket #'. $id or diag "error: $msg"; - is $ticket->Owner, $RT::Nobody->id, 'correct owner'; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; $agent_a->goto_ticket( $id ); ok !($agent_a->find_all_links( text => 'Take' ))[0], @@ -164,7 +162,7 @@ diag "Couldn't take without coresponding right" if $ENV{TEST_VERBOSE}; 'no Steal link as well'; } -diag "Couldn't steal without coresponding right" if $ENV{TEST_VERBOSE}; +diag "Couldn't steal without coresponding right"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -186,7 +184,7 @@ ok( RT::Test->set_rights( { Principal => $user_a, Right => [qw(SeeQueue ShowTicket CreateTicket TakeTicket)] }, ), 'set rights'); -diag "TakeTicket require OwnTicket to work" if $ENV{TEST_VERBOSE}; +diag "TakeTicket require OwnTicket to work"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -194,7 +192,7 @@ diag "TakeTicket require OwnTicket to work" if $ENV{TEST_VERBOSE}; Subject => 'test', ); ok $id, 'created a ticket #'. $id or diag "error: $msg"; - is $ticket->Owner, $RT::Nobody->id, 'correct owner'; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; $agent_a->goto_ticket( $id ); ok !($agent_a->find_all_links( text => 'Take' ))[0], @@ -208,7 +206,7 @@ ok( RT::Test->set_rights( { Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] }, ), 'set rights'); -diag "TakeTicket+OwnTicket work" if $ENV{TEST_VERBOSE}; +diag "TakeTicket+OwnTicket work"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -216,20 +214,20 @@ diag "TakeTicket+OwnTicket work" if $ENV{TEST_VERBOSE}; Subject => 'test', ); ok $id, 'created a ticket #'. $id or diag "error: $msg"; - is $ticket->Owner, $RT::Nobody->id, 'correct owner'; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; $agent_a->goto_ticket( $id ); ok !($agent_a->find_all_links( text => 'Steal' ))[0], 'no Steal link'; $agent_a->follow_link_ok({text => 'Take'}, 'Ticket -> Take'); - $ticket = RT::Ticket->new( $RT::SystemUser ); + $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $id ); ok $ticket->id, 'loaded the ticket'; is $ticket->Owner, $user_a->id, 'correct owner'; } -diag "TakeTicket+OwnTicket don't work when owner is not nobody" if $ENV{TEST_VERBOSE}; +diag "TakeTicket+OwnTicket don't work when owner is not nobody"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -252,7 +250,7 @@ ok( RT::Test->set_rights( { Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] }, ), 'set rights'); -diag "StealTicket require OwnTicket to work" if $ENV{TEST_VERBOSE}; +diag "StealTicket require OwnTicket to work"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -275,7 +273,7 @@ ok( RT::Test->set_rights( { Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] }, ), 'set rights'); -diag "StealTicket+OwnTicket work" if $ENV{TEST_VERBOSE}; +diag "StealTicket+OwnTicket work"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -291,13 +289,13 @@ diag "StealTicket+OwnTicket work" if $ENV{TEST_VERBOSE}; 'but no Take link'; $agent_a->follow_link_ok({text => 'Steal'}, 'Ticket -> Steal'); - $ticket = RT::Ticket->new( $RT::SystemUser ); + $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $id ); ok $ticket->id, 'loaded the ticket'; is $ticket->Owner, $user_a->id, 'correct owner'; } -diag "StealTicket+OwnTicket don't work when owner is nobody" if $ENV{TEST_VERBOSE}; +diag "StealTicket+OwnTicket don't work when owner is nobody"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -305,7 +303,7 @@ diag "StealTicket+OwnTicket don't work when owner is nobody" if $ENV{TEST_VERBOS Subject => 'test', ); ok $id, 'created a ticket #'. $id or diag "error: $msg"; - is $ticket->Owner, $RT::Nobody->id, 'correct owner'; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; $agent_a->goto_ticket( $id ); ok !($agent_a->find_all_links( text => 'Steal' ))[0], @@ -319,7 +317,7 @@ ok( RT::Test->set_rights( { Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] }, ), 'set rights'); -diag "no Steal link when owner nobody" if $ENV{TEST_VERBOSE}; +diag "no Steal link when owner nobody"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -327,7 +325,7 @@ diag "no Steal link when owner nobody" if $ENV{TEST_VERBOSE}; Subject => 'test', ); ok $id, 'created a ticket #'. $id or diag "error: $msg"; - is $ticket->Owner, $RT::Nobody->id, 'correct owner'; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; $agent_a->goto_ticket( $id ); ok !($agent_a->find_all_links( text => 'Steal' ))[0], @@ -336,7 +334,7 @@ diag "no Steal link when owner nobody" if $ENV{TEST_VERBOSE}; 'but have Take link'); } -diag "no Take link when owner is not nobody" if $ENV{TEST_VERBOSE}; +diag "no Take link when owner is not nobody"; { my $ticket = RT::Ticket->new( $user_a ); my ($id, $txn, $msg) = $ticket->Create( @@ -354,3 +352,68 @@ diag "no Take link when owner is not nobody" if $ENV{TEST_VERBOSE}; 'but have Steal link'); } +ok( + RT::Test->set_rights( + { + Principal => $user_a, + Right => [ + qw(SeeQueue ShowTicket CreateTicket ReplyToTicket OwnTicket TakeTicket StealTicket) + ] + }, + { Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] }, + ), + 'set rights' +); + +diag +"action is Take if old owner is nobody and new owner is current user in update page"; +{ + my $ticket = RT::Ticket->new( $user_a ); + my ( $id, $txn, $msg ) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + ); + ok $id, 'created a ticket #'. $id or diag "error: $msg"; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; + + $agent_a->goto_ticket( $id ); + $agent_a->content_lacks('Taken', 'no Taken'); + $agent_a->follow_link_ok({text => 'Reply'}, 'Ticket -> Reply'); + $agent_a->submit_form( + form_name => 'TicketUpdate', + fields => { Owner => $user_a->id }, + button => 'SubmitTicket', + ); + $agent_a->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message' ); + + $agent_b->goto_ticket($id); + $agent_b->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message for user b ' ); +} + +diag +"action is Take if old owner is nobody and new owner is current user in basics page"; +{ + my $ticket = RT::Ticket->new($user_a); + my ( $id, $txn, $msg ) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + ); + ok $id, 'created a ticket #' . $id or diag "error: $msg"; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; + + $agent_a->goto_ticket($id); + $agent_a->content_lacks('Taken', 'no Taken'); + $agent_a->follow_link_ok( { text => 'Basics' }, 'Ticket -> Basics' ); + $agent_a->submit_form( + form_name => 'TicketModify', + fields => { Owner => $user_a->id }, + ); + $agent_a->content_contains( 'Owner changed from Nobody to user_a', + 'got set message in Basics' ); + $agent_a->goto_ticket($id); + $agent_a->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message' ); + + $agent_b->goto_ticket($id); + $agent_b->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message for user b ' ); +} + diff --git a/rt/t/web/ticket_owner_autocomplete.t b/rt/t/web/ticket_owner_autocomplete.t new file mode 100644 index 000000000..3aa3f282f --- /dev/null +++ b/rt/t/web/ticket_owner_autocomplete.t @@ -0,0 +1,185 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test nodata => 1, tests => 43; +use JSON qw(from_json); + +my $queue = RT::Test->load_or_create_queue( Name => 'Regression' ); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user_a = RT::Test->load_or_create_user( + Name => 'user_a', Password => 'password', +); +ok $user_a && $user_a->id, 'loaded or created user'; + +my $user_b = RT::Test->load_or_create_user( + Name => 'user_b', Password => 'password', +); +ok $user_b && $user_b->id, 'loaded or created user'; + +RT->Config->Set( AutocompleteOwners => 1 ); +my ($baseurl, $agent_a) = RT::Test->started_ok; + +ok( RT::Test->set_rights( + { Principal => $user_a, Right => [qw(SeeQueue ShowTicket CreateTicket ReplyToTicket)] }, + { Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] }, +), 'set rights'); + +ok $agent_a->login('user_a', 'password'), 'logged in as user A'; + +diag "current user has no right to own, nobody selected as owner on create"; +{ + $agent_a->get_ok('/', 'open home page'); + $agent_a->form_name('CreateTicketInQueue'); + $agent_a->select( 'Queue', $queue->id ); + $agent_a->submit; + + $agent_a->content_contains('Create a new ticket', 'opened create ticket page'); + my $form = $agent_a->form_name('TicketCreate'); + is $form->value('Owner'), RT->Nobody->Name, 'correct owner selected'; + autocomplete_lacks( 'RT::Queue-'.$queue->id, 'user_a' ); + $agent_a->submit; + + $agent_a->content_like(qr/Ticket \d+ created in queue/i, 'created ticket'); + my ($id) = ($agent_a->content =~ /Ticket (\d+) created in queue/); + ok $id, 'found id of the ticket'; + + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load( $id ); + ok $ticket->id, 'loaded the ticket'; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; +} + +diag "user can chose owner of a new ticket"; +{ + $agent_a->get_ok('/', 'open home page'); + $agent_a->form_name('CreateTicketInQueue'); + $agent_a->select( 'Queue', $queue->id ); + $agent_a->submit; + + $agent_a->content_contains('Create a new ticket', 'opened create ticket page'); + my $form = $agent_a->form_name('TicketCreate'); + is $form->value('Owner'), RT->Nobody->Name, 'correct owner selected'; + + autocomplete_contains( 'RT::Queue-'.$queue->id, 'user_b' ); + $form->value('Owner', $user_b->Name); + $agent_a->submit; + + $agent_a->content_like(qr/Ticket \d+ created in queue/i, 'created ticket'); + my ($id) = ($agent_a->content =~ /Ticket (\d+) created in queue/); + ok $id, 'found id of the ticket'; + + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load( $id ); + ok $ticket->id, 'loaded the ticket'; + is $ticket->Owner, $user_b->id, 'correct owner'; +} + +my $agent_b = RT::Test::Web->new; +ok $agent_b->login('user_b', 'password'), 'logged in as user B'; + +diag "user A can not change owner after create"; +{ + my $ticket = RT::Ticket->new( $user_a ); + my ($id, $txn, $msg) = $ticket->Create( + Queue => $queue->id, + Owner => $user_b->id, + Subject => 'test', + ); + ok $id, 'created a ticket #'. $id or diag "error: $msg"; + is $ticket->Owner, $user_b->id, 'correct owner'; + + # try the following group of tests twice with different agents(logins) + my $test_cb = sub { + my $agent = shift; + $agent->get("/Ticket/Modify.html?id=$id"); + my $form = $agent->form_name('TicketModify'); + is $form->value('Owner'), $user_b->Name, 'correct owner selected'; + $form->value('Owner', RT->Nobody->Name); + $agent->submit; + + $agent->content_contains( + 'Permission Denied', + 'no way to change owner after create if you have no rights' + ); + + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load( $id ); + ok $ticket->id, 'loaded the ticket'; + is $ticket->Owner, $user_b->id, 'correct owner'; + }; + + $test_cb->($agent_a); + diag "even owner(user B) can not change owner"; + $test_cb->($agent_b); +} + +diag "on reply correct owner is selected"; +{ + my $ticket = RT::Ticket->new( $user_a ); + my ($id, $txn, $msg) = $ticket->Create( + Queue => $queue->id, + Owner => $user_b->id, + Subject => 'test', + ); + ok $id, 'created a ticket #'. $id or diag "error: $msg"; + is $ticket->Owner, $user_b->id, 'correct owner'; + + $agent_a->goto_ticket( $id ); + $agent_a->follow_link_ok( { id => 'page-actions-reply' }, 'Reply' ); + + my $form = $agent_a->form_number(3); + is $form->value('Owner'), 'user_b', 'current user selected'; + $agent_a->submit; + + $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load( $id ); + ok $ticket->id, 'loaded the ticket'; + is $ticket->Owner, $user_b->id, 'correct owner'; +} + +sub autocomplete { + my $limit = shift; + my $agent = shift; + $agent->get_ok("/Helpers/Autocomplete/Owners?term=&limit=$limit&return=Name", "fetched autocomplete values"); + return from_json($agent->content); +} + +sub autocomplete_contains { + my $limit = shift; + my $expected = shift; + my $agent = shift; + + unless ( $agent ) { + $agent = RT::Test::Web->new; + $agent->login('user_a', 'password'); + } + + my $results = autocomplete( $limit, $agent ); + + my %seen; + $seen{$_->{value}}++ for @$results; + $expected = [$expected] unless ref $expected eq 'ARRAY'; + is((scalar grep { not $seen{$_} } @$expected), 0, "got all expected values"); +} + +sub autocomplete_lacks { + my $limit = shift; + my $lacks = shift; + my $agent = shift; + + unless ( $agent ) { + $agent = RT::Test::Web->new; + $agent->login('user_a', 'password'); + } + + my $results = autocomplete( $limit, $agent ); + + my %seen; + $seen{$_->{value}}++ for @$results; + $lacks = [$lacks] unless ref $lacks eq 'ARRAY'; + is((scalar grep { $seen{$_} } @$lacks), 0, "didn't get any unexpected values"); +} + diff --git a/rt/t/web/ticket_owner_issues_16656.t b/rt/t/web/ticket_owner_issues_16656.t new file mode 100644 index 000000000..a6306f740 --- /dev/null +++ b/rt/t/web/ticket_owner_issues_16656.t @@ -0,0 +1,63 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use RT::Test tests => 19; + +my $queue = RT::Test->load_or_create_queue( Name => 'Test' ); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user_a = RT::Test->load_or_create_user( + Name => 'user_a', + EmailAddress => 'user_a@example.com', + Password => 'password', +); +ok $user_a && $user_a->id, 'loaded or created user'; + +RT->Config->Set( AutocompleteOwners => 0 ); +my ($baseurl, $agent_root) = RT::Test->started_ok; + +ok( RT::Test->set_rights({ + Principal => 'Requestor', + Object => $queue, + Right => [qw(OwnTicket)] +}), 'set rights'); + +ok $agent_root->login('root', 'password'), 'logged in as user root'; + +diag "user_a doesn't show up in create form"; +{ + $agent_root->get_ok('/', 'open home page'); + $agent_root->form_name('CreateTicketInQueue'); + $agent_root->select( 'Queue', '1' ); + $agent_root->submit; + + $agent_root->content_contains('Create a new ticket', 'opened create ticket page'); + my $form = $agent_root->form_name('TicketCreate'); + my $input = $form->find_input('Owner'); + is $input->value, RT->Nobody->Id, 'correct owner selected'; + ok((not scalar grep { $_ == $user_a->Id } $input->possible_values), 'no user_a value in dropdown'); + $form->value('Requestors', 'user_a@example.com'); + $agent_root->submit; + + $agent_root->content_like(qr/Ticket \d+ created in queue/i, 'created ticket'); + my ($id) = ($agent_root->content =~ /Ticket (\d+) created in queue/); + ok $id, 'found id of the ticket'; + + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load( $id ); + ok $ticket->id, 'loaded the ticket'; + is $ticket->Queue, '1', 'correct queue'; + is $ticket->Owner, RT->Nobody->Id, 'correct owner'; + is $ticket->RequestorAddresses, 'user_a@example.com', 'correct requestor'; +} + +diag "user_a doesn't appear in owner list after being made requestor"; +{ + $agent_root->get("/Ticket/Modify.html?id=1"); + my $form = $agent_root->form_name('TicketModify'); + my $input = $form->find_input('Owner'); + is $input->value, RT->Nobody->Id, 'correct owner selected'; + ok((not scalar grep { $_ == $user_a->Id } $input->possible_values), 'no user_a value in dropdown'); +} + diff --git a/rt/t/web/ticket_seen.t b/rt/t/web/ticket_seen.t index 3a77a5899..8dacaf59f 100644 --- a/rt/t/web/ticket_seen.t +++ b/rt/t/web/ticket_seen.t @@ -3,7 +3,7 @@ use strict; use warnings; -use RT::Test tests => 21; +use RT::Test nodata => 1, tests => 23, config => 'Set($ShowUnreadMessageNotifications, 1);'; my $queue = RT::Test->load_or_create_queue( Name => 'Regression' ); ok $queue && $queue->id, 'loaded or created queue'; @@ -30,7 +30,7 @@ ok $agent_a->login('user_a', 'password'), 'logged in as user A'; my $agent_b = RT::Test::Web->new; ok $agent_b->login('user_b', 'password'), 'logged in as user B'; -diag "create a ticket for testing" if $ENV{TEST_VERBOSE}; +diag "create a ticket for testing"; my $tid; { my $ticket = RT::Ticket->new( $user_a ); @@ -44,7 +44,7 @@ my $tid; is $ticket->Owner, $user_a->id, 'correct owner'; } -diag "user B adds a message, we check that user A see notification and can clear it" if $ENV{TEST_VERBOSE}; +diag "user B adds a message, we check that user A see notification and can clear it"; { my $ticket = RT::Ticket->new( $user_b ); $ticket->Load( $tid ); @@ -62,10 +62,10 @@ diag "user B adds a message, we check that user A see notification and can clear ok( $reply_id, 'got correspond txn id' ); $agent_a->goto_ticket($tid); - $agent_a->content_like(qr/bla-bla/ims, 'the message on the page'); + $agent_a->content_contains('bla-bla', 'the message on the page'); - $agent_a->content_like( - qr/unread message/ims, + $agent_a->content_contains( + 'unread message', 'we have not seen something' ); @@ -76,20 +76,20 @@ diag "user B adds a message, we check that user A see notification and can clear like( $agent_a->base, qr/#txn-$reply_id$/, 'contains anchor' ); $agent_a->follow_link_ok({text => 'jump to the first unread message and mark all messages as seen'}, 'try to mark all as seen'); - $agent_a->content_like( - qr/Marked all messages as seen/ims, + $agent_a->content_contains( + 'Marked all messages as seen', 'see success message' ); like( $agent_a->base, qr/#txn-$reply_id$/, 'contains anchor' ); - $agent_a->content_like( - qr/Marked all messages as seen/ims, + $agent_a->content_contains( + 'Marked all messages as seen', 'see success message' ); $agent_a->goto_ticket($tid); - $agent_a->content_unlike( - qr/unread message/ims, + $agent_a->content_lacks( + 'unread message', 'we have seen everything, so no messages' ); } diff --git a/rt/t/web/ticket_txn_content.t b/rt/t/web/ticket_txn_content.t index 1c1056a3e..db4751218 100644 --- a/rt/t/web/ticket_txn_content.t +++ b/rt/t/web/ticket_txn_content.t @@ -1,37 +1,48 @@ #!/usr/bin/perl -w use strict; -use RT::Test tests => 37; -use File::Temp 'tempfile'; -use File::Spec; -my ( $plain_fh, $plain_file ) = - tempfile( 'rttestXXXXXX', SUFFIX => '.txt', UNLINK => 1, TMPDIR => 1 ); +use RT::Test tests => 63; +my $plain_file = File::Spec->catfile( RT::Test->temp_directory, 'attachment.txt' ); +open my $plain_fh, '>', $plain_file or die $!; print $plain_fh "this is plain content"; close $plain_fh; my $plain_name = (File::Spec->splitpath($plain_file))[-1]; -my ( $html_fh, $html_file ) = - tempfile( 'rttestXXXXXX', SUFFIX => '.html', UNLINK => 1, TMPDIR => 1 ); -print $html_fh "this is html content"; +my $html_file = File::Spec->catfile( RT::Test->temp_directory, 'attachment.html' ); +open my $html_fh, '>', $html_file or die $!; +print $html_fh "this is plain content"; close $html_fh; my $html_name = (File::Spec->splitpath($html_file))[-1]; my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'logged in'; -my $queue = RT::Queue->new($RT::Nobody); +my $queue = RT::Queue->new(RT->Nobody); my $qid = $queue->Load('General'); ok( $qid, "Loaded General queue" ); -RT::Test->set_mail_catcher; RT::Test->clean_caught_mails; +sub follow_parent_with_headers_link { + my $m = shift; + my $link = $m->find_link(@_)->url; + $link =~ s{/(\d+)$}{"/" . ($1-1)}e; # get the parent attach + $m->get_ok($baseurl . $link); +} + +sub follow_with_headers_link { + my $m = shift; + my $link = $m->find_link(@_)->url; + $link =~ s{/\d+/(\d+)/.+$}{/WithHeaders/$1}; # frob into a with headers url + $m->get_ok($baseurl . $link); +} + for my $type ( 'text/plain', 'text/html' ) { $m->form_name('CreateTicketInQueue'); $m->field( 'Queue', $qid ); $m->submit; is( $m->status, 200, "request successful" ); - $m->content_like( qr/Create a new ticket/, 'ticket create page' ); + $m->content_contains('Create a new ticket', 'ticket create page' ); $m->form_name('TicketCreate'); $m->field( 'Subject', 'with plain attachment' ); @@ -40,16 +51,26 @@ for my $type ( 'text/plain', 'text/html' ) { $m->field( 'ContentType', $type ) unless $type eq 'text/plain'; $m->submit; is( $m->status, 200, "request successful" ); - $m->content_like( qr/with plain attachment/, + $m->content_contains('with plain attachment', 'we have subject on the page' ); - $m->content_like( qr/this is main content/, 'main content' ); - $m->content_like( qr/Download $plain_name/, 'download plain file link' ); + $m->content_contains('this is main content', 'main content' ); + $m->content_contains("Download $plain_name", 'download plain file link' ); + + # Check for Message-IDs + follow_parent_with_headers_link($m, text => 'with headers', n => 1); + $m->content_like(qr/^Message-ID:/im, 'create content has one Message-ID'); + $m->content_unlike(qr/^Message-ID:.+?Message-ID:/ism, 'but not two Message-IDs'); + $m->back; + + follow_with_headers_link($m, text => "Download $plain_name", n => 1); + $m->content_unlike(qr/^Message-ID:/im, 'attachment lacks a Message-ID'); + $m->back; my ( $mail ) = RT::Test->fetch_caught_mails; like( $mail, qr/this is main content/, 'email contains main content' ); # check the email link in page too $m->follow_link_ok( { text => 'Show' }, 'show the email outgoing' ); - $m->content_like( qr/this is main content/, 'email contains main content'); + $m->content_contains('this is main content', 'email contains main content'); $m->back; $m->follow_link_ok( { text => 'Reply' }, "reply to the ticket" ); @@ -67,13 +88,27 @@ for my $type ( 'text/plain', 'text/html' ) { $m->click('SubmitTicket'); is( $m->status, 200, "request successful" ); - $m->content_like( qr/this is main reply content/, 'main reply content' ); - $m->content_like( qr/Download $html_name/, 'download html file link' ); + $m->content_contains("this is main reply content", 'main reply content' ); + $m->content_contains("Download $html_name", 'download html file link' ); + + # Check for Message-IDs + follow_parent_with_headers_link($m, text => 'with headers', n => 2); + $m->content_like(qr/^Message-ID:/im, 'correspondence has one Message-ID'); + $m->content_unlike(qr/^Message-ID:.+?Message-ID:/ism, 'but not two Message-IDs'); + $m->back; + + follow_with_headers_link($m, text => "Download $plain_name", n => 2); + $m->content_unlike(qr/^Message-ID:/im, 'text/plain attach lacks a Message-ID'); + $m->back; + + follow_with_headers_link($m, text => "Download $html_name", n => 1); + $m->content_unlike(qr/^Message-ID:/im, 'text/html attach lacks a Message-ID'); + $m->back; ( $mail ) = RT::Test->fetch_caught_mails; like( $mail, qr/this is main reply content/, 'email contains main reply content' ); # check the email link in page too $m->follow_link_ok( { text => 'Show', n => 2 }, 'show the email outgoing' ); - $m->content_like( qr/this is main reply content/, 'email contains main reply content'); + $m->content_contains("this is main reply content", 'email contains main reply content'); $m->back; } diff --git a/rt/t/web/ticket_update_without_content.t b/rt/t/web/ticket_update_without_content.t index 595cb74e9..54f0c5a8a 100644 --- a/rt/t/web/ticket_update_without_content.t +++ b/rt/t/web/ticket_update_without_content.t @@ -2,11 +2,11 @@ use strict; use warnings; -use RT::Test tests => 10; +use RT::Test tests => 12; my ( $url, $m ) = RT::Test->started_ok; # merged tickets still show up in search -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); my ( $ret, $msg ) = $ticket->Create( Subject => 'base ticket' . $$, Queue => 'general', @@ -30,10 +30,10 @@ $m->submit_form( fields => { Priority => '1', } ); -$m->content_like(qr/priority changed/i); -$m->content_unlike(qr/message recorded/i); +$m->content_contains("Priority changed"); +$m->content_lacks("message recorded"); -my $root = RT::User->new( $RT::SystemUser ); +my $root = RT::User->new( RT->SystemUser ); $root->Load('root'); ( $ret, $msg ) = $root->SetSignature(<<EOF); best wishes @@ -48,5 +48,5 @@ $m->submit_form( form_number => 3, fields => { Priority => '2', } ); -$m->content_like(qr/priority changed/i); -$m->content_unlike(qr/message recorded/i); +$m->content_contains("Priority changed"); +$m->content_lacks("message recorded"); diff --git a/rt/t/web/transaction_batch.t b/rt/t/web/transaction_batch.t new file mode 100644 index 000000000..ae04e1fca --- /dev/null +++ b/rt/t/web/transaction_batch.t @@ -0,0 +1,55 @@ +use strict; +use warnings; +use RT; +use RT::Test tests => 12; + + +my $q = RT::Test->load_or_create_queue ( Name => 'General' ); + +my $s1 = RT::Scrip->new(RT->SystemUser); +my ($val, $msg) =$s1->Create( Queue => $q->Id, + ScripCondition => 'User Defined', + ScripAction => 'User Defined', + CustomIsApplicableCode => 'return ($self->TransactionObj->Field||"") eq "TimeEstimated"', + CustomPrepareCode => 'return 1', + CustomCommitCode => '$self->TicketObj->SetPriority($self->TicketObj->Priority + 2); return 1;', + Template => 'Blank', + Stage => 'TransactionBatch', + ); +ok($val,$msg); + +my $ticket = RT::Ticket->new(RT->SystemUser); +my ($tv,$ttv,$tm) = $ticket->Create(Queue => $q->Id, + Subject => "hair on fire", + ); +ok($tv, $tm); + +# Flush the Create transaction off of the ticket +$ticket->ApplyTransactionBatch; + +my $testuser = RT::Test->load_or_create_user( Name => 'bob', EmailAddress => 'bob@example.com', Password => 'password' ); +ok($testuser->Id, "Created test user bob"); + +ok( RT::Test->add_rights({ Principal => 'Privileged', Right => [qw(ShowTicket ModifyTicket SeeQueue)]}), 'Granted ticket management rights'); + +my $test_current_user = RT::CurrentUser->new(); +$test_current_user->LoadByName($testuser->Name); +my $api_test = RT::Ticket->new($test_current_user); +$api_test->Load($ticket->Id); +is($api_test->Priority,0,"Ticket priority starts at 0"); +$api_test->SetTimeEstimated(12); +$api_test->ApplyTransactionBatch; +is($api_test->CurrentUser->UserObj->Name, $testuser->Name,"User didn't change running Transaction Batch scrips"); +$api_test->Load($api_test->Id); +is($api_test->Priority,2,"Ticket priority updated"); + +my ($baseurl, $m) = RT::Test->started_ok; +$m->login('bob','password'); +$m->get_ok("$baseurl/Ticket/Modify.html?id=".$ticket->Id); + $m->submit_form( form_name => 'TicketModify', + fields => { TimeEstimated => 5 } + ); + + +$ticket->Load($ticket->Id); +is ($ticket->Priority , 4, "Ticket priority is set right"); diff --git a/rt/t/web/unlimited_search.t b/rt/t/web/unlimited_search.t index d98baaac0..988a2918d 100644 --- a/rt/t/web/unlimited_search.t +++ b/rt/t/web/unlimited_search.t @@ -2,12 +2,12 @@ use strict; -use RT::Test tests => 8; -RT::Test->started_ok; +use RT::Test tests => 85; +my ($baseurl, $agent) = RT::Test->started_ok; -my $ticket = RT::Ticket->new($RT::SystemUser); +my $ticket = RT::Ticket->new(RT->SystemUser); for ( 1 .. 75 ) { - $ticket->Create( + ok $ticket->Create( Subject => 'Ticket ' . $_, Queue => 'General', Owner => 'root', @@ -15,7 +15,6 @@ for ( 1 .. 75 ) { ); } -my $agent = RT::Test::Web->new; ok $agent->login('root', 'password'), 'logged in as root'; $agent->get_ok('/Search/Build.html'); @@ -27,7 +26,7 @@ $agent->form_name('BuildQuery'); $agent->field('RowsPerPage', '0'); $agent->submit('DoSearch'); $agent->follow_link_ok({text=>'Show Results'}); -$agent->content_like(qr/Ticket 75/); +$agent->content_contains("Ticket 75"); $agent->follow_link_ok({text=>'New Search'}); $agent->form_name('BuildQuery'); @@ -38,4 +37,4 @@ $agent->form_name('BuildQuery'); $agent->field('RowsPerPage', '50'); $agent->submit('DoSearch'); $agent->follow_link_ok({text=>'Bulk Update'}); -$agent->content_unlike(qr/Ticket 51/); +$agent->content_lacks("Ticket 51"); diff --git a/rt/t/web/user_update.t b/rt/t/web/user_update.t new file mode 100644 index 000000000..dc908fc10 --- /dev/null +++ b/rt/t/web/user_update.t @@ -0,0 +1,32 @@ +#!/usr/bin/perl +use strict; +use warnings; +use utf8; +use RT::Test tests => 9; + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login(), 'logged in' ); + +$m->follow_link_ok({text => 'About me'}); +$m->form_with_fields('Lang'); +$m->field(Lang => 'ja'); +$m->submit; + +$m->text_contains("Lang changed from (no value) to 'ja'"); + +# we only changed one field, and it wasn't the default, so this feedback is +# spurious and annoying +$m->content_lacks("That is already the current value"); + +# change back to English +$m->form_with_fields('Lang'); +$m->field(Lang => 'en_us'); +$m->submit; + +# This message shows up in Japanese +# $m->text_contains("Lang changed from 'ja' to 'en_us'"); +$m->text_contains("Langは「'ja'」から「'en_us'」に変更されました"); + +# another spurious update +$m->content_lacks("That is already the current value"); + diff --git a/rt/t/web/walk.t b/rt/t/web/walk.t new file mode 100644 index 000000000..34fab1476 --- /dev/null +++ b/rt/t/web/walk.t @@ -0,0 +1,92 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use RT::Test; +use HTML::TreeBuilder; + +my ( $baseurl, $m ) = RT::Test->started_ok; + +ok( $m->login( 'root' => 'password' ), 'login as root' ); + +my %viewed = ( '/NoAuth/Logout.html' => 1 ); # in case logout + +my $user = RT::User->new($RT::SystemUser); +$user->Load('root'); +ok( $user->id, 'loaded root' ); + +my $queue = RT::Queue->new($RT::SystemUser); +$queue->Load('General'); +ok( $queue->id, 'loaded General queue' ); + +my $group = RT::Group->new($RT::SystemUser); +ok( $group->CreateUserDefinedGroup( Name => 'group_foo' ) ); +my $cf = RT::CustomField->new($RT::SystemUser); +ok( + $cf->Create( + Name => 'cf_foo', + Type => 'Freeform', + LookupType => 'RT::Queue-RT::Ticket', + ) +); +ok( $cf->id, 'created cf_foo' ); + +my $class = RT::Class->new($RT::SystemUser); +ok( $class->Create( Name => 'class_foo' ) ); +ok( $class->id, 'created class_foo' ); + +# to make search have results +my $open_ticket = RT::Test->create_ticket( + Subject => 'ticket_foo', + Queue => 1, +); + +my $resolved_ticket = RT::Test->create_ticket( + Subject => 'ticket_bar', + Status => 'resolved', + Queue => 1, +); + +my @links = ( + '/', + '/Admin/Users/Modify.html?id=' . $user->id, + '/Admin/Groups/Modify.html?id=' . $group->id, + '/Admin/Queues/Modify.html?id=' . $queue->id, + '/Admin/CustomFields/Modify.html?id=' . $cf->id, + '/Admin/Global/Scrip.html?id=1', + '/Admin/Global/Template.html?Template=1', + '/Admin/Articles/Classes/Modify.html?id=' . $class->id, + '/Search/Build.html?Query=id<10', + '/Ticket/Display.html?id=' . $open_ticket->id, + '/Ticket/Display.html?id=' . $resolved_ticket->id, +); + +for my $link (@links) { + test_page($m, $link); +} + +$m->get_ok('/NoAuth/Logout.html'); + +sub test_page { + my $m = shift; + my $link = shift; + $m->get_ok( $link, $link ); + $m->no_warnings_ok($link); + + my $tree = HTML::TreeBuilder->new(); + $tree->parse( $m->content ); + $tree->elementify; + my ($top_menu) = $tree->look_down( id => 'main-navigation' ); + my ($page_menu) = $tree->look_down( id => 'page-navigation' ); + + my (@links) = + grep { !$viewed{$_}++ && /^[^#]/ } + map { $_->attr('href') || () } ( $top_menu ? $top_menu->find('a') : () ), + ( $page_menu ? $page_menu->find('a') : () ); + + for my $link (@links) { + test_page($m, $link); + } +} + |