summaryrefslogtreecommitdiff
path: root/rt/t
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2014-09-15 20:44:48 -0700
committerIvan Kohler <ivan@freeside.biz>2014-09-15 20:59:00 -0700
commit5b3efac57771fbc37874a3dd39d3df835cdd6133 (patch)
treef653976031646a27771f39902ed9296a4c129f30 /rt/t
parent008524b8e963831999983769f7fec11f55a72f16 (diff)
RT 4.0.22
Diffstat (limited to 'rt/t')
-rw-r--r--rt/t/00-mason-syntax.t3
-rw-r--r--rt/t/99-policy.t101
-rw-r--r--rt/t/api/attachment.t3
-rw-r--r--rt/t/api/canonical_charset.t3
-rw-r--r--rt/t/api/cfsearch.t106
-rw-r--r--rt/t/api/i18n_guess.t2
-rw-r--r--rt/t/api/menu.t85
-rw-r--r--rt/t/api/password-types.t6
-rw-r--r--rt/t/api/template-parsing.t306
-rw-r--r--rt/t/api/transaction.t52
-rw-r--r--rt/t/api/uri-canonicalize.t54
-rw-r--r--rt/t/customfields/date.t86
-rw-r--r--rt/t/customfields/datetime.t76
-rw-r--r--rt/t/customfields/iprangev6.t2
-rw-r--r--rt/t/customfields/repeated_values.t134
-rw-r--r--rt/t/data/configs/apache2.2+fastcgi.conf50
-rw-r--r--rt/t/data/configs/apache2.2+mod_perl.conf67
-rwxr-xr-xrt/t/data/emails/text-html-in-russian87
-rw-r--r--rt/t/data/plugins/Overlays/html/overlay_loaded8
-rw-r--r--rt/t/data/plugins/Overlays/html/user_accessible8
-rw-r--r--rt/t/data/plugins/Overlays/lib/Overlays.pm2
-rw-r--r--rt/t/data/plugins/Overlays/lib/RT/User_Local.pm11
-rw-r--r--rt/t/i18n/default.t8
-rw-r--r--rt/t/mail/charsets-outgoing.t17
-rw-r--r--rt/t/mail/dashboard-chart-with-utf8.t8
-rw-r--r--rt/t/mail/extractsubjecttag.t1
-rw-r--r--rt/t/mail/gateway.t9
-rw-r--r--rt/t/mail/header-characters.t81
-rw-r--r--rt/t/mail/not-supported-charset.t69
-rw-r--r--rt/t/mail/one-time-recipients.t1
-rw-r--r--rt/t/mail/rfc2231-attachment.t3
-rw-r--r--rt/t/mail/sendmail.t600
-rw-r--r--rt/t/mail/threading.t1
-rw-r--r--rt/t/mail/wrong_mime_charset.t10
-rw-r--r--rt/t/security/CVE-2011-2083-cf-urls.t48
-rw-r--r--rt/t/security/CVE-2011-2083-clickable-xss.t52
-rw-r--r--rt/t/security/CVE-2011-2083-scrub.t18
-rw-r--r--rt/t/security/CVE-2011-2084-attach-tickets.t64
-rw-r--r--rt/t/security/CVE-2011-2084-cf-values.t132
-rw-r--r--rt/t/security/CVE-2011-2084-modifyscrips-templates.t126
-rw-r--r--rt/t/security/CVE-2011-2084-transactions.t59
-rw-r--r--rt/t/security/CVE-2011-4458-verp.t48
-rw-r--r--rt/t/security/CVE-2011-4460-rows-per-page.t32
-rw-r--r--rt/t/security/CVE-2011-5092-datetimeformat.t48
-rw-r--r--rt/t/security/CVE-2011-5092-graph-links.t27
-rw-r--r--rt/t/security/CVE-2011-5092-installmode.t24
-rw-r--r--rt/t/security/CVE-2011-5092-localizeddatetime.t30
-rw-r--r--rt/t/security/CVE-2011-5092-prefs.t77
-rw-r--r--rt/t/security/CVE-2011-5093-execute-code.t53
-rw-r--r--rt/t/security/fake-sendmail24
-rw-r--r--rt/t/ticket/race.t51
-rw-r--r--rt/t/ticket/search_by_queue.t60
-rw-r--r--rt/t/web/action-results.t48
-rw-r--r--rt/t/web/admin_queue_lifecycle.t49
-rw-r--r--rt/t/web/attachment_encoding.t41
-rw-r--r--rt/t/web/basic.t5
-rw-r--r--rt/t/web/cf_date.t81
-rw-r--r--rt/t/web/cf_datetime.t86
-rw-r--r--rt/t/web/cf_values_class.t54
-rw-r--r--rt/t/web/command_line_cf_edge_cases.t87
-rw-r--r--rt/t/web/compilation_errors.t1
-rw-r--r--rt/t/web/current_user_outdated_email.t41
-rw-r--r--rt/t/web/helpers-http-cache-headers.t96
-rw-r--r--rt/t/web/html_template.t46
-rw-r--r--rt/t/web/login.t133
-rw-r--r--rt/t/web/offline_messages_utf8.t5
-rw-r--r--rt/t/web/offline_utf8.t13
-rw-r--r--rt/t/web/plugin-overlays.t30
-rw-r--r--rt/t/web/query_builder.t1
-rw-r--r--rt/t/web/rest-non-ascii-subject.t5
-rw-r--r--rt/t/web/sidebyside_layout.t45
-rw-r--r--rt/t/web/ticket-create-utf8.t2
-rw-r--r--rt/t/web/ticket_txn_subject.t85
-rw-r--r--rt/t/web/user_update.t10
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>
-&nbsp;- co eo oe p e oe opeeeo eeoc;<br>
-&nbsp;- 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&#39;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 &#34;default&#34; to &#34;foo&#34;');
+$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 &#34;foo&#34; to &#34;default&#34;');
+$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 &#39;RT::CustomFieldValues&#39; to &#39;RT::CustomFieldValues::Groups&#39;",
+ '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 &#39;RT::CustomFieldValues::Groups&#39; to &#39;RT::CustomFieldValues&#39;",
+ '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( '&lt;h1&gt;测试&lt;/h1&gt;',
- 'html has ticket html content 测试' );
+ $m->content_contains( $template, "html has $template" );
+ $m->content_contains( $subject,
+ "html has ticket subject $subject" );
+ $m->content_contains( "&lt;h1&gt;$content&lt;/h1&gt;",
+ "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+) 成功新增於 &#39;General&#39; 表單/, '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*&#39;test message&#39;\s*改為\s*&#39;test message update&#39;/,
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;