diff options
author | Ivan Kohler <ivan@freeside.biz> | 2015-07-09 22:18:55 -0700 |
---|---|---|
committer | Ivan Kohler <ivan@freeside.biz> | 2015-07-09 22:18:55 -0700 |
commit | 1c538bfabc2cd31f27067505f0c3d1a46cba6ef0 (patch) | |
tree | 96922ad4459eda1e649327fd391d60c58d454c53 /rt/t | |
parent | 4f5619288413a185e9933088d9dd8c5afbc55dfa (diff) |
RT 4.2.11, ticket#13852
Diffstat (limited to 'rt/t')
251 files changed, 11354 insertions, 2836 deletions
diff --git a/rt/t/00-mason-syntax.t b/rt/t/00-mason-syntax.t index ac0da0d58..e87840ac4 100644 --- a/rt/t/00-mason-syntax.t +++ b/rt/t/00-mason-syntax.t @@ -9,7 +9,7 @@ find( { no_chdir => 1, wanted => sub { return if /(?:\.(?:jpe?g|png|gif|rej)|\~)$/i; - return if m{/\.[^/]+\.swp$}; # vim swap files + return if m{/\.[^/]+\.sw[op]$}; # vim swap files return unless -f $_; local ($@); ok( eval { compile_file($_) }, "Compiled $File::Find::name ok: $@"); diff --git a/rt/t/99-policy.t b/rt/t/99-policy.t index 1980e342f..353e40c63 100644 --- a/rt/t/99-policy.t +++ b/rt/t/99-policy.t @@ -21,13 +21,14 @@ sub check { my %check = ( strict => 0, warnings => 0, + no_tabs => 0, shebang => 0, exec => 0, bps_tag => 0, @_, ); - if ($check{strict} or $check{warnings} or $check{shebang} or $check{bps_tag}) { + if ($check{strict} or $check{warnings} or $check{shebang} or $check{bps_tag} or $check{no_tabs}) { local $/; open my $fh, '<', $file or die $!; my $content = <$fh>; @@ -98,4 +99,21 @@ check( $_, exec => -1 ) for grep {m{^t/data/}} @files; check( $_, exec => -1, bps_tag => -1 ) + for grep {m{^etc/[^/]+$}} @files; + +check( $_, exec => -1, bps_tag => -1 ) for grep {m{^etc/upgrade/[^/]+/}} @files; + +check( $_, warnings => 1, strict => 1, compile_perl => 1, no_tabs => 1 ) + for grep {m{^etc/upgrade/.*/content$}} @files; + +check( $_, shebang => 1, exec => 1, warnings => 1, strict => 1, bps_tag => 1, no_tabs => 1 ) + for grep {m{^etc/upgrade/[^/]+$}} @files; + +check( $_, compile_perl => 1, exec => 1 ) + for grep{ -f $_} map {s/\.in$//; $_} grep {m{^etc/upgrade/[^/]+$}} @files; + +check( $_, exec => -1 ) + for grep {m{^(devel/)?docs/}} @files; + +done_testing; diff --git a/rt/t/api/attachment_filename.t b/rt/t/api/attachment_filename.t index 6bfc7072f..aa8acd2d9 100644 --- a/rt/t/api/attachment_filename.t +++ b/rt/t/api/attachment_filename.t @@ -10,18 +10,18 @@ my $mime = MIME::Entity->build( ); $mime->attach( - Path => 'share/html/NoAuth/images/bpslogo.png', + Path => 'share/static/images/bpslogo.png', Type => 'image/png', ); $mime->attach( - Path => 'share/html/NoAuth/images/bpslogo.png', + Path => 'share/static/images/bpslogo.png', Type => 'image/png', Filename => 'bpslogo.png', ); $mime->attach( - Path => 'share/html/NoAuth/images/bpslogo.png', + Path => 'share/static/images/bpslogo.png', Filename => 'images/bpslogo.png', Type => 'image/png', ); diff --git a/rt/t/api/cfsearch.t b/rt/t/api/cfsearch.t index 7a460ce2e..4df6e0af2 100644 --- a/rt/t/api/cfsearch.t +++ b/rt/t/api/cfsearch.t @@ -36,9 +36,9 @@ my $cfvalue1 = 'Foo'; { my ($id, $msg) = $u1->AddCustomFieldValue( - Field => $cfname, - Value => $cfvalue1, - RecordTransaction => 0 ); + Field => $cfname, + Value => $cfvalue1, + RecordTransaction => 0 ); ok( $id, "Adding CF value '$cfvalue1' - " . $msg ); } @@ -51,18 +51,18 @@ my $cfvalue1 = 'Foo'; { my ($id, $msg) = $u1->DeleteCustomFieldValue( - Field => $cfname, - Value => $cfvalue1, - RecordTransaction => 0 ); + Field => $cfname, + Value => $cfvalue1, + RecordTransaction => 0 ); ok( $id, "Deleting CF value - " . $msg ); } my $cfvalue2 = 'Bar'; { my ($id, $msg) = $u1->AddCustomFieldValue( - Field => $cfname, - Value => $cfvalue2, - RecordTransaction => 0 ); + Field => $cfname, + Value => $cfvalue2, + RecordTransaction => 0 ); ok( $id, "Adding second CF value '$cfvalue2' - " . $msg ); } @@ -92,9 +92,9 @@ sub QueryCFValue{ isa_ok( $users, 'RT::Users' ); $users->LimitCustomField( - CUSTOMFIELD => $cf_id, - OPERATOR => "=", - VALUE => $cf_value ); + CUSTOMFIELD => $cf_id, + OPERATOR => "=", + VALUE => $cf_value ); while ( my $filtered_user = $users->Next() ){ my $cf_values = $filtered_user->CustomFieldValues($cf->id); diff --git a/rt/t/api/config.t b/rt/t/api/config.t index 62b77dffa..b87531139 100644 --- a/rt/t/api/config.t +++ b/rt/t/api/config.t @@ -1,7 +1,7 @@ use strict; use warnings; use RT; -use RT::Test nodb => 1, tests => 11; +use RT::Test nodb => 1, tests => undef; use Test::Warn; ok( @@ -39,5 +39,11 @@ my @encodings = qw(utf-8-strict iso-8859-1 ascii); warning_is {RT::Config->PostLoadCheck} "Unknown encoding 'foo' in \@EmailInputEncodings option", 'Correct warning for encoding foo'; +RT::Config->Set( WebDefaultStylesheet => 'non-existent-skin-name' ); +warning_like {RT::Config->PostLoadCheck} qr{rudder}, + 'Correct warning for default stylesheet'; + my @canonical_encodings = RT::Config->Get('EmailInputEncodings'); is_deeply(\@encodings, \@canonical_encodings, 'Got correct encoding list'); + +done_testing;
\ No newline at end of file diff --git a/rt/t/api/cron.t b/rt/t/api/cron.t index 6bd992df3..b16adde82 100644 --- a/rt/t/api/cron.t +++ b/rt/t/api/cron.t @@ -24,10 +24,10 @@ 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, - ); + Name => 'recordtest', + Description => 'testing Record actions', + Content => $template_content, + ); # Create a queue and some tickets. @@ -36,17 +36,17 @@ my $queue_obj = RT::Queue->new($CurrentUser); 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, - ); +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' - ); + Requestor => ['root@localhost'], + Subject => 'hurdy gurdy' + ); ok($id, 'record test ticket creation 2'); @@ -58,7 +58,7 @@ 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; diff --git a/rt/t/api/customfield.t b/rt/t/api/customfield.t index 6be50bb3a..df8f66df2 100644 --- a/rt/t/api/customfield.t +++ b/rt/t/api/customfield.t @@ -2,72 +2,393 @@ use strict; use warnings; use RT; -use RT::Test nodata => 1, tests => 29; +use RT::Test tests => undef; use Test::Warn; +use_ok('RT::CustomField'); -{ +my $queue = RT::Queue->new( RT->SystemUser ); +$queue->Load( "General" ); +ok( $queue->id, "found the General queue" ); -use_ok('RT::CustomField'); -ok(my $cf = RT::CustomField->new(RT->SystemUser)); -ok(my ($id, $msg)= $cf->Create( Name => 'TestingCF', - Queue => '0', - SortOrder => '1', - Description => 'A Testing custom field', - Type=> 'SelectSingle'), 'Created a global CustomField'); -isnt($id , 0, 'Global custom field correctly created'); -ok ($cf->SingleValue); -is($cf->Type, 'Select'); -is($cf->MaxValues, 1); - -(my $val, $msg) = $cf->SetMaxValues('0'); -ok($val, $msg); -is($cf->Type, 'Select'); -is($cf->MaxValues, 0); -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_id, $bad_msg)= $cf->Create( Name => 'TestingCF-bad', - Queue => '0', - SortOrder => '1', - Description => 'A Testing custom field with a bogus Type', - Type=> 'SelectSingleton'), 'Created a global CustomField with a bogus type'); -is($bad_id , 0, 'Global custom field correctly decided to not create a cf with a bogus type '); - - -} - -{ - -ok(my $cf = RT::CustomField->new(RT->SystemUser)); +my $cf = RT::CustomField->new(RT->SystemUser); +ok($cf, "Have a CustomField object"); + +# Use the old Queue field to set up a ticket CF +my ($ok, $msg) = $cf->Create( + Name => 'TestingCF', + Queue => '0', + Description => 'A Testing custom field', + Type => 'SelectSingle' +); +ok($ok, 'Global custom field correctly created'); +is($cf->Type, 'Select', "Is a select CF"); +ok($cf->SingleValue, "Also a single-value CF"); +is($cf->MaxValues, 1, "...meaning only one value, max"); + +($ok, $msg) = $cf->SetMaxValues('0'); +ok($ok, "Set to infinite values: $msg"); +is($cf->Type, 'Select', "Still a select CF"); +ok( ! $cf->SingleValue, "No longer single-value" ); +is($cf->MaxValues, 0, "...meaning no maximum values"); + +# Test our sanity checking of CF types +($ok, $msg) = $cf->SetType('BogusType'); +ok( ! $ok, "Unable to set a custom field's type to a bogus type: $msg"); + +$cf = RT::CustomField->new(RT->SystemUser); +($ok, $msg) = $cf->Create( + Name => 'TestingCF-bad', + Queue => '0', + SortOrder => '1', + Description => 'A Testing custom field with a bogus Type', + Type=> 'SelectSingleton' +); +ok( ! $ok, "Correctly could not create with bogus type: $msg"); + + +# Deprecated types +warning_like { + ok($cf->ValidateType('SelectSingle'), "ValidateType accepts SelectSingle"); +} qr/deprecated/, "...but warns of deprecation"; + +warning_like { + ok($cf->ValidateType('SelectMultiple'), "ValidateType accepts SelectMultiple"); +} qr/deprecated/, "...but warns of deprecation"; + +warning_like { + ok( ! $cf->ValidateType('SelectFooMultiple'), "ValidateType does not accept SelectFooMultiple"); +} qr/deprecated/, "...and also warns of deprecation"; + + +# Test adding and removing CFVs $cf->Load(1); -is($cf->Id , 1); -ok(my ($val,$msg) = $cf->AddValue(Name => 'foo' , Description => 'TestCFValue', SortOrder => '6')); -isnt($val , 0); -ok (my ($delval, $delmsg) = $cf->DeleteValue($val)); -ok ($delval,"Deleting a cf value: $delmsg"); +($ok, $msg) = $cf->AddValue(Name => 'foo' , Description => 'TestCFValue', SortOrder => '6'); +ok($ok, "Added a new value to the select options"); +($ok, $msg) = $cf->DeleteValue($ok); +ok($ok, "Deleting it again"); + + +# Loading, and context objects +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName( Name => "TestingCF" ); +ok($cf->id, "Load finds it, given just a name" ); +ok( ! $cf->ContextObject, "Did not get a context object"); + +# Old Queue => form should find the global, gain no context object +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 0); +ok($cf->id, "Load finds it, given a Name and Queue => 0" ); +ok( ! $cf->ContextObject, 'Context object not set when queue is 0'); + +# We don't default to also searching global -- but do pick up a contextobject +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 1); +ok( ! $cf->id, "Load does not finds it, given a Name and Queue => 1" ); +ok($cf->ContextObject->id, 'Context object is now set'); +# If we IncludeGlobal, we find it +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 1, IncludeGlobal => 1 ); +ok($cf->id, "Load now finds it, given a Name and Queue => 1 and IncludeGlobal" ); +ok($cf->ContextObject->id, 'Context object is also set'); -} +# The explicit LookupType works +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType ); +ok($cf->id, "Load now finds it, given a Name and LookupType" ); +ok( ! $cf->ContextObject, 'No context object gained'); -{ +# The explicit LookupType, ObjectId, and IncludeGlobal -- what most folks want +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType, + ObjectId => 1, IncludeGlobal => 1 ); +ok($cf->id, "Load now finds it, given a Name, LookupType, ObjectId, IncludeGlobal" ); +ok($cf->ContextObject->id, 'And gains a context obj'); -ok(my $cf = RT::CustomField->new(RT->SystemUser)); +# Look for a queue by name +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => "General" ); +ok( ! $cf->id, "No IncludeGlobal, so queue by name fails" ); +ok($cf->ContextObject->id, 'But gains a context object'); +# Look for a queue by name, include global +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => "General", IncludeGlobal => 1 ); +ok($cf->id, "By name, and queue name works with IncludeGlobal" ); +ok($cf->ContextObject->id, 'And gains a context object'); + + + +# A bogus Queue gets you no results, but a warning +$cf = RT::CustomField->new( RT->SystemUser ); warning_like { -ok($cf->ValidateType('SelectSingle')); -} qr/deprecated/; + $cf->LoadByName(Name => 'TestingCF', Queue => "Bogus" ); + ok( ! $cf->id, "With a bogus queue name gets no results" ); + ok( ! $cf->ContextObject, 'And also no context object'); +} qr/Failed to load RT::Queue 'Bogus'/, "Generates a warning"; +# Ditto by number which is bogus +$cf = RT::CustomField->new( RT->SystemUser ); warning_like { -ok($cf->ValidateType('SelectMultiple')); -} qr/deprecated/; + $cf->LoadByName(Name => 'TestingCF', Queue => "9000" ); + ok( ! $cf->id, "With a bogus queue number gets no results" ); + ok( ! $cf->ContextObject, 'And also no context object'); +} qr/Failed to load RT::Queue '9000'/, "Generates a warning"; +# But if they also wanted global results, we might have an answer +$cf = RT::CustomField->new( RT->SystemUser ); warning_like { -ok(!$cf->ValidateType('SelectFooMultiple')); -} qr/deprecated/; + $cf->LoadByName(Name => 'TestingCF', Queue => "9000", IncludeGlobal => 1 ); + ok($cf->id, "Bogus queue but IncludeGlobal founds it" ); + ok( ! $cf->ContextObject, 'But no context object'); +} qr/Failed to load RT::Queue '9000'/, "And generates a warning"; + + +# Make it only apply to one queue +$cf->Load(1); +my $ocf = RT::ObjectCustomField->new( RT->SystemUser ); +( $ok, $msg ) = $ocf->LoadByCols( CustomField => $cf->id, ObjectId => 0 ); +ok( $ok, "Found global application of CF" ); +( $ok, $msg ) = $ocf->Delete; +ok( $ok, "...and deleted it"); +( $ok, $msg ) = $ocf->Add( CustomField => $cf->id, ObjectId => 1 ); +ok($ok, "Applied to just queue 1" ); + +# Looking for it globally with Queue => 0 should fail, gain no context object +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 0); +ok( ! $cf->id, "Load fails to find, given a Name and Queue => 0" ); +ok( ! $cf->ContextObject, 'Context object not set when queue is 0'); + +# Looking it up by Queue => 1 works fine, and gets context object +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 1); +ok($cf->id, "Load does finds it, given a Name and Queue => 1" ); +ok($cf->ContextObject->id, 'Context object is now set'); + +# Also find it with IncludeGlobal +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 1, IncludeGlobal => 1 ); +ok($cf->id, "Load also finds it, given a Name and Queue => 1 and IncludeGlobal" ); +ok($cf->ContextObject->id, 'Context object is also set'); + +# The explicit LookupType works +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType ); +ok($cf->id, "Load also finds it, given a Name and LookupType" ); +ok( ! $cf->ContextObject, 'But no context object gained'); + +# Explicit LookupType, ObjectId works +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType, + ObjectId => 1 ); +ok($cf->id, "Load still finds it, given a Name, LookupType, ObjectId" ); +ok($cf->ContextObject->id, 'And gains a context obj'); + +# Explicit LookupType, ObjectId works +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType, + ObjectId => 1, IncludeGlobal => 1 ); +ok($cf->id, "Load also finds it, given a Name, LookupType, ObjectId, and IncludeGlobal" ); +ok($cf->ContextObject->id, 'And gains a context obj'); + +# Look for a queue by name +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => "General" ); +ok($cf->id, "Finds it by queue name" ); +ok($cf->ContextObject->id, 'But gains a context object'); + +# Look for a queue by name, include global +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => "General", IncludeGlobal => 1 ); +ok($cf->id, "By name, and queue name works with IncludeGlobal" ); +ok($cf->ContextObject->id, 'And gains a context object'); + + + + +# Change the lookup type to be a _queue_ CF +($ok, $msg) = $cf->SetLookupType( RT::Queue->CustomFieldLookupType ); +ok($ok, "Changed CF type to be a CF on queues" ); +$ocf = RT::ObjectCustomField->new( RT->SystemUser ); +( $ok, $msg ) = $ocf->Add( CustomField => $cf->id, ObjectId => 0 ); +ok($ok, "Applied globally" ); + +# Just looking by name gets you CFs of any type +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF'); +ok($cf->id, "Find the CF by name, with no queue" ); + +# Queue => 0 means "ticket CF", so doesn't find it +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 0); +ok( ! $cf->id, "Wrong lookup type to find with Queue => 0" ); + +# Queue => 1 and IncludeGlobal also doesn't find it +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 0, IncludeGlobal => 1); +ok( ! $cf->id, "Also doesn't find with Queue => 0 and IncludeGlobal" ); + +# Find it with the right LookupType +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType ); +ok($cf->id, "Found for the right lookup type" ); + +# Found globally +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType, ObjectId => 0 ); +ok($cf->id, "Found for the right lookup type and ObjectId 0" ); + +# Also works with Queue instead of ObjectId +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType, Queue => 0 ); +ok($cf->id, "Found for the right lookup type and Queue 0" ); + +# Not found without IncludeGlobal +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType, ObjectId => 1 ); +ok( ! $cf->id, "Not found for ObjectId 1 and no IncludeGlobal" ); + +# Found with IncludeGlobal +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType, + ObjectId => 1, IncludeGlobal => 1 ); +ok($cf->id, "Found for ObjectId 1 and IncludeGlobal" ); + +# Found with IncludeGlobal and Queue instead of ObjectId +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType, + ObjectId => 1, IncludeGlobal => 1 ); +ok($cf->id, "Found for Queue 1 and IncludeGlobal" ); + + + +# Change the lookup type to be a _transaction_ CF +($ok, $msg) = $cf->SetLookupType( RT::Transaction->CustomFieldLookupType ); +ok($ok, "Changed CF type to be a CF on transactions" ); +$ocf = RT::ObjectCustomField->new( RT->SystemUser ); +( $ok, $msg ) = $ocf->Add( CustomField => $cf->id, ObjectId => 0 ); +ok($ok, "Applied globally" ); + +# Just looking by name gets you CFs of any type +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF'); +ok($cf->id, "Find the CF by name, with no queue" ); + +# Queue => 0 means "ticket CF", so doesn't find it +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 0); +ok( ! $cf->id, "Wrong lookup type to find with Queue => 0" ); + +# Queue => 1 and IncludeGlobal also doesn't find it +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 0, IncludeGlobal => 1); +ok( ! $cf->id, "Also doesn't find with Queue => 0 and IncludeGlobal" ); + + +# Change the lookup type to be a _user_ CF +$cf->Load(1); +($ok, $msg) = $cf->SetLookupType( RT::User->CustomFieldLookupType ); +ok($ok, "Changed CF type to be a CF on users" ); +$ocf = RT::ObjectCustomField->new( RT->SystemUser ); +( $ok, $msg ) = $ocf->Add( CustomField => $cf->id, ObjectId => 0 ); +ok($ok, "Applied globally" ); + +# Just looking by name gets you CFs of any type +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF'); +ok($cf->id, "Find the CF by name, with no queue" ); + +# Queue => 0 means "ticket CF", so doesn't find it +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 0); +ok( ! $cf->id, "Wrong lookup type to find with Queue => 0" ); + +# Queue => 1 and IncludeGlobal also doesn't find it +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 0, IncludeGlobal => 1); +ok( ! $cf->id, "Also doesn't find with Queue => 0 and IncludeGlobal" ); + +# But RT::User->CustomFieldLookupType does +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::User->CustomFieldLookupType ); +ok($cf->id, "User lookuptype does" ); + +# Also with an explicit global +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::User->CustomFieldLookupType, ObjectId => 0 ); +ok($cf->id, "Also with user CF and explicit global" ); + + + +# Add a second, queue-specific CF to test load order +$cf->Load(1); +($ok, $msg) = $cf->SetLookupType( RT::Ticket->CustomFieldLookupType ); +ok($ok, "Changed CF type back to be a CF on tickets" ); +$ocf = RT::ObjectCustomField->new( RT->SystemUser ); +( $ok, $msg ) = $ocf->Add( CustomField => $cf->id, ObjectId => 0 ); +ok($ok, "Applied globally" ); +($ok, $msg) = $cf->SetDescription( "Global CF" ); +ok($ok, "Changed CF type back to be a CF on tickets" ); + +($ok, $msg) = $cf->Create( + Name => 'TestingCF', + Queue => '1', + Description => 'Queue-specific CF', + Type => 'SelectSingle' +); +ok($ok, "Created second CF successfully"); + +# If passed just a name, you get the first by id +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF' ); +like($cf->Description, qr/Global/, "Gets the first (global) one if just loading by name" ); + +# Ditto if also limited to lookuptype +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType ); +like($cf->Description, qr/Global/, "Same, if one adds a LookupType" ); + +# Gets the global with Queue => 0 +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 0 ); +like($cf->Description, qr/Global/, "Specify Queue => 0 and get global" ); + +# Gets the queue with Queue => 1 +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 1 ); +like($cf->Description, qr/Queue/, "Specify Queue => 1 and get the queue" ); + +# Gets the queue with Queue => 1 and IncludeGlobal +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 1, IncludeGlobal => 1 ); +like($cf->Description, qr/Queue/, "Specify Queue => 1 and IncludeGlobal and get the queue" ); + + +# Disable one of them +($ok, $msg) = $cf->SetDisabled(1); +ok($ok, "Disabled the Queue-specific one"); + +# With just a name, prefers the non-disabled +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF' ); +like($cf->Description, qr/Global/, "Prefers non-disabled CFs" ); + +# Still finds the queue one, if asked +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 1 ); +like($cf->Description, qr/Queue/, "Still loads the disabled queue CF" ); + +# Prefers the global one if IncludeGlobal +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 1, IncludeGlobal => 1 ); +like($cf->Description, qr/Global/, "Prefers the global one with IncludeGlobal" ); +# IncludeDisabled allows filtering out the disabled one +$cf = RT::CustomField->new( RT->SystemUser ); +$cf->LoadByName(Name => 'TestingCF', Queue => 1, IncludeDisabled => 0 ); +ok( ! $cf->id, "Doesn't find it if IncludeDisabled => 0" ); -} +done_testing; diff --git a/rt/t/api/date.t b/rt/t/api/date.t index 22c6f1b58..dd22943c1 100644 --- a/rt/t/api/date.t +++ b/rt/t/api/date.t @@ -4,7 +4,7 @@ use DateTime; use warnings; use strict; -use RT::Test tests => 175; +use RT::Test tests => undef; use RT::User; use Test::Warn; @@ -57,7 +57,7 @@ my $current_user; is($date->Timezone('user'), 'Europe/Moscow', "in user context still returns user's timezone"); - + $current_user->UserObj->__Set( Field => 'Timezone', Value => ''); is_empty($current_user->UserObj->Timezone, "successfuly changed user's timezone"); @@ -83,6 +83,7 @@ my $current_user; { my $date = RT::Date->new(RT->SystemUser); + is($date->IsSet,0, "new date isn't set"); is($date->Unix, 0, "new date returns 0 in Unix format"); is($date->Get, '1970-01-01 00:00:00', "default is ISO format"); warning_like { @@ -207,6 +208,7 @@ my $current_user; $current_user->UserObj->__Set( Field => 'Timezone', Value => 'Europe/Moscow'); my $date = RT::Date->new( $current_user ); $date->Set( Format => 'ISO', Timezone => 'utc', Value => '2005-01-01 15:10:00' ); + is($date->IsSet,1,"Date has been set"); is($date->ISO( Timezone => 'user' ), '2005-01-01 18:10:00', "ISO"); is($date->W3CDTF( Timezone => 'user' ), '2005-01-01T18:10:00+03:00', "W3C DTF"); is($date->RFC2822( Timezone => 'user' ), 'Sat, 01 Jan 2005 18:10:00 +0300', "RFC2822"); @@ -239,7 +241,7 @@ warning_like { # bad format my $date = RT::Date->new(RT->SystemUser); $date->Set( Format => 'bad' ); - is($date->Unix, 0, "bad format"); + ok(!$date->IsSet, "bad format"); } qr{Unknown Date format: bad}; @@ -248,13 +250,26 @@ warning_like $date->Unix(1); is($date->ISO, '1970-01-01 00:00:01', "correct value"); - foreach (undef, 0, ''){ + foreach (undef, 0, '', -5){ $date->Unix(1); is($date->ISO, '1970-01-01 00:00:01', "correct value"); + is($date->IsSet,1,"Date has been set to a value"); $date->Set(Format => 'unix', Value => $_); is($date->ISO, '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT due to wrong call"); is($date->Unix, 0, "unix is 0 => unset"); + is($date->IsSet,0,"Date has been unset"); + } + + foreach (undef, 0, '', -5){ + $date->Unix(1); + is($date->ISO, '1970-01-01 00:00:01', "correct value"); + is($date->IsSet,1,"Date has been set to a value"); + + $date->Unix($_); + is($date->ISO, '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT due to wrong call"); + is($date->Unix, 0, "unix is 0 => unset"); + is($date->IsSet,0,"Date has been unset"); } } @@ -265,7 +280,7 @@ my $year = (localtime(time))[5] + 1900; warning_like { $date->Set(Format => 'ISO', Value => 'weird date'); } qr/Couldn't parse date 'weird date' as a ISO format/; - is($date->Unix, 0, "date was wrong => unix == 0"); + ok(!$date->IsSet, "date was wrong => unix == 0"); # XXX: ISO format has more feature than we suport # http://www.cl.cam.ac.uk/~mgk25/iso-time.html @@ -294,14 +309,18 @@ my $year = (localtime(time))[5] + 1900; $date->Set(Format => 'ISO', Value => '112815:10:00'); is($date->ISO, $year .'-11-28 15:10:00', "DDMMhh:mm:ss"); - $date->Set(Format => 'ISO', Value => '2005-13-28 15:10:00'); - is($date->Unix, 0, "wrong month value"); + warning_like { + $date->Set(Format => 'ISO', Value => '2005-13-28 15:10:00'); + } qr/Invalid date/; + ok(!$date->IsSet, "wrong month value"); - $date->Set(Format => 'ISO', Value => '2005-00-28 15:10:00'); - is($date->Unix, 0, "wrong month value"); + warning_like { + $date->Set(Format => 'ISO', Value => '2005-00-28 15:10:00'); + } qr/Invalid date/; + ok(!$date->IsSet, "wrong month value"); $date->Set(Format => 'ISO', Value => '1960-01-28 15:10:00'); - is($date->Unix, 0, "too old, we don't support"); + ok(!$date->IsSet, "too old, we don't support"); } { # set+datemanip format(Time::ParseDate) @@ -326,7 +345,7 @@ my $year = (localtime(time))[5] + 1900; warnings_like { $date->Set(Format => 'unknown', Value => 'weird date'); } qr{Couldn't parse date 'weird date' by Time::ParseDate}; - is($date->Unix, 0, "date was wrong"); + ok(!$date->IsSet, "date was wrong"); RT->Config->Set( Timezone => 'Europe/Moscow' ); $date->Set(Format => 'unknown', Value => '2005-11-28 15:10:00'); @@ -360,6 +379,34 @@ my $year = (localtime(time))[5] + 1900; is($date->ISO, '2005-11-28 15:10:00', "YYYY-DD-MM hh:mm:ss"); } +{ # 'tomorrow 10am' with TZ + $current_user->UserObj->__Set( Field => 'Timezone', Value => 'Europe/Moscow'); + + set_fixed_time("2012-06-14T15:10:00Z"); # 14th in UTC and Moscow + my $date = RT::Date->new( $current_user ); + $date->Set(Format => 'unknown', Value => 'tomorrow 10am'); + is($date->ISO, '2012-06-15 06:00:00', "YYYY-DD-MM hh:mm:ss"); + + set_fixed_time("2012-06-13T23:10:00Z"); # 13th in UTC and 14th in Moscow + $date = RT::Date->new( $current_user ); + $date->Set(Format => 'unknown', Value => 'tomorrow 10am'); + is($date->ISO, '2012-06-15 06:00:00', "YYYY-DD-MM hh:mm:ss"); + + $current_user->UserObj->__Set( Field => 'Timezone', Value => 'US/Hawaii'); + + set_fixed_time("2012-06-14T20:10:00Z"); # 14th in UTC and Hawaii + $date = RT::Date->new( $current_user ); + $date->Set(Format => 'unknown', Value => 'tomorrow 10am'); + is($date->ISO, '2012-06-15 20:00:00', "YYYY-DD-MM hh:mm:ss"); + + set_fixed_time("2012-06-15T05:10:00Z"); # 15th in UTC and 14th in Hawaii + $date = RT::Date->new( $current_user ); + $date->Set(Format => 'unknown', Value => 'tomorrow 10am'); + is($date->ISO, '2012-06-15 20:00:00', "YYYY-DD-MM hh:mm:ss"); + + restore_time(); +} + { # SetToMidnight my $date = RT::Date->new(RT->SystemUser); @@ -475,11 +522,11 @@ my $year = (localtime(time))[5] + 1900; { # DurationAsString my $date = RT::Date->new(RT->SystemUser); - is($date->DurationAsString(1), '1 sec', '1 sec'); - is($date->DurationAsString(59), '59 sec', '59 sec'); - is($date->DurationAsString(60), '1 min', '1 min'); - is($date->DurationAsString(60*119), '119 min', '119 min'); - is($date->DurationAsString(60*60*2-1), '120 min', '120 min'); + is($date->DurationAsString(1), '1 second', '1 sec'); + is($date->DurationAsString(59), '59 seconds', '59 sec'); + is($date->DurationAsString(60), '1 minute', '1 min'); + is($date->DurationAsString(60*119), '119 minutes', '119 min'); + is($date->DurationAsString(60*60*2-1), '120 minutes', '120 min'); is($date->DurationAsString(60*60*2), '2 hours', '2 hours'); is($date->DurationAsString(60*60*48-1), '48 hours', '48 hours'); is($date->DurationAsString(60*60*48), '2 days', '2 days'); @@ -488,9 +535,9 @@ my $year = (localtime(time))[5] + 1900; is($date->DurationAsString(60*60*24*7*8-1), '8 weeks', '8 weeks'); is($date->DurationAsString(60*60*24*61), '2 months', '2 months'); is($date->DurationAsString(60*60*24*365-1), '12 months', '12 months'); - is($date->DurationAsString(60*60*24*366), '1 years', '1 years'); + is($date->DurationAsString(60*60*24*366), '1 year', '1 year'); - is($date->DurationAsString(-1), '1 sec ago', '1 sec ago'); + is($date->DurationAsString(-1), '1 second ago', '1 sec ago'); } { # DiffAsString @@ -502,13 +549,13 @@ my $year = (localtime(time))[5] + 1900; $date->Unix(2); is($date->DiffAsString(-1), '', 'no diff, wrong input'); - is($date->DiffAsString(3), '1 sec ago', 'diff: 1 sec ago'); - is($date->DiffAsString(1), '1 sec', 'diff: 1 sec'); + is($date->DiffAsString(3), '1 second ago', 'diff: 1 sec ago'); + is($date->DiffAsString(1), '1 second', 'diff: 1 sec'); 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'); + is($date->DiffAsString($ndate), '1 second ago', 'diff: 1 sec ago'); } { # Diff @@ -523,7 +570,7 @@ my $year = (localtime(time))[5] + 1900; my $date = RT::Date->new(RT->SystemUser); $date->SetToNow; my $diff = $date->AgeAsString; - like($diff, qr/^(0 sec|[1-5] sec ago)$/, 'close enought'); + like($diff, qr/^(0 seconds|(1 second|[2-5] seconds) ago)$/, 'close enought'); } { # GetWeekday @@ -550,15 +597,16 @@ my $year = (localtime(time))[5] + 1900; # 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'" ); + ok( !$date->IsSet, "unix is 0 with Value => 0, Format => 'unknown'" ); $date->Set( Value => '', Format => 'unknown' ); - is( $date->Unix(), 0, "unix is 0 with Value => '', Format => 'unknown'" ); + ok( !$date->IsSet, "unix is 0 with Value => '', Format => 'unknown'" ); $date->Set( Value => ' ', Format => 'unknown' ); - is( $date->Unix(), 0, "unix is 0 with Value => ' ', Format => 'unknown'" ); + ok( !$date->IsSet, "unix is 0 with Value => ' ', Format => 'unknown'" ); } #TODO: AsString #TODO: RFC2822, W3CDTF with Timezones +done_testing; diff --git a/rt/t/api/db_indexes.t b/rt/t/api/db_indexes.t new file mode 100644 index 000000000..3c305d84d --- /dev/null +++ b/rt/t/api/db_indexes.t @@ -0,0 +1,165 @@ +use strict; +use warnings; +use Test::Warn; + +use RT::Test tests => undef; + +my $handle = $RT::Handle; +my $db_type = RT->Config->Get('DatabaseType'); + +# Pg,Oracle needs DBA +RT::Test::__reconnect_rt('as dba'); +ok( $handle->dbh->do("ALTER SESSION SET CURRENT_SCHEMA=". RT->Config->Get('DatabaseUser') ) ) + if $db_type eq 'Oracle'; + +note "test handle->Indexes method"; +{ + my %indexes = $handle->Indexes; + ok grep $_ eq 'tickets1', @{ $indexes{'tickets'} }; + ok grep $_ eq 'tickets2', @{ $indexes{'tickets'} }; + ok grep $_ eq 'users1', @{ $indexes{'users'} }; + ok grep $_ eq 'users4', @{ $indexes{'users'} }; +} + +note "test handle->DropIndex method"; +{ + my ($status, $msg) = $handle->DropIndex( Table => 'Tickets', Name => 'Tickets1' ); + ok $status, $msg; + + my %indexes = $handle->Indexes; + ok !grep $_ eq 'tickets1', @{ $indexes{'tickets'} }; + + ($status, $msg) = $handle->DropIndex( Table => 'Tickets', Name => 'Tickets1' ); + ok !$status, $msg; +} + +note "test handle->DropIndexIfExists method"; +{ + my ($status, $msg) = $handle->DropIndexIfExists( Table => 'Tickets', Name => 'Tickets2' ); + ok $status, $msg; + + my %indexes = $handle->Indexes; + ok !grep $_ eq 'tickets2', @{ $indexes{'tickets'} }; + + ($status, $msg) = $handle->DropIndexIfExists( Table => 'Tickets', Name => 'Tickets2' ); + ok $status, $msg; +} + +note "test handle->IndexInfo method"; +{ + if ($db_type ne 'Oracle' && $db_type ne 'mysql') { + my %res = $handle->IndexInfo( Table => 'Attachments', Name => 'Attachments1' ); + is_deeply( + \%res, + { + Table => 'attachments', Name => 'attachments1', + Unique => 0, Functional => 0, + Columns => ['parent'] + } + ); + } else { + my %res = $handle->IndexInfo( Table => 'Attachments', Name => 'Attachments2' ); + is_deeply( + \%res, + { + Table => 'attachments', Name => 'attachments2', + Unique => 0, Functional => 0, + Columns => ['transactionid'] + } + ); + } + + my %res = $handle->IndexInfo( Table => 'GroupMembers', Name => 'GroupMembers1' ); + is_deeply( + \%res, + { + Table => 'groupmembers', Name => 'groupmembers1', + Unique => 1, Functional => 0, + Columns => ['groupid', 'memberid'] + } + ); + + if ( $db_type eq 'Pg' || $db_type eq 'Oracle' ) { + %res = $handle->IndexInfo( Table => 'Queues', Name => 'Queues1' ); + is_deeply( + \%res, + { + Table => 'queues', Name => 'queues1', + Unique => 1, Functional => 1, + Columns => ['name'], + CaseInsensitive => { name => 1 }, + } + ); + } +} + +note "test ->CreateIndex and ->IndexesThatBeginWith methods"; +{ + { + my ($name, $msg) = $handle->CreateIndex( + Table => 'Users', Name => 'test_users1', + Columns => ['Organization'], + ); + ok $name, $msg; + } + { + my ($name, $msg) = $handle->CreateIndex( + Table => 'Users', Name => 'test_users2', + Columns => ['Organization', 'Name'], + ); + ok $name, $msg; + } + + my @list = $handle->IndexesThatBeginWith( Table => 'Users', Columns => ['Organization'] ); + is_deeply([sort map $_->{Name}, @list], [qw(test_users1 test_users2)]); + + my ($status, $msg) = $handle->DropIndex( Table => 'Users', Name => 'test_users1' ); + ok $status, $msg; + ($status, $msg) = $handle->DropIndex( Table => 'Users', Name => 'test_users2' ); + ok $status, $msg; +} + +note "Test some cases sensitivity aspects"; +{ + { + my %res = $handle->IndexInfo( Table => 'groupmembers', Name => 'groupmembers1' ); + is_deeply( + \%res, + { + Table => 'groupmembers', Name => 'groupmembers1', + Unique => 1, Functional => 0, + Columns => ['groupid', 'memberid'] + } + ); + } + + { + my ($status, $msg) = $handle->DropIndex( Table => 'groupmembers', Name => 'groupmembers1' ); + ok $status, $msg; + + my %indexes = $handle->Indexes; + ok !grep $_ eq 'groupmembers1', @{ $indexes{'groupmembers'} }; + } + + { + my ($name, $msg) = $handle->CreateIndex( + Table => 'groupmembers', Name => 'groupmembers1', + Unique => 1, + Columns => ['groupid', 'memberid'] + ); + ok $name, $msg; + + my %indexes = $handle->Indexes; + ok grep $_ eq 'groupmembers1', @{ $indexes{'groupmembers'} }; + } + + { + my ($status, $msg) = $handle->DropIndexIfExists( Table => 'groupmembers', Name => 'groupmembers1' ); + ok $status, $msg; + + my %indexes = $handle->Indexes; + ok !grep $_ eq 'groupmembers1', @{ $indexes{'groupmembers'} }; + } +} + +done_testing(); diff --git a/rt/t/api/group-rights.t b/rt/t/api/group-rights.t index 0494c286e..4f5f03d0d 100644 --- a/rt/t/api/group-rights.t +++ b/rt/t/api/group-rights.t @@ -2,7 +2,7 @@ use strict; use warnings; use RT::Test nodata => 1, tests => 114; -RT::Group->AddRights( +RT::Group->AddRight( General => 'RTxGroupRight' => 'Just a right for testing rights', ); diff --git a/rt/t/api/group.t b/rt/t/api/group.t index d55fc5c4a..f82dfc57e 100644 --- a/rt/t/api/group.t +++ b/rt/t/api/group.t @@ -96,7 +96,7 @@ is($group_3->HasMemberRecursively($principal_2), undef, "group 3 has member 2 re 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->id , 4, "user 4 is the fourth principal"); is($u->PrincipalObj->PrincipalType , 'Group' , "Principal 4 is a group"); diff --git a/rt/t/api/groups.t b/rt/t/api/groups.t index d2dc126dc..c2e7fc583 100644 --- a/rt/t/api/groups.t +++ b/rt/t/api/groups.t @@ -2,7 +2,7 @@ use strict; use warnings; use RT::Test nodata => 1, tests => 27; -RT::Group->AddRights( +RT::Group->AddRight( General => 'RTxGroupRight' => 'Just a right for testing rights', ); @@ -49,8 +49,7 @@ 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); -$global_admin_cc->LoadSystemRoleGroup('AdminCc'); +my $global_admin_cc = RT->System->RoleGroup( 'AdminCc' ); ok($global_admin_cc->id, "Found the global admincc group"); my $groups = RT::Groups->new(RT->SystemUser); $groups->WithRight(Right => 'OwnTicket', Object => $q); diff --git a/rt/t/api/i18n_guess.t b/rt/t/api/i18n_guess.t index a64b2952c..0a99011f2 100644 --- a/rt/t/api/i18n_guess.t +++ b/rt/t/api/i18n_guess.t @@ -4,8 +4,8 @@ use warnings; use RT::Test tests => 16; -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 } }; +use constant HAS_ENCODE_GUESS => Encode::Guess->require; +use constant HAS_ENCODE_DETECT => Encode::Detect::Detector->require; 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}"; diff --git a/rt/t/api/i18n_mime_encoding.t b/rt/t/api/i18n_mime_encoding.t new file mode 100644 index 000000000..5ad532ed2 --- /dev/null +++ b/rt/t/api/i18n_mime_encoding.t @@ -0,0 +1,32 @@ +use warnings; +use strict; + +use RT::Test nodata => 1, tests => undef; +use RT::I18N; +use Test::Warn; + +diag "normal mime encoding conversion: utf8 => iso-8859-1"; +{ + my $mime = MIME::Entity->build( + Type => 'text/plain; charset=utf-8', + Data => ['À中文'], + ); + + warning_like { + RT::I18N::SetMIMEEntityToEncoding( $mime, 'iso-8859-1', ); + } + [ qr/does not map to iso-8859-1/ ], 'got one "not map" error'; + is( $mime->stringify_body, 'À中文', 'body is not changed' ); + is( $mime->head->mime_attr('Content-Type'), 'application/octet-stream' ); +} + +diag "mime encoding conversion: utf8 => iso-8859-1"; +{ + my $mime = MIME::Entity->build( + Type => 'text/plain; charset=utf-8', + Data => ['À中文'], + ); + is( $mime->stringify_body, 'À中文', 'body is not changed' ); +} + +done_testing; diff --git a/rt/t/api/initialdata.t b/rt/t/api/initialdata.t new file mode 100644 index 000000000..5856528a3 --- /dev/null +++ b/rt/t/api/initialdata.t @@ -0,0 +1,16 @@ +use strict; +use warnings; + +use RT::Test tests => 'no_declare'; + +# This test script processes the sample initialdata file in +# ../data/initialdata/initialdata +# To add initialdata tests, add the data to the initialdata file and it +# will be processed by this script. + +my $initialdata = RT::Test::get_relocatable_file("initialdata" => "..", "data", "initialdata"); +my ($rv, $msg) = RT->DatabaseHandle->InsertData( $initialdata, undef, disconnect_after => 0 ); +ok($rv, "Inserted test data from $initialdata") + or diag "Error: $msg"; + +done_testing();
\ No newline at end of file diff --git a/rt/t/api/link.t b/rt/t/api/link.t index a9e54a716..3066388f2 100644 --- a/rt/t/api/link.t +++ b/rt/t/api/link.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test nodata => 1, tests => 84; +use RT::Test nodata => 1, tests => 83; use RT::Test::Web; use Test::Warn; @@ -35,9 +35,7 @@ ok $cid, 'created a ticket #'. $cid or diag "error: $msg"; my ($status, $msg); clean_links(); - warning_like { - ($status, $msg) = $parent->AddLink; - } qr/Base or Target must be specified/, "warned about linking a ticket to itself"; + ($status, $msg) = $parent->AddLink; ok(!$status, "didn't create a link: $msg"); warning_like { diff --git a/rt/t/api/password-types.t b/rt/t/api/password-types.t index 4cb634248..9eeded499 100644 --- a/rt/t/api/password-types.t +++ b/rt/t/api/password-types.t @@ -4,17 +4,29 @@ use warnings; use RT::Test; use Digest::MD5; -my $default = "sha512"; +my $default = "bcrypt"; my $root = RT::User->new(RT->SystemUser); $root->Load("root"); -# Salted SHA-512 (default) +# bcrypt (default) my $old = $root->__Value("Password"); like($old, qr/^\!$default\!/, "Stored as salted $default"); ok($root->IsPassword("password")); is($root->__Value("Password"), $old, "Unchanged after password check"); +# bcrypt (smaller number of rounds) +my $salt = Crypt::Eksblowfish::Bcrypt::en_base64("a"x16); +$root->_Set( Field => "Password", Value => RT::User->_GeneratePassword_bcrypt("smaller", 6, $salt) ); +like($root->__Value("Password"), qr/^\!$default\!06\!/, "Stored with a smaller number of rounds"); +ok($root->IsPassword("smaller"), "Smaller number of bcrypt rounds works"); +like($root->__Value("Password"), qr/^\!$default\!10\!/, "And is now upgraded to salted $default"); + +# Salted SHA-512, one round +$root->_Set( Field => "Password", Value => RT::User->_GeneratePassword_sha512("other", "salt") ); +ok($root->IsPassword("other"), "SHA-512 password works"); +like($root->__Value("Password"), qr/^\!$default\!/, "And is now upgraded to salted $default"); + # Crypt $root->_Set( Field => "Password", Value => crypt("something", "salt")); ok($root->IsPassword("something"), "crypt()ed password works"); diff --git a/rt/t/api/queue.t b/rt/t/api/queue.t index 07b8ed479..71efb4d39 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 nodata => 1, tests => 24; +use RT::Test nodata => 1, tests => undef; { @@ -58,8 +58,7 @@ ok(!$id, $val); 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($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Requestor')); +ok(my $group = $Queue->RoleGroup('Requestor')); ok ($group->Id, "Found the requestors object for this Queue"); { @@ -79,13 +78,12 @@ 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); -ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc')); +$group = $Queue->RoleGroup('Cc'); ok ($group->Id, "Found the cc object for this Queue"); -$group = RT::Group->new(RT->SystemUser); -ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'AdminCc')); +$group = $Queue->RoleGroup('AdminCc'); ok ($group->Id, "Found the AdminCc object for this Queue"); } +done_testing; diff --git a/rt/t/api/record.t b/rt/t/api/record.t index 4b6b0b89c..7abd41ca5 100644 --- a/rt/t/api/record.t +++ b/rt/t/api/record.t @@ -16,8 +16,8 @@ ok (require RT::Record); 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"); +is($ticket->RecordType, 'Ticket', "Ticket returns correct typestring"); +is($group->RecordType, 'Group', "Group returns correct typestring"); } diff --git a/rt/t/api/reminder-permissions.t b/rt/t/api/reminder-permissions.t new file mode 100644 index 000000000..8253d67af --- /dev/null +++ b/rt/t/api/reminder-permissions.t @@ -0,0 +1,49 @@ +use strict; +use warnings; +use RT::Test tests => 9; + +my $user_a = RT::Test->load_or_create_user( + Name => 'user_a', + Password => 'password', +); + +ok( $user_a && $user_a->id, 'created user_a' ); +ok( + RT::Test->add_rights( + { + Principal => $user_a, + Right => [qw/SeeQueue CreateTicket ShowTicket OwnTicket/] + }, + ), + 'add basic rights for user_a' +); + +my $ticket = RT::Test->create_ticket( + Subject => 'test reminder permission', + Queue => 'General', +); +ok( $ticket->id, 'created a ticket' ); +$ticket->CurrentUser($user_a); + +my ( $status, $msg ) = $ticket->Reminders->Add( + Subject => 'user a reminder', + Owner => $user_a->id, +); +ok( !$status, "couldn't create reminders without ModifyTicket: $msg" ); + +ok( + RT::Test->add_rights( + { + Principal => $user_a, + Right => [qw/ModifyTicket/] + }, + ), + 'add ModifyTicket right for user_a' +); + +( $status, $msg ) = $ticket->Reminders->Add( + Subject => 'user a reminder', + Owner => $user_a->id, +); +ok( $status, "created a reminder with ModifyTicket: $msg" ); + diff --git a/rt/t/api/rights.t b/rt/t/api/rights.t index 5cf3a0008..a6346a737 100644 --- a/rt/t/api/rights.t +++ b/rt/t/api/rights.t @@ -29,10 +29,8 @@ ok $user && $user->id, 'loaded or created user'; } { - my $group = RT::Group->new( RT->SystemUser ); - ok( $group->LoadQueueRoleGroup( Queue => $queue->id, Type=> 'Owner' ), - "load queue owners role group" - ); + my $group = $queue->RoleGroup( 'Owner' ); + ok( $group->Id, "load queue owners role group" ); my $ace = RT::ACE->new( RT->SystemUser ); my ($ace_id, $msg) = $group->PrincipalObj->GrantRight( Right => 'ReplyToTicket', Object => $queue @@ -84,10 +82,8 @@ my $ticket; { # Testing of EquivObjects - my $group = RT::Group->new( RT->SystemUser ); - ok( $group->LoadQueueRoleGroup( Queue => $queue->id, Type=> 'AdminCc' ), - "load queue AdminCc role group" - ); + my $group = $queue->RoleGroup( 'AdminCc' ); + ok( $group->Id, "load queue AdminCc role group" ); my $ace = RT::ACE->new( RT->SystemUser ); my ($ace_id, $msg) = $group->PrincipalObj->GrantRight( Right => 'ModifyTicket', Object => $queue diff --git a/rt/t/api/rtname.t b/rt/t/api/rtname.t index 8b7b54bd1..f2bffd559 100644 --- a/rt/t/api/rtname.t +++ b/rt/t/api/rtname.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test nodata => 1, tests => 9; +use RT::Test nodata => 1, tests => 12; use RT::Interface::Email; @@ -31,3 +31,8 @@ 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); +# Parens work fine +RT->Config->Set( EmailSubjectTagRegex => qr/(new|)(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/savedsearch.t b/rt/t/api/savedsearch.t index 0aa67eeda..2e924bf7b 100644 --- a/rt/t/api/savedsearch.t +++ b/rt/t/api/savedsearch.t @@ -13,9 +13,9 @@ use Test::Warn; my $searchuser = RT::User->new(RT->SystemUser); my ($ret, $msg) = $searchuser->Create(Name => 'searchuser'.$$, - Privileged => 1, - EmailAddress => "searchuser\@p$$.example.com", - RealName => 'Search user'); + 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'); @@ -26,9 +26,9 @@ my $ingroup = RT::Group->new(RT->SystemUser); $ingroup->CreateUserDefinedGroup(Name => 'searchgroup1'.$$); $ingroup->AddMember($searchuser->Id); $searchuser->PrincipalObj->GrantRight(Right => 'EditSavedSearches', - Object => $ingroup); + Object => $ingroup); $searchuser->PrincipalObj->GrantRight(Right => 'ShowSavedSearches', - Object => $ingroup); + Object => $ingroup); # This is the group whose searches searchuser should not be able to see. my $outgroup = RT::Group->new(RT->SystemUser); @@ -44,9 +44,9 @@ $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'); + Requestor => [ $searchuser->Name ], + Owner => $searchuser, + Subject => 'saved search test'); # Now start the search madness. @@ -67,41 +67,41 @@ my $format = '\' <b><a href="/Ticket/Display.html?id=__id__">__id__</a></b>/TI 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 - . "'"}); + 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 . "'"}); + 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'"}); + 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'"}); + 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. @@ -148,8 +148,8 @@ warning_like { isnt($loadedsearch4->Id, $othersearch->Id, "Did not load othersearch"); # Try to update an existing search. -$loadedsearch1->Update( SearchParams => {'Format' => $format, - 'Query' => "Queue = '" . $queue->Name . "'" } ); +$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"); @@ -162,8 +162,8 @@ like($mysearch->GetParameter('Query'), qr/Queue/, "other mysearch object updated my $genericsearch = RT::SavedSearch->new($curruser); $genericsearch->Save(Name => 'generic search', - Type => 'all', - SearchParams => {'Query' => "Queue = 'General'"}); + Type => 'all', + SearchParams => {'Query' => "Queue = 'General'"}); my $ticketsearches = RT::SavedSearches->new($curruser); $ticketsearches->LimitToPrivacy('RT::User-'.$curruser->Id, 'Ticket'); diff --git a/rt/t/api/scrip.t b/rt/t/api/scrip.t index eb543476b..d6019a082 100644 --- a/rt/t/api/scrip.t +++ b/rt/t/api/scrip.t @@ -1,52 +1,44 @@ use strict; use warnings; -use RT; -use RT::Test tests => 25; +use RT::Test; +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok $queue && $queue->id, 'loaded or created queue'; +note 'basic scrips functionality test: create+execute'; { - -ok (require RT::Scrip); - - -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 ($val, $msg) =$s1->Create( Queue => $q->Id, - ScripAction => 'User Defined', - ScripCondition => 'User Defined', - CustomIsApplicableCode => 'if ($self->TicketObj->Subject =~ /fire/) { return (1);} else { return(0)}', - CustomPrepareCode => 'return 1', - CustomCommitCode => '$self->TicketObj->SetPriority("87");', - Template => 'Blank' + my $s1 = RT::Scrip->new(RT->SystemUser); + my ($val, $msg) = $s1->Create( + Queue => $queue->Id, + ScripAction => 'User Defined', + ScripCondition => 'User Defined', + CustomIsApplicableCode => '$self->TicketObj->Subject =~ /fire/? 1 : 0', + CustomPrepareCode => 'return 1', + CustomCommitCode => '$self->TicketObj->SetPriority("87");', + Template => 'Blank' ); -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); - -is ($ticket->Priority , '87', "Ticket priority is set right"); - - -my $ticket2 = RT::Ticket->new(RT->SystemUser); -my ($t2v,$t2tv,$t2m) = $ticket2->Create(Queue => $q->Id, - Subject => "hair in water", - ); -ok($t2v, $t2m); - -isnt ($ticket2->Priority , '87', "Ticket priority is set right"); + ok($val,$msg); + my $ticket = RT::Ticket->new(RT->SystemUser); + my ($tv,$ttv,$tm) = $ticket->Create( + Queue => $queue->Id, + Subject => "hair on fire", + ); + ok($tv, $tm); + is ($ticket->Priority , '87', "Ticket priority is set right"); + my $ticket2 = RT::Ticket->new(RT->SystemUser); + my ($t2v,$t2tv,$t2m) = $ticket2->Create( + Queue => $queue->Id, + Subject => "hair in water", + ); + ok($t2v, $t2m); + isnt ($ticket2->Priority , '87', "Ticket priority is set right"); } - +note 'modify properties of a scrip'; { my $scrip = RT::Scrip->new($RT::SystemUser); my ( $val, $msg ) = $scrip->Create( @@ -117,3 +109,158 @@ isnt ($ticket2->Priority , '87', "Ticket priority is set right"); ok( $scrip->Delete, 'delete the scrip' ); } + +my $queue_B = RT::Test->load_or_create_queue( Name => 'B' ); +ok $queue_B && $queue_B->id, 'loaded or created queue'; + +note 'check creation errors vs. templates'; +{ + my $scrip = RT::Scrip->new(RT->SystemUser); + my ($status, $msg) = $scrip->Create( + Queue => $queue->id, + ScripAction => 'User Defined', + ScripCondition => 'User Defined', + Template => 'not exist', + ); + ok(!$status, "couldn't create scrip, not existing template"); + + ($status, $msg) = $scrip->Create( + ScripAction => 'User Defined', + ScripCondition => 'User Defined', + Template => 'not exist', + ); + ok(!$status, "couldn't create scrip, not existing template"); + + ($status, $msg) = $scrip->Create( + Queue => $queue->id, + ScripAction => 'User Defined', + ScripCondition => 'User Defined', + Template => 54321, + ); + ok(!$status, "couldn't create scrip, not existing template"); + + ($status, $msg) = $scrip->Create( + ScripAction => 'User Defined', + ScripCondition => 'User Defined', + Template => 54321, + ); + ok(!$status, "couldn't create scrip, not existing template"); + + my $template = RT::Template->new( RT->SystemUser ); + ($status, $msg) = $template->Create( Queue => $queue->id, Name => 'bar' ); + ok $status, 'created a template'; + + ($status, $msg) = $scrip->Create( + ScripAction => 'User Defined', + ScripCondition => 'User Defined', + Template => $template->id, + ); + ok(!$status, "couldn't create scrip, wrong template"); + + ($status, $msg) = $scrip->Create( + Queue => $queue_B->id, + ScripAction => 'User Defined', + ScripCondition => 'User Defined', + Template => $template->id, + ); + ok(!$status, "couldn't create scrip, wrong template"); +} + +note 'check applications vs. templates'; +{ + my $template = RT::Template->new( RT->SystemUser ); + my ($status, $msg) = $template->Create( Queue => $queue->id, Name => 'foo' ); + ok $status, 'created a template'; + + my $scrip = RT::Scrip->new(RT->SystemUser); + ($status, $msg) = $scrip->Create( + Queue => $queue->Id, + ScripAction => 'User Defined', + ScripCondition => 'User Defined', + Template => 'foo', + CustomIsApplicableCode => "1;", + CustomPrepareCode => "1;", + CustomCommitCode => "1;", + ); + ok($status, 'created a scrip') or diag "error: $msg"; + RT::Test->object_scrips_are($scrip, [$queue], [0, $queue_B]); + + ($status, $msg) = $scrip->AddToObject( $queue_B->id ); + ok(!$status, $msg); + RT::Test->object_scrips_are($scrip, [$queue], [0, $queue_B]); + my $obj_scrip = RT::ObjectScrip->new( RT->SystemUser ); + ok($obj_scrip->LoadByCols( Scrip => $scrip->id, ObjectId => $queue->id )); + is($obj_scrip->Stage, 'TransactionCreate'); + is($obj_scrip->FriendlyStage, 'Normal'); + + $template = RT::Template->new( RT->SystemUser ); + ($status, $msg) = $template->Create( Queue => $queue_B->id, Name => 'foo' ); + ok $status, 'created a template'; + + ($status, $msg) = $scrip->AddToObject( $queue_B->id ); + ok($status, 'added scrip to another queue'); + RT::Test->object_scrips_are($scrip, [$queue, $queue_B], [0]); + + ($status, $msg) = $scrip->RemoveFromObject( $queue_B->id ); + ok($status, 'removed scrip from queue'); + + ($status, $msg) = $template->Delete; + ok $status, 'deleted template foo in queue B'; + + ($status, $msg) = $scrip->AddToObject( $queue_B->id ); + ok(!$status, $msg); + RT::Test->object_scrips_are($scrip, [$queue], [0, $queue_B]); + + ($status, $msg) = $template->Create( Queue => 0, Name => 'foo' ); + ok $status, 'created a global template'; + + ($status, $msg) = $scrip->AddToObject( $queue_B->id ); + ok($status, 'added scrip'); + RT::Test->object_scrips_are($scrip, [$queue, $queue_B], [0]); +} + +note 'basic check for disabling scrips'; +{ + my $scrip = RT::Scrip->new(RT->SystemUser); + my ($status, $msg) = $scrip->Create( + Queue => $queue->id, + ScripCondition => 'On Create', + ScripAction => 'User Defined', + CustomPrepareCode => 'return 1', + CustomCommitCode => '$self->TicketObj->SetPriority("87"); return 1', + Template => 'Blank' + ); + ok($status, "created scrip"); + is($scrip->Disabled, 0, "not disabled"); + + { + my $ticket = RT::Ticket->new(RT->SystemUser); + my ($tid, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => "test", + ); + ok($tid, "created ticket") or diag "error: $msg"; + is ($ticket->Priority , '87', "Ticket priority is set right"); + } + + ($status,$msg) = $scrip->SetDisabled(1); + is($scrip->Disabled, 1, "disabled"); + + { + my $ticket = RT::Ticket->new(RT->SystemUser); + my ($tid, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => "test", + ); + ok($tid, "created ticket") or diag "error: $msg"; + isnt ($ticket->Priority , '87', "Ticket priority is set right"); + } + + is($scrip->FriendlyStage('TransactionCreate'), 'Normal', + 'Correct stage wording for TransactionCreate'); + is($scrip->FriendlyStage('TransactionBatch'), 'Batch', + 'Correct stage wording for TransactionBatch'); + RT->Config->Set('UseTransactionBatch', 0); + is($scrip->FriendlyStage('TransactionBatch'), 'Batch (disabled by config)', + 'Correct stage wording for TransactionBatch with UseTransactionBatch disabled'); +} diff --git a/rt/t/api/scrip_order.t b/rt/t/api/scrip_order.t index 6fba7e579..689b55970 100644 --- a/rt/t/api/scrip_order.t +++ b/rt/t/api/scrip_order.t @@ -2,52 +2,271 @@ use strict; use warnings; -use RT; -use RT::Test tests => 7; - - - -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 $id, $msg) = $priority_ten_scrip->Create( - Description => "10 set priority $$", - Queue => $queue_id, - ScripCondition => 'On Create', - ScripAction => 'User Defined', - CustomPrepareCode => '$RT::Logger->debug("Setting priority to 10..."); return 1;', - CustomCommitCode => '$self->TicketObj->SetPriority(10);', - Template => 'Blank', - Stage => 'TransactionCreate', -); -ok($id, "Created priority-10 scrip? ".$msg); - -my $priority_five_scrip = RT::Scrip->new(RT->SystemUser); -($id, $msg) = $priority_ten_scrip->Create( - Description => "05 set priority $$", - Queue => $queue_id, - ScripCondition => 'On Create', - ScripAction => 'User Defined', - CustomPrepareCode => '$RT::Logger->debug("Setting priority to 5..."); return 1;', - CustomCommitCode => '$self->TicketObj->SetPriority(5);', - Template => 'Blank', - Stage => 'TransactionCreate', -); -ok($id, "Created priority-5 scrip? ".$msg); - -my $ticket = RT::Ticket->new(RT->SystemUser); -($id, $msg) = $ticket->Create( - Queue => $queue_id, - Requestor => 'order@example.com', - Subject => "Scrip order test $$", -); -ok($ticket->id, "Created ticket? id=$id"); - -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"); +use RT::Test tests => 204; + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok $queue && $queue->id, 'loaded or created queue'; + +note "check that execution order reflects sort order"; +{ + my $ten = main->create_scrip_ok( + Description => "Set priority to 10", + Queue => $queue->id, + CustomCommitCode => '$self->TicketObj->SetPriority(10);', + ); + + my $five = main->create_scrip_ok( + Description => "Set priority to 5", + Queue => $queue->id, + CustomCommitCode => '$self->TicketObj->SetPriority(5);', + ); + + my $ticket = RT::Ticket->new(RT->SystemUser); + my ($id, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => "Scrip order test $$", + ); + ok($ticket->id, "Created ticket? id=$id"); + is($ticket->Priority , 5, "By default newer scrip is last"); + + main->move_scrip_ok( $five, $queue->id, 'up' ); + + $ticket = RT::Ticket->new(RT->SystemUser); + ($id, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => "Scrip order test $$", + ); + ok($ticket->id, "Created ticket? id=$id"); + is($ticket->Priority , 10, "Moved scrip and result is different"); +} + +my $queue_B = RT::Test->load_or_create_queue( Name => 'Other' ); +ok $queue_B && $queue_B->id, 'loaded or created queue'; + +note "move around two local scrips"; +{ + main->delete_all_scrips(); + + my @scrips; + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + main->check_scrips_order(\@scrips, [$queue]); + + main->move_scrip_ok( $scrips[0], $queue->id, 'down' ); + @scrips = @scrips[1, 0]; + main->check_scrips_order(\@scrips, [$queue]); + + main->move_scrip_ok( $scrips[0], $queue->id, 'down' ); + @scrips = @scrips[1, 0]; + main->check_scrips_order(\@scrips, [$queue]); + + main->move_scrip_ok( $scrips[1], $queue->id, 'up' ); + @scrips = @scrips[1, 0]; + main->check_scrips_order(\@scrips, [$queue]); + + main->move_scrip_ok( $scrips[1], $queue->id, 'up' ); + @scrips = @scrips[1, 0]; + main->check_scrips_order(\@scrips, [$queue]); +} + +note "move around two global scrips"; +{ + main->delete_all_scrips(); + + my @scrips; + push @scrips, main->create_scrip_ok( Queue => 0 ); + push @scrips, main->create_scrip_ok( Queue => 0 ); + main->check_scrips_order(\@scrips, [$queue]); + + main->move_scrip_ok( $scrips[0], 0, 'down' ); + @scrips = @scrips[1, 0]; + main->check_scrips_order(\@scrips, [$queue]); + + main->move_scrip_ok( $scrips[0], 0, 'down' ); + @scrips = @scrips[1, 0]; + main->check_scrips_order(\@scrips, [$queue]); + + main->move_scrip_ok( $scrips[1], 0, 'up' ); + @scrips = @scrips[1, 0]; + main->check_scrips_order(\@scrips, [$queue]); + + main->move_scrip_ok( $scrips[1], 0, 'up' ); + @scrips = @scrips[1, 0]; + main->check_scrips_order(\@scrips, [$queue]); +} + +note "move local scrip below global"; +{ + main->delete_all_scrips(); + my @scrips; + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + push @scrips, main->create_scrip_ok( Queue => $queue_B->id ); + push @scrips, main->create_scrip_ok( Queue => 0 ); + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + main->check_scrips_order(\@scrips, [$queue, $queue_B]); + + main->move_scrip_ok( $scrips[0], $queue->id, 'down' ); + @scrips = @scrips[1, 2, 0, 3]; + main->check_scrips_order(\@scrips, [$queue, $queue_B]); +} + +note "move local scrip above global"; +{ + main->delete_all_scrips(); + my @scrips; + push @scrips, main->create_scrip_ok( Queue => $queue_B->id ); + push @scrips, main->create_scrip_ok( Queue => 0 ); + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + push @scrips, main->create_scrip_ok( Queue => $queue_B->id ); + main->check_scrips_order(\@scrips, [$queue, $queue_B]); + + main->move_scrip_ok( $scrips[-1], $queue_B->id, 'up' ); + @scrips = @scrips[0, 3, 1, 2]; + main->check_scrips_order(\@scrips, [$queue, $queue_B]); +} + +note "move global scrip down with local in between"; +{ + main->delete_all_scrips(); + my @scrips; + push @scrips, main->create_scrip_ok( Queue => 0 ); + push @scrips, main->create_scrip_ok( Queue => $queue_B->id ); + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + push @scrips, main->create_scrip_ok( Queue => 0 ); + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + main->check_scrips_order(\@scrips, [$queue, $queue_B]); + + main->move_scrip_ok( $scrips[0], 0, 'down' ); + @scrips = @scrips[1, 2, 3, 0, 4]; + main->check_scrips_order(\@scrips, [$queue, $queue_B]); +} + +note "move global scrip up with local in between"; +{ + main->delete_all_scrips(); + my @scrips; + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + push @scrips, main->create_scrip_ok( Queue => 0 ); + push @scrips, main->create_scrip_ok( Queue => $queue_B->id ); + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + push @scrips, main->create_scrip_ok( Queue => 0 ); + main->check_scrips_order(\@scrips, [$queue, $queue_B]); + + main->move_scrip_ok( $scrips[-1], 0, 'up' ); + @scrips = @scrips[0, 4, 1, 2, 3]; + main->check_scrips_order(\@scrips, [$queue, $queue_B]); +} + +note "delete scrips one by one"; +{ + main->delete_all_scrips(); + my @scrips; + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + push @scrips, main->create_scrip_ok( Queue => $queue_B->id ); + push @scrips, main->create_scrip_ok( Queue => 0 ); + push @scrips, main->create_scrip_ok( Queue => $queue_B->id ); + push @scrips, main->create_scrip_ok( Queue => $queue->id ); + push @scrips, main->create_scrip_ok( Queue => 0 ); + main->check_scrips_order(\@scrips, [$queue, $queue_B]); + + foreach my $idx (3, 2, 0 ) { + $_->Delete foreach splice @scrips, $idx, 1; + main->check_scrips_order(\@scrips, [$queue, $queue_B]); + } +} + +sub create_scrip_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $self = shift; + my %args = ( + ScripCondition => 'On Create', + ScripAction => 'User Defined', + CustomPrepareCode => 'return 1', + CustomCommitCode => 'return 1', + Template => 'Blank', + Stage => 'TransactionCreate', + @_ + ); + + my $scrip = RT::Scrip->new( RT->SystemUser ); + my ($id, $msg) = $scrip->Create( %args ); + ok($id, "Created scrip") or diag "error: $msg"; + + return $scrip; +} + +sub delete_all_scrips { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $self = shift; + my $scrips = RT::Scrips->new( RT->SystemUser ); + $scrips->UnLimit; + $_->Delete foreach @{ $scrips->ItemsArrayRef }; +} + +sub move_scrip_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $self = shift; + my ($scrip, $queue, $dir) = @_; + + my $rec = RT::ObjectScrip->new( RT->SystemUser ); + $rec->LoadByCols( Scrip => $scrip->id, ObjectId => $queue ); + ok $rec->id, 'found application of the scrip'; + + my $method = 'Move'. ucfirst lc $dir; + my ($status, $msg) = $rec->$method(); + ok $status, "moved scrip $dir" or diag "error: $msg"; +} + +sub check_scrips_order { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $self = shift; + my $scrips = shift; + my $queues = shift; + + foreach my $qid ( 0, map $_->id, @$queues ) { + my $list = RT::Scrips->new( RT->SystemUser ); + $list->LimitToGlobal; + $list->LimitToQueue( $qid ) if $qid; + $list->ApplySortOrder; + is_deeply( + [map $_->id, @{ $list->ItemsArrayRef } ], + [map $_->id, grep $_->IsAdded( $qid ) || $_->IsGlobal, @$scrips], + 'list of scrips match expected' + ) + } + + foreach my $qid ( map $_->id, @$queues ) { + my $list = RT::ObjectScrips->new( RT->SystemUser ); + $list->LimitToObjectId( 0 ); + $list->LimitToObjectId( $qid ); + + my %so; + $so{ $_->SortOrder }++ foreach @{ $list->ItemsArrayRef }; + ok( !grep( {$_ != 1} values %so), 'no dublicate order' ); + } + { + my $list = RT::ObjectScrips->new( RT->SystemUser ); + $list->UnLimit; + $list->OrderBy( FIELD => 'SortOrder', ORDER => 'ASC' ); + + my $prev; + foreach my $rec ( @{ $list->ItemsArrayRef } ) { + my $so = $rec->SortOrder; + do { $prev = $so; next } unless defined $prev; + + ok $so == $prev || $so == $prev+1, "sequential order"; + $prev = $so; + } + } +} + +sub dump_sort_order { + + diag " id oid so"; + diag join "\n", map { join "\t", @$_ } map @$_, $RT::Handle->dbh->selectall_arrayref( + "select Scrip, ObjectId, SortOrder from ObjectScrips ORDER BY SortOrder" + ); + +} diff --git a/rt/t/api/searchbuilder.t b/rt/t/api/searchbuilder.t index 84568718d..9237dcd37 100644 --- a/rt/t/api/searchbuilder.t +++ b/rt/t/api/searchbuilder.t @@ -20,7 +20,7 @@ ok( $queues->UnLimit(),'Unlimited the result set of the queues object'); my $items = $queues->ItemsArrayRef(); my @items = @{$items}; -ok($queues->NewItem->_Accessible('Name','read')); +ok($queues->RecordClass->_Accessible('Name','read')); my @sorted = sort {lc($a->Name) cmp lc($b->Name)} @items; ok (@sorted, "We have an array of queues, sorted". join(',',map {$_->Name} @sorted)); diff --git a/rt/t/api/system-available-rights.t b/rt/t/api/system-available-rights.t new file mode 100644 index 000000000..d7b6f5e8d --- /dev/null +++ b/rt/t/api/system-available-rights.t @@ -0,0 +1,65 @@ +use strict; +use warnings; + +use RT::Test tests => undef; +use Set::Tiny; + +my @warnings; +local $SIG{__WARN__} = sub { + push @warnings, "@_"; +}; + +my $requestor = RT::Group->new( RT->SystemUser ); +$requestor->LoadRoleGroup( + Object => RT->System, + Name => "Requestor", +); +ok $requestor->id, "Loaded global requestor role group"; + +$requestor = $requestor->PrincipalObj; +ok $requestor->id, "Loaded global requestor role group principal"; + +note "Try granting an article right to a system role group"; +{ + my ($ok, $msg) = $requestor->GrantRight( + Right => "ShowArticle", + Object => RT->System, + ); + ok !$ok, "Couldn't grant nonsensical right to global Requestor role: $msg"; + like shift @warnings, qr/Couldn't validate right name.*?ShowArticle/; + + ($ok, $msg) = $requestor->GrantRight( + Right => "ShowTicket", + Object => RT->System, + ); + ok $ok, "Granted queue right to global queue role: $msg"; + + ($ok, $msg) = RT->PrivilegedUsers->PrincipalObj->GrantRight( + Right => "ShowArticle", + Object => RT->System, + ); + ok $ok, "Granted article right to non-role global group: $msg"; + + reset_rights(); +} + +note "AvailableRights"; +{ + my @available = ( + [ keys %{RT->System->AvailableRights} ], + [ keys %{RT->System->AvailableRights( $requestor )} ], + ); + + my $all = Set::Tiny->new( @{$available[0]} ); + my $role = Set::Tiny->new( @{$available[1]} ); + + ok $role->is_proper_subset($all), "role rights are a proper subset of all"; +} + +ok !@warnings, "No uncaught warnings" + or diag explain \@warnings; + +# for clarity +sub reset_rights { RT::Test->set_rights() } + +done_testing; diff --git a/rt/t/api/system.t b/rt/t/api/system.t index f1100d332..f3d9226cf 100644 --- a/rt/t/api/system.t +++ b/rt/t/api/system.t @@ -2,8 +2,18 @@ use strict; use warnings; use RT; -use RT::Test nodata => 1, tests => 7; +use RT::Test nodata => 1, tests => 16; +BEGIN{ + use_ok('RT::System'); +} + +# Skipping most of the methods added just to make RT::System +# look like RT::Record. + +can_ok('RT::System', qw( AvailableRights RightCategories AddRight + id Id SubjectTag Name QueueCacheNeedsUpdate AddUpgradeHistory + UpgradeHistory )); { @@ -15,18 +25,50 @@ ok ($rights->{'CreateTicket'},"CreateTicket right found"); ok ($rights->{'AdminGroupMembership'},"ModifyGroupMembers right found"); ok (!$rights->{'CasdasdsreateTicket'},"bogus right not found"); +} +{ +my $sys = RT::System->new(); +is( $sys->Id, 1, 'Id is 1'); +is ($sys->id, 1, 'id is 1'); } { -use RT::System; -my $sys = RT::System->new(); -is( $sys->Id, 1); -is ($sys->id, 1); +# Test upgrade history methods. +my $sys = RT::System->new(RT->SystemUser); +isa_ok($sys, 'RT::System'); -} +my $file = 'test_file.txt'; +my $content = 'Some file contents.'; +my $upgrade_history = RT->System->UpgradeHistory(); + +is( keys %$upgrade_history, 0, 'No history in test DB'); + +RT->System->AddUpgradeHistory(RT =>{ + action => 'insert', + filename => $file, + content => $content, + stage => 'before', + }); + +$upgrade_history = RT->System->UpgradeHistory(); +ok( exists($upgrade_history->{'RT'}), 'History has an RT key.'); +is( @{$upgrade_history->{'RT'}}, 1, '1 item in history array'); +is($upgrade_history->{RT}[0]{stage}, 'before', 'stage is before for item 1'); +RT->System->AddUpgradeHistory(RT =>{ + action => 'insert', + filename => $file, + content => $content, + stage => 'after', + }); + +$upgrade_history = RT->System->UpgradeHistory(); +is( @{$upgrade_history->{'RT'}}, 2, '2 item in history array'); +is($upgrade_history->{RT}[1]{stage}, 'after', 'stage is after for item 2'); + +} diff --git a/rt/t/api/template.t b/rt/t/api/template.t index 331d9f996..1baac44b6 100644 --- a/rt/t/api/template.t +++ b/rt/t/api/template.t @@ -2,23 +2,31 @@ use warnings; use strict; -use RT; -use RT::Test tests => 10; +use RT::Test tests => 37; + +use_ok('RT::Template'); my $queue = RT::Test->load_or_create_queue( Name => 'Templates' ); ok $queue && $queue->id, "loaded or created a queue"; +my $alt_queue = RT::Test->load_or_create_queue( Name => 'Alternative' ); +ok $alt_queue && $alt_queue->id, 'loaded or created queue'; + { - my $template = RT::Template->new( RT->SystemUser ); + my $template = RT::Template->new(RT->SystemUser); isa_ok($template, 'RT::Template'); +} + +{ + my $template = RT::Template->new( RT->SystemUser ); my ($val,$msg) = $template->Create( Queue => $queue->id, - Name => 'InsertTest', + Name => 'Test', Content => 'This is template content' ); ok $val, "created a template" or diag "error: $msg"; ok my $id = $template->id, "id is defined"; - is $template->Name, 'InsertTest'; + is $template->Name, 'Test'; is $template->Content, 'This is template content', "We created the object right"; ($val, $msg) = $template->SetContent( 'This is new template content'); @@ -32,3 +40,164 @@ ok $queue && $queue->id, "loaded or created a queue"; $template->Load($id); ok !$template->id, "can not load template after deletion"; } + +note "can not create template w/o Name"; +{ + clean_templates( Queue => $queue->id ); + my $template = RT::Template->new( RT->SystemUser ); + my ($val,$msg) = $template->Create( Queue => $queue->id ); + ok(!$val,$msg); +} + +note "can not create template with duplicate name"; +{ + clean_templates( Queue => $queue->id ); + my $template = RT::Template->new( RT->SystemUser ); + my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' ); + ok($val,$msg); + + ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' ); + ok(!$val,$msg); +} + +note "change template's name"; +{ + clean_templates( Queue => $queue->id ); + my $template = RT::Template->new( RT->SystemUser ); + my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' ); + ok($val,$msg); + + ($val,$msg) = $template->SetName( 'Some' ); + ok($val,$msg); + is $template->Name, 'Some'; +} + +note "can not change name to empty"; +{ + clean_templates( Queue => $queue->id ); + my $template = RT::Template->new( RT->SystemUser ); + my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' ); + ok($val,$msg); + + ($val,$msg) = $template->Create( Queue => $queue->id, Name => '' ); + ok(!$val,$msg); + ($val,$msg) = $template->Create( Queue => $queue->id, Name => undef ); + ok(!$val,$msg); +} + +note "can not change name to duplicate"; +{ + clean_templates( Queue => $queue->id ); + my $template = RT::Template->new( RT->SystemUser ); + my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' ); + ok($val,$msg); + + ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Some' ); + ok($val,$msg); +} + +note "changing queue of template is not implemented"; +{ + clean_templates( Queue => $queue->id ); + my $template = RT::Template->new( RT->SystemUser ); + my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' ); + ok($val,$msg); + + ($val,$msg) = $template->SetQueue( $alt_queue->id ); + ok(!$val,$msg); +} + +note "make sure template can not be deleted if it has scrips"; +{ + clean_templates( Queue => $queue->id ); + my $template = RT::Template->new( RT->SystemUser ); + my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' ); + ok($val,$msg); + + my $scrip = RT::Scrip->new( RT->SystemUser ); + ($val,$msg) = $scrip->Create( + Queue => $queue->id, + ScripCondition => "On Create", + ScripAction => 'Autoreply To Requestors', + Template => $template->Name, + ); + ok($val, $msg); + + ($val, $msg) = $template->Delete; + ok(!$val,$msg); +} + +note "make sure template can be deleted if it's an override"; +{ + clean_templates( Queue => $queue->id ); + my $template = RT::Template->new( RT->SystemUser ); + my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Overrided' ); + ok($val,$msg); + + $template = RT::Template->new( RT->SystemUser ); + ($val,$msg) = $template->Create( Queue => 0, Name => 'Overrided' ); + ok($val,$msg); + + my $scrip = RT::Scrip->new( RT->SystemUser ); + ($val,$msg) = $scrip->Create( + Queue => $queue->id, + ScripCondition => "On Create", + ScripAction => 'Autoreply To Requestors', + Template => $template->Name, + ); + ok($val, $msg); + + ($val, $msg) = $template->Delete; + ok($val,$msg); +} + +note "make sure template can be deleted if it has an override"; +{ + clean_templates( Queue => $queue->id ); + my $template = RT::Template->new( RT->SystemUser ); + my ($val,$msg) = $template->Create( Queue => 0, Name => 'Overrided' ); + ok($val,$msg); + + $template = RT::Template->new( RT->SystemUser ); + ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Overrided' ); + ok($val,$msg); + + my $scrip = RT::Scrip->new( RT->SystemUser ); + ($val,$msg) = $scrip->Create( + Queue => $queue->id, + ScripCondition => "On Create", + ScripAction => 'Autoreply To Requestors', + Template => $template->Name, + ); + ok($val, $msg); + + ($val, $msg) = $template->Delete; + ok($val,$msg); +} + + +{ + my $t = RT::Template->new(RT->SystemUser); + $t->Create(Name => "Foo", Queue => $queue->id); + my $t2 = RT::Template->new(RT->Nobody); + $t2->Load($t->Id); + ok($t2->QueueObj->id, "Got the template's queue objet"); +} + +sub clean_templates { + my %args = (@_); + + my $templates = RT::Templates->new( RT->SystemUser ); + $templates->Limit( FIELD => 'Queue', VALUE => $args{'Queue'} ) + if defined $args{'Queue'}; + $templates->Limit( FIELD => 'Name', VALUE => $_ ) + foreach ref $args{'Name'}? @{$args{'Name'}} : ($args{'Name'}||()); + while ( my $t = $templates->Next ) { + my ($status) = $t->Delete; + unless ( $status ) { + $_->Delete foreach @{ $t->UsedBy->ItemsArrayRef }; + $t->Delete; + } + } +} + diff --git a/rt/t/api/ticket.t b/rt/t/api/ticket.t index da287a607..c5f1e240f 100644 --- a/rt/t/api/ticket.t +++ b/rt/t/api/ticket.t @@ -100,7 +100,7 @@ ok( $t->Create(Queue => 'General', Due => '2002-05-21 00:00:00', ReferredToBy => ok ( my $id = $t->Id, "Got ticket id"); like ($t->RefersTo->First->Target , qr/fsck.com/, "Got refers to"); like ($t->ReferredToBy->First->Base , qr/cpan.org/, "Got referredtoby"); -is ($t->ResolvedObj->Unix, 0, "It hasn't been resolved - ". $t->ResolvedObj->Unix); +ok (!$t->ResolvedObj->IsSet, "It hasn't been resolved"); } @@ -115,8 +115,7 @@ my ($id, $msg) = $ticket->Create(Subject => "Foo", Queue => '1' ); ok ($id, "Ticket $id was created"); -ok(my $group = RT::Group->new(RT->SystemUser)); -ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Requestor')); +ok(my $group = $ticket->RoleGroup('Requestor')); ok ($group->Id, "Found the requestors object for this ticket"); ok(my $jesse = RT::User->new(RT->SystemUser), "Creating a jesse rt::user"); @@ -135,14 +134,11 @@ 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); -ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Cc')); +$group = $ticket->RoleGroup('Cc'); ok ($group->Id, "Found the cc object for this ticket"); -$group = RT::Group->new(RT->SystemUser); -ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'AdminCc')); +$group = $ticket->RoleGroup('AdminCc'); ok ($group->Id, "Found the AdminCc object for this ticket"); -$group = RT::Group->new(RT->SystemUser); -ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Owner')); +$group = $ticket->RoleGroup('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'"); @@ -209,20 +205,32 @@ is ($t1->Requestors->MembersObj->Count, 2); } +diag "Test owner changes"; { my $root = RT::User->new(RT->SystemUser); $root->Load('root'); ok ($root->Id, "Loaded the root user"); my $t = RT::Ticket->new(RT->SystemUser); -$t->Load(1); -$t->SetOwner('root'); +my ($val, $msg) = $t->Create( Subject => 'Owner test 1', Queue => 'General'); +ok( $t->Id, "Created a new ticket with id $val: $msg"); + +($val, $msg) = $t->SetOwner('bogususer'); +ok( !$val, "Can't set owner to bogus user"); +is( $msg, "That user does not exist", "Got message: $msg"); + +($val, $msg) = $t->SetOwner('root'); is ($t->OwnerObj->Name, 'root' , "Root owns the ticket"); + +($val, $msg) = $t->SetOwner('root'); +ok( !$val, "User already owns ticket"); +is( $msg, "That user already owns that ticket", "Got message: $msg"); + $t->Steal(); 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 => 'ObjectId', VALUE => $t->Id); $txns->Limit(FIELD => 'ObjectType', VALUE => 'RT::Ticket'); $txns->Limit(FIELD => 'Type', OPERATOR => '!=', VALUE => 'EmailRecord'); @@ -230,6 +238,37 @@ my $steal = $txns->First; is($steal->OldValue , $root->Id , "Stolen from root"); is($steal->NewValue , RT->SystemUser->Id , "Stolen by the systemuser"); +ok(my $user1 = RT::User->new(RT->SystemUser), "Creating a user1 rt::user"); +($val, $msg) = $user1->Create(Name => 'User1', EmailAddress => 'user1@example.com'); +ok( $val, "Created new user with id: $val"); +ok( $user1->Id, "Found the user1 rt user"); + +my $t1 = RT::Ticket->new($user1); +($val, $msg) = $t1->Load($t->Id); +ok( $t1->Id, "Loaded ticket with id $val"); + +($val, $msg) = $t1->SetOwner('root'); +ok( !$val, "user1 can't set owner to root: $msg"); +is ($t->OwnerObj->id, RT->SystemUser->id , "SystemUser still owns ticket " . $t1->Id); + +my $queue = RT::Queue->new(RT->SystemUser); +$queue->Load("General"); + +($val, $msg) = $user1->PrincipalObj->GrantRight( + Object => $queue, Right => 'ModifyTicket' + ); + +($val, $msg) = $t1->SetOwner('root'); +ok( !$val, "With ModifyTicket user1 can't set owner to root: $msg"); +is ($t->OwnerObj->id, RT->SystemUser->id , "SystemUser still owns ticket " . $t1->Id); + +($val, $msg) = $user1->PrincipalObj->GrantRight( + Object => $queue, Right => 'ReassignTicket' + ); + +($val, $msg) = $t1->SetOwner('root'); +ok( $val, "With ReassignTicket user1 reassigned ticket " . $t1->Id . " to root: $msg"); +is ($t1->OwnerObj->Name, 'root' , "Root owns ticket " . $t1->Id); } @@ -250,7 +289,16 @@ like($msg, qr/resolved/i, "Status message is correct"); ($id, $msg) = $tt->SetStatus('resolved'); ok(!$id,$msg); +my $dep = RT::Ticket->new( RT->SystemUser ); +my ( $dep_id, undef, $dep_msg ) = $dep->Create( + Queue => 'general', + Subject => 'dep ticket', + 'DependedOnBy' => $tt->id, +); +ok( $dep->id, $dep_msg ); +($id, $msg) = $tt->SetStatus('rejected'); +ok( $id, $msg ); } diff --git a/rt/t/api/tickets.t b/rt/t/api/tickets.t index 50d08f756..172965c3b 100644 --- a/rt/t/api/tickets.t +++ b/rt/t/api/tickets.t @@ -2,8 +2,7 @@ use strict; use warnings; use RT; -use RT::Test tests => 18; - +use RT::Test tests => undef; { @@ -114,3 +113,35 @@ ok( $unlimittickets->Count > 0, "UnLimited tickets object should return tickets" ok $count == 1, "found one ticket"; } +{ + my $tickets = RT::Tickets->new( RT->SystemUser ); + my ($ret, $msg) = $tickets->FromSQL("Resolved IS NULL"); + ok $ret, "Ran query with IS NULL: $msg"; + my $count = $tickets->Count(); + ok $count > 1, "Found more than one ticket"; + undef $count; +} + +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + ok $ticket->Load(1), "Loaded test ticket 1"; + ok $ticket->SetStatus('resolved'), "Set to resolved"; + + my $tickets = RT::Tickets->new( RT->SystemUser ); + my ($ret, $msg) = $tickets->FromSQL("Resolved IS NOT NULL"); + ok $ret, "Ran query with IS NOT NULL: $msg"; + my $count = $tickets->Count(); + ok $count == 1, "Found one ticket"; + undef $count; +} + +{ + my $tickets = RT::Tickets->new( RT->SystemUser ); + $tickets->LimitDate( FIELD => "Resolved", OPERATOR => "IS", VALUE => "NULL" ); + $tickets->LimitDate( FIELD => "Resolved", OPERATOR => "IS NOT", VALUE => "NULL" ); + my $count = $tickets->Count(); + ok $count > 1, "Found more than one ticket"; + undef $count; +} + +done_testing; diff --git a/rt/t/api/txn_content.t b/rt/t/api/txn_content.t index 392b6a73b..672d6c2e0 100644 --- a/rt/t/api/txn_content.t +++ b/rt/t/api/txn_content.t @@ -1,7 +1,7 @@ use warnings; use strict; -use RT::Test tests => 3; +use RT::Test tests => 4; use MIME::Entity; my $ticket = RT::Ticket->new(RT->SystemUser); my $mime = MIME::Entity->build( @@ -16,4 +16,8 @@ my $txns = $ticket->Transactions; $txns->Limit( FIELD => 'Type', VALUE => 'Create' ); my $txn = $txns->First; ok( $txn, 'got Create txn' ); -is( $txn->Content, "this is body\n", "txn's content" ); + +# ->Content converts from text/html to plain text if we don't explicitly ask +# for html. Our html -> text converter seems to add an extra trailing newline +like( $txn->Content, qr/^\s*this is body\s*$/, "txn's html content converted to plain text" ); +is( $txn->Content(Type => 'text/html'), "this is body\n", "txn's html content" ); diff --git a/rt/t/api/user-prefs.t b/rt/t/api/user-prefs.t new file mode 100644 index 000000000..a4aa49f1b --- /dev/null +++ b/rt/t/api/user-prefs.t @@ -0,0 +1,59 @@ + +use strict; +use warnings; +use RT; +use RT::Test tests => undef; + +use_ok( 'RT::User' ); + +my $create_user = RT::User->new(RT->SystemUser); +isa_ok($create_user, 'RT::User'); +my ($ret, $msg) = $create_user->Create(Name => 'CreateTest1'.$$, + EmailAddress => $$.'create-test-1@example.com'); +ok ($ret, "Creating user CreateTest1 - " . $msg ); + +# Create object to operate as the test user +my $user1 = RT::User->new($create_user); +($ret, $msg) = $user1->Load($create_user->Id); +ok ($ret, "Loaded the new user $msg"); + +diag "Set a search preference"; +my $prefs = { + 'Order' => 'DESC|ASC|ASC|ASC', + 'OrderBy' => 'Due', + 'Format' => '\'<a href="__WebPath__/Ticket/Display.html?id=__id__">__id__</a>/TITLE:#\', +\'<a href="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a>/TITLE:Subject\', +\'__Priority__\', +\'__QueueName__\', +\'__ExtendedStatus__\', +\'__Due__\'', + 'RowsPerPage' => '50' +}; + +ok (!$user1->HasRight( Right => 'ModifySelf', Object => $RT::System), "Can't ModifySelf"); +($ret, $msg) = $user1->SetPreferences("SearchDisplay", $prefs); +ok( !$ret, "No permission to set preferences"); +ok (($ret, $msg) = $create_user->PrincipalObj->GrantRight( Right => 'ModifySelf'), + "Granted ModifySelf"); +($ret, $msg) = $user1->SetPreferences("SearchDisplay", $prefs); +ok( $ret, "Search preference set"); + +diag "Fetch preference"; +ok (my $saved_prefs = $user1->Preferences("SearchDisplay"), "Fetched prefs"); +is ($prefs->{OrderBy}, 'Due', "Prefs look ok"); + +diag "Delete prefs"; +ok (($ret, $msg) = $create_user->PrincipalObj->RevokeRight( Right => 'ModifySelf'), + "Revoked ModifySelf"); +($ret, $msg) = $user1->DeletePreferences("SearchDisplay"); +ok( !$ret, "No permission to delete preferences"); +ok (($ret, $msg) = $create_user->PrincipalObj->GrantRight( Right => 'ModifySelf'), + "Granted ModifySelf"); +($ret, $msg) = $user1->DeletePreferences("SearchDisplay"); +ok( $ret, "Search preference deleted"); + +$saved_prefs = $user1->Preferences("SearchDisplay"); +ok (!$saved_prefs, "No saved preferences returned"); + +done_testing; + diff --git a/rt/t/api/user.t b/rt/t/api/user.t index e6b891f73..94494f162 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 => 111; +use RT::Test tests => 122; { @@ -106,7 +106,7 @@ ok($user->Privileged, "User 'root' is privileged again"); 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->id , 1, "user 1 is the first principal"); is($u->PrincipalObj->PrincipalType, 'User' , "Principal 1 is a user, not a group"); @@ -335,3 +335,30 @@ ok($rqv, "Revoked the right successfully - $rqm"); } +{ + my $root = RT::Test->load_or_create_user( Name => 'root' ); + ok $root && $root->id; + + my $queue = RT::Test->load_or_create_queue( Name => 'General' ); + ok $queue && $queue->id; + + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id) = $ticket->Create( Subject => 'test', Queue => $queue ); + ok $id; + + my $b_ticket = RT::Ticket->new( RT->SystemUser ); + ($id) = $b_ticket->Create( Subject => 'test', Queue => $queue ); + ok $id; + + ok $root->ToggleBookmark($b_ticket); + ok !$root->ToggleBookmark($b_ticket); + ok $root->ToggleBookmark($b_ticket); + + ok $root->HasBookmark( $b_ticket ); + ok !$root->HasBookmark( $ticket ); + + my @marks = $root->Bookmarks; + is scalar @marks, 1; + is $marks[0], $b_ticket->id; +} + diff --git a/rt/t/api/users.t b/rt/t/api/users.t index 1f3a48770..e65f9a9b5 100644 --- a/rt/t/api/users.t +++ b/rt/t/api/users.t @@ -2,7 +2,7 @@ use strict; use warnings; use RT::Test tests => 10; -RT::System->AddRights( +RT::System->AddRight( General => 'RTxUserRight' => 'Just a right for testing rights', ); diff --git a/rt/t/approval/admincc.t b/rt/t/approval/admincc.t index b43929603..da8cac23d 100644 --- a/rt/t/approval/admincc.t +++ b/rt/t/approval/admincc.t @@ -2,17 +2,12 @@ use strict; use warnings; use Test::More; -BEGIN { - eval { require Email::Abstract; require Test::Email; 1 } - or plan skip_all => 'require Email::Abstract and Test::Email'; -} - use RT; -use RT::Test tests => 62; +use RT::Test tests => "no_declare"; use RT::Test::Email; -RT->Config->Set( LogToScreen => 'debug' ); +RT->Config->Set( LogToSTDERR => 'debug' ); RT->Config->Set( UseTransactionBatch => 1 ); my ($baseurl, $m) = RT::Test->started_ok; @@ -42,7 +37,6 @@ Queue: ___Approvals Type: approval Owner: CTO AdminCCs: COO, CEO -Requestors: {$Tickets{"TOP"}->Requestors} DependedOnBy: TOP Subject: CTO Approval for PO: {$Tickets{"TOP"}->Id} - {$Tickets{"TOP"}->Subject} Due: {time + 86400} @@ -84,20 +78,20 @@ mail_ok { Requestor => 'minion', Queue => $q->Id, ); -} { from => qr/RT System/, - bcc => qr/ceo.*coo|coo.*ceo/i, - subject => qr/PO for stationary/i, -}, -{ from => qr/RT System/, - to => 'cto@company.com', - subject => qr/New Pending Approval: CTO Approval/, - body => qr/pending your approval.*Your approval is requested.*Blah/s -}, -{ from => qr/PO via RT/, +} { from => qr/PO via RT/, to => 'minion@company.com', subject => qr/PO for stationary/, body => qr/automatically generated in response/ -}; +},{ from => qr/RT System/, + to => 'root@localhost', + subject => qr/PO for stationary/, +},{ from => qr/RT System/, + to => 'cto@company.com', + bcc => qr/ceo.*coo|coo.*ceo/i, + subject => qr/New Pending Approval: CTO Approval/, + body => qr/pending your approval.*Your approval is requested.*Blah/s +} +; ok ($tid,$tmsg); @@ -134,6 +128,11 @@ mail_ok { }, { from => qr/RT System/, + to => 'root@localhost', + subject => qr/Ticket Approved:/, +}, +{ + from => qr/RT System/, to => 'minion@company.com', subject => qr/Ticket Approved:/, body => qr/approved by CTO.*notes: Resources exist to be consumed/s @@ -180,6 +179,12 @@ for my $admin (qw/coo ceo/) { body => qr/Resources exist to be consumed/, }, { + from => qr/RT System/, + to => 'root@localhost', + subject => qr/Ticket Approved:/, + body => qr/approved by \U$admin\E.*notes: Resources exist to be consumed/s + }, + { from => qr/RT System/, to => 'minion@company.com', subject => qr/Ticket Approved:/, @@ -273,3 +278,6 @@ $m_coo->content_lacks( 'second approval', 'coo: second approval is gone too' ); $m_ceo->content_lacks( 'second approval', 'ceo: second approval is gone too' ); RT::Test->clean_caught_mails; + +undef $m; +done_testing; diff --git a/rt/t/approval/basic.t b/rt/t/approval/basic.t index 2d00eb56e..e863bf12f 100644 --- a/rt/t/approval/basic.t +++ b/rt/t/approval/basic.t @@ -1,15 +1,10 @@ use strict; use warnings; use RT::Test tests => undef; -BEGIN { - plan skip_all => 'Email::Abstract and Test::Email required.' - unless eval { require Email::Abstract; require Test::Email; 1 }; - plan tests => 38; -} use RT::Test::Email; -RT->Config->Set( LogToScreen => 'debug' ); +RT->Config->Set( LogToSTDERR => 'debug' ); RT->Config->Set( UseTransactionBatch => 1 ); my $q = RT::Queue->new(RT->SystemUser); @@ -33,7 +28,6 @@ my $approvals = Queue: ___Approvals Type: approval Owner: CFO -Requestors: {$Tickets{"TOP"}->Requestors} Refers-To: TOP Subject: CFO Approval for PO: {$Tickets{"TOP"}->Id} - {$Tickets{"TOP"}->Subject} Due: {time + 86400} @@ -46,7 +40,6 @@ ENDOFCONTENT Queue: ___Approvals Type: approval Owner: CEO -Requestors: {$Tickets{"TOP"}->Requestors} Subject: PO approval request for {$Tickets{"TOP"}->Subject} Refers-To: TOP Depends-On: for-CFO @@ -86,14 +79,17 @@ mail_ok { $t->Create(Subject => "PO for stationary", Owner => "root", Requestor => 'minion', Queue => $q->Id); -} { from => qr/RT System/, - to => 'cfo@company.com', - subject => qr/New Pending Approval: CFO Approval/, - body => qr/pending your approval.*Your approval is requested.*Blah/s -},{ from => qr/PO via RT/, +} { from => qr/PO via RT/, to => 'minion@company.com', subject => qr/PO for stationary/, body => qr/automatically generated in response/ +},{ from => qr/RT System/, + to => 'root@localhost', + subject => qr/PO for stationary/, +}, { from => qr/RT System/, + to => 'cfo@company.com', + subject => qr/New Pending Approval: CFO Approval/, + body => qr/pending your approval.*Your approval is requested.*Blah/s }; ok ($tid,$tmsg); @@ -137,6 +133,9 @@ mail_ok { subject => qr/New Pending Approval: PO approval request for PO/, body => qr/pending your approval.*CFO approved.*ok with that\?/s },{ from => qr/RT System/, + to => 'root@localhost', + subject => qr/Ticket Approved:/, +},{ from => qr/RT System/, to => 'minion@company.com', subject => qr/Ticket Approved:/, body => qr/approved by CFO.*notes: Resources exist to be consumed/s @@ -165,10 +164,14 @@ mail_ok { ok($ok, "ceo can approve - $msg"); } { from => qr/RT System/, + to => 'root@localhost', + subject => qr/Ticket Approved:/, + body => qr/approved by CEO.*Its Owner may now start to act on it.*notes: And consumed they will be/s, +},{ from => qr/RT System/, to => 'minion@company.com', subject => qr/Ticket Approved:/, body => qr/approved by CEO.*Its Owner may now start to act on it.*notes: And consumed they will be/s, -}, { from => qr/CEO via RT/, +},{ from => qr/CEO via RT/, to => 'root@localhost', subject => qr/Ticket Approved/, body => qr/The ticket has been approved, you may now start to act on it/, @@ -203,6 +206,10 @@ mail_ok { ok($ok, "cfo can approve - $msg"); } { from => qr/RT System/, + to => 'root@localhost', + subject => qr/Ticket Rejected: PO for stationary/, + body => qr/rejected by CFO.*out of resources/s, +},{ from => qr/RT System/, to => 'minion@company.com', subject => qr/Ticket Rejected: PO for stationary/, body => qr/rejected by CFO.*out of resources/s, @@ -212,3 +219,4 @@ $t->Load($t->id);$dependson_ceo->Load($dependson_ceo->id); is_deeply([ $t->Status, $dependson_cfo->Status, $dependson_ceo->Status ], [ 'rejected', 'rejected', 'deleted'], 'ticket state after cfo rejection'); +done_testing; diff --git a/rt/t/articles/interface.t b/rt/t/articles/interface.t index 779127fa6..0aeefe7ca 100644 --- a/rt/t/articles/interface.t +++ b/rt/t/articles/interface.t @@ -20,11 +20,11 @@ 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'); + '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'); + 'Description' => 'Another general-purpose test class'); ok($ret, "Test class 2 created"); @@ -36,34 +36,34 @@ 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); + '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); + '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); + '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); + '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); + '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 ); + 'Name' => 'tlaTestTopicGlobal-'.$$, + 'ObjectType' => 'RT::System', + 'ObjectId' => $RT::System->Id ); ok($ret, "Global Topic created"); # Create some article custom fields @@ -71,18 +71,18 @@ ok($ret, "Global Topic created"); 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); + '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); + '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 @@ -93,13 +93,13 @@ 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'); + '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. @@ -108,36 +108,36 @@ 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'}, - ); + 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'}, - ); + 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'}, - ); + 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'}, - ); + 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. @@ -152,8 +152,8 @@ 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); + Subject => 'test ticket for articles '.$$, + MIMEObj => $parser->Entity); ok($ret, "Test ticket for articles created: $msg"); @@ -161,7 +161,7 @@ ok($ret, "Test ticket for articles created: $msg"); isa_ok($m, 'Test::WWW::Mechanize'); ok($m->login, 'logged in'); -$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! }, +$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/index.html! }, 'UI -> Articles' ); $m->content_contains($article3->Name); @@ -175,9 +175,9 @@ 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 } - ); + 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"); @@ -185,7 +185,7 @@ $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"); + "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); @@ -203,7 +203,7 @@ $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"); + "Article references originating ticket"); diag("Test creating a ticket in Class2 and make sure we don't see Class1 Topics") if $ENV{TEST_VERBOSE}; { diff --git a/rt/t/articles/search-interface.t b/rt/t/articles/search-interface.t index e957a6c31..3c75268b9 100644 --- a/rt/t/articles/search-interface.t +++ b/rt/t/articles/search-interface.t @@ -2,7 +2,7 @@ use strict; use warnings; -use RT::Test tests => 44; +use RT::Test tests => undef; use RT::CustomField; use RT::Queue; @@ -16,36 +16,39 @@ my ($url, $m) = RT::Test->started_ok; # Variables to test return values my ($ret, $msg); -# Create a test class +# Create two classes my $class = RT::Class->new($RT::SystemUser); -($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$, - 'Description' => 'A general-purpose test class'); +($ret, $msg) = $class->Create('Name' => 'First-class', + 'Description' => 'A general-purpose test class'); ok($ret, "Test class created"); +($ret, $msg) = $class->Create('Name' => 'Second-class', + 'Description' => 'Another 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); + '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); + '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); + '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 @@ -60,18 +63,18 @@ my $global_queue = RT::Queue->new($RT::SystemUser); 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' => 'This is an answer that is longer than 255 ' - . 'characters so these tests will be sure to use the LargeContent ' - . 'SQL as well as the normal SQL that would be generated if this ' - . 'was an answer that was shorter than 255 characters. This second ' - . 'sentence has a few extra characters to get this string to go ' - . 'over the 255 character boundary. Lorem ipsum.'); + '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' => 'This is an answer that is longer than 255 ' + . 'characters so these tests will be sure to use the LargeContent ' + . 'SQL as well as the normal SQL that would be generated if this ' + . 'was an answer that was shorter than 255 characters. This second ' + . 'sentence has a few extra characters to get this string to go ' + . 'over the 255 character boundary. Lorem ipsum.'); # Create an article or two with our custom field values. @@ -80,32 +83,32 @@ 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'}, - ); + 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'}, - ); + 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'}, - ); + 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'}, - ); + 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'); @@ -140,6 +143,9 @@ TODO:{ $m->text_contains('hoi polloi 4'); } +undef $m; +done_testing; + # When you send $m to this sub, it must be on a page with # a Search link. sub DoArticleSearch{ @@ -147,7 +153,7 @@ sub DoArticleSearch{ my $class_name = shift; my $search_text = shift; - $m->follow_link_ok( {text => 'Search'}, 'Articles -> Search'); + $m->follow_link_ok( {text => 'Articles'}, 'Articles Search'); $m->follow_link_ok( {text => 'in class '. $class_name}, 'Articles in class '. $class_name); $m->text_contains('First article'); diff --git a/rt/t/articles/set-subject.t b/rt/t/articles/set-subject.t new file mode 100644 index 000000000..9b9ff850a --- /dev/null +++ b/rt/t/articles/set-subject.t @@ -0,0 +1,110 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +use RT::CustomField; +use RT::EmailParser; +use RT::Queue; +use RT::Ticket; +use_ok 'RT::Class'; +use_ok 'RT::Topic'; +use_ok 'RT::Article'; + +# Variables to test return values +my ($ret, $msg); + +# Create a test class +my $class = RT::Class->new($RT::SystemUser); +($ret, $msg) = $class->Create('Name' => 'TestClass-'.$$, + 'Description' => 'A general-purpose test class'); +ok($ret, "Test class created: $msg"); +# because id 0 represents global, it uses an empty Queue object... +($ret, $msg) = $class->AddToObject(RT::Queue->new($RT::SystemUser)); +ok($ret, "Applied Class globally: $msg"); + +# Create some article custom fields +my $bodyCF = RT::CustomField->new($RT::SystemUser); +my $subjectCF = RT::CustomField->new($RT::SystemUser); +($ret, $msg) = $subjectCF->Create('Name' => 'Subject-'.$$, + 'Type' => 'Text', + 'MaxValues' => 1, + 'LookupType' => 'RT::Class-RT::Article', + 'Description' => 'The subject to be answered', + 'Disabled' => 0); +ok($ret, "Question CF created: $msg"); +($ret, $msg) = $bodyCF->Create('Name' => 'Body-'.$$, + 'Type' => 'Text', + 'MaxValues' => 1, + 'LookupType' => 'RT::Class-RT::Article', + 'Description' => 'The body to the subject', + 'Disabled' => 0); +ok($ret, "Answer CF created: $msg"); +my ($sid, $bid) = ($subjectCF->Id, $bodyCF->Id); + +# Attach the custom fields to our class +($ret, $msg) = $subjectCF->AddToObject($class); +ok($ret, "Subject CF added to class: $msg"); +($ret, $msg) = $bodyCF->AddToObject($class); +ok($ret, "Body CF added to class: $msg"); + +my $article = RT::Article->new($RT::SystemUser); +($ret, $msg) = $article->Create(Name => 'First article '.$$, + Summary => 'blah blah 1', + Class => $class->Id, + "CustomField-$bid" => 'This goes in the body', + "CustomField-$sid" => 'This clobbers your subject', + ); +ok($ret, "article 1 created: $msg"); + +# 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. + +my ($url, $m) = RT::Test->started_ok; +ok($m->login, 'logged in'); + +$m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket->id, + 'ticket update page' ); +is($m->form_number(3)->find_input('UpdateSubject')->value,$ticket->Subject,'Ticket Subject Found'); +$m->submit_form( + form_number => 3, + fields => { 'Articles-Include-Article-Named' => $article->Id }, + button => 'Go', +); +is($m->form_number(3)->find_input('UpdateSubject')->value,$ticket->Subject,'Ticket Subject Not Clobbered'); + +$m->get_ok("$url/Admin/Articles/Classes/"); +$m->follow_link_ok( { text => 'TestClass-'.$$ } ); +$m->submit_form_ok({ + form_number => 3, + fields => { SubjectOverride => $sid }, +}); +$m->content_contains("Added Subject Override: Subject-$$"); + +$m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket->id, + 'ticket update page' ); +is($m->form_number(3)->find_input('UpdateSubject')->value,$ticket->Subject,'Ticket Subject Found'); +$m->submit_form( + form_number => 3, + fields => { 'Articles-Include-Article-Named' => $article->Name }, + button => 'Go', +); +is($m->form_number(3)->find_input('UpdateSubject')->value,$article->FirstCustomFieldValue("Subject-$$"),'Ticket Subject Clobbered'); +undef $m; +done_testing; diff --git a/rt/t/articles/upload-customfields.t b/rt/t/articles/upload-customfields.t index e5ed5d190..29bd677ca 100644 --- a/rt/t/articles/upload-customfields.t +++ b/rt/t/articles/upload-customfields.t @@ -8,12 +8,12 @@ use RT; my $logo; BEGIN { $logo = - -e $RT::MasonComponentRoot . '/NoAuth/images/bpslogo.png' + -e $RT::StaticPath . '/images/bpslogo.png' ? 'bpslogo.png' : 'bplogo.gif'; } -use constant ImageFile => $RT::MasonComponentRoot . "/NoAuth/images/$logo"; +use constant ImageFile => $RT::StaticPath . "/images/$logo"; use constant ImageFileContent => do { local $/; @@ -25,14 +25,14 @@ use constant ImageFileContent => do { use RT::Class; my $class = RT::Class->new($RT::SystemUser); my ($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$, - 'Description' => 'A general-purpose test class'); + '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->follow_link_ok( { text => 'Admin' } ); $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'); diff --git a/rt/t/articles/uri-articles.t b/rt/t/articles/uri-articles.t index b6a1c8dac..9ad4b0724 100644 --- a/rt/t/articles/uri-articles.t +++ b/rt/t/articles/uri-articles.t @@ -2,7 +2,8 @@ use strict; use warnings; -use RT::Test tests => 9; +use RT::Test tests => undef; +use Test::Warn; use_ok "RT::URI::fsck_com_article"; my $uri = RT::URI::fsck_com_article->new( $RT::SystemUser ); @@ -12,7 +13,7 @@ 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/'; +is $uri->LocalURIPrefix, 'fsck.com-article://example.com'; my $class = RT::Class->new( $RT::SystemUser ); $class->Create( Name => 'URItest - '. $$ ); @@ -26,5 +27,24 @@ my ($id, $msg) = $article->Create( ok($id,$msg); $uri = RT::URI::fsck_com_article->new( $article->CurrentUser ); -is $uri->LocalURIPrefix . $article->id, $uri->URIForObject( $article ); +is $uri->URIForObject( $article ), + 'fsck.com-article://example.com/article/' . $article->id, + 'Got correct URIForObject'; +my $article_id = $article->Id; +ok ($uri->ParseURI("fsck.com-article://example.com/article/$article_id"), + 'Parsed URI'); +ok ($article->Delete(), 'Deleted article'); + +my $ret; +warning_like { + $ret = $uri->ParseURI("fsck.com-article://example.com/article/$article_id"); +} qr/Unable to load article for id $article_id. It may have been deleted/, + "Warned about missing article"; + +ok (!$ret, 'Returned false on missing article'); + +ok (!$uri->ParseURI("fsck.com-article://foo.com/article/$article_id"), + 'ParseURI returned false with incorrect Organization'); + +done_testing(); diff --git a/rt/t/charts/basics.t b/rt/t/charts/basics.t new file mode 100644 index 000000000..0f93e5a24 --- /dev/null +++ b/rt/t/charts/basics.t @@ -0,0 +1,91 @@ +use strict; +use warnings; + +use RT::Test tests => undef; +use RT::Ticket; + +my $q = RT::Test->load_or_create_queue( Name => 'General' ); +ok $q && $q->id, 'loaded or created queue'; +my $queue = $q->Name; + +my @tickets = add_tix_from_data( + { Subject => 'n', Status => 'new' }, + { Subject => 'o', Status => 'open' }, + { Subject => 'o', Status => 'open' }, + { Subject => 'r', Status => 'resolved' }, + { Subject => 'r', Status => 'resolved' }, + { Subject => 'r', Status => 'resolved' }, +); + +use_ok 'RT::Report::Tickets'; + +{ + my $report = RT::Report::Tickets->new( RT->SystemUser ); + my %columns = $report->SetupGroupings( + Query => 'Queue = '. $q->id, + GroupBy => ['Status'], + Function => ['COUNT'], + ); + $report->SortEntries; + + my @colors = RT->Config->Get("ChartColors"); + my $expected = { + 'thead' => [ { + 'cells' => [ + { 'value' => 'Status', 'type' => 'head' }, + { 'rowspan' => 1, 'value' => 'Ticket count', 'type' => 'head', 'color' => $colors[0] }, + ], + } ], + 'tfoot' => [ { + 'cells' => [ + { 'colspan' => 1, 'value' => 'Total', 'type' => 'label' }, + { 'value' => 6, 'type' => 'value' }, + ], + 'even' => 0 + } ], + 'tbody' => [ + { + 'cells' => [ + { 'value' => 'new', 'type' => 'label' }, + { 'query' => '(Status = \'new\')', 'value' => '1', 'type' => 'value' }, + ], + 'even' => 1 + }, + { + 'cells' => [ + { 'value' => 'open', 'type' => 'label' }, + { 'query' => '(Status = \'open\')', 'value' => '2', 'type' => 'value' } + ], + 'even' => 0 + }, + { + 'cells' => [ + { 'value' => 'resolved', 'type' => 'label' }, + { 'query' => '(Status = \'resolved\')', 'value' => '3', 'type' => 'value' } + ], + 'even' => 1 + }, + ] + }; + + my %table = $report->FormatTable( %columns ); + is_deeply( \%table, $expected, "basic table" ); +} + +done_testing; + + +sub add_tix_from_data { + my @data = @_; + my @res = (); + while (@data) { + my %info = %{ shift(@data) }; + my $t = RT::Ticket->new($RT::SystemUser); + my ( $id, undef, $msg ) = $t->Create( Queue => $q->id, %info ); + ok( $id, "ticket created" ) or diag("error: $msg"); + is $t->Status, $info{'Status'}, 'correct status'; + push @res, $t; + } + return @res; +} + diff --git a/rt/t/charts/compound-sql-function.t b/rt/t/charts/compound-sql-function.t new file mode 100644 index 000000000..3e6a3fdfe --- /dev/null +++ b/rt/t/charts/compound-sql-function.t @@ -0,0 +1,121 @@ + +use strict; +use warnings; + +use RT::Test tests => 14; +use RT::Ticket; + +my $q1 = RT::Test->load_or_create_queue( Name => 'One' ); +ok $q1 && $q1->id, 'loaded or created queue'; + +my $q2 = RT::Test->load_or_create_queue( Name => 'Two' ); +ok $q2 && $q2->id, 'loaded or created queue'; + +my @tickets = add_tix_from_data( + { Queue => $q1->id, Resolved => 3*60 }, + { Queue => $q1->id, Resolved => 3*60*60 }, + { Queue => $q1->id, Resolved => 3*24*60*60 }, + { Queue => $q1->id, Resolved => 3*30*24*60*60 }, + { Queue => $q1->id, Resolved => 9*30*24*60*60 }, + { Queue => $q2->id, Resolved => 7*60 }, + { Queue => $q2->id, Resolved => 7*60*60 }, + { Queue => $q2->id, Resolved => 7*24*60*60 }, + { Queue => $q2->id, Resolved => 7*30*24*60*60 }, + { Queue => $q2->id, Resolved => 24*30*24*60*60 }, +); + +use_ok 'RT::Report::Tickets'; + +{ + my $report = RT::Report::Tickets->new( RT->SystemUser ); + my %columns = $report->SetupGroupings( + Query => 'id > 0', + GroupBy => ['Queue'], + Function => ['ALL(Created-Resolved)'], + ); + $report->SortEntries; + + my @colors = RT->Config->Get("ChartColors"); + my $expected = { + 'thead' => [ + { + 'cells' => [ + { 'rowspan' => 2, 'value' => 'Queue', 'type' => 'head' }, + { 'colspan' => 4, 'value' => 'Summary of Created-Resolved', 'type' => 'head' } + ] + }, + { + 'cells' => [ + { 'value' => 'Minimum', 'type' => 'head', 'color' => $colors[0] }, + { 'value' => 'Average', 'type' => 'head', 'color' => $colors[1] }, + { 'value' => 'Maximum', 'type' => 'head', 'color' => $colors[2] }, + { 'value' => 'Total', 'type' => 'head', 'color' => $colors[3] } + ] + } + ], + 'tfoot' => [ + { + 'cells' => [ + { 'colspan' => 1, 'value' => 'Total', 'type' => 'label' }, + { 'value' => '10m', 'type' => 'value' }, + { 'value' => '8M 2W 3d', 'type' => 'value' }, + { 'value' => '2Y 8M 2W', 'type' => 'value' }, + { 'value' => '3Y 6M 3W', 'type' => 'value' } + ], + 'even' => 1 + } + ], + 'tbody' => [ + { + 'cells' => [ + { 'value' => 'One', 'type' => 'label' }, + { 'query' => '(Queue = 3)', 'value' => '3m', 'type' => 'value' }, + { 'query' => '(Queue = 3)', 'value' => '2M 1W 5d', 'type' => 'value' }, + { 'query' => '(Queue = 3)', 'value' => '8M 3W 6d', 'type' => 'value' }, + { 'query' => '(Queue = 3)', 'value' => '11M 4W 8h', 'type' => 'value' } + ], + 'even' => 1 + }, + { + 'cells' => [ + { 'value' => 'Two', 'type' => 'label' }, + { 'query' => '(Queue = 4)', 'value' => '7m', 'type' => 'value' }, + { 'query' => '(Queue = 4)', 'value' => '6M 4d 20h', 'type' => 'value' }, + { 'query' => '(Queue = 4)', 'value' => '1Y 11M 3W', 'type' => 'value' }, + { 'query' => '(Queue = 4)', 'value' => '2Y 6M 3W', 'type' => 'value' } + ], + 'even' => 0 + } + ] + }; + + my %table = $report->FormatTable( %columns ); + is_deeply( \%table, $expected, "basic table" ); +} + + +sub add_tix_from_data { + my @data = @_; + my @res = (); + + my $created = RT::Date->new( $RT::SystemUser ); + $created->SetToNow; + + my $resolved = RT::Date->new( $RT::SystemUser ); + + while (@data) { + $resolved->Set( Value => $created->Unix ); + $resolved->AddSeconds( $data[0]{'Resolved'} ); + my $t = RT::Ticket->new($RT::SystemUser); + my ( $id, undef $msg ) = $t->Create( + %{ shift(@data) }, + Created => $created->ISO, + Resolved => $resolved->ISO, + ); + ok( $id, "ticket created" ) or diag("error: $msg"); + push @res, $t; + } + return @res; +} + + diff --git a/rt/t/charts/group-by-cf.t b/rt/t/charts/group-by-cf.t new file mode 100644 index 000000000..f6bfb35e4 --- /dev/null +++ b/rt/t/charts/group-by-cf.t @@ -0,0 +1,71 @@ +use strict; +use warnings; + +use RT::Test tests => undef; +use RT::Ticket; + +my $q = RT::Test->load_or_create_queue( Name => 'General' ); +ok $q && $q->id, 'loaded or created queue'; +my $queue = $q->Name; + +my $cf = RT::CustomField->new(RT->SystemUser); +my ($id,$msg) = $cf->Create(Name => 'Test', Type => 'Freeform', MaxValues => '1', Queue => $q->id); +ok $id, $msg; +my $cfid = $cf->id; + + +my @tickets = RT::Test->create_tickets( + {}, + { Subject => 't1', Status => 'new', CustomFields => { Test => 'a' } }, + { Subject => 't2', Status => 'open', CustomFields => { Test => 'b' } }, +); + +use_ok 'RT::Report::Tickets'; + +{ + my $report = RT::Report::Tickets->new( RT->SystemUser ); + my %columns = $report->SetupGroupings( + Query => 'Queue = '. $q->id, + GroupBy => ["CF.{$cfid}"], # TODO: CF.{Name} is not supported at the moment + Function => ['COUNT'], + ); + $report->SortEntries; + + my @colors = RT->Config->Get("ChartColors"); + my $expected = { + 'thead' => [ { + 'cells' => [ + { 'value' => 'Custom field Test', 'type' => 'head' }, + { 'rowspan' => 1, 'value' => 'Ticket count', 'type' => 'head', 'color' => $colors[0] }, + ], + } ], + 'tfoot' => [ { + 'cells' => [ + { 'colspan' => 1, 'value' => 'Total', 'type' => 'label' }, + { 'value' => 2, 'type' => 'value' }, + ], + 'even' => 1 + } ], + 'tbody' => [ + { + 'cells' => [ + { 'value' => 'a', 'type' => 'label' }, + { 'query' => "(CF.{$cfid} = 'a')", 'value' => '1', 'type' => 'value' }, + ], + 'even' => 1 + }, + { + 'cells' => [ + { 'value' => 'b', 'type' => 'label' }, + { 'query' => "(CF.{$cfid} = 'b')", 'value' => '1', 'type' => 'value' }, + ], + 'even' => 0 + }, + ] + }; + + my %table = $report->FormatTable( %columns ); + is_deeply( \%table, $expected, "basic table" ); +} + +done_testing; diff --git a/rt/t/crypt/gnupg/attachments-in-db.t b/rt/t/crypt/gnupg/attachments-in-db.t new file mode 100644 index 000000000..1a377c341 --- /dev/null +++ b/rt/t/crypt/gnupg/attachments-in-db.t @@ -0,0 +1,49 @@ +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 12, + gnupg_options => { + passphrase => 'recipient', + 'trust-model' => 'always', + } +; + +RT->Config->Get('Crypt')->{'AllowEncryptDataInDB'} = 1; + +RT::Test->import_gnupg_key('general@example.com', 'public'); +RT::Test->import_gnupg_key('general@example.com', 'secret'); +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'general@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue->id, + Subject => 'test', + Content => 'test', + ); + + my $txn = $ticket->Transactions->First; + ok $txn && $txn->id, 'found first transaction'; + is $txn->Type, 'Create', 'it is Create transaction'; + + my $attach = $txn->Attachments->First; + ok $attach && $attach->id, 'found attachment'; + is $attach->Content, 'test', 'correct content'; + + my ($status, $msg) = $attach->Encrypt; + ok $status, 'encrypted attachment'; + + isnt $attach->Content, 'test', 'correct content'; + + ($status, $msg) = $attach->Decrypt; + ok $status, 'decrypted attachment'; + + is $attach->Content, 'test', 'correct content'; +} + + + diff --git a/rt/t/crypt/no-signer-address.t b/rt/t/crypt/no-signer-address.t new file mode 100644 index 000000000..31ba5ebc2 --- /dev/null +++ b/rt/t/crypt/no-signer-address.t @@ -0,0 +1,42 @@ +use strict; +use warnings; + +use RT::Test::GnuPG + tests => undef, + gnupg_options => { + passphrase => 'rt-test', + 'trust-model' => 'always', + } +; + +my $queue; +{ + $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + SignAuto => 1, + ); + ok $queue && $queue->id, 'loaded or created queue'; + ok !$queue->CorrespondAddress, 'address not set'; +} + +# We don't use Test::Warn here, because it apparently only captures up +# to the first newline -- and the meat of this message is on the fourth +# line. +my @warnings; +local $SIG{__WARN__} = sub { + push @warnings, "@_"; +}; + +my $ticket = RT::Ticket->new( RT->SystemUser ); +my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'root@localhost', +); +ok( $status, "created ticket" ) or diag "error: $msg"; + +is( scalar @warnings, 1, "Got a warning" ); +like( $warnings[0], qr{signing failed: secret key not available}, + "Found warning of no secret key"); + +done_testing; diff --git a/rt/t/crypt/smime/attachments-in-db.t b/rt/t/crypt/smime/attachments-in-db.t new file mode 100644 index 000000000..5230938cc --- /dev/null +++ b/rt/t/crypt/smime/attachments-in-db.t @@ -0,0 +1,45 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef; + +use IPC::Run3 'run3'; +use String::ShellQuote 'shell_quote'; +use RT::Tickets; + +RT->Config->Get('Crypt')->{'AllowEncryptDataInDB'} = 1; + +RT::Test::SMIME->import_key('sender@example.com'); +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue->id, + Subject => 'test', + Content => 'test', + ); + + my $txn = $ticket->Transactions->First; + ok $txn && $txn->id, 'found first transaction'; + is $txn->Type, 'Create', 'it is Create transaction'; + + my $attach = $txn->Attachments->First; + ok $attach && $attach->id, 'found attachment'; + is $attach->Content, 'test', 'correct content'; + + my ($status, $msg) = $attach->Encrypt; + ok $status, 'encrypted attachment' or diag "error: $msg"; + + isnt $attach->Content, 'test', 'correct content'; + + ($status, $msg) = $attach->Decrypt; + ok $status, 'decrypted attachment' or diag "error: $msg"; + + is $attach->Content, 'test', 'correct content'; +} + +done_testing; diff --git a/rt/t/crypt/smime/bad-recipients.t b/rt/t/crypt/smime/bad-recipients.t new file mode 100644 index 000000000..1dc097ade --- /dev/null +++ b/rt/t/crypt/smime/bad-recipients.t @@ -0,0 +1,58 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef; + +use RT::Tickets; + +RT::Test::SMIME->import_key('sender@example.com'); +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +{ + my ($status, $msg) = $queue->SetEncrypt(1); + ok $status, "turn on encyption by default" + or diag "error: $msg"; +} + +my $root; +{ + $root = RT::User->new($RT::SystemUser); + ok($root->LoadByEmail('root@localhost'), "Loaded user 'root'"); + ok($root->Load('root'), "Loaded user 'root'"); + is($root->EmailAddress, 'root@localhost'); + + RT::Test::SMIME->import_key( 'root@example.com.crt' => $root ); +} + +my $bad_user; +{ + $bad_user = RT::Test->load_or_create_user( + Name => 'bad_user', + EmailAddress => 'baduser@example.com', + ); + ok $bad_user && $bad_user->id, 'created a user without key'; +} + +RT::Test->clean_caught_mails; + +use Test::Warn; + +warnings_like { + my $ticket = RT::Ticket->new(RT->SystemUser); + my ($status, undef, $msg) = $ticket->Create( Queue => $queue->id, Requestor => [$root->id, $bad_user->id] ); + ok $status, "created a ticket" or diag "error: $msg"; + + my @mails = RT::Test->fetch_caught_mails; + is scalar @mails, 3, "autoreply, to bad user, to RT owner"; + + like $mails[0], qr{To: baduser\@example\.com}, "notification to bad user"; + like $mails[1], qr{To: root}, "notification to RT owner"; + like $mails[1], qr{Recipient 'baduser\@example\.com' is unusable, the reason is 'Key not found'}, + "notification to owner has error"; +} [qr{Recipient 'baduser\@example\.com' is unusable, the reason is 'Key not found'}]; + +done_testing; diff --git a/rt/t/crypt/smime/status-string.t b/rt/t/crypt/smime/status-string.t new file mode 100644 index 000000000..9317229fb --- /dev/null +++ b/rt/t/crypt/smime/status-string.t @@ -0,0 +1,26 @@ +use strict; +use warnings; + +use RT::Test tests => 2; + +require RT::Crypt::SMIME; +note "simple round trip"; +{ + my %data = (Foo => 'bar', Baz => 'zoo'); + is_deeply( + [ RT::Crypt::SMIME->ParseStatus( RT::Crypt::SMIME->FormatStatus( \%data, \%data ) ) ], + [ \%data, \%data ], + ); +} + +note "status appendability"; +{ + my %data = (Foo => 'bar', Baz => 'zoo'); + is_deeply( + [ RT::Crypt::SMIME->ParseStatus( + RT::Crypt::SMIME->FormatStatus( \%data ) + . RT::Crypt::SMIME->FormatStatus( \%data ) + ) ], + [ \%data, \%data ], + ); +} diff --git a/rt/t/customfields/access_via_queue.t b/rt/t/customfields/access_via_queue.t index a059d69ee..300e777b8 100644 --- a/rt/t/customfields/access_via_queue.t +++ b/rt/t/customfields/access_via_queue.t @@ -28,11 +28,8 @@ my $tester = RT::Test->load_or_create_user( ); ok $tester && $tester->id, 'loaded or created user'; -my $cc_role = RT::Group->new( $queue->CurrentUser ); -$cc_role->LoadQueueRoleGroup( Type => 'Cc', Queue => $queue->id ); - -my $owner_role = RT::Group->new( $queue->CurrentUser ); -$owner_role->LoadQueueRoleGroup( Type => 'Owner', Queue => $queue->id ); +my $cc_role = $queue->RoleGroup( 'Cc' ); +my $owner_role = $queue->RoleGroup( 'Owner' ); ok( RT::Test->set_rights( { Principal => $tester, Right => [qw(SeeQueue ShowTicket CreateTicket ReplyToTicket Watch OwnTicket TakeTicket)] }, diff --git a/rt/t/customfields/api.t b/rt/t/customfields/api.t index 2e1c07986..a50ca770c 100644 --- a/rt/t/customfields/api.t +++ b/rt/t/customfields/api.t @@ -5,12 +5,12 @@ use warnings FATAL => 'all'; use RT::Test nodata => 1, tests => 145; use Test::Warn; -# Before we get going, ditch all object_cfs; this will remove +# 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(); + $ocf->Delete(); } @@ -23,9 +23,9 @@ $queue2->Create( Name => 'RecordCustomFields2' ); my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Create( - Queue => $queue->Id, - Requestor => 'root@localhost', - Subject => 'RecordCustomFields1', + Queue => $queue->Id, + Requestor => 'root@localhost', + Subject => 'RecordCustomFields1', ); my $cfs = $ticket->CustomFields; @@ -86,13 +86,13 @@ warning_like { } qr{Couldn't load custom field}; for (@custom_fields) { - $cfvs = $ticket->CustomFieldValues( $_->id ); - is( $cfvs->Count, 0 ); + $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 ); + $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 {{{ @@ -103,84 +103,84 @@ 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 ); - } - + 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' ); + # 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" ); - } + 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 @@ -224,10 +224,10 @@ warning_like { } #SKIP: { -# skip "TODO: should we add CF values to objects via CF Name?", 48; +# 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 } ); + # lets test cycle via CF Name +# $test_add_delete_cycle->( sub { return $_[0]->Name } ); #} diff --git a/rt/t/customfields/date_search.t b/rt/t/customfields/date_search.t index 2a8e6ce7a..e9a5a5e76 100644 --- a/rt/t/customfields/date_search.t +++ b/rt/t/customfields/date_search.t @@ -3,7 +3,7 @@ use Test::MockTime qw(set_fixed_time restore_time); use warnings; use strict; -use RT::Test nodata => 1, tests => 21; +use RT::Test nodata => 1, tests => undef; RT::Test->set_rights( { Principal => 'Everyone', Right => [qw( @@ -16,9 +16,11 @@ ok $q && $q->id, 'loaded or created a queue'; my $user_m = RT::Test->load_or_create_user( Name => 'moscow', Timezone => 'Europe/Moscow' ); ok $user_m && $user_m->id; +$user_m = RT::CurrentUser->new( $user_m ); my $user_b = RT::Test->load_or_create_user( Name => 'boston', Timezone => 'America/New_York' ); ok $user_b && $user_b->id; +$user_b = RT::CurrentUser->new( $user_b ); my $cf = RT::CustomField->new(RT->SystemUser); ok( @@ -132,6 +134,28 @@ is( $ticket->CustomFieldValues->First->Content, '2010-05-04', 'date in db is' ); 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 => 'IS', + VALUE => 'NULL', + ); + + is( $tickets->Count, 0, 'did not find the ticket with date IS NULL' ); +} + +{ + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => 'IS NOT', + VALUE => 'NULL', + ); + + is( $tickets->Count, 1, 'did find the ticket with date IS NOT NULL' ); +} + # relative search by users in different TZs { my $ticket = RT::Ticket->new(RT->SystemUser); @@ -162,3 +186,4 @@ is( $ticket->CustomFieldValues->First->Content, '2010-05-04', 'date in db is' ); is( $tickets->Count, 1, 'found the tickets' ); } +done_testing; diff --git a/rt/t/customfields/datetime_search.t b/rt/t/customfields/datetime_search.t index 6b37cf1bc..2eaa0e629 100644 --- a/rt/t/customfields/datetime_search.t +++ b/rt/t/customfields/datetime_search.t @@ -3,7 +3,7 @@ use Test::MockTime qw(set_fixed_time restore_time); use warnings; use strict; -use RT::Test nodata => 1, tests => 30; +use RT::Test nodata => 1, tests => undef; RT->Config->Set( 'Timezone' => 'EST5EDT' ); # -04:00 RT::Test->set_rights( @@ -17,9 +17,11 @@ ok $q && $q->id, 'loaded or created a queue'; my $user_m = RT::Test->load_or_create_user( Name => 'moscow', Timezone => 'Europe/Moscow' ); ok $user_m && $user_m->id; +$user_m = RT::CurrentUser->new( $user_m ); my $user_b = RT::Test->load_or_create_user( Name => 'boston', Timezone => 'America/New_York' ); ok $user_b && $user_b->id; +$user_b = RT::CurrentUser->new( $user_b ); my $cf = RT::CustomField->new(RT->SystemUser); ok( @@ -204,6 +206,29 @@ while( my $ticket = $tickets->Next ) { is( $tickets->Count, 0); } +{ + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => 'IS', + VALUE => 'NULL', + ); + + is( $tickets->Count, 0, 'did not find the ticket with date IS NULL' ); +} + +{ + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->LimitCustomField( + CUSTOMFIELD => $cf->id, + OPERATOR => 'IS NOT', + VALUE => 'NULL', + ); + + is( $tickets->Count, 2, 'did find the ticket with date IS NOT NULL' ); +} + + # search by relative date with '=', but date only { my $ticket = RT::Ticket->new(RT->SystemUser); @@ -235,3 +260,4 @@ while( my $ticket = $tickets->Next ) { is( $tickets->Count, 0); } +done_testing; diff --git a/rt/t/customfields/external.t b/rt/t/customfields/external.t index 73549166a..4b84144ee 100644 --- a/rt/t/customfields/external.t +++ b/rt/t/customfields/external.t @@ -3,7 +3,7 @@ use warnings; use strict; use RT; -use RT::Test nodata => 1, tests => 13; +use RT::Test nodata => 1, tests => undef; sub new (*) { my $class = shift; @@ -51,6 +51,10 @@ isa_ok( $cf, 'RT::CustomField' ); } ok( !$failure, "all values have name" ); is( $values->Count, $count, "count is correct" ); + is( $values->CustomFieldObject->id, $cf->id, "Values stored the CF id" ); + is( $values->CustomFieldObject, $cf, "Values stored the identical CF object" ); + is( $values->First->CustomFieldObj->id, $cf->id, "A value stored the CF id" ); + is( $values->First->CustomFieldObj, $cf, "A value stored the identical CF object" ); } { @@ -59,3 +63,5 @@ isa_ok( $cf, 'RT::CustomField' ); ($ret, $msg) = $cf->SetValuesClass('RT::CustomFieldValues::Groups'); ok $ret, 'Reverting this CF as external source values based' or diag "error: $msg"; } + +done_testing; diff --git a/rt/t/customfields/ip.t b/rt/t/customfields/ip.t index 37afcb71d..35a245c5e 100644 --- a/rt/t/customfields/ip.t +++ b/rt/t/customfields/ip.t @@ -26,7 +26,7 @@ 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' ); + $cfs->Limit( FIELD => 'Name', VALUE => 'IP', CASESENSITIVE => 0 ); is( $cfs->Count, 1, "found one CF with name 'IP'" ); $cf = $cfs->First; @@ -269,8 +269,8 @@ diag "create a ticket with an IP of 10.0.0.1 and search for doesn't match '10.0. $tickets->FromSQL("id=$id AND CF.{IP} NOT LIKE '10.0.0.'"); } [qr/not a valid IPAddress/], "caught warning about valid IP address"; - SKIP: { - skip "partical ip parse causes ambiguity", 1; + TODO: { + local $TODO = "partial ip parse causes ambiguity"; is( $tickets->Count, 0, "should not have found the ticket" ); } } @@ -281,8 +281,8 @@ 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" ); + 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 index 4bccd9ac1..2a323a352 100644 --- a/rt/t/customfields/iprange.t +++ b/rt/t/customfields/iprange.t @@ -21,7 +21,7 @@ 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' ); + $cfs->Limit( FIELD => 'Name', VALUE => 'IP', CASESENSITIVE => 0 ); is( $cfs->Count, 1, "found one CF with name 'IP'" ); $cf = $cfs->First; @@ -461,8 +461,8 @@ 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" ); + 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 index 84fec16a0..445df333c 100644 --- a/rt/t/customfields/iprangev6.t +++ b/rt/t/customfields/iprangev6.t @@ -21,7 +21,7 @@ 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' ); + $cfs->Limit( FIELD => 'Name', VALUE => 'IP', CASESENSITIVE => 0 ); is( $cfs->Count, 1, "found one CF with name 'IP'" ); $cf = $cfs->First; diff --git a/rt/t/customfields/ipv6.t b/rt/t/customfields/ipv6.t index 3b02ef9d7..24f7c2a48 100644 --- a/rt/t/customfields/ipv6.t +++ b/rt/t/customfields/ipv6.t @@ -26,7 +26,7 @@ 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' ); + $cfs->Limit( FIELD => 'Name', VALUE => 'IP', CASESENSITIVE => 0 ); is( $cfs->Count, 1, "found one CF with name 'IP'" ); $cf = $cfs->First; @@ -246,8 +246,8 @@ diag "create a ticket with an IP of abcd:23:: and search for doesn't match 'abcd $tickets->FromSQL("id=$id AND CF.{IP} NOT LIKE 'abcd:23'"); } [qr/not a valid IPAddress/], "caught warning about IPAddress"; - SKIP: { - skip "partical ip parse can causes ambiguity", 1; + TODO: { + local $TODO = "partial ip parse can causes ambiguity"; is( $tickets->Count, 0, "should not have found the ticket" ); } } diff --git a/rt/t/customfields/sort_order.t b/rt/t/customfields/sort_order.t index ba0b654be..24e047ebf 100644 --- a/rt/t/customfields/sort_order.t +++ b/rt/t/customfields/sort_order.t @@ -50,7 +50,7 @@ diag "reorder CFs: C, A and B"; { $m->get( '/Admin/Queues/' ); $m->follow_link_ok( {text => $queue->id} ); - $m->follow_link_ok( {id => 'page-ticket-custom-fields'} ); + $m->follow_link_ok( {id => 'page-custom-fields-tickets'} ); my @tmp = ($m->content =~ /(CF [ABC])/g); is_deeply(\@tmp, ['CF B', 'CF A', 'CF C']); diff --git a/rt/t/customfields/transaction.t b/rt/t/customfields/transaction.t index f2e660ee2..47435bc87 100644 --- a/rt/t/customfields/transaction.t +++ b/rt/t/customfields/transaction.t @@ -3,7 +3,7 @@ use warnings; use strict; use Data::Dumper; -use RT::Test nodata => 1, tests => 14; +use RT::Test nodata => 1, tests => 13; use_ok('RT'); use_ok('RT::Transactions'); @@ -43,17 +43,8 @@ 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"); +$trans->UpdateCustomFields( 'CustomField-'.$cf->id => 'Test'); +$values = $trans->CustomFieldValues($txn_cf->id); +is ($values->Count, 1, "it has a value"); # TODO ok(0, "Should updating custom field values remove old values?"); diff --git a/rt/t/customfields/transaction_searching.t b/rt/t/customfields/transaction_searching.t new file mode 100644 index 000000000..0958b5ea6 --- /dev/null +++ b/rt/t/customfields/transaction_searching.t @@ -0,0 +1,140 @@ +use strict; +use warnings; + +use RT::Test tests => 'no_declare'; + +my $initialdata = RT::Test::get_relocatable_file("transaction-cfs" => "..", "data", "initialdata"); +my ($rv, $msg) = RT->DatabaseHandle->InsertData( $initialdata, undef, disconnect_after => 0 ); +ok($rv, "Inserted test data from $initialdata") + or diag "Error: $msg"; + +create_tickets( + Spam => { }, + Coffee => { Billable => "No", }, + Phone => { Billable => "Yes", Who => ["Telecom", "Information Technology"], When => "2013-06-25", Location => "Geology" }, + Stacks => { Billable => "Yes", Who => "Library", When => "2013-06-01" }, + Benches => { Billable => "Yes", Location => "Outdoors" }, +); + +# Sanity check +results_are("CF.Location IS NOT NULL", [qw( Phone Benches )]); +results_are("CF.Location IS NULL", [qw( Spam Coffee Stacks )]); + +# TODO: Ideal behaviour of TxnCF IS NULL not yet determined +#results_are("TxnCF.Billable IS NULL", [qw( Spam )]); + +results_are("TxnCF.Billable IS NOT NULL", [qw( Coffee Phone Stacks Benches )]); +results_are("TxnCF.Billable = 'No'", [qw( Coffee )]); +results_are("TxnCF.Billable = 'Yes'", [qw( Phone Stacks Benches )]); +results_are("TxnCF.Billable = 'Yes' AND CF.Location IS NOT NULL", [qw( Phone Benches )]); +results_are("TxnCF.Billable = 'Yes' AND CF.Location = 'Outdoors'", [qw( Benches )]); +results_are("TxnCF.Billable = 'Yes' AND CF.Location LIKE 'o'", [qw( Phone Benches )]); + +results_are("TxnCF.Who = 'Telecom' OR TxnCF.Who = 'Library'", [qw( Phone Stacks )]); + +# TODO: Negative searching finds tickets with at least one txn doesn't have the value +#results_are("TxnCF.Who != 'Library'", [qw( Spam Coffee Phone Benches )]); + +results_are("TxnCF.When > '2013-06-24'", [qw( Phone )]); +results_are("TxnCF.When < '2013-06-24'", [qw( Stacks )]); +results_are("TxnCF.When >= '2013-06-01' and TxnCF.When <= '2013-06-30'", [qw( Phone Stacks )]); + +results_are("TxnCF.Who LIKE 'e'", [qw( Phone )]); + +# TODO: Negative searching finds tickets with at least one txn doesn't have the value +#results_are("TxnCF.Who NOT LIKE 'e'", [qw( Spam Coffee Stacks Benches )]); + +results_are("TxnCF.Who NOT LIKE 'e' and TxnCF.Who IS NOT NULL", [qw( Stacks )]); + + +# Multiple CFs with same name applied to different queues +clear_tickets(); +create_tickets( + BlueNone => { Queue => "Blues" }, + PurpleNone => { Queue => "Purples" }, + + Blue => { Queue => "Blues", Color => "Blue" }, + Purple => { Queue => "Purples", Color => "Purple" }, +); + +# Queue-specific txn CFs +results_are("TxnCF.Blues.{Color} = 'Blue'", [qw( Blue )]); +results_are("TxnCF.Blues.{Color} = 'Purple'", []); + +# Multiple transaction CFs by name +results_are("TxnCF.{Color} IS NOT NULL", [qw( Blue Purple )]); +results_are("TxnCF.{Color} = 'Blue'", [qw( Blue )]); +results_are("TxnCF.{Color} = 'Purple'", [qw( Purple )]); +results_are("TxnCF.{Color} LIKE 'e'", [qw( Blue Purple )]); + +done_testing; + +sub results_are { + local $Test::Builder::Level = $Test::Builder::Level + 1; + + my $query = shift; + my $expected = shift; + my %expected = map { $_ => 1 } @$expected; + my @unexpected; + + my $tickets = RT::Tickets->new(RT->SystemUser); + my ($ok, $msg) = $tickets->FromSQL($query); + ok($ok, "Searched: $query") + or return diag $msg; + for my $t (@{$tickets->ItemsArrayRef || []}) { + if (delete $expected{$t->Subject}) { + ok(1, "Found expected ticket ".$t->Subject); + } else { + push @unexpected, $t->Subject; + } + } + ok(0, "Didn't find expected ticket $_") + for grep $expected{$_}, @$expected; + ok(0, "Found unexpected tickets: ".join ", ", @unexpected) + if @unexpected; +} + +sub create_tickets { + my %ticket = @_; + for my $subject (sort keys %ticket) { + my %data = %{$ticket{$subject}}; + my $location = delete $data{Location}; + my $queue = delete $data{Queue} || "General"; + + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($ok, $msg) = $ticket->Create( + Queue => $queue, + Subject => $subject, + ); + ok($ticket->id, "Created ticket: $msg") or next; + + if ($location) { + ($ok, $msg) = $ticket->AddCustomFieldValue( Field => "Location", Value => $location ); + ok($ok, "Added Location: $msg") or next; + } + + my ($txnid, $txnmsg, $txn) = $ticket->Correspond( Content => "test transaction" ); + unless ($txnid) { + RT->Logger->error("Unable to correspond on ticket $ok: $txnmsg"); + next; + } + for my $name (sort keys %data) { + my $values = ref $data{$name} ? $data{$name} : [$data{$name}]; + for my $v (@$values) { + ($ok, $msg) = $txn->_AddCustomFieldValue( + Field => $name, + Value => $v, + RecordTransaction => 0 + ); + ok($ok, "Added txn CF $name value '$v'") + or diag $msg; + } + } + } +} + +sub clear_tickets { + my $tickets = RT::Tickets->new( RT->SystemUser ); + $tickets->FromSQL("id > 0"); + $_->SetStatus("deleted") for @{$tickets->ItemsArrayRef}; +} diff --git a/rt/t/data/configs/apache2.2+fastcgi.conf.in b/rt/t/data/configs/apache2.2+fastcgi.conf.in index 03eaa9a70..329b0561e 100644 --- a/rt/t/data/configs/apache2.2+fastcgi.conf.in +++ b/rt/t/data/configs/apache2.2+fastcgi.conf.in @@ -34,7 +34,6 @@ FastCgiServer %%RT_SBIN_PATH%%/rt-server.fcgi \ -initial-env RT_SITE_CONFIG=%%RT_SITE_CONFIG%% \ -initial-env RT_TESTING=1 -Alias /NoAuth/images/ %%DOCUMENT_ROOT%%/NoAuth/images/ ScriptAlias / %%RT_SBIN_PATH%%/rt-server.fcgi/ DocumentRoot "%%DOCUMENT_ROOT%%" diff --git a/rt/t/data/initialdata/initialdata b/rt/t/data/initialdata/initialdata new file mode 100644 index 000000000..19e019652 --- /dev/null +++ b/rt/t/data/initialdata/initialdata @@ -0,0 +1,101 @@ +# Samples of all things we support in initialdata + +@Queues = ( + { + Name => 'Test Queue', + CorrespondAddress => 'help@example.com', + CommentAddress => 'help-comment@example.com', + } +); + +@Scrips = ( + { + Description => 'Test Without Stage', + ScripCondition => 'On Resolve', + ScripAction => 'Notify Requestors', + Template => 'Correspondence in HTML', + }, + { + Queue => 'General', + Description => 'Test Without Stage and One Queue', + ScripCondition => 'On Resolve', + ScripAction => 'Notify Requestors', + Template => 'Correspondence in HTML', + }, + { + Queue => ['General', 'Test Queue'], + Description => 'Test Without Stage and Two Queues', + ScripCondition => 'On Resolve', + ScripAction => 'Notify Requestors', + Template => 'Correspondence in HTML', + }, + { + Description => 'Test TransactionCreate', + ScripCondition => 'On Resolve', + ScripAction => 'Notify Requestors', + Template => 'Correspondence in HTML', + Stage => 'TransactionCreate', + }, + { + Description => 'Test TransactionBatch', + ScripCondition => 'On Resolve', + ScripAction => 'Notify Requestors', + Template => 'Correspondence in HTML', + Stage => 'TransactionBatch', + }, +); + +@CustomFields = ( + { + Name => 'Favorite color', + Type => 'FreeformSingle', + LookupType => 'RT::Queue-RT::Ticket', + Queue => 'Test Queue', + }, +); + +@Groups = ( + { + Name => 'Test Employees', + Description => 'All of the employees of my company', + Attributes => [ + { + Name => 'SavedSearch', + Description => 'Stalled Tickets in Test Queue', + Content => { + Query => "Status = 'stalled' AND Queue = 'Test Queue'", + OrderBy => 'id', + Order => 'DESC' + }, + }, + ], + } +); + +@ACL = ( + { GroupId => 'Test Employees', + GroupDomain => 'UserDefined', + CF => 'Favorite Color', + Queue => 'Test Queue', + Right => ['SeeCustomField', 'ModifyCustomField'], + }, +); + +@Attributes = ({ + Name => 'SavedSearch', + Description => 'New Tickets in Test Queue', + Object => sub { + my $GroupName = 'Test Employees'; + my $group = RT::Group->new( RT->SystemUser ); + + my( $ret, $msg ) = $group->LoadUserDefinedGroup( $GroupName ); + die $msg unless $ret; + + return $group; + }, + Content => { + Query => "Status = 'new' AND Queue = 'Test Queue'", + OrderBy => 'id', + Order => 'DESC' + }, +}); diff --git a/rt/t/data/initialdata/transaction-cfs b/rt/t/data/initialdata/transaction-cfs new file mode 100644 index 000000000..25c8274ff --- /dev/null +++ b/rt/t/data/initialdata/transaction-cfs @@ -0,0 +1,52 @@ +use strict; +use warnings; + +our @Queues = ( + { Name => "Blues" }, + { Name => "Purples" }, +); + +our @CustomFields = ( + map +{ + LookupType => RT::Transaction->CustomFieldLookupType, + MaxValues => 1, + Type => "Freeform", + %$_ + }, + { Name => "Billable", + Type => "Select", + Values => [ + { Name => "Yes", SortOrder => 1 }, + { Name => "No", SortOrder => 2 }, + ], + }, + { Name => "Who", + Type => "SelectMultiple", + Values => [ + map +{ Name => $_ }, + "Facilities", + "Information Technology", + "Library", + "Telecom", + ], + }, + { Name => "When", + Type => "Date", + }, + + # Two CFs named the same, but each applied to only one queue + # Note: Queue => ref forces RT::Handle to apply rather than + # RT::CustomField->Create; the former respects LookupType, the latter + # doesn't. + { Name => "Color", + Queue => ["Blues"], + }, + { Name => "Color", + Queue => ["Purples"], + }, + + # Some ticket CFs to test mixed searches + { Name => "Location", + LookupType => RT::Ticket->CustomFieldLookupType, + }, +); diff --git a/rt/t/data/plugins/RT-Extension-PSGIWrap/lib/RT/Extension/PSGIWrap.pm b/rt/t/data/plugins/RT-Extension-PSGIWrap/lib/RT/Extension/PSGIWrap.pm new file mode 100644 index 000000000..1d0a55e33 --- /dev/null +++ b/rt/t/data/plugins/RT-Extension-PSGIWrap/lib/RT/Extension/PSGIWrap.pm @@ -0,0 +1,16 @@ +package RT::Extension::PSGIWrap; + +use base 'Plack::Middleware'; + +sub call { + my ( $self, $env ) = @_; + my $res = $self->app->($env); + return $self->response_cb( $res, sub { + my $headers = shift->[1]; + Plack::Util::header_set($headers, 'X-RT-PSGIWrap' => '1'); + } ); +} + +sub PSGIWrap { return shift->wrap(@_) } + +1; diff --git a/rt/t/data/smime/keys/demoCA/cacert.pem b/rt/t/data/smime/keys/demoCA/cacert.pem new file mode 100644 index 000000000..de734a98f --- /dev/null +++ b/rt/t/data/smime/keys/demoCA/cacert.pem @@ -0,0 +1,58 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 11236924883769032812 (0x9bf193a560cd006c) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com + Validity + Not Before: Aug 28 21:19:44 2013 GMT + Not After : Aug 26 21:19:44 2023 GMT + Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:be:cc:62:70:bf:42:ee:9d:f0:05:04:2b:05:46: + 4e:c9:60:6a:b4:31:8c:a5:60:25:79:05:61:88:fe: + 36:9e:63:24:bf:33:91:6f:6a:90:27:81:47:5e:2f: + 49:54:19:c7:02:51:37:d9:ff:0b:9b:8a:cd:ed:7f: + b7:6b:bc:0a:de:e5:c8:32:f7:a4:16:51:d1:3f:a4: + 02:96:98:09:83:e2:ed:81:19:bb:e3:d4:2b:f1:87: + 97:03:08:05:e6:f7:65:c6:90:48:9d:75:07:31:93: + 04:6d:09:b7:0f:df:fa:f2:b3:ff:e1:44:f4:18:03: + 4f:59:b6:ba:d2:36:8b:0e:b3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 8D:1B:2D:BD:BD:24:E8:19:62:AE:4C:C9:2A:58:90:08:1C:D1:05:2B + X509v3 Authority Key Identifier: + keyid:8D:1B:2D:BD:BD:24:E8:19:62:AE:4C:C9:2A:58:90:08:1C:D1:05:2B + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 7b:f5:8f:d2:b9:44:34:fe:91:ab:1d:52:d3:10:2d:23:75:05: + 8e:17:70:be:52:11:b0:8e:ee:f6:33:50:7c:c7:82:f3:c4:d2: + 98:90:b3:a6:ad:00:33:36:dc:95:f4:4e:45:d2:09:e9:88:ae: + 88:a2:72:e4:75:95:7a:78:31:16:34:a3:50:e0:c9:25:7f:65: + 51:d4:59:20:23:d5:3e:35:79:cf:ed:3d:3c:8c:d1:79:b0:99: + d3:6b:99:ed:32:c5:29:7a:82:8a:98:cb:c6:95:c7:52:59:7c: + f8:1d:fd:18:b8:ef:4d:1f:9d:5d:09:b0:eb:68:50:ed:c0:21: + 61:eb +-----BEGIN CERTIFICATE----- +MIICyDCCAjGgAwIBAgIJAJvxk6VgzQBsMA0GCSqGSIb3DQEBBQUAMH0xCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxETAPBgNVBAMMCENBIE93bmVyMSMwIQYJKoZIhvcNAQkB +FhRjYS5vd25lckBleGFtcGxlLmNvbTAeFw0xMzA4MjgyMTE5NDRaFw0yMzA4MjYy +MTE5NDRaMH0xCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxETAPBgNVBAMMCENBIE93bmVy +MSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBleGFtcGxlLmNvbTCBnzANBgkqhkiG +9w0BAQEFAAOBjQAwgYkCgYEAvsxicL9C7p3wBQQrBUZOyWBqtDGMpWAleQVhiP42 +nmMkvzORb2qQJ4FHXi9JVBnHAlE32f8Lm4rN7X+3a7wK3uXIMvekFlHRP6QClpgJ +g+LtgRm749Qr8YeXAwgF5vdlxpBInXUHMZMEbQm3D9/68rP/4UT0GANPWba60jaL +DrMCAwEAAaNQME4wHQYDVR0OBBYEFI0bLb29JOgZYq5MySpYkAgc0QUrMB8GA1Ud +IwQYMBaAFI0bLb29JOgZYq5MySpYkAgc0QUrMAwGA1UdEwQFMAMBAf8wDQYJKoZI +hvcNAQEFBQADgYEAe/WP0rlENP6Rqx1S0xAtI3UFjhdwvlIRsI7u9jNQfMeC88TS +mJCzpq0AMzbclfRORdIJ6YiuiKJy5HWVengxFjSjUODJJX9lUdRZICPVPjV5z+09 +PIzRebCZ02uZ7TLFKXqCipjLxpXHUll8+B39GLjvTR+dXQmw62hQ7cAhYes= +-----END CERTIFICATE----- diff --git a/rt/t/data/smime/keys/demoCA/private/cakey.pem b/rt/t/data/smime/keys/demoCA/private/cakey.pem new file mode 100644 index 000000000..ad95c8df5 --- /dev/null +++ b/rt/t/data/smime/keys/demoCA/private/cakey.pem @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,8580147E208C5674 + +GTz9b2WFdP7gNjUWQnhWqq2o8bpYPbmPTLSyefUfI2UxL0bW96VBKyLpx/FO7Zxr +itfItZA4A7hG+CJLa6pz5C4/9onzHeihhLLDov3pE1hjZwwPFs1IHM/q1KLU4tK4 +yb/Xx1pw/3L1nlvWy4CQ/F1pmHG+akQNopy2Ru0XWLVw/gysmff8GW94Awx5MyZd +81tvuFu2U2BYdPbC/Zc+hrlTdqG2btgdll39gjRoNvLbA4tifLNy264yOS71lxF/ +rOtavqzCULo/cTTumcZzbMnowjpdrPliuGg6rox3xc3zFjNfogu7okH53XtOZClQ +n3/jjqI1LEUhOC0omUck4q3XbaCWGg6X/MUL8Fae+jDUs5NISt75xVs1uJdU2DuB +xUwtgzJCbt5eovbczmoKm44nY3TqsITG+vuI7qim3wds8WPbM4lnz7fx0AbHYOIK +ceCxDJirQRmblImJybPHJL6uuCo91Ahx7NmLcGw35QhhQf/EfKPJyh4Ih7+Cn2il +EGW9RWS7hl9JSCOZs30YwPQz1bgCHIt0+31WSK4hbZ/IyPnDrMY4XNVCeWxX2xcF +y2VjpoW305Glu2D522n0jUe/YJGHBaA7ijQkLpw2nL0qstlkq/2RoGZaDm0gUCUG +dNbmeQrOF7dJtSKKjxy/DqMPw+ymn/YCXVaCPvIEuqHyFKnUNJ/ak4vnAeV7Jrhz +0OlyqNR4O/FKjf4pgsTHqodTQrxHA2d/n/Evnes/TevnIp6sa8HpkMcJc2DL9hKB +aIWFQxGynI/S9juZXSKdTOMcUbSsicVELzzk+spHlZ9xKpuBvJvWxQ== +-----END RSA PRIVATE KEY----- diff --git a/rt/t/data/smime/keys/demoCA/serial b/rt/t/data/smime/keys/demoCA/serial new file mode 100644 index 000000000..7c398625f --- /dev/null +++ b/rt/t/data/smime/keys/demoCA/serial @@ -0,0 +1 @@ +8A6ACD51BE94A017 diff --git a/rt/t/data/smime/keys/otherCA/cacert.pem b/rt/t/data/smime/keys/otherCA/cacert.pem new file mode 100644 index 000000000..bebd5f3a6 --- /dev/null +++ b/rt/t/data/smime/keys/otherCA/cacert.pem @@ -0,0 +1,80 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 16372135729078323798 (0xe33582b3ca31ca56) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=AU, ST=Some-State, O=Other Widgits, LLC, CN=CA Owner/emailAddress=ca.owner@example.net + Validity + Not Before: Aug 28 22:16:28 2013 GMT + Not After : Aug 28 22:16:28 2023 GMT + Subject: C=AU, ST=Some-State, O=Other Widgits, LLC, CN=CA Owner/emailAddress=ca.owner@example.net + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d6:b6:53:04:53:e8:98:91:c6:6a:ab:27:c3:ba: + 01:53:e1:f3:56:1e:90:c9:61:7e:73:37:36:80:49: + a9:b4:6a:9f:3a:d8:08:6f:ba:82:64:c5:85:92:41: + 53:71:25:ec:18:85:1c:9e:80:4b:30:f7:16:b4:f8: + 07:3e:f7:9b:aa:2d:9f:f8:08:a4:0a:e6:9e:0a:d2: + 2f:06:59:28:53:9e:b4:77:8a:2b:f0:b5:c6:ca:af: + 41:be:ed:17:12:0f:37:2e:e9:b8:43:3a:76:20:fd: + e8:81:91:b8:bf:03:92:76:1f:40:d3:e0:44:fd:34: + c7:f3:d4:f6:77:c9:52:59:da:37:95:ab:54:a7:11: + a5:1a:03:fa:cc:71:19:72:cb:29:39:15:69:b5:f6: + 5b:16:22:d8:ed:a4:b3:b5:83:ed:69:d9:91:7f:2d: + 0c:af:4f:c6:4a:4a:4f:1d:a3:dc:1f:10:f4:77:c8: + 48:e5:94:64:3b:29:3d:9d:16:0c:d2:30:3a:44:0d: + a4:87:04:04:84:ec:fd:19:82:08:77:b5:77:64:f4: + ce:bc:6c:a5:c1:b7:17:7e:a2:4a:de:28:62:40:5e: + 3d:77:5c:9a:09:dc:7e:a6:b6:a3:34:ca:73:a4:c2: + 42:74:4e:d8:52:2d:98:4f:28:6e:89:93:7e:34:3b: + eb:37 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 7F:75:3B:B2:1E:CF:EF:D6:A7:D1:42:F8:1C:A9:13:63:CF:C9:0E:5A + X509v3 Authority Key Identifier: + keyid:7F:75:3B:B2:1E:CF:EF:D6:A7:D1:42:F8:1C:A9:13:63:CF:C9:0E:5A + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 44:f7:e8:e6:af:a9:be:cf:28:51:dc:86:14:e2:4d:e4:14:9f: + 09:4d:cb:e9:10:2c:ef:21:ec:b0:8c:14:57:59:45:52:b4:e3: + db:f4:34:e3:39:b6:de:0c:eb:68:78:db:d0:21:d2:c1:51:18: + ce:33:14:a4:4d:91:88:eb:cc:b0:4a:93:73:75:48:e8:56:ce: + 29:c9:07:73:18:28:20:e1:2e:ba:0f:cc:4c:26:e7:45:d5:4c: + 60:89:ef:1d:d7:7a:a5:80:62:bf:30:da:ac:bf:be:f8:54:f3: + fc:8a:09:1c:89:2d:2a:12:20:99:66:54:a0:78:50:f0:46:44: + 9d:ad:95:81:83:c0:47:38:b8:4a:81:3c:72:49:68:a2:a1:04: + c7:d3:e9:e8:6f:65:ce:10:11:7f:0a:8b:96:ce:4e:1e:55:c7: + 54:34:25:5e:ba:95:62:ad:45:43:b1:69:70:d4:c4:33:29:56: + cd:45:08:7d:e5:1e:5c:77:55:7b:f7:34:ea:c5:d5:48:21:b1: + 71:a5:02:16:50:78:64:e4:01:85:28:3e:e4:b8:f6:f8:02:3d: + 01:23:ba:2c:54:c3:72:a5:2a:3d:41:fd:c1:15:60:37:0b:65: + bf:23:bd:33:f6:d8:75:03:71:46:47:97:93:ae:bc:7f:76:1e: + f3:5f:ba:0f +-----BEGIN CERTIFICATE----- +MIIDwTCCAqmgAwIBAgIJAOM1grPKMcpWMA0GCSqGSIb3DQEBBQUAMHcxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRswGQYDVQQKDBJPdGhlciBXaWRn +aXRzLCBMTEMxETAPBgNVBAMMCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5v +d25lckBleGFtcGxlLm5ldDAeFw0xMzA4MjgyMjE2MjhaFw0yMzA4MjgyMjE2Mjha +MHcxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRswGQYDVQQKDBJP +dGhlciBXaWRnaXRzLCBMTEMxETAPBgNVBAMMCENBIE93bmVyMSMwIQYJKoZIhvcN +AQkBFhRjYS5vd25lckBleGFtcGxlLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANa2UwRT6JiRxmqrJ8O6AVPh81YekMlhfnM3NoBJqbRqnzrYCG+6 +gmTFhZJBU3El7BiFHJ6ASzD3FrT4Bz73m6otn/gIpArmngrSLwZZKFOetHeKK/C1 +xsqvQb7tFxIPNy7puEM6diD96IGRuL8DknYfQNPgRP00x/PU9nfJUlnaN5WrVKcR +pRoD+sxxGXLLKTkVabX2WxYi2O2ks7WD7WnZkX8tDK9PxkpKTx2j3B8Q9HfISOWU +ZDspPZ0WDNIwOkQNpIcEBITs/RmCCHe1d2T0zrxspcG3F36iSt4oYkBePXdcmgnc +fqa2ozTKc6TCQnRO2FItmE8obomTfjQ76zcCAwEAAaNQME4wHQYDVR0OBBYEFH91 +O7Iez+/Wp9FC+BypE2PPyQ5aMB8GA1UdIwQYMBaAFH91O7Iez+/Wp9FC+BypE2PP +yQ5aMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAET36Oavqb7PKFHc +hhTiTeQUnwlNy+kQLO8h7LCMFFdZRVK049v0NOM5tt4M62h429Ah0sFRGM4zFKRN +kYjrzLBKk3N1SOhWzinJB3MYKCDhLroPzEwm50XVTGCJ7x3XeqWAYr8w2qy/vvhU +8/yKCRyJLSoSIJlmVKB4UPBGRJ2tlYGDwEc4uEqBPHJJaKKhBMfT6ehvZc4QEX8K +i5bOTh5Vx1Q0JV66lWKtRUOxaXDUxDMpVs1FCH3lHlx3VXv3NOrF1UghsXGlAhZQ +eGTkAYUoPuS49vgCPQEjuixUw3KlKj1B/cEVYDcLZb8jvTP22HUDcUZHl5OuvH92 +HvNfug8= +-----END CERTIFICATE----- diff --git a/rt/t/data/smime/keys/otherCA/private/cakey.pem b/rt/t/data/smime/keys/otherCA/private/cakey.pem new file mode 100644 index 000000000..7447fbbd6 --- /dev/null +++ b/rt/t/data/smime/keys/otherCA/private/cakey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA1rZTBFPomJHGaqsnw7oBU+HzVh6QyWF+czc2gEmptGqfOtgI +b7qCZMWFkkFTcSXsGIUcnoBLMPcWtPgHPvebqi2f+AikCuaeCtIvBlkoU560d4or +8LXGyq9Bvu0XEg83Lum4Qzp2IP3ogZG4vwOSdh9A0+BE/TTH89T2d8lSWdo3latU +pxGlGgP6zHEZcsspORVptfZbFiLY7aSztYPtadmRfy0Mr0/GSkpPHaPcHxD0d8hI +5ZRkOyk9nRYM0jA6RA2khwQEhOz9GYIId7V3ZPTOvGylwbcXfqJK3ihiQF49d1ya +Cdx+prajNMpzpMJCdE7YUi2YTyhuiZN+NDvrNwIDAQABAoIBABa6G9V0cEVeAMuf +rEjacnOHkjNGbvrx9+mIKZuwsGbpdktLPLFe45h5E+dkRMnQQsphpKLeX5ciQGQN +cO7oVLDRvYIKoBqLSKVKlDGu1EbtoJqapIYJJ66imGn2PJ/rvmKX2Ko9EO3zEl5M +p2qInUMlkb4bmhHXOWcE3sXVKINcFSjUxx/EkE/hS4z4gZX1ZFz8r6NmnnSk3G5p +yS7JlTx9gIEqIp3LFmgPY8yhjdbQ+Qsde4FU1MSWWvmE4+LT4AicTAUGf61VEc+s +gVHVHl9yuOGJYRaKuqHevCMxr8Bh27WpPT+NGdPxVRZJ/kSoDKPdrv9oU99Rtgwp +RaanetECgYEA8Uuk/2pqOHQKd83jHynejJSK/B1XxAddn9PHWNw9gYFBPnrxL76/ +lIuEAHyjcqYC4XV7dmEpWklWFInV1cBUAGimX4ykwArQcneq9nJXxR5KQ7ofozB+ +eYZ1/QvhySJeg+ucsyi99HLFL845aGf4y48VkHD9MKnKMCNwYcytgBkCgYEA48v2 +6K70spBv/j4QQ3v/5ovsmvv9xQei5mPZKawKOx6OxDZJ0he6ltGQ6bJoNFFtcC/u +Lb/uX/0Ah/V5gurAVQAJU53o2t2Ai32NX80b2lUXi0H8nGvOxW8i95SUWx3dn1yz +EBJMgfjH5XJV+kZVUWeOIIl+hPXew+u3XAdq788CgYAobDa4/zfKO05hoaEx4E7D +GENsVvIUCfPaSZ00urinEGNAt1HeYMMxfGnhtv+evkbvREIpo79Mu8pq6GhlRbIM +23s7uJEFBwrCkl+Wp7Mid5+TVwPjz8TwUOFFQg9SJarVyMvYi7O+1tdH2fFuFzTr +zQ2cxAD2fQs9I0K5b5OFSQKBgQDXM0QiE86VtsAmhslkh4t8aKnwzKiz73/keWWZ +6a6MpVSoZsUcllAu1PI65NFuw5JIzu8LB2wSAHj0+GF/3XgvlOY6uU5XHbSnksfx +PlrWy1Z/t6oGuA5SFKkLDbGN1swdFj0PrMnca4Ok7nvtAW7uhY8Oi/YbdA+sNU42 +wccznwKBgBswApZRfZKCUD/1Khdz+HmG/YEPbk4Kqgi7a8MKpT4No0hjRsoO1HV2 +WggtvBjzagHkzZNkjJv+WvSU0DHk/JQnWIZFJd+72ZGR56neq8iXIQ28LnnGhcvk +m0YNZzB8MCvD5ZztH6GU5ecPzO+4Tjkruau2an4etLSs60ogKy5u +-----END RSA PRIVATE KEY----- diff --git a/rt/t/data/smime/keys/otherCA/serial b/rt/t/data/smime/keys/otherCA/serial new file mode 100644 index 000000000..5dd9faac8 --- /dev/null +++ b/rt/t/data/smime/keys/otherCA/serial @@ -0,0 +1 @@ +FB573398E9349E9D diff --git a/rt/t/data/smime/keys/root@example.com.crt b/rt/t/data/smime/keys/root@example.com.crt new file mode 100644 index 000000000..45e3eb448 --- /dev/null +++ b/rt/t/data/smime/keys/root@example.com.crt @@ -0,0 +1,43 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 9974010075738841110 (0x8a6acd51be94a016) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com + Validity + Not Before: Aug 28 21:41:07 2013 GMT + Not After : Aug 28 21:41:07 2023 GMT + Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Enoch Root/emailAddress=root@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (512 bit) + Modulus: + 00:b2:77:b9:bc:09:7d:14:8e:6b:6f:7e:33:a9:95: + 21:5d:f3:3c:91:61:f1:bc:5c:1d:7e:e7:54:25:e8: + cb:5f:b7:18:0e:23:26:00:42:09:bd:89:da:5c:06: + cb:52:08:43:f6:4e:fe:dd:f8:0a:8a:95:35:8f:4a: + 25:16:da:e6:bf + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + 1a:cd:7e:0e:e0:6f:90:b7:22:0e:4d:79:4d:6a:9b:ac:a1:6a: + ab:85:32:9c:86:9c:d2:10:96:f7:e0:00:2c:7d:3c:16:a4:ff: + dd:9e:37:fb:a3:7a:43:ab:2f:ee:c4:ff:be:77:0f:40:f8:0e: + 45:3e:48:46:bf:ec:e1:b0:46:8d:13:37:7a:a6:d1:7c:16:cb: + 28:6b:37:88:4d:0a:12:6b:87:b9:7c:d9:c4:d7:57:93:b9:f6: + 21:26:1b:32:88:1d:cd:84:0f:6a:f9:05:0a:76:01:de:5e:99: + 86:10:fc:7d:ee:d5:70:b2:44:99:41:0a:d7:0e:e8:5b:c9:ca: + 10:39 +-----BEGIN CERTIFICATE----- +MIICKzCCAZQCCQCKas1RvpSgFjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMREwDwYDVQQDDAhDQSBPd25lcjEjMCEGCSqGSIb3DQEJARYUY2Eu +b3duZXJAZXhhbXBsZS5jb20wHhcNMTMwODI4MjE0MTA3WhcNMjMwODI4MjE0MTA3 +WjB7MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMY +SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDEwpFbm9jaCBSb290MR8w +HQYJKoZIhvcNAQkBFhByb290QGV4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQAD +SwAwSAJBALJ3ubwJfRSOa29+M6mVIV3zPJFh8bxcHX7nVCXoy1+3GA4jJgBCCb2J +2lwGy1IIQ/ZO/t34CoqVNY9KJRba5r8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQAa +zX4O4G+QtyIOTXlNapusoWqrhTKchpzSEJb34AAsfTwWpP/dnjf7o3pDqy/uxP++ +dw9A+A5FPkhGv+zhsEaNEzd6ptF8FssoazeITQoSa4e5fNnE11eTufYhJhsyiB3N +hA9q+QUKdgHeXpmGEPx97tVwskSZQQrXDuhbycoQOQ== +-----END CERTIFICATE----- diff --git a/rt/t/data/smime/keys/root@example.com.csr b/rt/t/data/smime/keys/root@example.com.csr new file mode 100644 index 000000000..a72677a55 --- /dev/null +++ b/rt/t/data/smime/keys/root@example.com.csr @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBNTCB4AIBADB7MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEh +MB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDEwpFbm9j +aCBSb290MR8wHQYJKoZIhvcNAQkBFhByb290QGV4YW1wbGUuY29tMFwwDQYJKoZI +hvcNAQEBBQADSwAwSAJBALJ3ubwJfRSOa29+M6mVIV3zPJFh8bxcHX7nVCXoy1+3 +GA4jJgBCCb2J2lwGy1IIQ/ZO/t34CoqVNY9KJRba5r8CAwEAAaAAMA0GCSqGSIb3 +DQEBBQUAA0EABuN/lyQxMY6DNb9XZ7H+UZLJrNYei1HRvfIXig7EvkSDEnArSwfZ +uzAeLo3mnIp7WiDk3M7e19LQFkERs2xvHw== +-----END CERTIFICATE REQUEST----- diff --git a/rt/t/data/smime/keys/root@example.com.key b/rt/t/data/smime/keys/root@example.com.key new file mode 100644 index 000000000..7b24e4e82 --- /dev/null +++ b/rt/t/data/smime/keys/root@example.com.key @@ -0,0 +1,12 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,6356CE6012402B9B + +Lco5rf3/rHlShktH/o6NHF1mVH00k+pZ3bWodejMaHW1ofZXe9/yjzPM2jqqi+Dj +xmzZ9R/MijO07vpxWHqdvhXeFf0TW67gW413M/bwiRd/rV0mUFz81nowFe9e15tm +Itku1sePFvvL/UUxBGeYhplHAP6e76JqQcJTkBaG04KitH9GHtj1HFQR8P9/8h6d +f0ZtU8wqnhkZvtzb72ZLwsw0YZ7R9YLIqCmOn1twW0CC77deACy+deJOC0N4CxW6 ++jEGbJKMN5rOPsFiieDzZXAaTlGd6qXVWaxUPYH89yWedYoAZgbi6zxGGwNGbc/Q +2Y7g+qHi3L30uJvgJEGihIM+9iAKUJSazyGYl9Xl2FwTpNFOMJAYFyNKNv5FHwdm +deoslrbEXVtqurOQYr955cyqs2NN+JYLsz5nNnfBpGo= +-----END RSA PRIVATE KEY----- diff --git a/rt/t/data/smime/keys/root@example.com.pem b/rt/t/data/smime/keys/root@example.com.pem new file mode 100644 index 000000000..802475e66 --- /dev/null +++ b/rt/t/data/smime/keys/root@example.com.pem @@ -0,0 +1,55 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 9974010075738841110 (0x8a6acd51be94a016) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com + Validity + Not Before: Aug 28 21:41:07 2013 GMT + Not After : Aug 28 21:41:07 2023 GMT + Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Enoch Root/emailAddress=root@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (512 bit) + Modulus: + 00:b2:77:b9:bc:09:7d:14:8e:6b:6f:7e:33:a9:95: + 21:5d:f3:3c:91:61:f1:bc:5c:1d:7e:e7:54:25:e8: + cb:5f:b7:18:0e:23:26:00:42:09:bd:89:da:5c:06: + cb:52:08:43:f6:4e:fe:dd:f8:0a:8a:95:35:8f:4a: + 25:16:da:e6:bf + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + 1a:cd:7e:0e:e0:6f:90:b7:22:0e:4d:79:4d:6a:9b:ac:a1:6a: + ab:85:32:9c:86:9c:d2:10:96:f7:e0:00:2c:7d:3c:16:a4:ff: + dd:9e:37:fb:a3:7a:43:ab:2f:ee:c4:ff:be:77:0f:40:f8:0e: + 45:3e:48:46:bf:ec:e1:b0:46:8d:13:37:7a:a6:d1:7c:16:cb: + 28:6b:37:88:4d:0a:12:6b:87:b9:7c:d9:c4:d7:57:93:b9:f6: + 21:26:1b:32:88:1d:cd:84:0f:6a:f9:05:0a:76:01:de:5e:99: + 86:10:fc:7d:ee:d5:70:b2:44:99:41:0a:d7:0e:e8:5b:c9:ca: + 10:39 +-----BEGIN CERTIFICATE----- +MIICKzCCAZQCCQCKas1RvpSgFjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMREwDwYDVQQDDAhDQSBPd25lcjEjMCEGCSqGSIb3DQEJARYUY2Eu +b3duZXJAZXhhbXBsZS5jb20wHhcNMTMwODI4MjE0MTA3WhcNMjMwODI4MjE0MTA3 +WjB7MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMY +SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDEwpFbm9jaCBSb290MR8w +HQYJKoZIhvcNAQkBFhByb290QGV4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQAD +SwAwSAJBALJ3ubwJfRSOa29+M6mVIV3zPJFh8bxcHX7nVCXoy1+3GA4jJgBCCb2J +2lwGy1IIQ/ZO/t34CoqVNY9KJRba5r8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQAa +zX4O4G+QtyIOTXlNapusoWqrhTKchpzSEJb34AAsfTwWpP/dnjf7o3pDqy/uxP++ +dw9A+A5FPkhGv+zhsEaNEzd6ptF8FssoazeITQoSa4e5fNnE11eTufYhJhsyiB3N +hA9q+QUKdgHeXpmGEPx97tVwskSZQQrXDuhbycoQOQ== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,6356CE6012402B9B + +Lco5rf3/rHlShktH/o6NHF1mVH00k+pZ3bWodejMaHW1ofZXe9/yjzPM2jqqi+Dj +xmzZ9R/MijO07vpxWHqdvhXeFf0TW67gW413M/bwiRd/rV0mUFz81nowFe9e15tm +Itku1sePFvvL/UUxBGeYhplHAP6e76JqQcJTkBaG04KitH9GHtj1HFQR8P9/8h6d +f0ZtU8wqnhkZvtzb72ZLwsw0YZ7R9YLIqCmOn1twW0CC77deACy+deJOC0N4CxW6 ++jEGbJKMN5rOPsFiieDzZXAaTlGd6qXVWaxUPYH89yWedYoAZgbi6zxGGwNGbc/Q +2Y7g+qHi3L30uJvgJEGihIM+9iAKUJSazyGYl9Xl2FwTpNFOMJAYFyNKNv5FHwdm +deoslrbEXVtqurOQYr955cyqs2NN+JYLsz5nNnfBpGo= +-----END RSA PRIVATE KEY----- diff --git a/rt/t/data/smime/keys/sender@example.com.crt b/rt/t/data/smime/keys/sender@example.com.crt new file mode 100644 index 000000000..9497a2022 --- /dev/null +++ b/rt/t/data/smime/keys/sender@example.com.crt @@ -0,0 +1,43 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 9974010075738841109 (0x8a6acd51be94a015) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com + Validity + Not Before: Aug 28 21:41:45 2013 GMT + Not After : Aug 28 21:41:45 2023 GMT + Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=sender/emailAddress=sender@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (512 bit) + Modulus: + 00:a8:38:41:90:1d:e7:cd:2b:cb:62:cf:ad:ff:70: + f6:44:5d:f3:4b:7e:21:75:b6:5c:e1:7e:c2:27:3b: + 85:eb:72:9b:5a:94:0a:69:1d:83:ca:c5:91:b2:3f: + 04:72:61:e4:b8:eb:5b:ce:b5:10:77:d8:a7:df:8b: + c9:5a:14:15:61 + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + 91:74:84:00:98:40:30:6b:a6:61:6b:7b:d7:c9:9d:6e:ef:bb: + c8:ba:8b:83:15:62:3e:d1:c2:9d:1c:4e:ce:09:ce:d8:4f:4a: + 49:a8:97:e8:3b:ed:82:2c:a3:20:45:72:f3:d9:23:66:93:d5: + 54:14:ce:ce:cf:27:04:52:43:b4:a7:0b:ac:b8:45:a3:96:bf: + 2f:43:59:61:02:7a:36:39:9c:01:ad:b7:63:6e:b5:b6:29:cb: + 79:78:93:95:25:24:4a:83:bd:1d:d6:07:86:06:6a:fa:04:60: + 6e:ba:41:11:0a:cb:b2:84:03:ac:30:55:94:ed:b2:2d:3c:c5: + 99:6f +-----BEGIN CERTIFICATE----- +MIICKTCCAZICCQCKas1RvpSgFTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMREwDwYDVQQDDAhDQSBPd25lcjEjMCEGCSqGSIb3DQEJARYUY2Eu +b3duZXJAZXhhbXBsZS5jb20wHhcNMTMwODI4MjE0MTQ1WhcNMjMwODI4MjE0MTQ1 +WjB5MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMY +SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDEwZzZW5kZXIxITAfBgkq +hkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sA +MEgCQQCoOEGQHefNK8tiz63/cPZEXfNLfiF1tlzhfsInO4XrcptalAppHYPKxZGy +PwRyYeS461vOtRB32Kffi8laFBVhAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAkXSE +AJhAMGumYWt718mdbu+7yLqLgxViPtHCnRxOzgnO2E9KSaiX6DvtgiyjIEVy89kj +ZpPVVBTOzs8nBFJDtKcLrLhFo5a/L0NZYQJ6NjmcAa23Y261tinLeXiTlSUkSoO9 +HdYHhgZq+gRgbrpBEQrLsoQDrDBVlO2yLTzFmW8= +-----END CERTIFICATE----- diff --git a/rt/t/data/smime/keys/sender@example.com.csr b/rt/t/data/smime/keys/sender@example.com.csr new file mode 100644 index 000000000..18fa799a4 --- /dev/null +++ b/rt/t/data/smime/keys/sender@example.com.csr @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBMzCB3gIBADB5MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEh +MB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDEwZzZW5k +ZXIxITAfBgkqhkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmNvbTBcMA0GCSqGSIb3 +DQEBAQUAA0sAMEgCQQCoOEGQHefNK8tiz63/cPZEXfNLfiF1tlzhfsInO4Xrcpta +lAppHYPKxZGyPwRyYeS461vOtRB32Kffi8laFBVhAgMBAAGgADANBgkqhkiG9w0B +AQUFAANBAFoi5bepEWsl0cQiO7k314NAuHenXaVrsWt3kPWfwgWn0aLp3aH86aZ5 +g4MYNjJzTqnkU1apyY8MV+BUZaXfnII= +-----END CERTIFICATE REQUEST----- diff --git a/rt/t/data/smime/keys/sender@example.com.key b/rt/t/data/smime/keys/sender@example.com.key new file mode 100644 index 000000000..26ed85066 --- /dev/null +++ b/rt/t/data/smime/keys/sender@example.com.key @@ -0,0 +1,12 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,605762440BC8261C + +MpUs66ILz2ePX4NKQ408LOAwvmpLLLnSwDX/Zmr/LG4SyZ7AnY6dY06XB6suev3m +AS+xm/LM44lvUaDvPnl4gO8jnCw3D1yktcfeHc6XqcFx2U9AiUTawmoSTKwrT4P+ +tnpSrrBJY3WghElbckK3vbZboX9Eld+dJjGPf9YqMrkixObp0ul1zW7Wt+aSEV5B +ngP3VmQinB1EjSUhGF/gsFzhJsutsX4Z1SE/U4K1A1OPl3Oz4e+9VLGgUN4ao84y +pcNYdXO/BCax4Uk8l0r0DcMd73P9WZs9+bcSgmkqduWCXkNXDbfi4RTOEn19Ehpu +MyKc3JrskRhNRN1vfMSRFUsrmppxBdPfkrGrTCJNBuL7zdbQh9k9XMaNzfw5Tt2R +oCWay5shBGEEKXRLIEqzO+Jx1BWVlWwxUwDLr73ItHA= +-----END RSA PRIVATE KEY----- diff --git a/rt/t/data/smime/keys/sender@example.com.pem b/rt/t/data/smime/keys/sender@example.com.pem new file mode 100644 index 000000000..500bc83f7 --- /dev/null +++ b/rt/t/data/smime/keys/sender@example.com.pem @@ -0,0 +1,55 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 9974010075738841109 (0x8a6acd51be94a015) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com + Validity + Not Before: Aug 28 21:41:45 2013 GMT + Not After : Aug 28 21:41:45 2023 GMT + Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=sender/emailAddress=sender@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (512 bit) + Modulus: + 00:a8:38:41:90:1d:e7:cd:2b:cb:62:cf:ad:ff:70: + f6:44:5d:f3:4b:7e:21:75:b6:5c:e1:7e:c2:27:3b: + 85:eb:72:9b:5a:94:0a:69:1d:83:ca:c5:91:b2:3f: + 04:72:61:e4:b8:eb:5b:ce:b5:10:77:d8:a7:df:8b: + c9:5a:14:15:61 + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + 91:74:84:00:98:40:30:6b:a6:61:6b:7b:d7:c9:9d:6e:ef:bb: + c8:ba:8b:83:15:62:3e:d1:c2:9d:1c:4e:ce:09:ce:d8:4f:4a: + 49:a8:97:e8:3b:ed:82:2c:a3:20:45:72:f3:d9:23:66:93:d5: + 54:14:ce:ce:cf:27:04:52:43:b4:a7:0b:ac:b8:45:a3:96:bf: + 2f:43:59:61:02:7a:36:39:9c:01:ad:b7:63:6e:b5:b6:29:cb: + 79:78:93:95:25:24:4a:83:bd:1d:d6:07:86:06:6a:fa:04:60: + 6e:ba:41:11:0a:cb:b2:84:03:ac:30:55:94:ed:b2:2d:3c:c5: + 99:6f +-----BEGIN CERTIFICATE----- +MIICKTCCAZICCQCKas1RvpSgFTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMREwDwYDVQQDDAhDQSBPd25lcjEjMCEGCSqGSIb3DQEJARYUY2Eu +b3duZXJAZXhhbXBsZS5jb20wHhcNMTMwODI4MjE0MTQ1WhcNMjMwODI4MjE0MTQ1 +WjB5MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMY +SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDEwZzZW5kZXIxITAfBgkq +hkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sA +MEgCQQCoOEGQHefNK8tiz63/cPZEXfNLfiF1tlzhfsInO4XrcptalAppHYPKxZGy +PwRyYeS461vOtRB32Kffi8laFBVhAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAkXSE +AJhAMGumYWt718mdbu+7yLqLgxViPtHCnRxOzgnO2E9KSaiX6DvtgiyjIEVy89kj +ZpPVVBTOzs8nBFJDtKcLrLhFo5a/L0NZYQJ6NjmcAa23Y261tinLeXiTlSUkSoO9 +HdYHhgZq+gRgbrpBEQrLsoQDrDBVlO2yLTzFmW8= +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,605762440BC8261C + +MpUs66ILz2ePX4NKQ408LOAwvmpLLLnSwDX/Zmr/LG4SyZ7AnY6dY06XB6suev3m +AS+xm/LM44lvUaDvPnl4gO8jnCw3D1yktcfeHc6XqcFx2U9AiUTawmoSTKwrT4P+ +tnpSrrBJY3WghElbckK3vbZboX9Eld+dJjGPf9YqMrkixObp0ul1zW7Wt+aSEV5B +ngP3VmQinB1EjSUhGF/gsFzhJsutsX4Z1SE/U4K1A1OPl3Oz4e+9VLGgUN4ao84y +pcNYdXO/BCax4Uk8l0r0DcMd73P9WZs9+bcSgmkqduWCXkNXDbfi4RTOEn19Ehpu +MyKc3JrskRhNRN1vfMSRFUsrmppxBdPfkrGrTCJNBuL7zdbQh9k9XMaNzfw5Tt2R +oCWay5shBGEEKXRLIEqzO+Jx1BWVlWwxUwDLr73ItHA= +-----END RSA PRIVATE KEY----- diff --git a/rt/t/data/smime/mails/1-signed.eml b/rt/t/data/smime/mails/1-signed.eml new file mode 100644 index 000000000..57c09b7ac --- /dev/null +++ b/rt/t/data/smime/mails/1-signed.eml @@ -0,0 +1,74 @@ +X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709B50.6040609@example.com>
+Date: Tue, 09 Feb 2010 02:16:32 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:1
+Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha1; boundary="------------ms010504020705000203070202"
+
+This is a cryptographically signed message in MIME format.
+
+--------------ms010504020705000203070202
+Content-Type: text/plain; charset=UTF-8; format=flowed
+Content-Transfer-Encoding: quoted-printable
+
+This is a test email with detached signature.
+ID:1
+
+
+--------------ms010504020705000203070202
+Content-Type: application/pkcs7-signature; name="smime.p7s"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+Content-Description: S/MIME Cryptographic Signature
+
+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
+SMKKtZ91LwT/wfT8cqCADfh0LaTIFAAAAAAAAA==
+--------------ms010504020705000203070202--
diff --git a/rt/t/data/smime/mails/2-signed-attachment.eml b/rt/t/data/smime/mails/2-signed-attachment.eml new file mode 100644 index 000000000..5c8ab27cb --- /dev/null +++ b/rt/t/data/smime/mails/2-signed-attachment.eml @@ -0,0 +1,90 @@ +X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709C48.4030908@example.com>
+Date: Tue, 09 Feb 2010 02:20:40 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:2
+Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha1; boundary="------------ms090206030705090204050109"
+
+This is a cryptographically signed message in MIME format.
+
+--------------ms090206030705090204050109
+Content-Type: multipart/mixed;
+ boundary="------------090009090000030005040209"
+
+This is a multi-part message in MIME format.
+--------------090009090000030005040209
+Content-Type: text/plain; charset=UTF-8; format=flowed
+Content-Transfer-Encoding: quoted-printable
+
+This is a test email with a text attachment.
+ID:2
+
+
+--------------090009090000030005040209
+Content-Type: text/plain; x-mac-type="0"; x-mac-creator="0";
+ name="text-attachment"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="text-attachment"
+
+VGhpcyBpcyBhIHRlc3QgYXR0YWNobWVudC4gIFRoZSBtYWdpYyB3b3JkIGlzOiAgemFuemli
+YXIuCg==
+--------------090009090000030005040209--
+
+--------------ms090206030705090204050109
+Content-Type: application/pkcs7-signature; name="smime.p7s"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+Content-Description: S/MIME Cryptographic Signature
+
+MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIFXjCC
+AqswggIUoAMCAQICCQCKas1RvpSgFTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJBVTET
+MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
+MREwDwYDVQQDEwhDQSBPd25lcjEjMCEGCSqGSIb3DQEJARYUY2Eub3duZXJAZXhhbXBsZS5j
+b20wHhcNMTAwMjA4MTYyNTQyWhcNMTEwMjA4MTYyNTQyWjB5MQswCQYDVQQGEwJBVTETMBEG
+A1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8w
+DQYDVQQDEwZzZW5kZXIxITAfBgkqhkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmNvbTBcMA0G
+CSqGSIb3DQEBAQUAA0sAMEgCQQCoOEGQHefNK8tiz63/cPZEXfNLfiF1tlzhfsInO4Xrcpta
+lAppHYPKxZGyPwRyYeS461vOtRB32Kffi8laFBVhAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJ
+YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTz
+NObDUXiSm5NJjk4wvBiIJGk0CTAfBgNVHSMEGDAWgBSNGy29vSToGWKuTMkqWJAIHNEFKzAN
+BgkqhkiG9w0BAQUFAAOBgQBTkNtt0KoPyzKULqW80Q0nfIXYCGxS/rFNGJTCEP9Pj3Ergb+j
+qhE+bnJfSho4oD9fLInWr8Vs/1ljEy37Wi9Ysnc+UoMHAcZOzxmcmvMXrGPCAPMYuSem1RoU
+Edbw25ve5K3NKQH9OMEhnPqiC15tXRRUzgo8968P5n/HOQCzMzCCAqswggIUoAMCAQICCQCK
+as1RvpSgFTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1T
+dGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMREwDwYDVQQDEwhDQSBP
+d25lcjEjMCEGCSqGSIb3DQEJARYUY2Eub3duZXJAZXhhbXBsZS5jb20wHhcNMTAwMjA4MTYy
+NTQyWhcNMTEwMjA4MTYyNTQyWjB5MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0
+ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDEwZzZW5kZXIx
+ITAfBgkqhkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sA
+MEgCQQCoOEGQHefNK8tiz63/cPZEXfNLfiF1tlzhfsInO4XrcptalAppHYPKxZGyPwRyYeS4
+61vOtRB32Kffi8laFBVhAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9w
+ZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTzNObDUXiSm5NJjk4wvBiI
+JGk0CTAfBgNVHSMEGDAWgBSNGy29vSToGWKuTMkqWJAIHNEFKzANBgkqhkiG9w0BAQUFAAOB
+gQBTkNtt0KoPyzKULqW80Q0nfIXYCGxS/rFNGJTCEP9Pj3Ergb+jqhE+bnJfSho4oD9fLInW
+r8Vs/1ljEy37Wi9Ysnc+UoMHAcZOzxmcmvMXrGPCAPMYuSem1RoUEdbw25ve5K3NKQH9OMEh
+nPqiC15tXRRUzgo8968P5n/HOQCzMzGCAvAwggLsAgEBMIGKMH0xCzAJBgNVBAYTAkFVMRMw
+EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQx
+ETAPBgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBleGFtcGxlLmNv
+bQIJAIpqzVG+lKAVMAkGBSsOAwIaBQCgggH8MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEw
+HAYJKoZIhvcNAQkFMQ8XDTEwMDIwODIzMjA0MFowIwYJKoZIhvcNAQkEMRYEFJXLFU9+rB4Q
+gPV6QSV6J7blwox4MF8GCSqGSIb3DQEJDzFSMFAwCwYJYIZIAWUDBAECMAoGCCqGSIb3DQMH
+MA4GCCqGSIb3DQMCAgIAgDANBggqhkiG9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIB
+KDCBmwYJKwYBBAGCNxAEMYGNMIGKMH0xCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
+YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxETAPBgNVBAMTCENBIE93
+bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBleGFtcGxlLmNvbQIJAIpqzVG+lKAVMIGd
+BgsqhkiG9w0BCRACCzGBjaCBijB9MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0
+ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMREwDwYDVQQDEwhDQSBPd25l
+cjEjMCEGCSqGSIb3DQEJARYUY2Eub3duZXJAZXhhbXBsZS5jb20CCQCKas1RvpSgFTANBgkq
+hkiG9w0BAQEFAARAai2FuYDJS0n8idViQ6y3pocwSKJRg0hrSP1K3GiVyh4an5y1lWuotK/q
+tziPXZ2qeGSB/mmBf7mwfjPYgGZkoQAAAAAAAA==
+--------------ms090206030705090204050109--
diff --git a/rt/t/data/smime/mails/3-signed-binary.eml b/rt/t/data/smime/mails/3-signed-binary.eml new file mode 100644 index 000000000..ff3449daf --- /dev/null +++ b/rt/t/data/smime/mails/3-signed-binary.eml @@ -0,0 +1,95 @@ +X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709CC5.4010607@example.com>
+Date: Tue, 09 Feb 2010 02:22:45 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:3
+Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha1; boundary="------------ms020101060809030506070801"
+
+This is a cryptographically signed message in MIME format.
+
+--------------ms020101060809030506070801
+Content-Type: multipart/mixed;
+ boundary="------------060502090104050607070406"
+
+This is a multi-part message in MIME format.
+--------------060502090104050607070406
+Content-Type: text/plain; charset=UTF-8; format=flowed
+Content-Transfer-Encoding: quoted-printable
+
+This is a test email with binary attachment and detached signature.
+ID:3
+
+
+--------------060502090104050607070406
+Content-Type: image/png;
+ name="favicon.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="favicon.png"
+
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QAAAAAAAD5Q7t/AAAB
+BElEQVR42u1WWw6DMAwz0+5FbzbvZuZk2cfUritpea77wVIRIBQ7dhsBdIQkM8AMMJImyW6d
+BXweyJ7UAMnUvQFGwHp2bizIJfUTUHZO8j/k1pt8lntvchbdH8ndtqyS+Gj3fyVPAtZAkm3N
+ffCyi/chBIQQ3iqs3cQ0TZCERzbhngDocOS4z94wXTCmu2V45LuQW8hsSWpaP8v9sy+2IRZj
+ZTP5ububbp8Az4ly5W6QqJ33YwKSkIYbZVy5uNMFsOJGLaLTBMRC8Yy7bmR/OD8TUB00DvkW
+AcPSB7FIPoji0AGQBtU4jt+Fh1R6Dcc6B2Znv4HTHTiAJkfXv+ILFy5c8PACgtsiPj7qOgAA
+AAAASUVORK5CYII=
+--------------060502090104050607070406--
+
+--------------ms020101060809030506070801
+Content-Type: application/pkcs7-signature; name="smime.p7s"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+Content-Description: S/MIME Cryptographic Signature
+
+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
+53knAqbgZ9v3viuGCo0fj6RvFU4CHgAAAAAAAA==
+--------------ms020101060809030506070801--
diff --git a/rt/t/data/smime/mails/4-encrypted-plain.eml b/rt/t/data/smime/mails/4-encrypted-plain.eml new file mode 100644 index 000000000..481a858b0 --- /dev/null +++ b/rt/t/data/smime/mails/4-encrypted-plain.eml @@ -0,0 +1,32 @@ +X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709D39.6090102@example.com>
+Date: Tue, 09 Feb 2010 02:24:41 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:4
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+MIAGCSqGSIb3DQEHA6CAMIACAQAxggHIMIHhAgEAMIGKMH0xCzAJBgNVBAYTAkFVMRMwEQYD
+VQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxETAP
+BgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBleGFtcGxlLmNvbQIJ
+AIpqzVG+lKAVMA0GCSqGSIb3DQEBAQUABEB4NUWl1nJB+cQVXPRmHEj+uxapSKRQ2PFeP+Eh
+VJHyPpsgf8APPxhS/6s1DBIWE9fwkghiM7JTgYZow42q/tdfMIHhAgEAMIGKMH0xCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz
+IFB0eSBMdGQxETAPBgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBl
+eGFtcGxlLmNvbQIJAIpqzVG+lKAWMA0GCSqGSIb3DQEBAQUABEAFd/zqPwzjH8gKZoGUA/yY
+7aDfJzlAsg2tar47hM1xeSTgJ5JpluYy9V/43oK++Q+3HceI4P+aE91CjMrcbqvlMIAGCSqG
+SIb3DQEHATAaBggqhkiG9w0DAjAOAgIAoAQIsDsGzNXDhPmggASBiBdO/BdF/SrEjAeIi2is
+G71RuJ/lcnNlAltdk9lMJLoOxxTaa495lk8HuVD0xFYQueNS8AsACRjkOwgSf9Avh1elFRV5
+U3XZrmCOqbnDsWRTr2KEc8K9CXxqY6CwFizaoFlTftpji7W3ATU2+/QufIKYBS7Za3Zq1u7M
+HLbv4GLdEP1GVPDj2fAECP7azsN17fhCAAAAAAAAAAAAAA==
diff --git a/rt/t/data/smime/mails/5-encrypted-attachment.eml b/rt/t/data/smime/mails/5-encrypted-attachment.eml new file mode 100644 index 000000000..b6fb9b46b --- /dev/null +++ b/rt/t/data/smime/mails/5-encrypted-attachment.eml @@ -0,0 +1,42 @@ +X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709D8E.1000001@example.com>
+Date: Tue, 09 Feb 2010 02:26:06 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:5
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+MIAGCSqGSIb3DQEHA6CAMIACAQAxggHIMIHhAgEAMIGKMH0xCzAJBgNVBAYTAkFVMRMwEQYD
+VQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxETAP
+BgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBleGFtcGxlLmNvbQIJ
+AIpqzVG+lKAVMA0GCSqGSIb3DQEBAQUABEALN6in6tg2C0yVmkb0XWJr6qRLrwrJLiqcoamd
+a3VAyQeHcqIB14UYuHiN6zZA2lABUI1DsjFlDiCEg8TSyJuAMIHhAgEAMIGKMH0xCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz
+IFB0eSBMdGQxETAPBgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBl
+eGFtcGxlLmNvbQIJAIpqzVG+lKAWMA0GCSqGSIb3DQEBAQUABEBDqZbltMcqBRxIshfZ+jSa
+49l6RJAX6HVIBVZRu77rmlyVs2ft18qP0YVgwDPgD5Iok4c1Taemo3Rg7M2bHLwlMIAGCSqG
+SIb3DQEHATAaBggqhkiG9w0DAjAOAgIAoAQIWcv/5Jc8r/uggASCAqjneNECFsRSAPgwjW7G
+Hi+zLy+vPBLNfWgyuEAlKGeM347PdUciZNLhiz3D49lfHmVypGOxYTNU0kfeJVTW5bfwYHdS
+ZmPRx49tNJt08GR0eqbePKZtH0/0BW7LF1//lcNeJchsSdyRvkMB8zvTBhnNVhUSQWumrbda
+OUqvVpSdqx4SeqbyiyQKI+7AiZ2ChcZX9fA+YoiWT85NVtmgBNMMne0uHgmdBMaQHF4bTXvY
+/Mg8ew7Vg3TkVjg9QlaAe7JGrgyvSx/H4f+sn6mb68NaF5jGjiwen4a6ThRJO8lIJ30rTlb+
+WMqszV6OZK2ieWNn5BQlXOI/ew92UIuoyd5PtDkrLbkYio20KfCLpLbt1tvT8ZG0csgg9PO3
+iM3S0PWpjg4axknCYonphwSczsPcvUYZ+y4cIMdXvk5A3byMAQjLPYh0N6F9Q9tETc3HhDhA
+rSdVRot6JILv/tUs2ISPxJcMlLh8TcHZNchnRUcDg0wojULs49rONZIw8UGzbZi1H6IoGebk
+1HBsskw21pPDUjG7LpV0bKFKan0wxE5kJP1Xk5EN2Yw+2EHDE19QHs/ru2FdTjfbtcQFGlT1
+yiNUI7UwAPpCPyLoOpfvwYL2u4nSnbnKHCdjDHl4VAre8bngCMTzdRM91w/nydjpHfBbv+l5
+/EOKbYPC/SNG3IJZy70iExcXU+WydHdYCW0NhR7K1sCdwDsUpziQMvzlkJKclPC894Yljqnn
+83S8G2z3pTJ+SEAabdXY1GmYdfFeLGwnRmegzmWe0wCZKz0m4CabkDX4h0u8xu/C/5gbfU5Y
+JO1s3iVxzGa1TgyvD4aPvqh9pIQ/wSFt43HtZ5/ReG2ul7PTzOK92xC1c3xpMnXdtLEeRmNx
+2KU0Kfk1Si0lBJHf3R/JhwhHvwQI0IeGb5Ho55oAAAAAAAAAAAAA
diff --git a/rt/t/data/smime/mails/6-encrypted-binary.eml b/rt/t/data/smime/mails/6-encrypted-binary.eml new file mode 100644 index 000000000..f4d50886b --- /dev/null +++ b/rt/t/data/smime/mails/6-encrypted-binary.eml @@ -0,0 +1,48 @@ +X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709DE8.9000101@example.com>
+Date: Tue, 09 Feb 2010 02:27:36 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:6
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+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
diff --git a/rt/t/data/smime/mails/7-signed-encrypted-plain.eml b/rt/t/data/smime/mails/7-signed-encrypted-plain.eml new file mode 100644 index 000000000..7dd981a24 --- /dev/null +++ b/rt/t/data/smime/mails/7-signed-encrypted-plain.eml @@ -0,0 +1,97 @@ +X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709E37.3080003@example.com>
+Date: Tue, 09 Feb 2010 02:28:55 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:7
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+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
+AAAAAAA=
diff --git a/rt/t/data/smime/mails/8-signed-encrypted-attachment.eml b/rt/t/data/smime/mails/8-signed-encrypted-attachment.eml new file mode 100644 index 000000000..1c53ad956 --- /dev/null +++ b/rt/t/data/smime/mails/8-signed-encrypted-attachment.eml @@ -0,0 +1,107 @@ +X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709E7D.8050407@example.com>
+Date: Tue, 09 Feb 2010 02:30:05 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:8
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+MIAGCSqGSIb3DQEHA6CAMIACAQAxggHIMIHhAgEAMIGKMH0xCzAJBgNVBAYTAkFVMRMwEQYD
+VQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxETAP
+BgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBleGFtcGxlLmNvbQIJ
+AIpqzVG+lKAVMA0GCSqGSIb3DQEBAQUABEADxC8CAlzKMNF0mslS0vildCM5FQxOllhn1/nC
+DWM3qsFtrFLIy56M3Knz4GZUFAk3cRObg21WABJysenXaqYEMIHhAgEAMIGKMH0xCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz
+IFB0eSBMdGQxETAPBgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBl
+eGFtcGxlLmNvbQIJAIpqzVG+lKAWMA0GCSqGSIb3DQEBAQUABEAVZIiRyhVjCAOFOZpkGsES
+yu/zqlRn/AhhtHVwB+9+RpLr+POuaBCXlp8705wOyokMpFbV5Lan5MGitd7vmGpqMIAGCSqG
+SIb3DQEHATAaBggqhkiG9w0DAjAOAgIAoAQIEhlk2O2sbNCggASCEED/gpJpoTTN4xn+aW+X
+G+pGKN5s4lp00IyQTZ2yfrUIrEtcekJBSzGPbOTK6sbwN2LJzNUlgWzcblxvIhP+6kJtMQgC
+09O5/0BDpXhbq1KpZVizMqRoiHZc9K8X2vS7pZerCIG1FqWr5qyEdl7WMiSSrLxrGWWW5PQB
+CORILs8vJ+KLkVOGyJJavFFG3NLZrwiOSKlWevVPuRBWQAu3Z35CviVvGb1up0wbCZxx/hcI
+rnxoT7qMFv6x9KbNT/kiK6KbVIwdUb17YycxFyhihf7Eo+U+4twP12bXMCoGMR25KSHafyka
+3u8Y8Sww1MS3HHmfXOZ3sLEwq1NLjuHEKTYQKyo2eG6PHXXEnStUwhuYWtYKkVoJWjCv4I1e
+MryU4e8PkfLouktGOSzPON0Ju4KPwq1eIWRtTjI/AuhCGR59RHlwiDV2F5lXWCuqVOMD7hpv
+kgytAkUlkXRU1USSA4GQd6RIzuaxDn/VI2NyRJLqCo97J2XBU067yhgbwvDJCD1Of/01h4vw
+oSut3jXvLhz4daOW1hbuMtu4T429ye89+K/lLi6pCxxedr3DRqJDw38jbHCBS92zeGMDnmd2
+zUbBS8w/fRyLG6ac9tbKDG8fg2qtmJuR0rVKgkd4uw0HxBz2B2DOVcsy5/KoPWXAmTMGSHUh
+S4Dpxa2NHsPfYn89/o61nfKbwATEqUlPinnVaT8TllHfkSANP1MfFowr03W7e3e9FS3aILTt
+8scxSlkJ5Wn50R5shbu5QzZiQtOAkDHf/szhSR6SE5yeCmUuXzhEjv1Lplb6Go9FkPsZcZAy
+iieQTzidJ/0iUEkrjiTJdVC+jwPJhE6wPJ79PPqYCchJsdDiFrwOWtzaetZUzPrPrucyLxvk
+FojSDmCJ94XpGhdR+PwVabdeoAIac/bb3I8gfWH6q6e5TL4p01tyWMdCLVEuYJcBK1xZ8KGn
+Gmq+vYUOdTqLasdZ3Uu5D/KDlRYr1eSNOU51D8bukZT2D0ojEA4t9tXLNus1TAu3kuC39U0S
+qwXfm71n2wZZF0FUzHpelJbNTGwA6ZYL8Yb483yhDFOJyDly+3siN5u9Q6knmqHsyZ4VE7iJ
+O/pFOogowW0wn8kG6Wr5QCTd5eCHSehz7ZJHahPTVCeAr2i23W7GqT+Shp/FW9h1Iv2ARlEU
+QSY8ZKtOG8PBc6M4fvsDiS7LqwE20++DznHAFU5pRxUtRUo6Q5lO7xCoYD0MPgoW9IUgAFaS
+5RzGyYmGqFIMFZ9Q135/wA1SM3Fsx9J0oZbxOUCaSfwkpL/MlkPaRmIh9PEGuo1/Fs6AH9ui
+VEfbHWU6eixY8DY8UCKudptgeYpBfgXTtgyprwAxC3+/jhxcTBmJ5vRpTePOLjRbLZIJQ5nK
++cWtW0JvQR2Lm/I/cHtycZmz1oEPqZHVk98Sn6vxqByZFPLTvtf39tSzWaT4rpcTXUS6ybp9
+wxZeUkqEztbmnN6jSgSzk+7HP9jSiy+xYqm6oyjgVKIosaNAMKJfy/OHrUW08Mefook7Mprd
+nOVWMMRVVeLLewPVeN9mBnncocUWXVRvNL4BJ9G2jriEEx2St9JNsUkdTd3cACRvDywxreU4
+ubfVzzC9PLrUL1GW5UiR/lGqjD9mUvz9ZL5kR2Z4UyiUqyGAeul41TRPNwyosD7Fs+ryeU6/
+om2I/CJTwOq5oLtB6azEqR7C3cF3VZ44Q4IYjf/i/HNIhufFti1owJgG8wENNNQiQuEEMpeo
+j1fyBmUiAyv+4I1g83wnWTTWPNoHfQ4IWP2TlqdRjv7pYShIYoaPdgdTE5aYactX6B2+Bw0F
+ge3YyXfxevrhWkObPeyLDEjtAhidET8qSbQL1Z9X2hQZ5YnY9J1zay2JOIP1mqc+otapNQ1f
+rotU9cFBSI4oUykl+DulmDLbzR+QYRprtTgff5it3QaV0IDWp1oiUNm8PTAczek67o1Ftpw6
+PdsWDycs37GfP+KC0wJoSHRyR4Xw91WZth7le1Tuxe0Do+cDK3owP/b7dMZxggeHKrPi9V1u
+6HjS1N6SNxn4gsaPZaHOurAJAF42gOiV6O0pisAQy7kj/0hQYSiRZ8G8gIsgJVYPnT1JVQIs
+dZ8mqbNmM/bQXWkLjBNU1APzhmdaejzenYyJEnYiLfWfcWs/lyOgUwO3sU+5qQj0pN11k2bJ
+Ma2Auweq018H61jEetP4/aEIfFp4+jqjlKa07g1JdNpq5X7Sfytb6qQm0Mj6eK2bwR9JGFpH
+CihoWICFEpNYeZZdKUQBivLqep09JBOJYuPXUpeoJHx+tqe0ZFIEh+Ef6W+nm1ReKGZ1YGt4
+29noZnTTY7cgWbixVfTcCYLSQTgVNd87QIgqZwK8y3YwmMz1d4brlXUdT+uGC265aTOMtIDH
+jYv//4new3nP5RYijcPgdh1NrJm0ItoYpjk3/1OQsYy0WjVOF/XungHqblnZfLb+ll2+M6f6
+9J0nvXVyV2eqBGkLcVkBT/9T2qMEeChGXOQn0VLeBr7hpg/I8tHRw2COykagS8gIUiE6ymID
+JrsfT9L9smfXapoFbsazfi5sYK6/4/+YaLQX7yvVOrW36Fq2acAM3P6g0ICKBHiiSn0ob1WF
+QPZ3K8+Aool6V3RGM9S4gl/tsHqEmDwHmUt5bGn+P4pCB8idppljAW9Wce4Tco+w/CDrNrEW
+9nckhopXWpE0O2R6Jn1OWkWwJ+hNKd06HS0eBpMhnmJ1AWGpUR4dh89jCVoNmEvj/ar1QVkV
+h+hMHV6ti3VI5XjQ+1sXF3Zcxu8vjmf+fePgjrvoBpCxR1psxKLj1lprV5evtqSdKH5tjpnq
+JpfhtVsQ76/s5HkSknhkz8GAW+SvlfOR9DTx9leR1ZvM1KEW4ID+WcSzQoC2w1MlIdtGILua
+q2jZsK+Sb01VxxmHSuUn7DMvUo9R/7D0ukTPFOt57ZH7Ipr0S25CSxFLLmgaEUf1LCl5Mfew
+m89yJQso7SWHMEC10YJhJNkeCH+qMN4PnX2e0N6zDlHFPd7CqxoJJKba53rjOqKtEbudzHuv
+gvmQCPULmTIp9GhIS12y+jS3LkW1l7tbHHBJtQ7NdzyCqv74JHzOlxkawEoLcJDRdlsekOg9
+oHVSMRNVTvr8KF5QkI9n5pY+O3iKPVIUTGzvANbYjg99oSvWWSd5gqSSBZA2O1uSVSMcuhkg
+xNXRfDEL6AObmSULAkG3ymBQkBVt9XUMZKSMuHxTKFTGcJXf+vzsJYSHuvOvtTArV6FMm4gH
+s+eRpL/BgZu3tXU4MqNXhE5OdXNQJSxC4DkWetaocIYXPgSDbPv6L2oluTC+p9KSQZFkeH7o
+hxnj+xYB/KvR266kgsyd8tAxlvdUdTnIkhq+GCmvlRjgZv0N+qOinQCqtseUl4WlM+h7940l
+MNCNhjBlhLI1fD11eJLw5DjzPAamIoJWwhPfw2RRi4I0gKWTKBiWqoM6k04frrnKFLFqTm6x
+HdIPJxxexW4A9LopWAEotqF/av07+JpAnRPFVW4rqZENAeSz7ao1AlIz4yDTTiqubbP91f0e
+DbLmIz8X7tRDe/r9WA9OAIlzm4ISnqjazlQl0LvhIGerOebHshJnAskOVLwYhoAKW1bPGRaD
+PiWzHxHEFXz89bH6lLTLf9DuFq3HGOaXdiFTtqCForOpABXQaczIjjwRY4i/WyXKDDjrPmOl
+6g0OjJL4ZP83fryiG9qaaEMnXLJPkjy3Kd0yBVDZBhKfebOlQlNvSsJ7Q4SxOqGBsVecxiWf
+d3ftb1BUJRroTeAZDfQ/hADbCgLAgOfsiCCgV3E7shOTdJ91OaWrPYNAZiAnsjbGFYDmWJYH
+/tcYTh4o5v6y+PaiI6YCYOCV1vd9x0B2hQNrCFUloR7j9osBVqFMRtm3nJyTl5/69oGW1tMI
+EYUBm1QzLOmhnL9+nnDp8kEyjXJMEkhwG0duauOV1UY9Ouy1LgCT34TM1TE3hkvOF4weOF9L
+b4LRtdEp0SRqyL8cYYlEotMIqwyO2H3/J4BNiEuVGauFhUZhqnbZ4Ll9hebYGmb/sHrsQdC1
+5IlNzlTIOFjvhI4VzFYanPAkZTCZzMqiEIqqa4befyG28XRw3SGG8GjgQ6qMCWEIzRg6WNzz
+CRqEG4eBwG3txivFcysa48BTpiteo9hT7k1zu9MTypEiHiRh9x61PKnyL87Fh26IEA5vjda4
+HpDdgblqQu2kFo0UK+Sg52+l4SojTPSrC+K+1phmz4Y+OsVFFrog22ZQIJ684XP6cXjLI3G9
+0gkor2iMtYfV/MvEe5EAyw4NexLFpSo4wcxJxKGauyYAf/bzk9XNfrrWTG/CdVBswAP8psJ2
+W0pWFh1EVrPEvtU1FXFHHmNmPjFtusPJ8TvY9s5f/xnC6Deo3/gNojgpK4mRYICh8nnmYFJQ
+Mi0F13r7QQhDfHli8m0hqJ2c+rv2Iw43Ok7b7mBkDc4rOAIp7sXu6qaBOqmIE6fA0NYVI33p
+EFraKFJNM1mhUcj4c6UKa8mBPNba/CxHBEnN65peleMqiidGsiNLD26IKXISXb2uwFwTWOPV
+heOecqvyvZ7lcI32Lvqw9D4p4oKkHyJzxzgkcMGMcrw/TBT5gnGdw8cDDD8D3/Noxvv8w+dV
+OY6JHTnG6d8tS6th6ADuduiP+x4QLYfIvnTh1uK0COOpBYltuB88IsBqJ6DN8v6tAlgf7/+e
+r2EGJCpNDtJK5zkl8v73Ny5nhMgYUSZG9NlwYI+b+SrAJalZpWRU/vY14cbDSq+u4awzvlNH
+eATqqMdz6qVH6rl6NJksD+RxrzTxEI1WJVri3xBV1XfSiHm7R0YBGolfsQW95J1gSxY0w651
+qjVnSEWvCMClJ5eZstnXh9Vbw1gp+E1Lmg0SG4bxj7V9aIzWbKqLygWrVNNzUSbd4Efi8pu3
+bJ0/qy/stElV/+g5ULy+6qAb4zF1cqjfkS71yGJd8pQ8O6PLPVADbTe6Kvh4obYgJ3zeT3Vm
+oq4AKnfi6XYBLw6CACm/IuMa3zfyFKMw9m8r7uklGDJMa6/y1BPp/5qRMNgjeprX4er9U/EC
+bkSDeZKpAvXIh9Q7dVdpVGAL+Z+H8EJW+pe3sOurRR+HkXGPgIfjUg/XL32n/OqoMPWr28K1
+8ME63jxX5cPjMN4749Y7AbVlbw3yw6BKHpbZl5A56l3mJN9ejeJ9PEBsF9tBtzkjDSw6LV4+
+Rr/KoyC6IAro2fWUVy5rFQSDk7jWSQZIyCzD9VNhxMgpElVlwFBvve5tBRIzOJVJCplM0Ybf
+ovtJB+BA5qzaeMkH0ZbsZ5WLiuodF1RHf6dndDWyF0zP0hqn+DaBav4xUAtBAxSWGvZokShr
+sCPx2mzmvbuiCuQwHrioZveWsp6RA6pS3AGH1p+NvBzo1rbijBwiFlsgL7VyLUmXz6uxDvNm
+cnVof0+Y/nUJ30vWUzbW38lUDQOks+ZOhWBDZ4Dx1B9lrQ+SyQtIlXi5AEmTJcbYb+f1rp7O
+BKknFBBRd8kW7H5ryxyY1wyfYZ7TVhCQD7I41pXg8Mj41C6BTlqXvndhJQQI5iqg/OuwSHwA
+AAAAAAAAAAAA
diff --git a/rt/t/data/smime/mails/9-signed-encrypted-binary.eml b/rt/t/data/smime/mails/9-signed-encrypted-binary.eml new file mode 100644 index 000000000..eab9d5b65 --- /dev/null +++ b/rt/t/data/smime/mails/9-signed-encrypted-binary.eml @@ -0,0 +1,113 @@ +X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709ECC.4020500@example.com>
+Date: Tue, 09 Feb 2010 02:31:24 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:9
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+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
+qwQIeNe1tczOd/sAAAAAAAAAAAAA
diff --git a/rt/t/fts/indexed_mysql.t b/rt/t/fts/indexed_mysql.t index 0a4f02626..672b22072 100644 --- a/rt/t/fts/indexed_mysql.t +++ b/rt/t/fts/indexed_mysql.t @@ -4,18 +4,8 @@ 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 ); +RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Table => 'AttachmentsIndex' ); setup_indexing(); @@ -24,59 +14,22 @@ 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( + my %args = ( 'no-ask' => 1, command => $RT::SbinPath .'/rt-setup-fulltext-index', dba => $ENV{'RT_DBA_USER'}, 'dba-password' => $ENV{'RT_DBA_PASSWORD'}, - url => "sphinx://127.0.0.1:$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; - } + my ($exit_code, $output) = RT::Test->run_and_capture( %args ); + ok(!$exit_code, "setted up index") or diag "output: $output"; } 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; + 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 { @@ -113,21 +66,18 @@ sub run_test { @tickets = RT::Test->create_tickets( { Queue => $q->id }, - { Subject => 'book', Content => 'book' }, - { Subject => 'bar', Content => 'bar' }, + { Subject => 'first', Content => 'english' }, + { Subject => 'second', Content => 'french' }, + { Subject => 'third', Content => 'spanish' }, + { Subject => 'fourth', Content => 'german' }, ); sync_index(); run_tests( - "Content LIKE 'book'" => { book => 1, bar => 0 }, - "Content LIKE 'bar'" => { book => 0, bar => 1 }, + "Content LIKE 'english'" => { first => 1, second => 0, third => 0, fourth => 0 }, + "Content LIKE 'french'" => { first => 0, second => 1, third => 0, fourth => 0 }, ); -END { - my $Test = RT::Test->builder; - return if $Test->{Original_Pid} != $$; - return unless $sphinx{'started'}; +@tickets = (); - my $pid = int RT::Test->file_content([$sphinx{'directory'}, 'searchd.pid']); - kill TERM => $pid if $pid; -} +done_testing; diff --git a/rt/t/fts/indexed_pg.t b/rt/t/fts/indexed_pg.t index 88e35ab6a..1494fded2 100644 --- a/rt/t/fts/indexed_pg.t +++ b/rt/t/fts/indexed_pg.t @@ -9,7 +9,7 @@ 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; -RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Column => 'ContentIndex', Table => 'Attachments' ); +RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Column => 'ContentIndex', Table => 'AttachmentsIndex' ); setup_indexing(); diff --git a/rt/t/fts/indexed_sphinx.t b/rt/t/fts/indexed_sphinx.t new file mode 100644 index 000000000..38e56185f --- /dev/null +++ b/rt/t/fts/indexed_sphinx.t @@ -0,0 +1,150 @@ + +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 skip_all => "Can't determine sphinx version" + unless `$sphinx{searchd} --version` =~ /Sphinx (\d+)\.(\d+)(?:\.(\d+))?/; + +$sphinx{version} = sprintf "%d.%03d%03d", $1, $2, ($3 || 0); + +plan tests => 15; + +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://127.0.0.1:$port/rt", + 'index-type' => 'sphinx', + ); + 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; + + # Remove lines for different versions of sphinx than we're running + $sphinx_conf =~ s{^(\s+ \# \s+ for \s+ sphinx \s+ + (<=?|>=?|=) \s* + (\d+) \. (\d+) (?:\. (\d+))? + .* \n) + ((?:^\s* \w .*\n)+)}{ + my $v = sprintf "%d.%03d%03d", $3, $4, ($5 || 0); + my $prefix = eval "$sphinx{version} $2 $v" ? "" : "#"; + $1 . join("\n",map{"$prefix$_"} split "\n", $6) . "\n"; + }emix; + + $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(); + +RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Table => 'AttachmentsIndex', MaxMatches => 1000, Sphinx => 1 ); + +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/lifecycles/basics.t b/rt/t/lifecycles/basics.t index 554c95a73..e18bea3c4 100644 --- a/rt/t/lifecycles/basics.t +++ b/rt/t/lifecycles/basics.t @@ -1,7 +1,5 @@ - use strict; use warnings; -use Data::Dumper; BEGIN {require 't/lifecycles/utils.pl'}; @@ -19,7 +17,7 @@ my $tstatus = sub { diag "check basic API"; { - my $schema = $general->Lifecycle; + my $schema = $general->LifecycleObj; isa_ok($schema, 'RT::Lifecycle'); is $schema->Name, 'default', "it's a default schema"; is_deeply [$schema->Valid], @@ -80,7 +78,7 @@ diag "check status input on create"; my $valid = 1; foreach ( @form_values ) { - next if $general->Lifecycle->IsValid($_); + next if $general->LifecycleObj->IsValid($_); $valid = 0; diag("$_ doesn't appear to be a valid status, but it was in the form"); } @@ -244,3 +242,5 @@ diag "'!inactive -> inactive' actions are shown even if ticket has unresolved de ); } +undef $m; +done_testing; diff --git a/rt/t/lifecycles/dates.t b/rt/t/lifecycles/dates.t index 4f613f8d3..0c74a1b21 100644 --- a/rt/t/lifecycles/dates.t +++ b/rt/t/lifecycles/dates.t @@ -1,7 +1,5 @@ - use strict; use warnings; -use Data::Dumper; BEGIN {require 't/lifecycles/utils.pl'}; @@ -23,16 +21,13 @@ my $tstatus = sub { return $ticket->Status; }; -my ($baseurl, $m) = RT::Test->started_ok; -ok $m->login, 'logged in'; - diag "check basic API"; { - my $schema = $general->Lifecycle; + my $schema = $general->LifecycleObj; isa_ok($schema, 'RT::Lifecycle'); is $schema->Name, 'default', "it's a default schema"; - $schema = $delivery->Lifecycle; + $schema = $delivery->LifecycleObj; isa_ok($schema, 'RT::Lifecycle'); is $schema->Name, 'delivery', "it's a delivery schema"; } @@ -47,8 +42,8 @@ diag "dates on create for default schema"; 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'; + ok !$ticket->StartedObj->IsSet, 'started is not set'; + ok !$ticket->ResolvedObj->IsSet, 'resolved is not set'; } { my $ticket = RT::Ticket->new( RT->SystemUser ); @@ -58,8 +53,8 @@ diag "dates on create for default schema"; Status => 'open', ); ok $id, 'created a ticket'; - ok $ticket->StartedObj->Unix > 0, 'started is set'; - ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set'; + ok $ticket->StartedObj->IsSet, 'started is set'; + ok !$ticket->ResolvedObj->IsSet, 'resolved is not set'; } { my $ticket = RT::Ticket->new( RT->SystemUser ); @@ -69,8 +64,8 @@ diag "dates on create for default schema"; Status => 'resolved', ); ok $id, 'created a ticket'; - ok $ticket->StartedObj->Unix > 0, 'started is set'; - ok $ticket->ResolvedObj->Unix > 0, 'resolved is set'; + ok $ticket->StartedObj->IsSet, 'started is set'; + ok $ticket->ResolvedObj->IsSet, 'resolved is set'; } my $test_date = '2008-11-28 12:00:00'; @@ -140,7 +135,7 @@ diag "dates on create for delivery schema"; 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 ; + ok $ticket->StartedObj->IsSet, 'started is set to ' .$ticket->StartedObj->AsString ; is $ticket->ResolvedObj->Unix, 0, 'resolved is not set'; } { @@ -157,8 +152,8 @@ diag "dates on create for delivery schema"; ($statusval,$statusmsg) = $ticket->SetStatus('delivered'); ok($statusval,$statusmsg); - ok $ticket->StartedObj->Unix > 0, 'started is set'; - ok $ticket->ResolvedObj->Unix > 0, 'resolved is set'; + ok $ticket->StartedObj->IsSet, 'started is set'; + ok $ticket->ResolvedObj->IsSet, 'resolved is set'; } my $test_date = '2008-11-28 12:00:00'; @@ -220,30 +215,30 @@ diag "dates on status change for default schema"; 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'; + ok !$ticket->StartedObj->IsSet, 'started is not set'; + ok !$ticket->ResolvedObj->IsSet, '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'; + ok $ticket->StartedObj->IsSet, 'started is set'; + ok !$ticket->ResolvedObj->IsSet, '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'; + ok !$ticket->ResolvedObj->IsSet, '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'; + ok !$ticket->ResolvedObj->IsSet, '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'; + ok $ticket->ResolvedObj->IsSet, 'resolved is set'; } diag "dates on status change for delivery schema"; @@ -255,25 +250,25 @@ diag "dates on status change for delivery schema"; 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'; + ok !$ticket->StartedObj->IsSet, 'started is not set'; + ok !$ticket->ResolvedObj->IsSet, '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'; + ok $ticket->StartedObj->IsSet, 'started is set'; + ok !$ticket->ResolvedObj->IsSet, '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'; + ok !$ticket->ResolvedObj->IsSet, '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'; + ok $ticket->ResolvedObj->IsSet, 'resolved is set'; } diag "add partial map between general->delivery"; @@ -299,18 +294,20 @@ diag "check date changes on moving a ticket"; 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'; + ok !$ticket->StartedObj->IsSet, 'started is not set'; + ok !$ticket->ResolvedObj->IsSet, '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'; + ok $ticket->StartedObj->IsSet, 'started is set'; + ok !$ticket->ResolvedObj->IsSet, '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'; + ok $ticket->StartedObj->IsSet, 'started is set'; + ok $ticket->ResolvedObj->IsSet, 'resolved is set'; } + +done_testing; diff --git a/rt/t/lifecycles/moving.t b/rt/t/lifecycles/moving.t index 5f184e2c2..8a03e3ea6 100644 --- a/rt/t/lifecycles/moving.t +++ b/rt/t/lifecycles/moving.t @@ -1,7 +1,5 @@ - use strict; use warnings; -use Data::Dumper; BEGIN {require 't/lifecycles/utils.pl'}; @@ -94,3 +92,5 @@ diag "one way map doesn't work backwards"; is $ticket->Queue, $delivery->id, 'queue is steal the same'; is $ticket->Status, 'ordered', 'status is steal the same'; } + +done_testing; diff --git a/rt/t/lifecycles/types.t b/rt/t/lifecycles/types.t new file mode 100644 index 000000000..79b0714b1 --- /dev/null +++ b/rt/t/lifecycles/types.t @@ -0,0 +1,33 @@ +use strict; +use warnings; + +BEGIN {require 't/lifecycles/utils.pl'}; + +is_deeply( [ RT::Lifecycle->ListAll ], [qw/ approvals default delivery /], + "Get the list of all lifecycles (implicitly for for tickets)"); +is_deeply( [ RT::Lifecycle->ListAll('ticket') ], [qw/ approvals default delivery /], + "Get the list of all lifecycles for tickets"); +is_deeply( [ RT::Lifecycle->List], [qw/ default delivery /], + "Get the list of lifecycles without approvals (implicitly for for tickets)"); +is_deeply( [ RT::Lifecycle->List('ticket') ], [qw/ default delivery /], + "Get the list of lifecycles without approvals for tickets"); +is_deeply( [ RT::Lifecycle->List('racecar') ], [qw/ racing /], + "Get the list of lifecycles for other types"); + +my $tickets = RT::Lifecycle->Load( Name => '', Type => 'ticket' ); +ok($tickets, "Got a generalized lifecycle for tickets"); +isa_ok( $tickets, "RT::Lifecycle::Ticket", "Is the right subclass" ); +is_deeply( [ sort $tickets->Valid ], + [ sort qw(new open stalled resolved rejected deleted ordered), + 'on way', 'delayed', 'delivered' ], + "Only gets ticket statuses" ); + + +my $racecars = RT::Lifecycle->Load( Name => '', Type => 'racecar' ); +ok($racecars, "Got a generalized lifecycle for racecars"); +isa_ok( $racecars, "RT::Lifecycle", "Is the generalized subclass" ); +is_deeply( [ sort $racecars->Valid ], + [ sort ('on-your-mark', 'get-set', 'go', 'first', 'second', 'third', 'no-place') ], + "Only gets racecar statuses" ); + +done_testing; diff --git a/rt/t/lifecycles/unresolved-deps.t b/rt/t/lifecycles/unresolved-deps.t index 02c1942d7..5da4b8fc5 100644 --- a/rt/t/lifecycles/unresolved-deps.t +++ b/rt/t/lifecycles/unresolved-deps.t @@ -1,8 +1,6 @@ use strict; use warnings; -use Data::Dumper; -use Test::More tests => 15 + 1; # plus one for warnings check BEGIN {require 't/lifecycles/utils.pl'}; my $general = RT::Test->load_or_create_queue( @@ -41,3 +39,5 @@ ok $m->login, 'logged in'; ); } +undef $m; +done_testing; diff --git a/rt/t/lifecycles/utils.pl b/rt/t/lifecycles/utils.pl index 3813df3e9..4dbba238d 100644 --- a/rt/t/lifecycles/utils.pl +++ b/rt/t/lifecycles/utils.pl @@ -63,10 +63,15 @@ Set(\%Lifecycles, 'delayed -> on way' => {label => 'Put On Way', update => 'Respond'}, }, }, + racing => { + type => 'racecar', + active => ['on-your-mark', 'get-set', 'go'], + inactive => ['first', 'second', 'third', 'no-place'], + }, ); END } -use RT::Test config => $config; +use RT::Test config => $config, tests => undef; 1; diff --git a/rt/t/mail/autogenerated.t b/rt/t/mail/autogenerated.t new file mode 100644 index 000000000..a37c9b12d --- /dev/null +++ b/rt/t/mail/autogenerated.t @@ -0,0 +1,22 @@ +use strict; +use warnings; + +use RT::Test tests => undef; +use Email::Abstract; + +my $msg = Email::Abstract->new(<<'MSG')->cast("MIME::Entity"); +From: somebody@example.com +To: rt@example.com +Precedence: never-bounce +Precedence: bulk +Subject: testing precedence + +I am bulk mail, hear me roar! +MSG + +ok RT::Interface::Email::CheckForAutoGenerated($msg->head), "Is AutoGenerated"; + +$msg->head->delete("Precedence", 1); +ok !RT::Interface::Email::CheckForAutoGenerated($msg->head), "Isn't AutoGenerated"; + +done_testing; diff --git a/rt/t/mail/charsets-outgoing-plaintext.t b/rt/t/mail/charsets-outgoing-plaintext.t new file mode 100644 index 000000000..be576e0bd --- /dev/null +++ b/rt/t/mail/charsets-outgoing-plaintext.t @@ -0,0 +1,315 @@ +use strict; +use warnings; + +use RT::Test tests => 79, text_templates => 1; + +my %string = ( + ru => { + test => "\x{442}\x{435}\x{441}\x{442}", + autoreply => "\x{410}\x{432}\x{442}\x{43e}\x{43e}\x{442}\x{432}\x{435}\x{442}", + support => "\x{43f}\x{43e}\x{434}\x{434}\x{435}\x{440}\x{436}\x{43a}\x{430}", + }, + latin1 => { + test => Encode::decode('latin1', "t\xE9st"), + autoreply => Encode::decode('latin1', "a\xFCtoreply"), + support => Encode::decode('latin1', "supp\xF5rt"), + }, +); + +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'; + +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"; +{ + my $template = RT::Template->new( RT->SystemUser ); + $template->Load('Autoreply'); + ok $template->id, "loaded autoreply tempalte"; + + my ($status, $msg) = $template->SetContent( + "Subject: Autreply { \$Ticket->Subject }\n" + ."\n" + ."hi there it's an autoreply.\n" + ."\n" + ); + ok $status, "changed content of the template" + or diag "error: $msg"; +} + +diag "basic test of autoreply"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; +} + +diag "non-ascii Subject with ascii prefix set in the template"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +foreach my $tag_set ( 'ru', 'latin1' ) { + +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"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$tag_set}{support}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +diag "non-ascii subject with non-ascii subject tag"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$tag_set}{support}/ + or do { $status = 0; diag "wrong subject: $subject" }; + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +} # subject tag + +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"; +} + + +foreach my $prefix_set ( 'ru', 'latin1' ) { + +diag "add non-ascii subject prefix in the autoreply template"; +{ + my $template = RT::Template->new( RT->SystemUser ); + $template->Load('Autoreply'); + ok $template->id, "loaded autoreply tempalte"; + + my ($status, $msg) = $template->SetContent( + "Subject: $string{$prefix_set}{autoreply} { \$Ticket->Subject }\n" + ."\n" + ."hi there it's an autoreply.\n" + ."\n" + ); + ok $status, "changed content of the template" or diag "error: $msg"; +} + +diag "ascii subject with non-ascii subject prefix in template"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$prefix_set}{autoreply}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +diag "non-ascii subject with non-ascii subject prefix in template"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$prefix_set}{autoreply}/ + or do { $status = 0; diag "wrong subject: $subject" }; + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +foreach my $tag_set ( 'ru', 'latin1' ) { +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"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$prefix_set}{autoreply}/ + or do { $status = 0; diag "wrong subject: $subject" }; + $subject =~ /$string{$tag_set}{support}/ + or do { $status = 0; diag "wrong subject: $subject" }; + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +} # subject tag + +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"; +} + +} # prefix set + + +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 ); + $template->Load('Autoreply'); + ok $template->id, "loaded autoreply tempalte"; + + my ($status, $msg) = $template->SetContent( + "\n" + ."\n" + ."hi there it's an autoreply.\n" + ."\n" + ); + ok $status, "changed content of the template" or diag "error: $msg"; +} + +diag "non-ascii Subject without changes in template"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +foreach my $tag_set ( 'ru', 'latin1' ) { +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"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + $subject =~ /$string{$tag_set}{support}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +} # subject tag set + diff --git a/rt/t/mail/charsets-outgoing.t b/rt/t/mail/charsets-outgoing.t index 872721325..0f78f0a58 100644 --- a/rt/t/mail/charsets-outgoing.t +++ b/rt/t/mail/charsets-outgoing.t @@ -32,7 +32,7 @@ diag "make sure queue has no subject tag"; diag "set intial simple autoreply template"; { my $template = RT::Template->new( RT->SystemUser ); - $template->Load('Autoreply'); + $template->Load('Autoreply in HTML'); ok $template->id, "loaded autoreply tempalte"; my ($status, $msg) = $template->SetContent( @@ -144,7 +144,7 @@ foreach my $prefix_set ( 'ru', 'latin1' ) { diag "add non-ascii subject prefix in the autoreply template"; { my $template = RT::Template->new( RT->SystemUser ); - $template->Load('Autoreply'); + $template->Load('Autoreply in HTML'); ok $template->id, "loaded autoreply tempalte"; my ($status, $msg) = $template->SetContent( @@ -248,7 +248,7 @@ diag "don't change subject via template"; # we should test situation when subject is not changed from template { my $template = RT::Template->new( RT->SystemUser ); - $template->Load('Autoreply'); + $template->Load('Autoreply in HTML'); ok $template->id, "loaded autoreply tempalte"; my ($status, $msg) = $template->SetContent( diff --git a/rt/t/mail/crypt-gnupg.t b/rt/t/mail/crypt-gnupg.t index ffb059706..567573e93 100644 --- a/rt/t/mail/crypt-gnupg.t +++ b/rt/t/mail/crypt-gnupg.t @@ -10,9 +10,10 @@ BEGIN { qw/data gnupg keyrings/ ); } -use RT::Test::GnuPG tests => 96, gnupg_options => { homedir => $homedir }; +use RT::Test::GnuPG tests => 100, gnupg_options => { homedir => $homedir }; use Test::Warn; +use_ok('RT::Crypt'); use_ok('MIME::Entity'); diag 'only signing. correct passphrase'; @@ -22,10 +23,12 @@ diag 'only signing. correct passphrase'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); ok( $entity, 'signed entity'); ok( !$res{'logger'}, "log is here as well" ) or diag $res{'logger'}; - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 2, 'two records: passphrase, signing'); is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct'); is( $status[0]->{'Status'}, 'DONE', 'good passphrase'); @@ -36,15 +39,17 @@ diag 'only signing. correct passphrase'; ok( $entity->is_multipart, 'signed message is multipart' ); is( $entity->parts, 2, 'two parts' ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 1, 'one protected part' ); is( $parts[0]->{'Type'}, 'signed', "have signed part" ); is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); is( $parts[0]->{'Top'}, $entity, "it's the same entity" ); - my @res = RT::Crypt::GnuPG::VerifyDecrypt( Entity => $entity ); + my @res = RT::Crypt->VerifyDecrypt( Entity => $entity ); is scalar @res, 1, 'one operation'; - @status = RT::Crypt::GnuPG::ParseStatus( $res[0]{'status'} ); + @status = RT::Crypt->ParseStatus( + Protocol => $res[0]{'Protocol'}, Status => $res[0]{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Operation'}, 'Verify', 'operation is correct'); is( $status[0]->{'Status'}, 'DONE', 'good passphrase'); @@ -60,7 +65,7 @@ diag 'only signing. missing passphrase'; ); my %res; warning_like { - %res = RT::Crypt::GnuPG::SignEncrypt( + %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => '' @@ -69,7 +74,9 @@ diag 'only signing. missing passphrase'; ok( $res{'exit_code'}, "couldn't sign without passphrase"); ok( $res{'error'} || $res{'logger'}, "error is here" ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct'); is( $status[0]->{'Status'}, 'MISSING', 'missing passphrase'); @@ -85,7 +92,7 @@ diag 'only signing. wrong passphrase'; my %res; warning_like { - %res = RT::Crypt::GnuPG::SignEncrypt( + %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'wrong', @@ -95,7 +102,9 @@ diag 'only signing. wrong passphrase'; ok( $res{'exit_code'}, "couldn't sign with bad passphrase"); ok( $res{'error'} || $res{'logger'}, "error is here" ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct'); is( $status[0]->{'Status'}, 'BAD', 'wrong passphrase'); @@ -109,18 +118,20 @@ diag 'encryption only'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 ); ok( !$res{'exit_code'}, "successful encryption" ); ok( !$res{'logger'}, "no records in logger" ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Operation'}, 'Encrypt', 'operation is correct'); is( $status[0]->{'Status'}, 'DONE', 'done'); ok($entity, 'get an encrypted part'); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 1, 'one protected part' ); is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" ); is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); @@ -138,7 +149,7 @@ diag 'encryption only, bad recipient'; my %res; warning_like { - %res = RT::Crypt::GnuPG::SignEncrypt( + %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0, ); @@ -147,7 +158,9 @@ diag 'encryption only, bad recipient'; ok( $res{'exit_code'}, 'no way to encrypt without keys of recipients'); ok( $res{'logger'}, "errors are in logger" ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Keyword'}, 'INV_RECP', 'invalid recipient'); } @@ -160,11 +173,13 @@ diag 'encryption and signing with combined method'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Passphrase => 'test' ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Passphrase => 'test' ); ok( !$res{'exit_code'}, "successful encryption with signing" ); ok( !$res{'logger'}, "no records in logger" ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 3, 'three records: passphrase, sign and encrypt'); is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct'); is( $status[0]->{'Status'}, 'DONE', 'done'); @@ -175,7 +190,7 @@ diag 'encryption and signing with combined method'; ok($entity, 'get an encrypted and signed part'); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 1, 'one protected part' ); is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" ); is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); @@ -190,14 +205,14 @@ diag 'encryption and signing with cascading, sign on encrypted'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 ); ok( !$res{'exit_code'}, 'successful encryption' ); ok( !$res{'logger'}, "no records in logger" ); - %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); + %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); ok( !$res{'exit_code'}, 'successful signing' ); ok( !$res{'logger'}, "no records in logger" ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 1, 'one protected part, top most' ); is( $parts[0]->{'Type'}, 'signed', "have signed part" ); is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); @@ -212,7 +227,7 @@ diag 'find signed/encrypted part deep inside'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 ); ok( !$res{'exit_code'}, "success" ); $entity->make_multipart( 'mixed', Force => 1 ); $entity->attach( @@ -220,7 +235,7 @@ diag 'find signed/encrypted part deep inside'; Data => ['-'x76, 'this is mailing list'], ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 1, 'one protected part' ); is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" ); is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); @@ -236,7 +251,7 @@ diag 'wrong signed/encrypted parts: no protocol'; Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0, ); @@ -245,11 +260,12 @@ diag 'wrong signed/encrypted parts: no protocol'; $entity->head->mime_attr( 'Content-Type.protocol' => undef ); 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' ); + warning_like { @parts = RT::Crypt->FindProtectedParts( Entity => $entity ) } + qr{Entity is 'multipart/encrypted', but has no protocol defined. Checking for PGP part}; + is( scalar @parts, 1, 'one protected part' ); + is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" ); + is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); + is( $parts[0]->{'Top'}, $entity, "it's the same entity" ); } diag 'wrong signed/encrypted parts: not enought parts'; @@ -261,7 +277,7 @@ diag 'wrong signed/encrypted parts: not enought parts'; Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0, ); @@ -271,7 +287,7 @@ diag 'wrong signed/encrypted parts: not enought parts'; my @parts; warning_like { - @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); } qr/Encrypted or signed entity must has two subparts. Skipped/; is( scalar @parts, 0, 'no protected parts' ); } @@ -284,11 +300,11 @@ diag 'wrong signed/encrypted parts: wrong proto'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 ); ok( !$res{'exit_code'}, 'success' ); $entity->head->mime_attr( 'Content-Type.protocol' => 'application/bad-proto' ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 0, 'no protected parts' ); } @@ -300,11 +316,11 @@ diag 'wrong signed/encrypted parts: wrong proto'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); ok( !$res{'exit_code'}, 'success' ); $entity->head->mime_attr( 'Content-Type.protocol' => 'application/bad-proto' ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 0, 'no protected parts' ); } @@ -314,7 +330,7 @@ diag 'verify inline and in attachment signatures'; my $parser = new MIME::Parser; my $entity = $parser->parse( $fh ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 2, 'two protected parts' ); is( $parts[1]->{'Type'}, 'signed', "have signed part" ); is( $parts[1]->{'Format'}, 'Inline', "inline format" ); @@ -325,8 +341,10 @@ diag 'verify inline and in attachment signatures'; is( $parts[0]->{'Data'}, $entity->parts(1), "data in second part" ); is( $parts[0]->{'Signature'}, $entity->parts(2), "file's signature in third part" ); - my @res = RT::Crypt::GnuPG::VerifyDecrypt( Entity => $entity ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res[0]->{'status'} ); + my @res = RT::Crypt->VerifyDecrypt( Entity => $entity ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res[0]{'Protocol'}, Status => $res[0]{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Operation'}, 'Verify', 'operation is correct'); is( $status[0]->{'Status'}, 'DONE', 'good passphrase'); diff --git a/rt/t/mail/dashboard-chart-with-utf8.t b/rt/t/mail/dashboard-chart-with-utf8.t index 37f8ce0c6..4fe483a1b 100644 --- a/rt/t/mail/dashboard-chart-with-utf8.t +++ b/rt/t/mail/dashboard-chart-with-utf8.t @@ -1,16 +1,10 @@ use strict; use warnings; -BEGIN { - require RT::Test; +use RT::Test tests => undef; - if (eval { require GD }) { - RT::Test->import(tests => 15); - } - else { - RT::Test->import(skip_all => 'GD required.'); - } -} +plan skip_all => 'GD required' + unless GD->require; my $root = RT::Test->load_or_create_user( Name => 'root' ); @@ -29,6 +23,7 @@ $m->submit_form( fields => { SavedSearchDescription => 'chart foo', SavedSearchOwner => 'RT::User-' . $root->id, + ChartStyle => 'bar', }, button => 'SavedSearchSave', ); @@ -88,3 +83,5 @@ if ( my $io = $handle->open('r') ) { } is( $mail_image_data, $image, 'image in mail is the same one in web' ); +undef $m; +done_testing; diff --git a/rt/t/mail/dashboards.t b/rt/t/mail/dashboards.t index 6bf4ba520..d7b1ccc7a 100644 --- a/rt/t/mail/dashboards.t +++ b/rt/t/mail/dashboards.t @@ -101,7 +101,7 @@ sub produces_dashboard_mail_ok { # {{{ my $mail = parse_mail( $mails[0] ); is($mail->head->get('Subject'), $subject); - is($mail->head->get('From'), "root\n"); + is($mail->head->get('From'), qq{"root" <root\@localhost>\n}); is($mail->head->get('Content-Transfer-Encoding'), "base64\n"); is($mail->head->get('X-RT-Dashboard-Id'), "$dashboard_id\n"); is($mail->head->get('X-RT-Dashboard-Subscription-Id'), "$subscription_id\n"); diff --git a/rt/t/mail/digest-attributes.t b/rt/t/mail/digest-attributes.t index 54c1c803f..a0940dbb1 100644 --- a/rt/t/mail/digest-attributes.t +++ b/rt/t/mail/digest-attributes.t @@ -51,48 +51,48 @@ my $everyone = RT::Group->new( RT->SystemUser ); ok( $ret, "Loaded 'everyone' group: $msg" ); ( $ret, $msg ) = $everyone->PrincipalObj->GrantRight( Right => 'CreateTicket', - Object => $testq ); + 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 ); + Object => $testq ); ok( $ret || $msg =~ /already has/, "Granted dduser AdminQueue on testq: $msg" ); ( $ret, $msg ) = $testq->AddWatcher( Type => 'AdminCc', - PrincipalId => $user_d->PrincipalObj->id ); + 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 ); + Object => $testq ); ok( $ret || $msg =~ /already has/, "Granted emailnormal right on testq: $msg" ); ( $ret, $msg ) = $user_w->PrincipalObj->GrantRight( Right => 'AdminQueue', - Object => $testq ); + Object => $testq ); ok( $ret || $msg =~ /already has/, "Granted emailweekly right on testq: $msg" ); ( $ret, $msg ) = $user_s->PrincipalObj->GrantRight( Right => 'AdminQueue', - Object => $testq ); + 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', - ); + 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 ); + 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 ); + 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" ); + 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 @@ -113,11 +113,11 @@ while( my $txn = $txns->Next ) { # 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" ); - } + # ...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" ); + } } } diff --git a/rt/t/mail/gateway.t b/rt/t/mail/gateway.t index 4f906c89c..89b1b60ab 100644 --- a/rt/t/mail/gateway.t +++ b/rt/t/mail/gateway.t @@ -420,7 +420,7 @@ EOF diag "Testing preservation of binary attachments"; { # Get a binary blob (Best Practical logo) - my $LOGO_FILE = $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png'; + my $LOGO_FILE = $RT::StaticPath .'/images/bpslogo.png'; # Create a mime entity with an attachment my $entity = MIME::Entity->build( @@ -636,13 +636,15 @@ EOF close (MAIL); is ($? >> 8, 0, "The mail gateway exited normally"); +DBIx::SearchBuilder::Record::Cachable->FlushCache; + $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'); -# check that there is no text transactions writen -is( $tick->Transactions->Count, 2, 'no superfluous transactions'); +# check that there is no text transactions writen (create + 2 for take) +is( $tick->Transactions->Count, 3, 'no superfluous transactions'); my $status; ($status, $msg) = $tick->SetOwner( RT->Nobody->Id, 'Force' ); @@ -672,8 +674,8 @@ is( $tick->OwnerObj->EmailAddress, 'root@localhost', 'successfuly take ticket vi my $txns = $tick->Transactions; $txns->Limit( FIELD => 'Type', VALUE => 'Correspond'); $txns->OrderBy( FIELD => 'id', ORDER => 'DESC' ); -# +1 because of auto open -is( $tick->Transactions->Count, 6, 'no superfluous transactions'); +# +2 from owner to nobody, +1 because of auto open, +2 from take, +1 from correspond +is( $tick->Transactions->Count, 9, 'no superfluous transactions'); is( $txns->First->Subject, "[$RT::rtname \#$id] correspondence", 'successfuly add correspond within take via email' ); $m->no_warnings_ok; @@ -695,15 +697,16 @@ $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'); +# +1 from resolve +is( $tick->Transactions->Count, 10, 'no superfluous transactions'); use RT::User; my $user = RT::User->new( RT->SystemUser ); my ($uid) = $user->Create( Name => 'ext-mailgate', - EmailAddress => 'ext-mailgate@localhost', - Privileged => 1, - Password => 'qwe123', - ); + EmailAddress => 'ext-mailgate@localhost', + Privileged => 1, + Password => 'qwe123', + ); ok( $uid, 'user created for ext-mailgate tests' ); ok( !$user->HasRight( Right => 'OwnTicket', Object => $queue ), "User can't own ticket" ); @@ -733,7 +736,7 @@ 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/That user may not own tickets in that queue/); $m->next_warning_like(qr/Could not record email: Ticket not taken/); $m->no_leftover_warnings_ok; @@ -752,7 +755,7 @@ 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/That user may not own tickets in that queue/); $m->next_warning_like(qr/Could not record email: Ticket not taken/); $m->no_leftover_warnings_ok; @@ -771,7 +774,7 @@ 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/That user may not own tickets in that queue/); $m->next_warning_like(qr/Could not record email: Ticket not taken/); $m->no_leftover_warnings_ok; @@ -784,14 +787,14 @@ my $acl = RT::ACL->new(RT->SystemUser); $acl->Limit( FIELD => 'RightName', VALUE => 'ReplyToTicket' ); $acl->LimitToObject( $RT::System ); while( my $ace = $acl->Next ) { - $ace->Delete; + $ace->Delete; } ok( !$user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "User can't reply to ticket any more" ); -my $group = RT::Group->new( RT->SystemUser ); -ok( $group->LoadQueueRoleGroup( Queue => $qid, Type=> 'Owner' ), "load queue owners role group" ); +my $group = $queue->RoleGroup( 'Owner' ); +ok( $group->Id, "load queue owners role group" ); $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" ); @@ -816,7 +819,8 @@ DBIx::SearchBuilder::Record::Cachable->FlushCache; $tick->Load( $id ); 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" ); +# +2 from take, +1 from correspond +is( $tick->Transactions->Count, 6, "transactions added" ); $m->no_warnings_ok; diff --git a/rt/t/mail/gnupg-bad.t b/rt/t/mail/gnupg-bad.t index a1c45be05..570501c30 100644 --- a/rt/t/mail/gnupg-bad.t +++ b/rt/t/mail/gnupg-bad.t @@ -10,7 +10,7 @@ use RT::Test::GnuPG ), }; -RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' ); +RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::Crypt' ); my ($baseurl, $m) = RT::Test->started_ok; diff --git a/rt/t/mail/gnupg-incoming.t b/rt/t/mail/gnupg-incoming.t index 48d2d9b73..54b30d2a3 100644 --- a/rt/t/mail/gnupg-incoming.t +++ b/rt/t/mail/gnupg-incoming.t @@ -28,7 +28,7 @@ ok( $m->login, 'we did log in' ); $m->get( $baseurl.'/Admin/Queues/'); $m->follow_link_ok( {text => 'General'} ); $m->submit_form( form_number => 3, - fields => { CorrespondAddress => 'general@example.com' } ); + fields => { CorrespondAddress => 'general@example.com' } ); $m->content_like(qr/general\@example.com.* - never/, 'has key info.'); ok(my $user = RT::User->new(RT->SystemUser)); @@ -73,6 +73,7 @@ run3( '--default-key' => 'recipient@example.com', '--homedir' => $homedir, '--passphrase' => 'recipient', + '--no-permission-warning', ), \"fnord\r\n", \$buf, @@ -115,6 +116,7 @@ run3( '--default-key' => 'recipient@example.com', '--homedir' => $homedir, '--passphrase' => 'recipient', + '--no-permission-warning', ), \"clearfnord\r\n", \$buf, @@ -157,6 +159,7 @@ run3( '--default-key' => 'recipient@example.com', '--homedir' => $homedir, '--passphrase' => 'recipient', + '--no-permission-warning', ), \"orzzzzzz\r\n", \$buf, @@ -187,7 +190,7 @@ RT::Test->close_mailgate_ok($mail); 'recorded incoming mail that is encrypted' ); is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', 'recorded incoming mail that is encrypted' ); like( $attach->Content, qr/orz/); @@ -224,7 +227,7 @@ RT::Test->close_mailgate_ok($mail); 'recorded incoming mail that is encrypted' ); is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', 'recorded incoming mail that is encrypted' ); like( $attach->Content, qr/orz/); @@ -242,6 +245,7 @@ run3( '--default-key' => 'rt@example.com', '--homedir' => $homedir, '--passphrase' => 'test', + '--no-permission-warning', ), \"alright\r\n", \$buf, @@ -277,6 +281,7 @@ run3( qw(gpg --batch --no-tty --armor --encrypt), '--recipient' => 'random@localhost', '--homedir' => $homedir, + '--no-permission-warning', ), \"should not be there either\r\n", \$buf, @@ -314,6 +319,7 @@ run3( qw(gpg --batch --no-tty --armor --encrypt), '--recipient' => 'rt@example.com', '--homedir' => $homedir, + '--no-permission-warning', ), \"really should not be there either\r\n", \$buf, diff --git a/rt/t/mail/gnupg-outgoing-encrypted-plaintext.t b/rt/t/mail/gnupg-outgoing-encrypted-plaintext.t new file mode 100644 index 000000000..35cfceddd --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-encrypted-plaintext.t @@ -0,0 +1,27 @@ +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 104, + text_templates => 1, + 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-plaintext.t b/rt/t/mail/gnupg-outgoing-plain-plaintext.t new file mode 100644 index 000000000..32e7d5d8c --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-plain-plaintext.t @@ -0,0 +1,25 @@ +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 104, + text_templates => 1, + 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-plaintext.t b/rt/t/mail/gnupg-outgoing-signed-plaintext.t new file mode 100644 index 000000000..cf46edd52 --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-signed-plaintext.t @@ -0,0 +1,27 @@ +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 104, + text_templates => 1, + 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-plaintext.t b/rt/t/mail/gnupg-outgoing-signed_encrypted-plaintext.t new file mode 100644 index 000000000..c2753d00b --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-signed_encrypted-plaintext.t @@ -0,0 +1,28 @@ +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 104, + text_templates => 1, + 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 834014ccc..1609cffbb 100644 --- a/rt/t/mail/gnupg-realmail.t +++ b/rt/t/mail/gnupg-realmail.t @@ -62,7 +62,7 @@ sub email_ok { my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', "$eid: recorded incoming mail that is encrypted" ); diff --git a/rt/t/mail/gnupg-reverification.t b/rt/t/mail/gnupg-reverification.t index deef1ec24..06c2e0d40 100644 --- a/rt/t/mail/gnupg-reverification.t +++ b/rt/t/mail/gnupg-reverification.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test::GnuPG tests => 232, gnupg_options => { passphrase => 'rt-test' }; +use RT::Test::GnuPG tests => undef, gnupg_options => { passphrase => 'rt-test' }; diag "load Everyone group"; my $everyone; @@ -46,8 +46,7 @@ foreach my $file ( @files ) { is $status >> 8, 0, "$eid: the mail gateway exited normally"; ok $id, "$eid: got id of a newly created ticket - $id"; - like($warnings, qr/Had a problem during decrypting and verifying/); - like($warnings, qr/public key not found/); + like($warnings, qr/Public key '0xD328035D84881F1B' is not available/); my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $id ); @@ -62,12 +61,10 @@ foreach my $file ( @files ) { $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/); - $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); # some mails contain multiple signatures if ($eid == 5 || $eid == 17 || $eid == 18) { $m->next_warning_like(qr/public key not found/); - $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); } $m->no_leftover_warnings_ok; @@ -90,3 +87,5 @@ foreach my $id ( @ticket_ids ) { $m->no_warnings_ok; } +undef $m; +done_testing; diff --git a/rt/t/mail/header-characters.t b/rt/t/mail/header-characters.t index 004ba8522..38a04b5df 100644 --- a/rt/t/mail/header-characters.t +++ b/rt/t/mail/header-characters.t @@ -6,14 +6,10 @@ use Test::Warn; my ($baseurl, $m) = RT::Test->started_ok; -diag "Testing non-ASCII in From: header"; -SKIP:{ - skip "Test requires Email::Address 1.893 or later, " - . "you have $Email::Address::VERSION", 3, - if $Email::Address::VERSION < 1.893; - +diag "Testing non-ASCII latin1 in From: header"; +{ my $mail = Encode::encode( 'iso-8859-1', Encode::decode( "UTF-8", <<'.') ); -From: René@example.com> +From: <René@example.com> Reply-To: =?iso-8859-1?Q?Ren=E9?= <René@example.com> Subject: testing non-ASCII From Content-Type: text/plain; charset=iso-8859-1 @@ -23,24 +19,20 @@ here's some content my ($status, $id); warnings_like { ( $status, $id ) = RT::Test->send_via_mailgate($mail) } - [qr/Failed to parse Reply-To:.*, From:/, + [(qr/Unable to parse an email address from/) x 2, qr/Couldn't parse or find sender's address/ ], 'Got parse error for non-ASCII in From'; - is( $status >> 8, 0, "The mail gateway exited normally" ); TODO: { - local $TODO = "Currently don't handle non-ASCII for sender"; - ok( $id, "Created ticket" ); - } + local $TODO = "Currently don't handle non-ASCII for sender"; + is( $status >> 8, 0, "The mail gateway exited normally" ); + ok( $id, "Created ticket" ); + } } -diag "Testing iso-8859-1 encoded non-ASCII in From: header"; -SKIP:{ - skip "Test requires Email::Address 1.893 or later, " - . "you have $Email::Address::VERSION", 3, - if $Email::Address::VERSION < 1.893; - - my $mail = Encode::encode( 'iso-8859-1', Encode::decode( "UTF-8", <<'.' ) ); +diag "Testing non-ASCII latin1 in From: header with MIME-word-encoded phrase"; +{ + my $mail = Encode::encode( 'iso-8859-1', Encode::decode( "UTF-8", <<'.') ); From: =?iso-8859-1?Q?Ren=E9?= <René@example.com> Reply-To: =?iso-8859-1?Q?Ren=E9?= <René@example.com> Subject: testing non-ASCII From @@ -51,14 +43,14 @@ here's some content my ($status, $id); warnings_like { ( $status, $id ) = RT::Test->send_via_mailgate($mail) } - [qr/Failed to parse Reply-To:.*, From:/, + [(qr/Unable to parse an email address from/) x 2, qr/Couldn't parse or find sender's address/ ], 'Got parse error for iso-8859-1 in From'; - is( $status >> 8, 0, "The mail gateway exited normally" ); TODO: { - local $TODO = "Currently don't handle non-ASCII in sender"; - ok( $id, "Created ticket" ); + local $TODO = "Currently don't handle non-ASCII in sender"; + is( $status >> 8, 0, "The mail gateway exited normally" ); + ok( $id, "Created ticket" ); } } @@ -76,6 +68,6 @@ here's some content warnings_like { ( $status, $id ) = RT::Test->send_via_mailgate($mail) } [qr/Couldn't parse or find sender's address/], 'Got parse error with no sender fields'; - is( $status >> 8, 0, "The mail gateway exited normally" ); + is( $status >> 8, 1, "The mail gateway failed" ); ok( !$id, "No ticket created" ); } diff --git a/rt/t/mail/html-outgoing.t b/rt/t/mail/html-outgoing.t new file mode 100644 index 000000000..a37f52cdd --- /dev/null +++ b/rt/t/mail/html-outgoing.t @@ -0,0 +1,187 @@ +use strict; +use warnings; +use RT::Test tests => undef; + +use RT::Test::Email; +use Test::Warn; + +my $root = RT::User->new(RT->SystemUser); +$root->Load('root'); + +# Set root as admincc +my $q = RT::Queue->new(RT->SystemUser); +$q->Load('General'); +my ($ok, $msg) = $q->AddWatcher( Type => 'AdminCc', PrincipalId => $root->Id ); +ok($ok, "Added root as a watcher on the General queue"); + +# Create a couple users to test notifications +my %users; +for my $user_name (qw(enduser tech)) { + my $user = $users{$user_name} = RT::User->new(RT->SystemUser); + $user->Create( Name => ucfirst($user_name), + Privileged => 1, + EmailAddress => $user_name.'@example.com'); + my ($val, $msg); + ($val, $msg) = $user->PrincipalObj->GrantRight(Object =>$q, Right => $_) + for qw(ModifyTicket OwnTicket ShowTicket); +} + +my $t = RT::Ticket->new(RT->SystemUser); +my ($tid, $ttrans, $tmsg); + +diag "Autoreply and AdminCc (Transaction)"; +mail_ok { + ($tid, $ttrans, $tmsg) = + $t->Create(Subject => "The internet is broken", + Owner => 'Tech', Requestor => 'Enduser', + Queue => 'General'); +} { from => qr/The default queue/, + to => 'enduser@example.com', + subject => qr/\Q[example.com #1] AutoReply: The internet is broken\E/, + body => parts_regex( + 'trouble ticket regarding \*?The internet is broken\*?', + 'trouble ticket regarding <b>The internet is broken</b>' + ), + 'Content-Type' => qr{multipart}, +},{ from => qr/RT System/, + bcc => 'root@localhost', + subject => qr/\Q[example.com #1] The internet is broken\E/, + body => parts_regex( + 'Request (\[\d+\])?1(\s*[(<]http://localhost:\d+/Ticket/Display\.html\?id=1[)>])?\s*was acted upon by RT_System', + 'Request <a href="http://localhost:\d+/Ticket/Display\.html\?id=1">1</a> was acted upon by RT_System\.</b>' + ), + 'Content-Type' => qr{multipart}, +}; + +diag "Admin Correspondence and Correspondence"; +mail_ok { + ($ok, $tmsg) = $t->Correspond( + MIMEObj => HTML::Mason::Commands::MakeMIMEEntity( + Body => '<p>This is a test of <b>HTML</b> correspondence.</p>', + Type => 'text/html', + ), + ); +} { from => qr/RT System/, + bcc => 'root@localhost', + subject => qr/\Q[example.com #1] The internet is broken\E/, + body => parts_regex( + 'Ticket URL: (?:\[\d+\])?http://localhost:\d+/Ticket/Display\.html\?id=1.+?'. + 'This is a test of \*?HTML\*? correspondence\.', + 'Ticket URL: <a href="(http://localhost:\d+/Ticket/Display\.html\?id=1)">\1</a>.+?'. + '<p>This is a test of <b>HTML</b> correspondence\.</p>' + ), + 'Content-Type' => qr{multipart}, +},{ from => qr/RT System/, + to => 'enduser@example.com', + subject => qr/\Q[example.com #1] The internet is broken\E/, + body => parts_regex( + 'This is a test of \*?HTML\*? correspondence\.', + '<p>This is a test of <b>HTML</b> correspondence\.</p>' + ), + 'Content-Type' => qr{multipart}, +}; + +SKIP: { + skip "Only fails on core HTMLFormatter", 9 + unless RT->Config->Get("HTMLFormatter") eq "core"; + diag "Failing HTML -> Text conversion"; + warnings_like { + my $body = '<table><tr><td><table><tr><td>Foo</td></tr></table></td></tr></table>'; + mail_ok { + ($ok, $tmsg) = $t->Correspond( + MIMEObj => HTML::Mason::Commands::MakeMIMEEntity( + Body => $body, + Type => 'text/html', + ), + ); + } { from => qr/RT System/, + bcc => 'root@localhost', + subject => qr/\Q[example.com #1] The internet is broken\E/, + body => qr{Ticket URL: <a href="(http://localhost:\d+/Ticket/Display\.html\?id=1)">\1</a>.+?$body}s, + 'Content-Type' => qr{text/html}, # TODO + },{ from => qr/RT System/, + to => 'enduser@example.com', + subject => qr/\Q[example.com #1] The internet is broken\E/, + body => qr{$body}, + 'Content-Type' => qr{text/html}, # TODO + }; + } [(qr/uninitialized value/, qr/Failed to downgrade HTML/)x3]; +} + + +diag "Admin Comment in HTML"; +mail_ok { + ($ok, $tmsg) = $t->Comment( + MIMEObj => HTML::Mason::Commands::MakeMIMEEntity( + Body => '<p>Comment test, <em>please!</em></p>', + Type => 'text/html', + ), + ); +} { from => qr/RT System/, + bcc => 'root@localhost', + subject => qr/\Q[example.com #1] [Comment] The internet is broken\E/, + body => parts_regex( + 'This is a comment about (\[\d+\])?ticket.1(\s*[(<]http://localhost:\d+/Ticket/Display\.html\?id=1[)>])?\..+?'. + 'It is not sent to the Requestor\(s\):.+?'. + 'Comment test, _?please!_?', + + '<p>This is a comment about <a href="http://localhost:\d+/Ticket/Display\.html\?id=1">ticket 1</a>\. '. + 'It is not sent to the Requestor\(s\):</p>.+?'. + '<p>Comment test, <em>please!</em></p>', + ), + 'Content-Type' => qr{multipart}, +}; + + +diag "Resolved in HTML templates"; +mail_ok { + ($ok, $tmsg) = $t->SetStatus('resolved'); +} { from => qr/RT System/, + to => 'enduser@example.com', + subject => qr/\Q[example.com #1] Resolved: The internet is broken\E/, + body => parts_regex( + 'According to our records, your request has been resolved\.', + '<p>According to our records, your request has been resolved\.', + ), + 'Content-Type' => qr{multipart}, +}; + + +diag "Status changes in HTML"; +my $scrip = RT::Scrip->new(RT->SystemUser); +my ($sval, $smsg) =$scrip->Create( + ScripCondition => 'On Status Change', + ScripAction => 'Notify Requestors', + Template => 'Status Change in HTML', + Queue => $q->Id, + Description => 'Tell requestors about status changes' +); +ok ($sval, $smsg); +ok ($scrip->Id, "Created the scrip"); +ok ($scrip->TemplateObj->Id, "Created the scrip template"); +ok ($scrip->ConditionObj->Id, "Created the scrip condition"); +ok ($scrip->ActionObj->Id, "Created the scrip action"); + +mail_ok { + ($ok, $tmsg) = $t->SetStatus('stalled'); +} { from => qr/RT System/, + to => 'enduser@example.com', + subject => qr/\Q[example.com #1] Status Changed to: stalled\E/, + body => parts_regex( + 'http://localhost:\d+/Ticket/Display\.html\?id=1.+?', + '<a href="(http://localhost:\d+/Ticket/Display\.html\?id=1)">\1</a>' + ), + 'Content-Type' => qr{multipart}, +}; + +done_testing; + +sub parts_regex { + my ($text, $html) = @_; + + my $pattern = 'Content-Type: text/plain.+?' . $text . '.+?' . + 'Content-Type: text/html.+?' . $html; + + return qr/$pattern/s; +} + diff --git a/rt/t/mail/mime_decoding.t b/rt/t/mail/mime_decoding.t index fbf884932..1126f1f84 100644 --- a/rt/t/mail/mime_decoding.t +++ b/rt/t/mail/mime_decoding.t @@ -1,14 +1,23 @@ use strict; use warnings; -use RT::Test nodb => 1, tests => 14; +use RT::Test nodb => 1, tests => undef; +use Test::LongString; +use Test::Warn; use_ok('RT::I18N'); diag q{'=' char in a leading part before an encoded part}; { my $str = 'key="plain"; key="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="'; + warnings_like { + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + 'key="plain"; key="мой_файл.bin"', + "right decoding" + ); + } [qr/DecodeMIMEWordsTo.*?called without field name/i]; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), + RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'), 'key="plain"; key="мой_файл.bin"', "right decoding" ); @@ -17,8 +26,15 @@ diag q{'=' char in a leading part before an encoded part}; 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==?="'; + warnings_like { + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + 'attachment; filename="мой_файл.bin"', + "right decoding" + ); + } [qr/DecodeMIMEWordsTo.*?called without field name/i]; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), + RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'), 'attachment; filename="мой_файл.bin"', "right decoding" ); @@ -27,8 +43,15 @@ 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}; { my $str = 'attachment; filename="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="; some_prop="value"'; + warnings_like { + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + 'attachment; filename="мой_файл.bin"; some_prop="value"', + "right decoding" + ); + } [qr/DecodeMIMEWordsTo.*?called without field name/i]; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), + RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'), 'attachment; filename="мой_файл.bin"; some_prop="value"', "right decoding" ); @@ -36,12 +59,9 @@ diag q{'=' char in a trailing part after an encoded part}; diag q{adding quotes around mime words containing specials when word is already quoted}; { - my $str = <<"END"; -Content-Disposition: attachment; filename="=?iso-8859-1?Q?foobar,_?= - =?iso-8859-1?Q?barfoo.docx?=" -END - my $decoded = 'Content-Disposition: attachment; filename="foobar, barfoo.docx"'; - is( RT::I18N::DecodeMIMEWordsToUTF8($str), $decoded, "No added quotes" ); + my $str = 'attachment; filename="=?iso-8859-1?Q?foobar,_?=' . "\n" . '=?iso-8859-1?Q?barfoo.docx?="'; + my $decoded = 'attachment; filename="foobar, barfoo.docx"'; + is( RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'), $decoded, "No added quotes" ); } diag q{regression test for #5248 from rt3.fsck.com}; @@ -49,7 +69,7 @@ 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?=}; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Subject'), qq{Subject: Re: [XXXXXX#269] [Comment] Frage zu XXXXXX--xxxxxx / Xxxxxüxxxxxxxxxx}, "right decoding" ); @@ -58,9 +78,16 @@ diag q{regression test for #5248 from rt3.fsck.com}; diag q{newline and encoded file name}; { my $str = qq{application/vnd.ms-powerpoint;\n\tname="=?ISO-8859-1?Q?Main_presentation.ppt?="}; + warnings_like { + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + qq{application/vnd.ms-powerpoint;\tname="Main presentation.ppt"}, + "right decoding" + ); + } [qr/DecodeMIMEWordsTo.*?called without field name/i]; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), - qq{application/vnd.ms-powerpoint;\tname="Main presentation.ppt"}, + RT::I18N::DecodeMIMEWordsToUTF8($str,'content-type'), + qq{application/vnd.ms-powerpoint; name="Main presentation.ppt"}, "right decoding" ); } @@ -97,54 +124,116 @@ inline; diag q{canonicalize mime word encodings like gb2312}; { my $str = qq{Subject: =?gb2312?B?1NrKwL3nuPe12Lmy09CzrN9eX1NpbXBsaWZpZWRfQ05fR0IyMzEyYQ==?= - =?gb2312?B?dHRhY2hlbWVudCB0ZXN0IGluIENOIHNpbXBsaWZpZWQ=?=}; +\t=?gb2312?B?dHRhY2hlbWVudCB0ZXN0IGluIENOIHNpbXBsaWZpZWQ=?=}; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), + RT::I18N::DecodeMIMEWordsToUTF8($str, "Subject"), qq{Subject: 在世界各地共有超過_Simplified_CN_GB2312attachement test in CN simplified}, "right decoding" ); } - diag q{Whitespace between encoded words should be removed}; { - my $str = "=?utf-8?Q?=E3=82=AD?= =?utf-8?Q?=E3=83=A3?="; - is( - RT::I18N::DecodeMIMEWordsToUTF8($str), - "キャ", - "whitespace between encoded words is removed", - ); - - $str = "=?utf-8?Q?=E3=82=AD?= \n =?utf-8?Q?=E3=83=A3?="; - is( - RT::I18N::DecodeMIMEWordsToUTF8($str), - "キャ", - "newlines between encoded words also removed", - ); + warnings_like { + my $str = "=?utf-8?Q?=E3=82=AD?= =?utf-8?Q?=E3=83=A3?="; + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + "キャ", + "whitespace between encoded words is removed", + ); + + $str = "=?utf-8?Q?=E3=82=AD?= \n =?utf-8?Q?=E3=83=A3?="; + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + "キャ", + "newlines between encoded words also removed", + ); + } [(qr/DecodeMIMEWordsTo.*?called without field name/i) x 2]; } diag q{Multiple octets split across QP hunks are correctly reassembled}; { - # This passes even without explicit code to handle it because utf8 - # is perl's internal string encoding. - my $str = "=?utf-8?Q?=E3?= =?utf-8?Q?=82?= =?utf-8?Q?=AD?="; - is( - RT::I18N::DecodeMIMEWordsToUTF8($str), - "キ", - "UTF8 character split in three is successfully reassembled", - ); - - # Non-utf8 encodings thus also must be checked - $str = <<EOT; chomp $str; + warnings_like { + # This passes even without explicit code to handle it because utf8 + # is perl's internal string encoding. + my $str = "=?utf-8?Q?=E3?= =?utf-8?Q?=82?= =?utf-8?Q?=AD?="; + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + "キ", + "UTF8 character split in three is successfully reassembled", + ); + + # Non-utf8 encodings thus also must be checked + $str = <<EOT; chomp $str; =?gb2312?q?Chinese(gb2312)=20=20=C3=C0=B9=FA=C7=B0=CB=BE=B7=A8=B2=BF=B3?= =?gb2312?q?=A4=C3=E6=BC=FB=C8=F8=B4=EF=C4=B7=BA=F3=B3=C6=C6=E4=D7=B4=CC=AC?= =?gb2312?q?=BA=DC=BA=C3=20=20Chinese=20(gb2312)?= EOT - is( - RT::I18N::DecodeMIMEWordsToUTF8($str), - "Chinese(gb2312) 美国前司法部长面见萨达姆后称其状态很好 Chinese (gb2312)", - "gb2312 character is successfully reassembled", + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + "Chinese(gb2312) 美国前司法部长面见萨达姆后称其状态很好 Chinese (gb2312)", + "gb2312 character is successfully reassembled", + ); + } [(qr/DecodeMIMEWordsTo.*?called without field name/i) x 2]; +} + +diag "multiple mime words containing special chars already in quotes"; +{ + my $str = q{attachment; filename="=?ISO-2022-JP?B?Mi4bJEIlSyVlITwlOSVqJWohPCU5GyhC?= =?ISO-2022-JP?B?LnBkZg==?="}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="2.ニュースリリース.pdf"}, + "base64" ); + $str = q{attachment; filename="=?UTF-8?Q?2=2E=E3=83=8B=E3=83=A5=E3=83=BC=E3=82=B9=E3=83=AA=E3=83=AA?= =?UTF-8?Q?=E3=83=BC=E3=82=B9=2Epdf?="}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="2.ニュースリリース.pdf"}, + "QP" + ); } + +diag "mime word combined with text in quoted filename property"; +{ + my $str = q{attachment; filename="=?UTF-8?B?Q2VjaSBuJ2VzdCBwYXMgdW5l?= pipe.pdf"}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="Ceci n'est pas une pipe.pdf"}, + "base64" + ); + + $str = q{attachment; filename="=?UTF-8?B?Q2VjaSBuJ2VzdCBwYXMgdW5lLi4u?= pipe.pdf"}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="Ceci n'est pas une... pipe.pdf"}, + "base64" + ); + + $str = q{attachment; filename="=?UTF-8?Q?Ceci n'est pas une?= pipe.pdf"}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="Ceci n'est pas une pipe.pdf"}, + "QP" + ); + + $str = q{attachment; filename="=?UTF-8?Q?Ceci n'est pas une...?= pipe.pdf"}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="Ceci n'est pas une... pipe.pdf"}, + "QP" + ); +} + +diag "quotes in filename"; +{ + my $str = q{attachment; filename="=?UTF-8?B?YSAicXVvdGVkIiBmaWxl?="}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="a \"quoted\" file"}, + "quoted filename correctly decoded" + ); +} + +done_testing; diff --git a/rt/t/mail/multipart.t b/rt/t/mail/multipart.t index 1c1106421..644305ebd 100644 --- a/rt/t/mail/multipart.t +++ b/rt/t/mail/multipart.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test tests => 4; +use RT::Test tests => 4, config => q{Set($CorrespondAddress, 'rt@example.com');}; use RT::Test::Email; my $queue = RT::Test->load_or_create_queue( Name => 'General' ); @@ -37,4 +37,4 @@ is(@msgs,2,"sent 2 emails"); diag("We're skipping any testing of the autoreply"); my $entity = parse_mail($msgs[1]); -is($entity->parts, 0, "only one entity"); +is($entity->parts, 2, "only two parts"); diff --git a/rt/t/mail/one-time-recipients.t b/rt/t/mail/one-time-recipients.t index a9881cded..1bc172d71 100644 --- a/rt/t/mail/one-time-recipients.t +++ b/rt/t/mail/one-time-recipients.t @@ -1,7 +1,9 @@ use strict; use warnings; -use RT::Test tests => 38; +use RT::Test tests => undef; +use RT::Test::Email; +use Test::Warn; my $queue = RT::Test->load_or_create_queue( Name => 'General', @@ -17,191 +19,149 @@ my $user = RT::Test->load_or_create_user( ok $user && $user->id, 'loaded or created user'; diag "Reply to ticket with actor as one time cc"; -{ +warnings_are { 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' - } + mail_ok { + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'root@localhost', + ); + ok $status, "created ticket"; + } { To => 'root@localhost' }; 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' - } + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + } { To => 'root@localhost' }; 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' - } -} + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'root@localhost', + ); + ok $status, "replied to a ticket"; + } { Cc => 'root@localhost' }; +} []; diag "Reply to ticket with requestor squelched"; -{ +warnings_are { 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' - } + mail_ok { + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'test@localhost', + ); + ok $status, "created ticket"; + } { To => 'test@localhost' }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + } { To => 'test@localhost' }; $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' - } -} + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'test@localhost', + ); + ok $status, "replied to a ticket"; + } { Cc => 'test@localhost' }; +} []; diag "Reply to ticket with requestor squelched"; -{ +warnings_are { 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' - } -} + mail_ok { + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'test@localhost', + ); + ok $status, "created ticket"; + } { To => 'test@localhost' }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + } { To => 'test@localhost' }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + SquelchMailTo => ['test@localhost'], + ); + ok $status, "replied to a ticket"; + }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + } { To => 'test@localhost' }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'test@localhost', + SquelchMailTo => ['test@localhost'], + ); + ok $status, "replied to a ticket"; + } { Cc => 'test@localhost' }; +} []; + +diag "Requestor is an RT address"; +warnings_are { + my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) ); + mail_ok { + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'rt-address@example.com', + ); + ok $status, "created ticket"; + } { To => 'rt-address@example.com' }; + + RT->Config->Set( RTAddressRegexp => qr/^rt-address\@example\.com$/i ); + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'rt-address@example.com', + ); + ok $status, "replied to a ticket"; + }; +} []; + +done_testing; diff --git a/rt/t/mail/outlook.t b/rt/t/mail/outlook.t index 752a91fae..8f3b71bc8 100644 --- a/rt/t/mail/outlook.t +++ b/rt/t/mail/outlook.t @@ -15,11 +15,11 @@ X-Mailer: $mailer To: rt\@@{[RT->Config->Get('rtname')]} Subject: outlook basic test Content-Type: multipart/alternative; - boundary="----=_NextPart_000_0004_01CB045C.A5A075D0" +\tboundary="----=_NextPart_000_0004_01CB045C.A5A075D0" ------=_NextPart_000_0004_01CB045C.A5A075D0 Content-Type: text/plain; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: 7bit here is the content @@ -33,7 +33,7 @@ another line ------=_NextPart_000_0004_01CB045C.A5A075D0 Content-Type: text/html; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: quoted-printable <html>this is fake</html> @@ -61,16 +61,16 @@ X-Mailer: $mailer To: rt\@@{[RT->Config->Get('rtname')]} Subject: outlook basic test Content-Type: multipart/mixed; - boundary="----=_NextPart_000_000F_01CB045E.5222CB40" +\tboundary="----=_NextPart_000_000F_01CB045E.5222CB40" ------=_NextPart_000_000F_01CB045E.5222CB40 Content-Type: multipart/alternative; - boundary="----=_NextPart_001_0010_01CB045E.5222CB40" +\tboundary="----=_NextPart_001_0010_01CB045E.5222CB40" ------=_NextPart_001_0010_01CB045E.5222CB40 Content-Type: text/plain; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: 7bit foo @@ -84,7 +84,7 @@ baz ------=_NextPart_001_0010_01CB045E.5222CB40 Content-Type: text/html; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: quoted-printable <html>this is fake</html> @@ -93,10 +93,10 @@ Content-Transfer-Encoding: quoted-printable ------=_NextPart_000_000F_01CB045E.5222CB40 Content-Type: text/plain; - name="att.txt" +\tname="att.txt" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; - filename="att.txt" +\tfilename="att.txt" this is the attachment! :)=0A= @@ -210,8 +210,8 @@ John Smith Some Company email\@someco.com EOF - test_email( $text, $content, - ' with base64, no X-Mailer, \n\n are replaced' ); + test_email( $text, $content, + ' with base64, no X-Mailer, \n\n are replaced' ); } @@ -223,11 +223,11 @@ X-Mailer: Mutt To: rt\@@{[RT->Config->Get('rtname')]} Subject: outlook basic test Content-Type: multipart/alternative; - boundary="----=_NextPart_000_0004_01CB045C.A5A075D0" +\tboundary="----=_NextPart_000_0004_01CB045C.A5A075D0" ------=_NextPart_000_0004_01CB045C.A5A075D0 Content-Type: text/plain; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: 7bit foo @@ -241,7 +241,7 @@ baz ------=_NextPart_000_0004_01CB045C.A5A075D0 Content-Type: text/html; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: quoted-printable <html>this is fake</html> @@ -382,8 +382,8 @@ This isthesig EOF - test_email( $text, $content, - 'Another sample multipart message with Exchange headers' ); + test_email( $text, $content, + 'Another sample multipart message with Exchange headers' ); } sub test_email { diff --git a/rt/t/mail/sendmail-plaintext.t b/rt/t/mail/sendmail-plaintext.t new file mode 100644 index 000000000..b9eb71951 --- /dev/null +++ b/rt/t/mail/sendmail-plaintext.t @@ -0,0 +1,150 @@ +use strict; +use warnings; + +use RT::Test tests => undef, text_templates => 1; + +use File::Spec (); +use Email::Abstract; + +# We're not testing acls here. +my $everyone = RT::Group->new(RT->SystemUser); +$everyone->LoadSystemInternalGroup('Everyone'); +$everyone->PrincipalObj->GrantRight( Right =>'SuperUser' ); + +# some utils +sub first_txn { return $_[0]->Transactions->First } +sub first_attach { return first_txn($_[0])->Attachments->First } +sub count_attachs { return first_txn($_[0])->Attachments->Count } + +sub mail_in_ticket { + my ($filename) = @_; + my $path = RT::Test::get_relocatable_file($filename, + (File::Spec->updir(), 'data', 'emails')); + my $content = RT::Test->file_content($path); + + RT::Test->clean_caught_mails; + my ($status, $id) = RT::Test->send_via_mailgate( $content ); + ok( !$status, "Fed $filename into mailgate"); + + my $ticket = RT::Ticket->new(RT->SystemUser); + $ticket->Load($id); + ok( $ticket->Id, "Successfully created ticket ".$ticket->Id); + + my @mail = map {Email::Abstract->new($_)->cast('MIME::Entity')} + RT::Test->fetch_caught_mails; + return ($ticket, @mail); +} + +{ + my ($ticket) = mail_in_ticket('multipart-report'); + like( first_txn($ticket)->Content , qr/The original message was received/, "It's the bounce"); +} + +for my $encoding ('ISO-8859-1', 'UTF-8') { + RT->Config->Set( EmailOutputEncoding => $encoding ); + + my ($ticket, @mail) = mail_in_ticket('new-ticket-from-iso-8859-1'); + like (first_txn($ticket)->Content , qr/H\x{e5}vard/, "It's signed by havard. yay"); + + is(@mail, 1); + like( $mail[0]->head->get('Content-Type') , qr/$encoding/, + "Its content type is $encoding" ); + my $message_as_string = $mail[0]->bodyhandle->as_string(); + $message_as_string = Encode::decode($encoding, $message_as_string); + like( $message_as_string , qr/H\x{e5}vard/, + "The message's content contains havard's name in $encoding"); +} + +{ + my ($ticket) = mail_in_ticket('multipart-alternative-with-umlaut'); + like( first_txn($ticket)->Content, qr/causes Error/, + "We recorded the content as containing 'causes error'"); + is( count_attachs($ticket), 3, + "Has three attachments, presumably a text-plain, a text-html and a multipart alternative"); +} + +{ + my ($ticket, @mail) = mail_in_ticket('text-html-with-umlaut'); + like( first_attach($ticket)->Content, qr/causes Error/, + "We recorded the content as containing 'causes error'"); + like( first_attach($ticket)->ContentType , qr/text\/html/, + "We recorded the content as text/html"); + is (count_attachs($ticket), 1, + "Has one attachment, just a text-html"); + + is(@mail, 1); + is( $mail[0]->parts, 0, "generated correspondence mime entity does not have parts"); + is( $mail[0]->head->mime_type , "text/plain", "The mime type is a plain"); +} + +{ + my @InputEncodings = RT->Config->Get('EmailInputEncodings'); + RT->Config->Set( EmailInputEncodings => 'koi8-r', @InputEncodings ); + RT->Config->Set( EmailOutputEncoding => 'koi8-r' ); + + my ($ticket, @mail) = mail_in_ticket('russian-subject-no-content-type'); + like( first_attach($ticket)->ContentType, qr/text\/plain/, + "We recorded the content type right"); + is( count_attachs($ticket), 1, + "Has one attachment, presumably a text-plain"); + is( $ticket->Subject, Encode::decode("UTF-8","тест тест"), + "Recorded the subject right"); + + is(@mail, 1); + is( $mail[0]->head->mime_type , "text/plain", "The only part is text/plain "); + like( $mail[0]->head->get("subject"), qr/\Q=?KOI8-R?B?W2V4YW1wbGUuY29tICM2XSBBdXRvUmVwbHk6INTF09Qg1MXT1A==?=\E/, + "The subject is encoded correctly"); + + RT->Config->Set(EmailInputEncodings => @InputEncodings ); + RT->Config->Set(EmailOutputEncoding => 'utf-8'); +} + +{ + my ($ticket, @mail) = mail_in_ticket('nested-rfc-822'); + is( $ticket->Subject, "[Jonas Liljegren] Re: [Para] Niv\x{e5}er?"); + like( first_attach($ticket)->ContentType, qr/multipart\/mixed/, + "We recorded the content type right"); + is( count_attachs($ticket), 5, + "Has five attachments, presumably a text-plain and a message RFC 822 and another plain"); + + is(@mail, 1); + is( $mail[0]->head->mime_type , "text/plain", "The outgoing mail is plain text"); + + my $encoded_subject = $mail[0]->head->get("Subject"); + chomp $encoded_subject; + my $subject = Encode::decode('MIME-Header',$encoded_subject); + like($subject, qr/Niv\x{e5}er/, "The subject matches the word - $subject"); +} + +{ + my ($ticket) = mail_in_ticket('notes-uuencoded'); + like( first_txn($ticket)->Content, qr/from Lotus Notes/, + "We recorded the content right"); + is( count_attachs($ticket), 3, "Has three attachments"); +} + +{ + my ($ticket) = mail_in_ticket('crashes-file-based-parser'); + like( first_txn($ticket)->Content, qr/FYI/, "We recorded the content right"); + is( count_attachs($ticket), 5, "Has five attachments"); +} + +{ + my ($ticket) = mail_in_ticket('rt-send-cc'); + my $cc = first_attach($ticket)->GetHeader('RT-Send-Cc'); + like ($cc, qr/test$_/, "Found test $_") for 1..5; +} + +{ + diag "Regression test for #5248 from rt3.fsck.com"; + my ($ticket) = mail_in_ticket('subject-with-folding-ws'); + is ($ticket->Subject, 'test', 'correct subject'); +} + +{ + diag "Regression test for #5248 from rt3.fsck.com"; + my ($ticket) = mail_in_ticket('very-long-subject'); + is ($ticket->Subject, '0123456789'x20, 'correct subject'); +} + +done_testing; diff --git a/rt/t/mail/sendmail.t b/rt/t/mail/sendmail.t index 56202ad5d..4ef320611 100644 --- a/rt/t/mail/sendmail.t +++ b/rt/t/mail/sendmail.t @@ -24,7 +24,7 @@ sub mail_in_ticket { RT::Test->clean_caught_mails; my ($status, $id) = RT::Test->send_via_mailgate( $content ); - ok( $status, "Fed $filename into mailgate"); + ok( !$status, "Fed $filename into mailgate"); my $ticket = RT::Ticket->new(RT->SystemUser); $ticket->Load($id); @@ -47,12 +47,30 @@ for my $encoding ('ISO-8859-1', 'UTF-8') { like (first_txn($ticket)->Content , qr/H\x{e5}vard/, "It's signed by havard. yay"); is(@mail, 1); - like( $mail[0]->head->get('Content-Type') , qr/$encoding/, - "Its content type is $encoding" ); - my $message_as_string = $mail[0]->bodyhandle->as_string(); + like( $mail[0]->head->get('Content-Type'), qr/multipart\/alternative/, + "Its content type is multipart/alternative" ); + + # The text/html part is guaranteed to not have had non-latin-1 + # characters introduced by the HTML-to-text conversion, so it is + # guaranteed to be able to be represented in latin-1 + like( $mail[0]->parts(1)->head->get('Content-Type'), qr/text\/html.+?$encoding/, + "Second part's content type is text/html $encoding" ); + my $message_as_string = $mail[0]->parts(1)->bodyhandle->as_string(); $message_as_string = Encode::decode($encoding, $message_as_string); like( $message_as_string , qr/H\x{e5}vard/, "The message's content contains havard's name in $encoding"); + + # The text/plain part may have utf-8 characters in it. Accept either encoding. + like( $mail[0]->parts(0)->head->get('Content-Type'), qr/text\/plain.+?(ISO-8859-1|UTF-8)/i, + "First part's content type is text/plain (ISO-8859-1 or UTF-8)" ); + + # Make sure it checks out in whatever encoding it ended up in + $mail[0]->parts(0)->head->get('Content-Type') =~ /text\/plain.+?(ISO-8859-1|UTF-8)/i; + my $found = $1 || $encoding; + $message_as_string = $mail[0]->parts(0)->bodyhandle->as_string(); + $message_as_string = Encode::decode($found, $message_as_string); + like( $message_as_string , qr/H\x{e5}vard/, + "The message's content contains havard's name in $encoding"); } { @@ -73,8 +91,9 @@ for my $encoding ('ISO-8859-1', 'UTF-8') { "Has one attachment, just a text-html"); is(@mail, 1); - is( $mail[0]->parts, 0, "generated correspondence mime entity does not have parts"); - is( $mail[0]->head->mime_type , "text/plain", "The mime type is a plain"); + is( $mail[0]->parts, 2, "generated correspondence mime entity has parts"); + is( $mail[0]->parts(0)->head->mime_type , "text/plain", "The first part mime type is a plain"); + is( $mail[0]->parts(1)->head->mime_type , "text/html", "The second part mime type is an html"); } { @@ -91,7 +110,10 @@ for my $encoding ('ISO-8859-1', 'UTF-8') { "Recorded the subject right"); is(@mail, 1); - is( $mail[0]->head->mime_type , "text/plain", "The only part is text/plain "); + is( $mail[0]->head->mime_type , "multipart/alternative", "The top part is multipart/alternative"); + is( $mail[0]->parts, 2, "generated correspondnece mime entity has parts"); + is( $mail[0]->parts(0)->head->mime_type , "text/plain", "The first part is a plain"); + is( $mail[0]->parts(1)->head->mime_type , "text/html", "The second part is an html"); like( $mail[0]->head->get("subject"), qr/\Q=?KOI8-R?B?W2V4YW1wbGUuY29tICM2XSBBdXRvUmVwbHk6INTF09Qg1MXT1A==?=\E/, "The subject is encoded correctly"); @@ -108,7 +130,10 @@ for my $encoding ('ISO-8859-1', 'UTF-8') { "Has five attachments, presumably a text-plain and a message RFC 822 and another plain"); is(@mail, 1); - is( $mail[0]->head->mime_type , "text/plain", "The outgoing mail is plain text"); + is( $mail[0]->head->mime_type , "multipart/alternative", "The top part is multipart/alternative"); + is( $mail[0]->parts, 2, "generated correspondnece mime entity has parts"); + is( $mail[0]->parts(0)->head->mime_type , "text/plain", "The first part is a plain"); + is( $mail[0]->parts(1)->head->mime_type , "text/html", "The second part is an html"); my $encoded_subject = $mail[0]->head->get("Subject"); chomp $encoded_subject; diff --git a/rt/t/mail/smime/incoming.t b/rt/t/mail/smime/incoming.t new file mode 100644 index 000000000..918844a88 --- /dev/null +++ b/rt/t/mail/smime/incoming.t @@ -0,0 +1,202 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef, actual_server => 1; +my $test = 'RT::Test::SMIME'; + +use IPC::Run3 'run3'; +use String::ShellQuote 'shell_quote'; +use RT::Tickets; +use Test::Warn; + +my ($url, $m) = RT::Test->started_ok; +ok $m->login, "logged in"; + +# configure key for General queue +RT::Test::SMIME->import_key('sender@example.com'); +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'sender@example.com', + CommentAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user = RT::Test->load_or_create_user( + Name => 'root@example.com', + EmailAddress => 'root@example.com', +); +RT::Test::SMIME->import_key('root@example.com.crt', $user); +RT::Test->add_rights( Principal => $user, Right => 'SuperUser', Object => RT->System ); + +my $mail = RT::Test->open_mailgate_ok($url); +print $mail <<EOF; +From: root\@localhost +To: rt\@$RT::rtname +Subject: This is a test of new ticket creation as root + +Blah! +Foob! +EOF +RT::Test->close_mailgate_ok($mail); + +{ + my $tick = RT::Test->last_ticket; + is( $tick->Subject, + 'This is a test of new ticket creation as root', + "Created the ticket" + ); + my $txn = $tick->Transactions->First; + like( + $txn->Attachments->First->Headers, + qr/^X-RT-Incoming-Encryption: Not encrypted/m, + 'recorded incoming mail that is not encrypted' + ); + like( $txn->Attachments->First->Content, qr'Blah'); +} + +{ + # test for encrypted mail + my $buf = ''; + run3( + shell_quote( + qw(openssl smime -encrypt -des3), + -from => 'root@example.com', + -to => 'sender@example.com', + -subject => "Encrypted message for queue", + $test->key_path('sender@example.com.crt'), + ), + \"Subject: test\n\norzzzzzz", + \$buf, + \*STDERR + ); + + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + is ($status >> 8, 0, "The mail gateway exited normally"); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + is( $tick->Subject, 'Encrypted message for queue', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Success', + 'recorded incoming mail that is encrypted' + ); + is( $msg->GetHeader('X-RT-Privacy'), + 'SMIME', + 'recorded incoming mail that is encrypted' + ); + like( $attach->Content, qr'orz'); + + is( $orig->GetHeader('Content-Type'), 'application/x-rt-original-message'); +} + +{ + my $buf = ''; + + run3( + join( + ' ', + shell_quote( + RT->Config->Get('SMIME')->{'OpenSSL'}, + qw( smime -sign -nodetach -passin pass:123456), + -signer => $test->key_path('root@example.com.crt'), + -inkey => $test->key_path('root@example.com.key'), + ), + '|', + shell_quote( + qw(openssl smime -encrypt -des3), + -from => 'root@example.com', + -to => 'sender@example.com', + -subject => "Encrypted and signed message for queue", + $test->key_path('sender@example.com.crt'), + )), + \"Subject: test\n\norzzzzzz", + \$buf, + \*STDERR + ); + + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + ok( $tick->Id, "found ticket " . $tick->Id ); + is( $tick->Subject, 'Encrypted and signed message for queue', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Success', + 'recorded incoming mail that is encrypted' + ); + like( $attach->Content, qr'orzzzz'); +} + +{ + my $buf = ''; + + run3( + shell_quote( + RT->Config->Get('SMIME')->{'OpenSSL'}, + qw( smime -sign -passin pass:123456), + -signer => $test->key_path('root@example.com.crt'), + -inkey => $test->key_path('root@example.com.key'), + ), + \"Content-type: text/plain\n\nThis is the body", + \$buf, + \*STDERR + ); + $buf = "Subject: Signed email\n" + . "From: root\@example.com\n" + . $buf; + + { + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + ok( $tick->Id, "found ticket " . $tick->Id ); + is( $tick->Subject, 'Signed email', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + is( $msg->GetHeader('X-RT-Incoming-Signature'), + '"Enoch Root" <root@example.com>', + "Message was signed" + ); + like( $attach->Content, qr/This is the body/ ); + } + + # Make the signature not match + $buf =~ s/This is the body/This is not the body/; + + warning_like { + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + ok( $tick->Id, "found ticket " . $tick->Id ); + is( $tick->Subject, 'Signed email', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + isnt( $msg->GetHeader('X-RT-Incoming-Signature'), + '"Enoch Root" <root@example.com>', + "Message was not marked signed" + ); + like( $attach->Content, qr/This is not the body/ ); + } qr/Failure during SMIME verify: The signature did not verify/; + +} + +undef $m; +done_testing; diff --git a/rt/t/mail/smime/other-signed.t b/rt/t/mail/smime/other-signed.t new file mode 100644 index 000000000..4e97e711f --- /dev/null +++ b/rt/t/mail/smime/other-signed.t @@ -0,0 +1,135 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef; +my $test = 'RT::Test::SMIME'; + +use IPC::Run3 'run3'; +use String::ShellQuote 'shell_quote'; +use RT::Tickets; +use Test::Warn; + +# configure key for General queue +RT::Test::SMIME->import_key('sender@example.com'); +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'sender@example.com', + CommentAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user = RT::Test->load_or_create_user( + Name => 'root@example.com', + EmailAddress => 'root@example.com', +); +RT::Test::SMIME->import_key('root@example.com.crt', $user); +RT::Test->add_rights( Principal => $user, Right => 'SuperUser', Object => RT->System ); + +my $buf = ''; + +run3( + shell_quote( + RT->Config->Get('SMIME')->{'OpenSSL'}, + qw( smime -sign -passin pass:123456), + -signer => $test->key_path('root@example.com.crt'), + -inkey => $test->key_path('root@example.com.key'), + ), + \"Content-type: text/plain\n\nThis is the body", + \$buf, + \*STDERR +); +$buf = "Subject: Signed email\n" + . "From: root\@example.com\n" + . $buf; + +my $send_mail = sub { + my %args = ( CAPath => undef, AcceptUntrustedCAs => undef, @_ ); + + RT->Config->Get('SMIME')->{$_} = $args{$_} for keys %args; + + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + ok( $tick->Id, "found ticket " . $tick->Id ); + is( $tick->Subject, 'Signed email', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + + ($status) = RT::Crypt->ParseStatus( + Protocol => 'SMIME', + Status => $msg->GetHeader('X-RT-SMIME-Status') + ); + + return ($msg, $status); +}; + +# Test with no CA path; should not be marked as signed +warning_like { + my ($msg, $status) = $send_mail->( CAPath => undef ); + is( $msg->GetHeader('X-RT-Incoming-Signature'), + undef, + "Message was not marked as signed" + ); + + is($status->{Operation}, "Verify", "Found the Verify operation"); + is($status->{Status}, "BAD", "Verify was a failure"); + is($status->{Trust}, "NONE", "Noted the no trust level"); + like($status->{Message}, qr/not trusted/, "Verify was a failure"); +} qr/Failure during SMIME verify: The signing CA was not trusted/; + +# Test with the correct CA path; marked as signed, trusted +{ + my ($msg, $status) = $send_mail->( CAPath => $test->key_path . "/demoCA/cacert.pem" ); + is( $msg->GetHeader('X-RT-Incoming-Signature'), + '"Enoch Root" <root@example.com>', "Message is signed" ); + + is($status->{Operation}, "Verify", "Found the Verify operation"); + is($status->{Status}, "DONE", "Verify was a success"); + is($status->{Trust}, "FULL", "Noted the full trust level"); +} + +# Test with the other CA +warning_like { + my ($msg, $status) = $send_mail->( CAPath => $test->key_path . "/otherCA/cacert.pem" ); + is( $msg->GetHeader('X-RT-Incoming-Signature'), + undef, + "Message was not marked as signed" + ); + + is($status->{Operation}, "Verify", "Found the Verify operation"); + is($status->{Status}, "BAD", "Verify was a failure"); + is($status->{Trust}, "NONE", "Noted the no trust level"); + like($status->{Message}, qr/not trusted/, "Verify was a failure"); +} qr/Failure during SMIME verify: The signing CA was not trusted/; + +# Other CA, but allow all CAs +{ + my ($msg, $status) = $send_mail->( CAPath => $test->key_path . "/otherCA/cacert.pem", AcceptUntrustedCAs => 1 ); + is( $msg->GetHeader('X-RT-Incoming-Signature'), + '"Enoch Root" <root@example.com>', + "Message was marked as signed" + ); + + is($status->{Operation}, "Verify", "Found the Verify operation"); + is($status->{Status}, "DONE", "Verify was a success"); + is($status->{Trust}, "NONE", "Noted the no trust level"); +} + +# No CA path, but allow all CAs +{ + my ($msg, $status) = $send_mail->( CAPath => undef, AcceptUntrustedCAs => 1 ); + is( $msg->GetHeader('X-RT-Incoming-Signature'), + '"Enoch Root" <root@example.com>', + "Message was marked as signed" + ); + + is($status->{Operation}, "Verify", "Found the Verify operation"); + is($status->{Status}, "DONE", "Verify was a success"); + is($status->{Trust}, "UNKNOWN", "Noted the no trust level"); +} + +done_testing; diff --git a/rt/t/mail/smime/outgoing.t b/rt/t/mail/smime/outgoing.t new file mode 100644 index 000000000..6f6b00d68 --- /dev/null +++ b/rt/t/mail/smime/outgoing.t @@ -0,0 +1,80 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef; +my $test = 'RT::Test::SMIME'; + +use IPC::Run3 'run3'; +use RT::Interface::Email; + +my ($url, $m) = RT::Test->started_ok; +ok $m->login, "logged in"; + +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'sender@example.com', + CommentAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +{ + my ($status, $msg) = $queue->SetEncrypt(1); + ok $status, "turn on encyption by default" + or diag "error: $msg"; +} + +my $user; +{ + $user = RT::User->new($RT::SystemUser); + ok($user->LoadByEmail('root@localhost'), "Loaded user 'root'"); + ok($user->Load('root'), "Loaded user 'root'"); + is($user->EmailAddress, 'root@localhost'); + + RT::Test::SMIME->import_key( 'root@example.com.crt' => $user ); +} + +RT::Test->clean_caught_mails; + +{ + my $mail = <<END; +From: root\@localhost +To: rt\@example.com +Subject: This is a test of new ticket creation as an unknown user + +Blah! +Foob! + +END + + my ($status, $id) = RT::Test->send_via_mailgate( + $mail, queue => $queue->Name, + ); + is $status >> 8, 0, "successfuly executed mailgate"; + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load( $id ); + ok ($ticket->id, "found ticket ". $ticket->id); +} + +{ + my @mails = RT::Test->fetch_caught_mails; + is scalar @mails, 1, "autoreply"; + + my ($buf, $err); + local $@; + ok(eval { + run3([ + qw(openssl smime -decrypt -passin pass:123456), + '-inkey', $test->key_path('root@example.com.key'), + '-recip', $test->key_path('root@example.com.crt') + ], \$mails[0], \$buf, \$err ) + }, 'can decrypt' + ); + diag $@ if $@; + diag $err if $err; + diag "Error code: $?" if $?; + like($buf, qr'This message has been automatically generated in response'); +} + +undef $m; +done_testing; diff --git a/rt/t/mail/smime/realmail.t b/rt/t/mail/smime/realmail.t new file mode 100644 index 000000000..be157aaee --- /dev/null +++ b/rt/t/mail/smime/realmail.t @@ -0,0 +1,125 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef; +use Digest::MD5 qw(md5_hex); + +my $test = 'RT::Test::SMIME'; +my $mails = $test->mail_set_path; + +RT->Config->Get('SMIME')->{AcceptUntrustedCAs} = 1; + +RT::Test::SMIME->import_key('root@example.com'); +RT::Test::SMIME->import_key('sender@example.com'); + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'we did log in'; +$m->get_ok( '/Admin/Queues/'); +$m->follow_link_ok( {text => 'General'} ); +$m->submit_form( form_number => 3, + fields => { CorrespondAddress => 'root@example.com' } ); + +diag "load Everyone group" if $ENV{'TEST_VERBOSE'}; +my $everyone; +{ + $everyone = RT::Group->new( $RT::SystemUser ); + $everyone->LoadSystemInternalGroup('Everyone'); + ok $everyone->id, "loaded 'everyone' group"; +} + +RT::Test->set_rights( + Principal => $everyone, + Right => ['CreateTicket'], +); + + +my $eid = 0; +for my $usage (qw/signed encrypted signed&encrypted/) { + for my $attachment (qw/plain text-attachment binary-attachment/) { + ++$eid; + diag "Email $eid: $usage, $attachment email" if $ENV{TEST_VERBOSE}; + eval { email_ok($eid, $usage, $attachment) }; + } +} + +undef $m; +done_testing; + +sub email_ok { + my ($eid, $usage, $attachment) = @_; + diag "email_ok $eid: $usage, $attachment" if $ENV{'TEST_VERBOSE'}; + + my ($file) = glob("$mails/$eid-*"); + my $mail = RT::Test->file_content($file); + + my ($status, $id) = RT::Test->send_via_mailgate($mail); + 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 ); + $tick->Load( $id ); + ok ($tick->id, "$eid: loaded ticket #$id"); + + is ($tick->Subject, + "Test Email ID:$eid", + "$eid: Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; + + is( $msg->GetHeader('X-RT-Privacy'), + 'SMIME', + "$eid: recorded incoming mail that is secured" + ); + + if ($usage =~ /encrypted/) { + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Success', + "$eid: recorded incoming mail that is encrypted" + ); + like( $attachments[0]->Content, qr/ID:$eid/, + "$eid: incoming mail did NOT have original body" + ); + } + else { + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Not encrypted', + "$eid: recorded incoming mail that is not encrypted" + ); + like( $msg->Content || $attachments[0]->Content, qr/ID:$eid/, + "$eid: got original content" + ); + } + + if ($usage =~ /signed/) { + is( $msg->GetHeader('X-RT-Incoming-Signature'), + '"sender" <sender@example.com>', + "$eid: recorded incoming mail that is signed" + ); + } + else { + is( $msg->GetHeader('X-RT-Incoming-Signature'), + undef, + "$eid: recorded incoming mail that is not signed" + ); + } + + if ($attachment =~ /attachment/) { + my ($a) = grep $_->Filename, @attachments; + ok ($a && $a->Id, "$eid: found attachment with filename"); + + my $acontent = $a->Content; + if ($attachment =~ /binary/) + { + is(md5_hex($acontent), '1e35f1aa90c98ca2bab85c26ae3e1ba7', "$eid: The binary attachment's md5sum matches"); + } + else + { + like($acontent, qr/zanzibar/, "$eid: The attachment isn't screwed up in the database."); + } + } + + return 0; +} + diff --git a/rt/t/mail/smime/reject_on_unencrypted.t b/rt/t/mail/smime/reject_on_unencrypted.t new file mode 100644 index 000000000..ab62d83fc --- /dev/null +++ b/rt/t/mail/smime/reject_on_unencrypted.t @@ -0,0 +1,137 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef, actual_server => 1, config => 'Set( %Crypt, RejectOnUnencrypted => 1 );'; +my $test = 'RT::Test::SMIME'; + +use IPC::Run3 'run3'; +use String::ShellQuote 'shell_quote'; +use RT::Tickets; + +my ($url, $m) = RT::Test->started_ok; +ok $m->login, "logged in"; + +# configure key for General queue +RT::Test::SMIME->import_key('sender@example.com'); +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'sender@example.com', + CommentAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user = RT::Test->load_or_create_user( + Name => 'root@example.com', + EmailAddress => 'root@example.com', +); +RT::Test::SMIME->import_key('root@example.com.crt', $user); +RT::Test->add_rights( Principal => $user, Right => 'SuperUser', Object => RT->System ); + +my $mail = RT::Test->open_mailgate_ok($url); +print $mail <<EOF; +From: root\@localhost +To: rt\@$RT::rtname +Subject: This is a test of new ticket creation as root + +Blah! +Foob! +EOF +RT::Test->close_mailgate_ok($mail); + +{ + ok(!RT::Test->last_ticket, 'A ticket was not created'); + my ($mail) = RT::Test->fetch_caught_mails; + like( + $mail, + qr/^Subject: RT requires that all incoming mail be encrypted/m, + 'rejected mail that is not encrypted' + ); + my ($warning) = $m->get_warnings; + like($warning, qr/rejected because the message is unencrypted/); +} + +{ + # test for encrypted mail + my $buf = ''; + run3( + shell_quote( + qw(openssl smime -encrypt -des3), + -from => 'root@example.com', + -to => 'sender@example.com', + -subject => "Encrypted message for queue", + $test->key_path('sender@example.com.crt' ), + ), + \"Subject: test\n\norzzzzzz", + \$buf, + \*STDERR + ); + + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + is ($status >> 8, 0, "The mail gateway exited normally"); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + is( $tick->Subject, 'Encrypted message for queue', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Success', + 'recorded incoming mail that is encrypted' + ); + is( $msg->GetHeader('X-RT-Privacy'), + 'SMIME', + 'recorded incoming mail that is encrypted' + ); + like( $attach->Content, qr'orz'); + + is( $orig->GetHeader('Content-Type'), 'application/x-rt-original-message'); +} + +{ + my $buf = ''; + + run3( + join( + ' ', + shell_quote( + RT->Config->Get('SMIME')->{'OpenSSL'}, + qw( smime -sign -nodetach -passin pass:123456), + -signer => $test->key_path('root@example.com.crt' ), + -inkey => $test->key_path('root@example.com.key' ), + ), + '|', + shell_quote( + qw(openssl smime -encrypt -des3), + -from => 'root@example.com', + -to => 'sender@example.com', + -subject => "Encrypted and signed message for queue", + $test->key_path('sender@example.com.crt' ), + )), + \"Subject: test\n\norzzzzzz", + \$buf, + \*STDERR + ); + + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + ok( $tick->Id, "found ticket " . $tick->Id ); + is( $tick->Subject, 'Encrypted and signed message for queue', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Success', + 'recorded incoming mail that is encrypted' + ); + like( $attach->Content, qr'orzzzz'); +} + +undef $m; +done_testing; diff --git a/rt/t/mail/specials-in-encodedwords.t b/rt/t/mail/specials-in-encodedwords.t index f9da9c6e9..36efcd5e7 100644 --- a/rt/t/mail/specials-in-encodedwords.t +++ b/rt/t/mail/specials-in-encodedwords.t @@ -14,7 +14,7 @@ diag "specials (, and ;) in MIME encoded-words aren't treated as specials"; From: root@localhost Subject: testing mime encoded specials Cc: a@example.com, =?utf8?q?d=40example.com=2ce=40example.com=3b?= - <b@example.com>; c@example.com + <b@example.com>, c@example.com Content-Type: text/plain; charset=utf8 here's some content diff --git a/rt/t/mail/wrong_mime_charset.t b/rt/t/mail/wrong_mime_charset.t index 6bbaca1bb..a3986d72f 100644 --- a/rt/t/mail/wrong_mime_charset.t +++ b/rt/t/mail/wrong_mime_charset.t @@ -1,6 +1,6 @@ use strict; use warnings; -use RT::Test nodb => 1, tests => 6; +use RT::Test nodb => 1, tests => undef; use_ok('RT::I18N'); my $test_string = Encode::decode("UTF-8", 'À'); @@ -20,10 +20,6 @@ local $SIG{__WARN__} = sub { 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. @@ -44,4 +40,5 @@ is( $subject, $test_string, 'subject is set to iso-8859-1' ); my $body = Encode::decode( 'iso-8859-1', $mime->stringify_body ); chomp $body; is( $body, $test_string, 'body is set to iso-8859-1' ); -} + +done_testing; diff --git a/rt/t/pod.t b/rt/t/pod.t index 697a30b44..1283cfe6c 100644 --- a/rt/t/pod.t +++ b/rt/t/pod.t @@ -2,6 +2,9 @@ use strict; use warnings; use Test::More; -eval "use Test::Pod 1.14"; -plan skip_all => "Test::Pod 1.14 required for testing POD" if $@; -all_pod_files_ok( all_pod_files("lib","docs","etc","bin","sbin")); +use Test::Pod; +all_pod_files_ok( + all_pod_files("lib","devel","docs","etc","bin","sbin"), + <docs/UPGRADING*>, + <devel/docs/UPGRADING*>, +); diff --git a/rt/t/security/CVE-2011-2083-clickable-xss.t b/rt/t/security/CVE-2011-2083-clickable-xss.t index 008c80378..753d8c770 100644 --- a/rt/t/security/CVE-2011-2083-clickable-xss.t +++ b/rt/t/security/CVE-2011-2083-clickable-xss.t @@ -25,7 +25,7 @@ for my $link ( map { ($_, ucfirst $_) } @links ) { Type => 'RefersTo', Target => $link, ); - } [qr/Could not determine a URI scheme/, qr/Couldn't resolve/]; + } [qr/Could not determine a URI scheme/]; ok !$ok, $msg; ok $m->login, "logged in"; @@ -40,7 +40,6 @@ for my $link ( map { ($_, ucfirst $_) } @links ) { }, 'submitted links page'); $m->content_contains("Couldn't resolve "); $m->next_warning_like(qr/Could not determine a URI scheme/, 'expected warning'); - $m->next_warning_like(qr/Couldn't resolve/, 'expected warning'); my $element = $m->find_link( url => $link ); ok !$element, "no <a> link"; diff --git a/rt/t/security/CVE-2011-2084-cf-values.t b/rt/t/security/CVE-2011-2084-cf-values.t index 1178b15af..21c8547f6 100644 --- a/rt/t/security/CVE-2011-2084-cf-values.t +++ b/rt/t/security/CVE-2011-2084-cf-values.t @@ -41,7 +41,7 @@ sub ac { $args{ContextType} = ref($obj) unless defined $args{ContextType}; } - $args{"Object---CustomField-$args{CF}-Values"} = ""; + $args{"Object-RT::Ticket--CustomField-$args{CF}-Values"} = ""; delete $args{CF}; delete $args{$_} for grep {not defined $args{$_}} keys %args; diff --git a/rt/t/security/CVE-2011-2084-modifyscrips-templates.t b/rt/t/security/CVE-2011-2084-modifyscrips-templates.t index f68706e52..0e59c528e 100644 --- a/rt/t/security/CVE-2011-2084-modifyscrips-templates.t +++ b/rt/t/security/CVE-2011-2084-modifyscrips-templates.t @@ -59,32 +59,39 @@ diag "ModifyScrips"; $scrip = RT::Scrip->new( $cu ); $scrip->Load( $scrip_id ); ok $scrip->id, "loaded scrip as test user"; - is $scrip->Queue, $qa->Id, 'queue is A'; + ok $scrip->IsAdded( $qa->Id ), 'queue is A'; ok +($scrip->SetName('Testing ModifyScrips')); - set_fails( Queue => $scrip => $qb ); - set_fails( Queue => $scrip => 0 ); - set_fails( Queue => $scrip => undef ); - set_fails( Queue => $scrip => '' ); + for my $value ($qb->id, 0, undef, '') { + my ($ok, $why) = $scrip->AddToObject( $value ); + my $disp = (defined($value) ? "'$value'" : "undef"); + ok( !$ok, "Correctly not added to $disp: $why" ); + } RT::Test->add_rights( Principal => $user, Right => 'ModifyScrips', Object => $qb ); - set_ok( Queue => $scrip => $qb ); - set_fails( Queue => $scrip => 0 ); - set_fails( Queue => $scrip => undef ); - set_fails( Queue => $scrip => '' ); + for my $value ($qb->id, 0, undef, '') { + my ($ok, $why) = $scrip->AddToObject( $value ); + my $disp = (defined($value) ? "'$value'" : "undef"); + if ($value) { + ok( $ok, "Correctly added to $disp: $why" ); + } else { + ok( !$ok, "Correctly not added to $disp: $why" ); + } + } RT::Test->add_rights( Principal => $user, Right => 'ModifyScrips' ); - set_ok( Queue => $scrip => 0 ); + my ($ok, $why) = $scrip->AddToObject( 0 ); + ok( $ok, "Correctly added globally: $why" ); - set_fails( Template => $scrip => 2 ); + set_fails( Template => $scrip => "Autoreply" ); RT::Test->add_rights( Principal => $user, Right => 'ShowTemplate' ); - set_ok( Template => $scrip => 2 ); - is $scrip->TemplateObj->Name, 'Autoreply', 'template name is right'; + set_ok( Template => $scrip => "Autoreply" ); + is $scrip->Template, 'Autoreply', 'template name is right'; } diag "ModifyTemplate"; @@ -115,12 +122,12 @@ diag "ModifyTemplate"; RT::Test->add_rights( Principal => $user, Right => 'ModifyTemplate', Object => $qb ); - set_ok( Queue => $template => $qb ); + set_fails( Queue => $template => $qb ); set_fails( Queue => $template => 0 ); RT::Test->add_rights( Principal => $user, Right => 'ModifyTemplate' ); - set_ok( Queue => $template => 0 ); + set_fails( Queue => $template => 0 ); } done_testing; diff --git a/rt/t/security/CVE-2011-5092-graph-links.t b/rt/t/security/CVE-2011-5092-graph-links.t index 5e98dd3b5..c6397f5a2 100644 --- a/rt/t/security/CVE-2011-5092-graph-links.t +++ b/rt/t/security/CVE-2011-5092-graph-links.t @@ -13,12 +13,12 @@ for my $arg (qw(LeadingLink ShowLinks)) { ); ok $ticket->id, 'created ticket'; - ok !$ticket->ToldObj->Unix, 'no Told'; + ok !$ticket->ToldObj->IsSet, 'no Told'; $m->get_ok("$base/Ticket/Graphs/index.html?$arg=SetTold;id=" . $ticket->id); $ticket->Load($ticket->id); # cache busting - ok !$ticket->ToldObj->Unix, 'still no Told'; + ok !$ticket->ToldObj->IsSet, 'still no Told'; $m->content_lacks('GotoFirstItem', 'no GotoFirstItem error'); $m->content_like(qr|<img[^>]+?src=['"]/Ticket/Graphs/@{[$ticket->id]}|, 'found image element'); } diff --git a/rt/t/shredder/00load.t b/rt/t/shredder/00load.t index a78e5e4c3..2d78da45c 100644 --- a/rt/t/shredder/00load.t +++ b/rt/t/shredder/00load.t @@ -1,14 +1,6 @@ use strict; use warnings; -use File::Spec; -use Test::More tests => 11 + 1; # plus one for warnings check -use RT::Test nodb => 1; - -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} +use RT::Test nodb => 1, tests => 11; use_ok("RT::Shredder"); diff --git a/rt/t/shredder/00skeleton.t b/rt/t/shredder/00skeleton.t index 9c6e3a1c0..86f6fa9c7 100644 --- a/rt/t/shredder/00skeleton.t +++ b/rt/t/shredder/00skeleton.t @@ -3,22 +3,14 @@ use strict; use warnings; use Test::Deep; -use File::Spec; -use Test::More tests => 1 + 1; # plus one for warnings check -use RT::Test (); -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} -init_db(); +use RT::Test::Shredder tests => 1; +my $test = "RT::Test::Shredder"; - -create_savepoint('clean'); # backup of the clean RT DB -my $shredder = shredder_new(); # new shredder object +$test->create_savepoint('clean'); # backup of the clean RT DB +my $shredder = $test->shredder_new(); # new shredder object # .... # create and wipe RT objects # -cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); +cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); diff --git a/rt/t/shredder/01basics.t b/rt/t/shredder/01basics.t index 1fa9f75ba..368c9ba74 100644 --- a/rt/t/shredder/01basics.t +++ b/rt/t/shredder/01basics.t @@ -3,18 +3,10 @@ use strict; use warnings; use Test::Deep; -use File::Spec; -use Test::More tests => 3 + 1; # plus one for warnings check -use RT::Test (); -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} -init_db(); +use RT::Test::Shredder tests => 4; +my $test = "RT::Test::Shredder"; - -create_savepoint(); +$test->create_savepoint(); use RT::Tickets; my $ticket = RT::Ticket->new( RT->SystemUser ); @@ -25,7 +17,9 @@ $ticket = RT::Ticket->new( RT->SystemUser ); my ($status, $msg) = $ticket->Load( $id ); ok( $id, "load ticket" ) or diag( "error: $msg" ); -my $shredder = shredder_new(); +my $shredder = $test->shredder_new(); $shredder->Wipeout( Object => $ticket ); -cmp_deeply( dump_current_and_savepoint(), "current DB equal to savepoint"); +$test->db_is_valid; + +cmp_deeply( $test->dump_current_and_savepoint(), "current DB equal to savepoint"); diff --git a/rt/t/shredder/01ticket.t b/rt/t/shredder/01ticket.t index 0a9da413e..57179bc0b 100644 --- a/rt/t/shredder/01ticket.t +++ b/rt/t/shredder/01ticket.t @@ -3,19 +3,10 @@ use strict; use warnings; use Test::Deep; -use File::Spec; -use Test::More tests => 15 + 1; # plus one for warnings check -use RT::Test (); +use RT::Test::Shredder tests => 20; +my $test = "RT::Test::Shredder"; - -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} - -init_db(); -create_savepoint('clean'); +$test->create_savepoint('clean'); use RT::Ticket; use RT::Tickets; @@ -26,23 +17,25 @@ use RT::Tickets; ok( $id, "created new ticket" ); $ticket->Delete; is( $ticket->Status, 'deleted', "successfuly changed status" ); + $ticket->ApplyTransactionBatch; my $tickets = RT::Tickets->new( RT->SystemUser ); $tickets->{'allow_deleted_search'} = 1; $tickets->LimitStatus( VALUE => 'deleted' ); is( $tickets->Count, 1, "found one deleted ticket" ); - my $shredder = shredder_new(); + my $shredder = $test->shredder_new(); $shredder->PutObjects( Objects => $tickets ); $shredder->WipeoutAll; + $test->db_is_valid; } -cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); +cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); { my $parent = RT::Ticket->new( RT->SystemUser ); my ($pid) = $parent->Create( Subject => 'test', Queue => 1 ); ok( $pid, "created new ticket" ); - create_savepoint('parent_ticket'); + $test->create_savepoint('parent_ticket'); my $child = RT::Ticket->new( RT->SystemUser ); my ($cid) = $child->Create( Subject => 'test', Queue => 1 ); @@ -50,15 +43,21 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint" my ($status, $msg) = $parent->AddLink( Type => 'MemberOf', Target => $cid ); ok( $status, "Added link between tickets") or diag("error: $msg"); - my $shredder = shredder_new(); + + $parent->ApplyTransactionBatch; + $child->ApplyTransactionBatch; + + my $shredder = $test->shredder_new(); $shredder->PutObjects( Objects => $child ); $shredder->WipeoutAll; - cmp_deeply( dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint"); + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint"); $shredder->PutObjects( Objects => $parent ); $shredder->WipeoutAll; + $test->db_is_valid; } -cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); +cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); { my $parent = RT::Ticket->new( RT->SystemUser ); @@ -66,24 +65,26 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint" ok( $pid, "created new ticket" ); my ($status, $msg) = $parent->Delete; ok( $status, 'deleted parent ticket'); - create_savepoint('parent_ticket'); + $test->create_savepoint('parent_ticket'); my $child = RT::Ticket->new( RT->SystemUser ); my ($cid) = $child->Create( Subject => 'test', Queue => 1 ); - ok( $cid, "created new ticket" ); + ok( $cid, "created new ticket #$cid" ); ($status, $msg) = $parent->AddLink( Type => 'DependsOn', Target => $cid ); ok( $status, "Added link between tickets") or diag("error: $msg"); - my $shredder = shredder_new(); + + $parent->ApplyTransactionBatch; + $child->ApplyTransactionBatch; + + my $shredder = $test->shredder_new(); $shredder->PutObjects( Objects => $child ); $shredder->WipeoutAll; - - TODO: { - local $TODO = "Shredder doesn't delete all links and transactions"; - cmp_deeply( dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint"); - } + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint"); $shredder->PutObjects( Objects => $parent ); $shredder->WipeoutAll; + $test->db_is_valid; } -cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); +cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); diff --git a/rt/t/shredder/02group_member.t b/rt/t/shredder/02group_member.t index 9dc4f6126..87d1e3ce7 100644 --- a/rt/t/shredder/02group_member.t +++ b/rt/t/shredder/02group_member.t @@ -3,100 +3,133 @@ use strict; use warnings; use Test::Deep; -use File::Spec; -use Test::More tests => 22 + 1; # plus one for warnings check -use RT::Test (); -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} -init_db(); - +use RT::Test::Shredder tests => 34; +my $test = "RT::Test::Shredder"; ### nested membership check { - create_savepoint('clean'); - my $pgroup = RT::Group->new( RT->SystemUser ); - my ($pgid) = $pgroup->CreateUserDefinedGroup( Name => 'Parent group' ); - ok( $pgid, "created parent group" ); - is( $pgroup->id, $pgid, "id is correct" ); - - my $cgroup = RT::Group->new( RT->SystemUser ); - my ($cgid) = $cgroup->CreateUserDefinedGroup( Name => 'Child group' ); - ok( $cgid, "created child group" ); - is( $cgroup->id, $cgid, "id is correct" ); - - my ($status, $msg) = $pgroup->AddMember( $cgroup->id ); - ok( $status, "added child group to parent") or diag "error: $msg"; - - create_savepoint('bucreate'); # before user create - my $user = RT::User->new( RT->SystemUser ); - my $uid; - ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 ); - ok( $uid, "created new user" ) or diag "error: $msg"; - is( $user->id, $uid, "id is correct" ); - - create_savepoint('buadd'); # before group add - ($status, $msg) = $cgroup->AddMember( $user->id ); - ok( $status, "added user to child group") or diag "error: $msg"; - - my $members = RT::GroupMembers->new( RT->SystemUser ); - $members->Limit( FIELD => 'MemberId', VALUE => $uid ); - $members->Limit( FIELD => 'GroupId', VALUE => $cgid ); - is( $members->Count, 1, "find membership record" ); - - my $shredder = shredder_new(); - $shredder->PutObjects( Objects => $members ); - $shredder->WipeoutAll(); - cmp_deeply( dump_current_and_savepoint('buadd'), "current DB equal to savepoint"); - - $shredder->PutObjects( Objects => $user ); - $shredder->WipeoutAll(); - cmp_deeply( dump_current_and_savepoint('bucreate'), "current DB equal to savepoint"); - - $shredder->PutObjects( Objects => [$pgroup, $cgroup] ); - $shredder->WipeoutAll(); - cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); + $test->create_savepoint('clean'); + my $pgroup = RT::Group->new( RT->SystemUser ); + my ($pgid) = $pgroup->CreateUserDefinedGroup( Name => 'Parent group' ); + ok( $pgid, "created parent group" ); + is( $pgroup->id, $pgid, "id is correct" ); + + my $cgroup = RT::Group->new( RT->SystemUser ); + my ($cgid) = $cgroup->CreateUserDefinedGroup( Name => 'Child group' ); + ok( $cgid, "created child group" ); + is( $cgroup->id, $cgid, "id is correct" ); + + my ($status, $msg) = $pgroup->AddMember( $cgroup->id ); + ok( $status, "added child group to parent") or diag "error: $msg"; + + $test->create_savepoint('bucreate'); # before user create + my $user = RT::User->new( RT->SystemUser ); + my $uid; + ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 ); + ok( $uid, "created new user" ) or diag "error: $msg"; + is( $user->id, $uid, "id is correct" ); + + $test->create_savepoint('buadd'); # before group add + ($status, $msg) = $cgroup->AddMember( $user->id ); + ok( $status, "added user to child group") or diag "error: $msg"; + + my $members = RT::GroupMembers->new( RT->SystemUser ); + $members->Limit( FIELD => 'MemberId', VALUE => $uid ); + $members->Limit( FIELD => 'GroupId', VALUE => $cgid ); + is( $members->Count, 1, "find membership record" ); + + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $members ); + $shredder->WipeoutAll(); + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('buadd'), "current DB equal to savepoint"); + + $shredder->PutObjects( Objects => $user ); + $shredder->WipeoutAll(); + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('bucreate'), "current DB equal to savepoint"); + + $shredder->PutObjects( Objects => [$pgroup, $cgroup] ); + $shredder->WipeoutAll(); + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); +} + +### deleting member of the ticket AdminCc role group +{ + $test->restore_savepoint('clean'); + + my $user = RT::User->new( RT->SystemUser ); + my ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 ); + ok( $uid, "created new user" ) or diag "error: $msg"; + is( $user->id, $uid, "id is correct" ); + + use RT::Queue; + my $queue = RT::Queue->new( RT->SystemUser ); + $queue->Load('general'); + ok( $queue->id, "queue loaded succesfully" ); + + use RT::Tickets; + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id) = $ticket->Create( Subject => 'test', Queue => $queue->id ); + ok( $id, "created new ticket" ); + $ticket = RT::Ticket->new( RT->SystemUser ); + my $status; + ($status, $msg) = $ticket->Load( $id ); + ok( $id, "load ticket" ) or diag( "error: $msg" ); + + ($status, $msg) = $ticket->AddWatcher( Type => "AdminCc", PrincipalId => $user->id ); + ok( $status, "AdminCC successfuly added") or diag( "error: $msg" ); + + my $member = $ticket->AdminCc->MembersObj->First; + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $member ); + $shredder->WipeoutAll(); + $test->db_is_valid; + + $shredder->PutObjects( Objects => $user ); + $shredder->WipeoutAll(); + $test->db_is_valid; } ### deleting member of the ticket Owner role group { - restore_savepoint('clean'); - - my $user = RT::User->new( RT->SystemUser ); - my ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 ); - ok( $uid, "created new user" ) or diag "error: $msg"; - is( $user->id, $uid, "id is correct" ); - - use RT::Queue; - my $queue = RT::Queue->new( RT->SystemUser ); - $queue->Load('general'); - ok( $queue->id, "queue loaded succesfully" ); - - $user->PrincipalObj->GrantRight( Right => 'OwnTicket', Object => $queue ); - - use RT::Tickets; - my $ticket = RT::Ticket->new( RT->SystemUser ); - my ($id) = $ticket->Create( Subject => 'test', Queue => $queue->id ); - ok( $id, "created new ticket" ); - $ticket = RT::Ticket->new( RT->SystemUser ); - my $status; - ($status, $msg) = $ticket->Load( $id ); - ok( $id, "load ticket" ) or diag( "error: $msg" ); - - ($status, $msg) = $ticket->SetOwner( $user->id ); - ok( $status, "owner successfuly set") or diag( "error: $msg" ); - is( $ticket->Owner, $user->id, "owner successfuly set") or diag( "error: $msg" ); - - my $member = $ticket->OwnerGroup->MembersObj->First; - my $shredder = shredder_new(); - $shredder->PutObjects( Objects => $member ); - $shredder->WipeoutAll(); - - $ticket = RT::Ticket->new( RT->SystemUser ); - ($status, $msg) = $ticket->Load( $id ); - ok( $id, "load ticket" ) or diag( "error: $msg" ); - is( $ticket->Owner, RT->Nobody->id, "owner switched back to nobody" ); - is( $ticket->OwnerGroup->MembersObj->First->MemberId, RT->Nobody->id, "and owner role group member is nobody"); + $test->restore_savepoint('clean'); + + my $user = RT::User->new( RT->SystemUser ); + my ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 ); + ok( $uid, "created new user" ) or diag "error: $msg"; + is( $user->id, $uid, "id is correct" ); + + use RT::Queue; + my $queue = RT::Queue->new( RT->SystemUser ); + $queue->Load('general'); + ok( $queue->id, "queue loaded succesfully" ); + + $user->PrincipalObj->GrantRight( Right => 'OwnTicket', Object => $queue ); + + use RT::Tickets; + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id) = $ticket->Create( Subject => 'test', Queue => $queue->id ); + ok( $id, "created new ticket" ); + $ticket = RT::Ticket->new( RT->SystemUser ); + my $status; + ($status, $msg) = $ticket->Load( $id ); + ok( $id, "load ticket" ) or diag( "error: $msg" ); + + ($status, $msg) = $ticket->SetOwner( $user->id ); + ok( $status, "owner successfuly set") or diag( "error: $msg" ); + is( $ticket->Owner, $user->id, "owner successfuly set") or diag( "error: $msg" ); + + my $member = $ticket->OwnerGroup->MembersObj->First; + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $member ); + $shredder->WipeoutAll(); + $test->db_is_valid; + + $ticket = RT::Ticket->new( RT->SystemUser ); + ($status, $msg) = $ticket->Load( $id ); + ok( $id, "load ticket" ) or diag( "error: $msg" ); + is( $ticket->Owner, RT->Nobody->id, "owner switched back to nobody" ); + is( $ticket->OwnerGroup->MembersObj->First->MemberId, RT->Nobody->id, "and owner role group member is nobody"); } diff --git a/rt/t/shredder/02queue.t b/rt/t/shredder/02queue.t index dd5581754..0c65c5daf 100644 --- a/rt/t/shredder/02queue.t +++ b/rt/t/shredder/02queue.t @@ -3,33 +3,26 @@ use strict; use warnings; use Test::Deep; -use File::Spec; -use Test::More tests => 16 + 1; # plus one for warnings check -use RT::Test (); -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} -init_db(); - +use RT::Test::Shredder tests => 21; +my $test = "RT::Test::Shredder"; diag 'simple queue' if $ENV{TEST_VERBOSE}; { - create_savepoint('clean'); + $test->create_savepoint('clean'); my $queue = RT::Queue->new( RT->SystemUser ); my ($id, $msg) = $queue->Create( Name => 'my queue' ); ok($id, 'created queue') or diag "error: $msg"; - my $shredder = shredder_new(); - $shredder->PutObjects( Objects => $queue ); - $shredder->WipeoutAll; - cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $queue ); + $shredder->WipeoutAll; + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); } diag 'queue with scrip' if $ENV{TEST_VERBOSE}; { - create_savepoint('clean'); + $test->create_savepoint('clean'); my $queue = RT::Queue->new( RT->SystemUser ); my ($id, $msg) = $queue->Create( Name => 'my queue' ); ok($id, 'created queue') or diag "error: $msg"; @@ -44,15 +37,16 @@ diag 'queue with scrip' if $ENV{TEST_VERBOSE}; ); ok($id, 'created scrip') or diag "error: $msg"; - my $shredder = shredder_new(); - $shredder->PutObjects( Objects => $queue ); - $shredder->WipeoutAll; - cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $queue ); + $shredder->WipeoutAll; + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); } diag 'queue with template' if $ENV{TEST_VERBOSE}; { - create_savepoint('clean'); + $test->create_savepoint('clean'); my $queue = RT::Queue->new( RT->SystemUser ); my ($id, $msg) = $queue->Create( Name => 'my queue' ); ok($id, 'created queue') or diag "error: $msg"; @@ -65,15 +59,16 @@ diag 'queue with template' if $ENV{TEST_VERBOSE}; ); ok($id, 'created template') or diag "error: $msg"; - my $shredder = shredder_new(); - $shredder->PutObjects( Objects => $queue ); - $shredder->WipeoutAll; - cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $queue ); + $shredder->WipeoutAll; + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); } diag 'queue with a right granted' if $ENV{TEST_VERBOSE}; { - create_savepoint('clean'); + $test->create_savepoint('clean'); my $queue = RT::Queue->new( RT->SystemUser ); my ($id, $msg) = $queue->Create( Name => 'my queue' ); ok($id, 'created queue') or diag "error: $msg"; @@ -88,21 +83,22 @@ diag 'queue with a right granted' if $ENV{TEST_VERBOSE}; ); ok($id, 'granted right') or diag "error: $msg"; - my $shredder = shredder_new(); - $shredder->PutObjects( Objects => $queue ); - $shredder->WipeoutAll; - cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $queue ); + $shredder->WipeoutAll; + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); } diag 'queue with a watcher' if $ENV{TEST_VERBOSE}; { # XXX, FIXME: if uncomment these lines then we'll get 'Bizarre...' -# create_savepoint('clean'); +# $test->create_savepoint('clean'); my $group = RT::Group->new( RT->SystemUser ); my ($id, $msg) = $group->CreateUserDefinedGroup(Name => 'my group'); ok($id, 'created group') or diag "error: $msg"; - create_savepoint('bqcreate'); + $test->create_savepoint('bqcreate'); my $queue = RT::Queue->new( RT->SystemUser ); ($id, $msg) = $queue->Create( Name => 'my queue' ); ok($id, 'created queue') or diag "error: $msg"; @@ -113,12 +109,13 @@ diag 'queue with a watcher' if $ENV{TEST_VERBOSE}; ); ok($id, 'added watcher') or diag "error: $msg"; - my $shredder = shredder_new(); - $shredder->PutObjects( Objects => $queue ); - $shredder->WipeoutAll; - cmp_deeply( dump_current_and_savepoint('bqcreate'), "current DB equal to savepoint"); + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $queue ); + $shredder->WipeoutAll; + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('bqcreate'), "current DB equal to savepoint"); -# $shredder->PutObjects( Objects => $group ); -# $shredder->WipeoutAll; -# cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); +# $shredder->PutObjects( Objects => $group ); +# $shredder->WipeoutAll; +# cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); } diff --git a/rt/t/shredder/02template.t b/rt/t/shredder/02template.t index aeb318ed6..56dd852ed 100644 --- a/rt/t/shredder/02template.t +++ b/rt/t/shredder/02template.t @@ -3,20 +3,12 @@ use strict; use warnings; use Test::Deep; -use File::Spec; -use Test::More tests => 7 + 1; # plus one for warnings check -use RT::Test (); -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} -init_db(); - +use RT::Test::Shredder tests => 10; +my $test = "RT::Test::Shredder"; diag 'global template' if $ENV{TEST_VERBOSE}; { - create_savepoint('clean'); + $test->create_savepoint('clean'); my $template = RT::Template->new( RT->SystemUser ); my ($id, $msg) = $template->Create( Name => 'my template', @@ -24,15 +16,16 @@ diag 'global template' if $ENV{TEST_VERBOSE}; ); ok($id, 'created template') or diag "error: $msg"; - my $shredder = shredder_new(); - $shredder->PutObjects( Objects => $template ); - $shredder->WipeoutAll; - cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $template ); + $shredder->WipeoutAll; + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); } diag 'local template' if $ENV{TEST_VERBOSE}; { - create_savepoint('clean'); + $test->create_savepoint('clean'); my $template = RT::Template->new( RT->SystemUser ); my ($id, $msg) = $template->Create( Name => 'my template', @@ -41,15 +34,16 @@ diag 'local template' if $ENV{TEST_VERBOSE}; ); ok($id, 'created template') or diag "error: $msg"; - my $shredder = shredder_new(); - $shredder->PutObjects( Objects => $template ); - $shredder->WipeoutAll; - cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $template ); + $shredder->WipeoutAll; + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); } diag 'template used in scrip' if $ENV{TEST_VERBOSE}; { - create_savepoint('clean'); + $test->create_savepoint('clean'); my $template = RT::Template->new( RT->SystemUser ); my ($id, $msg) = $template->Create( Name => 'my template', @@ -68,8 +62,9 @@ diag 'template used in scrip' if $ENV{TEST_VERBOSE}; ); ok($id, 'created scrip') or diag "error: $msg"; - my $shredder = shredder_new(); - $shredder->PutObjects( Objects => $template ); - $shredder->WipeoutAll; - cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); + my $shredder = $test->shredder_new(); + $shredder->PutObjects( Objects => $template ); + $shredder->WipeoutAll; + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); } diff --git a/rt/t/shredder/02user.t b/rt/t/shredder/02user.t index 9f1577015..620c2c587 100644 --- a/rt/t/shredder/02user.t +++ b/rt/t/shredder/02user.t @@ -3,18 +3,10 @@ use strict; use warnings; use Test::Deep; -use File::Spec; -use Test::More tests => 8 + 1; # plus one for warnings check -use RT::Test (); -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} -init_db(); - +use RT::Test::Shredder tests => 10; +my $test = "RT::Test::Shredder"; -create_savepoint('clean'); +$test->create_savepoint('clean'); my $queue = RT::Queue->new( RT->SystemUser ); my ($qid) = $queue->Load( 'General' ); @@ -24,38 +16,40 @@ my $ticket = RT::Ticket->new( RT->SystemUser ); my ($tid) = $ticket->Create( Queue => $qid, Subject => 'test' ); ok( $tid, "ticket created" ); -create_savepoint('bucreate'); # berfore user create +$test->create_savepoint('bucreate'); # berfore user create my $user = RT::User->new( RT->SystemUser ); my ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 ); ok( $uid, "created new user" ) or diag "error: $msg"; is( $user->id, $uid, "id is correct" ); # HACK: set ticket props to enable VARIABLE dependencies $ticket->__Set( Field => 'LastUpdatedBy', Value => $uid ); -create_savepoint('aucreate'); # after user create +$test->create_savepoint('aucreate'); # after user create { my $resolver = sub { my %args = (@_); - my $t = $args{'TargetObject'}; + my $t = $args{'TargetObject'}; my $resolver_uid = RT->SystemUser->id; foreach my $method ( qw(Creator LastUpdatedBy) ) { next unless $t->_Accessible( $method => 'read' ); $t->__Set( Field => $method, Value => $resolver_uid ); } }; - my $shredder = shredder_new(); + my $shredder = $test->shredder_new(); $shredder->PutResolver( BaseClass => 'RT::User', Code => $resolver ); $shredder->Wipeout( Object => $user ); - cmp_deeply( dump_current_and_savepoint('bucreate'), "current DB equal to savepoint"); + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('bucreate'), "current DB equal to savepoint"); } { - restore_savepoint('aucreate'); + $test->restore_savepoint('aucreate'); my $user = RT::User->new( RT->SystemUser ); $user->Load($uid); ok($user->id, "loaded user after restore"); - my $shredder = shredder_new(); + my $shredder = $test->shredder_new(); eval { $shredder->Wipeout( Object => $user ) }; ok($@, "wipeout throw exception if no resolvers"); - cmp_deeply( dump_current_and_savepoint('aucreate'), "current DB equal to savepoint"); + $test->db_is_valid; + cmp_deeply( $test->dump_current_and_savepoint('aucreate'), "current DB equal to savepoint"); } diff --git a/rt/t/shredder/03plugin.t b/rt/t/shredder/03plugin.t index 15766cd2e..de5d44fa7 100644 --- a/rt/t/shredder/03plugin.t +++ b/rt/t/shredder/03plugin.t @@ -3,14 +3,8 @@ use strict; use warnings; use Test::Deep; -use File::Spec; -use Test::More tests => 28 + 1; # plus one for warnings check -use RT::Test (); -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} +use RT::Test::Shredder nodb => 1, tests => 28; +my $test = "RT::Test::Shredder"; my @PLUGINS = sort qw(Attachments Base Objects SQLDump Summary Tickets Users); diff --git a/rt/t/shredder/03plugin_summary.t b/rt/t/shredder/03plugin_summary.t index d450c70a2..7da8bb454 100644 --- a/rt/t/shredder/03plugin_summary.t +++ b/rt/t/shredder/03plugin_summary.t @@ -2,16 +2,7 @@ use strict; use warnings; -use Test::Deep; -use File::Spec; -use Test::More tests => 4 + 1; # plus one for warnings check -use RT::Test nodb => 1; -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} - +use RT::Test::Shredder nodb => 1, tests => 4; use_ok('RT::Shredder::Plugin'); my $plugin_obj = RT::Shredder::Plugin->new; diff --git a/rt/t/shredder/03plugin_tickets.t b/rt/t/shredder/03plugin_tickets.t index 1579bc54b..dd2b120c4 100644 --- a/rt/t/shredder/03plugin_tickets.t +++ b/rt/t/shredder/03plugin_tickets.t @@ -3,15 +3,10 @@ use strict; use warnings; use Test::Deep; -use File::Spec; -use Test::More tests => 44 + 1; # plus one for warnings check -use RT::Test (); -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} +use RT::Test::Shredder tests => 49; +my $test = "RT::Test::Shredder"; +use_ok('RT::Shredder'); use_ok('RT::Shredder::Plugin::Tickets'); { @@ -21,8 +16,7 @@ use_ok('RT::Shredder::Plugin::Tickets'); is(lc $plugin->Type, 'search', 'correct type'); } -init_db(); -create_savepoint('clean'); +$test->create_savepoint('clean'); use_ok('RT::Ticket'); use_ok('RT::Tickets'); @@ -59,11 +53,12 @@ use_ok('RT::Tickets'); ok($has{$pid}, "parent is in the result set"); ok($has{$cid}, "child is in the result set"); - my $shredder = shredder_new(); + my $shredder = $test->shredder_new(); $shredder->PutObjects( Objects => \@objs ); $shredder->WipeoutAll; + $test->db_is_valid; } -cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); +cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); { # create parent and child and link them reqursively to check that we don't hang my $parent = RT::Ticket->new( RT->SystemUser ); @@ -103,11 +98,12 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint" ok($has{$pid}, "parent is in the result set"); ok($has{$cid}, "child is in the result set"); - my $shredder = shredder_new(); + my $shredder = $test->shredder_new(); $shredder->PutObjects( Objects => \@objs ); $shredder->WipeoutAll; + $test->db_is_valid; } -cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); +cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); { # create parent and child and check functionality of 'apply_query_to_linked' arg my $parent = RT::Ticket->new( RT->SystemUser ); @@ -141,9 +137,10 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint" ok(!$has{$cid1}, "first child is in the result set"); ok($has{$cid2}, "second child is in the result set"); - my $shredder = shredder_new(); + my $shredder = $test->shredder_new(); $shredder->PutObjects( Objects => \@objs ); $shredder->WipeoutAll; + $test->db_is_valid; my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $cid1 ); @@ -151,5 +148,6 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint" $shredder->PutObjects( Objects => $ticket ); $shredder->WipeoutAll; + $test->db_is_valid; } -cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); +cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); diff --git a/rt/t/shredder/03plugin_users.t b/rt/t/shredder/03plugin_users.t index 131ffa0c3..477f1474f 100644 --- a/rt/t/shredder/03plugin_users.t +++ b/rt/t/shredder/03plugin_users.t @@ -3,17 +3,10 @@ use strict; use warnings; use Test::Deep; -use File::Spec; -use Test::More tests => 21 + 1; # plus one for warnings check -use RT::Test (); -BEGIN { - my $shredder_utils = RT::Test::get_relocatable_file('utils.pl', - File::Spec->curdir()); - require $shredder_utils; -} - +use RT::Test::Shredder tests => 21; +my $test = "RT::Test::Shredder"; -my @ARGS = sort qw(limit status name member_of email replace_relations no_tickets); +my @ARGS = sort qw(limit status name member_of not_member_of email replace_relations no_tickets); use_ok('RT::Shredder::Plugin::Users'); { @@ -37,13 +30,11 @@ use_ok('RT::Shredder::Plugin::Users'); ok(!$status, "bad 'status' arg value"); } -init_db(); - RT::Test->set_rights( { Principal => 'Everyone', Right => [qw(CreateTicket)] }, ); -create_savepoint('clean'); +$test->create_savepoint('clean'); { # Create two users and a ticket. Shred second user and replace relations with first user my ($uidA, $uidB, $msg); @@ -59,6 +50,7 @@ create_savepoint('clean'); my $ticket = RT::Ticket->new( RT::CurrentUser->new($userB) ); ($tid, $trid, $msg) = $ticket->Create( Subject => 'UserB Ticket', Queue => 1 ); ok( $tid, "created new ticket") or diag "error: $msg"; + $ticket->ApplyTransactionBatch; my $transaction = RT::Transaction->new( RT->SystemUser ); $transaction->Load($trid); @@ -71,14 +63,14 @@ create_savepoint('clean'); ($status, $msg) = $plugin->TestArgs( status => 'any', name => 'userB', replace_relations => $uidA ); ok($status, "plugin arguments are ok") or diag "error: $msg"; + my $shredder = $test->shredder_new(); + my @objs; ($status, @objs) = $plugin->Run; ok($status, "executed plugin successfully") or diag "error: @objs"; @objs = RT::Shredder->CastObjectsToRecords( Objects => \@objs ); is(scalar @objs, 1, "one object in the result set"); - my $shredder = shredder_new(); - ($status, $msg) = $plugin->SetResolvers( Shredder => $shredder ); ok($status, "set conflicts resolver") or diag "error: $msg"; @@ -94,4 +86,4 @@ create_savepoint('clean'); $shredder->Wipeout( Object => $ticket ); $shredder->Wipeout( Object => $userA ); } -cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"); +cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint"); diff --git a/rt/t/shredder/utils.pl b/rt/t/shredder/utils.pl deleted file mode 100644 index a3d0cf59a..000000000 --- a/rt/t/shredder/utils.pl +++ /dev/null @@ -1,394 +0,0 @@ - -use strict; -use warnings; - -require File::Copy; -require Cwd; -require RT::Test; - -BEGIN { -### after: push @INC, qw(@RT_LIB_PATH@); - - use RT; - RT->LoadConfig; - RT->InitPluginPaths; - RT->InitClasses; -} - -require RT::Shredder; - -=head1 DESCRIPTION - -RT::Shredder test suite utilities - -=head1 TESTING - -Since RT:Shredder 0.01_03 we have a test suite. You -can run tests and see if everything works as expected -before you try shredder on your actual data. -Tests also help in the development process. - -The test suite uses SQLite databases to store data in individual files, -so you could sun tests on your production servers without risking -damage to your production data. - -You'll want to run the test suite almost every time you install or update -the shredder distribution, especialy if you have local customizations of -the DB schema and/or RT code. - -Tests are one thing you can write even if you don't know much perl, -but want to learn more about RT's internals. New tests are very welcome. - -=head2 WRITING TESTS - -The shredder distribution has several files to help write new tests. - - t/shredder/utils.pl - this file, utilities - t/00skeleton.t - skeleteton .t file for new tests - -All tests follow this algorithm: - - require "t/shredder/utils.pl"; # plug in utilities - init_db(); # create new tmp RT DB and init RT API - # create RT data you want to be always in the RT DB - # ... - create_savepoint('mysp'); # create DB savepoint - # create data you want delete with shredder - # ... - # run shredder on the objects you've created - # ... - # check that shredder deletes things you want - # this command will compare savepoint DB with current - cmp_deeply( dump_current_and_savepoint('mysp'), "current DB equal to savepoint"); - # then you can create another object and delete it, then check again - -Savepoints are named and you can create two or more savepoints. - -=head1 FUNCTIONS - -=head2 RT CONFIG - -=head3 rewrite_rtconfig - -Call this sub after C<RT::LoadConfig>. It changes the RT config -options necessary to switch to a local SQLite database. - -=cut - -sub rewrite_rtconfig -{ - # database - config_set( '$DatabaseType' , 'SQLite' ); - config_set( '$DatabaseHost' , 'localhost' ); - config_set( '$DatabaseRTHost' , 'localhost' ); - config_set( '$DatabasePort' , '' ); - config_set( '$DatabaseUser' , 'rt_user' ); - config_set( '$DatabasePassword' , 'rt_pass' ); - config_set( '$DatabaseRequireSSL' , undef ); - # database file name - config_set( '$DatabaseName' , db_name() ); - - # generic logging - config_set( '$LogToSyslog' , undef ); - config_set( '$LogToScreen' , 'error' ); - config_set( '$LogStackTraces' , 'crit' ); - # logging to standalone file - config_set( '$LogToFile' , 'debug' ); - my $fname = File::Spec->catfile(RT::Test->temp_directory(), test_name() .".log"); - config_set( '$LogToFileNamed' , $fname ); - config_set('@LexiconLanguages', qw(en)); -} - -=head3 config_set - -This sub is a helper used by C<rewrite_rtconfig>. You shouldn't -need to use it elsewhere unless you need to change other RT -configuration variables. - -=cut - -sub config_set { - my $opt = shift; - $opt =~ s/^[\$\%\@]//; - RT->Config->Set($opt, @_) -} - -=head2 DATABASES - -=head3 init_db - -Creates a new RT DB with initial data in a new test tmp dir. -Also runs RT::Init() and RT::InitLogging(). - -This is all you need to call to setup a testing environment -in most situations. - -=cut - -sub init_db -{ - RT::Test->bootstrap_tempdir() unless RT::Test->temp_directory(); - RT::LoadConfig(); - rewrite_rtconfig(); - RT::InitLogging(); - - _init_db(); - - RT::Init(); - $SIG{__WARN__} = sub { $RT::Logger->warning( @_ ); warn @_ }; - $SIG{__DIE__} = sub { $RT::Logger->crit( @_ ) unless $^S; die @_ }; -} - -use IPC::Open2; -sub _init_db -{ - - - foreach ( qw(Type Host Port Name User Password) ) { - $ENV{ "RT_DB_". uc $_ } = RT->Config->Get("Database$_"); - } - my $rt_setup_database = RT::Test::get_relocatable_file( - 'rt-setup-database', (File::Spec->updir(), File::Spec->updir(), 'sbin')); - my $cmd = "$^X $rt_setup_database --action init 2>&1"; - - my ($child_out, $child_in); - my $pid = open2($child_out, $child_in, $cmd); - close $child_in; - my $result = do { local $/; <$child_out> }; - return $result; -} - -=head3 db_name - -Returns the absolute file path to the current DB. -It is <<RT::Test->temp_directory . test_name() .'.db'>>. - -See also the C<test_name> function. - -=cut - -sub db_name { return File::Spec->catfile(RT::Test->temp_directory(), test_name() .".db") } - -=head3 connect_sqlite - -Returns connected DBI DB handle. - -Takes path to sqlite db. - -=cut - -sub connect_sqlite -{ - return DBI->connect("dbi:SQLite:dbname=". shift, "", ""); -} - -=head2 SHREDDER - -=head3 shredder_new - -Creates and returns a new RT::Shredder object. - -=cut - -sub shredder_new -{ - my $obj = RT::Shredder->new; - - my $file = File::Spec->catfile( RT::Test->temp_directory, test_name() .'.XXXX.sql' ); - $obj->AddDumpPlugin( Arguments => { - file_name => $file, - from_storage => 0, - } ); - - return $obj; -} - - -=head2 TEST FILES - -=head3 test_name - -Returns name of the test file running now with file extension and -directory names stripped. - -For example, it returns '00load' for the test file 't/00load.t'. - -=cut - -sub test_name -{ - my $name = $0; - $name =~ s/^.*[\\\/]//; - $name =~ s/\..*$//; - return $name; -} - -=head2 SAVEPOINTS - -=head3 savepoint_name - -Returns the absolute path to the named savepoint DB file. -Takes one argument - savepoint name, by default C<sp>. - -=cut - -sub savepoint_name -{ - my $name = shift || 'sp'; - return File::Spec->catfile( RT::Test->temp_directory, test_name() .".$name.db" ); -} - -=head3 create_savepoint - -Creates savepoint DB from the current DB. -Takes name of the savepoint as argument. - -=head3 restore_savepoint - -Restores current DB to savepoint state. -Takes name of the savepoint as argument. - -=cut - -sub create_savepoint { return __cp_db( db_name() => savepoint_name( shift ) ) } -sub restore_savepoint { return __cp_db( savepoint_name( shift ) => db_name() ) } -sub __cp_db -{ - my( $orig, $dest ) = @_; - RT::Test::__disconnect_rt(); - File::Copy::copy( $orig, $dest ) or die "Couldn't copy '$orig' => '$dest': $!"; - RT::Test::__reconnect_rt(); - return; -} - - -=head2 DUMPS - -=head3 dump_sqlite - -Returns DB dump as a complex hash structure: - { - TableName => { - #id => { - lc_field => 'value', - } - } - } - -Takes named argument C<CleanDates>. If true, clean all date fields from -dump. True by default. - -=cut - -sub dump_sqlite -{ - my $dbh = shift; - my %args = ( CleanDates => 1, @_ ); - - my $old_fhkn = $dbh->{'FetchHashKeyName'}; - $dbh->{'FetchHashKeyName'} = 'NAME_lc'; - - my @tables = $RT::Handle->_TableNames( $dbh ); - - my $res = {}; - foreach my $t( @tables ) { - next if lc($t) eq 'sessions'; - $res->{$t} = $dbh->selectall_hashref("SELECT * FROM $t".dump_sqlite_exceptions($t), 'id'); - clean_dates( $res->{$t} ) if $args{'CleanDates'}; - die $DBI::err if $DBI::err; - } - - $dbh->{'FetchHashKeyName'} = $old_fhkn; - return $res; -} - -=head3 dump_sqlite_exceptions - -If there are parts of the DB which can change from creating and deleting -a queue, skip them when doing the comparison. One example is the global -queue cache attribute on RT::System which will be updated on Queue creation -and can't be rolled back by the shredder. It may actually make sense for -Shredder to be updating this at some point in the future. - -=cut - -sub dump_sqlite_exceptions { - my $table = shift; - - my $special_wheres = { - attributes => " WHERE Name != 'QueueCacheNeedsUpdate'" - }; - - return $special_wheres->{lc $table}||''; - -} - -=head3 dump_current_and_savepoint - -Returns dump of the current DB and of the named savepoint. -Takes one argument - savepoint name. - -=cut - -sub dump_current_and_savepoint -{ - my $orig = savepoint_name( shift ); - die "Couldn't find savepoint file" unless -f $orig && -r _; - my $odbh = connect_sqlite( $orig ); - return ( dump_sqlite( $RT::Handle->dbh, @_ ), dump_sqlite( $odbh, @_ ) ); -} - -=head3 dump_savepoint_and_current - -Returns the same data as C<dump_current_and_savepoint> function, -but in reversed order. - -=cut - -sub dump_savepoint_and_current { return reverse dump_current_and_savepoint(@_) } - -sub clean_dates -{ - my $h = shift; - my $date_re = qr/^\d\d\d\d\-\d\d\-\d\d\s*\d\d\:\d\d(\:\d\d)?$/i; - foreach my $id ( keys %{ $h } ) { - next unless $h->{ $id }; - foreach ( keys %{ $h->{ $id } } ) { - delete $h->{$id}{$_} if $h->{$id}{$_} && - $h->{$id}{$_} =~ /$date_re/; - } - } -} - -=head2 NOTES - -Function that returns debug notes. - -=head3 note_on_fail - -Returns a note about debug info that you can display if tests fail. - -=cut - -sub note_on_fail -{ - my $name = test_name(); - my $tmpdir = RT::Test->temp_directory(); - return <<END; -Some tests in '$0' file failed. -You can find debug info in '$tmpdir' dir. -There should be: - $name.log - RT debug log file - $name.db - latest RT DB used while testing - $name.*.db - savepoint databases -See also perldoc t/shredder/utils.pl for how to use this info. -END -} - -END { - if ( ! RT::Test->builder->is_passing ) { - diag( note_on_fail() ); - } -} - -1; diff --git a/rt/t/ticket/action_linear_escalate.t b/rt/t/ticket/action_linear_escalate.t index 0d1ff8aa5..8c158aa59 100644 --- a/rt/t/ticket/action_linear_escalate.t +++ b/rt/t/ticket/action_linear_escalate.t @@ -19,7 +19,7 @@ ok $q && $q->id, 'loaded or created queue'; my $gecos = RT::Test->load_or_create_user( Name => 'gecos', Password => 'password', - Gecos => ($^O eq 'MSWin32') ? Win32::LoginName() : (getpwuid($<))[0], + Gecos => (getpwuid($<))[0], ); ok $gecos && $gecos->id, 'loaded or created gecos user'; diff --git a/rt/t/ticket/add-watchers.t b/rt/t/ticket/add-watchers.t index 11b3a8453..12b0bb3e2 100644 --- a/rt/t/ticket/add-watchers.t +++ b/rt/t/ticket/add-watchers.t @@ -1,4 +1,4 @@ -use RT::Test nodata => 1, tests => 32; +use RT::Test nodata => 1, tests => 34; use strict; use warnings; @@ -15,7 +15,7 @@ my $acl = RT::ACL->new(RT->SystemUser); $acl->Limit( FIELD => 'RightName', OPERATOR => '!=', VALUE => 'SuperUser' ); $acl->LimitToObject( RT->System ); while( my $ace = $acl->Next ) { - $ace->Delete; + $ace->Delete; } # create new queue to be sure we do not mess with rights @@ -25,11 +25,12 @@ ok( $queue_id, 'queue created for watcher tests' ); # new privileged user to check rights my $user = RT::User->new( RT->SystemUser ); -my ($user_id) = $user->Create( Name => 'watcher'.$$, - EmailAddress => "watcher$$".'@localhost', - Privileged => 1, - Password => 'qwe123', - ); +my ($user_id) = $user->Create( + Name => 'watcher'.$$, + EmailAddress => "watcher$$".'@localhost', + Privileged => 1, + Password => 'qwe123', +); my $cu= RT::CurrentUser->new($user); # make sure user can see tickets in the queue @@ -86,6 +87,13 @@ ok( $rv, "user can add self as Cc by username" ); ($rv, $msg) = $ticket2->AddWatcher( Type => 'Requestor', Email => $user->Name ); ok( $rv, "user can add self as Requestor by username" ); +# Add an email address with a phrase +($rv, $msg) = $ticket->AddWatcher( Type => 'Cc', Email => q["Foo Bar" <foo@example.com>] ); +ok $rv, "Added email address with phrase" or diag $msg; + +my $foo = RT::Test->load_or_create_user( EmailAddress => 'foo@example.com' ); +is $foo->RealName, "Foo Bar", "RealName matches"; + # Queue watcher tests $principal->RevokeRight( Right => 'Watch' , Object => $queue ); ok( !$user->HasRight( Right => 'Watch', Object => $queue ), "user queue watch right revoked" ); diff --git a/rt/t/ticket/cfsort-freeform-single.t b/rt/t/ticket/cfsort-freeform-single.t index ae109e9e4..262f84a22 100644 --- a/rt/t/ticket/cfsort-freeform-single.t +++ b/rt/t/ticket/cfsort-freeform-single.t @@ -1,158 +1,169 @@ -use RT::Test nodata => 1, tests => 89; - use strict; use warnings; -use RT::Tickets; -use RT::Queue; -use RT::CustomField; - -# Test Sorting by FreeformSingle custom field. +use RT::Test nodata => 1, tests => undef; -diag "Create a queue to test with."; -my $queue_name = "CFSortQueue-$$"; -my $queue; -{ - $queue = RT::Queue->new( RT->SystemUser ); - my ($ret, $msg) = $queue->Create( - Name => $queue_name, - Description => 'queue for custom field sort testing' - ); - ok($ret, "$queue test queue creation. $msg"); -} +my $queue = RT::Test->load_or_create_queue( Name => "sorting" ); +ok $queue && $queue->id, "Created queue"; +my $queue_name = $queue->Name; # CFs for testing, later we create another one -my %CF; -my $cf_name; - +my $cf; +my $cf_name = "ordering"; diag "create a CF"; { - $cf_name = $CF{'CF'}{'name'} = "Order$$"; - $CF{'CF'}{'obj'} = RT::CustomField->new( RT->SystemUser ); - my ($ret, $msg) = $CF{'CF'}{'obj'}->Create( - Name => $CF{'CF'}{'name'}, + $cf = RT::CustomField->new( RT->SystemUser ); + my ($ret, $msg) = $cf->Create( + Name => $cf_name, Queue => $queue->id, Type => 'FreeformSingle', ); - ok($ret, "Custom Field $CF{'CF'}{'name'} created"); -} - -my ($total, @data, @tickets, @test) = (0, ()); - -sub run_tests { - my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; - foreach my $test ( @test ) { - my $query = join " AND ", map "( $_ )", grep defined && length, - $query_prefix, $test->{'Query'}; - - foreach my $order (qw(ASC DESC)) { - my $error = 0; - my $tix = RT::Tickets->new( RT->SystemUser ); - $tix->FromSQL( $query ); - $tix->OrderBy( FIELD => $test->{'Order'}, ORDER => $order ); - - ok($tix->Count, "found ticket(s)") - or $error = 1; - - my ($order_ok, $last) = (1, $order eq 'ASC'? '-': 'zzzzzz'); - my $last_id = $tix->Last->id; - while ( my $t = $tix->Next ) { - my $tmp; - next if $t->id == $last_id and $t->Subject eq "-"; # Nulls are allowed to come last, in Pg - - if ( $order eq 'ASC' ) { - $tmp = ((split( /,/, $last))[0] cmp (split( /,/, $t->Subject))[0]); - } else { - $tmp = -((split( /,/, $last))[-1] cmp (split( /,/, $t->Subject))[-1]); - } - if ( $tmp > 0 ) { - $order_ok = 0; last; - } - $last = $t->Subject; - } - - ok( $order_ok, "$order order of tickets is good" ) - or $error = 1; - - if ( $error ) { - diag "Wrong SQL query:". $tix->BuildSelectQuery; - $tix->GotoFirstItem; - while ( my $t = $tix->Next ) { - diag sprintf "%02d - %s", $t->id, $t->Subject; - } - } - } - } + ok($ret, "Custom Field created"); } -@data = ( - { Subject => '-' }, - { Subject => 'a', 'CustomField-' . $CF{CF}{obj}->id => 'a' }, - { Subject => 'b', 'CustomField-' . $CF{CF}{obj}->id => 'b' }, -); - -@tickets = RT::Test->create_tickets( { Queue => $queue->id, RandomOrder => 1 }, @data); -@test = ( - { Order => "CF.{$cf_name}" }, - { Order => "CF.$queue_name.{$cf_name}" }, -); -run_tests(); - -@data = ( - { Subject => '-' }, - { Subject => 'aa', 'CustomField-' . $CF{CF}{obj}->id => 'aa' }, - { Subject => 'bb', 'CustomField-' . $CF{CF}{obj}->id => 'bb' }, -); -@tickets = RT::Test->create_tickets( { Queue => $queue->id, RandomOrder => 1 }, @data); -@test = ( - { Query => "CF.{$cf_name} LIKE 'a'", Order => "CF.{$cf_name}" }, - { Query => "CF.{$cf_name} LIKE 'a'", Order => "CF.$queue_name.{$cf_name}" }, -); -run_tests(); - -@data = ( - { Subject => '-', }, - { Subject => 'a', CF => 'a' }, - { Subject => 'b', CF => 'b' }, - { Subject => 'c', CF => 'c' }, -); -@tickets = RT::Test->create_tickets( { Queue => $queue->id, RandomOrder => 1 }, @data); -@test = ( - { Query => "CF.{$cf_name} != 'c'", Order => "CF.{$cf_name}" }, - { Query => "CF.{$cf_name} != 'c'", Order => "CF.$queue_name.{$cf_name}" }, +run_tests( + [ + { Subject => '-' }, + { Subject => 'aa', 'CustomField-' . $cf->id => 'aa' }, + { Subject => 'bb', 'CustomField-' . $cf->id => 'bb' }, + { Subject => 'cc', 'CustomField-' . $cf->id => 'cc' }, + ], + { Count => 4, Order => "CF.{$cf_name}" }, + { Count => 4, Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.{$cf_name}" }, + { Query => "CF.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.{$cf_name} != 'cc'", Count => 3, Order => "CF.{$cf_name}" }, + { Query => "CF.{$cf_name} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_name}" }, ); -run_tests(); +my $other_cf; +my $other_name = "othercf"; diag "create another CF"; { - $CF{'AnotherCF'}{'name'} = "OrderAnother$$"; - $CF{'AnotherCF'}{'obj'} = RT::CustomField->new( RT->SystemUser ); - my ($ret, $msg) = $CF{'AnotherCF'}{'obj'}->Create( - Name => $CF{'AnotherCF'}{'name'}, + $other_cf = RT::CustomField->new( RT->SystemUser ); + my ($ret, $msg) = $other_cf->Create( + Name => $other_name, Queue => $queue->id, Type => 'FreeformSingle', ); - ok($ret, "Custom Field $CF{'AnotherCF'}{'name'} created"); + ok($ret, "Other Custom Field created"); } -# test that order is not affect by other fields (had such problem) -@data = ( - { Subject => '-', }, - { Subject => 'a', CF => 'a', AnotherCF => 'za' }, - { Subject => 'b', CF => 'b', AnotherCF => 'ya' }, - { Subject => 'c', CF => 'c', AnotherCF => 'xa' }, +# Test that order is not affected by other CFs +run_tests( + [ + { Subject => '-', }, + { Subject => 'aa', "CustomField-" . $cf->id => 'aa', "CustomField-" . $other_cf->id => 'za' }, + { Subject => 'bb', "CustomField-" . $cf->id => 'bb', "CustomField-" . $other_cf->id => 'ya' }, + { Subject => 'cc', "CustomField-" . $cf->id => 'cc', "CustomField-" . $other_cf->id => 'xa' }, + ], + { Count => 4, Order => "CF.{$cf_name}" }, + { Count => 4, Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.{$cf_name}" }, + { Query => "CF.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.{$cf_name} != 'cc'", Count => 3, Order => "CF.{$cf_name}" }, + { Query => "CF.{$cf_name} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.{$cf_name}" }, + { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.$queue_name.{$cf_name}" }, ); -@tickets = RT::Test->create_tickets( { Queue => $queue->id, RandomOrder => 1 }, @data); -@test = ( - { Order => "CF.{$cf_name}" }, - { Order => "CF.$queue_name.{$cf_name}" }, - { Query => "CF.{$cf_name} != 'c'", Order => "CF.{$cf_name}" }, - { Query => "CF.{$cf_name} != 'c'", Order => "CF.$queue_name.{$cf_name}" }, + +# And then add a CF with a duplicate name, on a different queue +{ + my $other_queue = RT::Test->load_or_create_queue( Name => "other_queue" ); + ok $other_queue && $other_queue->id, "Created queue"; + + my $dup = RT::CustomField->new( RT->SystemUser ); + my ($ret, $msg) = $dup->Create( + Name => $cf_name, + Queue => $other_queue->id, + Type => 'FreeformSingle', + ); + ok($ret, "Custom Field created"); +} + +my $cf_id = $cf->id; +run_tests( + [ + { Subject => '-', }, + { Subject => 'aa', "CustomField-" . $cf->id => 'aa', "CustomField-" . $other_cf->id => 'za' }, + { Subject => 'bb', "CustomField-" . $cf->id => 'bb', "CustomField-" . $other_cf->id => 'ya' }, + { Subject => 'cc', "CustomField-" . $cf->id => 'cc', "CustomField-" . $other_cf->id => 'xa' }, + ], + { Count => 4, Order => "CF.{$cf_name}" }, + { Count => 4, Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.{$cf_id} LIKE 'a'", Count => 1, Order => "CF.{$cf_name}" }, + { Query => "CF.{$cf_id} LIKE 'a'", Count => 1, Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.{$cf_id} != 'cc'", Count => 3, Order => "CF.{$cf_name}" }, + { Query => "CF.{$cf_id} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.$queue_name.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.{$cf_name}" }, + { Query => "CF.$queue_name.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.$queue_name.{$cf_name} != 'cc'", Count => 3, Order => "CF.{$cf_name}" }, + { Query => "CF.$queue_name.{$cf_name} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_name}" }, + { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.{$cf_name}" }, + { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.$queue_name.{$cf_name}" }, + + { Query => "CF.{$cf_id} != 'cc'", Count => 3, Order => "CF.{$cf_id}" }, + { Query => "CF.{$cf_id} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_id}" }, + { Query => "CF.$queue_name.{$cf_name} != 'cc'", Count => 3, Order => "CF.{$cf_id}" }, + { Query => "CF.$queue_name.{$cf_name} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_id}" }, + { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.{$cf_id}" }, + { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.$queue_name.{$cf_id}" }, ); -run_tests(); -@tickets = (); +sub run_tests { + my $tickets = shift; + my @tickets = RT::Test->create_tickets( { Queue => $queue->id, RandomOrder => 1 }, @{ $tickets }); + my $base_query = join(" OR ", map {"id = ".$_->id} @tickets) || "id > 0"; + + my @tests = @_; + for my $test ( @tests ) { + $test->{'Query'} ||= "id > 0"; + my $query = "( $base_query ) AND " . $test->{'Query'}; + for my $order (qw(ASC DESC)) { + subtest $test->{'Query'} . " ORDER BY ".$test->{'Order'}. " $order" => sub { + my $error = 0; + my $tix = RT::Tickets->new( RT->SystemUser ); + $tix->FromSQL( $query ); + $tix->OrderBy( FIELD => $test->{'Order'}, ORDER => $order ); + + is($tix->Count, $test->{'Count'}, "found right number of tickets (".$test->{Count}.")") + or $error = 1; + + my ($order_ok, $last) = (1, $order eq 'ASC'? '-': 'zzzzzz'); + if ($tix->Count) { + my $last_id = $tix->Last->id; + while ( my $t = $tix->Next ) { + my $tmp; + next if $t->id == $last_id and $t->Subject eq "-"; # Nulls are allowed to come last, in Pg + + if ( $order eq 'ASC' ) { + $tmp = ((split( /,/, $last))[0] cmp (split( /,/, $t->Subject))[0]); + } else { + $tmp = -((split( /,/, $last))[-1] cmp (split( /,/, $t->Subject))[-1]); + } + if ( $tmp > 0 ) { + $order_ok = 0; last; + } + $last = $t->Subject; + } + } + + ok( $order_ok, "$order order of tickets is good" ) + or $error = 1; + + if ( $error ) { + diag "Wrong SQL query:". $tix->BuildSelectQuery; + $tix->GotoFirstItem; + while ( my $t = $tix->Next ) { + diag sprintf "%02d - %s", $t->id, $t->Subject; + } + } + }; + } + } +} +done_testing; diff --git a/rt/t/ticket/circular_links.t b/rt/t/ticket/circular_links.t new file mode 100644 index 000000000..b6695feea --- /dev/null +++ b/rt/t/ticket/circular_links.t @@ -0,0 +1,45 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my ( $foo, $bar, $baz ) = RT::Test->create_tickets( + { Queue => 'General' }, + { Subject => 'foo' }, + { Subject => 'bar' }, + { Subject => 'baz' } +); + +diag "test circular DependsOn"; +my ( $status, $msg ) = $foo->AddLink( Type => 'DependsOn', Target => $bar->id ); +ok( $status, "foo depends on bar" ); +( $status, $msg ) = $foo->AddLink( Type => 'DependsOn', Base => $bar->id ); +ok( !$status, "foo can't be depended on bar" ); +( $status, $msg ) = $bar->AddLink( Type => 'DependsOn', Target => $foo->id ); +ok( !$status, "bar can't depend on foo back" ); +( $status, $msg ) = $bar->AddLink( Type => 'DependsOn', Target => $baz->id ); +ok( $status, "bar depends on baz" ); +( $status, $msg ) = $baz->AddLink( Type => 'DependsOn', Target => $foo->id ); +ok( !$status, "baz can't depend on foo back" ); + + +diag "test circular MemberOf"; +( $status, $msg ) = $foo->AddLink( Type => 'MemberOf', Target => $bar->id ); +ok( $status, "foo is a member of bar" ); +( $status, $msg ) = $foo->AddLink( Type => 'MemberOf', Base => $bar->id ); +ok( !$status, "foo can't have member bar" ); +( $status, $msg ) = $bar->AddLink( Type => 'MemberOf', Target => $foo->id ); +ok( !$status, "bar can't be a member of foo" ); +( $status, $msg ) = $bar->AddLink( Type => 'MemberOf', Target => $baz->id ); +ok( $status, "baz is a member of bar" ); +( $status, $msg ) = $baz->AddLink( Type => 'DependsOn', Target => $foo->id ); +ok( !$status, "baz can't be a member of foo" ); + + +diag "test circular RefersTo"; +( $status, $msg ) = $foo->AddLink( Type => 'RefersTo', Target => $bar->id ); +ok( $status, "foo refers to bar" ); +( $status, $msg ) = $foo->AddLink( Type => 'RefersTo', Base => $bar->id ); +ok( $status, "foo can be referred to by bar" ); + +done_testing; diff --git a/rt/t/ticket/deferred_owner.t b/rt/t/ticket/deferred_owner.t index fe90d539d..a0aa350ec 100644 --- a/rt/t/ticket/deferred_owner.t +++ b/rt/t/ticket/deferred_owner.t @@ -1,10 +1,7 @@ - use strict; use warnings; -use RT::Test nodata => 1, tests => 18; -use_ok('RT'); -use_ok('RT::Ticket'); +use RT::Test nodata => 1, tests => undef; use Test::Warn; @@ -16,8 +13,7 @@ ok $tester && $tester->id, 'loaded or created user'; my $queue = RT::Test->load_or_create_queue( Name => 'General' ); ok $queue && $queue->id, 'loaded or created queue'; -my $owner_role_group = RT::Group->new( RT->SystemUser ); -$owner_role_group->LoadQueueRoleGroup( Type => 'Owner', Queue => $queue->id ); +my $owner_role_group = $queue->RoleGroup( 'Owner' ); ok $owner_role_group->id, 'loaded owners role group of the queue'; diag "check that deffering owner doesn't regress"; @@ -63,7 +59,7 @@ diag "check that previous trick doesn't work without sufficient rights"; diag $msg if $msg; ok $tid, "created a ticket"; is $ticket->Owner, $tester->id, 'correct owner'; - unlike $ticket->AdminCcAddresses, qr/root\@localhost/, 'root is there'; + unlike $ticket->AdminCcAddresses, qr/root\@localhost/, 'root is not there'; } diag "check that deffering owner really works"; @@ -88,6 +84,9 @@ diag "check that deffering owner really works"; ok $tid, "created a ticket"; like $ticket->CcAddresses, qr/tester\@localhost/, 'tester is in the cc list'; is $ticket->Owner, $tester->id, 'tester is also owner'; + my $owners = $ticket->OwnerGroup->MembersObj; + is $owners->Count, 1, 'one record in owner group'; + is $owners->First->MemberObj->Id, $tester->id, 'and it is tester'; } diag "check that deffering doesn't work without correct rights"; @@ -112,8 +111,10 @@ diag "check that deffering doesn't work without correct rights"; diag $msg if $msg; ok $tid, "created a ticket"; like $ticket->CcAddresses, qr/tester\@localhost/, 'tester is in the cc list'; - isnt $ticket->Owner, $tester->id, 'tester is also owner'; + is $ticket->Owner, RT->Nobody->id, 'nobody is the owner'; + my $owners = $ticket->OwnerGroup->MembersObj; + is $owners->Count, 1, 'one record in owner group'; + is $owners->First->MemberObj->Id, RT->Nobody->id, 'and it is nobody'; } - - +done_testing; diff --git a/rt/t/ticket/linking.t b/rt/t/ticket/linking.t index 1bd83d633..9eaf93987 100644 --- a/rt/t/ticket/linking.t +++ b/rt/t/ticket/linking.t @@ -17,9 +17,6 @@ my $filename = File::Spec->catfile( RT::Test->temp_directory, 'link_count' ); open my $fh, '>', $filename or die $!; close $fh; -my $link_scrips_orig = RT->Config->Get( 'LinkTransactionsRun1Scrip' ); -RT->Config->Set( 'LinkTransactionsRun1Scrip', 1 ); - my $link_acl_checks_orig = RT->Config->Get( 'StrictLinkACL' ); RT->Config->Set( 'StrictLinkACL', 1); @@ -125,8 +122,8 @@ diag('Create tickets with rights checks on one end of a link'); $child->CurrentUser( RT->SystemUser ); is($child->_Links('Base')->Count, 1, 'link was created'); is($child->_Links('Target')->Count, 0, 'link was created only one'); - # no scrip run on second ticket accroding to config option - is(link_count($filename), undef, "scrips ok"); + # only one scrip run (on second ticket) since this is on a ticket Create txn + is(link_count($filename), 1, "scrips ok"); RT->Config->Set( StrictLinkACL => 1 ); } @@ -144,7 +141,7 @@ diag('try to add link without rights'); ok($id,$msg); ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id); ok(!$id, $msg); - is(link_count($filename), undef, "scrips ok"); + is(link_count($filename), 0, "scrips ok"); $child->CurrentUser( RT->SystemUser ); is($child->_Links('Base')->Count, 0, 'link was not created, no permissions'); is($child->_Links('Target')->Count, 0, 'link was not create, no permissions'); @@ -162,7 +159,7 @@ diag('add link with rights only on base'); ok($id,$msg); ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id); ok($id, $msg); - is(link_count($filename), 1, "scrips ok"); + is(link_count($filename), 2, "scrips ok"); $child->CurrentUser( RT->SystemUser ); is($child->_Links('Base')->Count, 1, 'link was created'); is($child->_Links('Target')->Count, 0, 'link was created only one'); @@ -170,9 +167,9 @@ diag('add link with rights only on base'); # turn off feature and try to delete link, we should fail RT->Config->Set( StrictLinkACL => 1 ); - ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id); + ($id, $msg) = $child->DeleteLink(Type => 'MemberOf', Target => $parent->id); ok(!$id, $msg); - is(link_count($filename), 1, "scrips ok"); + is(link_count($filename), 0, "scrips ok"); $child->CurrentUser( RT->SystemUser ); $child->_Links('Base')->_DoCount; is($child->_Links('Base')->Count, 1, 'link was not deleted'); @@ -182,7 +179,7 @@ diag('add link with rights only on base'); RT->Config->Set( StrictLinkACL => 0 ); ($id, $msg) = $child->DeleteLink(Type => 'MemberOf', Target => $parent->id); ok($id, $msg); - is(link_count($filename), 0, "scrips ok"); + is(link_count($filename), -2, "scrips ok"); $child->CurrentUser( RT->SystemUser ); $child->_Links('Base')->_DoCount; is($child->_Links('Base')->Count, 0, 'link was deleted'); @@ -217,18 +214,17 @@ ok ($id,$msg); ok ($id,$msg); ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id); ok($id,$msg); -is(link_count($filename), 1, "scrips ok"); +is(link_count($filename), 2, "scrips ok"); warnings_like { ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => -1); } [ qr/Could not determine a URI scheme for -1/, - qr/Couldn't resolve '-1' into a URI/, ]; ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id); ok($id,$msg); -is(link_count($filename), 1, "scrips ok"); +is(link_count($filename), 0, "scrips ok"); # already added my $transactions = $ticket2->Transactions; $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' ); @@ -238,21 +234,19 @@ is( $transactions->First->NewValue , $ticket->URI ); ($id,$msg) = $ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id); ok($id,$msg); -is(link_count($filename), 0, "scrips ok"); +is(link_count($filename), -2, "scrips ok"); $transactions = $ticket2->Transactions; $transactions->Limit( FIELD => 'Type', VALUE => 'DeleteLink' ); is( $transactions->Count, 1, "Transaction found in other ticket" ); is( $transactions->First->Field , 'ReferredToBy'); is( $transactions->First->OldValue , $ticket->URI ); -RT->Config->Set( LinkTransactionsRun1Scrip => 0 ); - ($id,$msg) =$ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id); ok($id,$msg); is(link_count($filename), 2, "scrips ok"); ($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id); ok($id,$msg); -is(link_count($filename), 0, "scrips ok"); +is(link_count($filename), -2, "scrips ok"); # tests for silent behaviour ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id, Silent => 1); @@ -287,7 +281,7 @@ is(link_count($filename), 1, "scrips ok"); } ($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id, SilentBase => 1); ok($id,$msg); -is(link_count($filename), 0, "scrips ok"); +is(link_count($filename), -1, "scrips ok"); ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id, SilentTarget => 1); ok($id,$msg); @@ -303,11 +297,10 @@ is(link_count($filename), 1, "scrips ok"); } ($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id, SilentTarget => 1); ok($id,$msg); -is(link_count($filename), 0, "scrips ok"); +is(link_count($filename), -1, "scrips ok"); # restore -RT->Config->Set( LinkTransactionsRun1Scrip => $link_scrips_orig ); RT->Config->Set( StrictLinkACL => $link_acl_checks_orig ); { @@ -388,8 +381,9 @@ sub link_count { open( my $fh, '<', $file ) or die "couldn't open $file"; my $data = <$fh>; close $fh; + truncate($file, 0); - return undef unless $data; + return 0 unless defined $data; chomp $data; return $data + 0; } diff --git a/rt/t/ticket/merge.t b/rt/t/ticket/merge.t index 2484b6512..99c723b8a 100644 --- a/rt/t/ticket/merge.t +++ b/rt/t/ticket/merge.t @@ -4,7 +4,7 @@ use warnings; use RT; -use RT::Test tests => '29'; +use RT::Test tests => '44'; # validate that when merging two tickets, the comments from both tickets @@ -134,3 +134,46 @@ ok $user && $user->id, 'loaded or created user'; ($status,$msg) = $t->MergeInto($t2->id); ok($status, "Merged tickets: $msg"); } + +# check Time* fields after merge +{ + my @tickets; + my @values = ( + { Worked => 11, Estimated => 17, Left => 6 }, + { Worked => 7, Estimated => 12, Left => 5 }, + ); + + for my $i (0 .. 1) { + my $t = RT::Ticket->new(RT->SystemUser); + $t->Create( Queue => 'general'); + ok ($t->id); + push @tickets, $t; + + foreach my $field ( keys %{ $values[ $i ] } ) { + my $method = "SetTime$field"; + my ($status, $msg) = $t->$method( $values[ $i ]{ $field } ); + ok $status, "changed $field on the ticket" + or diag "error: $msg"; + } + } + + my ($status, $msg) = $tickets[1]->MergeInto($tickets[0]->id); + ok($status,$msg); + + my $t = RT::Ticket->new(RT->SystemUser); + $t->Load( $tickets[0]->id ); + foreach my $field ( keys %{ $values[0] } ) { + my $method = "Time$field"; + my $expected = 0; + $expected += $_->{ $field } foreach @values; + is $t->$method, $expected, "correct value"; + + my $from_history = 0; + my $txns = $t->Transactions; + while ( my $txn = $txns->Next ) { + next unless $txn->Type eq 'Set' && $txn->Field eq $method; + $from_history += $txn->NewValue - $txn->OldValue; + } + is $from_history, $expected, "history is correct"; + } +} diff --git a/rt/t/ticket/scrips_batch.t b/rt/t/ticket/scrips_batch.t index 44d7f8e34..0a996ce45 100644 --- a/rt/t/ticket/scrips_batch.t +++ b/rt/t/ticket/scrips_batch.t @@ -16,28 +16,33 @@ ok $m->login, 'logged in as root'; my $sid; { - $m->follow_link_ok( { id => 'tools-config-queues' } ); + $m->follow_link_ok( { id => 'admin-queues' } ); $m->follow_link_ok( { text => $queue->Name } ); $m->follow_link_ok( { id => 'page-scrips-create'}); - $m->form_name('ModifyScrip'); - $m->field('Scrip-new-Description' => 'test'); - $m->select('Scrip-new-ScripCondition' => 'On Transaction'); - $m->select('Scrip-new-ScripAction' => 'User Defined'); - $m->select('Scrip-new-Template' => 'Global template: Blank'); - $m->select('Scrip-new-Stage' => 'TransactionBatch'); - $m->field('Scrip-new-CustomPrepareCode' => 'return 1;'); - $m->field('Scrip-new-CustomCommitCode' => 'return 1;'); - $m->submit; - $m->content_contains("Scrip Created"); + $m->form_name('CreateScrip'); + $m->field('Description' => 'test'); + $m->select('ScripCondition' => 'On Transaction'); + $m->select('ScripAction' => 'User Defined'); + $m->select('Template' => 'Blank'); + $m->select('Stage' => 'Batch'); + $m->field('CustomPrepareCode' => 'return 1;'); + $m->field('CustomCommitCode' => 'return 1;'); + $m->click('Create'); + $m->content_contains("Scrip Created"); my $form = $m->form_name('ModifyScrip'); $sid = $form->value('id'); - is $m->value("Scrip-$sid-Description"), 'test', 'correct description'; - is value_name($form, "Scrip-$sid-ScripCondition"), 'On Transaction', 'correct condition'; - is value_name($form, "Scrip-$sid-ScripAction"), 'User Defined', 'correct action'; - is value_name($form, "Scrip-$sid-Template"), 'Global template: Blank', 'correct template'; - is value_name($form, "Scrip-$sid-Stage"), 'TransactionBatch', 'correct stage'; + is $m->value("Description"), 'test', 'correct description'; + is value_name($form, "ScripCondition"), 'On Transaction', 'correct condition'; + is value_name($form, "ScripAction"), 'User Defined', 'correct action'; + is value_name($form, "Template"), 'Blank', 'correct template'; + + { + my $rec = RT::ObjectScrip->new( RT->SystemUser ); + $rec->LoadByCols( Scrip => $sid, ObjectId => $queue->id ); + is $rec->Stage, 'TransactionBatch', "correct stage"; + } my $tmp_fn = File::Spec->catfile( RT::Test->temp_directory, 'transactions' ); open my $tmp_fh, '+>', $tmp_fn or die $!; @@ -56,8 +61,8 @@ foreach my \$txn ( \@\$batch ) { return 1; END - $m->field( "Scrip-$sid-CustomCommitCode" => $code ); - $m->submit; + $m->field( "CustomCommitCode" => $code ); + $m->click('Update'); $m->goto_create_ticket( $queue ); $m->form_name('TicketCreate'); diff --git a/rt/t/ticket/search.t b/rt/t/ticket/search.t index 852241fe5..a43433f06 100644 --- a/rt/t/ticket/search.t +++ b/rt/t/ticket/search.t @@ -283,4 +283,35 @@ like($tix->BuildSelectCountQuery, qr/\bNULL\b/, "Contains upper-case NULL"); unlike($tix->BuildSelectCountQuery, qr/\bnull\b/, "Lacks lower-case NULL"); +# tests for searching by queue lifecycle +$tix = RT::Tickets->new(RT->SystemUser); +$tix->FromSQL('Lifecycle="default"'); +is($tix->Count,7,"We found all 7 tickets in a queue with the default lifecycle"); + +$tix = RT::Tickets->new(RT->SystemUser); +$tix->FromSQL('Lifecycle ="approvals" OR Lifecycle="default"'); +is($tix->Count,7,"We found 7 tickets in a queue with a lifecycle of default or approvals"); + +$tix = RT::Tickets->new(RT->SystemUser); +$tix->FromSQL('Lifecycle ="approvals" AND Lifecycle="default"'); +is($tix->Count,0,"We found 0 tickets in a queue with a lifecycle of default AND approvals...(because that's impossible"); + +$tix = RT::Tickets->new(RT->SystemUser); +$tix->FromSQL('Queue="'.$queue.'" AND Lifecycle="default"'); +is($tix->Count,7,"We found 7 tickets in $queue with a lifecycle of default"); + + +$tix = RT::Tickets->new(RT->SystemUser); +$tix->FromSQL('Lifecycle !="approvals"'); +is($tix->Count,7,"We found 7 tickets in a queue with a lifecycle other than approvals"); + +$tix = RT::Tickets->new(RT->SystemUser); +$tix->FromSQL('Lifecycle!="default"'); +is($tix->Count,0,"We found 0 tickets in a queue with a lifecycle other than default"); + +$tix = RT::Tickets->new(RT->SystemUser); +$tix->FromSQL('Lifecycle="approvals"'); +is($tix->Count,0,"We found 0 tickets in a queue with the approvals lifecycle"); + + done_testing; diff --git a/rt/t/ticket/search_by_cf_freeform_multiple.t b/rt/t/ticket/search_by_cf_freeform_multiple.t index 1b4f0922c..1324abd66 100644 --- a/rt/t/ticket/search_by_cf_freeform_multiple.t +++ b/rt/t/ticket/search_by_cf_freeform_multiple.t @@ -2,8 +2,7 @@ use strict; use warnings; -use RT::Test nodata => 1, tests => 118; -use RT::Ticket; +use RT::Test nodata => 1, tests => undef; my $q = RT::Test->load_or_create_queue( Name => 'Regression' ); ok $q && $q->id, 'loaded or created queue'; @@ -22,104 +21,136 @@ my ($cf_name, $cf_id, $cf) = ("Test", 0, undef); $cf_id = $cf->id; } -my ($total, @data, @tickets, %test) = (0, ()); +my $ylong = "y" x 300; +subtest "Creating tickets" => sub { + RT::Test->create_tickets( { Queue => $q->id }, + { Subject => '-' }, + { Subject => 'x', "CustomField-$cf_id" => 'x', }, + { Subject => 'y', "CustomField-$cf_id" => 'y', }, + { Subject => 'z', "CustomField-$cf_id" => 'z', }, + { Subject => 'xy', "CustomField-$cf_id" => [ 'x', 'y' ], }, + { Subject => 'xz', "CustomField-$cf_id" => [ 'x', 'z' ], }, + { Subject => 'yz', "CustomField-$cf_id" => [ 'y', 'z' ], }, + { Subject => 'x_ylong', "CustomField-$cf_id" => [ 'x', $ylong ], }, + { Subject => 'ylong', "CustomField-$cf_id" => $ylong, }, + ); +}; + +my @tests = ( + "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "CF.{$cf_id}.Content IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 }, + "CF.{$cf_id}.LargeContent IS NULL" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 }, + "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "'CF.{$cf_name}.Content' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 }, + "'CF.{$cf_name}.LargeContent' IS NULL" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 }, + "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + + "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + "CF.{$cf_id}.Content IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 }, + "CF.{$cf_id}.LargeContent IS NOT NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 }, + "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + "'CF.{$cf_name}.Content' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 }, + "'CF.{$cf_name}.LargeContent' IS NOT NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 }, + "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + + "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "CF.{$cf_id}.Content = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "CF.{$cf_id}.LargeContent = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "CF.{$cf_id} = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 }, + "CF.{$cf_id}.Content = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "CF.{$cf_id}.LargeContent = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 }, + "CF.{$cf_id} LIKE 'yyy'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 }, + "CF.{$cf_id}.Content LIKE 'yyy'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "CF.{$cf_id}.LargeContent LIKE 'yyy'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 }, + "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + + "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 }, + "CF.{$cf_id}.Content != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 }, + "CF.{$cf_id}.LargeContent != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + "CF.{$cf_id} != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 }, + "CF.{$cf_id}.Content != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + "CF.{$cf_id}.LargeContent != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 }, +"TODO: CF.{$cf_id} NOT LIKE 'yyy'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 }, + "CF.{$cf_id}.Content NOT LIKE 'yyy'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + "CF.{$cf_id}.LargeContent NOT LIKE 'yyy'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 }, + "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 }, + "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 }, + "'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 }, + "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + + "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 }, + "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 }, + "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 }, + "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 }, +); +run_tests(@tests); + sub run_tests { - my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; - foreach my $key ( sort keys %test ) { - my $tix = RT::Tickets->new(RT->SystemUser); - $tix->FromSQL( "( $query_prefix ) AND ( $key )" ); - - my $error = 0; - - my $count = 0; - $count++ foreach grep $_, values %{ $test{$key} }; - is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1; - - my $good_tickets = ($tix->Count == $count); - while ( my $ticket = $tix->Next ) { - next if $test{$key}->{ $ticket->Subject }; - diag $ticket->Subject ." ticket has been found when it's not expected"; - $good_tickets = 0; - } - ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1; - - diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error; + my @tests = @_; + while (@tests) { + my $query = shift @tests; + my %results = %{ shift @tests }; + local $TODO = "Not implemented correctly" if $query =~ s/^TODO:\s*//; + subtest $query => sub { + my $tix = RT::Tickets->new(RT->SystemUser); + $tix->FromSQL( "$query" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %results; + is($tix->Count, $count, "found correct number of ticket(s)") or $error = 1; + + my $good_tickets = ($tix->Count == $count); + while ( my $ticket = $tix->Next ) { + next if $results{ $ticket->Subject }; + diag $ticket->Subject ." ticket has been found when it's not expected"; + $good_tickets = 0; + } + ok( $good_tickets, "all tickets are good" ) or $error = 1; + + diag "Wrong SQL: ". $tix->BuildSelectQuery if $error; + }; } } -@data = ( - { Subject => '-' }, - { Subject => 'x', "CustomField-$cf_id" => 'x', }, - { Subject => 'y', "CustomField-$cf_id" => 'y', }, - { Subject => 'z', "CustomField-$cf_id" => 'z', }, - { Subject => 'xy', "CustomField-$cf_id" => [ 'x', 'y' ], }, - { Subject => 'xz', "CustomField-$cf_id" => [ 'x', 'z' ], }, - { Subject => 'yz', "CustomField-$cf_id" => [ 'y', 'z' ], }, -); -%test = ( - "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, - "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, - "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, - "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, - - "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, - "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, - "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, - "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, - - "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - - "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 }, - "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 }, - "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 }, - "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 }, - - "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 }, - "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 }, - "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 }, - "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 }, - - "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 }, - "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 }, - "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 }, - "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 }, - - "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 }, - "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 }, - "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 }, - "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 }, - - "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, - "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, - "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, - "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 }, - - "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - - "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 }, - - "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, - "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, - "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, - "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 }, -); -@tickets = RT::Test->create_tickets( { Queue => $q->id }, @data); -$total = scalar @tickets; - -{ - my $tix = RT::Tickets->new(RT->SystemUser); - $tix->FromSQL("Queue = '$queue'"); - is($tix->Count, $total, "found $total tickets"); -} -run_tests(); +done_testing; diff --git a/rt/t/ticket/search_by_cf_freeform_single.t b/rt/t/ticket/search_by_cf_freeform_single.t index f8462a9af..0463412ca 100644 --- a/rt/t/ticket/search_by_cf_freeform_single.t +++ b/rt/t/ticket/search_by_cf_freeform_single.t @@ -2,8 +2,7 @@ use strict; use warnings; -use RT::Test nodata => 1, tests => 106; -use RT::Ticket; +use RT::Test nodata => 1, tests => undef; my $q = RT::Test->load_or_create_queue( Name => 'Regression' ); ok $q && $q->id, 'loaded or created queue'; @@ -22,103 +21,143 @@ my ($cf_name, $cf_id, $cf) = ("Test", 0, undef); $cf_id = $cf->id; } -my ($total, @data, @tickets, %test) = (0, ()); +my $other_q = RT::Test->load_or_create_queue( Name => 'Other' ); +ok $other_q && $other_q->id, 'loaded or created queue'; + +my $ylong = 'y' x 300; +subtest "Creating tickets" => sub { + RT::Test->create_tickets( { Queue => $q->id }, + { Subject => '-' }, + { Subject => "other", Queue => $other_q->id }, + { Subject => 'x', "CustomField-$cf_id" => 'x', }, + { Subject => 'y', "CustomField-$cf_id" => 'y', }, + { Subject => 'z', "CustomField-$cf_id" => 'z', }, + { Subject => 'ylong', "CustomField-$cf_id" => $ylong, }, + ); +}; + +my @tests = ( + "CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 }, + "CF.{$cf_id}.Content IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 1 }, + "CF.{$cf_id}.LargeContent IS NULL" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 }, + "'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 }, + "'CF.{$cf_name}.Content' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 1 }, + "'CF.{$cf_name}.LargeContent' IS NULL" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 }, + "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 }, + + "CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 }, + "CF.{$cf_id}.Content IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 0 }, + "CF.{$cf_id}.LargeContent IS NOT NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 }, + "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 }, + "'CF.{$cf_name}.Content' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 0 }, + "'CF.{$cf_name}.LargeContent' IS NOT NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 }, + "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 }, + + "CF.{$cf_id} = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "CF.{$cf_id}.Content = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "CF.{$cf_id}.LargeContent = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "CF.{$cf_id} = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 }, + "CF.{$cf_id}.Content = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "CF.{$cf_id}.LargeContent = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 }, + "CF.{$cf_id} LIKE 'yyy'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 }, + "CF.{$cf_id}.Content LIKE 'yyy'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "CF.{$cf_id}.LargeContent LIKE 'yyy'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 }, + "'CF.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + + "CF.{$cf_id} != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 }, + "CF.{$cf_id}.Content != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 }, + "CF.{$cf_id}.LargeContent != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 }, + "CF.{$cf_id} != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 }, + "CF.{$cf_id}.Content != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 }, + "CF.{$cf_id}.LargeContent != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 }, + "CF.{$cf_id} NOT LIKE 'yyy'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 }, + "CF.{$cf_id}.Content NOT LIKE 'yyy'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 }, + "CF.{$cf_id}.LargeContent NOT LIKE 'yyy'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 }, + "'CF.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 }, + "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 }, + "'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_id}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_id}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + + "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 }, + "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 }, + + "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 }, + + "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 }, + "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 }, + "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 }, +); +run_tests(@tests); + sub run_tests { - my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; - foreach my $key ( sort keys %test ) { - my $tix = RT::Tickets->new(RT->SystemUser); - $tix->FromSQL( "( $query_prefix ) AND ( $key )" ); - - my $error = 0; - - my $count = 0; - $count++ foreach grep $_, values %{ $test{$key} }; - is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1; - - my $good_tickets = ($tix->Count == $count); - while ( my $ticket = $tix->Next ) { - next if $test{$key}->{ $ticket->Subject }; - diag $ticket->Subject ." ticket has been found when it's not expected"; - $good_tickets = 0; - } - ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1; - - diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error; + my @tests = @_; + while (@tests) { + my $query = shift @tests; + my %results = %{ shift @tests }; + subtest $query => sub { + my $tix = RT::Tickets->new(RT->SystemUser); + $tix->FromSQL( "$query" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %results; + is($tix->Count, $count, "found correct number of ticket(s)") or $error = 1; + + my $good_tickets = ($tix->Count == $count); + while ( my $ticket = $tix->Next ) { + next if $results{ $ticket->Subject }; + diag $ticket->Subject ." ticket has been found when it's not expected"; + $good_tickets = 0; + } + ok( $good_tickets, "all tickets are good" ) or $error = 1; + + diag "Wrong SQL: ". $tix->BuildSelectQuery if $error; + }; } } -@data = ( - { Subject => '-' }, - { Subject => 'x', "CustomField-$cf_id" => 'x', }, - { Subject => 'y', "CustomField-$cf_id" => 'y', }, - { Subject => 'z', "CustomField-$cf_id" => 'z', }, -); -%test = ( - "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 }, - "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 }, - "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 }, - "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 }, - - "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, - "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, - "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, - "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, - - "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 }, - "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 }, - "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 }, - "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 }, - - "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 }, - "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 }, - "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 }, - "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 }, - - "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 }, - "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 }, - "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 }, - "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 }, - - "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 }, - "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 }, - "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 }, - "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 }, - - "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 }, - "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 }, - "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 }, - "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 }, - - "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 }, - "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 }, - "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 }, - "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 }, - - "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 }, - "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 }, - "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 }, - "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 }, - - "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 }, - "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 }, - "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 }, - "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 }, - - "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, - "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, - "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, - "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 }, - -); -@tickets = RT::Test->create_tickets( { Queue => $q->id }, @data); -$total = scalar @tickets; -{ - my $tix = RT::Tickets->new(RT->SystemUser); - $tix->FromSQL("Queue = '$queue'"); - is($tix->Count, $total, "found $total tickets"); -} -run_tests(); - -@tickets = (); +done_testing; diff --git a/rt/t/ticket/search_by_watcher_group.t b/rt/t/ticket/search_by_watcher_group.t new file mode 100644 index 000000000..9c522d5d0 --- /dev/null +++ b/rt/t/ticket/search_by_watcher_group.t @@ -0,0 +1,75 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +use Test::Warn; + +my $q = RT::Test->load_or_create_queue( Name => 'General' ); +ok $q && $q->id, 'loaded or created queue'; +my $queue = $q->Name; + + +my $group; +{ + $group = RT::Group->new( RT->SystemUser ); + my ($id, $msg) = $group->CreateUserDefinedGroup( Name => 'Test' ); + ok $id, "$msg"; +} + +my $root = RT::Test->load_or_create_user( Name => 'root', MemberOf => $group->id ); +ok $root && $root->id; + +RT::Test->create_tickets( + { Queue => $q, }, + { Subject => '-', }, + { Subject => 'o', Owner => $root->id }, + { Subject => 'r', Requestor => $root->id }, + { Subject => 'c', Cc => $root->id }, + { Subject => 'a', AdminCc => $root->id }, +); + +run_tests( + 'OwnerGroup = "Test"' => { '-' => 0, o => 1, r => 0, c => 0, a => 0 }, + 'RequestorGroup = "Test"' => { '-' => 0, o => 0, r => 1, c => 0, a => 0 }, + 'CCGroup = "Test"' => { '-' => 0, o => 0, r => 0, c => 1, a => 0 }, + 'AdminCCGroup = "Test"' => { '-' => 0, o => 0, r => 0, c => 0, a => 1 }, + 'WatcherGroup = "Test"' => { '-' => 0, o => 1, r => 1, c => 1, a => 1 }, +); + +warning_like { + my $tickets = RT::Tickets->new( RT->SystemUser ); + my ($status, $msg) = $tickets->FromSQL('OwnerGroup != "Test"'); + ok !$status, "incorrect op: $msg"; +} qr{Invalid OwnerGroup Op: !=}; + +done_testing(); + +sub run_tests { + my @test = @_; + while ( my ($query, $checks) = splice @test, 0, 2 ) { + run_test( $query, %$checks ); + } +} + +sub run_test { + my ($query, %checks) = @_; + + my $tix = RT::Tickets->new(RT->SystemUser); + $tix->FromSQL($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; +} diff --git a/rt/t/ticket/googleish_search.t b/rt/t/ticket/simple_search.t index d372801bc..02bf7334d 100644 --- a/rt/t/ticket/googleish_search.t +++ b/rt/t/ticket/simple_search.t @@ -20,12 +20,12 @@ my ( $id, undef, $msg ) = $t1->Create( ); ok( $id, $msg ); -use_ok("RT::Search::Googleish"); +use_ok("RT::Search::Simple"); my $active_statuses = join( " OR ", map "Status = '$_'", RT::Queue->ActiveStatusArray()); my $tickets = RT::Tickets->new(RT->SystemUser); -my $quick = RT::Search::Googleish->new(Argument => "", +my $quick = RT::Search::Simple->new(Argument => "", TicketsObj => $tickets); my @tests = ( "General new open root" => "( Owner = 'root' ) AND ( Queue = 'General' ) AND ( Status = 'new' OR Status = 'open' )", @@ -34,8 +34,8 @@ my @tests = ( "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' )"); + "notauser $queue" => "( Subject LIKE 'notauser' ) AND ( Queue = '$queue' ) AND ( $active_statuses )", + "notauser $queue root" => "( Subject LIKE 'notauser' ) AND ( Owner = 'root' ) AND ( Queue = '$queue' ) AND ( $active_statuses )"); while (my ($from, $to) = splice @tests, 0, 2) { is($quick->QueryToSQL($from), $to, "<$from> -> <$to>"); diff --git a/rt/t/ticket/time-worked.t b/rt/t/ticket/time-worked.t new file mode 100644 index 000000000..3e8724221 --- /dev/null +++ b/rt/t/ticket/time-worked.t @@ -0,0 +1,80 @@ +use strict; +use warnings; + +use RT::Test tests => 27; + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok $queue && $queue->id, "loaded or created a queue"; + +note 'set on Create'; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue->id, TimeWorked => 10, + ); + is $ticket->TimeWorked, 10, 'correct value'; + + my $txn = RT::Transaction->new( RT->SystemUser ); + $txn->LoadByCols( + ObjectType => 'RT::Ticket', ObjectId => $ticket->id, + Type => 'Create', + ); + ok $txn->id, 'found transaction'; + is $txn->TimeTaken, 10, 'correct value'; +} + +note 'set on Comment'; +{ + my $ticket = RT::Test->create_ticket( Queue => $queue->id ); + ok !$ticket->TimeWorked, 'correct value'; + $ticket->Comment( Content => 'test', TimeTaken => 10 ); + is $ticket->TimeWorked, 10, 'correct value'; + + my $txn = RT::Transaction->new( RT->SystemUser ); + $txn->LoadByCols( + ObjectType => 'RT::Ticket', ObjectId => $ticket->id, + Type => 'Comment', + ); + ok $txn->id, 'found transaction'; + is $txn->TimeTaken, 10, 'correct value'; +} + +note 'update'; +{ + my $ticket = RT::Test->create_ticket( Queue => $queue->id ); + ok !$ticket->TimeWorked, 'correct value'; + $ticket->SetTimeWorked( 10 ); + is $ticket->TimeWorked, 10, 'correct value'; + + my $txn = RT::Transaction->new( RT->SystemUser ); + $txn->LoadByCols( + ObjectType => 'RT::Ticket', ObjectId => $ticket->id, + Type => 'Set', Field => 'TimeWorked', + ); + ok $txn->id, 'found transaction'; + is $txn->TimeTaken, 10, 'correct value'; +} + +note 'on Merge'; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue->id, TimeWorked => 7, + ); + { + my $tmp = RT::Test->create_ticket( + Queue => $queue->id, TimeWorked => 13, + ); + my ($status, $msg) = $tmp->MergeInto( $ticket->id ); + ok $status, "merged tickets"; + } + $ticket->Load( $ticket->id ); + is $ticket->TimeWorked, 20, 'correct value'; +} + +sub dump_txns { + my $ticket = shift; + my $txns = $ticket->Transactions; + while ( my $txn = $txns->Next ) { + diag sprintf "#%d\t%s\t%s\t%d", map $txn->$_() // '', qw(id Type Field TimeTaken); + } +} + diff --git a/rt/t/validator/group_members.t b/rt/t/validator/group_members.t index af93c518e..0fd1a749a 100644 --- a/rt/t/validator/group_members.t +++ b/rt/t/validator/group_members.t @@ -2,19 +2,15 @@ use strict; use warnings; -use RT::Test tests => 63; +use RT::Test tests => undef; -{ - my ($ecode, $res) = RT::Test->run_validator(); - is $res, '', 'empty result'; -} +RT::Test->db_is_valid; { my $group = RT::Test->load_or_create_group('test', Members => [] ); ok $group, "loaded or created a group"; - my ($ecode, $res) = RT::Test->run_validator(); - is $res, '', 'empty result'; + RT::Test->db_is_valid; } # G1 -> G2 @@ -28,20 +24,19 @@ use RT::Test tests => 63; ok $group2->HasMember( $group1->id ), "has member"; ok $group2->HasMemberRecursively( $group1->id ), "has member"; - my ($ecode, $res) = RT::Test->run_validator(); - is $res, '', 'empty result'; + RT::Test->db_is_valid; $RT::Handle->dbh->do("DELETE FROM CachedGroupMembers"); DBIx::SearchBuilder::Record::Cachable->FlushCache; ok !$group2->HasMemberRecursively( $group1->id ), "has no member, broken DB"; - ($ecode, $res) = RT::Test->run_validator(resolve => 1); + my ($ecode, $res) = RT::Test->run_validator(resolve => 1); + isnt($ecode, 0, 'non-zero exit code'); ok $group2->HasMember( $group1->id ), "has member"; ok $group2->HasMemberRecursively( $group1->id ), "has member"; - ($ecode, $res) = RT::Test->run_validator(); - is $res, '', 'empty result'; + RT::Test->db_is_valid; } # G1 <- G2 <- G3 <- G4 <- G5 @@ -61,15 +56,15 @@ use RT::Test tests => 63; push @groups, $group; } - my ($ecode, $res) = RT::Test->run_validator(); - is $res, '', 'empty result'; + RT::Test->db_is_valid; $RT::Handle->dbh->do("DELETE FROM CachedGroupMembers"); DBIx::SearchBuilder::Record::Cachable->FlushCache; ok !$groups[1]->HasMemberRecursively( $groups[0]->id ), "has no member, broken DB"; - ($ecode, $res) = RT::Test->run_validator(resolve => 1); + my ($ecode, $res) = RT::Test->run_validator(resolve => 1); + isnt($ecode, 0, 'non-zero exit code'); for ( my $i = 1; $i < @groups; $i++ ) { ok $groups[$i]->HasMember( $groups[$i-1]->id ), "has member"; @@ -77,8 +72,7 @@ use RT::Test tests => 63; foreach 0..$i-1; } - ($ecode, $res) = RT::Test->run_validator(); - is $res, '', 'empty result'; + RT::Test->db_is_valid; } # G1 <- (G2, G3, G4, G5) @@ -93,8 +87,7 @@ use RT::Test tests => 63; my $parent = RT::Test->load_or_create_group( 'test1', Members => \@groups ); ok $parent, "loaded or created a group"; - my ($ecode, $res) = RT::Test->run_validator(); - is $res, '', 'empty result'; + RT::Test->db_is_valid; } # G1 <- (G2, G3, G4) <- G5 @@ -112,8 +105,7 @@ use RT::Test tests => 63; my $parent = RT::Test->load_or_create_group( 'test1', Members => \@groups ); ok $parent, "loaded or created a group"; - my ($ecode, $res) = RT::Test->run_validator(); - is $res, '', 'empty result'; + RT::Test->db_is_valid; } # group without principal record and cgm records @@ -128,8 +120,9 @@ use RT::Test tests => 63; DBIx::SearchBuilder::Record::Cachable->FlushCache; my ($ecode, $res) = RT::Test->run_validator(resolve => 1, timeout => 30); - ok $res; + isnt($ecode, 0, 'non-zero exit code'); - ($ecode, $res) = RT::Test->run_validator(); - is $res, '', 'empty result'; + RT::Test->db_is_valid; } + +done_testing; diff --git a/rt/t/web/admin_queue_lifecycle.t b/rt/t/web/admin_queue_lifecycle.t index 295e9ea57..6b8401283 100644 --- a/rt/t/web/admin_queue_lifecycle.t +++ b/rt/t/web/admin_queue_lifecycle.t @@ -24,7 +24,7 @@ my $lifecycle_input = $form->find_input('Lifecycle'); is( $lifecycle_input->value, 'default', 'default lifecycle' ); my @lifecycles = sort $lifecycle_input->possible_values; -is_deeply( \@lifecycles, [qw/approvals default foo/], 'found all lifecycles' ); +is_deeply( \@lifecycles, [qw/default foo/], 'found all lifecycles' ); $m->submit_form(); $m->content_lacks( 'Lifecycle changed from', diff --git a/rt/t/web/admin_user.t b/rt/t/web/admin_user.t index 36b9af1b4..dc984eb75 100644 --- a/rt/t/web/admin_user.t +++ b/rt/t/web/admin_user.t @@ -27,9 +27,9 @@ 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'); -diag "test gnupg page" if $ENV{TEST_VERBOSE}; -$m->follow_link_ok( { text => 'GnuPG' } ); -$m->content_contains('GnuPG public key'); +diag "test keys page" if $ENV{TEST_VERBOSE}; +$m->follow_link_ok( { text => 'Private keys' } ); +$m->content_contains('Public key(s) for rt-test@example.com'); $m->content_contains('The key is ultimately trusted'); $m->content_contains('F0CB3B482CFA485680A4A0BDD328035D84881F1B'); $m->content_contains('Tue Aug 07 2007'); diff --git a/rt/t/web/articles-links.t b/rt/t/web/articles-links.t index eb6de51b3..4aa8f9133 100644 --- a/rt/t/web/articles-links.t +++ b/rt/t/web/articles-links.t @@ -44,7 +44,7 @@ $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'); +$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'); diff --git a/rt/t/web/attachment_dropping.t b/rt/t/web/attachment_dropping.t new file mode 100644 index 000000000..466f7a0f0 --- /dev/null +++ b/rt/t/web/attachment_dropping.t @@ -0,0 +1,52 @@ +use warnings; +use strict; + +use RT::Test tests => undef; +use File::Temp 'tempfile'; + +my $content = 'a' x 1000 . 'b' x 10; +my ( $fh, $path ) = tempfile( UNLINK => 1, SUFFIX => '.txt' ); +print $fh $content; +close $fh; + +my $name = ( File::Spec->splitpath($path) )[2]; + +RT->Config->Set( 'WebSessionClass', "Apache::Session::File"); +RT->Config->Set( 'MaxAttachmentSize', 1000 ); +RT->Config->Set( 'TruncateLongAttachments', '0' ); +RT->Config->Set( 'DropLongAttachments', '1' ); + +my $cf = RT::CustomField->new( RT->SystemUser ); +ok( + $cf->Create( + Name => 'test truncation', + Queue => '0', + Type => 'FreeformSingle', + ), +); +my $cfid = $cf->id; + +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 General queue" ); +$m->get_ok( $baseurl . '/Ticket/Create.html?Queue=' . $queue->id ); +$m->content_contains( "Create a new ticket", 'ticket create page' ); + +$m->form_name('TicketCreate'); +$m->field( 'Subject', 'Attachments dropping test' ); +$m->field( 'Attach', $path ); +$m->field( 'Content', 'Some content' ); +my $cf_content = 'cf' . 'a' x 998 . 'cfb'; +$m->field( "Object-RT::Ticket--CustomField-$cfid-Value", $cf_content ); +$m->submit; +is( $m->status, 200, "request successful" ); + +$m->content_contains( "File '$name' dropped because its size (1010 bytes) exceeded configured maximum size setting (1000 bytes).", 'dropped message' ); +$m->content_lacks( 'cfaaaa', 'cf value was dropped' ); +$m->follow_link_ok( { text => "Download $name" } ); +is( $m->content, 'Large attachment dropped', 'dropped $name' ); + +undef $m; +done_testing; diff --git a/rt/t/web/attachment_encoding.t b/rt/t/web/attachment_encoding.t index f49720e0f..3f7d6d1cf 100644 --- a/rt/t/web/attachment_encoding.t +++ b/rt/t/web/attachment_encoding.t @@ -86,7 +86,7 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; '-> /Ticket/Attachment/...' ); $m->content_contains( $filename, "has file content $filename" ); - ( $id ) = $m->uri =~ /(\d+)\D+$/; + ( $id ) = $m->uri =~ m{/(\d+)/[^/]+$}; ok( $id, 'found attachment id' ); $attachment = RT::Attachment->new( $RT::SystemUser ); ok($attachment->Load($id), "load att $id"); diff --git a/rt/t/web/attachment_truncation.t b/rt/t/web/attachment_truncation.t new file mode 100644 index 000000000..b60f29e90 --- /dev/null +++ b/rt/t/web/attachment_truncation.t @@ -0,0 +1,53 @@ +use warnings; +use strict; + +use RT::Test tests => undef; +use File::Temp 'tempfile'; + +my $content = 'a' x 1000 . 'b' x 10; +my ( $fh, $path ) = tempfile( UNLINK => 1, SUFFIX => '.txt' ); +print $fh $content; +close $fh; +my $name = ( File::Spec->splitpath($path) )[2]; + +RT->Config->Set( 'WebSessionClass', "Apache::Session::File"); +RT->Config->Set( 'MaxAttachmentSize', 1000 ); +RT->Config->Set( 'TruncateLongAttachments', '1' ); + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok( $queue->id, "Loaded General queue" ); + +my $cf = RT::CustomField->new( RT->SystemUser ); +ok( + $cf->Create( + Name => 'test truncation', + Queue => '0', + Type => 'FreeformSingle', + ), +); +my $cfid = $cf->id; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +$m->get_ok( $baseurl . '/Ticket/Create.html?Queue=' . $queue->id ); +$m->content_contains( "Create a new ticket", 'ticket create page' ); + +$m->form_name('TicketCreate'); +$m->field( 'Subject', 'Attachments test' ); +$m->field( 'Attach', $path ); +$m->field( 'Content', 'Some content' ); +my $cf_content = 'cf' . 'a' x 998 . 'cfb'; +$m->field( "Object-RT::Ticket--CustomField-$cfid-Value", $cf_content ); +$m->submit; +is( $m->status, 200, "request successful" ); + +$m->content_contains( "File '$name' truncated because its size (1010 bytes) exceeded configured maximum size setting (1000 bytes).", 'truncated message' ); +$m->content_contains( 'cf' . 'a' x 998, 'has the first 1000 cf chars' ); +$m->content_lacks( 'aaacfb', 'lacks cf chars after that' ); +$m->follow_link_ok( { text => "Download $name" } ); +$m->content_contains( 'a' x 1000, 'has the first 1000 chars' ); +$m->content_lacks( 'b', 'lacks chars after that' ); + +undef $m; +done_testing; diff --git a/rt/t/web/attachments.t b/rt/t/web/attachments.t index b518ec176..0ae407d7f 100644 --- a/rt/t/web/attachments.t +++ b/rt/t/web/attachments.t @@ -1,94 +1,506 @@ use strict; use warnings; -use RT::Test tests => 33; +use RT::Test tests => 159; -use constant LogoFile => $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png'; -use constant FaviconFile => $RT::MasonComponentRoot .'/NoAuth/images/favicon.png'; -use constant TextFile => $RT::MasonComponentRoot .'/NoAuth/css/print.css'; +use constant LogoFile => $RT::StaticPath .'/images/bpslogo.png'; +use constant FaviconFile => $RT::StaticPath .'/images/favicon.png'; +use constant TextFile => $RT::StaticPath .'/css/mobile.css'; -my ($baseurl, $m) = RT::Test->started_ok; +my ($url, $m) = RT::Test->started_ok; ok $m->login, 'logged in'; -my $queue = RT::Queue->new(RT->Nobody); -my $qid = $queue->Load('General'); -ok( $qid, "Loaded General queue" ); - -$m->form_name('CreateTicketInQueue'); -$m->field('Queue', $qid); -$m->submit; -is($m->status, 200, "request successful"); -$m->content_contains("Create a new ticket", 'ticket create page'); - -$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('Some content', 'and content'); -$m->content_contains('Download bpslogo.png', 'page has file name'); - -open LOGO, "<", LogoFile or die "Can't open logo file: $!"; -binmode LOGO; -my $logo_contents = do {local $/; <LOGO>}; -close LOGO; -$m->follow_link_ok({text => "Download bpslogo.png"}); -is($m->content_type, "image/png"); -is($m->content, $logo_contents, "Binary content matches"); - -$m->back; -$m->follow_link_ok({text => 'Reply'}, "reply to the ticket"); -$m->form_name('TicketUpdate'); -$m->field('Attach', TextFile); -$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('Download bpslogo.png', 'page has file name'); -$m->content_contains('Download favicon.png', 'page has file name'); -$m->content_contains('Download print.css', 'page has file name'); - -$m->follow_link_ok( { text => 'Download bpslogo.png' } ); -is( $m->response->header('Content-Type'), 'image/png', 'Content-Type of png lacks charset' ); - -$m->back; - -$m->follow_link_ok( { text => 'Download print.css' } ); -is( $m->response->header('Content-Type'), - 'text/css;charset=UTF-8', 'Content-Type of text has charset' ); - -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'); +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok( $queue && $queue->id, "Loaded General queue" ); + +diag "create a ticket in full interface"; +diag "w/o attachments"; +{ + $m->goto_create_ticket( $queue ); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->content_contains("Create a new ticket", 'ticket create page'); + $m->submit; + is($m->status, 200, "request successful"); +} + +diag "with one attachment"; +{ + $m->goto_create_ticket( $queue ); + + $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('Some content', 'and content'); + $m->content_contains('Download bpslogo.png', 'page has file name'); +} + +diag "with two attachments"; +{ + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->field('Attach', FaviconFile); + $m->field('Subject', 'Attachments test'); + $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('Some content', 'and content'); + $m->content_contains('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "with one attachment, but delete one along the way"; +{ + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->field('Attach', FaviconFile); + $m->tick( 'DeleteAttach', LogoFile ); + $m->field('Subject', 'Attachments test'); + $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('Some content', 'and content'); + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "with one attachment, but delete one along the way"; +{ + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->tick( 'DeleteAttach', LogoFile ); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->field('Attach', FaviconFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->field('Subject', 'Attachments test'); + $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('Some content', 'and content'); + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "reply to a ticket in full interface"; +diag "with one attachment"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok({text => 'Reply'}, "reply to the ticket"); + $m->form_name('TicketUpdate'); + $m->field('Attach', LogoFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->content_contains('Download bpslogo.png', 'page has file name'); +} + +diag "with two attachments"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $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('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "with one attachment, delete one along the way"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $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->tick('DeleteAttach', LogoFile); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "jumbo interface"; +diag "with one attachment"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket"); + $m->form_name('TicketModifyAll'); + $m->field('Attach', LogoFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->goto_ticket( $ticket->id ); + $m->content_contains('Download bpslogo.png', 'page has file name'); +} + +diag "with two attachments"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket"); + $m->form_name('TicketModifyAll'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketModifyAll'); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->goto_ticket( $ticket->id ); + $m->content_contains('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "with one attachment, delete one along the way"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket"); + $m->form_name('TicketModifyAll'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketModifyAll'); + $m->tick('DeleteAttach', LogoFile); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->goto_ticket( $ticket->id ); + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "bulk update"; +diag "one attachment"; +{ + my @tickets = RT::Test->create_tickets( + { + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + }, + {}, + {}, + ); + my $query = join ' OR ', map "id=$_", map $_->id, @tickets; + $query =~ s/ /%20/g; + $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" ); + + $m->form_name('BulkUpdate'); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->submit; + is($m->status, 200, "request successful"); + + foreach my $ticket ( @tickets ) { + $m->goto_ticket( $ticket->id ); + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); + } +} + +diag "two attachments"; +{ + my @tickets = RT::Test->create_tickets( + { + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + }, + {}, + {}, + ); + my $query = join ' OR ', map "id=$_", map $_->id, @tickets; + $query =~ s/ /%20/g; + $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" ); + + $m->form_name('BulkUpdate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('BulkUpdate'); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->submit; + is($m->status, 200, "request successful"); + + foreach my $ticket ( @tickets ) { + $m->goto_ticket( $ticket->id ); + $m->content_contains('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); + } +} + +diag "one attachment, delete one along the way"; +{ + my @tickets = RT::Test->create_tickets( + { + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + }, + {}, + {}, + ); + my $query = join ' OR ', map "id=$_", map $_->id, @tickets; + $query =~ s/ /%20/g; + $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" ); + + $m->form_name('BulkUpdate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('BulkUpdate'); + $m->tick('DeleteAttach', LogoFile); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->submit; + is($m->status, 200, "request successful"); + + foreach my $ticket ( @tickets ) { + $m->goto_ticket( $ticket->id ); + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); + } +} + +diag "self service"; +diag "create with attachment"; +{ + $m->get_ok( $url . "/SelfService/Create.html?Queue=". $queue->id ); + + $m->form_name('TicketCreate'); + $m->field('Attach', FaviconFile); + $m->field('Subject', 'Subject'); + $m->field('Content', 'Message'); + ok($m->current_form->find_input('AddMoreAttach'), "more than one attachment"); + $m->submit; + is($m->status, 200, "request successful"); + + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "update with attachment"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->get_ok( $url . "/SelfService/Update.html?id=". $ticket->id ); + $m->form_name('TicketUpdate'); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + ok($m->current_form->find_input('AddMoreAttach'), "more than one attachment"); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "mobile ui"; + +diag "simple create + reply"; +{ + $m->get_ok( $url . '/m/ticket/create?Queue=' . $queue->id ); + + $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'); +} + + +diag "check content type and content"; +{ + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->field('Attach', TextFile); + $m->field('Subject', 'Attachments test'); + $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('Some content', 'and content'); + $m->content_contains('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download mobile.css', 'page has file name'); + + $m->follow_link_ok({text => "Download bpslogo.png"}); + is($m->response->header('Content-Type'), 'image/png', 'Content-Type of png lacks charset' ); + is($m->content_type, "image/png"); + is($m->content, RT::Test->file_content(LogoFile), "Binary content matches"); + $m->back; + + $m->follow_link_ok( { text => 'Download mobile.css' } ); + is( $m->response->header('Content-Type'), + 'text/css;charset=UTF-8', + 'Content-Type of text has charset', + ); + is($m->content_type, "text/css"); + is($m->content, RT::Test->file_content(TextFile), "Text content matches"); +} + +diag "concurent actions"; +my $m2 = RT::Test::Web->new; +ok $m2->login, 'second login'; + +diag "update and create"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m2->goto_ticket( $ticket->id ); + $m2->follow_link_ok({text => 'Reply'}, "reply to the ticket"); + $m2->form_name('TicketUpdate'); + $m2->field('Attach', LogoFile); + $m2->click('AddMoreAttach'); + is($m2->status, 200, "request successful"); + + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Attach', FaviconFile); + $m->field('Subject', 'Attachments test'); + $m->field('Content', 'Some content'); + $m->submit; + is($m->status, 200, "request successful"); + + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); + + $m2->form_name('TicketUpdate'); + $m2->click('SubmitTicket'); + $m2->content_contains('Download bpslogo.png', 'page has file name'); + $m2->content_lacks('Download favicon.png', 'page has no file name'); +} + diff --git a/rt/t/web/basic.t b/rt/t/web/basic.t index 02483b208..79c247d24 100644 --- a/rt/t/web/basic.t +++ b/rt/t/web/basic.t @@ -2,7 +2,7 @@ use strict; use warnings; -use RT::Test tests => 23; +use RT::Test tests => 24; my ($baseurl, $agent) = RT::Test->started_ok; @@ -71,16 +71,15 @@ my $url = $agent->rt_base_url; fields => { TimeWorked => 5, 'TimeWorked-TimeUnits' => "hours" } ); - $agent->content_contains("to '300'", "5 hours is 300 minutes"); + $agent->content_contains("5 hours", "5 hours is displayed"); + $agent->content_contains("300 min", "but minutes is also"); } -TODO: { - todo_skip("Need to handle mason trying to compile images",1); -$agent->get( $url."NoAuth/images/test.png" ); +$agent->get( $url."static/images/test.png" ); my $file = RT::Test::get_relocatable_file( File::Spec->catfile( - qw(.. .. share html NoAuth images test.png) + qw(.. .. share static images test.png) ) ); is( @@ -88,7 +87,6 @@ is( -s $file, "got a file of the correct size ($file)", ); -} # # XXX: hey-ho, we have these tests in t/web/query-builder diff --git a/rt/t/web/basic_auth.t b/rt/t/web/basic_auth.t new file mode 100644 index 000000000..ff77f29f2 --- /dev/null +++ b/rt/t/web/basic_auth.t @@ -0,0 +1,34 @@ +use strict; +use warnings; +use RT; +use RT::Test tests => 9; + +RT->Config->Set( DevelMode => 0 ); +RT->Config->Set( WebRemoteUserAuth => 1 ); + +my ( $url, $m ) = RT::Test->started_ok( basic_auth => 1 ); + +# This tests the plack middleware, not RT +$m->get($url); +is($m->status, 401, "Initial request with no creds gets 401"); + +# This tests the plack middleware, not RT +$m->get($url, $m->auth_header( root => "wrong" )); +is($m->status, 401, "Request with wrong creds gets 401"); + +$m->get($url, $m->auth_header( 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 WebFallbackToRTLogin"); + +# Again, testing the plack middleware +$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->auth( root => "password" ); diff --git a/rt/t/web/case-sensitivity.t b/rt/t/web/case-sensitivity.t index 5f40ef690..759937192 100644 --- a/rt/t/web/case-sensitivity.t +++ b/rt/t/web/case-sensitivity.t @@ -22,7 +22,7 @@ $m->login; require JSON; is_deeply( JSON::from_json( $m->content ), - [{"value" => "root\@localhost","label" => "Enoch Root", id=>$root_id}] + [{id => 12, "value" => "root\@localhost","label" => "root (Enoch Root)"}] ); } @@ -73,7 +73,7 @@ my $cf; # test custom field values auto completer { - $m->get_ok('/Helpers/Autocomplete/CustomFieldValues?term=eNo&Object---CustomField-'. $cf->id .'-Value&ContextId=1&ContextType=RT::Queue'); + $m->get_ok('/Helpers/Autocomplete/CustomFieldValues?term=eNo&Object-RT::Ticket--CustomField-'. $cf->id .'-Value&ContextId=1&ContextType=RT::Queue'); require JSON; is_deeply( JSON::from_json( $m->content ), diff --git a/rt/t/web/cf_access.t b/rt/t/web/cf_access.t index 675fa2177..48ab5a21b 100644 --- a/rt/t/web/cf_access.t +++ b/rt/t/web/cf_access.t @@ -5,14 +5,14 @@ use RT::Test tests => 32; my ($baseurl, $m) = RT::Test->started_ok; -use constant ImageFile => $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png'; +use constant ImageFile => $RT::StaticPath .'/images/bpslogo.png'; use constant ImageFileContent => RT::Test->file_content(ImageFile); ok $m->login, 'logged in'; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); # Test form validation $m->submit_form( @@ -94,10 +94,10 @@ diag "apply the CF to General queue"; my ( $cf, $cfid, $tid ); { $m->title_is(q/Editing CustomField img/, 'admin-cf created'); - $m->follow_link( id => 'tools-config-queues'); + $m->follow_link( id => 'admin-queues'); $m->follow_link( text => 'General' ); $m->title_is(q/Configuration for queue General/, 'admin-queue: general'); - $m->follow_link( id => 'page-ticket-custom-fields'); + $m->follow_link( id => 'page-custom-fields-tickets'); $m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid'); $m->form_name('EditCustomFields'); @@ -215,7 +215,7 @@ diag "create a ticket with an image"; } $m->get( $m->rt_base_url ); -$m->follow_link( id => 'search-new'); +$m->follow_link( id => 'search-tickets-new'); $m->title_is(q/Query Builder/, 'Query building'); $m->submit_form( form_name => "BuildQuery", diff --git a/rt/t/web/cf_date.t b/rt/t/web/cf_date.t index 2180e140f..a38388972 100644 --- a/rt/t/web/cf_date.t +++ b/rt/t/web/cf_date.t @@ -14,7 +14,7 @@ my $cf_name = 'test cf date'; my $cfid; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { @@ -33,12 +33,12 @@ 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->follow_link( id => 'admin-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->follow_link( id => 'page-custom-fields-tickets' ); $m->title_is( q/Custom Fields for queue General/, 'admin-queue: general cfid' ); @@ -186,7 +186,7 @@ diag 'check invalid inputs'; my @warnings = $m->get_warnings; chomp @warnings; - is_deeply( @warnings, q{Couldn't parse date 'foodate' by Time::ParseDate} ); + is_deeply( [@warnings], [(q{Couldn't parse date 'foodate' by Time::ParseDate})x2] ); } diag 'retain values when adding attachments'; diff --git a/rt/t/web/cf_datetime.t b/rt/t/web/cf_datetime.t index 72a8b3f7e..da938ab85 100644 --- a/rt/t/web/cf_datetime.t +++ b/rt/t/web/cf_datetime.t @@ -24,7 +24,7 @@ if ( ( $ENV{RT_TEST_WEB_HANDLER} || '' ) =~ /^apache(\+mod_perl)?$/ my $cfid; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { @@ -47,7 +47,7 @@ ok $queue && $queue->id, 'loaded or created queue'; $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->follow_link( id => 'page-custom-fields-tickets' ); $m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid'); $m->form_name('EditCustomFields'); @@ -212,7 +212,7 @@ diag 'check invalid inputs'; my @warnings = $m->get_warnings; chomp @warnings; - is_deeply( @warnings, q{Couldn't parse date 'foodate' by Time::ParseDate} ); + is_deeply( [@warnings], [(q{Couldn't parse date 'foodate' by Time::ParseDate})x2] ); } diag 'retain values when adding attachments'; diff --git a/rt/t/web/cf_groupings.t b/rt/t/web/cf_groupings.t new file mode 100644 index 000000000..0a40f71af --- /dev/null +++ b/rt/t/web/cf_groupings.t @@ -0,0 +1,277 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my @groupings = qw/Basics Dates People Links More/; +RT->Config->Set( 'CustomFieldGroupings', + 'RT::Ticket' => { + map { +($_ => ["Test$_"]) } @groupings, + }, +); + +my %CF; +for my $grouping (@groupings) { + my $name = "Test$grouping"; + my $cf = RT::CustomField->new( RT->SystemUser ); + my ($id, $msg) = $cf->Create( + Name => $name, + Queue => '0', + Description => 'A Testing custom field', + Type => 'FreeformSingle', + Pattern => '^(?!bad value).*$', + ); + ok $id, "custom field '$name' correctly created"; + $CF{$grouping} = $id; +} + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; + +my %location = ( + Basics => ".ticket-info-basics", + Dates => ".ticket-info-dates", + People => "#ticket-create-message", + Links => ".ticket-info-links", + More => ".ticket-info-cfs", +); +{ + note "testing Create"; + $m->goto_create_ticket($queue); + + my $prefix = 'Object-RT::Ticket--CustomField:'; + my $dom = $m->dom; + $m->form_name('TicketCreate'); + $m->field("Subject", "CF grouping test"); + + for my $grouping (@groupings) { + my $input_name = $prefix . "$grouping-$CF{$grouping}-Value"; + is $dom->find(qq{input[name="$input_name"]})->size, 1, "only one CF input on the page"; + ok $dom->at(qq{$location{$grouping} input[name="$input_name"]}), "CF is in the right place"; + $m->field( $input_name, "Test" . $grouping . "Value" ); + } + $m->submit; +} + +my $id = $m->get_ticket_id; +{ + note "testing Display"; + ok $id, "created a ticket"; + my $dom = $m->dom; + + $location{People} = ".ticket-info-people"; + foreach my $grouping (@groupings) { + my $row_id = "CF-$CF{$grouping}-ShowRow"; + is $dom->find(qq{#$row_id})->size, 1, "CF on the page"; + is $dom->at(qq{#$row_id})->all_text, "Test$grouping: Test${grouping}Value", "value is set"; + ok $dom->at(qq{$location{$grouping} #$row_id}), "CF is in the right place"; + } +} + +{ + note "testing Basics/People/Dates/Links pages"; + my $prefix = 'Object-RT::Ticket-'. $id .'-CustomField:'; + { # Basics and More both show up on "Basics" + for my $name (qw/Basics More/) { + $m->follow_link_ok({id => 'page-basics'}, 'Ticket -> Basics'); + is $m->dom->find(qq{input[name^="$prefix"][name\$="-Value"]})->size, 2, + "two CF inputs on the page"; + + my $input_name = "$prefix$name-$CF{$name}-Value"; + ok $m->dom->at(qq{$location{$name} input[name="$input_name"]}), + "CF is in the right place"; + $m->submit_form_ok({ + with_fields => { $input_name => "Test${name}Changed" }, + button => 'SubmitTicket', + }); + $m->content_like(qr{to Test${name}Changed}); + + $m->submit_form_ok({ + with_fields => { $input_name => "bad value" }, + button => 'SubmitTicket', + }); + $m->content_like(qr{Test\Q$name\E: Input must match}); + } + } + + # Everything else gets its own page + foreach my $name ( qw(People Dates Links) ) { + $m->follow_link_ok({id => "page-\L$name"}, "Ticket's $name page"); + is $m->dom->find(qq{input[name^="$prefix"][name\$="-Value"]})->size, 1, + "only one CF input on the page"; + my $input_name = "$prefix$name-$CF{$name}-Value"; + $m->submit_form_ok({ + with_fields => { $input_name => "Test${name}Changed" }, + button => 'SubmitTicket', + }); + $m->content_like(qr{to Test${name}Changed}); + + $m->submit_form_ok({ + with_fields => { $input_name => "bad value" }, + button => 'SubmitTicket', + }); + $m->content_like(qr{Could not add new custom field value: Input must match}); + } +} + +{ + note "testing Jumbo"; + my $prefix = 'Object-RT::Ticket-'. $id .'-CustomField:'; + $m->follow_link_ok({id => "page-jumbo"}, "Ticket's Jumbo page"); + my $dom = $m->dom; + $m->form_name("TicketModifyAll"); + + foreach my $name ( qw(Basics People Dates Links More) ) { + my $input_name = "$prefix$name-$CF{$name}-Value"; + is $dom->find(qq{input[name="$input_name"]})->size, 1, + "only one CF input on the page"; + $m->field( $input_name, "Test${name}Again" ); + } + $m->click('SubmitTicket'); + foreach my $name ( qw(Basics People Dates Links More) ) { + $m->content_like(qr{to Test${name}Again}); + } +} + +{ + note "Reconfigure to place one CF in multiple boxes"; + $m->no_warnings_ok; + RT::Test->stop_server; + + RT->Config->Set( 'CustomFieldGroupings', + 'RT::Ticket' => { + Basics => [ 'TestMore' ], + More => [ 'TestMore' ], + }, + ); + + ( $baseurl, $m ) = RT::Test->started_ok; + ok $m->login, 'logged in as root'; +} + +{ + note "Testing one CF in multiple boxes"; + $m->goto_create_ticket($queue); + + my $prefix = 'Object-RT::Ticket--CustomField:'; + my $dom = $m->dom; + $m->form_name('TicketCreate'); + + my $cf = $CF{More}; + is $m->dom->find(qq{input[name^="$prefix"][name\$="-$cf-Value"]})->size, 2, + "Two 'More' CF inputs on the page"; + for my $grouping (qw/Basics More/) { + my $input_name = $prefix . "$grouping-$cf-Value"; + is $dom->find(qq{input[name="$input_name"]})->size, 1, "Found the $grouping grouping"; + ok $dom->at(qq{$location{$grouping} input[name="$input_name"]}), "CF is in the right place"; + $m->field( $input_name, "TestMoreValue" ); + } + $m->submit; + $m->no_warnings_ok( "Submitting CF with two (identical) values had no warnings" ); +} + +$id = $m->get_ticket_id; +my $ticket = RT::Ticket->new ( RT->SystemUser ); +$ticket->Load( $id ); +is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "TestMoreValue", + "Value submitted twice is set correctly (and only once)"; + +my $cf = $CF{More}; +my $prefix = 'Object-RT::Ticket-'. $id .'-CustomField:'; +{ + note "Updating with multiple appearances of a CF"; + $m->follow_link_ok({id => 'page-basics'}, 'Ticket -> Basics'); + + is $m->dom->find(qq{input[name^="$prefix"][name\$="-$cf-Value"]})->size, 2, + "Two 'More' CF inputs on the page"; + my @inputs; + for my $grouping (qw/Basics More/) { + my $input_name = "$prefix$grouping-$cf-Value"; + push @inputs, $input_name; + ok $m->dom->at(qq{$location{$grouping} input[name="$input_name"]}), + "CF is in the right place"; + } + $m->submit_form_ok({ + with_fields => { + map {+($_ => "TestMoreChanged")} @inputs, + }, + button => 'SubmitTicket', + }); + $m->no_warnings_ok; + $m->content_like(qr{to TestMoreChanged}); + + $ticket->Load( $id ); + is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "TestMoreChanged", + "Updated value submitted twice is set correctly (and only once)"; +} + +{ + note "Updating with _differing_ values in multiple appearances of a CF"; + + my %inputs = map {+($_ => "$prefix$_-$cf-Value")} qw/Basics More/; + $m->submit_form_ok({ + with_fields => { + $inputs{Basics} => "BasicsValue", + $inputs{More} => "MoreValue", + }, + button => 'SubmitTicket', + }); + $m->warning_like(qr{CF $cf submitted with multiple differing values}); + $m->content_like(qr{to BasicsValue}, "Arbitrarily chose first value"); + + $ticket->Load( $id ); + is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "BasicsValue", + "Conflicting value submitted twice is set correctly (and only once)"; +} + +{ + note "Configuring CF to be a select-multiple"; + my $custom_field = RT::CustomField->new( RT->SystemUser ); + $custom_field->Load( $cf ); + $custom_field->SetType( "Select" ); + $custom_field->SetMaxValues( 0 ); + $custom_field->AddValue( Name => $_ ) for 1..9; +} + +{ + note "Select multiples do not interfere with each other when appearing multiple times"; + $m->follow_link_ok({id => 'page-basics'}, 'Ticket -> Basics'); + + $m->form_name('TicketModify'); + my %inputs = map {+($_ => "$prefix$_-$cf-Values")} qw/Basics More/; + ok $m->dom->at(qq{select[name="$inputs{Basics}"]}), "Found 'More' CF in Basics box"; + ok $m->dom->at(qq{select[name="$inputs{More}"]}), "Found 'More' CF in More box"; + + $m->select( $inputs{Basics} => [1, 3, 9] ); + $m->select( $inputs{More} => [1, 3, 9] ); + $m->click( 'SubmitTicket' ); + $m->no_warnings_ok; + $m->content_like(qr{$_ added as a value for TestMore}) for 1, 3, 9; + $m->content_like(qr{BasicsValue is no longer a value for custom field TestMore}); + + $ticket->Load( $id ); + is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "1|3|9", + "Multi-select values submitted correctly"; +} + +{ + note "Submit multiples correctly choose one set of values when conflicting information is submitted"; + $m->form_name('TicketModify'); + my %inputs = map {+($_ => "$prefix$_-$cf-Values")} qw/Basics More/; + $m->select( $inputs{Basics} => [2, 3, 4] ); + $m->select( $inputs{More} => [8, 9] ); + $m->click( 'SubmitTicket' ); + $m->warning_like(qr{CF $cf submitted with multiple differing values}); + $m->content_like(qr{$_ added as a value for TestMore}) for 2, 4; + $m->content_unlike(qr{$_ added as a value for TestMore}) for 8; + $m->content_like(qr{$_ is no longer a value for custom field TestMore}) for 1, 9; + + $ticket->Load( $id ); + is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "3|2|4", + "Multi-select values submitted correctly"; +} + +undef $m; +done_testing; diff --git a/rt/t/web/cf_groupings_user.t b/rt/t/web/cf_groupings_user.t new file mode 100644 index 000000000..fe79ae5ad --- /dev/null +++ b/rt/t/web/cf_groupings_user.t @@ -0,0 +1,110 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +RT->Config->Set( 'CustomFieldGroupings', + 'RT::User' => { + Identity => ['TestIdentity'], + 'Access control' => ['TestAccessControl'], + Location => ['TestLocation'], + Phones => ['TestPhones'], + More => ['TestMore'], + }, +); + +my %CF; + +while (my ($group,$cfs) = each %{ RT->Config->Get('CustomFieldGroupings')->{'RT::User'} } ) { + my $name = $cfs->[0]; + my $cf = RT::CustomField->new( RT->SystemUser ); + my ($id, $msg) = $cf->Create( + Name => $name, + Description => 'A custom field', + LookupType => RT::User->new( $RT::SystemUser )->CustomFieldLookupType, + Type => 'FreeformSingle', + Pattern => '^(?!bad value).*$', + ); + ok $id, "custom field '$name' correctly created"; + + ($id, $msg) = $cf->AddToObject( RT::User->new( $cf->CurrentUser ) ); + ok $id, "applied custom field" or diag "error: $msg"; + + $group =~ s/\W//g; + $CF{$name} = "$group-" . $cf->Id; +} + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; + +my %location = ( + Identity => ".user-info-identity", + AccessControl => ".user-info-access-control", + Location => ".user-info-location", + Phones => ".user-info-phones", + More => ".user-info-cfs", +); +{ + note "testing Create"; + $m->follow_link_ok({id => 'admin-users-create'}, 'Create '); + + my $dom = $m->dom; + $m->form_name('UserCreate'); + + $m->field( 'Name', 'user1' ); + + my $prefix = 'Object-RT::User--CustomField:'; + for my $name (keys %location) { + my $input_name = $prefix . $CF{"Test$name"} .'-Value'; + is $dom->find(qq{input[name="$input_name"]})->size, 1, "only one CF input on the page"; + ok $dom->at(qq{$location{$name} input[name="$input_name"]}), "CF is in the right place"; + $m->field( $input_name, "Test${name}Value" ); + } + + $m->submit; + $m->content_like(qr{User created}); +} + +my ($id) = ($m->uri =~ /id=(\d+)/); +ok $id, "found user's id #$id"; + +{ + note "testing values on Modify page and on the object"; + my $user = RT::User->new( RT->SystemUser ); + $user->Load( $id ); + ok $user->id, "loaded user"; + + my $dom = $m->dom; + $m->form_name('UserModify'); + my $prefix = "Object-RT::User-$id-CustomField:"; + foreach my $name ( keys %location ) { + is $user->FirstCustomFieldValue("Test$name"), "Test${name}Value", + "correct value of Test$name CF"; + my $input_name = $prefix . $CF{"Test$name"} .'-Value'; + is $m->value($input_name), "Test${name}Value", + "correct value in UI"; + $m->field( $input_name, "Test${name}Changed" ); + ok $dom->at(qq{$location{$name} input[name="$input_name"]}), "CF is in the right place"; + } + $m->submit; +} + +{ + note "testing that update works"; + my $user = RT::User->new( RT->SystemUser ); + $user->Load( $id ); + ok $user->id, "loaded user"; + + $m->form_name('UserModify'); + my $prefix = "Object-RT::User-$id-CustomField:"; + foreach my $name ( keys %location ) { + is $user->FirstCustomFieldValue("Test$name"), "Test${name}Changed", + "correct value of Test$name CF"; + my $input = $prefix . $CF{"Test$name"} .'-Value'; + is $m->value($input), "Test${name}Changed", + "correct value in UI"; + } +} + +undef $m; +done_testing; diff --git a/rt/t/web/cf_image.t b/rt/t/web/cf_image.t new file mode 100644 index 000000000..355f25968 --- /dev/null +++ b/rt/t/web/cf_image.t @@ -0,0 +1,61 @@ +use strict; +use warnings; + +use RT::Test tests => 'no_declare'; + +my (undef, $m) = RT::Test->started_ok; +$m->login; +$m->follow_link( id => 'admin-custom-fields-create' ); +$m->submit_form_ok({ + form_name => "ModifyCustomField", + fields => { + Name => 'Images', + TypeComposite => 'Image-1', + LookupType => 'RT::Queue-RT::Ticket', + }, +}); +$m->content_contains("Object created"); +my $cfid = $m->form_name('ModifyCustomField')->value('id'); +ok $cfid, "Created CF correctly"; + +$m->follow_link_ok( {id => "page-applies-to"} ); +$m->form_with_fields( "AddCustomField-1" ); +$m->tick( "AddCustomField-1", 0 ); +$m->click_ok( "UpdateObjs" ); +$m->content_contains("Object created"); + + +$m->submit_form_ok({ + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, +}); +$m->content_contains("Upload one image"); +$m->submit_form_ok({ + form_name => "TicketCreate", + fields => { + Subject => 'Test ticket', + Content => 'test', + }, +}); +$m->content_like( qr/Ticket \d+ created/, + "a ticket is created succesfully" ); + +$m->follow_link_ok( {id => "page-basics"} ); +$m->content_contains("Upload one image"); +$m->submit_form_ok({ + form_name => "TicketModify", + fields => { + "Object-RT::Ticket-1-CustomField-1-Upload" => + RT::Test::get_relocatable_file('bpslogo.png', '..', 'data'), + }, +}); +$m->content_contains("bpslogo.png added"); +$m->content_contains("/Download/CustomFieldValue/1/bpslogo.png"); + +$m->form_name("TicketModify"); +$m->tick("Object-RT::Ticket-1-CustomField-1-DeleteValueIds", 1); +$m->click_ok("SubmitTicket"); +$m->content_lacks("/Download/CustomFieldValue/1/bpslogo.png"); + +undef $m; +done_testing; diff --git a/rt/t/web/cf_onqueue.t b/rt/t/web/cf_onqueue.t index bd6ae66aa..dd3320a25 100644 --- a/rt/t/web/cf_onqueue.t +++ b/rt/t/web/cf_onqueue.t @@ -8,7 +8,7 @@ ok $m->login, 'logged in'; diag "Create a queue CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { diff --git a/rt/t/web/cf_pattern.t b/rt/t/web/cf_pattern.t new file mode 100644 index 000000000..ff85ec6c7 --- /dev/null +++ b/rt/t/web/cf_pattern.t @@ -0,0 +1,80 @@ +use strict; +use warnings; + +use RT::Test tests => 'no_declare'; + +my ($base, $m) = RT::Test->started_ok; + +my $cf = RT::Test->load_or_create_custom_field( + Name => 'Yaks', + Type => 'FreeformSingle', + Pattern => '(?#Digits)^\d+$', + Queue => 0, + LookupType => 'RT::Queue-RT::Ticket', +); +ok $cf && $cf->id, "Created CF with Pattern"; + +my $ticket = RT::Test->create_ticket( + Queue => 1, + Subject => 'a test ticket', +); +ok $ticket && $ticket->id, "Created ticket"; + +$m->login; + +for my $page ("/Ticket/Create.html?Queue=1", "/Ticket/Modify.html?id=".$ticket->id) { + diag $page; + $m->get_ok($page, "Fetched $page"); + $m->content_contains("Yaks"); + $m->content_contains("Input must match [Digits]"); + $m->content_lacks("cfinvalidfield"); + + my $cfinput = RT::Interface::Web::GetCustomFieldInputName( + Object => ( $page =~ /Create/ ? RT::Ticket->new( RT->SystemUser ) : $ticket ), + CustomField => $cf, + ); + $m->submit_form_ok({ + with_fields => { + $cfinput => "too many", + "${cfinput}-Magic" => "1", + }, + }); + $m->content_contains("Input must match [Digits]"); + $m->content_contains("cfinvalidfield"); + + $m->submit_form_ok({ + with_fields => { + $cfinput => "42", + "${cfinput}-Magic" => "1", + }, + }); + + if ($page =~ /Create/) { + $m->content_like(qr/Ticket \d+ created/, "Created ticket"); + } else { + $m->content_contains("Yaks 42 added", "Updated ticket"); + $m->content_contains("Input must match [Digits]"); + $m->content_lacks("cfinvalidfield"); + } +} + +diag "Quick ticket creation"; +{ + $m->get_ok("/"); + $m->submit_form_ok({ + with_fields => { + Subject => "test quick create", + QuickCreate => 1, + }, + }); + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->FromSQL("Subject = 'test quick create'"); + is $tickets->Count, 0, "No ticket created"; + + like $m->uri, qr/Ticket\/Create\.html/, "Redirected to the ticket create page"; + $m->content_contains("Yaks: Input must match", "Found CF validation error"); + $m->content_contains("test quick create", "Found prefilled Subject"); +} + +undef $m; +done_testing; diff --git a/rt/t/web/cf_render_type.t b/rt/t/web/cf_render_type.t index 8d8efa897..42efb32bc 100644 --- a/rt/t/web/cf_render_type.t +++ b/rt/t/web/cf_render_type.t @@ -11,7 +11,7 @@ my $cf_name = 'test render type'; my $cfid; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { diff --git a/rt/t/web/cf_select_one.t b/rt/t/web/cf_select_one.t index 92fcf53f3..4f81e2a1a 100644 --- a/rt/t/web/cf_select_one.t +++ b/rt/t/web/cf_select_one.t @@ -2,7 +2,7 @@ use strict; use warnings; -use RT::Test tests => 45; +use RT::Test tests => undef; my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'logged in as root'; @@ -12,7 +12,7 @@ my $cf_name = 'test select one value'; my $cfid; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { @@ -49,10 +49,10 @@ ok $queue && $queue->id, 'loaded or created queue'; diag "apply the CF to General queue"; { - $m->follow_link( id => 'tools-config-queues'); + $m->follow_link( id => 'admin-queues'); $m->follow_link( text => 'General' ); $m->title_is(q/Configuration for queue General/, 'admin-queue: general'); - $m->follow_link( id => 'page-ticket-custom-fields'); + $m->follow_link( id => 'page-custom-fields-tickets'); $m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid'); $m->form_name('EditCustomFields'); @@ -151,3 +151,32 @@ diag "check that we can set empty value when the current is 0"; undef, 'API returns correct value'; } +diag 'retain selected cf values when adding attachments'; +{ + my ( $ticket, $id ); + $m->submit_form( + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, + ); + $m->content_contains($cf_name, 'Found cf field' ); + + $m->submit_form_ok( + { form_name => "TicketCreate", + fields => { + Subject => 'test defaults', + Content => 'test', + "Object-RT::Ticket--CustomField-$cfid-Values" => 'qwe', + }, + button => 'AddMoreAttach', + }, + 'Add an attachment on create' + ); + + $m->form_name("TicketCreate"); + is($m->value("Object-RT::Ticket--CustomField-$cfid-Values"), + "qwe", + "Selected value still on form" ); +} + +undef $m; +done_testing; diff --git a/rt/t/web/cf_textarea.t b/rt/t/web/cf_textarea.t new file mode 100644 index 000000000..d11bda4d5 --- /dev/null +++ b/rt/t/web/cf_textarea.t @@ -0,0 +1,75 @@ +use strict; +use warnings; + +use RT::Test tests => 'no_declare'; + +my $content = join ' ', ('The quick brown fox jumps over the lazy dog.') x 5; +$content = join "\n\n", $content, $content, $content; + +my ($base, $m) = RT::Test->started_ok; + +$m->login; + +my $ticket = RT::Test->create_ticket( + Queue => 1, + Subject => 'a test ticket', +); +ok $ticket && $ticket->id, "Created ticket"; + +my $EditUrl = "/Ticket/Modify.html?id=" . $ticket->id; + +my $cfs = { + area => { + type => 'Text', + name => 'TheTextarea', + }, + text => { + type => 'FreeformSingle', + name => 'TheControlField', + }, +}; + +while ( my( $label, $data ) = each %$cfs ) { + my $cf = $data->{obj} = RT::Test->load_or_create_custom_field( + Name => $data->{name}, + Type => $data->{type}, + Queue => 0, + LookupType => 'RT::Queue-RT::Ticket', + ); + ok $cf && $cf->id, "Created $data->{type} CF"; + + # get cf input field name + $data->{input} = RT::Interface::Web::GetCustomFieldInputName( + Object => $ticket, + CustomField => $cf, + ); +} + +# open ticket "Basics" page +$m->get_ok($EditUrl, "Fetched $EditUrl"); +$m->content_contains($_->{name} . ':') for ( values %$cfs ); + +$m->submit_form_ok({ + with_fields => { + $cfs->{area}{input} => $content, + $cfs->{area}{input} . '-Magic' => "1", + $cfs->{text}{input} => 'value a', + $cfs->{text}{input} . '-Magic' => "1", + }, +}, 'submitted form to initially set CFs'); +$m->content_contains('<li>TheControlField value a added</li>'); +$m->content_contains("<li>TheTextarea $content added</li>", 'content found'); + +# http://issues.bestpractical.com/Ticket/Display.html?id=30378 +# #30378: RT 4.2.6 - Very long text fields get updated even when they haven't changed +$m->submit_form_ok({ + with_fields => { + $cfs->{text}{input} => 'value b', + $cfs->{text}{input} . '-Magic' => "1", + }, +}, 'submitted form to initially set CFs'); +$m->content_contains('<li>TheControlField value a changed to value b</li>'); +$m->content_lacks("<li>TheTextarea $content changed to $content</li>", 'textarea wasnt updated'); + +undef $m; +done_testing; diff --git a/rt/t/web/cf_values_class.t b/rt/t/web/cf_values_class.t index 646642781..6535c505b 100644 --- a/rt/t/web/cf_values_class.t +++ b/rt/t/web/cf_values_class.t @@ -14,7 +14,7 @@ my $cf_name = 'test values class'; my $cfid; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { diff --git a/rt/t/web/charting.t b/rt/t/web/charting.t index e19ec41ae..5131f9cef 100644 --- a/rt/t/web/charting.t +++ b/rt/t/web/charting.t @@ -1,16 +1,10 @@ use strict; use warnings; -BEGIN { - require RT::Test; - - if (eval { require GD; 1 }) { - RT::Test->import(plan => 'no_plan'); - } - else { - RT::Test->import(skip_all => 'GD required.'); - } -} +use RT::Test tests => undef; + +plan skip_all => 'GD required' + unless GD->require; for my $n (1..7) { my $ticket = RT::Ticket->new( RT->SystemUser ); @@ -35,8 +29,8 @@ ok( $m->login, "Logged in" ); # Test that defaults work $m->get_ok( "/Search/Chart.html?Query=id>0" ); -$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{<th[^>]*>Status\s*</th>\s*<th[^>]*>Ticket count\s*</th>}, "Grouped by status"); +$m->content_like(qr{new\s*</th>\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" ); @@ -45,35 +39,41 @@ 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[^>]*>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->get_ok( "/Search/Chart.html?Query=id>0&GroupBy=Queue" ); +$m->content_like(qr{<th[^>]*>Queue\s*</th>\s*<th[^>]*>Ticket count\s*</th>}, "Grouped by queue"); +$m->content_like(qr{General\s*</th>\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" ); +$m->get_ok( "/Search/Chart?Query=id>0&GroupBy=Queue" ); is( $m->content_type, "image/png" ); 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[^>]*>Requestor\.EmailAddress\s*</th>\s*<th[^>]*>Tickets\s*</th>}, +$m->get_ok( "/Search/Chart.html?Query=id>0&GroupBy=Requestor.EmailAddress" ); +$m->content_like(qr{<th[^>]*>Requestor\s+EmailAddress</th>\s*<th[^>]*>Ticket count\s*</th>}, "Grouped by requestor"); -$m->content_like(qr{root0\@localhost</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>3</a>}, "Found results in table"); +$m->content_like(qr{root0\@localhost\s*</th>\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" ); +$m->get_ok( "/Search/Chart?Query=id>0&GroupBy=Requestor.EmailAddress" ); is( $m->content_type, "image/png" ); 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*<a[^>]*>7</a>}, + +$m->get_ok( "/Search/Chart.html?Query=id>0&GroupBy=Requestor.Phone" ); +$m->warning_like( qr{'Requestor\.Phone' is not a valid grouping for reports} ); + +TODO: { + local $TODO = "UI should show that it's group by status"; + $m->content_like(qr{new\s*</th>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found queue results in table, as a default"); +} $m->content_like(qr{<img src="/Search/Chart\?}, "Found image"); -$m->get_ok( "/Search/Chart?Query=id>0&PrimaryGroupBy=Requestor.Phone" ); +$m->get_ok( "/Search/Chart?Query=id>0&GroupBy=Requestor.Phone" ); +$m->warning_like( qr{'Requestor\.Phone' is not a valid grouping for reports} ); is( $m->content_type, "image/png" ); ok( length($m->content), "Has content" ); @@ -93,3 +93,6 @@ $advanced = $m->find_link( text => 'Advanced' )->URI->equery; like( $advanced, qr{Query=id%3E0}, 'Advanced link still has Query param with id search' ); + +undef $m; +done_testing; diff --git a/rt/t/web/class_create.t b/rt/t/web/class_create.t index 2d9ad035d..7723d7aa9 100644 --- a/rt/t/web/class_create.t +++ b/rt/t/web/class_create.t @@ -13,7 +13,7 @@ my $class_name = 'test class'; my $class_id; diag "Create a class"; { - $m->follow_link( id => 'tools-config-articles-classes-create'); + $m->follow_link( id => 'admin-articles-classes-create'); # Test class form validation $m->submit_form( diff --git a/rt/t/web/command_line.t b/rt/t/web/command_line.t index a5c52d261..47f672856 100644 --- a/rt/t/web/command_line.t +++ b/rt/t/web/command_line.t @@ -111,11 +111,11 @@ ok($val,$msg); # add a comment to ticket expect_send("comment -m 'comment-$$' $ticket_id", "Adding a comment..."); - expect_like(qr/Message recorded/, "Added the comment"); + expect_like(qr/Comments added/, "Added the comment"); ### should test to make sure it actually got added # add correspondance to ticket (?) expect_send("correspond -m 'correspond-$$' $ticket_id", "Adding correspondence..."); - expect_like(qr/Message recorded/, "Added the correspondence"); + expect_like(qr/Correspondence added/, "Added the correspondence"); ### should test to make sure it actually got added my $test_email = RT::Test::get_relocatable_file('lorem-ipsum', @@ -124,7 +124,7 @@ ok($val,$msg); # text attachment check_attachment($test_email); # binary attachment - check_attachment($RT::MasonComponentRoot.'/NoAuth/images/bpslogo.png'); + check_attachment($RT::StaticPath . '/images/bpslogo.png'); # change a ticket's Owner expect_send("edit ticket/$ticket_id set owner=root", 'Changing owner...'); @@ -158,7 +158,7 @@ expect_send("show ticket/$ticket_id -f queue", 'Verifying change...'); expect_like(qr/Queue: EditedQueue$$/, 'Verified change'); # cannot move ticket to a nonexistent queue expect_send("edit ticket/$ticket_id set queue=nonexistent-$$", 'Changing to nonexistent queue...'); -expect_like(qr/queue does not exist/i, 'Errored out'); +expect_like(qr/Queue nonexistent-$$ does not exist/i, 'Errored out'); expect_send("show ticket/$ticket_id -f queue", 'Verifying lack of change...'); expect_like(qr/Queue: EditedQueue$$/, 'Verified lack of change'); @@ -213,7 +213,7 @@ expect_send("edit ticket/$ticket_id set CF-myCF$$=1,2,3", 'Changing CF...'); expect_like(qr/Ticket $ticket_id updated/, 'Changed cf'); expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value'); expect_like(qr/\QCF.{myCF$$}\E: 1,2,3/i, 'Verified change'); -expect_send("edit ticket/$ticket_id set CF-myCF$$=\"1's,2,3\"", 'Changing CF...'); +expect_send(qq{edit ticket/$ticket_id set CF-myCF$$="1's,2,3"}, 'Changing CF...'); expect_like(qr/Ticket $ticket_id updated/, 'Changed cf'); expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value'); expect_like(qr/\QCF.{myCF$$}\E: 1's,2,3/i, 'Verified change'); @@ -238,32 +238,85 @@ expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); expect_like(qr/\QCF.{MultipleCF$$}\E: b,\s*c,\s*o/i, 'Verified multiple cf change'); -expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"'a,b,c'\" ", 'Changing CF...'); -expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change'); -expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=a", 'Changing CF...'); -expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change'); +sub multi_round_trip { + my ($op, $value, $regex) = @_; + $Test::Builder::Level++; + # The outer double quotes are for the argument parsing that the + # command-line does; the extra layer of escaping is to for them, as + # well. It is equivilent to the quoting that the shell would + # require. + my $quoted = $value; + $quoted =~ s/(["\\])/\\$1/g; + expect_send(qq{edit ticket/$ticket_id $op CF.{MultipleCF$$}="$quoted"}, qq{CF $op $value}); + expect_like(qr/Ticket $ticket_id updated/, qq{Got expected "updated" answer}); + expect_send(qq{show ticket/$ticket_id -f CF.{MultipleCF$$}}, qq{Sent "show"}); + expect_like(qr/\QCF.{MultipleCF$$}\E: $regex$/i, qq{Answer matches $regex}); +} -expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=q{a,b,c}", 'Changing CF...'); -expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change'); -expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=a", 'Changing CF...'); -expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change'); -expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=\"'a,b,c'\"", 'Changing CF...'); +# Test simple quoting +my $ticket = RT::Ticket->new($RT::SystemUser); +$ticket->Load($ticket_id); +multi_round_trip(set => q|'a,b,c'|, qr/'a,b,c'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has only one CF value"); +is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected"); + +multi_round_trip(del => q|a|, qr/'a,b,c'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value"); +is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected"); + +multi_round_trip(set => q|q{a,b,c}|, qr/'a,b,c'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value"); +is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected"); + +multi_round_trip(del => q|a|, qr/'a,b,c'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value"); +is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected"); + +multi_round_trip(del => q|'a,b,c'|, qr/\s*/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 0, "Now has no CF values"); + +multi_round_trip(set => q|q{1,2's,3}|, qr/'1,2\\'s,3'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value"); +is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{1,2's,3}, "And that CF value is as expected"); + +multi_round_trip(del => q|q{1,2's,3}|, qr/\s*/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 0, "Now has no CF values"); + +# Test escaping of quotes - generate (foo)(bar') with no escapes +multi_round_trip(set => q|'foo',bar'|, qr/foo,bar'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two values"); +is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo|, "Direct value checks out"); +is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content, q|bar'|, "Direct value checks out"); +multi_round_trip(del => q|bar'|, qr/foo/); + +# With one \, generate (foo',bar) + +# We obviously need two \s in the following q|| string in order to +# generate a string with one actual \ in it; this causes the string to, +# in general, have twice as many \s in it as we wish to test. +multi_round_trip(set => q|'foo\\',bar'|, qr/'foo\\',bar'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has one value"); +is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo',bar|, "Direct value checks out"); + +# With two \, generate (foo\)(bar') +multi_round_trip(set => q|'foo\\\\',bar'|, qr/foo\\,bar'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two values"); +is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo\\|, "Direct value checks out"); +is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content, q|bar'|, "Direct value checks out"); +multi_round_trip(del => q|bar'|, qr/foo\\/); + +# With three \, generate (foo\',bar) +multi_round_trip(set => q|'foo\\\\\\',bar'|, qr/'foo\\\\\\',bar'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has one value"); +is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo\\',bar|, "Direct value checks out"); + +# Check that we don't infinite-loop on 'foo'bar,baz; this should be ('foo'bar)(baz) +expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"'foo'bar,baz\"", 'Changing CF to have quotes not at commas'); expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: \s*$/i, 'Verified change'); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two value"); +is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|'foo'bar|, "Direct value checks out"); +is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content, q|baz|, "Direct value checks out"); -expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"q{1,2's,3}\"", 'Changing CF...'); -expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: '1,2\\'s,3'/i, 'Verified change'); # ... # change a ticket's ...[other properties]... @@ -401,9 +454,9 @@ expect_send("merge $merge_ticket_B $merge_ticket_A", 'Merging the tickets...'); expect_like(qr/Merge completed/, 'Merged the tickets'); 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_like(qr/Merged into #$merge_ticket_A: CLIMergeTest1-$$ 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'); +expect_like(qr/Merged into #$merge_ticket_A: CLIMergeTest1-$$ by root/, 'Merge recorded in second ticket'); { # create a user; give them privileges to take and steal @@ -522,7 +575,7 @@ sub check_attachment { my $attachment_path = shift; (my $filename = $attachment_path) =~ s/.*\/(.*)$/$1/; expect_send("comment -m 'attach file' -a $attachment_path $ticket_id", "Adding an attachment ($filename)"); - expect_like(qr/Message recorded/, "Added the attachment"); + expect_like(qr/Comments added/, "Added the attachment"); expect_send("show ticket/$ticket_id/attachments","Finding Attachment"); my $attachment_regex = qr/(\d+):\s+$filename/; expect_like($attachment_regex,"Attachment Uploaded"); @@ -536,7 +589,11 @@ sub check_attachment { 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"); + is( + MIME::Base64::encode_base64(Test::Expect::before()), + MIME::Base64::encode_base64($attachment_content), + "Attachment contains original text" + ); } } diff --git a/rt/t/web/compilation_errors.t b/rt/t/web/compilation_errors.t index 126d33691..e4845e0de 100644 --- a/rt/t/web/compilation_errors.t +++ b/rt/t/web/compilation_errors.t @@ -6,7 +6,7 @@ BEGIN { sub wanted { -f && /\.html$/ && $_ !~ /Logout.html$/ && $File::Find::dir !~ /RichText/; } - my $tests = 8; + my $tests = 7; find( sub { wanted() and $tests += 4 }, 'share/html/' ); plan tests => $tests + 1; # plus one for warnings check } @@ -36,12 +36,10 @@ 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 ( { wanted => sub { wanted() and test_get($agent, $File::Find::name) }, no_chdir => 1 } , 'share/html/'); -TODO: { - local $TODO = "we spew *lots* of undef warnings"; - $agent->no_warnings_ok; -}; +# We expect to spew a lot of warnings; toss them away +$agent->get_warnings; sub test_get { my $agent = shift; diff --git a/rt/t/web/config_tab_right.t b/rt/t/web/config_tab_right.t index 69bf80c69..df146903a 100644 --- a/rt/t/web/config_tab_right.t +++ b/rt/t/web/config_tab_right.t @@ -19,7 +19,7 @@ my ($baseurl, $m) = RT::Test->started_ok; ok $m->login($uname, $upass), "logged in"; { - $m->content_lacks('Configuration', 'no configuration tab'); + $m->content_lacks('li-admin', 'no Admin tab'); $m->get('/Admin/'); is $m->status, 403, 'no access to /Admin/'; } @@ -32,9 +32,9 @@ RT::Test->set_rights( { $m->get('/'); - $m->content_contains('Configuration', 'configuration tab is there'); + $m->content_contains('li-admin', 'admin tab is there'); - $m->follow_link_ok({text => 'Configuration'}); + $m->follow_link_ok({text => 'Admin'}); 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 85e090cbc..995b45d99 100644 --- a/rt/t/web/crypt-gnupg.t +++ b/rt/t/web/crypt-gnupg.t @@ -2,7 +2,7 @@ use strict; use warnings; use RT::Test::GnuPG - tests => 104, + tests => undef, gnupg_options => { passphrase => 'recipient', 'trust-model' => 'always', @@ -17,9 +17,9 @@ RT->Config->Set( CorrespondAddress => 'general@example.com'); RT->Config->Set( DefaultSearchResultFormat => qq{ '<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#', '<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject', - 'OO-__OwnerName__-O', + 'OO-__Owner__-O', 'OR-__Requestors__-O', - 'KO-__KeyOwnerName__-K', + 'KO-__KeyOwner__-K', 'KR-__KeyRequestors__-K', Status}); @@ -101,7 +101,7 @@ MAIL my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', "RT's outgoing mail has crypto" ); is( $msg->GetHeader('X-RT-Incoming-Encryption'), @@ -169,7 +169,7 @@ MAIL my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', "RT's outgoing mail has crypto" ); is( $msg->GetHeader('X-RT-Incoming-Encryption'), @@ -241,7 +241,7 @@ MAIL my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', "RT's outgoing mail has crypto" ); is( $msg->GetHeader('X-RT-Incoming-Encryption'), @@ -307,7 +307,7 @@ MAIL my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', "RT's outgoing mail has crypto" ); is( $msg->GetHeader('X-RT-Incoming-Encryption'), @@ -351,8 +351,13 @@ $nokey->PrincipalObj->GrantRight(Right => 'CreateTicket'); $nokey->PrincipalObj->GrantRight(Right => 'OwnTicket'); my $tick = RT::Ticket->new( RT->SystemUser ); -$tick->Create(Subject => 'owner lacks pubkey', Queue => 'general', - Owner => $nokey); +warning_like { + $tick->Create(Subject => 'owner lacks pubkey', Queue => 'general', + Owner => $nokey); +} [ + qr/nokey\@example.com: skipped: public key not found/, + qr/Recipient 'nokey\@example.com' is unusable/, +]; ok(my $id = $tick->id, 'created ticket for owner-without-pubkey'); $tick = RT::Ticket->new( RT->SystemUser ); @@ -426,25 +431,36 @@ $m->get("$baseurl/Search/Simple.html?q=General"); my $content = $m->content; $content =~ s/(/(/g; $content =~ s/)/)/g; - -like($content, qr/OO-Nobody-O/, "original OwnerName untouched"); -like($content, qr/OO-nokey-O/, "original OwnerName untouched"); -like($content, qr/OO-root-O/, "original OwnerName untouched"); - -like($content, qr/OR-recipient\@example.com-O/, "original Requestors untouched"); -like($content, qr/OR-nokey\@example.com-O/, "original Requestors untouched"); - -like($content, qr/KO-root-K/, "KeyOwnerName does not issue no-pubkey warning for recipient"); -like($content, qr/KO-nokey \(no pubkey!\)-K/, "KeyOwnerName issues no-pubkey warning for root"); -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"); +$content =~ s/<(a|span)\b[^>]+>//g; +$content =~ s/<\/(a|span)>//g; +$content =~ s/</</g; +$content =~ s/>/>/g; + +like($content, qr/OO-Nobody in particular-O/, + "original Owner untouched"); +like($content, qr/OO-nokey-O/, + "original Owner untouched"); +like($content, qr/OO-root \(Enoch Root\)-O/, + "original Owner untouched"); +like($content, qr/OR-<recipient\@example\.com>-O/, + "original Requestors untouched"); +like($content, qr/OR-nokey-O/, + "original Requestors untouched"); + +like($content, qr/KO-Nobody in particular \(no pubkey!\)-K/, + "KeyOwner issues no-pubkey warning for nobody"); +like($content, qr/KO-nokey \(no pubkey!\)-K/, + "KeyOwner issues no-pubkey warning for root"); +like($content, qr/KO-root \(Enoch Root\)-K/, + "KeyOwner does not issue no-pubkey warning for recipient"); +like($content, qr/KR-<recipient\@example\.com>-K/, + "KeyRequestors does not issue no-pubkey warning for recipient\@example.com"); +like($content, qr/KR-nokey \(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/above error may result from an unconfigured RT\/GPG/); $m->next_warning_like(qr/public key not found/); -$m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); $m->no_leftover_warnings_ok; + +undef $m; +done_testing; diff --git a/rt/t/web/csrf.t b/rt/t/web/csrf.t index 24aae40a1..9d95d0685 100644 --- a/rt/t/web/csrf.t +++ b/rt/t/web/csrf.t @@ -99,9 +99,9 @@ $m->title_is('Possible cross-site request forgery'); my $link = $m->find_link(text_regex => qr{resume your request}); (my $broken_url = $link->url) =~ s/(CSRF_Token)=\w+/$1=crud/; $m->get_ok($broken_url); -$m->content_contains("Queue could not be loaded"); +$m->content_like(qr/Queue\s+could not be loaded/); $m->title_is('RT Error'); -$m->warning_like(qr/Queue could not be loaded/); +$m->warning_like(qr/Queue\s+could not be loaded/); # The token doesn't work for other pages, or other arguments to the same page. $m->add_header(Referer => undef); @@ -134,7 +134,7 @@ $m->content_contains("Create a new ticket", 'ticket create page'); $m->form_name('TicketCreate'); $m->field('Subject', 'Attachments test'); -my $logofile = "$RT::MasonComponentRoot/NoAuth/images/bpslogo.png"; +my $logofile = "$RT::StaticPath/images/bpslogo.png"; open LOGO, "<", $logofile or die "Can't open logo file: $!"; binmode LOGO; my $logo_contents = do {local $/; <LOGO>}; diff --git a/rt/t/web/custom_search.t b/rt/t/web/custom_search.t index bf7d659cd..75f832d30 100644 --- a/rt/t/web/custom_search.t +++ b/rt/t/web/custom_search.t @@ -11,7 +11,7 @@ my $url = $m->rt_base_url; my $t = RT::Ticket->new(RT->SystemUser); $t->Create(Subject => 'for custom search'.$$, Queue => 'general', - Owner => 'root', Requestor => 'customsearch@localhost'); + Owner => 'root', Requestor => 'customsearch@localhost'); ok(my $id = $t->id, 'created ticket for custom search'); ok $m->login, 'logged in'; diff --git a/rt/t/web/dashboards-basics.t b/rt/t/web/dashboards-basics.t index edb706810..c3533a3f1 100644 --- a/rt/t/web/dashboards-basics.t +++ b/rt/t/web/dashboards-basics.t @@ -41,24 +41,24 @@ $m->content_lacks('<a href="/Dashboards/Modify.html?Create=1">New</a>', $m->no_warnings_ok; $m->get_ok($url."Dashboards/Modify.html?Create=1"); -$m->content_contains("Permission denied"); +$m->content_contains("Permission Denied"); $m->content_lacks("Save Changes"); -$m->warning_like(qr/Permission denied/, "got a permission denied warning"); +$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_contains("Permission Denied"); $m->content_lacks("Save Changes"); -$m->warning_like(qr/Permission denied/, "got a permission denied warning"); +$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_lacks("Permission Denied"); $m->content_contains("Create"); $m->get_ok($url."Dashboards/index.html"); @@ -72,12 +72,12 @@ $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("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->content_lacks("Permission Denied"); $m->follow_link_ok({text => "different dashboard"}); $m->content_contains("Basics"); @@ -132,9 +132,9 @@ like($searches[1]->Name, qr/highest priority tickets I own/, "correct new search my $ticket = RT::Ticket->new(RT->SystemUser); $ticket->Create( Queue => $queue->Id, - Requestor => [ $user_obj->Name ], - Owner => $user_obj, - Subject => 'dashboard test', + Requestor => [ $user_obj->Name ], + Owner => $user_obj, + Subject => 'dashboard test', ); $m->follow_link_ok({id => 'page-show'}); @@ -154,8 +154,8 @@ $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"); +$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"); @@ -172,7 +172,7 @@ $m->content_unlike( qr/Bookmarked Tickets.*Bookmarked Tickets/s, $m->form_name('SubscribeDashboard'); $m->click_button(name => 'Save'); -$m->content_lacks("Permission denied"); +$m->content_lacks("Permission Denied"); $m->content_contains("Subscribed to dashboard different dashboard"); $user_obj->Attributes->RedoSearch; @@ -183,9 +183,9 @@ $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->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"); +$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); diff --git a/rt/t/web/dashboards-groups.t b/rt/t/web/dashboards-groups.t index db2fccf1c..9f1c37deb 100644 --- a/rt/t/web/dashboards-groups.t +++ b/rt/t/web/dashboards-groups.t @@ -79,7 +79,7 @@ $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("Permission denied", "we now have SeeGroupDashboard"); +$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"); @@ -95,15 +95,15 @@ is($dashboard->PossibleHiddenSearches, 0, "all searches are visible"); $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_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"); +$m->get_ok("/Prefs/DashboardsInMenu.html"); +$m->content_contains("inner dashboard", "Can also see it in the menu options"); my ($group) = grep {$_->isa("RT::Group") and $_->Id == $inner_group->Id} RT::Dashboard->new($currentuser)->_PrivacyObjects; @@ -191,5 +191,5 @@ is_deeply( $m->get_ok("/Dashboards/index.html"); $m->content_contains("inner dashboard", "The dashboards list includes superuser rights"); -$m->get_ok("/index.html"); +$m->get_ok("/Prefs/DashboardsInMenu.html"); $m->content_lacks("inner dashboard", "But the menu skips them"); diff --git a/rt/t/web/dashboards-in-menu.t b/rt/t/web/dashboards-in-menu.t new file mode 100644 index 000000000..3126d55c3 --- /dev/null +++ b/rt/t/web/dashboards-in-menu.t @@ -0,0 +1,85 @@ +use strict; +use warnings; + +use RT::Test tests => 31; +my ($baseurl, $m) = RT::Test->started_ok; + +my $system_foo = RT::Dashboard->new($RT::SystemUser); +$system_foo->Save( + Name => 'system foo', + Privacy => 'RT::System-' . $RT::System->id, +); + +my $system_bar = RT::Dashboard->new($RT::SystemUser); +$system_bar->Save( + Name => 'system bar', + Privacy => 'RT::System-' . $RT::System->id, +); + +ok( $m->login(), "logged in" ); + +diag "global setting"; +# in case "RT at a glance" contains dashboards stuff. +$m->get_ok( $baseurl . "/Search/Simple.html" ); +ok( !$m->find_link( text => 'system foo' ), 'no system foo link' ); +$m->get_ok( $baseurl."/Admin/Global/DashboardsInMenu.html"); + +my $form_name = 'SelectionBox-dashboards_in_menu'; +$m->form_name($form_name); + +$m->field('dashboards_in_menu-Available' => [$system_foo->id],); +$m->click_button(name => 'add'); +$m->content_contains('Global dashboards in menu saved.', 'saved'); + +$m->logout; +ok( $m->login(), "relogged in" ); + +$m->get_ok( $baseurl . "/Search/Simple.html" ); +$m->follow_link_ok( { text => 'system foo' }, 'follow system foo link' ); +$m->title_is( 'system foo Dashboard', 'got system foo dashboard page' ); + +diag "setting in admin users"; +my $root = RT::CurrentUser->new( $RT::SystemUser ); +ok( $root->Load('root') ); +my $self_foo = RT::Dashboard->new($root); +$self_foo->Save( Name => 'self foo', Privacy => 'RT::User-' . $root->id ); +my $self_bar = RT::Dashboard->new($root); +$self_bar->Save( Name => 'self bar', Privacy => 'RT::User-' . $root->id ); + +ok( !$m->find_link( text => 'self foo' ), 'no self foo link' ); +$m->get_ok( $baseurl."/Admin/Users/DashboardsInMenu.html?id=" . $root->id); +$m->form_name($form_name); +$m->field('dashboards_in_menu-Available' => [$self_foo->id]); +$m->click_button(name => 'add'); +$m->content_contains( 'Preferences saved for dashboards in menu.', + 'prefs saved' ); +$m->form_name($form_name); +$m->field('dashboards_in_menu-Selected' => [$system_foo->id]); +$m->content_contains( 'Preferences saved for dashboards in menu.', + 'prefs saved' ); +$m->click_button(name => 'remove'); + +$m->logout; +ok( $m->login(), "relogged in" ); +$m->get_ok( $baseurl . "/Search/Simple.html" ); +ok( !$m->find_link( text => 'system foo' ), 'no system foo link' ); +$m->follow_link_ok( { text => 'self foo' }, 'follow self foo link' ); +$m->title_is( 'self foo Dashboard', 'got self foo dashboard page' ); + +diag "setting in prefs"; +$m->get_ok( $baseurl."/Prefs/DashboardsInMenu.html"); +$m->form_name($form_name); +$m->field('dashboards_in_menu-Available' => [$self_bar->id]); +$m->click_button(name => 'add'); +$m->content_contains( 'Preferences saved for dashboards in menu.', + 'prefs saved' ); +$m->follow_link_ok( { text => 'self bar' }, 'follow self bar link' ); +$m->title_is( 'self bar Dashboard', 'got self bar dashboard page' ); +$m->get_ok( $baseurl."/Prefs/DashboardsInMenu.html"); +$m->form_with_fields('Reset'); +$m->click; +$m->content_contains( 'Preferences saved', 'prefs saved' ); +ok( $m->find_link( text => 'system foo' ), 'got system foo link' ); +ok( !$m->find_link( text => 'self foo' ), 'no self foo link' ); +ok( !$m->find_link( text => 'self bar' ), 'no self bar link' ); + diff --git a/rt/t/web/dashboards-search-cache.t b/rt/t/web/dashboards-search-cache.t index 517e26ee6..18989d54e 100644 --- a/rt/t/web/dashboards-search-cache.t +++ b/rt/t/web/dashboards-search-cache.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test tests => 20; +use RT::Test tests => 33; my ($baseurl, $m) = RT::Test->started_ok; my $url = $m->rt_base_url; @@ -20,6 +20,16 @@ $m->form_name('BuildQuery'); $m->field(SavedSearchDescription => 'Original Name'); $m->click('SavedSearchSave'); +# create the inner dashboard +$m->get_ok("$url/Dashboards/Modify.html?Create=1"); +$m->form_name('ModifyDashboard'); +$m->field('Name' => 'inner dashboard'); +$m->click_button(value => 'Create'); +$m->text_contains('Saved dashboard inner dashboard'); + +my ($inner_id) = $m->content =~ /name="id" value="(\d+)"/; +ok($inner_id, "got an ID, $inner_id"); + # create a dashboard $m->get_ok("$url/Dashboards/Modify.html?Create=1"); $m->form_name('ModifyDashboard'); @@ -34,16 +44,28 @@ ok($dashboard_id, "got an ID, $dashboard_id"); $m->follow_link_ok({text => 'Content'}); my $form = $m->form_name('Dashboard-Searches-body'); my @input = $form->find_input('Searches-body-Available'); -my ($search) = +my ($search_value) = map { ( $_->possible_values )[1] } grep { ( $_->value_names )[1] =~ /Saved Search: Original Name/ } @input; -$form->value('Searches-body-Available' => $search ); +$form->value('Searches-body-Available' => $search_value ); +$m->click_button(name => 'add'); +$m->text_contains('Dashboard updated'); + +# add the dashboard to the dashboard +$m->follow_link_ok({text => 'Content'}); +$form = $m->form_name('Dashboard-Searches-body'); +@input = $form->find_input('Searches-body-Available'); +my ($dashboard_value) = + map { ( $_->possible_values )[1] } + grep { ( $_->value_names )[1] =~ /Dashboard: inner dashboard/ } @input; +$form->value('Searches-body-Available' => $dashboard_value ); $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->text_contains('Dashboard: inner dashboard'); $m->form_name('SubscribeDashboard'); $m->click_button(name => 'Save'); $m->text_contains('Subscribed to dashboard cachey dashboard'); @@ -52,10 +74,10 @@ $m->text_contains('Subscribed to dashboard cachey dashboard'); $m->follow_link_ok({text => 'Tickets'}, 'to query builder'); $form = $m->form_name('BuildQuery'); @input = $form->find_input('SavedSearchLoad'); -($search) = +($search_value) = map { ( $_->possible_values )[1] } grep { ( $_->value_names )[1] =~ /Original Name/ } @input; -$form->value('SavedSearchLoad' => $search ); +$form->value('SavedSearchLoad' => $search_value ); $m->click_button(value => 'Load'); $m->text_contains('Loaded saved search "Original Name"'); @@ -64,10 +86,24 @@ $m->field('SavedSearchDescription' => 'New Name'); $m->click_button(value => 'Update'); $m->text_contains('Updated saved search "New Name"'); +# rename the dashboard +$m->get_ok("/Dashboards/Modify.html?id=$inner_id"); +$m->form_name('ModifyDashboard'); +$m->field('Name' => 'recursive dashboard'); +$m->click_button(value => 'Save Changes'); +$m->text_contains('Dashboard recursive dashboard updated'); + # 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 + + $m->text_contains('Dashboard: recursive dashboard'); + $m->text_unlike(qr/Dashboard: inner dashboard/); # t-w-m lacks text_lacks } + +$m->get_ok("/Dashboards/Render.html?id=$dashboard_id"); +$m->text_contains('New Name'); +$m->text_unlike(qr/Original Name/); # t-w-m lacks text_lacks diff --git a/rt/t/web/gnupg-select-keys-on-create.t b/rt/t/web/gnupg-select-keys-on-create.t index 8c1ae448c..e30b264d9 100644 --- a/rt/t/web/gnupg-select-keys-on-create.t +++ b/rt/t/web/gnupg-select-keys-on-create.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test::GnuPG tests => 83, gnupg_options => { passphrase => 'rt-test' }; +use RT::Test::GnuPG tests => 79, gnupg_options => { passphrase => 'rt-test' }; use RT::Action::SendEmail; my $queue = RT::Test->load_or_create_queue( @@ -37,7 +37,7 @@ diag "check that signing doesn't work if there is no key"; { RT::Test->import_gnupg_key('rt-recipient@example.com'); RT::Test->trust_gnupg_key('rt-recipient@example.com'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-recipient@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-recipient@example.com' ); is $res{'info'}[0]{'TrustTerse'}, 'ultimate', 'ultimately trusted key'; } @@ -66,11 +66,7 @@ diag "check that things don't work if there is no key"; my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; - for (1 .. 4) { - $m->next_warning_like(qr/public key not found/) ; - $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); - } - + $m->next_warning_like(qr/public key not found/) for 1 .. 4; $m->no_leftover_warnings_ok; } @@ -78,7 +74,7 @@ diag "import first key of rt-test\@example.com"; my $fpr1 = ''; { RT::Test->import_gnupg_key('rt-test@example.com', 'public'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); is $res{'info'}[0]{'TrustLevel'}, 0, 'is not trusted key'; $fpr1 = $res{'info'}[0]{'Fingerprint'}; } @@ -127,7 +123,7 @@ diag "import a second key of rt-test\@example.com"; my $fpr2 = ''; { RT::Test->import_gnupg_key('rt-test@example.com.2', 'public'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key'; $fpr2 = $res{'info'}[2]{'Fingerprint'}; } @@ -174,7 +170,7 @@ diag "check that things still doesn't work if two keys are not trusted"; { RT::Test->lsign_gnupg_key( $fpr1 ); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); ok $res{'info'}[0]{'TrustLevel'} > 0, 'trusted key'; is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key'; } diff --git a/rt/t/web/gnupg-select-keys-on-update.t b/rt/t/web/gnupg-select-keys-on-update.t index a5b01d3ae..a666851db 100644 --- a/rt/t/web/gnupg-select-keys-on-update.t +++ b/rt/t/web/gnupg-select-keys-on-update.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test::GnuPG tests => 88, gnupg_options => { passphrase => 'rt-test' }; +use RT::Test::GnuPG tests => 86, gnupg_options => { passphrase => 'rt-test' }; use RT::Action::SendEmail; @@ -52,7 +52,7 @@ diag "check that signing doesn't work if there is no key"; { RT::Test->import_gnupg_key('rt-recipient@example.com'); RT::Test->trust_gnupg_key('rt-recipient@example.com'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-recipient@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-recipient@example.com' ); is $res{'info'}[0]{'TrustTerse'}, 'ultimate', 'ultimately trusted key'; } @@ -82,10 +82,7 @@ diag "check that things don't work if there is no key"; my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; - for (1 .. 2) { - $m->next_warning_like(qr/public key not found/); - $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); - } + $m->next_warning_like(qr/public key not found/) for 1 .. 2; $m->no_leftover_warnings_ok; } @@ -94,7 +91,7 @@ diag "import first key of rt-test\@example.com"; my $fpr1 = ''; { RT::Test->import_gnupg_key('rt-test@example.com', 'public'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); is $res{'info'}[0]{'TrustLevel'}, 0, 'is not trusted key'; $fpr1 = $res{'info'}[0]{'Fingerprint'}; } @@ -144,7 +141,7 @@ diag "import a second key of rt-test\@example.com"; my $fpr2 = ''; { RT::Test->import_gnupg_key('rt-test@example.com.2', 'public'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key'; $fpr2 = $res{'info'}[2]{'Fingerprint'}; } @@ -192,7 +189,7 @@ diag "check that things still doesn't work if two keys are not trusted"; { RT::Test->lsign_gnupg_key( $fpr1 ); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); ok $res{'info'}[0]{'TrustLevel'} > 0, 'trusted key'; is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key'; } @@ -253,7 +250,7 @@ diag "check that key selector works and we can select trusted key"; $m->select( 'UseKey-rt-test@example.com' => $fpr1 ); $m->click('SubmitTicket'); - $m->content_contains('Message recorded', 'Message recorded' ); + $m->content_contains('Correspondence added', 'Correspondence added' ); my @mail = RT::Test->fetch_caught_mails; ok @mail, 'there are some emails'; @@ -289,7 +286,7 @@ diag "check encrypting of attachments"; $m->select( 'UseKey-rt-test@example.com' => $fpr1 ); $m->click('SubmitTicket'); - $m->content_contains('Message recorded', 'Message recorded' ); + $m->content_contains('Correspondence added', 'Correspondence added' ); my @mail = RT::Test->fetch_caught_mails; ok @mail, 'there are some emails'; diff --git a/rt/t/web/group_create.t b/rt/t/web/group_create.t index 548970d2d..f62e56595 100644 --- a/rt/t/web/group_create.t +++ b/rt/t/web/group_create.t @@ -13,7 +13,7 @@ my $group_name = 'test group'; my $group_id; diag "Create a group"; { - $m->follow_link( id => 'tools-config-groups-create'); + $m->follow_link( id => 'admin-groups-create'); # Test group form validation $m->submit_form( diff --git a/rt/t/web/helpers-http-cache-headers.t b/rt/t/web/helpers-http-cache-headers.t index 1731e9d17..1020832ca 100644 --- a/rt/t/web/helpers-http-cache-headers.t +++ b/rt/t/web/helpers-http-cache-headers.t @@ -23,10 +23,14 @@ ok $m->login, 'logged in'; my $docroot = join '/', qw(share html); # find endpoints to loop over -my @endpoints = ('/NoAuth/css/print.css'); +my @endpoints = ( + "/NoAuth/css/aileron/squished-".("0"x32).".css", + '/static/images/bpslogo.png', +); find({ wanted => sub { if ( -f $_ && $_ !~ m|autohandler$| ) { + return if m{/\.[^/]+\.sw[op]$}; # vim swap files ( my $endpoint = $_ ) =~ s|^$docroot||; push @endpoints, $endpoint; } @@ -76,7 +80,7 @@ foreach my $endpoint ( @endpoints ) { my $header_key = 'default'; if ( $endpoint =~ m|Autocomplete| ) { $header_key = 'Autocomplete'; - } elsif ( $endpoint =~ m|NoAuth| ) { + } elsif ( $endpoint =~ m/NoAuth|static/ ) { $header_key = 'NoAuth'; } my $headers = $expected->{$header_key}; 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/ModifyLoginRedirect index 90278ae49..90278ae49 100644 --- a/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/Default +++ b/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/ModifyLoginRedirect diff --git a/rt/t/web/html_template.t b/rt/t/web/html_template.t index a2764556f..108d83fa1 100644 --- a/rt/t/web/html_template.t +++ b/rt/t/web/html_template.t @@ -13,8 +13,8 @@ my $template = Encode::decode("UTF-8", "你好 éèà€"); my $subject = Encode::decode("UTF-8", "标题"); my $content = Encode::decode("UTF-8", "测试"); { - $m->follow_link_ok( { id => 'tools-config-global-templates' }, '-> Templates' ); - $m->follow_link_ok( { text => 'Autoreply' }, '-> Autoreply' ); + $m->follow_link_ok( { id => 'admin-global-templates' }, '-> Templates' ); + $m->follow_link_ok( { text => 'Autoreply in HTML' }, '-> Autoreply in HTML' ); $m->submit_form( form_name => 'ModifyTemplate', diff --git a/rt/t/web/install.t b/rt/t/web/install.t new file mode 100644 index 000000000..e33e58b16 --- /dev/null +++ b/rt/t/web/install.t @@ -0,0 +1,173 @@ +use strict; +use warnings; +use File::Spec; + +$ENV{RT_TEST_WEB_HANDLER} = 'plack+rt-server'; +use RT::Test + tests => undef, + nodb => 1, + server_ok => 1; + +my $dbname = 'rt4test_install_xxx'; +my $rtname = 'rttestname'; +my $domain = 'rttes.com'; +my $password = 'newpass'; +my $correspond = 'reply@example.com'; +my $comment = 'comment@example.com'; + +# use bin/rt to fake sendmail to make sure the file exists +my $sendmail = File::Spec->catfile( $RT::BinPath, 'rt' ); +my $owner = 'root@localhost'; + +unlink File::Spec->catfile( $RT::VarPath, $dbname ); + +my ( $url, $m ) = RT::Test->started_ok; +$m->warning_like(qr/If this is a new installation of RT/, + "Got startup warning"); + +my ($port) = $url =~ /:(\d+)/; +$m->get_ok($url); + +is( $m->uri, $url . '/Install/index.html', 'install page' ); +$m->select( 'Lang', 'zh-cn' ); +$m->click('ChangeLang'); +$m->content_contains( Encode::decode("UTF-8",'语言'), 'select chinese' ); + +$m->click('Run'); +$m->content_contains( Encode::decode("UTF-8",'数据库'), 'select db type in chinese' ); + +$m->back; +$m->select( 'Lang', 'en' ); +$m->click('ChangeLang'); +$m->content_contains( 'Select another language', 'back to english' ); + +$m->click('Run'); + +is( $m->uri, $url . '/Install/DatabaseType.html', 'db type page' ); +my $select_type = $m->current_form->find_input('DatabaseType'); +my @possible_types = $select_type->possible_values; +ok( @possible_types, 'we have at least 1 db type' ); + +SKIP: { + skip 'no mysql found', 7 unless grep { /mysql/ } @possible_types; + $m->select( 'DatabaseType', 'mysql' ); + $m->click; + for my $field (qw/Name Host Port Admin AdminPassword User Password/) { + ok( $m->current_form->find_input("Database$field"), + "db mysql has field Database$field" ); + } + $m->back; +} + +SKIP: { + skip 'no pg found', 8 unless grep { /Pg/ } @possible_types; + $m->select( 'DatabaseType', 'Pg' ); + $m->click; + for my $field ( + qw/Name Host Port Admin AdminPassword User Password/) + { + ok( $m->current_form->find_input("Database$field"), + "db Pg has field Database$field" ); + } + $m->back; +} + +$m->select( 'DatabaseType', 'SQLite' ); +$m->click; + +is( $m->uri, $url . '/Install/DatabaseDetails.html', 'db details page' ); +$m->field( 'DatabaseName' => $dbname ); +$m->submit_form( fields => { DatabaseName => $dbname } ); +$m->content_contains( 'Connection succeeded', 'succeed msg' ); +$m->content_contains( +qq{$dbname already exists, but does not contain RT's tables or metadata. The 'Initialize Database' step later on can insert tables and metadata into this existing database. if this is acceptable, click 'Customize Basic' below to continue customizing RT.}, + 'more db state msg' +); +$m->click; + +is( $m->uri, $url . '/Install/Basics.html', 'basics page' ); +$m->click; +$m->content_contains( + 'You must enter an Administrative password', + "got password can't be empty error" +); + +for my $field (qw/rtname WebDomain WebPort Password/) { + ok( $m->current_form->find_input($field), "has field $field" ); +} +is( $m->value('WebPort'), $port, 'default port' ); +$m->field( 'rtname' => $rtname ); +$m->field( 'WebDomain' => $domain ); +$m->field( 'Password' => $password ); +$m->click; + +is( $m->uri, $url . '/Install/Sendmail.html', 'mail page' ); +for my $field (qw/SendmailPath OwnerEmail/) { + ok( $m->current_form->find_input($field), "has field $field" ); +} + +$m->field( 'OwnerEmail' => '' ); +$m->click; +$m->content_contains( "doesn't look like an email address", + 'got email error' ); + +$m->field( 'SendmailPath' => '/fake/path/sendmail' ); +$m->click; +$m->content_contains( "/fake/path/sendmail doesn't exist", + 'got sendmail error' ); + +$m->field( 'SendmailPath' => $sendmail ); +$m->field( 'OwnerEmail' => $owner ); +$m->click; + +is( $m->uri, $url . '/Install/Global.html', 'global page' ); +for my $field (qw/CommentAddress CorrespondAddress/) { + ok( $m->current_form->find_input($field), "has field $field" ); +} + +$m->click; +is( $m->uri, $url . '/Install/Initialize.html', 'init db page' ); +$m->back; + +is( $m->uri, $url . '/Install/Global.html', 'global page' ); +$m->field( 'CorrespondAddress' => 'reply' ); +$m->click; +$m->content_contains( "doesn't look like an email address", + 'got email error' ); +$m->field( 'CommentAddress' => 'comment' ); +$m->click; +$m->content_contains( "doesn't look like an email address", + 'got email error' ); + +$m->field( 'CorrespondAddress' => 'reply@example.com' ); +$m->field( 'CommentAddress' => 'comment@example.com' ); +$m->click; + +is( $m->uri, $url . '/Install/Initialize.html', 'init db page' ); +$m->click; + +is( $m->uri, $url . '/Install/Finish.html', 'finish page' ); +$m->click; + +is( $m->uri, $url . '/', 'home page' ); +$m->login( 'root', $password ); +$m->content_contains( 'RT at a glance', 'logged in with newpass' ); + +RT->LoadConfig; +my $config = RT->Config; + +is( $config->Get('DatabaseType'), 'SQLite', 'DatabaseType in config' ); +is( $config->Get('DatabaseName'), $dbname, 'DatabaseName in config' ); +is( $config->Get('rtname'), $rtname, 'rtname in config' ); +is( $config->Get('WebDomain'), $domain, 'WebDomain email in config' ); +is( $config->Get('WebPort'), $port, 'WebPort email in config' ); +is( $config->Get('SendmailPath'), $sendmail, 'SendmailPath in config' ); +is( $config->Get('OwnerEmail'), $owner, 'OwnerEmail in config' ); +is( $config->Get('CorrespondAddress'), + $correspond, 'correspond address in config' ); +is( $config->Get('CommentAddress'), $comment, 'comment address in config' ); + +unlink File::Spec->catfile( $RT::VarPath, $dbname ); + +undef $m; +done_testing; diff --git a/rt/t/web/language_update.t b/rt/t/web/language_update.t new file mode 100644 index 000000000..35082f886 --- /dev/null +++ b/rt/t/web/language_update.t @@ -0,0 +1,22 @@ +use strict; +use warnings; +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 => 'zh_TW'); +$m->submit; + +$m->text_contains(Encode::decode("UTF-8","並讓現存的 iCal feeds不再能用"), "successfully updated to zh_TW"); +$m->text_contains(Encode::decode("UTF-8","使用語言 的值從 (無) 改為 'zh_TW'"), "when updating to language zh_TW, results are in zh_TW"); + +$m->form_with_fields('Lang'); +$m->field(Lang => 'en_us'); +$m->submit; + +$m->text_contains("breaking all existing iCal feeds", "successfully updated to en_us"); +$m->text_contains("Lang changed from 'zh_TW' to 'en_us'", "when updating to language en_us, results are in en_us"); + diff --git a/rt/t/web/login.t b/rt/t/web/login.t index d0213c373..4b3620d41 100644 --- a/rt/t/web/login.t +++ b/rt/t/web/login.t @@ -1,7 +1,9 @@ use strict; use warnings; -use RT::Test tests => 34; +use RT::Test; + +RT::Config->Set(AllowLoginPasswordAutoComplete => 1); my ( $baseurl, $m ) = RT::Test->started_ok; @@ -17,6 +19,7 @@ diag "normal login"; $m->get($baseurl); $m->title_is('Login'); is( $m->uri, $baseurl, "right url" ); + $m->content_lacks('autocomplete="off"'); $m->submit_form( form_id => 'login', diff --git a/rt/t/web/mobile.t b/rt/t/web/mobile.t new file mode 100644 index 000000000..3f32e49e6 --- /dev/null +++ b/rt/t/web/mobile.t @@ -0,0 +1,210 @@ +use strict; +use warnings; +use RT::Test tests => 170; + +my ( $url, $m ) = RT::Test->started_ok; +my $root = RT::Test->load_or_create_user( Name => 'root' ); + +diag "create another queue"; +my $test_queue = RT::Queue->new( $RT::SystemUser ); +ok( $test_queue->Create( Name => 'foo' ) ); + +diag "create cf cfbar"; +my $cfbar = RT::CustomField->new( $RT::SystemUser ); +ok( + $cfbar->Create( + Name => 'cfbar', + Type => 'Freeform', + LookupType => 'RT::Queue-RT::Ticket' + ) +); + +$cfbar->AddToObject( $test_queue ); + +diag "create some tickets to link"; +# yep, create 3 tickets for DependsOn +my @tickets = map { { Subject => "link of $_" } } + qw/DependsOn DependsOn DependsOn DependedOnBy HasMember HasMember + MemberOf RefersTo RefersTo ReferredToBy/; +RT::Test->create_tickets( { Status => 'resolved' }, @tickets ); + +diag "test different mobile agents"; +my @agents = ( + 'hiptop', 'Blazer', 'Novarra', 'Vagabond', + 'SonyEricsson', 'Symbian', 'NetFront', 'UP.Browser', + 'UP.Link', 'Windows CE', 'MIDP', 'J2ME', + 'DoCoMo', 'J-PHONE', 'PalmOS', 'PalmSource', + 'iPhone', 'iPod', 'AvantGo', 'Nokia', + 'Android', 'WebOS', 'S60' +); + +for my $agent (@agents) { + $m->agent($agent); + $m->get_ok($url); + $m->content_contains( 'Not using a mobile browser', + "mobile login page for agent $agent" ); +} + +$m->submit_form( fields => { user => 'root', pass => 'password' } ); +is( $m->uri, $url . '/m/', 'logged in via mobile ui' ); +ok( $m->find_link( text => 'Home' ), 'has homepage link, so really logged in' ); + +diag "create some tickets"; +$m->follow_link_ok( { text => 'New ticket' } ); +like( $m->uri, qr'/m/ticket/select_create_queue', 'queue select page' ); +$m->follow_link_ok( { text => 'General' } ); +like( $m->uri, qr'/m/ticket/create', 'ticket create page' ); +$m->submit_form( + fields => { + Subject => 'ticket1', + Content => 'content 1', + Status => 'open', + Cc => 'cc@example.com', + AdminCc => 'admincc@example.com', + InitialPriority => 13, + FinalPriority => 93, + TimeEstimated => 2, + 'TimeEstimated-TimeUnits' => 'hours', + TimeWorked => 30, + TimeLeft => 60, + Starts => '2011-01-11 11:11:11', + Due => '2011-02-12 12:12:12', + 'new-DependsOn' => '1 2 3', + 'DependsOn-new' => '4', + 'new-MemberOf' => '5 6', + 'MemberOf-new' => '7', + 'new-RefersTo' => '8 9', + 'RefersTo-new' => '10', + } +); +like( $m->uri, qr'/m/ticket/show', 'ticket show page' ); +$m->content_contains( 'ticket1', 'subject' ); +$m->content_contains( 'open', 'status' ); +$m->content_contains( 'cc@example.com', 'cc' ); +$m->content_contains( 'admincc@example.com', 'admincc' ); +$m->content_contains( '13/93', 'priority' ); +$m->content_contains( '2 hour', 'time estimates' ); +$m->content_contains( '30 min', 'time worked' ); +$m->content_contains( '60 min', 'time left' ); +$m->content_contains( 'Tue Jan 11 11:11:11', 'starts' ); +$m->content_contains( 'Sat Feb 12 12:12:12', 'due' ); +$m->content_like( qr/(link of DependsOn).*\1.*\1/s, 'depends on' ); +$m->content_contains( 'link of DependedOnBy', 'depended on by' ); +$m->content_like( qr/(link of HasMember).*\1/s, 'has member' ); +$m->content_contains( 'link of MemberOf', 'member of' ); +$m->content_like( qr/(link of RefersTo).*\1/s, 'refers to' ); +$m->content_contains( 'link of ReferredToBy', 'referred to by' ); + +diag "test ticket reply"; +$m->follow_link_ok( { text => 'Reply' } ); +like( $m->uri, qr'/m/ticket/reply', 'ticket reply page' ); +$m->submit_form( + fields => { + UpdateContent => 'reply 1', + UpdateTimeWorked => '30', + UpdateStatus => 'resolved', + UpdateType => 'response', + }, + button => 'SubmitTicket', +); +like( $m->uri, qr'/m/ticket/show', 'back to ticket show page' ); +$m->content_contains( '1 hour', 'time worked' ); +$m->content_contains( 'resolved', 'status' ); +$m->follow_link_ok( { text => 'Reply' } ); +like( $m->uri, qr'/m/ticket/reply', 'ticket reply page' ); +$m->submit_form( + fields => { + UpdateContent => 'reply 2', + UpdateSubject => 'ticket1', + UpdateStatus => 'open', + UpdateType => 'private', + }, + button => 'SubmitTicket', +); +$m->no_warnings_ok; +$m->content_contains( 'ticket1', 'subject' ); +$m->content_contains( 'open', 'status' ); + +like( $m->uri, qr'/m/ticket/show', 'back to ticket show page' ); + +diag "test ticket history"; +$m->follow_link_ok( { text => 'History' } ); +like( $m->uri, qr'/m/ticket/history', 'ticket history page' ); +$m->content_contains( 'content 1', 'has main content' ); +$m->content_contains( 'reply 1', 'has replied content' ); +$m->content_contains( 'reply 2', 'has replied content' ); + +diag "create another ticket in queue foo"; +$m->follow_link_ok( { text => 'Home' } ); +is( $m->uri, "$url/m/", 'main mobile page' ); +$m->follow_link_ok( { text => 'New ticket' } ); +like( $m->uri, qr'/m/ticket/select_create_queue', 'queue select page' ); +$m->follow_link_ok( { text => 'foo' } ); +like( $m->uri, qr'/m/ticket/create', 'ticket create page' ); +$m->content_contains( 'cfbar', 'has cf name' ); +$m->content_contains( 'Object-RT::Ticket--CustomField-' . $cfbar->id . '-Value', 'has cf input name' ); +$m->submit_form( + fields => { + Subject => 'ticket2', + Content => 'content 2', + Owner => $root->id, + 'Object-RT::Ticket--CustomField-' . $cfbar->id . '-Value' => 'cfvalue', + } +); +$m->no_warnings_ok; +like( $m->uri, qr'/m/ticket/show', 'ticket show page' ); +$m->content_contains( 'cfbar', 'has cf name' ); +$m->content_contains( 'cfvalue', 'has cf value' ); + +$m->follow_link_ok( { text => 'Home' } ); +is( $m->uri, "$url/m/", 'main mobile page' ); + +diag "test unowned tickets link"; +$m->follow_link_ok( { text => 'Unowned tickets' } ); +$m->content_contains( 'Found 1 ticket', 'found 1 ticket' ); +$m->content_contains( 'ticket1', 'has ticket1' ); +$m->content_lacks( 'ticket2', 'no ticket2' ); +$m->back; + +diag "test tickets I own link"; +$m->follow_link_ok( { text => 'Tickets I own' } ); +$m->content_contains( 'Found 1 ticket', 'found 1 ticket' ); +$m->content_lacks( 'ticket1', 'no ticket1' ); +ok( $m->find_link( text_regex => qr/ticket2/ ), 'has ticket2 link' ); +$m->back; + +diag "test all tickets link"; +$m->follow_link_ok( { text => 'All tickets' } ); +$m->content_contains( 'Found 12 tickets', 'found 12 tickets' ); +ok( $m->find_link( text_regex => qr/ticket1/ ), 'has ticket1 link' ); +ok( $m->find_link( text_regex => qr/ticket2/ ), 'has ticket2 link' ); +$m->back; + +diag "test bookmarked tickets link"; +my $ticket = RT::Ticket->new(RT::CurrentUser->new('root')); +$ticket->Load(11); +$root->ToggleBookmark($ticket); + +$m->follow_link_ok( { text => 'Bookmarked tickets' } ); +$m->content_contains( 'Found 1 ticket', 'found 1 ticket' ); +ok( $m->find_link( text_regex => qr/ticket1/ ), 'has ticket1 link' ); +$m->content_lacks( 'ticket2', 'no ticket2' ); +$m->back; + +diag "test tickets search"; +$m->submit_form( fields => { q => 'ticket2' } ); +$m->content_contains( 'Found 1 ticket', 'found 1 ticket' ); +$m->content_lacks( 'ticket1', 'no ticket1' ); +ok( $m->find_link( text_regex => qr/ticket2/ ), 'has ticket2 link' ); +$m->back; + +diag "test logout link"; +$m->follow_link_ok( { text => 'Logout' } ); +is( $m->uri, "$url/m/", 'still in mobile' ); +$m->submit_form( fields => { user => 'root', pass => 'password' } ); + +diag "test notmobile link"; +$m->follow_link_ok( { text => 'Home' } ); +$m->follow_link_ok( { text => 'Not using a mobile browser?' } ); +is( $m->uri, $url . '/', 'got full ui' ); + diff --git a/rt/t/web/offline.t b/rt/t/web/offline.t deleted file mode 100644 index 06d51913a..000000000 --- a/rt/t/web/offline.t +++ /dev/null @@ -1,77 +0,0 @@ -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 deleted file mode 100644 index 4cf6954bd..000000000 --- a/rt/t/web/offline_messages_utf8.t +++ /dev/null @@ -1,64 +0,0 @@ -use strict; -use warnings; - -use RT::Test tests => 8; -use RT::Ticket; - -my ( $url, $m ) = RT::Test->started_ok; -$m->default_header( 'Accept-Language' => "zh-tw" ); -ok( $m->login, 'logged in' ); - -my $ticket_id; -my $template; - -{ - - # test create message - $template = <<EOF; -===Create-Ticket: ticket1 -Queue: General -Subject: test message -Status: new -Content: -ENDOFCONTENT -Due: -TimeEstimated: 100 -TimeLeft: 100 -FinalPriority: 90 -EOF - - $m->get_ok( $url . '/Tools/Offline.html' ); - - $m->submit_form( - form_name => 'TicketUpdate', - fields => { string => $template, }, - button => 'UpdateTickets', - ); - my $content = Encode::encode("UTF-8", $m->content); - ok( $content =~ m/申請單 #(\d+) 成功新增於 'General' 表單/, 'message is shown right' ); - $ticket_id = $1; -} - -{ - - # test update message - $template = <<EOF; -===Update-Ticket: 1 -Subject: test message update -EOF - - $m->get_ok( $url . '/Tools/Offline.html' ); - $m->submit_form( - form_name => 'TicketUpdate', - fields => { string => $template, }, - button => 'UpdateTickets', - ); - - my $content = Encode::encode("UTF-8", $m->content); - ok( - $content =~ -qr/主題\s*的值從\s*'test message'\s*改為\s*'test message update'/, - 'subject is updated' - ); -} - diff --git a/rt/t/web/offline_utf8.t b/rt/t/web/offline_utf8.t deleted file mode 100644 index aab3049a3..000000000 --- a/rt/t/web/offline_utf8.t +++ /dev/null @@ -1,53 +0,0 @@ -use strict; -use warnings; - -use RT::Test tests => 9; - -use RT::Ticket; -my $file = File::Spec->catfile( RT::Test->temp_directory, 'template' ); -open my $fh, '>', $file or die $!; -my $template = Encode::decode("UTF-8",<<EOF); -===Create-Ticket: ticket1 -Queue: General -Subject: 标题 -Status: new -Content: -这是正文 -ENDOFCONTENT -EOF - -print $fh Encode::encode("UTF-8",$template); -close $fh; - -my ( $url, $m ) = RT::Test->started_ok; -ok( $m->login, 'logged in' ); - -$m->get_ok( $url . '/Tools/Offline.html' ); - -$m->submit_form( - form_name => 'TicketUpdate', - fields => { Template => $file, }, - button => 'Parse', -); - -$m->content_contains( Encode::decode("UTF-8",'这是正文'), 'content is parsed right' ); - -$m->submit_form( - form_name => 'TicketUpdate', - button => 'UpdateTickets', - - # mimic what browsers do: they seems decoded $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 ); -$ticket->Load( $ticket_id ); -is( $ticket->Subject, Encode::decode("UTF-8",'标题'), 'subject in $ticket is right' ); - -$m->goto_ticket($ticket_id); -$m->content_contains( Encode::decode("UTF-8",'这是正文'), - 'content is right in ticket display page' ); - diff --git a/rt/t/web/owner_disabled_group_19221.t b/rt/t/web/owner_disabled_group_19221.t index d41decfd2..b71fc5b3e 100644 --- a/rt/t/web/owner_disabled_group_19221.t +++ b/rt/t/web/owner_disabled_group_19221.t @@ -122,7 +122,7 @@ diag "Check WithMember and WithoutMember recursively"; { my $with = RT::Groups->new( RT->SystemUser ); $with->WithMember( PrincipalId => $user->PrincipalObj->Id, Recursively => 1 ); - $with->Limit( FIELD => 'domain', OPERATOR => '=', VALUE => 'UserDefined' ); + $with->LimitToUserDefinedGroups; is_deeply( [map {$_->Name} @{$with->ItemsArrayRef}], ['Disabled Group','Supergroup'], @@ -131,7 +131,7 @@ diag "Check WithMember and WithoutMember recursively"; my $without = RT::Groups->new( RT->SystemUser ); $without->WithoutMember( PrincipalId => $user->PrincipalObj->Id, Recursively => 1 ); - $without->Limit( FIELD => 'domain', OPERATOR => '=', VALUE => 'UserDefined' ); + $without->LimitToUserDefinedGroups; is_deeply( [map {$_->Name} @{$without->ItemsArrayRef}], [], diff --git a/rt/t/web/path-traversal.t b/rt/t/web/path-traversal.t index 01302e672..8207265ef 100644 --- a/rt/t/web/path-traversal.t +++ b/rt/t/web/path-traversal.t @@ -8,35 +8,30 @@ ok($agent->login); $agent->get("$baseurl/NoAuth/../Elements/HeaderJavascript"); is($agent->status, 400); -$agent->warning_like(qr/Invalid request.*aborting/,); +$agent->warning_like(qr/Invalid request.*aborting/); $agent->get("$baseurl/NoAuth/../%45lements/HeaderJavascript"); is($agent->status, 400); -$agent->warning_like(qr/Invalid request.*aborting/,); +$agent->warning_like(qr/Invalid request.*aborting/); $agent->get("$baseurl/NoAuth/%2E%2E/Elements/HeaderJavascript"); is($agent->status, 400); -$agent->warning_like(qr/Invalid request.*aborting/,); +$agent->warning_like(qr/Invalid request.*aborting/); $agent->get("$baseurl/NoAuth/../../../etc/RT_Config.pm"); is($agent->status, 400); -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->warning_like(qr/Invalid request.*aborting/) unless $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/; -$agent->get("$baseurl/NoAuth/css/web2/images/../../../../../../etc/RT_Config.pm"); -is($agent->status, 400); -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/static/css/web2/images/../../../../../../etc/RT_Config.pm"); +# Apache hardcodes a 400m but the static handler returns a 403 for traversal too high +is($agent->status, $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/ ? 400 : 403); # Do not reject a simple /. in the URL, for downloading uploaded # dotfiles, for example. $agent->get("$baseurl/Ticket/Attachment/28/9/.bashrc"); is($agent->status, 200); # Even for a file not found, we return 200 -$agent->content_contains("Bad attachment id"); +$agent->next_warning_like(qr/could not be loaded/, "couldn't loaded warning"); +$agent->content_like(qr/Attachment \S+ could not be loaded/); # 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/psgi-wrap.t b/rt/t/web/psgi-wrap.t new file mode 100644 index 000000000..0e4b0532b --- /dev/null +++ b/rt/t/web/psgi-wrap.t @@ -0,0 +1,15 @@ +use strict; +use warnings; + +use RT::Test + tests => undef, + plugins => [qw(RT::Extension::PSGIWrap)]; + +my ($base, $m) = RT::Test->started_ok; +$m->login; +ok(my $res = $m->get("/")); +is($res->code, 200, 'Successful request to /'); +ok($res->header('X-RT-PSGIWrap'), 'X-RT-PSGIWrap header set from the plugin'); + +undef $m; +done_testing(); diff --git a/rt/t/web/query_builder.t b/rt/t/web/query_builder.t index 3589c381a..dbe909939 100644 --- a/rt/t/web/query_builder.t +++ b/rt/t/web/query_builder.t @@ -196,7 +196,7 @@ diag "click advanced, enter 'C1 OR ( C2 AND C3 )', apply, aggregators should sta # create a custom field with nonascii name and try to add a condition { my $cf = RT::CustomField->new( RT->SystemUser ); - $cf->LoadByName( Name => "\x{442}", Queue => 0 ); + $cf->LoadByName( Name => "\x{442}", LookupType => RT::Ticket->CustomFieldLookupType, ObjectId => 0 ); if ( $cf->id ) { is($cf->Type, 'Freeform', 'loaded and type is correct'); } else { @@ -212,10 +212,10 @@ diag "click advanced, enter 'C1 OR ( C2 AND C3 )', apply, aggregators should sta ok( $response->is_success, "Fetched " . $url."Search/Build.html" ); ok($agent->form_name('BuildQuery'), "found the form once"); - $agent->field("ValueOf'CF.{\x{442}}'", "\x{441}"); + $agent->field("ValueOfCF.{\x{442}}", "\x{441}"); $agent->submit(); is( getQueryFromForm($agent), - "'CF.{\x{442}}' LIKE '\x{441}'", + "CF.{\x{442}} LIKE '\x{441}'", "no changes, no duplicate condition with badly encoded text" ); diff --git a/rt/t/web/query_builder_queue_limits.t b/rt/t/web/query_builder_queue_limits.t index 332cc939c..6bbf33386 100644 --- a/rt/t/web/query_builder_queue_limits.t +++ b/rt/t/web/query_builder_queue_limits.t @@ -71,9 +71,9 @@ $m->get_ok( $url . '/Search/Build.html' ); diag "check default statuses, cf and owners"; my $form = $m->form_name('BuildQuery'); ok( $form, 'found BuildQuery form' ); -ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf by default' ); -ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'no general_cf by default' ); -ok( !$form->find_input("ValueOf'CF.{foo_cf}'"), 'no foo_cf by default' ); +ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf by default' ); +ok( !$form->find_input("ValueOfCF.{general_cf}"), 'no general_cf by default' ); +ok( !$form->find_input("ValueOfCF.{foo_cf}"), 'no foo_cf by default' ); my $status_input = $form->find_input('ValueOfStatus'); my @statuses = sort $status_input->possible_values; @@ -94,9 +94,9 @@ $m->submit_form( ); $form = $m->form_name('BuildQuery'); -ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' ); -ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); -ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'still no general_cf' ); +ok( $form->find_input("ValueOfCF.{foo_cf}"), 'found foo_cf' ); +ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' ); +ok( !$form->find_input("ValueOfCF.{general_cf}"), 'still no general_cf' ); $status_input = $form->find_input('ValueOfStatus'); @statuses = sort $status_input->possible_values; is_deeply( @@ -119,9 +119,9 @@ $m->submit_form( ); $form = $m->form_name('BuildQuery'); -ok( $form->find_input("ValueOf'CF.{general_cf}'"), 'found general_cf' ); -ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' ); -ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); +ok( $form->find_input("ValueOfCF.{general_cf}"), 'found general_cf' ); +ok( $form->find_input("ValueOfCF.{foo_cf}"), 'found foo_cf' ); +ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' ); $status_input = $form->find_input('ValueOfStatus'); @statuses = sort $status_input->possible_values; is_deeply( @@ -144,9 +144,9 @@ $m->submit_form( ); $form = $m->form_name('BuildQuery'); -ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); -ok( !$form->find_input("ValueOf'CF.{foo_cf}'"), 'no foo_cf' ); -ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'no general_cf' ); +ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' ); +ok( !$form->find_input("ValueOfCF.{foo_cf}"), 'no foo_cf' ); +ok( !$form->find_input("ValueOfCF.{general_cf}"), 'no general_cf' ); $status_input = $form->find_input('ValueOfStatus'); @statuses = sort $status_input->possible_values; is_deeply( @@ -166,9 +166,9 @@ $m->submit_form( fields => { Query => q{Queue = 'General' OR Queue = 'foo'} }, ); $form = $m->form_name('BuildQuery'); -ok( $form->find_input("ValueOf'CF.{general_cf}'"), 'found general_cf' ); -ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' ); -ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); +ok( $form->find_input("ValueOfCF.{general_cf}"), 'found general_cf' ); +ok( $form->find_input("ValueOfCF.{foo_cf}"), 'found foo_cf' ); +ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' ); $status_input = $form->find_input('ValueOfStatus'); @statuses = sort $status_input->possible_values; is_deeply( diff --git a/rt/t/web/query_log.t b/rt/t/web/query_log.t index 89cca2d7b..cfb4d81d7 100644 --- a/rt/t/web/query_log.t +++ b/rt/t/web/query_log.t @@ -14,6 +14,5 @@ $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_like(qr{/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_create.t b/rt/t/web/queue_create.t index 566876231..40f7b3b8b 100644 --- a/rt/t/web/queue_create.t +++ b/rt/t/web/queue_create.t @@ -13,7 +13,7 @@ my $queue_name = 'test queue'; my $queue_id; diag "Create a queue"; { - $m->follow_link( id => 'tools-config-queues-create'); + $m->follow_link( id => 'admin-queues-create'); # Test queue form validation $m->submit_form( diff --git a/rt/t/web/redirect-after-login.t b/rt/t/web/redirect-after-login.t index 35025a1e1..eb2718cf3 100644 --- a/rt/t/web/redirect-after-login.t +++ b/rt/t/web/redirect-after-login.t @@ -4,8 +4,6 @@ use warnings; use RT::Test tests => 122; -RT->Config->Set( GnuPG => Enable => 0 ); - my ($baseurl, $agent) = RT::Test->started_ok; my $url = $agent->rt_base_url; @@ -226,7 +224,7 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) { unlike($agent->content, qr/Your username or password is incorrect/, "didn't get any error message"); } -# XXX TODO: we should also be testing WebExternalAuth here, but we don't have +# XXX TODO: we should also be testing WebRemoteUserAuth here, but we don't have # the framework for dealing with that 1; diff --git a/rt/t/web/reminder-permissions.t b/rt/t/web/reminder-permissions.t new file mode 100644 index 000000000..dd859cd33 --- /dev/null +++ b/rt/t/web/reminder-permissions.t @@ -0,0 +1,178 @@ +use strict; +use warnings; +use RT::Test tests => 40; + +my $user_a = RT::Test->load_or_create_user( + Name => 'user_a', + Password => 'password', +); + +ok( $user_a && $user_a->id, 'created user_a' ); +ok( + RT::Test->add_rights( + { + Principal => $user_a, + Right => [qw/SeeQueue CreateTicket ShowTicket OwnTicket/] + }, + ), + 'add basic rights for user_a' +); + +ok( + RT::Test->add_rights( + { + Principal => 'Owner', + Right => [qw/ModifyTicket/], + }, + ), + 'add basic rights for owner' +); + +my $ticket = RT::Test->create_ticket( + Subject => 'test reminder permission', + Queue => 'General', +); + +ok( $ticket->id, 'created a ticket' ); + +my ( $baseurl, $m ) = RT::Test->started_ok; +$m->login; + +my ( $root_reminder_id, $user_a_reminder_id ); +diag "create two reminders, with owner root and user_a, respectively"; +{ + $m->goto_ticket( $ticket->id ); + $m->text_contains( 'New reminder:', 'can create a new reminder' ); + $m->form_name('UpdateReminders'); + $m->field( 'NewReminder-Subject' => "root reminder" ); + $m->submit; + $m->text_contains( "Reminder 'root reminder': Created", + 'created root reminder' ); + + $m->form_name('UpdateReminders'); + $m->field( 'NewReminder-Subject' => "user_a reminder", ); + $m->field( 'NewReminder-Owner' => $user_a->id, ); + $m->submit; + $m->text_contains( "Reminder 'user_a reminder': Created", + 'created user_a reminder' ); + + my $reminders = RT::Reminders->new($user_a); + $reminders->Ticket( $ticket->id ); + my $col = $reminders->Collection; + while ( my $c = $col->Next ) { + if ( $c->Subject eq 'root reminder' ) { + $root_reminder_id = $c->id; + } + elsif ( $c->Subject eq 'user_a reminder' ) { + $user_a_reminder_id = $c->id; + } + } +} + +diag "check root_a can update user_a reminder but not root reminder"; +my $m_a = RT::Test::Web->new; +{ + ok( $m_a->login( user_a => 'password' ), 'logged in as user_a' ); + $m_a->goto_ticket( $ticket->id ); + $m_a->content_lacks( 'New reminder:', 'can not create a new reminder' ); + $m_a->content_contains( 'root reminder', 'can see root reminder' ); + $m_a->content_contains( 'user_a reminder', 'can see user_a reminder' ); + $m_a->content_like( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is disabled" + ); + + $m_a->form_name('UpdateReminders'); + $m_a->tick( "Complete-Reminder-$user_a_reminder_id" => 1 ); + $m_a->submit; + $m_a->text_contains( + "Reminder 'user_a reminder': Status changed from 'open' to 'resolved'", + 'complete user_a reminder' ); + + $m_a->follow_link_ok( { id => 'page-reminders' } ); + $m_a->title_is( "Reminders for ticket #" . $ticket->id ); + $m_a->content_contains( 'root reminder', 'can see root reminder' ); + $m_a->content_contains( 'user_a reminder', 'can see user_a reminder' ); + $m_a->content_lacks( 'New reminder:', 'can not create a new reminder' ); + $m_a->content_like( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is disabled" + ); + + $m_a->form_name('UpdateReminders'); + $m_a->untick( "Complete-Reminder-$user_a_reminder_id", 1 ); + $m_a->submit; + $m_a->text_contains( + "Reminder 'user_a reminder': Status changed from 'resolved' to 'open'", + 'reopen user_a reminder' + ); + +} + +diag "set ticket owner to user_a to let user_a grant modify ticket right"; +{ + $ticket->SetOwner( $user_a->id ); + + $m_a->goto_ticket( $ticket->id ); + $m_a->content_contains( 'New reminder:', 'can create a new reminder' ); + $m_a->content_like( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is still disabled" + ); + $m_a->form_name('UpdateReminders'); + $m_a->field( 'NewReminder-Subject' => "user_a from display reminder" ); + $m_a->submit; + $m_a->text_contains( "Reminder 'user_a from display reminder': Created", + 'created user_a from display reminder' ); + + $m_a->follow_link_ok( { id => 'page-reminders' } ); + $m_a->title_is( "Reminders for ticket #" . $ticket->id ); + $m_a->content_contains( 'New reminder:', 'can create a new reminder' ); + $m_a->content_like( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is still disabled" + ); + $m_a->form_name('UpdateReminders'); + $m_a->field( 'NewReminder-Subject' => "user_a from reminders reminder" ); + $m_a->submit; + $m_a->text_contains( "Reminder 'user_a from reminders reminder': Created", + 'created user_a from reminders reminder' ); +} + +diag "grant user_a with ModifyTicket globally"; +{ + ok( + RT::Test->add_rights( + { + Principal => $user_a, + Right => [qw/ModifyTicket/], + }, + ), + 'add ModifyTicket rights to user_a' + ); + + $m_a->goto_ticket( $ticket->id ); + $m_a->content_unlike( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is enabled" + ); + $m_a->form_name('UpdateReminders'); + $m_a->tick( "Complete-Reminder-$root_reminder_id" => 1 ); + $m_a->submit; + $m_a->text_contains( + "Reminder 'root reminder': Status changed from 'open' to 'resolved'", + 'complete root reminder' ); + + $m_a->follow_link_ok( { id => 'page-reminders' } ); + $m_a->content_unlike( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is enabled" + ); + $m_a->form_name('UpdateReminders'); + $m_a->untick( "Complete-Reminder-$root_reminder_id" => 1 ); + $m_a->submit; + $m_a->text_contains( + "Reminder 'root reminder': Status changed from 'resolved' to 'open'", + 'reopen root reminder' ); +} + diff --git a/rt/t/web/reminders.t b/rt/t/web/reminders.t index 510235156..98a8d6954 100644 --- a/rt/t/web/reminders.t +++ b/rt/t/web/reminders.t @@ -26,7 +26,7 @@ $m->goto_ticket($ticket->id); $m->form_name('UpdateReminders'); $m->field( 'NewReminder-Subject' => "baby's first reminder" ); $m->submit; -$m->content_contains("Reminder 'baby's first reminder' added"); +$m->content_contains("Reminder 'baby's first reminder': Created"); $ticket->SetStatus('deleted'); is( $ticket->Status, 'deleted', 'deleted ticket' ); diff --git a/rt/t/web/remote_user.t b/rt/t/web/remote_user.t index edad6ef95..c17a93379 100644 --- a/rt/t/web/remote_user.t +++ b/rt/t/web/remote_user.t @@ -1,36 +1,197 @@ use strict; use warnings; use RT; -use RT::Test tests => 9; -use MIME::Base64 qw//; +use RT::Test plan => 'no_plan'; -RT->Config->Set( DevelMode => 0 ); -RT->Config->Set( WebExternalAuth => 1 ); +sub stop_server { + my $mech = shift; -sub auth { - return Authorization => "Basic " . - MIME::Base64::encode( join(":", @_) ); + # Ensure we're logged in for the final warnings check + $$mech->auth("root"); + + # Force the warnings check before we stop the server + undef $$mech; + + RT::Test->stop_server; +} + +diag "Continuous + Fallback"; +{ + RT->Config->Set( DevelMode => 0 ); + RT->Config->Set( WebRemoteUserAuth => 1 ); + RT->Config->Set( WebRemoteUserAuthContinuous => 1 ); + RT->Config->Set( WebFallbackToRTLogin => 1 ); + RT->Config->Set( WebRemoteUserAutocreate => 0 ); + + my ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' ); + + diag "Internal auth"; + { + # Empty REMOTE_USER + $m->auth(""); + + # First request gets the login form + $m->get_ok($url, "No basic auth is OK"); + $m->content_like(qr/Login/, "Login form"); + + # Log in using RT's form + $m->submit_form_ok({ + with_fields => { + user => 'root', + pass => 'password', + }, + }, "Submitted login form"); + ok $m->logged_in_as("root"), "Logged in as root"; + + # Still logged in on another request without REMOTE_USER + $m->follow_link_ok({ text => 'My Tickets' }); + ok $m->logged_in_as("root"), "Logged in as root"; + + ok $m->logout, "Logged out"; + + # We're definitely logged out? + $m->get_ok($url); + $m->content_like(qr/Login/, "Login form"); + } + + diag "External auth"; + { + # REMOTE_USER of root + $m->auth("root"); + + # Automatically logged in as root without Login page + $m->get_ok($url); + ok $m->logged_in_as("root"), "Logged in as root"; + + # Still logged in on another request + $m->follow_link_ok({ text => 'My Tickets' }); + ok $m->logged_in_as("root"), "Still logged in as root"; + + # Drop credentials and... + $m->auth(""); + + # ...see if RT notices + $m->get($url); + is $m->status, 403, "403 Forbidden from RT"; + + # Next request gets us the login form + $m->get_ok($url); + $m->content_like(qr/Login/, "Login form"); + } + + diag "External auth with invalid user, login internally"; + { + # REMOTE_USER of invalid + $m->auth("invalid"); + + # Login internally via the login link + $m->get("$url/Search/Build.html"); + is $m->status, 403, "403 Forbidden"; + $m->follow_link_ok({ url_regex => qr'NoAuth/Login\.html' }, "follow logout link"); + $m->content_like(qr/Login/, "Login form"); + + # Log in using RT's form + $m->submit_form_ok({ + with_fields => { + user => 'root', + pass => 'password', + }, + }, "Submitted login form"); + ok $m->logged_in_as("root"), "Logged in as root"; + like $m->uri, qr'Search/Build\.html', "at our originally requested page"; + + # Still logged in on another request + $m->follow_link_ok({ text => 'Tools' }); + ok $m->logged_in_as("root"), "Logged in as root"; + + ok $m->logout, "Logged out"; + + $m->next_warning_like(qr/Couldn't find internal user for 'invalid'/, "found warning for first request"); + $m->next_warning_like(qr/Couldn't find internal user for 'invalid'/, "found warning for second request"); + } + + stop_server(\$m); } -my ( $url, $m ) = RT::Test->started_ok( basic_auth => 1 ); -$m->get($url); -is($m->status, 401, "Initial request with no creds gets 401"); +diag "Fallback OFF"; +{ + RT->Config->Set( DevelMode => 0 ); + RT->Config->Set( WebRemoteUserAuth => 1 ); + RT->Config->Set( WebRemoteUserContinuous => 0 ); + RT->Config->Set( WebFallbackToRTLogin => 0 ); + RT->Config->Set( WebRemoteUserAutocreate => 0 ); -$m->get($url, auth( root => "wrong" )); -is($m->status, 401, "Request with wrong creds gets 401"); + my ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' ); -$m->get($url, auth( root => "password" )); -is($m->status, 200, "Request with right creds gets 200"); + diag "No remote user"; + { + $m->auth(""); + $m->get($url); + is $m->status, 403, "Forbidden"; + } + + stop_server(\$m); +} -$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"); +diag "WebRemoteUserAutocreate"; +{ + RT->Config->Set( DevelMode => 0 ); + RT->Config->Set( WebRemoteUserAuth => 1 ); + RT->Config->Set( WebRemoteUserContinuous => 1 ); + RT->Config->Set( WebFallbackToRTLogin => 0 ); + RT->Config->Set( WebRemoteUserAutocreate => 1 ); + RT->Config->Set( UserAutocreateDefaultsOnLogin => { Organization => "BPS" } ); -$m->get($url); -is($m->status, 401, "Subsequent requests without credentials aren't still logged in"); + my ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' ); + diag "New user"; + { + $m->auth("anewuser"); + $m->get_ok($url); + ok $m->logged_in_as("anewuser"), "Logged in as anewuser"; + + my $user = RT::User->new( RT->SystemUser ); + $user->Load("anewuser"); + ok $user->id, "Found newly created user"; + is $user->Organization, "BPS", "Found Organization from UserAutocreateDefaultsOnLogin hash"; + ok $user->Privileged, "Privileged by default"; + } + + stop_server(\$m); + RT->Config->Set( + UserAutocreateDefaultsOnLogin => { + Privileged => 0, + EmailAddress => 'foo@example.com', + }, + ); + ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' ); + + diag "Create unprivileged users"; + { + $m->auth("unpriv"); + $m->get_ok($url); + ok $m->logged_in_as("unpriv"), "Logged in as an unpriv user"; + like $m->uri->path, RT->Config->Get('SelfServiceRegex'), "SelfService URL"; + + my $user = RT::User->new( RT->SystemUser ); + $user->Load("unpriv"); + ok $user->id, "Found newly created user"; + ok !$user->Privileged, "Unprivileged per config"; + is $user->EmailAddress, 'foo@example.com', "Email address per config"; + } + + diag "User creation failure"; + { + $m->auth("conflicting"); + $m->get($url); + is $m->status, 403, "Forbidden"; + $m->next_warning_like(qr/Couldn't auto-create user 'conflicting' when attempting WebRemoteUser: Email address in use/, 'found failed auth warning'); + + my $user = RT::User->new( RT->SystemUser ); + $user->Load("conflicting"); + ok !$user->id, "Couldn't find conflicting user"; + } + + stop_server(\$m); +} -# Put the credentials back for the warnings check at the end -$m->default_header( auth( root => "password" )); diff --git a/rt/t/web/rest-search-group.t b/rt/t/web/rest-search-group.t new file mode 100644 index 000000000..b62aa09e4 --- /dev/null +++ b/rt/t/web/rest-search-group.t @@ -0,0 +1,102 @@ +use strict; +use warnings; +use RT::Test tests => undef; + +my $group_foo = RT::Group->new($RT::SystemUser); +$group_foo->CreateUserDefinedGroup( Name => 'foo' ); + +my $group_bar = RT::Group->new($RT::SystemUser); +$group_bar->CreateUserDefinedGroup( Name => 'bar' ); + +my $group_baz = RT::Group->new($RT::SystemUser); +$group_baz->CreateUserDefinedGroup( Name => 'baz' ); +$group_baz->SetDisabled(1); + +my ( $baseurl, $m ) = RT::Test->started_ok; + +ok( $m->login, 'logged in' ); + +search_groups_ok( + { query => 'id = ' . $group_foo->id }, + [ $group_foo->id . ': foo' ], + 'search by id' +); + +search_groups_ok( + { + query => 'Name = ' . $group_foo->Name, + format => 's', + fields => 'id,name', + }, + [ "id\tName", $group_foo->id . "\tfoo" ], + 'search by name with customized fields' +); + +search_groups_ok( + { query => 'foo = 3' }, + ['Invalid field specification: foo'], + 'invalid field' +); + +search_groups_ok( + { query => 'id foo 3' }, + ['Invalid operator specification: foo'], + 'invalid op' +); + +search_groups_ok( + { query => '', orderby => 'id' }, + [ $group_foo->id . ': foo', $group_bar->id . ': bar', ], + 'order by id' +); + +search_groups_ok( + { query => '', orderby => 'name' }, + [ $group_bar->id . ': bar', $group_foo->id . ': foo' ], + 'order by name' +); + +search_groups_ok( + { query => '', orderby => '+name' }, + [ $group_bar->id . ': bar', $group_foo->id . ': foo' ], + 'order by +name' +); + +search_groups_ok( + { query => '', orderby => '-name' }, + [ $group_foo->id . ': foo', $group_bar->id . ': bar' ], + 'order by -name' +); + +search_groups_ok( + { query => 'Disabled = 0', orderby => 'id' }, + [ $group_foo->id . ': foo', $group_bar->id . ': bar' ], + 'enabled groups' +); + +search_groups_ok( + { query => 'Disabled = 1', orderby => 'id' }, + [ $group_baz->id . ': baz' ], + 'disabled groups' +); + +sub search_groups_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $query = shift; + my $expected = shift; + my $name = shift || 'search groups'; + + my $uri = URI->new("$baseurl/REST/1.0/search/group"); + $uri->query_form(%$query); + $m->get_ok($uri); + + my @lines = split /\n/, $m->content; + shift @lines; # header + shift @lines; # empty line + + is_deeply( \@lines, $expected, $name ); + +} + +undef $m; +done_testing(); diff --git a/rt/t/web/rest-search-queue.t b/rt/t/web/rest-search-queue.t new file mode 100644 index 000000000..a827d8643 --- /dev/null +++ b/rt/t/web/rest-search-queue.t @@ -0,0 +1,104 @@ +use strict; +use warnings; +use RT::Test tests => undef; + +my $queue_foo = RT::Test->load_or_create_queue( Name => 'Foo' ); +my $queue_bar = RT::Test->load_or_create_queue( Name => 'Bar' ); +my $queue_baz = RT::Test->load_or_create_queue( Name => 'Baz' ); +$queue_baz->SetDisabled(1); + +my ( $baseurl, $m ) = RT::Test->started_ok; + +ok( $m->login, 'logged in' ); + +search_queues_ok( { query => 'id = 1' }, ['1: General'], 'search id = 1' ); +search_queues_ok( + { + query => 'Name = General', + format => 's', + fields => 'id,name,description' + }, + [ "id\tName\tDescription", "1\tGeneral\tThe default queue" ], + 'search by name with customized fields' +); + +search_queues_ok( + { query => 'id > 10' }, + ['No matching results.'], + 'no matching results' +); + +search_queues_ok( + { query => 'foo = 3' }, + ['Invalid field specification: foo'], + 'invalid field' +); + +search_queues_ok( + { query => 'id foo 3' }, + ['Invalid operator specification: foo'], + 'invalid op' +); + +search_queues_ok( + { query => '', orderby => 'id' }, + [ '1: General', $queue_foo->id . ': Foo', $queue_bar->id . ': Bar', ], + 'order by id' +); + +search_queues_ok( + { query => '', orderby => 'name' }, + [ $queue_bar->id . ': Bar', $queue_foo->id . ': Foo', '1: General', ], + 'order by name' +); + +search_queues_ok( + { query => '', orderby => '+name' }, + [ $queue_bar->id . ': Bar', $queue_foo->id . ': Foo', '1: General', ], + 'order by +name' +); + +search_queues_ok( + { query => '', orderby => '-name' }, + [ '1: General', $queue_foo->id . ': Foo', $queue_bar->id . ': Bar', ], + 'order by -name' +); + +search_queues_ok( + { query => 'Disabled = 0', orderby => 'id' }, + [ '1: General', $queue_foo->id . ': Foo', $queue_bar->id . ': Bar', ], + 'enabled queues' +); + +search_queues_ok( + { query => 'Disabled = 1', orderby => 'id' }, + [ $queue_baz->id . ': Baz', ], + 'disabled queues' +); + +search_queues_ok( + { query => 'Disabled = 2', orderby => 'id' }, + [ '2: ___Approvals', ], + 'special Approvals queue' +); + +sub search_queues_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $query = shift; + my $expected = shift; + my $name = shift || 'search queues'; + + my $uri = URI->new("$baseurl/REST/1.0/search/queue"); + $uri->query_form(%$query); + $m->get_ok($uri); + + my @lines = split /\n/, $m->content; + shift @lines; # header + shift @lines; # empty line + + is_deeply( \@lines, $expected, $name ); + +} + +undef $m; +done_testing(); diff --git a/rt/t/web/rest-search-user.t b/rt/t/web/rest-search-user.t new file mode 100644 index 000000000..84a967377 --- /dev/null +++ b/rt/t/web/rest-search-user.t @@ -0,0 +1,115 @@ +use strict; +use warnings; +use RT::Test tests => undef; + +my $root = RT::Test->load_or_create_user( Name => 'root', ); +my $user_foo = RT::Test->load_or_create_user( + Name => 'foo', + Password => 'password', +); +my $user_bar = RT::Test->load_or_create_user( Name => 'bar' ); +my $user_baz = RT::Test->load_or_create_user( Name => 'baz' ); +$user_baz->SetDisabled(1); + +my ( $baseurl, $m ) = RT::Test->started_ok; + +ok( $m->login, 'logged in' ); + +search_users_ok( + { query => 'id = ' . $user_foo->id }, + [ $user_foo->id . ': foo' ], + 'search by id' +); + +search_users_ok( + { + query => 'Name = ' . $user_foo->Name, + format => 's', + fields => 'id,name' + }, + [ "id\tName", $user_foo->id . "\tfoo" ], + 'search by name with customized fields' +); + + +search_users_ok( + { query => 'foo = 3' }, + ['Invalid field specification: foo'], + 'invalid field' +); + +search_users_ok( + { query => 'id foo 3' }, + ['Invalid operator specification: foo'], + 'invalid op' +); + +search_users_ok( + { query => 'password = foo' }, + ['Invalid field specification: password'], + "can't search password" +); + +search_users_ok( + { query => '', orderby => 'id' }, + [ $root->id . ': root', $user_foo->id . ': foo', $user_bar->id . ': bar', ], + 'order by id' +); + +search_users_ok( + { query => '', orderby => 'name' }, + [ $user_bar->id . ': bar', $user_foo->id . ': foo', $root->id . ': root' ], + 'order by name' +); + +search_users_ok( + { query => '', orderby => '+name' }, + [ $user_bar->id . ': bar', $user_foo->id . ': foo', $root->id . ': root' ], + 'order by +name' +); + +search_users_ok( + { query => '', orderby => '-name' }, + [ $root->id . ': root', $user_foo->id . ': foo', $user_bar->id . ': bar' ], + 'order by -name' +); + +search_users_ok( + { query => 'Disabled = 0', orderby => 'id' }, + [ $root->id . ': root', $user_foo->id . ': foo', $user_bar->id . ': bar', ], + 'enabled users' +); + +search_users_ok( + { query => 'Disabled = 1', orderby => 'id' }, + [ $user_baz->id . ': baz', ], + 'disabled users' +); + +ok( $m->login( 'foo', 'password', logout => 1 ), 'logged in as foo' ); +search_users_ok( + { query => 'id = ' . $user_foo->id }, + [ 'Permission denied' ], + "can't search without permission" +); + +sub search_users_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $query = shift; + my $expected = shift; + my $name = shift || 'search users'; + + my $uri = URI->new("$baseurl/REST/1.0/search/user"); + $uri->query_form(%$query); + $m->get_ok($uri); + + my @lines = split /\n/, $m->content; + shift @lines; # header + shift @lines; # empty line + + is_deeply( \@lines, $expected, $name ); + +} + +undef $m; +done_testing(); diff --git a/rt/t/web/rest.t b/rt/t/web/rest.t index 3a84b2a01..8b8cbcb86 100644 --- a/rt/t/web/rest.t +++ b/rt/t/web/rest.t @@ -204,7 +204,7 @@ is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content) $text = $m->content; $text =~ s/.*?\n\n//; $text =~ s/\n\n/\n/; - $text =~ s{CF\.{severity}:.*\n}{}img; + $text =~ s{CF\.\{severity\}:.*\n}{}img; $text .= "CF.{severity}: explosive, a bit\n"; $m->post( "$baseurl/REST/1.0/ticket/edit", @@ -234,7 +234,7 @@ is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content) ] ); $text = $m->content; - $text =~ s{CF\.{severity}:.*\n}{}img; + $text =~ s{CF\.\{severity\}:.*\n}{}img; $text .= "CF.{severity}:\n"; $m->post( "$baseurl/REST/1.0/ticket/edit", @@ -301,7 +301,7 @@ is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content) ] ); $text = $m->content; - $text =~ s{CF\.{single}:.*\n}{}img; + $text =~ s{CF\.\{single\}:.*\n}{}img; $text .= "CF.{single}: that\n"; $m->post( "$baseurl/REST/1.0/ticket/edit", diff --git a/rt/t/web/rest_user_cf.t b/rt/t/web/rest_user_cf.t new file mode 100644 index 000000000..d9f4ea3ba --- /dev/null +++ b/rt/t/web/rest_user_cf.t @@ -0,0 +1,26 @@ +use strict; +use warnings; +use RT::Interface::REST; + +use RT::Test tests => undef; + +my ( $baseurl, $m ) = RT::Test->started_ok; + +my $cf = RT::Test->load_or_create_custom_field( + Name => 'foo', + Type => 'Freeform', + LookupType => 'RT::User', +); +$cf->AddToObject(RT::User->new(RT->SystemUser)); + +my $root = RT::User->new( RT->SystemUser ); +$root->Load('root'); +$root->AddCustomFieldValue( Field => 'foo', Value => 'blabla' ); +is( $root->FirstCustomFieldValue('foo'), 'blabla', 'cf is set' ); + +ok( $m->login, 'logged in' ); +$m->post( "$baseurl/REST/1.0/show", [ id => 'user/12', ] ); +like( $m->content, qr/CF-foo: blabla/, 'found the cf' ); + +undef $m; +done_testing; diff --git a/rt/t/web/richtext-autohandler.t b/rt/t/web/richtext-autohandler.t deleted file mode 100644 index 724a7b34c..000000000 --- a/rt/t/web/richtext-autohandler.t +++ /dev/null @@ -1,14 +0,0 @@ -use strict; -use warnings; - -use RT::Test tests => 9; -my ($baseurl, $agent) = RT::Test->started_ok; - -$agent->get("$baseurl/NoAuth/RichText/ckeditor/config.js"); -is($agent->status, 403); -$agent->content_lacks("config.disableNativeSpellChecker"); - -$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 23b357f79..c7e8aac00 100644 --- a/rt/t/web/rights.t +++ b/rt/t/web/rights.t @@ -6,7 +6,7 @@ use RT::Test tests => 14; my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, "logged in"; -$m->follow_link_ok({ id => 'tools-config-global-group-rights'}); +$m->follow_link_ok({ id => 'admin-global-group-rights'}); sub get_rights { diff --git a/rt/t/web/rights1.t b/rt/t/web/rights1.t index 63ddb38c4..2cc7689c6 100644 --- a/rt/t/web/rights1.t +++ b/rt/t/web/rights1.t @@ -29,9 +29,9 @@ $agent->login( $user_obj->Name, 'customer'); # Test for absence of Configure and Preferences tabs. ok(!$agent->find_link( url => "$RT::WebPath/Admin/", - text => 'Configuration'), "No config tab" ); + text => 'Admin'), "No admin tab" ); ok(!$agent->find_link( url => "$RT::WebPath/User/Prefs.html", - text => 'Preferences'), "No prefs pane" ); + text => 'Preferences'), "No prefs pane" ); # Now test for their presence, one at a time. Sleep for a bit after # ACL changes, thanks to the 10s ACL cache. @@ -43,20 +43,20 @@ $agent->reload; $agent->content_contains('Logout', "Reloaded page successfully"); ok($agent->find_link( url => "$RT::WebPath/Admin/", - text => 'Configuration'), "Found config tab" ); + text => 'Admin'), "Found admin tab" ); my ($revokeid,$revokemsg) =$user_obj->PrincipalObj->RevokeRight(Right => 'ShowConfigTab'); ok ($revokeid,$revokemsg); ($grantid,$grantmsg) =$user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf'); ok ($grantid,$grantmsg); $agent->reload(); $agent->content_contains('Logout', "Reloaded page successfully"); -ok($agent->find_link( - id => 'preferences-settings' ), "Found prefs pane" ); +ok($agent->find_link( + 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'); + text => 'Tickets'); 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"); @@ -79,23 +79,23 @@ $agent->content_like(qr/input\s+type=['"]submit['"][^>]+name=['"]SavedSearchSave # via SelectOwner. my $queue_obj = RT::Queue->new(RT->SystemUser); -($ret, $msg) = $queue_obj->Create(Name => 'CustomerQueue-'.$$, - Description => 'queue for SelectOwner testing'); +($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); ($ret, $msg) = $group_obj->CreateUserDefinedGroup(Name => 'CustomerGroup-'.$$, - Description => 'group for SelectOwner testing'); + Description => 'group for SelectOwner testing'); ok($ret, "SelectOwner test group creation. $msg"); # Add our customer to the customer group, and give it queue rights. ($ret, $msg) = $group_obj->AddMember($user_obj->PrincipalObj->Id()); ok($ret, "Added customer to its group. $msg"); ($grantid,$grantmsg) =$group_obj->PrincipalObj->GrantRight(Right => 'OwnTicket', - Object => $queue_obj); - + Object => $queue_obj); + ok($grantid,$grantmsg); ($grantid,$grantmsg) =$group_obj->PrincipalObj->GrantRight(Right => 'SeeQueue', - Object => $queue_obj); + Object => $queue_obj); ok ($grantid,$grantmsg); # Now. When we look at the search page we should be able to see # ourself in the list of possible owners. diff --git a/rt/t/web/saved_search_chart.t b/rt/t/web/saved_search_chart.t index 70111b97c..3737b512e 100644 --- a/rt/t/web/saved_search_chart.t +++ b/rt/t/web/saved_search_chart.t @@ -58,7 +58,7 @@ $m->submit_form( form_name => 'SaveSearch', fields => { Query => 'id=2', - PrimaryGroupBy => 'Status', + GroupBy => 'Status', ChartStyle => 'pie', }, button => 'SavedSearchSave', @@ -67,13 +67,13 @@ $m->submit_form( $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' ); + 'GroupBy is updated' ); $m->content_like( qr/value="pie"\s+selected="selected"/, 'ChartType is updated' ); ok( $search->Load($id) ); is( $search->SubValue('Query'), 'id=2', 'Query is indeed updated' ); -is( $search->SubValue('PrimaryGroupBy'), - 'Status', 'PrimaryGroupBy is indeed updated' ); +is( $search->SubValue('GroupBy'), + 'Status', 'GroupBy is indeed updated' ); is( $search->SubValue('ChartStyle'), 'pie', 'ChartStyle is indeed updated' ); # finally, let's test delete diff --git a/rt/t/web/saved_search_permissions.t b/rt/t/web/saved_search_permissions.t index f61c931a0..e24ae6146 100644 --- a/rt/t/web/saved_search_permissions.t +++ b/rt/t/web/saved_search_permissions.t @@ -26,7 +26,7 @@ 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"}; -RT::Interface::Web::EscapeUTF8( \$message ); +RT::Interface::Web::EscapeHTML( \$message ); $m->content_contains( $message, 'user foo can not load saved search of root' ); $m->warning_like( qr/User #\d+ tried to load container user #\d+/, diff --git a/rt/t/web/scrips.t b/rt/t/web/scrips.t index 0ff46bf26..d669f4c4e 100644 --- a/rt/t/web/scrips.t +++ b/rt/t/web/scrips.t @@ -1,7 +1,9 @@ use strict; use warnings; -use RT::Test tests => 14; +use RT::Test tests => undef; + +RT->Config->Set( UseTransactionBatch => 1 ); # TODO: # Test the rest of the conditions. @@ -9,10 +11,16 @@ use RT::Test tests => 14; # Test templates? # Test cleanup scripts. +my $queue_g = RT::Test->load_or_create_queue( Name => 'General' ); +ok $queue_g && $queue_g->id, 'loaded or created queue'; + +my $queue_r = RT::Test->load_or_create_queue( Name => 'Regression' ); +ok $queue_r && $queue_r->id, 'loaded or created queue'; + my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, "logged in"; -$m->follow_link_ok({id => 'tools-config-global-scrips-create'}); +$m->follow_link_ok({id => 'admin-global-scrips-create'}); sub prepare_code_with_value { my $value = shift; @@ -47,16 +55,17 @@ sub prepare_code_with_value { 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'}); + $m->follow_link_ok({id => 'admin-global-scrips-create'}); my $prepare_code = prepare_code_with_value($prepare_code_value); - $m->form_name('ModifyScrip'); + $m->form_name('CreateScrip'); $m->set_fields( - 'Scrip-new-ScripCondition' => $condition, - 'Scrip-new-ScripAction' => 15, # User Defined - 'Scrip-new-Template' => 1, # Blank - 'Scrip-new-CustomPrepareCode' => $prepare_code, + 'ScripCondition' => $condition, + 'ScripAction' => 'User Defined', + 'Template' => 'Blank', + 'CustomPrepareCode' => $prepare_code, ); - $m->submit; + $m->click('Create'); + $m->content_like(qr{Scrip Created}); } my $ticket_obj = RT::Test->create_ticket( @@ -76,7 +85,7 @@ sub prepare_code_with_value { $m->submit_form( form_name => 'ForwardMessage', fields => { - To => 'rt-test, rt-to@example.com', + To => 'rt-test@example.com, rt-to@example.com', }, button => 'ForwardAndReturn' ); @@ -92,7 +101,7 @@ sub prepare_code_with_value { $m->submit_form( form_name => 'ForwardMessage', fields => { - To => 'rt-test, rt-to@example.com', + To => 'rt-test@example.com, rt-to@example.com', }, button => 'ForwardAndReturn' ); @@ -101,3 +110,191 @@ sub prepare_code_with_value { RT::Test->clean_caught_mails; } + +note "check basics in scrip's admin interface"; +{ + $m->follow_link_ok( { id => 'admin-global-scrips-create' } ); + ok $m->form_name('CreateScrip'); + is $m->value_name('Description'), '', 'empty value'; + is $m->value_name('ScripAction'), '-', 'empty value'; + is $m->value_name('ScripCondition'), '-', 'empty value'; + is $m->value_name('Template'), '-', 'empty value'; + $m->field('Description' => 'test'); + $m->click('Create'); + $m->content_contains("Action is mandatory argument"); + + ok $m->form_name('CreateScrip'); + is $m->value_name('Description'), 'test', 'value stays on the page'; + $m->select('ScripAction' => 'Notify Ccs'); + $m->click('Create'); + $m->content_contains("Template is mandatory argument"); + + ok $m->form_name('CreateScrip'); + is $m->value_name('Description'), 'test', 'value stays on the page'; + is $m->value_name('ScripAction'), 'Notify Ccs', 'value stays on the page'; + $m->select('Template' => 'Blank'); + $m->click('Create'); + $m->content_contains("Condition is mandatory argument"); + + ok $m->form_name('CreateScrip'); + is $m->value_name('Description'), 'test', 'value stays on the page'; + is $m->value_name('ScripAction'), 'Notify Ccs', 'value stays on the page'; + $m->select('ScripCondition' => 'On Close'); + $m->click('Create'); + $m->content_contains("Scrip Created"); + + ok $m->form_name('ModifyScrip'); + is $m->value_name('Description'), 'test', 'correct value'; + is $m->value_name('ScripCondition'), 'On Close', 'correct value'; + is $m->value_name('ScripAction'), 'Notify Ccs', 'correct value'; + is $m->value_name('Template'), 'Blank', 'correct value'; + $m->field('Description' => 'test test'); + $m->click('Update'); + # regression + $m->content_lacks("Template is mandatory argument"); + + ok $m->form_name('ModifyScrip'); + is $m->value_name('Description'), 'test test', 'correct value'; + $m->content_contains("Description changed from", "found action result message"); +} + +note "check application in admin interface"; +{ + $m->follow_link_ok({ id => 'admin-global-scrips-create' }); + $m->submit_form_ok({ + with_fields => { + Description => "testing application", + ScripCondition => "On Create", + ScripAction => "Open Tickets", + Template => "Blank", + }, + button => 'Create', + }, "created scrip"); + $m->content_contains("Scrip Created", "found result message"); + + my ($sid) = ($m->content =~ /Modify scrip #(\d+)/); + ok $sid, "found scrip id on the page"; + RT::Test->object_scrips_are($sid, [0]); + + $m->follow_link_ok({ id => 'page-applies-to' }); + ok $m->form_name("AddRemoveScrip"), "found form"; + $m->tick("RemoveScrip-$sid", 0); + $m->click_ok("Update", "update scrip application"); + RT::Test->object_scrips_are($sid, []); + + ok $m->form_name("AddRemoveScrip"), "found form"; + $m->tick("AddScrip-$sid", 0); + $m->tick("AddScrip-$sid", $queue_g->id); + $m->click_ok("Update", "update scrip application"); + RT::Test->object_scrips_are($sid, [0], [$queue_g->id, $queue_r->id]); +} + +note "check templates in scrip's admin interface"; +{ + my $template = RT::Template->new( RT->SystemUser ); + my ($status, $msg) = $template->Create( Queue => $queue_g->id, Name => 'foo' ); + ok $status, 'created a template'; + + my $templates = RT::Templates->new( RT->SystemUser ); + $templates->LimitToGlobal; + + my @default = ( + '', + map $_->Name, @{$templates->ItemsArrayRef} + ); + + $m->follow_link_ok( { id => 'admin-global-scrips-create' } ); + ok $m->form_name('CreateScrip'); + my @templates = ($m->find_all_inputs( type => 'option', name => 'Template' ))[0] + ->possible_values; + is_deeply([sort @templates], [sort @default]); + + $m->follow_link_ok( { id => 'admin-queues' } ); + $m->follow_link_ok( { text => 'General' } ); + $m->follow_link_ok( { id => 'page-scrips-create' } ); + + ok $m->form_name('CreateScrip'); + @templates = ($m->find_all_inputs( type => 'option', name => 'Template' ))[0] + ->possible_values; + is_deeply([sort @templates], [sort @default, 'foo']); + +note "make sure we can not apply scrip to queue without required template"; + $m->field('Description' => 'test template'); + $m->select('ScripCondition' => 'On Close'); + $m->select('ScripAction' => 'Notify Ccs'); + $m->select('Template' => 'foo'); + $m->click('Create'); + $m->content_contains("Scrip Created"); + + $m->follow_link_ok( { id => 'page-applies-to' } ); + my ($id) = ($m->content =~ /Modify associated objects for scrip #(\d+)/); + $m->form_name('AddRemoveScrip'); + $m->tick('AddScrip-'.$id, $queue_r->id); + $m->click('Update'); + $m->content_like(qr{No template foo in queue Regression or global}); + +note "unapply the scrip from any queue"; + $m->form_name('AddRemoveScrip'); + $m->tick('RemoveScrip-'.$id, $queue_g->id); + $m->click('Update'); + $m->content_like(qr{Object deleted}); + +note "you can pick any template"; + $m->follow_link_ok( { id => 'page-basics' } ); + ok $m->form_name('ModifyScrip'); + @templates = ($m->find_all_inputs( type => 'option', name => 'Template' ))[0] + ->possible_values; + is_deeply( + [sort @templates], + [sort do { + my $t = RT::Templates->new( RT->SystemUser ); + $t->UnLimit; + ('', $t->DistinctFieldValues('Name')) + }], + ); + +note "go to apply page and apply with template change"; + $m->follow_link_ok( { id => 'page-applies-to' } ); + $m->form_name('AddRemoveScrip'); + $m->field('Template' => 'blank'); + $m->tick('AddScrip-'.$id, $queue_g->id); + $m->tick('AddScrip-'.$id, $queue_r->id); + $m->click('Update'); + $m->content_contains("Template: Template changed from "); + $m->content_contains("Object created"); +} + +note "apply scrip in different stage to different queues"; +{ + $m->follow_link_ok( { id => 'admin-queues' } ); + $m->follow_link_ok( { text => 'General' } ); + $m->follow_link_ok( { id => 'page-scrips-create'}); + + ok $m->form_name('CreateScrip'); + $m->field('Description' => 'test stage'); + $m->select('ScripCondition' => 'On Close'); + $m->select('ScripAction' => 'Notify Ccs'); + $m->select('Template' => 'Blank'); + $m->click('Create'); + $m->content_contains("Scrip Created"); + + my ($sid) = ($m->content =~ /Modify scrip #(\d+)/); + ok $sid, "found scrip id on the page"; + + $m->follow_link_ok({ text => 'Applies to' }); + ok $m->form_name('AddRemoveScrip'); + $m->select('Stage' => 'Batch'); + $m->tick( "AddScrip-$sid" => $queue_r->id ); + $m->click('Update'); + $m->content_contains("Object created"); + + $m->follow_link_ok({ text => 'General' }); + $m->follow_link_ok({ id => 'page-scrips' }); + + my (@matches) = $m->content =~ /test stage/g; + # regression + is scalar @matches, 1, 'scrip mentioned only once'; +} + +undef $m; +done_testing; diff --git a/rt/t/web/search_bulk_update_links.t b/rt/t/web/search_bulk_update_links.t index ffe2efe81..d9b477e03 100644 --- a/rt/t/web/search_bulk_update_links.t +++ b/rt/t/web/search_bulk_update_links.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test tests => 47; +use RT::Test tests => 46; my ( $url, $m ) = RT::Test->started_ok; ok( $m->login, 'logged in' ); @@ -79,7 +79,7 @@ $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/; +UpdateSubject UpdateContent/; for my $field ( @fields ) { is( $m->value($field), '', "default $field is empty" ); } diff --git a/rt/t/web/search_ical.t b/rt/t/web/search_ical.t new file mode 100644 index 000000000..094d8a2de --- /dev/null +++ b/rt/t/web/search_ical.t @@ -0,0 +1,196 @@ +use strict; +use warnings; + +use Data::ICal; +use RT::Test tests => 77; + +my $start_obj = RT::Date->new( RT->SystemUser ); +$start_obj->SetToNow; +my $start = $start_obj->iCal( Time => 1); + +my $due_obj = RT::Date->new( RT->SystemUser ); +$due_obj->SetToNow; +$due_obj->AddDays(2); +my $due = $due_obj->iCal( Time => 1); + +diag 'Test iCal with date only'; +{ + my ($baseurl, $agent) = RT::Test->started_ok; + + my $ticket = RT::Ticket->new(RT->SystemUser); + + for ( 1 .. 5 ) { + $ticket->Create( + Subject => 'Ticket ' . $_, + Queue => 'General', + Owner => 'root', + Requestor => 'ical@localhost', + Starts => $start_obj->ISO, + Due => $due_obj->ISO, + ); + } + + ok $agent->login('root', 'password'), 'logged in as root'; + + $agent->get_ok('/Search/Build.html'); + $agent->form_name('BuildQuery'); + $agent->field('idOp', '>'); + $agent->field('ValueOfid', '0'); + $agent->submit('DoSearch'); + $agent->follow_link_ok({id => 'page-results'}); + + for ( 1 .. 5 ) { + $agent->content_contains('Ticket ' . $_); + } + + $agent->follow_link_ok( { text => 'iCal' } ); + + is( $agent->content_type, 'text/calendar', 'content type is text/calendar' ); + + for ( 1 .. 5 ) { + $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/); + } + + my $ical = Data::ICal->new(data => $agent->content); + + my @entries = $ical->entries; + my $ical_count = @{$entries[0]}; + is( $ical_count, 10, "Got $ical_count ical entries"); + + my $prop_ref = $entries[0]->[0]->properties; + my $start_as_root = RT::Date->new( RT::CurrentUser->new( 'root' ) ); + $start_as_root->Unix( $start_obj->Unix ); + my $start = $start_as_root->ISO( Time => 0, Timezone => 'user' ); + $start =~ s/-//g; + is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date: $start"); + like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE\:/, 'Got DATE value'); + + $prop_ref = $entries[0]->[1]->properties; + my $due_as_root = RT::Date->new( RT::CurrentUser->new( 'root' ) ); + $due_as_root->Unix( $due_obj->Unix ); + my $due = $due_as_root->ISO( Time => 0, Timezone => 'user' ); + $due =~ s/-//g; + is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date: $due"); + like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE\:/, 'Got DATE value'); +} + +RT::Test->stop_server; + +diag 'Test iCal with date and time with config option'; +{ + RT->Config->Set(TimeInICal =>1); + my ($baseurl, $agent) = RT::Test->started_ok; + + ok $agent->login('root', 'password'), 'logged in as root'; + + $agent->get_ok('/Search/Build.html'); + $agent->form_name('BuildQuery'); + $agent->field('idOp', '>'); + $agent->field('ValueOfid', '0'); + $agent->submit('DoSearch'); + $agent->follow_link_ok({id => 'page-results'}); + + for ( 1 .. 5 ) { + $agent->content_contains('Ticket ' . $_); + } + + my $link = $agent->find_link( text => 'iCal' ); # use $link later + $agent->get_ok($link->url); + + is( $agent->content_type, 'text/calendar', 'content type is text/calendar' ); + + for ( 1 .. 5 ) { + $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/); + } + + my $ical = Data::ICal->new(data => $agent->content); + + my @entries = $ical->entries; + my $ical_count = @{$entries[0]}; + is( $ical_count, 10, "Got $ical_count ical entries"); + + my $prop_ref = $entries[0]->[0]->properties; + $start =~ s/-//g; + is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date with time: $start"); + like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); + + $prop_ref = $entries[0]->[1]->properties; + $due =~ s/-//g; + is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date with time: $due"); + like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); +} + +RT::Test->stop_server; + +diag 'Test iCal with date and time using query param'; +{ + RT->Config->Set(TimeInICal =>0); + my ($baseurl, $agent) = RT::Test->started_ok; + + ok $agent->login('root', 'password'), 'logged in as root'; + + $agent->get_ok('/Search/Build.html'); + $agent->form_name('BuildQuery'); + $agent->field('idOp', '>'); + $agent->field('ValueOfid', '0'); + $agent->submit('DoSearch'); + $agent->follow_link_ok({id => 'page-results'}); + + for ( 1 .. 5 ) { + $agent->content_contains('Ticket ' . $_); + } + + my $link = $agent->find_link( text => 'iCal' ); + $agent->get_ok($link->url . '?Time=1'); + + is( $agent->content_type, 'text/calendar', 'content type is text/calendar' ); + + for ( 1 .. 5 ) { + $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/); + } + + my $ical = Data::ICal->new(data => $agent->content); + + my @entries = $ical->entries; + my $ical_count = @{$entries[0]}; + is( $ical_count, 10, "Got $ical_count ical entries"); + + my $prop_ref = $entries[0]->[0]->properties; + $start =~ s/-//g; + is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date with time: $start"); + like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); + + $prop_ref = $entries[0]->[1]->properties; + $due =~ s/-//g; + is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date with time: $due"); + like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); + + diag 'Test iCal with date and time in single events'; + + my $url = $link->url . '?SingleEvent=1&Time=1'; + $agent->get_ok($url); + + is( $agent->content_type, 'text/calendar', 'content type is text/calendar' ); + + for ( 1 .. 5 ) { + $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/); + } + + $ical = Data::ICal->new(data => $agent->content); + + @entries = $ical->entries; + $ical_count = @{$entries[0]}; + + # Only 5 entries in single event mode + is( $ical_count, 5, "Got $ical_count ical entries"); + + $prop_ref = $entries[0]->[0]->properties; + $start =~ s/-//g; + is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date with time: $start"); + like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); + + $prop_ref = $entries[0]->[1]->properties; + $due =~ s/-//g; + is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date with time: $due"); + like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); +} diff --git a/rt/t/web/search_rss.t b/rt/t/web/search_rss.t index 9a53a8d94..7f1fdc1c3 100644 --- a/rt/t/web/search_rss.t +++ b/rt/t/web/search_rss.t @@ -39,14 +39,11 @@ my $rss_content = $agent->content; $agent->get_ok($rdf_path); is($agent->content, $rss_content, 'old Results.rdf still works'); -SKIP: { - eval { require XML::Simple; }; - skip 'no XML::Simple found', 6 if $@; - my $rss = XML::Simple::XMLin( $rss_content ); - is( scalar @{ $rss->{item} }, 5, 'item number' ); - for ( 1 .. 5 ) { - is( $rss->{item}[$_-1]{title}, 'Ticket ' . $_, 'title' . $_ ); - } +use XML::Simple; +my $rss = XML::Simple::XMLin( $rss_content ); +is( scalar @{ $rss->{item} }, 5, 'item number' ); +for ( 1 .. 5 ) { + is( $rss->{item}[$_-1]{title}, 'Ticket ' . $_, 'title' . $_ ); } # not login at all diff --git a/rt/t/web/search_simple.t b/rt/t/web/search_simple.t index a1a3ce806..d7c47279f 100644 --- a/rt/t/web/search_simple.t +++ b/rt/t/web/search_simple.t @@ -44,14 +44,14 @@ my $t = RT::Ticket->new(RT->SystemUser); { my ($status, $msg) = $t->AddCustomFieldValue( Field => $cf1->id, - Value => 'Downtown'); + Value => 'Downtown'); ok( $status, "Added CF value - $msg" ); } { my ($status, $msg) = $t->AddCustomFieldValue( Field => $cf2->id, - Value => 'Proxy'); + Value => 'Proxy'); ok( $status, "Added CF value - $msg" ); } diff --git a/rt/t/web/self_service.t b/rt/t/web/self_service.t index adc90d776..7afc008c6 100644 --- a/rt/t/web/self_service.t +++ b/rt/t/web/self_service.t @@ -18,9 +18,8 @@ ok( $user_a && $user_a->id, 'loaded or created user' ); ok( ! $user_a->Privileged, 'user is not privileged' ); # Load Cc group -my $Cc = RT::Group->new( RT->SystemUser ); -my($ok, $msg) = $Cc->LoadSystemRoleGroup( 'Cc' ); -ok($ok, $msg); +my $Cc = RT::System->RoleGroup( 'Cc' ); +ok($Cc->id); RT::Test->add_rights( { Principal => $Cc, Right => ['ShowTicket'] } ); my ($ticket) = RT::Test->create_ticket( diff --git a/rt/t/web/googleish_search.t b/rt/t/web/simple_search.t index a5f834eee..710efb1d1 100644 --- a/rt/t/web/googleish_search.t +++ b/rt/t/web/simple_search.t @@ -21,40 +21,41 @@ ok $two_words_queue && $two_words_queue->id, 'loaded or created a queue'; 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( + require RT::Search::Simple; + my $parser = RT::Search::Simple->new( TicketsObj => $tickets, Argument => '', ); - is $parser->QueryToSQL("foo"), "$active AND ( Subject LIKE 'foo' )", "correct parsing"; + is $parser->QueryToSQL("foo"), "( Subject LIKE 'foo' ) AND $active", "correct parsing"; + is $parser->QueryToSQL("1 foo"), "( Subject LIKE 'foo' AND Subject LIKE '1' ) AND $active", "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("'1'"), "( Subject LIKE '1' ) AND $active", "correct parsing"; is $parser->QueryToSQL("foo bar"), - "$active AND ( Subject LIKE 'foo' AND Subject LIKE 'bar' )", + "( Subject LIKE 'foo' AND Subject LIKE 'bar' ) AND $active", "correct parsing"; is $parser->QueryToSQL("'foo bar'"), - "$active AND ( Subject LIKE 'foo bar' )", + "( Subject LIKE 'foo bar' ) AND $active", "correct parsing"; is $parser->QueryToSQL("'foo \\' bar'"), - "$active AND ( Subject LIKE 'foo \\' bar' )", + "( Subject LIKE 'foo \\' bar' ) AND $active", "correct parsing"; is $parser->QueryToSQL('"foo \' bar"'), - "$active AND ( Subject LIKE 'foo \\' bar' )", + "( Subject LIKE 'foo \\' bar' ) AND $active", "correct parsing"; is $parser->QueryToSQL('"\f\o\o"'), - "$active AND ( Subject LIKE '\\\\f\\\\o\\\\o' )", + "( Subject LIKE '\\\\f\\\\o\\\\o' ) AND $active", "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("'Two Words'"), "( Subject LIKE 'Two Words' ) AND $active", "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("'me'"), "( Subject LIKE 'me' ) AND $active", "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('owner:root@localhost'), "( Owner.EmailAddress = 'root\@localhost' ) AND $active", "Email address as owner"; @@ -164,7 +165,9 @@ for my $quote ( q{'}, q{"} ) { Subject => qq!base${quote}ticket $$!, Queue => 'general', Owner => $user->Name, - Requestor => qq!custom${quote}search\@localhost!, + ( $quote eq q{'} + ? (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' ); @@ -188,7 +191,7 @@ for my $quote ( q{'}, q{"} ) { $m->field( q => $q ); $m->submit; my $escape_quote = $quote; - RT::Interface::Web::EscapeUTF8(\$escape_quote); + RT::Interface::Web::EscapeHTML(\$escape_quote); $m->content_contains( "base${escape_quote}ticket", "base${quote}ticket is found" ); } diff --git a/rt/t/web/smime/outgoing.t b/rt/t/web/smime/outgoing.t new file mode 100644 index 000000000..21d2328f2 --- /dev/null +++ b/rt/t/web/smime/outgoing.t @@ -0,0 +1,384 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef; +my $test = 'RT::Test::SMIME'; + +use RT::Action::SendEmail; +use File::Temp qw(tempdir); + +use_ok('RT::Crypt::SMIME'); + +RT::Test::SMIME->import_key('sender@example.com'); + +my $user_email = 'root@example.com'; +{ + my $user = RT::Test->load_or_create_user( + Name => $user_email, EmailAddress => $user_email + ); + ok $user && $user->id, 'loaded or created user'; + RT::Test::SMIME->import_key($user_email, $user); +} + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'sender@example.com', + CommentAddress => 'sender@example.com', +); +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 @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" if $ENV{TEST_VERBOSE}; +{ + foreach my $variant ( @variants ) { + set_queue_crypt_options( %$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(); + + my $ticket = RT::Ticket->new( $RT::SystemUser ); + my ($id) = $ticket->Create( + Subject => 'test', + Queue => $queue->id, + Requestor => $user_email, + ); + ok $id, 'ticket created'; + + foreach my $variant ( @variants ) { + set_queue_crypt_options( %$variant ); + $m->goto_ticket( $id ); + $m->follow_link_ok({text => 'Reply'}, '-> reply'); + $m->form_number(3); + 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"; + } + } +} + +# create a ticket for each combination +foreach my $queue_set ( @variants ) { + set_queue_crypt_options( %$queue_set ); + foreach my $ticket_set ( @variants ) { + create_a_ticket( %$ticket_set ); + } +} + +my $tid; +{ + my $ticket = RT::Ticket->new( $RT::SystemUser ); + ($tid) = $ticket->Create( + Subject => 'test', + Queue => $queue->id, + Requestor => $user_email, + ); + ok $tid, 'ticket created'; +} + +# again for each combination add a reply message +foreach my $queue_set ( @variants ) { + set_queue_crypt_options( %$queue_set ); + foreach my $ticket_set ( @variants ) { + update_ticket( $tid, %$ticket_set ); + } +} + + +# ------------------------------------------------------------------------------ +# now delete all keys from the keyring and put back secret/pub pair for rt-test@ +# and only public key for sender@ so we can verify signatures and decrypt +# like we are on another side recieving emails +# ------------------------------------------------------------------------------ + +my $keyring = $test->keyring_path; +unlink $_ foreach glob( $keyring ."/*" ); +RT::Test::SMIME->import_key('sender@example.com.crt'); +RT::Test::SMIME->import_key($user_email); + +$queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => $user_email, + CommentAddress => $user_email, +); +ok $queue && $queue->id, 'changed props of the queue'; + +foreach my $mail ( map cleanup_headers($_), @{ $mail{'plain'} } ) { + my ($status, $id) = RT::Test->send_via_mailgate($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 ); + $tick->Load( $id ); + ok ($tick->id, "loaded ticket #$id"); + + my $txn = $tick->Transactions->First; + my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; + + ok !$msg->GetHeader('X-RT-Privacy'), "RT's outgoing mail has no crypto"; + is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Not encrypted', + "RT's outgoing mail looks not encrypted"; + ok !$msg->GetHeader('X-RT-Incoming-Signature'), + "RT's outgoing mail looks not signed"; + + like $txn->Content, qr/Some content/, "RT's mail includes copy of ticket text"; +} + +foreach my $mail ( map cleanup_headers($_), @{ $mail{'signed'} } ) { + my ($status, $id) = RT::Test->send_via_mailgate($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 ); + $tick->Load( $id ); + ok ($tick->id, "loaded ticket #$id"); + + my $txn = $tick->Transactions->First; + my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; + + is $msg->GetHeader('X-RT-Privacy'), 'SMIME', + "RT's outgoing mail has crypto" or exit 0; + is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Not encrypted', + "RT's outgoing mail looks not encrypted"; + like $msg->GetHeader('X-RT-Incoming-Signature'), + qr/<sender\@example\.com>/, + "RT's outgoing mail looks signed"; + + like $attachments[0]->Content, qr/Some content/, + "RT's mail includes copy of ticket text"; +} + +foreach my $mail ( map cleanup_headers($_), @{ $mail{'encrypted'} } ) { + my ($status, $id) = RT::Test->send_via_mailgate($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 ); + $tick->Load( $id ); + ok ($tick->id, "loaded ticket #$id"); + + my $txn = $tick->Transactions->First; + my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; + + is $msg->GetHeader('X-RT-Privacy'), 'SMIME', + "RT's outgoing mail has crypto"; + is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Success', + "RT's outgoing mail looks encrypted"; + ok !$msg->GetHeader('X-RT-Incoming-Signature'), + "RT's outgoing mail looks not signed"; + + like $attachments[0]->Content, qr/Some content/, + "RT's mail includes copy of ticket text"; +} + +foreach my $mail ( map cleanup_headers($_), @{ $mail{'signed_encrypted'} } ) { + my ($status, $id) = RT::Test->send_via_mailgate($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 ); + $tick->Load( $id ); + ok ($tick->id, "loaded ticket #$id"); + + my $txn = $tick->Transactions->First; + my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; + + is $msg->GetHeader('X-RT-Privacy'), 'SMIME', + "RT's outgoing mail has crypto"; + is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Success', + "RT's outgoing mail looks encrypted"; + like $msg->GetHeader('X-RT-Incoming-Signature'), + qr/<sender\@example.com>/, + "RT's outgoing mail looks signed"; + + like $attachments[0]->Content, qr/Some content/, + "RT's mail includes copy of ticket text"; +} + +sub create_a_ticket { + my %args = (@_); + + RT::Test->clean_caught_mails; + + describe_options('creating a ticket: ', %args); + + $m->goto_create_ticket( $queue ); + $m->form_name('TicketCreate'); + $m->field( Subject => 'test' ); + $m->field( Requestors => $user_email ); + $m->field( Content => 'Some content' ); + + foreach ( qw(Sign Encrypt) ) { + if ( $args{ $_ } ) { + $m->tick( $_ => 1 ); + } else { + $m->untick( $_ => 1 ); + } + } + + $m->submit; + is $m->status, 200, "request successful"; + + unlike($m->content, qr/unable to sign outgoing email messages/); + + $m->get_ok('/'); # ensure that the mail has been processed + + my @mail = RT::Test->fetch_caught_mails; + check_text_emails( \%args, @mail ); +} + +sub update_ticket { + my $tid = shift; + my %args = (@_); + + RT::Test->clean_caught_mails; + + describe_options('updating ticket #'. $tid .': ', %args); + + ok $m->goto_ticket( $tid ), "UI -> ticket #$tid"; + $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' ); + $m->form_number(3); + $m->field( UpdateContent => 'Some content' ); + + foreach ( qw(Sign Encrypt) ) { + if ( $args{ $_ } ) { + $m->tick( $_ => 1 ); + } else { + $m->untick( $_ => 1 ); + } + } + + $m->click('SubmitTicket'); + is $m->status, 200, "request successful"; + $m->content_like(qr/Correspondence added/, 'Correspondence added');# or diag $m->content; + + $m->get_ok('/'); # ensure that the mail has been processed + + my @mail = RT::Test->fetch_caught_mails; + check_text_emails( \%args, @mail ); +} + +undef $m; +done_testing; + +sub check_text_emails { + my %args = %{ shift @_ }; + my @mail = @_; + + describe_options('testing that we got at least one mail: ', %args); + + ok scalar @mail, "got some mail"; + for my $mail (@mail) { + if ( $args{'Encrypt'} ) { + unlike $mail, qr/Some content/, "outgoing email was encrypted"; + } else { + like $mail, qr/Some content/, "outgoing email was not encrypted"; + } + + if ( $args{'Encrypt'} ) { + like $mail, qr/application\/(?:x-)?pkcs7-mime/, 'outgoing email was processed'; + } elsif ( $args{'Sign'} ) { + like $mail, qr/(?:x-)?pkcs7-signature/, 'outgoing email was processed'; + } else { + unlike $mail, qr/smime/, 'outgoing email was not processed'; + } + } + if ( $args{'Sign'} && $args{'Encrypt'} ) { + push @{ $mail{'signed_encrypted'} }, @mail; + } elsif ( $args{'Sign'} ) { + push @{ $mail{'signed'} }, @mail; + } elsif ( $args{'Encrypt'} ) { + push @{ $mail{'encrypted'} }, @mail; + } else { + push @{ $mail{'plain'} }, @mail; + } +} + +sub cleanup_headers { + my $mail = shift; + # strip id from subject to create new ticket + $mail =~ s/^(Subject:)\s*\[.*?\s+#\d+\]\s*/$1 /m; + # strip several headers + foreach my $field ( qw(Message-ID RT-Originator RT-Ticket X-RT-Loop-Prevention) ) { + $mail =~ s/^$field:.*?\n(?! |\t)//gmsi; + } + return $mail; +} + +sub set_queue_crypt_options { + my %args = @_; + + describe_options('setting queue options: ', %args); + + $m->get_ok("/Admin/Queues/Modify.html?id=". $queue->id); + $m->form_with_fields('Sign', 'Encrypt'); + foreach my $opt ('Sign', 'Encrypt') { + if ( $args{$opt} ) { + $m->tick($opt => 1); + } else { + $m->untick($opt => 1); + } + } + $m->submit; +} + +sub describe_options { + return unless $ENV{'TEST_VERBOSE'}; + + my $msg = shift; + my %args = @_; + if ( $args{'Encrypt'} && $args{'Sign'} ) { + $msg .= 'encrypt and sign'; + } + elsif ( $args{'Sign'} ) { + $msg .= 'sign'; + } + elsif ( $args{'Encrypt'} ) { + $msg .= 'encrypt'; + } + else { + $msg .= 'no encrypt and no sign'; + } + diag $msg; +} + diff --git a/rt/t/web/squish.t b/rt/t/web/squish.t index ff43e74fb..9d1c01b1f 100644 --- a/rt/t/web/squish.t +++ b/rt/t/web/squish.t @@ -5,18 +5,18 @@ 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') ); +RT->Config->Set( LocalStaticPath => RT::Test::get_abs_relocatable_dir('static') ); 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! }, +$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->content_lacks( 'counteract the titlebox', + 'no mobile.css by default' ); $m->back; my ($js_link) = @@ -29,16 +29,16 @@ 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' ); +RT->AddStyleSheets( 'mobile.css' ); ( $url, $m ) = RT::Test->started_ok; $m->login; -$m->follow_link_ok( { url_regex => qr!aileron-squished-([a-f0-9]{32})\.css! }, +$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->content_contains( 'counteract the titlebox', + 'has mobile.css' ); $m->back; ($js_link) = diff --git a/rt/t/web/html/NoAuth/js/not-by-default.js b/rt/t/web/static/js/not-by-default.js index 568f670ee..568f670ee 100644 --- a/rt/t/web/html/NoAuth/js/not-by-default.js +++ b/rt/t/web/static/js/not-by-default.js diff --git a/rt/t/web/template.t b/rt/t/web/template.t index 4a2e6c13a..1a02dc98d 100644 --- a/rt/t/web/template.t +++ b/rt/t/web/template.t @@ -17,7 +17,7 @@ ok( RT::Test->set_rights( ok $m->login('user_a', 'password'), 'logged in as user A'; # get to the templates screen -$m->follow_link( text => 'Configuration' ); +$m->follow_link( text => 'Admin' ); $m->title_is(q{RT Administration}, 'admin screen'); $m->follow_link( text => 'Global' ); diff --git a/rt/t/web/ticket-create-utf8.t b/rt/t/web/ticket-create-utf8.t index 107e41d71..ebb2d5eab 100644 --- a/rt/t/web/ticket-create-utf8.t +++ b/rt/t/web/ticket-create-utf8.t @@ -32,7 +32,7 @@ foreach my $test_str ( $ru_test, $l1_test ) { $m->submit; $m->content_like( - qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i, + qr{<td\s+class="message-header-value\s*"[^>]*>\s*\Q$test_str\E\s*</td>}i, 'header on the page' ); @@ -50,7 +50,7 @@ foreach my $test_str ( $ru_test, $l1_test ) { $m->submit; $m->content_like( - qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i, + qr{<td\s+class="message-header-value\s*"[^>]*>\s*\Q$test_str\E\s*</td>}i, 'header on the page' ); $m->content_contains( @@ -73,7 +73,7 @@ foreach my $test_str ( $ru_test, $l1_test ) { $m->submit; $m->content_like( - qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i, + qr{<td\s+class="message-header-value\s*"[^>]*>\s*\Q$test_str\E\s*</td>}i, 'header on the page' ); $m->content_contains( diff --git a/rt/t/web/ticket_forward.t b/rt/t/web/ticket_forward.t index adf4d6f69..439242d48 100644 --- a/rt/t/web/ticket_forward.t +++ b/rt/t/web/ticket_forward.t @@ -36,19 +36,21 @@ diag "Forward Ticket" if $ENV{TEST_VERBOSE}; $m->submit_form( form_name => 'ForwardMessage', fields => { - To => 'rt-test, rt-to@example.com', - Cc => 'rt-cc@example.com', + To => '"Foo" <rt-foo@example.com>, rt-too@example.com', + Cc => 'rt-cc@example.com', + Bcc => 'root', }, 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', + 'Forwarded Ticket to Foo <rt-foo@example.com>, <rt-too@example.com>, <rt-cc@example.com>, root (Enoch Root)', '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!To: .*?rt-foo\@example.com!i, 'To field' ); + like( $mail, qr!To: .*?rt-too\@example.com!i, 'To field' ); like( $mail, qr!Cc: rt-cc\@example.com!i, 'Cc field' ); + like( $mail, qr!Bcc: root\@localhost!i, 'Bcc 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' ); @@ -60,22 +62,22 @@ diag "Forward Transaction" if $ENV{TEST_VERBOSE}; $m->submit_form( form_name => 'ForwardMessage', fields => { - To => 'rt-test, rt-to@example.com', + To => 'rt-to@example.com, rt-too@example.com', Cc => 'rt-cc@example.com', - Bcc => 'rt-bcc@example.com' + Bcc => 'root' }, 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/, +qr/Forwarded .*?Transaction #\d+.*? to <rt-to\@example\.com>, <rt-too\@example\.com>, <rt-cc\@example\.com>, root (Enoch Root)/, '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!To: .*rt-to\@example.com!i, 'To field' ); + like( $mail, qr!To: .*rt-too\@example.com!i, 'To field' ); like( $mail, qr!Cc: rt-cc\@example.com!i, 'Cc field' ); - like( $mail, qr!Bcc: rt-bcc\@example.com!i, 'Bcc field' ); + like( $mail, qr!Bcc: root\@localhost!i, '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' ); @@ -93,9 +95,8 @@ diag "Forward Ticket without content" if $ENV{TEST_VERBOSE}; 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/Subject: \[example\.com #\d\] Fwd: 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' ); } @@ -107,7 +108,7 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO $m->form_name('TicketCreate'); my $attach = $m->current_form->find_input('Attach'); - $attach->filename("awesome.patch"); + $attach->filename('awesome.pátch'); $attach->headers('Content-Type' => 'text/x-diff'); $m->set_fields( Subject => 'test forward, empty content but attachments', @@ -122,8 +123,8 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO 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/Ticket \d+ created/i, 'created the ticket' ); + $m->content_like( qr/awesome.p\%C3\%A1tch/, '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' ); @@ -137,13 +138,12 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO }, 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' ); + $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/filename\*\=\"UTF\-8\'\'awesome.p\%C3\%A1tch\"/, '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' ); @@ -153,7 +153,7 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_VERBOSE}; { my $mime = MIME::Entity->build( - From => 'test@example.com', + From => '"Tést" <test@example.com>', Subject => 'attachments for everyone', Type => 'multipart/mixed', ); @@ -195,9 +195,8 @@ diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_V }, 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' ); - + $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( @@ -207,15 +206,15 @@ diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_V }, button => 'ForwardAndReturn' ); - $m->content_contains( 'Sent email successfully', 'sent mail msg' ); - $m->content_like( qr/Forwarded Ticket to rt-test\@example\.com/, 'txn 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+\]/; + my $tag = qr/\[example\.com #\d+\] Fwd:/; 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' ); + like( $forward_ticket, qr/From: \=\?UTF-8\?.* \<test\@example\.com\>/i ); for my $mail ($forward_txn, $forward_ticket) { like( $mail, qr/To: rt-test\@example.com/, 'To field' ); @@ -259,7 +258,26 @@ diag "Forward Ticket Template with a Subject: line" if $ENV{TEST_VERBOSE}; ); my ($mail) = RT::Test->fetch_caught_mails; - like($mail, qr/Subject: OVERRIDING SUBJECT/); + like($mail, qr/Subject: \[example.com #\d+\] OVERRIDING SUBJECT/); +} + +diag "Forward Transaction with non-ascii subject" if $ENV{TEST_VERBOSE}; +{ + $m->follow_link_ok( { text => 'Forward', n => 2 }, 'follow 2nd Forward' ); + my $subject = Encode::decode("UTF-8", 'test non-ascii äöü'); + $m->submit_form( + form_name => 'ForwardMessage', + fields => { + Subject => $subject, + To => 'rt-to@example.com', + }, + button => 'ForwardAndReturn' + ); + my ($mail) = RT::Test->fetch_caught_mails; + if ( $mail =~ /Subject: (.+)/ ) { + like( Encode::decode("UTF-8", RT::I18N::DecodeMIMEWordsToUTF8( $1, 'Subject' )), qr/$subject/, 'non-ascii subject' ); + } + $m->content_contains( $subject, 'non-ascii subject got displayed correctly' ); } undef $m; diff --git a/rt/t/web/ticket_links.t b/rt/t/web/ticket_links.t index efb615107..994630efd 100644 --- a/rt/t/web/ticket_links.t +++ b/rt/t/web/ticket_links.t @@ -52,7 +52,7 @@ for my $type ( "DependsOn", "MemberOf", "RefersTo" ) { $m->submit; $m->content_like(qr/Ticket \d+ created/, 'created ticket'); - $m->content_contains("Can't link to a deleted ticket"); + $m->content_contains("Linking to a deleted ticket is not allowed"); $id = RT::Test->last_ticket->id; } @@ -75,7 +75,7 @@ for my $type ( "DependsOn", "MemberOf", "RefersTo" ) { $m->field( "$type-$id", "$deleted_id $active_id $inactive_id" ); } $m->submit; - $m->content_contains("Can't link to a deleted ticket"); + $m->content_contains("Linking to a deleted ticket is not allowed"); if ( $c eq 'base' ) { $m->content_like( @@ -165,7 +165,7 @@ for my $type ( "DependsOn", "MemberOf", "RefersTo" ) { $m->content_lacks('hello test reminder subject'); if ($type eq 'RefersTo') { $m->text_contains("$baseurl/test_ticket_reference"); - $m->text_contains("Article " . $article->Id . ': test article'); + $m->text_contains("Article #" . $article->Id . ': test article'); } } } diff --git a/rt/t/web/ticket_modify_all.t b/rt/t/web/ticket_modify_all.t index 6d19b28e4..6b85d98cf 100644 --- a/rt/t/web/ticket_modify_all.t +++ b/rt/t/web/ticket_modify_all.t @@ -1,13 +1,15 @@ use strict; use warnings; -use RT::Test tests => 22; +use RT::Test tests => undef; my $ticket = RT::Test->create_ticket( Subject => 'test bulk update', Queue => 1, ); +RT->Config->Set(AutocompleteOwners => 1); + my ( $url, $m ) = RT::Test->started_ok; ok( $m->login, 'logged in' ); @@ -19,18 +21,12 @@ $m->submit_form( button => 'SubmitTicket', ); -$m->content_contains("Message recorded", 'updated ticket'); +$m->content_contains("Comments added", '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'); @@ -57,10 +53,18 @@ $m->field('Told_Date' => "2015-01-01 00:00:00"); $m->click('SubmitTicket'); $m->text_contains("Last Contact: (Thu Jan 01 00:00:00 2015)", 'told date successfully updated'); -$m->form_name('TicketModifyAll'); -$m->field('Due_Date' => "2016-01-01 00:00:00"); -$m->click('SubmitTicket'); -$m->text_contains("Due: (Fri Jan 01 00:00:00 2016)", 'due date successfully updated'); +for my $unset ("0", "-", " ") { + $m->form_name('TicketModifyAll'); + $m->field('Due_Date' => "2016-01-01 00:00:00"); + $m->click('SubmitTicket'); + $m->text_contains("Due: (Fri Jan 01 00:00:00 2016)", 'due date successfully updated'); + + $m->form_name('TicketModifyAll'); + $m->field('Due_Date' => $unset); + $m->click('SubmitTicket'); + $m->text_contains("Due: (Not set)", "due date successfully cleared with '$unset'"); + $m->warning_like(qr/Couldn't parse date '-'/) if $unset eq "-"; +} $m->get( $url . '/Ticket/ModifyAll.html?id=' . $ticket->id ); $m->form_name('TicketModifyAll'); @@ -76,8 +80,9 @@ $m->field(WatcherTypeEmail => 'Requestor'); $m->field(WatcherAddressEmail => 'root@localhost'); $m->click('SubmitTicket'); $m->text_contains( - "root is already a Requestor for this ticket", + "root is already a Requestor", 'no duplicate watchers', ); -# XXX TODO test other parts, i.e. links +undef $m; +done_testing; diff --git a/rt/t/web/ticket_modify_people.t b/rt/t/web/ticket_modify_people.t index 750be3f2c..cefbf915b 100644 --- a/rt/t/web/ticket_modify_people.t +++ b/rt/t/web/ticket_modify_people.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test tests => 23; +use RT::Test tests => 25; my $root = RT::Test->load_or_create_user( Name => 'root' ); my $group_foo = RT::Group->new($RT::SystemUser); @@ -80,7 +80,7 @@ ok( $m->reload; ok( $m->find_link( - text => 'Enoch Root', + text => 'root (Enoch Root)', url_regex => qr!/Admin/Users/Modify\.html!, ), 'got link to modify user' @@ -108,6 +108,16 @@ ok( 'got link to modify group' ); +$m->submit_form_ok({ + with_fields => { + WatcherTypeEmail1 => 'Cc', + WatcherAddressEmail1 => '"Foo Bar" <foo@example.com>', + }, + button => 'SubmitTicket', +}, "Added email with phrase as watcher"); + +my $foo = RT::Test->load_or_create_user( EmailAddress => 'foo@example.com' ); +is $foo->RealName, "Foo Bar", "RealName matches"; # TODO test Add|Delete people diff --git a/rt/t/web/ticket_owner.t b/rt/t/web/ticket_owner.t index 81508534a..782e68f8d 100644 --- a/rt/t/web/ticket_owner.t +++ b/rt/t/web/ticket_owner.t @@ -2,7 +2,7 @@ use strict; use warnings; -use RT::Test nodata => 1, tests => 105; +use RT::Test nodata => 1, tests => undef; my $queue = RT::Test->load_or_create_queue( Name => 'Regression' ); ok $queue && $queue->id, 'loaded or created queue'; @@ -10,12 +10,18 @@ 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'; +ok $user_a && $user_a->id, 'loaded or created user: ' . $user_a->Name; my $user_b = RT::Test->load_or_create_user( Name => 'user_b', Password => 'password', ); -ok $user_b && $user_b->id, 'loaded or created user'; +ok $user_b && $user_b->id, 'loaded or created user: ' . $user_b->Name; + +# To give ReassignTicket +my $user_c = RT::Test->load_or_create_user( + Name => 'user_c', Password => 'password', +); +ok $user_c && $user_c->id, 'loaded or created user: ' . $user_c->Name; my ($baseurl, $agent_a) = RT::Test->started_ok; @@ -360,6 +366,7 @@ ok( ] }, { Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] }, + { Principal => $user_c, Right => [qw(SeeQueue ShowTicket ReassignTicket)] }, ), 'set rights' ); @@ -383,10 +390,12 @@ diag fields => { Owner => $user_a->id }, button => 'SubmitTicket', ); - $agent_a->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message' ); + like($agent_a->dom->at('.transaction.people .description')->all_text, + 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 ' ); + like($agent_b->dom->at('.transaction.people .description')->all_text, + qr/user_a\s*-\s*Taken/, 'got user_a Taken message for user b' ); } diag @@ -410,9 +419,106 @@ diag $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' ); + like($agent_a->dom->at('.transaction.people .description')->all_text, + 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 ' ); + like($agent_b->dom->at('.transaction.people .description')->all_text, + qr/user_a\s*-\s*Taken/, 'got user_a Taken message for user b' ); +} + +my $agent_c = RT::Test::Web->new; +ok $agent_c->login('user_c', 'password'), 'logged in as user C'; + +diag "user can assign ticket to new owner with ReassignTicket right"; +{ + 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_c->goto_ticket($id); + ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link'; + ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link'; + + $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); + like($agent_a->dom->at('.transaction.people .description')->all_text, + qr{user_a\s*-\s*Taken}, 'got user_a Taken message' ); + + $agent_c->goto_ticket($id); + ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link'; + ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link'; + $agent_c->follow_link_ok( { text => 'Basics' }, 'Ticket -> Basics' ); + my $form = $agent_c->form_name('TicketModify'); + is $form->value('Owner'), $user_a->id, 'correct owner selected'; + + ok grep($_ == $user_b->id, $form->find_input('Owner')->possible_values), + 'user B is listed as potential owner'; + $agent_c->select('Owner', $user_b->id); + $agent_c->submit; + $agent_c->content_contains( 'Owner changed from user_a to user_b', + 'got set message in Basics' ); + $agent_c->goto_ticket($id); + $agent_c->content_like( qr{Owner forcibly changed}, 'got owner forcibly changed message' ); + ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link'; +} + +ok( + RT::Test->add_rights( + { Principal => $user_c, Right => [qw(OwnTicket)] }, + ), + 'add rights' +); +diag "user can take/steal ticket with ReassignTicket+OwnTicket right"; +{ + 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_c->goto_ticket($id); + ok( ($agent_c->find_all_links( text => 'Take' ))[0], 'has Take link' ); + ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link'; + + $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); + like($agent_a->dom->at('.transaction.people .description')->all_text, + qr{user_a\s*-\s*Taken}, 'got user_a Taken message' ); + + $agent_c->goto_ticket($id); + ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link'; + ok( ($agent_c->find_all_links( text => 'Steal' ))[0], 'has Steal link' ); + $agent_c->follow_link_ok( { text => 'Steal' }, 'Ticket -> Steal' ); + $agent_c->content_contains( 'Owner changed from user_a to user_c', 'steal message' ); + ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link'; + ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link'; } + +undef $agent_a; +undef $agent_b; +undef $agent_c; +done_testing; diff --git a/rt/t/web/ticket_preserve_basics.t b/rt/t/web/ticket_preserve_basics.t new file mode 100644 index 000000000..145941407 --- /dev/null +++ b/rt/t/web/ticket_preserve_basics.t @@ -0,0 +1,110 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my $ticket = RT::Test->create_ticket( + Subject => 'test ticket basics', + Queue => 1, +); + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login, 'logged in' ); + +my $root = RT::Test->load_or_create_user( Name => 'root' ); + +# Failing test where the time units are not preserved when you +# click 'Add more files' on Display +my @form_tries = ( + {Subject => "hello rt"}, + {Status => "open"}, + {Owner => $root->id}, + + ( + map +{ + "Time$_" => undef, + "Time$_-TimeUnits" => 'hours', + }, qw/Estimated Worked Left/ + ), + ( + map +{ + "Time$_" => '1', + "Time$_-TimeUnits" => 'hours', + }, qw/Estimated Worked Left/ + ), + + {InitialPriority => "10"}, + {FinalPriority => "10"}, +); + +for my $try (@form_tries) { + $m->goto_create_ticket(1); + $m->form_name('TicketCreate'); + $m->set_fields(%$try); + $m->click('AddMoreAttach'); + $m->form_name('TicketCreate'); + for my $field (keys %$try) { + is( + $m->value($field), + defined($try->{$field}) ? $try->{$field} : '', + "field $field is the same after the form was submitted" + ); + } +} + +# Test for time unit preservation in Jumbo +for my $try (@form_tries) { + my $jumbo_ticket = RT::Test->create_ticket( + Subject => 'test jumbo ticket basics', + Queue => 1, + ); + + local($try->{Priority}) = delete local($try->{InitialPriority}) + if exists $try->{InitialPriority}; + + $m->get( $url . "/Ticket/ModifyAll.html?id=" . $jumbo_ticket->id ); + $m->form_name('TicketModifyAll'); + $m->set_fields(%$try); + $m->click('AddMoreAttach'); + $m->form_name('TicketModifyAll'); + for my $field (keys %$try) { + is( + $m->value($field), + defined($try->{$field}) ? $try->{$field} : '', + "field $field is the same after the Jumbo form was submitted" + ); + } +} + +my $cf = RT::Test->load_or_create_custom_field( + Name => 'CF1', + Type => 'Freeform', + Pattern => '.', # mandatory + Queue => 'General', +); + +# More time unit testing by a failing CF validation +$m->get_ok($url.'/Admin/CustomFields/Objects.html?id='.$cf->id); +$m->form_with_fields('UpdateObjs'); +$m->tick('AddCustomField-'.$cf->id => '0'); # Make CF global +$m->click('UpdateObjs'); +$m->text_contains('Object created', 'CF applied globally'); + +# Test for preservation when a ticket is submitted and CF validation fails +for my $try (@form_tries) { + $m->goto_create_ticket(1); + $m->form_name('TicketCreate'); + $m->set_fields(%$try); + $m->submit(); + $m->form_name('TicketCreate'); + for my $field (keys %$try) { + is( + $m->value($field), + defined($try->{$field}) ? $try->{$field} : '', + "field $field is the same after the form was submitted" + ); + } +} + +undef $m; +done_testing(); diff --git a/rt/t/web/ticket_txn_content.t b/rt/t/web/ticket_txn_content.t index c0cae976c..096d78e31 100644 --- a/rt/t/web/ticket_txn_content.t +++ b/rt/t/web/ticket_txn_content.t @@ -27,14 +27,14 @@ 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); + $m->get_ok($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); + $m->get_ok($link); } for my $type ( 'text/plain', 'text/html' ) { diff --git a/rt/t/web/user_update.t b/rt/t/web/user_update.t index 54139d797..7be088b0a 100644 --- a/rt/t/web/user_update.t +++ b/rt/t/web/user_update.t @@ -8,7 +8,7 @@ ok( $m->login(), 'logged in' ); $m->follow_link_ok({text => 'About me'}); $m->submit_form_ok({ with_fields => { Lang => 'ja'} }, "Change to Japanese"); -$m->text_contains("Lang changed from (no value) to 'ja'"); +$m->text_contains(Encode::decode("UTF-8","Langは「(値なし)」から「'ja'」に変更されました")); $m->text_contains(Encode::decode("UTF-8","実名"), "Page content is japanese"); # we only changed one field, and it wasn't the default, so this feedback is @@ -19,9 +19,7 @@ $m->content_lacks("That is already the current value"); $m->submit_form_ok({ with_fields => { Lang => 'en_us'} }, "Change back to english"); -# This message shows up in Japanese -# $m->text_contains("Lang changed from 'ja' to 'en_us'"); -$m->text_contains(Encode::decode("UTF-8","Langは「'ja'」から「'en_us'」に変更されました")); +$m->text_contains("Lang changed from 'ja' to 'en_us'"); $m->text_contains("Real Name", "Page content is english"); # Check for a lack of spurious updates @@ -30,11 +28,11 @@ $m->content_lacks("That is already the current value"); # Ensure that we can change the language back to the default. $m->submit_form_ok({ with_fields => { Lang => 'ja'} }, "Back briefly to Japanese"); -$m->text_contains("Lang changed from 'en_us' to 'ja'"); +$m->text_contains(Encode::decode("UTF-8","Langは「'en_us'」から「'ja'」に変更されました")); $m->text_contains(Encode::decode("UTF-8","実名"), "Page content is japanese"); $m->submit_form_ok({ with_fields => { Lang => ''} }, "And set to the default"); -$m->text_contains(Encode::decode("UTF-8","Langは「'ja'」から「(値なし)」に変更されました")); +$m->text_contains("Lang changed from 'ja' to (no value)"); $m->text_contains("Real Name", "Page content is english"); undef $m; diff --git a/rt/t/web/walk.t b/rt/t/web/walk.t index 97aa36e12..2f7272739 100644 --- a/rt/t/web/walk.t +++ b/rt/t/web/walk.t @@ -53,7 +53,7 @@ my @links = ( '/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/Scrips/Modify.html?id=1', '/Admin/Global/Template.html?Template=1', '/Admin/Articles/Classes/Modify.html?id=' . $class->id, '/Search/Build.html?Query=id<10', |