diff options
author | Ivan Kohler <ivan@freeside.biz> | 2014-09-15 20:44:48 -0700 |
---|---|---|
committer | Ivan Kohler <ivan@freeside.biz> | 2014-09-15 20:59:00 -0700 |
commit | 5b3efac57771fbc37874a3dd39d3df835cdd6133 (patch) | |
tree | f653976031646a27771f39902ed9296a4c129f30 /rt/t | |
parent | 008524b8e963831999983769f7fec11f55a72f16 (diff) |
RT 4.0.22
Diffstat (limited to 'rt/t')
74 files changed, 3293 insertions, 704 deletions
diff --git a/rt/t/00-mason-syntax.t b/rt/t/00-mason-syntax.t index 0f77876ae..ac0da0d58 100644 --- a/rt/t/00-mason-syntax.t +++ b/rt/t/00-mason-syntax.t @@ -20,12 +20,11 @@ use HTML::Mason; use HTML::Mason::Compiler; use HTML::Mason::Compiler::ToObject; BEGIN { require RT::Test; } -use Encode qw(decode_utf8); sub compile_file { my $file = shift; - my $text = decode_utf8(RT::Test->file_content($file)); + my $text = Encode::decode( "UTF-8", RT::Test->file_content($file)); my $compiler = new HTML::Mason::Compiler::ToObject; $compiler->compile( diff --git a/rt/t/99-policy.t b/rt/t/99-policy.t new file mode 100644 index 000000000..1980e342f --- /dev/null +++ b/rt/t/99-policy.t @@ -0,0 +1,101 @@ +use strict; +use warnings; + +use RT::Test nodb => 1; +use File::Find; + +my @files; +find( sub { push @files, $File::Find::name if -f }, + qw{etc lib share t bin sbin devel/tools} ); +if ( my $dir = `git rev-parse --git-dir 2>/dev/null` ) { + # We're in a git repo, use the ignore list + chomp $dir; + my %ignores; + $ignores{ $_ }++ for grep $_, split /\n/, + `git ls-files -o -i --exclude-standard .`; + @files = grep {not $ignores{$_}} @files; +} + +sub check { + my $file = shift; + my %check = ( + strict => 0, + warnings => 0, + shebang => 0, + exec => 0, + bps_tag => 0, + @_, + ); + + if ($check{strict} or $check{warnings} or $check{shebang} or $check{bps_tag}) { + local $/; + open my $fh, '<', $file or die $!; + my $content = <$fh>; + + like( + $content, + qr/^use strict(?:;|\s+)/m, + "$file has 'use strict'" + ) if $check{strict}; + + like( + $content, + qr/^use warnings(?:;|\s+)/m, + "$file has 'use warnings'" + ) if $check{warnings}; + + if ($check{shebang} == 1) { + like( $content, qr/^#!/, "$file has shebang" ); + } elsif ($check{shebang} == -1) { + unlike( $content, qr/^#!/, "$file has no shebang" ); + } + + $check{bps_tag} = -1 if $check{bps_tag} == 1 + and not $content =~ /Copyright\s+\(c\)\s+\d\d\d\d-\d\d\d\d Best Practical Solutions/i + and $file =~ /(?:ckeditor|scriptaculous|superfish|tablesorter|farbtastic)/i; + $check{bps_tag} = -1 if $check{bps_tag} == 1 + and not $content =~ /Copyright\s+\(c\)\s+\d\d\d\d-\d\d\d\d Best Practical Solutions/i + and ($content =~ /\b(copyright|GPL|Public Domain)\b/i + or /\(c\)\s+\d\d\d\d(?:-\d\d\d\d)?/i); + if ($check{bps_tag} == 1) { + like( $content, qr/[B]EGIN BPS TAGGED BLOCK {{{/, "$file has BPS license tag"); + } elsif ($check{bps_tag} == -1) { + unlike( $content, qr/[B]EGIN BPS TAGGED BLOCK {{{/, "$file has no BPS license tag"); + } + } + + my $executable = ( stat $file )[2] & 0100; + if ($check{exec} == 1) { + if ( $file =~ /\.in$/ ) { + ok( !$executable, "$file permission is u-x (.in will add +x)" ); + } else { + ok( $executable, "$file permission is u+x" ); + } + } elsif ($check{exec} == -1) { + ok( !$executable, "$file permission is u-x" ); + } +} + +check( $_, shebang => -1, exec => -1, warnings => 1, strict => 1, bps_tag => 1 ) + for grep {m{^lib/.*\.pm$}} @files; + +check( $_, shebang => -1, exec => -1, warnings => 1, strict => 1, bps_tag => -1 ) + for grep {m{^t/.*\.t$}} @files; + +check( $_, shebang => 1, exec => 1, warnings => 1, strict => 1, bps_tag => 1 ) + for grep {m{^s?bin/}} @files; + +check( $_, shebang => 1, exec => 1, warnings => 1, strict => 1, bps_tag => 1 ) + for grep {m{^devel/tools/} and not m{/(localhost\.(crt|key)|mime\.types)$}} @files; + +check( $_, exec => -1, bps_tag => not m{\.(png|gif|jpe?g)$} ) + for grep {m{^share/html/}} @files; + +check( $_, exec => -1 ) + for grep {m{^share/(po|fonts)/}} @files; + +check( $_, exec => -1 ) + for grep {m{^t/data/}} @files; + +check( $_, exec => -1, bps_tag => -1 ) + for grep {m{^etc/upgrade/[^/]+/}} @files; diff --git a/rt/t/api/attachment.t b/rt/t/api/attachment.t index 8b7cb608b..52e3c3f16 100644 --- a/rt/t/api/attachment.t +++ b/rt/t/api/attachment.t @@ -58,10 +58,9 @@ is ($#headers, 2, "testing a bunch of singline multiple headers" ); my $mime = $attachment->ContentAsMIME; like( $mime->head->get('Content-Type'), qr/charset="iso-8859-1"/, 'content type of ContentAsMIME is original' ); - require Encode; is( Encode::decode( 'iso-8859-1', $mime->stringify_body ), - Encode::decode( 'utf8', "Håvard\n" ), + Encode::decode( 'UTF-8', "Håvard\n" ), 'body of ContentAsMIME is original' ); } diff --git a/rt/t/api/canonical_charset.t b/rt/t/api/canonical_charset.t index a426d89b6..86c3e97b3 100644 --- a/rt/t/api/canonical_charset.t +++ b/rt/t/api/canonical_charset.t @@ -3,7 +3,6 @@ use strict; use RT::Test nodata => 1, tests => 11; use RT::I18N; -use Encode; my %map = ( 'euc-cn' => 'gbk', @@ -22,7 +21,7 @@ for my $charset ( keys %map ) { my $mime = MIME::Entity->build( Type => 'text/plain; charset=gb2312', - Data => [encode('gbk', decode_utf8("法新社倫敦11日電"))], + Data => [Encode::encode("gbk", Encode::decode( "UTF-8", "法新社倫敦11日電"))], ); RT::I18N::SetMIMEEntityToUTF8($mime); diff --git a/rt/t/api/cfsearch.t b/rt/t/api/cfsearch.t new file mode 100644 index 000000000..7a460ce2e --- /dev/null +++ b/rt/t/api/cfsearch.t @@ -0,0 +1,106 @@ +use strict; +use warnings; + +use RT::Test tests => 18; + +my $suffix = '-'. $$; + +use_ok 'RT::Users'; +use_ok 'RT::CustomField'; + +my $u1 = RT::User->new( RT->SystemUser ); +isa_ok( $u1, 'RT::User' ); +ok( $u1->Load('root'), "Loaded user 'root'" ); + +# create cf +my $cfname = 'TestUserCF'. $suffix; +my $cf = RT::CustomField->new( RT->SystemUser ); +isa_ok( $cf, 'RT::CustomField' ); + +{ + my ($id, $msg) = $cf->Create( + Name => $cfname, + LookupType => 'RT::User', + Type => 'Freeform', + Description => 'Freeform CF for tests', + ); + ok( $id, "Created cf '$cfname' - " . $msg ); +} + +{ + my ($status, $msg) = $cf->AddToObject( $u1 ); + ok( $status, "Added CF to user object - " . $msg); +} + +my $cfvalue1 = 'Foo'; + +{ + my ($id, $msg) = $u1->AddCustomFieldValue( + Field => $cfname, + Value => $cfvalue1, + RecordTransaction => 0 ); + ok( $id, "Adding CF value '$cfvalue1' - " . $msg ); +} + +# Confirm value is returned. +{ + my $cf_value_ref = QueryCFValue( $cfvalue1, $cf->id ); + is( scalar(@$cf_value_ref), 1, 'Got one value.' ); + is( $cf_value_ref->[0], 'Foo', 'Got Foo back for value.' ); +} + +{ + my ($id, $msg) = $u1->DeleteCustomFieldValue( + 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 ); + ok( $id, "Adding second CF value '$cfvalue2' - " . $msg ); +} + +# Confirm no value is returned for Foo. +{ + # Calling with $cfvalue1 on purpose to confirm + # it has been disabled by the delete above. + + my $cf_value_ref = QueryCFValue( $cfvalue1, $cf->id ); + is( scalar(@$cf_value_ref), 0, 'No values returned for Foo.' ); +} + +# Confirm value is returned for Bar. +{ + my $cf_value_ref = QueryCFValue( $cfvalue2, $cf->id ); + is( scalar(@$cf_value_ref), 1, 'Got one value.' ); + is( $cf_value_ref->[0], 'Bar', 'Got Bar back for value.' ); +} + + +sub QueryCFValue{ + my $cf_value = shift; + my $cf_id = shift; + my @cf_value_strs; + + my $users = RT::Users->new(RT->SystemUser); + isa_ok( $users, 'RT::Users' ); + + $users->LimitCustomField( + CUSTOMFIELD => $cf_id, + OPERATOR => "=", + VALUE => $cf_value ); + + while ( my $filtered_user = $users->Next() ){ + my $cf_values = $filtered_user->CustomFieldValues($cf->id); + while (my $cf_value = $cf_values->Next() ){ + push @cf_value_strs, $cf_value->Content; + } + } + return \@cf_value_strs; +} diff --git a/rt/t/api/i18n_guess.t b/rt/t/api/i18n_guess.t index 956cb1505..a64b2952c 100644 --- a/rt/t/api/i18n_guess.t +++ b/rt/t/api/i18n_guess.t @@ -4,8 +4,6 @@ use warnings; use RT::Test tests => 16; -use Encode qw(encode); - use constant HAS_ENCODE_GUESS => do { local $@; eval { require Encode::Guess; 1 } }; use constant HAS_ENCODE_DETECT => do { local $@; eval { require Encode::Detect::Detector; 1 } }; diff --git a/rt/t/api/menu.t b/rt/t/api/menu.t new file mode 100644 index 000000000..a9cda69c7 --- /dev/null +++ b/rt/t/api/menu.t @@ -0,0 +1,85 @@ +use strict; +use warnings; + +use RT::Test tests => undef; +use RT::Interface::Web::Menu; + +sub child_path_is($$$) { + my ($menu, $child, $expected) = @_; + my $c = $menu->child($child->[0], path => $child->[1]); + is $c->path, $expected, "'$child->[1]' normalizes to '$expected'"; + return $c; +} + +{ + package FakeRequest; + sub new { bless {}, shift } + sub path_info { "" } + + package FakeInterp; + require CGI; + sub new { bless {}, shift } + sub cgi_object { CGI->new } +} + +local $HTML::Mason::Commands::r = FakeRequest->new; +local $HTML::Mason::Commands::m = FakeInterp->new; + +my $menu = RT::Interface::Web::Menu->new; +ok $menu, "Created top level menu"; + +child_path_is $menu, [search => "Search/Simple.html"], "/Search/Simple.html"; +child_path_is $menu, [absolute => "/Prefs/Other.html"], "/Prefs/Other.html"; +child_path_is $menu, [scheme => "http://example.com"], "http://example.com"; + +my $tools = + child_path_is $menu, [tools => "/Tools/"], "/Tools/"; + child_path_is $tools, [myday => "MyDay.html"], "/Tools/MyDay.html"; + child_path_is $tools, [activity => "/Activity.html"], "/Activity.html"; + my $ext = + child_path_is $tools, [external => "http://example.com"], "http://example.com"; + child_path_is $ext, [wiki => "wiki/"], "http://example.com/wiki/"; + +# Pathological case of multiplying slashes +my $home = + child_path_is $menu, [home => "/"], "/"; + child_path_is $home, [slash => "/"], "/"; + child_path_is $home, [empty => ""], "/"; + + + +sub order_ok($$;$) { + my ($menu, $expected, $name) = @_; + my @children = $menu->children; + + is scalar @children, scalar @$expected, "correct number of children"; + is_deeply [map { $_->key } @children], $expected, $name; + + my $last_child = shift @children; # first child's sort doesn't matter + for (@children) { + ok $_->sort_order > $last_child->sort_order, sprintf "%s order higher than %s's", $_->key, $last_child->key; + $last_child = $_; + } +} + +$menu = RT::Interface::Web::Menu->new; + +ok $menu->child("foo", title => "foo"), "added child foo"; +order_ok $menu, [qw(foo)], "sorted"; + +ok $menu->child("foo")->add_after("bar", title => "bar"), "added child bar after foo"; +order_ok $menu, [qw(foo bar)], "sorted after"; + +ok $menu->child("bar")->add_before("baz", title => "baz"), "added child baz before bar"; +order_ok $menu, [qw(foo baz bar)], "sorted before (in between)"; + +ok $menu->child("bat", title => "bat", sort_order => 2.2), "added child bat between baz and bar"; +order_ok $menu, [qw(foo baz bat bar)], "sorted between manually"; + +ok $menu->child("bat")->add_before("pre", title => "pre"), "added child pre before bat"; +order_ok $menu, [qw(foo baz pre bat bar)], "sorted between (before)"; + +ok $menu->child("bat")->add_after("post", title => "post"), "added child post after bat"; +order_ok $menu, [qw(foo baz pre bat post bar)], "sorted between (after)"; + +done_testing; diff --git a/rt/t/api/password-types.t b/rt/t/api/password-types.t index 10a874a3d..4cb634248 100644 --- a/rt/t/api/password-types.t +++ b/rt/t/api/password-types.t @@ -3,8 +3,6 @@ use warnings; use RT::Test; use Digest::MD5; -use Encode 'encode_utf8'; -use utf8; my $default = "sha512"; @@ -43,9 +41,9 @@ like($root->__Value("Password"), qr/^\!$default\!/, "And is now upgraded to salt # Non-ASCII salted truncated SHA-256 my $non_ascii_trunc = MIME::Base64::encode_base64( - "salt" . substr(Digest::SHA::sha256("salt".Digest::MD5::md5(encode_utf8("áěšý"))),0,26), + "salt" . substr(Digest::SHA::sha256("salt".Digest::MD5::md5("áěšý")),0,26), "" ); $root->_Set( Field => "Password", Value => $non_ascii_trunc); -ok($root->IsPassword("áěšý"), "Unsalted MD5 base64 works"); +ok($root->IsPassword(Encode::decode("UTF-8", "áěšý")), "Unsalted MD5 base64 works"); like($root->__Value("Password"), qr/^\!$default\!/, "And is now upgraded to salted $default"); diff --git a/rt/t/api/template-parsing.t b/rt/t/api/template-parsing.t new file mode 100644 index 000000000..455b84d27 --- /dev/null +++ b/rt/t/api/template-parsing.t @@ -0,0 +1,306 @@ +use strict; +use warnings; +use RT; +use RT::Test tests => 266; +use Test::Warn; + +my $queue = RT::Queue->new(RT->SystemUser); +$queue->Load("General"); + +my $ticket_cf = RT::CustomField->new(RT->SystemUser); +$ticket_cf->Create( + Name => 'Department', + Queue => '0', + Type => 'FreeformSingle', +); + +my $txn_cf = RT::CustomField->new(RT->SystemUser); +$txn_cf->Create( + Name => 'Category', + LookupType => RT::Transaction->CustomFieldLookupType, + Type => 'FreeformSingle', +); +$txn_cf->AddToObject($queue); + +my $ticket = RT::Ticket->new(RT->SystemUser); +my ($id, $msg) = $ticket->Create( + Subject => "template testing", + Queue => "General", + Owner => 'root@localhost', + Requestor => ["dom\@example.com"], + "CustomField-" . $txn_cf->id => "Special", +); +ok($id, "Created ticket: $msg"); +my $txn = $ticket->Transactions->First; + +$ticket->AddCustomFieldValue( + Field => 'Department', + Value => 'Coolio', +); + +TemplateTest( + Content => "\ntest", + PerlOutput => "test", + SimpleOutput => "test", +); + +TemplateTest( + Content => "\ntest { 5 * 5 }", + PerlOutput => "test 25", + SimpleOutput => "test { 5 * 5 }", +); + +TemplateTest( + Content => "\ntest { \$Requestor }", + PerlOutput => "test dom\@example.com", + SimpleOutput => "test dom\@example.com", +); + +TemplateTest( + Content => "\ntest { \$TicketSubject }", + PerlOutput => "test ", + SimpleOutput => "test template testing", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketQueueId }", + Output => "test 1", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketQueueName }", + Output => "test General", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketOwnerId }", + Output => "test 12", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketOwnerName }", + Output => "test root", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketOwnerEmailAddress }", + Output => "test root\@localhost", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketStatus }", + Output => "test new", +); + +SimpleTemplateTest( + Content => "\ntest #{ \$TicketId }", + Output => "test #" . $ticket->id, +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketCFDepartment }", + Output => "test Coolio", +); + +SimpleTemplateTest( + Content => "\ntest #{ \$TransactionId }", + Output => "test #" . $txn->id, +); + +SimpleTemplateTest( + Content => "\ntest { \$TransactionType }", + Output => "test Create", +); + +SimpleTemplateTest( + Content => "\ntest { \$TransactionCFCategory }", + Output => "test Special", +); + +SimpleTemplateTest( + Content => "\ntest { \$TicketDelete }", + Output => "test { \$TicketDelete }", +); + +SimpleTemplateTest( + Content => "\ntest { \$Nonexistent }", + Output => "test { \$Nonexistent }", +); + +warning_like { + TemplateTest( + Content => "\ntest { \$Ticket->Nonexistent }", + PerlOutput => undef, + SimpleOutput => "test { \$Ticket->Nonexistent }", + ); +} qr/RT::Ticket::Nonexistent Unimplemented/; + +warning_like { + TemplateTest( + Content => "\ntest { \$Nonexistent->Nonexistent }", + PerlOutput => undef, + SimpleOutput => "test { \$Nonexistent->Nonexistent }", + ); +} qr/Can't call method "Nonexistent" on an undefined value/; + +TemplateTest( + Content => "\ntest { \$Ticket->OwnerObj->Name }", + PerlOutput => "test root", + SimpleOutput => "test { \$Ticket->OwnerObj->Name }", +); + +warning_like { + TemplateTest( + Content => "\ntest { *!( }", + SyntaxError => 1, + PerlOutput => undef, + SimpleOutput => "test { *!( }", + ); +} qr/Template parsing error: syntax error/; + +TemplateTest( + Content => "\ntest { \$rtname ", + SyntaxError => 1, + PerlOutput => undef, + SimpleOutput => undef, +); + +is($ticket->Status, 'new', "test setup"); +SimpleTemplateTest( + Content => "\ntest { \$Ticket->SetStatus('resolved') }", + Output => "test { \$Ticket->SetStatus('resolved') }", +); +is($ticket->Status, 'new', "simple templates can't call ->SetStatus"); + +note "test arguments passing"; +{ + PerlTemplateTest( + Content => "\ntest { \$Nonexistent }", + Output => "test ", + ); + PerlTemplateTest( + Content => "\ntest { \$Nonexistent }", + Arguments => { Nonexistent => 'foo' }, + Output => "test foo", + ); + + PerlTemplateTest( + Content => "\n".'array: { join ", ", @array }', + Arguments => { array => [qw(foo bar)] }, + Output => "array: foo, bar", + ); + PerlTemplateTest( + Content => "\n".'hash: { join ", ", map "$_ => $hash{$_}", sort keys %hash }', + Arguments => { hash => {1 => 2, a => 'b'} }, + Output => "hash: 1 => 2, a => b", + ); + PerlTemplateTest( + Content => "\n".'code: { code() }', + Arguments => { code => sub { "baz" } }, + Output => "code: baz", + ); +} + +# Make sure changing the template's type works +{ + my $template = RT::Template->new(RT->SystemUser); + $template->Create( + Name => "type chameleon", + Type => "Perl", + Content => "\ntest { 10 * 7 }", + ); + ok($id = $template->id, "Created template"); + $template->Parse; + is($template->MIMEObj->stringify_body, "test 70", "Perl output"); + + $template = RT::Template->new(RT->SystemUser); + $template->Load($id); + is($template->Name, "type chameleon"); + + $template->SetType('Simple'); + $template->Parse; + is($template->MIMEObj->stringify_body, "test { 10 * 7 }", "Simple output"); + + $template = RT::Template->new(RT->SystemUser); + $template->Load($id); + is($template->Name, "type chameleon"); + + $template->SetType('Perl'); + $template->Parse; + is($template->MIMEObj->stringify_body, "test 70", "Perl output"); +} + +undef $ticket; + +my $counter = 0; +sub IndividualTemplateTest { + local $Test::Builder::Level = $Test::Builder::Level + 1; + + my %args = ( + Name => "Test-" . ++$counter, + Type => "Perl", + @_, + ); + + my $t = RT::Template->new(RT->SystemUser); + $t->Create( + Name => $args{Name}, + Type => $args{Type}, + Content => $args{Content}, + ); + + ok($t->id, "Created $args{Type} template"); + is($t->Name, $args{Name}, "$args{Type} template name"); + is($t->Content, $args{Content}, "$args{Type} content"); + is($t->Type, $args{Type}, "template type"); + + # this should never blow up! + my ($ok, $msg) = $t->CompileCheck; + + # we don't need to syntax check simple templates since if you mess them up + # it's safe to just use the input directly as the template's output + if ($args{SyntaxError} && $args{Type} eq 'Perl') { + ok(!$ok, "got a syntax error"); + } + else { + ok($ok, $msg); + } + + ($ok, $msg) = $t->Parse( + $args{'Arguments'} + ? ( %{ $args{'Arguments'} } ) + : (TicketObj => $ticket, TransactionObj => $txn ) + , + ); + if (defined $args{Output}) { + ok($ok, $msg); + is($t->MIMEObj->stringify_body, $args{Output}, "$args{Type} template's output"); + } + else { + ok(!$ok, "expected a failure"); + } +} + +sub TemplateTest { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my %args = @_; + + for my $type ('Perl', 'Simple') { + IndividualTemplateTest( + %args, + Type => $type, + Output => $args{$type . 'Output'}, + ); + } +} + +sub SimpleTemplateTest { + local $Test::Builder::Level = $Test::Builder::Level + 1; + IndividualTemplateTest( @_, Type => 'Simple' ); +} + +sub PerlTemplateTest { + local $Test::Builder::Level = $Test::Builder::Level + 1; + IndividualTemplateTest( @_, Type => 'Perl' ); +} + diff --git a/rt/t/api/transaction.t b/rt/t/api/transaction.t new file mode 100644 index 000000000..22c3cfe72 --- /dev/null +++ b/rt/t/api/transaction.t @@ -0,0 +1,52 @@ + +use strict; +use warnings; +use RT; +use RT::Test tests => undef; +use Test::Warn; + +use_ok ('RT::Transaction'); + +{ + my $u = RT::User->new(RT->SystemUser); + $u->Load("root"); + ok ($u->Id, "Found the root user"); + ok(my $t = RT::Ticket->new(RT->SystemUser)); + my ($id, $msg) = $t->Create( Queue => 'General', + Subject => 'Testing', + Owner => $u->Id + ); + ok($id, "Create new ticket $id"); + isnt($id , 0); + + my $txn = RT::Transaction->new(RT->SystemUser); + my ($txn_id, $txn_msg) = $txn->Create( + Type => 'AddLink', + Field => 'RefersTo', + Ticket => $id, + NewValue => 'ticket 42', ); + ok( $txn_id, "Created transaction $txn_id: $txn_msg"); + + my $brief; + warning_like { $brief = $txn->BriefDescription } + qr/Could not determine a URI scheme/, + "Caught URI warning"; + + is( $brief, 'Reference to ticket 42 added', "Got string description: $brief"); + + $txn = RT::Transaction->new(RT->SystemUser); + ($txn_id, $txn_msg) = $txn->Create( + Type => 'DeleteLink', + Field => 'RefersTo', + Ticket => $id, + OldValue => 'ticket 42', ); + ok( $txn_id, "Created transaction $txn_id: $txn_msg"); + + warning_like { $brief = $txn->BriefDescription } + qr/Could not determine a URI scheme/, + "Caught URI warning"; + + is( $brief, 'Reference to ticket 42 deleted', "Got string description: $brief"); +} + +done_testing; diff --git a/rt/t/api/uri-canonicalize.t b/rt/t/api/uri-canonicalize.t new file mode 100644 index 000000000..288569c7f --- /dev/null +++ b/rt/t/api/uri-canonicalize.t @@ -0,0 +1,54 @@ +use strict; +use warnings; +use RT::Test tests => undef; + +my @warnings; +local $SIG{__WARN__} = sub { + push @warnings, "@_"; +}; + +# Create ticket +my $ticket = RT::Test->create_ticket( Queue => 1, Subject => 'test ticket' ); +ok $ticket->id, 'created ticket'; + +# Create article class +my $class = RT::Class->new( $RT::SystemUser ); +$class->Create( Name => 'URItest - '. $$ ); +ok $class->id, 'created a class'; + +# Create article +my $article = RT::Article->new( $RT::SystemUser ); +$article->Create( + Name => 'Testing URI parsing - '. $$, + Summary => 'In which this should load', + Class => $class->Id +); +ok $article->id, 'create article'; + +# Test permutations of URIs +my $ORG = RT->Config->Get('Organization'); +my $URI = RT::URI->new( RT->SystemUser ); +my %expected = ( + # tickets + "1" => "fsck.com-rt://$ORG/ticket/1", + "t:1" => "fsck.com-rt://$ORG/ticket/1", + "fsck.com-rt://$ORG/ticket/1" => "fsck.com-rt://$ORG/ticket/1", + + # articles + "a:1" => "fsck.com-article://$ORG/article/1", + "fsck.com-article://$ORG/article/1" => "fsck.com-article://$ORG/article/1", + + # random stuff + "http://$ORG" => "http://$ORG", + "mailto:foo\@example.com" => "mailto:foo\@example.com", + "invalid" => "invalid", # doesn't trigger die +); +for my $uri (sort keys %expected) { + is $URI->CanonicalizeURI($uri), $expected{$uri}, "canonicalized as expected"; +} + +is_deeply \@warnings, [ + "Could not determine a URI scheme for invalid\n", +], "expected warnings"; + +done_testing; diff --git a/rt/t/customfields/date.t b/rt/t/customfields/date.t new file mode 100644 index 000000000..475ace664 --- /dev/null +++ b/rt/t/customfields/date.t @@ -0,0 +1,86 @@ +use Test::MockTime qw(set_fixed_time restore_time); + +use warnings; +use strict; + +use RT::Test tests => undef; + +RT::Test->set_rights( + { Principal => 'Everyone', Right => [qw( + SeeQueue ShowTicket CreateTicket SeeCustomField ModifyCustomField + )] }, +); + +my $q = RT::Test->load_or_create_queue( Name => 'General' ); +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; + +my $user_b = RT::Test->load_or_create_user( Name => 'boston', Timezone => 'America/New_York' ); +ok $user_b && $user_b->id; + + +my $cf_name = 'A Date'; +my $cf; +{ + $cf = RT::CustomField->new(RT->SystemUser); + ok( + $cf->Create( + Name => $cf_name, + Type => 'Date', + MaxValues => 1, + LookupType => RT::Ticket->CustomFieldLookupType, + ), + 'create cf date' + ); + ok( $cf->AddToObject($q), 'date cf apply to queue' ); +} + +{ + my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user_m ) ); + my ($id) = $ticket->Create( + Queue => $q->id, + Subject => 'Test', + 'CustomField-'. $cf->id => '2013-02-11', + ); + my $cf_value = $ticket->CustomFieldValues($cf_name)->First; + is( $cf_value->Content, '2013-02-11', 'correct value' ); + + $ticket = RT::Ticket->new( RT::CurrentUser->new( $user_b ) ); + $ticket->Load($id); + is( $ticket->FirstCustomFieldValue($cf_name), '2013-02-11', 'correct value' ); +} + +{ + my $ticket = RT::Ticket->new(RT->SystemUser); + ok( + $ticket->Create( + Queue => $q->id, + Subject => 'Test', + 'CustomField-' . $cf->id => '2010-05-04 11:34:56', + ), + 'create ticket with cf set to 2010-05-04 11:34:56' + ); + is( $ticket->CustomFieldValues->First->Content, + '2010-05-04', 'date in db only has date' ); +} + +# in moscow it's already Feb 11, so tomorrow is Feb 12 +set_fixed_time("2013-02-10T23:10:00Z"); +{ + my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user_m ) ); + my ($id) = $ticket->Create( + Queue => $q->id, + Subject => 'Test', + 'CustomField-'. $cf->id => 'tomorrow', + ); + my $cf_value = $ticket->CustomFieldValues($cf_name)->First; + is( $cf_value->Content, '2013-02-12', 'correct value' ); + + $ticket = RT::Ticket->new( RT::CurrentUser->new( $user_b ) ); + $ticket->Load($id); + is( $ticket->FirstCustomFieldValue($cf_name), '2013-02-12', 'correct value' ); +} + +done_testing(); diff --git a/rt/t/customfields/datetime.t b/rt/t/customfields/datetime.t new file mode 100644 index 000000000..5e4497d0c --- /dev/null +++ b/rt/t/customfields/datetime.t @@ -0,0 +1,76 @@ +use Test::MockTime qw(set_fixed_time restore_time); + +use warnings; +use strict; + +use RT::Test tests => undef; + +RT::Test->set_rights( + { Principal => 'Everyone', Right => [qw( + SeeQueue ShowTicket CreateTicket SeeCustomField ModifyCustomField + )] }, +); + +my $q = RT::Test->load_or_create_queue( Name => 'General' ); +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; + +my $user_b = RT::Test->load_or_create_user( Name => 'boston', Timezone => 'America/New_York' ); +ok $user_b && $user_b->id; + + +my $cf_name = 'A Date and Time'; +my $cf; +{ + $cf = RT::CustomField->new(RT->SystemUser); + ok( + $cf->Create( + Name => $cf_name, + Type => 'DateTime', + MaxValues => 1, + LookupType => RT::Ticket->CustomFieldLookupType, + ), + 'create cf date' + ); + ok( $cf->AddToObject($q), 'date cf apply to queue' ); +} + +{ + my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user_m ) ); + my ($id) = $ticket->Create( + Queue => $q->id, + Subject => 'Test', + 'CustomField-'. $cf->id => '2013-02-11 00:00:00', + ); + my $cf_value = $ticket->CustomFieldValues($cf_name)->First; + TODO: { + local $TODO = 'questionable result, should we change?'; + # $Ticket->Created returns UTC, not user's date, but + # ticket has ->CreatedObj method to get all required + # transformation + # No more TODO. + is( $cf_value->Content, '2013-02-11 00:00:00', 'correct value' ); + } + is( $cf_value->Content, '2013-02-10 20:00:00', 'correct value' ); + + $ticket = RT::Ticket->new( RT::CurrentUser->new( $user_b ) ); + $ticket->Load($id); + is( $ticket->FirstCustomFieldValue($cf_name), '2013-02-10 20:00:00', 'correct value' ); +} + +# in moscow it's already Feb 11, so tomorrow is Feb 12 +set_fixed_time("2013-02-10T23:10:00Z"); +{ + my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user_m ) ); + my ($id) = $ticket->Create( + Queue => $q->id, + Subject => 'Test', + 'CustomField-'. $cf->id => 'tomorrow', + ); + my $cf_value = $ticket->CustomFieldValues($cf_name)->First; + is( $cf_value->Content, '2013-02-11 23:10:00', 'correct value' ); +} + +done_testing(); diff --git a/rt/t/customfields/iprangev6.t b/rt/t/customfields/iprangev6.t index 3b8a4d60a..84fec16a0 100644 --- a/rt/t/customfields/iprangev6.t +++ b/rt/t/customfields/iprangev6.t @@ -193,7 +193,7 @@ diag "check that we parse correct IPs only" if $ENV{'TEST_VERBOSE'}; } ); - $agent->content_like( qr/can not be parsed as an IP address range/, + $agent->content_like( qr/is not a valid IP address range/, 'ticket fails to create' ); } diff --git a/rt/t/customfields/repeated_values.t b/rt/t/customfields/repeated_values.t new file mode 100644 index 000000000..584512c7d --- /dev/null +++ b/rt/t/customfields/repeated_values.t @@ -0,0 +1,134 @@ +use warnings; +use strict; + +use RT::Test tests => undef; + + +my $ticket = RT::Test->create_ticket( Subject => 'test repeated values', Queue => 'General' ); +my ( $ret, $msg ); + +{ + diag "testing freeform single cf"; + my $freeform_single = RT::Test->load_or_create_custom_field( + Name => 'freeform single', + Type => 'FreeformSingle', + Queue => 0, + ); + + ( $ret, $msg ) = + $ticket->AddCustomFieldValue( Field => $freeform_single, Value => 'foo' ); + ok( $ret, $msg ); + is( $ticket->FirstCustomFieldValue($freeform_single), 'foo', 'value is foo' ); + + my $ocfv = $ticket->CustomFieldValues($freeform_single)->First; + ( $ret, $msg ) = + $ticket->AddCustomFieldValue( Field => $freeform_single, Value => 'foo' ); + is( $ret, $ocfv->id, "got the same previous object" ); + is( $ticket->FirstCustomFieldValue($freeform_single), 'foo', 'value is still foo' ); + + ( $ret, $msg ) = + $ticket->AddCustomFieldValue( Field => $freeform_single, Value => 'FOO' ); + ok( $ret, $msg ); + isnt( $ret, $ocfv->id, "got a new value" ); + is( $ticket->FirstCustomFieldValue($freeform_single), 'FOO', 'value is FOO' ); +} + +{ + diag "testing freeform multiple cf"; + my $freeform_multiple = RT::Test->load_or_create_custom_field( + Name => 'freeform multiple', + Type => 'FreeformMultiple', + Queue => 0, + ); + + ($ret, $msg) = $ticket->AddCustomFieldValue( Field => $freeform_multiple, Value => 'foo' ); + ok($ret, $msg); + is( $ticket->FirstCustomFieldValue($freeform_multiple), 'foo', 'value is foo' ); + + my $ocfv = $ticket->CustomFieldValues($freeform_multiple)->First; + ($ret, $msg) = $ticket->AddCustomFieldValue( Field => $freeform_multiple, Value => 'foo' ); + is($ret, $ocfv->id, "got the same previous object"); + is( $ticket->FirstCustomFieldValue($freeform_multiple), 'foo', 'value is still foo' ); + + ($ret, $msg) = $ticket->AddCustomFieldValue( Field => $freeform_multiple, Value => 'bar' ); + ok($ret, $msg); + + my $ocfvs = $ticket->CustomFieldValues($freeform_multiple)->ItemsArrayRef; + is( scalar @$ocfvs, 2, 'has 2 values'); + is( $ocfvs->[0]->Content, 'foo', 'first is foo' ); + is( $ocfvs->[1]->Content, 'bar', 'sencond is bar' ); +} + +{ + diag "testing select single cf"; + + my $select_single = RT::Test->load_or_create_custom_field( + Name => 'select single', + Type => 'SelectSingle', + Queue => 0, + ); + + for my $value ( qw/foo bar baz/ ) { + $select_single->AddValue( Name => $value ); + } + + ( $ret, $msg ) = + $ticket->AddCustomFieldValue( Field => $select_single, Value => 'foo' ); + ok( $ret, $msg ); + my $ocfv = $ticket->CustomFieldValues($select_single)->First; + is( $ticket->FirstCustomFieldValue($select_single), 'foo', 'value is foo' ); + ( $ret, $msg ) = + $ticket->AddCustomFieldValue( Field => $select_single, Value => 'foo' ); + is( $ret, $ocfv->id, "got the same previous object" ); + is( $ticket->FirstCustomFieldValue($select_single), 'foo', 'value is still foo' ); + + diag "select values are case insensitive"; + + ( $ret, $msg ) = + $ticket->AddCustomFieldValue( Field => $select_single, Value => 'FOO' ); + is( $ret, $ocfv->id, "got the same previous object" ); + is( $ticket->FirstCustomFieldValue($select_single), 'foo', 'value is still foo' ); + + ($ret, $msg) = $ticket->AddCustomFieldValue( Field => $select_single, Value => 'bar' ); + ok($ret, $msg); + isnt( $ret, $ocfv->id, "got a new value" ); + is( $ticket->FirstCustomFieldValue($select_single), 'bar', 'new value is bar' ); +} + +{ + diag "testing binary single cf"; + + my $binary_single = RT::Test->load_or_create_custom_field( + Name => 'upload single', + Type => 'BinarySingle', + Queue => 0, + ); + + ( $ret, $msg ) = + $ticket->AddCustomFieldValue( Field => $binary_single, Value => 'foo', LargeContent => 'bar' ); + ok( $ret, $msg ); + my $ocfv = $ticket->CustomFieldValues($binary_single)->First; + ( $ret, $msg ) = + $ticket->AddCustomFieldValue( Field => $binary_single, Value => 'foo', LargeContent => 'bar' ); + is( $ret, $ocfv->id, "got the same previous object" ); + is($ocfv->Content, 'foo', 'name is foo'); + is($ocfv->LargeContent, 'bar', 'content is bar'); + + ( $ret, $msg ) = + $ticket->AddCustomFieldValue( Field => $binary_single, Value => 'foo', LargeContent => 'baz' ); + ok( $ret, $msg ); + isnt( $ret, $ocfv->id, "got a new value" ); + $ocfv = $ticket->CustomFieldValues($binary_single)->First; + is($ocfv->Content, 'foo', 'name is foo'); + is($ocfv->LargeContent, 'baz', 'content is baz'); + + ( $ret, $msg ) = + $ticket->AddCustomFieldValue( Field => $binary_single, Value => 'foo.2', LargeContent => 'baz' ); + ok( $ret, $msg ); + isnt( $ret, $ocfv->id, "got a new value" ); + $ocfv = $ticket->CustomFieldValues($binary_single)->First; + is($ocfv->Content, 'foo.2', 'name is foo.2'); + is($ocfv->LargeContent, 'baz', 'content is baz'); +} + +done_testing(); diff --git a/rt/t/data/configs/apache2.2+fastcgi.conf b/rt/t/data/configs/apache2.2+fastcgi.conf new file mode 100644 index 000000000..ab2613662 --- /dev/null +++ b/rt/t/data/configs/apache2.2+fastcgi.conf @@ -0,0 +1,50 @@ +ServerRoot %%SERVER_ROOT%% +PidFile %%PID_FILE%% +LockFile %%LOCK_FILE%% +ServerAdmin root@localhost + +%%LOAD_MODULES%% + +<IfModule !mpm_netware_module> +<IfModule !mpm_winnt_module> +User freeside +Group freeside +</IfModule> +</IfModule> + +ServerName localhost +Listen %%LISTEN%% + +ErrorLog "%%LOG_FILE%%" +LogLevel debug + +<Directory /> + Options FollowSymLinks + AllowOverride None + Order deny,allow + Deny from all +</Directory> + +AddDefaultCharset UTF-8 + +FastCgiServer %%RT_SBIN_PATH%%/rt-server.fcgi \ + -socket %%TMP_DIR%%/socket \ + -processes 1 \ + -idle-timeout 180 \ + -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%%" +<Location /> + Order allow,deny + Allow from all + +%%BASIC_AUTH%% + + Options +ExecCGI + AddHandler fastcgi-script fcgi +</Location> + diff --git a/rt/t/data/configs/apache2.2+mod_perl.conf b/rt/t/data/configs/apache2.2+mod_perl.conf new file mode 100644 index 000000000..ae84c9d6b --- /dev/null +++ b/rt/t/data/configs/apache2.2+mod_perl.conf @@ -0,0 +1,67 @@ +<IfModule mpm_prefork_module> + StartServers 1 + MinSpareServers 1 + MaxSpareServers 1 + MaxClients 1 + MaxRequestsPerChild 0 +</IfModule> + +<IfModule mpm_worker_module> + StartServers 1 + MinSpareThreads 1 + MaxSpareThreads 1 + ThreadLimit 1 + ThreadsPerChild 1 + MaxClients 1 + MaxRequestsPerChild 0 +</IfModule> + +ServerRoot %%SERVER_ROOT%% +PidFile %%PID_FILE%% +LockFile %%LOCK_FILE%% +ServerAdmin root@localhost + +%%LOAD_MODULES%% + +<IfModule !mpm_netware_module> +<IfModule !mpm_winnt_module> +User freeside +Group freeside +</IfModule> +</IfModule> + +ServerName localhost +Listen %%LISTEN%% + +ErrorLog "%%LOG_FILE%%" +LogLevel debug + +<Directory /> + Options FollowSymLinks + AllowOverride None + Order deny,allow + Deny from all +</Directory> + +AddDefaultCharset UTF-8 +PerlSetEnv RT_SITE_CONFIG %%RT_SITE_CONFIG%% + +DocumentRoot "%%DOCUMENT_ROOT%%" +<Location /> + Order allow,deny + Allow from all + +%%BASIC_AUTH%% + + SetHandler modperl + + PerlResponseHandler Plack::Handler::Apache2 + PerlSetVar psgi_app %%RT_SBIN_PATH%%/rt-server +</Location> + +<Perl> + $ENV{RT_TESTING}=1; + use Plack::Handler::Apache2; + Plack::Handler::Apache2->preload("%%RT_SBIN_PATH%%/rt-server"); +</Perl> + diff --git a/rt/t/data/emails/text-html-in-russian b/rt/t/data/emails/text-html-in-russian deleted file mode 100755 index b965b1b59..000000000 --- a/rt/t/data/emails/text-html-in-russian +++ /dev/null @@ -1,87 +0,0 @@ -From rickt@other-example.com Tue Jun 17 20:39:13 2003 -Return-Path: <rickt@other-example.com> -X-Original-To: info -Delivered-To: mitya@vh.example.com -Received: from example.com (mx.example.com [194.87.0.32]) - by vh.example.com (Postfix) with ESMTP id 8D77B16E6BD - for <info>; Tue, 17 Jun 2003 20:39:05 +0400 (MSD) -Received: from hotline@example.com - by example.com (CommuniGate Pro GROUP 4.1b7/D) - with GROUP id 76033026; Tue, 17 Jun 2003 20:38:00 +0400 -Received: by example.com (CommuniGate Pro PIPE 4.1b7/D) - with PIPE id 76033052; Tue, 17 Jun 2003 20:38:00 +0400 -Received: from [217.132.49.75] (HELO compuserve.com) - by example.com (CommuniGate Pro SMTP 4.1b7/D) - with SMTP id 76032971 for info@example.com; Tue, 17 Jun 2003 20:37:41 +0400 -Date: Wed, 18 Jun 2003 01:41:01 +0000 -From: <rickt@other-example.com> -Subject: , YXLWLJ3LPT9UHuLyGTzyuKQc06eIZ96Y6RVTCZFt -To: Info <info@example.com> -References: <0ID97EGL951H1907@example.com> -In-Reply-To: <0ID97EGL951H1907@example.com> -Message-ID: <HDE46LIK8GGJJ72I@other-example.com> -MIME-Version: 1.0 -Content-Type: text/html; charset=Windows-1251 -Content-Transfer-Encoding: 8bit -X-Spam-Flag: YES -X-Spam-Checker-Version: SpamAssassin 2.60-cvs-jumbo.demos (1.190-2003-06-01-exp) -X-Spam-Level: ++++++++++++++ -X-Spam-Status: Yes, hits=14.9 required=5.0 tests=BAYES_99,DATE_IN_FUTURE_06_12 - FROM_ILLEGAL_CHARS,HTML_10_20,HTML_FONTCOLOR_UNKNOWN,HTML_FONT_BIG - MIME_HTML_ONLY,RCVD_IN_NJABL,SUBJ_HAS_SPACES,SUBJ_HAS_UNIQ_ID - SUBJ_ILLEGAL_CHARS autolearn=no version=2.60-cvs-jumbo.demos -X-Spam-Report: 14.9 points, 5.0 required; - * 2.3 -- Subject contains lots of white space - * 1.0 -- BODY: HTML font color is unknown to us - * 0.3 -- BODY: FONT Size +2 and up or 3 and up - [score: 1.0000] - * 2.8 -- BODY: Bayesian classifier spam probability is 99 to 100% - * 1.0 -- BODY: Message is 10% to 20% HTML - * 1.0 -- From contains too many raw illegal characters - * 1.0 -- Subject contains a unique ID - * 1.0 -- Subject contains too many raw illegal characters - * 1.2 -- Date: is 6 to 12 hours after Received: date - [217.132.49.75 listed in dnsbl.njabl.org] - * 1.2 -- RBL: Received via a relay in dnsbl.njabl.org - * 2.0 -- Message only has text/html MIME parts -Status: RO -Content-Length: 2743 -Lines: 36 - -<html><body><basefont face="times new roman, times, serif" size="2"> -<center>e ep " " paae a pe:<br> -<font size="5"><b> </b></font><br> -<font color="red"><b>19 2003 .</b></font><br> -<b><i>pe peaae ceo cpeeo paeecoo epcoaa.</i></b><br></center><br> -<p align="justify"><b>peep: opoo ae.</b> paec coo, o pao oee 10 e oac coo ec-peo. op pa a eoec oco pa apae coo, o ce: eoo eooo oe, e pe e epeoopo, pae oppoa opopaoo a p. ao ae coao coo, occc ae ocapceo c p peee , pc MBA.<br><br> -<b><u>e pea:</u></b><br> -1. co pe pae oae;<br> -2. o paece a oa epcoaa paoe;<br> -3. co ocoe a oaoopaoa;<br> -4. ae paec eoa coa ce paoe oa, oaoopaoa.<br><br> -<b><u>aa pea:</u></b><br> - - co eo oe p e oe opeeeo eeoc;<br> - - ac apa oe copo cooece c aaa opaa.<br><br> -<b><u>oepae popa:</u></b><br> -<b>I. aepae eaepae op oa:</b><br> -1. eco po oa pae epcoao;<br> -2. paa pae opaa.<br> -<b>II. paecoe peee oa pae epcoao:</b><br> -1. ope pacope;<br> -2. oa oea eeoc (po aeca copo);<br> -3. oa paa aaa.<br><br> -<b><u> aepe popa ac co:</u></b><br> -1. pepoa copo a ocee opeeeoo peaa;<br> -2. ae eoo aa pae oae epcoaa;<br> -3. pe oee a pae pae epcoao;<br> -4. pee ae ocoeoc (peoe) oa copo opaa.<br> -<i> oe pea coec pao cpao aepa o oa cpoa epcoaa poccc oa. o ooa aec cepa.</i><br><br> -<center>pooeoc: 1 e, 8 aco (a epepa, oe)<br> -<b>ooc ac: 4 700 pe e .</b><br> -921-5862, 928-4156, 928-4200, 928-5321</center><br> -<font size=1> c opa oooo poa ac e epece o p opoca - e: <a href="mailto:motiv@mailje.nl">seminar</a></font> -<br><font size="1" color="#ffffff">3ZkRPb60QBbiHef1IRVl</font> -</body></html> - - - diff --git a/rt/t/data/plugins/Overlays/html/overlay_loaded b/rt/t/data/plugins/Overlays/html/overlay_loaded new file mode 100644 index 000000000..eeeb0320f --- /dev/null +++ b/rt/t/data/plugins/Overlays/html/overlay_loaded @@ -0,0 +1,8 @@ +<%flags> +inherit => undef # avoid auth +</%flags> +<%init> +$r->content_type("text/plain"); +$m->out( $RT::User::LOADED_OVERLAY ? "yes" : "no" ); +$m->abort(200); +</%init> diff --git a/rt/t/data/plugins/Overlays/html/user_accessible b/rt/t/data/plugins/Overlays/html/user_accessible new file mode 100644 index 000000000..8eef2b437 --- /dev/null +++ b/rt/t/data/plugins/Overlays/html/user_accessible @@ -0,0 +1,8 @@ +<%flags> +inherit => undef # avoid auth +</%flags> +<%init> +$r->content_type("application/json"); +$m->out( JSON( RT::User->_ClassAccessible() ) ); +$m->abort(200); +</%init> diff --git a/rt/t/data/plugins/Overlays/lib/Overlays.pm b/rt/t/data/plugins/Overlays/lib/Overlays.pm new file mode 100644 index 000000000..f18b45877 --- /dev/null +++ b/rt/t/data/plugins/Overlays/lib/Overlays.pm @@ -0,0 +1,2 @@ +package Overlays; +1; diff --git a/rt/t/data/plugins/Overlays/lib/RT/User_Local.pm b/rt/t/data/plugins/Overlays/lib/RT/User_Local.pm new file mode 100644 index 000000000..312cc09f6 --- /dev/null +++ b/rt/t/data/plugins/Overlays/lib/RT/User_Local.pm @@ -0,0 +1,11 @@ +package RT::User; +use strict; +use warnings; + +our $LOADED_OVERLAY = 1; + +sub _LocalAccessible { + { Comments => { public => 1 } } +} + +1; diff --git a/rt/t/i18n/default.t b/rt/t/i18n/default.t index ea0848f54..d98828f0b 100644 --- a/rt/t/i18n/default.t +++ b/rt/t/i18n/default.t @@ -13,10 +13,10 @@ $m->content_contains('<html lang="en">'); $m->add_header('Accept-Language' => 'zh-tw,zh;q=0.8,en-gb;q=0.5,en;q=0.3'); $m->get_ok('/'); -use utf8; -Encode::_utf8_on($m->{content}); -$m->title_is('登入', 'Page title properly translated to chinese'); -$m->content_contains('密碼','Password properly translated'); +$m->title_is( Encode::decode("UTF-8",'登入'), + 'Page title properly translated to chinese'); +$m->content_contains( Encode::decode("UTF-8",'密碼'), + 'Password properly translated'); { local $TODO = "We fail to correctly advertise the langauage in the <html> block"; $m->content_contains('<html lang="zh-tw">'); diff --git a/rt/t/mail/charsets-outgoing.t b/rt/t/mail/charsets-outgoing.t index 2fc91f2e0..872721325 100644 --- a/rt/t/mail/charsets-outgoing.t +++ b/rt/t/mail/charsets-outgoing.t @@ -1,6 +1,5 @@ use strict; use warnings; -use Encode; use RT::Test tests => 78; @@ -72,7 +71,7 @@ foreach my $set ( 'ru', 'latin1' ) { my $status = 1; foreach my $mail ( @mails ) { my $entity = parse_mail( $mail ); - my $subject = Encode::decode_utf8( $entity->head->get('Subject') ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); $subject =~ /$string{$set}{test}/ or do { $status = 0; diag "wrong subject: $subject" }; } @@ -101,7 +100,7 @@ diag "ascii subject with non-ascii subject tag"; my $status = 1; foreach my $mail ( @mails ) { my $entity = parse_mail( $mail ); - my $subject = Encode::decode_utf8( $entity->head->get('Subject') ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); $subject =~ /$string{$tag_set}{support}/ or do { $status = 0; diag "wrong subject: $subject" }; } @@ -122,7 +121,7 @@ foreach my $set ( 'ru', 'latin1' ) { my $status = 1; foreach my $mail ( @mails ) { my $entity = parse_mail( $mail ); - my $subject = Encode::decode_utf8( $entity->head->get('Subject') ); + 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}/ @@ -171,7 +170,7 @@ diag "ascii subject with non-ascii subject prefix in template"; my $status = 1; foreach my $mail ( @mails ) { my $entity = parse_mail( $mail ); - my $subject = Encode::decode_utf8( $entity->head->get('Subject') ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); $subject =~ /$string{$prefix_set}{autoreply}/ or do { $status = 0; diag "wrong subject: $subject" }; } @@ -192,7 +191,7 @@ foreach my $set ( 'ru', 'latin1' ) { my $status = 1; foreach my $mail ( @mails ) { my $entity = parse_mail( $mail ); - my $subject = Encode::decode_utf8( $entity->head->get('Subject') ); + 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}/ @@ -222,7 +221,7 @@ foreach my $set ( 'ru', 'latin1' ) { my $status = 1; foreach my $mail ( @mails ) { my $entity = parse_mail( $mail ); - my $subject = Encode::decode_utf8( $entity->head->get('Subject') ); + 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}/ @@ -275,7 +274,7 @@ foreach my $set ( 'ru', 'latin1' ) { my $status = 1; foreach my $mail ( @mails ) { my $entity = parse_mail( $mail ); - my $subject = Encode::decode_utf8( $entity->head->get('Subject') ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); $subject =~ /$string{$set}{test}/ or do { $status = 0; diag "wrong subject: $subject" }; } @@ -303,7 +302,7 @@ foreach my $set ( 'ru', 'latin1' ) { my $status = 1; foreach my $mail ( @mails ) { my $entity = parse_mail( $mail ); - my $subject = Encode::decode_utf8( $entity->head->get('Subject') ); + 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}/ diff --git a/rt/t/mail/dashboard-chart-with-utf8.t b/rt/t/mail/dashboard-chart-with-utf8.t index 79f5f0e11..37f8ce0c6 100644 --- a/rt/t/mail/dashboard-chart-with-utf8.t +++ b/rt/t/mail/dashboard-chart-with-utf8.t @@ -12,8 +12,6 @@ BEGIN { } } -use utf8; - my $root = RT::Test->load_or_create_user( Name => 'root' ); my ( $baseurl, $m ) = RT::Test->started_ok; @@ -21,11 +19,11 @@ ok( $m->login, 'logged in' ); my $ticket = RT::Ticket->new( $RT::SystemUser ); $ticket->Create( Queue => 'General', - Subject => 'test äöü', + Subject => Encode::decode("UTF-8",'test äöü'), ); ok( $ticket->id, 'created ticket' ); -$m->get_ok(q{/Search/Chart.html?Query=Subject LIKE 'test äöü'}); +$m->get_ok(Encode::decode("UTF-8", q{/Search/Chart.html?Query=Subject LIKE 'test äöü'})); $m->submit_form( form_name => 'SaveSearch', fields => { @@ -58,7 +56,7 @@ $m->field( 'Hour' => '06:00' ); $m->click_button( name => 'Save' ); $m->content_contains('Subscribed to dashboard dashboard foo'); -my $c = $m->get(q{/Search/Chart?Query=Subject LIKE 'test äöü'}); +my $c = $m->get(Encode::decode("UTF-8",q{/Search/Chart?Query=Subject LIKE 'test äöü'})); my $image = $c->content; RT::Test->run_and_capture( command => $RT::SbinPath . '/rt-email-dashboards', all => 1 diff --git a/rt/t/mail/extractsubjecttag.t b/rt/t/mail/extractsubjecttag.t index 14fab44b5..1aadaa7b7 100644 --- a/rt/t/mail/extractsubjecttag.t +++ b/rt/t/mail/extractsubjecttag.t @@ -1,6 +1,5 @@ use strict; use warnings; -use utf8; use RT::Test tests => 18; diff --git a/rt/t/mail/gateway.t b/rt/t/mail/gateway.t index 9482ffcb2..4f906c89c 100644 --- a/rt/t/mail/gateway.t +++ b/rt/t/mail/gateway.t @@ -504,8 +504,7 @@ EOF is ($tick->Id, $id, "correct ticket"); is ($tick->Subject , 'This is a test of I18N ticket creation', "Created the ticket - ". $tick->Subject); - my $unistring = "\303\241\303\251\303\255\303\263\303\272"; - Encode::_utf8_on($unistring); + my $unistring = Encode::decode("UTF-8","\303\241\303\251\303\255\303\263\303\272"); is ( $tick->Transactions->First->Content, $tick->Transactions->First->Attachments->First->Content, @@ -542,8 +541,7 @@ EOF is ($tick->Id, $id, "correct ticket"); is ($tick->Subject , 'This is a test of I18N ticket creation', "Created the ticket"); - my $unistring = "\303\241\303\251\303\255\303\263\303\272"; - Encode::_utf8_on($unistring); + my $unistring = Encode::decode("UTF-8","\303\241\303\251\303\255\303\263\303\272"); ok ( $tick->Transactions->First->Content =~ $unistring, @@ -573,8 +571,7 @@ EOF my $tick = RT::Test->last_ticket; is ($tick->Id, $id, "correct ticket"); - my $content = $tick->Transactions->First->Content; - Encode::_utf8_off($content); + my $content = Encode::encode("UTF-8",$tick->Transactions->First->Content); like $content, qr{informaci\303\263n confidencial}; like $content, qr{informaci\357\277\275n confidencial}; diff --git a/rt/t/mail/header-characters.t b/rt/t/mail/header-characters.t new file mode 100644 index 000000000..004ba8522 --- /dev/null +++ b/rt/t/mail/header-characters.t @@ -0,0 +1,81 @@ +use strict; +use warnings; + +use RT::Test tests => 12; +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; + + my $mail = Encode::encode( 'iso-8859-1', Encode::decode( "UTF-8", <<'.') ); +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 + +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/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" ); + } +} + +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", <<'.' ) ); +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 +Content-Type: text/plain; charset=iso-8859-1 + +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/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" ); + } +} + +diag "No sender"; +{ + my $mail = <<'.'; +To: rt@example.com +Subject: testing non-ASCII From +Content-Type: text/plain; charset=iso-8859-1 + +here's some content +. + + my ($status, $id); + 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" ); + ok( !$id, "No ticket created" ); +} diff --git a/rt/t/mail/not-supported-charset.t b/rt/t/mail/not-supported-charset.t new file mode 100644 index 000000000..bf2fe8f05 --- /dev/null +++ b/rt/t/mail/not-supported-charset.t @@ -0,0 +1,69 @@ +use strict; +use warnings; + +use RT::Test tests => undef; +use Test::Warn; + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok $queue->id, 'loaded queue'; + +{ + my $mail = <<'END'; +From: root@localhost +Subject: test +Content-type: text/plain; charset="not-supported-encoding" + +ho hum just some text + +END + + my ($stat, $id); + warning_like { + ($stat, $id) = RT::Test->send_via_mailgate($mail); + is( $stat >> 8, 0, "The mail gateway exited normally" ); + ok( $id, "created ticket" ); + } qr/Encoding 'not-supported-encoding' is not supported/; + + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load($id); + ok $ticket->id, "loaded ticket"; + + my $txn = $ticket->Transactions->First; + ok !$txn->ContentObj, 'no content'; + + my $attach = $txn->Attachments->First; + like $attach->Content, qr{ho hum just some text}, 'attachment is there'; + is $attach->GetHeader('Content-Type'), + 'application/octet-stream; charset="not-supported-encoding"', + 'content type is changed' + ; + is $attach->GetHeader('X-RT-Original-Content-Type'), + 'text/plain', + 'original content type is saved' + ; +} + +{ + my $mail = <<'END'; +From: root@localhost +Subject: =?not-supported?Q?=07test=A9?= +Content-type: text/plain; charset="ascii" + +ho hum just some text + +END + + my ($stat, $id); + warning_like { + ($stat, $id) = RT::Test->send_via_mailgate($mail); + is( $stat >> 8, 0, "The mail gateway exited normally" ); + ok( $id, "created ticket" ); + } qr/Charset 'not-supported' is not supported/; + + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load($id); + ok $ticket->id, "loaded ticket"; + is $ticket->Subject, "\x{FFFD}test\x{FFFD}"; +} + +done_testing; diff --git a/rt/t/mail/one-time-recipients.t b/rt/t/mail/one-time-recipients.t index 3484d1470..a9881cded 100644 --- a/rt/t/mail/one-time-recipients.t +++ b/rt/t/mail/one-time-recipients.t @@ -1,6 +1,5 @@ use strict; use warnings; -use utf8; use RT::Test tests => 38; diff --git a/rt/t/mail/rfc2231-attachment.t b/rt/t/mail/rfc2231-attachment.t index fc74c4720..9610961f0 100644 --- a/rt/t/mail/rfc2231-attachment.t +++ b/rt/t/mail/rfc2231-attachment.t @@ -1,7 +1,6 @@ use strict; use warnings; -use utf8; use RT::Test tests => undef; my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'logged in as root'; @@ -20,7 +19,7 @@ diag "encoded attachment filename with parameter continuations"; ok( $id, "Created ticket" ); $m->get_ok("/Ticket/Display.html?id=$id"); - $m->content_contains("新しいテキスト ドキュメント.txt", "found full filename"); + $m->content_contains(Encode::decode("UTF-8","新しいテキスト ドキュメント.txt"), "found full filename"); } undef $m; diff --git a/rt/t/mail/sendmail.t b/rt/t/mail/sendmail.t index 44903f375..56202ad5d 100644 --- a/rt/t/mail/sendmail.t +++ b/rt/t/mail/sendmail.t @@ -1,546 +1,150 @@ use strict; use warnings; -use File::Spec (); - -use RT::Test tests => 141; -use RT::EmailParser; -use RT::Tickets; -use RT::Action::SendEmail; +use RT::Test tests => undef; -my @_outgoing_messages; -my @scrips_fired; +use File::Spec (); +use Email::Abstract; -#We're not testing acls here. +# We're not testing acls here. my $everyone = RT::Group->new(RT->SystemUser); $everyone->LoadSystemInternalGroup('Everyone'); $everyone->PrincipalObj->GrantRight( Right =>'SuperUser' ); - -is (__PACKAGE__, 'main', "We're operating in the main package"); - -{ - no warnings qw/redefine/; - *RT::Action::SendEmail::SendMessage = sub { - my $self = shift; - my $MIME = shift; - - main::_fired_scrip($self->ScripObj); - main::is(ref($MIME) , 'MIME::Entity', "hey, look. it's a mime entity"); - }; -} - # some utils sub first_txn { return $_[0]->Transactions->First } sub first_attach { return first_txn($_[0])->Attachments->First } - -sub count_txns { return $_[0]->Transactions->Count } sub count_attachs { return first_txn($_[0])->Attachments->Count } -# instrument SendEmail to pass us what it's about to send. -# create a regular ticket - -my $parser = RT::EmailParser->new(); - -# Let's test to make sure a multipart/report is processed correctly -my $multipart_report_email = RT::Test::get_relocatable_file('multipart-report', - (File::Spec->updir(), 'data', 'emails')); -my $content = RT::Test->file_content($multipart_report_email); -# be as much like the mail gateway as possible. -use RT::Interface::Email; -my %args = (message => $content, queue => 1, action => 'correspond'); -my ($status, $msg) = RT::Interface::Email::Gateway(\%args); -ok($status, "successfuly used Email::Gateway interface") or diag("error: $msg"); -my $tickets = RT::Tickets->new(RT->SystemUser); -$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); -$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); -my $tick= $tickets->First(); -isa_ok($tick, "RT::Ticket", "got a ticket object"); -ok ($tick->Id, "found ticket ".$tick->Id); -like (first_txn($tick)->Content , qr/The original message was received/, "It's the bounce"); - - -# make sure it fires scrips. -is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation"); - -undef @scrips_fired; - - - - -$parser->ParseMIMEEntityFromScalar('From: root@localhost -To: rt@example.com -Subject: This is a test of new ticket creation as an unknown user - -Blah! -Foob!'); - - -use Data::Dumper; - -my $ticket = RT::Ticket->new(RT->SystemUser); -my ($id, undef, $create_msg ) = $ticket->Create(Requestor => ['root@localhost'], Queue => 'general', Subject => 'I18NTest', MIMEObj => $parser->Entity); -ok ($id,$create_msg); -$tickets = RT::Tickets->new(RT->SystemUser); -$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); -$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); - $tick = $tickets->First(); -ok ($tick->Id, "found ticket ".$tick->Id); -is ($tick->Subject , 'I18NTest', "failed to create the new ticket from an unprivileged account"); - -# make sure it fires scrips. -is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation"); -# make sure it sends an autoreply -# make sure it sends a notification to adminccs - - -# we need to swap out SendMessage to test the new things we care about; -&utf8_redef_sendmessage; - -# create an iso 8859-1 ticket -@scrips_fired = (); - -my $iso_8859_1_ticket_email = RT::Test::get_relocatable_file( - 'new-ticket-from-iso-8859-1', (File::Spec->updir(), 'data', 'emails')); -$content = RT::Test->file_content($iso_8859_1_ticket_email); - - - -$parser->ParseMIMEEntityFromScalar($content); - - -# be as much like the mail gateway as possible. -use RT::Interface::Email; - - %args = (message => $content, queue => 1, action => 'correspond'); - RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new(RT->SystemUser); -$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); -$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); - $tick = $tickets->First(); -ok ($tick->Id, "found ticket ".$tick->Id); - -like (first_txn($tick)->Content , qr/H\x{e5}vard/, "It's signed by havard. yay"); - - -# make sure it fires scrips. -is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation"); -# make sure it sends an autoreply - - -# make sure it sends a notification to adminccs - -# If we correspond, does it do the right thing to the outbound messages? - -$parser->ParseMIMEEntityFromScalar($content); - ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity); -ok ($id, $msg); - -$parser->ParseMIMEEntityFromScalar($content); -($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity); -ok ($id, $msg); - - - - - -# we need to swap out SendMessage to test the new things we care about; -&iso8859_redef_sendmessage; -RT->Config->Set( EmailOutputEncoding => 'iso-8859-1' ); -# create an iso 8859-1 ticket -@scrips_fired = (); - - $content = RT::Test->file_content($iso_8859_1_ticket_email); -# be as much like the mail gateway as possible. -use RT::Interface::Email; - - %args = (message => $content, queue => 1, action => 'correspond'); - RT::Interface::Email::Gateway(\%args); -$tickets = RT::Tickets->new(RT->SystemUser); -$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); -$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); - $tick = $tickets->First(); -ok ($tick->Id, "found ticket ".$tick->Id); - -like (first_txn($tick)->Content , qr/H\x{e5}vard/, "It's signed by havard. yay"); - - -# make sure it fires scrips. -is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation"); -# make sure it sends an autoreply - - -# make sure it sends a notification to adminccs - - -# If we correspond, does it do the right thing to the outbound messages? - -$parser->ParseMIMEEntityFromScalar($content); - ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity); -ok ($id, $msg); - -$parser->ParseMIMEEntityFromScalar($content); -($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity); -ok ($id, $msg); - - -sub _fired_scrip { - my $scrip = shift; - push @scrips_fired, $scrip; -} +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); -sub utf8_redef_sendmessage { - no warnings qw/redefine/; - *RT::Action::SendEmail::SendMessage = sub { - my $self = shift; - my $MIME = shift; + RT::Test->clean_caught_mails; + my ($status, $id) = RT::Test->send_via_mailgate( $content ); + ok( $status, "Fed $filename into mailgate"); - my $scrip = $self->ScripObj->id; - ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name); - main::_fired_scrip($self->ScripObj); - $MIME->make_singlepart; - main::is( ref($MIME) , 'MIME::Entity', - "hey, look. it's a mime entity" ); - main::is( ref( $MIME->head ) , 'MIME::Head', - "its mime header is a mime header. yay" ); - main::like( $MIME->head->get('Content-Type') , qr/utf-8/, - "Its content type is utf-8" ); - my $message_as_string = $MIME->bodyhandle->as_string(); - use Encode; - $message_as_string = Encode::decode_utf8($message_as_string); - main::like( - $message_as_string , qr/H\x{e5}vard/, -"The message's content contains havard's name. this will fail if it's not utf8 out"); + 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); } -sub iso8859_redef_sendmessage { - no warnings qw/redefine/; - *RT::Action::SendEmail::SendMessage = sub { - my $self = shift; - my $MIME = shift; - - my $scrip = $self->ScripObj->id; - ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name); - main::_fired_scrip($self->ScripObj); - $MIME->make_singlepart; - main::is( ref($MIME) , 'MIME::Entity', - "hey, look. it's a mime entity" ); - main::is( ref( $MIME->head ) , 'MIME::Head', - "its mime header is a mime header. yay" ); - main::like( $MIME->head->get('Content-Type') , qr/iso-8859-1/, - "Its content type is iso-8859-1 - " . $MIME->head->get("Content-Type") ); - my $message_as_string = $MIME->bodyhandle->as_string(); - use Encode; - $message_as_string = Encode::decode("iso-8859-1",$message_as_string); - main::like( - $message_as_string , qr/H\x{e5}vard/, "The message's content contains havard's name. this will fail if it's not utf8 out"); - }; +{ + 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 $alt_umlaut_email = RT::Test::get_relocatable_file( - 'multipart-alternative-with-umlaut', (File::Spec->updir(), 'data', 'emails')); - $content = RT::Test->file_content($alt_umlaut_email); - -$parser->ParseMIMEEntityFromScalar($content); + 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"); +} -# be as much like the mail gateway as possible. { - no warnings qw/redefine/; - local *RT::Action::SendEmail::SendMessage = sub { return 1}; - - %args = (message => $content, queue => 1, action => 'correspond'); - RT::Interface::Email::Gateway(\%args); - # TODO: following 5 lines should replaced by get_latest_ticket_ok() - $tickets = RT::Tickets->new(RT->SystemUser); - $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); - $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); - $tick = $tickets->First(); - - ok ($tick->Id, "found ticket ".$tick->Id); - - like (first_txn($tick)->Content , qr/causes Error/, "We recorded the content right as text-plain"); - is (count_attachs($tick) , 3 , "Has three attachments, presumably a text-plain, a text-html and a multipart alternative"); - + 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 $text_html_email = RT::Test::get_relocatable_file('text-html-with-umlaut', - (File::Spec->updir(), 'data', 'emails')); - $content = RT::Test->file_content($text_html_email); - -$parser->ParseMIMEEntityFromScalar($content); - - -# be as much like the mail gateway as possible. -&text_html_redef_sendmessage; - - %args = (message => $content, queue => 1, action => 'correspond'); - RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new(RT->SystemUser); -$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); -$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); - $tick = $tickets->First(); -ok ($tick->Id, "found ticket ".$tick->Id); - -like (first_attach($tick)->Content , qr/causes Error/, "We recorded the content as containing 'causes error'") or diag( first_attach($tick)->Content ); -like (first_attach($tick)->ContentType , qr/text\/html/, "We recorded the content as text/html"); -is (count_attachs($tick), 1 , "Has one attachment, presumably a text-html and a multipart alternative"); - -sub text_html_redef_sendmessage { - no warnings qw/redefine/; - *RT::Action::SendEmail::SendMessage = sub { - my $self = shift; - my $MIME = shift; - return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" ); - is ($MIME->parts, 0, "generated correspondence mime entity - does not have parts"); - is ($MIME->head->mime_type , "text/plain", "The mime type is a plain"); - }; +{ + 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 $russian_email = RT::Test::get_relocatable_file('text-html-in-russian', - (File::Spec->updir(), 'data', 'emails')); - $content = RT::Test->file_content($russian_email); - -$parser->ParseMIMEEntityFromScalar($content); - -# be as much like the mail gateway as possible. -&text_html_redef_sendmessage; - - %args = (message => $content, queue => 1, action => 'correspond'); - { - -my @warnings; -local $SIG{__WARN__} = sub { - push @warnings, "@_"; -}; - -RT::Interface::Email::Gateway(\%args); - -TODO: { - local $TODO = -'need a better approach of encoding converter, should be fixed in 4.2'; -ok( @warnings == 1 || @warnings == 2, "1 or 2 warnings are ok" ); -ok( @warnings == 1 || ( @warnings == 2 && $warnings[1] eq $warnings[0] ), - 'if there are 2 warnings, they should be same' ); - -like( - $warnings[0], - qr/\QEncoding error: "\x{041f}" does not map to iso-8859-1/, -"The badly formed Russian spam we have isn't actually well-formed UTF8, which makes Encode (correctly) warn", -); - -} + 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'); } - $tickets = RT::Tickets->new(RT->SystemUser); -$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); -$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); - $tick = $tickets->First(); -ok ($tick->Id, "found ticket ".$tick->Id); - -like (first_attach($tick)->ContentType , qr/text\/html/, "We recorded the content right as text-html"); - -is (count_attachs($tick) ,1 , "Has one attachment, presumably a text-html and a multipart alternative"); - - - -RT->Config->Set( EmailInputEncodings => 'koi8-r', RT->Config->Get('EmailInputEncodings') ); -RT->Config->Set( EmailOutputEncoding => 'koi8-r' ); -my $russian_subject_email = RT::Test::get_relocatable_file( - 'russian-subject-no-content-type', (File::Spec->updir(), 'data', 'emails')); -$content = RT::Test->file_content($russian_subject_email); - -$parser->ParseMIMEEntityFromScalar($content); - - -# be as much like the mail gateway as possible. -&text_plain_russian_redef_sendmessage; - %args = (message => $content, queue => 1, action => 'correspond'); - RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new(RT->SystemUser); -$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); -$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); -$tick= $tickets->First(); -ok ($tick->Id, "found ticket ".$tick->Id); - -like (first_attach($tick)->ContentType , qr/text\/plain/, "We recorded the content type right"); -is (count_attachs($tick) ,1 , "Has one attachment, presumably a text-plain"); -is ($tick->Subject, "\x{442}\x{435}\x{441}\x{442} \x{442}\x{435}\x{441}\x{442}", "Recorded the subject right"); -sub text_plain_russian_redef_sendmessage { - no warnings qw/redefine/; - *RT::Action::SendEmail::SendMessage = sub { - my $self = shift; - my $MIME = shift; - return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" ); - is ($MIME->head->mime_type , "text/plain", "The only part is text/plain "); - my $subject = $MIME->head->get("subject"); - chomp($subject); - #is( $subject , /^=\?KOI8-R\?B\?W2V4YW1wbGUuY39tICM3XSDUxdPUINTF09Q=\?=/ , "The $subject is encoded correctly"); - }; +{ + my ($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 @input_encodings = RT->Config->Get( 'EmailInputEncodings' ); -shift @input_encodings; -RT->Config->Set(EmailInputEncodings => @input_encodings ); -RT->Config->Set(EmailOutputEncoding => 'utf-8'); - - - -my $nested_rfc822_email = RT::Test::get_relocatable_file('nested-rfc-822', - (File::Spec->updir(), 'data', 'emails')); -$content = RT::Test->file_content($nested_rfc822_email); -ok ($content, "Loaded nested-rfc-822 to test"); - -$parser->ParseMIMEEntityFromScalar($content); - - -# be as much like the mail gateway as possible. -&text_plain_nested_redef_sendmessage; - %args = (message => $content, queue => 1, action => 'correspond'); - RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new(RT->SystemUser); -$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); -$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); -$tick= $tickets->First(); -ok ($tick->Id, "found ticket ".$tick->Id); -is ($tick->Subject, "[Jonas Liljegren] Re: [Para] Niv\x{e5}er?"); -like (first_attach($tick)->ContentType , qr/multipart\/mixed/, "We recorded the content type right"); -is (count_attachs($tick) , 5 , "Has one attachment, presumably a text-plain and a message RFC 822 and another plain"); -sub text_plain_nested_redef_sendmessage { - no warnings qw/redefine/; - *RT::Action::SendEmail::SendMessage = sub { - my $self = shift; - my $MIME = shift; - - return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" ); - - is ($MIME->head->mime_type , "multipart/mixed", "It is a mixed multipart"); - - use MIME::Words qw(:all); - my $encoded_subject = $MIME->head->get("subject"); - my $subject = decode_mimewords($encoded_subject); - - # MIME::Words isn't actually UTF8-safe. There go 4 hours I'll never get back. - utf8::decode($subject); - like($subject, qr/Niv\x{e5}er/, "The subject matches the word - $subject"); - - 1; - }; +{ + 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 $uuencoded_email = RT::Test::get_relocatable_file('notes-uuencoded', - (File::Spec->updir(), 'data', 'emails')); - $content = RT::Test->file_content($uuencoded_email); - -$parser->ParseMIMEEntityFromScalar($content); - - -# be as much like the mail gateway as possible. { - no warnings qw/redefine/; - local *RT::Action::SendEmail::SendMessage = sub { return 1}; - %args = (message => $content, queue => 1, action => 'correspond'); - RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new(RT->SystemUser); - $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); - $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); - $tick= $tickets->First(); - ok ($tick->Id, "found ticket ".$tick->Id); - - like (first_txn($tick)->Content , qr/from Lotus Notes/, "We recorded the content right"); - is (count_attachs($tick) , 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; +} - - my $crashes_file_based_parser_email = RT::Test::get_relocatable_file( - 'crashes-file-based-parser', (File::Spec->updir(), 'data', 'emails')); - $content = RT::Test->file_content($crashes_file_based_parser_email); - -$parser->ParseMIMEEntityFromScalar($content); - - -# be as much like the mail gateway as possible. - -no warnings qw/redefine/; -local *RT::Action::SendEmail::SendMessage = sub { return 1}; - %args = (message => $content, queue => 1, action => 'correspond'); - RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new(RT->SystemUser); -$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); -$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); -$tick= $tickets->First(); -ok ($tick->Id, "found ticket ".$tick->Id); - -like (first_txn($tick)->Content , qr/FYI/, "We recorded the content right"); -is (count_attachs($tick) , 5 , "Has three attachments"); - - - - - - - my $rt_send_cc_email = RT::Test::get_relocatable_file('rt-send-cc', - (File::Spec->updir(), 'data', 'emails')); - $content = RT::Test->file_content($rt_send_cc_email); - -$parser->ParseMIMEEntityFromScalar($content); - - - - %args = (message => $content, queue => 1, action => 'correspond'); - RT::Interface::Email::Gateway(\%args); - $tickets = RT::Tickets->new(RT->SystemUser); -$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC'); -$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0'); -$tick= $tickets->First(); -ok ($tick->Id, "found ticket ".$tick->Id); - -my $cc = first_attach($tick)->GetHeader('RT-Send-Cc'); -like ($cc , qr/test1/, "Found test 1"); -like ($cc , qr/test2/, "Found test 2"); -like ($cc , qr/test3/, "Found test 3"); -like ($cc , qr/test4/, "Found test 4"); -like ($cc , qr/test5/, "Found test 5"); - - -diag q{regression test for #5248 from rt3.fsck.com}; { - my $subject_folding_email = RT::Test::get_relocatable_file( - 'subject-with-folding-ws', (File::Spec->updir(), 'data', 'emails')); - my $content = RT::Test->file_content($subject_folding_email); - my ($status, $msg, $ticket) = RT::Interface::Email::Gateway( - { message => $content, queue => 1, action => 'correspond' } - ); - ok ($status, 'created ticket') or diag "error: $msg"; - ok ($ticket->id, "found ticket ". $ticket->id); + 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 q{regression test for #5248 from rt3.fsck.com}; { - my $long_subject_email = RT::Test::get_relocatable_file('very-long-subject', - (File::Spec->updir(), 'data', 'emails')); - my $content = RT::Test->file_content($long_subject_email); - my ($status, $msg, $ticket) = RT::Interface::Email::Gateway( - { message => $content, queue => 1, action => 'correspond' } - ); - ok ($status, 'created ticket') or diag "error: $msg"; - ok ($ticket->id, "found ticket ". $ticket->id); + diag "Regression test for #5248 from rt3.fsck.com"; + my ($ticket) = mail_in_ticket('very-long-subject'); is ($ticket->Subject, '0123456789'x20, 'correct subject'); } - - -# Don't taint the environment -$everyone->PrincipalObj->RevokeRight(Right =>'SuperUser'); +done_testing; diff --git a/rt/t/mail/threading.t b/rt/t/mail/threading.t index 773b7207f..9d3a07751 100644 --- a/rt/t/mail/threading.t +++ b/rt/t/mail/threading.t @@ -1,6 +1,5 @@ use strict; use warnings; -use utf8; use RT::Test tests => 22; RT->Config->Set( NotifyActor => 1 ); diff --git a/rt/t/mail/wrong_mime_charset.t b/rt/t/mail/wrong_mime_charset.t index 530b5f38d..6bbaca1bb 100644 --- a/rt/t/mail/wrong_mime_charset.t +++ b/rt/t/mail/wrong_mime_charset.t @@ -3,10 +3,8 @@ use warnings; use RT::Test nodb => 1, tests => 6; use_ok('RT::I18N'); -use utf8; -use Encode; -my $test_string = 'À'; -my $encoded_string = encode( 'iso-8859-1', $test_string ); +my $test_string = Encode::decode("UTF-8", 'À'); +my $encoded_string = Encode::encode( 'iso-8859-1', $test_string ); my $mime = MIME::Entity->build( "Subject" => $encoded_string, "Data" => [$encoded_string], @@ -40,10 +38,10 @@ like( "We can't encode something into the wrong encoding without Encode complaining" ); -my $subject = decode( 'iso-8859-1', $mime->head->get('Subject') ); +my $subject = Encode::decode( 'iso-8859-1', $mime->head->get('Subject') ); chomp $subject; is( $subject, $test_string, 'subject is set to iso-8859-1' ); -my $body = decode( 'iso-8859-1', $mime->stringify_body ); +my $body = Encode::decode( 'iso-8859-1', $mime->stringify_body ); chomp $body; is( $body, $test_string, 'body is set to iso-8859-1' ); } diff --git a/rt/t/security/CVE-2011-2083-cf-urls.t b/rt/t/security/CVE-2011-2083-cf-urls.t new file mode 100644 index 000000000..b1e1f3b0f --- /dev/null +++ b/rt/t/security/CVE-2011-2083-cf-urls.t @@ -0,0 +1,48 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my ($base, $m) = RT::Test->started_ok; + +my $link = RT::Test->load_or_create_custom_field( + Name => 'link', + Type => 'Freeform', + MaxValues => 1, + Queue => 0, + LinkValueTo => '__CustomField__', +); + +my $include = RT::Test->load_or_create_custom_field( + Name => 'include', + Type => 'Freeform', + MaxValues => 1, + Queue => 0, + IncludeContentForValue => '__CustomField__', +); + +my $data_uri = 'data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5jb29raWUpPC9zY3JpcHQ+'; +my $xss = q{')-eval(decodeURI('alert("xss")'))-('}; + +my $ticket = RT::Ticket->new(RT->SystemUser); +$ticket->Create( + Queue => 'General', + Subject => 'ticket A', + 'CustomField-'.$link->id => $data_uri, + 'CustomField-'.$include->id => $xss, +); +ok $ticket->Id, 'created ticket'; + +ok $m->login('root', 'password'), "logged in"; +$m->get_ok($base . "/Ticket/Display.html?id=" . $ticket->id); + +# look for lack of link to data:text/html;base64,... +ok !$m->find_link(text => $data_uri), "no data: link"; +ok !$m->find_link(url => $data_uri), "no data: link"; + +# look for unescaped JS +$m->content_lacks($xss, 'escaped js'); + +$m->warning_like(qr/Potentially dangerous URL type/, "found warning about dangerous link"); +undef $m; +done_testing; diff --git a/rt/t/security/CVE-2011-2083-clickable-xss.t b/rt/t/security/CVE-2011-2083-clickable-xss.t new file mode 100644 index 000000000..008c80378 --- /dev/null +++ b/rt/t/security/CVE-2011-2083-clickable-xss.t @@ -0,0 +1,52 @@ +use strict; +use warnings; + +use RT::Test tests => undef; +use Test::Warn; + +my ($base, $m) = RT::Test->started_ok; + +my $ticket = RT::Test->create_ticket( + Queue => 'General', + Subject => 'test ticket A', +); +my $id = $ticket->id; +ok $id, "created ticket"; + +my @links = ( + 'javascript:alert("xss")', + 'data:text/html,<script>alert("xss")</script>', +); + +for my $link ( map { ($_, ucfirst $_) } @links ) { + my ($ok, $msg); + warnings_like { + ($ok, $msg) = $ticket->AddLink( + Type => 'RefersTo', + Target => $link, + ); + } [qr/Could not determine a URI scheme/, qr/Couldn't resolve/]; + ok !$ok, $msg; + + ok $m->login, "logged in"; + $m->get_ok($base); + $m->follow_link_ok({ text => 'test ticket A' }, 'ticket page'); + $m->follow_link_ok({ text => 'Links' }, 'links page'); + $m->submit_form_ok({ + with_fields => { + "$id-RefersTo" => $link, + }, + button => 'SubmitTicket', + }, '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"; +} + +$m->no_leftover_warnings_ok; + +undef $m; +done_testing; diff --git a/rt/t/security/CVE-2011-2083-scrub.t b/rt/t/security/CVE-2011-2083-scrub.t new file mode 100644 index 000000000..f05378398 --- /dev/null +++ b/rt/t/security/CVE-2011-2083-scrub.t @@ -0,0 +1,18 @@ +use strict; +use warnings; + +use RT::Test nodb => 1, tests => undef; +use RT::Interface::Web; # This gets us HTML::Mason::Commands +use Test::LongString; + +{ + my $html = '<div id="metadata"><span class="actions"><a>OH HAI</a></span></div><p>Moose</p>'; + my $expected = '<div><span><a>OH HAI</a></span></div><p>Moose</p>'; + is_string(scrub_html($html), $expected, "class and id are stripped"); +} + +sub scrub_html { + return HTML::Mason::Commands::ScrubHTML(shift); +} + +done_testing; diff --git a/rt/t/security/CVE-2011-2084-attach-tickets.t b/rt/t/security/CVE-2011-2084-attach-tickets.t new file mode 100644 index 000000000..d7352cb85 --- /dev/null +++ b/rt/t/security/CVE-2011-2084-attach-tickets.t @@ -0,0 +1,64 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my $user = RT::Test->load_or_create_user( + Name => 'user', + EmailAddress => 'user@example.com', + Privileged => 1, + Password => 'password', +); + +ok( + RT::Test->set_rights( + { Principal => 'Everyone', Right => [qw/CreateTicket/] }, + { Principal => 'Requestor', Right => [qw/ShowTicket/] }, + ), + 'set rights' +); + +my $secret = "sekrit message"; + +RT::Test->create_tickets( + {}, + { + Subject => 'ticket A', + Requestor => $user->EmailAddress, + Content => "user's ticket", + }, + { + Subject => 'ticket B', + Requestor => 'root@localhost', + Content => $secret, + }, +); + +my $ticket_b = RT::Test->last_ticket; + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login( 'user', 'password' ), 'logged in as user'; + +$m->get_ok("$baseurl/Ticket/Display.html?id=" . $ticket_b->id); +$m->content_contains('No permission'); +$m->warning_like(qr/no permission/i, 'no permission warning'); + +RT::Test->clean_caught_mails; + +# Ticket Create is just one example of where this is vulnerable +$m->get_ok('/Ticket/Create.html?Queue=1'); +$m->submit_form_ok({ + form_name => 'TicketCreate', + fields => { + Subject => 'ticket C', + AttachTickets => $ticket_b->id, + }, +}, 'create a ticket'); + +my @mail = RT::Test->fetch_caught_mails; +ok @mail, "got some outgoing emails"; +unlike $mail[0], qr/\Q$secret\E/, "doesn't contain ticket user can't see"; + +undef $m; +done_testing; + diff --git a/rt/t/security/CVE-2011-2084-cf-values.t b/rt/t/security/CVE-2011-2084-cf-values.t new file mode 100644 index 000000000..1178b15af --- /dev/null +++ b/rt/t/security/CVE-2011-2084-cf-values.t @@ -0,0 +1,132 @@ +use strict; +use warnings; + +use RT::Test tests => undef; +use JSON qw(decode_json); + +my ($base, $m) = RT::Test->started_ok; + +my $cf1 = RT::Test->load_or_create_custom_field( + Name => 'cf1', + Type => 'Select', + MaxValues => 1, + Queue => 0, +); +ok $cf1->id, "created cf1"; + +my $cf2 = RT::Test->load_or_create_custom_field( + Name => 'cf2', + Type => 'Select', + MaxValues => 1, + Queue => 0, +); +ok $cf2->id, "created cf2"; + +ok( $cf1->AddValue( Name => "cf1 value $_" ) ) for qw(a b c); +ok( $cf2->AddValue( Name => "cf2 value $_" ) ) for qw(x y z); + +sub ac { + my (%args) = ( + CF => $cf1->id, + Term => "%", + Context => undef, + ContextId => undef, + ContextType => undef, + @_ + ); + $args{term} = delete $args{Term}; + + if (my $obj = delete $args{Context}) { + $args{ContextId} = $obj->Id unless defined $args{ContextId}; + $args{ContextType} = ref($obj) unless defined $args{ContextType}; + } + + $args{"Object---CustomField-$args{CF}-Values"} = ""; + delete $args{CF}; + + delete $args{$_} for grep {not defined $args{$_}} keys %args; + + my $URI = URI->new("$base/Helpers/Autocomplete/CustomFieldValues"); + $URI->query_form( %args ); + $m->get_ok($URI, "GET to autocompleter"); + return decode_json($m->content); +} + +$m->login; +is_deeply ac(CF => 12345, ContextId => 1, ContextType => "RT::Queue"), + [], 'nothing for invalid CF'; + +is_deeply ac(), + [], "Nothing without a context id"; +is_deeply ac( ContextId => 12345, ContextType => "RT::Queue"), + [], "Nothing with invalid contextid id"; +is_deeply ac( ContextId => 12, ContextType => "RT::User"), + [], "Nothing with invalid contextid type"; + + + +my $user = RT::Test->load_or_create_user( + Name => 'user', + Password => 'password', + Privileged => 1, +); +my $queue = RT::Test->load_or_create_queue( Name => 'CF Test' ); +ok $queue->id, 'found or created queue'; +my $ticket = RT::Test->create_ticket( + Queue => $queue->id, + Subject => "CF application", +); +ok $queue->id, 'created ticket'; + +$m->logout; +$m->login('user','password'); + +is_deeply ac( Context => $queue ), [], 'queue context, no permissions, no result'; +is_deeply ac( Context => $ticket ), [], 'ticket context, no permissions, no result'; + +ok( RT::Test->set_rights( + { Principal => $user, Right => [qw(SeeCustomField)], Object => $queue }, +), 'add queue level CF viewing rights'); + +my $cfvalues = [ ( map { { value => "cf1 value $_" , label => "cf1 value $_" } } qw(a b c) ) ]; +is_deeply ac( Context => $queue ), $cfvalues, 'queue context, with permissions get result'; +is_deeply ac( Context => $ticket ), $cfvalues, 'ticket context, with permissions get result'; + +{ + diag "Switching to non-global CFs"; + my $globalq = RT::Queue->new( RT->SystemUser ); + my ($status, $msg) = $cf1->RemoveFromObject( $globalq ); + ok($status, "Removed CF1 globally: $msg"); + ($status, $msg) = $cf1->AddToObject( $queue ); + ok($status, "Added CF1 to queue @{[$queue->id]}: $msg"); + ($status, $msg) = $cf2->RemoveFromObject( $globalq ); + ok($status, "Removed CF2 globally: $msg"); +} + +is_deeply ac( CF => $cf2->id, Context => $queue ), [], 'queue context, but not applied, get no result'; +is_deeply ac( CF => $cf2->id, Context => $ticket ), [], 'ticket context, but not applied, get no result'; + +is_deeply ac( Context => $queue ), $cfvalues, 'queue context, applied correctly, get result'; +is_deeply ac( Context => $ticket ), $cfvalues, 'ticket context, applied correctly, get result'; + + + +diag "Ticket-level rights"; + +ok( RT::Test->set_rights( + { Principal => "Owner", Right => [qw(SeeCustomField)], Object => $queue }, + { Principal => $user, Right => [qw(OwnTicket SeeTicket)], Object => RT->System }, +), 'add owner level CF viewing rights'); + +is_deeply ac( Context => $queue ), [], 'queue context, but not owner'; +is_deeply ac( Context => $ticket ), [], 'ticket context, but not owner'; + +my ($status, $msg) = $ticket->SetOwner( $user->id ); +ok( $status, "Set owner to user: $msg" ); + +is_deeply ac( Context => $queue ), [], 'queue context is not enough'; +is_deeply ac( Context => $ticket ), $cfvalues, 'ticket context, get values'; + + +undef $m; +done_testing; diff --git a/rt/t/security/CVE-2011-2084-modifyscrips-templates.t b/rt/t/security/CVE-2011-2084-modifyscrips-templates.t new file mode 100644 index 000000000..f68706e52 --- /dev/null +++ b/rt/t/security/CVE-2011-2084-modifyscrips-templates.t @@ -0,0 +1,126 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +sub set_fails { + my $col = shift; + my $obj = shift; + my $to = ref $_[0] ? +shift->Id : shift; + my $from = $obj->$col; + my $meth = "Set$col"; + + my ($ok, $msg) = $obj->$meth($to); + ok !$ok, "$meth denied: $msg"; + is $obj->$col, $from, "$col left alone"; +} + +sub set_ok { + my $col = shift; + my $obj = shift; + my $to = ref $_[0] ? +shift->Id : shift; + my $from = $obj->$col; + my $meth = "Set$col"; + + my ($ok, $msg) = $obj->$meth($to); + ok $ok, "$meth allowed: $msg"; + is $obj->$col, $to, "$col updated"; +} + +my $qa = RT::Test->load_or_create_queue( Name => 'Queue A' ); +my $qb = RT::Test->load_or_create_queue( Name => 'Queue B' ); +ok $qa->id, "created Queue A"; +ok $qb->id, "created Queue B"; + +my $user = RT::Test->load_or_create_user( Name => 'testuser' ); +my $cu = RT::CurrentUser->new( $user ); +ok $user->id, "created testuser"; + +diag "ModifyScrips"; +{ + my $scrip = RT::Scrip->new( RT->SystemUser ); + my ($scrip_id, $msg) = $scrip->Create( + Description => 'Testing', + Queue => $qa->Id, + ScripCondition => 'User Defined', + ScripAction => 'User Defined', + Template => 'Blank', + CustomIsApplicableCode => 'if ($self->TicketObj->Subject =~ /fire/) { return (1);} else { return(0)}', + CustomPrepareCode => '1;', + CustomCommitCode => 'warn "scrip fired!";', + ); + ok $scrip_id, $msg; + + RT::Test->set_rights( + { Principal => $user, Right => 'ShowScrips' }, + { Principal => $user, Right => 'ModifyScrips', Object => $qa }, + ); + + $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->SetName('Testing ModifyScrips')); + + set_fails( Queue => $scrip => $qb ); + set_fails( Queue => $scrip => 0 ); + set_fails( Queue => $scrip => undef ); + set_fails( Queue => $scrip => '' ); + + 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 => '' ); + + RT::Test->add_rights( Principal => $user, Right => 'ModifyScrips' ); + + set_ok( Queue => $scrip => 0 ); + + set_fails( Template => $scrip => 2 ); + + RT::Test->add_rights( Principal => $user, Right => 'ShowTemplate' ); + + set_ok( Template => $scrip => 2 ); + is $scrip->TemplateObj->Name, 'Autoreply', 'template name is right'; +} + +diag "ModifyTemplate"; +{ + RT::Test->set_rights( + { Principal => $user, Right => 'ShowTemplate' }, + { Principal => $user, Right => 'ModifyTemplate', Object => $qa }, + ); + + my $template = RT::Template->new( RT->SystemUser ); + my ($id, $msg) = $template->Create( + Queue => $qa->Id, + Name => 'Testing', + Type => 'Perl', + Content => "\n\nThis is a test template.\n", + ); + ok $id, $msg; + + $template = RT::Template->new( $cu ); + $template->Load( $id ); + ok $template->id, "loaded template as test user"; + is $template->Queue, $qa->Id, 'queue is A'; + + ok +($template->SetName('Testing ModifyTemplate')); + + set_fails( Queue => $template => $qb ); + set_fails( Queue => $template => 0 ); + + RT::Test->add_rights( Principal => $user, Right => 'ModifyTemplate', Object => $qb ); + + set_ok( Queue => $template => $qb ); + set_fails( Queue => $template => 0 ); + + RT::Test->add_rights( Principal => $user, Right => 'ModifyTemplate' ); + + set_ok( Queue => $template => 0 ); +} + +done_testing; diff --git a/rt/t/security/CVE-2011-2084-transactions.t b/rt/t/security/CVE-2011-2084-transactions.t new file mode 100644 index 000000000..817288ded --- /dev/null +++ b/rt/t/security/CVE-2011-2084-transactions.t @@ -0,0 +1,59 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +# A privileged user, but with no privs +my $bad = RT::Test->load_or_create_user( + Name => 'testing', + EmailAddress => 'test@example.com', + Password => 'password', +); +ok( $bad, "Got a user object back" ); +ok( $bad->id, "Successfully created a user" ); + + +# A ticket CF +my $obj = RT::Test->load_or_create_custom_field( + Name => "Private CF", + Type => "Freeform", + Queue => 0, +); + +my ($t) = RT::Test->create_tickets( {}, + { Subject => 'Testing' } +); +ok($t->id, "Created a ticket"); + +# Add a txn on it +my ($cfid) = $t->AddCustomFieldValue( + Field => $obj->Id, + Value => "hidden-value" +); +ok($cfid, "Got CF id $cfid"); +my $update_id = $t->Transactions->Last->Id; + +# Somebody else shouldn't be able to see the old and new values +my ($base, $m) = RT::Test->started_ok; +$m->post_ok("$base/REST/1.0/transaction/$update_id", [ + user => 'testing', + pass => 'password', + format => 'l', +]); +$m->content_lacks("hidden-value"); + +# Make a transaction on a user +my $root = RT::Test->load_or_create_user( Name => "root" ); +$root->SetHomePhone("hidden-value"); +$update_id = $root->Transactions->Last->Id; + +# Which should also be hidden from random privileged users +$m->post_ok("$base/REST/1.0/transaction/$update_id", [ + user => 'testing', + pass => 'password', + format => 'l', +]); +$m->content_lacks("hidden-value"); + +undef $m; +done_testing; diff --git a/rt/t/security/CVE-2011-4458-verp.t b/rt/t/security/CVE-2011-4458-verp.t new file mode 100644 index 000000000..f84b79403 --- /dev/null +++ b/rt/t/security/CVE-2011-4458-verp.t @@ -0,0 +1,48 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +RT->Config->Set( MailCommand => 'sendmailpipe' ); +RT->Config->Set( VERPPrefix => "verp-" ); +RT->Config->Set( VERPDomain => "example.com" ); + +# Ensure that the fake sendmail knows where to write to +$ENV{RT_MAILLOGFILE} = RT::Test->temp_directory . "/sendmailpipe.log"; +my $fake = File::Spec->rel2abs( File::Spec->catfile( + 't', 'security', 'fake-sendmail' ) ); +RT->Config->Set( SendmailPath => $fake); + +ok( + RT::Test->set_rights( + { Principal => 'Everyone', Right => [qw/CreateTicket/] }, + ), + 'set rights' +); + +my $bad = RT::Test->load_or_create_user( + EmailAddress => 'danger-$USER@example.com', +); +ok( $bad, "Got a user object back" ); +ok( $bad->id, "Successfully created a user" ); + +my $current_user = RT::CurrentUser->new(RT->SystemUser); +my ($id, $msg) = $current_user->Load($bad->Id); +ok( $id, "Loaded the user successfully" ); + +my $ticket = RT::Ticket->new( $current_user ); +($id, $msg) = $ticket->Create( + Requestor => $bad->Id, + Subject => "Danger, Will Robinson!", + Queue => "General" +); +ok( $id, "Created a ticket: $msg" ); + +open(LOG, "<", $ENV{RT_MAILLOGFILE}) or die "Can't open log file: $!"; +while (my $line = <LOG>) { + next unless $line =~ /^-f/; + like($line, qr/\$USER/, "Contains uninterpolated \$USER"); +} +close(LOG); + +done_testing; diff --git a/rt/t/security/CVE-2011-4460-rows-per-page.t b/rt/t/security/CVE-2011-4460-rows-per-page.t new file mode 100644 index 000000000..92d6853e5 --- /dev/null +++ b/rt/t/security/CVE-2011-4460-rows-per-page.t @@ -0,0 +1,32 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +plan skip_all => 'valid SQL only on mysql' + unless RT->Config->Get('DatabaseType') eq 'mysql'; + +my ($base, $m) = RT::Test->started_ok; +ok $m->login, "logged in"; + +my $t = RT::Ticket->new( RT->SystemUser ); +$t->Create( + Queue => 1, + Subject => 'seed', +); +ok $t->id, 'created seed ticket'; + +my $root = RT::User->new( RT->SystemUser ); +$root->Load('root'); +my $password = $root->__Value('Password'); +ok $password, 'pulled hashed password from db'; + +my $sql = q[1 union select 1+id as id, 1+id as EffectiveId, 1 as Queue, 'ticket' as Type, 0 as IssueStatement, 0 as Resolution, 12 as Owner, Password as Subject, 0 as InitialPriority, 0 as FinalPriority, 0 as Priority, 0 as TimeEstimated, 0 as TimeWorked, Name as Status, 0 as TimeLeft, null as Told, null as Starts, null as Started, null as Due, null as Resolved, 0 as LastUpdatedBy, null as LastUpdated, 6 as Creator, null as Created, 0 as Disabled from Users]; +RT::Interface::Web::EscapeURI(\$sql); + +$m->get_ok("$base/Search/Results.html?Format=id,Subject,Status;Query=id%3E0;OrderBy=|;Rows=$sql"); +$m->content_lacks($password, "our password hash doesn't show up!"); +$m->warning_like(qr/isn't numeric/); + +undef $m; +done_testing; diff --git a/rt/t/security/CVE-2011-5092-datetimeformat.t b/rt/t/security/CVE-2011-5092-datetimeformat.t new file mode 100644 index 000000000..470f4f4f6 --- /dev/null +++ b/rt/t/security/CVE-2011-5092-datetimeformat.t @@ -0,0 +1,48 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my ($base, $m) = RT::Test->started_ok; + +my $user = RT::Test->load_or_create_user( + Name => 'user', + Password => 'password', + Privileged => 1, +); + +ok $user->id, 'created user'; + +ok( + RT::Test->set_rights( + { Principal => 'privileged', Right => [qw(ModifySelf ShowTicket)] }, + ), + "granted ModifySelf to privileged" +); + +my $ticket = RT::Test->create_ticket( + Queue => 'General', + Subject => 'testing', +); + +ok $ticket->id, 'created ticket'; + +$m->login('user'); +$m->get_ok("$base/Prefs/Other.html"); +my $format = 'Formatters'; +$m->submit_form_ok({ + form_name => 'ModifyPreferences', + fields => { + DateTimeFormat => $format, + }, + button => 'Update', +}, 'update prefs'); +is $user->Preferences(RT->System, {})->{DateTimeFormat}, $format, 'set preference'; + +$m->no_warnings_ok; +$m->get_ok("$base/Ticket/Display.html?id=" . $ticket->id); +$m->next_warning_like(qr/Invalid date formatter.+?\Q$format\E/, 'invalid formatter warning'); +$m->content_lacks($_, "lacks formatter in page") for @RT::Date::FORMATTERS; + +undef $m; +done_testing; diff --git a/rt/t/security/CVE-2011-5092-graph-links.t b/rt/t/security/CVE-2011-5092-graph-links.t new file mode 100644 index 000000000..5e98dd3b5 --- /dev/null +++ b/rt/t/security/CVE-2011-5092-graph-links.t @@ -0,0 +1,27 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my ($base, $m) = RT::Test->started_ok; +$m->login; + +for my $arg (qw(LeadingLink ShowLinks)) { + my $ticket = RT::Test->create_ticket( + Queue => 'General', + Subject => 'testing', + ); + ok $ticket->id, 'created ticket'; + + ok !$ticket->ToldObj->Unix, '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'; + $m->content_lacks('GotoFirstItem', 'no GotoFirstItem error'); + $m->content_like(qr|<img[^>]+?src=['"]/Ticket/Graphs/@{[$ticket->id]}|, 'found image element'); +} + +undef $m; +done_testing; diff --git a/rt/t/security/CVE-2011-5092-installmode.t b/rt/t/security/CVE-2011-5092-installmode.t new file mode 100644 index 000000000..ce88a4fec --- /dev/null +++ b/rt/t/security/CVE-2011-5092-installmode.t @@ -0,0 +1,24 @@ +use strict; +use warnings; + +BEGIN { + $ENV{RT_TEST_WEB_HANDLER} = 'inline'; +} + +use RT::Test tests => undef; +use Test::Warn; + +my ($base, $m) = RT::Test->started_ok; + +$m->login; +$m->content_like(qr/RT at a glance/i, 'homepage'); + +warning_like { + ok !RT->InstallMode(1), 'install mode failed to turn on'; +} qr/tried to turn on InstallMode/; + +$m->reload; +$m->content_like(qr/RT at a glance/i, 'still homepage'); + +undef $m; +done_testing; diff --git a/rt/t/security/CVE-2011-5092-localizeddatetime.t b/rt/t/security/CVE-2011-5092-localizeddatetime.t new file mode 100644 index 000000000..733afc08a --- /dev/null +++ b/rt/t/security/CVE-2011-5092-localizeddatetime.t @@ -0,0 +1,30 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my $root = RT::CurrentUser->new('root'); +my ($ok, $msg) = $root->UserObj->SetLang('en-us'); +ok $ok, $msg; + +my $year = (localtime time)[5] + 1900; +my $date = RT::Date->new( $root ); +$date->SetToNow; + +like $date->AsString( Format => 'LocalizedDateTime' ), + qr/\Q$year\E/, 'contains full year'; + +unlike $date->AsString( Format => 'LocalizedDateTime', DateFormat => 'date_format_short' ), + qr/\Q$year\E/, 'lacks full year'; + +eval { + $date->AsString( Format => 'LocalizedDateTime', DateFormat => 'bogus::format' ); +}; +ok !$@, "didn't die with bogus DateFormat"; + +eval { + $date->AsString( Format => 'LocalizedDateTime', TimeFormat => 'bogus::format' ); +}; +ok !$@, "didn't die with bogus TimeFormat"; + +done_testing; diff --git a/rt/t/security/CVE-2011-5092-prefs.t b/rt/t/security/CVE-2011-5092-prefs.t new file mode 100644 index 000000000..b8e15aae0 --- /dev/null +++ b/rt/t/security/CVE-2011-5092-prefs.t @@ -0,0 +1,77 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my ($base, $m) = RT::Test->started_ok; + +my $user = RT::Test->load_or_create_user( + Name => 'ausername', + EmailAddress => 'user@example.com', + Password => 'password', + Privileged => 1, +); + +ok $user->id, 'created user'; + +ok( + RT::Test->set_rights( + { Principal => 'privileged', Right => [qw(ModifySelf ShowTicket)] }, + ), + "granted ModifySelf to privileged" +); + +$m->login('ausername'); + +{ + $m->get_ok("$base/Prefs/Other.html"); + my $style = '../css/base'; + $m->submit_form_ok({ + with_fields => { + WebDefaultStylesheet => $style, + }, + button => 'Update', + }, 'update prefs'); + is(RT->Config->Get('WebDefaultStylesheet', $user), $style, 'set preference'); + + SKIP: { + skip "RT::User->Stylesheet wasn't backported", 1 unless $user->can("Stylesheet"); + is $user->Stylesheet, RT->Config->Get('WebDefaultStylesheet'), '$user->Stylesheet is the default'; + } + + $m->get_ok($base); + $m->content_unlike(qr/<link.+?\Q$style\E/, "lack .. path in page <link>"); + $m->content_contains( RT->Config->Get('WebDefaultStylesheet') ); +} + +{ + $m->get_ok("$base/Prefs/Other.html"); + my $format = '/../../m/_elements/full_site_link'; + $m->submit_form_ok({ + form_name => 'ModifyPreferences', + fields => { + UsernameFormat => $format, + }, + button => 'Update', + }, 'update prefs'); + $m->content_contains('saved'); + + my $ticket = RT::Test->create_ticket( + Queue => 'General', + Subject => 'test ticket', + Requestor => 'user@example.com', + ); + ok $ticket->id, 'created ticket'; + $m->get_ok($base . "/Ticket/Display.html?id=" . $ticket->id); + $m->content_lacks('NotMobile', "lacks NotMobile"); + $m->next_warning_like(qr/UsernameFormat/, 'caught UsernameFormat warning'); +} + +{ + $m->get_ok("$base/Helpers/Toggle/ShowRequestor?Status=/../../../Elements/Logo;Requestor=root"); + $m->content_lacks('logo', "didn't display /Elements/Logo"); + $m->content_contains('Results.html', "found link to search results"); +} + +undef $m; +done_testing; diff --git a/rt/t/security/CVE-2011-5093-execute-code.t b/rt/t/security/CVE-2011-5093-execute-code.t new file mode 100644 index 000000000..5124ab88b --- /dev/null +++ b/rt/t/security/CVE-2011-5093-execute-code.t @@ -0,0 +1,53 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my $template = RT::Template->new( RT->SystemUser ); +my ($ok, $msg) = $template->Create( + Queue => 0, + Name => 'test', + Type => 'Simple', + Content => <<'.', +===Create-Ticket: testing +Queue: General +Subject: duplicate: { $Tickets{TOP}->Subject } +. +); +ok $ok, $msg; + +my $ticket = RT::Test->create_ticket( + Queue => 'General', + Subject => 'a ticket', +); +ok $ticket->id, "created ticket"; + +for my $type (qw(Simple Perl)) { + if ($template->Type ne $type) { + my ($ok, $msg) = $template->SetType($type); + ok $ok, $msg; + } + + require RT::Action::CreateTickets; + my $action = RT::Action::CreateTickets->new( + CurrentUser => RT->SystemUser, + TemplateObj => $template, + TicketObj => $ticket, + ); + $action->{TransactionObj} = $ticket->Transactions->First; + ok $action->Prepare, 'prepares'; + ok $action->Commit, 'commits'; + + my $new_ticket = RT::Test->last_ticket; + ok $new_ticket->id > $ticket->id, 'new ticket'; + + if ($type eq 'Perl') { + is $new_ticket->Subject, 'duplicate: a ticket', 'interpolated'; + isnt $new_ticket->Subject, 'duplicate: { $Tickets{TOP}->Subject }', 'interpolated'; + } else { + isnt $new_ticket->Subject, 'duplicate: a ticket', 'not interpolated'; + is $new_ticket->Subject, 'duplicate: { $Tickets{TOP}->Subject }', 'not interpolated'; + } +} + +done_testing; diff --git a/rt/t/security/fake-sendmail b/rt/t/security/fake-sendmail new file mode 100644 index 000000000..43259b603 --- /dev/null +++ b/rt/t/security/fake-sendmail @@ -0,0 +1,24 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +die "No \$RT_MAILLOGFILE set in environment" + unless $ENV{RT_MAILLOGFILE}; +open LOG, ">", $ENV{RT_MAILLOGFILE} + or die "Can't write to $ENV{RT_MAILLOGFILE}: $!"; + +my $needs_newline; +for (@ARGV) { + if (/^-/) { + print LOG "\n" if $needs_newline++; + print LOG $_; + } else { + print LOG " $_"; + } +} +print LOG "\n"; + +1 while $_ = <STDIN>; + +exit 0; diff --git a/rt/t/ticket/race.t b/rt/t/ticket/race.t new file mode 100644 index 000000000..aa1150ecb --- /dev/null +++ b/rt/t/ticket/race.t @@ -0,0 +1,51 @@ +use strict; +use warnings; + +use RT::Test tests => 2; + +use constant KIDS => 50; + +my $id; + +{ + my $t = RT::Ticket->new( RT->SystemUser ); + ($id) = $t->Create( + Queue => "General", + Subject => "Race $$", + ); +} + +diag "Created ticket $id"; +RT->DatabaseHandle->Disconnect; + +my @kids; +for (1..KIDS) { + if (my $pid = fork()) { + push @kids, $pid; + next; + } + + # In the kid, load up the ticket and correspond + RT->ConnectToDatabase; + my $t = RT::Ticket->new( RT->SystemUser ); + $t->Load( $id ); + $t->Correspond( Content => "Correspondence from PID $$" ); + undef $t; + exit 0; +} + + +diag "Forked @kids"; +waitpid $_, 0 for @kids; +diag "All kids finished corresponding"; + +RT->ConnectToDatabase; +my $t = RT::Ticket->new( RT->SystemUser ); +$t->Load($id); +my $txns = $t->Transactions; +$txns->Limit( FIELD => 'Type', VALUE => 'Status' ); +is($txns->Count, 1, "Only one transaction change recorded" ); + +$txns = $t->Transactions; +$txns->Limit( FIELD => 'Type', VALUE => 'Correspond' ); +is($txns->Count, KIDS, "But all correspondences were recorded" ); diff --git a/rt/t/ticket/search_by_queue.t b/rt/t/ticket/search_by_queue.t new file mode 100644 index 000000000..0327152d5 --- /dev/null +++ b/rt/t/ticket/search_by_queue.t @@ -0,0 +1,60 @@ +use strict; +use warnings; + +use RT::Test nodata => 1, tests => 34; +use RT::Ticket; + +my $qa = RT::Test->load_or_create_queue( Name => 'Queue A' ); +ok $qa && $qa->id, 'loaded or created queue'; + +my $qb = RT::Test->load_or_create_queue( Name => 'Queue B' ); +ok $qb && $qb->id, 'loaded or created queue'; + +my @tickets = RT::Test->create_tickets( + {}, + { Queue => $qa->id, Subject => 'a1', }, + { Queue => $qa->id, Subject => 'a2', }, + { Queue => $qb->id, Subject => 'b1', }, + { Queue => $qb->id, Subject => 'b2', }, +); + +run_tests( \@tickets, + 'Queue = "Queue A"' => { a1 => 1, a2 => 1, b1 => 0, b2 => 0 }, + 'Queue = '. $qa->id => { a1 => 1, a2 => 1, b1 => 0, b2 => 0 }, + 'Queue != "Queue A"' => { a1 => 0, a2 => 0, b1 => 1, b2 => 1 }, + 'Queue != '. $qa->id => { a1 => 0, a2 => 0, b1 => 1, b2 => 1 }, + + 'Queue = "Queue B"' => { a1 => 0, a2 => 0, b1 => 1, b2 => 1 }, + 'Queue = '. $qb->id => { a1 => 0, a2 => 0, b1 => 1, b2 => 1 }, + 'Queue != "Queue B"' => { a1 => 1, a2 => 1, b1 => 0, b2 => 0 }, + 'Queue != '. $qb->id => { a1 => 1, a2 => 1, b1 => 0, b2 => 0 }, + + 'Queue = "Bad Queue"' => { a1 => 0, a2 => 0, b1 => 0, b2 => 0 }, + 'Queue != "Bad Queue"' => { a1 => 1, a2 => 1, b1 => 1, b2 => 1 }, +); + +sub run_tests { + my @tickets = @{ shift() }; + my %test = @_; + my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets; + foreach my $key ( sort keys %test ) { + my $tix = RT::Tickets->new(RT->SystemUser); + $tix->FromSQL( "( $query_prefix ) AND ( $key )" ); + + my $error = 0; + + my $count = 0; + $count++ foreach grep $_, values %{ $test{$key} }; + is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1; + + my $good_tickets = 1; + while ( my $ticket = $tix->Next ) { + next if $test{$key}->{ $ticket->Subject }; + diag $ticket->Subject ." ticket has been found when it's not expected"; + $good_tickets = 0; + } + ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1; + + diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error; + } +} diff --git a/rt/t/web/action-results.t b/rt/t/web/action-results.t new file mode 100644 index 000000000..db8c26bb8 --- /dev/null +++ b/rt/t/web/action-results.t @@ -0,0 +1,48 @@ +use strict; +use warnings; +use RT::Test tests => 'no_declare'; + +my ($url, $m) = RT::Test->started_ok; + +ok $m->login, "Logged in"; + +# We test two ticket creation paths since one historically doesn't update the +# session (quick create) and the other does. +for my $quick (1, 0) { + diag $quick ? "Quick ticket creation" : "Normal ticket creation"; + + $m->get_ok("/"); + $m->submit_form_ok({ form_name => 'CreateTicketInQueue' }, "Create new ticket form") + unless $quick; + $m->submit_form_ok({ + with_fields => { + Subject => "The Plants", + Content => "Please water them.", + }, + }, "Submitted new ticket"); + + my $id = RT::Test->last_ticket->id; + + like $m->uri, qr/results=[A-Za-z0-9]{32}/, "URI contains results hash"; + $m->content_contains("Ticket $id created", "Page contains results message"); + $m->content_contains("#$id: The Plants") unless $quick; + + diag "Reloading without a referer but with a results hash doesn't trigger the CSRF"; { + # Mech's API here sucks. To drop the Referer and simulate a real browser + # reload, we need to make a new request which explicitly adds an empty Referer + # header (causing it to never be sent) and then deletes the empty Referer + # header to let it be automatically managed again. + $m->add_header("Referer" => undef); + $m->get_ok( $m->uri, "Reloading the results page without a Referer" ); + $m->delete_header("Referer"); + + like $m->uri, qr/results=[A-Za-z0-9]{32}/, "URI contains results hash"; + $m->content_lacks("cross-site request forgery", "Skipped the CSRF interstitial") + or $m->follow_link_ok({ text => "click here to resume your request" }, "Ignoring CSRF warning"); + $m->content_lacks("Ticket $id created", "Page lacks results message"); + $m->content_contains("#$id: The Plants") unless $quick; + } +} + +undef $m; +done_testing; diff --git a/rt/t/web/admin_queue_lifecycle.t b/rt/t/web/admin_queue_lifecycle.t new file mode 100644 index 000000000..295e9ea57 --- /dev/null +++ b/rt/t/web/admin_queue_lifecycle.t @@ -0,0 +1,49 @@ +use strict; +use warnings; + +use RT::Test tests => 13; + +my $lifecycles = RT->Config->Get('Lifecycles'); +RT->Config->Set( Lifecycles => %{$lifecycles}, + foo => { + initial => ['initial'], + active => ['open'], + inactive => ['resolved'], + } +); + +RT::Lifecycle->FillCache(); + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login(), 'logged in' ); + +$m->get_ok( $url . '/Admin/Queues/Modify.html?id=1' ); + +my $form = $m->form_name('ModifyQueue'); +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' ); + +$m->submit_form(); +$m->content_lacks( 'Lifecycle changed from', + 'no message of "Lifecycle changed from"' ); +$m->content_lacks( 'That is already the current value', + 'no message of "That is already the current value"' ); + +$form = $m->form_name('ModifyQueue'); +$m->submit_form( fields => { Lifecycle => 'foo' }, ); +$m->content_contains( + 'Lifecycle changed from "default" to "foo"'); +$lifecycle_input = $form->find_input('Lifecycle'); +is( $lifecycle_input->value, 'foo', 'lifecycle is changed to foo' ); + +$form = $m->form_name('ModifyQueue'); +$m->submit_form( fields => { Lifecycle => 'default' }, ); +$m->content_contains( + 'Lifecycle changed from "foo" to "default"'); +$lifecycle_input = $form->find_input('Lifecycle'); +is( $lifecycle_input->value, 'default', + 'lifecycle is changed back to default' ); + diff --git a/rt/t/web/attachment_encoding.t b/rt/t/web/attachment_encoding.t index 5af7fda20..f49720e0f 100644 --- a/rt/t/web/attachment_encoding.t +++ b/rt/t/web/attachment_encoding.t @@ -3,14 +3,15 @@ use strict; use warnings; use RT::Test tests => 32; -use Encode; my ( $baseurl, $m ) = RT::Test->started_ok; ok $m->login, 'logged in as root'; -use utf8; - use File::Spec; +my $subject = Encode::decode("UTF-8",'标题'); +my $content = Encode::decode("UTF-8",'测试'); +my $filename = Encode::decode("UTF-8",'附件.txt'); + diag 'test without attachments' if $ENV{TEST_VERBOSE}; { @@ -19,13 +20,13 @@ diag 'test without attachments' if $ENV{TEST_VERBOSE}; $m->form_name('TicketModify'); $m->submit_form( form_number => 3, - fields => { Subject => '标题', Content => '测试' }, + fields => { Subject => $subject, Content => $content }, ); $m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); $m->follow_link_ok( { text => 'with headers' }, '-> /Ticket/Attachment/WithHeaders/...' ); - $m->content_contains( '标题', 'has subject 标题' ); - $m->content_contains( '测试', 'has content 测试' ); + $m->content_contains( $subject, "has subject $subject" ); + $m->content_contains( $content, "has content $content" ); my ( $id ) = $m->uri =~ /(\d+)$/; ok( $id, 'found attachment id' ); @@ -35,8 +36,8 @@ diag 'test without attachments' if $ENV{TEST_VERBOSE}; ok( $attachment->SetHeader( 'X-RT-Original-Encoding' => 'gbk' ), 'set original encoding to gbk' ); $m->get( $m->uri ); - $m->content_contains( '标题', 'has subject 标题' ); - $m->content_contains( '测试', 'has content 测试' ); + $m->content_contains( $subject, "has subject $subject" ); + $m->content_contains( $content, "has content $content" ); } diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; @@ -44,10 +45,10 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; { my $file = - File::Spec->catfile( RT::Test->temp_directory, encode_utf8 '附件.txt' ); + File::Spec->catfile( RT::Test->temp_directory, Encode::encode("UTF-8",$filename) ); open( my $fh, '>', $file ) or die $!; binmode $fh, ':utf8'; - print $fh '附件'; + print $fh $filename; close $fh; $m->get_ok( $baseurl . '/Ticket/Create.html?Queue=1' ); @@ -55,17 +56,17 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; $m->form_name('TicketModify'); $m->submit_form( form_number => 3, - fields => { Subject => '标题', Content => '测试', Attach => $file }, + fields => { Subject => $subject, Content => $content, Attach => $file }, ); $m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); - $m->content_contains( '附件.txt', 'attached filename' ); - $m->content_lacks( encode_utf8 '附件.txt', 'no double encoded attached filename' ); + $m->content_contains( $filename, 'attached filename' ); + $m->content_lacks( Encode::encode("UTF-8",$filename), 'no double encoded attached filename' ); $m->follow_link_ok( { text => 'with headers' }, '-> /Ticket/Attachment/WithHeaders/...' ); # subject is in the parent attachment, so there is no 标题 - $m->content_lacks( '标题', 'does not have content 标题' ); - $m->content_contains( '测试', 'has content 测试' ); + $m->content_lacks( $subject, "does not have content $subject" ); + $m->content_contains( $content, "has content $content" ); my ( $id ) = $m->uri =~ /(\d+)$/; ok( $id, 'found attachment id' ); @@ -75,15 +76,15 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; ok( $attachment->SetHeader( 'X-RT-Original-Encoding' => 'gbk' ), 'set original encoding to gbk' ); $m->get( $m->uri ); - $m->content_lacks( '标题', 'does not have content 标题' ); - $m->content_contains( '测试', 'has content 测试' ); + $m->content_lacks( $subject, "does not have content $subject" ); + $m->content_contains( $content, "has content $content" ); $m->back; $m->back; - $m->follow_link_ok( { text => 'Download 附件.txt' }, + $m->follow_link_ok( { text => "Download $filename" }, '-> /Ticket/Attachment/...' ); - $m->content_contains( '附件', 'has content 附件' ); + $m->content_contains( $filename, "has file content $filename" ); ( $id ) = $m->uri =~ /(\d+)\D+$/; ok( $id, 'found attachment id' ); @@ -94,7 +95,7 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; ok( $attachment->SetHeader( 'X-RT-Original-Encoding' => 'gbk' ), 'set original encoding to gbk' ); $m->get( $m->uri ); - $m->content_contains( '附件', 'has content 附件' ); + $m->content_contains( $filename, "has content $filename" ); unlink $file; } diff --git a/rt/t/web/basic.t b/rt/t/web/basic.t index e61e80e9c..02483b208 100644 --- a/rt/t/web/basic.t +++ b/rt/t/web/basic.t @@ -1,7 +1,6 @@ use strict; use warnings; -use Encode; use RT::Test tests => 23; @@ -27,7 +26,7 @@ my $url = $agent->rt_base_url; $agent->goto_create_ticket(1); is ($agent->status, 200, "Loaded Create.html"); $agent->form_name('TicketCreate'); - my $string = Encode::decode_utf8("I18N Web Testing æøå"); + my $string = Encode::decode("UTF-8","I18N Web Testing æøå"); $agent->field('Subject' => "Ticket with utf8 body"); $agent->field('Content' => $string); ok($agent->submit, "Created new ticket with $string as Content"); @@ -49,7 +48,7 @@ my $url = $agent->rt_base_url; is ($agent->status, 200, "Loaded Create.html"); $agent->form_name('TicketCreate'); - my $string = Encode::decode_utf8("I18N Web Testing æøå"); + my $string = Encode::decode( "UTF-8","I18N Web Testing æøå"); $agent->field('Subject' => $string); $agent->field('Content' => "Ticket with utf8 subject"); ok($agent->submit, "Created new ticket with $string as Content"); diff --git a/rt/t/web/cf_date.t b/rt/t/web/cf_date.t index e69833c13..2180e140f 100644 --- a/rt/t/web/cf_date.t +++ b/rt/t/web/cf_date.t @@ -189,4 +189,85 @@ diag 'check invalid inputs'; is_deeply( @warnings, q{Couldn't parse date 'foodate' by Time::ParseDate} ); } +diag 'retain values when adding attachments'; +{ + my ( $ticket, $id ); + + my $txn_cf = RT::CustomField->new( RT->SystemUser ); + my ( $ret, $msg ) = $txn_cf->Create( + Name => 'test txn cf date', + TypeComposite => 'Date-1', + LookupType => 'RT::Queue-RT::Ticket-RT::Transaction', + ); + ok( $ret, "created 'txn datetime': $msg" ); + $txn_cf->AddToObject(RT::Queue->new(RT->SystemUser)); + my $txn_cfid = $txn_cf->id; + + $m->submit_form( + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, + ); + $m->content_contains('test cf date', 'has cf' ); + $m->content_contains('test txn cf date', 'has txn cf' ); + + $m->submit_form_ok( + { + form_name => "TicketCreate", + fields => { + Subject => 'test 2015-06-04', + Content => 'test', + "Object-RT::Ticket--CustomField-$cfid-Values" => '2015-06-04', + "Object-RT::Transaction--CustomField-$txn_cfid-Values" => '2015-08-15', + }, + button => 'AddMoreAttach', + }, + 'create test ticket' + ); + $m->form_name("TicketCreate"); + is( $m->value( "Object-RT::Ticket--CustomField-$cfid-Values" ), + "2015-06-04", "ticket cf date value still on form" ); + is( $m->value( "Object-RT::Transaction--CustomField-$txn_cfid-Values" ), + "2015-08-15", "txn cf date date value still on form" ); + + $m->submit_form(); + ok( ($id) = $m->content =~ /Ticket (\d+) created/, "created ticket $id" ); + + $m->follow_link_ok( {text => 'Reply'} ); + $m->title_like( qr/Update/ ); + $m->content_contains('test txn cf date', 'has txn cf'); + $m->submit_form_ok( + { + form_name => "TicketUpdate", + fields => { + Content => 'test', + "Object-RT::Transaction--CustomField-$txn_cfid-Values" => '2015-09-16', + }, + button => 'AddMoreAttach', + }, + 'Update test ticket' + ); + $m->form_name("TicketUpdate"); + is( $m->value( "Object-RT::Transaction--CustomField-$txn_cfid-Values" ), + "2015-09-16", "txn date value still on form" ); + + $m->follow_link_ok( {text => 'Jumbo'} ); + $m->title_like( qr/Jumbo/ ); + + $m->submit_form_ok( + { + form_name => "TicketModifyAll", + fields => { + "Object-RT::Transaction--CustomField-$txn_cfid-Values" => + '2015-12-16', + }, + button => 'AddMoreAttach', + }, + 'jumbo form' + ); + + $m->form_name("TicketModifyAll"); + is( $m->value( "Object-RT::Transaction--CustomField-$txn_cfid-Values" ), + "2015-12-16", "txn date value still on form" ); +} + done_testing; diff --git a/rt/t/web/cf_datetime.t b/rt/t/web/cf_datetime.t index 4580c4a4f..72a8b3f7e 100644 --- a/rt/t/web/cf_datetime.t +++ b/rt/t/web/cf_datetime.t @@ -215,6 +215,92 @@ diag 'check invalid inputs'; is_deeply( @warnings, q{Couldn't parse date 'foodate' by Time::ParseDate} ); } +diag 'retain values when adding attachments'; +{ + my ( $ticket, $id ); + + my $txn_cf = RT::CustomField->new( RT->SystemUser ); + my ( $ret, $msg ) = $txn_cf->Create( + Name => 'test txn cf datetime', + TypeComposite => 'DateTime-1', + LookupType => 'RT::Queue-RT::Ticket-RT::Transaction', + ); + ok( $ret, "created 'txn datetime': $msg" ); + $txn_cf->AddToObject(RT::Queue->new(RT->SystemUser)); + my $txn_cfid = $txn_cf->id; + + $m->submit_form( + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, + ); + $m->content_contains('test cf datetime', 'has cf' ); + $m->content_contains('test txn cf datetime', 'has txn cf' ); + + $m->submit_form_ok( + { + form_name => "TicketCreate", + fields => { + Subject => 'test 2015-06-04', + Content => 'test', + "Object-RT::Ticket--CustomField-$cfid-Values" => '2015-06-04 08:30:00', + "Object-RT::Transaction--CustomField-$txn_cfid-Values" => '2015-08-15 12:30:30', + }, + button => 'AddMoreAttach', + }, + 'Create test ticket' + ); + $m->form_name("TicketCreate"); + is( $m->value( "Object-RT::Ticket--CustomField-$cfid-Values" ), + "2015-06-04 08:30:00", "ticket cf date value still on form" ); + $m->content_contains( "Jun 04 08:30:00 2015", 'date in parens' ); + is( $m->value( "Object-RT::Transaction--CustomField-$txn_cfid-Values" ), + "2015-08-15 12:30:30", "txn cf date date value still on form" ); + $m->content_contains( "Aug 15 12:30:30 2015", 'date in parens' ); + + $m->submit_form(); + ok( ($id) = $m->content =~ /Ticket (\d+) created/, "Created ticket $id" ); + + $m->follow_link_ok( {text => 'Reply'} ); + $m->title_like( qr/Update/ ); + $m->content_contains('test txn cf date', 'has txn cf'); + $m->submit_form_ok( + { + form_name => "TicketUpdate", + fields => { + Content => 'test', + "Object-RT::Transaction--CustomField-$txn_cfid-Values" => '2015-09-16 09:30:40', + }, + button => 'AddMoreAttach', + }, + 'Update test ticket' + ); + $m->form_name("TicketUpdate"); + is( $m->value( "Object-RT::Transaction--CustomField-$txn_cfid-Values" ), + "2015-09-16 09:30:40", "Date value still on form" ); + $m->content_contains( "Sep 16 09:30:40 2015", 'date in parens' ); + + $m->follow_link_ok( {text => 'Jumbo'} ); + $m->title_like( qr/Jumbo/ ); + + $m->submit_form_ok( + { + form_name => "TicketModifyAll", + fields => { + "Object-RT::Transaction--CustomField-$txn_cfid-Values" => + '2015-12-16 03:00:00', + }, + button => 'AddMoreAttach', + }, + 'jumbo form' + ); + $m->save_content('/tmp/x.html'); + + $m->form_name("TicketModifyAll"); + is( $m->value( "Object-RT::Transaction--CustomField-$txn_cfid-Values" ), + "2015-12-16 03:00:00", "txn date value still on form" ); + $m->content_contains( "Dec 16 03:00:00 2015", 'date in parens' ); +} + sub is_results_number { local $Test::Builder::Level = $Test::Builder::Level + 1; my $fields = shift; diff --git a/rt/t/web/cf_values_class.t b/rt/t/web/cf_values_class.t new file mode 100644 index 000000000..646642781 --- /dev/null +++ b/rt/t/web/cf_values_class.t @@ -0,0 +1,54 @@ +use strict; +use warnings; + +use RT::Test tests => 8; + +use constant VALUES_CLASS => 'RT::CustomFieldValues::Groups'; +RT->Config->Set(CustomFieldValuesSources => VALUES_CLASS); + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; + +my $cf_name = 'test values class'; + +my $cfid; +diag "Create a CF"; +{ + $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->submit_form( + form_name => "ModifyCustomField", + fields => { + Name => $cf_name, + TypeComposite => 'Select-1', + LookupType => 'RT::Queue-RT::Ticket', + }, + ); + $m->content_contains('Object created', 'created Select-1' ); + $cfid = $m->form_name('ModifyCustomField')->value('id'); + ok $cfid, "found id of the CF in the form, it's #$cfid"; +} + +diag "change to external values class"; +{ + $m->submit_form( + form_name => "ModifyCustomField", + fields => { ValuesClass => 'RT::CustomFieldValues::Groups', }, + button => 'Update', + ); + $m->content_contains( + "Field values source changed from 'RT::CustomFieldValues' to 'RT::CustomFieldValues::Groups'", + 'changed to external values class' ); +} + +diag "change to internal values class"; +{ + $m->submit_form( + form_name => "ModifyCustomField", + fields => { ValuesClass => 'RT::CustomFieldValues', }, + button => 'Update', + ); + $m->content_contains( + "Field values source changed from 'RT::CustomFieldValues::Groups' to 'RT::CustomFieldValues'", + 'changed to internal values class' ); +} + diff --git a/rt/t/web/command_line_cf_edge_cases.t b/rt/t/web/command_line_cf_edge_cases.t new file mode 100644 index 000000000..d7c777768 --- /dev/null +++ b/rt/t/web/command_line_cf_edge_cases.t @@ -0,0 +1,87 @@ +use strict; +use warnings; +use Test::Expect; +use RT::Test tests => 100, actual_server => 1; +my ( $baseurl, $m ) = RT::Test->started_ok; + +my $rt_tool_path = "$RT::BinPath/rt"; + +$ENV{'RTUSER'} = 'root'; +$ENV{'RTPASSWD'} = 'password'; +$ENV{'RTSERVER'} = RT->Config->Get('WebBaseURL'); +$ENV{'RTDEBUG'} = '1'; +$ENV{'RTCONFIG'} = '/dev/null'; + +my @cfs = ( + 'foo=bar', 'foo.bar', 'foo:bar', 'foo bar', + 'foo{bar}', 'foo-bar', 'foo()bar', +); +for my $name (@cfs) { + RT::Test->load_or_create_custom_field( + Name => $name, + Type => 'Freeform', + MaxValues => 1, + Queue => 0, + ); +} + +expect_run( + command => "$rt_tool_path shell", + prompt => 'rt> ', + quit => 'quit', +); + +# create a ticket +for my $name (@cfs) { + expect_send( +qq{create -t ticket set subject='test cf $name' 'CF.{$name}=foo:b a.r=baz'}, + "creating a ticket for cf $name" + ); + + expect_handle->before() =~ /Ticket (\d+) created/; + my $ticket_id = $1; + + expect_send( "show ticket/$ticket_id -f 'CF.{$name}'", + 'checking new value' ); + expect_like( qr/CF\.{\Q$name\E}: foo:b a\.r=baz/i, 'verified change' ); + + expect_send( "edit ticket/$ticket_id set 'CF.{$name}=bar'", + "changing cf $name to bar" ); + expect_like( qr/Ticket $ticket_id updated/, 'changed cf' ); + expect_send( "show ticket/$ticket_id -f 'CF.{$name}'", + 'checking new value' ); + expect_like( qr/CF\.{\Q$name\E}: bar/i, 'verified change' ); + + expect_send( +qq{create -t ticket set subject='test cf $name' 'CF-$name=foo:b a.r=baz'}, + "creating a ticket for cf $name" + ); + expect_handle->before() =~ /Ticket (\d+) created/; + $ticket_id = $1; + + expect_send( "show ticket/$ticket_id -f 'CF-$name'", 'checking new value' ); + if ( $name eq 'foo=bar' ) { + expect_like( qr/CF\.{\Q$name\E}: $/mi, + "can't use = in cf name with old style" ); + } + else { + expect_like( qr/CF\.{\Q$name\E}: foo:b a\.r=baz/i, 'verified change' ); + expect_send( "edit ticket/$ticket_id set 'CF-$name=bar'", + "changing cf $name to bar" ); + expect_like( qr/Ticket $ticket_id updated/, 'changed cf' ); + expect_send( "show ticket/$ticket_id -f 'CF-$name'", + 'checking new value' ); + expect_like( qr/CF\.{\Q$name\E}: bar/i, 'verified change' ); + } +} + +my @invalid = ('foo,bar'); +for my $name (@invalid) { + expect_send( + qq{create -t ticket set subject='test cf $name' 'CF.{$name}=foo'}, + "creating a ticket for cf $name" ); + expect_like( qr/You shouldn't specify objects as arguments to create/i, + '$name is not a valid cf name' ); +} + +expect_quit(); diff --git a/rt/t/web/compilation_errors.t b/rt/t/web/compilation_errors.t index 0ae6ead5b..126d33691 100644 --- a/rt/t/web/compilation_errors.t +++ b/rt/t/web/compilation_errors.t @@ -15,7 +15,6 @@ BEGIN { use HTTP::Request::Common; use HTTP::Cookies; use LWP; -use Encode; my $cookie_jar = HTTP::Cookies->new; diff --git a/rt/t/web/current_user_outdated_email.t b/rt/t/web/current_user_outdated_email.t new file mode 100644 index 000000000..51fc803c6 --- /dev/null +++ b/rt/t/web/current_user_outdated_email.t @@ -0,0 +1,41 @@ + +use strict; +use warnings; +use RT::Test tests => 39; + +my ( $url, $m ) = RT::Test->started_ok; + +$m->login(); + +my @links = ( + '/', '/Ticket/Create.html?Queue=1', + '/SelfService/Create.html?Queue=1', '/m/ticket/create?Queue=1' +); + +my $root = RT::Test->load_or_create_user( Name => 'root' ); +ok( $root->id, 'loaded root' ); +is( $root->EmailAddress, 'root@localhost', 'default root email' ); + +for my $link (@links) { + $m->get_ok($link); + $m->content_contains( '"root@localhost"', "default email in $link" ); +} + +$root->SetEmailAddress('foo@example.com'); +is( $root->EmailAddress, 'foo@example.com', 'changed to foo@example.com' ); + +for my $link (@links) { + $m->get_ok($link); + $m->content_lacks( '"root@localhost"', "no default email in $link" ); + $m->content_contains( '"foo@example.com"', "new email in $link" ); +} + +$root->SetEmailAddress('root@localhost'); +is( $root->EmailAddress, 'root@localhost', 'changed back to root@localhost' ); + +for my $link (@links) { + $m->get_ok($link); + $m->content_lacks( '"foo@example.com"', "no previous email in $link" ); + $m->content_contains( '"root@localhost"', "default email in $link" ); +} + diff --git a/rt/t/web/helpers-http-cache-headers.t b/rt/t/web/helpers-http-cache-headers.t new file mode 100644 index 000000000..1731e9d17 --- /dev/null +++ b/rt/t/web/helpers-http-cache-headers.t @@ -0,0 +1,96 @@ +use strict; +use warnings; + +# trs: I'd write a quick t/web/caching-headers.t file which loops the available +# endpoints checking for the right headers. + +use File::Find; + +BEGIN { + # Ensure that the test and server processes use the same fixed time. + use constant TIME => 1365175699; + use Test::MockTime 'set_fixed_time'; + set_fixed_time(TIME); + + use RT::Test + tests => undef, + config => "use Test::MockTime 'set_fixed_time'; set_fixed_time(".TIME.");"; +} + +my ($base, $m) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +my $docroot = join '/', qw(share html); + +# find endpoints to loop over +my @endpoints = ('/NoAuth/css/print.css'); +find({ + wanted => sub { + if ( -f $_ && $_ !~ m|autohandler$| ) { + ( my $endpoint = $_ ) =~ s|^$docroot||; + push @endpoints, $endpoint; + } + }, + no_chdir => 1, +} => join '/', $docroot => 'Helpers'); + +my $ticket_id; +diag "create a ticket via the API"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $txn, $msg) = $ticket->Create( + Queue => 'General', + Subject => 'test ticket', + ); + ok $id, 'created a ticket #'. $id or diag "error: $msg"; + is $ticket->Subject, 'test ticket', 'correct subject'; + $ticket_id = $id; +} + + +my $expected; +diag "set up expected date headers"; +{ + + # expected headers + $expected = { + Autocomplete => { + 'Cache-Control' => 'max-age=120, private', + 'Expires' => 'Fri, 5 Apr 2013 15:30:19 GMT', + }, + NoAuth => { + 'Cache-Control' => 'max-age=2592000, public', + 'Expires' => 'Sun, 5 May 2013 15:28:19 GMT', + }, + default => { + 'Cache-Control' => 'no-cache', + 'Expires' => 'Fri, 5 Apr 2013 15:28:19 GMT', + }, + }; + +} + +foreach my $endpoint ( @endpoints ) { + $m->get_ok( $endpoint . "?id=${ticket_id}&Status=open&Requestor=root" ); + + my $header_key = 'default'; + if ( $endpoint =~ m|Autocomplete| ) { + $header_key = 'Autocomplete'; + } elsif ( $endpoint =~ m|NoAuth| ) { + $header_key = 'NoAuth'; + } + my $headers = $expected->{$header_key}; + + is( + $m->res->header('Cache-Control') => $headers->{'Cache-Control'}, + 'got expected Cache-Control header' + ); + + is( + $m->res->header('Expires') => $headers->{'Expires'}, + 'got expected Expires header' + ); +} + +undef $m; +done_testing; diff --git a/rt/t/web/html_template.t b/rt/t/web/html_template.t index 78b95a3b2..a2764556f 100644 --- a/rt/t/web/html_template.t +++ b/rt/t/web/html_template.t @@ -2,16 +2,16 @@ use strict; use warnings; -use RT::Test tests => 19; -use Encode; +use RT::Test tests => undef; my ( $baseurl, $m ) = RT::Test->started_ok; ok $m->login, 'logged in as root'; -use utf8; - diag('make Autoreply template a html one and add utf8 chars') if $ENV{TEST_VERBOSE}; +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' ); @@ -19,20 +19,20 @@ diag('make Autoreply template a html one and add utf8 chars') $m->submit_form( form_name => 'ModifyTemplate', fields => { - Content => <<'EOF', -Subject: AutoReply: {$Ticket->Subject} + Content => <<EOF, +Subject: AutoReply: {\$Ticket->Subject} Content-Type: text/html -你好 éèà€ -{$Ticket->Subject} +$template +{\$Ticket->Subject} ------------------------------------------------------------------------- -{$Transaction->Content()} +{\$Transaction->Content()} EOF }, ); $m->content_like( qr/Content updated/, 'content is changed' ); - $m->content_contains( '你好', 'content is really updated' ); + $m->content_contains( $template, 'content is really updated' ); } diag('create a ticket to see the autoreply mail') if $ENV{TEST_VERBOSE}; @@ -42,17 +42,16 @@ diag('create a ticket to see the autoreply mail') if $ENV{TEST_VERBOSE}; $m->submit_form( form_name => 'TicketCreate', - fields => { Subject => '标题', Content => '<h1>测试</h1>', + fields => { Subject => $subject, Content => "<h1>$content</h1>", ContentType => 'text/html' }, ); $m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); $m->follow_link( text => 'Show' ); - $m->content_contains( '你好', 'html has 你好' ); - $m->content_contains( 'éèà€', 'html has éèà€' ); - $m->content_contains( '标题', - 'html has ticket subject 标题' ); - $m->content_contains( '<h1>测试</h1>', - 'html has ticket html content 测试' ); + $m->content_contains( $template, "html has $template" ); + $m->content_contains( $subject, + "html has ticket subject $subject" ); + $m->content_contains( "<h1>$content</h1>", + "html has ticket html content $content" ); } diag('test real mail outgoing') if $ENV{TEST_VERBOSE}; @@ -61,11 +60,12 @@ diag('test real mail outgoing') if $ENV{TEST_VERBOSE}; # $mail is utf8 encoded my ($mail) = RT::Test->fetch_caught_mails; - $mail = decode_utf8 $mail; - like( $mail, qr/你好.*你好/s, 'mail has éèà€' ); - like( $mail, qr/éèà€.*éèà€/s, 'mail has éèà€' ); - like( $mail, qr/标题.*标题/s, 'mail has ticket subject 标题' ); - like( $mail, qr/测试.*测试/s, 'mail has ticket content 测试' ); - like( $mail, qr!<h1>测试</h1>!, 'mail has ticket html content 测试' ); + $mail = Encode::decode("UTF-8", $mail ); + like( $mail, qr/$template.*$template/s, 'mail has template content $template twice' ); + like( $mail, qr/$subject.*$subject/s, 'mail has ticket subject $sujbect twice' ); + like( $mail, qr/$content.*$content/s, 'mail has ticket content $content twice' ); + like( $mail, qr!<h1>$content</h1>!, 'mail has ticket html content <h1>$content</h1>' ); } +undef $m; +done_testing; diff --git a/rt/t/web/login.t b/rt/t/web/login.t new file mode 100644 index 000000000..d0213c373 --- /dev/null +++ b/rt/t/web/login.t @@ -0,0 +1,133 @@ +use strict; +use warnings; + +use RT::Test tests => 34; + +my ( $baseurl, $m ) = RT::Test->started_ok; + +my $ticket = RT::Test->create_ticket( + Subject => 'ticket_foo', + Queue => 'General', +); + +my ( $user, $pass ) = ( 'root', 'password' ); + +diag "normal login"; +{ + $m->get($baseurl); + $m->title_is('Login'); + is( $m->uri, $baseurl, "right url" ); + + $m->submit_form( + form_id => 'login', + fields => { + user => $user, + pass => 'wrong pass', + } + ); + $m->content_contains( "Your username or password is incorrect", + 'login error message' ); + $m->warning_like( qr/FAILED LOGIN for root/, + "got failed login warning" ); + + $m->submit_form( + form_id => 'login', + fields => { + user => $user, + pass => $pass, + } + ); + + $m->title_is( 'RT at a glance', 'logged in' ); + + $m->follow_link_ok( { text => 'Logout' }, 'follow logout' ); + $m->title_is( 'Logout', 'logout' ); +} + +diag "tangent login"; + +{ + $m->get( $baseurl . '/Ticket/Display.html?id=1' ); + $m->title_is('Login'); + $m->submit_form( + form_id => 'login', + fields => { + user => $user, + pass => $pass, + } + ); + like( $m->uri, qr{/Ticket/Display\.html}, 'normal ticket page' ); + $m->follow_link_ok( { text => 'Logout' }, 'follow logout' ); +} + +diag "mobile login with not mobile client"; +{ + $m->get( $baseurl . '/m' ); + is( $m->uri, $baseurl . '/m', "right url" ); + $m->content_contains( "/m/index.html?NotMobile=1", 'mobile login' ); + + $m->submit_form( + form_id => 'login', + fields => { + user => $user, + pass => 'wrong pass', + } + ); + $m->content_contains( "Your username or password is incorrect", + 'login error message' ); + $m->warning_like( qr/FAILED LOGIN for root/, + "got failed login warning" ); + + $m->submit_form( + form_id => 'login', + fields => { + user => $user, + pass => $pass, + } + ); + like( $m->uri, qr{\Q$baseurl/m\E}, "mobile url" ); + $m->follow_link_ok( { text => 'Logout' }, 'follow logout' ); + $m->content_contains( "/m/index.html?NotMobile=1", + 'back to mobile login page' ); + $m->content_lacks( 'Logout', 'really logout' ); +} + + +diag "mobile normal login"; +{ + + # default browser in android 2.3.6 + $m->agent( +"Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Nexus One Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1" + ); + + $m->get($baseurl); + is( $m->uri, $baseurl, "right url" ); + $m->content_contains( "/m/index.html?NotMobile=1", 'mobile login' ); + $m->submit_form( + form_id => 'login', + fields => { + user => $user, + pass => $pass, + } + ); + is( $m->uri, $baseurl . '/m/', "mobile url" ); + $m->follow_link_ok( { text => 'Logout' }, 'follow logout' ); + $m->content_contains( "/m/index.html?NotMobile=1", 'back to mobile login page' ); + $m->content_lacks( 'Logout', 'really logout' ); +} + +diag "mobile tangent login"; +{ + $m->get( $baseurl . '/Ticket/Display.html?id=1' ); + $m->content_contains( "/m/index.html?NotMobile=1", 'mobile login' ); + $m->submit_form( + form_id => 'login', + fields => { + user => $user, + pass => $pass, + } + ); + like( $m->uri, qr{/m/ticket/show}, 'mobile ticket page' ); +} + diff --git a/rt/t/web/offline_messages_utf8.t b/rt/t/web/offline_messages_utf8.t index 4518c7b7a..4cf6954bd 100644 --- a/rt/t/web/offline_messages_utf8.t +++ b/rt/t/web/offline_messages_utf8.t @@ -2,7 +2,6 @@ use strict; use warnings; use RT::Test tests => 8; -use Encode; use RT::Ticket; my ( $url, $m ) = RT::Test->started_ok; @@ -35,7 +34,7 @@ EOF fields => { string => $template, }, button => 'UpdateTickets', ); - my $content = encode 'utf8', $m->content; + my $content = Encode::encode("UTF-8", $m->content); ok( $content =~ m/申請單 #(\d+) 成功新增於 'General' 表單/, 'message is shown right' ); $ticket_id = $1; } @@ -55,7 +54,7 @@ EOF button => 'UpdateTickets', ); - my $content = encode 'utf8', $m->content; + my $content = Encode::encode("UTF-8", $m->content); ok( $content =~ qr/主題\s*的值從\s*'test message'\s*改為\s*'test message update'/, diff --git a/rt/t/web/offline_utf8.t b/rt/t/web/offline_utf8.t index c317a4616..aab3049a3 100644 --- a/rt/t/web/offline_utf8.t +++ b/rt/t/web/offline_utf8.t @@ -2,14 +2,11 @@ use strict; use warnings; use RT::Test tests => 9; -use utf8; - -use Encode; use RT::Ticket; my $file = File::Spec->catfile( RT::Test->temp_directory, 'template' ); open my $fh, '>', $file or die $!; -my $template = <<EOF; +my $template = Encode::decode("UTF-8",<<EOF); ===Create-Ticket: ticket1 Queue: General Subject: 标题 @@ -19,7 +16,7 @@ Content: ENDOFCONTENT EOF -print $fh $template; +print $fh Encode::encode("UTF-8",$template); close $fh; my ( $url, $m ) = RT::Test->started_ok; @@ -33,7 +30,7 @@ $m->submit_form( button => 'Parse', ); -$m->content_contains( '这是正文', 'content is parsed right' ); +$m->content_contains( Encode::decode("UTF-8",'这是正文'), 'content is parsed right' ); $m->submit_form( form_name => 'TicketUpdate', @@ -48,9 +45,9 @@ my ( $ticket_id ) = $m->content =~ /Ticket (\d+) created/; my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $ticket_id ); -is( $ticket->Subject, '标题', 'subject in $ticket is right' ); +is( $ticket->Subject, Encode::decode("UTF-8",'标题'), 'subject in $ticket is right' ); $m->goto_ticket($ticket_id); -$m->content_contains( '这是正文', +$m->content_contains( Encode::decode("UTF-8",'这是正文'), 'content is right in ticket display page' ); diff --git a/rt/t/web/plugin-overlays.t b/rt/t/web/plugin-overlays.t new file mode 100644 index 000000000..fec458964 --- /dev/null +++ b/rt/t/web/plugin-overlays.t @@ -0,0 +1,30 @@ +use strict; +use warnings; + +BEGIN { + use Test::More; + plan skip_all => "Testing the rt-server init sequence in isolation requires Apache" + unless ($ENV{RT_TEST_WEB_HANDLER} || '') =~ /^apache/; +} + +use JSON qw(from_json); + +use RT::Test + tests => undef, + plugins => ["Overlays"]; + +my ($base, $m) = RT::Test->started_ok; + +# Check that the overlay was actually loaded +$m->get_ok("$base/overlay_loaded"); +is $m->content, "yes", "Plugin's RT/User_Local.pm was loaded"; + +# Check accessible is correct and doesn't need to be rebuilt from overlay +$m->get_ok("$base/user_accessible"); +ok $m->content, "Received some content"; + +my $info = from_json($m->content) || {}; +ok $info->{Comments}{public}, "User.Comments is marked public via overlay"; + +undef $m; +done_testing; diff --git a/rt/t/web/query_builder.t b/rt/t/web/query_builder.t index 13cd1b5d0..3589c381a 100644 --- a/rt/t/web/query_builder.t +++ b/rt/t/web/query_builder.t @@ -3,7 +3,6 @@ use warnings; use HTTP::Request::Common; use HTTP::Cookies; use LWP; -use Encode; use RT::Test tests => 70; my $cookie_jar = HTTP::Cookies->new; diff --git a/rt/t/web/rest-non-ascii-subject.t b/rt/t/web/rest-non-ascii-subject.t index 8b870a8b1..0d3e14dfb 100644 --- a/rt/t/web/rest-non-ascii-subject.t +++ b/rt/t/web/rest-non-ascii-subject.t @@ -3,8 +3,6 @@ use strict; use warnings; use RT::Test tests => 9; -use Encode; -# \x{XX} where XX is less than 255 is not treated as unicode code point my $subject = Encode::decode('latin1', "Sujet accentu\x{e9}"); my $text = Encode::decode('latin1', "Contenu accentu\x{e9}"); @@ -32,8 +30,7 @@ Text: $text"; $m->post("$baseurl/REST/1.0/ticket/new", [ user => 'root', pass => 'password', -# error message from HTTP::Message: content must be bytes - content => Encode::encode_utf8($content), + content => Encode::encode( "UTF-8", $content), ], Content_Type => 'form-data' ); my ($id) = $m->content =~ /Ticket (\d+) created/; diff --git a/rt/t/web/sidebyside_layout.t b/rt/t/web/sidebyside_layout.t new file mode 100644 index 000000000..88ea10cc5 --- /dev/null +++ b/rt/t/web/sidebyside_layout.t @@ -0,0 +1,45 @@ +use strict; +use warnings; +use RT::Test tests => 11; + +RT->Config->Set( UseSideBySideLayout => 0 ); + +my $root = RT::Test->load_or_create_user( Name => 'root', ); +my ( $status, $msg ) = $root->SetPreferences( + $RT::System => { + %{ $root->Preferences($RT::System) || {} }, 'UseSideBySideLayout' => 1 + } +); +ok( $status, 'use side by side layout for root' ); + +my $user_a = RT::Test->load_or_create_user( + Name => 'user_a', + Password => 'password', +); +ok( $user_a->id, 'created user_a' ); + +ok( + RT::Test->set_rights( + { + Principal => $user_a, + Right => ['CreateTicket'] + }, + ), + 'granted user_a the right of CreateTicket' +); + +my ( $url, $m ) = RT::Test->started_ok; +$m->login; +$m->get_ok( $url . '/Ticket/Create.html?Queue=1', "root's ticket create page" ); +$m->content_like( qr/<body [^>]*class="[^>"]*\bsidebyside\b/, + 'found sidebyside css for root' ); + +my $m_a = RT::Test::Web->new; +ok $m_a->login( 'user_a', 'password' ), 'logged in as user_a'; +$m_a->get_ok( $url . '/Ticket/Create.html?Queue=1', + "user_a's ticket create page" ); +$m_a->content_unlike( + qr/<body [^>]*class="[^>"]*\bsidebyside\b/, + "didn't find sidebyside class for user_a" +); + diff --git a/rt/t/web/ticket-create-utf8.t b/rt/t/web/ticket-create-utf8.t index bebc57b51..107e41d71 100644 --- a/rt/t/web/ticket-create-utf8.t +++ b/rt/t/web/ticket-create-utf8.t @@ -4,8 +4,6 @@ use warnings; use RT::Test tests => 43; -use Encode; - my $ru_test = "\x{442}\x{435}\x{441}\x{442}"; my $ru_support = "\x{43f}\x{43e}\x{434}\x{434}\x{435}\x{440}\x{436}\x{43a}\x{430}"; diff --git a/rt/t/web/ticket_txn_subject.t b/rt/t/web/ticket_txn_subject.t new file mode 100644 index 000000000..a43f05d96 --- /dev/null +++ b/rt/t/web/ticket_txn_subject.t @@ -0,0 +1,85 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my ($base, $m) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +my @tickets; + +diag "create a ticket via the API"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + my ($id, $txn, $msg) = $ticket->Create( + Queue => 'General', + Subject => Encode::decode("UTF-8",'bad subject‽'), + ); + ok $id, 'created a ticket #'. $id or diag "error: $msg"; + is $ticket->Subject, Encode::decode("UTF-8",'bad subject‽'), 'correct subject'; + push @tickets, $id; +} + +diag "create a ticket via the web"; +{ + $m->submit_form_ok({ + form_name => "CreateTicketInQueue", + fields => { Queue => 1 }, + }, 'create ticket in Queue'); + $m->submit_form_ok({ + with_fields => { + Subject => Encode::decode("UTF-8",'bad subject #2‽'), + }, + }, 'create ticket'); + $m->content_contains(Encode::decode("UTF-8",'bad subject #2‽'), 'correct subject'); + push @tickets, 2; +} + +diag "create a ticket via the web without a unicode subject"; +{ + $m->submit_form_ok({ + with_fields => { Queue => 1 }, + }, 'create ticket in Queue'); + $m->submit_form_ok({ + with_fields => { + Subject => 'a fine subject #3', + }, + }, 'create ticket'); + $m->content_contains('a fine subject #3', 'correct subject'); + push @tickets, 3; +} + +for my $tid (@tickets) { + diag "ticket #$tid"; + diag "add a reply which adds to the subject, but without an attachment"; + { + $m->goto_ticket($tid); + $m->follow_link_ok({ id => 'page-actions-reply' }, "Actions -> Reply"); + $m->submit_form_ok({ + with_fields => { + UpdateSubject => Encode::decode("UTF-8",'bad subject‽ without attachment'), + UpdateContent => 'testing unicode txn subjects', + }, + button => 'SubmitTicket', + }, 'submit reply'); + $m->content_contains(Encode::decode("UTF-8",'bad subject‽ without attachment'), "found txn subject"); + } + + diag "add a reply which adds to the subject with an attachment"; + { + $m->goto_ticket($tid); + $m->follow_link_ok({ id => 'page-actions-reply' }, "Actions -> Reply"); + $m->submit_form_ok({ + with_fields => { + UpdateSubject => Encode::decode("UTF-8",'bad subject‽ with attachment'), + UpdateContent => 'testing unicode txn subjects', + Attach => RT::Test::get_relocatable_file('bpslogo.png', '..', 'data'), + }, + button => 'SubmitTicket', + }, 'submit reply'); + $m->content_contains(Encode::decode("UTF-8",'bad subject‽ with attachment'), "found txn subject"); + } +} + +undef $m; +done_testing; diff --git a/rt/t/web/user_update.t b/rt/t/web/user_update.t index c0e9e5264..54139d797 100644 --- a/rt/t/web/user_update.t +++ b/rt/t/web/user_update.t @@ -1,6 +1,5 @@ use strict; use warnings; -use utf8; use RT::Test tests => undef; my ( $url, $m ) = RT::Test->started_ok; @@ -10,7 +9,7 @@ $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("実名", "Page content is japanese"); +$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 # spurious and annoying @@ -22,7 +21,7 @@ $m->submit_form_ok({ with_fields => { Lang => 'en_us'} }, # This message shows up in Japanese # $m->text_contains("Lang changed from 'ja' to 'en_us'"); -$m->text_contains("Langは「'ja'」から「'en_us'」に変更されました"); +$m->text_contains(Encode::decode("UTF-8","Langは「'ja'」から「'en_us'」に変更されました")); $m->text_contains("Real Name", "Page content is english"); # Check for a lack of spurious updates @@ -32,12 +31,11 @@ $m->content_lacks("That is already the current value"); $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("実名", "Page content is japanese"); +$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("Langは「'ja'」から「(値なし)」に変更されました"); +$m->text_contains(Encode::decode("UTF-8","Langは「'ja'」から「(値なし)」に変更されました")); $m->text_contains("Real Name", "Page content is english"); undef $m; - done_testing; |