summaryrefslogtreecommitdiff
path: root/rt/t
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2015-07-09 22:18:55 -0700
committerIvan Kohler <ivan@freeside.biz>2015-07-09 22:18:55 -0700
commit1c538bfabc2cd31f27067505f0c3d1a46cba6ef0 (patch)
tree96922ad4459eda1e649327fd391d60c58d454c53 /rt/t
parent4f5619288413a185e9933088d9dd8c5afbc55dfa (diff)
RT 4.2.11, ticket#13852
Diffstat (limited to 'rt/t')
-rw-r--r--rt/t/00-mason-syntax.t2
-rw-r--r--rt/t/99-policy.t20
-rw-r--r--rt/t/api/attachment_filename.t6
-rw-r--r--rt/t/api/cfsearch.t24
-rw-r--r--rt/t/api/config.t8
-rw-r--r--rt/t/api/cron.t26
-rw-r--r--rt/t/api/customfield.t423
-rw-r--r--rt/t/api/date.t98
-rw-r--r--rt/t/api/db_indexes.t165
-rw-r--r--rt/t/api/group-rights.t2
-rw-r--r--rt/t/api/group.t2
-rw-r--r--rt/t/api/groups.t5
-rw-r--r--rt/t/api/i18n_guess.t4
-rw-r--r--rt/t/api/i18n_mime_encoding.t32
-rw-r--r--rt/t/api/initialdata.t16
-rw-r--r--rt/t/api/link.t6
-rw-r--r--rt/t/api/password-types.t16
-rw-r--r--rt/t/api/queue.t12
-rw-r--r--rt/t/api/record.t4
-rw-r--r--rt/t/api/reminder-permissions.t49
-rw-r--r--rt/t/api/rights.t12
-rw-r--r--rt/t/api/rtname.t7
-rw-r--r--rt/t/api/savedsearch.t66
-rw-r--r--rt/t/api/scrip.t221
-rw-r--r--rt/t/api/scrip_order.t313
-rw-r--r--rt/t/api/searchbuilder.t2
-rw-r--r--rt/t/api/system-available-rights.t65
-rw-r--r--rt/t/api/system.t54
-rw-r--r--rt/t/api/template.t179
-rw-r--r--rt/t/api/ticket.t72
-rw-r--r--rt/t/api/tickets.t35
-rw-r--r--rt/t/api/txn_content.t8
-rw-r--r--rt/t/api/user-prefs.t59
-rw-r--r--rt/t/api/user.t31
-rw-r--r--rt/t/api/users.t2
-rw-r--r--rt/t/approval/admincc.t46
-rw-r--r--rt/t/approval/basic.t36
-rw-r--r--rt/t/articles/interface.t138
-rw-r--r--rt/t/articles/search-interface.t108
-rw-r--r--rt/t/articles/set-subject.t110
-rw-r--r--rt/t/articles/upload-customfields.t8
-rw-r--r--rt/t/articles/uri-articles.t26
-rw-r--r--rt/t/charts/basics.t91
-rw-r--r--rt/t/charts/compound-sql-function.t121
-rw-r--r--rt/t/charts/group-by-cf.t71
-rw-r--r--rt/t/crypt/gnupg/attachments-in-db.t49
-rw-r--r--rt/t/crypt/no-signer-address.t42
-rw-r--r--rt/t/crypt/smime/attachments-in-db.t45
-rw-r--r--rt/t/crypt/smime/bad-recipients.t58
-rw-r--r--rt/t/crypt/smime/status-string.t26
-rw-r--r--rt/t/customfields/access_via_queue.t7
-rw-r--r--rt/t/customfields/api.t172
-rw-r--r--rt/t/customfields/date_search.t27
-rw-r--r--rt/t/customfields/datetime_search.t28
-rw-r--r--rt/t/customfields/external.t8
-rw-r--r--rt/t/customfields/ip.t10
-rw-r--r--rt/t/customfields/iprange.t6
-rw-r--r--rt/t/customfields/iprangev6.t2
-rw-r--r--rt/t/customfields/ipv6.t6
-rw-r--r--rt/t/customfields/sort_order.t2
-rw-r--r--rt/t/customfields/transaction.t17
-rw-r--r--rt/t/customfields/transaction_searching.t140
-rw-r--r--rt/t/data/configs/apache2.2+fastcgi.conf.in1
-rw-r--r--rt/t/data/initialdata/initialdata101
-rw-r--r--rt/t/data/initialdata/transaction-cfs52
-rw-r--r--rt/t/data/plugins/RT-Extension-PSGIWrap/lib/RT/Extension/PSGIWrap.pm16
-rw-r--r--rt/t/data/smime/keys/demoCA/cacert.pem58
-rw-r--r--rt/t/data/smime/keys/demoCA/private/cakey.pem18
-rw-r--r--rt/t/data/smime/keys/demoCA/serial1
-rw-r--r--rt/t/data/smime/keys/otherCA/cacert.pem80
-rw-r--r--rt/t/data/smime/keys/otherCA/private/cakey.pem27
-rw-r--r--rt/t/data/smime/keys/otherCA/serial1
-rw-r--r--rt/t/data/smime/keys/root@example.com.crt43
-rw-r--r--rt/t/data/smime/keys/root@example.com.csr9
-rw-r--r--rt/t/data/smime/keys/root@example.com.key12
-rw-r--r--rt/t/data/smime/keys/root@example.com.pem55
-rw-r--r--rt/t/data/smime/keys/sender@example.com.crt43
-rw-r--r--rt/t/data/smime/keys/sender@example.com.csr9
-rw-r--r--rt/t/data/smime/keys/sender@example.com.key12
-rw-r--r--rt/t/data/smime/keys/sender@example.com.pem55
-rw-r--r--rt/t/data/smime/mails/1-signed.eml74
-rw-r--r--rt/t/data/smime/mails/2-signed-attachment.eml90
-rw-r--r--rt/t/data/smime/mails/3-signed-binary.eml95
-rw-r--r--rt/t/data/smime/mails/4-encrypted-plain.eml32
-rw-r--r--rt/t/data/smime/mails/5-encrypted-attachment.eml42
-rw-r--r--rt/t/data/smime/mails/6-encrypted-binary.eml48
-rw-r--r--rt/t/data/smime/mails/7-signed-encrypted-plain.eml97
-rw-r--r--rt/t/data/smime/mails/8-signed-encrypted-attachment.eml107
-rw-r--r--rt/t/data/smime/mails/9-signed-encrypted-binary.eml113
-rw-r--r--rt/t/fts/indexed_mysql.t84
-rw-r--r--rt/t/fts/indexed_pg.t2
-rw-r--r--rt/t/fts/indexed_sphinx.t150
-rw-r--r--rt/t/lifecycles/basics.t8
-rw-r--r--rt/t/lifecycles/dates.t67
-rw-r--r--rt/t/lifecycles/moving.t4
-rw-r--r--rt/t/lifecycles/types.t33
-rw-r--r--rt/t/lifecycles/unresolved-deps.t4
-rw-r--r--rt/t/lifecycles/utils.pl7
-rw-r--r--rt/t/mail/autogenerated.t22
-rw-r--r--rt/t/mail/charsets-outgoing-plaintext.t315
-rw-r--r--rt/t/mail/charsets-outgoing.t6
-rw-r--r--rt/t/mail/crypt-gnupg.t94
-rw-r--r--rt/t/mail/dashboard-chart-with-utf8.t15
-rw-r--r--rt/t/mail/dashboards.t2
-rw-r--r--rt/t/mail/digest-attributes.t34
-rw-r--r--rt/t/mail/gateway.t38
-rw-r--r--rt/t/mail/gnupg-bad.t2
-rw-r--r--rt/t/mail/gnupg-incoming.t12
-rw-r--r--rt/t/mail/gnupg-outgoing-encrypted-plaintext.t27
-rw-r--r--rt/t/mail/gnupg-outgoing-plain-plaintext.t25
-rw-r--r--rt/t/mail/gnupg-outgoing-signed-plaintext.t27
-rw-r--r--rt/t/mail/gnupg-outgoing-signed_encrypted-plaintext.t28
-rw-r--r--rt/t/mail/gnupg-realmail.t2
-rw-r--r--rt/t/mail/gnupg-reverification.t9
-rw-r--r--rt/t/mail/header-characters.t40
-rw-r--r--rt/t/mail/html-outgoing.t187
-rw-r--r--rt/t/mail/mime_decoding.t177
-rw-r--r--rt/t/mail/multipart.t4
-rw-r--r--rt/t/mail/one-time-recipients.t312
-rw-r--r--rt/t/mail/outlook.t32
-rw-r--r--rt/t/mail/sendmail-plaintext.t150
-rw-r--r--rt/t/mail/sendmail.t41
-rw-r--r--rt/t/mail/smime/incoming.t202
-rw-r--r--rt/t/mail/smime/other-signed.t135
-rw-r--r--rt/t/mail/smime/outgoing.t80
-rw-r--r--rt/t/mail/smime/realmail.t125
-rw-r--r--rt/t/mail/smime/reject_on_unencrypted.t137
-rw-r--r--rt/t/mail/specials-in-encodedwords.t2
-rw-r--r--rt/t/mail/wrong_mime_charset.t9
-rw-r--r--rt/t/pod.t9
-rw-r--r--rt/t/security/CVE-2011-2083-clickable-xss.t3
-rw-r--r--rt/t/security/CVE-2011-2084-cf-values.t2
-rw-r--r--rt/t/security/CVE-2011-2084-modifyscrips-templates.t37
-rw-r--r--rt/t/security/CVE-2011-5092-graph-links.t4
-rw-r--r--rt/t/shredder/00load.t10
-rw-r--r--rt/t/shredder/00skeleton.t18
-rw-r--r--rt/t/shredder/01basics.t20
-rw-r--r--rt/t/shredder/01ticket.t55
-rw-r--r--rt/t/shredder/02group_member.t211
-rw-r--r--rt/t/shredder/02queue.t75
-rw-r--r--rt/t/shredder/02template.t45
-rw-r--r--rt/t/shredder/02user.t32
-rw-r--r--rt/t/shredder/03plugin.t10
-rw-r--r--rt/t/shredder/03plugin_summary.t11
-rw-r--r--rt/t/shredder/03plugin_tickets.t30
-rw-r--r--rt/t/shredder/03plugin_users.t24
-rw-r--r--rt/t/shredder/utils.pl394
-rw-r--r--rt/t/ticket/action_linear_escalate.t2
-rw-r--r--rt/t/ticket/add-watchers.t22
-rw-r--r--rt/t/ticket/cfsort-freeform-single.t269
-rw-r--r--rt/t/ticket/circular_links.t45
-rw-r--r--rt/t/ticket/deferred_owner.t21
-rw-r--r--rt/t/ticket/linking.t36
-rw-r--r--rt/t/ticket/merge.t45
-rw-r--r--rt/t/ticket/scrips_batch.t41
-rw-r--r--rt/t/ticket/search.t31
-rw-r--r--rt/t/ticket/search_by_cf_freeform_multiple.t225
-rw-r--r--rt/t/ticket/search_by_cf_freeform_single.t231
-rw-r--r--rt/t/ticket/search_by_watcher_group.t75
-rw-r--r--rt/t/ticket/simple_search.t (renamed from rt/t/ticket/googleish_search.t)8
-rw-r--r--rt/t/ticket/time-worked.t80
-rw-r--r--rt/t/validator/group_members.t41
-rw-r--r--rt/t/web/admin_queue_lifecycle.t2
-rw-r--r--rt/t/web/admin_user.t6
-rw-r--r--rt/t/web/articles-links.t2
-rw-r--r--rt/t/web/attachment_dropping.t52
-rw-r--r--rt/t/web/attachment_encoding.t2
-rw-r--r--rt/t/web/attachment_truncation.t53
-rw-r--r--rt/t/web/attachments.t586
-rw-r--r--rt/t/web/basic.t12
-rw-r--r--rt/t/web/basic_auth.t34
-rw-r--r--rt/t/web/case-sensitivity.t4
-rw-r--r--rt/t/web/cf_access.t10
-rw-r--r--rt/t/web/cf_date.t8
-rw-r--r--rt/t/web/cf_datetime.t6
-rw-r--r--rt/t/web/cf_groupings.t277
-rw-r--r--rt/t/web/cf_groupings_user.t110
-rw-r--r--rt/t/web/cf_image.t61
-rw-r--r--rt/t/web/cf_onqueue.t2
-rw-r--r--rt/t/web/cf_pattern.t80
-rw-r--r--rt/t/web/cf_render_type.t2
-rw-r--r--rt/t/web/cf_select_one.t37
-rw-r--r--rt/t/web/cf_textarea.t75
-rw-r--r--rt/t/web/cf_values_class.t2
-rw-r--r--rt/t/web/charting.t51
-rw-r--r--rt/t/web/class_create.t2
-rw-r--r--rt/t/web/command_line.t121
-rw-r--r--rt/t/web/compilation_errors.t10
-rw-r--r--rt/t/web/config_tab_right.t6
-rw-r--r--rt/t/web/crypt-gnupg.t70
-rw-r--r--rt/t/web/csrf.t6
-rw-r--r--rt/t/web/custom_search.t2
-rw-r--r--rt/t/web/dashboards-basics.t30
-rw-r--r--rt/t/web/dashboards-groups.t10
-rw-r--r--rt/t/web/dashboards-in-menu.t85
-rw-r--r--rt/t/web/dashboards-search-cache.t46
-rw-r--r--rt/t/web/gnupg-select-keys-on-create.t16
-rw-r--r--rt/t/web/gnupg-select-keys-on-update.t19
-rw-r--r--rt/t/web/group_create.t2
-rw-r--r--rt/t/web/helpers-http-cache-headers.t8
-rw-r--r--rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/ModifyLoginRedirect (renamed from rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/Default)0
-rw-r--r--rt/t/web/html_template.t4
-rw-r--r--rt/t/web/install.t173
-rw-r--r--rt/t/web/language_update.t22
-rw-r--r--rt/t/web/login.t5
-rw-r--r--rt/t/web/mobile.t210
-rw-r--r--rt/t/web/offline.t77
-rw-r--r--rt/t/web/offline_messages_utf8.t64
-rw-r--r--rt/t/web/offline_utf8.t53
-rw-r--r--rt/t/web/owner_disabled_group_19221.t4
-rw-r--r--rt/t/web/path-traversal.t23
-rw-r--r--rt/t/web/psgi-wrap.t15
-rw-r--r--rt/t/web/query_builder.t6
-rw-r--r--rt/t/web/query_builder_queue_limits.t30
-rw-r--r--rt/t/web/query_log.t3
-rw-r--r--rt/t/web/queue_create.t2
-rw-r--r--rt/t/web/redirect-after-login.t4
-rw-r--r--rt/t/web/reminder-permissions.t178
-rw-r--r--rt/t/web/reminders.t2
-rw-r--r--rt/t/web/remote_user.t207
-rw-r--r--rt/t/web/rest-search-group.t102
-rw-r--r--rt/t/web/rest-search-queue.t104
-rw-r--r--rt/t/web/rest-search-user.t115
-rw-r--r--rt/t/web/rest.t6
-rw-r--r--rt/t/web/rest_user_cf.t26
-rw-r--r--rt/t/web/richtext-autohandler.t14
-rw-r--r--rt/t/web/rights.t2
-rw-r--r--rt/t/web/rights1.t24
-rw-r--r--rt/t/web/saved_search_chart.t8
-rw-r--r--rt/t/web/saved_search_permissions.t2
-rw-r--r--rt/t/web/scrips.t219
-rw-r--r--rt/t/web/search_bulk_update_links.t4
-rw-r--r--rt/t/web/search_ical.t196
-rw-r--r--rt/t/web/search_rss.t13
-rw-r--r--rt/t/web/search_simple.t4
-rw-r--r--rt/t/web/self_service.t5
-rw-r--r--rt/t/web/simple_search.t (renamed from rt/t/web/googleish_search.t)29
-rw-r--r--rt/t/web/smime/outgoing.t384
-rw-r--r--rt/t/web/squish.t16
-rw-r--r--rt/t/web/static/js/not-by-default.js (renamed from rt/t/web/html/NoAuth/js/not-by-default.js)0
-rw-r--r--rt/t/web/template.t2
-rw-r--r--rt/t/web/ticket-create-utf8.t6
-rw-r--r--rt/t/web/ticket_forward.t72
-rw-r--r--rt/t/web/ticket_links.t6
-rw-r--r--rt/t/web/ticket_modify_all.t33
-rw-r--r--rt/t/web/ticket_modify_people.t14
-rw-r--r--rt/t/web/ticket_owner.t120
-rw-r--r--rt/t/web/ticket_preserve_basics.t110
-rw-r--r--rt/t/web/ticket_txn_content.t4
-rw-r--r--rt/t/web/user_update.t10
-rw-r--r--rt/t/web/walk.t2
251 files changed, 11354 insertions, 2836 deletions
diff --git a/rt/t/00-mason-syntax.t b/rt/t/00-mason-syntax.t
index ac0da0d58..e87840ac4 100644
--- a/rt/t/00-mason-syntax.t
+++ b/rt/t/00-mason-syntax.t
@@ -9,7 +9,7 @@ find( {
no_chdir => 1,
wanted => sub {
return if /(?:\.(?:jpe?g|png|gif|rej)|\~)$/i;
- return if m{/\.[^/]+\.swp$}; # vim swap files
+ return if m{/\.[^/]+\.sw[op]$}; # vim swap files
return unless -f $_;
local ($@);
ok( eval { compile_file($_) }, "Compiled $File::Find::name ok: $@");
diff --git a/rt/t/99-policy.t b/rt/t/99-policy.t
index 1980e342f..353e40c63 100644
--- a/rt/t/99-policy.t
+++ b/rt/t/99-policy.t
@@ -21,13 +21,14 @@ sub check {
my %check = (
strict => 0,
warnings => 0,
+ no_tabs => 0,
shebang => 0,
exec => 0,
bps_tag => 0,
@_,
);
- if ($check{strict} or $check{warnings} or $check{shebang} or $check{bps_tag}) {
+ if ($check{strict} or $check{warnings} or $check{shebang} or $check{bps_tag} or $check{no_tabs}) {
local $/;
open my $fh, '<', $file or die $!;
my $content = <$fh>;
@@ -98,4 +99,21 @@ check( $_, exec => -1 )
for grep {m{^t/data/}} @files;
check( $_, exec => -1, bps_tag => -1 )
+ for grep {m{^etc/[^/]+$}} @files;
+
+check( $_, exec => -1, bps_tag => -1 )
for grep {m{^etc/upgrade/[^/]+/}} @files;
+
+check( $_, warnings => 1, strict => 1, compile_perl => 1, no_tabs => 1 )
+ for grep {m{^etc/upgrade/.*/content$}} @files;
+
+check( $_, shebang => 1, exec => 1, warnings => 1, strict => 1, bps_tag => 1, no_tabs => 1 )
+ for grep {m{^etc/upgrade/[^/]+$}} @files;
+
+check( $_, compile_perl => 1, exec => 1 )
+ for grep{ -f $_} map {s/\.in$//; $_} grep {m{^etc/upgrade/[^/]+$}} @files;
+
+check( $_, exec => -1 )
+ for grep {m{^(devel/)?docs/}} @files;
+
+done_testing;
diff --git a/rt/t/api/attachment_filename.t b/rt/t/api/attachment_filename.t
index 6bfc7072f..aa8acd2d9 100644
--- a/rt/t/api/attachment_filename.t
+++ b/rt/t/api/attachment_filename.t
@@ -10,18 +10,18 @@ my $mime = MIME::Entity->build(
);
$mime->attach(
- Path => 'share/html/NoAuth/images/bpslogo.png',
+ Path => 'share/static/images/bpslogo.png',
Type => 'image/png',
);
$mime->attach(
- Path => 'share/html/NoAuth/images/bpslogo.png',
+ Path => 'share/static/images/bpslogo.png',
Type => 'image/png',
Filename => 'bpslogo.png',
);
$mime->attach(
- Path => 'share/html/NoAuth/images/bpslogo.png',
+ Path => 'share/static/images/bpslogo.png',
Filename => 'images/bpslogo.png',
Type => 'image/png',
);
diff --git a/rt/t/api/cfsearch.t b/rt/t/api/cfsearch.t
index 7a460ce2e..4df6e0af2 100644
--- a/rt/t/api/cfsearch.t
+++ b/rt/t/api/cfsearch.t
@@ -36,9 +36,9 @@ my $cfvalue1 = 'Foo';
{
my ($id, $msg) = $u1->AddCustomFieldValue(
- Field => $cfname,
- Value => $cfvalue1,
- RecordTransaction => 0 );
+ Field => $cfname,
+ Value => $cfvalue1,
+ RecordTransaction => 0 );
ok( $id, "Adding CF value '$cfvalue1' - " . $msg );
}
@@ -51,18 +51,18 @@ my $cfvalue1 = 'Foo';
{
my ($id, $msg) = $u1->DeleteCustomFieldValue(
- Field => $cfname,
- Value => $cfvalue1,
- RecordTransaction => 0 );
+ Field => $cfname,
+ Value => $cfvalue1,
+ RecordTransaction => 0 );
ok( $id, "Deleting CF value - " . $msg );
}
my $cfvalue2 = 'Bar';
{
my ($id, $msg) = $u1->AddCustomFieldValue(
- Field => $cfname,
- Value => $cfvalue2,
- RecordTransaction => 0 );
+ Field => $cfname,
+ Value => $cfvalue2,
+ RecordTransaction => 0 );
ok( $id, "Adding second CF value '$cfvalue2' - " . $msg );
}
@@ -92,9 +92,9 @@ sub QueryCFValue{
isa_ok( $users, 'RT::Users' );
$users->LimitCustomField(
- CUSTOMFIELD => $cf_id,
- OPERATOR => "=",
- VALUE => $cf_value );
+ CUSTOMFIELD => $cf_id,
+ OPERATOR => "=",
+ VALUE => $cf_value );
while ( my $filtered_user = $users->Next() ){
my $cf_values = $filtered_user->CustomFieldValues($cf->id);
diff --git a/rt/t/api/config.t b/rt/t/api/config.t
index 62b77dffa..b87531139 100644
--- a/rt/t/api/config.t
+++ b/rt/t/api/config.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
use RT;
-use RT::Test nodb => 1, tests => 11;
+use RT::Test nodb => 1, tests => undef;
use Test::Warn;
ok(
@@ -39,5 +39,11 @@ my @encodings = qw(utf-8-strict iso-8859-1 ascii);
warning_is {RT::Config->PostLoadCheck} "Unknown encoding 'foo' in \@EmailInputEncodings option",
'Correct warning for encoding foo';
+RT::Config->Set( WebDefaultStylesheet => 'non-existent-skin-name' );
+warning_like {RT::Config->PostLoadCheck} qr{rudder},
+ 'Correct warning for default stylesheet';
+
my @canonical_encodings = RT::Config->Get('EmailInputEncodings');
is_deeply(\@encodings, \@canonical_encodings, 'Got correct encoding list');
+
+done_testing; \ No newline at end of file
diff --git a/rt/t/api/cron.t b/rt/t/api/cron.t
index 6bd992df3..b16adde82 100644
--- a/rt/t/api/cron.t
+++ b/rt/t/api/cron.t
@@ -24,10 +24,10 @@ This is a content string with no content.';
my $template_obj = RT::Template->new($CurrentUser);
$template_obj->Create(Queue => '0',
- Name => 'recordtest',
- Description => 'testing Record actions',
- Content => $template_content,
- );
+ Name => 'recordtest',
+ Description => 'testing Record actions',
+ Content => $template_content,
+ );
# Create a queue and some tickets.
@@ -36,17 +36,17 @@ my $queue_obj = RT::Queue->new($CurrentUser);
ok($ret, 'record test queue creation');
my $ticket1 = RT::Ticket->new($CurrentUser);
-my ($id, $tobj, $msg2) = $ticket1->Create(Queue => $queue_obj,
- Requestor => ['tara@example.com'],
- Subject => 'bork bork bork',
- Priority => 22,
- );
+my ($id, $tobj, $msg2) = $ticket1->Create(Queue => $queue_obj,
+ Requestor => ['tara@example.com'],
+ Subject => 'bork bork bork',
+ Priority => 22,
+ );
ok($id, 'record test ticket creation 1');
my $ticket2 = RT::Ticket->new($CurrentUser);
($id, $tobj, $msg2) = $ticket2->Create(Queue => $queue_obj,
- Requestor => ['root@localhost'],
- Subject => 'hurdy gurdy'
- );
+ Requestor => ['root@localhost'],
+ Subject => 'hurdy gurdy'
+ );
ok($id, 'record test ticket creation 2');
@@ -58,7 +58,7 @@ ok(require RT::Search::FromSQL, "Search::FromSQL loaded");
my $ticketsqlstr = "Requestor.EmailAddress = '" . $CurrentUser->EmailAddress .
"' AND Priority > '20'";
my $search = RT::Search::FromSQL->new(Argument => $ticketsqlstr, TicketsObj => RT::Tickets->new($CurrentUser),
- );
+ );
is(ref($search), 'RT::Search::FromSQL', "search created");
ok($search->Prepare(), "fromsql search run");
my $counter = 0;
diff --git a/rt/t/api/customfield.t b/rt/t/api/customfield.t
index 6be50bb3a..df8f66df2 100644
--- a/rt/t/api/customfield.t
+++ b/rt/t/api/customfield.t
@@ -2,72 +2,393 @@
use strict;
use warnings;
use RT;
-use RT::Test nodata => 1, tests => 29;
+use RT::Test tests => undef;
use Test::Warn;
+use_ok('RT::CustomField');
-{
+my $queue = RT::Queue->new( RT->SystemUser );
+$queue->Load( "General" );
+ok( $queue->id, "found the General queue" );
-use_ok('RT::CustomField');
-ok(my $cf = RT::CustomField->new(RT->SystemUser));
-ok(my ($id, $msg)= $cf->Create( Name => 'TestingCF',
- Queue => '0',
- SortOrder => '1',
- Description => 'A Testing custom field',
- Type=> 'SelectSingle'), 'Created a global CustomField');
-isnt($id , 0, 'Global custom field correctly created');
-ok ($cf->SingleValue);
-is($cf->Type, 'Select');
-is($cf->MaxValues, 1);
-
-(my $val, $msg) = $cf->SetMaxValues('0');
-ok($val, $msg);
-is($cf->Type, 'Select');
-is($cf->MaxValues, 0);
-ok(!$cf->SingleValue );
-ok(my ($bogus_val, $bogus_msg) = $cf->SetType('BogusType') , "Trying to set a custom field's type to a bogus type");
-is($bogus_val , 0, "Unable to set a custom field's type to a bogus type");
-
-ok(my $bad_cf = RT::CustomField->new(RT->SystemUser));
-ok(my ($bad_id, $bad_msg)= $cf->Create( Name => 'TestingCF-bad',
- Queue => '0',
- SortOrder => '1',
- Description => 'A Testing custom field with a bogus Type',
- Type=> 'SelectSingleton'), 'Created a global CustomField with a bogus type');
-is($bad_id , 0, 'Global custom field correctly decided to not create a cf with a bogus type ');
-
-
-}
-
-{
-
-ok(my $cf = RT::CustomField->new(RT->SystemUser));
+my $cf = RT::CustomField->new(RT->SystemUser);
+ok($cf, "Have a CustomField object");
+
+# Use the old Queue field to set up a ticket CF
+my ($ok, $msg) = $cf->Create(
+ Name => 'TestingCF',
+ Queue => '0',
+ Description => 'A Testing custom field',
+ Type => 'SelectSingle'
+);
+ok($ok, 'Global custom field correctly created');
+is($cf->Type, 'Select', "Is a select CF");
+ok($cf->SingleValue, "Also a single-value CF");
+is($cf->MaxValues, 1, "...meaning only one value, max");
+
+($ok, $msg) = $cf->SetMaxValues('0');
+ok($ok, "Set to infinite values: $msg");
+is($cf->Type, 'Select', "Still a select CF");
+ok( ! $cf->SingleValue, "No longer single-value" );
+is($cf->MaxValues, 0, "...meaning no maximum values");
+
+# Test our sanity checking of CF types
+($ok, $msg) = $cf->SetType('BogusType');
+ok( ! $ok, "Unable to set a custom field's type to a bogus type: $msg");
+
+$cf = RT::CustomField->new(RT->SystemUser);
+($ok, $msg) = $cf->Create(
+ Name => 'TestingCF-bad',
+ Queue => '0',
+ SortOrder => '1',
+ Description => 'A Testing custom field with a bogus Type',
+ Type=> 'SelectSingleton'
+);
+ok( ! $ok, "Correctly could not create with bogus type: $msg");
+
+
+# Deprecated types
+warning_like {
+ ok($cf->ValidateType('SelectSingle'), "ValidateType accepts SelectSingle");
+} qr/deprecated/, "...but warns of deprecation";
+
+warning_like {
+ ok($cf->ValidateType('SelectMultiple'), "ValidateType accepts SelectMultiple");
+} qr/deprecated/, "...but warns of deprecation";
+
+warning_like {
+ ok( ! $cf->ValidateType('SelectFooMultiple'), "ValidateType does not accept SelectFooMultiple");
+} qr/deprecated/, "...and also warns of deprecation";
+
+
+# Test adding and removing CFVs
$cf->Load(1);
-is($cf->Id , 1);
-ok(my ($val,$msg) = $cf->AddValue(Name => 'foo' , Description => 'TestCFValue', SortOrder => '6'));
-isnt($val , 0);
-ok (my ($delval, $delmsg) = $cf->DeleteValue($val));
-ok ($delval,"Deleting a cf value: $delmsg");
+($ok, $msg) = $cf->AddValue(Name => 'foo' , Description => 'TestCFValue', SortOrder => '6');
+ok($ok, "Added a new value to the select options");
+($ok, $msg) = $cf->DeleteValue($ok);
+ok($ok, "Deleting it again");
+
+
+# Loading, and context objects
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName( Name => "TestingCF" );
+ok($cf->id, "Load finds it, given just a name" );
+ok( ! $cf->ContextObject, "Did not get a context object");
+
+# Old Queue => form should find the global, gain no context object
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 0);
+ok($cf->id, "Load finds it, given a Name and Queue => 0" );
+ok( ! $cf->ContextObject, 'Context object not set when queue is 0');
+
+# We don't default to also searching global -- but do pick up a contextobject
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 1);
+ok( ! $cf->id, "Load does not finds it, given a Name and Queue => 1" );
+ok($cf->ContextObject->id, 'Context object is now set');
+# If we IncludeGlobal, we find it
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 1, IncludeGlobal => 1 );
+ok($cf->id, "Load now finds it, given a Name and Queue => 1 and IncludeGlobal" );
+ok($cf->ContextObject->id, 'Context object is also set');
-}
+# The explicit LookupType works
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType );
+ok($cf->id, "Load now finds it, given a Name and LookupType" );
+ok( ! $cf->ContextObject, 'No context object gained');
-{
+# The explicit LookupType, ObjectId, and IncludeGlobal -- what most folks want
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType,
+ ObjectId => 1, IncludeGlobal => 1 );
+ok($cf->id, "Load now finds it, given a Name, LookupType, ObjectId, IncludeGlobal" );
+ok($cf->ContextObject->id, 'And gains a context obj');
-ok(my $cf = RT::CustomField->new(RT->SystemUser));
+# Look for a queue by name
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => "General" );
+ok( ! $cf->id, "No IncludeGlobal, so queue by name fails" );
+ok($cf->ContextObject->id, 'But gains a context object');
+# Look for a queue by name, include global
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => "General", IncludeGlobal => 1 );
+ok($cf->id, "By name, and queue name works with IncludeGlobal" );
+ok($cf->ContextObject->id, 'And gains a context object');
+
+
+
+# A bogus Queue gets you no results, but a warning
+$cf = RT::CustomField->new( RT->SystemUser );
warning_like {
-ok($cf->ValidateType('SelectSingle'));
-} qr/deprecated/;
+ $cf->LoadByName(Name => 'TestingCF', Queue => "Bogus" );
+ ok( ! $cf->id, "With a bogus queue name gets no results" );
+ ok( ! $cf->ContextObject, 'And also no context object');
+} qr/Failed to load RT::Queue 'Bogus'/, "Generates a warning";
+# Ditto by number which is bogus
+$cf = RT::CustomField->new( RT->SystemUser );
warning_like {
-ok($cf->ValidateType('SelectMultiple'));
-} qr/deprecated/;
+ $cf->LoadByName(Name => 'TestingCF', Queue => "9000" );
+ ok( ! $cf->id, "With a bogus queue number gets no results" );
+ ok( ! $cf->ContextObject, 'And also no context object');
+} qr/Failed to load RT::Queue '9000'/, "Generates a warning";
+# But if they also wanted global results, we might have an answer
+$cf = RT::CustomField->new( RT->SystemUser );
warning_like {
-ok(!$cf->ValidateType('SelectFooMultiple'));
-} qr/deprecated/;
+ $cf->LoadByName(Name => 'TestingCF', Queue => "9000", IncludeGlobal => 1 );
+ ok($cf->id, "Bogus queue but IncludeGlobal founds it" );
+ ok( ! $cf->ContextObject, 'But no context object');
+} qr/Failed to load RT::Queue '9000'/, "And generates a warning";
+
+
+# Make it only apply to one queue
+$cf->Load(1);
+my $ocf = RT::ObjectCustomField->new( RT->SystemUser );
+( $ok, $msg ) = $ocf->LoadByCols( CustomField => $cf->id, ObjectId => 0 );
+ok( $ok, "Found global application of CF" );
+( $ok, $msg ) = $ocf->Delete;
+ok( $ok, "...and deleted it");
+( $ok, $msg ) = $ocf->Add( CustomField => $cf->id, ObjectId => 1 );
+ok($ok, "Applied to just queue 1" );
+
+# Looking for it globally with Queue => 0 should fail, gain no context object
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 0);
+ok( ! $cf->id, "Load fails to find, given a Name and Queue => 0" );
+ok( ! $cf->ContextObject, 'Context object not set when queue is 0');
+
+# Looking it up by Queue => 1 works fine, and gets context object
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 1);
+ok($cf->id, "Load does finds it, given a Name and Queue => 1" );
+ok($cf->ContextObject->id, 'Context object is now set');
+
+# Also find it with IncludeGlobal
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 1, IncludeGlobal => 1 );
+ok($cf->id, "Load also finds it, given a Name and Queue => 1 and IncludeGlobal" );
+ok($cf->ContextObject->id, 'Context object is also set');
+
+# The explicit LookupType works
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType );
+ok($cf->id, "Load also finds it, given a Name and LookupType" );
+ok( ! $cf->ContextObject, 'But no context object gained');
+
+# Explicit LookupType, ObjectId works
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType,
+ ObjectId => 1 );
+ok($cf->id, "Load still finds it, given a Name, LookupType, ObjectId" );
+ok($cf->ContextObject->id, 'And gains a context obj');
+
+# Explicit LookupType, ObjectId works
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType,
+ ObjectId => 1, IncludeGlobal => 1 );
+ok($cf->id, "Load also finds it, given a Name, LookupType, ObjectId, and IncludeGlobal" );
+ok($cf->ContextObject->id, 'And gains a context obj');
+
+# Look for a queue by name
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => "General" );
+ok($cf->id, "Finds it by queue name" );
+ok($cf->ContextObject->id, 'But gains a context object');
+
+# Look for a queue by name, include global
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => "General", IncludeGlobal => 1 );
+ok($cf->id, "By name, and queue name works with IncludeGlobal" );
+ok($cf->ContextObject->id, 'And gains a context object');
+
+
+
+
+# Change the lookup type to be a _queue_ CF
+($ok, $msg) = $cf->SetLookupType( RT::Queue->CustomFieldLookupType );
+ok($ok, "Changed CF type to be a CF on queues" );
+$ocf = RT::ObjectCustomField->new( RT->SystemUser );
+( $ok, $msg ) = $ocf->Add( CustomField => $cf->id, ObjectId => 0 );
+ok($ok, "Applied globally" );
+
+# Just looking by name gets you CFs of any type
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF');
+ok($cf->id, "Find the CF by name, with no queue" );
+
+# Queue => 0 means "ticket CF", so doesn't find it
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 0);
+ok( ! $cf->id, "Wrong lookup type to find with Queue => 0" );
+
+# Queue => 1 and IncludeGlobal also doesn't find it
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 0, IncludeGlobal => 1);
+ok( ! $cf->id, "Also doesn't find with Queue => 0 and IncludeGlobal" );
+
+# Find it with the right LookupType
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType );
+ok($cf->id, "Found for the right lookup type" );
+
+# Found globally
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType, ObjectId => 0 );
+ok($cf->id, "Found for the right lookup type and ObjectId 0" );
+
+# Also works with Queue instead of ObjectId
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType, Queue => 0 );
+ok($cf->id, "Found for the right lookup type and Queue 0" );
+
+# Not found without IncludeGlobal
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType, ObjectId => 1 );
+ok( ! $cf->id, "Not found for ObjectId 1 and no IncludeGlobal" );
+
+# Found with IncludeGlobal
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType,
+ ObjectId => 1, IncludeGlobal => 1 );
+ok($cf->id, "Found for ObjectId 1 and IncludeGlobal" );
+
+# Found with IncludeGlobal and Queue instead of ObjectId
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Queue->CustomFieldLookupType,
+ ObjectId => 1, IncludeGlobal => 1 );
+ok($cf->id, "Found for Queue 1 and IncludeGlobal" );
+
+
+
+# Change the lookup type to be a _transaction_ CF
+($ok, $msg) = $cf->SetLookupType( RT::Transaction->CustomFieldLookupType );
+ok($ok, "Changed CF type to be a CF on transactions" );
+$ocf = RT::ObjectCustomField->new( RT->SystemUser );
+( $ok, $msg ) = $ocf->Add( CustomField => $cf->id, ObjectId => 0 );
+ok($ok, "Applied globally" );
+
+# Just looking by name gets you CFs of any type
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF');
+ok($cf->id, "Find the CF by name, with no queue" );
+
+# Queue => 0 means "ticket CF", so doesn't find it
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 0);
+ok( ! $cf->id, "Wrong lookup type to find with Queue => 0" );
+
+# Queue => 1 and IncludeGlobal also doesn't find it
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 0, IncludeGlobal => 1);
+ok( ! $cf->id, "Also doesn't find with Queue => 0 and IncludeGlobal" );
+
+
+# Change the lookup type to be a _user_ CF
+$cf->Load(1);
+($ok, $msg) = $cf->SetLookupType( RT::User->CustomFieldLookupType );
+ok($ok, "Changed CF type to be a CF on users" );
+$ocf = RT::ObjectCustomField->new( RT->SystemUser );
+( $ok, $msg ) = $ocf->Add( CustomField => $cf->id, ObjectId => 0 );
+ok($ok, "Applied globally" );
+
+# Just looking by name gets you CFs of any type
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF');
+ok($cf->id, "Find the CF by name, with no queue" );
+
+# Queue => 0 means "ticket CF", so doesn't find it
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 0);
+ok( ! $cf->id, "Wrong lookup type to find with Queue => 0" );
+
+# Queue => 1 and IncludeGlobal also doesn't find it
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 0, IncludeGlobal => 1);
+ok( ! $cf->id, "Also doesn't find with Queue => 0 and IncludeGlobal" );
+
+# But RT::User->CustomFieldLookupType does
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::User->CustomFieldLookupType );
+ok($cf->id, "User lookuptype does" );
+
+# Also with an explicit global
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::User->CustomFieldLookupType, ObjectId => 0 );
+ok($cf->id, "Also with user CF and explicit global" );
+
+
+
+# Add a second, queue-specific CF to test load order
+$cf->Load(1);
+($ok, $msg) = $cf->SetLookupType( RT::Ticket->CustomFieldLookupType );
+ok($ok, "Changed CF type back to be a CF on tickets" );
+$ocf = RT::ObjectCustomField->new( RT->SystemUser );
+( $ok, $msg ) = $ocf->Add( CustomField => $cf->id, ObjectId => 0 );
+ok($ok, "Applied globally" );
+($ok, $msg) = $cf->SetDescription( "Global CF" );
+ok($ok, "Changed CF type back to be a CF on tickets" );
+
+($ok, $msg) = $cf->Create(
+ Name => 'TestingCF',
+ Queue => '1',
+ Description => 'Queue-specific CF',
+ Type => 'SelectSingle'
+);
+ok($ok, "Created second CF successfully");
+
+# If passed just a name, you get the first by id
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF' );
+like($cf->Description, qr/Global/, "Gets the first (global) one if just loading by name" );
+
+# Ditto if also limited to lookuptype
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', LookupType => RT::Ticket->CustomFieldLookupType );
+like($cf->Description, qr/Global/, "Same, if one adds a LookupType" );
+
+# Gets the global with Queue => 0
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 0 );
+like($cf->Description, qr/Global/, "Specify Queue => 0 and get global" );
+
+# Gets the queue with Queue => 1
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 1 );
+like($cf->Description, qr/Queue/, "Specify Queue => 1 and get the queue" );
+
+# Gets the queue with Queue => 1 and IncludeGlobal
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 1, IncludeGlobal => 1 );
+like($cf->Description, qr/Queue/, "Specify Queue => 1 and IncludeGlobal and get the queue" );
+
+
+# Disable one of them
+($ok, $msg) = $cf->SetDisabled(1);
+ok($ok, "Disabled the Queue-specific one");
+
+# With just a name, prefers the non-disabled
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF' );
+like($cf->Description, qr/Global/, "Prefers non-disabled CFs" );
+
+# Still finds the queue one, if asked
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 1 );
+like($cf->Description, qr/Queue/, "Still loads the disabled queue CF" );
+
+# Prefers the global one if IncludeGlobal
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 1, IncludeGlobal => 1 );
+like($cf->Description, qr/Global/, "Prefers the global one with IncludeGlobal" );
+# IncludeDisabled allows filtering out the disabled one
+$cf = RT::CustomField->new( RT->SystemUser );
+$cf->LoadByName(Name => 'TestingCF', Queue => 1, IncludeDisabled => 0 );
+ok( ! $cf->id, "Doesn't find it if IncludeDisabled => 0" );
-}
+done_testing;
diff --git a/rt/t/api/date.t b/rt/t/api/date.t
index 22c6f1b58..dd22943c1 100644
--- a/rt/t/api/date.t
+++ b/rt/t/api/date.t
@@ -4,7 +4,7 @@ use DateTime;
use warnings;
use strict;
-use RT::Test tests => 175;
+use RT::Test tests => undef;
use RT::User;
use Test::Warn;
@@ -57,7 +57,7 @@ my $current_user;
is($date->Timezone('user'),
'Europe/Moscow',
"in user context still returns user's timezone");
-
+
$current_user->UserObj->__Set( Field => 'Timezone', Value => '');
is_empty($current_user->UserObj->Timezone,
"successfuly changed user's timezone");
@@ -83,6 +83,7 @@ my $current_user;
{
my $date = RT::Date->new(RT->SystemUser);
+ is($date->IsSet,0, "new date isn't set");
is($date->Unix, 0, "new date returns 0 in Unix format");
is($date->Get, '1970-01-01 00:00:00', "default is ISO format");
warning_like {
@@ -207,6 +208,7 @@ my $current_user;
$current_user->UserObj->__Set( Field => 'Timezone', Value => 'Europe/Moscow');
my $date = RT::Date->new( $current_user );
$date->Set( Format => 'ISO', Timezone => 'utc', Value => '2005-01-01 15:10:00' );
+ is($date->IsSet,1,"Date has been set");
is($date->ISO( Timezone => 'user' ), '2005-01-01 18:10:00', "ISO");
is($date->W3CDTF( Timezone => 'user' ), '2005-01-01T18:10:00+03:00', "W3C DTF");
is($date->RFC2822( Timezone => 'user' ), 'Sat, 01 Jan 2005 18:10:00 +0300', "RFC2822");
@@ -239,7 +241,7 @@ warning_like
{ # bad format
my $date = RT::Date->new(RT->SystemUser);
$date->Set( Format => 'bad' );
- is($date->Unix, 0, "bad format");
+ ok(!$date->IsSet, "bad format");
} qr{Unknown Date format: bad};
@@ -248,13 +250,26 @@ warning_like
$date->Unix(1);
is($date->ISO, '1970-01-01 00:00:01', "correct value");
- foreach (undef, 0, ''){
+ foreach (undef, 0, '', -5){
$date->Unix(1);
is($date->ISO, '1970-01-01 00:00:01', "correct value");
+ is($date->IsSet,1,"Date has been set to a value");
$date->Set(Format => 'unix', Value => $_);
is($date->ISO, '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT due to wrong call");
is($date->Unix, 0, "unix is 0 => unset");
+ is($date->IsSet,0,"Date has been unset");
+ }
+
+ foreach (undef, 0, '', -5){
+ $date->Unix(1);
+ is($date->ISO, '1970-01-01 00:00:01', "correct value");
+ is($date->IsSet,1,"Date has been set to a value");
+
+ $date->Unix($_);
+ is($date->ISO, '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT due to wrong call");
+ is($date->Unix, 0, "unix is 0 => unset");
+ is($date->IsSet,0,"Date has been unset");
}
}
@@ -265,7 +280,7 @@ my $year = (localtime(time))[5] + 1900;
warning_like {
$date->Set(Format => 'ISO', Value => 'weird date');
} qr/Couldn't parse date 'weird date' as a ISO format/;
- is($date->Unix, 0, "date was wrong => unix == 0");
+ ok(!$date->IsSet, "date was wrong => unix == 0");
# XXX: ISO format has more feature than we suport
# http://www.cl.cam.ac.uk/~mgk25/iso-time.html
@@ -294,14 +309,18 @@ my $year = (localtime(time))[5] + 1900;
$date->Set(Format => 'ISO', Value => '112815:10:00');
is($date->ISO, $year .'-11-28 15:10:00', "DDMMhh:mm:ss");
- $date->Set(Format => 'ISO', Value => '2005-13-28 15:10:00');
- is($date->Unix, 0, "wrong month value");
+ warning_like {
+ $date->Set(Format => 'ISO', Value => '2005-13-28 15:10:00');
+ } qr/Invalid date/;
+ ok(!$date->IsSet, "wrong month value");
- $date->Set(Format => 'ISO', Value => '2005-00-28 15:10:00');
- is($date->Unix, 0, "wrong month value");
+ warning_like {
+ $date->Set(Format => 'ISO', Value => '2005-00-28 15:10:00');
+ } qr/Invalid date/;
+ ok(!$date->IsSet, "wrong month value");
$date->Set(Format => 'ISO', Value => '1960-01-28 15:10:00');
- is($date->Unix, 0, "too old, we don't support");
+ ok(!$date->IsSet, "too old, we don't support");
}
{ # set+datemanip format(Time::ParseDate)
@@ -326,7 +345,7 @@ my $year = (localtime(time))[5] + 1900;
warnings_like {
$date->Set(Format => 'unknown', Value => 'weird date');
} qr{Couldn't parse date 'weird date' by Time::ParseDate};
- is($date->Unix, 0, "date was wrong");
+ ok(!$date->IsSet, "date was wrong");
RT->Config->Set( Timezone => 'Europe/Moscow' );
$date->Set(Format => 'unknown', Value => '2005-11-28 15:10:00');
@@ -360,6 +379,34 @@ my $year = (localtime(time))[5] + 1900;
is($date->ISO, '2005-11-28 15:10:00', "YYYY-DD-MM hh:mm:ss");
}
+{ # 'tomorrow 10am' with TZ
+ $current_user->UserObj->__Set( Field => 'Timezone', Value => 'Europe/Moscow');
+
+ set_fixed_time("2012-06-14T15:10:00Z"); # 14th in UTC and Moscow
+ my $date = RT::Date->new( $current_user );
+ $date->Set(Format => 'unknown', Value => 'tomorrow 10am');
+ is($date->ISO, '2012-06-15 06:00:00', "YYYY-DD-MM hh:mm:ss");
+
+ set_fixed_time("2012-06-13T23:10:00Z"); # 13th in UTC and 14th in Moscow
+ $date = RT::Date->new( $current_user );
+ $date->Set(Format => 'unknown', Value => 'tomorrow 10am');
+ is($date->ISO, '2012-06-15 06:00:00', "YYYY-DD-MM hh:mm:ss");
+
+ $current_user->UserObj->__Set( Field => 'Timezone', Value => 'US/Hawaii');
+
+ set_fixed_time("2012-06-14T20:10:00Z"); # 14th in UTC and Hawaii
+ $date = RT::Date->new( $current_user );
+ $date->Set(Format => 'unknown', Value => 'tomorrow 10am');
+ is($date->ISO, '2012-06-15 20:00:00', "YYYY-DD-MM hh:mm:ss");
+
+ set_fixed_time("2012-06-15T05:10:00Z"); # 15th in UTC and 14th in Hawaii
+ $date = RT::Date->new( $current_user );
+ $date->Set(Format => 'unknown', Value => 'tomorrow 10am');
+ is($date->ISO, '2012-06-15 20:00:00', "YYYY-DD-MM hh:mm:ss");
+
+ restore_time();
+}
+
{ # SetToMidnight
my $date = RT::Date->new(RT->SystemUser);
@@ -475,11 +522,11 @@ my $year = (localtime(time))[5] + 1900;
{ # DurationAsString
my $date = RT::Date->new(RT->SystemUser);
- is($date->DurationAsString(1), '1 sec', '1 sec');
- is($date->DurationAsString(59), '59 sec', '59 sec');
- is($date->DurationAsString(60), '1 min', '1 min');
- is($date->DurationAsString(60*119), '119 min', '119 min');
- is($date->DurationAsString(60*60*2-1), '120 min', '120 min');
+ is($date->DurationAsString(1), '1 second', '1 sec');
+ is($date->DurationAsString(59), '59 seconds', '59 sec');
+ is($date->DurationAsString(60), '1 minute', '1 min');
+ is($date->DurationAsString(60*119), '119 minutes', '119 min');
+ is($date->DurationAsString(60*60*2-1), '120 minutes', '120 min');
is($date->DurationAsString(60*60*2), '2 hours', '2 hours');
is($date->DurationAsString(60*60*48-1), '48 hours', '48 hours');
is($date->DurationAsString(60*60*48), '2 days', '2 days');
@@ -488,9 +535,9 @@ my $year = (localtime(time))[5] + 1900;
is($date->DurationAsString(60*60*24*7*8-1), '8 weeks', '8 weeks');
is($date->DurationAsString(60*60*24*61), '2 months', '2 months');
is($date->DurationAsString(60*60*24*365-1), '12 months', '12 months');
- is($date->DurationAsString(60*60*24*366), '1 years', '1 years');
+ is($date->DurationAsString(60*60*24*366), '1 year', '1 year');
- is($date->DurationAsString(-1), '1 sec ago', '1 sec ago');
+ is($date->DurationAsString(-1), '1 second ago', '1 sec ago');
}
{ # DiffAsString
@@ -502,13 +549,13 @@ my $year = (localtime(time))[5] + 1900;
$date->Unix(2);
is($date->DiffAsString(-1), '', 'no diff, wrong input');
- is($date->DiffAsString(3), '1 sec ago', 'diff: 1 sec ago');
- is($date->DiffAsString(1), '1 sec', 'diff: 1 sec');
+ is($date->DiffAsString(3), '1 second ago', 'diff: 1 sec ago');
+ is($date->DiffAsString(1), '1 second', 'diff: 1 sec');
my $ndate = RT::Date->new(RT->SystemUser);
is($date->DiffAsString($ndate), '', 'no diff, wrong input');
$ndate->Unix(3);
- is($date->DiffAsString($ndate), '1 sec ago', 'diff: 1 sec ago');
+ is($date->DiffAsString($ndate), '1 second ago', 'diff: 1 sec ago');
}
{ # Diff
@@ -523,7 +570,7 @@ my $year = (localtime(time))[5] + 1900;
my $date = RT::Date->new(RT->SystemUser);
$date->SetToNow;
my $diff = $date->AgeAsString;
- like($diff, qr/^(0 sec|[1-5] sec ago)$/, 'close enought');
+ like($diff, qr/^(0 seconds|(1 second|[2-5] seconds) ago)$/, 'close enought');
}
{ # GetWeekday
@@ -550,15 +597,16 @@ my $year = (localtime(time))[5] + 1900;
# set unknown format: edge cases
my $date = RT::Date->new(RT->SystemUser);
$date->Set( Value => 0, Format => 'unknown' );
- is( $date->Unix(), 0, "unix is 0 with Value => 0, Format => 'unknown'" );
+ ok( !$date->IsSet, "unix is 0 with Value => 0, Format => 'unknown'" );
$date->Set( Value => '', Format => 'unknown' );
- is( $date->Unix(), 0, "unix is 0 with Value => '', Format => 'unknown'" );
+ ok( !$date->IsSet, "unix is 0 with Value => '', Format => 'unknown'" );
$date->Set( Value => ' ', Format => 'unknown' );
- is( $date->Unix(), 0, "unix is 0 with Value => ' ', Format => 'unknown'" );
+ ok( !$date->IsSet, "unix is 0 with Value => ' ', Format => 'unknown'" );
}
#TODO: AsString
#TODO: RFC2822, W3CDTF with Timezones
+done_testing;
diff --git a/rt/t/api/db_indexes.t b/rt/t/api/db_indexes.t
new file mode 100644
index 000000000..3c305d84d
--- /dev/null
+++ b/rt/t/api/db_indexes.t
@@ -0,0 +1,165 @@
+use strict;
+use warnings;
+use Test::Warn;
+
+use RT::Test tests => undef;
+
+my $handle = $RT::Handle;
+my $db_type = RT->Config->Get('DatabaseType');
+
+# Pg,Oracle needs DBA
+RT::Test::__reconnect_rt('as dba');
+ok( $handle->dbh->do("ALTER SESSION SET CURRENT_SCHEMA=". RT->Config->Get('DatabaseUser') ) )
+ if $db_type eq 'Oracle';
+
+note "test handle->Indexes method";
+{
+ my %indexes = $handle->Indexes;
+ ok grep $_ eq 'tickets1', @{ $indexes{'tickets'} };
+ ok grep $_ eq 'tickets2', @{ $indexes{'tickets'} };
+ ok grep $_ eq 'users1', @{ $indexes{'users'} };
+ ok grep $_ eq 'users4', @{ $indexes{'users'} };
+}
+
+note "test handle->DropIndex method";
+{
+ my ($status, $msg) = $handle->DropIndex( Table => 'Tickets', Name => 'Tickets1' );
+ ok $status, $msg;
+
+ my %indexes = $handle->Indexes;
+ ok !grep $_ eq 'tickets1', @{ $indexes{'tickets'} };
+
+ ($status, $msg) = $handle->DropIndex( Table => 'Tickets', Name => 'Tickets1' );
+ ok !$status, $msg;
+}
+
+note "test handle->DropIndexIfExists method";
+{
+ my ($status, $msg) = $handle->DropIndexIfExists( Table => 'Tickets', Name => 'Tickets2' );
+ ok $status, $msg;
+
+ my %indexes = $handle->Indexes;
+ ok !grep $_ eq 'tickets2', @{ $indexes{'tickets'} };
+
+ ($status, $msg) = $handle->DropIndexIfExists( Table => 'Tickets', Name => 'Tickets2' );
+ ok $status, $msg;
+}
+
+note "test handle->IndexInfo method";
+{
+ if ($db_type ne 'Oracle' && $db_type ne 'mysql') {
+ my %res = $handle->IndexInfo( Table => 'Attachments', Name => 'Attachments1' );
+ is_deeply(
+ \%res,
+ {
+ Table => 'attachments', Name => 'attachments1',
+ Unique => 0, Functional => 0,
+ Columns => ['parent']
+ }
+ );
+ } else {
+ my %res = $handle->IndexInfo( Table => 'Attachments', Name => 'Attachments2' );
+ is_deeply(
+ \%res,
+ {
+ Table => 'attachments', Name => 'attachments2',
+ Unique => 0, Functional => 0,
+ Columns => ['transactionid']
+ }
+ );
+ }
+
+ my %res = $handle->IndexInfo( Table => 'GroupMembers', Name => 'GroupMembers1' );
+ is_deeply(
+ \%res,
+ {
+ Table => 'groupmembers', Name => 'groupmembers1',
+ Unique => 1, Functional => 0,
+ Columns => ['groupid', 'memberid']
+ }
+ );
+
+ if ( $db_type eq 'Pg' || $db_type eq 'Oracle' ) {
+ %res = $handle->IndexInfo( Table => 'Queues', Name => 'Queues1' );
+ is_deeply(
+ \%res,
+ {
+ Table => 'queues', Name => 'queues1',
+ Unique => 1, Functional => 1,
+ Columns => ['name'],
+ CaseInsensitive => { name => 1 },
+ }
+ );
+ }
+}
+
+note "test ->CreateIndex and ->IndexesThatBeginWith methods";
+{
+ {
+ my ($name, $msg) = $handle->CreateIndex(
+ Table => 'Users', Name => 'test_users1',
+ Columns => ['Organization'],
+ );
+ ok $name, $msg;
+ }
+ {
+ my ($name, $msg) = $handle->CreateIndex(
+ Table => 'Users', Name => 'test_users2',
+ Columns => ['Organization', 'Name'],
+ );
+ ok $name, $msg;
+ }
+
+ my @list = $handle->IndexesThatBeginWith( Table => 'Users', Columns => ['Organization'] );
+ is_deeply([sort map $_->{Name}, @list], [qw(test_users1 test_users2)]);
+
+ my ($status, $msg) = $handle->DropIndex( Table => 'Users', Name => 'test_users1' );
+ ok $status, $msg;
+ ($status, $msg) = $handle->DropIndex( Table => 'Users', Name => 'test_users2' );
+ ok $status, $msg;
+}
+
+note "Test some cases sensitivity aspects";
+{
+ {
+ my %res = $handle->IndexInfo( Table => 'groupmembers', Name => 'groupmembers1' );
+ is_deeply(
+ \%res,
+ {
+ Table => 'groupmembers', Name => 'groupmembers1',
+ Unique => 1, Functional => 0,
+ Columns => ['groupid', 'memberid']
+ }
+ );
+ }
+
+ {
+ my ($status, $msg) = $handle->DropIndex( Table => 'groupmembers', Name => 'groupmembers1' );
+ ok $status, $msg;
+
+ my %indexes = $handle->Indexes;
+ ok !grep $_ eq 'groupmembers1', @{ $indexes{'groupmembers'} };
+ }
+
+ {
+ my ($name, $msg) = $handle->CreateIndex(
+ Table => 'groupmembers', Name => 'groupmembers1',
+ Unique => 1,
+ Columns => ['groupid', 'memberid']
+ );
+ ok $name, $msg;
+
+ my %indexes = $handle->Indexes;
+ ok grep $_ eq 'groupmembers1', @{ $indexes{'groupmembers'} };
+ }
+
+ {
+ my ($status, $msg) = $handle->DropIndexIfExists( Table => 'groupmembers', Name => 'groupmembers1' );
+ ok $status, $msg;
+
+ my %indexes = $handle->Indexes;
+ ok !grep $_ eq 'groupmembers1', @{ $indexes{'groupmembers'} };
+ }
+}
+
+done_testing();
diff --git a/rt/t/api/group-rights.t b/rt/t/api/group-rights.t
index 0494c286e..4f5f03d0d 100644
--- a/rt/t/api/group-rights.t
+++ b/rt/t/api/group-rights.t
@@ -2,7 +2,7 @@ use strict;
use warnings;
use RT::Test nodata => 1, tests => 114;
-RT::Group->AddRights(
+RT::Group->AddRight( General =>
'RTxGroupRight' => 'Just a right for testing rights',
);
diff --git a/rt/t/api/group.t b/rt/t/api/group.t
index d55fc5c4a..f82dfc57e 100644
--- a/rt/t/api/group.t
+++ b/rt/t/api/group.t
@@ -96,7 +96,7 @@ is($group_3->HasMemberRecursively($principal_2), undef, "group 3 has member 2 re
ok(my $u = RT::Group->new(RT->SystemUser));
ok($u->Load(4), "Loaded the first user");
-is($u->PrincipalObj->ObjectId , 4, "user 4 is the fourth principal");
+is($u->PrincipalObj->id , 4, "user 4 is the fourth principal");
is($u->PrincipalObj->PrincipalType , 'Group' , "Principal 4 is a group");
diff --git a/rt/t/api/groups.t b/rt/t/api/groups.t
index d2dc126dc..c2e7fc583 100644
--- a/rt/t/api/groups.t
+++ b/rt/t/api/groups.t
@@ -2,7 +2,7 @@ use strict;
use warnings;
use RT::Test nodata => 1, tests => 27;
-RT::Group->AddRights(
+RT::Group->AddRight( General =>
'RTxGroupRight' => 'Just a right for testing rights',
);
@@ -49,8 +49,7 @@ my $testuser = RT::User->new(RT->SystemUser);
($id,$msg) = $testuser->Create(Name => 'JustAnAdminCc');
ok ($id,$msg);
-my $global_admin_cc = RT::Group->new(RT->SystemUser);
-$global_admin_cc->LoadSystemRoleGroup('AdminCc');
+my $global_admin_cc = RT->System->RoleGroup( 'AdminCc' );
ok($global_admin_cc->id, "Found the global admincc group");
my $groups = RT::Groups->new(RT->SystemUser);
$groups->WithRight(Right => 'OwnTicket', Object => $q);
diff --git a/rt/t/api/i18n_guess.t b/rt/t/api/i18n_guess.t
index a64b2952c..0a99011f2 100644
--- a/rt/t/api/i18n_guess.t
+++ b/rt/t/api/i18n_guess.t
@@ -4,8 +4,8 @@ use warnings;
use RT::Test tests => 16;
-use constant HAS_ENCODE_GUESS => do { local $@; eval { require Encode::Guess; 1 } };
-use constant HAS_ENCODE_DETECT => do { local $@; eval { require Encode::Detect::Detector; 1 } };
+use constant HAS_ENCODE_GUESS => Encode::Guess->require;
+use constant HAS_ENCODE_DETECT => Encode::Detect::Detector->require;
my $string = "\x{442}\x{435}\x{441}\x{442} \x{43f}\x{43e}\x{434}\x{434}\x{435}\x{440}\x{436}\x{43a}\x{430}";
diff --git a/rt/t/api/i18n_mime_encoding.t b/rt/t/api/i18n_mime_encoding.t
new file mode 100644
index 000000000..5ad532ed2
--- /dev/null
+++ b/rt/t/api/i18n_mime_encoding.t
@@ -0,0 +1,32 @@
+use warnings;
+use strict;
+
+use RT::Test nodata => 1, tests => undef;
+use RT::I18N;
+use Test::Warn;
+
+diag "normal mime encoding conversion: utf8 => iso-8859-1";
+{
+ my $mime = MIME::Entity->build(
+ Type => 'text/plain; charset=utf-8',
+ Data => ['À中文'],
+ );
+
+ warning_like {
+ RT::I18N::SetMIMEEntityToEncoding( $mime, 'iso-8859-1', );
+ }
+ [ qr/does not map to iso-8859-1/ ], 'got one "not map" error';
+ is( $mime->stringify_body, 'À中文', 'body is not changed' );
+ is( $mime->head->mime_attr('Content-Type'), 'application/octet-stream' );
+}
+
+diag "mime encoding conversion: utf8 => iso-8859-1";
+{
+ my $mime = MIME::Entity->build(
+ Type => 'text/plain; charset=utf-8',
+ Data => ['À中文'],
+ );
+ is( $mime->stringify_body, 'À中文', 'body is not changed' );
+}
+
+done_testing;
diff --git a/rt/t/api/initialdata.t b/rt/t/api/initialdata.t
new file mode 100644
index 000000000..5856528a3
--- /dev/null
+++ b/rt/t/api/initialdata.t
@@ -0,0 +1,16 @@
+use strict;
+use warnings;
+
+use RT::Test tests => 'no_declare';
+
+# This test script processes the sample initialdata file in
+# ../data/initialdata/initialdata
+# To add initialdata tests, add the data to the initialdata file and it
+# will be processed by this script.
+
+my $initialdata = RT::Test::get_relocatable_file("initialdata" => "..", "data", "initialdata");
+my ($rv, $msg) = RT->DatabaseHandle->InsertData( $initialdata, undef, disconnect_after => 0 );
+ok($rv, "Inserted test data from $initialdata")
+ or diag "Error: $msg";
+
+done_testing(); \ No newline at end of file
diff --git a/rt/t/api/link.t b/rt/t/api/link.t
index a9e54a716..3066388f2 100644
--- a/rt/t/api/link.t
+++ b/rt/t/api/link.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test nodata => 1, tests => 84;
+use RT::Test nodata => 1, tests => 83;
use RT::Test::Web;
use Test::Warn;
@@ -35,9 +35,7 @@ ok $cid, 'created a ticket #'. $cid or diag "error: $msg";
my ($status, $msg);
clean_links();
- warning_like {
- ($status, $msg) = $parent->AddLink;
- } qr/Base or Target must be specified/, "warned about linking a ticket to itself";
+ ($status, $msg) = $parent->AddLink;
ok(!$status, "didn't create a link: $msg");
warning_like {
diff --git a/rt/t/api/password-types.t b/rt/t/api/password-types.t
index 4cb634248..9eeded499 100644
--- a/rt/t/api/password-types.t
+++ b/rt/t/api/password-types.t
@@ -4,17 +4,29 @@ use warnings;
use RT::Test;
use Digest::MD5;
-my $default = "sha512";
+my $default = "bcrypt";
my $root = RT::User->new(RT->SystemUser);
$root->Load("root");
-# Salted SHA-512 (default)
+# bcrypt (default)
my $old = $root->__Value("Password");
like($old, qr/^\!$default\!/, "Stored as salted $default");
ok($root->IsPassword("password"));
is($root->__Value("Password"), $old, "Unchanged after password check");
+# bcrypt (smaller number of rounds)
+my $salt = Crypt::Eksblowfish::Bcrypt::en_base64("a"x16);
+$root->_Set( Field => "Password", Value => RT::User->_GeneratePassword_bcrypt("smaller", 6, $salt) );
+like($root->__Value("Password"), qr/^\!$default\!06\!/, "Stored with a smaller number of rounds");
+ok($root->IsPassword("smaller"), "Smaller number of bcrypt rounds works");
+like($root->__Value("Password"), qr/^\!$default\!10\!/, "And is now upgraded to salted $default");
+
+# Salted SHA-512, one round
+$root->_Set( Field => "Password", Value => RT::User->_GeneratePassword_sha512("other", "salt") );
+ok($root->IsPassword("other"), "SHA-512 password works");
+like($root->__Value("Password"), qr/^\!$default\!/, "And is now upgraded to salted $default");
+
# Crypt
$root->_Set( Field => "Password", Value => crypt("something", "salt"));
ok($root->IsPassword("something"), "crypt()ed password works");
diff --git a/rt/t/api/queue.t b/rt/t/api/queue.t
index 07b8ed479..71efb4d39 100644
--- a/rt/t/api/queue.t
+++ b/rt/t/api/queue.t
@@ -2,7 +2,7 @@
use strict;
use warnings;
use RT;
-use RT::Test nodata => 1, tests => 24;
+use RT::Test nodata => 1, tests => undef;
{
@@ -58,8 +58,7 @@ ok(!$id, $val);
my $Queue = RT::Queue->new(RT->SystemUser);
my ($id, $msg) = $Queue->Create(Name => "Foo");
ok ($id, "Foo $id was created");
-ok(my $group = RT::Group->new(RT->SystemUser));
-ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Requestor'));
+ok(my $group = $Queue->RoleGroup('Requestor'));
ok ($group->Id, "Found the requestors object for this Queue");
{
@@ -79,13 +78,12 @@ ok ($Queue->IsWatcher(Type => 'Cc', PrincipalId => $bob->PrincipalId), "The Queu
"The Queue no longer has bob at fsck.com as a requestor");
}
-$group = RT::Group->new(RT->SystemUser);
-ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc'));
+$group = $Queue->RoleGroup('Cc');
ok ($group->Id, "Found the cc object for this Queue");
-$group = RT::Group->new(RT->SystemUser);
-ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'AdminCc'));
+$group = $Queue->RoleGroup('AdminCc');
ok ($group->Id, "Found the AdminCc object for this Queue");
}
+done_testing;
diff --git a/rt/t/api/record.t b/rt/t/api/record.t
index 4b6b0b89c..7abd41ca5 100644
--- a/rt/t/api/record.t
+++ b/rt/t/api/record.t
@@ -16,8 +16,8 @@ ok (require RT::Record);
my $ticket = RT::Ticket->new(RT->SystemUser);
my $group = RT::Group->new(RT->SystemUser);
-is($ticket->ObjectTypeStr, 'Ticket', "Ticket returns correct typestring");
-is($group->ObjectTypeStr, 'Group', "Group returns correct typestring");
+is($ticket->RecordType, 'Ticket', "Ticket returns correct typestring");
+is($group->RecordType, 'Group', "Group returns correct typestring");
}
diff --git a/rt/t/api/reminder-permissions.t b/rt/t/api/reminder-permissions.t
new file mode 100644
index 000000000..8253d67af
--- /dev/null
+++ b/rt/t/api/reminder-permissions.t
@@ -0,0 +1,49 @@
+use strict;
+use warnings;
+use RT::Test tests => 9;
+
+my $user_a = RT::Test->load_or_create_user(
+ Name => 'user_a',
+ Password => 'password',
+);
+
+ok( $user_a && $user_a->id, 'created user_a' );
+ok(
+ RT::Test->add_rights(
+ {
+ Principal => $user_a,
+ Right => [qw/SeeQueue CreateTicket ShowTicket OwnTicket/]
+ },
+ ),
+ 'add basic rights for user_a'
+);
+
+my $ticket = RT::Test->create_ticket(
+ Subject => 'test reminder permission',
+ Queue => 'General',
+);
+ok( $ticket->id, 'created a ticket' );
+$ticket->CurrentUser($user_a);
+
+my ( $status, $msg ) = $ticket->Reminders->Add(
+ Subject => 'user a reminder',
+ Owner => $user_a->id,
+);
+ok( !$status, "couldn't create reminders without ModifyTicket: $msg" );
+
+ok(
+ RT::Test->add_rights(
+ {
+ Principal => $user_a,
+ Right => [qw/ModifyTicket/]
+ },
+ ),
+ 'add ModifyTicket right for user_a'
+);
+
+( $status, $msg ) = $ticket->Reminders->Add(
+ Subject => 'user a reminder',
+ Owner => $user_a->id,
+);
+ok( $status, "created a reminder with ModifyTicket: $msg" );
+
diff --git a/rt/t/api/rights.t b/rt/t/api/rights.t
index 5cf3a0008..a6346a737 100644
--- a/rt/t/api/rights.t
+++ b/rt/t/api/rights.t
@@ -29,10 +29,8 @@ ok $user && $user->id, 'loaded or created user';
}
{
- my $group = RT::Group->new( RT->SystemUser );
- ok( $group->LoadQueueRoleGroup( Queue => $queue->id, Type=> 'Owner' ),
- "load queue owners role group"
- );
+ my $group = $queue->RoleGroup( 'Owner' );
+ ok( $group->Id, "load queue owners role group" );
my $ace = RT::ACE->new( RT->SystemUser );
my ($ace_id, $msg) = $group->PrincipalObj->GrantRight(
Right => 'ReplyToTicket', Object => $queue
@@ -84,10 +82,8 @@ my $ticket;
{
# Testing of EquivObjects
- my $group = RT::Group->new( RT->SystemUser );
- ok( $group->LoadQueueRoleGroup( Queue => $queue->id, Type=> 'AdminCc' ),
- "load queue AdminCc role group"
- );
+ my $group = $queue->RoleGroup( 'AdminCc' );
+ ok( $group->Id, "load queue AdminCc role group" );
my $ace = RT::ACE->new( RT->SystemUser );
my ($ace_id, $msg) = $group->PrincipalObj->GrantRight(
Right => 'ModifyTicket', Object => $queue
diff --git a/rt/t/api/rtname.t b/rt/t/api/rtname.t
index 8b7b54bd1..f2bffd559 100644
--- a/rt/t/api/rtname.t
+++ b/rt/t/api/rtname.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test nodata => 1, tests => 9;
+use RT::Test nodata => 1, tests => 12;
use RT::Interface::Email;
@@ -31,3 +31,8 @@ is(RT::Interface::Email::ParseTicketId("[site #123] test"), 123);
is(RT::Interface::Email::ParseTicketId("[newsite #123] test"), 123);
is(RT::Interface::Email::ParseTicketId("[othersite #123] test"), undef);
+# Parens work fine
+RT->Config->Set( EmailSubjectTagRegex => qr/(new|)(site)/ );
+is(RT::Interface::Email::ParseTicketId("[site #123] test"), 123);
+is(RT::Interface::Email::ParseTicketId("[newsite #123] test"), 123);
+is(RT::Interface::Email::ParseTicketId("[othersite #123] test"), undef);
diff --git a/rt/t/api/savedsearch.t b/rt/t/api/savedsearch.t
index 0aa67eeda..2e924bf7b 100644
--- a/rt/t/api/savedsearch.t
+++ b/rt/t/api/savedsearch.t
@@ -13,9 +13,9 @@ use Test::Warn;
my $searchuser = RT::User->new(RT->SystemUser);
my ($ret, $msg) = $searchuser->Create(Name => 'searchuser'.$$,
- Privileged => 1,
- EmailAddress => "searchuser\@p$$.example.com",
- RealName => 'Search user');
+ Privileged => 1,
+ EmailAddress => "searchuser\@p$$.example.com",
+ RealName => 'Search user');
ok($ret, "created searchuser: $msg");
$searchuser->PrincipalObj->GrantRight(Right => 'LoadSavedSearch');
$searchuser->PrincipalObj->GrantRight(Right => 'CreateSavedSearch');
@@ -26,9 +26,9 @@ my $ingroup = RT::Group->new(RT->SystemUser);
$ingroup->CreateUserDefinedGroup(Name => 'searchgroup1'.$$);
$ingroup->AddMember($searchuser->Id);
$searchuser->PrincipalObj->GrantRight(Right => 'EditSavedSearches',
- Object => $ingroup);
+ Object => $ingroup);
$searchuser->PrincipalObj->GrantRight(Right => 'ShowSavedSearches',
- Object => $ingroup);
+ Object => $ingroup);
# This is the group whose searches searchuser should not be able to see.
my $outgroup = RT::Group->new(RT->SystemUser);
@@ -44,9 +44,9 @@ $searchuser->PrincipalObj->GrantRight(Right => 'OwnTicket', Object => $queue);
my $ticket = RT::Ticket->new(RT->SystemUser);
$ticket->Create(Queue => $queue->Id,
- Requestor => [ $searchuser->Name ],
- Owner => $searchuser,
- Subject => 'saved search test');
+ Requestor => [ $searchuser->Name ],
+ Owner => $searchuser,
+ Subject => 'saved search test');
# Now start the search madness.
@@ -67,41 +67,41 @@ my $format = '\' <b><a href="/Ticket/Display.html?id=__id__">__id__</a></b>/TI
my $mysearch = RT::SavedSearch->new($curruser);
($ret, $msg) = $mysearch->Save(Privacy => 'RT::User-' . $searchuser->Id,
- Type => 'Ticket',
- Name => 'owned by me',
- SearchParams => {'Format' => $format,
- 'Query' => "Owner = '"
- . $searchuser->Name
- . "'"});
+ Type => 'Ticket',
+ Name => 'owned by me',
+ SearchParams => {'Format' => $format,
+ 'Query' => "Owner = '"
+ . $searchuser->Name
+ . "'"});
ok($ret, "mysearch was created");
my $groupsearch = RT::SavedSearch->new($curruser);
($ret, $msg) = $groupsearch->Save(Privacy => 'RT::Group-' . $ingroup->Id,
- Type => 'Ticket',
- Name => 'search queue',
- SearchParams => {'Format' => $format,
- 'Query' => "Queue = '"
- . $queue->Name . "'"});
+ Type => 'Ticket',
+ Name => 'search queue',
+ SearchParams => {'Format' => $format,
+ 'Query' => "Queue = '"
+ . $queue->Name . "'"});
ok($ret, "groupsearch was created");
my $othersearch = RT::SavedSearch->new($curruser);
($ret, $msg) = $othersearch->Save(Privacy => 'RT::Group-' . $outgroup->Id,
- Type => 'Ticket',
- Name => 'searchuser requested',
- SearchParams => {'Format' => $format,
- 'Query' =>
- "Requestor.Name LIKE 'search'"});
+ Type => 'Ticket',
+ Name => 'searchuser requested',
+ SearchParams => {'Format' => $format,
+ 'Query' =>
+ "Requestor.Name LIKE 'search'"});
ok(!$ret, "othersearch NOT created");
like($msg, qr/Failed to load object for/, "...for the right reason");
$othersearch = RT::SavedSearch->new(RT->SystemUser);
($ret, $msg) = $othersearch->Save(Privacy => 'RT::Group-' . $outgroup->Id,
- Type => 'Ticket',
- Name => 'searchuser requested',
- SearchParams => {'Format' => $format,
- 'Query' =>
- "Requestor.Name LIKE 'search'"});
+ Type => 'Ticket',
+ Name => 'searchuser requested',
+ SearchParams => {'Format' => $format,
+ 'Query' =>
+ "Requestor.Name LIKE 'search'"});
ok($ret, "othersearch created by systemuser");
# Now try to load some searches.
@@ -148,8 +148,8 @@ warning_like {
isnt($loadedsearch4->Id, $othersearch->Id, "Did not load othersearch");
# Try to update an existing search.
-$loadedsearch1->Update( SearchParams => {'Format' => $format,
- 'Query' => "Queue = '" . $queue->Name . "'" } );
+$loadedsearch1->Update( SearchParams => {'Format' => $format,
+ 'Query' => "Queue = '" . $queue->Name . "'" } );
like($loadedsearch1->GetParameter('Query'), qr/Queue/,
"Updated mysearch parameter");
is($loadedsearch1->Type, 'Ticket', "mysearch is still for tickets");
@@ -162,8 +162,8 @@ like($mysearch->GetParameter('Query'), qr/Queue/, "other mysearch object updated
my $genericsearch = RT::SavedSearch->new($curruser);
$genericsearch->Save(Name => 'generic search',
- Type => 'all',
- SearchParams => {'Query' => "Queue = 'General'"});
+ Type => 'all',
+ SearchParams => {'Query' => "Queue = 'General'"});
my $ticketsearches = RT::SavedSearches->new($curruser);
$ticketsearches->LimitToPrivacy('RT::User-'.$curruser->Id, 'Ticket');
diff --git a/rt/t/api/scrip.t b/rt/t/api/scrip.t
index eb543476b..d6019a082 100644
--- a/rt/t/api/scrip.t
+++ b/rt/t/api/scrip.t
@@ -1,52 +1,44 @@
use strict;
use warnings;
-use RT;
-use RT::Test tests => 25;
+use RT::Test;
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+ok $queue && $queue->id, 'loaded or created queue';
+note 'basic scrips functionality test: create+execute';
{
-
-ok (require RT::Scrip);
-
-
-my $q = RT::Queue->new(RT->SystemUser);
-$q->Create(Name => 'ScripTest');
-ok($q->Id, "Created a scriptest queue");
-
-my $s1 = RT::Scrip->new(RT->SystemUser);
-my ($val, $msg) =$s1->Create( Queue => $q->Id,
- ScripAction => 'User Defined',
- ScripCondition => 'User Defined',
- CustomIsApplicableCode => 'if ($self->TicketObj->Subject =~ /fire/) { return (1);} else { return(0)}',
- CustomPrepareCode => 'return 1',
- CustomCommitCode => '$self->TicketObj->SetPriority("87");',
- Template => 'Blank'
+ my $s1 = RT::Scrip->new(RT->SystemUser);
+ my ($val, $msg) = $s1->Create(
+ Queue => $queue->Id,
+ ScripAction => 'User Defined',
+ ScripCondition => 'User Defined',
+ CustomIsApplicableCode => '$self->TicketObj->Subject =~ /fire/? 1 : 0',
+ CustomPrepareCode => 'return 1',
+ CustomCommitCode => '$self->TicketObj->SetPriority("87");',
+ Template => 'Blank'
);
-ok($val,$msg);
-
-my $ticket = RT::Ticket->new(RT->SystemUser);
-my ($tv,$ttv,$tm) = $ticket->Create(Queue => $q->Id,
- Subject => "hair on fire",
- );
-ok($tv, $tm);
-
-is ($ticket->Priority , '87', "Ticket priority is set right");
-
-
-my $ticket2 = RT::Ticket->new(RT->SystemUser);
-my ($t2v,$t2tv,$t2m) = $ticket2->Create(Queue => $q->Id,
- Subject => "hair in water",
- );
-ok($t2v, $t2m);
-
-isnt ($ticket2->Priority , '87', "Ticket priority is set right");
+ ok($val,$msg);
+ my $ticket = RT::Ticket->new(RT->SystemUser);
+ my ($tv,$ttv,$tm) = $ticket->Create(
+ Queue => $queue->Id,
+ Subject => "hair on fire",
+ );
+ ok($tv, $tm);
+ is ($ticket->Priority , '87', "Ticket priority is set right");
+ my $ticket2 = RT::Ticket->new(RT->SystemUser);
+ my ($t2v,$t2tv,$t2m) = $ticket2->Create(
+ Queue => $queue->Id,
+ Subject => "hair in water",
+ );
+ ok($t2v, $t2m);
+ isnt ($ticket2->Priority , '87', "Ticket priority is set right");
}
-
+note 'modify properties of a scrip';
{
my $scrip = RT::Scrip->new($RT::SystemUser);
my ( $val, $msg ) = $scrip->Create(
@@ -117,3 +109,158 @@ isnt ($ticket2->Priority , '87', "Ticket priority is set right");
ok( $scrip->Delete, 'delete the scrip' );
}
+
+my $queue_B = RT::Test->load_or_create_queue( Name => 'B' );
+ok $queue_B && $queue_B->id, 'loaded or created queue';
+
+note 'check creation errors vs. templates';
+{
+ my $scrip = RT::Scrip->new(RT->SystemUser);
+ my ($status, $msg) = $scrip->Create(
+ Queue => $queue->id,
+ ScripAction => 'User Defined',
+ ScripCondition => 'User Defined',
+ Template => 'not exist',
+ );
+ ok(!$status, "couldn't create scrip, not existing template");
+
+ ($status, $msg) = $scrip->Create(
+ ScripAction => 'User Defined',
+ ScripCondition => 'User Defined',
+ Template => 'not exist',
+ );
+ ok(!$status, "couldn't create scrip, not existing template");
+
+ ($status, $msg) = $scrip->Create(
+ Queue => $queue->id,
+ ScripAction => 'User Defined',
+ ScripCondition => 'User Defined',
+ Template => 54321,
+ );
+ ok(!$status, "couldn't create scrip, not existing template");
+
+ ($status, $msg) = $scrip->Create(
+ ScripAction => 'User Defined',
+ ScripCondition => 'User Defined',
+ Template => 54321,
+ );
+ ok(!$status, "couldn't create scrip, not existing template");
+
+ my $template = RT::Template->new( RT->SystemUser );
+ ($status, $msg) = $template->Create( Queue => $queue->id, Name => 'bar' );
+ ok $status, 'created a template';
+
+ ($status, $msg) = $scrip->Create(
+ ScripAction => 'User Defined',
+ ScripCondition => 'User Defined',
+ Template => $template->id,
+ );
+ ok(!$status, "couldn't create scrip, wrong template");
+
+ ($status, $msg) = $scrip->Create(
+ Queue => $queue_B->id,
+ ScripAction => 'User Defined',
+ ScripCondition => 'User Defined',
+ Template => $template->id,
+ );
+ ok(!$status, "couldn't create scrip, wrong template");
+}
+
+note 'check applications vs. templates';
+{
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($status, $msg) = $template->Create( Queue => $queue->id, Name => 'foo' );
+ ok $status, 'created a template';
+
+ my $scrip = RT::Scrip->new(RT->SystemUser);
+ ($status, $msg) = $scrip->Create(
+ Queue => $queue->Id,
+ ScripAction => 'User Defined',
+ ScripCondition => 'User Defined',
+ Template => 'foo',
+ CustomIsApplicableCode => "1;",
+ CustomPrepareCode => "1;",
+ CustomCommitCode => "1;",
+ );
+ ok($status, 'created a scrip') or diag "error: $msg";
+ RT::Test->object_scrips_are($scrip, [$queue], [0, $queue_B]);
+
+ ($status, $msg) = $scrip->AddToObject( $queue_B->id );
+ ok(!$status, $msg);
+ RT::Test->object_scrips_are($scrip, [$queue], [0, $queue_B]);
+ my $obj_scrip = RT::ObjectScrip->new( RT->SystemUser );
+ ok($obj_scrip->LoadByCols( Scrip => $scrip->id, ObjectId => $queue->id ));
+ is($obj_scrip->Stage, 'TransactionCreate');
+ is($obj_scrip->FriendlyStage, 'Normal');
+
+ $template = RT::Template->new( RT->SystemUser );
+ ($status, $msg) = $template->Create( Queue => $queue_B->id, Name => 'foo' );
+ ok $status, 'created a template';
+
+ ($status, $msg) = $scrip->AddToObject( $queue_B->id );
+ ok($status, 'added scrip to another queue');
+ RT::Test->object_scrips_are($scrip, [$queue, $queue_B], [0]);
+
+ ($status, $msg) = $scrip->RemoveFromObject( $queue_B->id );
+ ok($status, 'removed scrip from queue');
+
+ ($status, $msg) = $template->Delete;
+ ok $status, 'deleted template foo in queue B';
+
+ ($status, $msg) = $scrip->AddToObject( $queue_B->id );
+ ok(!$status, $msg);
+ RT::Test->object_scrips_are($scrip, [$queue], [0, $queue_B]);
+
+ ($status, $msg) = $template->Create( Queue => 0, Name => 'foo' );
+ ok $status, 'created a global template';
+
+ ($status, $msg) = $scrip->AddToObject( $queue_B->id );
+ ok($status, 'added scrip');
+ RT::Test->object_scrips_are($scrip, [$queue, $queue_B], [0]);
+}
+
+note 'basic check for disabling scrips';
+{
+ my $scrip = RT::Scrip->new(RT->SystemUser);
+ my ($status, $msg) = $scrip->Create(
+ Queue => $queue->id,
+ ScripCondition => 'On Create',
+ ScripAction => 'User Defined',
+ CustomPrepareCode => 'return 1',
+ CustomCommitCode => '$self->TicketObj->SetPriority("87"); return 1',
+ Template => 'Blank'
+ );
+ ok($status, "created scrip");
+ is($scrip->Disabled, 0, "not disabled");
+
+ {
+ my $ticket = RT::Ticket->new(RT->SystemUser);
+ my ($tid, undef, $msg) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => "test",
+ );
+ ok($tid, "created ticket") or diag "error: $msg";
+ is ($ticket->Priority , '87', "Ticket priority is set right");
+ }
+
+ ($status,$msg) = $scrip->SetDisabled(1);
+ is($scrip->Disabled, 1, "disabled");
+
+ {
+ my $ticket = RT::Ticket->new(RT->SystemUser);
+ my ($tid, undef, $msg) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => "test",
+ );
+ ok($tid, "created ticket") or diag "error: $msg";
+ isnt ($ticket->Priority , '87', "Ticket priority is set right");
+ }
+
+ is($scrip->FriendlyStage('TransactionCreate'), 'Normal',
+ 'Correct stage wording for TransactionCreate');
+ is($scrip->FriendlyStage('TransactionBatch'), 'Batch',
+ 'Correct stage wording for TransactionBatch');
+ RT->Config->Set('UseTransactionBatch', 0);
+ is($scrip->FriendlyStage('TransactionBatch'), 'Batch (disabled by config)',
+ 'Correct stage wording for TransactionBatch with UseTransactionBatch disabled');
+}
diff --git a/rt/t/api/scrip_order.t b/rt/t/api/scrip_order.t
index 6fba7e579..689b55970 100644
--- a/rt/t/api/scrip_order.t
+++ b/rt/t/api/scrip_order.t
@@ -2,52 +2,271 @@
use strict;
use warnings;
-use RT;
-use RT::Test tests => 7;
-
-
-
-my $scrip_queue = RT::Queue->new(RT->SystemUser);
-my ($queue_id, $msg) = $scrip_queue->Create( Name => "ScripOrdering-$$",
- Description => 'Test scrip ordering by description' );
-ok($queue_id, "Created scrip-ordering test queue? ".$msg);
-
-my $priority_ten_scrip = RT::Scrip->new(RT->SystemUser);
-(my $id, $msg) = $priority_ten_scrip->Create(
- Description => "10 set priority $$",
- Queue => $queue_id,
- ScripCondition => 'On Create',
- ScripAction => 'User Defined',
- CustomPrepareCode => '$RT::Logger->debug("Setting priority to 10..."); return 1;',
- CustomCommitCode => '$self->TicketObj->SetPriority(10);',
- Template => 'Blank',
- Stage => 'TransactionCreate',
-);
-ok($id, "Created priority-10 scrip? ".$msg);
-
-my $priority_five_scrip = RT::Scrip->new(RT->SystemUser);
-($id, $msg) = $priority_ten_scrip->Create(
- Description => "05 set priority $$",
- Queue => $queue_id,
- ScripCondition => 'On Create',
- ScripAction => 'User Defined',
- CustomPrepareCode => '$RT::Logger->debug("Setting priority to 5..."); return 1;',
- CustomCommitCode => '$self->TicketObj->SetPriority(5);',
- Template => 'Blank',
- Stage => 'TransactionCreate',
-);
-ok($id, "Created priority-5 scrip? ".$msg);
-
-my $ticket = RT::Ticket->new(RT->SystemUser);
-($id, $msg) = $ticket->Create(
- Queue => $queue_id,
- Requestor => 'order@example.com',
- Subject => "Scrip order test $$",
-);
-ok($ticket->id, "Created ticket? id=$id");
-
-isnt($ticket->Priority , 0, "Ticket shouldn't be priority 0");
-isnt($ticket->Priority , 5, "Ticket shouldn't be priority 5");
-is ($ticket->Priority , 10, "Ticket should be priority 10");
+use RT::Test tests => 204;
+
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+ok $queue && $queue->id, 'loaded or created queue';
+
+note "check that execution order reflects sort order";
+{
+ my $ten = main->create_scrip_ok(
+ Description => "Set priority to 10",
+ Queue => $queue->id,
+ CustomCommitCode => '$self->TicketObj->SetPriority(10);',
+ );
+
+ my $five = main->create_scrip_ok(
+ Description => "Set priority to 5",
+ Queue => $queue->id,
+ CustomCommitCode => '$self->TicketObj->SetPriority(5);',
+ );
+
+ my $ticket = RT::Ticket->new(RT->SystemUser);
+ my ($id, $msg) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => "Scrip order test $$",
+ );
+ ok($ticket->id, "Created ticket? id=$id");
+ is($ticket->Priority , 5, "By default newer scrip is last");
+
+ main->move_scrip_ok( $five, $queue->id, 'up' );
+
+ $ticket = RT::Ticket->new(RT->SystemUser);
+ ($id, $msg) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => "Scrip order test $$",
+ );
+ ok($ticket->id, "Created ticket? id=$id");
+ is($ticket->Priority , 10, "Moved scrip and result is different");
+}
+
+my $queue_B = RT::Test->load_or_create_queue( Name => 'Other' );
+ok $queue_B && $queue_B->id, 'loaded or created queue';
+
+note "move around two local scrips";
+{
+ main->delete_all_scrips();
+
+ my @scrips;
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ main->check_scrips_order(\@scrips, [$queue]);
+
+ main->move_scrip_ok( $scrips[0], $queue->id, 'down' );
+ @scrips = @scrips[1, 0];
+ main->check_scrips_order(\@scrips, [$queue]);
+
+ main->move_scrip_ok( $scrips[0], $queue->id, 'down' );
+ @scrips = @scrips[1, 0];
+ main->check_scrips_order(\@scrips, [$queue]);
+
+ main->move_scrip_ok( $scrips[1], $queue->id, 'up' );
+ @scrips = @scrips[1, 0];
+ main->check_scrips_order(\@scrips, [$queue]);
+
+ main->move_scrip_ok( $scrips[1], $queue->id, 'up' );
+ @scrips = @scrips[1, 0];
+ main->check_scrips_order(\@scrips, [$queue]);
+}
+
+note "move around two global scrips";
+{
+ main->delete_all_scrips();
+
+ my @scrips;
+ push @scrips, main->create_scrip_ok( Queue => 0 );
+ push @scrips, main->create_scrip_ok( Queue => 0 );
+ main->check_scrips_order(\@scrips, [$queue]);
+
+ main->move_scrip_ok( $scrips[0], 0, 'down' );
+ @scrips = @scrips[1, 0];
+ main->check_scrips_order(\@scrips, [$queue]);
+
+ main->move_scrip_ok( $scrips[0], 0, 'down' );
+ @scrips = @scrips[1, 0];
+ main->check_scrips_order(\@scrips, [$queue]);
+
+ main->move_scrip_ok( $scrips[1], 0, 'up' );
+ @scrips = @scrips[1, 0];
+ main->check_scrips_order(\@scrips, [$queue]);
+
+ main->move_scrip_ok( $scrips[1], 0, 'up' );
+ @scrips = @scrips[1, 0];
+ main->check_scrips_order(\@scrips, [$queue]);
+}
+
+note "move local scrip below global";
+{
+ main->delete_all_scrips();
+ my @scrips;
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+ push @scrips, main->create_scrip_ok( Queue => 0 );
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+
+ main->move_scrip_ok( $scrips[0], $queue->id, 'down' );
+ @scrips = @scrips[1, 2, 0, 3];
+ main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+}
+
+note "move local scrip above global";
+{
+ main->delete_all_scrips();
+ my @scrips;
+ push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+ push @scrips, main->create_scrip_ok( Queue => 0 );
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+ main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+
+ main->move_scrip_ok( $scrips[-1], $queue_B->id, 'up' );
+ @scrips = @scrips[0, 3, 1, 2];
+ main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+}
+
+note "move global scrip down with local in between";
+{
+ main->delete_all_scrips();
+ my @scrips;
+ push @scrips, main->create_scrip_ok( Queue => 0 );
+ push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ push @scrips, main->create_scrip_ok( Queue => 0 );
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+
+ main->move_scrip_ok( $scrips[0], 0, 'down' );
+ @scrips = @scrips[1, 2, 3, 0, 4];
+ main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+}
+
+note "move global scrip up with local in between";
+{
+ main->delete_all_scrips();
+ my @scrips;
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ push @scrips, main->create_scrip_ok( Queue => 0 );
+ push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ push @scrips, main->create_scrip_ok( Queue => 0 );
+ main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+
+ main->move_scrip_ok( $scrips[-1], 0, 'up' );
+ @scrips = @scrips[0, 4, 1, 2, 3];
+ main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+}
+
+note "delete scrips one by one";
+{
+ main->delete_all_scrips();
+ my @scrips;
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+ push @scrips, main->create_scrip_ok( Queue => 0 );
+ push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+ push @scrips, main->create_scrip_ok( Queue => $queue->id );
+ push @scrips, main->create_scrip_ok( Queue => 0 );
+ main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+
+ foreach my $idx (3, 2, 0 ) {
+ $_->Delete foreach splice @scrips, $idx, 1;
+ main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+ }
+}
+
+sub create_scrip_ok {
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+ my $self = shift;
+ my %args = (
+ ScripCondition => 'On Create',
+ ScripAction => 'User Defined',
+ CustomPrepareCode => 'return 1',
+ CustomCommitCode => 'return 1',
+ Template => 'Blank',
+ Stage => 'TransactionCreate',
+ @_
+ );
+
+ my $scrip = RT::Scrip->new( RT->SystemUser );
+ my ($id, $msg) = $scrip->Create( %args );
+ ok($id, "Created scrip") or diag "error: $msg";
+
+ return $scrip;
+}
+
+sub delete_all_scrips {
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+ my $self = shift;
+ my $scrips = RT::Scrips->new( RT->SystemUser );
+ $scrips->UnLimit;
+ $_->Delete foreach @{ $scrips->ItemsArrayRef };
+}
+
+sub move_scrip_ok {
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+ my $self = shift;
+ my ($scrip, $queue, $dir) = @_;
+
+ my $rec = RT::ObjectScrip->new( RT->SystemUser );
+ $rec->LoadByCols( Scrip => $scrip->id, ObjectId => $queue );
+ ok $rec->id, 'found application of the scrip';
+
+ my $method = 'Move'. ucfirst lc $dir;
+ my ($status, $msg) = $rec->$method();
+ ok $status, "moved scrip $dir" or diag "error: $msg";
+}
+
+sub check_scrips_order {
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+ my $self = shift;
+ my $scrips = shift;
+ my $queues = shift;
+
+ foreach my $qid ( 0, map $_->id, @$queues ) {
+ my $list = RT::Scrips->new( RT->SystemUser );
+ $list->LimitToGlobal;
+ $list->LimitToQueue( $qid ) if $qid;
+ $list->ApplySortOrder;
+ is_deeply(
+ [map $_->id, @{ $list->ItemsArrayRef } ],
+ [map $_->id, grep $_->IsAdded( $qid ) || $_->IsGlobal, @$scrips],
+ 'list of scrips match expected'
+ )
+ }
+
+ foreach my $qid ( map $_->id, @$queues ) {
+ my $list = RT::ObjectScrips->new( RT->SystemUser );
+ $list->LimitToObjectId( 0 );
+ $list->LimitToObjectId( $qid );
+
+ my %so;
+ $so{ $_->SortOrder }++ foreach @{ $list->ItemsArrayRef };
+ ok( !grep( {$_ != 1} values %so), 'no dublicate order' );
+ }
+ {
+ my $list = RT::ObjectScrips->new( RT->SystemUser );
+ $list->UnLimit;
+ $list->OrderBy( FIELD => 'SortOrder', ORDER => 'ASC' );
+
+ my $prev;
+ foreach my $rec ( @{ $list->ItemsArrayRef } ) {
+ my $so = $rec->SortOrder;
+ do { $prev = $so; next } unless defined $prev;
+
+ ok $so == $prev || $so == $prev+1, "sequential order";
+ $prev = $so;
+ }
+ }
+}
+
+sub dump_sort_order {
+
+ diag " id oid so";
+ diag join "\n", map { join "\t", @$_ } map @$_, $RT::Handle->dbh->selectall_arrayref(
+ "select Scrip, ObjectId, SortOrder from ObjectScrips ORDER BY SortOrder"
+ );
+
+}
diff --git a/rt/t/api/searchbuilder.t b/rt/t/api/searchbuilder.t
index 84568718d..9237dcd37 100644
--- a/rt/t/api/searchbuilder.t
+++ b/rt/t/api/searchbuilder.t
@@ -20,7 +20,7 @@ ok( $queues->UnLimit(),'Unlimited the result set of the queues object');
my $items = $queues->ItemsArrayRef();
my @items = @{$items};
-ok($queues->NewItem->_Accessible('Name','read'));
+ok($queues->RecordClass->_Accessible('Name','read'));
my @sorted = sort {lc($a->Name) cmp lc($b->Name)} @items;
ok (@sorted, "We have an array of queues, sorted". join(',',map {$_->Name} @sorted));
diff --git a/rt/t/api/system-available-rights.t b/rt/t/api/system-available-rights.t
new file mode 100644
index 000000000..d7b6f5e8d
--- /dev/null
+++ b/rt/t/api/system-available-rights.t
@@ -0,0 +1,65 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+use Set::Tiny;
+
+my @warnings;
+local $SIG{__WARN__} = sub {
+ push @warnings, "@_";
+};
+
+my $requestor = RT::Group->new( RT->SystemUser );
+$requestor->LoadRoleGroup(
+ Object => RT->System,
+ Name => "Requestor",
+);
+ok $requestor->id, "Loaded global requestor role group";
+
+$requestor = $requestor->PrincipalObj;
+ok $requestor->id, "Loaded global requestor role group principal";
+
+note "Try granting an article right to a system role group";
+{
+ my ($ok, $msg) = $requestor->GrantRight(
+ Right => "ShowArticle",
+ Object => RT->System,
+ );
+ ok !$ok, "Couldn't grant nonsensical right to global Requestor role: $msg";
+ like shift @warnings, qr/Couldn't validate right name.*?ShowArticle/;
+
+ ($ok, $msg) = $requestor->GrantRight(
+ Right => "ShowTicket",
+ Object => RT->System,
+ );
+ ok $ok, "Granted queue right to global queue role: $msg";
+
+ ($ok, $msg) = RT->PrivilegedUsers->PrincipalObj->GrantRight(
+ Right => "ShowArticle",
+ Object => RT->System,
+ );
+ ok $ok, "Granted article right to non-role global group: $msg";
+
+ reset_rights();
+}
+
+note "AvailableRights";
+{
+ my @available = (
+ [ keys %{RT->System->AvailableRights} ],
+ [ keys %{RT->System->AvailableRights( $requestor )} ],
+ );
+
+ my $all = Set::Tiny->new( @{$available[0]} );
+ my $role = Set::Tiny->new( @{$available[1]} );
+
+ ok $role->is_proper_subset($all), "role rights are a proper subset of all";
+}
+
+ok !@warnings, "No uncaught warnings"
+ or diag explain \@warnings;
+
+# for clarity
+sub reset_rights { RT::Test->set_rights() }
+
+done_testing;
diff --git a/rt/t/api/system.t b/rt/t/api/system.t
index f1100d332..f3d9226cf 100644
--- a/rt/t/api/system.t
+++ b/rt/t/api/system.t
@@ -2,8 +2,18 @@
use strict;
use warnings;
use RT;
-use RT::Test nodata => 1, tests => 7;
+use RT::Test nodata => 1, tests => 16;
+BEGIN{
+ use_ok('RT::System');
+}
+
+# Skipping most of the methods added just to make RT::System
+# look like RT::Record.
+
+can_ok('RT::System', qw( AvailableRights RightCategories AddRight
+ id Id SubjectTag Name QueueCacheNeedsUpdate AddUpgradeHistory
+ UpgradeHistory ));
{
@@ -15,18 +25,50 @@ ok ($rights->{'CreateTicket'},"CreateTicket right found");
ok ($rights->{'AdminGroupMembership'},"ModifyGroupMembers right found");
ok (!$rights->{'CasdasdsreateTicket'},"bogus right not found");
+}
+{
+my $sys = RT::System->new();
+is( $sys->Id, 1, 'Id is 1');
+is ($sys->id, 1, 'id is 1');
}
{
-use RT::System;
-my $sys = RT::System->new();
-is( $sys->Id, 1);
-is ($sys->id, 1);
+# Test upgrade history methods.
+my $sys = RT::System->new(RT->SystemUser);
+isa_ok($sys, 'RT::System');
-}
+my $file = 'test_file.txt';
+my $content = 'Some file contents.';
+my $upgrade_history = RT->System->UpgradeHistory();
+
+is( keys %$upgrade_history, 0, 'No history in test DB');
+
+RT->System->AddUpgradeHistory(RT =>{
+ action => 'insert',
+ filename => $file,
+ content => $content,
+ stage => 'before',
+ });
+
+$upgrade_history = RT->System->UpgradeHistory();
+ok( exists($upgrade_history->{'RT'}), 'History has an RT key.');
+is( @{$upgrade_history->{'RT'}}, 1, '1 item in history array');
+is($upgrade_history->{RT}[0]{stage}, 'before', 'stage is before for item 1');
+RT->System->AddUpgradeHistory(RT =>{
+ action => 'insert',
+ filename => $file,
+ content => $content,
+ stage => 'after',
+ });
+
+$upgrade_history = RT->System->UpgradeHistory();
+is( @{$upgrade_history->{'RT'}}, 2, '2 item in history array');
+is($upgrade_history->{RT}[1]{stage}, 'after', 'stage is after for item 2');
+
+}
diff --git a/rt/t/api/template.t b/rt/t/api/template.t
index 331d9f996..1baac44b6 100644
--- a/rt/t/api/template.t
+++ b/rt/t/api/template.t
@@ -2,23 +2,31 @@
use warnings;
use strict;
-use RT;
-use RT::Test tests => 10;
+use RT::Test tests => 37;
+
+use_ok('RT::Template');
my $queue = RT::Test->load_or_create_queue( Name => 'Templates' );
ok $queue && $queue->id, "loaded or created a queue";
+my $alt_queue = RT::Test->load_or_create_queue( Name => 'Alternative' );
+ok $alt_queue && $alt_queue->id, 'loaded or created queue';
+
{
- my $template = RT::Template->new( RT->SystemUser );
+ my $template = RT::Template->new(RT->SystemUser);
isa_ok($template, 'RT::Template');
+}
+
+{
+ my $template = RT::Template->new( RT->SystemUser );
my ($val,$msg) = $template->Create(
Queue => $queue->id,
- Name => 'InsertTest',
+ Name => 'Test',
Content => 'This is template content'
);
ok $val, "created a template" or diag "error: $msg";
ok my $id = $template->id, "id is defined";
- is $template->Name, 'InsertTest';
+ is $template->Name, 'Test';
is $template->Content, 'This is template content', "We created the object right";
($val, $msg) = $template->SetContent( 'This is new template content');
@@ -32,3 +40,164 @@ ok $queue && $queue->id, "loaded or created a queue";
$template->Load($id);
ok !$template->id, "can not load template after deletion";
}
+
+note "can not create template w/o Name";
+{
+ clean_templates( Queue => $queue->id );
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($val,$msg) = $template->Create( Queue => $queue->id );
+ ok(!$val,$msg);
+}
+
+note "can not create template with duplicate name";
+{
+ clean_templates( Queue => $queue->id );
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' );
+ ok($val,$msg);
+
+ ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' );
+ ok(!$val,$msg);
+}
+
+note "change template's name";
+{
+ clean_templates( Queue => $queue->id );
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' );
+ ok($val,$msg);
+
+ ($val,$msg) = $template->SetName( 'Some' );
+ ok($val,$msg);
+ is $template->Name, 'Some';
+}
+
+note "can not change name to empty";
+{
+ clean_templates( Queue => $queue->id );
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' );
+ ok($val,$msg);
+
+ ($val,$msg) = $template->Create( Queue => $queue->id, Name => '' );
+ ok(!$val,$msg);
+ ($val,$msg) = $template->Create( Queue => $queue->id, Name => undef );
+ ok(!$val,$msg);
+}
+
+note "can not change name to duplicate";
+{
+ clean_templates( Queue => $queue->id );
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' );
+ ok($val,$msg);
+
+ ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Some' );
+ ok($val,$msg);
+}
+
+note "changing queue of template is not implemented";
+{
+ clean_templates( Queue => $queue->id );
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' );
+ ok($val,$msg);
+
+ ($val,$msg) = $template->SetQueue( $alt_queue->id );
+ ok(!$val,$msg);
+}
+
+note "make sure template can not be deleted if it has scrips";
+{
+ clean_templates( Queue => $queue->id );
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Test' );
+ ok($val,$msg);
+
+ my $scrip = RT::Scrip->new( RT->SystemUser );
+ ($val,$msg) = $scrip->Create(
+ Queue => $queue->id,
+ ScripCondition => "On Create",
+ ScripAction => 'Autoreply To Requestors',
+ Template => $template->Name,
+ );
+ ok($val, $msg);
+
+ ($val, $msg) = $template->Delete;
+ ok(!$val,$msg);
+}
+
+note "make sure template can be deleted if it's an override";
+{
+ clean_templates( Queue => $queue->id );
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Overrided' );
+ ok($val,$msg);
+
+ $template = RT::Template->new( RT->SystemUser );
+ ($val,$msg) = $template->Create( Queue => 0, Name => 'Overrided' );
+ ok($val,$msg);
+
+ my $scrip = RT::Scrip->new( RT->SystemUser );
+ ($val,$msg) = $scrip->Create(
+ Queue => $queue->id,
+ ScripCondition => "On Create",
+ ScripAction => 'Autoreply To Requestors',
+ Template => $template->Name,
+ );
+ ok($val, $msg);
+
+ ($val, $msg) = $template->Delete;
+ ok($val,$msg);
+}
+
+note "make sure template can be deleted if it has an override";
+{
+ clean_templates( Queue => $queue->id );
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($val,$msg) = $template->Create( Queue => 0, Name => 'Overrided' );
+ ok($val,$msg);
+
+ $template = RT::Template->new( RT->SystemUser );
+ ($val,$msg) = $template->Create( Queue => $queue->id, Name => 'Overrided' );
+ ok($val,$msg);
+
+ my $scrip = RT::Scrip->new( RT->SystemUser );
+ ($val,$msg) = $scrip->Create(
+ Queue => $queue->id,
+ ScripCondition => "On Create",
+ ScripAction => 'Autoreply To Requestors',
+ Template => $template->Name,
+ );
+ ok($val, $msg);
+
+ ($val, $msg) = $template->Delete;
+ ok($val,$msg);
+}
+
+
+{
+ my $t = RT::Template->new(RT->SystemUser);
+ $t->Create(Name => "Foo", Queue => $queue->id);
+ my $t2 = RT::Template->new(RT->Nobody);
+ $t2->Load($t->Id);
+ ok($t2->QueueObj->id, "Got the template's queue objet");
+}
+
+sub clean_templates {
+ my %args = (@_);
+
+ my $templates = RT::Templates->new( RT->SystemUser );
+ $templates->Limit( FIELD => 'Queue', VALUE => $args{'Queue'} )
+ if defined $args{'Queue'};
+ $templates->Limit( FIELD => 'Name', VALUE => $_ )
+ foreach ref $args{'Name'}? @{$args{'Name'}} : ($args{'Name'}||());
+ while ( my $t = $templates->Next ) {
+ my ($status) = $t->Delete;
+ unless ( $status ) {
+ $_->Delete foreach @{ $t->UsedBy->ItemsArrayRef };
+ $t->Delete;
+ }
+ }
+}
+
diff --git a/rt/t/api/ticket.t b/rt/t/api/ticket.t
index da287a607..c5f1e240f 100644
--- a/rt/t/api/ticket.t
+++ b/rt/t/api/ticket.t
@@ -100,7 +100,7 @@ ok( $t->Create(Queue => 'General', Due => '2002-05-21 00:00:00', ReferredToBy =>
ok ( my $id = $t->Id, "Got ticket id");
like ($t->RefersTo->First->Target , qr/fsck.com/, "Got refers to");
like ($t->ReferredToBy->First->Base , qr/cpan.org/, "Got referredtoby");
-is ($t->ResolvedObj->Unix, 0, "It hasn't been resolved - ". $t->ResolvedObj->Unix);
+ok (!$t->ResolvedObj->IsSet, "It hasn't been resolved");
}
@@ -115,8 +115,7 @@ my ($id, $msg) = $ticket->Create(Subject => "Foo",
Queue => '1'
);
ok ($id, "Ticket $id was created");
-ok(my $group = RT::Group->new(RT->SystemUser));
-ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Requestor'));
+ok(my $group = $ticket->RoleGroup('Requestor'));
ok ($group->Id, "Found the requestors object for this ticket");
ok(my $jesse = RT::User->new(RT->SystemUser), "Creating a jesse rt::user");
@@ -135,14 +134,11 @@ ok ( ($add_id, $add_msg) = $ticket->DeleteWatcher(Type =>'Requestor', Email => '
ok (!$ticket->IsWatcher(Type => 'Requestor', PrincipalId => $bob->PrincipalId), "The ticket no longer has bob at fsck.com as a requestor");
-$group = RT::Group->new(RT->SystemUser);
-ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Cc'));
+$group = $ticket->RoleGroup('Cc');
ok ($group->Id, "Found the cc object for this ticket");
-$group = RT::Group->new(RT->SystemUser);
-ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'AdminCc'));
+$group = $ticket->RoleGroup('AdminCc');
ok ($group->Id, "Found the AdminCc object for this ticket");
-$group = RT::Group->new(RT->SystemUser);
-ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Owner'));
+$group = $ticket->RoleGroup('Owner');
ok ($group->Id, "Found the Owner object for this ticket");
ok($group->HasMember(RT->SystemUser->UserObj->PrincipalObj), "the owner group has the member 'RT_System'");
@@ -209,20 +205,32 @@ is ($t1->Requestors->MembersObj->Count, 2);
}
+diag "Test owner changes";
{
my $root = RT::User->new(RT->SystemUser);
$root->Load('root');
ok ($root->Id, "Loaded the root user");
my $t = RT::Ticket->new(RT->SystemUser);
-$t->Load(1);
-$t->SetOwner('root');
+my ($val, $msg) = $t->Create( Subject => 'Owner test 1', Queue => 'General');
+ok( $t->Id, "Created a new ticket with id $val: $msg");
+
+($val, $msg) = $t->SetOwner('bogususer');
+ok( !$val, "Can't set owner to bogus user");
+is( $msg, "That user does not exist", "Got message: $msg");
+
+($val, $msg) = $t->SetOwner('root');
is ($t->OwnerObj->Name, 'root' , "Root owns the ticket");
+
+($val, $msg) = $t->SetOwner('root');
+ok( !$val, "User already owns ticket");
+is( $msg, "That user already owns that ticket", "Got message: $msg");
+
$t->Steal();
is ($t->OwnerObj->id, RT->SystemUser->id , "SystemUser owns the ticket");
my $txns = RT::Transactions->new(RT->SystemUser);
$txns->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$txns->Limit(FIELD => 'ObjectId', VALUE => '1');
+$txns->Limit(FIELD => 'ObjectId', VALUE => $t->Id);
$txns->Limit(FIELD => 'ObjectType', VALUE => 'RT::Ticket');
$txns->Limit(FIELD => 'Type', OPERATOR => '!=', VALUE => 'EmailRecord');
@@ -230,6 +238,37 @@ my $steal = $txns->First;
is($steal->OldValue , $root->Id , "Stolen from root");
is($steal->NewValue , RT->SystemUser->Id , "Stolen by the systemuser");
+ok(my $user1 = RT::User->new(RT->SystemUser), "Creating a user1 rt::user");
+($val, $msg) = $user1->Create(Name => 'User1', EmailAddress => 'user1@example.com');
+ok( $val, "Created new user with id: $val");
+ok( $user1->Id, "Found the user1 rt user");
+
+my $t1 = RT::Ticket->new($user1);
+($val, $msg) = $t1->Load($t->Id);
+ok( $t1->Id, "Loaded ticket with id $val");
+
+($val, $msg) = $t1->SetOwner('root');
+ok( !$val, "user1 can't set owner to root: $msg");
+is ($t->OwnerObj->id, RT->SystemUser->id , "SystemUser still owns ticket " . $t1->Id);
+
+my $queue = RT::Queue->new(RT->SystemUser);
+$queue->Load("General");
+
+($val, $msg) = $user1->PrincipalObj->GrantRight(
+ Object => $queue, Right => 'ModifyTicket'
+ );
+
+($val, $msg) = $t1->SetOwner('root');
+ok( !$val, "With ModifyTicket user1 can't set owner to root: $msg");
+is ($t->OwnerObj->id, RT->SystemUser->id , "SystemUser still owns ticket " . $t1->Id);
+
+($val, $msg) = $user1->PrincipalObj->GrantRight(
+ Object => $queue, Right => 'ReassignTicket'
+ );
+
+($val, $msg) = $t1->SetOwner('root');
+ok( $val, "With ReassignTicket user1 reassigned ticket " . $t1->Id . " to root: $msg");
+is ($t1->OwnerObj->Name, 'root' , "Root owns ticket " . $t1->Id);
}
@@ -250,7 +289,16 @@ like($msg, qr/resolved/i, "Status message is correct");
($id, $msg) = $tt->SetStatus('resolved');
ok(!$id,$msg);
+my $dep = RT::Ticket->new( RT->SystemUser );
+my ( $dep_id, undef, $dep_msg ) = $dep->Create(
+ Queue => 'general',
+ Subject => 'dep ticket',
+ 'DependedOnBy' => $tt->id,
+);
+ok( $dep->id, $dep_msg );
+($id, $msg) = $tt->SetStatus('rejected');
+ok( $id, $msg );
}
diff --git a/rt/t/api/tickets.t b/rt/t/api/tickets.t
index 50d08f756..172965c3b 100644
--- a/rt/t/api/tickets.t
+++ b/rt/t/api/tickets.t
@@ -2,8 +2,7 @@
use strict;
use warnings;
use RT;
-use RT::Test tests => 18;
-
+use RT::Test tests => undef;
{
@@ -114,3 +113,35 @@ ok( $unlimittickets->Count > 0, "UnLimited tickets object should return tickets"
ok $count == 1, "found one ticket";
}
+{
+ my $tickets = RT::Tickets->new( RT->SystemUser );
+ my ($ret, $msg) = $tickets->FromSQL("Resolved IS NULL");
+ ok $ret, "Ran query with IS NULL: $msg";
+ my $count = $tickets->Count();
+ ok $count > 1, "Found more than one ticket";
+ undef $count;
+}
+
+{
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ ok $ticket->Load(1), "Loaded test ticket 1";
+ ok $ticket->SetStatus('resolved'), "Set to resolved";
+
+ my $tickets = RT::Tickets->new( RT->SystemUser );
+ my ($ret, $msg) = $tickets->FromSQL("Resolved IS NOT NULL");
+ ok $ret, "Ran query with IS NOT NULL: $msg";
+ my $count = $tickets->Count();
+ ok $count == 1, "Found one ticket";
+ undef $count;
+}
+
+{
+ my $tickets = RT::Tickets->new( RT->SystemUser );
+ $tickets->LimitDate( FIELD => "Resolved", OPERATOR => "IS", VALUE => "NULL" );
+ $tickets->LimitDate( FIELD => "Resolved", OPERATOR => "IS NOT", VALUE => "NULL" );
+ my $count = $tickets->Count();
+ ok $count > 1, "Found more than one ticket";
+ undef $count;
+}
+
+done_testing;
diff --git a/rt/t/api/txn_content.t b/rt/t/api/txn_content.t
index 392b6a73b..672d6c2e0 100644
--- a/rt/t/api/txn_content.t
+++ b/rt/t/api/txn_content.t
@@ -1,7 +1,7 @@
use warnings;
use strict;
-use RT::Test tests => 3;
+use RT::Test tests => 4;
use MIME::Entity;
my $ticket = RT::Ticket->new(RT->SystemUser);
my $mime = MIME::Entity->build(
@@ -16,4 +16,8 @@ my $txns = $ticket->Transactions;
$txns->Limit( FIELD => 'Type', VALUE => 'Create' );
my $txn = $txns->First;
ok( $txn, 'got Create txn' );
-is( $txn->Content, "this is body\n", "txn's content" );
+
+# ->Content converts from text/html to plain text if we don't explicitly ask
+# for html. Our html -> text converter seems to add an extra trailing newline
+like( $txn->Content, qr/^\s*this is body\s*$/, "txn's html content converted to plain text" );
+is( $txn->Content(Type => 'text/html'), "this is body\n", "txn's html content" );
diff --git a/rt/t/api/user-prefs.t b/rt/t/api/user-prefs.t
new file mode 100644
index 000000000..a4aa49f1b
--- /dev/null
+++ b/rt/t/api/user-prefs.t
@@ -0,0 +1,59 @@
+
+use strict;
+use warnings;
+use RT;
+use RT::Test tests => undef;
+
+use_ok( 'RT::User' );
+
+my $create_user = RT::User->new(RT->SystemUser);
+isa_ok($create_user, 'RT::User');
+my ($ret, $msg) = $create_user->Create(Name => 'CreateTest1'.$$,
+ EmailAddress => $$.'create-test-1@example.com');
+ok ($ret, "Creating user CreateTest1 - " . $msg );
+
+# Create object to operate as the test user
+my $user1 = RT::User->new($create_user);
+($ret, $msg) = $user1->Load($create_user->Id);
+ok ($ret, "Loaded the new user $msg");
+
+diag "Set a search preference";
+my $prefs = {
+ 'Order' => 'DESC|ASC|ASC|ASC',
+ 'OrderBy' => 'Due',
+ 'Format' => '\'<a href="__WebPath__/Ticket/Display.html?id=__id__">__id__</a>/TITLE:#\',
+\'<a href="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a>/TITLE:Subject\',
+\'__Priority__\',
+\'__QueueName__\',
+\'__ExtendedStatus__\',
+\'__Due__\'',
+ 'RowsPerPage' => '50'
+};
+
+ok (!$user1->HasRight( Right => 'ModifySelf', Object => $RT::System), "Can't ModifySelf");
+($ret, $msg) = $user1->SetPreferences("SearchDisplay", $prefs);
+ok( !$ret, "No permission to set preferences");
+ok (($ret, $msg) = $create_user->PrincipalObj->GrantRight( Right => 'ModifySelf'),
+ "Granted ModifySelf");
+($ret, $msg) = $user1->SetPreferences("SearchDisplay", $prefs);
+ok( $ret, "Search preference set");
+
+diag "Fetch preference";
+ok (my $saved_prefs = $user1->Preferences("SearchDisplay"), "Fetched prefs");
+is ($prefs->{OrderBy}, 'Due', "Prefs look ok");
+
+diag "Delete prefs";
+ok (($ret, $msg) = $create_user->PrincipalObj->RevokeRight( Right => 'ModifySelf'),
+ "Revoked ModifySelf");
+($ret, $msg) = $user1->DeletePreferences("SearchDisplay");
+ok( !$ret, "No permission to delete preferences");
+ok (($ret, $msg) = $create_user->PrincipalObj->GrantRight( Right => 'ModifySelf'),
+ "Granted ModifySelf");
+($ret, $msg) = $user1->DeletePreferences("SearchDisplay");
+ok( $ret, "Search preference deleted");
+
+$saved_prefs = $user1->Preferences("SearchDisplay");
+ok (!$saved_prefs, "No saved preferences returned");
+
+done_testing;
+
diff --git a/rt/t/api/user.t b/rt/t/api/user.t
index e6b891f73..94494f162 100644
--- a/rt/t/api/user.t
+++ b/rt/t/api/user.t
@@ -2,7 +2,7 @@
use strict;
use warnings;
use RT;
-use RT::Test tests => 111;
+use RT::Test tests => 122;
{
@@ -106,7 +106,7 @@ ok($user->Privileged, "User 'root' is privileged again");
ok(my $u = RT::User->new(RT->SystemUser));
ok($u->Load(1), "Loaded the first user");
-is($u->PrincipalObj->ObjectId , 1, "user 1 is the first principal");
+is($u->PrincipalObj->id , 1, "user 1 is the first principal");
is($u->PrincipalObj->PrincipalType, 'User' , "Principal 1 is a user, not a group");
@@ -335,3 +335,30 @@ ok($rqv, "Revoked the right successfully - $rqm");
}
+{
+ my $root = RT::Test->load_or_create_user( Name => 'root' );
+ ok $root && $root->id;
+
+ my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+ ok $queue && $queue->id;
+
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ my ($id) = $ticket->Create( Subject => 'test', Queue => $queue );
+ ok $id;
+
+ my $b_ticket = RT::Ticket->new( RT->SystemUser );
+ ($id) = $b_ticket->Create( Subject => 'test', Queue => $queue );
+ ok $id;
+
+ ok $root->ToggleBookmark($b_ticket);
+ ok !$root->ToggleBookmark($b_ticket);
+ ok $root->ToggleBookmark($b_ticket);
+
+ ok $root->HasBookmark( $b_ticket );
+ ok !$root->HasBookmark( $ticket );
+
+ my @marks = $root->Bookmarks;
+ is scalar @marks, 1;
+ is $marks[0], $b_ticket->id;
+}
+
diff --git a/rt/t/api/users.t b/rt/t/api/users.t
index 1f3a48770..e65f9a9b5 100644
--- a/rt/t/api/users.t
+++ b/rt/t/api/users.t
@@ -2,7 +2,7 @@ use strict;
use warnings;
use RT::Test tests => 10;
-RT::System->AddRights(
+RT::System->AddRight( General =>
'RTxUserRight' => 'Just a right for testing rights',
);
diff --git a/rt/t/approval/admincc.t b/rt/t/approval/admincc.t
index b43929603..da8cac23d 100644
--- a/rt/t/approval/admincc.t
+++ b/rt/t/approval/admincc.t
@@ -2,17 +2,12 @@
use strict;
use warnings;
use Test::More;
-BEGIN {
- eval { require Email::Abstract; require Test::Email; 1 }
- or plan skip_all => 'require Email::Abstract and Test::Email';
-}
-
use RT;
-use RT::Test tests => 62;
+use RT::Test tests => "no_declare";
use RT::Test::Email;
-RT->Config->Set( LogToScreen => 'debug' );
+RT->Config->Set( LogToSTDERR => 'debug' );
RT->Config->Set( UseTransactionBatch => 1 );
my ($baseurl, $m) = RT::Test->started_ok;
@@ -42,7 +37,6 @@ Queue: ___Approvals
Type: approval
Owner: CTO
AdminCCs: COO, CEO
-Requestors: {$Tickets{"TOP"}->Requestors}
DependedOnBy: TOP
Subject: CTO Approval for PO: {$Tickets{"TOP"}->Id} - {$Tickets{"TOP"}->Subject}
Due: {time + 86400}
@@ -84,20 +78,20 @@ mail_ok {
Requestor => 'minion',
Queue => $q->Id,
);
-} { from => qr/RT System/,
- bcc => qr/ceo.*coo|coo.*ceo/i,
- subject => qr/PO for stationary/i,
-},
-{ from => qr/RT System/,
- to => 'cto@company.com',
- subject => qr/New Pending Approval: CTO Approval/,
- body => qr/pending your approval.*Your approval is requested.*Blah/s
-},
-{ from => qr/PO via RT/,
+} { from => qr/PO via RT/,
to => 'minion@company.com',
subject => qr/PO for stationary/,
body => qr/automatically generated in response/
-};
+},{ from => qr/RT System/,
+ to => 'root@localhost',
+ subject => qr/PO for stationary/,
+},{ from => qr/RT System/,
+ to => 'cto@company.com',
+ bcc => qr/ceo.*coo|coo.*ceo/i,
+ subject => qr/New Pending Approval: CTO Approval/,
+ body => qr/pending your approval.*Your approval is requested.*Blah/s
+}
+;
ok ($tid,$tmsg);
@@ -134,6 +128,11 @@ mail_ok {
},
{
from => qr/RT System/,
+ to => 'root@localhost',
+ subject => qr/Ticket Approved:/,
+},
+{
+ from => qr/RT System/,
to => 'minion@company.com',
subject => qr/Ticket Approved:/,
body => qr/approved by CTO.*notes: Resources exist to be consumed/s
@@ -180,6 +179,12 @@ for my $admin (qw/coo ceo/) {
body => qr/Resources exist to be consumed/,
},
{
+ from => qr/RT System/,
+ to => 'root@localhost',
+ subject => qr/Ticket Approved:/,
+ body => qr/approved by \U$admin\E.*notes: Resources exist to be consumed/s
+ },
+ {
from => qr/RT System/,
to => 'minion@company.com',
subject => qr/Ticket Approved:/,
@@ -273,3 +278,6 @@ $m_coo->content_lacks( 'second approval', 'coo: second approval is gone too' );
$m_ceo->content_lacks( 'second approval', 'ceo: second approval is gone too' );
RT::Test->clean_caught_mails;
+
+undef $m;
+done_testing;
diff --git a/rt/t/approval/basic.t b/rt/t/approval/basic.t
index 2d00eb56e..e863bf12f 100644
--- a/rt/t/approval/basic.t
+++ b/rt/t/approval/basic.t
@@ -1,15 +1,10 @@
use strict;
use warnings;
use RT::Test tests => undef;
-BEGIN {
- plan skip_all => 'Email::Abstract and Test::Email required.'
- unless eval { require Email::Abstract; require Test::Email; 1 };
- plan tests => 38;
-}
use RT::Test::Email;
-RT->Config->Set( LogToScreen => 'debug' );
+RT->Config->Set( LogToSTDERR => 'debug' );
RT->Config->Set( UseTransactionBatch => 1 );
my $q = RT::Queue->new(RT->SystemUser);
@@ -33,7 +28,6 @@ my $approvals =
Queue: ___Approvals
Type: approval
Owner: CFO
-Requestors: {$Tickets{"TOP"}->Requestors}
Refers-To: TOP
Subject: CFO Approval for PO: {$Tickets{"TOP"}->Id} - {$Tickets{"TOP"}->Subject}
Due: {time + 86400}
@@ -46,7 +40,6 @@ ENDOFCONTENT
Queue: ___Approvals
Type: approval
Owner: CEO
-Requestors: {$Tickets{"TOP"}->Requestors}
Subject: PO approval request for {$Tickets{"TOP"}->Subject}
Refers-To: TOP
Depends-On: for-CFO
@@ -86,14 +79,17 @@ mail_ok {
$t->Create(Subject => "PO for stationary",
Owner => "root", Requestor => 'minion',
Queue => $q->Id);
-} { from => qr/RT System/,
- to => 'cfo@company.com',
- subject => qr/New Pending Approval: CFO Approval/,
- body => qr/pending your approval.*Your approval is requested.*Blah/s
-},{ from => qr/PO via RT/,
+} { from => qr/PO via RT/,
to => 'minion@company.com',
subject => qr/PO for stationary/,
body => qr/automatically generated in response/
+},{ from => qr/RT System/,
+ to => 'root@localhost',
+ subject => qr/PO for stationary/,
+}, { from => qr/RT System/,
+ to => 'cfo@company.com',
+ subject => qr/New Pending Approval: CFO Approval/,
+ body => qr/pending your approval.*Your approval is requested.*Blah/s
};
ok ($tid,$tmsg);
@@ -137,6 +133,9 @@ mail_ok {
subject => qr/New Pending Approval: PO approval request for PO/,
body => qr/pending your approval.*CFO approved.*ok with that\?/s
},{ from => qr/RT System/,
+ to => 'root@localhost',
+ subject => qr/Ticket Approved:/,
+},{ from => qr/RT System/,
to => 'minion@company.com',
subject => qr/Ticket Approved:/,
body => qr/approved by CFO.*notes: Resources exist to be consumed/s
@@ -165,10 +164,14 @@ mail_ok {
ok($ok, "ceo can approve - $msg");
} { from => qr/RT System/,
+ to => 'root@localhost',
+ subject => qr/Ticket Approved:/,
+ body => qr/approved by CEO.*Its Owner may now start to act on it.*notes: And consumed they will be/s,
+},{ from => qr/RT System/,
to => 'minion@company.com',
subject => qr/Ticket Approved:/,
body => qr/approved by CEO.*Its Owner may now start to act on it.*notes: And consumed they will be/s,
-}, { from => qr/CEO via RT/,
+},{ from => qr/CEO via RT/,
to => 'root@localhost',
subject => qr/Ticket Approved/,
body => qr/The ticket has been approved, you may now start to act on it/,
@@ -203,6 +206,10 @@ mail_ok {
ok($ok, "cfo can approve - $msg");
} { from => qr/RT System/,
+ to => 'root@localhost',
+ subject => qr/Ticket Rejected: PO for stationary/,
+ body => qr/rejected by CFO.*out of resources/s,
+},{ from => qr/RT System/,
to => 'minion@company.com',
subject => qr/Ticket Rejected: PO for stationary/,
body => qr/rejected by CFO.*out of resources/s,
@@ -212,3 +219,4 @@ $t->Load($t->id);$dependson_ceo->Load($dependson_ceo->id);
is_deeply([ $t->Status, $dependson_cfo->Status, $dependson_ceo->Status ],
[ 'rejected', 'rejected', 'deleted'], 'ticket state after cfo rejection');
+done_testing;
diff --git a/rt/t/articles/interface.t b/rt/t/articles/interface.t
index 779127fa6..0aeefe7ca 100644
--- a/rt/t/articles/interface.t
+++ b/rt/t/articles/interface.t
@@ -20,11 +20,11 @@ my ($ret, $msg);
# Create a test class
my $class = RT::Class->new($RT::SystemUser);
($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$,
- 'Description' => 'A general-purpose test class');
+ 'Description' => 'A general-purpose test class');
ok($ret, "Test class created");
my $class2 = RT::Class->new($RT::SystemUser);
($ret, $msg) = $class2->Create('Name' => 'tlaTestClass2-'.$$,
- 'Description' => 'Another general-purpose test class');
+ 'Description' => 'Another general-purpose test class');
ok($ret, "Test class 2 created");
@@ -36,34 +36,34 @@ my $topic2 = RT::Topic->new($RT::SystemUser);
my $topic_class2= RT::Topic->new($RT::SystemUser);
my $gtopic = RT::Topic->new($RT::SystemUser);
($ret, $msg) = $topic1->Create('Parent' => 0,
- 'Name' => 'tlaTestTopic1-'.$$,
- 'ObjectType' => 'RT::Class',
- 'ObjectId' => $class->Id);
+ 'Name' => 'tlaTestTopic1-'.$$,
+ 'ObjectType' => 'RT::Class',
+ 'ObjectId' => $class->Id);
ok($ret, "Topic 1 created");
($ret, $msg) = $topic11->Create('Parent' => $topic1->Id,
- 'Name' => 'tlaTestTopic1.1-'.$$,
- 'ObjectType' => 'RT::Class',
- 'ObjectId' => $class->Id);
+ 'Name' => 'tlaTestTopic1.1-'.$$,
+ 'ObjectType' => 'RT::Class',
+ 'ObjectId' => $class->Id);
ok($ret, "Topic 1.1 created");
($ret, $msg) = $topic12->Create('Parent' => $topic1->Id,
- 'Name' => 'tlaTestTopic1.2-'.$$,
- 'ObjectType' => 'RT::Class',
- 'ObjectId' => $class->Id);
+ 'Name' => 'tlaTestTopic1.2-'.$$,
+ 'ObjectType' => 'RT::Class',
+ 'ObjectId' => $class->Id);
ok($ret, "Topic 1.2 created");
($ret, $msg) = $topic2->Create('Parent' => 0,
- 'Name' => 'tlaTestTopic2-'.$$,
- 'ObjectType' => 'RT::Class',
- 'ObjectId' => $class->Id);
+ 'Name' => 'tlaTestTopic2-'.$$,
+ 'ObjectType' => 'RT::Class',
+ 'ObjectId' => $class->Id);
ok($ret, "Topic 2 created");
($ret, $msg) = $topic_class2->Create('Parent' => 0,
- 'Name' => 'tlaTestTopicClass2-'.$$,
- 'ObjectType' => 'RT::Class',
- 'ObjectId' => $class2->Id);
+ 'Name' => 'tlaTestTopicClass2-'.$$,
+ 'ObjectType' => 'RT::Class',
+ 'ObjectId' => $class2->Id);
ok($ret, "Topic Class2 created");
($ret, $msg) = $gtopic->Create('Parent' => 0,
- 'Name' => 'tlaTestTopicGlobal-'.$$,
- 'ObjectType' => 'RT::System',
- 'ObjectId' => $RT::System->Id );
+ 'Name' => 'tlaTestTopicGlobal-'.$$,
+ 'ObjectType' => 'RT::System',
+ 'ObjectId' => $RT::System->Id );
ok($ret, "Global Topic created");
# Create some article custom fields
@@ -71,18 +71,18 @@ ok($ret, "Global Topic created");
my $questionCF = RT::CustomField->new($RT::SystemUser);
my $answerCF = RT::CustomField->new($RT::SystemUser);
($ret, $msg) = $questionCF->Create('Name' => 'Question-'.$$,
- 'Type' => 'Text',
- 'MaxValues' => 1,
- 'LookupType' => 'RT::Class-RT::Article',
- 'Description' => 'The question to be answered',
- 'Disabled' => 0);
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Class-RT::Article',
+ 'Description' => 'The question to be answered',
+ 'Disabled' => 0);
ok($ret, "Question CF created: $msg");
($ret, $msg) = $answerCF->Create('Name' => 'Answer-'.$$,
- 'Type' => 'Text',
- 'MaxValues' => 1,
- 'LookupType' => 'RT::Class-RT::Article',
- 'Description' => 'The answer to the question',
- 'Disabled' => 0);
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Class-RT::Article',
+ 'Description' => 'The answer to the question',
+ 'Disabled' => 0);
ok($ret, "Answer CF created: $msg");
# Attach the custom fields to our class
@@ -93,13 +93,13 @@ ok($ret, "Answer CF added to class: $msg");
my ($qid, $aid) = ($questionCF->Id, $answerCF->Id);
my %cvals = ('article1q' => 'Some question about swallows',
- 'article1a' => 'Some answer about Europe and Africa',
- 'article2q' => 'Another question about Monty Python',
- 'article2a' => 'Romani ite domum',
- 'article3q' => 'Why should I eat my supper?',
- 'article3a' => 'There are starving children in Africa',
- 'article4q' => 'What did Brian originally write?',
- 'article4a' => 'Romanes eunt domus');
+ 'article1a' => 'Some answer about Europe and Africa',
+ 'article2q' => 'Another question about Monty Python',
+ 'article2a' => 'Romani ite domum',
+ 'article3q' => 'Why should I eat my supper?',
+ 'article3a' => 'There are starving children in Africa',
+ 'article4q' => 'What did Brian originally write?',
+ 'article4a' => 'Romanes eunt domus');
# Create an article or two with our custom field values.
@@ -108,36 +108,36 @@ my $article2 = RT::Article->new($RT::SystemUser);
my $article3 = RT::Article->new($RT::SystemUser);
my $article4 = RT::Article->new($RT::SystemUser);
($ret, $msg) = $article1->Create(Name => 'First article '.$$,
- Summary => 'blah blah 1',
- Class => $class->Id,
- Topics => [$topic1->Id],
- "CustomField-$qid" => $cvals{'article1q'},
- "CustomField-$aid" => $cvals{'article1a'},
- );
+ Summary => 'blah blah 1',
+ Class => $class->Id,
+ Topics => [$topic1->Id],
+ "CustomField-$qid" => $cvals{'article1q'},
+ "CustomField-$aid" => $cvals{'article1a'},
+ );
ok($ret, "article 1 created");
($ret, $msg) = $article2->Create(Name => 'Second article '.$$,
- Summary => 'foo bar 2',
- Class => $class->Id,
- Topics => [$topic11->Id],
- "CustomField-$qid" => $cvals{'article2q'},
- "CustomField-$aid" => $cvals{'article2a'},
- );
+ Summary => 'foo bar 2',
+ Class => $class->Id,
+ Topics => [$topic11->Id],
+ "CustomField-$qid" => $cvals{'article2q'},
+ "CustomField-$aid" => $cvals{'article2a'},
+ );
ok($ret, "article 2 created");
($ret, $msg) = $article3->Create(Name => 'Third article '.$$,
- Summary => 'ping pong 3',
- Class => $class->Id,
- Topics => [$topic12->Id],
- "CustomField-$qid" => $cvals{'article3q'},
- "CustomField-$aid" => $cvals{'article3a'},
- );
+ Summary => 'ping pong 3',
+ Class => $class->Id,
+ Topics => [$topic12->Id],
+ "CustomField-$qid" => $cvals{'article3q'},
+ "CustomField-$aid" => $cvals{'article3a'},
+ );
ok($ret, "article 3 created");
($ret, $msg) = $article4->Create(Name => 'Fourth article '.$$,
- Summary => 'hoi polloi 4',
- Class => $class->Id,
- Topics => [$topic2->Id],
- "CustomField-$qid" => $cvals{'article4q'},
- "CustomField-$aid" => $cvals{'article4a'},
- );
+ Summary => 'hoi polloi 4',
+ Class => $class->Id,
+ Topics => [$topic2->Id],
+ "CustomField-$qid" => $cvals{'article4q'},
+ "CustomField-$aid" => $cvals{'article4a'},
+ );
ok($ret, "article 4 created");
# Create a ticket.
@@ -152,8 +152,8 @@ May as well say something about Africa.');
my $ticket = RT::Ticket->new($RT::SystemUser);
my $obj;
($ret, $obj, $msg) = $ticket->Create(Queue => 'General',
- Subject => 'test ticket for articles '.$$,
- MIMEObj => $parser->Entity);
+ Subject => 'test ticket for articles '.$$,
+ MIMEObj => $parser->Entity);
ok($ret, "Test ticket for articles created: $msg");
@@ -161,7 +161,7 @@ ok($ret, "Test ticket for articles created: $msg");
isa_ok($m, 'Test::WWW::Mechanize');
ok($m->login, 'logged in');
-$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! },
+$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/index.html! },
'UI -> Articles' );
$m->content_contains($article3->Name);
@@ -175,9 +175,9 @@ my $ticket_id = $ticket->Id;
my $turi = "t:$ticket_id";
my $a1uri = 'a:'.$article1->Id;
$m->submit_form(form_name => 'EditArticle',
- fields => { $article3->Id.'-RefersTo' => $turi,
- 'RefersTo-'.$article3->Id => $a1uri }
- );
+ fields => { $article3->Id.'-RefersTo' => $turi,
+ 'RefersTo-'.$article3->Id => $a1uri }
+ );
$m->content_like(qr/Ticket.*$ticket_id/, "Ticket linkto was created");
$m->content_like(qr/URI.*$a1uri/, "Article linkfrom was created");
@@ -185,7 +185,7 @@ $m->content_like(qr/URI.*$a1uri/, "Article linkfrom was created");
# Now try to extract an article from a link.
$m->get_ok($url."/Ticket/Display.html?id=".$ticket->Id,
- "Loaded ticket display");
+ "Loaded ticket display");
$m->content_like(qr/Extract Article/, "Article extraction link shows up");
$m->follow_link_ok( { text => 'Extract Article' }, '-> Extract Article' );
$m->content_contains($class->Name);
@@ -203,7 +203,7 @@ $m->title_like(qr/Modify article/);
$m->follow_link_ok( { text => 'Display' }, '-> Display' );
$m->content_like(qr/Africa/, "Article content exist");
$m->content_contains($ticket->Subject,
- "Article references originating ticket");
+ "Article references originating ticket");
diag("Test creating a ticket in Class2 and make sure we don't see Class1 Topics") if $ENV{TEST_VERBOSE};
{
diff --git a/rt/t/articles/search-interface.t b/rt/t/articles/search-interface.t
index e957a6c31..3c75268b9 100644
--- a/rt/t/articles/search-interface.t
+++ b/rt/t/articles/search-interface.t
@@ -2,7 +2,7 @@
use strict;
use warnings;
-use RT::Test tests => 44;
+use RT::Test tests => undef;
use RT::CustomField;
use RT::Queue;
@@ -16,36 +16,39 @@ my ($url, $m) = RT::Test->started_ok;
# Variables to test return values
my ($ret, $msg);
-# Create a test class
+# Create two classes
my $class = RT::Class->new($RT::SystemUser);
-($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$,
- 'Description' => 'A general-purpose test class');
+($ret, $msg) = $class->Create('Name' => 'First-class',
+ 'Description' => 'A general-purpose test class');
ok($ret, "Test class created");
+($ret, $msg) = $class->Create('Name' => 'Second-class',
+ 'Description' => 'Another class');
+ok($ret, "Test class created");
my $questionCF = RT::CustomField->new($RT::SystemUser);
my $answerCF = RT::CustomField->new($RT::SystemUser);
my $ticketCF = RT::CustomField->new($RT::SystemUser);
($ret, $msg) = $questionCF->Create('Name' => 'Question-'.$$,
- 'Type' => 'Text',
- 'MaxValues' => 1,
- 'LookupType' => 'RT::Class-RT::Article',
- 'Description' => 'The question to be answered',
- 'Disabled' => 0);
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Class-RT::Article',
+ 'Description' => 'The question to be answered',
+ 'Disabled' => 0);
ok($ret, "Question CF created: $msg");
($ret, $msg) = $answerCF->Create('Name' => 'Answer-'.$$,
- 'Type' => 'Text',
- 'MaxValues' => 1,
- 'LookupType' => 'RT::Class-RT::Article',
- 'Description' => 'The answer to the question',
- 'Disabled' => 0);
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Class-RT::Article',
+ 'Description' => 'The answer to the question',
+ 'Disabled' => 0);
ok($ret, "Answer CF created: $msg");
($ret, $msg) = $ticketCF->Create('Name' => 'Class',
- 'Type' => 'Text',
- 'MaxValues' => 1,
- 'LookupType' => 'RT::Queue-RT::Ticket',
- 'Disabled' => 0);
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Queue-RT::Ticket',
+ 'Disabled' => 0);
ok($ret, "Ticket CF 'Class' created: $msg");
# Attach the custom fields to our class
@@ -60,18 +63,18 @@ my $global_queue = RT::Queue->new($RT::SystemUser);
ok($ret, "Ticket CF added globally: $msg");
my %cvals = ('article1q' => 'Some question about swallows',
- 'article1a' => 'Some answer about Europe and Africa',
- 'article2q' => 'Another question about Monty Python',
- 'article2a' => 'Romani ite domum',
- 'article3q' => 'Why should I eat my supper?',
- 'article3a' => 'There are starving children in Africa',
- 'article4q' => 'What did Brian originally write?',
- 'article4a' => 'This is an answer that is longer than 255 '
- . 'characters so these tests will be sure to use the LargeContent '
- . 'SQL as well as the normal SQL that would be generated if this '
- . 'was an answer that was shorter than 255 characters. This second '
- . 'sentence has a few extra characters to get this string to go '
- . 'over the 255 character boundary. Lorem ipsum.');
+ 'article1a' => 'Some answer about Europe and Africa',
+ 'article2q' => 'Another question about Monty Python',
+ 'article2a' => 'Romani ite domum',
+ 'article3q' => 'Why should I eat my supper?',
+ 'article3a' => 'There are starving children in Africa',
+ 'article4q' => 'What did Brian originally write?',
+ 'article4a' => 'This is an answer that is longer than 255 '
+ . 'characters so these tests will be sure to use the LargeContent '
+ . 'SQL as well as the normal SQL that would be generated if this '
+ . 'was an answer that was shorter than 255 characters. This second '
+ . 'sentence has a few extra characters to get this string to go '
+ . 'over the 255 character boundary. Lorem ipsum.');
# Create an article or two with our custom field values.
@@ -80,32 +83,32 @@ my $article2 = RT::Article->new($RT::SystemUser);
my $article3 = RT::Article->new($RT::SystemUser);
my $article4 = RT::Article->new($RT::SystemUser);
($ret, $msg) = $article1->Create(Name => 'First article '.$$,
- Summary => 'blah blah 1',
- Class => $class->Id,
- "CustomField-$qid" => $cvals{'article1q'},
- "CustomField-$aid" => $cvals{'article1a'},
- );
+ Summary => 'blah blah 1',
+ Class => $class->Id,
+ "CustomField-$qid" => $cvals{'article1q'},
+ "CustomField-$aid" => $cvals{'article1a'},
+ );
ok($ret, "article 1 created");
($ret, $msg) = $article2->Create(Name => 'Second article '.$$,
- Summary => 'foo bar 2',
- Class => $class->Id,
- "CustomField-$qid" => $cvals{'article2q'},
- "CustomField-$aid" => $cvals{'article2a'},
- );
+ Summary => 'foo bar 2',
+ Class => $class->Id,
+ "CustomField-$qid" => $cvals{'article2q'},
+ "CustomField-$aid" => $cvals{'article2a'},
+ );
ok($ret, "article 2 created");
($ret, $msg) = $article3->Create(Name => 'Third article '.$$,
- Summary => 'ping pong 3',
- Class => $class->Id,
- "CustomField-$qid" => $cvals{'article3q'},
- "CustomField-$aid" => $cvals{'article3a'},
- );
+ Summary => 'ping pong 3',
+ Class => $class->Id,
+ "CustomField-$qid" => $cvals{'article3q'},
+ "CustomField-$aid" => $cvals{'article3a'},
+ );
ok($ret, "article 3 created");
($ret, $msg) = $article4->Create(Name => 'Fourth article '.$$,
- Summary => 'hoi polloi 4',
- Class => $class->Id,
- "CustomField-$qid" => $cvals{'article4q'},
- "CustomField-$aid" => $cvals{'article4a'},
- );
+ Summary => 'hoi polloi 4',
+ Class => $class->Id,
+ "CustomField-$qid" => $cvals{'article4q'},
+ "CustomField-$aid" => $cvals{'article4a'},
+ );
ok($ret, "article 4 created");
isa_ok($m, 'Test::WWW::Mechanize');
@@ -140,6 +143,9 @@ TODO:{
$m->text_contains('hoi polloi 4');
}
+undef $m;
+done_testing;
+
# When you send $m to this sub, it must be on a page with
# a Search link.
sub DoArticleSearch{
@@ -147,7 +153,7 @@ sub DoArticleSearch{
my $class_name = shift;
my $search_text = shift;
- $m->follow_link_ok( {text => 'Search'}, 'Articles -> Search');
+ $m->follow_link_ok( {text => 'Articles'}, 'Articles Search');
$m->follow_link_ok( {text => 'in class '. $class_name}, 'Articles in class '. $class_name);
$m->text_contains('First article');
diff --git a/rt/t/articles/set-subject.t b/rt/t/articles/set-subject.t
new file mode 100644
index 000000000..9b9ff850a
--- /dev/null
+++ b/rt/t/articles/set-subject.t
@@ -0,0 +1,110 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+use RT::CustomField;
+use RT::EmailParser;
+use RT::Queue;
+use RT::Ticket;
+use_ok 'RT::Class';
+use_ok 'RT::Topic';
+use_ok 'RT::Article';
+
+# Variables to test return values
+my ($ret, $msg);
+
+# Create a test class
+my $class = RT::Class->new($RT::SystemUser);
+($ret, $msg) = $class->Create('Name' => 'TestClass-'.$$,
+ 'Description' => 'A general-purpose test class');
+ok($ret, "Test class created: $msg");
+# because id 0 represents global, it uses an empty Queue object...
+($ret, $msg) = $class->AddToObject(RT::Queue->new($RT::SystemUser));
+ok($ret, "Applied Class globally: $msg");
+
+# Create some article custom fields
+my $bodyCF = RT::CustomField->new($RT::SystemUser);
+my $subjectCF = RT::CustomField->new($RT::SystemUser);
+($ret, $msg) = $subjectCF->Create('Name' => 'Subject-'.$$,
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Class-RT::Article',
+ 'Description' => 'The subject to be answered',
+ 'Disabled' => 0);
+ok($ret, "Question CF created: $msg");
+($ret, $msg) = $bodyCF->Create('Name' => 'Body-'.$$,
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Class-RT::Article',
+ 'Description' => 'The body to the subject',
+ 'Disabled' => 0);
+ok($ret, "Answer CF created: $msg");
+my ($sid, $bid) = ($subjectCF->Id, $bodyCF->Id);
+
+# Attach the custom fields to our class
+($ret, $msg) = $subjectCF->AddToObject($class);
+ok($ret, "Subject CF added to class: $msg");
+($ret, $msg) = $bodyCF->AddToObject($class);
+ok($ret, "Body CF added to class: $msg");
+
+my $article = RT::Article->new($RT::SystemUser);
+($ret, $msg) = $article->Create(Name => 'First article '.$$,
+ Summary => 'blah blah 1',
+ Class => $class->Id,
+ "CustomField-$bid" => 'This goes in the body',
+ "CustomField-$sid" => 'This clobbers your subject',
+ );
+ok($ret, "article 1 created: $msg");
+
+# Create a ticket.
+my $parser = RT::EmailParser->new();
+$parser->ParseMIMEEntityFromScalar('From: root@localhost
+To: rt@example.com
+Subject: test ticket for articles
+
+This is some form of new request.
+May as well say something about Africa.');
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my $obj;
+($ret, $obj, $msg) = $ticket->Create(Queue => 'General',
+ Subject => 'test ticket for articles '.$$,
+ MIMEObj => $parser->Entity);
+ok($ret, "Test ticket for articles created: $msg");
+
+
+#### Right. That's our data. Now begin the real testing.
+
+my ($url, $m) = RT::Test->started_ok;
+ok($m->login, 'logged in');
+
+$m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket->id,
+ 'ticket update page' );
+is($m->form_number(3)->find_input('UpdateSubject')->value,$ticket->Subject,'Ticket Subject Found');
+$m->submit_form(
+ form_number => 3,
+ fields => { 'Articles-Include-Article-Named' => $article->Id },
+ button => 'Go',
+);
+is($m->form_number(3)->find_input('UpdateSubject')->value,$ticket->Subject,'Ticket Subject Not Clobbered');
+
+$m->get_ok("$url/Admin/Articles/Classes/");
+$m->follow_link_ok( { text => 'TestClass-'.$$ } );
+$m->submit_form_ok({
+ form_number => 3,
+ fields => { SubjectOverride => $sid },
+});
+$m->content_contains("Added Subject Override: Subject-$$");
+
+$m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket->id,
+ 'ticket update page' );
+is($m->form_number(3)->find_input('UpdateSubject')->value,$ticket->Subject,'Ticket Subject Found');
+$m->submit_form(
+ form_number => 3,
+ fields => { 'Articles-Include-Article-Named' => $article->Name },
+ button => 'Go',
+);
+is($m->form_number(3)->find_input('UpdateSubject')->value,$article->FirstCustomFieldValue("Subject-$$"),'Ticket Subject Clobbered');
+undef $m;
+done_testing;
diff --git a/rt/t/articles/upload-customfields.t b/rt/t/articles/upload-customfields.t
index e5ed5d190..29bd677ca 100644
--- a/rt/t/articles/upload-customfields.t
+++ b/rt/t/articles/upload-customfields.t
@@ -8,12 +8,12 @@ use RT;
my $logo;
BEGIN {
$logo =
- -e $RT::MasonComponentRoot . '/NoAuth/images/bpslogo.png'
+ -e $RT::StaticPath . '/images/bpslogo.png'
? 'bpslogo.png'
: 'bplogo.gif';
}
-use constant ImageFile => $RT::MasonComponentRoot . "/NoAuth/images/$logo";
+use constant ImageFile => $RT::StaticPath . "/images/$logo";
use constant ImageFileContent => do {
local $/;
@@ -25,14 +25,14 @@ use constant ImageFileContent => do {
use RT::Class;
my $class = RT::Class->new($RT::SystemUser);
my ($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$,
- 'Description' => 'A general-purpose test class');
+ 'Description' => 'A general-purpose test class');
ok($ret, "Test class created");
my ($url, $m) = RT::Test->started_ok;
isa_ok($m, 'Test::WWW::Mechanize');
ok($m->login, 'logged in');
-$m->follow_link_ok( { text => 'Configuration' } );
+$m->follow_link_ok( { text => 'Admin' } );
$m->title_is(q/RT Administration/, 'admin screen');
$m->follow_link_ok( { text => 'Custom Fields' } );
$m->title_is(q/Select a Custom Field/, 'admin-cf screen');
diff --git a/rt/t/articles/uri-articles.t b/rt/t/articles/uri-articles.t
index b6a1c8dac..9ad4b0724 100644
--- a/rt/t/articles/uri-articles.t
+++ b/rt/t/articles/uri-articles.t
@@ -2,7 +2,8 @@
use strict;
use warnings;
-use RT::Test tests => 9;
+use RT::Test tests => undef;
+use Test::Warn;
use_ok "RT::URI::fsck_com_article";
my $uri = RT::URI::fsck_com_article->new( $RT::SystemUser );
@@ -12,7 +13,7 @@ isa_ok $uri, 'RT::URI::fsck_com_article';
isa_ok $uri, 'RT::URI::base';
isa_ok $uri, 'RT::Base';
-is $uri->LocalURIPrefix, 'fsck.com-article://example.com/article/';
+is $uri->LocalURIPrefix, 'fsck.com-article://example.com';
my $class = RT::Class->new( $RT::SystemUser );
$class->Create( Name => 'URItest - '. $$ );
@@ -26,5 +27,24 @@ my ($id, $msg) = $article->Create(
ok($id,$msg);
$uri = RT::URI::fsck_com_article->new( $article->CurrentUser );
-is $uri->LocalURIPrefix . $article->id, $uri->URIForObject( $article );
+is $uri->URIForObject( $article ),
+ 'fsck.com-article://example.com/article/' . $article->id,
+ 'Got correct URIForObject';
+my $article_id = $article->Id;
+ok ($uri->ParseURI("fsck.com-article://example.com/article/$article_id"),
+ 'Parsed URI');
+ok ($article->Delete(), 'Deleted article');
+
+my $ret;
+warning_like {
+ $ret = $uri->ParseURI("fsck.com-article://example.com/article/$article_id");
+} qr/Unable to load article for id $article_id. It may have been deleted/,
+ "Warned about missing article";
+
+ok (!$ret, 'Returned false on missing article');
+
+ok (!$uri->ParseURI("fsck.com-article://foo.com/article/$article_id"),
+ 'ParseURI returned false with incorrect Organization');
+
+done_testing();
diff --git a/rt/t/charts/basics.t b/rt/t/charts/basics.t
new file mode 100644
index 000000000..0f93e5a24
--- /dev/null
+++ b/rt/t/charts/basics.t
@@ -0,0 +1,91 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+use RT::Ticket;
+
+my $q = RT::Test->load_or_create_queue( Name => 'General' );
+ok $q && $q->id, 'loaded or created queue';
+my $queue = $q->Name;
+
+my @tickets = add_tix_from_data(
+ { Subject => 'n', Status => 'new' },
+ { Subject => 'o', Status => 'open' },
+ { Subject => 'o', Status => 'open' },
+ { Subject => 'r', Status => 'resolved' },
+ { Subject => 'r', Status => 'resolved' },
+ { Subject => 'r', Status => 'resolved' },
+);
+
+use_ok 'RT::Report::Tickets';
+
+{
+ my $report = RT::Report::Tickets->new( RT->SystemUser );
+ my %columns = $report->SetupGroupings(
+ Query => 'Queue = '. $q->id,
+ GroupBy => ['Status'],
+ Function => ['COUNT'],
+ );
+ $report->SortEntries;
+
+ my @colors = RT->Config->Get("ChartColors");
+ my $expected = {
+ 'thead' => [ {
+ 'cells' => [
+ { 'value' => 'Status', 'type' => 'head' },
+ { 'rowspan' => 1, 'value' => 'Ticket count', 'type' => 'head', 'color' => $colors[0] },
+ ],
+ } ],
+ 'tfoot' => [ {
+ 'cells' => [
+ { 'colspan' => 1, 'value' => 'Total', 'type' => 'label' },
+ { 'value' => 6, 'type' => 'value' },
+ ],
+ 'even' => 0
+ } ],
+ 'tbody' => [
+ {
+ 'cells' => [
+ { 'value' => 'new', 'type' => 'label' },
+ { 'query' => '(Status = \'new\')', 'value' => '1', 'type' => 'value' },
+ ],
+ 'even' => 1
+ },
+ {
+ 'cells' => [
+ { 'value' => 'open', 'type' => 'label' },
+ { 'query' => '(Status = \'open\')', 'value' => '2', 'type' => 'value' }
+ ],
+ 'even' => 0
+ },
+ {
+ 'cells' => [
+ { 'value' => 'resolved', 'type' => 'label' },
+ { 'query' => '(Status = \'resolved\')', 'value' => '3', 'type' => 'value' }
+ ],
+ 'even' => 1
+ },
+ ]
+ };
+
+ my %table = $report->FormatTable( %columns );
+ is_deeply( \%table, $expected, "basic table" );
+}
+
+done_testing;
+
+
+sub add_tix_from_data {
+ my @data = @_;
+ my @res = ();
+ while (@data) {
+ my %info = %{ shift(@data) };
+ my $t = RT::Ticket->new($RT::SystemUser);
+ my ( $id, undef, $msg ) = $t->Create( Queue => $q->id, %info );
+ ok( $id, "ticket created" ) or diag("error: $msg");
+ is $t->Status, $info{'Status'}, 'correct status';
+ push @res, $t;
+ }
+ return @res;
+}
+
diff --git a/rt/t/charts/compound-sql-function.t b/rt/t/charts/compound-sql-function.t
new file mode 100644
index 000000000..3e6a3fdfe
--- /dev/null
+++ b/rt/t/charts/compound-sql-function.t
@@ -0,0 +1,121 @@
+
+use strict;
+use warnings;
+
+use RT::Test tests => 14;
+use RT::Ticket;
+
+my $q1 = RT::Test->load_or_create_queue( Name => 'One' );
+ok $q1 && $q1->id, 'loaded or created queue';
+
+my $q2 = RT::Test->load_or_create_queue( Name => 'Two' );
+ok $q2 && $q2->id, 'loaded or created queue';
+
+my @tickets = add_tix_from_data(
+ { Queue => $q1->id, Resolved => 3*60 },
+ { Queue => $q1->id, Resolved => 3*60*60 },
+ { Queue => $q1->id, Resolved => 3*24*60*60 },
+ { Queue => $q1->id, Resolved => 3*30*24*60*60 },
+ { Queue => $q1->id, Resolved => 9*30*24*60*60 },
+ { Queue => $q2->id, Resolved => 7*60 },
+ { Queue => $q2->id, Resolved => 7*60*60 },
+ { Queue => $q2->id, Resolved => 7*24*60*60 },
+ { Queue => $q2->id, Resolved => 7*30*24*60*60 },
+ { Queue => $q2->id, Resolved => 24*30*24*60*60 },
+);
+
+use_ok 'RT::Report::Tickets';
+
+{
+ my $report = RT::Report::Tickets->new( RT->SystemUser );
+ my %columns = $report->SetupGroupings(
+ Query => 'id > 0',
+ GroupBy => ['Queue'],
+ Function => ['ALL(Created-Resolved)'],
+ );
+ $report->SortEntries;
+
+ my @colors = RT->Config->Get("ChartColors");
+ my $expected = {
+ 'thead' => [
+ {
+ 'cells' => [
+ { 'rowspan' => 2, 'value' => 'Queue', 'type' => 'head' },
+ { 'colspan' => 4, 'value' => 'Summary of Created-Resolved', 'type' => 'head' }
+ ]
+ },
+ {
+ 'cells' => [
+ { 'value' => 'Minimum', 'type' => 'head', 'color' => $colors[0] },
+ { 'value' => 'Average', 'type' => 'head', 'color' => $colors[1] },
+ { 'value' => 'Maximum', 'type' => 'head', 'color' => $colors[2] },
+ { 'value' => 'Total', 'type' => 'head', 'color' => $colors[3] }
+ ]
+ }
+ ],
+ 'tfoot' => [
+ {
+ 'cells' => [
+ { 'colspan' => 1, 'value' => 'Total', 'type' => 'label' },
+ { 'value' => '10m', 'type' => 'value' },
+ { 'value' => '8M 2W 3d', 'type' => 'value' },
+ { 'value' => '2Y 8M 2W', 'type' => 'value' },
+ { 'value' => '3Y 6M 3W', 'type' => 'value' }
+ ],
+ 'even' => 1
+ }
+ ],
+ 'tbody' => [
+ {
+ 'cells' => [
+ { 'value' => 'One', 'type' => 'label' },
+ { 'query' => '(Queue = 3)', 'value' => '3m', 'type' => 'value' },
+ { 'query' => '(Queue = 3)', 'value' => '2M 1W 5d', 'type' => 'value' },
+ { 'query' => '(Queue = 3)', 'value' => '8M 3W 6d', 'type' => 'value' },
+ { 'query' => '(Queue = 3)', 'value' => '11M 4W 8h', 'type' => 'value' }
+ ],
+ 'even' => 1
+ },
+ {
+ 'cells' => [
+ { 'value' => 'Two', 'type' => 'label' },
+ { 'query' => '(Queue = 4)', 'value' => '7m', 'type' => 'value' },
+ { 'query' => '(Queue = 4)', 'value' => '6M 4d 20h', 'type' => 'value' },
+ { 'query' => '(Queue = 4)', 'value' => '1Y 11M 3W', 'type' => 'value' },
+ { 'query' => '(Queue = 4)', 'value' => '2Y 6M 3W', 'type' => 'value' }
+ ],
+ 'even' => 0
+ }
+ ]
+ };
+
+ my %table = $report->FormatTable( %columns );
+ is_deeply( \%table, $expected, "basic table" );
+}
+
+
+sub add_tix_from_data {
+ my @data = @_;
+ my @res = ();
+
+ my $created = RT::Date->new( $RT::SystemUser );
+ $created->SetToNow;
+
+ my $resolved = RT::Date->new( $RT::SystemUser );
+
+ while (@data) {
+ $resolved->Set( Value => $created->Unix );
+ $resolved->AddSeconds( $data[0]{'Resolved'} );
+ my $t = RT::Ticket->new($RT::SystemUser);
+ my ( $id, undef $msg ) = $t->Create(
+ %{ shift(@data) },
+ Created => $created->ISO,
+ Resolved => $resolved->ISO,
+ );
+ ok( $id, "ticket created" ) or diag("error: $msg");
+ push @res, $t;
+ }
+ return @res;
+}
+
+
diff --git a/rt/t/charts/group-by-cf.t b/rt/t/charts/group-by-cf.t
new file mode 100644
index 000000000..f6bfb35e4
--- /dev/null
+++ b/rt/t/charts/group-by-cf.t
@@ -0,0 +1,71 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+use RT::Ticket;
+
+my $q = RT::Test->load_or_create_queue( Name => 'General' );
+ok $q && $q->id, 'loaded or created queue';
+my $queue = $q->Name;
+
+my $cf = RT::CustomField->new(RT->SystemUser);
+my ($id,$msg) = $cf->Create(Name => 'Test', Type => 'Freeform', MaxValues => '1', Queue => $q->id);
+ok $id, $msg;
+my $cfid = $cf->id;
+
+
+my @tickets = RT::Test->create_tickets(
+ {},
+ { Subject => 't1', Status => 'new', CustomFields => { Test => 'a' } },
+ { Subject => 't2', Status => 'open', CustomFields => { Test => 'b' } },
+);
+
+use_ok 'RT::Report::Tickets';
+
+{
+ my $report = RT::Report::Tickets->new( RT->SystemUser );
+ my %columns = $report->SetupGroupings(
+ Query => 'Queue = '. $q->id,
+ GroupBy => ["CF.{$cfid}"], # TODO: CF.{Name} is not supported at the moment
+ Function => ['COUNT'],
+ );
+ $report->SortEntries;
+
+ my @colors = RT->Config->Get("ChartColors");
+ my $expected = {
+ 'thead' => [ {
+ 'cells' => [
+ { 'value' => 'Custom field Test', 'type' => 'head' },
+ { 'rowspan' => 1, 'value' => 'Ticket count', 'type' => 'head', 'color' => $colors[0] },
+ ],
+ } ],
+ 'tfoot' => [ {
+ 'cells' => [
+ { 'colspan' => 1, 'value' => 'Total', 'type' => 'label' },
+ { 'value' => 2, 'type' => 'value' },
+ ],
+ 'even' => 1
+ } ],
+ 'tbody' => [
+ {
+ 'cells' => [
+ { 'value' => 'a', 'type' => 'label' },
+ { 'query' => "(CF.{$cfid} = 'a')", 'value' => '1', 'type' => 'value' },
+ ],
+ 'even' => 1
+ },
+ {
+ 'cells' => [
+ { 'value' => 'b', 'type' => 'label' },
+ { 'query' => "(CF.{$cfid} = 'b')", 'value' => '1', 'type' => 'value' },
+ ],
+ 'even' => 0
+ },
+ ]
+ };
+
+ my %table = $report->FormatTable( %columns );
+ is_deeply( \%table, $expected, "basic table" );
+}
+
+done_testing;
diff --git a/rt/t/crypt/gnupg/attachments-in-db.t b/rt/t/crypt/gnupg/attachments-in-db.t
new file mode 100644
index 000000000..1a377c341
--- /dev/null
+++ b/rt/t/crypt/gnupg/attachments-in-db.t
@@ -0,0 +1,49 @@
+use strict;
+use warnings;
+
+use RT::Test::GnuPG
+ tests => 12,
+ gnupg_options => {
+ passphrase => 'recipient',
+ 'trust-model' => 'always',
+ }
+;
+
+RT->Config->Get('Crypt')->{'AllowEncryptDataInDB'} = 1;
+
+RT::Test->import_gnupg_key('general@example.com', 'public');
+RT::Test->import_gnupg_key('general@example.com', 'secret');
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'General',
+ CorrespondAddress => 'general@example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue->id,
+ Subject => 'test',
+ Content => 'test',
+ );
+
+ my $txn = $ticket->Transactions->First;
+ ok $txn && $txn->id, 'found first transaction';
+ is $txn->Type, 'Create', 'it is Create transaction';
+
+ my $attach = $txn->Attachments->First;
+ ok $attach && $attach->id, 'found attachment';
+ is $attach->Content, 'test', 'correct content';
+
+ my ($status, $msg) = $attach->Encrypt;
+ ok $status, 'encrypted attachment';
+
+ isnt $attach->Content, 'test', 'correct content';
+
+ ($status, $msg) = $attach->Decrypt;
+ ok $status, 'decrypted attachment';
+
+ is $attach->Content, 'test', 'correct content';
+}
+
+
+
diff --git a/rt/t/crypt/no-signer-address.t b/rt/t/crypt/no-signer-address.t
new file mode 100644
index 000000000..31ba5ebc2
--- /dev/null
+++ b/rt/t/crypt/no-signer-address.t
@@ -0,0 +1,42 @@
+use strict;
+use warnings;
+
+use RT::Test::GnuPG
+ tests => undef,
+ gnupg_options => {
+ passphrase => 'rt-test',
+ 'trust-model' => 'always',
+ }
+;
+
+my $queue;
+{
+ $queue = RT::Test->load_or_create_queue(
+ Name => 'Regression',
+ SignAuto => 1,
+ );
+ ok $queue && $queue->id, 'loaded or created queue';
+ ok !$queue->CorrespondAddress, 'address not set';
+}
+
+# We don't use Test::Warn here, because it apparently only captures up
+# to the first newline -- and the meat of this message is on the fourth
+# line.
+my @warnings;
+local $SIG{__WARN__} = sub {
+ push @warnings, "@_";
+};
+
+my $ticket = RT::Ticket->new( RT->SystemUser );
+my ($status, undef, $msg) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => 'test',
+ Requestor => 'root@localhost',
+);
+ok( $status, "created ticket" ) or diag "error: $msg";
+
+is( scalar @warnings, 1, "Got a warning" );
+like( $warnings[0], qr{signing failed: secret key not available},
+ "Found warning of no secret key");
+
+done_testing;
diff --git a/rt/t/crypt/smime/attachments-in-db.t b/rt/t/crypt/smime/attachments-in-db.t
new file mode 100644
index 000000000..5230938cc
--- /dev/null
+++ b/rt/t/crypt/smime/attachments-in-db.t
@@ -0,0 +1,45 @@
+use strict;
+use warnings;
+
+use RT::Test::SMIME tests => undef;
+
+use IPC::Run3 'run3';
+use String::ShellQuote 'shell_quote';
+use RT::Tickets;
+
+RT->Config->Get('Crypt')->{'AllowEncryptDataInDB'} = 1;
+
+RT::Test::SMIME->import_key('sender@example.com');
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'General',
+ CorrespondAddress => 'sender@example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue->id,
+ Subject => 'test',
+ Content => 'test',
+ );
+
+ my $txn = $ticket->Transactions->First;
+ ok $txn && $txn->id, 'found first transaction';
+ is $txn->Type, 'Create', 'it is Create transaction';
+
+ my $attach = $txn->Attachments->First;
+ ok $attach && $attach->id, 'found attachment';
+ is $attach->Content, 'test', 'correct content';
+
+ my ($status, $msg) = $attach->Encrypt;
+ ok $status, 'encrypted attachment' or diag "error: $msg";
+
+ isnt $attach->Content, 'test', 'correct content';
+
+ ($status, $msg) = $attach->Decrypt;
+ ok $status, 'decrypted attachment' or diag "error: $msg";
+
+ is $attach->Content, 'test', 'correct content';
+}
+
+done_testing;
diff --git a/rt/t/crypt/smime/bad-recipients.t b/rt/t/crypt/smime/bad-recipients.t
new file mode 100644
index 000000000..1dc097ade
--- /dev/null
+++ b/rt/t/crypt/smime/bad-recipients.t
@@ -0,0 +1,58 @@
+use strict;
+use warnings;
+
+use RT::Test::SMIME tests => undef;
+
+use RT::Tickets;
+
+RT::Test::SMIME->import_key('sender@example.com');
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'General',
+ CorrespondAddress => 'sender@example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+{
+ my ($status, $msg) = $queue->SetEncrypt(1);
+ ok $status, "turn on encyption by default"
+ or diag "error: $msg";
+}
+
+my $root;
+{
+ $root = RT::User->new($RT::SystemUser);
+ ok($root->LoadByEmail('root@localhost'), "Loaded user 'root'");
+ ok($root->Load('root'), "Loaded user 'root'");
+ is($root->EmailAddress, 'root@localhost');
+
+ RT::Test::SMIME->import_key( 'root@example.com.crt' => $root );
+}
+
+my $bad_user;
+{
+ $bad_user = RT::Test->load_or_create_user(
+ Name => 'bad_user',
+ EmailAddress => 'baduser@example.com',
+ );
+ ok $bad_user && $bad_user->id, 'created a user without key';
+}
+
+RT::Test->clean_caught_mails;
+
+use Test::Warn;
+
+warnings_like {
+ my $ticket = RT::Ticket->new(RT->SystemUser);
+ my ($status, undef, $msg) = $ticket->Create( Queue => $queue->id, Requestor => [$root->id, $bad_user->id] );
+ ok $status, "created a ticket" or diag "error: $msg";
+
+ my @mails = RT::Test->fetch_caught_mails;
+ is scalar @mails, 3, "autoreply, to bad user, to RT owner";
+
+ like $mails[0], qr{To: baduser\@example\.com}, "notification to bad user";
+ like $mails[1], qr{To: root}, "notification to RT owner";
+ like $mails[1], qr{Recipient 'baduser\@example\.com' is unusable, the reason is 'Key not found'},
+ "notification to owner has error";
+} [qr{Recipient 'baduser\@example\.com' is unusable, the reason is 'Key not found'}];
+
+done_testing;
diff --git a/rt/t/crypt/smime/status-string.t b/rt/t/crypt/smime/status-string.t
new file mode 100644
index 000000000..9317229fb
--- /dev/null
+++ b/rt/t/crypt/smime/status-string.t
@@ -0,0 +1,26 @@
+use strict;
+use warnings;
+
+use RT::Test tests => 2;
+
+require RT::Crypt::SMIME;
+note "simple round trip";
+{
+ my %data = (Foo => 'bar', Baz => 'zoo');
+ is_deeply(
+ [ RT::Crypt::SMIME->ParseStatus( RT::Crypt::SMIME->FormatStatus( \%data, \%data ) ) ],
+ [ \%data, \%data ],
+ );
+}
+
+note "status appendability";
+{
+ my %data = (Foo => 'bar', Baz => 'zoo');
+ is_deeply(
+ [ RT::Crypt::SMIME->ParseStatus(
+ RT::Crypt::SMIME->FormatStatus( \%data )
+ . RT::Crypt::SMIME->FormatStatus( \%data )
+ ) ],
+ [ \%data, \%data ],
+ );
+}
diff --git a/rt/t/customfields/access_via_queue.t b/rt/t/customfields/access_via_queue.t
index a059d69ee..300e777b8 100644
--- a/rt/t/customfields/access_via_queue.t
+++ b/rt/t/customfields/access_via_queue.t
@@ -28,11 +28,8 @@ my $tester = RT::Test->load_or_create_user(
);
ok $tester && $tester->id, 'loaded or created user';
-my $cc_role = RT::Group->new( $queue->CurrentUser );
-$cc_role->LoadQueueRoleGroup( Type => 'Cc', Queue => $queue->id );
-
-my $owner_role = RT::Group->new( $queue->CurrentUser );
-$owner_role->LoadQueueRoleGroup( Type => 'Owner', Queue => $queue->id );
+my $cc_role = $queue->RoleGroup( 'Cc' );
+my $owner_role = $queue->RoleGroup( 'Owner' );
ok( RT::Test->set_rights(
{ Principal => $tester, Right => [qw(SeeQueue ShowTicket CreateTicket ReplyToTicket Watch OwnTicket TakeTicket)] },
diff --git a/rt/t/customfields/api.t b/rt/t/customfields/api.t
index 2e1c07986..a50ca770c 100644
--- a/rt/t/customfields/api.t
+++ b/rt/t/customfields/api.t
@@ -5,12 +5,12 @@ use warnings FATAL => 'all';
use RT::Test nodata => 1, tests => 145;
use Test::Warn;
-# Before we get going, ditch all object_cfs; this will remove
+# Before we get going, ditch all object_cfs; this will remove
# all custom fields systemwide;
my $object_cfs = RT::ObjectCustomFields->new(RT->SystemUser);
$object_cfs->UnLimit();
while (my $ocf = $object_cfs->Next) {
- $ocf->Delete();
+ $ocf->Delete();
}
@@ -23,9 +23,9 @@ $queue2->Create( Name => 'RecordCustomFields2' );
my $ticket = RT::Ticket->new( RT->SystemUser );
$ticket->Create(
- Queue => $queue->Id,
- Requestor => 'root@localhost',
- Subject => 'RecordCustomFields1',
+ Queue => $queue->Id,
+ Requestor => 'root@localhost',
+ Subject => 'RecordCustomFields1',
);
my $cfs = $ticket->CustomFields;
@@ -86,13 +86,13 @@ warning_like {
} qr{Couldn't load custom field};
for (@custom_fields) {
- $cfvs = $ticket->CustomFieldValues( $_->id );
- is( $cfvs->Count, 0 );
+ $cfvs = $ticket->CustomFieldValues( $_->id );
+ is( $cfvs->Count, 0 );
- $cfvs = $ticket->CustomFieldValues( $_->Name );
- is( $cfvs->Count, 0 );
- is( $ticket->FirstCustomFieldValue( $_->id ), undef );
- is( $ticket->FirstCustomFieldValue( $_->Name ), undef );
+ $cfvs = $ticket->CustomFieldValues( $_->Name );
+ is( $cfvs->Count, 0 );
+ is( $ticket->FirstCustomFieldValue( $_->id ), undef );
+ is( $ticket->FirstCustomFieldValue( $_->Name ), undef );
}
# try to add field value with fields that do not exist {{{
@@ -103,84 +103,84 @@ ok(!$status, "shouldn't add value" );
SKIP: {
- skip "TODO: We want fields that are not allowed to set unexpected values", 10;
- for (@custom_fields) {
- ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_ , Value => 'SomeUnexpectedCFValue' );
- ok( !$status, 'value doesn\'t exist');
-
- ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_->id , Value => 'SomeUnexpectedCFValue' );
- ok( !$status, 'value doesn\'t exist');
-
- ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_->Name , Value => 'SomeUnexpectedCFValue' );
- ok( !$status, 'value doesn\'t exist');
- }
-
- # Let check that we did not add value to be sure
- # using only FirstCustomFieldValue sub because
- # we checked other variants allready
- for (@custom_fields) {
- is( $ticket->FirstCustomFieldValue( $_->id ), undef );
- }
-
+ skip "TODO: We want fields that are not allowed to set unexpected values", 10;
+ for (@custom_fields) {
+ ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_ , Value => 'SomeUnexpectedCFValue' );
+ ok( !$status, 'value doesn\'t exist');
+
+ ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_->id , Value => 'SomeUnexpectedCFValue' );
+ ok( !$status, 'value doesn\'t exist');
+
+ ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_->Name , Value => 'SomeUnexpectedCFValue' );
+ ok( !$status, 'value doesn\'t exist');
+ }
+
+ # Let check that we did not add value to be sure
+ # using only FirstCustomFieldValue sub because
+ # we checked other variants allready
+ for (@custom_fields) {
+ is( $ticket->FirstCustomFieldValue( $_->id ), undef );
+ }
+
}
# Add some values to our custom fields
for (@custom_fields) {
- # this should be tested elsewhere
- $_->AddValue( Name => 'Foo' );
- $_->AddValue( Name => 'Bar' );
+ # this should be tested elsewhere
+ $_->AddValue( Name => 'Foo' );
+ $_->AddValue( Name => 'Bar' );
}
my $test_add_delete_cycle = sub {
- my $cb = shift;
- for (@custom_fields) {
- ($status, $msg) = $ticket->AddCustomFieldValue( Field => $cb->($_) , Value => 'Foo' );
- ok( $status, "message: $msg");
- }
-
- # does it exist?
- $cfvs = $ticket->CustomFieldValues;
- is( $cfvs->Count, 3, "We found all three custom fields on our ticket" );
- for (@custom_fields) {
- $cfvs = $ticket->CustomFieldValues( $_->id );
- is( $cfvs->Count, 1 , "we found one custom field when searching by id");
-
- $cfvs = $ticket->CustomFieldValues( $_->Name );
- is( $cfvs->Count, 1 , " We found one custom field when searching by name for " . $_->Name);
- is( $ticket->FirstCustomFieldValue( $_->id ), 'Foo' , "first value by id is foo");
- is( $ticket->FirstCustomFieldValue( $_->Name ), 'Foo' , "first value by name is foo");
- }
- # because our CFs are SingleValue then new value addition should override
- for (@custom_fields) {
- ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_ , Value => 'Bar' );
- ok( $status, "message: $msg");
- }
- $cfvs = $ticket->CustomFieldValues;
- is( $cfvs->Count, 3 );
- for (@custom_fields) {
- $cfvs = $ticket->CustomFieldValues( $_->id );
- is( $cfvs->Count, 1 );
-
- $cfvs = $ticket->CustomFieldValues( $_->Name );
- is( $cfvs->Count, 1 );
- is( $ticket->FirstCustomFieldValue( $_->id ), 'Bar' );
- is( $ticket->FirstCustomFieldValue( $_->Name ), 'Bar' );
- }
- # delete it
- for (@custom_fields ) {
- ($status, $msg) = $ticket->DeleteCustomFieldValue( Field => $_ , Value => 'Bar' );
- ok( $status, "Deleted a custom field value 'Bar' for field ".$_->id.": $msg");
- }
- $cfvs = $ticket->CustomFieldValues;
- is( $cfvs->Count, 0, "The ticket (".$ticket->id.") no longer has any custom field values" );
- for (@custom_fields) {
- $cfvs = $ticket->CustomFieldValues( $_->id );
- is( $cfvs->Count, 0, $ticket->id." has no values for cf ".$_->id );
-
- $cfvs = $ticket->CustomFieldValues( $_->Name );
- is( $cfvs->Count, 0 , $ticket->id." has no values for cf '".$_->Name. "'" );
- is( $ticket->FirstCustomFieldValue( $_->id ), undef , "There is no first custom field value when loading by id" );
- is( $ticket->FirstCustomFieldValue( $_->Name ), undef, "There is no first custom field value when loading by Name" );
- }
+ my $cb = shift;
+ for (@custom_fields) {
+ ($status, $msg) = $ticket->AddCustomFieldValue( Field => $cb->($_) , Value => 'Foo' );
+ ok( $status, "message: $msg");
+ }
+
+ # does it exist?
+ $cfvs = $ticket->CustomFieldValues;
+ is( $cfvs->Count, 3, "We found all three custom fields on our ticket" );
+ for (@custom_fields) {
+ $cfvs = $ticket->CustomFieldValues( $_->id );
+ is( $cfvs->Count, 1 , "we found one custom field when searching by id");
+
+ $cfvs = $ticket->CustomFieldValues( $_->Name );
+ is( $cfvs->Count, 1 , " We found one custom field when searching by name for " . $_->Name);
+ is( $ticket->FirstCustomFieldValue( $_->id ), 'Foo' , "first value by id is foo");
+ is( $ticket->FirstCustomFieldValue( $_->Name ), 'Foo' , "first value by name is foo");
+ }
+ # because our CFs are SingleValue then new value addition should override
+ for (@custom_fields) {
+ ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_ , Value => 'Bar' );
+ ok( $status, "message: $msg");
+ }
+ $cfvs = $ticket->CustomFieldValues;
+ is( $cfvs->Count, 3 );
+ for (@custom_fields) {
+ $cfvs = $ticket->CustomFieldValues( $_->id );
+ is( $cfvs->Count, 1 );
+
+ $cfvs = $ticket->CustomFieldValues( $_->Name );
+ is( $cfvs->Count, 1 );
+ is( $ticket->FirstCustomFieldValue( $_->id ), 'Bar' );
+ is( $ticket->FirstCustomFieldValue( $_->Name ), 'Bar' );
+ }
+ # delete it
+ for (@custom_fields ) {
+ ($status, $msg) = $ticket->DeleteCustomFieldValue( Field => $_ , Value => 'Bar' );
+ ok( $status, "Deleted a custom field value 'Bar' for field ".$_->id.": $msg");
+ }
+ $cfvs = $ticket->CustomFieldValues;
+ is( $cfvs->Count, 0, "The ticket (".$ticket->id.") no longer has any custom field values" );
+ for (@custom_fields) {
+ $cfvs = $ticket->CustomFieldValues( $_->id );
+ is( $cfvs->Count, 0, $ticket->id." has no values for cf ".$_->id );
+
+ $cfvs = $ticket->CustomFieldValues( $_->Name );
+ is( $cfvs->Count, 0 , $ticket->id." has no values for cf '".$_->Name. "'" );
+ is( $ticket->FirstCustomFieldValue( $_->id ), undef , "There is no first custom field value when loading by id" );
+ is( $ticket->FirstCustomFieldValue( $_->Name ), undef, "There is no first custom field value when loading by Name" );
+ }
};
# lets test cycle via CF id
@@ -224,10 +224,10 @@ warning_like {
}
#SKIP: {
-# skip "TODO: should we add CF values to objects via CF Name?", 48;
+# skip "TODO: should we add CF values to objects via CF Name?", 48;
# names are not unique
- # lets test cycle via CF Name
-# $test_add_delete_cycle->( sub { return $_[0]->Name } );
+ # lets test cycle via CF Name
+# $test_add_delete_cycle->( sub { return $_[0]->Name } );
#}
diff --git a/rt/t/customfields/date_search.t b/rt/t/customfields/date_search.t
index 2a8e6ce7a..e9a5a5e76 100644
--- a/rt/t/customfields/date_search.t
+++ b/rt/t/customfields/date_search.t
@@ -3,7 +3,7 @@ use Test::MockTime qw(set_fixed_time restore_time);
use warnings;
use strict;
-use RT::Test nodata => 1, tests => 21;
+use RT::Test nodata => 1, tests => undef;
RT::Test->set_rights(
{ Principal => 'Everyone', Right => [qw(
@@ -16,9 +16,11 @@ ok $q && $q->id, 'loaded or created a queue';
my $user_m = RT::Test->load_or_create_user( Name => 'moscow', Timezone => 'Europe/Moscow' );
ok $user_m && $user_m->id;
+$user_m = RT::CurrentUser->new( $user_m );
my $user_b = RT::Test->load_or_create_user( Name => 'boston', Timezone => 'America/New_York' );
ok $user_b && $user_b->id;
+$user_b = RT::CurrentUser->new( $user_b );
my $cf = RT::CustomField->new(RT->SystemUser);
ok(
@@ -132,6 +134,28 @@ is( $ticket->CustomFieldValues->First->Content, '2010-05-04', 'date in db is' );
is( $tickets->Count, 0, 'did not find the ticket with > 2010-05-05' );
}
+{
+ my $tickets = RT::Tickets->new(RT->SystemUser);
+ $tickets->LimitCustomField(
+ CUSTOMFIELD => $cf->id,
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ );
+
+ is( $tickets->Count, 0, 'did not find the ticket with date IS NULL' );
+}
+
+{
+ my $tickets = RT::Tickets->new(RT->SystemUser);
+ $tickets->LimitCustomField(
+ CUSTOMFIELD => $cf->id,
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ );
+
+ is( $tickets->Count, 1, 'did find the ticket with date IS NOT NULL' );
+}
+
# relative search by users in different TZs
{
my $ticket = RT::Ticket->new(RT->SystemUser);
@@ -162,3 +186,4 @@ is( $ticket->CustomFieldValues->First->Content, '2010-05-04', 'date in db is' );
is( $tickets->Count, 1, 'found the tickets' );
}
+done_testing;
diff --git a/rt/t/customfields/datetime_search.t b/rt/t/customfields/datetime_search.t
index 6b37cf1bc..2eaa0e629 100644
--- a/rt/t/customfields/datetime_search.t
+++ b/rt/t/customfields/datetime_search.t
@@ -3,7 +3,7 @@ use Test::MockTime qw(set_fixed_time restore_time);
use warnings;
use strict;
-use RT::Test nodata => 1, tests => 30;
+use RT::Test nodata => 1, tests => undef;
RT->Config->Set( 'Timezone' => 'EST5EDT' ); # -04:00
RT::Test->set_rights(
@@ -17,9 +17,11 @@ ok $q && $q->id, 'loaded or created a queue';
my $user_m = RT::Test->load_or_create_user( Name => 'moscow', Timezone => 'Europe/Moscow' );
ok $user_m && $user_m->id;
+$user_m = RT::CurrentUser->new( $user_m );
my $user_b = RT::Test->load_or_create_user( Name => 'boston', Timezone => 'America/New_York' );
ok $user_b && $user_b->id;
+$user_b = RT::CurrentUser->new( $user_b );
my $cf = RT::CustomField->new(RT->SystemUser);
ok(
@@ -204,6 +206,29 @@ while( my $ticket = $tickets->Next ) {
is( $tickets->Count, 0);
}
+{
+ my $tickets = RT::Tickets->new(RT->SystemUser);
+ $tickets->LimitCustomField(
+ CUSTOMFIELD => $cf->id,
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ );
+
+ is( $tickets->Count, 0, 'did not find the ticket with date IS NULL' );
+}
+
+{
+ my $tickets = RT::Tickets->new(RT->SystemUser);
+ $tickets->LimitCustomField(
+ CUSTOMFIELD => $cf->id,
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ );
+
+ is( $tickets->Count, 2, 'did find the ticket with date IS NOT NULL' );
+}
+
+
# search by relative date with '=', but date only
{
my $ticket = RT::Ticket->new(RT->SystemUser);
@@ -235,3 +260,4 @@ while( my $ticket = $tickets->Next ) {
is( $tickets->Count, 0);
}
+done_testing;
diff --git a/rt/t/customfields/external.t b/rt/t/customfields/external.t
index 73549166a..4b84144ee 100644
--- a/rt/t/customfields/external.t
+++ b/rt/t/customfields/external.t
@@ -3,7 +3,7 @@ use warnings;
use strict;
use RT;
-use RT::Test nodata => 1, tests => 13;
+use RT::Test nodata => 1, tests => undef;
sub new (*) {
my $class = shift;
@@ -51,6 +51,10 @@ isa_ok( $cf, 'RT::CustomField' );
}
ok( !$failure, "all values have name" );
is( $values->Count, $count, "count is correct" );
+ is( $values->CustomFieldObject->id, $cf->id, "Values stored the CF id" );
+ is( $values->CustomFieldObject, $cf, "Values stored the identical CF object" );
+ is( $values->First->CustomFieldObj->id, $cf->id, "A value stored the CF id" );
+ is( $values->First->CustomFieldObj, $cf, "A value stored the identical CF object" );
}
{
@@ -59,3 +63,5 @@ isa_ok( $cf, 'RT::CustomField' );
($ret, $msg) = $cf->SetValuesClass('RT::CustomFieldValues::Groups');
ok $ret, 'Reverting this CF as external source values based' or diag "error: $msg";
}
+
+done_testing;
diff --git a/rt/t/customfields/ip.t b/rt/t/customfields/ip.t
index 37afcb71d..35a245c5e 100644
--- a/rt/t/customfields/ip.t
+++ b/rt/t/customfields/ip.t
@@ -26,7 +26,7 @@ my $cf;
diag "load and check basic properties of the IP CF" if $ENV{'TEST_VERBOSE'};
{
my $cfs = RT::CustomFields->new($RT::SystemUser);
- $cfs->Limit( FIELD => 'Name', VALUE => 'IP' );
+ $cfs->Limit( FIELD => 'Name', VALUE => 'IP', CASESENSITIVE => 0 );
is( $cfs->Count, 1, "found one CF with name 'IP'" );
$cf = $cfs->First;
@@ -269,8 +269,8 @@ diag "create a ticket with an IP of 10.0.0.1 and search for doesn't match '10.0.
$tickets->FromSQL("id=$id AND CF.{IP} NOT LIKE '10.0.0.'");
} [qr/not a valid IPAddress/], "caught warning about valid IP address";
- SKIP: {
- skip "partical ip parse causes ambiguity", 1;
+ TODO: {
+ local $TODO = "partial ip parse causes ambiguity";
is( $tickets->Count, 0, "should not have found the ticket" );
}
}
@@ -281,8 +281,8 @@ diag "test the operators in search page" if $ENV{'TEST_VERBOSE'};
$agent->get_ok( $baseurl . "/Search/Build.html?Query=Queue='General'" );
$agent->content_contains('CF.{IP}', 'got CF.{IP}');
my $form = $agent->form_name('BuildQuery');
- my $op = $form->find_input("'CF.{IP}'Op");
- ok( $op, "found 'CF.{IP}'Op" );
+ my $op = $form->find_input("CF.{IP}Op");
+ ok( $op, "found CF.{IP}Op" );
is_deeply( [ $op->possible_values ], [ '=', '!=', '<', '>' ], 'op values' );
}
diff --git a/rt/t/customfields/iprange.t b/rt/t/customfields/iprange.t
index 4bccd9ac1..2a323a352 100644
--- a/rt/t/customfields/iprange.t
+++ b/rt/t/customfields/iprange.t
@@ -21,7 +21,7 @@ my $cf;
diag "load and check basic properties of the IP CF" if $ENV{'TEST_VERBOSE'};
{
my $cfs = RT::CustomFields->new( $RT::SystemUser );
- $cfs->Limit( FIELD => 'Name', VALUE => 'IP' );
+ $cfs->Limit( FIELD => 'Name', VALUE => 'IP', CASESENSITIVE => 0 );
is( $cfs->Count, 1, "found one CF with name 'IP'" );
$cf = $cfs->First;
@@ -461,8 +461,8 @@ diag "test the operators in search page" if $ENV{'TEST_VERBOSE'};
$agent->get_ok( $baseurl . "/Search/Build.html?Query=Queue='General'" );
$agent->content_contains('CF.{IP}', 'got CF.{IP}');
my $form = $agent->form_name('BuildQuery');
- my $op = $form->find_input("'CF.{IP}'Op");
- ok( $op, "found 'CF.{IP}'Op" );
+ my $op = $form->find_input("CF.{IP}Op");
+ ok( $op, "found CF.{IP}Op" );
is_deeply( [ $op->possible_values ], [ '=', '!=', '<', '>' ], 'op values' );
}
diff --git a/rt/t/customfields/iprangev6.t b/rt/t/customfields/iprangev6.t
index 84fec16a0..445df333c 100644
--- a/rt/t/customfields/iprangev6.t
+++ b/rt/t/customfields/iprangev6.t
@@ -21,7 +21,7 @@ my $cf;
diag "load and check basic properties of the IP CF" if $ENV{'TEST_VERBOSE'};
{
my $cfs = RT::CustomFields->new( $RT::SystemUser );
- $cfs->Limit( FIELD => 'Name', VALUE => 'IP' );
+ $cfs->Limit( FIELD => 'Name', VALUE => 'IP', CASESENSITIVE => 0 );
is( $cfs->Count, 1, "found one CF with name 'IP'" );
$cf = $cfs->First;
diff --git a/rt/t/customfields/ipv6.t b/rt/t/customfields/ipv6.t
index 3b02ef9d7..24f7c2a48 100644
--- a/rt/t/customfields/ipv6.t
+++ b/rt/t/customfields/ipv6.t
@@ -26,7 +26,7 @@ my $cf;
diag "load and check basic properties of the IP CF" if $ENV{'TEST_VERBOSE'};
{
my $cfs = RT::CustomFields->new($RT::SystemUser);
- $cfs->Limit( FIELD => 'Name', VALUE => 'IP' );
+ $cfs->Limit( FIELD => 'Name', VALUE => 'IP', CASESENSITIVE => 0 );
is( $cfs->Count, 1, "found one CF with name 'IP'" );
$cf = $cfs->First;
@@ -246,8 +246,8 @@ diag "create a ticket with an IP of abcd:23:: and search for doesn't match 'abcd
$tickets->FromSQL("id=$id AND CF.{IP} NOT LIKE 'abcd:23'");
} [qr/not a valid IPAddress/], "caught warning about IPAddress";
- SKIP: {
- skip "partical ip parse can causes ambiguity", 1;
+ TODO: {
+ local $TODO = "partial ip parse can causes ambiguity";
is( $tickets->Count, 0, "should not have found the ticket" );
}
}
diff --git a/rt/t/customfields/sort_order.t b/rt/t/customfields/sort_order.t
index ba0b654be..24e047ebf 100644
--- a/rt/t/customfields/sort_order.t
+++ b/rt/t/customfields/sort_order.t
@@ -50,7 +50,7 @@ diag "reorder CFs: C, A and B";
{
$m->get( '/Admin/Queues/' );
$m->follow_link_ok( {text => $queue->id} );
- $m->follow_link_ok( {id => 'page-ticket-custom-fields'} );
+ $m->follow_link_ok( {id => 'page-custom-fields-tickets'} );
my @tmp = ($m->content =~ /(CF [ABC])/g);
is_deeply(\@tmp, ['CF B', 'CF A', 'CF C']);
diff --git a/rt/t/customfields/transaction.t b/rt/t/customfields/transaction.t
index f2e660ee2..47435bc87 100644
--- a/rt/t/customfields/transaction.t
+++ b/rt/t/customfields/transaction.t
@@ -3,7 +3,7 @@ use warnings;
use strict;
use Data::Dumper;
-use RT::Test nodata => 1, tests => 14;
+use RT::Test nodata => 1, tests => 13;
use_ok('RT');
use_ok('RT::Transactions');
@@ -43,17 +43,8 @@ is ($txn_cf->id, $cf->id, "It's the right custom field");
my $values = $trans->CustomFieldValues($txn_cf->id);
is ($values->Count, 0, "It has no values");
-# Old API
-my %cf_updates = ( 'CustomField-'.$cf->id => 'Testing');
-$trans->UpdateCustomFields( ARGSRef => \%cf_updates);
-
- $values = $trans->CustomFieldValues($txn_cf->id);
-is ($values->Count, 1, "It has one value");
-
-# New API
-
-$trans->UpdateCustomFields( 'CustomField-'.$cf->id => 'Test two');
- $values = $trans->CustomFieldValues($txn_cf->id);
-is ($values->Count, 2, "it has two values");
+$trans->UpdateCustomFields( 'CustomField-'.$cf->id => 'Test');
+$values = $trans->CustomFieldValues($txn_cf->id);
+is ($values->Count, 1, "it has a value");
# TODO ok(0, "Should updating custom field values remove old values?");
diff --git a/rt/t/customfields/transaction_searching.t b/rt/t/customfields/transaction_searching.t
new file mode 100644
index 000000000..0958b5ea6
--- /dev/null
+++ b/rt/t/customfields/transaction_searching.t
@@ -0,0 +1,140 @@
+use strict;
+use warnings;
+
+use RT::Test tests => 'no_declare';
+
+my $initialdata = RT::Test::get_relocatable_file("transaction-cfs" => "..", "data", "initialdata");
+my ($rv, $msg) = RT->DatabaseHandle->InsertData( $initialdata, undef, disconnect_after => 0 );
+ok($rv, "Inserted test data from $initialdata")
+ or diag "Error: $msg";
+
+create_tickets(
+ Spam => { },
+ Coffee => { Billable => "No", },
+ Phone => { Billable => "Yes", Who => ["Telecom", "Information Technology"], When => "2013-06-25", Location => "Geology" },
+ Stacks => { Billable => "Yes", Who => "Library", When => "2013-06-01" },
+ Benches => { Billable => "Yes", Location => "Outdoors" },
+);
+
+# Sanity check
+results_are("CF.Location IS NOT NULL", [qw( Phone Benches )]);
+results_are("CF.Location IS NULL", [qw( Spam Coffee Stacks )]);
+
+# TODO: Ideal behaviour of TxnCF IS NULL not yet determined
+#results_are("TxnCF.Billable IS NULL", [qw( Spam )]);
+
+results_are("TxnCF.Billable IS NOT NULL", [qw( Coffee Phone Stacks Benches )]);
+results_are("TxnCF.Billable = 'No'", [qw( Coffee )]);
+results_are("TxnCF.Billable = 'Yes'", [qw( Phone Stacks Benches )]);
+results_are("TxnCF.Billable = 'Yes' AND CF.Location IS NOT NULL", [qw( Phone Benches )]);
+results_are("TxnCF.Billable = 'Yes' AND CF.Location = 'Outdoors'", [qw( Benches )]);
+results_are("TxnCF.Billable = 'Yes' AND CF.Location LIKE 'o'", [qw( Phone Benches )]);
+
+results_are("TxnCF.Who = 'Telecom' OR TxnCF.Who = 'Library'", [qw( Phone Stacks )]);
+
+# TODO: Negative searching finds tickets with at least one txn doesn't have the value
+#results_are("TxnCF.Who != 'Library'", [qw( Spam Coffee Phone Benches )]);
+
+results_are("TxnCF.When > '2013-06-24'", [qw( Phone )]);
+results_are("TxnCF.When < '2013-06-24'", [qw( Stacks )]);
+results_are("TxnCF.When >= '2013-06-01' and TxnCF.When <= '2013-06-30'", [qw( Phone Stacks )]);
+
+results_are("TxnCF.Who LIKE 'e'", [qw( Phone )]);
+
+# TODO: Negative searching finds tickets with at least one txn doesn't have the value
+#results_are("TxnCF.Who NOT LIKE 'e'", [qw( Spam Coffee Stacks Benches )]);
+
+results_are("TxnCF.Who NOT LIKE 'e' and TxnCF.Who IS NOT NULL", [qw( Stacks )]);
+
+
+# Multiple CFs with same name applied to different queues
+clear_tickets();
+create_tickets(
+ BlueNone => { Queue => "Blues" },
+ PurpleNone => { Queue => "Purples" },
+
+ Blue => { Queue => "Blues", Color => "Blue" },
+ Purple => { Queue => "Purples", Color => "Purple" },
+);
+
+# Queue-specific txn CFs
+results_are("TxnCF.Blues.{Color} = 'Blue'", [qw( Blue )]);
+results_are("TxnCF.Blues.{Color} = 'Purple'", []);
+
+# Multiple transaction CFs by name
+results_are("TxnCF.{Color} IS NOT NULL", [qw( Blue Purple )]);
+results_are("TxnCF.{Color} = 'Blue'", [qw( Blue )]);
+results_are("TxnCF.{Color} = 'Purple'", [qw( Purple )]);
+results_are("TxnCF.{Color} LIKE 'e'", [qw( Blue Purple )]);
+
+done_testing;
+
+sub results_are {
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+ my $query = shift;
+ my $expected = shift;
+ my %expected = map { $_ => 1 } @$expected;
+ my @unexpected;
+
+ my $tickets = RT::Tickets->new(RT->SystemUser);
+ my ($ok, $msg) = $tickets->FromSQL($query);
+ ok($ok, "Searched: $query")
+ or return diag $msg;
+ for my $t (@{$tickets->ItemsArrayRef || []}) {
+ if (delete $expected{$t->Subject}) {
+ ok(1, "Found expected ticket ".$t->Subject);
+ } else {
+ push @unexpected, $t->Subject;
+ }
+ }
+ ok(0, "Didn't find expected ticket $_")
+ for grep $expected{$_}, @$expected;
+ ok(0, "Found unexpected tickets: ".join ", ", @unexpected)
+ if @unexpected;
+}
+
+sub create_tickets {
+ my %ticket = @_;
+ for my $subject (sort keys %ticket) {
+ my %data = %{$ticket{$subject}};
+ my $location = delete $data{Location};
+ my $queue = delete $data{Queue} || "General";
+
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ my ($ok, $msg) = $ticket->Create(
+ Queue => $queue,
+ Subject => $subject,
+ );
+ ok($ticket->id, "Created ticket: $msg") or next;
+
+ if ($location) {
+ ($ok, $msg) = $ticket->AddCustomFieldValue( Field => "Location", Value => $location );
+ ok($ok, "Added Location: $msg") or next;
+ }
+
+ my ($txnid, $txnmsg, $txn) = $ticket->Correspond( Content => "test transaction" );
+ unless ($txnid) {
+ RT->Logger->error("Unable to correspond on ticket $ok: $txnmsg");
+ next;
+ }
+ for my $name (sort keys %data) {
+ my $values = ref $data{$name} ? $data{$name} : [$data{$name}];
+ for my $v (@$values) {
+ ($ok, $msg) = $txn->_AddCustomFieldValue(
+ Field => $name,
+ Value => $v,
+ RecordTransaction => 0
+ );
+ ok($ok, "Added txn CF $name value '$v'")
+ or diag $msg;
+ }
+ }
+ }
+}
+
+sub clear_tickets {
+ my $tickets = RT::Tickets->new( RT->SystemUser );
+ $tickets->FromSQL("id > 0");
+ $_->SetStatus("deleted") for @{$tickets->ItemsArrayRef};
+}
diff --git a/rt/t/data/configs/apache2.2+fastcgi.conf.in b/rt/t/data/configs/apache2.2+fastcgi.conf.in
index 03eaa9a70..329b0561e 100644
--- a/rt/t/data/configs/apache2.2+fastcgi.conf.in
+++ b/rt/t/data/configs/apache2.2+fastcgi.conf.in
@@ -34,7 +34,6 @@ FastCgiServer %%RT_SBIN_PATH%%/rt-server.fcgi \
-initial-env RT_SITE_CONFIG=%%RT_SITE_CONFIG%% \
-initial-env RT_TESTING=1
-Alias /NoAuth/images/ %%DOCUMENT_ROOT%%/NoAuth/images/
ScriptAlias / %%RT_SBIN_PATH%%/rt-server.fcgi/
DocumentRoot "%%DOCUMENT_ROOT%%"
diff --git a/rt/t/data/initialdata/initialdata b/rt/t/data/initialdata/initialdata
new file mode 100644
index 000000000..19e019652
--- /dev/null
+++ b/rt/t/data/initialdata/initialdata
@@ -0,0 +1,101 @@
+# Samples of all things we support in initialdata
+
+@Queues = (
+ {
+ Name => 'Test Queue',
+ CorrespondAddress => 'help@example.com',
+ CommentAddress => 'help-comment@example.com',
+ }
+);
+
+@Scrips = (
+ {
+ Description => 'Test Without Stage',
+ ScripCondition => 'On Resolve',
+ ScripAction => 'Notify Requestors',
+ Template => 'Correspondence in HTML',
+ },
+ {
+ Queue => 'General',
+ Description => 'Test Without Stage and One Queue',
+ ScripCondition => 'On Resolve',
+ ScripAction => 'Notify Requestors',
+ Template => 'Correspondence in HTML',
+ },
+ {
+ Queue => ['General', 'Test Queue'],
+ Description => 'Test Without Stage and Two Queues',
+ ScripCondition => 'On Resolve',
+ ScripAction => 'Notify Requestors',
+ Template => 'Correspondence in HTML',
+ },
+ {
+ Description => 'Test TransactionCreate',
+ ScripCondition => 'On Resolve',
+ ScripAction => 'Notify Requestors',
+ Template => 'Correspondence in HTML',
+ Stage => 'TransactionCreate',
+ },
+ {
+ Description => 'Test TransactionBatch',
+ ScripCondition => 'On Resolve',
+ ScripAction => 'Notify Requestors',
+ Template => 'Correspondence in HTML',
+ Stage => 'TransactionBatch',
+ },
+);
+
+@CustomFields = (
+ {
+ Name => 'Favorite color',
+ Type => 'FreeformSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Queue => 'Test Queue',
+ },
+);
+
+@Groups = (
+ {
+ Name => 'Test Employees',
+ Description => 'All of the employees of my company',
+ Attributes => [
+ {
+ Name => 'SavedSearch',
+ Description => 'Stalled Tickets in Test Queue',
+ Content => {
+ Query => "Status = 'stalled' AND Queue = 'Test Queue'",
+ OrderBy => 'id',
+ Order => 'DESC'
+ },
+ },
+ ],
+ }
+);
+
+@ACL = (
+ { GroupId => 'Test Employees',
+ GroupDomain => 'UserDefined',
+ CF => 'Favorite Color',
+ Queue => 'Test Queue',
+ Right => ['SeeCustomField', 'ModifyCustomField'],
+ },
+);
+
+@Attributes = ({
+ Name => 'SavedSearch',
+ Description => 'New Tickets in Test Queue',
+ Object => sub {
+ my $GroupName = 'Test Employees';
+ my $group = RT::Group->new( RT->SystemUser );
+
+ my( $ret, $msg ) = $group->LoadUserDefinedGroup( $GroupName );
+ die $msg unless $ret;
+
+ return $group;
+ },
+ Content => {
+ Query => "Status = 'new' AND Queue = 'Test Queue'",
+ OrderBy => 'id',
+ Order => 'DESC'
+ },
+});
diff --git a/rt/t/data/initialdata/transaction-cfs b/rt/t/data/initialdata/transaction-cfs
new file mode 100644
index 000000000..25c8274ff
--- /dev/null
+++ b/rt/t/data/initialdata/transaction-cfs
@@ -0,0 +1,52 @@
+use strict;
+use warnings;
+
+our @Queues = (
+ { Name => "Blues" },
+ { Name => "Purples" },
+);
+
+our @CustomFields = (
+ map +{
+ LookupType => RT::Transaction->CustomFieldLookupType,
+ MaxValues => 1,
+ Type => "Freeform",
+ %$_
+ },
+ { Name => "Billable",
+ Type => "Select",
+ Values => [
+ { Name => "Yes", SortOrder => 1 },
+ { Name => "No", SortOrder => 2 },
+ ],
+ },
+ { Name => "Who",
+ Type => "SelectMultiple",
+ Values => [
+ map +{ Name => $_ },
+ "Facilities",
+ "Information Technology",
+ "Library",
+ "Telecom",
+ ],
+ },
+ { Name => "When",
+ Type => "Date",
+ },
+
+ # Two CFs named the same, but each applied to only one queue
+ # Note: Queue => ref forces RT::Handle to apply rather than
+ # RT::CustomField->Create; the former respects LookupType, the latter
+ # doesn't.
+ { Name => "Color",
+ Queue => ["Blues"],
+ },
+ { Name => "Color",
+ Queue => ["Purples"],
+ },
+
+ # Some ticket CFs to test mixed searches
+ { Name => "Location",
+ LookupType => RT::Ticket->CustomFieldLookupType,
+ },
+);
diff --git a/rt/t/data/plugins/RT-Extension-PSGIWrap/lib/RT/Extension/PSGIWrap.pm b/rt/t/data/plugins/RT-Extension-PSGIWrap/lib/RT/Extension/PSGIWrap.pm
new file mode 100644
index 000000000..1d0a55e33
--- /dev/null
+++ b/rt/t/data/plugins/RT-Extension-PSGIWrap/lib/RT/Extension/PSGIWrap.pm
@@ -0,0 +1,16 @@
+package RT::Extension::PSGIWrap;
+
+use base 'Plack::Middleware';
+
+sub call {
+ my ( $self, $env ) = @_;
+ my $res = $self->app->($env);
+ return $self->response_cb( $res, sub {
+ my $headers = shift->[1];
+ Plack::Util::header_set($headers, 'X-RT-PSGIWrap' => '1');
+ } );
+}
+
+sub PSGIWrap { return shift->wrap(@_) }
+
+1;
diff --git a/rt/t/data/smime/keys/demoCA/cacert.pem b/rt/t/data/smime/keys/demoCA/cacert.pem
new file mode 100644
index 000000000..de734a98f
--- /dev/null
+++ b/rt/t/data/smime/keys/demoCA/cacert.pem
@@ -0,0 +1,58 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 11236924883769032812 (0x9bf193a560cd006c)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com
+ Validity
+ Not Before: Aug 28 21:19:44 2013 GMT
+ Not After : Aug 26 21:19:44 2023 GMT
+ Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:be:cc:62:70:bf:42:ee:9d:f0:05:04:2b:05:46:
+ 4e:c9:60:6a:b4:31:8c:a5:60:25:79:05:61:88:fe:
+ 36:9e:63:24:bf:33:91:6f:6a:90:27:81:47:5e:2f:
+ 49:54:19:c7:02:51:37:d9:ff:0b:9b:8a:cd:ed:7f:
+ b7:6b:bc:0a:de:e5:c8:32:f7:a4:16:51:d1:3f:a4:
+ 02:96:98:09:83:e2:ed:81:19:bb:e3:d4:2b:f1:87:
+ 97:03:08:05:e6:f7:65:c6:90:48:9d:75:07:31:93:
+ 04:6d:09:b7:0f:df:fa:f2:b3:ff:e1:44:f4:18:03:
+ 4f:59:b6:ba:d2:36:8b:0e:b3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 8D:1B:2D:BD:BD:24:E8:19:62:AE:4C:C9:2A:58:90:08:1C:D1:05:2B
+ X509v3 Authority Key Identifier:
+ keyid:8D:1B:2D:BD:BD:24:E8:19:62:AE:4C:C9:2A:58:90:08:1C:D1:05:2B
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 7b:f5:8f:d2:b9:44:34:fe:91:ab:1d:52:d3:10:2d:23:75:05:
+ 8e:17:70:be:52:11:b0:8e:ee:f6:33:50:7c:c7:82:f3:c4:d2:
+ 98:90:b3:a6:ad:00:33:36:dc:95:f4:4e:45:d2:09:e9:88:ae:
+ 88:a2:72:e4:75:95:7a:78:31:16:34:a3:50:e0:c9:25:7f:65:
+ 51:d4:59:20:23:d5:3e:35:79:cf:ed:3d:3c:8c:d1:79:b0:99:
+ d3:6b:99:ed:32:c5:29:7a:82:8a:98:cb:c6:95:c7:52:59:7c:
+ f8:1d:fd:18:b8:ef:4d:1f:9d:5d:09:b0:eb:68:50:ed:c0:21:
+ 61:eb
+-----BEGIN CERTIFICATE-----
+MIICyDCCAjGgAwIBAgIJAJvxk6VgzQBsMA0GCSqGSIb3DQEBBQUAMH0xCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQxETAPBgNVBAMMCENBIE93bmVyMSMwIQYJKoZIhvcNAQkB
+FhRjYS5vd25lckBleGFtcGxlLmNvbTAeFw0xMzA4MjgyMTE5NDRaFw0yMzA4MjYy
+MTE5NDRaMH0xCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
+VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxETAPBgNVBAMMCENBIE93bmVy
+MSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBleGFtcGxlLmNvbTCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEAvsxicL9C7p3wBQQrBUZOyWBqtDGMpWAleQVhiP42
+nmMkvzORb2qQJ4FHXi9JVBnHAlE32f8Lm4rN7X+3a7wK3uXIMvekFlHRP6QClpgJ
+g+LtgRm749Qr8YeXAwgF5vdlxpBInXUHMZMEbQm3D9/68rP/4UT0GANPWba60jaL
+DrMCAwEAAaNQME4wHQYDVR0OBBYEFI0bLb29JOgZYq5MySpYkAgc0QUrMB8GA1Ud
+IwQYMBaAFI0bLb29JOgZYq5MySpYkAgc0QUrMAwGA1UdEwQFMAMBAf8wDQYJKoZI
+hvcNAQEFBQADgYEAe/WP0rlENP6Rqx1S0xAtI3UFjhdwvlIRsI7u9jNQfMeC88TS
+mJCzpq0AMzbclfRORdIJ6YiuiKJy5HWVengxFjSjUODJJX9lUdRZICPVPjV5z+09
+PIzRebCZ02uZ7TLFKXqCipjLxpXHUll8+B39GLjvTR+dXQmw62hQ7cAhYes=
+-----END CERTIFICATE-----
diff --git a/rt/t/data/smime/keys/demoCA/private/cakey.pem b/rt/t/data/smime/keys/demoCA/private/cakey.pem
new file mode 100644
index 000000000..ad95c8df5
--- /dev/null
+++ b/rt/t/data/smime/keys/demoCA/private/cakey.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,8580147E208C5674
+
+GTz9b2WFdP7gNjUWQnhWqq2o8bpYPbmPTLSyefUfI2UxL0bW96VBKyLpx/FO7Zxr
+itfItZA4A7hG+CJLa6pz5C4/9onzHeihhLLDov3pE1hjZwwPFs1IHM/q1KLU4tK4
+yb/Xx1pw/3L1nlvWy4CQ/F1pmHG+akQNopy2Ru0XWLVw/gysmff8GW94Awx5MyZd
+81tvuFu2U2BYdPbC/Zc+hrlTdqG2btgdll39gjRoNvLbA4tifLNy264yOS71lxF/
+rOtavqzCULo/cTTumcZzbMnowjpdrPliuGg6rox3xc3zFjNfogu7okH53XtOZClQ
+n3/jjqI1LEUhOC0omUck4q3XbaCWGg6X/MUL8Fae+jDUs5NISt75xVs1uJdU2DuB
+xUwtgzJCbt5eovbczmoKm44nY3TqsITG+vuI7qim3wds8WPbM4lnz7fx0AbHYOIK
+ceCxDJirQRmblImJybPHJL6uuCo91Ahx7NmLcGw35QhhQf/EfKPJyh4Ih7+Cn2il
+EGW9RWS7hl9JSCOZs30YwPQz1bgCHIt0+31WSK4hbZ/IyPnDrMY4XNVCeWxX2xcF
+y2VjpoW305Glu2D522n0jUe/YJGHBaA7ijQkLpw2nL0qstlkq/2RoGZaDm0gUCUG
+dNbmeQrOF7dJtSKKjxy/DqMPw+ymn/YCXVaCPvIEuqHyFKnUNJ/ak4vnAeV7Jrhz
+0OlyqNR4O/FKjf4pgsTHqodTQrxHA2d/n/Evnes/TevnIp6sa8HpkMcJc2DL9hKB
+aIWFQxGynI/S9juZXSKdTOMcUbSsicVELzzk+spHlZ9xKpuBvJvWxQ==
+-----END RSA PRIVATE KEY-----
diff --git a/rt/t/data/smime/keys/demoCA/serial b/rt/t/data/smime/keys/demoCA/serial
new file mode 100644
index 000000000..7c398625f
--- /dev/null
+++ b/rt/t/data/smime/keys/demoCA/serial
@@ -0,0 +1 @@
+8A6ACD51BE94A017
diff --git a/rt/t/data/smime/keys/otherCA/cacert.pem b/rt/t/data/smime/keys/otherCA/cacert.pem
new file mode 100644
index 000000000..bebd5f3a6
--- /dev/null
+++ b/rt/t/data/smime/keys/otherCA/cacert.pem
@@ -0,0 +1,80 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 16372135729078323798 (0xe33582b3ca31ca56)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=AU, ST=Some-State, O=Other Widgits, LLC, CN=CA Owner/emailAddress=ca.owner@example.net
+ Validity
+ Not Before: Aug 28 22:16:28 2013 GMT
+ Not After : Aug 28 22:16:28 2023 GMT
+ Subject: C=AU, ST=Some-State, O=Other Widgits, LLC, CN=CA Owner/emailAddress=ca.owner@example.net
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d6:b6:53:04:53:e8:98:91:c6:6a:ab:27:c3:ba:
+ 01:53:e1:f3:56:1e:90:c9:61:7e:73:37:36:80:49:
+ a9:b4:6a:9f:3a:d8:08:6f:ba:82:64:c5:85:92:41:
+ 53:71:25:ec:18:85:1c:9e:80:4b:30:f7:16:b4:f8:
+ 07:3e:f7:9b:aa:2d:9f:f8:08:a4:0a:e6:9e:0a:d2:
+ 2f:06:59:28:53:9e:b4:77:8a:2b:f0:b5:c6:ca:af:
+ 41:be:ed:17:12:0f:37:2e:e9:b8:43:3a:76:20:fd:
+ e8:81:91:b8:bf:03:92:76:1f:40:d3:e0:44:fd:34:
+ c7:f3:d4:f6:77:c9:52:59:da:37:95:ab:54:a7:11:
+ a5:1a:03:fa:cc:71:19:72:cb:29:39:15:69:b5:f6:
+ 5b:16:22:d8:ed:a4:b3:b5:83:ed:69:d9:91:7f:2d:
+ 0c:af:4f:c6:4a:4a:4f:1d:a3:dc:1f:10:f4:77:c8:
+ 48:e5:94:64:3b:29:3d:9d:16:0c:d2:30:3a:44:0d:
+ a4:87:04:04:84:ec:fd:19:82:08:77:b5:77:64:f4:
+ ce:bc:6c:a5:c1:b7:17:7e:a2:4a:de:28:62:40:5e:
+ 3d:77:5c:9a:09:dc:7e:a6:b6:a3:34:ca:73:a4:c2:
+ 42:74:4e:d8:52:2d:98:4f:28:6e:89:93:7e:34:3b:
+ eb:37
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 7F:75:3B:B2:1E:CF:EF:D6:A7:D1:42:F8:1C:A9:13:63:CF:C9:0E:5A
+ X509v3 Authority Key Identifier:
+ keyid:7F:75:3B:B2:1E:CF:EF:D6:A7:D1:42:F8:1C:A9:13:63:CF:C9:0E:5A
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 44:f7:e8:e6:af:a9:be:cf:28:51:dc:86:14:e2:4d:e4:14:9f:
+ 09:4d:cb:e9:10:2c:ef:21:ec:b0:8c:14:57:59:45:52:b4:e3:
+ db:f4:34:e3:39:b6:de:0c:eb:68:78:db:d0:21:d2:c1:51:18:
+ ce:33:14:a4:4d:91:88:eb:cc:b0:4a:93:73:75:48:e8:56:ce:
+ 29:c9:07:73:18:28:20:e1:2e:ba:0f:cc:4c:26:e7:45:d5:4c:
+ 60:89:ef:1d:d7:7a:a5:80:62:bf:30:da:ac:bf:be:f8:54:f3:
+ fc:8a:09:1c:89:2d:2a:12:20:99:66:54:a0:78:50:f0:46:44:
+ 9d:ad:95:81:83:c0:47:38:b8:4a:81:3c:72:49:68:a2:a1:04:
+ c7:d3:e9:e8:6f:65:ce:10:11:7f:0a:8b:96:ce:4e:1e:55:c7:
+ 54:34:25:5e:ba:95:62:ad:45:43:b1:69:70:d4:c4:33:29:56:
+ cd:45:08:7d:e5:1e:5c:77:55:7b:f7:34:ea:c5:d5:48:21:b1:
+ 71:a5:02:16:50:78:64:e4:01:85:28:3e:e4:b8:f6:f8:02:3d:
+ 01:23:ba:2c:54:c3:72:a5:2a:3d:41:fd:c1:15:60:37:0b:65:
+ bf:23:bd:33:f6:d8:75:03:71:46:47:97:93:ae:bc:7f:76:1e:
+ f3:5f:ba:0f
+-----BEGIN CERTIFICATE-----
+MIIDwTCCAqmgAwIBAgIJAOM1grPKMcpWMA0GCSqGSIb3DQEBBQUAMHcxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRswGQYDVQQKDBJPdGhlciBXaWRn
+aXRzLCBMTEMxETAPBgNVBAMMCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5v
+d25lckBleGFtcGxlLm5ldDAeFw0xMzA4MjgyMjE2MjhaFw0yMzA4MjgyMjE2Mjha
+MHcxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRswGQYDVQQKDBJP
+dGhlciBXaWRnaXRzLCBMTEMxETAPBgNVBAMMCENBIE93bmVyMSMwIQYJKoZIhvcN
+AQkBFhRjYS5vd25lckBleGFtcGxlLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBANa2UwRT6JiRxmqrJ8O6AVPh81YekMlhfnM3NoBJqbRqnzrYCG+6
+gmTFhZJBU3El7BiFHJ6ASzD3FrT4Bz73m6otn/gIpArmngrSLwZZKFOetHeKK/C1
+xsqvQb7tFxIPNy7puEM6diD96IGRuL8DknYfQNPgRP00x/PU9nfJUlnaN5WrVKcR
+pRoD+sxxGXLLKTkVabX2WxYi2O2ks7WD7WnZkX8tDK9PxkpKTx2j3B8Q9HfISOWU
+ZDspPZ0WDNIwOkQNpIcEBITs/RmCCHe1d2T0zrxspcG3F36iSt4oYkBePXdcmgnc
+fqa2ozTKc6TCQnRO2FItmE8obomTfjQ76zcCAwEAAaNQME4wHQYDVR0OBBYEFH91
+O7Iez+/Wp9FC+BypE2PPyQ5aMB8GA1UdIwQYMBaAFH91O7Iez+/Wp9FC+BypE2PP
+yQ5aMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAET36Oavqb7PKFHc
+hhTiTeQUnwlNy+kQLO8h7LCMFFdZRVK049v0NOM5tt4M62h429Ah0sFRGM4zFKRN
+kYjrzLBKk3N1SOhWzinJB3MYKCDhLroPzEwm50XVTGCJ7x3XeqWAYr8w2qy/vvhU
+8/yKCRyJLSoSIJlmVKB4UPBGRJ2tlYGDwEc4uEqBPHJJaKKhBMfT6ehvZc4QEX8K
+i5bOTh5Vx1Q0JV66lWKtRUOxaXDUxDMpVs1FCH3lHlx3VXv3NOrF1UghsXGlAhZQ
+eGTkAYUoPuS49vgCPQEjuixUw3KlKj1B/cEVYDcLZb8jvTP22HUDcUZHl5OuvH92
+HvNfug8=
+-----END CERTIFICATE-----
diff --git a/rt/t/data/smime/keys/otherCA/private/cakey.pem b/rt/t/data/smime/keys/otherCA/private/cakey.pem
new file mode 100644
index 000000000..7447fbbd6
--- /dev/null
+++ b/rt/t/data/smime/keys/otherCA/private/cakey.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA1rZTBFPomJHGaqsnw7oBU+HzVh6QyWF+czc2gEmptGqfOtgI
+b7qCZMWFkkFTcSXsGIUcnoBLMPcWtPgHPvebqi2f+AikCuaeCtIvBlkoU560d4or
+8LXGyq9Bvu0XEg83Lum4Qzp2IP3ogZG4vwOSdh9A0+BE/TTH89T2d8lSWdo3latU
+pxGlGgP6zHEZcsspORVptfZbFiLY7aSztYPtadmRfy0Mr0/GSkpPHaPcHxD0d8hI
+5ZRkOyk9nRYM0jA6RA2khwQEhOz9GYIId7V3ZPTOvGylwbcXfqJK3ihiQF49d1ya
+Cdx+prajNMpzpMJCdE7YUi2YTyhuiZN+NDvrNwIDAQABAoIBABa6G9V0cEVeAMuf
+rEjacnOHkjNGbvrx9+mIKZuwsGbpdktLPLFe45h5E+dkRMnQQsphpKLeX5ciQGQN
+cO7oVLDRvYIKoBqLSKVKlDGu1EbtoJqapIYJJ66imGn2PJ/rvmKX2Ko9EO3zEl5M
+p2qInUMlkb4bmhHXOWcE3sXVKINcFSjUxx/EkE/hS4z4gZX1ZFz8r6NmnnSk3G5p
+yS7JlTx9gIEqIp3LFmgPY8yhjdbQ+Qsde4FU1MSWWvmE4+LT4AicTAUGf61VEc+s
+gVHVHl9yuOGJYRaKuqHevCMxr8Bh27WpPT+NGdPxVRZJ/kSoDKPdrv9oU99Rtgwp
+RaanetECgYEA8Uuk/2pqOHQKd83jHynejJSK/B1XxAddn9PHWNw9gYFBPnrxL76/
+lIuEAHyjcqYC4XV7dmEpWklWFInV1cBUAGimX4ykwArQcneq9nJXxR5KQ7ofozB+
+eYZ1/QvhySJeg+ucsyi99HLFL845aGf4y48VkHD9MKnKMCNwYcytgBkCgYEA48v2
+6K70spBv/j4QQ3v/5ovsmvv9xQei5mPZKawKOx6OxDZJ0he6ltGQ6bJoNFFtcC/u
+Lb/uX/0Ah/V5gurAVQAJU53o2t2Ai32NX80b2lUXi0H8nGvOxW8i95SUWx3dn1yz
+EBJMgfjH5XJV+kZVUWeOIIl+hPXew+u3XAdq788CgYAobDa4/zfKO05hoaEx4E7D
+GENsVvIUCfPaSZ00urinEGNAt1HeYMMxfGnhtv+evkbvREIpo79Mu8pq6GhlRbIM
+23s7uJEFBwrCkl+Wp7Mid5+TVwPjz8TwUOFFQg9SJarVyMvYi7O+1tdH2fFuFzTr
+zQ2cxAD2fQs9I0K5b5OFSQKBgQDXM0QiE86VtsAmhslkh4t8aKnwzKiz73/keWWZ
+6a6MpVSoZsUcllAu1PI65NFuw5JIzu8LB2wSAHj0+GF/3XgvlOY6uU5XHbSnksfx
+PlrWy1Z/t6oGuA5SFKkLDbGN1swdFj0PrMnca4Ok7nvtAW7uhY8Oi/YbdA+sNU42
+wccznwKBgBswApZRfZKCUD/1Khdz+HmG/YEPbk4Kqgi7a8MKpT4No0hjRsoO1HV2
+WggtvBjzagHkzZNkjJv+WvSU0DHk/JQnWIZFJd+72ZGR56neq8iXIQ28LnnGhcvk
+m0YNZzB8MCvD5ZztH6GU5ecPzO+4Tjkruau2an4etLSs60ogKy5u
+-----END RSA PRIVATE KEY-----
diff --git a/rt/t/data/smime/keys/otherCA/serial b/rt/t/data/smime/keys/otherCA/serial
new file mode 100644
index 000000000..5dd9faac8
--- /dev/null
+++ b/rt/t/data/smime/keys/otherCA/serial
@@ -0,0 +1 @@
+FB573398E9349E9D
diff --git a/rt/t/data/smime/keys/root@example.com.crt b/rt/t/data/smime/keys/root@example.com.crt
new file mode 100644
index 000000000..45e3eb448
--- /dev/null
+++ b/rt/t/data/smime/keys/root@example.com.crt
@@ -0,0 +1,43 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 9974010075738841110 (0x8a6acd51be94a016)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com
+ Validity
+ Not Before: Aug 28 21:41:07 2013 GMT
+ Not After : Aug 28 21:41:07 2023 GMT
+ Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Enoch Root/emailAddress=root@example.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (512 bit)
+ Modulus:
+ 00:b2:77:b9:bc:09:7d:14:8e:6b:6f:7e:33:a9:95:
+ 21:5d:f3:3c:91:61:f1:bc:5c:1d:7e:e7:54:25:e8:
+ cb:5f:b7:18:0e:23:26:00:42:09:bd:89:da:5c:06:
+ cb:52:08:43:f6:4e:fe:dd:f8:0a:8a:95:35:8f:4a:
+ 25:16:da:e6:bf
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 1a:cd:7e:0e:e0:6f:90:b7:22:0e:4d:79:4d:6a:9b:ac:a1:6a:
+ ab:85:32:9c:86:9c:d2:10:96:f7:e0:00:2c:7d:3c:16:a4:ff:
+ dd:9e:37:fb:a3:7a:43:ab:2f:ee:c4:ff:be:77:0f:40:f8:0e:
+ 45:3e:48:46:bf:ec:e1:b0:46:8d:13:37:7a:a6:d1:7c:16:cb:
+ 28:6b:37:88:4d:0a:12:6b:87:b9:7c:d9:c4:d7:57:93:b9:f6:
+ 21:26:1b:32:88:1d:cd:84:0f:6a:f9:05:0a:76:01:de:5e:99:
+ 86:10:fc:7d:ee:d5:70:b2:44:99:41:0a:d7:0e:e8:5b:c9:ca:
+ 10:39
+-----BEGIN CERTIFICATE-----
+MIICKzCCAZQCCQCKas1RvpSgFjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMREwDwYDVQQDDAhDQSBPd25lcjEjMCEGCSqGSIb3DQEJARYUY2Eu
+b3duZXJAZXhhbXBsZS5jb20wHhcNMTMwODI4MjE0MTA3WhcNMjMwODI4MjE0MTA3
+WjB7MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMY
+SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDEwpFbm9jaCBSb290MR8w
+HQYJKoZIhvcNAQkBFhByb290QGV4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQAD
+SwAwSAJBALJ3ubwJfRSOa29+M6mVIV3zPJFh8bxcHX7nVCXoy1+3GA4jJgBCCb2J
+2lwGy1IIQ/ZO/t34CoqVNY9KJRba5r8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQAa
+zX4O4G+QtyIOTXlNapusoWqrhTKchpzSEJb34AAsfTwWpP/dnjf7o3pDqy/uxP++
+dw9A+A5FPkhGv+zhsEaNEzd6ptF8FssoazeITQoSa4e5fNnE11eTufYhJhsyiB3N
+hA9q+QUKdgHeXpmGEPx97tVwskSZQQrXDuhbycoQOQ==
+-----END CERTIFICATE-----
diff --git a/rt/t/data/smime/keys/root@example.com.csr b/rt/t/data/smime/keys/root@example.com.csr
new file mode 100644
index 000000000..a72677a55
--- /dev/null
+++ b/rt/t/data/smime/keys/root@example.com.csr
@@ -0,0 +1,9 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBNTCB4AIBADB7MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEh
+MB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDEwpFbm9j
+aCBSb290MR8wHQYJKoZIhvcNAQkBFhByb290QGV4YW1wbGUuY29tMFwwDQYJKoZI
+hvcNAQEBBQADSwAwSAJBALJ3ubwJfRSOa29+M6mVIV3zPJFh8bxcHX7nVCXoy1+3
+GA4jJgBCCb2J2lwGy1IIQ/ZO/t34CoqVNY9KJRba5r8CAwEAAaAAMA0GCSqGSIb3
+DQEBBQUAA0EABuN/lyQxMY6DNb9XZ7H+UZLJrNYei1HRvfIXig7EvkSDEnArSwfZ
+uzAeLo3mnIp7WiDk3M7e19LQFkERs2xvHw==
+-----END CERTIFICATE REQUEST-----
diff --git a/rt/t/data/smime/keys/root@example.com.key b/rt/t/data/smime/keys/root@example.com.key
new file mode 100644
index 000000000..7b24e4e82
--- /dev/null
+++ b/rt/t/data/smime/keys/root@example.com.key
@@ -0,0 +1,12 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,6356CE6012402B9B
+
+Lco5rf3/rHlShktH/o6NHF1mVH00k+pZ3bWodejMaHW1ofZXe9/yjzPM2jqqi+Dj
+xmzZ9R/MijO07vpxWHqdvhXeFf0TW67gW413M/bwiRd/rV0mUFz81nowFe9e15tm
+Itku1sePFvvL/UUxBGeYhplHAP6e76JqQcJTkBaG04KitH9GHtj1HFQR8P9/8h6d
+f0ZtU8wqnhkZvtzb72ZLwsw0YZ7R9YLIqCmOn1twW0CC77deACy+deJOC0N4CxW6
++jEGbJKMN5rOPsFiieDzZXAaTlGd6qXVWaxUPYH89yWedYoAZgbi6zxGGwNGbc/Q
+2Y7g+qHi3L30uJvgJEGihIM+9iAKUJSazyGYl9Xl2FwTpNFOMJAYFyNKNv5FHwdm
+deoslrbEXVtqurOQYr955cyqs2NN+JYLsz5nNnfBpGo=
+-----END RSA PRIVATE KEY-----
diff --git a/rt/t/data/smime/keys/root@example.com.pem b/rt/t/data/smime/keys/root@example.com.pem
new file mode 100644
index 000000000..802475e66
--- /dev/null
+++ b/rt/t/data/smime/keys/root@example.com.pem
@@ -0,0 +1,55 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 9974010075738841110 (0x8a6acd51be94a016)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com
+ Validity
+ Not Before: Aug 28 21:41:07 2013 GMT
+ Not After : Aug 28 21:41:07 2023 GMT
+ Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Enoch Root/emailAddress=root@example.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (512 bit)
+ Modulus:
+ 00:b2:77:b9:bc:09:7d:14:8e:6b:6f:7e:33:a9:95:
+ 21:5d:f3:3c:91:61:f1:bc:5c:1d:7e:e7:54:25:e8:
+ cb:5f:b7:18:0e:23:26:00:42:09:bd:89:da:5c:06:
+ cb:52:08:43:f6:4e:fe:dd:f8:0a:8a:95:35:8f:4a:
+ 25:16:da:e6:bf
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 1a:cd:7e:0e:e0:6f:90:b7:22:0e:4d:79:4d:6a:9b:ac:a1:6a:
+ ab:85:32:9c:86:9c:d2:10:96:f7:e0:00:2c:7d:3c:16:a4:ff:
+ dd:9e:37:fb:a3:7a:43:ab:2f:ee:c4:ff:be:77:0f:40:f8:0e:
+ 45:3e:48:46:bf:ec:e1:b0:46:8d:13:37:7a:a6:d1:7c:16:cb:
+ 28:6b:37:88:4d:0a:12:6b:87:b9:7c:d9:c4:d7:57:93:b9:f6:
+ 21:26:1b:32:88:1d:cd:84:0f:6a:f9:05:0a:76:01:de:5e:99:
+ 86:10:fc:7d:ee:d5:70:b2:44:99:41:0a:d7:0e:e8:5b:c9:ca:
+ 10:39
+-----BEGIN CERTIFICATE-----
+MIICKzCCAZQCCQCKas1RvpSgFjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMREwDwYDVQQDDAhDQSBPd25lcjEjMCEGCSqGSIb3DQEJARYUY2Eu
+b3duZXJAZXhhbXBsZS5jb20wHhcNMTMwODI4MjE0MTA3WhcNMjMwODI4MjE0MTA3
+WjB7MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMY
+SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDEwpFbm9jaCBSb290MR8w
+HQYJKoZIhvcNAQkBFhByb290QGV4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQAD
+SwAwSAJBALJ3ubwJfRSOa29+M6mVIV3zPJFh8bxcHX7nVCXoy1+3GA4jJgBCCb2J
+2lwGy1IIQ/ZO/t34CoqVNY9KJRba5r8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQAa
+zX4O4G+QtyIOTXlNapusoWqrhTKchpzSEJb34AAsfTwWpP/dnjf7o3pDqy/uxP++
+dw9A+A5FPkhGv+zhsEaNEzd6ptF8FssoazeITQoSa4e5fNnE11eTufYhJhsyiB3N
+hA9q+QUKdgHeXpmGEPx97tVwskSZQQrXDuhbycoQOQ==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,6356CE6012402B9B
+
+Lco5rf3/rHlShktH/o6NHF1mVH00k+pZ3bWodejMaHW1ofZXe9/yjzPM2jqqi+Dj
+xmzZ9R/MijO07vpxWHqdvhXeFf0TW67gW413M/bwiRd/rV0mUFz81nowFe9e15tm
+Itku1sePFvvL/UUxBGeYhplHAP6e76JqQcJTkBaG04KitH9GHtj1HFQR8P9/8h6d
+f0ZtU8wqnhkZvtzb72ZLwsw0YZ7R9YLIqCmOn1twW0CC77deACy+deJOC0N4CxW6
++jEGbJKMN5rOPsFiieDzZXAaTlGd6qXVWaxUPYH89yWedYoAZgbi6zxGGwNGbc/Q
+2Y7g+qHi3L30uJvgJEGihIM+9iAKUJSazyGYl9Xl2FwTpNFOMJAYFyNKNv5FHwdm
+deoslrbEXVtqurOQYr955cyqs2NN+JYLsz5nNnfBpGo=
+-----END RSA PRIVATE KEY-----
diff --git a/rt/t/data/smime/keys/sender@example.com.crt b/rt/t/data/smime/keys/sender@example.com.crt
new file mode 100644
index 000000000..9497a2022
--- /dev/null
+++ b/rt/t/data/smime/keys/sender@example.com.crt
@@ -0,0 +1,43 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 9974010075738841109 (0x8a6acd51be94a015)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com
+ Validity
+ Not Before: Aug 28 21:41:45 2013 GMT
+ Not After : Aug 28 21:41:45 2023 GMT
+ Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=sender/emailAddress=sender@example.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (512 bit)
+ Modulus:
+ 00:a8:38:41:90:1d:e7:cd:2b:cb:62:cf:ad:ff:70:
+ f6:44:5d:f3:4b:7e:21:75:b6:5c:e1:7e:c2:27:3b:
+ 85:eb:72:9b:5a:94:0a:69:1d:83:ca:c5:91:b2:3f:
+ 04:72:61:e4:b8:eb:5b:ce:b5:10:77:d8:a7:df:8b:
+ c9:5a:14:15:61
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 91:74:84:00:98:40:30:6b:a6:61:6b:7b:d7:c9:9d:6e:ef:bb:
+ c8:ba:8b:83:15:62:3e:d1:c2:9d:1c:4e:ce:09:ce:d8:4f:4a:
+ 49:a8:97:e8:3b:ed:82:2c:a3:20:45:72:f3:d9:23:66:93:d5:
+ 54:14:ce:ce:cf:27:04:52:43:b4:a7:0b:ac:b8:45:a3:96:bf:
+ 2f:43:59:61:02:7a:36:39:9c:01:ad:b7:63:6e:b5:b6:29:cb:
+ 79:78:93:95:25:24:4a:83:bd:1d:d6:07:86:06:6a:fa:04:60:
+ 6e:ba:41:11:0a:cb:b2:84:03:ac:30:55:94:ed:b2:2d:3c:c5:
+ 99:6f
+-----BEGIN CERTIFICATE-----
+MIICKTCCAZICCQCKas1RvpSgFTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMREwDwYDVQQDDAhDQSBPd25lcjEjMCEGCSqGSIb3DQEJARYUY2Eu
+b3duZXJAZXhhbXBsZS5jb20wHhcNMTMwODI4MjE0MTQ1WhcNMjMwODI4MjE0MTQ1
+WjB5MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMY
+SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDEwZzZW5kZXIxITAfBgkq
+hkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sA
+MEgCQQCoOEGQHefNK8tiz63/cPZEXfNLfiF1tlzhfsInO4XrcptalAppHYPKxZGy
+PwRyYeS461vOtRB32Kffi8laFBVhAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAkXSE
+AJhAMGumYWt718mdbu+7yLqLgxViPtHCnRxOzgnO2E9KSaiX6DvtgiyjIEVy89kj
+ZpPVVBTOzs8nBFJDtKcLrLhFo5a/L0NZYQJ6NjmcAa23Y261tinLeXiTlSUkSoO9
+HdYHhgZq+gRgbrpBEQrLsoQDrDBVlO2yLTzFmW8=
+-----END CERTIFICATE-----
diff --git a/rt/t/data/smime/keys/sender@example.com.csr b/rt/t/data/smime/keys/sender@example.com.csr
new file mode 100644
index 000000000..18fa799a4
--- /dev/null
+++ b/rt/t/data/smime/keys/sender@example.com.csr
@@ -0,0 +1,9 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBMzCB3gIBADB5MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEh
+MB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDEwZzZW5k
+ZXIxITAfBgkqhkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmNvbTBcMA0GCSqGSIb3
+DQEBAQUAA0sAMEgCQQCoOEGQHefNK8tiz63/cPZEXfNLfiF1tlzhfsInO4Xrcpta
+lAppHYPKxZGyPwRyYeS461vOtRB32Kffi8laFBVhAgMBAAGgADANBgkqhkiG9w0B
+AQUFAANBAFoi5bepEWsl0cQiO7k314NAuHenXaVrsWt3kPWfwgWn0aLp3aH86aZ5
+g4MYNjJzTqnkU1apyY8MV+BUZaXfnII=
+-----END CERTIFICATE REQUEST-----
diff --git a/rt/t/data/smime/keys/sender@example.com.key b/rt/t/data/smime/keys/sender@example.com.key
new file mode 100644
index 000000000..26ed85066
--- /dev/null
+++ b/rt/t/data/smime/keys/sender@example.com.key
@@ -0,0 +1,12 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,605762440BC8261C
+
+MpUs66ILz2ePX4NKQ408LOAwvmpLLLnSwDX/Zmr/LG4SyZ7AnY6dY06XB6suev3m
+AS+xm/LM44lvUaDvPnl4gO8jnCw3D1yktcfeHc6XqcFx2U9AiUTawmoSTKwrT4P+
+tnpSrrBJY3WghElbckK3vbZboX9Eld+dJjGPf9YqMrkixObp0ul1zW7Wt+aSEV5B
+ngP3VmQinB1EjSUhGF/gsFzhJsutsX4Z1SE/U4K1A1OPl3Oz4e+9VLGgUN4ao84y
+pcNYdXO/BCax4Uk8l0r0DcMd73P9WZs9+bcSgmkqduWCXkNXDbfi4RTOEn19Ehpu
+MyKc3JrskRhNRN1vfMSRFUsrmppxBdPfkrGrTCJNBuL7zdbQh9k9XMaNzfw5Tt2R
+oCWay5shBGEEKXRLIEqzO+Jx1BWVlWwxUwDLr73ItHA=
+-----END RSA PRIVATE KEY-----
diff --git a/rt/t/data/smime/keys/sender@example.com.pem b/rt/t/data/smime/keys/sender@example.com.pem
new file mode 100644
index 000000000..500bc83f7
--- /dev/null
+++ b/rt/t/data/smime/keys/sender@example.com.pem
@@ -0,0 +1,55 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 9974010075738841109 (0x8a6acd51be94a015)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=CA Owner/emailAddress=ca.owner@example.com
+ Validity
+ Not Before: Aug 28 21:41:45 2013 GMT
+ Not After : Aug 28 21:41:45 2023 GMT
+ Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=sender/emailAddress=sender@example.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (512 bit)
+ Modulus:
+ 00:a8:38:41:90:1d:e7:cd:2b:cb:62:cf:ad:ff:70:
+ f6:44:5d:f3:4b:7e:21:75:b6:5c:e1:7e:c2:27:3b:
+ 85:eb:72:9b:5a:94:0a:69:1d:83:ca:c5:91:b2:3f:
+ 04:72:61:e4:b8:eb:5b:ce:b5:10:77:d8:a7:df:8b:
+ c9:5a:14:15:61
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 91:74:84:00:98:40:30:6b:a6:61:6b:7b:d7:c9:9d:6e:ef:bb:
+ c8:ba:8b:83:15:62:3e:d1:c2:9d:1c:4e:ce:09:ce:d8:4f:4a:
+ 49:a8:97:e8:3b:ed:82:2c:a3:20:45:72:f3:d9:23:66:93:d5:
+ 54:14:ce:ce:cf:27:04:52:43:b4:a7:0b:ac:b8:45:a3:96:bf:
+ 2f:43:59:61:02:7a:36:39:9c:01:ad:b7:63:6e:b5:b6:29:cb:
+ 79:78:93:95:25:24:4a:83:bd:1d:d6:07:86:06:6a:fa:04:60:
+ 6e:ba:41:11:0a:cb:b2:84:03:ac:30:55:94:ed:b2:2d:3c:c5:
+ 99:6f
+-----BEGIN CERTIFICATE-----
+MIICKTCCAZICCQCKas1RvpSgFTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMREwDwYDVQQDDAhDQSBPd25lcjEjMCEGCSqGSIb3DQEJARYUY2Eu
+b3duZXJAZXhhbXBsZS5jb20wHhcNMTMwODI4MjE0MTQ1WhcNMjMwODI4MjE0MTQ1
+WjB5MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMY
+SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDEwZzZW5kZXIxITAfBgkq
+hkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sA
+MEgCQQCoOEGQHefNK8tiz63/cPZEXfNLfiF1tlzhfsInO4XrcptalAppHYPKxZGy
+PwRyYeS461vOtRB32Kffi8laFBVhAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAkXSE
+AJhAMGumYWt718mdbu+7yLqLgxViPtHCnRxOzgnO2E9KSaiX6DvtgiyjIEVy89kj
+ZpPVVBTOzs8nBFJDtKcLrLhFo5a/L0NZYQJ6NjmcAa23Y261tinLeXiTlSUkSoO9
+HdYHhgZq+gRgbrpBEQrLsoQDrDBVlO2yLTzFmW8=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,605762440BC8261C
+
+MpUs66ILz2ePX4NKQ408LOAwvmpLLLnSwDX/Zmr/LG4SyZ7AnY6dY06XB6suev3m
+AS+xm/LM44lvUaDvPnl4gO8jnCw3D1yktcfeHc6XqcFx2U9AiUTawmoSTKwrT4P+
+tnpSrrBJY3WghElbckK3vbZboX9Eld+dJjGPf9YqMrkixObp0ul1zW7Wt+aSEV5B
+ngP3VmQinB1EjSUhGF/gsFzhJsutsX4Z1SE/U4K1A1OPl3Oz4e+9VLGgUN4ao84y
+pcNYdXO/BCax4Uk8l0r0DcMd73P9WZs9+bcSgmkqduWCXkNXDbfi4RTOEn19Ehpu
+MyKc3JrskRhNRN1vfMSRFUsrmppxBdPfkrGrTCJNBuL7zdbQh9k9XMaNzfw5Tt2R
+oCWay5shBGEEKXRLIEqzO+Jx1BWVlWwxUwDLr73ItHA=
+-----END RSA PRIVATE KEY-----
diff --git a/rt/t/data/smime/mails/1-signed.eml b/rt/t/data/smime/mails/1-signed.eml
new file mode 100644
index 000000000..57c09b7ac
--- /dev/null
+++ b/rt/t/data/smime/mails/1-signed.eml
@@ -0,0 +1,74 @@
+X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709B50.6040609@example.com>
+Date: Tue, 09 Feb 2010 02:16:32 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:1
+Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha1; boundary="------------ms010504020705000203070202"
+
+This is a cryptographically signed message in MIME format.
+
+--------------ms010504020705000203070202
+Content-Type: text/plain; charset=UTF-8; format=flowed
+Content-Transfer-Encoding: quoted-printable
+
+This is a test email with detached signature.
+ID:1
+
+
+--------------ms010504020705000203070202
+Content-Type: application/pkcs7-signature; name="smime.p7s"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+Content-Description: S/MIME Cryptographic Signature
+
+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
+SMKKtZ91LwT/wfT8cqCADfh0LaTIFAAAAAAAAA==
+--------------ms010504020705000203070202--
diff --git a/rt/t/data/smime/mails/2-signed-attachment.eml b/rt/t/data/smime/mails/2-signed-attachment.eml
new file mode 100644
index 000000000..5c8ab27cb
--- /dev/null
+++ b/rt/t/data/smime/mails/2-signed-attachment.eml
@@ -0,0 +1,90 @@
+X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709C48.4030908@example.com>
+Date: Tue, 09 Feb 2010 02:20:40 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:2
+Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha1; boundary="------------ms090206030705090204050109"
+
+This is a cryptographically signed message in MIME format.
+
+--------------ms090206030705090204050109
+Content-Type: multipart/mixed;
+ boundary="------------090009090000030005040209"
+
+This is a multi-part message in MIME format.
+--------------090009090000030005040209
+Content-Type: text/plain; charset=UTF-8; format=flowed
+Content-Transfer-Encoding: quoted-printable
+
+This is a test email with a text attachment.
+ID:2
+
+
+--------------090009090000030005040209
+Content-Type: text/plain; x-mac-type="0"; x-mac-creator="0";
+ name="text-attachment"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="text-attachment"
+
+VGhpcyBpcyBhIHRlc3QgYXR0YWNobWVudC4gIFRoZSBtYWdpYyB3b3JkIGlzOiAgemFuemli
+YXIuCg==
+--------------090009090000030005040209--
+
+--------------ms090206030705090204050109
+Content-Type: application/pkcs7-signature; name="smime.p7s"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+Content-Description: S/MIME Cryptographic Signature
+
+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
+tziPXZ2qeGSB/mmBf7mwfjPYgGZkoQAAAAAAAA==
+--------------ms090206030705090204050109--
diff --git a/rt/t/data/smime/mails/3-signed-binary.eml b/rt/t/data/smime/mails/3-signed-binary.eml
new file mode 100644
index 000000000..ff3449daf
--- /dev/null
+++ b/rt/t/data/smime/mails/3-signed-binary.eml
@@ -0,0 +1,95 @@
+X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709CC5.4010607@example.com>
+Date: Tue, 09 Feb 2010 02:22:45 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:3
+Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha1; boundary="------------ms020101060809030506070801"
+
+This is a cryptographically signed message in MIME format.
+
+--------------ms020101060809030506070801
+Content-Type: multipart/mixed;
+ boundary="------------060502090104050607070406"
+
+This is a multi-part message in MIME format.
+--------------060502090104050607070406
+Content-Type: text/plain; charset=UTF-8; format=flowed
+Content-Transfer-Encoding: quoted-printable
+
+This is a test email with binary attachment and detached signature.
+ID:3
+
+
+--------------060502090104050607070406
+Content-Type: image/png;
+ name="favicon.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="favicon.png"
+
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QAAAAAAAD5Q7t/AAAB
+BElEQVR42u1WWw6DMAwz0+5FbzbvZuZk2cfUritpea77wVIRIBQ7dhsBdIQkM8AMMJImyW6d
+BXweyJ7UAMnUvQFGwHp2bizIJfUTUHZO8j/k1pt8lntvchbdH8ndtqyS+Gj3fyVPAtZAkm3N
+ffCyi/chBIQQ3iqs3cQ0TZCERzbhngDocOS4z94wXTCmu2V45LuQW8hsSWpaP8v9sy+2IRZj
+ZTP5ububbp8Az4ly5W6QqJ33YwKSkIYbZVy5uNMFsOJGLaLTBMRC8Yy7bmR/OD8TUB00DvkW
+AcPSB7FIPoji0AGQBtU4jt+Fh1R6Dcc6B2Znv4HTHTiAJkfXv+ILFy5c8PACgtsiPj7qOgAA
+AAAASUVORK5CYII=
+--------------060502090104050607070406--
+
+--------------ms020101060809030506070801
+Content-Type: application/pkcs7-signature; name="smime.p7s"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+Content-Description: S/MIME Cryptographic Signature
+
+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
+53knAqbgZ9v3viuGCo0fj6RvFU4CHgAAAAAAAA==
+--------------ms020101060809030506070801--
diff --git a/rt/t/data/smime/mails/4-encrypted-plain.eml b/rt/t/data/smime/mails/4-encrypted-plain.eml
new file mode 100644
index 000000000..481a858b0
--- /dev/null
+++ b/rt/t/data/smime/mails/4-encrypted-plain.eml
@@ -0,0 +1,32 @@
+X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709D39.6090102@example.com>
+Date: Tue, 09 Feb 2010 02:24:41 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:4
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+MIAGCSqGSIb3DQEHA6CAMIACAQAxggHIMIHhAgEAMIGKMH0xCzAJBgNVBAYTAkFVMRMwEQYD
+VQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxETAP
+BgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBleGFtcGxlLmNvbQIJ
+AIpqzVG+lKAVMA0GCSqGSIb3DQEBAQUABEB4NUWl1nJB+cQVXPRmHEj+uxapSKRQ2PFeP+Eh
+VJHyPpsgf8APPxhS/6s1DBIWE9fwkghiM7JTgYZow42q/tdfMIHhAgEAMIGKMH0xCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz
+IFB0eSBMdGQxETAPBgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBl
+eGFtcGxlLmNvbQIJAIpqzVG+lKAWMA0GCSqGSIb3DQEBAQUABEAFd/zqPwzjH8gKZoGUA/yY
+7aDfJzlAsg2tar47hM1xeSTgJ5JpluYy9V/43oK++Q+3HceI4P+aE91CjMrcbqvlMIAGCSqG
+SIb3DQEHATAaBggqhkiG9w0DAjAOAgIAoAQIsDsGzNXDhPmggASBiBdO/BdF/SrEjAeIi2is
+G71RuJ/lcnNlAltdk9lMJLoOxxTaa495lk8HuVD0xFYQueNS8AsACRjkOwgSf9Avh1elFRV5
+U3XZrmCOqbnDsWRTr2KEc8K9CXxqY6CwFizaoFlTftpji7W3ATU2+/QufIKYBS7Za3Zq1u7M
+HLbv4GLdEP1GVPDj2fAECP7azsN17fhCAAAAAAAAAAAAAA==
diff --git a/rt/t/data/smime/mails/5-encrypted-attachment.eml b/rt/t/data/smime/mails/5-encrypted-attachment.eml
new file mode 100644
index 000000000..b6fb9b46b
--- /dev/null
+++ b/rt/t/data/smime/mails/5-encrypted-attachment.eml
@@ -0,0 +1,42 @@
+X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709D8E.1000001@example.com>
+Date: Tue, 09 Feb 2010 02:26:06 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:5
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+MIAGCSqGSIb3DQEHA6CAMIACAQAxggHIMIHhAgEAMIGKMH0xCzAJBgNVBAYTAkFVMRMwEQYD
+VQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxETAP
+BgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBleGFtcGxlLmNvbQIJ
+AIpqzVG+lKAVMA0GCSqGSIb3DQEBAQUABEALN6in6tg2C0yVmkb0XWJr6qRLrwrJLiqcoamd
+a3VAyQeHcqIB14UYuHiN6zZA2lABUI1DsjFlDiCEg8TSyJuAMIHhAgEAMIGKMH0xCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz
+IFB0eSBMdGQxETAPBgNVBAMTCENBIE93bmVyMSMwIQYJKoZIhvcNAQkBFhRjYS5vd25lckBl
+eGFtcGxlLmNvbQIJAIpqzVG+lKAWMA0GCSqGSIb3DQEBAQUABEBDqZbltMcqBRxIshfZ+jSa
+49l6RJAX6HVIBVZRu77rmlyVs2ft18qP0YVgwDPgD5Iok4c1Taemo3Rg7M2bHLwlMIAGCSqG
+SIb3DQEHATAaBggqhkiG9w0DAjAOAgIAoAQIWcv/5Jc8r/uggASCAqjneNECFsRSAPgwjW7G
+Hi+zLy+vPBLNfWgyuEAlKGeM347PdUciZNLhiz3D49lfHmVypGOxYTNU0kfeJVTW5bfwYHdS
+ZmPRx49tNJt08GR0eqbePKZtH0/0BW7LF1//lcNeJchsSdyRvkMB8zvTBhnNVhUSQWumrbda
+OUqvVpSdqx4SeqbyiyQKI+7AiZ2ChcZX9fA+YoiWT85NVtmgBNMMne0uHgmdBMaQHF4bTXvY
+/Mg8ew7Vg3TkVjg9QlaAe7JGrgyvSx/H4f+sn6mb68NaF5jGjiwen4a6ThRJO8lIJ30rTlb+
+WMqszV6OZK2ieWNn5BQlXOI/ew92UIuoyd5PtDkrLbkYio20KfCLpLbt1tvT8ZG0csgg9PO3
+iM3S0PWpjg4axknCYonphwSczsPcvUYZ+y4cIMdXvk5A3byMAQjLPYh0N6F9Q9tETc3HhDhA
+rSdVRot6JILv/tUs2ISPxJcMlLh8TcHZNchnRUcDg0wojULs49rONZIw8UGzbZi1H6IoGebk
+1HBsskw21pPDUjG7LpV0bKFKan0wxE5kJP1Xk5EN2Yw+2EHDE19QHs/ru2FdTjfbtcQFGlT1
+yiNUI7UwAPpCPyLoOpfvwYL2u4nSnbnKHCdjDHl4VAre8bngCMTzdRM91w/nydjpHfBbv+l5
+/EOKbYPC/SNG3IJZy70iExcXU+WydHdYCW0NhR7K1sCdwDsUpziQMvzlkJKclPC894Yljqnn
+83S8G2z3pTJ+SEAabdXY1GmYdfFeLGwnRmegzmWe0wCZKz0m4CabkDX4h0u8xu/C/5gbfU5Y
+JO1s3iVxzGa1TgyvD4aPvqh9pIQ/wSFt43HtZ5/ReG2ul7PTzOK92xC1c3xpMnXdtLEeRmNx
+2KU0Kfk1Si0lBJHf3R/JhwhHvwQI0IeGb5Ho55oAAAAAAAAAAAAA
diff --git a/rt/t/data/smime/mails/6-encrypted-binary.eml b/rt/t/data/smime/mails/6-encrypted-binary.eml
new file mode 100644
index 000000000..f4d50886b
--- /dev/null
+++ b/rt/t/data/smime/mails/6-encrypted-binary.eml
@@ -0,0 +1,48 @@
+X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709DE8.9000101@example.com>
+Date: Tue, 09 Feb 2010 02:27:36 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:6
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+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
diff --git a/rt/t/data/smime/mails/7-signed-encrypted-plain.eml b/rt/t/data/smime/mails/7-signed-encrypted-plain.eml
new file mode 100644
index 000000000..7dd981a24
--- /dev/null
+++ b/rt/t/data/smime/mails/7-signed-encrypted-plain.eml
@@ -0,0 +1,97 @@
+X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709E37.3080003@example.com>
+Date: Tue, 09 Feb 2010 02:28:55 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:7
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+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
+AAAAAAA=
diff --git a/rt/t/data/smime/mails/8-signed-encrypted-attachment.eml b/rt/t/data/smime/mails/8-signed-encrypted-attachment.eml
new file mode 100644
index 000000000..1c53ad956
--- /dev/null
+++ b/rt/t/data/smime/mails/8-signed-encrypted-attachment.eml
@@ -0,0 +1,107 @@
+X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709E7D.8050407@example.com>
+Date: Tue, 09 Feb 2010 02:30:05 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:8
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+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
+AAAAAAAAAAAA
diff --git a/rt/t/data/smime/mails/9-signed-encrypted-binary.eml b/rt/t/data/smime/mails/9-signed-encrypted-binary.eml
new file mode 100644
index 000000000..eab9d5b65
--- /dev/null
+++ b/rt/t/data/smime/mails/9-signed-encrypted-binary.eml
@@ -0,0 +1,113 @@
+X-Mozilla-Status: 0801
+X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:
+FCC: imap://sender@localhost/Sent
+X-Identity-Key: id1
+X-Account-Key: account1
+Message-ID: <4B709ECC.4020500@example.com>
+Date: Tue, 09 Feb 2010 02:31:24 +0300
+From: tester <sender@example.com>
+X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ru; rv:1.9.1.7) Gecko/20100111 Thunderbird/3.0.1
+MIME-Version: 1.0
+To: root@example.com
+Subject: Test Email ID:9
+Content-Type: application/pkcs7-mime; name="smime.p7m"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Description: S/MIME Encrypted Message
+
+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
+qwQIeNe1tczOd/sAAAAAAAAAAAAA
diff --git a/rt/t/fts/indexed_mysql.t b/rt/t/fts/indexed_mysql.t
index 0a4f02626..672b22072 100644
--- a/rt/t/fts/indexed_mysql.t
+++ b/rt/t/fts/indexed_mysql.t
@@ -4,18 +4,8 @@ use warnings;
use RT::Test tests => undef;
plan skip_all => 'Not mysql' unless RT->Config->Get('DatabaseType') eq 'mysql';
-plan skip_all => "No SphinxSE in mysql" unless $RT::Handle->CheckSphinxSE;
-my %sphinx;
-$sphinx{'searchd'} = RT::Test->find_executable('searchd');
-$sphinx{'indexer'} = RT::Test->find_executable('indexer');
-
-plan skip_all => "No searchd and indexer under PATH"
- unless $sphinx{'searchd'} && $sphinx{'indexer'};
-
-plan tests => 15;
-
-RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Table => 'AttachmentsIndex', MaxMatches => 1000 );
+RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Table => 'AttachmentsIndex' );
setup_indexing();
@@ -24,59 +14,22 @@ ok $q && $q->id, 'loaded or created queue';
my $queue = $q->Name;
sub setup_indexing {
- # Since we're not running a webserver in this test, use the
- # known-safe port we determined at test setup
- my $port = $RT::Test::port;
- my ($exit_code, $output) = RT::Test->run_and_capture(
+ my %args = (
'no-ask' => 1,
command => $RT::SbinPath .'/rt-setup-fulltext-index',
dba => $ENV{'RT_DBA_USER'},
'dba-password' => $ENV{'RT_DBA_PASSWORD'},
- url => "sphinx://127.0.0.1:$port/rt",
);
- ok(!$exit_code, "setted up index");
- diag "output: $output" if $ENV{'TEST_VERBOSE'};
-
- my $tmp = $sphinx{'directory'} = File::Spec->catdir( RT::Test->temp_directory, 'sphinx' );
- mkdir $tmp;
-
- my $sphinx_conf = $output;
- $sphinx_conf =~ s/.*?source rt \{/source rt {/ms;
- $sphinx_conf =~ s{\Q$RT::VarPath\E/sphinx/}{$tmp/}g;
-
- $sphinx{'config'} = File::Spec->catfile( $tmp, 'sphinx.conf' );
- {
- open my $fh, ">", $sphinx{'config'};
- print $fh $sphinx_conf;
- close $fh;
- }
-
- sync_index();
-
- {
- my ($exit_code, $output) = RT::Test->run_and_capture(
- command => $sphinx{'searchd'},
- config => $sphinx{'config'},
- );
- ok(!$exit_code, "setted up index") or diag "output: $output";
- $sphinx{'started'} = 1 if !$exit_code;
- }
+ my ($exit_code, $output) = RT::Test->run_and_capture( %args );
+ ok(!$exit_code, "setted up index") or diag "output: $output";
}
sub sync_index {
- local $SIG{'CHLD'} = 'DEFAULT';
- local $SIG{'PIPE'} = 'DEFAULT';
- open my $fh, '-|', $sphinx{'indexer'}, '--all',
- '--config' => $sphinx{'config'},
- $sphinx{'started'}? ('--rotate') : (),
- ;
- my $output = <$fh>;
- close $fh;
- my $exit_code = $?>>8;
- ok(!$exit_code, "indexed") or diag "output: $output";
-
- # We may need to wait a second for searchd to pick up the changes
- sleep 1;
+ my %args = (
+ command => $RT::SbinPath .'/rt-fulltext-indexer',
+ );
+ my ($exit_code, $output) = RT::Test->run_and_capture( %args );
+ ok(!$exit_code, "setted up index") or diag "output: $output";
}
sub run_tests {
@@ -113,21 +66,18 @@ sub run_test {
@tickets = RT::Test->create_tickets(
{ Queue => $q->id },
- { Subject => 'book', Content => 'book' },
- { Subject => 'bar', Content => 'bar' },
+ { Subject => 'first', Content => 'english' },
+ { Subject => 'second', Content => 'french' },
+ { Subject => 'third', Content => 'spanish' },
+ { Subject => 'fourth', Content => 'german' },
);
sync_index();
run_tests(
- "Content LIKE 'book'" => { book => 1, bar => 0 },
- "Content LIKE 'bar'" => { book => 0, bar => 1 },
+ "Content LIKE 'english'" => { first => 1, second => 0, third => 0, fourth => 0 },
+ "Content LIKE 'french'" => { first => 0, second => 1, third => 0, fourth => 0 },
);
-END {
- my $Test = RT::Test->builder;
- return if $Test->{Original_Pid} != $$;
- return unless $sphinx{'started'};
+@tickets = ();
- my $pid = int RT::Test->file_content([$sphinx{'directory'}, 'searchd.pid']);
- kill TERM => $pid if $pid;
-}
+done_testing;
diff --git a/rt/t/fts/indexed_pg.t b/rt/t/fts/indexed_pg.t
index 88e35ab6a..1494fded2 100644
--- a/rt/t/fts/indexed_pg.t
+++ b/rt/t/fts/indexed_pg.t
@@ -9,7 +9,7 @@ my ($major, $minor) = $RT::Handle->dbh->get_info(18) =~ /^0*(\d+)\.0*(\d+)/;
plan skip_all => "Need Pg 8.2 or higher; we have $major.$minor"
if "$major.$minor" < 8.2;
-RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Column => 'ContentIndex', Table => 'Attachments' );
+RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Column => 'ContentIndex', Table => 'AttachmentsIndex' );
setup_indexing();
diff --git a/rt/t/fts/indexed_sphinx.t b/rt/t/fts/indexed_sphinx.t
new file mode 100644
index 000000000..38e56185f
--- /dev/null
+++ b/rt/t/fts/indexed_sphinx.t
@@ -0,0 +1,150 @@
+
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+plan skip_all => 'Not mysql' unless RT->Config->Get('DatabaseType') eq 'mysql';
+plan skip_all => "No SphinxSE in mysql" unless $RT::Handle->CheckSphinxSE;
+
+my %sphinx;
+$sphinx{'searchd'} = RT::Test->find_executable('searchd');
+$sphinx{'indexer'} = RT::Test->find_executable('indexer');
+
+plan skip_all => "No searchd and indexer under PATH"
+ unless $sphinx{'searchd'} && $sphinx{'indexer'};
+
+plan skip_all => "Can't determine sphinx version"
+ unless `$sphinx{searchd} --version` =~ /Sphinx (\d+)\.(\d+)(?:\.(\d+))?/;
+
+$sphinx{version} = sprintf "%d.%03d%03d", $1, $2, ($3 || 0);
+
+plan tests => 15;
+
+setup_indexing();
+
+my $q = RT::Test->load_or_create_queue( Name => 'General' );
+ok $q && $q->id, 'loaded or created queue';
+my $queue = $q->Name;
+
+sub setup_indexing {
+ # Since we're not running a webserver in this test, use the
+ # known-safe port we determined at test setup
+ my $port = $RT::Test::port;
+ my ($exit_code, $output) = RT::Test->run_and_capture(
+ 'no-ask' => 1,
+ command => $RT::SbinPath .'/rt-setup-fulltext-index',
+ dba => $ENV{'RT_DBA_USER'},
+ 'dba-password' => $ENV{'RT_DBA_PASSWORD'},
+ url => "sphinx://127.0.0.1:$port/rt",
+ 'index-type' => 'sphinx',
+ );
+ ok(!$exit_code, "setted up index");
+ diag "output: $output" if $ENV{'TEST_VERBOSE'};
+
+ my $tmp = $sphinx{'directory'} = File::Spec->catdir( RT::Test->temp_directory, 'sphinx' );
+ mkdir $tmp;
+
+ my $sphinx_conf = $output;
+ $sphinx_conf =~ s/.*?source rt \{/source rt {/ms;
+ $sphinx_conf =~ s{\Q$RT::VarPath\E/sphinx/}{$tmp/}g;
+
+ # Remove lines for different versions of sphinx than we're running
+ $sphinx_conf =~ s{^(\s+ \# \s+ for \s+ sphinx \s+
+ (<=?|>=?|=) \s*
+ (\d+) \. (\d+) (?:\. (\d+))?
+ .* \n)
+ ((?:^\s* \w .*\n)+)}{
+ my $v = sprintf "%d.%03d%03d", $3, $4, ($5 || 0);
+ my $prefix = eval "$sphinx{version} $2 $v" ? "" : "#";
+ $1 . join("\n",map{"$prefix$_"} split "\n", $6) . "\n";
+ }emix;
+
+ $sphinx{'config'} = File::Spec->catfile( $tmp, 'sphinx.conf' );
+ {
+ open my $fh, ">", $sphinx{'config'};
+ print $fh $sphinx_conf;
+ close $fh;
+ }
+
+ sync_index();
+
+ {
+ my ($exit_code, $output) = RT::Test->run_and_capture(
+ command => $sphinx{'searchd'},
+ config => $sphinx{'config'},
+ );
+ ok(!$exit_code, "setted up index") or diag "output: $output";
+ $sphinx{'started'} = 1 if !$exit_code;
+ }
+}
+
+sub sync_index {
+ local $SIG{'CHLD'} = 'DEFAULT';
+ local $SIG{'PIPE'} = 'DEFAULT';
+ open my $fh, '-|', $sphinx{'indexer'}, '--all',
+ '--config' => $sphinx{'config'},
+ $sphinx{'started'}? ('--rotate') : (),
+ ;
+ my $output = <$fh>;
+ close $fh;
+ my $exit_code = $?>>8;
+ ok(!$exit_code, "indexed") or diag "output: $output";
+
+ # We may need to wait a second for searchd to pick up the changes
+ sleep 1;
+}
+
+sub run_tests {
+ my @test = @_;
+ while ( my ($query, $checks) = splice @test, 0, 2 ) {
+ run_test( $query, %$checks );
+ }
+}
+
+my @tickets;
+sub run_test {
+ my ($query, %checks) = @_;
+ my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets;
+
+ my $tix = RT::Tickets->new(RT->SystemUser);
+ $tix->FromSQL( "( $query_prefix ) AND ( $query )" );
+
+ my $error = 0;
+
+ my $count = 0;
+ $count++ foreach grep $_, values %checks;
+ is($tix->Count, $count, "found correct number of ticket(s) by '$query'") or $error = 1;
+
+ my $good_tickets = ($tix->Count == $count);
+ while ( my $ticket = $tix->Next ) {
+ next if $checks{ $ticket->Subject };
+ diag $ticket->Subject ." ticket has been found when it's not expected";
+ $good_tickets = 0;
+ }
+ ok( $good_tickets, "all tickets are good with '$query'" ) or $error = 1;
+
+ diag "Wrong SQL query for '$query':". $tix->BuildSelectQuery if $error;
+}
+
+@tickets = RT::Test->create_tickets(
+ { Queue => $q->id },
+ { Subject => 'book', Content => 'book' },
+ { Subject => 'bar', Content => 'bar' },
+);
+sync_index();
+
+RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, Table => 'AttachmentsIndex', MaxMatches => 1000, Sphinx => 1 );
+
+run_tests(
+ "Content LIKE 'book'" => { book => 1, bar => 0 },
+ "Content LIKE 'bar'" => { book => 0, bar => 1 },
+);
+
+END {
+ my $Test = RT::Test->builder;
+ return if $Test->{Original_Pid} != $$;
+ return unless $sphinx{'started'};
+
+ my $pid = int RT::Test->file_content([$sphinx{'directory'}, 'searchd.pid']);
+ kill TERM => $pid if $pid;
+}
diff --git a/rt/t/lifecycles/basics.t b/rt/t/lifecycles/basics.t
index 554c95a73..e18bea3c4 100644
--- a/rt/t/lifecycles/basics.t
+++ b/rt/t/lifecycles/basics.t
@@ -1,7 +1,5 @@
-
use strict;
use warnings;
-use Data::Dumper;
BEGIN {require 't/lifecycles/utils.pl'};
@@ -19,7 +17,7 @@ my $tstatus = sub {
diag "check basic API";
{
- my $schema = $general->Lifecycle;
+ my $schema = $general->LifecycleObj;
isa_ok($schema, 'RT::Lifecycle');
is $schema->Name, 'default', "it's a default schema";
is_deeply [$schema->Valid],
@@ -80,7 +78,7 @@ diag "check status input on create";
my $valid = 1;
foreach ( @form_values ) {
- next if $general->Lifecycle->IsValid($_);
+ next if $general->LifecycleObj->IsValid($_);
$valid = 0;
diag("$_ doesn't appear to be a valid status, but it was in the form");
}
@@ -244,3 +242,5 @@ diag "'!inactive -> inactive' actions are shown even if ticket has unresolved de
);
}
+undef $m;
+done_testing;
diff --git a/rt/t/lifecycles/dates.t b/rt/t/lifecycles/dates.t
index 4f613f8d3..0c74a1b21 100644
--- a/rt/t/lifecycles/dates.t
+++ b/rt/t/lifecycles/dates.t
@@ -1,7 +1,5 @@
-
use strict;
use warnings;
-use Data::Dumper;
BEGIN {require 't/lifecycles/utils.pl'};
@@ -23,16 +21,13 @@ my $tstatus = sub {
return $ticket->Status;
};
-my ($baseurl, $m) = RT::Test->started_ok;
-ok $m->login, 'logged in';
-
diag "check basic API";
{
- my $schema = $general->Lifecycle;
+ my $schema = $general->LifecycleObj;
isa_ok($schema, 'RT::Lifecycle');
is $schema->Name, 'default', "it's a default schema";
- $schema = $delivery->Lifecycle;
+ $schema = $delivery->LifecycleObj;
isa_ok($schema, 'RT::Lifecycle');
is $schema->Name, 'delivery', "it's a delivery schema";
}
@@ -47,8 +42,8 @@ diag "dates on create for default schema";
Status => 'new',
);
ok $id, 'created a ticket';
- ok $ticket->StartedObj->Unix <= 0, 'started is not set';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok !$ticket->StartedObj->IsSet, 'started is not set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
}
{
my $ticket = RT::Ticket->new( RT->SystemUser );
@@ -58,8 +53,8 @@ diag "dates on create for default schema";
Status => 'open',
);
ok $id, 'created a ticket';
- ok $ticket->StartedObj->Unix > 0, 'started is set';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok $ticket->StartedObj->IsSet, 'started is set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
}
{
my $ticket = RT::Ticket->new( RT->SystemUser );
@@ -69,8 +64,8 @@ diag "dates on create for default schema";
Status => 'resolved',
);
ok $id, 'created a ticket';
- ok $ticket->StartedObj->Unix > 0, 'started is set';
- ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
+ ok $ticket->StartedObj->IsSet, 'started is set';
+ ok $ticket->ResolvedObj->IsSet, 'resolved is set';
}
my $test_date = '2008-11-28 12:00:00';
@@ -140,7 +135,7 @@ diag "dates on create for delivery schema";
is $ticket->Status, 'ordered', "Status is ordered";
my ($statusval,$statusmsg) = $ticket->SetStatus('on way');
ok($statusval,$statusmsg);
- ok $ticket->StartedObj->Unix > 0, 'started is set to ' .$ticket->StartedObj->AsString ;
+ ok $ticket->StartedObj->IsSet, 'started is set to ' .$ticket->StartedObj->AsString ;
is $ticket->ResolvedObj->Unix, 0, 'resolved is not set';
}
{
@@ -157,8 +152,8 @@ diag "dates on create for delivery schema";
($statusval,$statusmsg) = $ticket->SetStatus('delivered');
ok($statusval,$statusmsg);
- ok $ticket->StartedObj->Unix > 0, 'started is set';
- ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
+ ok $ticket->StartedObj->IsSet, 'started is set';
+ ok $ticket->ResolvedObj->IsSet, 'resolved is set';
}
my $test_date = '2008-11-28 12:00:00';
@@ -220,30 +215,30 @@ diag "dates on status change for default schema";
Status => 'new',
);
ok $id, 'created a ticket';
- ok $ticket->StartedObj->Unix <= 0, 'started is not set';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok !$ticket->StartedObj->IsSet, 'started is not set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
(my $status, $msg) = $ticket->SetStatus('open');
ok $status, 'changed status' or diag "error: $msg";
- ok $ticket->StartedObj->Unix > 0, 'started is set';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok $ticket->StartedObj->IsSet, 'started is set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
my $started = $ticket->StartedObj->Unix;
($status, $msg) = $ticket->SetStatus('stalled');
ok $status, 'changed status' or diag "error: $msg";
is $ticket->StartedObj->Unix, $started, 'started is set and the same';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
($status, $msg) = $ticket->SetStatus('open');
ok $status, 'changed status' or diag "error: $msg";
is $ticket->StartedObj->Unix, $started, 'started is set and the same';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
($status, $msg) = $ticket->SetStatus('resolved');
ok $status, 'changed status' or diag "error: $msg";
is $ticket->StartedObj->Unix, $started, 'started is set and the same';
- ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
+ ok $ticket->ResolvedObj->IsSet, 'resolved is set';
}
diag "dates on status change for delivery schema";
@@ -255,25 +250,25 @@ diag "dates on status change for delivery schema";
Status => 'ordered',
);
ok $id, 'created a ticket';
- ok $ticket->StartedObj->Unix <= 0, 'started is not set';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok !$ticket->StartedObj->IsSet, 'started is not set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
(my $status, $msg) = $ticket->SetStatus('delayed');
ok $status, 'changed status' or diag "error: $msg";
- ok $ticket->StartedObj->Unix > 0, 'started is set';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok $ticket->StartedObj->IsSet, 'started is set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
my $started = $ticket->StartedObj->Unix;
($status, $msg) = $ticket->SetStatus('on way');
ok $status, 'changed status' or diag "error: $msg";
is $ticket->StartedObj->Unix, $started, 'started is set and the same';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
($status, $msg) = $ticket->SetStatus('delivered');
ok $status, 'changed status' or diag "error: $msg";
is $ticket->StartedObj->Unix, $started, 'started is set and the same';
- ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
+ ok $ticket->ResolvedObj->IsSet, 'resolved is set';
}
diag "add partial map between general->delivery";
@@ -299,18 +294,20 @@ diag "check date changes on moving a ticket";
Status => 'new',
);
ok $id, 'created a ticket';
- ok $ticket->StartedObj->Unix <= 0, 'started is not set';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok !$ticket->StartedObj->IsSet, 'started is not set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
(my $status, $msg) = $ticket->SetQueue( $delivery->id );
ok $status, "moved ticket between queues with different schemas";
is $ticket->Status, 'on way', 'status has been changed';
- ok $ticket->StartedObj->Unix > 0, 'started is set';
- ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+ ok $ticket->StartedObj->IsSet, 'started is set';
+ ok !$ticket->ResolvedObj->IsSet, 'resolved is not set';
($status, $msg) = $ticket->SetQueue( $general->id );
ok $status, "moved ticket between queues with different schemas";
is $ticket->Status, 'resolved', 'status has been changed';
- ok $ticket->StartedObj->Unix > 0, 'started is set';
- ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
+ ok $ticket->StartedObj->IsSet, 'started is set';
+ ok $ticket->ResolvedObj->IsSet, 'resolved is set';
}
+
+done_testing;
diff --git a/rt/t/lifecycles/moving.t b/rt/t/lifecycles/moving.t
index 5f184e2c2..8a03e3ea6 100644
--- a/rt/t/lifecycles/moving.t
+++ b/rt/t/lifecycles/moving.t
@@ -1,7 +1,5 @@
-
use strict;
use warnings;
-use Data::Dumper;
BEGIN {require 't/lifecycles/utils.pl'};
@@ -94,3 +92,5 @@ diag "one way map doesn't work backwards";
is $ticket->Queue, $delivery->id, 'queue is steal the same';
is $ticket->Status, 'ordered', 'status is steal the same';
}
+
+done_testing;
diff --git a/rt/t/lifecycles/types.t b/rt/t/lifecycles/types.t
new file mode 100644
index 000000000..79b0714b1
--- /dev/null
+++ b/rt/t/lifecycles/types.t
@@ -0,0 +1,33 @@
+use strict;
+use warnings;
+
+BEGIN {require 't/lifecycles/utils.pl'};
+
+is_deeply( [ RT::Lifecycle->ListAll ], [qw/ approvals default delivery /],
+ "Get the list of all lifecycles (implicitly for for tickets)");
+is_deeply( [ RT::Lifecycle->ListAll('ticket') ], [qw/ approvals default delivery /],
+ "Get the list of all lifecycles for tickets");
+is_deeply( [ RT::Lifecycle->List], [qw/ default delivery /],
+ "Get the list of lifecycles without approvals (implicitly for for tickets)");
+is_deeply( [ RT::Lifecycle->List('ticket') ], [qw/ default delivery /],
+ "Get the list of lifecycles without approvals for tickets");
+is_deeply( [ RT::Lifecycle->List('racecar') ], [qw/ racing /],
+ "Get the list of lifecycles for other types");
+
+my $tickets = RT::Lifecycle->Load( Name => '', Type => 'ticket' );
+ok($tickets, "Got a generalized lifecycle for tickets");
+isa_ok( $tickets, "RT::Lifecycle::Ticket", "Is the right subclass" );
+is_deeply( [ sort $tickets->Valid ],
+ [ sort qw(new open stalled resolved rejected deleted ordered),
+ 'on way', 'delayed', 'delivered' ],
+ "Only gets ticket statuses" );
+
+
+my $racecars = RT::Lifecycle->Load( Name => '', Type => 'racecar' );
+ok($racecars, "Got a generalized lifecycle for racecars");
+isa_ok( $racecars, "RT::Lifecycle", "Is the generalized subclass" );
+is_deeply( [ sort $racecars->Valid ],
+ [ sort ('on-your-mark', 'get-set', 'go', 'first', 'second', 'third', 'no-place') ],
+ "Only gets racecar statuses" );
+
+done_testing;
diff --git a/rt/t/lifecycles/unresolved-deps.t b/rt/t/lifecycles/unresolved-deps.t
index 02c1942d7..5da4b8fc5 100644
--- a/rt/t/lifecycles/unresolved-deps.t
+++ b/rt/t/lifecycles/unresolved-deps.t
@@ -1,8 +1,6 @@
use strict;
use warnings;
-use Data::Dumper;
-use Test::More tests => 15 + 1; # plus one for warnings check
BEGIN {require 't/lifecycles/utils.pl'};
my $general = RT::Test->load_or_create_queue(
@@ -41,3 +39,5 @@ ok $m->login, 'logged in';
);
}
+undef $m;
+done_testing;
diff --git a/rt/t/lifecycles/utils.pl b/rt/t/lifecycles/utils.pl
index 3813df3e9..4dbba238d 100644
--- a/rt/t/lifecycles/utils.pl
+++ b/rt/t/lifecycles/utils.pl
@@ -63,10 +63,15 @@ Set(\%Lifecycles,
'delayed -> on way' => {label => 'Put On Way', update => 'Respond'},
},
},
+ racing => {
+ type => 'racecar',
+ active => ['on-your-mark', 'get-set', 'go'],
+ inactive => ['first', 'second', 'third', 'no-place'],
+ },
);
END
}
-use RT::Test config => $config;
+use RT::Test config => $config, tests => undef;
1;
diff --git a/rt/t/mail/autogenerated.t b/rt/t/mail/autogenerated.t
new file mode 100644
index 000000000..a37c9b12d
--- /dev/null
+++ b/rt/t/mail/autogenerated.t
@@ -0,0 +1,22 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+use Email::Abstract;
+
+my $msg = Email::Abstract->new(<<'MSG')->cast("MIME::Entity");
+From: somebody@example.com
+To: rt@example.com
+Precedence: never-bounce
+Precedence: bulk
+Subject: testing precedence
+
+I am bulk mail, hear me roar!
+MSG
+
+ok RT::Interface::Email::CheckForAutoGenerated($msg->head), "Is AutoGenerated";
+
+$msg->head->delete("Precedence", 1);
+ok !RT::Interface::Email::CheckForAutoGenerated($msg->head), "Isn't AutoGenerated";
+
+done_testing;
diff --git a/rt/t/mail/charsets-outgoing-plaintext.t b/rt/t/mail/charsets-outgoing-plaintext.t
new file mode 100644
index 000000000..be576e0bd
--- /dev/null
+++ b/rt/t/mail/charsets-outgoing-plaintext.t
@@ -0,0 +1,315 @@
+use strict;
+use warnings;
+
+use RT::Test tests => 79, text_templates => 1;
+
+my %string = (
+ ru => {
+ test => "\x{442}\x{435}\x{441}\x{442}",
+ autoreply => "\x{410}\x{432}\x{442}\x{43e}\x{43e}\x{442}\x{432}\x{435}\x{442}",
+ support => "\x{43f}\x{43e}\x{434}\x{434}\x{435}\x{440}\x{436}\x{43a}\x{430}",
+ },
+ latin1 => {
+ test => Encode::decode('latin1', "t\xE9st"),
+ autoreply => Encode::decode('latin1', "a\xFCtoreply"),
+ support => Encode::decode('latin1', "supp\xF5rt"),
+ },
+);
+
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'Regression',
+ CorrespondAddress => 'rt-recipient@example.com',
+ CommentAddress => 'rt-recipient@example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+diag "make sure queue has no subject tag";
+{
+ my ($status, $msg) = $queue->SetSubjectTag( undef );
+ ok $status, "set subject tag for the queue" or diag "error: $msg";
+}
+
+diag "set intial simple autoreply template";
+{
+ my $template = RT::Template->new( RT->SystemUser );
+ $template->Load('Autoreply');
+ ok $template->id, "loaded autoreply tempalte";
+
+ my ($status, $msg) = $template->SetContent(
+ "Subject: Autreply { \$Ticket->Subject }\n"
+ ."\n"
+ ."hi there it's an autoreply.\n"
+ ."\n"
+ );
+ ok $status, "changed content of the template"
+ or diag "error: $msg";
+}
+
+diag "basic test of autoreply";
+{
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Create(
+ Queue => $queue->id,
+ Subject => 'test',
+ Requestor => 'root@localhost',
+ );
+ my @mails = RT::Test->fetch_caught_mails;
+ ok @mails, "got some outgoing emails";
+}
+
+diag "non-ascii Subject with ascii prefix set in the template";
+foreach my $set ( 'ru', 'latin1' ) {
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Create(
+ Queue => $queue->id,
+ Subject => $string{$set}{test},
+ Requestor => 'root@localhost',
+ );
+ my @mails = RT::Test->fetch_caught_mails;
+ ok @mails, "got some outgoing emails";
+
+ my $status = 1;
+ foreach my $mail ( @mails ) {
+ my $entity = parse_mail( $mail );
+ my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') );
+ $subject =~ /$string{$set}{test}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ }
+ ok $status, "all mails have correct data";
+}
+
+foreach my $tag_set ( 'ru', 'latin1' ) {
+
+diag "set non-ascii subject tag for the queue";
+{
+ my ($status, $msg) = $queue->SetSubjectTag( $string{$tag_set}{support} );
+ ok $status, "set subject tag for the queue" or diag "error: $msg";
+}
+
+diag "ascii subject with non-ascii subject tag";
+{
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Create(
+ Queue => $queue->id,
+ Subject => 'test',
+ Requestor => 'root@localhost',
+ );
+ my @mails = RT::Test->fetch_caught_mails;
+ ok @mails, "got some outgoing emails";
+
+ my $status = 1;
+ foreach my $mail ( @mails ) {
+ my $entity = parse_mail( $mail );
+ my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') );
+ $subject =~ /$string{$tag_set}{support}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ }
+ ok $status, "all mails have correct data";
+}
+
+diag "non-ascii subject with non-ascii subject tag";
+foreach my $set ( 'ru', 'latin1' ) {
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Create(
+ Queue => $queue->id,
+ Subject => $string{$set}{test},
+ Requestor => 'root@localhost',
+ );
+ my @mails = RT::Test->fetch_caught_mails;
+ ok @mails, "got some outgoing emails";
+
+ my $status = 1;
+ foreach my $mail ( @mails ) {
+ my $entity = parse_mail( $mail );
+ my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') );
+ $subject =~ /$string{$tag_set}{support}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ $subject =~ /$string{$set}{test}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ }
+ ok $status, "all mails have correct data";
+}
+
+} # subject tag
+
+diag "return back the empty subject tag";
+{
+ my ($status, $msg) = $queue->SetSubjectTag( undef );
+ ok $status, "set subject tag for the queue" or diag "error: $msg";
+}
+
+
+foreach my $prefix_set ( 'ru', 'latin1' ) {
+
+diag "add non-ascii subject prefix in the autoreply template";
+{
+ my $template = RT::Template->new( RT->SystemUser );
+ $template->Load('Autoreply');
+ ok $template->id, "loaded autoreply tempalte";
+
+ my ($status, $msg) = $template->SetContent(
+ "Subject: $string{$prefix_set}{autoreply} { \$Ticket->Subject }\n"
+ ."\n"
+ ."hi there it's an autoreply.\n"
+ ."\n"
+ );
+ ok $status, "changed content of the template" or diag "error: $msg";
+}
+
+diag "ascii subject with non-ascii subject prefix in template";
+{
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Create(
+ Queue => $queue->id,
+ Subject => 'test',
+ Requestor => 'root@localhost',
+ );
+ my @mails = RT::Test->fetch_caught_mails;
+ ok @mails, "got some outgoing emails";
+
+ my $status = 1;
+ foreach my $mail ( @mails ) {
+ my $entity = parse_mail( $mail );
+ my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') );
+ $subject =~ /$string{$prefix_set}{autoreply}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ }
+ ok $status, "all mails have correct data";
+}
+
+diag "non-ascii subject with non-ascii subject prefix in template";
+foreach my $set ( 'ru', 'latin1' ) {
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Create(
+ Queue => $queue->id,
+ Subject => $string{$set}{test},
+ Requestor => 'root@localhost',
+ );
+ my @mails = RT::Test->fetch_caught_mails;
+ ok @mails, "got some outgoing emails";
+
+ my $status = 1;
+ foreach my $mail ( @mails ) {
+ my $entity = parse_mail( $mail );
+ my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') );
+ $subject =~ /$string{$prefix_set}{autoreply}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ $subject =~ /$string{$set}{test}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ }
+ ok $status, "all mails have correct data";
+}
+
+foreach my $tag_set ( 'ru', 'latin1' ) {
+diag "set non-ascii subject tag for the queue";
+{
+ my ($status, $msg) = $queue->SetSubjectTag( $string{$tag_set}{support} );
+ ok $status, "set subject tag for the queue" or diag "error: $msg";
+}
+
+diag "non-ascii subject, non-ascii prefix in template and non-ascii tag";
+foreach my $set ( 'ru', 'latin1' ) {
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Create(
+ Queue => $queue->id,
+ Subject => $string{$set}{test},
+ Requestor => 'root@localhost',
+ );
+ my @mails = RT::Test->fetch_caught_mails;
+ ok @mails, "got some outgoing emails";
+
+ my $status = 1;
+ foreach my $mail ( @mails ) {
+ my $entity = parse_mail( $mail );
+ my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') );
+ $subject =~ /$string{$prefix_set}{autoreply}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ $subject =~ /$string{$tag_set}{support}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ $subject =~ /$string{$set}{test}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ }
+ ok $status, "all mails have correct data";
+}
+
+} # subject tag
+
+diag "flush subject tag of the queue";
+{
+ my ($status, $msg) = $queue->SetSubjectTag( undef );
+ ok $status, "set subject tag for the queue" or diag "error: $msg";
+}
+
+} # prefix set
+
+
+diag "don't change subject via template";
+# clean DB has autoreply that always changes subject in template,
+# we should test situation when subject is not changed from template
+{
+ my $template = RT::Template->new( RT->SystemUser );
+ $template->Load('Autoreply');
+ ok $template->id, "loaded autoreply tempalte";
+
+ my ($status, $msg) = $template->SetContent(
+ "\n"
+ ."\n"
+ ."hi there it's an autoreply.\n"
+ ."\n"
+ );
+ ok $status, "changed content of the template" or diag "error: $msg";
+}
+
+diag "non-ascii Subject without changes in template";
+foreach my $set ( 'ru', 'latin1' ) {
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Create(
+ Queue => $queue->id,
+ Subject => $string{$set}{test},
+ Requestor => 'root@localhost',
+ );
+ my @mails = RT::Test->fetch_caught_mails;
+ ok @mails, "got some outgoing emails";
+
+ my $status = 1;
+ foreach my $mail ( @mails ) {
+ my $entity = parse_mail( $mail );
+ my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') );
+ $subject =~ /$string{$set}{test}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ }
+ ok $status, "all mails have correct data";
+}
+
+foreach my $tag_set ( 'ru', 'latin1' ) {
+diag "set non-ascii subject tag for the queue";
+{
+ my ($status, $msg) = $queue->SetSubjectTag( $string{$tag_set}{support} );
+ ok $status, "set subject tag for the queue" or diag "error: $msg";
+}
+
+diag "non-ascii Subject without changes in template and with non-ascii subject tag";
+foreach my $set ( 'ru', 'latin1' ) {
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Create(
+ Queue => $queue->id,
+ Subject => $string{$set}{test},
+ Requestor => 'root@localhost',
+ );
+ my @mails = RT::Test->fetch_caught_mails;
+ ok @mails, "got some outgoing emails";
+
+ my $status = 1;
+ foreach my $mail ( @mails ) {
+ my $entity = parse_mail( $mail );
+ my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') );
+ $subject =~ /$string{$set}{test}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ $subject =~ /$string{$tag_set}{support}/
+ or do { $status = 0; diag "wrong subject: $subject" };
+ }
+ ok $status, "all mails have correct data";
+}
+
+} # subject tag set
+
diff --git a/rt/t/mail/charsets-outgoing.t b/rt/t/mail/charsets-outgoing.t
index 872721325..0f78f0a58 100644
--- a/rt/t/mail/charsets-outgoing.t
+++ b/rt/t/mail/charsets-outgoing.t
@@ -32,7 +32,7 @@ diag "make sure queue has no subject tag";
diag "set intial simple autoreply template";
{
my $template = RT::Template->new( RT->SystemUser );
- $template->Load('Autoreply');
+ $template->Load('Autoreply in HTML');
ok $template->id, "loaded autoreply tempalte";
my ($status, $msg) = $template->SetContent(
@@ -144,7 +144,7 @@ foreach my $prefix_set ( 'ru', 'latin1' ) {
diag "add non-ascii subject prefix in the autoreply template";
{
my $template = RT::Template->new( RT->SystemUser );
- $template->Load('Autoreply');
+ $template->Load('Autoreply in HTML');
ok $template->id, "loaded autoreply tempalte";
my ($status, $msg) = $template->SetContent(
@@ -248,7 +248,7 @@ diag "don't change subject via template";
# we should test situation when subject is not changed from template
{
my $template = RT::Template->new( RT->SystemUser );
- $template->Load('Autoreply');
+ $template->Load('Autoreply in HTML');
ok $template->id, "loaded autoreply tempalte";
my ($status, $msg) = $template->SetContent(
diff --git a/rt/t/mail/crypt-gnupg.t b/rt/t/mail/crypt-gnupg.t
index ffb059706..567573e93 100644
--- a/rt/t/mail/crypt-gnupg.t
+++ b/rt/t/mail/crypt-gnupg.t
@@ -10,9 +10,10 @@ BEGIN {
qw/data gnupg keyrings/ );
}
-use RT::Test::GnuPG tests => 96, gnupg_options => { homedir => $homedir };
+use RT::Test::GnuPG tests => 100, gnupg_options => { homedir => $homedir };
use Test::Warn;
+use_ok('RT::Crypt');
use_ok('MIME::Entity');
diag 'only signing. correct passphrase';
@@ -22,10 +23,12 @@ diag 'only signing. correct passphrase';
Subject => 'test',
Data => ['test'],
);
- my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' );
+ my %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' );
ok( $entity, 'signed entity');
ok( !$res{'logger'}, "log is here as well" ) or diag $res{'logger'};
- my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} );
+ my @status = RT::Crypt->ParseStatus(
+ Protocol => $res{'Protocol'}, Status => $res{'status'}
+ );
is( scalar @status, 2, 'two records: passphrase, signing');
is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct');
is( $status[0]->{'Status'}, 'DONE', 'good passphrase');
@@ -36,15 +39,17 @@ diag 'only signing. correct passphrase';
ok( $entity->is_multipart, 'signed message is multipart' );
is( $entity->parts, 2, 'two parts' );
- my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity );
+ my @parts = RT::Crypt->FindProtectedParts( Entity => $entity );
is( scalar @parts, 1, 'one protected part' );
is( $parts[0]->{'Type'}, 'signed', "have signed part" );
is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" );
is( $parts[0]->{'Top'}, $entity, "it's the same entity" );
- my @res = RT::Crypt::GnuPG::VerifyDecrypt( Entity => $entity );
+ my @res = RT::Crypt->VerifyDecrypt( Entity => $entity );
is scalar @res, 1, 'one operation';
- @status = RT::Crypt::GnuPG::ParseStatus( $res[0]{'status'} );
+ @status = RT::Crypt->ParseStatus(
+ Protocol => $res[0]{'Protocol'}, Status => $res[0]{'status'}
+ );
is( scalar @status, 1, 'one record');
is( $status[0]->{'Operation'}, 'Verify', 'operation is correct');
is( $status[0]->{'Status'}, 'DONE', 'good passphrase');
@@ -60,7 +65,7 @@ diag 'only signing. missing passphrase';
);
my %res;
warning_like {
- %res = RT::Crypt::GnuPG::SignEncrypt(
+ %res = RT::Crypt->SignEncrypt(
Entity => $entity,
Encrypt => 0,
Passphrase => ''
@@ -69,7 +74,9 @@ diag 'only signing. missing passphrase';
ok( $res{'exit_code'}, "couldn't sign without passphrase");
ok( $res{'error'} || $res{'logger'}, "error is here" );
- my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} );
+ my @status = RT::Crypt->ParseStatus(
+ Protocol => $res{'Protocol'}, Status => $res{'status'}
+ );
is( scalar @status, 1, 'one record');
is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct');
is( $status[0]->{'Status'}, 'MISSING', 'missing passphrase');
@@ -85,7 +92,7 @@ diag 'only signing. wrong passphrase';
my %res;
warning_like {
- %res = RT::Crypt::GnuPG::SignEncrypt(
+ %res = RT::Crypt->SignEncrypt(
Entity => $entity,
Encrypt => 0,
Passphrase => 'wrong',
@@ -95,7 +102,9 @@ diag 'only signing. wrong passphrase';
ok( $res{'exit_code'}, "couldn't sign with bad passphrase");
ok( $res{'error'} || $res{'logger'}, "error is here" );
- my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} );
+ my @status = RT::Crypt->ParseStatus(
+ Protocol => $res{'Protocol'}, Status => $res{'status'}
+ );
is( scalar @status, 1, 'one record');
is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct');
is( $status[0]->{'Status'}, 'BAD', 'wrong passphrase');
@@ -109,18 +118,20 @@ diag 'encryption only';
Subject => 'test',
Data => ['test'],
);
- my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 );
+ my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 );
ok( !$res{'exit_code'}, "successful encryption" );
ok( !$res{'logger'}, "no records in logger" );
- my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} );
+ my @status = RT::Crypt->ParseStatus(
+ Protocol => $res{'Protocol'}, Status => $res{'status'}
+ );
is( scalar @status, 1, 'one record');
is( $status[0]->{'Operation'}, 'Encrypt', 'operation is correct');
is( $status[0]->{'Status'}, 'DONE', 'done');
ok($entity, 'get an encrypted part');
- my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity );
+ my @parts = RT::Crypt->FindProtectedParts( Entity => $entity );
is( scalar @parts, 1, 'one protected part' );
is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" );
is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" );
@@ -138,7 +149,7 @@ diag 'encryption only, bad recipient';
my %res;
warning_like {
- %res = RT::Crypt::GnuPG::SignEncrypt(
+ %res = RT::Crypt->SignEncrypt(
Entity => $entity,
Sign => 0,
);
@@ -147,7 +158,9 @@ diag 'encryption only, bad recipient';
ok( $res{'exit_code'}, 'no way to encrypt without keys of recipients');
ok( $res{'logger'}, "errors are in logger" );
- my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} );
+ my @status = RT::Crypt->ParseStatus(
+ Protocol => $res{'Protocol'}, Status => $res{'status'}
+ );
is( scalar @status, 1, 'one record');
is( $status[0]->{'Keyword'}, 'INV_RECP', 'invalid recipient');
}
@@ -160,11 +173,13 @@ diag 'encryption and signing with combined method';
Subject => 'test',
Data => ['test'],
);
- my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Passphrase => 'test' );
+ my %res = RT::Crypt->SignEncrypt( Entity => $entity, Passphrase => 'test' );
ok( !$res{'exit_code'}, "successful encryption with signing" );
ok( !$res{'logger'}, "no records in logger" );
- my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} );
+ my @status = RT::Crypt->ParseStatus(
+ Protocol => $res{'Protocol'}, Status => $res{'status'}
+ );
is( scalar @status, 3, 'three records: passphrase, sign and encrypt');
is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct');
is( $status[0]->{'Status'}, 'DONE', 'done');
@@ -175,7 +190,7 @@ diag 'encryption and signing with combined method';
ok($entity, 'get an encrypted and signed part');
- my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity );
+ my @parts = RT::Crypt->FindProtectedParts( Entity => $entity );
is( scalar @parts, 1, 'one protected part' );
is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" );
is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" );
@@ -190,14 +205,14 @@ diag 'encryption and signing with cascading, sign on encrypted';
Subject => 'test',
Data => ['test'],
);
- my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 );
+ my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 );
ok( !$res{'exit_code'}, 'successful encryption' );
ok( !$res{'logger'}, "no records in logger" );
- %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' );
+ %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' );
ok( !$res{'exit_code'}, 'successful signing' );
ok( !$res{'logger'}, "no records in logger" );
- my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity );
+ my @parts = RT::Crypt->FindProtectedParts( Entity => $entity );
is( scalar @parts, 1, 'one protected part, top most' );
is( $parts[0]->{'Type'}, 'signed', "have signed part" );
is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" );
@@ -212,7 +227,7 @@ diag 'find signed/encrypted part deep inside';
Subject => 'test',
Data => ['test'],
);
- my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 );
+ my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 );
ok( !$res{'exit_code'}, "success" );
$entity->make_multipart( 'mixed', Force => 1 );
$entity->attach(
@@ -220,7 +235,7 @@ diag 'find signed/encrypted part deep inside';
Data => ['-'x76, 'this is mailing list'],
);
- my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity );
+ my @parts = RT::Crypt->FindProtectedParts( Entity => $entity );
is( scalar @parts, 1, 'one protected part' );
is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" );
is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" );
@@ -236,7 +251,7 @@ diag 'wrong signed/encrypted parts: no protocol';
Data => ['test'],
);
- my %res = RT::Crypt::GnuPG::SignEncrypt(
+ my %res = RT::Crypt->SignEncrypt(
Entity => $entity,
Sign => 0,
);
@@ -245,11 +260,12 @@ diag 'wrong signed/encrypted parts: no protocol';
$entity->head->mime_attr( 'Content-Type.protocol' => undef );
my @parts;
- warning_like {
- @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity );
- } qr{Entity is 'multipart/encrypted', but has no protocol defined. Skipped};
-
- is( scalar @parts, 0, 'no protected parts' );
+ warning_like { @parts = RT::Crypt->FindProtectedParts( Entity => $entity ) }
+ qr{Entity is 'multipart/encrypted', but has no protocol defined. Checking for PGP part};
+ is( scalar @parts, 1, 'one protected part' );
+ is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" );
+ is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" );
+ is( $parts[0]->{'Top'}, $entity, "it's the same entity" );
}
diag 'wrong signed/encrypted parts: not enought parts';
@@ -261,7 +277,7 @@ diag 'wrong signed/encrypted parts: not enought parts';
Data => ['test'],
);
- my %res = RT::Crypt::GnuPG::SignEncrypt(
+ my %res = RT::Crypt->SignEncrypt(
Entity => $entity,
Sign => 0,
);
@@ -271,7 +287,7 @@ diag 'wrong signed/encrypted parts: not enought parts';
my @parts;
warning_like {
- @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity );
+ @parts = RT::Crypt->FindProtectedParts( Entity => $entity );
} qr/Encrypted or signed entity must has two subparts. Skipped/;
is( scalar @parts, 0, 'no protected parts' );
}
@@ -284,11 +300,11 @@ diag 'wrong signed/encrypted parts: wrong proto';
Subject => 'test',
Data => ['test'],
);
- my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 );
+ my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 );
ok( !$res{'exit_code'}, 'success' );
$entity->head->mime_attr( 'Content-Type.protocol' => 'application/bad-proto' );
- my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity );
+ my @parts = RT::Crypt->FindProtectedParts( Entity => $entity );
is( scalar @parts, 0, 'no protected parts' );
}
@@ -300,11 +316,11 @@ diag 'wrong signed/encrypted parts: wrong proto';
Subject => 'test',
Data => ['test'],
);
- my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' );
+ my %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' );
ok( !$res{'exit_code'}, 'success' );
$entity->head->mime_attr( 'Content-Type.protocol' => 'application/bad-proto' );
- my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity );
+ my @parts = RT::Crypt->FindProtectedParts( Entity => $entity );
is( scalar @parts, 0, 'no protected parts' );
}
@@ -314,7 +330,7 @@ diag 'verify inline and in attachment signatures';
my $parser = new MIME::Parser;
my $entity = $parser->parse( $fh );
- my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity );
+ my @parts = RT::Crypt->FindProtectedParts( Entity => $entity );
is( scalar @parts, 2, 'two protected parts' );
is( $parts[1]->{'Type'}, 'signed', "have signed part" );
is( $parts[1]->{'Format'}, 'Inline', "inline format" );
@@ -325,8 +341,10 @@ diag 'verify inline and in attachment signatures';
is( $parts[0]->{'Data'}, $entity->parts(1), "data in second part" );
is( $parts[0]->{'Signature'}, $entity->parts(2), "file's signature in third part" );
- my @res = RT::Crypt::GnuPG::VerifyDecrypt( Entity => $entity );
- my @status = RT::Crypt::GnuPG::ParseStatus( $res[0]->{'status'} );
+ my @res = RT::Crypt->VerifyDecrypt( Entity => $entity );
+ my @status = RT::Crypt->ParseStatus(
+ Protocol => $res[0]{'Protocol'}, Status => $res[0]{'status'}
+ );
is( scalar @status, 1, 'one record');
is( $status[0]->{'Operation'}, 'Verify', 'operation is correct');
is( $status[0]->{'Status'}, 'DONE', 'good passphrase');
diff --git a/rt/t/mail/dashboard-chart-with-utf8.t b/rt/t/mail/dashboard-chart-with-utf8.t
index 37f8ce0c6..4fe483a1b 100644
--- a/rt/t/mail/dashboard-chart-with-utf8.t
+++ b/rt/t/mail/dashboard-chart-with-utf8.t
@@ -1,16 +1,10 @@
use strict;
use warnings;
-BEGIN {
- require RT::Test;
+use RT::Test tests => undef;
- if (eval { require GD }) {
- RT::Test->import(tests => 15);
- }
- else {
- RT::Test->import(skip_all => 'GD required.');
- }
-}
+plan skip_all => 'GD required'
+ unless GD->require;
my $root = RT::Test->load_or_create_user( Name => 'root' );
@@ -29,6 +23,7 @@ $m->submit_form(
fields => {
SavedSearchDescription => 'chart foo',
SavedSearchOwner => 'RT::User-' . $root->id,
+ ChartStyle => 'bar',
},
button => 'SavedSearchSave',
);
@@ -88,3 +83,5 @@ if ( my $io = $handle->open('r') ) {
}
is( $mail_image_data, $image, 'image in mail is the same one in web' );
+undef $m;
+done_testing;
diff --git a/rt/t/mail/dashboards.t b/rt/t/mail/dashboards.t
index 6bf4ba520..d7b1ccc7a 100644
--- a/rt/t/mail/dashboards.t
+++ b/rt/t/mail/dashboards.t
@@ -101,7 +101,7 @@ sub produces_dashboard_mail_ok { # {{{
my $mail = parse_mail( $mails[0] );
is($mail->head->get('Subject'), $subject);
- is($mail->head->get('From'), "root\n");
+ is($mail->head->get('From'), qq{"root" <root\@localhost>\n});
is($mail->head->get('Content-Transfer-Encoding'), "base64\n");
is($mail->head->get('X-RT-Dashboard-Id'), "$dashboard_id\n");
is($mail->head->get('X-RT-Dashboard-Subscription-Id'), "$subscription_id\n");
diff --git a/rt/t/mail/digest-attributes.t b/rt/t/mail/digest-attributes.t
index 54c1c803f..a0940dbb1 100644
--- a/rt/t/mail/digest-attributes.t
+++ b/rt/t/mail/digest-attributes.t
@@ -51,48 +51,48 @@ my $everyone = RT::Group->new( RT->SystemUser );
ok( $ret, "Loaded 'everyone' group: $msg" );
( $ret, $msg ) = $everyone->PrincipalObj->GrantRight( Right => 'CreateTicket',
- Object => $testq );
+ Object => $testq );
ok( $ret || $msg =~ /already has/, "Granted everyone CreateTicket on testq: $msg" );
# Make user_d an admincc for the queue.
( $ret, $msg ) = $user_d->PrincipalObj->GrantRight( Right => 'AdminQueue',
- Object => $testq );
+ Object => $testq );
ok( $ret || $msg =~ /already has/, "Granted dduser AdminQueue on testq: $msg" );
( $ret, $msg ) = $testq->AddWatcher( Type => 'AdminCc',
- PrincipalId => $user_d->PrincipalObj->id );
+ PrincipalId => $user_d->PrincipalObj->id );
ok( $ret || $msg =~ /already/, "dduser added as a queue watcher: $msg" );
# Give the others queue rights.
( $ret, $msg ) = $user_n->PrincipalObj->GrantRight( Right => 'AdminQueue',
- Object => $testq );
+ Object => $testq );
ok( $ret || $msg =~ /already has/, "Granted emailnormal right on testq: $msg" );
( $ret, $msg ) = $user_w->PrincipalObj->GrantRight( Right => 'AdminQueue',
- Object => $testq );
+ Object => $testq );
ok( $ret || $msg =~ /already has/, "Granted emailweekly right on testq: $msg" );
( $ret, $msg ) = $user_s->PrincipalObj->GrantRight( Right => 'AdminQueue',
- Object => $testq );
+ Object => $testq );
ok( $ret || $msg =~ /already has/, "Granted emailsusp right on testq: $msg" );
# Create a ticket with To: Cc: Bcc: fields using our four users.
my $id;
my $ticket = RT::Ticket->new( RT->SystemUser );
( $id, $ret, $msg ) = $ticket->Create( Queue => $testq->Name,
- Requestor => [ $user_w->Name ],
- Subject => 'Test ticket for RT::Extension::EmailDigest',
- );
+ Requestor => [ $user_w->Name ],
+ Subject => 'Test ticket for RT::Extension::EmailDigest',
+ );
ok( $ret, "Ticket $id created: $msg" );
# Make the other users ticket watchers.
( $ret, $msg ) = $ticket->AddWatcher( Type => 'Cc',
- PrincipalId => $user_n->PrincipalObj->id );
+ PrincipalId => $user_n->PrincipalObj->id );
ok( $ret, "Added user_n as a ticket watcher: $msg" );
( $ret, $msg ) = $ticket->AddWatcher( Type => 'Cc',
- PrincipalId => $user_s->PrincipalObj->id );
+ PrincipalId => $user_s->PrincipalObj->id );
ok( $ret, "Added user_s as a ticket watcher: $msg" );
my $obj;
($id, $msg, $obj ) = $ticket->Correspond(
- Content => "This is a ticket response for CC action" );
+ Content => "This is a ticket response for CC action" );
ok( $ret, "Transaction created: $msg" );
# Get the deferred notifications that should result. Should be two for
@@ -113,11 +113,11 @@ while( my $txn = $txns->Next ) {
# If the transaction has content...
if( $txn->ContentObj ) {
- # ...none of the deferred folk should be in the header.
- my $headerstr = $txn->ContentObj->Headers;
- foreach my $rcpt( @daily_rcpt, @weekly_rcpt, @susp_rcpt ) {
- ok( $headerstr !~ /$rcpt/, "Deferred recipient $rcpt not found in header" );
- }
+ # ...none of the deferred folk should be in the header.
+ my $headerstr = $txn->ContentObj->Headers;
+ foreach my $rcpt( @daily_rcpt, @weekly_rcpt, @susp_rcpt ) {
+ ok( $headerstr !~ /$rcpt/, "Deferred recipient $rcpt not found in header" );
+ }
}
}
diff --git a/rt/t/mail/gateway.t b/rt/t/mail/gateway.t
index 4f906c89c..89b1b60ab 100644
--- a/rt/t/mail/gateway.t
+++ b/rt/t/mail/gateway.t
@@ -420,7 +420,7 @@ EOF
diag "Testing preservation of binary attachments";
{
# Get a binary blob (Best Practical logo)
- my $LOGO_FILE = $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png';
+ my $LOGO_FILE = $RT::StaticPath .'/images/bpslogo.png';
# Create a mime entity with an attachment
my $entity = MIME::Entity->build(
@@ -636,13 +636,15 @@ EOF
close (MAIL);
is ($? >> 8, 0, "The mail gateway exited normally");
+DBIx::SearchBuilder::Record::Cachable->FlushCache;
+
$tick = RT::Ticket->new(RT->SystemUser);
$tick->Load( $id );
is( $tick->Id, $id, 'load correct ticket');
is( $tick->OwnerObj->EmailAddress, 'root@localhost', 'successfuly take ticket via email');
-# check that there is no text transactions writen
-is( $tick->Transactions->Count, 2, 'no superfluous transactions');
+# check that there is no text transactions writen (create + 2 for take)
+is( $tick->Transactions->Count, 3, 'no superfluous transactions');
my $status;
($status, $msg) = $tick->SetOwner( RT->Nobody->Id, 'Force' );
@@ -672,8 +674,8 @@ is( $tick->OwnerObj->EmailAddress, 'root@localhost', 'successfuly take ticket vi
my $txns = $tick->Transactions;
$txns->Limit( FIELD => 'Type', VALUE => 'Correspond');
$txns->OrderBy( FIELD => 'id', ORDER => 'DESC' );
-# +1 because of auto open
-is( $tick->Transactions->Count, 6, 'no superfluous transactions');
+# +2 from owner to nobody, +1 because of auto open, +2 from take, +1 from correspond
+is( $tick->Transactions->Count, 9, 'no superfluous transactions');
is( $txns->First->Subject, "[$RT::rtname \#$id] correspondence", 'successfuly add correspond within take via email' );
$m->no_warnings_ok;
@@ -695,15 +697,16 @@ $tick = RT::Ticket->new(RT->SystemUser);
$tick->Load( $id );
is( $tick->Id, $id, 'load correct ticket');
is( $tick->Status, 'resolved', 'successfuly resolved ticket via email');
-is( $tick->Transactions->Count, 7, 'no superfluous transactions');
+# +1 from resolve
+is( $tick->Transactions->Count, 10, 'no superfluous transactions');
use RT::User;
my $user = RT::User->new( RT->SystemUser );
my ($uid) = $user->Create( Name => 'ext-mailgate',
- EmailAddress => 'ext-mailgate@localhost',
- Privileged => 1,
- Password => 'qwe123',
- );
+ EmailAddress => 'ext-mailgate@localhost',
+ Privileged => 1,
+ Password => 'qwe123',
+ );
ok( $uid, 'user created for ext-mailgate tests' );
ok( !$user->HasRight( Right => 'OwnTicket', Object => $queue ), "User can't own ticket" );
@@ -733,7 +736,7 @@ ok( $status, "successfuly granted right: $msg" );
my $ace_id = $status;
ok( $user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "User can reply to ticket" );
-$m->next_warning_like(qr/Permission Denied/);
+$m->next_warning_like(qr/That user may not own tickets in that queue/);
$m->next_warning_like(qr/Could not record email: Ticket not taken/);
$m->no_leftover_warnings_ok;
@@ -752,7 +755,7 @@ DBIx::SearchBuilder::Record::Cachable->FlushCache;
cmp_ok( $tick->Owner, '!=', $user->id, "we didn't change owner" );
is( $tick->Transactions->Count, 3, "one transactions added" );
-$m->next_warning_like(qr/Permission Denied/);
+$m->next_warning_like(qr/That user may not own tickets in that queue/);
$m->next_warning_like(qr/Could not record email: Ticket not taken/);
$m->no_leftover_warnings_ok;
@@ -771,7 +774,7 @@ DBIx::SearchBuilder::Record::Cachable->FlushCache;
cmp_ok( $tick->Owner, '!=', $user->id, "we didn't change owner" );
is( $tick->Transactions->Count, 3, "no transactions added, user can't take ticket first" );
-$m->next_warning_like(qr/Permission Denied/);
+$m->next_warning_like(qr/That user may not own tickets in that queue/);
$m->next_warning_like(qr/Could not record email: Ticket not taken/);
$m->no_leftover_warnings_ok;
@@ -784,14 +787,14 @@ my $acl = RT::ACL->new(RT->SystemUser);
$acl->Limit( FIELD => 'RightName', VALUE => 'ReplyToTicket' );
$acl->LimitToObject( $RT::System );
while( my $ace = $acl->Next ) {
- $ace->Delete;
+ $ace->Delete;
}
ok( !$user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "User can't reply to ticket any more" );
-my $group = RT::Group->new( RT->SystemUser );
-ok( $group->LoadQueueRoleGroup( Queue => $qid, Type=> 'Owner' ), "load queue owners role group" );
+my $group = $queue->RoleGroup( 'Owner' );
+ok( $group->Id, "load queue owners role group" );
$ace = RT::ACE->new( RT->SystemUser );
($ace_id, $msg) = $group->PrincipalObj->GrantRight( Right => 'ReplyToTicket', Object => $queue );
ok( $ace_id, "Granted queue owners role group with ReplyToTicket right" );
@@ -816,7 +819,8 @@ DBIx::SearchBuilder::Record::Cachable->FlushCache;
$tick->Load( $id );
is( $tick->Owner, $user->id, "we changed owner" );
ok( $user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "owner can reply to ticket" );
-is( $tick->Transactions->Count, 5, "transactions added" );
+# +2 from take, +1 from correspond
+is( $tick->Transactions->Count, 6, "transactions added" );
$m->no_warnings_ok;
diff --git a/rt/t/mail/gnupg-bad.t b/rt/t/mail/gnupg-bad.t
index a1c45be05..570501c30 100644
--- a/rt/t/mail/gnupg-bad.t
+++ b/rt/t/mail/gnupg-bad.t
@@ -10,7 +10,7 @@ use RT::Test::GnuPG
),
};
-RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' );
+RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::Crypt' );
my ($baseurl, $m) = RT::Test->started_ok;
diff --git a/rt/t/mail/gnupg-incoming.t b/rt/t/mail/gnupg-incoming.t
index 48d2d9b73..54b30d2a3 100644
--- a/rt/t/mail/gnupg-incoming.t
+++ b/rt/t/mail/gnupg-incoming.t
@@ -28,7 +28,7 @@ ok( $m->login, 'we did log in' );
$m->get( $baseurl.'/Admin/Queues/');
$m->follow_link_ok( {text => 'General'} );
$m->submit_form( form_number => 3,
- fields => { CorrespondAddress => 'general@example.com' } );
+ fields => { CorrespondAddress => 'general@example.com' } );
$m->content_like(qr/general\@example.com.* - never/, 'has key info.');
ok(my $user = RT::User->new(RT->SystemUser));
@@ -73,6 +73,7 @@ run3(
'--default-key' => 'recipient@example.com',
'--homedir' => $homedir,
'--passphrase' => 'recipient',
+ '--no-permission-warning',
),
\"fnord\r\n",
\$buf,
@@ -115,6 +116,7 @@ run3(
'--default-key' => 'recipient@example.com',
'--homedir' => $homedir,
'--passphrase' => 'recipient',
+ '--no-permission-warning',
),
\"clearfnord\r\n",
\$buf,
@@ -157,6 +159,7 @@ run3(
'--default-key' => 'recipient@example.com',
'--homedir' => $homedir,
'--passphrase' => 'recipient',
+ '--no-permission-warning',
),
\"orzzzzzz\r\n",
\$buf,
@@ -187,7 +190,7 @@ RT::Test->close_mailgate_ok($mail);
'recorded incoming mail that is encrypted'
);
is( $msg->GetHeader('X-RT-Privacy'),
- 'PGP',
+ 'GnuPG',
'recorded incoming mail that is encrypted'
);
like( $attach->Content, qr/orz/);
@@ -224,7 +227,7 @@ RT::Test->close_mailgate_ok($mail);
'recorded incoming mail that is encrypted'
);
is( $msg->GetHeader('X-RT-Privacy'),
- 'PGP',
+ 'GnuPG',
'recorded incoming mail that is encrypted'
);
like( $attach->Content, qr/orz/);
@@ -242,6 +245,7 @@ run3(
'--default-key' => 'rt@example.com',
'--homedir' => $homedir,
'--passphrase' => 'test',
+ '--no-permission-warning',
),
\"alright\r\n",
\$buf,
@@ -277,6 +281,7 @@ run3(
qw(gpg --batch --no-tty --armor --encrypt),
'--recipient' => 'random@localhost',
'--homedir' => $homedir,
+ '--no-permission-warning',
),
\"should not be there either\r\n",
\$buf,
@@ -314,6 +319,7 @@ run3(
qw(gpg --batch --no-tty --armor --encrypt),
'--recipient' => 'rt@example.com',
'--homedir' => $homedir,
+ '--no-permission-warning',
),
\"really should not be there either\r\n",
\$buf,
diff --git a/rt/t/mail/gnupg-outgoing-encrypted-plaintext.t b/rt/t/mail/gnupg-outgoing-encrypted-plaintext.t
new file mode 100644
index 000000000..35cfceddd
--- /dev/null
+++ b/rt/t/mail/gnupg-outgoing-encrypted-plaintext.t
@@ -0,0 +1,27 @@
+use strict;
+use warnings;
+
+use RT::Test::GnuPG
+ tests => 104,
+ text_templates => 1,
+ gnupg_options => {
+ passphrase => 'rt-test',
+ 'trust-model' => 'always',
+ };
+
+RT::Test->import_gnupg_key('rt-recipient@example.com');
+RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' );
+
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'Regression',
+ CorrespondAddress => 'rt-recipient@example.com',
+ CommentAddress => 'rt-recipient@example.com',
+ Encrypt => 1,
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok $m->login, 'logged in';
+
+create_and_test_outgoing_emails( $queue, $m );
+
diff --git a/rt/t/mail/gnupg-outgoing-plain-plaintext.t b/rt/t/mail/gnupg-outgoing-plain-plaintext.t
new file mode 100644
index 000000000..32e7d5d8c
--- /dev/null
+++ b/rt/t/mail/gnupg-outgoing-plain-plaintext.t
@@ -0,0 +1,25 @@
+use strict;
+use warnings;
+
+use RT::Test::GnuPG
+ tests => 104,
+ text_templates => 1,
+ gnupg_options => {
+ passphrase => 'rt-test',
+ 'trust-model' => 'always',
+ };
+
+RT::Test->import_gnupg_key('rt-recipient@example.com');
+RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' );
+
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'Regression',
+ CorrespondAddress => 'rt-recipient@example.com',
+ CommentAddress => 'rt-recipient@example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok $m->login, 'logged in';
+
+create_and_test_outgoing_emails( $queue, $m );
diff --git a/rt/t/mail/gnupg-outgoing-signed-plaintext.t b/rt/t/mail/gnupg-outgoing-signed-plaintext.t
new file mode 100644
index 000000000..cf46edd52
--- /dev/null
+++ b/rt/t/mail/gnupg-outgoing-signed-plaintext.t
@@ -0,0 +1,27 @@
+use strict;
+use warnings;
+
+use RT::Test::GnuPG
+ tests => 104,
+ text_templates => 1,
+ gnupg_options => {
+ passphrase => 'rt-test',
+ 'trust-model' => 'always',
+ };
+
+RT::Test->import_gnupg_key('rt-recipient@example.com');
+RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' );
+
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'Regression',
+ CorrespondAddress => 'rt-recipient@example.com',
+ CommentAddress => 'rt-recipient@example.com',
+ Sign => 1,
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok $m->login, 'logged in';
+
+create_and_test_outgoing_emails( $queue, $m );
+
diff --git a/rt/t/mail/gnupg-outgoing-signed_encrypted-plaintext.t b/rt/t/mail/gnupg-outgoing-signed_encrypted-plaintext.t
new file mode 100644
index 000000000..c2753d00b
--- /dev/null
+++ b/rt/t/mail/gnupg-outgoing-signed_encrypted-plaintext.t
@@ -0,0 +1,28 @@
+use strict;
+use warnings;
+
+use RT::Test::GnuPG
+ tests => 104,
+ text_templates => 1,
+ gnupg_options => {
+ passphrase => 'rt-test',
+ 'trust-model' => 'always',
+ };
+
+RT::Test->import_gnupg_key('rt-recipient@example.com');
+RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' );
+
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'Regression',
+ CorrespondAddress => 'rt-recipient@example.com',
+ CommentAddress => 'rt-recipient@example.com',
+ Sign => 1,
+ Encrypt => 1,
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok $m->login, 'logged in';
+
+create_and_test_outgoing_emails( $queue, $m );
+
diff --git a/rt/t/mail/gnupg-realmail.t b/rt/t/mail/gnupg-realmail.t
index 834014ccc..1609cffbb 100644
--- a/rt/t/mail/gnupg-realmail.t
+++ b/rt/t/mail/gnupg-realmail.t
@@ -62,7 +62,7 @@ sub email_ok {
my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
is( $msg->GetHeader('X-RT-Privacy'),
- 'PGP',
+ 'GnuPG',
"$eid: recorded incoming mail that is encrypted"
);
diff --git a/rt/t/mail/gnupg-reverification.t b/rt/t/mail/gnupg-reverification.t
index deef1ec24..06c2e0d40 100644
--- a/rt/t/mail/gnupg-reverification.t
+++ b/rt/t/mail/gnupg-reverification.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test::GnuPG tests => 232, gnupg_options => { passphrase => 'rt-test' };
+use RT::Test::GnuPG tests => undef, gnupg_options => { passphrase => 'rt-test' };
diag "load Everyone group";
my $everyone;
@@ -46,8 +46,7 @@ foreach my $file ( @files ) {
is $status >> 8, 0, "$eid: the mail gateway exited normally";
ok $id, "$eid: got id of a newly created ticket - $id";
- like($warnings, qr/Had a problem during decrypting and verifying/);
- like($warnings, qr/public key not found/);
+ like($warnings, qr/Public key '0xD328035D84881F1B' is not available/);
my $ticket = RT::Ticket->new( RT->SystemUser );
$ticket->Load( $id );
@@ -62,12 +61,10 @@ foreach my $file ( @files ) {
$m->content_like(qr/This is .*ID:$eid/ims, "$eid: content is there and message is decrypted");
$m->next_warning_like(qr/public key not found/);
- $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
# some mails contain multiple signatures
if ($eid == 5 || $eid == 17 || $eid == 18) {
$m->next_warning_like(qr/public key not found/);
- $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
}
$m->no_leftover_warnings_ok;
@@ -90,3 +87,5 @@ foreach my $id ( @ticket_ids ) {
$m->no_warnings_ok;
}
+undef $m;
+done_testing;
diff --git a/rt/t/mail/header-characters.t b/rt/t/mail/header-characters.t
index 004ba8522..38a04b5df 100644
--- a/rt/t/mail/header-characters.t
+++ b/rt/t/mail/header-characters.t
@@ -6,14 +6,10 @@ use Test::Warn;
my ($baseurl, $m) = RT::Test->started_ok;
-diag "Testing non-ASCII in From: header";
-SKIP:{
- skip "Test requires Email::Address 1.893 or later, "
- . "you have $Email::Address::VERSION", 3,
- if $Email::Address::VERSION < 1.893;
-
+diag "Testing non-ASCII latin1 in From: header";
+{
my $mail = Encode::encode( 'iso-8859-1', Encode::decode( "UTF-8", <<'.') );
-From: René@example.com>
+From: <René@example.com>
Reply-To: =?iso-8859-1?Q?Ren=E9?= <René@example.com>
Subject: testing non-ASCII From
Content-Type: text/plain; charset=iso-8859-1
@@ -23,24 +19,20 @@ here's some content
my ($status, $id);
warnings_like { ( $status, $id ) = RT::Test->send_via_mailgate($mail) }
- [qr/Failed to parse Reply-To:.*, From:/,
+ [(qr/Unable to parse an email address from/) x 2,
qr/Couldn't parse or find sender's address/
],
'Got parse error for non-ASCII in From';
- is( $status >> 8, 0, "The mail gateway exited normally" );
TODO: {
- local $TODO = "Currently don't handle non-ASCII for sender";
- ok( $id, "Created ticket" );
- }
+ local $TODO = "Currently don't handle non-ASCII for sender";
+ is( $status >> 8, 0, "The mail gateway exited normally" );
+ ok( $id, "Created ticket" );
+ }
}
-diag "Testing iso-8859-1 encoded non-ASCII in From: header";
-SKIP:{
- skip "Test requires Email::Address 1.893 or later, "
- . "you have $Email::Address::VERSION", 3,
- if $Email::Address::VERSION < 1.893;
-
- my $mail = Encode::encode( 'iso-8859-1', Encode::decode( "UTF-8", <<'.' ) );
+diag "Testing non-ASCII latin1 in From: header with MIME-word-encoded phrase";
+{
+ my $mail = Encode::encode( 'iso-8859-1', Encode::decode( "UTF-8", <<'.') );
From: =?iso-8859-1?Q?Ren=E9?= <René@example.com>
Reply-To: =?iso-8859-1?Q?Ren=E9?= <René@example.com>
Subject: testing non-ASCII From
@@ -51,14 +43,14 @@ here's some content
my ($status, $id);
warnings_like { ( $status, $id ) = RT::Test->send_via_mailgate($mail) }
- [qr/Failed to parse Reply-To:.*, From:/,
+ [(qr/Unable to parse an email address from/) x 2,
qr/Couldn't parse or find sender's address/
],
'Got parse error for iso-8859-1 in From';
- is( $status >> 8, 0, "The mail gateway exited normally" );
TODO: {
- local $TODO = "Currently don't handle non-ASCII in sender";
- ok( $id, "Created ticket" );
+ local $TODO = "Currently don't handle non-ASCII in sender";
+ is( $status >> 8, 0, "The mail gateway exited normally" );
+ ok( $id, "Created ticket" );
}
}
@@ -76,6 +68,6 @@ here's some content
warnings_like { ( $status, $id ) = RT::Test->send_via_mailgate($mail) }
[qr/Couldn't parse or find sender's address/],
'Got parse error with no sender fields';
- is( $status >> 8, 0, "The mail gateway exited normally" );
+ is( $status >> 8, 1, "The mail gateway failed" );
ok( !$id, "No ticket created" );
}
diff --git a/rt/t/mail/html-outgoing.t b/rt/t/mail/html-outgoing.t
new file mode 100644
index 000000000..a37f52cdd
--- /dev/null
+++ b/rt/t/mail/html-outgoing.t
@@ -0,0 +1,187 @@
+use strict;
+use warnings;
+use RT::Test tests => undef;
+
+use RT::Test::Email;
+use Test::Warn;
+
+my $root = RT::User->new(RT->SystemUser);
+$root->Load('root');
+
+# Set root as admincc
+my $q = RT::Queue->new(RT->SystemUser);
+$q->Load('General');
+my ($ok, $msg) = $q->AddWatcher( Type => 'AdminCc', PrincipalId => $root->Id );
+ok($ok, "Added root as a watcher on the General queue");
+
+# Create a couple users to test notifications
+my %users;
+for my $user_name (qw(enduser tech)) {
+ my $user = $users{$user_name} = RT::User->new(RT->SystemUser);
+ $user->Create( Name => ucfirst($user_name),
+ Privileged => 1,
+ EmailAddress => $user_name.'@example.com');
+ my ($val, $msg);
+ ($val, $msg) = $user->PrincipalObj->GrantRight(Object =>$q, Right => $_)
+ for qw(ModifyTicket OwnTicket ShowTicket);
+}
+
+my $t = RT::Ticket->new(RT->SystemUser);
+my ($tid, $ttrans, $tmsg);
+
+diag "Autoreply and AdminCc (Transaction)";
+mail_ok {
+ ($tid, $ttrans, $tmsg) =
+ $t->Create(Subject => "The internet is broken",
+ Owner => 'Tech', Requestor => 'Enduser',
+ Queue => 'General');
+} { from => qr/The default queue/,
+ to => 'enduser@example.com',
+ subject => qr/\Q[example.com #1] AutoReply: The internet is broken\E/,
+ body => parts_regex(
+ 'trouble ticket regarding \*?The internet is broken\*?',
+ 'trouble ticket regarding <b>The internet is broken</b>'
+ ),
+ 'Content-Type' => qr{multipart},
+},{ from => qr/RT System/,
+ bcc => 'root@localhost',
+ subject => qr/\Q[example.com #1] The internet is broken\E/,
+ body => parts_regex(
+ 'Request (\[\d+\])?1(\s*[(<]http://localhost:\d+/Ticket/Display\.html\?id=1[)>])?\s*was acted upon by RT_System',
+ 'Request <a href="http://localhost:\d+/Ticket/Display\.html\?id=1">1</a> was acted upon by RT_System\.</b>'
+ ),
+ 'Content-Type' => qr{multipart},
+};
+
+diag "Admin Correspondence and Correspondence";
+mail_ok {
+ ($ok, $tmsg) = $t->Correspond(
+ MIMEObj => HTML::Mason::Commands::MakeMIMEEntity(
+ Body => '<p>This is a test of <b>HTML</b> correspondence.</p>',
+ Type => 'text/html',
+ ),
+ );
+} { from => qr/RT System/,
+ bcc => 'root@localhost',
+ subject => qr/\Q[example.com #1] The internet is broken\E/,
+ body => parts_regex(
+ 'Ticket URL: (?:\[\d+\])?http://localhost:\d+/Ticket/Display\.html\?id=1.+?'.
+ 'This is a test of \*?HTML\*? correspondence\.',
+ 'Ticket URL: <a href="(http://localhost:\d+/Ticket/Display\.html\?id=1)">\1</a>.+?'.
+ '<p>This is a test of <b>HTML</b> correspondence\.</p>'
+ ),
+ 'Content-Type' => qr{multipart},
+},{ from => qr/RT System/,
+ to => 'enduser@example.com',
+ subject => qr/\Q[example.com #1] The internet is broken\E/,
+ body => parts_regex(
+ 'This is a test of \*?HTML\*? correspondence\.',
+ '<p>This is a test of <b>HTML</b> correspondence\.</p>'
+ ),
+ 'Content-Type' => qr{multipart},
+};
+
+SKIP: {
+ skip "Only fails on core HTMLFormatter", 9
+ unless RT->Config->Get("HTMLFormatter") eq "core";
+ diag "Failing HTML -> Text conversion";
+ warnings_like {
+ my $body = '<table><tr><td><table><tr><td>Foo</td></tr></table></td></tr></table>';
+ mail_ok {
+ ($ok, $tmsg) = $t->Correspond(
+ MIMEObj => HTML::Mason::Commands::MakeMIMEEntity(
+ Body => $body,
+ Type => 'text/html',
+ ),
+ );
+ } { from => qr/RT System/,
+ bcc => 'root@localhost',
+ subject => qr/\Q[example.com #1] The internet is broken\E/,
+ body => qr{Ticket URL: <a href="(http://localhost:\d+/Ticket/Display\.html\?id=1)">\1</a>.+?$body}s,
+ 'Content-Type' => qr{text/html}, # TODO
+ },{ from => qr/RT System/,
+ to => 'enduser@example.com',
+ subject => qr/\Q[example.com #1] The internet is broken\E/,
+ body => qr{$body},
+ 'Content-Type' => qr{text/html}, # TODO
+ };
+ } [(qr/uninitialized value/, qr/Failed to downgrade HTML/)x3];
+}
+
+
+diag "Admin Comment in HTML";
+mail_ok {
+ ($ok, $tmsg) = $t->Comment(
+ MIMEObj => HTML::Mason::Commands::MakeMIMEEntity(
+ Body => '<p>Comment test, <em>please!</em></p>',
+ Type => 'text/html',
+ ),
+ );
+} { from => qr/RT System/,
+ bcc => 'root@localhost',
+ subject => qr/\Q[example.com #1] [Comment] The internet is broken\E/,
+ body => parts_regex(
+ 'This is a comment about (\[\d+\])?ticket.1(\s*[(<]http://localhost:\d+/Ticket/Display\.html\?id=1[)>])?\..+?'.
+ 'It is not sent to the Requestor\(s\):.+?'.
+ 'Comment test, _?please!_?',
+
+ '<p>This is a comment about <a href="http://localhost:\d+/Ticket/Display\.html\?id=1">ticket 1</a>\. '.
+ 'It is not sent to the Requestor\(s\):</p>.+?'.
+ '<p>Comment test, <em>please!</em></p>',
+ ),
+ 'Content-Type' => qr{multipart},
+};
+
+
+diag "Resolved in HTML templates";
+mail_ok {
+ ($ok, $tmsg) = $t->SetStatus('resolved');
+} { from => qr/RT System/,
+ to => 'enduser@example.com',
+ subject => qr/\Q[example.com #1] Resolved: The internet is broken\E/,
+ body => parts_regex(
+ 'According to our records, your request has been resolved\.',
+ '<p>According to our records, your request has been resolved\.',
+ ),
+ 'Content-Type' => qr{multipart},
+};
+
+
+diag "Status changes in HTML";
+my $scrip = RT::Scrip->new(RT->SystemUser);
+my ($sval, $smsg) =$scrip->Create(
+ ScripCondition => 'On Status Change',
+ ScripAction => 'Notify Requestors',
+ Template => 'Status Change in HTML',
+ Queue => $q->Id,
+ Description => 'Tell requestors about status changes'
+);
+ok ($sval, $smsg);
+ok ($scrip->Id, "Created the scrip");
+ok ($scrip->TemplateObj->Id, "Created the scrip template");
+ok ($scrip->ConditionObj->Id, "Created the scrip condition");
+ok ($scrip->ActionObj->Id, "Created the scrip action");
+
+mail_ok {
+ ($ok, $tmsg) = $t->SetStatus('stalled');
+} { from => qr/RT System/,
+ to => 'enduser@example.com',
+ subject => qr/\Q[example.com #1] Status Changed to: stalled\E/,
+ body => parts_regex(
+ 'http://localhost:\d+/Ticket/Display\.html\?id=1.+?',
+ '<a href="(http://localhost:\d+/Ticket/Display\.html\?id=1)">\1</a>'
+ ),
+ 'Content-Type' => qr{multipart},
+};
+
+done_testing;
+
+sub parts_regex {
+ my ($text, $html) = @_;
+
+ my $pattern = 'Content-Type: text/plain.+?' . $text . '.+?' .
+ 'Content-Type: text/html.+?' . $html;
+
+ return qr/$pattern/s;
+}
+
diff --git a/rt/t/mail/mime_decoding.t b/rt/t/mail/mime_decoding.t
index fbf884932..1126f1f84 100644
--- a/rt/t/mail/mime_decoding.t
+++ b/rt/t/mail/mime_decoding.t
@@ -1,14 +1,23 @@
use strict;
use warnings;
-use RT::Test nodb => 1, tests => 14;
+use RT::Test nodb => 1, tests => undef;
+use Test::LongString;
+use Test::Warn;
use_ok('RT::I18N');
diag q{'=' char in a leading part before an encoded part};
{
my $str = 'key="plain"; key="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="';
+ warnings_like {
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ 'key="plain"; key="мой_файл.bin"',
+ "right decoding"
+ );
+ } [qr/DecodeMIMEWordsTo.*?called without field name/i];
is(
- RT::I18N::DecodeMIMEWordsToUTF8($str),
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'),
'key="plain"; key="мой_файл.bin"',
"right decoding"
);
@@ -17,8 +26,15 @@ diag q{'=' char in a leading part before an encoded part};
diag q{not compliant with standards, but MUAs send such field when attachment has non-ascii in name};
{
my $str = 'attachment; filename="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="';
+ warnings_like {
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ 'attachment; filename="мой_файл.bin"',
+ "right decoding"
+ );
+ } [qr/DecodeMIMEWordsTo.*?called without field name/i];
is(
- RT::I18N::DecodeMIMEWordsToUTF8($str),
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'),
'attachment; filename="мой_файл.bin"',
"right decoding"
);
@@ -27,8 +43,15 @@ diag q{not compliant with standards, but MUAs send such field when attachment ha
diag q{'=' char in a trailing part after an encoded part};
{
my $str = 'attachment; filename="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="; some_prop="value"';
+ warnings_like {
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ 'attachment; filename="мой_файл.bin"; some_prop="value"',
+ "right decoding"
+ );
+ } [qr/DecodeMIMEWordsTo.*?called without field name/i];
is(
- RT::I18N::DecodeMIMEWordsToUTF8($str),
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'),
'attachment; filename="мой_файл.bin"; some_prop="value"',
"right decoding"
);
@@ -36,12 +59,9 @@ diag q{'=' char in a trailing part after an encoded part};
diag q{adding quotes around mime words containing specials when word is already quoted};
{
- my $str = <<"END";
-Content-Disposition: attachment; filename="=?iso-8859-1?Q?foobar,_?=
- =?iso-8859-1?Q?barfoo.docx?="
-END
- my $decoded = 'Content-Disposition: attachment; filename="foobar, barfoo.docx"';
- is( RT::I18N::DecodeMIMEWordsToUTF8($str), $decoded, "No added quotes" );
+ my $str = 'attachment; filename="=?iso-8859-1?Q?foobar,_?=' . "\n" . '=?iso-8859-1?Q?barfoo.docx?="';
+ my $decoded = 'attachment; filename="foobar, barfoo.docx"';
+ is( RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'), $decoded, "No added quotes" );
}
diag q{regression test for #5248 from rt3.fsck.com};
@@ -49,7 +69,7 @@ diag q{regression test for #5248 from rt3.fsck.com};
my $str = qq{Subject: =?ISO-8859-1?Q?Re=3A_=5BXXXXXX=23269=5D_=5BComment=5D_Frag?=}
. qq{\n =?ISO-8859-1?Q?e_zu_XXXXXX--xxxxxx_/_Xxxxx=FCxxxxxxxxxx?=};
is(
- RT::I18N::DecodeMIMEWordsToUTF8($str),
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'Subject'),
qq{Subject: Re: [XXXXXX#269] [Comment] Frage zu XXXXXX--xxxxxx / Xxxxxüxxxxxxxxxx},
"right decoding"
);
@@ -58,9 +78,16 @@ diag q{regression test for #5248 from rt3.fsck.com};
diag q{newline and encoded file name};
{
my $str = qq{application/vnd.ms-powerpoint;\n\tname="=?ISO-8859-1?Q?Main_presentation.ppt?="};
+ warnings_like {
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ qq{application/vnd.ms-powerpoint;\tname="Main presentation.ppt"},
+ "right decoding"
+ );
+ } [qr/DecodeMIMEWordsTo.*?called without field name/i];
is(
- RT::I18N::DecodeMIMEWordsToUTF8($str),
- qq{application/vnd.ms-powerpoint;\tname="Main presentation.ppt"},
+ RT::I18N::DecodeMIMEWordsToUTF8($str,'content-type'),
+ qq{application/vnd.ms-powerpoint; name="Main presentation.ppt"},
"right decoding"
);
}
@@ -97,54 +124,116 @@ inline;
diag q{canonicalize mime word encodings like gb2312};
{
my $str = qq{Subject: =?gb2312?B?1NrKwL3nuPe12Lmy09CzrN9eX1NpbXBsaWZpZWRfQ05fR0IyMzEyYQ==?=
- =?gb2312?B?dHRhY2hlbWVudCB0ZXN0IGluIENOIHNpbXBsaWZpZWQ=?=};
+\t=?gb2312?B?dHRhY2hlbWVudCB0ZXN0IGluIENOIHNpbXBsaWZpZWQ=?=};
is(
- RT::I18N::DecodeMIMEWordsToUTF8($str),
+ RT::I18N::DecodeMIMEWordsToUTF8($str, "Subject"),
qq{Subject: 在世界各地共有超過_Simplified_CN_GB2312attachement test in CN simplified},
"right decoding"
);
}
-
diag q{Whitespace between encoded words should be removed};
{
- my $str = "=?utf-8?Q?=E3=82=AD?= =?utf-8?Q?=E3=83=A3?=";
- is(
- RT::I18N::DecodeMIMEWordsToUTF8($str),
- "キャ",
- "whitespace between encoded words is removed",
- );
-
- $str = "=?utf-8?Q?=E3=82=AD?= \n =?utf-8?Q?=E3=83=A3?=";
- is(
- RT::I18N::DecodeMIMEWordsToUTF8($str),
- "キャ",
- "newlines between encoded words also removed",
- );
+ warnings_like {
+ my $str = "=?utf-8?Q?=E3=82=AD?= =?utf-8?Q?=E3=83=A3?=";
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ "キャ",
+ "whitespace between encoded words is removed",
+ );
+
+ $str = "=?utf-8?Q?=E3=82=AD?= \n =?utf-8?Q?=E3=83=A3?=";
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ "キャ",
+ "newlines between encoded words also removed",
+ );
+ } [(qr/DecodeMIMEWordsTo.*?called without field name/i) x 2];
}
diag q{Multiple octets split across QP hunks are correctly reassembled};
{
- # This passes even without explicit code to handle it because utf8
- # is perl's internal string encoding.
- my $str = "=?utf-8?Q?=E3?= =?utf-8?Q?=82?= =?utf-8?Q?=AD?=";
- is(
- RT::I18N::DecodeMIMEWordsToUTF8($str),
- "キ",
- "UTF8 character split in three is successfully reassembled",
- );
-
- # Non-utf8 encodings thus also must be checked
- $str = <<EOT; chomp $str;
+ warnings_like {
+ # This passes even without explicit code to handle it because utf8
+ # is perl's internal string encoding.
+ my $str = "=?utf-8?Q?=E3?= =?utf-8?Q?=82?= =?utf-8?Q?=AD?=";
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ "キ",
+ "UTF8 character split in three is successfully reassembled",
+ );
+
+ # Non-utf8 encodings thus also must be checked
+ $str = <<EOT; chomp $str;
=?gb2312?q?Chinese(gb2312)=20=20=C3=C0=B9=FA=C7=B0=CB=BE=B7=A8=B2=BF=B3?=
=?gb2312?q?=A4=C3=E6=BC=FB=C8=F8=B4=EF=C4=B7=BA=F3=B3=C6=C6=E4=D7=B4=CC=AC?=
=?gb2312?q?=BA=DC=BA=C3=20=20Chinese=20(gb2312)?=
EOT
- is(
- RT::I18N::DecodeMIMEWordsToUTF8($str),
- "Chinese(gb2312) 美国前司法部长面见萨达姆后称其状态很好 Chinese (gb2312)",
- "gb2312 character is successfully reassembled",
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ "Chinese(gb2312) 美国前司法部长面见萨达姆后称其状态很好 Chinese (gb2312)",
+ "gb2312 character is successfully reassembled",
+ );
+ } [(qr/DecodeMIMEWordsTo.*?called without field name/i) x 2];
+}
+
+diag "multiple mime words containing special chars already in quotes";
+{
+ my $str = q{attachment; filename="=?ISO-2022-JP?B?Mi4bJEIlSyVlITwlOSVqJWohPCU5GyhC?= =?ISO-2022-JP?B?LnBkZg==?="};
+ is_string(
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'),
+ q{attachment; filename="2.ニュースリリース.pdf"},
+ "base64"
);
+ $str = q{attachment; filename="=?UTF-8?Q?2=2E=E3=83=8B=E3=83=A5=E3=83=BC=E3=82=B9=E3=83=AA=E3=83=AA?= =?UTF-8?Q?=E3=83=BC=E3=82=B9=2Epdf?="};
+ is_string(
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'),
+ q{attachment; filename="2.ニュースリリース.pdf"},
+ "QP"
+ );
}
+
+diag "mime word combined with text in quoted filename property";
+{
+ my $str = q{attachment; filename="=?UTF-8?B?Q2VjaSBuJ2VzdCBwYXMgdW5l?= pipe.pdf"};
+ is_string(
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'),
+ q{attachment; filename="Ceci n'est pas une pipe.pdf"},
+ "base64"
+ );
+
+ $str = q{attachment; filename="=?UTF-8?B?Q2VjaSBuJ2VzdCBwYXMgdW5lLi4u?= pipe.pdf"};
+ is_string(
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'),
+ q{attachment; filename="Ceci n'est pas une... pipe.pdf"},
+ "base64"
+ );
+
+ $str = q{attachment; filename="=?UTF-8?Q?Ceci n'est pas une?= pipe.pdf"};
+ is_string(
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'),
+ q{attachment; filename="Ceci n'est pas une pipe.pdf"},
+ "QP"
+ );
+
+ $str = q{attachment; filename="=?UTF-8?Q?Ceci n'est pas une...?= pipe.pdf"};
+ is_string(
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'),
+ q{attachment; filename="Ceci n'est pas une... pipe.pdf"},
+ "QP"
+ );
+}
+
+diag "quotes in filename";
+{
+ my $str = q{attachment; filename="=?UTF-8?B?YSAicXVvdGVkIiBmaWxl?="};
+ is_string(
+ RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'),
+ q{attachment; filename="a \"quoted\" file"},
+ "quoted filename correctly decoded"
+ );
+}
+
+done_testing;
diff --git a/rt/t/mail/multipart.t b/rt/t/mail/multipart.t
index 1c1106421..644305ebd 100644
--- a/rt/t/mail/multipart.t
+++ b/rt/t/mail/multipart.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test tests => 4;
+use RT::Test tests => 4, config => q{Set($CorrespondAddress, 'rt@example.com');};
use RT::Test::Email;
my $queue = RT::Test->load_or_create_queue( Name => 'General' );
@@ -37,4 +37,4 @@ is(@msgs,2,"sent 2 emails");
diag("We're skipping any testing of the autoreply");
my $entity = parse_mail($msgs[1]);
-is($entity->parts, 0, "only one entity");
+is($entity->parts, 2, "only two parts");
diff --git a/rt/t/mail/one-time-recipients.t b/rt/t/mail/one-time-recipients.t
index a9881cded..1bc172d71 100644
--- a/rt/t/mail/one-time-recipients.t
+++ b/rt/t/mail/one-time-recipients.t
@@ -1,7 +1,9 @@
use strict;
use warnings;
-use RT::Test tests => 38;
+use RT::Test tests => undef;
+use RT::Test::Email;
+use Test::Warn;
my $queue = RT::Test->load_or_create_queue(
Name => 'General',
@@ -17,191 +19,149 @@ my $user = RT::Test->load_or_create_user(
ok $user && $user->id, 'loaded or created user';
diag "Reply to ticket with actor as one time cc";
-{
+warnings_are {
my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) );
- my ($status, undef, $msg) = $ticket->Create(
- Queue => $queue->id,
- Subject => 'test',
- Requestor => 'root@localhost',
- );
- ok $status, "created ticket";
-
- my @mails = RT::Test->fetch_caught_mails;
- ok @mails, "got some outgoing emails";
- foreach my $mail ( @mails ) {
- my $entity = parse_mail( $mail );
- my $to = $entity->head->get('To');
- $to =~ s/^\s+|\s+$//;
- is $to, 'root@localhost', 'got mail'
- }
+ mail_ok {
+ my ($status, undef, $msg) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => 'test',
+ Requestor => 'root@localhost',
+ );
+ ok $status, "created ticket";
+ } { To => 'root@localhost' };
RT->Config->Set( NotifyActor => 1 );
- ($status, $msg) = $ticket->Correspond(
- Content => 'test mail',
- );
- ok $status, "replied to a ticket";
-
- @mails = RT::Test->fetch_caught_mails;
- ok @mails, "got some outgoing emails";
- foreach my $mail ( @mails ) {
- my $entity = parse_mail( $mail );
- my $to = $entity->head->get('To');
- $to =~ s/^\s+|\s+$//;
- is $to, 'root@localhost', 'got mail'
- }
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ );
+ ok $status, "replied to a ticket";
+ } { To => 'root@localhost' };
RT->Config->Set( NotifyActor => 0 );
- ($status, $msg) = $ticket->Correspond(
- Content => 'test mail',
- );
- ok $status, "replied to a ticket";
-
- @mails = RT::Test->fetch_caught_mails;
- ok !@mails, "no mail - don't notify actor";
-
- ($status, $msg) = $ticket->Correspond(
- Content => 'test mail',
- CcMessageTo => 'root@localhost',
- );
- ok $status, "replied to a ticket";
-
- @mails = RT::Test->fetch_caught_mails;
- ok @mails, "got some outgoing emails";
- foreach my $mail ( @mails ) {
- my $entity = parse_mail( $mail );
- my $to = $entity->head->get('Cc');
- $to =~ s/^\s+|\s+$//;
- is $to, 'root@localhost', 'got mail'
- }
-}
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ );
+ ok $status, "replied to a ticket";
+ };
+
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ CcMessageTo => 'root@localhost',
+ );
+ ok $status, "replied to a ticket";
+ } { Cc => 'root@localhost' };
+} [];
diag "Reply to ticket with requestor squelched";
-{
+warnings_are {
my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) );
- my ($status, undef, $msg) = $ticket->Create(
- Queue => $queue->id,
- Subject => 'test',
- Requestor => 'test@localhost',
- );
- ok $status, "created ticket";
-
- my @mails = RT::Test->fetch_caught_mails;
- ok @mails, "got some outgoing emails";
- foreach my $mail ( @mails ) {
- my $entity = parse_mail( $mail );
- my $to = $entity->head->get('To');
- $to =~ s/^\s+|\s+$//;
- is $to, 'test@localhost', 'got mail'
- }
-
- ($status, $msg) = $ticket->Correspond(
- Content => 'test mail',
- );
- ok $status, "replied to a ticket";
-
- @mails = RT::Test->fetch_caught_mails;
- ok @mails, "got some outgoing emails";
- foreach my $mail ( @mails ) {
- my $entity = parse_mail( $mail );
- my $to = $entity->head->get('To');
- $to =~ s/^\s+|\s+$//;
- is $to, 'test@localhost', 'got mail'
- }
+ mail_ok {
+ my ($status, undef, $msg) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => 'test',
+ Requestor => 'test@localhost',
+ );
+ ok $status, "created ticket";
+ } { To => 'test@localhost' };
+
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ );
+ ok $status, "replied to a ticket";
+ } { To => 'test@localhost' };
$ticket->SquelchMailTo('test@localhost');
- ($status, $msg) = $ticket->Correspond(
- Content => 'test mail',
- );
- ok $status, "replied to a ticket";
-
- @mails = RT::Test->fetch_caught_mails;
- ok !@mails, "no mail - squelched";
-
- ($status, $msg) = $ticket->Correspond(
- Content => 'test mail',
- CcMessageTo => 'test@localhost',
- );
- ok $status, "replied to a ticket";
-
- @mails = RT::Test->fetch_caught_mails;
- ok @mails, "got some outgoing emails";
- foreach my $mail ( @mails ) {
- my $entity = parse_mail( $mail );
- my $to = $entity->head->get('Cc');
- $to =~ s/^\s+|\s+$//;
- is $to, 'test@localhost', 'got mail'
- }
-}
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ );
+ ok $status, "replied to a ticket";
+ };
+
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ CcMessageTo => 'test@localhost',
+ );
+ ok $status, "replied to a ticket";
+ } { Cc => 'test@localhost' };
+} [];
diag "Reply to ticket with requestor squelched";
-{
+warnings_are {
my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) );
- my ($status, undef, $msg) = $ticket->Create(
- Queue => $queue->id,
- Subject => 'test',
- Requestor => 'test@localhost',
- );
- ok $status, "created ticket";
-
- my @mails = RT::Test->fetch_caught_mails;
- ok @mails, "got some outgoing emails";
- foreach my $mail ( @mails ) {
- my $entity = parse_mail( $mail );
- my $to = $entity->head->get('To');
- $to =~ s/^\s+|\s+$//;
- is $to, 'test@localhost', 'got mail'
- }
-
- ($status, $msg) = $ticket->Correspond(
- Content => 'test mail',
- );
- ok $status, "replied to a ticket";
-
- @mails = RT::Test->fetch_caught_mails;
- ok @mails, "got some outgoing emails";
- foreach my $mail ( @mails ) {
- my $entity = parse_mail( $mail );
- my $to = $entity->head->get('To');
- $to =~ s/^\s+|\s+$//;
- is $to, 'test@localhost', 'got mail'
- }
-
- ($status, $msg) = $ticket->Correspond(
- Content => 'test mail',
- SquelchMailTo => ['test@localhost'],
- );
- ok $status, "replied to a ticket";
-
- @mails = RT::Test->fetch_caught_mails;
- ok !@mails, "no mail - squelched";
-
- ($status, $msg) = $ticket->Correspond(
- Content => 'test mail',
- );
- ok $status, "replied to a ticket";
-
- @mails = RT::Test->fetch_caught_mails;
- ok @mails, "got some outgoing emails";
- foreach my $mail ( @mails ) {
- my $entity = parse_mail( $mail );
- my $to = $entity->head->get('To');
- $to =~ s/^\s+|\s+$//;
- is $to, 'test@localhost', 'got mail'
- }
-
- ($status, $msg) = $ticket->Correspond(
- Content => 'test mail',
- CcMessageTo => 'test@localhost',
- SquelchMailTo => ['test@localhost'],
- );
- ok $status, "replied to a ticket";
-
- @mails = RT::Test->fetch_caught_mails;
- ok @mails, "got some outgoing emails";
- foreach my $mail ( @mails ) {
- my $entity = parse_mail( $mail );
- my $to = $entity->head->get('Cc');
- $to =~ s/^\s+|\s+$//;
- is $to, 'test@localhost', 'got mail'
- }
-}
+ mail_ok {
+ my ($status, undef, $msg) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => 'test',
+ Requestor => 'test@localhost',
+ );
+ ok $status, "created ticket";
+ } { To => 'test@localhost' };
+
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ );
+ ok $status, "replied to a ticket";
+ } { To => 'test@localhost' };
+
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ SquelchMailTo => ['test@localhost'],
+ );
+ ok $status, "replied to a ticket";
+ };
+
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ );
+ ok $status, "replied to a ticket";
+ } { To => 'test@localhost' };
+
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ CcMessageTo => 'test@localhost',
+ SquelchMailTo => ['test@localhost'],
+ );
+ ok $status, "replied to a ticket";
+ } { Cc => 'test@localhost' };
+} [];
+
+diag "Requestor is an RT address";
+warnings_are {
+ my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) );
+ mail_ok {
+ my ($status, undef, $msg) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => 'test',
+ Requestor => 'rt-address@example.com',
+ );
+ ok $status, "created ticket";
+ } { To => 'rt-address@example.com' };
+
+ RT->Config->Set( RTAddressRegexp => qr/^rt-address\@example\.com$/i );
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ );
+ ok $status, "replied to a ticket";
+ };
+
+ mail_ok {
+ my ($status, $msg) = $ticket->Correspond(
+ Content => 'test mail',
+ CcMessageTo => 'rt-address@example.com',
+ );
+ ok $status, "replied to a ticket";
+ };
+} [];
+
+done_testing;
diff --git a/rt/t/mail/outlook.t b/rt/t/mail/outlook.t
index 752a91fae..8f3b71bc8 100644
--- a/rt/t/mail/outlook.t
+++ b/rt/t/mail/outlook.t
@@ -15,11 +15,11 @@ X-Mailer: $mailer
To: rt\@@{[RT->Config->Get('rtname')]}
Subject: outlook basic test
Content-Type: multipart/alternative;
- boundary="----=_NextPart_000_0004_01CB045C.A5A075D0"
+\tboundary="----=_NextPart_000_0004_01CB045C.A5A075D0"
------=_NextPart_000_0004_01CB045C.A5A075D0
Content-Type: text/plain;
- charset="us-ascii"
+\tcharset="us-ascii"
Content-Transfer-Encoding: 7bit
here is the content
@@ -33,7 +33,7 @@ another line
------=_NextPart_000_0004_01CB045C.A5A075D0
Content-Type: text/html;
- charset="us-ascii"
+\tcharset="us-ascii"
Content-Transfer-Encoding: quoted-printable
<html>this is fake</html>
@@ -61,16 +61,16 @@ X-Mailer: $mailer
To: rt\@@{[RT->Config->Get('rtname')]}
Subject: outlook basic test
Content-Type: multipart/mixed;
- boundary="----=_NextPart_000_000F_01CB045E.5222CB40"
+\tboundary="----=_NextPart_000_000F_01CB045E.5222CB40"
------=_NextPart_000_000F_01CB045E.5222CB40
Content-Type: multipart/alternative;
- boundary="----=_NextPart_001_0010_01CB045E.5222CB40"
+\tboundary="----=_NextPart_001_0010_01CB045E.5222CB40"
------=_NextPart_001_0010_01CB045E.5222CB40
Content-Type: text/plain;
- charset="us-ascii"
+\tcharset="us-ascii"
Content-Transfer-Encoding: 7bit
foo
@@ -84,7 +84,7 @@ baz
------=_NextPart_001_0010_01CB045E.5222CB40
Content-Type: text/html;
- charset="us-ascii"
+\tcharset="us-ascii"
Content-Transfer-Encoding: quoted-printable
<html>this is fake</html>
@@ -93,10 +93,10 @@ Content-Transfer-Encoding: quoted-printable
------=_NextPart_000_000F_01CB045E.5222CB40
Content-Type: text/plain;
- name="att.txt"
+\tname="att.txt"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
- filename="att.txt"
+\tfilename="att.txt"
this is the attachment! :)=0A=
@@ -210,8 +210,8 @@ John Smith
Some Company
email\@someco.com
EOF
- test_email( $text, $content,
- ' with base64, no X-Mailer, \n\n are replaced' );
+ test_email( $text, $content,
+ ' with base64, no X-Mailer, \n\n are replaced' );
}
@@ -223,11 +223,11 @@ X-Mailer: Mutt
To: rt\@@{[RT->Config->Get('rtname')]}
Subject: outlook basic test
Content-Type: multipart/alternative;
- boundary="----=_NextPart_000_0004_01CB045C.A5A075D0"
+\tboundary="----=_NextPart_000_0004_01CB045C.A5A075D0"
------=_NextPart_000_0004_01CB045C.A5A075D0
Content-Type: text/plain;
- charset="us-ascii"
+\tcharset="us-ascii"
Content-Transfer-Encoding: 7bit
foo
@@ -241,7 +241,7 @@ baz
------=_NextPart_000_0004_01CB045C.A5A075D0
Content-Type: text/html;
- charset="us-ascii"
+\tcharset="us-ascii"
Content-Transfer-Encoding: quoted-printable
<html>this is fake</html>
@@ -382,8 +382,8 @@ This isthesig
EOF
- test_email( $text, $content,
- 'Another sample multipart message with Exchange headers' );
+ test_email( $text, $content,
+ 'Another sample multipart message with Exchange headers' );
}
sub test_email {
diff --git a/rt/t/mail/sendmail-plaintext.t b/rt/t/mail/sendmail-plaintext.t
new file mode 100644
index 000000000..b9eb71951
--- /dev/null
+++ b/rt/t/mail/sendmail-plaintext.t
@@ -0,0 +1,150 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef, text_templates => 1;
+
+use File::Spec ();
+use Email::Abstract;
+
+# We're not testing acls here.
+my $everyone = RT::Group->new(RT->SystemUser);
+$everyone->LoadSystemInternalGroup('Everyone');
+$everyone->PrincipalObj->GrantRight( Right =>'SuperUser' );
+
+# some utils
+sub first_txn { return $_[0]->Transactions->First }
+sub first_attach { return first_txn($_[0])->Attachments->First }
+sub count_attachs { return first_txn($_[0])->Attachments->Count }
+
+sub mail_in_ticket {
+ my ($filename) = @_;
+ my $path = RT::Test::get_relocatable_file($filename,
+ (File::Spec->updir(), 'data', 'emails'));
+ my $content = RT::Test->file_content($path);
+
+ RT::Test->clean_caught_mails;
+ my ($status, $id) = RT::Test->send_via_mailgate( $content );
+ ok( !$status, "Fed $filename into mailgate");
+
+ my $ticket = RT::Ticket->new(RT->SystemUser);
+ $ticket->Load($id);
+ ok( $ticket->Id, "Successfully created ticket ".$ticket->Id);
+
+ my @mail = map {Email::Abstract->new($_)->cast('MIME::Entity')}
+ RT::Test->fetch_caught_mails;
+ return ($ticket, @mail);
+}
+
+{
+ my ($ticket) = mail_in_ticket('multipart-report');
+ like( first_txn($ticket)->Content , qr/The original message was received/, "It's the bounce");
+}
+
+for my $encoding ('ISO-8859-1', 'UTF-8') {
+ RT->Config->Set( EmailOutputEncoding => $encoding );
+
+ my ($ticket, @mail) = mail_in_ticket('new-ticket-from-iso-8859-1');
+ like (first_txn($ticket)->Content , qr/H\x{e5}vard/, "It's signed by havard. yay");
+
+ is(@mail, 1);
+ like( $mail[0]->head->get('Content-Type') , qr/$encoding/,
+ "Its content type is $encoding" );
+ my $message_as_string = $mail[0]->bodyhandle->as_string();
+ $message_as_string = Encode::decode($encoding, $message_as_string);
+ like( $message_as_string , qr/H\x{e5}vard/,
+ "The message's content contains havard's name in $encoding");
+}
+
+{
+ my ($ticket) = mail_in_ticket('multipart-alternative-with-umlaut');
+ like( first_txn($ticket)->Content, qr/causes Error/,
+ "We recorded the content as containing 'causes error'");
+ is( count_attachs($ticket), 3,
+ "Has three attachments, presumably a text-plain, a text-html and a multipart alternative");
+}
+
+{
+ my ($ticket, @mail) = mail_in_ticket('text-html-with-umlaut');
+ like( first_attach($ticket)->Content, qr/causes Error/,
+ "We recorded the content as containing 'causes error'");
+ like( first_attach($ticket)->ContentType , qr/text\/html/,
+ "We recorded the content as text/html");
+ is (count_attachs($ticket), 1,
+ "Has one attachment, just a text-html");
+
+ is(@mail, 1);
+ is( $mail[0]->parts, 0, "generated correspondence mime entity does not have parts");
+ is( $mail[0]->head->mime_type , "text/plain", "The mime type is a plain");
+}
+
+{
+ my @InputEncodings = RT->Config->Get('EmailInputEncodings');
+ RT->Config->Set( EmailInputEncodings => 'koi8-r', @InputEncodings );
+ RT->Config->Set( EmailOutputEncoding => 'koi8-r' );
+
+ my ($ticket, @mail) = mail_in_ticket('russian-subject-no-content-type');
+ like( first_attach($ticket)->ContentType, qr/text\/plain/,
+ "We recorded the content type right");
+ is( count_attachs($ticket), 1,
+ "Has one attachment, presumably a text-plain");
+ is( $ticket->Subject, Encode::decode("UTF-8","тест тест"),
+ "Recorded the subject right");
+
+ is(@mail, 1);
+ is( $mail[0]->head->mime_type , "text/plain", "The only part is text/plain ");
+ like( $mail[0]->head->get("subject"), qr/\Q=?KOI8-R?B?W2V4YW1wbGUuY29tICM2XSBBdXRvUmVwbHk6INTF09Qg1MXT1A==?=\E/,
+ "The subject is encoded correctly");
+
+ RT->Config->Set(EmailInputEncodings => @InputEncodings );
+ RT->Config->Set(EmailOutputEncoding => 'utf-8');
+}
+
+{
+ my ($ticket, @mail) = mail_in_ticket('nested-rfc-822');
+ is( $ticket->Subject, "[Jonas Liljegren] Re: [Para] Niv\x{e5}er?");
+ like( first_attach($ticket)->ContentType, qr/multipart\/mixed/,
+ "We recorded the content type right");
+ is( count_attachs($ticket), 5,
+ "Has five attachments, presumably a text-plain and a message RFC 822 and another plain");
+
+ is(@mail, 1);
+ is( $mail[0]->head->mime_type , "text/plain", "The outgoing mail is plain text");
+
+ my $encoded_subject = $mail[0]->head->get("Subject");
+ chomp $encoded_subject;
+ my $subject = Encode::decode('MIME-Header',$encoded_subject);
+ like($subject, qr/Niv\x{e5}er/, "The subject matches the word - $subject");
+}
+
+{
+ my ($ticket) = mail_in_ticket('notes-uuencoded');
+ like( first_txn($ticket)->Content, qr/from Lotus Notes/,
+ "We recorded the content right");
+ is( count_attachs($ticket), 3, "Has three attachments");
+}
+
+{
+ my ($ticket) = mail_in_ticket('crashes-file-based-parser');
+ like( first_txn($ticket)->Content, qr/FYI/, "We recorded the content right");
+ is( count_attachs($ticket), 5, "Has five attachments");
+}
+
+{
+ my ($ticket) = mail_in_ticket('rt-send-cc');
+ my $cc = first_attach($ticket)->GetHeader('RT-Send-Cc');
+ like ($cc, qr/test$_/, "Found test $_") for 1..5;
+}
+
+{
+ diag "Regression test for #5248 from rt3.fsck.com";
+ my ($ticket) = mail_in_ticket('subject-with-folding-ws');
+ is ($ticket->Subject, 'test', 'correct subject');
+}
+
+{
+ diag "Regression test for #5248 from rt3.fsck.com";
+ my ($ticket) = mail_in_ticket('very-long-subject');
+ is ($ticket->Subject, '0123456789'x20, 'correct subject');
+}
+
+done_testing;
diff --git a/rt/t/mail/sendmail.t b/rt/t/mail/sendmail.t
index 56202ad5d..4ef320611 100644
--- a/rt/t/mail/sendmail.t
+++ b/rt/t/mail/sendmail.t
@@ -24,7 +24,7 @@ sub mail_in_ticket {
RT::Test->clean_caught_mails;
my ($status, $id) = RT::Test->send_via_mailgate( $content );
- ok( $status, "Fed $filename into mailgate");
+ ok( !$status, "Fed $filename into mailgate");
my $ticket = RT::Ticket->new(RT->SystemUser);
$ticket->Load($id);
@@ -47,12 +47,30 @@ for my $encoding ('ISO-8859-1', 'UTF-8') {
like (first_txn($ticket)->Content , qr/H\x{e5}vard/, "It's signed by havard. yay");
is(@mail, 1);
- like( $mail[0]->head->get('Content-Type') , qr/$encoding/,
- "Its content type is $encoding" );
- my $message_as_string = $mail[0]->bodyhandle->as_string();
+ like( $mail[0]->head->get('Content-Type'), qr/multipart\/alternative/,
+ "Its content type is multipart/alternative" );
+
+ # The text/html part is guaranteed to not have had non-latin-1
+ # characters introduced by the HTML-to-text conversion, so it is
+ # guaranteed to be able to be represented in latin-1
+ like( $mail[0]->parts(1)->head->get('Content-Type'), qr/text\/html.+?$encoding/,
+ "Second part's content type is text/html $encoding" );
+ my $message_as_string = $mail[0]->parts(1)->bodyhandle->as_string();
$message_as_string = Encode::decode($encoding, $message_as_string);
like( $message_as_string , qr/H\x{e5}vard/,
"The message's content contains havard's name in $encoding");
+
+ # The text/plain part may have utf-8 characters in it. Accept either encoding.
+ like( $mail[0]->parts(0)->head->get('Content-Type'), qr/text\/plain.+?(ISO-8859-1|UTF-8)/i,
+ "First part's content type is text/plain (ISO-8859-1 or UTF-8)" );
+
+ # Make sure it checks out in whatever encoding it ended up in
+ $mail[0]->parts(0)->head->get('Content-Type') =~ /text\/plain.+?(ISO-8859-1|UTF-8)/i;
+ my $found = $1 || $encoding;
+ $message_as_string = $mail[0]->parts(0)->bodyhandle->as_string();
+ $message_as_string = Encode::decode($found, $message_as_string);
+ like( $message_as_string , qr/H\x{e5}vard/,
+ "The message's content contains havard's name in $encoding");
}
{
@@ -73,8 +91,9 @@ for my $encoding ('ISO-8859-1', 'UTF-8') {
"Has one attachment, just a text-html");
is(@mail, 1);
- is( $mail[0]->parts, 0, "generated correspondence mime entity does not have parts");
- is( $mail[0]->head->mime_type , "text/plain", "The mime type is a plain");
+ is( $mail[0]->parts, 2, "generated correspondence mime entity has parts");
+ is( $mail[0]->parts(0)->head->mime_type , "text/plain", "The first part mime type is a plain");
+ is( $mail[0]->parts(1)->head->mime_type , "text/html", "The second part mime type is an html");
}
{
@@ -91,7 +110,10 @@ for my $encoding ('ISO-8859-1', 'UTF-8') {
"Recorded the subject right");
is(@mail, 1);
- is( $mail[0]->head->mime_type , "text/plain", "The only part is text/plain ");
+ is( $mail[0]->head->mime_type , "multipart/alternative", "The top part is multipart/alternative");
+ is( $mail[0]->parts, 2, "generated correspondnece mime entity has parts");
+ is( $mail[0]->parts(0)->head->mime_type , "text/plain", "The first part is a plain");
+ is( $mail[0]->parts(1)->head->mime_type , "text/html", "The second part is an html");
like( $mail[0]->head->get("subject"), qr/\Q=?KOI8-R?B?W2V4YW1wbGUuY29tICM2XSBBdXRvUmVwbHk6INTF09Qg1MXT1A==?=\E/,
"The subject is encoded correctly");
@@ -108,7 +130,10 @@ for my $encoding ('ISO-8859-1', 'UTF-8') {
"Has five attachments, presumably a text-plain and a message RFC 822 and another plain");
is(@mail, 1);
- is( $mail[0]->head->mime_type , "text/plain", "The outgoing mail is plain text");
+ is( $mail[0]->head->mime_type , "multipart/alternative", "The top part is multipart/alternative");
+ is( $mail[0]->parts, 2, "generated correspondnece mime entity has parts");
+ is( $mail[0]->parts(0)->head->mime_type , "text/plain", "The first part is a plain");
+ is( $mail[0]->parts(1)->head->mime_type , "text/html", "The second part is an html");
my $encoded_subject = $mail[0]->head->get("Subject");
chomp $encoded_subject;
diff --git a/rt/t/mail/smime/incoming.t b/rt/t/mail/smime/incoming.t
new file mode 100644
index 000000000..918844a88
--- /dev/null
+++ b/rt/t/mail/smime/incoming.t
@@ -0,0 +1,202 @@
+use strict;
+use warnings;
+
+use RT::Test::SMIME tests => undef, actual_server => 1;
+my $test = 'RT::Test::SMIME';
+
+use IPC::Run3 'run3';
+use String::ShellQuote 'shell_quote';
+use RT::Tickets;
+use Test::Warn;
+
+my ($url, $m) = RT::Test->started_ok;
+ok $m->login, "logged in";
+
+# configure key for General queue
+RT::Test::SMIME->import_key('sender@example.com');
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'General',
+ CorrespondAddress => 'sender@example.com',
+ CommentAddress => 'sender@example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+my $user = RT::Test->load_or_create_user(
+ Name => 'root@example.com',
+ EmailAddress => 'root@example.com',
+);
+RT::Test::SMIME->import_key('root@example.com.crt', $user);
+RT::Test->add_rights( Principal => $user, Right => 'SuperUser', Object => RT->System );
+
+my $mail = RT::Test->open_mailgate_ok($url);
+print $mail <<EOF;
+From: root\@localhost
+To: rt\@$RT::rtname
+Subject: This is a test of new ticket creation as root
+
+Blah!
+Foob!
+EOF
+RT::Test->close_mailgate_ok($mail);
+
+{
+ my $tick = RT::Test->last_ticket;
+ is( $tick->Subject,
+ 'This is a test of new ticket creation as root',
+ "Created the ticket"
+ );
+ my $txn = $tick->Transactions->First;
+ like(
+ $txn->Attachments->First->Headers,
+ qr/^X-RT-Incoming-Encryption: Not encrypted/m,
+ 'recorded incoming mail that is not encrypted'
+ );
+ like( $txn->Attachments->First->Content, qr'Blah');
+}
+
+{
+ # test for encrypted mail
+ my $buf = '';
+ run3(
+ shell_quote(
+ qw(openssl smime -encrypt -des3),
+ -from => 'root@example.com',
+ -to => 'sender@example.com',
+ -subject => "Encrypted message for queue",
+ $test->key_path('sender@example.com.crt'),
+ ),
+ \"Subject: test\n\norzzzzzz",
+ \$buf,
+ \*STDERR
+ );
+
+ my ($status, $tid) = RT::Test->send_via_mailgate( $buf );
+ is ($status >> 8, 0, "The mail gateway exited normally");
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $tid );
+ is( $tick->Subject, 'Encrypted message for queue',
+ "Created the ticket"
+ );
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef};
+ is( $msg->GetHeader('X-RT-Incoming-Encryption'),
+ 'Success',
+ 'recorded incoming mail that is encrypted'
+ );
+ is( $msg->GetHeader('X-RT-Privacy'),
+ 'SMIME',
+ 'recorded incoming mail that is encrypted'
+ );
+ like( $attach->Content, qr'orz');
+
+ is( $orig->GetHeader('Content-Type'), 'application/x-rt-original-message');
+}
+
+{
+ my $buf = '';
+
+ run3(
+ join(
+ ' ',
+ shell_quote(
+ RT->Config->Get('SMIME')->{'OpenSSL'},
+ qw( smime -sign -nodetach -passin pass:123456),
+ -signer => $test->key_path('root@example.com.crt'),
+ -inkey => $test->key_path('root@example.com.key'),
+ ),
+ '|',
+ shell_quote(
+ qw(openssl smime -encrypt -des3),
+ -from => 'root@example.com',
+ -to => 'sender@example.com',
+ -subject => "Encrypted and signed message for queue",
+ $test->key_path('sender@example.com.crt'),
+ )),
+ \"Subject: test\n\norzzzzzz",
+ \$buf,
+ \*STDERR
+ );
+
+ my ($status, $tid) = RT::Test->send_via_mailgate( $buf );
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $tid );
+ ok( $tick->Id, "found ticket " . $tick->Id );
+ is( $tick->Subject, 'Encrypted and signed message for queue',
+ "Created the ticket"
+ );
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef};
+ is( $msg->GetHeader('X-RT-Incoming-Encryption'),
+ 'Success',
+ 'recorded incoming mail that is encrypted'
+ );
+ like( $attach->Content, qr'orzzzz');
+}
+
+{
+ my $buf = '';
+
+ run3(
+ shell_quote(
+ RT->Config->Get('SMIME')->{'OpenSSL'},
+ qw( smime -sign -passin pass:123456),
+ -signer => $test->key_path('root@example.com.crt'),
+ -inkey => $test->key_path('root@example.com.key'),
+ ),
+ \"Content-type: text/plain\n\nThis is the body",
+ \$buf,
+ \*STDERR
+ );
+ $buf = "Subject: Signed email\n"
+ . "From: root\@example.com\n"
+ . $buf;
+
+ {
+ my ($status, $tid) = RT::Test->send_via_mailgate( $buf );
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $tid );
+ ok( $tick->Id, "found ticket " . $tick->Id );
+ is( $tick->Subject, 'Signed email',
+ "Created the ticket"
+ );
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef};
+ is( $msg->GetHeader('X-RT-Incoming-Signature'),
+ '"Enoch Root" <root@example.com>',
+ "Message was signed"
+ );
+ like( $attach->Content, qr/This is the body/ );
+ }
+
+ # Make the signature not match
+ $buf =~ s/This is the body/This is not the body/;
+
+ warning_like {
+ my ($status, $tid) = RT::Test->send_via_mailgate( $buf );
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $tid );
+ ok( $tick->Id, "found ticket " . $tick->Id );
+ is( $tick->Subject, 'Signed email',
+ "Created the ticket"
+ );
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef};
+ isnt( $msg->GetHeader('X-RT-Incoming-Signature'),
+ '"Enoch Root" <root@example.com>',
+ "Message was not marked signed"
+ );
+ like( $attach->Content, qr/This is not the body/ );
+ } qr/Failure during SMIME verify: The signature did not verify/;
+
+}
+
+undef $m;
+done_testing;
diff --git a/rt/t/mail/smime/other-signed.t b/rt/t/mail/smime/other-signed.t
new file mode 100644
index 000000000..4e97e711f
--- /dev/null
+++ b/rt/t/mail/smime/other-signed.t
@@ -0,0 +1,135 @@
+use strict;
+use warnings;
+
+use RT::Test::SMIME tests => undef;
+my $test = 'RT::Test::SMIME';
+
+use IPC::Run3 'run3';
+use String::ShellQuote 'shell_quote';
+use RT::Tickets;
+use Test::Warn;
+
+# configure key for General queue
+RT::Test::SMIME->import_key('sender@example.com');
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'General',
+ CorrespondAddress => 'sender@example.com',
+ CommentAddress => 'sender@example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+my $user = RT::Test->load_or_create_user(
+ Name => 'root@example.com',
+ EmailAddress => 'root@example.com',
+);
+RT::Test::SMIME->import_key('root@example.com.crt', $user);
+RT::Test->add_rights( Principal => $user, Right => 'SuperUser', Object => RT->System );
+
+my $buf = '';
+
+run3(
+ shell_quote(
+ RT->Config->Get('SMIME')->{'OpenSSL'},
+ qw( smime -sign -passin pass:123456),
+ -signer => $test->key_path('root@example.com.crt'),
+ -inkey => $test->key_path('root@example.com.key'),
+ ),
+ \"Content-type: text/plain\n\nThis is the body",
+ \$buf,
+ \*STDERR
+);
+$buf = "Subject: Signed email\n"
+ . "From: root\@example.com\n"
+ . $buf;
+
+my $send_mail = sub {
+ my %args = ( CAPath => undef, AcceptUntrustedCAs => undef, @_ );
+
+ RT->Config->Get('SMIME')->{$_} = $args{$_} for keys %args;
+
+ my ($status, $tid) = RT::Test->send_via_mailgate( $buf );
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $tid );
+ ok( $tick->Id, "found ticket " . $tick->Id );
+ is( $tick->Subject, 'Signed email',
+ "Created the ticket"
+ );
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef};
+
+ ($status) = RT::Crypt->ParseStatus(
+ Protocol => 'SMIME',
+ Status => $msg->GetHeader('X-RT-SMIME-Status')
+ );
+
+ return ($msg, $status);
+};
+
+# Test with no CA path; should not be marked as signed
+warning_like {
+ my ($msg, $status) = $send_mail->( CAPath => undef );
+ is( $msg->GetHeader('X-RT-Incoming-Signature'),
+ undef,
+ "Message was not marked as signed"
+ );
+
+ is($status->{Operation}, "Verify", "Found the Verify operation");
+ is($status->{Status}, "BAD", "Verify was a failure");
+ is($status->{Trust}, "NONE", "Noted the no trust level");
+ like($status->{Message}, qr/not trusted/, "Verify was a failure");
+} qr/Failure during SMIME verify: The signing CA was not trusted/;
+
+# Test with the correct CA path; marked as signed, trusted
+{
+ my ($msg, $status) = $send_mail->( CAPath => $test->key_path . "/demoCA/cacert.pem" );
+ is( $msg->GetHeader('X-RT-Incoming-Signature'),
+ '"Enoch Root" <root@example.com>', "Message is signed" );
+
+ is($status->{Operation}, "Verify", "Found the Verify operation");
+ is($status->{Status}, "DONE", "Verify was a success");
+ is($status->{Trust}, "FULL", "Noted the full trust level");
+}
+
+# Test with the other CA
+warning_like {
+ my ($msg, $status) = $send_mail->( CAPath => $test->key_path . "/otherCA/cacert.pem" );
+ is( $msg->GetHeader('X-RT-Incoming-Signature'),
+ undef,
+ "Message was not marked as signed"
+ );
+
+ is($status->{Operation}, "Verify", "Found the Verify operation");
+ is($status->{Status}, "BAD", "Verify was a failure");
+ is($status->{Trust}, "NONE", "Noted the no trust level");
+ like($status->{Message}, qr/not trusted/, "Verify was a failure");
+} qr/Failure during SMIME verify: The signing CA was not trusted/;
+
+# Other CA, but allow all CAs
+{
+ my ($msg, $status) = $send_mail->( CAPath => $test->key_path . "/otherCA/cacert.pem", AcceptUntrustedCAs => 1 );
+ is( $msg->GetHeader('X-RT-Incoming-Signature'),
+ '"Enoch Root" <root@example.com>',
+ "Message was marked as signed"
+ );
+
+ is($status->{Operation}, "Verify", "Found the Verify operation");
+ is($status->{Status}, "DONE", "Verify was a success");
+ is($status->{Trust}, "NONE", "Noted the no trust level");
+}
+
+# No CA path, but allow all CAs
+{
+ my ($msg, $status) = $send_mail->( CAPath => undef, AcceptUntrustedCAs => 1 );
+ is( $msg->GetHeader('X-RT-Incoming-Signature'),
+ '"Enoch Root" <root@example.com>',
+ "Message was marked as signed"
+ );
+
+ is($status->{Operation}, "Verify", "Found the Verify operation");
+ is($status->{Status}, "DONE", "Verify was a success");
+ is($status->{Trust}, "UNKNOWN", "Noted the no trust level");
+}
+
+done_testing;
diff --git a/rt/t/mail/smime/outgoing.t b/rt/t/mail/smime/outgoing.t
new file mode 100644
index 000000000..6f6b00d68
--- /dev/null
+++ b/rt/t/mail/smime/outgoing.t
@@ -0,0 +1,80 @@
+use strict;
+use warnings;
+
+use RT::Test::SMIME tests => undef;
+my $test = 'RT::Test::SMIME';
+
+use IPC::Run3 'run3';
+use RT::Interface::Email;
+
+my ($url, $m) = RT::Test->started_ok;
+ok $m->login, "logged in";
+
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'General',
+ CorrespondAddress => 'sender@example.com',
+ CommentAddress => 'sender@example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+{
+ my ($status, $msg) = $queue->SetEncrypt(1);
+ ok $status, "turn on encyption by default"
+ or diag "error: $msg";
+}
+
+my $user;
+{
+ $user = RT::User->new($RT::SystemUser);
+ ok($user->LoadByEmail('root@localhost'), "Loaded user 'root'");
+ ok($user->Load('root'), "Loaded user 'root'");
+ is($user->EmailAddress, 'root@localhost');
+
+ RT::Test::SMIME->import_key( 'root@example.com.crt' => $user );
+}
+
+RT::Test->clean_caught_mails;
+
+{
+ my $mail = <<END;
+From: root\@localhost
+To: rt\@example.com
+Subject: This is a test of new ticket creation as an unknown user
+
+Blah!
+Foob!
+
+END
+
+ my ($status, $id) = RT::Test->send_via_mailgate(
+ $mail, queue => $queue->Name,
+ );
+ is $status >> 8, 0, "successfuly executed mailgate";
+
+ my $ticket = RT::Ticket->new($RT::SystemUser);
+ $ticket->Load( $id );
+ ok ($ticket->id, "found ticket ". $ticket->id);
+}
+
+{
+ my @mails = RT::Test->fetch_caught_mails;
+ is scalar @mails, 1, "autoreply";
+
+ my ($buf, $err);
+ local $@;
+ ok(eval {
+ run3([
+ qw(openssl smime -decrypt -passin pass:123456),
+ '-inkey', $test->key_path('root@example.com.key'),
+ '-recip', $test->key_path('root@example.com.crt')
+ ], \$mails[0], \$buf, \$err )
+ }, 'can decrypt'
+ );
+ diag $@ if $@;
+ diag $err if $err;
+ diag "Error code: $?" if $?;
+ like($buf, qr'This message has been automatically generated in response');
+}
+
+undef $m;
+done_testing;
diff --git a/rt/t/mail/smime/realmail.t b/rt/t/mail/smime/realmail.t
new file mode 100644
index 000000000..be157aaee
--- /dev/null
+++ b/rt/t/mail/smime/realmail.t
@@ -0,0 +1,125 @@
+use strict;
+use warnings;
+
+use RT::Test::SMIME tests => undef;
+use Digest::MD5 qw(md5_hex);
+
+my $test = 'RT::Test::SMIME';
+my $mails = $test->mail_set_path;
+
+RT->Config->Get('SMIME')->{AcceptUntrustedCAs} = 1;
+
+RT::Test::SMIME->import_key('root@example.com');
+RT::Test::SMIME->import_key('sender@example.com');
+
+my ($baseurl, $m) = RT::Test->started_ok;
+ok $m->login, 'we did log in';
+$m->get_ok( '/Admin/Queues/');
+$m->follow_link_ok( {text => 'General'} );
+$m->submit_form( form_number => 3,
+ fields => { CorrespondAddress => 'root@example.com' } );
+
+diag "load Everyone group" if $ENV{'TEST_VERBOSE'};
+my $everyone;
+{
+ $everyone = RT::Group->new( $RT::SystemUser );
+ $everyone->LoadSystemInternalGroup('Everyone');
+ ok $everyone->id, "loaded 'everyone' group";
+}
+
+RT::Test->set_rights(
+ Principal => $everyone,
+ Right => ['CreateTicket'],
+);
+
+
+my $eid = 0;
+for my $usage (qw/signed encrypted signed&encrypted/) {
+ for my $attachment (qw/plain text-attachment binary-attachment/) {
+ ++$eid;
+ diag "Email $eid: $usage, $attachment email" if $ENV{TEST_VERBOSE};
+ eval { email_ok($eid, $usage, $attachment) };
+ }
+}
+
+undef $m;
+done_testing;
+
+sub email_ok {
+ my ($eid, $usage, $attachment) = @_;
+ diag "email_ok $eid: $usage, $attachment" if $ENV{'TEST_VERBOSE'};
+
+ my ($file) = glob("$mails/$eid-*");
+ my $mail = RT::Test->file_content($file);
+
+ my ($status, $id) = RT::Test->send_via_mailgate($mail);
+ is ($status >> 8, 0, "$eid: The mail gateway exited normally");
+ ok ($id, "$eid: got id of a newly created ticket - $id");
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $id );
+ ok ($tick->id, "$eid: loaded ticket #$id");
+
+ is ($tick->Subject,
+ "Test Email ID:$eid",
+ "$eid: Created the ticket"
+ );
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
+
+ is( $msg->GetHeader('X-RT-Privacy'),
+ 'SMIME',
+ "$eid: recorded incoming mail that is secured"
+ );
+
+ if ($usage =~ /encrypted/) {
+ is( $msg->GetHeader('X-RT-Incoming-Encryption'),
+ 'Success',
+ "$eid: recorded incoming mail that is encrypted"
+ );
+ like( $attachments[0]->Content, qr/ID:$eid/,
+ "$eid: incoming mail did NOT have original body"
+ );
+ }
+ else {
+ is( $msg->GetHeader('X-RT-Incoming-Encryption'),
+ 'Not encrypted',
+ "$eid: recorded incoming mail that is not encrypted"
+ );
+ like( $msg->Content || $attachments[0]->Content, qr/ID:$eid/,
+ "$eid: got original content"
+ );
+ }
+
+ if ($usage =~ /signed/) {
+ is( $msg->GetHeader('X-RT-Incoming-Signature'),
+ '"sender" <sender@example.com>',
+ "$eid: recorded incoming mail that is signed"
+ );
+ }
+ else {
+ is( $msg->GetHeader('X-RT-Incoming-Signature'),
+ undef,
+ "$eid: recorded incoming mail that is not signed"
+ );
+ }
+
+ if ($attachment =~ /attachment/) {
+ my ($a) = grep $_->Filename, @attachments;
+ ok ($a && $a->Id, "$eid: found attachment with filename");
+
+ my $acontent = $a->Content;
+ if ($attachment =~ /binary/)
+ {
+ is(md5_hex($acontent), '1e35f1aa90c98ca2bab85c26ae3e1ba7', "$eid: The binary attachment's md5sum matches");
+ }
+ else
+ {
+ like($acontent, qr/zanzibar/, "$eid: The attachment isn't screwed up in the database.");
+ }
+ }
+
+ return 0;
+}
+
diff --git a/rt/t/mail/smime/reject_on_unencrypted.t b/rt/t/mail/smime/reject_on_unencrypted.t
new file mode 100644
index 000000000..ab62d83fc
--- /dev/null
+++ b/rt/t/mail/smime/reject_on_unencrypted.t
@@ -0,0 +1,137 @@
+use strict;
+use warnings;
+
+use RT::Test::SMIME tests => undef, actual_server => 1, config => 'Set( %Crypt, RejectOnUnencrypted => 1 );';
+my $test = 'RT::Test::SMIME';
+
+use IPC::Run3 'run3';
+use String::ShellQuote 'shell_quote';
+use RT::Tickets;
+
+my ($url, $m) = RT::Test->started_ok;
+ok $m->login, "logged in";
+
+# configure key for General queue
+RT::Test::SMIME->import_key('sender@example.com');
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'General',
+ CorrespondAddress => 'sender@example.com',
+ CommentAddress => 'sender@example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+my $user = RT::Test->load_or_create_user(
+ Name => 'root@example.com',
+ EmailAddress => 'root@example.com',
+);
+RT::Test::SMIME->import_key('root@example.com.crt', $user);
+RT::Test->add_rights( Principal => $user, Right => 'SuperUser', Object => RT->System );
+
+my $mail = RT::Test->open_mailgate_ok($url);
+print $mail <<EOF;
+From: root\@localhost
+To: rt\@$RT::rtname
+Subject: This is a test of new ticket creation as root
+
+Blah!
+Foob!
+EOF
+RT::Test->close_mailgate_ok($mail);
+
+{
+ ok(!RT::Test->last_ticket, 'A ticket was not created');
+ my ($mail) = RT::Test->fetch_caught_mails;
+ like(
+ $mail,
+ qr/^Subject: RT requires that all incoming mail be encrypted/m,
+ 'rejected mail that is not encrypted'
+ );
+ my ($warning) = $m->get_warnings;
+ like($warning, qr/rejected because the message is unencrypted/);
+}
+
+{
+ # test for encrypted mail
+ my $buf = '';
+ run3(
+ shell_quote(
+ qw(openssl smime -encrypt -des3),
+ -from => 'root@example.com',
+ -to => 'sender@example.com',
+ -subject => "Encrypted message for queue",
+ $test->key_path('sender@example.com.crt' ),
+ ),
+ \"Subject: test\n\norzzzzzz",
+ \$buf,
+ \*STDERR
+ );
+
+ my ($status, $tid) = RT::Test->send_via_mailgate( $buf );
+ is ($status >> 8, 0, "The mail gateway exited normally");
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $tid );
+ is( $tick->Subject, 'Encrypted message for queue',
+ "Created the ticket"
+ );
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef};
+ is( $msg->GetHeader('X-RT-Incoming-Encryption'),
+ 'Success',
+ 'recorded incoming mail that is encrypted'
+ );
+ is( $msg->GetHeader('X-RT-Privacy'),
+ 'SMIME',
+ 'recorded incoming mail that is encrypted'
+ );
+ like( $attach->Content, qr'orz');
+
+ is( $orig->GetHeader('Content-Type'), 'application/x-rt-original-message');
+}
+
+{
+ my $buf = '';
+
+ run3(
+ join(
+ ' ',
+ shell_quote(
+ RT->Config->Get('SMIME')->{'OpenSSL'},
+ qw( smime -sign -nodetach -passin pass:123456),
+ -signer => $test->key_path('root@example.com.crt' ),
+ -inkey => $test->key_path('root@example.com.key' ),
+ ),
+ '|',
+ shell_quote(
+ qw(openssl smime -encrypt -des3),
+ -from => 'root@example.com',
+ -to => 'sender@example.com',
+ -subject => "Encrypted and signed message for queue",
+ $test->key_path('sender@example.com.crt' ),
+ )),
+ \"Subject: test\n\norzzzzzz",
+ \$buf,
+ \*STDERR
+ );
+
+ my ($status, $tid) = RT::Test->send_via_mailgate( $buf );
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $tid );
+ ok( $tick->Id, "found ticket " . $tick->Id );
+ is( $tick->Subject, 'Encrypted and signed message for queue',
+ "Created the ticket"
+ );
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef};
+ is( $msg->GetHeader('X-RT-Incoming-Encryption'),
+ 'Success',
+ 'recorded incoming mail that is encrypted'
+ );
+ like( $attach->Content, qr'orzzzz');
+}
+
+undef $m;
+done_testing;
diff --git a/rt/t/mail/specials-in-encodedwords.t b/rt/t/mail/specials-in-encodedwords.t
index f9da9c6e9..36efcd5e7 100644
--- a/rt/t/mail/specials-in-encodedwords.t
+++ b/rt/t/mail/specials-in-encodedwords.t
@@ -14,7 +14,7 @@ diag "specials (, and ;) in MIME encoded-words aren't treated as specials";
From: root@localhost
Subject: testing mime encoded specials
Cc: a@example.com, =?utf8?q?d=40example.com=2ce=40example.com=3b?=
- <b@example.com>; c@example.com
+ <b@example.com>, c@example.com
Content-Type: text/plain; charset=utf8
here's some content
diff --git a/rt/t/mail/wrong_mime_charset.t b/rt/t/mail/wrong_mime_charset.t
index 6bbaca1bb..a3986d72f 100644
--- a/rt/t/mail/wrong_mime_charset.t
+++ b/rt/t/mail/wrong_mime_charset.t
@@ -1,6 +1,6 @@
use strict;
use warnings;
-use RT::Test nodb => 1, tests => 6;
+use RT::Test nodb => 1, tests => undef;
use_ok('RT::I18N');
my $test_string = Encode::decode("UTF-8", 'À');
@@ -20,10 +20,6 @@ local $SIG{__WARN__} = sub {
RT::I18N::SetMIMEEntityToEncoding( $mime, 'iso-8859-1' );
-TODO: {
- local $TODO =
-'need a better approach of encoding converter, should be fixed in 4.2';
-
# this is a weird behavior for different perl versions, 5.12 warns twice,
# which is correct since we do the encoding thing twice, for Subject
# and Data respectively.
@@ -44,4 +40,5 @@ is( $subject, $test_string, 'subject is set to iso-8859-1' );
my $body = Encode::decode( 'iso-8859-1', $mime->stringify_body );
chomp $body;
is( $body, $test_string, 'body is set to iso-8859-1' );
-}
+
+done_testing;
diff --git a/rt/t/pod.t b/rt/t/pod.t
index 697a30b44..1283cfe6c 100644
--- a/rt/t/pod.t
+++ b/rt/t/pod.t
@@ -2,6 +2,9 @@ use strict;
use warnings;
use Test::More;
-eval "use Test::Pod 1.14";
-plan skip_all => "Test::Pod 1.14 required for testing POD" if $@;
-all_pod_files_ok( all_pod_files("lib","docs","etc","bin","sbin"));
+use Test::Pod;
+all_pod_files_ok(
+ all_pod_files("lib","devel","docs","etc","bin","sbin"),
+ <docs/UPGRADING*>,
+ <devel/docs/UPGRADING*>,
+);
diff --git a/rt/t/security/CVE-2011-2083-clickable-xss.t b/rt/t/security/CVE-2011-2083-clickable-xss.t
index 008c80378..753d8c770 100644
--- a/rt/t/security/CVE-2011-2083-clickable-xss.t
+++ b/rt/t/security/CVE-2011-2083-clickable-xss.t
@@ -25,7 +25,7 @@ for my $link ( map { ($_, ucfirst $_) } @links ) {
Type => 'RefersTo',
Target => $link,
);
- } [qr/Could not determine a URI scheme/, qr/Couldn't resolve/];
+ } [qr/Could not determine a URI scheme/];
ok !$ok, $msg;
ok $m->login, "logged in";
@@ -40,7 +40,6 @@ for my $link ( map { ($_, ucfirst $_) } @links ) {
}, 'submitted links page');
$m->content_contains("Couldn&#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";
diff --git a/rt/t/security/CVE-2011-2084-cf-values.t b/rt/t/security/CVE-2011-2084-cf-values.t
index 1178b15af..21c8547f6 100644
--- a/rt/t/security/CVE-2011-2084-cf-values.t
+++ b/rt/t/security/CVE-2011-2084-cf-values.t
@@ -41,7 +41,7 @@ sub ac {
$args{ContextType} = ref($obj) unless defined $args{ContextType};
}
- $args{"Object---CustomField-$args{CF}-Values"} = "";
+ $args{"Object-RT::Ticket--CustomField-$args{CF}-Values"} = "";
delete $args{CF};
delete $args{$_} for grep {not defined $args{$_}} keys %args;
diff --git a/rt/t/security/CVE-2011-2084-modifyscrips-templates.t b/rt/t/security/CVE-2011-2084-modifyscrips-templates.t
index f68706e52..0e59c528e 100644
--- a/rt/t/security/CVE-2011-2084-modifyscrips-templates.t
+++ b/rt/t/security/CVE-2011-2084-modifyscrips-templates.t
@@ -59,32 +59,39 @@ diag "ModifyScrips";
$scrip = RT::Scrip->new( $cu );
$scrip->Load( $scrip_id );
ok $scrip->id, "loaded scrip as test user";
- is $scrip->Queue, $qa->Id, 'queue is A';
+ ok $scrip->IsAdded( $qa->Id ), 'queue is A';
ok +($scrip->SetName('Testing ModifyScrips'));
- set_fails( Queue => $scrip => $qb );
- set_fails( Queue => $scrip => 0 );
- set_fails( Queue => $scrip => undef );
- set_fails( Queue => $scrip => '' );
+ for my $value ($qb->id, 0, undef, '') {
+ my ($ok, $why) = $scrip->AddToObject( $value );
+ my $disp = (defined($value) ? "'$value'" : "undef");
+ ok( !$ok, "Correctly not added to $disp: $why" );
+ }
RT::Test->add_rights( Principal => $user, Right => 'ModifyScrips', Object => $qb );
- set_ok( Queue => $scrip => $qb );
- set_fails( Queue => $scrip => 0 );
- set_fails( Queue => $scrip => undef );
- set_fails( Queue => $scrip => '' );
+ for my $value ($qb->id, 0, undef, '') {
+ my ($ok, $why) = $scrip->AddToObject( $value );
+ my $disp = (defined($value) ? "'$value'" : "undef");
+ if ($value) {
+ ok( $ok, "Correctly added to $disp: $why" );
+ } else {
+ ok( !$ok, "Correctly not added to $disp: $why" );
+ }
+ }
RT::Test->add_rights( Principal => $user, Right => 'ModifyScrips' );
- set_ok( Queue => $scrip => 0 );
+ my ($ok, $why) = $scrip->AddToObject( 0 );
+ ok( $ok, "Correctly added globally: $why" );
- set_fails( Template => $scrip => 2 );
+ set_fails( Template => $scrip => "Autoreply" );
RT::Test->add_rights( Principal => $user, Right => 'ShowTemplate' );
- set_ok( Template => $scrip => 2 );
- is $scrip->TemplateObj->Name, 'Autoreply', 'template name is right';
+ set_ok( Template => $scrip => "Autoreply" );
+ is $scrip->Template, 'Autoreply', 'template name is right';
}
diag "ModifyTemplate";
@@ -115,12 +122,12 @@ diag "ModifyTemplate";
RT::Test->add_rights( Principal => $user, Right => 'ModifyTemplate', Object => $qb );
- set_ok( Queue => $template => $qb );
+ set_fails( Queue => $template => $qb );
set_fails( Queue => $template => 0 );
RT::Test->add_rights( Principal => $user, Right => 'ModifyTemplate' );
- set_ok( Queue => $template => 0 );
+ set_fails( Queue => $template => 0 );
}
done_testing;
diff --git a/rt/t/security/CVE-2011-5092-graph-links.t b/rt/t/security/CVE-2011-5092-graph-links.t
index 5e98dd3b5..c6397f5a2 100644
--- a/rt/t/security/CVE-2011-5092-graph-links.t
+++ b/rt/t/security/CVE-2011-5092-graph-links.t
@@ -13,12 +13,12 @@ for my $arg (qw(LeadingLink ShowLinks)) {
);
ok $ticket->id, 'created ticket';
- ok !$ticket->ToldObj->Unix, 'no Told';
+ ok !$ticket->ToldObj->IsSet, 'no Told';
$m->get_ok("$base/Ticket/Graphs/index.html?$arg=SetTold;id=" . $ticket->id);
$ticket->Load($ticket->id); # cache busting
- ok !$ticket->ToldObj->Unix, 'still no Told';
+ ok !$ticket->ToldObj->IsSet, 'still no Told';
$m->content_lacks('GotoFirstItem', 'no GotoFirstItem error');
$m->content_like(qr|<img[^>]+?src=['"]/Ticket/Graphs/@{[$ticket->id]}|, 'found image element');
}
diff --git a/rt/t/shredder/00load.t b/rt/t/shredder/00load.t
index a78e5e4c3..2d78da45c 100644
--- a/rt/t/shredder/00load.t
+++ b/rt/t/shredder/00load.t
@@ -1,14 +1,6 @@
use strict;
use warnings;
-use File::Spec;
-use Test::More tests => 11 + 1; # plus one for warnings check
-use RT::Test nodb => 1;
-
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
+use RT::Test nodb => 1, tests => 11;
use_ok("RT::Shredder");
diff --git a/rt/t/shredder/00skeleton.t b/rt/t/shredder/00skeleton.t
index 9c6e3a1c0..86f6fa9c7 100644
--- a/rt/t/shredder/00skeleton.t
+++ b/rt/t/shredder/00skeleton.t
@@ -3,22 +3,14 @@ use strict;
use warnings;
use Test::Deep;
-use File::Spec;
-use Test::More tests => 1 + 1; # plus one for warnings check
-use RT::Test ();
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
-init_db();
+use RT::Test::Shredder tests => 1;
+my $test = "RT::Test::Shredder";
-
-create_savepoint('clean'); # backup of the clean RT DB
-my $shredder = shredder_new(); # new shredder object
+$test->create_savepoint('clean'); # backup of the clean RT DB
+my $shredder = $test->shredder_new(); # new shredder object
# ....
# create and wipe RT objects
#
-cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
diff --git a/rt/t/shredder/01basics.t b/rt/t/shredder/01basics.t
index 1fa9f75ba..368c9ba74 100644
--- a/rt/t/shredder/01basics.t
+++ b/rt/t/shredder/01basics.t
@@ -3,18 +3,10 @@ use strict;
use warnings;
use Test::Deep;
-use File::Spec;
-use Test::More tests => 3 + 1; # plus one for warnings check
-use RT::Test ();
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
-init_db();
+use RT::Test::Shredder tests => 4;
+my $test = "RT::Test::Shredder";
-
-create_savepoint();
+$test->create_savepoint();
use RT::Tickets;
my $ticket = RT::Ticket->new( RT->SystemUser );
@@ -25,7 +17,9 @@ $ticket = RT::Ticket->new( RT->SystemUser );
my ($status, $msg) = $ticket->Load( $id );
ok( $id, "load ticket" ) or diag( "error: $msg" );
-my $shredder = shredder_new();
+my $shredder = $test->shredder_new();
$shredder->Wipeout( Object => $ticket );
-cmp_deeply( dump_current_and_savepoint(), "current DB equal to savepoint");
+$test->db_is_valid;
+
+cmp_deeply( $test->dump_current_and_savepoint(), "current DB equal to savepoint");
diff --git a/rt/t/shredder/01ticket.t b/rt/t/shredder/01ticket.t
index 0a9da413e..57179bc0b 100644
--- a/rt/t/shredder/01ticket.t
+++ b/rt/t/shredder/01ticket.t
@@ -3,19 +3,10 @@ use strict;
use warnings;
use Test::Deep;
-use File::Spec;
-use Test::More tests => 15 + 1; # plus one for warnings check
-use RT::Test ();
+use RT::Test::Shredder tests => 20;
+my $test = "RT::Test::Shredder";
-
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
-
-init_db();
-create_savepoint('clean');
+$test->create_savepoint('clean');
use RT::Ticket;
use RT::Tickets;
@@ -26,23 +17,25 @@ use RT::Tickets;
ok( $id, "created new ticket" );
$ticket->Delete;
is( $ticket->Status, 'deleted', "successfuly changed status" );
+ $ticket->ApplyTransactionBatch;
my $tickets = RT::Tickets->new( RT->SystemUser );
$tickets->{'allow_deleted_search'} = 1;
$tickets->LimitStatus( VALUE => 'deleted' );
is( $tickets->Count, 1, "found one deleted ticket" );
- my $shredder = shredder_new();
+ my $shredder = $test->shredder_new();
$shredder->PutObjects( Objects => $tickets );
$shredder->WipeoutAll;
+ $test->db_is_valid;
}
-cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
{
my $parent = RT::Ticket->new( RT->SystemUser );
my ($pid) = $parent->Create( Subject => 'test', Queue => 1 );
ok( $pid, "created new ticket" );
- create_savepoint('parent_ticket');
+ $test->create_savepoint('parent_ticket');
my $child = RT::Ticket->new( RT->SystemUser );
my ($cid) = $child->Create( Subject => 'test', Queue => 1 );
@@ -50,15 +43,21 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"
my ($status, $msg) = $parent->AddLink( Type => 'MemberOf', Target => $cid );
ok( $status, "Added link between tickets") or diag("error: $msg");
- my $shredder = shredder_new();
+
+ $parent->ApplyTransactionBatch;
+ $child->ApplyTransactionBatch;
+
+ my $shredder = $test->shredder_new();
$shredder->PutObjects( Objects => $child );
$shredder->WipeoutAll;
- cmp_deeply( dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint");
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint");
$shredder->PutObjects( Objects => $parent );
$shredder->WipeoutAll;
+ $test->db_is_valid;
}
-cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
{
my $parent = RT::Ticket->new( RT->SystemUser );
@@ -66,24 +65,26 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"
ok( $pid, "created new ticket" );
my ($status, $msg) = $parent->Delete;
ok( $status, 'deleted parent ticket');
- create_savepoint('parent_ticket');
+ $test->create_savepoint('parent_ticket');
my $child = RT::Ticket->new( RT->SystemUser );
my ($cid) = $child->Create( Subject => 'test', Queue => 1 );
- ok( $cid, "created new ticket" );
+ ok( $cid, "created new ticket #$cid" );
($status, $msg) = $parent->AddLink( Type => 'DependsOn', Target => $cid );
ok( $status, "Added link between tickets") or diag("error: $msg");
- my $shredder = shredder_new();
+
+ $parent->ApplyTransactionBatch;
+ $child->ApplyTransactionBatch;
+
+ my $shredder = $test->shredder_new();
$shredder->PutObjects( Objects => $child );
$shredder->WipeoutAll;
-
- TODO: {
- local $TODO = "Shredder doesn't delete all links and transactions";
- cmp_deeply( dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint");
- }
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint");
$shredder->PutObjects( Objects => $parent );
$shredder->WipeoutAll;
+ $test->db_is_valid;
}
-cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
diff --git a/rt/t/shredder/02group_member.t b/rt/t/shredder/02group_member.t
index 9dc4f6126..87d1e3ce7 100644
--- a/rt/t/shredder/02group_member.t
+++ b/rt/t/shredder/02group_member.t
@@ -3,100 +3,133 @@ use strict;
use warnings;
use Test::Deep;
-use File::Spec;
-use Test::More tests => 22 + 1; # plus one for warnings check
-use RT::Test ();
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
-init_db();
-
+use RT::Test::Shredder tests => 34;
+my $test = "RT::Test::Shredder";
### nested membership check
{
- create_savepoint('clean');
- my $pgroup = RT::Group->new( RT->SystemUser );
- my ($pgid) = $pgroup->CreateUserDefinedGroup( Name => 'Parent group' );
- ok( $pgid, "created parent group" );
- is( $pgroup->id, $pgid, "id is correct" );
-
- my $cgroup = RT::Group->new( RT->SystemUser );
- my ($cgid) = $cgroup->CreateUserDefinedGroup( Name => 'Child group' );
- ok( $cgid, "created child group" );
- is( $cgroup->id, $cgid, "id is correct" );
-
- my ($status, $msg) = $pgroup->AddMember( $cgroup->id );
- ok( $status, "added child group to parent") or diag "error: $msg";
-
- create_savepoint('bucreate'); # before user create
- my $user = RT::User->new( RT->SystemUser );
- my $uid;
- ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 );
- ok( $uid, "created new user" ) or diag "error: $msg";
- is( $user->id, $uid, "id is correct" );
-
- create_savepoint('buadd'); # before group add
- ($status, $msg) = $cgroup->AddMember( $user->id );
- ok( $status, "added user to child group") or diag "error: $msg";
-
- my $members = RT::GroupMembers->new( RT->SystemUser );
- $members->Limit( FIELD => 'MemberId', VALUE => $uid );
- $members->Limit( FIELD => 'GroupId', VALUE => $cgid );
- is( $members->Count, 1, "find membership record" );
-
- my $shredder = shredder_new();
- $shredder->PutObjects( Objects => $members );
- $shredder->WipeoutAll();
- cmp_deeply( dump_current_and_savepoint('buadd'), "current DB equal to savepoint");
-
- $shredder->PutObjects( Objects => $user );
- $shredder->WipeoutAll();
- cmp_deeply( dump_current_and_savepoint('bucreate'), "current DB equal to savepoint");
-
- $shredder->PutObjects( Objects => [$pgroup, $cgroup] );
- $shredder->WipeoutAll();
- cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+ $test->create_savepoint('clean');
+ my $pgroup = RT::Group->new( RT->SystemUser );
+ my ($pgid) = $pgroup->CreateUserDefinedGroup( Name => 'Parent group' );
+ ok( $pgid, "created parent group" );
+ is( $pgroup->id, $pgid, "id is correct" );
+
+ my $cgroup = RT::Group->new( RT->SystemUser );
+ my ($cgid) = $cgroup->CreateUserDefinedGroup( Name => 'Child group' );
+ ok( $cgid, "created child group" );
+ is( $cgroup->id, $cgid, "id is correct" );
+
+ my ($status, $msg) = $pgroup->AddMember( $cgroup->id );
+ ok( $status, "added child group to parent") or diag "error: $msg";
+
+ $test->create_savepoint('bucreate'); # before user create
+ my $user = RT::User->new( RT->SystemUser );
+ my $uid;
+ ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 );
+ ok( $uid, "created new user" ) or diag "error: $msg";
+ is( $user->id, $uid, "id is correct" );
+
+ $test->create_savepoint('buadd'); # before group add
+ ($status, $msg) = $cgroup->AddMember( $user->id );
+ ok( $status, "added user to child group") or diag "error: $msg";
+
+ my $members = RT::GroupMembers->new( RT->SystemUser );
+ $members->Limit( FIELD => 'MemberId', VALUE => $uid );
+ $members->Limit( FIELD => 'GroupId', VALUE => $cgid );
+ is( $members->Count, 1, "find membership record" );
+
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $members );
+ $shredder->WipeoutAll();
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('buadd'), "current DB equal to savepoint");
+
+ $shredder->PutObjects( Objects => $user );
+ $shredder->WipeoutAll();
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('bucreate'), "current DB equal to savepoint");
+
+ $shredder->PutObjects( Objects => [$pgroup, $cgroup] );
+ $shredder->WipeoutAll();
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+}
+
+### deleting member of the ticket AdminCc role group
+{
+ $test->restore_savepoint('clean');
+
+ my $user = RT::User->new( RT->SystemUser );
+ my ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 );
+ ok( $uid, "created new user" ) or diag "error: $msg";
+ is( $user->id, $uid, "id is correct" );
+
+ use RT::Queue;
+ my $queue = RT::Queue->new( RT->SystemUser );
+ $queue->Load('general');
+ ok( $queue->id, "queue loaded succesfully" );
+
+ use RT::Tickets;
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ my ($id) = $ticket->Create( Subject => 'test', Queue => $queue->id );
+ ok( $id, "created new ticket" );
+ $ticket = RT::Ticket->new( RT->SystemUser );
+ my $status;
+ ($status, $msg) = $ticket->Load( $id );
+ ok( $id, "load ticket" ) or diag( "error: $msg" );
+
+ ($status, $msg) = $ticket->AddWatcher( Type => "AdminCc", PrincipalId => $user->id );
+ ok( $status, "AdminCC successfuly added") or diag( "error: $msg" );
+
+ my $member = $ticket->AdminCc->MembersObj->First;
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $member );
+ $shredder->WipeoutAll();
+ $test->db_is_valid;
+
+ $shredder->PutObjects( Objects => $user );
+ $shredder->WipeoutAll();
+ $test->db_is_valid;
}
### deleting member of the ticket Owner role group
{
- restore_savepoint('clean');
-
- my $user = RT::User->new( RT->SystemUser );
- my ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 );
- ok( $uid, "created new user" ) or diag "error: $msg";
- is( $user->id, $uid, "id is correct" );
-
- use RT::Queue;
- my $queue = RT::Queue->new( RT->SystemUser );
- $queue->Load('general');
- ok( $queue->id, "queue loaded succesfully" );
-
- $user->PrincipalObj->GrantRight( Right => 'OwnTicket', Object => $queue );
-
- use RT::Tickets;
- my $ticket = RT::Ticket->new( RT->SystemUser );
- my ($id) = $ticket->Create( Subject => 'test', Queue => $queue->id );
- ok( $id, "created new ticket" );
- $ticket = RT::Ticket->new( RT->SystemUser );
- my $status;
- ($status, $msg) = $ticket->Load( $id );
- ok( $id, "load ticket" ) or diag( "error: $msg" );
-
- ($status, $msg) = $ticket->SetOwner( $user->id );
- ok( $status, "owner successfuly set") or diag( "error: $msg" );
- is( $ticket->Owner, $user->id, "owner successfuly set") or diag( "error: $msg" );
-
- my $member = $ticket->OwnerGroup->MembersObj->First;
- my $shredder = shredder_new();
- $shredder->PutObjects( Objects => $member );
- $shredder->WipeoutAll();
-
- $ticket = RT::Ticket->new( RT->SystemUser );
- ($status, $msg) = $ticket->Load( $id );
- ok( $id, "load ticket" ) or diag( "error: $msg" );
- is( $ticket->Owner, RT->Nobody->id, "owner switched back to nobody" );
- is( $ticket->OwnerGroup->MembersObj->First->MemberId, RT->Nobody->id, "and owner role group member is nobody");
+ $test->restore_savepoint('clean');
+
+ my $user = RT::User->new( RT->SystemUser );
+ my ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 );
+ ok( $uid, "created new user" ) or diag "error: $msg";
+ is( $user->id, $uid, "id is correct" );
+
+ use RT::Queue;
+ my $queue = RT::Queue->new( RT->SystemUser );
+ $queue->Load('general');
+ ok( $queue->id, "queue loaded succesfully" );
+
+ $user->PrincipalObj->GrantRight( Right => 'OwnTicket', Object => $queue );
+
+ use RT::Tickets;
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ my ($id) = $ticket->Create( Subject => 'test', Queue => $queue->id );
+ ok( $id, "created new ticket" );
+ $ticket = RT::Ticket->new( RT->SystemUser );
+ my $status;
+ ($status, $msg) = $ticket->Load( $id );
+ ok( $id, "load ticket" ) or diag( "error: $msg" );
+
+ ($status, $msg) = $ticket->SetOwner( $user->id );
+ ok( $status, "owner successfuly set") or diag( "error: $msg" );
+ is( $ticket->Owner, $user->id, "owner successfuly set") or diag( "error: $msg" );
+
+ my $member = $ticket->OwnerGroup->MembersObj->First;
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $member );
+ $shredder->WipeoutAll();
+ $test->db_is_valid;
+
+ $ticket = RT::Ticket->new( RT->SystemUser );
+ ($status, $msg) = $ticket->Load( $id );
+ ok( $id, "load ticket" ) or diag( "error: $msg" );
+ is( $ticket->Owner, RT->Nobody->id, "owner switched back to nobody" );
+ is( $ticket->OwnerGroup->MembersObj->First->MemberId, RT->Nobody->id, "and owner role group member is nobody");
}
diff --git a/rt/t/shredder/02queue.t b/rt/t/shredder/02queue.t
index dd5581754..0c65c5daf 100644
--- a/rt/t/shredder/02queue.t
+++ b/rt/t/shredder/02queue.t
@@ -3,33 +3,26 @@ use strict;
use warnings;
use Test::Deep;
-use File::Spec;
-use Test::More tests => 16 + 1; # plus one for warnings check
-use RT::Test ();
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
-init_db();
-
+use RT::Test::Shredder tests => 21;
+my $test = "RT::Test::Shredder";
diag 'simple queue' if $ENV{TEST_VERBOSE};
{
- create_savepoint('clean');
+ $test->create_savepoint('clean');
my $queue = RT::Queue->new( RT->SystemUser );
my ($id, $msg) = $queue->Create( Name => 'my queue' );
ok($id, 'created queue') or diag "error: $msg";
- my $shredder = shredder_new();
- $shredder->PutObjects( Objects => $queue );
- $shredder->WipeoutAll;
- cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $queue );
+ $shredder->WipeoutAll;
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
}
diag 'queue with scrip' if $ENV{TEST_VERBOSE};
{
- create_savepoint('clean');
+ $test->create_savepoint('clean');
my $queue = RT::Queue->new( RT->SystemUser );
my ($id, $msg) = $queue->Create( Name => 'my queue' );
ok($id, 'created queue') or diag "error: $msg";
@@ -44,15 +37,16 @@ diag 'queue with scrip' if $ENV{TEST_VERBOSE};
);
ok($id, 'created scrip') or diag "error: $msg";
- my $shredder = shredder_new();
- $shredder->PutObjects( Objects => $queue );
- $shredder->WipeoutAll;
- cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $queue );
+ $shredder->WipeoutAll;
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
}
diag 'queue with template' if $ENV{TEST_VERBOSE};
{
- create_savepoint('clean');
+ $test->create_savepoint('clean');
my $queue = RT::Queue->new( RT->SystemUser );
my ($id, $msg) = $queue->Create( Name => 'my queue' );
ok($id, 'created queue') or diag "error: $msg";
@@ -65,15 +59,16 @@ diag 'queue with template' if $ENV{TEST_VERBOSE};
);
ok($id, 'created template') or diag "error: $msg";
- my $shredder = shredder_new();
- $shredder->PutObjects( Objects => $queue );
- $shredder->WipeoutAll;
- cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $queue );
+ $shredder->WipeoutAll;
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
}
diag 'queue with a right granted' if $ENV{TEST_VERBOSE};
{
- create_savepoint('clean');
+ $test->create_savepoint('clean');
my $queue = RT::Queue->new( RT->SystemUser );
my ($id, $msg) = $queue->Create( Name => 'my queue' );
ok($id, 'created queue') or diag "error: $msg";
@@ -88,21 +83,22 @@ diag 'queue with a right granted' if $ENV{TEST_VERBOSE};
);
ok($id, 'granted right') or diag "error: $msg";
- my $shredder = shredder_new();
- $shredder->PutObjects( Objects => $queue );
- $shredder->WipeoutAll;
- cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $queue );
+ $shredder->WipeoutAll;
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
}
diag 'queue with a watcher' if $ENV{TEST_VERBOSE};
{
# XXX, FIXME: if uncomment these lines then we'll get 'Bizarre...'
-# create_savepoint('clean');
+# $test->create_savepoint('clean');
my $group = RT::Group->new( RT->SystemUser );
my ($id, $msg) = $group->CreateUserDefinedGroup(Name => 'my group');
ok($id, 'created group') or diag "error: $msg";
- create_savepoint('bqcreate');
+ $test->create_savepoint('bqcreate');
my $queue = RT::Queue->new( RT->SystemUser );
($id, $msg) = $queue->Create( Name => 'my queue' );
ok($id, 'created queue') or diag "error: $msg";
@@ -113,12 +109,13 @@ diag 'queue with a watcher' if $ENV{TEST_VERBOSE};
);
ok($id, 'added watcher') or diag "error: $msg";
- my $shredder = shredder_new();
- $shredder->PutObjects( Objects => $queue );
- $shredder->WipeoutAll;
- cmp_deeply( dump_current_and_savepoint('bqcreate'), "current DB equal to savepoint");
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $queue );
+ $shredder->WipeoutAll;
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('bqcreate'), "current DB equal to savepoint");
-# $shredder->PutObjects( Objects => $group );
-# $shredder->WipeoutAll;
-# cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+# $shredder->PutObjects( Objects => $group );
+# $shredder->WipeoutAll;
+# cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
}
diff --git a/rt/t/shredder/02template.t b/rt/t/shredder/02template.t
index aeb318ed6..56dd852ed 100644
--- a/rt/t/shredder/02template.t
+++ b/rt/t/shredder/02template.t
@@ -3,20 +3,12 @@ use strict;
use warnings;
use Test::Deep;
-use File::Spec;
-use Test::More tests => 7 + 1; # plus one for warnings check
-use RT::Test ();
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
-init_db();
-
+use RT::Test::Shredder tests => 10;
+my $test = "RT::Test::Shredder";
diag 'global template' if $ENV{TEST_VERBOSE};
{
- create_savepoint('clean');
+ $test->create_savepoint('clean');
my $template = RT::Template->new( RT->SystemUser );
my ($id, $msg) = $template->Create(
Name => 'my template',
@@ -24,15 +16,16 @@ diag 'global template' if $ENV{TEST_VERBOSE};
);
ok($id, 'created template') or diag "error: $msg";
- my $shredder = shredder_new();
- $shredder->PutObjects( Objects => $template );
- $shredder->WipeoutAll;
- cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $template );
+ $shredder->WipeoutAll;
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
}
diag 'local template' if $ENV{TEST_VERBOSE};
{
- create_savepoint('clean');
+ $test->create_savepoint('clean');
my $template = RT::Template->new( RT->SystemUser );
my ($id, $msg) = $template->Create(
Name => 'my template',
@@ -41,15 +34,16 @@ diag 'local template' if $ENV{TEST_VERBOSE};
);
ok($id, 'created template') or diag "error: $msg";
- my $shredder = shredder_new();
- $shredder->PutObjects( Objects => $template );
- $shredder->WipeoutAll;
- cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $template );
+ $shredder->WipeoutAll;
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
}
diag 'template used in scrip' if $ENV{TEST_VERBOSE};
{
- create_savepoint('clean');
+ $test->create_savepoint('clean');
my $template = RT::Template->new( RT->SystemUser );
my ($id, $msg) = $template->Create(
Name => 'my template',
@@ -68,8 +62,9 @@ diag 'template used in scrip' if $ENV{TEST_VERBOSE};
);
ok($id, 'created scrip') or diag "error: $msg";
- my $shredder = shredder_new();
- $shredder->PutObjects( Objects => $template );
- $shredder->WipeoutAll;
- cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+ my $shredder = $test->shredder_new();
+ $shredder->PutObjects( Objects => $template );
+ $shredder->WipeoutAll;
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
}
diff --git a/rt/t/shredder/02user.t b/rt/t/shredder/02user.t
index 9f1577015..620c2c587 100644
--- a/rt/t/shredder/02user.t
+++ b/rt/t/shredder/02user.t
@@ -3,18 +3,10 @@ use strict;
use warnings;
use Test::Deep;
-use File::Spec;
-use Test::More tests => 8 + 1; # plus one for warnings check
-use RT::Test ();
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
-init_db();
-
+use RT::Test::Shredder tests => 10;
+my $test = "RT::Test::Shredder";
-create_savepoint('clean');
+$test->create_savepoint('clean');
my $queue = RT::Queue->new( RT->SystemUser );
my ($qid) = $queue->Load( 'General' );
@@ -24,38 +16,40 @@ my $ticket = RT::Ticket->new( RT->SystemUser );
my ($tid) = $ticket->Create( Queue => $qid, Subject => 'test' );
ok( $tid, "ticket created" );
-create_savepoint('bucreate'); # berfore user create
+$test->create_savepoint('bucreate'); # berfore user create
my $user = RT::User->new( RT->SystemUser );
my ($uid, $msg) = $user->Create( Name => 'new user', Privileged => 1, Disabled => 0 );
ok( $uid, "created new user" ) or diag "error: $msg";
is( $user->id, $uid, "id is correct" );
# HACK: set ticket props to enable VARIABLE dependencies
$ticket->__Set( Field => 'LastUpdatedBy', Value => $uid );
-create_savepoint('aucreate'); # after user create
+$test->create_savepoint('aucreate'); # after user create
{
my $resolver = sub {
my %args = (@_);
- my $t = $args{'TargetObject'};
+ my $t = $args{'TargetObject'};
my $resolver_uid = RT->SystemUser->id;
foreach my $method ( qw(Creator LastUpdatedBy) ) {
next unless $t->_Accessible( $method => 'read' );
$t->__Set( Field => $method, Value => $resolver_uid );
}
};
- my $shredder = shredder_new();
+ my $shredder = $test->shredder_new();
$shredder->PutResolver( BaseClass => 'RT::User', Code => $resolver );
$shredder->Wipeout( Object => $user );
- cmp_deeply( dump_current_and_savepoint('bucreate'), "current DB equal to savepoint");
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('bucreate'), "current DB equal to savepoint");
}
{
- restore_savepoint('aucreate');
+ $test->restore_savepoint('aucreate');
my $user = RT::User->new( RT->SystemUser );
$user->Load($uid);
ok($user->id, "loaded user after restore");
- my $shredder = shredder_new();
+ my $shredder = $test->shredder_new();
eval { $shredder->Wipeout( Object => $user ) };
ok($@, "wipeout throw exception if no resolvers");
- cmp_deeply( dump_current_and_savepoint('aucreate'), "current DB equal to savepoint");
+ $test->db_is_valid;
+ cmp_deeply( $test->dump_current_and_savepoint('aucreate'), "current DB equal to savepoint");
}
diff --git a/rt/t/shredder/03plugin.t b/rt/t/shredder/03plugin.t
index 15766cd2e..de5d44fa7 100644
--- a/rt/t/shredder/03plugin.t
+++ b/rt/t/shredder/03plugin.t
@@ -3,14 +3,8 @@ use strict;
use warnings;
use Test::Deep;
-use File::Spec;
-use Test::More tests => 28 + 1; # plus one for warnings check
-use RT::Test ();
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
+use RT::Test::Shredder nodb => 1, tests => 28;
+my $test = "RT::Test::Shredder";
my @PLUGINS = sort qw(Attachments Base Objects SQLDump Summary Tickets Users);
diff --git a/rt/t/shredder/03plugin_summary.t b/rt/t/shredder/03plugin_summary.t
index d450c70a2..7da8bb454 100644
--- a/rt/t/shredder/03plugin_summary.t
+++ b/rt/t/shredder/03plugin_summary.t
@@ -2,16 +2,7 @@
use strict;
use warnings;
-use Test::Deep;
-use File::Spec;
-use Test::More tests => 4 + 1; # plus one for warnings check
-use RT::Test nodb => 1;
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
-
+use RT::Test::Shredder nodb => 1, tests => 4;
use_ok('RT::Shredder::Plugin');
my $plugin_obj = RT::Shredder::Plugin->new;
diff --git a/rt/t/shredder/03plugin_tickets.t b/rt/t/shredder/03plugin_tickets.t
index 1579bc54b..dd2b120c4 100644
--- a/rt/t/shredder/03plugin_tickets.t
+++ b/rt/t/shredder/03plugin_tickets.t
@@ -3,15 +3,10 @@ use strict;
use warnings;
use Test::Deep;
-use File::Spec;
-use Test::More tests => 44 + 1; # plus one for warnings check
-use RT::Test ();
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
+use RT::Test::Shredder tests => 49;
+my $test = "RT::Test::Shredder";
+use_ok('RT::Shredder');
use_ok('RT::Shredder::Plugin::Tickets');
{
@@ -21,8 +16,7 @@ use_ok('RT::Shredder::Plugin::Tickets');
is(lc $plugin->Type, 'search', 'correct type');
}
-init_db();
-create_savepoint('clean');
+$test->create_savepoint('clean');
use_ok('RT::Ticket');
use_ok('RT::Tickets');
@@ -59,11 +53,12 @@ use_ok('RT::Tickets');
ok($has{$pid}, "parent is in the result set");
ok($has{$cid}, "child is in the result set");
- my $shredder = shredder_new();
+ my $shredder = $test->shredder_new();
$shredder->PutObjects( Objects => \@objs );
$shredder->WipeoutAll;
+ $test->db_is_valid;
}
-cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
{ # create parent and child and link them reqursively to check that we don't hang
my $parent = RT::Ticket->new( RT->SystemUser );
@@ -103,11 +98,12 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"
ok($has{$pid}, "parent is in the result set");
ok($has{$cid}, "child is in the result set");
- my $shredder = shredder_new();
+ my $shredder = $test->shredder_new();
$shredder->PutObjects( Objects => \@objs );
$shredder->WipeoutAll;
+ $test->db_is_valid;
}
-cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
{ # create parent and child and check functionality of 'apply_query_to_linked' arg
my $parent = RT::Ticket->new( RT->SystemUser );
@@ -141,9 +137,10 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"
ok(!$has{$cid1}, "first child is in the result set");
ok($has{$cid2}, "second child is in the result set");
- my $shredder = shredder_new();
+ my $shredder = $test->shredder_new();
$shredder->PutObjects( Objects => \@objs );
$shredder->WipeoutAll;
+ $test->db_is_valid;
my $ticket = RT::Ticket->new( RT->SystemUser );
$ticket->Load( $cid1 );
@@ -151,5 +148,6 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"
$shredder->PutObjects( Objects => $ticket );
$shredder->WipeoutAll;
+ $test->db_is_valid;
}
-cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
diff --git a/rt/t/shredder/03plugin_users.t b/rt/t/shredder/03plugin_users.t
index 131ffa0c3..477f1474f 100644
--- a/rt/t/shredder/03plugin_users.t
+++ b/rt/t/shredder/03plugin_users.t
@@ -3,17 +3,10 @@ use strict;
use warnings;
use Test::Deep;
-use File::Spec;
-use Test::More tests => 21 + 1; # plus one for warnings check
-use RT::Test ();
-BEGIN {
- my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
- File::Spec->curdir());
- require $shredder_utils;
-}
-
+use RT::Test::Shredder tests => 21;
+my $test = "RT::Test::Shredder";
-my @ARGS = sort qw(limit status name member_of email replace_relations no_tickets);
+my @ARGS = sort qw(limit status name member_of not_member_of email replace_relations no_tickets);
use_ok('RT::Shredder::Plugin::Users');
{
@@ -37,13 +30,11 @@ use_ok('RT::Shredder::Plugin::Users');
ok(!$status, "bad 'status' arg value");
}
-init_db();
-
RT::Test->set_rights(
{ Principal => 'Everyone', Right => [qw(CreateTicket)] },
);
-create_savepoint('clean');
+$test->create_savepoint('clean');
{ # Create two users and a ticket. Shred second user and replace relations with first user
my ($uidA, $uidB, $msg);
@@ -59,6 +50,7 @@ create_savepoint('clean');
my $ticket = RT::Ticket->new( RT::CurrentUser->new($userB) );
($tid, $trid, $msg) = $ticket->Create( Subject => 'UserB Ticket', Queue => 1 );
ok( $tid, "created new ticket") or diag "error: $msg";
+ $ticket->ApplyTransactionBatch;
my $transaction = RT::Transaction->new( RT->SystemUser );
$transaction->Load($trid);
@@ -71,14 +63,14 @@ create_savepoint('clean');
($status, $msg) = $plugin->TestArgs( status => 'any', name => 'userB', replace_relations => $uidA );
ok($status, "plugin arguments are ok") or diag "error: $msg";
+ my $shredder = $test->shredder_new();
+
my @objs;
($status, @objs) = $plugin->Run;
ok($status, "executed plugin successfully") or diag "error: @objs";
@objs = RT::Shredder->CastObjectsToRecords( Objects => \@objs );
is(scalar @objs, 1, "one object in the result set");
- my $shredder = shredder_new();
-
($status, $msg) = $plugin->SetResolvers( Shredder => $shredder );
ok($status, "set conflicts resolver") or diag "error: $msg";
@@ -94,4 +86,4 @@ create_savepoint('clean');
$shredder->Wipeout( Object => $ticket );
$shredder->Wipeout( Object => $userA );
}
-cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
+cmp_deeply( $test->dump_current_and_savepoint('clean'), "current DB equal to savepoint");
diff --git a/rt/t/shredder/utils.pl b/rt/t/shredder/utils.pl
deleted file mode 100644
index a3d0cf59a..000000000
--- a/rt/t/shredder/utils.pl
+++ /dev/null
@@ -1,394 +0,0 @@
-
-use strict;
-use warnings;
-
-require File::Copy;
-require Cwd;
-require RT::Test;
-
-BEGIN {
-### after: push @INC, qw(@RT_LIB_PATH@);
-
- use RT;
- RT->LoadConfig;
- RT->InitPluginPaths;
- RT->InitClasses;
-}
-
-require RT::Shredder;
-
-=head1 DESCRIPTION
-
-RT::Shredder test suite utilities
-
-=head1 TESTING
-
-Since RT:Shredder 0.01_03 we have a test suite. You
-can run tests and see if everything works as expected
-before you try shredder on your actual data.
-Tests also help in the development process.
-
-The test suite uses SQLite databases to store data in individual files,
-so you could sun tests on your production servers without risking
-damage to your production data.
-
-You'll want to run the test suite almost every time you install or update
-the shredder distribution, especialy if you have local customizations of
-the DB schema and/or RT code.
-
-Tests are one thing you can write even if you don't know much perl,
-but want to learn more about RT's internals. New tests are very welcome.
-
-=head2 WRITING TESTS
-
-The shredder distribution has several files to help write new tests.
-
- t/shredder/utils.pl - this file, utilities
- t/00skeleton.t - skeleteton .t file for new tests
-
-All tests follow this algorithm:
-
- require "t/shredder/utils.pl"; # plug in utilities
- init_db(); # create new tmp RT DB and init RT API
- # create RT data you want to be always in the RT DB
- # ...
- create_savepoint('mysp'); # create DB savepoint
- # create data you want delete with shredder
- # ...
- # run shredder on the objects you've created
- # ...
- # check that shredder deletes things you want
- # this command will compare savepoint DB with current
- cmp_deeply( dump_current_and_savepoint('mysp'), "current DB equal to savepoint");
- # then you can create another object and delete it, then check again
-
-Savepoints are named and you can create two or more savepoints.
-
-=head1 FUNCTIONS
-
-=head2 RT CONFIG
-
-=head3 rewrite_rtconfig
-
-Call this sub after C<RT::LoadConfig>. It changes the RT config
-options necessary to switch to a local SQLite database.
-
-=cut
-
-sub rewrite_rtconfig
-{
- # database
- config_set( '$DatabaseType' , 'SQLite' );
- config_set( '$DatabaseHost' , 'localhost' );
- config_set( '$DatabaseRTHost' , 'localhost' );
- config_set( '$DatabasePort' , '' );
- config_set( '$DatabaseUser' , 'rt_user' );
- config_set( '$DatabasePassword' , 'rt_pass' );
- config_set( '$DatabaseRequireSSL' , undef );
- # database file name
- config_set( '$DatabaseName' , db_name() );
-
- # generic logging
- config_set( '$LogToSyslog' , undef );
- config_set( '$LogToScreen' , 'error' );
- config_set( '$LogStackTraces' , 'crit' );
- # logging to standalone file
- config_set( '$LogToFile' , 'debug' );
- my $fname = File::Spec->catfile(RT::Test->temp_directory(), test_name() .".log");
- config_set( '$LogToFileNamed' , $fname );
- config_set('@LexiconLanguages', qw(en));
-}
-
-=head3 config_set
-
-This sub is a helper used by C<rewrite_rtconfig>. You shouldn't
-need to use it elsewhere unless you need to change other RT
-configuration variables.
-
-=cut
-
-sub config_set {
- my $opt = shift;
- $opt =~ s/^[\$\%\@]//;
- RT->Config->Set($opt, @_)
-}
-
-=head2 DATABASES
-
-=head3 init_db
-
-Creates a new RT DB with initial data in a new test tmp dir.
-Also runs RT::Init() and RT::InitLogging().
-
-This is all you need to call to setup a testing environment
-in most situations.
-
-=cut
-
-sub init_db
-{
- RT::Test->bootstrap_tempdir() unless RT::Test->temp_directory();
- RT::LoadConfig();
- rewrite_rtconfig();
- RT::InitLogging();
-
- _init_db();
-
- RT::Init();
- $SIG{__WARN__} = sub { $RT::Logger->warning( @_ ); warn @_ };
- $SIG{__DIE__} = sub { $RT::Logger->crit( @_ ) unless $^S; die @_ };
-}
-
-use IPC::Open2;
-sub _init_db
-{
-
-
- foreach ( qw(Type Host Port Name User Password) ) {
- $ENV{ "RT_DB_". uc $_ } = RT->Config->Get("Database$_");
- }
- my $rt_setup_database = RT::Test::get_relocatable_file(
- 'rt-setup-database', (File::Spec->updir(), File::Spec->updir(), 'sbin'));
- my $cmd = "$^X $rt_setup_database --action init 2>&1";
-
- my ($child_out, $child_in);
- my $pid = open2($child_out, $child_in, $cmd);
- close $child_in;
- my $result = do { local $/; <$child_out> };
- return $result;
-}
-
-=head3 db_name
-
-Returns the absolute file path to the current DB.
-It is <<RT::Test->temp_directory . test_name() .'.db'>>.
-
-See also the C<test_name> function.
-
-=cut
-
-sub db_name { return File::Spec->catfile(RT::Test->temp_directory(), test_name() .".db") }
-
-=head3 connect_sqlite
-
-Returns connected DBI DB handle.
-
-Takes path to sqlite db.
-
-=cut
-
-sub connect_sqlite
-{
- return DBI->connect("dbi:SQLite:dbname=". shift, "", "");
-}
-
-=head2 SHREDDER
-
-=head3 shredder_new
-
-Creates and returns a new RT::Shredder object.
-
-=cut
-
-sub shredder_new
-{
- my $obj = RT::Shredder->new;
-
- my $file = File::Spec->catfile( RT::Test->temp_directory, test_name() .'.XXXX.sql' );
- $obj->AddDumpPlugin( Arguments => {
- file_name => $file,
- from_storage => 0,
- } );
-
- return $obj;
-}
-
-
-=head2 TEST FILES
-
-=head3 test_name
-
-Returns name of the test file running now with file extension and
-directory names stripped.
-
-For example, it returns '00load' for the test file 't/00load.t'.
-
-=cut
-
-sub test_name
-{
- my $name = $0;
- $name =~ s/^.*[\\\/]//;
- $name =~ s/\..*$//;
- return $name;
-}
-
-=head2 SAVEPOINTS
-
-=head3 savepoint_name
-
-Returns the absolute path to the named savepoint DB file.
-Takes one argument - savepoint name, by default C<sp>.
-
-=cut
-
-sub savepoint_name
-{
- my $name = shift || 'sp';
- return File::Spec->catfile( RT::Test->temp_directory, test_name() .".$name.db" );
-}
-
-=head3 create_savepoint
-
-Creates savepoint DB from the current DB.
-Takes name of the savepoint as argument.
-
-=head3 restore_savepoint
-
-Restores current DB to savepoint state.
-Takes name of the savepoint as argument.
-
-=cut
-
-sub create_savepoint { return __cp_db( db_name() => savepoint_name( shift ) ) }
-sub restore_savepoint { return __cp_db( savepoint_name( shift ) => db_name() ) }
-sub __cp_db
-{
- my( $orig, $dest ) = @_;
- RT::Test::__disconnect_rt();
- File::Copy::copy( $orig, $dest ) or die "Couldn't copy '$orig' => '$dest': $!";
- RT::Test::__reconnect_rt();
- return;
-}
-
-
-=head2 DUMPS
-
-=head3 dump_sqlite
-
-Returns DB dump as a complex hash structure:
- {
- TableName => {
- #id => {
- lc_field => 'value',
- }
- }
- }
-
-Takes named argument C<CleanDates>. If true, clean all date fields from
-dump. True by default.
-
-=cut
-
-sub dump_sqlite
-{
- my $dbh = shift;
- my %args = ( CleanDates => 1, @_ );
-
- my $old_fhkn = $dbh->{'FetchHashKeyName'};
- $dbh->{'FetchHashKeyName'} = 'NAME_lc';
-
- my @tables = $RT::Handle->_TableNames( $dbh );
-
- my $res = {};
- foreach my $t( @tables ) {
- next if lc($t) eq 'sessions';
- $res->{$t} = $dbh->selectall_hashref("SELECT * FROM $t".dump_sqlite_exceptions($t), 'id');
- clean_dates( $res->{$t} ) if $args{'CleanDates'};
- die $DBI::err if $DBI::err;
- }
-
- $dbh->{'FetchHashKeyName'} = $old_fhkn;
- return $res;
-}
-
-=head3 dump_sqlite_exceptions
-
-If there are parts of the DB which can change from creating and deleting
-a queue, skip them when doing the comparison. One example is the global
-queue cache attribute on RT::System which will be updated on Queue creation
-and can't be rolled back by the shredder. It may actually make sense for
-Shredder to be updating this at some point in the future.
-
-=cut
-
-sub dump_sqlite_exceptions {
- my $table = shift;
-
- my $special_wheres = {
- attributes => " WHERE Name != 'QueueCacheNeedsUpdate'"
- };
-
- return $special_wheres->{lc $table}||'';
-
-}
-
-=head3 dump_current_and_savepoint
-
-Returns dump of the current DB and of the named savepoint.
-Takes one argument - savepoint name.
-
-=cut
-
-sub dump_current_and_savepoint
-{
- my $orig = savepoint_name( shift );
- die "Couldn't find savepoint file" unless -f $orig && -r _;
- my $odbh = connect_sqlite( $orig );
- return ( dump_sqlite( $RT::Handle->dbh, @_ ), dump_sqlite( $odbh, @_ ) );
-}
-
-=head3 dump_savepoint_and_current
-
-Returns the same data as C<dump_current_and_savepoint> function,
-but in reversed order.
-
-=cut
-
-sub dump_savepoint_and_current { return reverse dump_current_and_savepoint(@_) }
-
-sub clean_dates
-{
- my $h = shift;
- my $date_re = qr/^\d\d\d\d\-\d\d\-\d\d\s*\d\d\:\d\d(\:\d\d)?$/i;
- foreach my $id ( keys %{ $h } ) {
- next unless $h->{ $id };
- foreach ( keys %{ $h->{ $id } } ) {
- delete $h->{$id}{$_} if $h->{$id}{$_} &&
- $h->{$id}{$_} =~ /$date_re/;
- }
- }
-}
-
-=head2 NOTES
-
-Function that returns debug notes.
-
-=head3 note_on_fail
-
-Returns a note about debug info that you can display if tests fail.
-
-=cut
-
-sub note_on_fail
-{
- my $name = test_name();
- my $tmpdir = RT::Test->temp_directory();
- return <<END;
-Some tests in '$0' file failed.
-You can find debug info in '$tmpdir' dir.
-There should be:
- $name.log - RT debug log file
- $name.db - latest RT DB used while testing
- $name.*.db - savepoint databases
-See also perldoc t/shredder/utils.pl for how to use this info.
-END
-}
-
-END {
- if ( ! RT::Test->builder->is_passing ) {
- diag( note_on_fail() );
- }
-}
-
-1;
diff --git a/rt/t/ticket/action_linear_escalate.t b/rt/t/ticket/action_linear_escalate.t
index 0d1ff8aa5..8c158aa59 100644
--- a/rt/t/ticket/action_linear_escalate.t
+++ b/rt/t/ticket/action_linear_escalate.t
@@ -19,7 +19,7 @@ ok $q && $q->id, 'loaded or created queue';
my $gecos = RT::Test->load_or_create_user(
Name => 'gecos',
Password => 'password',
- Gecos => ($^O eq 'MSWin32') ? Win32::LoginName() : (getpwuid($<))[0],
+ Gecos => (getpwuid($<))[0],
);
ok $gecos && $gecos->id, 'loaded or created gecos user';
diff --git a/rt/t/ticket/add-watchers.t b/rt/t/ticket/add-watchers.t
index 11b3a8453..12b0bb3e2 100644
--- a/rt/t/ticket/add-watchers.t
+++ b/rt/t/ticket/add-watchers.t
@@ -1,4 +1,4 @@
-use RT::Test nodata => 1, tests => 32;
+use RT::Test nodata => 1, tests => 34;
use strict;
use warnings;
@@ -15,7 +15,7 @@ my $acl = RT::ACL->new(RT->SystemUser);
$acl->Limit( FIELD => 'RightName', OPERATOR => '!=', VALUE => 'SuperUser' );
$acl->LimitToObject( RT->System );
while( my $ace = $acl->Next ) {
- $ace->Delete;
+ $ace->Delete;
}
# create new queue to be sure we do not mess with rights
@@ -25,11 +25,12 @@ ok( $queue_id, 'queue created for watcher tests' );
# new privileged user to check rights
my $user = RT::User->new( RT->SystemUser );
-my ($user_id) = $user->Create( Name => 'watcher'.$$,
- EmailAddress => "watcher$$".'@localhost',
- Privileged => 1,
- Password => 'qwe123',
- );
+my ($user_id) = $user->Create(
+ Name => 'watcher'.$$,
+ EmailAddress => "watcher$$".'@localhost',
+ Privileged => 1,
+ Password => 'qwe123',
+);
my $cu= RT::CurrentUser->new($user);
# make sure user can see tickets in the queue
@@ -86,6 +87,13 @@ ok( $rv, "user can add self as Cc by username" );
($rv, $msg) = $ticket2->AddWatcher( Type => 'Requestor', Email => $user->Name );
ok( $rv, "user can add self as Requestor by username" );
+# Add an email address with a phrase
+($rv, $msg) = $ticket->AddWatcher( Type => 'Cc', Email => q["Foo Bar" <foo@example.com>] );
+ok $rv, "Added email address with phrase" or diag $msg;
+
+my $foo = RT::Test->load_or_create_user( EmailAddress => 'foo@example.com' );
+is $foo->RealName, "Foo Bar", "RealName matches";
+
# Queue watcher tests
$principal->RevokeRight( Right => 'Watch' , Object => $queue );
ok( !$user->HasRight( Right => 'Watch', Object => $queue ), "user queue watch right revoked" );
diff --git a/rt/t/ticket/cfsort-freeform-single.t b/rt/t/ticket/cfsort-freeform-single.t
index ae109e9e4..262f84a22 100644
--- a/rt/t/ticket/cfsort-freeform-single.t
+++ b/rt/t/ticket/cfsort-freeform-single.t
@@ -1,158 +1,169 @@
-use RT::Test nodata => 1, tests => 89;
-
use strict;
use warnings;
-use RT::Tickets;
-use RT::Queue;
-use RT::CustomField;
-
-# Test Sorting by FreeformSingle custom field.
+use RT::Test nodata => 1, tests => undef;
-diag "Create a queue to test with.";
-my $queue_name = "CFSortQueue-$$";
-my $queue;
-{
- $queue = RT::Queue->new( RT->SystemUser );
- my ($ret, $msg) = $queue->Create(
- Name => $queue_name,
- Description => 'queue for custom field sort testing'
- );
- ok($ret, "$queue test queue creation. $msg");
-}
+my $queue = RT::Test->load_or_create_queue( Name => "sorting" );
+ok $queue && $queue->id, "Created queue";
+my $queue_name = $queue->Name;
# CFs for testing, later we create another one
-my %CF;
-my $cf_name;
-
+my $cf;
+my $cf_name = "ordering";
diag "create a CF";
{
- $cf_name = $CF{'CF'}{'name'} = "Order$$";
- $CF{'CF'}{'obj'} = RT::CustomField->new( RT->SystemUser );
- my ($ret, $msg) = $CF{'CF'}{'obj'}->Create(
- Name => $CF{'CF'}{'name'},
+ $cf = RT::CustomField->new( RT->SystemUser );
+ my ($ret, $msg) = $cf->Create(
+ Name => $cf_name,
Queue => $queue->id,
Type => 'FreeformSingle',
);
- ok($ret, "Custom Field $CF{'CF'}{'name'} created");
-}
-
-my ($total, @data, @tickets, @test) = (0, ());
-
-sub run_tests {
- my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets;
- foreach my $test ( @test ) {
- my $query = join " AND ", map "( $_ )", grep defined && length,
- $query_prefix, $test->{'Query'};
-
- foreach my $order (qw(ASC DESC)) {
- my $error = 0;
- my $tix = RT::Tickets->new( RT->SystemUser );
- $tix->FromSQL( $query );
- $tix->OrderBy( FIELD => $test->{'Order'}, ORDER => $order );
-
- ok($tix->Count, "found ticket(s)")
- or $error = 1;
-
- my ($order_ok, $last) = (1, $order eq 'ASC'? '-': 'zzzzzz');
- my $last_id = $tix->Last->id;
- while ( my $t = $tix->Next ) {
- my $tmp;
- next if $t->id == $last_id and $t->Subject eq "-"; # Nulls are allowed to come last, in Pg
-
- if ( $order eq 'ASC' ) {
- $tmp = ((split( /,/, $last))[0] cmp (split( /,/, $t->Subject))[0]);
- } else {
- $tmp = -((split( /,/, $last))[-1] cmp (split( /,/, $t->Subject))[-1]);
- }
- if ( $tmp > 0 ) {
- $order_ok = 0; last;
- }
- $last = $t->Subject;
- }
-
- ok( $order_ok, "$order order of tickets is good" )
- or $error = 1;
-
- if ( $error ) {
- diag "Wrong SQL query:". $tix->BuildSelectQuery;
- $tix->GotoFirstItem;
- while ( my $t = $tix->Next ) {
- diag sprintf "%02d - %s", $t->id, $t->Subject;
- }
- }
- }
- }
+ ok($ret, "Custom Field created");
}
-@data = (
- { Subject => '-' },
- { Subject => 'a', 'CustomField-' . $CF{CF}{obj}->id => 'a' },
- { Subject => 'b', 'CustomField-' . $CF{CF}{obj}->id => 'b' },
-);
-
-@tickets = RT::Test->create_tickets( { Queue => $queue->id, RandomOrder => 1 }, @data);
-@test = (
- { Order => "CF.{$cf_name}" },
- { Order => "CF.$queue_name.{$cf_name}" },
-);
-run_tests();
-
-@data = (
- { Subject => '-' },
- { Subject => 'aa', 'CustomField-' . $CF{CF}{obj}->id => 'aa' },
- { Subject => 'bb', 'CustomField-' . $CF{CF}{obj}->id => 'bb' },
-);
-@tickets = RT::Test->create_tickets( { Queue => $queue->id, RandomOrder => 1 }, @data);
-@test = (
- { Query => "CF.{$cf_name} LIKE 'a'", Order => "CF.{$cf_name}" },
- { Query => "CF.{$cf_name} LIKE 'a'", Order => "CF.$queue_name.{$cf_name}" },
-);
-run_tests();
-
-@data = (
- { Subject => '-', },
- { Subject => 'a', CF => 'a' },
- { Subject => 'b', CF => 'b' },
- { Subject => 'c', CF => 'c' },
-);
-@tickets = RT::Test->create_tickets( { Queue => $queue->id, RandomOrder => 1 }, @data);
-@test = (
- { Query => "CF.{$cf_name} != 'c'", Order => "CF.{$cf_name}" },
- { Query => "CF.{$cf_name} != 'c'", Order => "CF.$queue_name.{$cf_name}" },
+run_tests(
+ [
+ { Subject => '-' },
+ { Subject => 'aa', 'CustomField-' . $cf->id => 'aa' },
+ { Subject => 'bb', 'CustomField-' . $cf->id => 'bb' },
+ { Subject => 'cc', 'CustomField-' . $cf->id => 'cc' },
+ ],
+ { Count => 4, Order => "CF.{$cf_name}" },
+ { Count => 4, Order => "CF.$queue_name.{$cf_name}" },
+ { Query => "CF.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.{$cf_name}" },
+ { Query => "CF.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.$queue_name.{$cf_name}" },
+ { Query => "CF.{$cf_name} != 'cc'", Count => 3, Order => "CF.{$cf_name}" },
+ { Query => "CF.{$cf_name} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_name}" },
);
-run_tests();
+my $other_cf;
+my $other_name = "othercf";
diag "create another CF";
{
- $CF{'AnotherCF'}{'name'} = "OrderAnother$$";
- $CF{'AnotherCF'}{'obj'} = RT::CustomField->new( RT->SystemUser );
- my ($ret, $msg) = $CF{'AnotherCF'}{'obj'}->Create(
- Name => $CF{'AnotherCF'}{'name'},
+ $other_cf = RT::CustomField->new( RT->SystemUser );
+ my ($ret, $msg) = $other_cf->Create(
+ Name => $other_name,
Queue => $queue->id,
Type => 'FreeformSingle',
);
- ok($ret, "Custom Field $CF{'AnotherCF'}{'name'} created");
+ ok($ret, "Other Custom Field created");
}
-# test that order is not affect by other fields (had such problem)
-@data = (
- { Subject => '-', },
- { Subject => 'a', CF => 'a', AnotherCF => 'za' },
- { Subject => 'b', CF => 'b', AnotherCF => 'ya' },
- { Subject => 'c', CF => 'c', AnotherCF => 'xa' },
+# Test that order is not affected by other CFs
+run_tests(
+ [
+ { Subject => '-', },
+ { Subject => 'aa', "CustomField-" . $cf->id => 'aa', "CustomField-" . $other_cf->id => 'za' },
+ { Subject => 'bb', "CustomField-" . $cf->id => 'bb', "CustomField-" . $other_cf->id => 'ya' },
+ { Subject => 'cc', "CustomField-" . $cf->id => 'cc', "CustomField-" . $other_cf->id => 'xa' },
+ ],
+ { Count => 4, Order => "CF.{$cf_name}" },
+ { Count => 4, Order => "CF.$queue_name.{$cf_name}" },
+ { Query => "CF.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.{$cf_name}" },
+ { Query => "CF.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.$queue_name.{$cf_name}" },
+ { Query => "CF.{$cf_name} != 'cc'", Count => 3, Order => "CF.{$cf_name}" },
+ { Query => "CF.{$cf_name} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_name}" },
+ { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.{$cf_name}" },
+ { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.$queue_name.{$cf_name}" },
);
-@tickets = RT::Test->create_tickets( { Queue => $queue->id, RandomOrder => 1 }, @data);
-@test = (
- { Order => "CF.{$cf_name}" },
- { Order => "CF.$queue_name.{$cf_name}" },
- { Query => "CF.{$cf_name} != 'c'", Order => "CF.{$cf_name}" },
- { Query => "CF.{$cf_name} != 'c'", Order => "CF.$queue_name.{$cf_name}" },
+
+# And then add a CF with a duplicate name, on a different queue
+{
+ my $other_queue = RT::Test->load_or_create_queue( Name => "other_queue" );
+ ok $other_queue && $other_queue->id, "Created queue";
+
+ my $dup = RT::CustomField->new( RT->SystemUser );
+ my ($ret, $msg) = $dup->Create(
+ Name => $cf_name,
+ Queue => $other_queue->id,
+ Type => 'FreeformSingle',
+ );
+ ok($ret, "Custom Field created");
+}
+
+my $cf_id = $cf->id;
+run_tests(
+ [
+ { Subject => '-', },
+ { Subject => 'aa', "CustomField-" . $cf->id => 'aa', "CustomField-" . $other_cf->id => 'za' },
+ { Subject => 'bb', "CustomField-" . $cf->id => 'bb', "CustomField-" . $other_cf->id => 'ya' },
+ { Subject => 'cc', "CustomField-" . $cf->id => 'cc', "CustomField-" . $other_cf->id => 'xa' },
+ ],
+ { Count => 4, Order => "CF.{$cf_name}" },
+ { Count => 4, Order => "CF.$queue_name.{$cf_name}" },
+ { Query => "CF.{$cf_id} LIKE 'a'", Count => 1, Order => "CF.{$cf_name}" },
+ { Query => "CF.{$cf_id} LIKE 'a'", Count => 1, Order => "CF.$queue_name.{$cf_name}" },
+ { Query => "CF.{$cf_id} != 'cc'", Count => 3, Order => "CF.{$cf_name}" },
+ { Query => "CF.{$cf_id} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_name}" },
+ { Query => "CF.$queue_name.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.{$cf_name}" },
+ { Query => "CF.$queue_name.{$cf_name} LIKE 'a'", Count => 1, Order => "CF.$queue_name.{$cf_name}" },
+ { Query => "CF.$queue_name.{$cf_name} != 'cc'", Count => 3, Order => "CF.{$cf_name}" },
+ { Query => "CF.$queue_name.{$cf_name} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_name}" },
+ { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.{$cf_name}" },
+ { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.$queue_name.{$cf_name}" },
+
+ { Query => "CF.{$cf_id} != 'cc'", Count => 3, Order => "CF.{$cf_id}" },
+ { Query => "CF.{$cf_id} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_id}" },
+ { Query => "CF.$queue_name.{$cf_name} != 'cc'", Count => 3, Order => "CF.{$cf_id}" },
+ { Query => "CF.$queue_name.{$cf_name} != 'cc'", Count => 3, Order => "CF.$queue_name.{$cf_id}" },
+ { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.{$cf_id}" },
+ { Query => "CF.{$other_name} != 'za'", Count => 3, Order => "CF.$queue_name.{$cf_id}" },
);
-run_tests();
-@tickets = ();
+sub run_tests {
+ my $tickets = shift;
+ my @tickets = RT::Test->create_tickets( { Queue => $queue->id, RandomOrder => 1 }, @{ $tickets });
+ my $base_query = join(" OR ", map {"id = ".$_->id} @tickets) || "id > 0";
+
+ my @tests = @_;
+ for my $test ( @tests ) {
+ $test->{'Query'} ||= "id > 0";
+ my $query = "( $base_query ) AND " . $test->{'Query'};
+ for my $order (qw(ASC DESC)) {
+ subtest $test->{'Query'} . " ORDER BY ".$test->{'Order'}. " $order" => sub {
+ my $error = 0;
+ my $tix = RT::Tickets->new( RT->SystemUser );
+ $tix->FromSQL( $query );
+ $tix->OrderBy( FIELD => $test->{'Order'}, ORDER => $order );
+
+ is($tix->Count, $test->{'Count'}, "found right number of tickets (".$test->{Count}.")")
+ or $error = 1;
+
+ my ($order_ok, $last) = (1, $order eq 'ASC'? '-': 'zzzzzz');
+ if ($tix->Count) {
+ my $last_id = $tix->Last->id;
+ while ( my $t = $tix->Next ) {
+ my $tmp;
+ next if $t->id == $last_id and $t->Subject eq "-"; # Nulls are allowed to come last, in Pg
+
+ if ( $order eq 'ASC' ) {
+ $tmp = ((split( /,/, $last))[0] cmp (split( /,/, $t->Subject))[0]);
+ } else {
+ $tmp = -((split( /,/, $last))[-1] cmp (split( /,/, $t->Subject))[-1]);
+ }
+ if ( $tmp > 0 ) {
+ $order_ok = 0; last;
+ }
+ $last = $t->Subject;
+ }
+ }
+
+ ok( $order_ok, "$order order of tickets is good" )
+ or $error = 1;
+
+ if ( $error ) {
+ diag "Wrong SQL query:". $tix->BuildSelectQuery;
+ $tix->GotoFirstItem;
+ while ( my $t = $tix->Next ) {
+ diag sprintf "%02d - %s", $t->id, $t->Subject;
+ }
+ }
+ };
+ }
+ }
+}
+done_testing;
diff --git a/rt/t/ticket/circular_links.t b/rt/t/ticket/circular_links.t
new file mode 100644
index 000000000..b6695feea
--- /dev/null
+++ b/rt/t/ticket/circular_links.t
@@ -0,0 +1,45 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my ( $foo, $bar, $baz ) = RT::Test->create_tickets(
+ { Queue => 'General' },
+ { Subject => 'foo' },
+ { Subject => 'bar' },
+ { Subject => 'baz' }
+);
+
+diag "test circular DependsOn";
+my ( $status, $msg ) = $foo->AddLink( Type => 'DependsOn', Target => $bar->id );
+ok( $status, "foo depends on bar" );
+( $status, $msg ) = $foo->AddLink( Type => 'DependsOn', Base => $bar->id );
+ok( !$status, "foo can't be depended on bar" );
+( $status, $msg ) = $bar->AddLink( Type => 'DependsOn', Target => $foo->id );
+ok( !$status, "bar can't depend on foo back" );
+( $status, $msg ) = $bar->AddLink( Type => 'DependsOn', Target => $baz->id );
+ok( $status, "bar depends on baz" );
+( $status, $msg ) = $baz->AddLink( Type => 'DependsOn', Target => $foo->id );
+ok( !$status, "baz can't depend on foo back" );
+
+
+diag "test circular MemberOf";
+( $status, $msg ) = $foo->AddLink( Type => 'MemberOf', Target => $bar->id );
+ok( $status, "foo is a member of bar" );
+( $status, $msg ) = $foo->AddLink( Type => 'MemberOf', Base => $bar->id );
+ok( !$status, "foo can't have member bar" );
+( $status, $msg ) = $bar->AddLink( Type => 'MemberOf', Target => $foo->id );
+ok( !$status, "bar can't be a member of foo" );
+( $status, $msg ) = $bar->AddLink( Type => 'MemberOf', Target => $baz->id );
+ok( $status, "baz is a member of bar" );
+( $status, $msg ) = $baz->AddLink( Type => 'DependsOn', Target => $foo->id );
+ok( !$status, "baz can't be a member of foo" );
+
+
+diag "test circular RefersTo";
+( $status, $msg ) = $foo->AddLink( Type => 'RefersTo', Target => $bar->id );
+ok( $status, "foo refers to bar" );
+( $status, $msg ) = $foo->AddLink( Type => 'RefersTo', Base => $bar->id );
+ok( $status, "foo can be referred to by bar" );
+
+done_testing;
diff --git a/rt/t/ticket/deferred_owner.t b/rt/t/ticket/deferred_owner.t
index fe90d539d..a0aa350ec 100644
--- a/rt/t/ticket/deferred_owner.t
+++ b/rt/t/ticket/deferred_owner.t
@@ -1,10 +1,7 @@
-
use strict;
use warnings;
-use RT::Test nodata => 1, tests => 18;
-use_ok('RT');
-use_ok('RT::Ticket');
+use RT::Test nodata => 1, tests => undef;
use Test::Warn;
@@ -16,8 +13,7 @@ ok $tester && $tester->id, 'loaded or created user';
my $queue = RT::Test->load_or_create_queue( Name => 'General' );
ok $queue && $queue->id, 'loaded or created queue';
-my $owner_role_group = RT::Group->new( RT->SystemUser );
-$owner_role_group->LoadQueueRoleGroup( Type => 'Owner', Queue => $queue->id );
+my $owner_role_group = $queue->RoleGroup( 'Owner' );
ok $owner_role_group->id, 'loaded owners role group of the queue';
diag "check that deffering owner doesn't regress";
@@ -63,7 +59,7 @@ diag "check that previous trick doesn't work without sufficient rights";
diag $msg if $msg;
ok $tid, "created a ticket";
is $ticket->Owner, $tester->id, 'correct owner';
- unlike $ticket->AdminCcAddresses, qr/root\@localhost/, 'root is there';
+ unlike $ticket->AdminCcAddresses, qr/root\@localhost/, 'root is not there';
}
diag "check that deffering owner really works";
@@ -88,6 +84,9 @@ diag "check that deffering owner really works";
ok $tid, "created a ticket";
like $ticket->CcAddresses, qr/tester\@localhost/, 'tester is in the cc list';
is $ticket->Owner, $tester->id, 'tester is also owner';
+ my $owners = $ticket->OwnerGroup->MembersObj;
+ is $owners->Count, 1, 'one record in owner group';
+ is $owners->First->MemberObj->Id, $tester->id, 'and it is tester';
}
diag "check that deffering doesn't work without correct rights";
@@ -112,8 +111,10 @@ diag "check that deffering doesn't work without correct rights";
diag $msg if $msg;
ok $tid, "created a ticket";
like $ticket->CcAddresses, qr/tester\@localhost/, 'tester is in the cc list';
- isnt $ticket->Owner, $tester->id, 'tester is also owner';
+ is $ticket->Owner, RT->Nobody->id, 'nobody is the owner';
+ my $owners = $ticket->OwnerGroup->MembersObj;
+ is $owners->Count, 1, 'one record in owner group';
+ is $owners->First->MemberObj->Id, RT->Nobody->id, 'and it is nobody';
}
-
-
+done_testing;
diff --git a/rt/t/ticket/linking.t b/rt/t/ticket/linking.t
index 1bd83d633..9eaf93987 100644
--- a/rt/t/ticket/linking.t
+++ b/rt/t/ticket/linking.t
@@ -17,9 +17,6 @@ my $filename = File::Spec->catfile( RT::Test->temp_directory, 'link_count' );
open my $fh, '>', $filename or die $!;
close $fh;
-my $link_scrips_orig = RT->Config->Get( 'LinkTransactionsRun1Scrip' );
-RT->Config->Set( 'LinkTransactionsRun1Scrip', 1 );
-
my $link_acl_checks_orig = RT->Config->Get( 'StrictLinkACL' );
RT->Config->Set( 'StrictLinkACL', 1);
@@ -125,8 +122,8 @@ diag('Create tickets with rights checks on one end of a link');
$child->CurrentUser( RT->SystemUser );
is($child->_Links('Base')->Count, 1, 'link was created');
is($child->_Links('Target')->Count, 0, 'link was created only one');
- # no scrip run on second ticket accroding to config option
- is(link_count($filename), undef, "scrips ok");
+ # only one scrip run (on second ticket) since this is on a ticket Create txn
+ is(link_count($filename), 1, "scrips ok");
RT->Config->Set( StrictLinkACL => 1 );
}
@@ -144,7 +141,7 @@ diag('try to add link without rights');
ok($id,$msg);
($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id);
ok(!$id, $msg);
- is(link_count($filename), undef, "scrips ok");
+ is(link_count($filename), 0, "scrips ok");
$child->CurrentUser( RT->SystemUser );
is($child->_Links('Base')->Count, 0, 'link was not created, no permissions');
is($child->_Links('Target')->Count, 0, 'link was not create, no permissions');
@@ -162,7 +159,7 @@ diag('add link with rights only on base');
ok($id,$msg);
($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id);
ok($id, $msg);
- is(link_count($filename), 1, "scrips ok");
+ is(link_count($filename), 2, "scrips ok");
$child->CurrentUser( RT->SystemUser );
is($child->_Links('Base')->Count, 1, 'link was created');
is($child->_Links('Target')->Count, 0, 'link was created only one');
@@ -170,9 +167,9 @@ diag('add link with rights only on base');
# turn off feature and try to delete link, we should fail
RT->Config->Set( StrictLinkACL => 1 );
- ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id);
+ ($id, $msg) = $child->DeleteLink(Type => 'MemberOf', Target => $parent->id);
ok(!$id, $msg);
- is(link_count($filename), 1, "scrips ok");
+ is(link_count($filename), 0, "scrips ok");
$child->CurrentUser( RT->SystemUser );
$child->_Links('Base')->_DoCount;
is($child->_Links('Base')->Count, 1, 'link was not deleted');
@@ -182,7 +179,7 @@ diag('add link with rights only on base');
RT->Config->Set( StrictLinkACL => 0 );
($id, $msg) = $child->DeleteLink(Type => 'MemberOf', Target => $parent->id);
ok($id, $msg);
- is(link_count($filename), 0, "scrips ok");
+ is(link_count($filename), -2, "scrips ok");
$child->CurrentUser( RT->SystemUser );
$child->_Links('Base')->_DoCount;
is($child->_Links('Base')->Count, 0, 'link was deleted');
@@ -217,18 +214,17 @@ ok ($id,$msg);
ok ($id,$msg);
($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
ok($id,$msg);
-is(link_count($filename), 1, "scrips ok");
+is(link_count($filename), 2, "scrips ok");
warnings_like {
($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => -1);
} [
qr/Could not determine a URI scheme for -1/,
- qr/Couldn't resolve '-1' into a URI/,
];
($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
ok($id,$msg);
-is(link_count($filename), 1, "scrips ok");
+is(link_count($filename), 0, "scrips ok"); # already added
my $transactions = $ticket2->Transactions;
$transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' );
@@ -238,21 +234,19 @@ is( $transactions->First->NewValue , $ticket->URI );
($id,$msg) = $ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id);
ok($id,$msg);
-is(link_count($filename), 0, "scrips ok");
+is(link_count($filename), -2, "scrips ok");
$transactions = $ticket2->Transactions;
$transactions->Limit( FIELD => 'Type', VALUE => 'DeleteLink' );
is( $transactions->Count, 1, "Transaction found in other ticket" );
is( $transactions->First->Field , 'ReferredToBy');
is( $transactions->First->OldValue , $ticket->URI );
-RT->Config->Set( LinkTransactionsRun1Scrip => 0 );
-
($id,$msg) =$ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
ok($id,$msg);
is(link_count($filename), 2, "scrips ok");
($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id);
ok($id,$msg);
-is(link_count($filename), 0, "scrips ok");
+is(link_count($filename), -2, "scrips ok");
# tests for silent behaviour
($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id, Silent => 1);
@@ -287,7 +281,7 @@ is(link_count($filename), 1, "scrips ok");
}
($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id, SilentBase => 1);
ok($id,$msg);
-is(link_count($filename), 0, "scrips ok");
+is(link_count($filename), -1, "scrips ok");
($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id, SilentTarget => 1);
ok($id,$msg);
@@ -303,11 +297,10 @@ is(link_count($filename), 1, "scrips ok");
}
($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id, SilentTarget => 1);
ok($id,$msg);
-is(link_count($filename), 0, "scrips ok");
+is(link_count($filename), -1, "scrips ok");
# restore
-RT->Config->Set( LinkTransactionsRun1Scrip => $link_scrips_orig );
RT->Config->Set( StrictLinkACL => $link_acl_checks_orig );
{
@@ -388,8 +381,9 @@ sub link_count {
open( my $fh, '<', $file ) or die "couldn't open $file";
my $data = <$fh>;
close $fh;
+ truncate($file, 0);
- return undef unless $data;
+ return 0 unless defined $data;
chomp $data;
return $data + 0;
}
diff --git a/rt/t/ticket/merge.t b/rt/t/ticket/merge.t
index 2484b6512..99c723b8a 100644
--- a/rt/t/ticket/merge.t
+++ b/rt/t/ticket/merge.t
@@ -4,7 +4,7 @@ use warnings;
use RT;
-use RT::Test tests => '29';
+use RT::Test tests => '44';
# validate that when merging two tickets, the comments from both tickets
@@ -134,3 +134,46 @@ ok $user && $user->id, 'loaded or created user';
($status,$msg) = $t->MergeInto($t2->id);
ok($status, "Merged tickets: $msg");
}
+
+# check Time* fields after merge
+{
+ my @tickets;
+ my @values = (
+ { Worked => 11, Estimated => 17, Left => 6 },
+ { Worked => 7, Estimated => 12, Left => 5 },
+ );
+
+ for my $i (0 .. 1) {
+ my $t = RT::Ticket->new(RT->SystemUser);
+ $t->Create( Queue => 'general');
+ ok ($t->id);
+ push @tickets, $t;
+
+ foreach my $field ( keys %{ $values[ $i ] } ) {
+ my $method = "SetTime$field";
+ my ($status, $msg) = $t->$method( $values[ $i ]{ $field } );
+ ok $status, "changed $field on the ticket"
+ or diag "error: $msg";
+ }
+ }
+
+ my ($status, $msg) = $tickets[1]->MergeInto($tickets[0]->id);
+ ok($status,$msg);
+
+ my $t = RT::Ticket->new(RT->SystemUser);
+ $t->Load( $tickets[0]->id );
+ foreach my $field ( keys %{ $values[0] } ) {
+ my $method = "Time$field";
+ my $expected = 0;
+ $expected += $_->{ $field } foreach @values;
+ is $t->$method, $expected, "correct value";
+
+ my $from_history = 0;
+ my $txns = $t->Transactions;
+ while ( my $txn = $txns->Next ) {
+ next unless $txn->Type eq 'Set' && $txn->Field eq $method;
+ $from_history += $txn->NewValue - $txn->OldValue;
+ }
+ is $from_history, $expected, "history is correct";
+ }
+}
diff --git a/rt/t/ticket/scrips_batch.t b/rt/t/ticket/scrips_batch.t
index 44d7f8e34..0a996ce45 100644
--- a/rt/t/ticket/scrips_batch.t
+++ b/rt/t/ticket/scrips_batch.t
@@ -16,28 +16,33 @@ ok $m->login, 'logged in as root';
my $sid;
{
- $m->follow_link_ok( { id => 'tools-config-queues' } );
+ $m->follow_link_ok( { id => 'admin-queues' } );
$m->follow_link_ok( { text => $queue->Name } );
$m->follow_link_ok( { id => 'page-scrips-create'});
- $m->form_name('ModifyScrip');
- $m->field('Scrip-new-Description' => 'test');
- $m->select('Scrip-new-ScripCondition' => 'On Transaction');
- $m->select('Scrip-new-ScripAction' => 'User Defined');
- $m->select('Scrip-new-Template' => 'Global template: Blank');
- $m->select('Scrip-new-Stage' => 'TransactionBatch');
- $m->field('Scrip-new-CustomPrepareCode' => 'return 1;');
- $m->field('Scrip-new-CustomCommitCode' => 'return 1;');
- $m->submit;
- $m->content_contains("Scrip Created");
+ $m->form_name('CreateScrip');
+ $m->field('Description' => 'test');
+ $m->select('ScripCondition' => 'On Transaction');
+ $m->select('ScripAction' => 'User Defined');
+ $m->select('Template' => 'Blank');
+ $m->select('Stage' => 'Batch');
+ $m->field('CustomPrepareCode' => 'return 1;');
+ $m->field('CustomCommitCode' => 'return 1;');
+ $m->click('Create');
+ $m->content_contains("Scrip Created");
my $form = $m->form_name('ModifyScrip');
$sid = $form->value('id');
- is $m->value("Scrip-$sid-Description"), 'test', 'correct description';
- is value_name($form, "Scrip-$sid-ScripCondition"), 'On Transaction', 'correct condition';
- is value_name($form, "Scrip-$sid-ScripAction"), 'User Defined', 'correct action';
- is value_name($form, "Scrip-$sid-Template"), 'Global template: Blank', 'correct template';
- is value_name($form, "Scrip-$sid-Stage"), 'TransactionBatch', 'correct stage';
+ is $m->value("Description"), 'test', 'correct description';
+ is value_name($form, "ScripCondition"), 'On Transaction', 'correct condition';
+ is value_name($form, "ScripAction"), 'User Defined', 'correct action';
+ is value_name($form, "Template"), 'Blank', 'correct template';
+
+ {
+ my $rec = RT::ObjectScrip->new( RT->SystemUser );
+ $rec->LoadByCols( Scrip => $sid, ObjectId => $queue->id );
+ is $rec->Stage, 'TransactionBatch', "correct stage";
+ }
my $tmp_fn = File::Spec->catfile( RT::Test->temp_directory, 'transactions' );
open my $tmp_fh, '+>', $tmp_fn or die $!;
@@ -56,8 +61,8 @@ foreach my \$txn ( \@\$batch ) {
return 1;
END
- $m->field( "Scrip-$sid-CustomCommitCode" => $code );
- $m->submit;
+ $m->field( "CustomCommitCode" => $code );
+ $m->click('Update');
$m->goto_create_ticket( $queue );
$m->form_name('TicketCreate');
diff --git a/rt/t/ticket/search.t b/rt/t/ticket/search.t
index 852241fe5..a43433f06 100644
--- a/rt/t/ticket/search.t
+++ b/rt/t/ticket/search.t
@@ -283,4 +283,35 @@ like($tix->BuildSelectCountQuery, qr/\bNULL\b/, "Contains upper-case NULL");
unlike($tix->BuildSelectCountQuery, qr/\bnull\b/, "Lacks lower-case NULL");
+# tests for searching by queue lifecycle
+$tix = RT::Tickets->new(RT->SystemUser);
+$tix->FromSQL('Lifecycle="default"');
+is($tix->Count,7,"We found all 7 tickets in a queue with the default lifecycle");
+
+$tix = RT::Tickets->new(RT->SystemUser);
+$tix->FromSQL('Lifecycle ="approvals" OR Lifecycle="default"');
+is($tix->Count,7,"We found 7 tickets in a queue with a lifecycle of default or approvals");
+
+$tix = RT::Tickets->new(RT->SystemUser);
+$tix->FromSQL('Lifecycle ="approvals" AND Lifecycle="default"');
+is($tix->Count,0,"We found 0 tickets in a queue with a lifecycle of default AND approvals...(because that's impossible");
+
+$tix = RT::Tickets->new(RT->SystemUser);
+$tix->FromSQL('Queue="'.$queue.'" AND Lifecycle="default"');
+is($tix->Count,7,"We found 7 tickets in $queue with a lifecycle of default");
+
+
+$tix = RT::Tickets->new(RT->SystemUser);
+$tix->FromSQL('Lifecycle !="approvals"');
+is($tix->Count,7,"We found 7 tickets in a queue with a lifecycle other than approvals");
+
+$tix = RT::Tickets->new(RT->SystemUser);
+$tix->FromSQL('Lifecycle!="default"');
+is($tix->Count,0,"We found 0 tickets in a queue with a lifecycle other than default");
+
+$tix = RT::Tickets->new(RT->SystemUser);
+$tix->FromSQL('Lifecycle="approvals"');
+is($tix->Count,0,"We found 0 tickets in a queue with the approvals lifecycle");
+
+
done_testing;
diff --git a/rt/t/ticket/search_by_cf_freeform_multiple.t b/rt/t/ticket/search_by_cf_freeform_multiple.t
index 1b4f0922c..1324abd66 100644
--- a/rt/t/ticket/search_by_cf_freeform_multiple.t
+++ b/rt/t/ticket/search_by_cf_freeform_multiple.t
@@ -2,8 +2,7 @@
use strict;
use warnings;
-use RT::Test nodata => 1, tests => 118;
-use RT::Ticket;
+use RT::Test nodata => 1, tests => undef;
my $q = RT::Test->load_or_create_queue( Name => 'Regression' );
ok $q && $q->id, 'loaded or created queue';
@@ -22,104 +21,136 @@ my ($cf_name, $cf_id, $cf) = ("Test", 0, undef);
$cf_id = $cf->id;
}
-my ($total, @data, @tickets, %test) = (0, ());
+my $ylong = "y" x 300;
+subtest "Creating tickets" => sub {
+ RT::Test->create_tickets( { Queue => $q->id },
+ { Subject => '-' },
+ { Subject => 'x', "CustomField-$cf_id" => 'x', },
+ { Subject => 'y', "CustomField-$cf_id" => 'y', },
+ { Subject => 'z', "CustomField-$cf_id" => 'z', },
+ { Subject => 'xy', "CustomField-$cf_id" => [ 'x', 'y' ], },
+ { Subject => 'xz', "CustomField-$cf_id" => [ 'x', 'z' ], },
+ { Subject => 'yz', "CustomField-$cf_id" => [ 'y', 'z' ], },
+ { Subject => 'x_ylong', "CustomField-$cf_id" => [ 'x', $ylong ], },
+ { Subject => 'ylong', "CustomField-$cf_id" => $ylong, },
+ );
+};
+
+my @tests = (
+ "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id}.Content IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent IS NULL" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.{$cf_name}.Content' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "'CF.{$cf_name}.LargeContent' IS NULL" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+
+ "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.Content IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "CF.{$cf_id}.LargeContent IS NOT NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.{$cf_name}.Content' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}.LargeContent' IS NOT NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+
+ "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "CF.{$cf_id}.Content = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "CF.{$cf_id}.LargeContent = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id} = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.Content = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id}.LargeContent = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id} LIKE 'yyy'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.Content LIKE 'yyy'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id}.LargeContent LIKE 'yyy'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+
+ "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+ "CF.{$cf_id}.Content != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+ "CF.{$cf_id}.LargeContent != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id} != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id}.Content != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 },
+"TODO: CF.{$cf_id} NOT LIKE 'yyy'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id}.Content NOT LIKE 'yyy'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent NOT LIKE 'yyy'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 },
+ "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+ "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+ "'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+ "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+
+ "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 },
+ "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 },
+ "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 },
+ "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+);
+run_tests(@tests);
+
sub run_tests {
- my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets;
- foreach my $key ( sort keys %test ) {
- my $tix = RT::Tickets->new(RT->SystemUser);
- $tix->FromSQL( "( $query_prefix ) AND ( $key )" );
-
- my $error = 0;
-
- my $count = 0;
- $count++ foreach grep $_, values %{ $test{$key} };
- is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1;
-
- my $good_tickets = ($tix->Count == $count);
- while ( my $ticket = $tix->Next ) {
- next if $test{$key}->{ $ticket->Subject };
- diag $ticket->Subject ." ticket has been found when it's not expected";
- $good_tickets = 0;
- }
- ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1;
-
- diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error;
+ my @tests = @_;
+ while (@tests) {
+ my $query = shift @tests;
+ my %results = %{ shift @tests };
+ local $TODO = "Not implemented correctly" if $query =~ s/^TODO:\s*//;
+ subtest $query => sub {
+ my $tix = RT::Tickets->new(RT->SystemUser);
+ $tix->FromSQL( "$query" );
+
+ my $error = 0;
+
+ my $count = 0;
+ $count++ foreach grep $_, values %results;
+ is($tix->Count, $count, "found correct number of ticket(s)") or $error = 1;
+
+ my $good_tickets = ($tix->Count == $count);
+ while ( my $ticket = $tix->Next ) {
+ next if $results{ $ticket->Subject };
+ diag $ticket->Subject ." ticket has been found when it's not expected";
+ $good_tickets = 0;
+ }
+ ok( $good_tickets, "all tickets are good" ) or $error = 1;
+
+ diag "Wrong SQL: ". $tix->BuildSelectQuery if $error;
+ };
}
}
-@data = (
- { Subject => '-' },
- { Subject => 'x', "CustomField-$cf_id" => 'x', },
- { Subject => 'y', "CustomField-$cf_id" => 'y', },
- { Subject => 'z', "CustomField-$cf_id" => 'z', },
- { Subject => 'xy', "CustomField-$cf_id" => [ 'x', 'y' ], },
- { Subject => 'xz', "CustomField-$cf_id" => [ 'x', 'z' ], },
- { Subject => 'yz', "CustomField-$cf_id" => [ 'y', 'z' ], },
-);
-%test = (
- "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
-
- "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
-
- "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
-
- "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
- "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
- "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
- "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 },
-
- "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 },
- "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
-);
-@tickets = RT::Test->create_tickets( { Queue => $q->id }, @data);
-$total = scalar @tickets;
-
-{
- my $tix = RT::Tickets->new(RT->SystemUser);
- $tix->FromSQL("Queue = '$queue'");
- is($tix->Count, $total, "found $total tickets");
-}
-run_tests();
+done_testing;
diff --git a/rt/t/ticket/search_by_cf_freeform_single.t b/rt/t/ticket/search_by_cf_freeform_single.t
index f8462a9af..0463412ca 100644
--- a/rt/t/ticket/search_by_cf_freeform_single.t
+++ b/rt/t/ticket/search_by_cf_freeform_single.t
@@ -2,8 +2,7 @@
use strict;
use warnings;
-use RT::Test nodata => 1, tests => 106;
-use RT::Ticket;
+use RT::Test nodata => 1, tests => undef;
my $q = RT::Test->load_or_create_queue( Name => 'Regression' );
ok $q && $q->id, 'loaded or created queue';
@@ -22,103 +21,143 @@ my ($cf_name, $cf_id, $cf) = ("Test", 0, undef);
$cf_id = $cf->id;
}
-my ($total, @data, @tickets, %test) = (0, ());
+my $other_q = RT::Test->load_or_create_queue( Name => 'Other' );
+ok $other_q && $other_q->id, 'loaded or created queue';
+
+my $ylong = 'y' x 300;
+subtest "Creating tickets" => sub {
+ RT::Test->create_tickets( { Queue => $q->id },
+ { Subject => '-' },
+ { Subject => "other", Queue => $other_q->id },
+ { Subject => 'x', "CustomField-$cf_id" => 'x', },
+ { Subject => 'y', "CustomField-$cf_id" => 'y', },
+ { Subject => 'z', "CustomField-$cf_id" => 'z', },
+ { Subject => 'ylong', "CustomField-$cf_id" => $ylong, },
+ );
+};
+
+my @tests = (
+ "CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id}.Content IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 1 },
+ "CF.{$cf_id}.LargeContent IS NULL" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}.Content' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 1 },
+ "'CF.{$cf_name}.LargeContent' IS NULL" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id}.Content IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 0 },
+ "CF.{$cf_id}.LargeContent IS NOT NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.{$cf_name}.Content' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 0 },
+ "'CF.{$cf_name}.LargeContent' IS NOT NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+
+ "CF.{$cf_id} = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id}.Content = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id}.LargeContent = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id} = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "CF.{$cf_id}.Content = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id}.LargeContent = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "CF.{$cf_id} LIKE 'yyy'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "CF.{$cf_id}.Content LIKE 'yyy'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id}.LargeContent LIKE 'yyy'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "'CF.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id}.Content != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id} != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "CF.{$cf_id}.Content != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "CF.{$cf_id} NOT LIKE 'yyy'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "CF.{$cf_id}.Content NOT LIKE 'yyy'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent NOT LIKE 'yyy'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "'CF.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 },
+ "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+);
+run_tests(@tests);
+
sub run_tests {
- my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets;
- foreach my $key ( sort keys %test ) {
- my $tix = RT::Tickets->new(RT->SystemUser);
- $tix->FromSQL( "( $query_prefix ) AND ( $key )" );
-
- my $error = 0;
-
- my $count = 0;
- $count++ foreach grep $_, values %{ $test{$key} };
- is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1;
-
- my $good_tickets = ($tix->Count == $count);
- while ( my $ticket = $tix->Next ) {
- next if $test{$key}->{ $ticket->Subject };
- diag $ticket->Subject ." ticket has been found when it's not expected";
- $good_tickets = 0;
- }
- ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1;
-
- diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error;
+ my @tests = @_;
+ while (@tests) {
+ my $query = shift @tests;
+ my %results = %{ shift @tests };
+ subtest $query => sub {
+ my $tix = RT::Tickets->new(RT->SystemUser);
+ $tix->FromSQL( "$query" );
+
+ my $error = 0;
+
+ my $count = 0;
+ $count++ foreach grep $_, values %results;
+ is($tix->Count, $count, "found correct number of ticket(s)") or $error = 1;
+
+ my $good_tickets = ($tix->Count == $count);
+ while ( my $ticket = $tix->Next ) {
+ next if $results{ $ticket->Subject };
+ diag $ticket->Subject ." ticket has been found when it's not expected";
+ $good_tickets = 0;
+ }
+ ok( $good_tickets, "all tickets are good" ) or $error = 1;
+
+ diag "Wrong SQL: ". $tix->BuildSelectQuery if $error;
+ };
}
}
-@data = (
- { Subject => '-' },
- { Subject => 'x', "CustomField-$cf_id" => 'x', },
- { Subject => 'y', "CustomField-$cf_id" => 'y', },
- { Subject => 'z', "CustomField-$cf_id" => 'z', },
-);
-%test = (
- "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
-
- "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
-
- "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
- "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 },
- "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 },
- "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 },
- "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
-
-);
-@tickets = RT::Test->create_tickets( { Queue => $q->id }, @data);
-$total = scalar @tickets;
-{
- my $tix = RT::Tickets->new(RT->SystemUser);
- $tix->FromSQL("Queue = '$queue'");
- is($tix->Count, $total, "found $total tickets");
-}
-run_tests();
-
-@tickets = ();
+done_testing;
diff --git a/rt/t/ticket/search_by_watcher_group.t b/rt/t/ticket/search_by_watcher_group.t
new file mode 100644
index 000000000..9c522d5d0
--- /dev/null
+++ b/rt/t/ticket/search_by_watcher_group.t
@@ -0,0 +1,75 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+use Test::Warn;
+
+my $q = RT::Test->load_or_create_queue( Name => 'General' );
+ok $q && $q->id, 'loaded or created queue';
+my $queue = $q->Name;
+
+
+my $group;
+{
+ $group = RT::Group->new( RT->SystemUser );
+ my ($id, $msg) = $group->CreateUserDefinedGroup( Name => 'Test' );
+ ok $id, "$msg";
+}
+
+my $root = RT::Test->load_or_create_user( Name => 'root', MemberOf => $group->id );
+ok $root && $root->id;
+
+RT::Test->create_tickets(
+ { Queue => $q, },
+ { Subject => '-', },
+ { Subject => 'o', Owner => $root->id },
+ { Subject => 'r', Requestor => $root->id },
+ { Subject => 'c', Cc => $root->id },
+ { Subject => 'a', AdminCc => $root->id },
+);
+
+run_tests(
+ 'OwnerGroup = "Test"' => { '-' => 0, o => 1, r => 0, c => 0, a => 0 },
+ 'RequestorGroup = "Test"' => { '-' => 0, o => 0, r => 1, c => 0, a => 0 },
+ 'CCGroup = "Test"' => { '-' => 0, o => 0, r => 0, c => 1, a => 0 },
+ 'AdminCCGroup = "Test"' => { '-' => 0, o => 0, r => 0, c => 0, a => 1 },
+ 'WatcherGroup = "Test"' => { '-' => 0, o => 1, r => 1, c => 1, a => 1 },
+);
+
+warning_like {
+ my $tickets = RT::Tickets->new( RT->SystemUser );
+ my ($status, $msg) = $tickets->FromSQL('OwnerGroup != "Test"');
+ ok !$status, "incorrect op: $msg";
+} qr{Invalid OwnerGroup Op: !=};
+
+done_testing();
+
+sub run_tests {
+ my @test = @_;
+ while ( my ($query, $checks) = splice @test, 0, 2 ) {
+ run_test( $query, %$checks );
+ }
+}
+
+sub run_test {
+ my ($query, %checks) = @_;
+
+ my $tix = RT::Tickets->new(RT->SystemUser);
+ $tix->FromSQL($query);
+ my $error = 0;
+
+ my $count = 0;
+ $count++ foreach grep $_, values %checks;
+ is($tix->Count, $count, "found correct number of ticket(s) by '$query'") or $error = 1;
+
+ my $good_tickets = ($tix->Count == $count);
+ while ( my $ticket = $tix->Next ) {
+ next if $checks{ $ticket->Subject };
+ diag $ticket->Subject ." ticket has been found when it's not expected";
+ $good_tickets = 0;
+ }
+ ok( $good_tickets, "all tickets are good with '$query'" ) or $error = 1;
+
+ diag "Wrong SQL query for '$query':". $tix->BuildSelectQuery if $error;
+}
diff --git a/rt/t/ticket/googleish_search.t b/rt/t/ticket/simple_search.t
index d372801bc..02bf7334d 100644
--- a/rt/t/ticket/googleish_search.t
+++ b/rt/t/ticket/simple_search.t
@@ -20,12 +20,12 @@ my ( $id, undef, $msg ) = $t1->Create(
);
ok( $id, $msg );
-use_ok("RT::Search::Googleish");
+use_ok("RT::Search::Simple");
my $active_statuses = join( " OR ", map "Status = '$_'", RT::Queue->ActiveStatusArray());
my $tickets = RT::Tickets->new(RT->SystemUser);
-my $quick = RT::Search::Googleish->new(Argument => "",
+my $quick = RT::Search::Simple->new(Argument => "",
TicketsObj => $tickets);
my @tests = (
"General new open root" => "( Owner = 'root' ) AND ( Queue = 'General' ) AND ( Status = 'new' OR Status = 'open' )",
@@ -34,8 +34,8 @@ my @tests = (
"fulltext:jesse" => "( Content LIKE 'jesse' ) AND ( $active_statuses )",
$queue => "( Queue = '$queue' ) AND ( $active_statuses )",
"root $queue" => "( Owner = 'root' ) AND ( Queue = '$queue' ) AND ( $active_statuses )",
- "notauser $queue" => "( Queue = '$queue' ) AND ( $active_statuses ) AND ( Subject LIKE 'notauser' )",
- "notauser $queue root" => "( Owner = 'root' ) AND ( Queue = '$queue' ) AND ( $active_statuses ) AND ( Subject LIKE 'notauser' )");
+ "notauser $queue" => "( Subject LIKE 'notauser' ) AND ( Queue = '$queue' ) AND ( $active_statuses )",
+ "notauser $queue root" => "( Subject LIKE 'notauser' ) AND ( Owner = 'root' ) AND ( Queue = '$queue' ) AND ( $active_statuses )");
while (my ($from, $to) = splice @tests, 0, 2) {
is($quick->QueryToSQL($from), $to, "<$from> -> <$to>");
diff --git a/rt/t/ticket/time-worked.t b/rt/t/ticket/time-worked.t
new file mode 100644
index 000000000..3e8724221
--- /dev/null
+++ b/rt/t/ticket/time-worked.t
@@ -0,0 +1,80 @@
+use strict;
+use warnings;
+
+use RT::Test tests => 27;
+
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+ok $queue && $queue->id, "loaded or created a queue";
+
+note 'set on Create';
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue->id, TimeWorked => 10,
+ );
+ is $ticket->TimeWorked, 10, 'correct value';
+
+ my $txn = RT::Transaction->new( RT->SystemUser );
+ $txn->LoadByCols(
+ ObjectType => 'RT::Ticket', ObjectId => $ticket->id,
+ Type => 'Create',
+ );
+ ok $txn->id, 'found transaction';
+ is $txn->TimeTaken, 10, 'correct value';
+}
+
+note 'set on Comment';
+{
+ my $ticket = RT::Test->create_ticket( Queue => $queue->id );
+ ok !$ticket->TimeWorked, 'correct value';
+ $ticket->Comment( Content => 'test', TimeTaken => 10 );
+ is $ticket->TimeWorked, 10, 'correct value';
+
+ my $txn = RT::Transaction->new( RT->SystemUser );
+ $txn->LoadByCols(
+ ObjectType => 'RT::Ticket', ObjectId => $ticket->id,
+ Type => 'Comment',
+ );
+ ok $txn->id, 'found transaction';
+ is $txn->TimeTaken, 10, 'correct value';
+}
+
+note 'update';
+{
+ my $ticket = RT::Test->create_ticket( Queue => $queue->id );
+ ok !$ticket->TimeWorked, 'correct value';
+ $ticket->SetTimeWorked( 10 );
+ is $ticket->TimeWorked, 10, 'correct value';
+
+ my $txn = RT::Transaction->new( RT->SystemUser );
+ $txn->LoadByCols(
+ ObjectType => 'RT::Ticket', ObjectId => $ticket->id,
+ Type => 'Set', Field => 'TimeWorked',
+ );
+ ok $txn->id, 'found transaction';
+ is $txn->TimeTaken, 10, 'correct value';
+}
+
+note 'on Merge';
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue->id, TimeWorked => 7,
+ );
+ {
+ my $tmp = RT::Test->create_ticket(
+ Queue => $queue->id, TimeWorked => 13,
+ );
+ my ($status, $msg) = $tmp->MergeInto( $ticket->id );
+ ok $status, "merged tickets";
+ }
+ $ticket->Load( $ticket->id );
+ is $ticket->TimeWorked, 20, 'correct value';
+}
+
+sub dump_txns {
+ my $ticket = shift;
+ my $txns = $ticket->Transactions;
+ while ( my $txn = $txns->Next ) {
+ diag sprintf "#%d\t%s\t%s\t%d", map $txn->$_() // '', qw(id Type Field TimeTaken);
+ }
+}
+
diff --git a/rt/t/validator/group_members.t b/rt/t/validator/group_members.t
index af93c518e..0fd1a749a 100644
--- a/rt/t/validator/group_members.t
+++ b/rt/t/validator/group_members.t
@@ -2,19 +2,15 @@
use strict;
use warnings;
-use RT::Test tests => 63;
+use RT::Test tests => undef;
-{
- my ($ecode, $res) = RT::Test->run_validator();
- is $res, '', 'empty result';
-}
+RT::Test->db_is_valid;
{
my $group = RT::Test->load_or_create_group('test', Members => [] );
ok $group, "loaded or created a group";
- my ($ecode, $res) = RT::Test->run_validator();
- is $res, '', 'empty result';
+ RT::Test->db_is_valid;
}
# G1 -> G2
@@ -28,20 +24,19 @@ use RT::Test tests => 63;
ok $group2->HasMember( $group1->id ), "has member";
ok $group2->HasMemberRecursively( $group1->id ), "has member";
- my ($ecode, $res) = RT::Test->run_validator();
- is $res, '', 'empty result';
+ RT::Test->db_is_valid;
$RT::Handle->dbh->do("DELETE FROM CachedGroupMembers");
DBIx::SearchBuilder::Record::Cachable->FlushCache;
ok !$group2->HasMemberRecursively( $group1->id ), "has no member, broken DB";
- ($ecode, $res) = RT::Test->run_validator(resolve => 1);
+ my ($ecode, $res) = RT::Test->run_validator(resolve => 1);
+ isnt($ecode, 0, 'non-zero exit code');
ok $group2->HasMember( $group1->id ), "has member";
ok $group2->HasMemberRecursively( $group1->id ), "has member";
- ($ecode, $res) = RT::Test->run_validator();
- is $res, '', 'empty result';
+ RT::Test->db_is_valid;
}
# G1 <- G2 <- G3 <- G4 <- G5
@@ -61,15 +56,15 @@ use RT::Test tests => 63;
push @groups, $group;
}
- my ($ecode, $res) = RT::Test->run_validator();
- is $res, '', 'empty result';
+ RT::Test->db_is_valid;
$RT::Handle->dbh->do("DELETE FROM CachedGroupMembers");
DBIx::SearchBuilder::Record::Cachable->FlushCache;
ok !$groups[1]->HasMemberRecursively( $groups[0]->id ), "has no member, broken DB";
- ($ecode, $res) = RT::Test->run_validator(resolve => 1);
+ my ($ecode, $res) = RT::Test->run_validator(resolve => 1);
+ isnt($ecode, 0, 'non-zero exit code');
for ( my $i = 1; $i < @groups; $i++ ) {
ok $groups[$i]->HasMember( $groups[$i-1]->id ), "has member";
@@ -77,8 +72,7 @@ use RT::Test tests => 63;
foreach 0..$i-1;
}
- ($ecode, $res) = RT::Test->run_validator();
- is $res, '', 'empty result';
+ RT::Test->db_is_valid;
}
# G1 <- (G2, G3, G4, G5)
@@ -93,8 +87,7 @@ use RT::Test tests => 63;
my $parent = RT::Test->load_or_create_group( 'test1', Members => \@groups );
ok $parent, "loaded or created a group";
- my ($ecode, $res) = RT::Test->run_validator();
- is $res, '', 'empty result';
+ RT::Test->db_is_valid;
}
# G1 <- (G2, G3, G4) <- G5
@@ -112,8 +105,7 @@ use RT::Test tests => 63;
my $parent = RT::Test->load_or_create_group( 'test1', Members => \@groups );
ok $parent, "loaded or created a group";
- my ($ecode, $res) = RT::Test->run_validator();
- is $res, '', 'empty result';
+ RT::Test->db_is_valid;
}
# group without principal record and cgm records
@@ -128,8 +120,9 @@ use RT::Test tests => 63;
DBIx::SearchBuilder::Record::Cachable->FlushCache;
my ($ecode, $res) = RT::Test->run_validator(resolve => 1, timeout => 30);
- ok $res;
+ isnt($ecode, 0, 'non-zero exit code');
- ($ecode, $res) = RT::Test->run_validator();
- is $res, '', 'empty result';
+ RT::Test->db_is_valid;
}
+
+done_testing;
diff --git a/rt/t/web/admin_queue_lifecycle.t b/rt/t/web/admin_queue_lifecycle.t
index 295e9ea57..6b8401283 100644
--- a/rt/t/web/admin_queue_lifecycle.t
+++ b/rt/t/web/admin_queue_lifecycle.t
@@ -24,7 +24,7 @@ my $lifecycle_input = $form->find_input('Lifecycle');
is( $lifecycle_input->value, 'default', 'default lifecycle' );
my @lifecycles = sort $lifecycle_input->possible_values;
-is_deeply( \@lifecycles, [qw/approvals default foo/], 'found all lifecycles' );
+is_deeply( \@lifecycles, [qw/default foo/], 'found all lifecycles' );
$m->submit_form();
$m->content_lacks( 'Lifecycle changed from',
diff --git a/rt/t/web/admin_user.t b/rt/t/web/admin_user.t
index 36b9af1b4..dc984eb75 100644
--- a/rt/t/web/admin_user.t
+++ b/rt/t/web/admin_user.t
@@ -27,9 +27,9 @@ diag "test the history page" if $ENV{TEST_VERBOSE};
$m->get_ok( $url . '/Admin/Users/History.html?id=' . $root->id );
$m->content_contains('User created', 'has User created entry');
-diag "test gnupg page" if $ENV{TEST_VERBOSE};
-$m->follow_link_ok( { text => 'GnuPG' } );
-$m->content_contains('GnuPG public key');
+diag "test keys page" if $ENV{TEST_VERBOSE};
+$m->follow_link_ok( { text => 'Private keys' } );
+$m->content_contains('Public key&#40;s&#41; for rt-test@example.com');
$m->content_contains('The key is ultimately trusted');
$m->content_contains('F0CB3B482CFA485680A4A0BDD328035D84881F1B');
$m->content_contains('Tue Aug 07 2007');
diff --git a/rt/t/web/articles-links.t b/rt/t/web/articles-links.t
index eb6de51b3..4aa8f9133 100644
--- a/rt/t/web/articles-links.t
+++ b/rt/t/web/articles-links.t
@@ -44,7 +44,7 @@ $m->click('SubmitTicket');
$m->follow_link_ok({text => 'Links'});
-$m->text_contains('Article ' . $article->id . ': instance of ticket #17421', 'Article appears with its name in the links table');
+$m->text_contains('Article #' . $article->id . ': instance of ticket #17421', 'Article appears with its name in the links table');
my $refers_to = $ticket->RefersTo;
is($refers_to->Count, 1, 'the ticket has a refers-to link');
diff --git a/rt/t/web/attachment_dropping.t b/rt/t/web/attachment_dropping.t
new file mode 100644
index 000000000..466f7a0f0
--- /dev/null
+++ b/rt/t/web/attachment_dropping.t
@@ -0,0 +1,52 @@
+use warnings;
+use strict;
+
+use RT::Test tests => undef;
+use File::Temp 'tempfile';
+
+my $content = 'a' x 1000 . 'b' x 10;
+my ( $fh, $path ) = tempfile( UNLINK => 1, SUFFIX => '.txt' );
+print $fh $content;
+close $fh;
+
+my $name = ( File::Spec->splitpath($path) )[2];
+
+RT->Config->Set( 'WebSessionClass', "Apache::Session::File");
+RT->Config->Set( 'MaxAttachmentSize', 1000 );
+RT->Config->Set( 'TruncateLongAttachments', '0' );
+RT->Config->Set( 'DropLongAttachments', '1' );
+
+my $cf = RT::CustomField->new( RT->SystemUser );
+ok(
+ $cf->Create(
+ Name => 'test truncation',
+ Queue => '0',
+ Type => 'FreeformSingle',
+ ),
+);
+my $cfid = $cf->id;
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok $m->login, 'logged in';
+
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+ok( $queue->id, "Loaded General queue" );
+$m->get_ok( $baseurl . '/Ticket/Create.html?Queue=' . $queue->id );
+$m->content_contains( "Create a new ticket", 'ticket create page' );
+
+$m->form_name('TicketCreate');
+$m->field( 'Subject', 'Attachments dropping test' );
+$m->field( 'Attach', $path );
+$m->field( 'Content', 'Some content' );
+my $cf_content = 'cf' . 'a' x 998 . 'cfb';
+$m->field( "Object-RT::Ticket--CustomField-$cfid-Value", $cf_content );
+$m->submit;
+is( $m->status, 200, "request successful" );
+
+$m->content_contains( "File '$name' dropped because its size (1010 bytes) exceeded configured maximum size setting (1000 bytes).", 'dropped message' );
+$m->content_lacks( 'cfaaaa', 'cf value was dropped' );
+$m->follow_link_ok( { text => "Download $name" } );
+is( $m->content, 'Large attachment dropped', 'dropped $name' );
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/attachment_encoding.t b/rt/t/web/attachment_encoding.t
index f49720e0f..3f7d6d1cf 100644
--- a/rt/t/web/attachment_encoding.t
+++ b/rt/t/web/attachment_encoding.t
@@ -86,7 +86,7 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE};
'-> /Ticket/Attachment/...' );
$m->content_contains( $filename, "has file content $filename" );
- ( $id ) = $m->uri =~ /(\d+)\D+$/;
+ ( $id ) = $m->uri =~ m{/(\d+)/[^/]+$};
ok( $id, 'found attachment id' );
$attachment = RT::Attachment->new( $RT::SystemUser );
ok($attachment->Load($id), "load att $id");
diff --git a/rt/t/web/attachment_truncation.t b/rt/t/web/attachment_truncation.t
new file mode 100644
index 000000000..b60f29e90
--- /dev/null
+++ b/rt/t/web/attachment_truncation.t
@@ -0,0 +1,53 @@
+use warnings;
+use strict;
+
+use RT::Test tests => undef;
+use File::Temp 'tempfile';
+
+my $content = 'a' x 1000 . 'b' x 10;
+my ( $fh, $path ) = tempfile( UNLINK => 1, SUFFIX => '.txt' );
+print $fh $content;
+close $fh;
+my $name = ( File::Spec->splitpath($path) )[2];
+
+RT->Config->Set( 'WebSessionClass', "Apache::Session::File");
+RT->Config->Set( 'MaxAttachmentSize', 1000 );
+RT->Config->Set( 'TruncateLongAttachments', '1' );
+
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+ok( $queue->id, "Loaded General queue" );
+
+my $cf = RT::CustomField->new( RT->SystemUser );
+ok(
+ $cf->Create(
+ Name => 'test truncation',
+ Queue => '0',
+ Type => 'FreeformSingle',
+ ),
+);
+my $cfid = $cf->id;
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok $m->login, 'logged in';
+
+$m->get_ok( $baseurl . '/Ticket/Create.html?Queue=' . $queue->id );
+$m->content_contains( "Create a new ticket", 'ticket create page' );
+
+$m->form_name('TicketCreate');
+$m->field( 'Subject', 'Attachments test' );
+$m->field( 'Attach', $path );
+$m->field( 'Content', 'Some content' );
+my $cf_content = 'cf' . 'a' x 998 . 'cfb';
+$m->field( "Object-RT::Ticket--CustomField-$cfid-Value", $cf_content );
+$m->submit;
+is( $m->status, 200, "request successful" );
+
+$m->content_contains( "File '$name' truncated because its size (1010 bytes) exceeded configured maximum size setting (1000 bytes).", 'truncated message' );
+$m->content_contains( 'cf' . 'a' x 998, 'has the first 1000 cf chars' );
+$m->content_lacks( 'aaacfb', 'lacks cf chars after that' );
+$m->follow_link_ok( { text => "Download $name" } );
+$m->content_contains( 'a' x 1000, 'has the first 1000 chars' );
+$m->content_lacks( 'b', 'lacks chars after that' );
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/attachments.t b/rt/t/web/attachments.t
index b518ec176..0ae407d7f 100644
--- a/rt/t/web/attachments.t
+++ b/rt/t/web/attachments.t
@@ -1,94 +1,506 @@
use strict;
use warnings;
-use RT::Test tests => 33;
+use RT::Test tests => 159;
-use constant LogoFile => $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png';
-use constant FaviconFile => $RT::MasonComponentRoot .'/NoAuth/images/favicon.png';
-use constant TextFile => $RT::MasonComponentRoot .'/NoAuth/css/print.css';
+use constant LogoFile => $RT::StaticPath .'/images/bpslogo.png';
+use constant FaviconFile => $RT::StaticPath .'/images/favicon.png';
+use constant TextFile => $RT::StaticPath .'/css/mobile.css';
-my ($baseurl, $m) = RT::Test->started_ok;
+my ($url, $m) = RT::Test->started_ok;
ok $m->login, 'logged in';
-my $queue = RT::Queue->new(RT->Nobody);
-my $qid = $queue->Load('General');
-ok( $qid, "Loaded General queue" );
-
-$m->form_name('CreateTicketInQueue');
-$m->field('Queue', $qid);
-$m->submit;
-is($m->status, 200, "request successful");
-$m->content_contains("Create a new ticket", 'ticket create page');
-
-$m->form_name('TicketCreate');
-$m->field('Subject', 'Attachments test');
-$m->field('Attach', LogoFile);
-$m->field('Content', 'Some content');
-$m->submit;
-is($m->status, 200, "request successful");
-
-$m->content_contains('Attachments test', 'we have subject on the page');
-$m->content_contains('Some content', 'and content');
-$m->content_contains('Download bpslogo.png', 'page has file name');
-
-open LOGO, "<", LogoFile or die "Can't open logo file: $!";
-binmode LOGO;
-my $logo_contents = do {local $/; <LOGO>};
-close LOGO;
-$m->follow_link_ok({text => "Download bpslogo.png"});
-is($m->content_type, "image/png");
-is($m->content, $logo_contents, "Binary content matches");
-
-$m->back;
-$m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
-$m->form_name('TicketUpdate');
-$m->field('Attach', TextFile);
-$m->click('AddMoreAttach');
-is($m->status, 200, "request successful");
-
-$m->form_name('TicketUpdate');
-$m->field('Attach', FaviconFile);
-$m->field('UpdateContent', 'Message');
-$m->click('SubmitTicket');
-is($m->status, 200, "request successful");
-
-$m->content_contains('Download bpslogo.png', 'page has file name');
-$m->content_contains('Download favicon.png', 'page has file name');
-$m->content_contains('Download print.css', 'page has file name');
-
-$m->follow_link_ok( { text => 'Download bpslogo.png' } );
-is( $m->response->header('Content-Type'), 'image/png', 'Content-Type of png lacks charset' );
-
-$m->back;
-
-$m->follow_link_ok( { text => 'Download print.css' } );
-is( $m->response->header('Content-Type'),
- 'text/css;charset=UTF-8', 'Content-Type of text has charset' );
-
-diag "test mobile ui";
-$m->get_ok( $baseurl . '/m/ticket/create?Queue=' . $qid );
-
-$m->form_name('TicketCreate');
-$m->field('Subject', 'Attachments test');
-$m->field('Attach', LogoFile);
-$m->field('Content', 'Some content');
-$m->submit;
-is($m->status, 200, "request successful");
-
-$m->content_contains('Attachments test', 'we have subject on the page');
-$m->content_contains('bpslogo.png', 'page has file name');
-
-$m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
-$m->form_name('TicketUpdate');
-$m->field('Attach', LogoFile);
-$m->click('AddMoreAttach');
-is($m->status, 200, "request successful");
-
-$m->form_name('TicketUpdate');
-$m->field('Attach', FaviconFile);
-$m->field('UpdateContent', 'Message');
-$m->click('SubmitTicket');
-is($m->status, 200, "request successful");
-
-$m->content_contains('bpslogo.png', 'page has file name');
-$m->content_contains('favicon.png', 'page has file name');
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+ok( $queue && $queue->id, "Loaded General queue" );
+
+diag "create a ticket in full interface";
+diag "w/o attachments";
+{
+ $m->goto_create_ticket( $queue );
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketCreate');
+ $m->content_contains("Create a new ticket", 'ticket create page');
+ $m->submit;
+ is($m->status, 200, "request successful");
+}
+
+diag "with one attachment";
+{
+ $m->goto_create_ticket( $queue );
+
+ $m->form_name('TicketCreate');
+ $m->field('Subject', 'Attachments test');
+ $m->field('Attach', LogoFile);
+ $m->field('Content', 'Some content');
+
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('Attachments test', 'we have subject on the page');
+ $m->content_contains('Some content', 'and content');
+ $m->content_contains('Download bpslogo.png', 'page has file name');
+}
+
+diag "with two attachments";
+{
+ $m->goto_create_ticket( $queue );
+
+ $m->form_name('TicketCreate');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketCreate');
+ $m->field('Attach', FaviconFile);
+ $m->field('Subject', 'Attachments test');
+ $m->field('Content', 'Some content');
+
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('Attachments test', 'we have subject on the page');
+ $m->content_contains('Some content', 'and content');
+ $m->content_contains('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "with one attachment, but delete one along the way";
+{
+ $m->goto_create_ticket( $queue );
+
+ $m->form_name('TicketCreate');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketCreate');
+ $m->field('Attach', FaviconFile);
+ $m->tick( 'DeleteAttach', LogoFile );
+ $m->field('Subject', 'Attachments test');
+ $m->field('Content', 'Some content');
+
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('Attachments test', 'we have subject on the page');
+ $m->content_contains('Some content', 'and content');
+ $m->content_lacks('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "with one attachment, but delete one along the way";
+{
+ $m->goto_create_ticket( $queue );
+
+ $m->form_name('TicketCreate');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketCreate');
+ $m->tick( 'DeleteAttach', LogoFile );
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketCreate');
+ $m->field('Attach', FaviconFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketCreate');
+ $m->field('Subject', 'Attachments test');
+ $m->field('Content', 'Some content');
+
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('Attachments test', 'we have subject on the page');
+ $m->content_contains('Some content', 'and content');
+ $m->content_lacks('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "reply to a ticket in full interface";
+diag "with one attachment";
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ );
+
+ $m->goto_ticket( $ticket->id );
+ $m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+ $m->form_name('TicketUpdate');
+ $m->field('Attach', LogoFile);
+ $m->field('UpdateContent', 'Message');
+ $m->click('SubmitTicket');
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('Download bpslogo.png', 'page has file name');
+}
+
+diag "with two attachments";
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ );
+
+ $m->goto_ticket( $ticket->id );
+ $m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+ $m->form_name('TicketUpdate');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketUpdate');
+ $m->field('Attach', FaviconFile);
+ $m->field('UpdateContent', 'Message');
+ $m->click('SubmitTicket');
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "with one attachment, delete one along the way";
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ );
+
+ $m->goto_ticket( $ticket->id );
+ $m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+ $m->form_name('TicketUpdate');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketUpdate');
+ $m->tick('DeleteAttach', LogoFile);
+ $m->field('Attach', FaviconFile);
+ $m->field('UpdateContent', 'Message');
+ $m->click('SubmitTicket');
+ is($m->status, 200, "request successful");
+
+ $m->content_lacks('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "jumbo interface";
+diag "with one attachment";
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ );
+
+ $m->goto_ticket( $ticket->id );
+ $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket");
+ $m->form_name('TicketModifyAll');
+ $m->field('Attach', LogoFile);
+ $m->field('UpdateContent', 'Message');
+ $m->click('SubmitTicket');
+ is($m->status, 200, "request successful");
+
+ $m->goto_ticket( $ticket->id );
+ $m->content_contains('Download bpslogo.png', 'page has file name');
+}
+
+diag "with two attachments";
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ );
+
+ $m->goto_ticket( $ticket->id );
+ $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket");
+ $m->form_name('TicketModifyAll');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketModifyAll');
+ $m->field('Attach', FaviconFile);
+ $m->field('UpdateContent', 'Message');
+ $m->click('SubmitTicket');
+ is($m->status, 200, "request successful");
+
+ $m->goto_ticket( $ticket->id );
+ $m->content_contains('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "with one attachment, delete one along the way";
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ );
+
+ $m->goto_ticket( $ticket->id );
+ $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket");
+ $m->form_name('TicketModifyAll');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketModifyAll');
+ $m->tick('DeleteAttach', LogoFile);
+ $m->field('Attach', FaviconFile);
+ $m->field('UpdateContent', 'Message');
+ $m->click('SubmitTicket');
+ is($m->status, 200, "request successful");
+
+ $m->goto_ticket( $ticket->id );
+ $m->content_lacks('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "bulk update";
+diag "one attachment";
+{
+ my @tickets = RT::Test->create_tickets(
+ {
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ },
+ {},
+ {},
+ );
+ my $query = join ' OR ', map "id=$_", map $_->id, @tickets;
+ $query =~ s/ /%20/g;
+ $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" );
+
+ $m->form_name('BulkUpdate');
+ $m->field('Attach', FaviconFile);
+ $m->field('UpdateContent', 'Message');
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ foreach my $ticket ( @tickets ) {
+ $m->goto_ticket( $ticket->id );
+ $m->content_lacks('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+ }
+}
+
+diag "two attachments";
+{
+ my @tickets = RT::Test->create_tickets(
+ {
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ },
+ {},
+ {},
+ );
+ my $query = join ' OR ', map "id=$_", map $_->id, @tickets;
+ $query =~ s/ /%20/g;
+ $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" );
+
+ $m->form_name('BulkUpdate');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('BulkUpdate');
+ $m->field('Attach', FaviconFile);
+ $m->field('UpdateContent', 'Message');
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ foreach my $ticket ( @tickets ) {
+ $m->goto_ticket( $ticket->id );
+ $m->content_contains('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+ }
+}
+
+diag "one attachment, delete one along the way";
+{
+ my @tickets = RT::Test->create_tickets(
+ {
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ },
+ {},
+ {},
+ );
+ my $query = join ' OR ', map "id=$_", map $_->id, @tickets;
+ $query =~ s/ /%20/g;
+ $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" );
+
+ $m->form_name('BulkUpdate');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('BulkUpdate');
+ $m->tick('DeleteAttach', LogoFile);
+ $m->field('Attach', FaviconFile);
+ $m->field('UpdateContent', 'Message');
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ foreach my $ticket ( @tickets ) {
+ $m->goto_ticket( $ticket->id );
+ $m->content_lacks('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+ }
+}
+
+diag "self service";
+diag "create with attachment";
+{
+ $m->get_ok( $url . "/SelfService/Create.html?Queue=". $queue->id );
+
+ $m->form_name('TicketCreate');
+ $m->field('Attach', FaviconFile);
+ $m->field('Subject', 'Subject');
+ $m->field('Content', 'Message');
+ ok($m->current_form->find_input('AddMoreAttach'), "more than one attachment");
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "update with attachment";
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ );
+
+ $m->get_ok( $url . "/SelfService/Update.html?id=". $ticket->id );
+ $m->form_name('TicketUpdate');
+ $m->field('Attach', FaviconFile);
+ $m->field('UpdateContent', 'Message');
+ ok($m->current_form->find_input('AddMoreAttach'), "more than one attachment");
+ $m->click('SubmitTicket');
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "mobile ui";
+
+diag "simple create + reply";
+{
+ $m->get_ok( $url . '/m/ticket/create?Queue=' . $queue->id );
+
+ $m->form_name('TicketCreate');
+ $m->field('Subject', 'Attachments test');
+ $m->field('Attach', LogoFile);
+ $m->field('Content', 'Some content');
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('Attachments test', 'we have subject on the page');
+ $m->content_contains('bpslogo.png', 'page has file name');
+
+ $m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+ $m->form_name('TicketUpdate');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketUpdate');
+ $m->field('Attach', FaviconFile);
+ $m->field('UpdateContent', 'Message');
+ $m->click('SubmitTicket');
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('bpslogo.png', 'page has file name');
+ $m->content_contains('favicon.png', 'page has file name');
+}
+
+
+diag "check content type and content";
+{
+ $m->goto_create_ticket( $queue );
+
+ $m->form_name('TicketCreate');
+ $m->field('Attach', LogoFile);
+ $m->click('AddMoreAttach');
+ is($m->status, 200, "request successful");
+
+ $m->form_name('TicketCreate');
+ $m->field('Attach', TextFile);
+ $m->field('Subject', 'Attachments test');
+ $m->field('Content', 'Some content');
+
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ $m->content_contains('Attachments test', 'we have subject on the page');
+ $m->content_contains('Some content', 'and content');
+ $m->content_contains('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download mobile.css', 'page has file name');
+
+ $m->follow_link_ok({text => "Download bpslogo.png"});
+ is($m->response->header('Content-Type'), 'image/png', 'Content-Type of png lacks charset' );
+ is($m->content_type, "image/png");
+ is($m->content, RT::Test->file_content(LogoFile), "Binary content matches");
+ $m->back;
+
+ $m->follow_link_ok( { text => 'Download mobile.css' } );
+ is( $m->response->header('Content-Type'),
+ 'text/css;charset=UTF-8',
+ 'Content-Type of text has charset',
+ );
+ is($m->content_type, "text/css");
+ is($m->content, RT::Test->file_content(TextFile), "Text content matches");
+}
+
+diag "concurent actions";
+my $m2 = RT::Test::Web->new;
+ok $m2->login, 'second login';
+
+diag "update and create";
+{
+ my $ticket = RT::Test->create_ticket(
+ Queue => $queue,
+ Subject => 'Attachments test',
+ Content => 'Some content',
+ );
+
+ $m2->goto_ticket( $ticket->id );
+ $m2->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+ $m2->form_name('TicketUpdate');
+ $m2->field('Attach', LogoFile);
+ $m2->click('AddMoreAttach');
+ is($m2->status, 200, "request successful");
+
+ $m->goto_create_ticket( $queue );
+
+ $m->form_name('TicketCreate');
+ $m->field('Attach', FaviconFile);
+ $m->field('Subject', 'Attachments test');
+ $m->field('Content', 'Some content');
+ $m->submit;
+ is($m->status, 200, "request successful");
+
+ $m->content_lacks('Download bpslogo.png', 'page has file name');
+ $m->content_contains('Download favicon.png', 'page has file name');
+
+ $m2->form_name('TicketUpdate');
+ $m2->click('SubmitTicket');
+ $m2->content_contains('Download bpslogo.png', 'page has file name');
+ $m2->content_lacks('Download favicon.png', 'page has no file name');
+}
+
diff --git a/rt/t/web/basic.t b/rt/t/web/basic.t
index 02483b208..79c247d24 100644
--- a/rt/t/web/basic.t
+++ b/rt/t/web/basic.t
@@ -2,7 +2,7 @@
use strict;
use warnings;
-use RT::Test tests => 23;
+use RT::Test tests => 24;
my ($baseurl, $agent) = RT::Test->started_ok;
@@ -71,16 +71,15 @@ my $url = $agent->rt_base_url;
fields => { TimeWorked => 5, 'TimeWorked-TimeUnits' => "hours" }
);
- $agent->content_contains("to &#39;300&#39;", "5 hours is 300 minutes");
+ $agent->content_contains("5 hours", "5 hours is displayed");
+ $agent->content_contains("300 min", "but minutes is also");
}
-TODO: {
- todo_skip("Need to handle mason trying to compile images",1);
-$agent->get( $url."NoAuth/images/test.png" );
+$agent->get( $url."static/images/test.png" );
my $file = RT::Test::get_relocatable_file(
File::Spec->catfile(
- qw(.. .. share html NoAuth images test.png)
+ qw(.. .. share static images test.png)
)
);
is(
@@ -88,7 +87,6 @@ is(
-s $file,
"got a file of the correct size ($file)",
);
-}
#
# XXX: hey-ho, we have these tests in t/web/query-builder
diff --git a/rt/t/web/basic_auth.t b/rt/t/web/basic_auth.t
new file mode 100644
index 000000000..ff77f29f2
--- /dev/null
+++ b/rt/t/web/basic_auth.t
@@ -0,0 +1,34 @@
+use strict;
+use warnings;
+use RT;
+use RT::Test tests => 9;
+
+RT->Config->Set( DevelMode => 0 );
+RT->Config->Set( WebRemoteUserAuth => 1 );
+
+my ( $url, $m ) = RT::Test->started_ok( basic_auth => 1 );
+
+# This tests the plack middleware, not RT
+$m->get($url);
+is($m->status, 401, "Initial request with no creds gets 401");
+
+# This tests the plack middleware, not RT
+$m->get($url, $m->auth_header( root => "wrong" ));
+is($m->status, 401, "Request with wrong creds gets 401");
+
+$m->get($url, $m->auth_header( root => "password" ));
+is($m->status, 200, "Request with right creds gets 200");
+
+$m->content_like(
+ qr{<span class="current-user">\Qroot\E</span>}i,
+ "Has user on the page"
+);
+$m->content_unlike(qr/Logout/i, "Has no logout button, no WebFallbackToRTLogin");
+
+# Again, testing the plack middleware
+$m->get($url);
+is($m->status, 401, "Subsequent requests without credentials aren't still logged in");
+
+
+# Put the credentials back for the warnings check at the end
+$m->auth( root => "password" );
diff --git a/rt/t/web/case-sensitivity.t b/rt/t/web/case-sensitivity.t
index 5f40ef690..759937192 100644
--- a/rt/t/web/case-sensitivity.t
+++ b/rt/t/web/case-sensitivity.t
@@ -22,7 +22,7 @@ $m->login;
require JSON;
is_deeply(
JSON::from_json( $m->content ),
- [{"value" => "root\@localhost","label" => "Enoch Root", id=>$root_id}]
+ [{id => 12, "value" => "root\@localhost","label" => "root (Enoch Root)"}]
);
}
@@ -73,7 +73,7 @@ my $cf;
# test custom field values auto completer
{
- $m->get_ok('/Helpers/Autocomplete/CustomFieldValues?term=eNo&Object---CustomField-'. $cf->id .'-Value&ContextId=1&ContextType=RT::Queue');
+ $m->get_ok('/Helpers/Autocomplete/CustomFieldValues?term=eNo&Object-RT::Ticket--CustomField-'. $cf->id .'-Value&ContextId=1&ContextType=RT::Queue');
require JSON;
is_deeply(
JSON::from_json( $m->content ),
diff --git a/rt/t/web/cf_access.t b/rt/t/web/cf_access.t
index 675fa2177..48ab5a21b 100644
--- a/rt/t/web/cf_access.t
+++ b/rt/t/web/cf_access.t
@@ -5,14 +5,14 @@ use RT::Test tests => 32;
my ($baseurl, $m) = RT::Test->started_ok;
-use constant ImageFile => $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png';
+use constant ImageFile => $RT::StaticPath .'/images/bpslogo.png';
use constant ImageFileContent => RT::Test->file_content(ImageFile);
ok $m->login, 'logged in';
diag "Create a CF";
{
- $m->follow_link( id => 'tools-config-custom-fields-create');
+ $m->follow_link( id => 'admin-custom-fields-create');
# Test form validation
$m->submit_form(
@@ -94,10 +94,10 @@ diag "apply the CF to General queue";
my ( $cf, $cfid, $tid );
{
$m->title_is(q/Editing CustomField img/, 'admin-cf created');
- $m->follow_link( id => 'tools-config-queues');
+ $m->follow_link( id => 'admin-queues');
$m->follow_link( text => 'General' );
$m->title_is(q/Configuration for queue General/, 'admin-queue: general');
- $m->follow_link( id => 'page-ticket-custom-fields');
+ $m->follow_link( id => 'page-custom-fields-tickets');
$m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid');
$m->form_name('EditCustomFields');
@@ -215,7 +215,7 @@ diag "create a ticket with an image";
}
$m->get( $m->rt_base_url );
-$m->follow_link( id => 'search-new');
+$m->follow_link( id => 'search-tickets-new');
$m->title_is(q/Query Builder/, 'Query building');
$m->submit_form(
form_name => "BuildQuery",
diff --git a/rt/t/web/cf_date.t b/rt/t/web/cf_date.t
index 2180e140f..a38388972 100644
--- a/rt/t/web/cf_date.t
+++ b/rt/t/web/cf_date.t
@@ -14,7 +14,7 @@ my $cf_name = 'test cf date';
my $cfid;
diag "Create a CF";
{
- $m->follow_link( id => 'tools-config-custom-fields-create');
+ $m->follow_link( id => 'admin-custom-fields-create');
$m->submit_form(
form_name => "ModifyCustomField",
fields => {
@@ -33,12 +33,12 @@ my $queue = RT::Test->load_or_create_queue( Name => 'General' );
ok $queue && $queue->id, 'loaded or created queue';
{
- $m->follow_link( id => 'tools-config-queues-select');
+ $m->follow_link( id => 'admin-queues-select');
$m->title_is( q/Admin queues/, 'admin-queues screen' );
$m->follow_link( text => 'General' );
$m->title_is( q/Configuration for queue General/,
'admin-queue: general' );
- $m->follow_link( text => 'Ticket Custom Fields' );
+ $m->follow_link( id => 'page-custom-fields-tickets' );
$m->title_is( q/Custom Fields for queue General/,
'admin-queue: general cfid' );
@@ -186,7 +186,7 @@ diag 'check invalid inputs';
my @warnings = $m->get_warnings;
chomp @warnings;
- is_deeply( @warnings, q{Couldn't parse date 'foodate' by Time::ParseDate} );
+ is_deeply( [@warnings], [(q{Couldn't parse date 'foodate' by Time::ParseDate})x2] );
}
diag 'retain values when adding attachments';
diff --git a/rt/t/web/cf_datetime.t b/rt/t/web/cf_datetime.t
index 72a8b3f7e..da938ab85 100644
--- a/rt/t/web/cf_datetime.t
+++ b/rt/t/web/cf_datetime.t
@@ -24,7 +24,7 @@ if ( ( $ENV{RT_TEST_WEB_HANDLER} || '' ) =~ /^apache(\+mod_perl)?$/
my $cfid;
diag "Create a CF";
{
- $m->follow_link( id => 'tools-config-custom-fields-create');
+ $m->follow_link( id => 'admin-custom-fields-create');
$m->submit_form(
form_name => "ModifyCustomField",
fields => {
@@ -47,7 +47,7 @@ ok $queue && $queue->id, 'loaded or created queue';
$m->title_is(q/Admin queues/, 'admin-queues screen');
$m->follow_link( text => 'General' );
$m->title_is(q/Configuration for queue General/, 'admin-queue: general');
- $m->follow_link( text => 'Ticket Custom Fields' );
+ $m->follow_link( id => 'page-custom-fields-tickets' );
$m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid');
$m->form_name('EditCustomFields');
@@ -212,7 +212,7 @@ diag 'check invalid inputs';
my @warnings = $m->get_warnings;
chomp @warnings;
- is_deeply( @warnings, q{Couldn't parse date 'foodate' by Time::ParseDate} );
+ is_deeply( [@warnings], [(q{Couldn't parse date 'foodate' by Time::ParseDate})x2] );
}
diag 'retain values when adding attachments';
diff --git a/rt/t/web/cf_groupings.t b/rt/t/web/cf_groupings.t
new file mode 100644
index 000000000..0a40f71af
--- /dev/null
+++ b/rt/t/web/cf_groupings.t
@@ -0,0 +1,277 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my @groupings = qw/Basics Dates People Links More/;
+RT->Config->Set( 'CustomFieldGroupings',
+ 'RT::Ticket' => {
+ map { +($_ => ["Test$_"]) } @groupings,
+ },
+);
+
+my %CF;
+for my $grouping (@groupings) {
+ my $name = "Test$grouping";
+ my $cf = RT::CustomField->new( RT->SystemUser );
+ my ($id, $msg) = $cf->Create(
+ Name => $name,
+ Queue => '0',
+ Description => 'A Testing custom field',
+ Type => 'FreeformSingle',
+ Pattern => '^(?!bad value).*$',
+ );
+ ok $id, "custom field '$name' correctly created";
+ $CF{$grouping} = $id;
+}
+
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok $m->login, 'logged in as root';
+
+my %location = (
+ Basics => ".ticket-info-basics",
+ Dates => ".ticket-info-dates",
+ People => "#ticket-create-message",
+ Links => ".ticket-info-links",
+ More => ".ticket-info-cfs",
+);
+{
+ note "testing Create";
+ $m->goto_create_ticket($queue);
+
+ my $prefix = 'Object-RT::Ticket--CustomField:';
+ my $dom = $m->dom;
+ $m->form_name('TicketCreate');
+ $m->field("Subject", "CF grouping test");
+
+ for my $grouping (@groupings) {
+ my $input_name = $prefix . "$grouping-$CF{$grouping}-Value";
+ is $dom->find(qq{input[name="$input_name"]})->size, 1, "only one CF input on the page";
+ ok $dom->at(qq{$location{$grouping} input[name="$input_name"]}), "CF is in the right place";
+ $m->field( $input_name, "Test" . $grouping . "Value" );
+ }
+ $m->submit;
+}
+
+my $id = $m->get_ticket_id;
+{
+ note "testing Display";
+ ok $id, "created a ticket";
+ my $dom = $m->dom;
+
+ $location{People} = ".ticket-info-people";
+ foreach my $grouping (@groupings) {
+ my $row_id = "CF-$CF{$grouping}-ShowRow";
+ is $dom->find(qq{#$row_id})->size, 1, "CF on the page";
+ is $dom->at(qq{#$row_id})->all_text, "Test$grouping: Test${grouping}Value", "value is set";
+ ok $dom->at(qq{$location{$grouping} #$row_id}), "CF is in the right place";
+ }
+}
+
+{
+ note "testing Basics/People/Dates/Links pages";
+ my $prefix = 'Object-RT::Ticket-'. $id .'-CustomField:';
+ { # Basics and More both show up on "Basics"
+ for my $name (qw/Basics More/) {
+ $m->follow_link_ok({id => 'page-basics'}, 'Ticket -> Basics');
+ is $m->dom->find(qq{input[name^="$prefix"][name\$="-Value"]})->size, 2,
+ "two CF inputs on the page";
+
+ my $input_name = "$prefix$name-$CF{$name}-Value";
+ ok $m->dom->at(qq{$location{$name} input[name="$input_name"]}),
+ "CF is in the right place";
+ $m->submit_form_ok({
+ with_fields => { $input_name => "Test${name}Changed" },
+ button => 'SubmitTicket',
+ });
+ $m->content_like(qr{to Test${name}Changed});
+
+ $m->submit_form_ok({
+ with_fields => { $input_name => "bad value" },
+ button => 'SubmitTicket',
+ });
+ $m->content_like(qr{Test\Q$name\E: Input must match});
+ }
+ }
+
+ # Everything else gets its own page
+ foreach my $name ( qw(People Dates Links) ) {
+ $m->follow_link_ok({id => "page-\L$name"}, "Ticket's $name page");
+ is $m->dom->find(qq{input[name^="$prefix"][name\$="-Value"]})->size, 1,
+ "only one CF input on the page";
+ my $input_name = "$prefix$name-$CF{$name}-Value";
+ $m->submit_form_ok({
+ with_fields => { $input_name => "Test${name}Changed" },
+ button => 'SubmitTicket',
+ });
+ $m->content_like(qr{to Test${name}Changed});
+
+ $m->submit_form_ok({
+ with_fields => { $input_name => "bad value" },
+ button => 'SubmitTicket',
+ });
+ $m->content_like(qr{Could not add new custom field value: Input must match});
+ }
+}
+
+{
+ note "testing Jumbo";
+ my $prefix = 'Object-RT::Ticket-'. $id .'-CustomField:';
+ $m->follow_link_ok({id => "page-jumbo"}, "Ticket's Jumbo page");
+ my $dom = $m->dom;
+ $m->form_name("TicketModifyAll");
+
+ foreach my $name ( qw(Basics People Dates Links More) ) {
+ my $input_name = "$prefix$name-$CF{$name}-Value";
+ is $dom->find(qq{input[name="$input_name"]})->size, 1,
+ "only one CF input on the page";
+ $m->field( $input_name, "Test${name}Again" );
+ }
+ $m->click('SubmitTicket');
+ foreach my $name ( qw(Basics People Dates Links More) ) {
+ $m->content_like(qr{to Test${name}Again});
+ }
+}
+
+{
+ note "Reconfigure to place one CF in multiple boxes";
+ $m->no_warnings_ok;
+ RT::Test->stop_server;
+
+ RT->Config->Set( 'CustomFieldGroupings',
+ 'RT::Ticket' => {
+ Basics => [ 'TestMore' ],
+ More => [ 'TestMore' ],
+ },
+ );
+
+ ( $baseurl, $m ) = RT::Test->started_ok;
+ ok $m->login, 'logged in as root';
+}
+
+{
+ note "Testing one CF in multiple boxes";
+ $m->goto_create_ticket($queue);
+
+ my $prefix = 'Object-RT::Ticket--CustomField:';
+ my $dom = $m->dom;
+ $m->form_name('TicketCreate');
+
+ my $cf = $CF{More};
+ is $m->dom->find(qq{input[name^="$prefix"][name\$="-$cf-Value"]})->size, 2,
+ "Two 'More' CF inputs on the page";
+ for my $grouping (qw/Basics More/) {
+ my $input_name = $prefix . "$grouping-$cf-Value";
+ is $dom->find(qq{input[name="$input_name"]})->size, 1, "Found the $grouping grouping";
+ ok $dom->at(qq{$location{$grouping} input[name="$input_name"]}), "CF is in the right place";
+ $m->field( $input_name, "TestMoreValue" );
+ }
+ $m->submit;
+ $m->no_warnings_ok( "Submitting CF with two (identical) values had no warnings" );
+}
+
+$id = $m->get_ticket_id;
+my $ticket = RT::Ticket->new ( RT->SystemUser );
+$ticket->Load( $id );
+is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "TestMoreValue",
+ "Value submitted twice is set correctly (and only once)";
+
+my $cf = $CF{More};
+my $prefix = 'Object-RT::Ticket-'. $id .'-CustomField:';
+{
+ note "Updating with multiple appearances of a CF";
+ $m->follow_link_ok({id => 'page-basics'}, 'Ticket -> Basics');
+
+ is $m->dom->find(qq{input[name^="$prefix"][name\$="-$cf-Value"]})->size, 2,
+ "Two 'More' CF inputs on the page";
+ my @inputs;
+ for my $grouping (qw/Basics More/) {
+ my $input_name = "$prefix$grouping-$cf-Value";
+ push @inputs, $input_name;
+ ok $m->dom->at(qq{$location{$grouping} input[name="$input_name"]}),
+ "CF is in the right place";
+ }
+ $m->submit_form_ok({
+ with_fields => {
+ map {+($_ => "TestMoreChanged")} @inputs,
+ },
+ button => 'SubmitTicket',
+ });
+ $m->no_warnings_ok;
+ $m->content_like(qr{to TestMoreChanged});
+
+ $ticket->Load( $id );
+ is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "TestMoreChanged",
+ "Updated value submitted twice is set correctly (and only once)";
+}
+
+{
+ note "Updating with _differing_ values in multiple appearances of a CF";
+
+ my %inputs = map {+($_ => "$prefix$_-$cf-Value")} qw/Basics More/;
+ $m->submit_form_ok({
+ with_fields => {
+ $inputs{Basics} => "BasicsValue",
+ $inputs{More} => "MoreValue",
+ },
+ button => 'SubmitTicket',
+ });
+ $m->warning_like(qr{CF $cf submitted with multiple differing values});
+ $m->content_like(qr{to BasicsValue}, "Arbitrarily chose first value");
+
+ $ticket->Load( $id );
+ is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "BasicsValue",
+ "Conflicting value submitted twice is set correctly (and only once)";
+}
+
+{
+ note "Configuring CF to be a select-multiple";
+ my $custom_field = RT::CustomField->new( RT->SystemUser );
+ $custom_field->Load( $cf );
+ $custom_field->SetType( "Select" );
+ $custom_field->SetMaxValues( 0 );
+ $custom_field->AddValue( Name => $_ ) for 1..9;
+}
+
+{
+ note "Select multiples do not interfere with each other when appearing multiple times";
+ $m->follow_link_ok({id => 'page-basics'}, 'Ticket -> Basics');
+
+ $m->form_name('TicketModify');
+ my %inputs = map {+($_ => "$prefix$_-$cf-Values")} qw/Basics More/;
+ ok $m->dom->at(qq{select[name="$inputs{Basics}"]}), "Found 'More' CF in Basics box";
+ ok $m->dom->at(qq{select[name="$inputs{More}"]}), "Found 'More' CF in More box";
+
+ $m->select( $inputs{Basics} => [1, 3, 9] );
+ $m->select( $inputs{More} => [1, 3, 9] );
+ $m->click( 'SubmitTicket' );
+ $m->no_warnings_ok;
+ $m->content_like(qr{$_ added as a value for TestMore}) for 1, 3, 9;
+ $m->content_like(qr{BasicsValue is no longer a value for custom field TestMore});
+
+ $ticket->Load( $id );
+ is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "1|3|9",
+ "Multi-select values submitted correctly";
+}
+
+{
+ note "Submit multiples correctly choose one set of values when conflicting information is submitted";
+ $m->form_name('TicketModify');
+ my %inputs = map {+($_ => "$prefix$_-$cf-Values")} qw/Basics More/;
+ $m->select( $inputs{Basics} => [2, 3, 4] );
+ $m->select( $inputs{More} => [8, 9] );
+ $m->click( 'SubmitTicket' );
+ $m->warning_like(qr{CF $cf submitted with multiple differing values});
+ $m->content_like(qr{$_ added as a value for TestMore}) for 2, 4;
+ $m->content_unlike(qr{$_ added as a value for TestMore}) for 8;
+ $m->content_like(qr{$_ is no longer a value for custom field TestMore}) for 1, 9;
+
+ $ticket->Load( $id );
+ is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "3|2|4",
+ "Multi-select values submitted correctly";
+}
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/cf_groupings_user.t b/rt/t/web/cf_groupings_user.t
new file mode 100644
index 000000000..fe79ae5ad
--- /dev/null
+++ b/rt/t/web/cf_groupings_user.t
@@ -0,0 +1,110 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+RT->Config->Set( 'CustomFieldGroupings',
+ 'RT::User' => {
+ Identity => ['TestIdentity'],
+ 'Access control' => ['TestAccessControl'],
+ Location => ['TestLocation'],
+ Phones => ['TestPhones'],
+ More => ['TestMore'],
+ },
+);
+
+my %CF;
+
+while (my ($group,$cfs) = each %{ RT->Config->Get('CustomFieldGroupings')->{'RT::User'} } ) {
+ my $name = $cfs->[0];
+ my $cf = RT::CustomField->new( RT->SystemUser );
+ my ($id, $msg) = $cf->Create(
+ Name => $name,
+ Description => 'A custom field',
+ LookupType => RT::User->new( $RT::SystemUser )->CustomFieldLookupType,
+ Type => 'FreeformSingle',
+ Pattern => '^(?!bad value).*$',
+ );
+ ok $id, "custom field '$name' correctly created";
+
+ ($id, $msg) = $cf->AddToObject( RT::User->new( $cf->CurrentUser ) );
+ ok $id, "applied custom field" or diag "error: $msg";
+
+ $group =~ s/\W//g;
+ $CF{$name} = "$group-" . $cf->Id;
+}
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok $m->login, 'logged in as root';
+
+my %location = (
+ Identity => ".user-info-identity",
+ AccessControl => ".user-info-access-control",
+ Location => ".user-info-location",
+ Phones => ".user-info-phones",
+ More => ".user-info-cfs",
+);
+{
+ note "testing Create";
+ $m->follow_link_ok({id => 'admin-users-create'}, 'Create ');
+
+ my $dom = $m->dom;
+ $m->form_name('UserCreate');
+
+ $m->field( 'Name', 'user1' );
+
+ my $prefix = 'Object-RT::User--CustomField:';
+ for my $name (keys %location) {
+ my $input_name = $prefix . $CF{"Test$name"} .'-Value';
+ is $dom->find(qq{input[name="$input_name"]})->size, 1, "only one CF input on the page";
+ ok $dom->at(qq{$location{$name} input[name="$input_name"]}), "CF is in the right place";
+ $m->field( $input_name, "Test${name}Value" );
+ }
+
+ $m->submit;
+ $m->content_like(qr{User created});
+}
+
+my ($id) = ($m->uri =~ /id=(\d+)/);
+ok $id, "found user's id #$id";
+
+{
+ note "testing values on Modify page and on the object";
+ my $user = RT::User->new( RT->SystemUser );
+ $user->Load( $id );
+ ok $user->id, "loaded user";
+
+ my $dom = $m->dom;
+ $m->form_name('UserModify');
+ my $prefix = "Object-RT::User-$id-CustomField:";
+ foreach my $name ( keys %location ) {
+ is $user->FirstCustomFieldValue("Test$name"), "Test${name}Value",
+ "correct value of Test$name CF";
+ my $input_name = $prefix . $CF{"Test$name"} .'-Value';
+ is $m->value($input_name), "Test${name}Value",
+ "correct value in UI";
+ $m->field( $input_name, "Test${name}Changed" );
+ ok $dom->at(qq{$location{$name} input[name="$input_name"]}), "CF is in the right place";
+ }
+ $m->submit;
+}
+
+{
+ note "testing that update works";
+ my $user = RT::User->new( RT->SystemUser );
+ $user->Load( $id );
+ ok $user->id, "loaded user";
+
+ $m->form_name('UserModify');
+ my $prefix = "Object-RT::User-$id-CustomField:";
+ foreach my $name ( keys %location ) {
+ is $user->FirstCustomFieldValue("Test$name"), "Test${name}Changed",
+ "correct value of Test$name CF";
+ my $input = $prefix . $CF{"Test$name"} .'-Value';
+ is $m->value($input), "Test${name}Changed",
+ "correct value in UI";
+ }
+}
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/cf_image.t b/rt/t/web/cf_image.t
new file mode 100644
index 000000000..355f25968
--- /dev/null
+++ b/rt/t/web/cf_image.t
@@ -0,0 +1,61 @@
+use strict;
+use warnings;
+
+use RT::Test tests => 'no_declare';
+
+my (undef, $m) = RT::Test->started_ok;
+$m->login;
+$m->follow_link( id => 'admin-custom-fields-create' );
+$m->submit_form_ok({
+ form_name => "ModifyCustomField",
+ fields => {
+ Name => 'Images',
+ TypeComposite => 'Image-1',
+ LookupType => 'RT::Queue-RT::Ticket',
+ },
+});
+$m->content_contains("Object created");
+my $cfid = $m->form_name('ModifyCustomField')->value('id');
+ok $cfid, "Created CF correctly";
+
+$m->follow_link_ok( {id => "page-applies-to"} );
+$m->form_with_fields( "AddCustomField-1" );
+$m->tick( "AddCustomField-1", 0 );
+$m->click_ok( "UpdateObjs" );
+$m->content_contains("Object created");
+
+
+$m->submit_form_ok({
+ form_name => "CreateTicketInQueue",
+ fields => { Queue => 'General' },
+});
+$m->content_contains("Upload one image");
+$m->submit_form_ok({
+ form_name => "TicketCreate",
+ fields => {
+ Subject => 'Test ticket',
+ Content => 'test',
+ },
+});
+$m->content_like( qr/Ticket \d+ created/,
+ "a ticket is created succesfully" );
+
+$m->follow_link_ok( {id => "page-basics"} );
+$m->content_contains("Upload one image");
+$m->submit_form_ok({
+ form_name => "TicketModify",
+ fields => {
+ "Object-RT::Ticket-1-CustomField-1-Upload" =>
+ RT::Test::get_relocatable_file('bpslogo.png', '..', 'data'),
+ },
+});
+$m->content_contains("bpslogo.png added");
+$m->content_contains("/Download/CustomFieldValue/1/bpslogo.png");
+
+$m->form_name("TicketModify");
+$m->tick("Object-RT::Ticket-1-CustomField-1-DeleteValueIds", 1);
+$m->click_ok("SubmitTicket");
+$m->content_lacks("/Download/CustomFieldValue/1/bpslogo.png");
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/cf_onqueue.t b/rt/t/web/cf_onqueue.t
index bd6ae66aa..dd3320a25 100644
--- a/rt/t/web/cf_onqueue.t
+++ b/rt/t/web/cf_onqueue.t
@@ -8,7 +8,7 @@ ok $m->login, 'logged in';
diag "Create a queue CF";
{
- $m->follow_link( id => 'tools-config-custom-fields-create');
+ $m->follow_link( id => 'admin-custom-fields-create');
$m->submit_form(
form_name => "ModifyCustomField",
fields => {
diff --git a/rt/t/web/cf_pattern.t b/rt/t/web/cf_pattern.t
new file mode 100644
index 000000000..ff85ec6c7
--- /dev/null
+++ b/rt/t/web/cf_pattern.t
@@ -0,0 +1,80 @@
+use strict;
+use warnings;
+
+use RT::Test tests => 'no_declare';
+
+my ($base, $m) = RT::Test->started_ok;
+
+my $cf = RT::Test->load_or_create_custom_field(
+ Name => 'Yaks',
+ Type => 'FreeformSingle',
+ Pattern => '(?#Digits)^\d+$',
+ Queue => 0,
+ LookupType => 'RT::Queue-RT::Ticket',
+);
+ok $cf && $cf->id, "Created CF with Pattern";
+
+my $ticket = RT::Test->create_ticket(
+ Queue => 1,
+ Subject => 'a test ticket',
+);
+ok $ticket && $ticket->id, "Created ticket";
+
+$m->login;
+
+for my $page ("/Ticket/Create.html?Queue=1", "/Ticket/Modify.html?id=".$ticket->id) {
+ diag $page;
+ $m->get_ok($page, "Fetched $page");
+ $m->content_contains("Yaks");
+ $m->content_contains("Input must match [Digits]");
+ $m->content_lacks("cfinvalidfield");
+
+ my $cfinput = RT::Interface::Web::GetCustomFieldInputName(
+ Object => ( $page =~ /Create/ ? RT::Ticket->new( RT->SystemUser ) : $ticket ),
+ CustomField => $cf,
+ );
+ $m->submit_form_ok({
+ with_fields => {
+ $cfinput => "too many",
+ "${cfinput}-Magic" => "1",
+ },
+ });
+ $m->content_contains("Input must match [Digits]");
+ $m->content_contains("cfinvalidfield");
+
+ $m->submit_form_ok({
+ with_fields => {
+ $cfinput => "42",
+ "${cfinput}-Magic" => "1",
+ },
+ });
+
+ if ($page =~ /Create/) {
+ $m->content_like(qr/Ticket \d+ created/, "Created ticket");
+ } else {
+ $m->content_contains("Yaks 42 added", "Updated ticket");
+ $m->content_contains("Input must match [Digits]");
+ $m->content_lacks("cfinvalidfield");
+ }
+}
+
+diag "Quick ticket creation";
+{
+ $m->get_ok("/");
+ $m->submit_form_ok({
+ with_fields => {
+ Subject => "test quick create",
+ QuickCreate => 1,
+ },
+ });
+ my $tickets = RT::Tickets->new(RT->SystemUser);
+ $tickets->FromSQL("Subject = 'test quick create'");
+ is $tickets->Count, 0, "No ticket created";
+
+ like $m->uri, qr/Ticket\/Create\.html/, "Redirected to the ticket create page";
+ $m->content_contains("Yaks: Input must match", "Found CF validation error");
+ $m->content_contains("test quick create", "Found prefilled Subject");
+}
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/cf_render_type.t b/rt/t/web/cf_render_type.t
index 8d8efa897..42efb32bc 100644
--- a/rt/t/web/cf_render_type.t
+++ b/rt/t/web/cf_render_type.t
@@ -11,7 +11,7 @@ my $cf_name = 'test render type';
my $cfid;
diag "Create a CF";
{
- $m->follow_link( id => 'tools-config-custom-fields-create');
+ $m->follow_link( id => 'admin-custom-fields-create');
$m->submit_form(
form_name => "ModifyCustomField",
fields => {
diff --git a/rt/t/web/cf_select_one.t b/rt/t/web/cf_select_one.t
index 92fcf53f3..4f81e2a1a 100644
--- a/rt/t/web/cf_select_one.t
+++ b/rt/t/web/cf_select_one.t
@@ -2,7 +2,7 @@
use strict;
use warnings;
-use RT::Test tests => 45;
+use RT::Test tests => undef;
my ($baseurl, $m) = RT::Test->started_ok;
ok $m->login, 'logged in as root';
@@ -12,7 +12,7 @@ my $cf_name = 'test select one value';
my $cfid;
diag "Create a CF";
{
- $m->follow_link( id => 'tools-config-custom-fields-create');
+ $m->follow_link( id => 'admin-custom-fields-create');
$m->submit_form(
form_name => "ModifyCustomField",
fields => {
@@ -49,10 +49,10 @@ ok $queue && $queue->id, 'loaded or created queue';
diag "apply the CF to General queue";
{
- $m->follow_link( id => 'tools-config-queues');
+ $m->follow_link( id => 'admin-queues');
$m->follow_link( text => 'General' );
$m->title_is(q/Configuration for queue General/, 'admin-queue: general');
- $m->follow_link( id => 'page-ticket-custom-fields');
+ $m->follow_link( id => 'page-custom-fields-tickets');
$m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid');
$m->form_name('EditCustomFields');
@@ -151,3 +151,32 @@ diag "check that we can set empty value when the current is 0";
undef, 'API returns correct value';
}
+diag 'retain selected cf values when adding attachments';
+{
+ my ( $ticket, $id );
+ $m->submit_form(
+ form_name => "CreateTicketInQueue",
+ fields => { Queue => 'General' },
+ );
+ $m->content_contains($cf_name, 'Found cf field' );
+
+ $m->submit_form_ok(
+ { form_name => "TicketCreate",
+ fields => {
+ Subject => 'test defaults',
+ Content => 'test',
+ "Object-RT::Ticket--CustomField-$cfid-Values" => 'qwe',
+ },
+ button => 'AddMoreAttach',
+ },
+ 'Add an attachment on create'
+ );
+
+ $m->form_name("TicketCreate");
+ is($m->value("Object-RT::Ticket--CustomField-$cfid-Values"),
+ "qwe",
+ "Selected value still on form" );
+}
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/cf_textarea.t b/rt/t/web/cf_textarea.t
new file mode 100644
index 000000000..d11bda4d5
--- /dev/null
+++ b/rt/t/web/cf_textarea.t
@@ -0,0 +1,75 @@
+use strict;
+use warnings;
+
+use RT::Test tests => 'no_declare';
+
+my $content = join ' ', ('The quick brown fox jumps over the lazy dog.') x 5;
+$content = join "\n\n", $content, $content, $content;
+
+my ($base, $m) = RT::Test->started_ok;
+
+$m->login;
+
+my $ticket = RT::Test->create_ticket(
+ Queue => 1,
+ Subject => 'a test ticket',
+);
+ok $ticket && $ticket->id, "Created ticket";
+
+my $EditUrl = "/Ticket/Modify.html?id=" . $ticket->id;
+
+my $cfs = {
+ area => {
+ type => 'Text',
+ name => 'TheTextarea',
+ },
+ text => {
+ type => 'FreeformSingle',
+ name => 'TheControlField',
+ },
+};
+
+while ( my( $label, $data ) = each %$cfs ) {
+ my $cf = $data->{obj} = RT::Test->load_or_create_custom_field(
+ Name => $data->{name},
+ Type => $data->{type},
+ Queue => 0,
+ LookupType => 'RT::Queue-RT::Ticket',
+ );
+ ok $cf && $cf->id, "Created $data->{type} CF";
+
+ # get cf input field name
+ $data->{input} = RT::Interface::Web::GetCustomFieldInputName(
+ Object => $ticket,
+ CustomField => $cf,
+ );
+}
+
+# open ticket "Basics" page
+$m->get_ok($EditUrl, "Fetched $EditUrl");
+$m->content_contains($_->{name} . ':') for ( values %$cfs );
+
+$m->submit_form_ok({
+ with_fields => {
+ $cfs->{area}{input} => $content,
+ $cfs->{area}{input} . '-Magic' => "1",
+ $cfs->{text}{input} => 'value a',
+ $cfs->{text}{input} . '-Magic' => "1",
+ },
+}, 'submitted form to initially set CFs');
+$m->content_contains('<li>TheControlField value a added</li>');
+$m->content_contains("<li>TheTextarea $content added</li>", 'content found');
+
+# http://issues.bestpractical.com/Ticket/Display.html?id=30378
+# #30378: RT 4.2.6 - Very long text fields get updated even when they haven't changed
+$m->submit_form_ok({
+ with_fields => {
+ $cfs->{text}{input} => 'value b',
+ $cfs->{text}{input} . '-Magic' => "1",
+ },
+}, 'submitted form to initially set CFs');
+$m->content_contains('<li>TheControlField value a changed to value b</li>');
+$m->content_lacks("<li>TheTextarea $content changed to $content</li>", 'textarea wasnt updated');
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/cf_values_class.t b/rt/t/web/cf_values_class.t
index 646642781..6535c505b 100644
--- a/rt/t/web/cf_values_class.t
+++ b/rt/t/web/cf_values_class.t
@@ -14,7 +14,7 @@ my $cf_name = 'test values class';
my $cfid;
diag "Create a CF";
{
- $m->follow_link( id => 'tools-config-custom-fields-create');
+ $m->follow_link( id => 'admin-custom-fields-create');
$m->submit_form(
form_name => "ModifyCustomField",
fields => {
diff --git a/rt/t/web/charting.t b/rt/t/web/charting.t
index e19ec41ae..5131f9cef 100644
--- a/rt/t/web/charting.t
+++ b/rt/t/web/charting.t
@@ -1,16 +1,10 @@
use strict;
use warnings;
-BEGIN {
- require RT::Test;
-
- if (eval { require GD; 1 }) {
- RT::Test->import(plan => 'no_plan');
- }
- else {
- RT::Test->import(skip_all => 'GD required.');
- }
-}
+use RT::Test tests => undef;
+
+plan skip_all => 'GD required'
+ unless GD->require;
for my $n (1..7) {
my $ticket = RT::Ticket->new( RT->SystemUser );
@@ -35,8 +29,8 @@ ok( $m->login, "Logged in" );
# Test that defaults work
$m->get_ok( "/Search/Chart.html?Query=id>0" );
-$m->content_like(qr{<th[^>]*>Queue\s*</th>\s*<th[^>]*>Tickets\s*</th>}, "Grouped by queue");
-$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found results in table");
+$m->content_like(qr{<th[^>]*>Status\s*</th>\s*<th[^>]*>Ticket count\s*</th>}, "Grouped by status");
+$m->content_like(qr{new\s*</th>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found results in table");
$m->content_like(qr{<img src="/Search/Chart\?}, "Found image");
$m->get_ok( "/Search/Chart?Query=id>0" );
@@ -45,35 +39,41 @@ ok( length($m->content), "Has content" );
# Group by Queue
-$m->get_ok( "/Search/Chart.html?Query=id>0&PrimaryGroupBy=Queue" );
-$m->content_like(qr{<th[^>]*>Queue\s*</th>\s*<th[^>]*>Tickets\s*</th>}, "Grouped by queue");
-$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found results in table");
+$m->get_ok( "/Search/Chart.html?Query=id>0&GroupBy=Queue" );
+$m->content_like(qr{<th[^>]*>Queue\s*</th>\s*<th[^>]*>Ticket count\s*</th>}, "Grouped by queue");
+$m->content_like(qr{General\s*</th>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found results in table");
$m->content_like(qr{<img src="/Search/Chart\?}, "Found image");
-$m->get_ok( "/Search/Chart?Query=id>0&PrimaryGroupBy=Queue" );
+$m->get_ok( "/Search/Chart?Query=id>0&GroupBy=Queue" );
is( $m->content_type, "image/png" );
ok( length($m->content), "Has content" );
# Group by Requestor email
-$m->get_ok( "/Search/Chart.html?Query=id>0&PrimaryGroupBy=Requestor.EmailAddress" );
-$m->content_like(qr{<th[^>]*>Requestor\.EmailAddress\s*</th>\s*<th[^>]*>Tickets\s*</th>},
+$m->get_ok( "/Search/Chart.html?Query=id>0&GroupBy=Requestor.EmailAddress" );
+$m->content_like(qr{<th[^>]*>Requestor\s+EmailAddress</th>\s*<th[^>]*>Ticket count\s*</th>},
"Grouped by requestor");
-$m->content_like(qr{root0\@localhost</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>3</a>}, "Found results in table");
+$m->content_like(qr{root0\@localhost\s*</th>\s*<td[^>]*>\s*<a[^>]*>3</a>}, "Found results in table");
$m->content_like(qr{<img src="/Search/Chart\?}, "Found image");
-$m->get_ok( "/Search/Chart?Query=id>0&PrimaryGroupBy=Requestor.Email" );
+$m->get_ok( "/Search/Chart?Query=id>0&GroupBy=Requestor.EmailAddress" );
is( $m->content_type, "image/png" );
ok( length($m->content), "Has content" );
-
# Group by Requestor phone -- which is bogus, and falls back to queue
-$m->get_ok( "/Search/Chart.html?Query=id>0&PrimaryGroupBy=Requestor.Phone" );
-$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>7</a>},
+
+$m->get_ok( "/Search/Chart.html?Query=id>0&GroupBy=Requestor.Phone" );
+$m->warning_like( qr{'Requestor\.Phone' is not a valid grouping for reports} );
+
+TODO: {
+ local $TODO = "UI should show that it's group by status";
+ $m->content_like(qr{new\s*</th>\s*<td[^>]*>\s*<a[^>]*>7</a>},
"Found queue results in table, as a default");
+}
$m->content_like(qr{<img src="/Search/Chart\?}, "Found image");
-$m->get_ok( "/Search/Chart?Query=id>0&PrimaryGroupBy=Requestor.Phone" );
+$m->get_ok( "/Search/Chart?Query=id>0&GroupBy=Requestor.Phone" );
+$m->warning_like( qr{'Requestor\.Phone' is not a valid grouping for reports} );
is( $m->content_type, "image/png" );
ok( length($m->content), "Has content" );
@@ -93,3 +93,6 @@ $advanced = $m->find_link( text => 'Advanced' )->URI->equery;
like( $advanced, qr{Query=id%3E0},
'Advanced link still has Query param with id search'
);
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/class_create.t b/rt/t/web/class_create.t
index 2d9ad035d..7723d7aa9 100644
--- a/rt/t/web/class_create.t
+++ b/rt/t/web/class_create.t
@@ -13,7 +13,7 @@ my $class_name = 'test class';
my $class_id;
diag "Create a class";
{
- $m->follow_link( id => 'tools-config-articles-classes-create');
+ $m->follow_link( id => 'admin-articles-classes-create');
# Test class form validation
$m->submit_form(
diff --git a/rt/t/web/command_line.t b/rt/t/web/command_line.t
index a5c52d261..47f672856 100644
--- a/rt/t/web/command_line.t
+++ b/rt/t/web/command_line.t
@@ -111,11 +111,11 @@ ok($val,$msg);
# add a comment to ticket
expect_send("comment -m 'comment-$$' $ticket_id", "Adding a comment...");
- expect_like(qr/Message recorded/, "Added the comment");
+ expect_like(qr/Comments added/, "Added the comment");
### should test to make sure it actually got added
# add correspondance to ticket (?)
expect_send("correspond -m 'correspond-$$' $ticket_id", "Adding correspondence...");
- expect_like(qr/Message recorded/, "Added the correspondence");
+ expect_like(qr/Correspondence added/, "Added the correspondence");
### should test to make sure it actually got added
my $test_email = RT::Test::get_relocatable_file('lorem-ipsum',
@@ -124,7 +124,7 @@ ok($val,$msg);
# text attachment
check_attachment($test_email);
# binary attachment
- check_attachment($RT::MasonComponentRoot.'/NoAuth/images/bpslogo.png');
+ check_attachment($RT::StaticPath . '/images/bpslogo.png');
# change a ticket's Owner
expect_send("edit ticket/$ticket_id set owner=root", 'Changing owner...');
@@ -158,7 +158,7 @@ expect_send("show ticket/$ticket_id -f queue", 'Verifying change...');
expect_like(qr/Queue: EditedQueue$$/, 'Verified change');
# cannot move ticket to a nonexistent queue
expect_send("edit ticket/$ticket_id set queue=nonexistent-$$", 'Changing to nonexistent queue...');
-expect_like(qr/queue does not exist/i, 'Errored out');
+expect_like(qr/Queue nonexistent-$$ does not exist/i, 'Errored out');
expect_send("show ticket/$ticket_id -f queue", 'Verifying lack of change...');
expect_like(qr/Queue: EditedQueue$$/, 'Verified lack of change');
@@ -213,7 +213,7 @@ expect_send("edit ticket/$ticket_id set CF-myCF$$=1,2,3", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
expect_like(qr/\QCF.{myCF$$}\E: 1,2,3/i, 'Verified change');
-expect_send("edit ticket/$ticket_id set CF-myCF$$=\"1's,2,3\"", 'Changing CF...');
+expect_send(qq{edit ticket/$ticket_id set CF-myCF$$="1's,2,3"}, 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
expect_like(qr/\QCF.{myCF$$}\E: 1's,2,3/i, 'Verified change');
@@ -238,32 +238,85 @@ expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
expect_like(qr/\QCF.{MultipleCF$$}\E: b,\s*c,\s*o/i, 'Verified multiple cf change');
-expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"'a,b,c'\" ", 'Changing CF...');
-expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
-expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change');
-expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=a", 'Changing CF...');
-expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
-expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change');
+sub multi_round_trip {
+ my ($op, $value, $regex) = @_;
+ $Test::Builder::Level++;
+ # The outer double quotes are for the argument parsing that the
+ # command-line does; the extra layer of escaping is to for them, as
+ # well. It is equivilent to the quoting that the shell would
+ # require.
+ my $quoted = $value;
+ $quoted =~ s/(["\\])/\\$1/g;
+ expect_send(qq{edit ticket/$ticket_id $op CF.{MultipleCF$$}="$quoted"}, qq{CF $op $value});
+ expect_like(qr/Ticket $ticket_id updated/, qq{Got expected "updated" answer});
+ expect_send(qq{show ticket/$ticket_id -f CF.{MultipleCF$$}}, qq{Sent "show"});
+ expect_like(qr/\QCF.{MultipleCF$$}\E: $regex$/i, qq{Answer matches $regex});
+}
-expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=q{a,b,c}", 'Changing CF...');
-expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
-expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change');
-expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=a", 'Changing CF...');
-expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
-expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change');
-expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=\"'a,b,c'\"", 'Changing CF...');
+# Test simple quoting
+my $ticket = RT::Ticket->new($RT::SystemUser);
+$ticket->Load($ticket_id);
+multi_round_trip(set => q|'a,b,c'|, qr/'a,b,c'/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has only one CF value");
+is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected");
+
+multi_round_trip(del => q|a|, qr/'a,b,c'/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value");
+is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected");
+
+multi_round_trip(set => q|q{a,b,c}|, qr/'a,b,c'/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value");
+is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected");
+
+multi_round_trip(del => q|a|, qr/'a,b,c'/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value");
+is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected");
+
+multi_round_trip(del => q|'a,b,c'|, qr/\s*/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 0, "Now has no CF values");
+
+multi_round_trip(set => q|q{1,2's,3}|, qr/'1,2\\'s,3'/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value");
+is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{1,2's,3}, "And that CF value is as expected");
+
+multi_round_trip(del => q|q{1,2's,3}|, qr/\s*/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 0, "Now has no CF values");
+
+# Test escaping of quotes - generate (foo)(bar') with no escapes
+multi_round_trip(set => q|'foo',bar'|, qr/foo,bar'/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two values");
+is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo|, "Direct value checks out");
+is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content, q|bar'|, "Direct value checks out");
+multi_round_trip(del => q|bar'|, qr/foo/);
+
+# With one \, generate (foo',bar)
+
+# We obviously need two \s in the following q|| string in order to
+# generate a string with one actual \ in it; this causes the string to,
+# in general, have twice as many \s in it as we wish to test.
+multi_round_trip(set => q|'foo\\',bar'|, qr/'foo\\',bar'/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has one value");
+is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo',bar|, "Direct value checks out");
+
+# With two \, generate (foo\)(bar')
+multi_round_trip(set => q|'foo\\\\',bar'|, qr/foo\\,bar'/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two values");
+is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo\\|, "Direct value checks out");
+is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content, q|bar'|, "Direct value checks out");
+multi_round_trip(del => q|bar'|, qr/foo\\/);
+
+# With three \, generate (foo\',bar)
+multi_round_trip(set => q|'foo\\\\\\',bar'|, qr/'foo\\\\\\',bar'/);
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has one value");
+is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo\\',bar|, "Direct value checks out");
+
+# Check that we don't infinite-loop on 'foo'bar,baz; this should be ('foo'bar)(baz)
+expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"'foo'bar,baz\"", 'Changing CF to have quotes not at commas');
expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
-expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/\QCF.{MultipleCF$$}\E: \s*$/i, 'Verified change');
+is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two value");
+is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|'foo'bar|, "Direct value checks out");
+is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content, q|baz|, "Direct value checks out");
-expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"q{1,2's,3}\"", 'Changing CF...');
-expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
-expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/\QCF.{MultipleCF$$}\E: '1,2\\'s,3'/i, 'Verified change');
# ...
# change a ticket's ...[other properties]...
@@ -401,9 +454,9 @@ expect_send("merge $merge_ticket_B $merge_ticket_A", 'Merging the tickets...');
expect_like(qr/Merge completed/, 'Merged the tickets');
expect_send("show ticket/$merge_ticket_A/history", 'Checking merge on first ticket');
-expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in first ticket');
+expect_like(qr/Merged into #$merge_ticket_A: CLIMergeTest1-$$ by root/, 'Merge recorded in first ticket');
expect_send("show ticket/$merge_ticket_B/history", 'Checking merge on second ticket');
-expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in second ticket');
+expect_like(qr/Merged into #$merge_ticket_A: CLIMergeTest1-$$ by root/, 'Merge recorded in second ticket');
{
# create a user; give them privileges to take and steal
@@ -522,7 +575,7 @@ sub check_attachment {
my $attachment_path = shift;
(my $filename = $attachment_path) =~ s/.*\/(.*)$/$1/;
expect_send("comment -m 'attach file' -a $attachment_path $ticket_id", "Adding an attachment ($filename)");
- expect_like(qr/Message recorded/, "Added the attachment");
+ expect_like(qr/Comments added/, "Added the attachment");
expect_send("show ticket/$ticket_id/attachments","Finding Attachment");
my $attachment_regex = qr/(\d+):\s+$filename/;
expect_like($attachment_regex,"Attachment Uploaded");
@@ -536,7 +589,11 @@ sub check_attachment {
TODO: {
local $TODO = "Binary PNG content is getting mangled somewhere along the way"
if $attachment_path =~ /\.png$/;
- expect_is($attachment_content,"Attachment contains original text");
+ is(
+ MIME::Base64::encode_base64(Test::Expect::before()),
+ MIME::Base64::encode_base64($attachment_content),
+ "Attachment contains original text"
+ );
}
}
diff --git a/rt/t/web/compilation_errors.t b/rt/t/web/compilation_errors.t
index 126d33691..e4845e0de 100644
--- a/rt/t/web/compilation_errors.t
+++ b/rt/t/web/compilation_errors.t
@@ -6,7 +6,7 @@ BEGIN {
sub wanted {
-f && /\.html$/ && $_ !~ /Logout.html$/ && $File::Find::dir !~ /RichText/;
}
- my $tests = 8;
+ my $tests = 7;
find( sub { wanted() and $tests += 4 }, 'share/html/' );
plan tests => $tests + 1; # plus one for warnings check
}
@@ -36,12 +36,10 @@ is($agent->status, 200, "Fetched the page ok");
$agent->content_contains('Logout', "Found a logout link");
-find ( sub { wanted() and test_get($agent, $File::Find::name) } , 'share/html/');
+find ( { wanted => sub { wanted() and test_get($agent, $File::Find::name) }, no_chdir => 1 } , 'share/html/');
-TODO: {
- local $TODO = "we spew *lots* of undef warnings";
- $agent->no_warnings_ok;
-};
+# We expect to spew a lot of warnings; toss them away
+$agent->get_warnings;
sub test_get {
my $agent = shift;
diff --git a/rt/t/web/config_tab_right.t b/rt/t/web/config_tab_right.t
index 69bf80c69..df146903a 100644
--- a/rt/t/web/config_tab_right.t
+++ b/rt/t/web/config_tab_right.t
@@ -19,7 +19,7 @@ my ($baseurl, $m) = RT::Test->started_ok;
ok $m->login($uname, $upass), "logged in";
{
- $m->content_lacks('Configuration', 'no configuration tab');
+ $m->content_lacks('li-admin', 'no Admin tab');
$m->get('/Admin/');
is $m->status, 403, 'no access to /Admin/';
}
@@ -32,9 +32,9 @@ RT::Test->set_rights(
{
$m->get('/');
- $m->content_contains('Configuration', 'configuration tab is there');
+ $m->content_contains('li-admin', 'admin tab is there');
- $m->follow_link_ok({text => 'Configuration'});
+ $m->follow_link_ok({text => 'Admin'});
is $m->status, 200, 'user has access to /Admin/';
}
diff --git a/rt/t/web/crypt-gnupg.t b/rt/t/web/crypt-gnupg.t
index 85e090cbc..995b45d99 100644
--- a/rt/t/web/crypt-gnupg.t
+++ b/rt/t/web/crypt-gnupg.t
@@ -2,7 +2,7 @@ use strict;
use warnings;
use RT::Test::GnuPG
- tests => 104,
+ tests => undef,
gnupg_options => {
passphrase => 'recipient',
'trust-model' => 'always',
@@ -17,9 +17,9 @@ RT->Config->Set( CorrespondAddress => 'general@example.com');
RT->Config->Set( DefaultSearchResultFormat => qq{
'<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#',
'<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
- 'OO-__OwnerName__-O',
+ 'OO-__Owner__-O',
'OR-__Requestors__-O',
- 'KO-__KeyOwnerName__-K',
+ 'KO-__KeyOwner__-K',
'KR-__KeyRequestors__-K',
Status});
@@ -101,7 +101,7 @@ MAIL
my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
is( $msg->GetHeader('X-RT-Privacy'),
- 'PGP',
+ 'GnuPG',
"RT's outgoing mail has crypto"
);
is( $msg->GetHeader('X-RT-Incoming-Encryption'),
@@ -169,7 +169,7 @@ MAIL
my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
is( $msg->GetHeader('X-RT-Privacy'),
- 'PGP',
+ 'GnuPG',
"RT's outgoing mail has crypto"
);
is( $msg->GetHeader('X-RT-Incoming-Encryption'),
@@ -241,7 +241,7 @@ MAIL
my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
is( $msg->GetHeader('X-RT-Privacy'),
- 'PGP',
+ 'GnuPG',
"RT's outgoing mail has crypto"
);
is( $msg->GetHeader('X-RT-Incoming-Encryption'),
@@ -307,7 +307,7 @@ MAIL
my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
is( $msg->GetHeader('X-RT-Privacy'),
- 'PGP',
+ 'GnuPG',
"RT's outgoing mail has crypto"
);
is( $msg->GetHeader('X-RT-Incoming-Encryption'),
@@ -351,8 +351,13 @@ $nokey->PrincipalObj->GrantRight(Right => 'CreateTicket');
$nokey->PrincipalObj->GrantRight(Right => 'OwnTicket');
my $tick = RT::Ticket->new( RT->SystemUser );
-$tick->Create(Subject => 'owner lacks pubkey', Queue => 'general',
- Owner => $nokey);
+warning_like {
+ $tick->Create(Subject => 'owner lacks pubkey', Queue => 'general',
+ Owner => $nokey);
+} [
+ qr/nokey\@example.com: skipped: public key not found/,
+ qr/Recipient 'nokey\@example.com' is unusable/,
+];
ok(my $id = $tick->id, 'created ticket for owner-without-pubkey');
$tick = RT::Ticket->new( RT->SystemUser );
@@ -426,25 +431,36 @@ $m->get("$baseurl/Search/Simple.html?q=General");
my $content = $m->content;
$content =~ s/&#40;/(/g;
$content =~ s/&#41;/)/g;
-
-like($content, qr/OO-Nobody-O/, "original OwnerName untouched");
-like($content, qr/OO-nokey-O/, "original OwnerName untouched");
-like($content, qr/OO-root-O/, "original OwnerName untouched");
-
-like($content, qr/OR-recipient\@example.com-O/, "original Requestors untouched");
-like($content, qr/OR-nokey\@example.com-O/, "original Requestors untouched");
-
-like($content, qr/KO-root-K/, "KeyOwnerName does not issue no-pubkey warning for recipient");
-like($content, qr/KO-nokey \(no pubkey!\)-K/, "KeyOwnerName issues no-pubkey warning for root");
-like($content, qr/KO-Nobody \(no pubkey!\)-K/, "KeyOwnerName issues no-pubkey warning for nobody");
-
-like($content, qr/KR-recipient\@example.com-K/, "KeyRequestors does not issue no-pubkey warning for recipient\@example.com");
-
-like($content, qr/KR-general\@example.com-K/, "KeyRequestors does not issue no-pubkey warning for general\@example.com");
-like($content, qr/KR-nokey\@example.com \(no pubkey!\)-K/, "KeyRequestors DOES issue no-pubkey warning for nokey\@example.com");
+$content =~ s/<(a|span)\b[^>]+>//g;
+$content =~ s/<\/(a|span)>//g;
+$content =~ s/&lt;/</g;
+$content =~ s/&gt;/>/g;
+
+like($content, qr/OO-Nobody in particular-O/,
+ "original Owner untouched");
+like($content, qr/OO-nokey-O/,
+ "original Owner untouched");
+like($content, qr/OO-root \(Enoch Root\)-O/,
+ "original Owner untouched");
+like($content, qr/OR-<recipient\@example\.com>-O/,
+ "original Requestors untouched");
+like($content, qr/OR-nokey-O/,
+ "original Requestors untouched");
+
+like($content, qr/KO-Nobody in particular \(no pubkey!\)-K/,
+ "KeyOwner issues no-pubkey warning for nobody");
+like($content, qr/KO-nokey \(no pubkey!\)-K/,
+ "KeyOwner issues no-pubkey warning for root");
+like($content, qr/KO-root \(Enoch Root\)-K/,
+ "KeyOwner does not issue no-pubkey warning for recipient");
+like($content, qr/KR-<recipient\@example\.com>-K/,
+ "KeyRequestors does not issue no-pubkey warning for recipient\@example.com");
+like($content, qr/KR-nokey \(no pubkey!\)-K/,
+ "KeyRequestors DOES issue no-pubkey warning for nokey\@example.com");
$m->next_warning_like(qr/public key not found/);
-$m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
$m->next_warning_like(qr/public key not found/);
-$m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
$m->no_leftover_warnings_ok;
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/csrf.t b/rt/t/web/csrf.t
index 24aae40a1..9d95d0685 100644
--- a/rt/t/web/csrf.t
+++ b/rt/t/web/csrf.t
@@ -99,9 +99,9 @@ $m->title_is('Possible cross-site request forgery');
my $link = $m->find_link(text_regex => qr{resume your request});
(my $broken_url = $link->url) =~ s/(CSRF_Token)=\w+/$1=crud/;
$m->get_ok($broken_url);
-$m->content_contains("Queue could not be loaded");
+$m->content_like(qr/Queue\s+could not be loaded/);
$m->title_is('RT Error');
-$m->warning_like(qr/Queue could not be loaded/);
+$m->warning_like(qr/Queue\s+could not be loaded/);
# The token doesn't work for other pages, or other arguments to the same page.
$m->add_header(Referer => undef);
@@ -134,7 +134,7 @@ $m->content_contains("Create a new ticket", 'ticket create page');
$m->form_name('TicketCreate');
$m->field('Subject', 'Attachments test');
-my $logofile = "$RT::MasonComponentRoot/NoAuth/images/bpslogo.png";
+my $logofile = "$RT::StaticPath/images/bpslogo.png";
open LOGO, "<", $logofile or die "Can't open logo file: $!";
binmode LOGO;
my $logo_contents = do {local $/; <LOGO>};
diff --git a/rt/t/web/custom_search.t b/rt/t/web/custom_search.t
index bf7d659cd..75f832d30 100644
--- a/rt/t/web/custom_search.t
+++ b/rt/t/web/custom_search.t
@@ -11,7 +11,7 @@ my $url = $m->rt_base_url;
my $t = RT::Ticket->new(RT->SystemUser);
$t->Create(Subject => 'for custom search'.$$, Queue => 'general',
- Owner => 'root', Requestor => 'customsearch@localhost');
+ Owner => 'root', Requestor => 'customsearch@localhost');
ok(my $id = $t->id, 'created ticket for custom search');
ok $m->login, 'logged in';
diff --git a/rt/t/web/dashboards-basics.t b/rt/t/web/dashboards-basics.t
index edb706810..c3533a3f1 100644
--- a/rt/t/web/dashboards-basics.t
+++ b/rt/t/web/dashboards-basics.t
@@ -41,24 +41,24 @@ $m->content_lacks('<a href="/Dashboards/Modify.html?Create=1">New</a>',
$m->no_warnings_ok;
$m->get_ok($url."Dashboards/Modify.html?Create=1");
-$m->content_contains("Permission denied");
+$m->content_contains("Permission Denied");
$m->content_lacks("Save Changes");
-$m->warning_like(qr/Permission denied/, "got a permission denied warning");
+$m->warning_like(qr/Permission Denied/, "got a permission denied warning");
$user_obj->PrincipalObj->GrantRight(Right => 'ModifyOwnDashboard', Object => $RT::System);
# Modify itself is no longer good enough, you need Create
$m->get_ok($url."Dashboards/Modify.html?Create=1");
-$m->content_contains("Permission denied");
+$m->content_contains("Permission Denied");
$m->content_lacks("Save Changes");
-$m->warning_like(qr/Permission denied/, "got a permission denied warning");
+$m->warning_like(qr/Permission Denied/, "got a permission denied warning");
$user_obj->PrincipalObj->GrantRight(Right => 'CreateOwnDashboard', Object => $RT::System);
$m->get_ok($url."Dashboards/Modify.html?Create=1");
-$m->content_lacks("Permission denied");
+$m->content_lacks("Permission Denied");
$m->content_contains("Create");
$m->get_ok($url."Dashboards/index.html");
@@ -72,12 +72,12 @@ $m->content_contains("Saved dashboard different dashboard");
$user_obj->PrincipalObj->GrantRight(Right => 'SeeOwnDashboard', Object => $RT::System);
$m->get($url."Dashboards/index.html");
$m->follow_link_ok({ text => 'different dashboard'});
-$m->content_lacks("Permission denied", "we now have SeeOwnDashboard");
+$m->content_lacks("Permission Denied", "we now have SeeOwnDashboard");
$m->content_lacks('Delete', "Delete button hidden because we lack DeleteOwnDashboard");
$m->get_ok($url."Dashboards/index.html");
$m->content_contains("different dashboard", "we now have SeeOwnDashboard");
-$m->content_lacks("Permission denied");
+$m->content_lacks("Permission Denied");
$m->follow_link_ok({text => "different dashboard"});
$m->content_contains("Basics");
@@ -132,9 +132,9 @@ like($searches[1]->Name, qr/highest priority tickets I own/, "correct new search
my $ticket = RT::Ticket->new(RT->SystemUser);
$ticket->Create(
Queue => $queue->Id,
- Requestor => [ $user_obj->Name ],
- Owner => $user_obj,
- Subject => 'dashboard test',
+ Requestor => [ $user_obj->Name ],
+ Owner => $user_obj,
+ Subject => 'dashboard test',
);
$m->follow_link_ok({id => 'page-show'});
@@ -154,8 +154,8 @@ $m->content_contains("dashboard test", "ticket subject");
$m->get_ok("/Dashboards/Subscription.html?id=$id");
$m->form_name('SubscribeDashboard');
$m->click_button(name => 'Save');
-$m->content_contains("Permission denied");
-$m->warning_like(qr/Unable to subscribe to dashboard.*Permission denied/, "got a permission denied warning when trying to subscribe to a dashboard");
+$m->content_contains("Permission Denied");
+$m->warning_like(qr/Unable to subscribe to dashboard.*Permission Denied/, "got a permission denied warning when trying to subscribe to a dashboard");
$user_obj->Attributes->RedoSearch;
is($user_obj->Attributes->Named('Subscription'), 0, "no subscriptions");
@@ -172,7 +172,7 @@ $m->content_unlike( qr/Bookmarked Tickets.*Bookmarked Tickets/s,
$m->form_name('SubscribeDashboard');
$m->click_button(name => 'Save');
-$m->content_lacks("Permission denied");
+$m->content_lacks("Permission Denied");
$m->content_contains("Subscribed to dashboard different dashboard");
$user_obj->Attributes->RedoSearch;
@@ -183,9 +183,9 @@ $m->follow_link_ok({text => "Subscription"});
$m->content_contains("Modify the subscription to dashboard different dashboard");
$m->get_ok("/Dashboards/Modify.html?id=$id&Delete=1");
-$m->content_contains("Permission denied", "unable to delete dashboard because we lack DeleteOwnDashboard");
+$m->content_contains("Permission Denied", "unable to delete dashboard because we lack DeleteOwnDashboard");
-$m->warning_like(qr/Couldn't delete dashboard.*Permission denied/, "got a permission denied warning when trying to delete the dashboard");
+$m->warning_like(qr/Couldn't delete dashboard.*Permission Denied/, "got a permission denied warning when trying to delete the dashboard");
$user_obj->PrincipalObj->GrantRight(Right => 'DeleteOwnDashboard', Object => $RT::System);
diff --git a/rt/t/web/dashboards-groups.t b/rt/t/web/dashboards-groups.t
index db2fccf1c..9f1c37deb 100644
--- a/rt/t/web/dashboards-groups.t
+++ b/rt/t/web/dashboards-groups.t
@@ -79,7 +79,7 @@ $m->form_name('ModifyDashboard');
$m->field("Name" => 'inner dashboard');
$m->field("Privacy" => "RT::Group-" . $inner_group->Id);
$m->click_button(value => 'Create');
-$m->content_lacks("Permission denied", "we now have SeeGroupDashboard");
+$m->content_lacks("Permission Denied", "we now have SeeGroupDashboard");
$m->content_contains("Saved dashboard inner dashboard");
$m->content_lacks('Delete', "Delete button hidden because we lack DeleteDashboard");
@@ -95,15 +95,15 @@ is($dashboard->PossibleHiddenSearches, 0, "all searches are visible");
$m->get_ok("/Dashboards/Modify.html?id=$id");
$m->content_contains("inner dashboard", "we now have SeeGroupDashboard right");
-$m->content_lacks("Permission denied");
+$m->content_lacks("Permission Denied");
$m->content_contains('Subscription', "Subscription link not hidden because we have SubscribeDashboard");
$m->get_ok("/Dashboards/index.html");
$m->content_contains("inner dashboard", "We can see the inner dashboard from the UI");
-$m->get_ok("/index.html");
-$m->content_contains("inner dashboard", "We can see the inner dashboard from the menu drop-down");
+$m->get_ok("/Prefs/DashboardsInMenu.html");
+$m->content_contains("inner dashboard", "Can also see it in the menu options");
my ($group) = grep {$_->isa("RT::Group") and $_->Id == $inner_group->Id}
RT::Dashboard->new($currentuser)->_PrivacyObjects;
@@ -191,5 +191,5 @@ is_deeply(
$m->get_ok("/Dashboards/index.html");
$m->content_contains("inner dashboard", "The dashboards list includes superuser rights");
-$m->get_ok("/index.html");
+$m->get_ok("/Prefs/DashboardsInMenu.html");
$m->content_lacks("inner dashboard", "But the menu skips them");
diff --git a/rt/t/web/dashboards-in-menu.t b/rt/t/web/dashboards-in-menu.t
new file mode 100644
index 000000000..3126d55c3
--- /dev/null
+++ b/rt/t/web/dashboards-in-menu.t
@@ -0,0 +1,85 @@
+use strict;
+use warnings;
+
+use RT::Test tests => 31;
+my ($baseurl, $m) = RT::Test->started_ok;
+
+my $system_foo = RT::Dashboard->new($RT::SystemUser);
+$system_foo->Save(
+ Name => 'system foo',
+ Privacy => 'RT::System-' . $RT::System->id,
+);
+
+my $system_bar = RT::Dashboard->new($RT::SystemUser);
+$system_bar->Save(
+ Name => 'system bar',
+ Privacy => 'RT::System-' . $RT::System->id,
+);
+
+ok( $m->login(), "logged in" );
+
+diag "global setting";
+# in case "RT at a glance" contains dashboards stuff.
+$m->get_ok( $baseurl . "/Search/Simple.html" );
+ok( !$m->find_link( text => 'system foo' ), 'no system foo link' );
+$m->get_ok( $baseurl."/Admin/Global/DashboardsInMenu.html");
+
+my $form_name = 'SelectionBox-dashboards_in_menu';
+$m->form_name($form_name);
+
+$m->field('dashboards_in_menu-Available' => [$system_foo->id],);
+$m->click_button(name => 'add');
+$m->content_contains('Global dashboards in menu saved.', 'saved');
+
+$m->logout;
+ok( $m->login(), "relogged in" );
+
+$m->get_ok( $baseurl . "/Search/Simple.html" );
+$m->follow_link_ok( { text => 'system foo' }, 'follow system foo link' );
+$m->title_is( 'system foo Dashboard', 'got system foo dashboard page' );
+
+diag "setting in admin users";
+my $root = RT::CurrentUser->new( $RT::SystemUser );
+ok( $root->Load('root') );
+my $self_foo = RT::Dashboard->new($root);
+$self_foo->Save( Name => 'self foo', Privacy => 'RT::User-' . $root->id );
+my $self_bar = RT::Dashboard->new($root);
+$self_bar->Save( Name => 'self bar', Privacy => 'RT::User-' . $root->id );
+
+ok( !$m->find_link( text => 'self foo' ), 'no self foo link' );
+$m->get_ok( $baseurl."/Admin/Users/DashboardsInMenu.html?id=" . $root->id);
+$m->form_name($form_name);
+$m->field('dashboards_in_menu-Available' => [$self_foo->id]);
+$m->click_button(name => 'add');
+$m->content_contains( 'Preferences saved for dashboards in menu.',
+ 'prefs saved' );
+$m->form_name($form_name);
+$m->field('dashboards_in_menu-Selected' => [$system_foo->id]);
+$m->content_contains( 'Preferences saved for dashboards in menu.',
+ 'prefs saved' );
+$m->click_button(name => 'remove');
+
+$m->logout;
+ok( $m->login(), "relogged in" );
+$m->get_ok( $baseurl . "/Search/Simple.html" );
+ok( !$m->find_link( text => 'system foo' ), 'no system foo link' );
+$m->follow_link_ok( { text => 'self foo' }, 'follow self foo link' );
+$m->title_is( 'self foo Dashboard', 'got self foo dashboard page' );
+
+diag "setting in prefs";
+$m->get_ok( $baseurl."/Prefs/DashboardsInMenu.html");
+$m->form_name($form_name);
+$m->field('dashboards_in_menu-Available' => [$self_bar->id]);
+$m->click_button(name => 'add');
+$m->content_contains( 'Preferences saved for dashboards in menu.',
+ 'prefs saved' );
+$m->follow_link_ok( { text => 'self bar' }, 'follow self bar link' );
+$m->title_is( 'self bar Dashboard', 'got self bar dashboard page' );
+$m->get_ok( $baseurl."/Prefs/DashboardsInMenu.html");
+$m->form_with_fields('Reset');
+$m->click;
+$m->content_contains( 'Preferences saved', 'prefs saved' );
+ok( $m->find_link( text => 'system foo' ), 'got system foo link' );
+ok( !$m->find_link( text => 'self foo' ), 'no self foo link' );
+ok( !$m->find_link( text => 'self bar' ), 'no self bar link' );
+
diff --git a/rt/t/web/dashboards-search-cache.t b/rt/t/web/dashboards-search-cache.t
index 517e26ee6..18989d54e 100644
--- a/rt/t/web/dashboards-search-cache.t
+++ b/rt/t/web/dashboards-search-cache.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test tests => 20;
+use RT::Test tests => 33;
my ($baseurl, $m) = RT::Test->started_ok;
my $url = $m->rt_base_url;
@@ -20,6 +20,16 @@ $m->form_name('BuildQuery');
$m->field(SavedSearchDescription => 'Original Name');
$m->click('SavedSearchSave');
+# create the inner dashboard
+$m->get_ok("$url/Dashboards/Modify.html?Create=1");
+$m->form_name('ModifyDashboard');
+$m->field('Name' => 'inner dashboard');
+$m->click_button(value => 'Create');
+$m->text_contains('Saved dashboard inner dashboard');
+
+my ($inner_id) = $m->content =~ /name="id" value="(\d+)"/;
+ok($inner_id, "got an ID, $inner_id");
+
# create a dashboard
$m->get_ok("$url/Dashboards/Modify.html?Create=1");
$m->form_name('ModifyDashboard');
@@ -34,16 +44,28 @@ ok($dashboard_id, "got an ID, $dashboard_id");
$m->follow_link_ok({text => 'Content'});
my $form = $m->form_name('Dashboard-Searches-body');
my @input = $form->find_input('Searches-body-Available');
-my ($search) =
+my ($search_value) =
map { ( $_->possible_values )[1] }
grep { ( $_->value_names )[1] =~ /Saved Search: Original Name/ } @input;
-$form->value('Searches-body-Available' => $search );
+$form->value('Searches-body-Available' => $search_value );
+$m->click_button(name => 'add');
+$m->text_contains('Dashboard updated');
+
+# add the dashboard to the dashboard
+$m->follow_link_ok({text => 'Content'});
+$form = $m->form_name('Dashboard-Searches-body');
+@input = $form->find_input('Searches-body-Available');
+my ($dashboard_value) =
+ map { ( $_->possible_values )[1] }
+ grep { ( $_->value_names )[1] =~ /Dashboard: inner dashboard/ } @input;
+$form->value('Searches-body-Available' => $dashboard_value );
$m->click_button(name => 'add');
$m->text_contains('Dashboard updated');
# subscribe to the dashboard
$m->follow_link_ok({text => 'Subscription'});
$m->text_contains('Saved Search: Original Name');
+$m->text_contains('Dashboard: inner dashboard');
$m->form_name('SubscribeDashboard');
$m->click_button(name => 'Save');
$m->text_contains('Subscribed to dashboard cachey dashboard');
@@ -52,10 +74,10 @@ $m->text_contains('Subscribed to dashboard cachey dashboard');
$m->follow_link_ok({text => 'Tickets'}, 'to query builder');
$form = $m->form_name('BuildQuery');
@input = $form->find_input('SavedSearchLoad');
-($search) =
+($search_value) =
map { ( $_->possible_values )[1] }
grep { ( $_->value_names )[1] =~ /Original Name/ } @input;
-$form->value('SavedSearchLoad' => $search );
+$form->value('SavedSearchLoad' => $search_value );
$m->click_button(value => 'Load');
$m->text_contains('Loaded saved search "Original Name"');
@@ -64,10 +86,24 @@ $m->field('SavedSearchDescription' => 'New Name');
$m->click_button(value => 'Update');
$m->text_contains('Updated saved search "New Name"');
+# rename the dashboard
+$m->get_ok("/Dashboards/Modify.html?id=$inner_id");
+$m->form_name('ModifyDashboard');
+$m->field('Name' => 'recursive dashboard');
+$m->click_button(value => 'Save Changes');
+$m->text_contains('Dashboard recursive dashboard updated');
+
# check subscription page again
$m->get_ok("/Dashboards/Subscription.html?id=$dashboard_id");
TODO: {
local $TODO = 'we cache search names too aggressively';
$m->text_contains('Saved Search: New Name');
$m->text_unlike(qr/Saved Search: Original Name/); # t-w-m lacks text_lacks
+
+ $m->text_contains('Dashboard: recursive dashboard');
+ $m->text_unlike(qr/Dashboard: inner dashboard/); # t-w-m lacks text_lacks
}
+
+$m->get_ok("/Dashboards/Render.html?id=$dashboard_id");
+$m->text_contains('New Name');
+$m->text_unlike(qr/Original Name/); # t-w-m lacks text_lacks
diff --git a/rt/t/web/gnupg-select-keys-on-create.t b/rt/t/web/gnupg-select-keys-on-create.t
index 8c1ae448c..e30b264d9 100644
--- a/rt/t/web/gnupg-select-keys-on-create.t
+++ b/rt/t/web/gnupg-select-keys-on-create.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test::GnuPG tests => 83, gnupg_options => { passphrase => 'rt-test' };
+use RT::Test::GnuPG tests => 79, gnupg_options => { passphrase => 'rt-test' };
use RT::Action::SendEmail;
my $queue = RT::Test->load_or_create_queue(
@@ -37,7 +37,7 @@ diag "check that signing doesn't work if there is no key";
{
RT::Test->import_gnupg_key('rt-recipient@example.com');
RT::Test->trust_gnupg_key('rt-recipient@example.com');
- my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-recipient@example.com');
+ my %res = RT::Crypt->GetKeysInfo( Key => 'rt-recipient@example.com' );
is $res{'info'}[0]{'TrustTerse'}, 'ultimate', 'ultimately trusted key';
}
@@ -66,11 +66,7 @@ diag "check that things don't work if there is no key";
my @mail = RT::Test->fetch_caught_mails;
ok !@mail, 'there are no outgoing emails';
- for (1 .. 4) {
- $m->next_warning_like(qr/public key not found/) ;
- $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
- }
-
+ $m->next_warning_like(qr/public key not found/) for 1 .. 4;
$m->no_leftover_warnings_ok;
}
@@ -78,7 +74,7 @@ diag "import first key of rt-test\@example.com";
my $fpr1 = '';
{
RT::Test->import_gnupg_key('rt-test@example.com', 'public');
- my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com');
+ my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' );
is $res{'info'}[0]{'TrustLevel'}, 0, 'is not trusted key';
$fpr1 = $res{'info'}[0]{'Fingerprint'};
}
@@ -127,7 +123,7 @@ diag "import a second key of rt-test\@example.com";
my $fpr2 = '';
{
RT::Test->import_gnupg_key('rt-test@example.com.2', 'public');
- my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com');
+ my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' );
is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key';
$fpr2 = $res{'info'}[2]{'Fingerprint'};
}
@@ -174,7 +170,7 @@ diag "check that things still doesn't work if two keys are not trusted";
{
RT::Test->lsign_gnupg_key( $fpr1 );
- my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com');
+ my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' );
ok $res{'info'}[0]{'TrustLevel'} > 0, 'trusted key';
is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key';
}
diff --git a/rt/t/web/gnupg-select-keys-on-update.t b/rt/t/web/gnupg-select-keys-on-update.t
index a5b01d3ae..a666851db 100644
--- a/rt/t/web/gnupg-select-keys-on-update.t
+++ b/rt/t/web/gnupg-select-keys-on-update.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test::GnuPG tests => 88, gnupg_options => { passphrase => 'rt-test' };
+use RT::Test::GnuPG tests => 86, gnupg_options => { passphrase => 'rt-test' };
use RT::Action::SendEmail;
@@ -52,7 +52,7 @@ diag "check that signing doesn't work if there is no key";
{
RT::Test->import_gnupg_key('rt-recipient@example.com');
RT::Test->trust_gnupg_key('rt-recipient@example.com');
- my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-recipient@example.com');
+ my %res = RT::Crypt->GetKeysInfo( Key => 'rt-recipient@example.com' );
is $res{'info'}[0]{'TrustTerse'}, 'ultimate', 'ultimately trusted key';
}
@@ -82,10 +82,7 @@ diag "check that things don't work if there is no key";
my @mail = RT::Test->fetch_caught_mails;
ok !@mail, 'there are no outgoing emails';
- for (1 .. 2) {
- $m->next_warning_like(qr/public key not found/);
- $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
- }
+ $m->next_warning_like(qr/public key not found/) for 1 .. 2;
$m->no_leftover_warnings_ok;
}
@@ -94,7 +91,7 @@ diag "import first key of rt-test\@example.com";
my $fpr1 = '';
{
RT::Test->import_gnupg_key('rt-test@example.com', 'public');
- my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com');
+ my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' );
is $res{'info'}[0]{'TrustLevel'}, 0, 'is not trusted key';
$fpr1 = $res{'info'}[0]{'Fingerprint'};
}
@@ -144,7 +141,7 @@ diag "import a second key of rt-test\@example.com";
my $fpr2 = '';
{
RT::Test->import_gnupg_key('rt-test@example.com.2', 'public');
- my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com');
+ my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' );
is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key';
$fpr2 = $res{'info'}[2]{'Fingerprint'};
}
@@ -192,7 +189,7 @@ diag "check that things still doesn't work if two keys are not trusted";
{
RT::Test->lsign_gnupg_key( $fpr1 );
- my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com');
+ my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' );
ok $res{'info'}[0]{'TrustLevel'} > 0, 'trusted key';
is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key';
}
@@ -253,7 +250,7 @@ diag "check that key selector works and we can select trusted key";
$m->select( 'UseKey-rt-test@example.com' => $fpr1 );
$m->click('SubmitTicket');
- $m->content_contains('Message recorded', 'Message recorded' );
+ $m->content_contains('Correspondence added', 'Correspondence added' );
my @mail = RT::Test->fetch_caught_mails;
ok @mail, 'there are some emails';
@@ -289,7 +286,7 @@ diag "check encrypting of attachments";
$m->select( 'UseKey-rt-test@example.com' => $fpr1 );
$m->click('SubmitTicket');
- $m->content_contains('Message recorded', 'Message recorded' );
+ $m->content_contains('Correspondence added', 'Correspondence added' );
my @mail = RT::Test->fetch_caught_mails;
ok @mail, 'there are some emails';
diff --git a/rt/t/web/group_create.t b/rt/t/web/group_create.t
index 548970d2d..f62e56595 100644
--- a/rt/t/web/group_create.t
+++ b/rt/t/web/group_create.t
@@ -13,7 +13,7 @@ my $group_name = 'test group';
my $group_id;
diag "Create a group";
{
- $m->follow_link( id => 'tools-config-groups-create');
+ $m->follow_link( id => 'admin-groups-create');
# Test group form validation
$m->submit_form(
diff --git a/rt/t/web/helpers-http-cache-headers.t b/rt/t/web/helpers-http-cache-headers.t
index 1731e9d17..1020832ca 100644
--- a/rt/t/web/helpers-http-cache-headers.t
+++ b/rt/t/web/helpers-http-cache-headers.t
@@ -23,10 +23,14 @@ ok $m->login, 'logged in';
my $docroot = join '/', qw(share html);
# find endpoints to loop over
-my @endpoints = ('/NoAuth/css/print.css');
+my @endpoints = (
+ "/NoAuth/css/aileron/squished-".("0"x32).".css",
+ '/static/images/bpslogo.png',
+);
find({
wanted => sub {
if ( -f $_ && $_ !~ m|autohandler$| ) {
+ return if m{/\.[^/]+\.sw[op]$}; # vim swap files
( my $endpoint = $_ ) =~ s|^$docroot||;
push @endpoints, $endpoint;
}
@@ -76,7 +80,7 @@ foreach my $endpoint ( @endpoints ) {
my $header_key = 'default';
if ( $endpoint =~ m|Autocomplete| ) {
$header_key = 'Autocomplete';
- } elsif ( $endpoint =~ m|NoAuth| ) {
+ } elsif ( $endpoint =~ m/NoAuth|static/ ) {
$header_key = 'NoAuth';
}
my $headers = $expected->{$header_key};
diff --git a/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/Default b/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/ModifyLoginRedirect
index 90278ae49..90278ae49 100644
--- a/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/Default
+++ b/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/ModifyLoginRedirect
diff --git a/rt/t/web/html_template.t b/rt/t/web/html_template.t
index a2764556f..108d83fa1 100644
--- a/rt/t/web/html_template.t
+++ b/rt/t/web/html_template.t
@@ -13,8 +13,8 @@ my $template = Encode::decode("UTF-8", "你好 éèà€");
my $subject = Encode::decode("UTF-8", "标题");
my $content = Encode::decode("UTF-8", "测试");
{
- $m->follow_link_ok( { id => 'tools-config-global-templates' }, '-> Templates' );
- $m->follow_link_ok( { text => 'Autoreply' }, '-> Autoreply' );
+ $m->follow_link_ok( { id => 'admin-global-templates' }, '-> Templates' );
+ $m->follow_link_ok( { text => 'Autoreply in HTML' }, '-> Autoreply in HTML' );
$m->submit_form(
form_name => 'ModifyTemplate',
diff --git a/rt/t/web/install.t b/rt/t/web/install.t
new file mode 100644
index 000000000..e33e58b16
--- /dev/null
+++ b/rt/t/web/install.t
@@ -0,0 +1,173 @@
+use strict;
+use warnings;
+use File::Spec;
+
+$ENV{RT_TEST_WEB_HANDLER} = 'plack+rt-server';
+use RT::Test
+ tests => undef,
+ nodb => 1,
+ server_ok => 1;
+
+my $dbname = 'rt4test_install_xxx';
+my $rtname = 'rttestname';
+my $domain = 'rttes.com';
+my $password = 'newpass';
+my $correspond = 'reply@example.com';
+my $comment = 'comment@example.com';
+
+# use bin/rt to fake sendmail to make sure the file exists
+my $sendmail = File::Spec->catfile( $RT::BinPath, 'rt' );
+my $owner = 'root@localhost';
+
+unlink File::Spec->catfile( $RT::VarPath, $dbname );
+
+my ( $url, $m ) = RT::Test->started_ok;
+$m->warning_like(qr/If this is a new installation of RT/,
+ "Got startup warning");
+
+my ($port) = $url =~ /:(\d+)/;
+$m->get_ok($url);
+
+is( $m->uri, $url . '/Install/index.html', 'install page' );
+$m->select( 'Lang', 'zh-cn' );
+$m->click('ChangeLang');
+$m->content_contains( Encode::decode("UTF-8",'语言'), 'select chinese' );
+
+$m->click('Run');
+$m->content_contains( Encode::decode("UTF-8",'数据库'), 'select db type in chinese' );
+
+$m->back;
+$m->select( 'Lang', 'en' );
+$m->click('ChangeLang');
+$m->content_contains( 'Select another language', 'back to english' );
+
+$m->click('Run');
+
+is( $m->uri, $url . '/Install/DatabaseType.html', 'db type page' );
+my $select_type = $m->current_form->find_input('DatabaseType');
+my @possible_types = $select_type->possible_values;
+ok( @possible_types, 'we have at least 1 db type' );
+
+SKIP: {
+ skip 'no mysql found', 7 unless grep { /mysql/ } @possible_types;
+ $m->select( 'DatabaseType', 'mysql' );
+ $m->click;
+ for my $field (qw/Name Host Port Admin AdminPassword User Password/) {
+ ok( $m->current_form->find_input("Database$field"),
+ "db mysql has field Database$field" );
+ }
+ $m->back;
+}
+
+SKIP: {
+ skip 'no pg found', 8 unless grep { /Pg/ } @possible_types;
+ $m->select( 'DatabaseType', 'Pg' );
+ $m->click;
+ for my $field (
+ qw/Name Host Port Admin AdminPassword User Password/)
+ {
+ ok( $m->current_form->find_input("Database$field"),
+ "db Pg has field Database$field" );
+ }
+ $m->back;
+}
+
+$m->select( 'DatabaseType', 'SQLite' );
+$m->click;
+
+is( $m->uri, $url . '/Install/DatabaseDetails.html', 'db details page' );
+$m->field( 'DatabaseName' => $dbname );
+$m->submit_form( fields => { DatabaseName => $dbname } );
+$m->content_contains( 'Connection succeeded', 'succeed msg' );
+$m->content_contains(
+qq{$dbname already exists, but does not contain RT&#39;s tables or metadata. The &#39;Initialize Database&#39; step later on can insert tables and metadata into this existing database. if this is acceptable, click &#39;Customize Basic&#39; below to continue customizing RT.},
+ 'more db state msg'
+);
+$m->click;
+
+is( $m->uri, $url . '/Install/Basics.html', 'basics page' );
+$m->click;
+$m->content_contains(
+ 'You must enter an Administrative password',
+ "got password can't be empty error"
+);
+
+for my $field (qw/rtname WebDomain WebPort Password/) {
+ ok( $m->current_form->find_input($field), "has field $field" );
+}
+is( $m->value('WebPort'), $port, 'default port' );
+$m->field( 'rtname' => $rtname );
+$m->field( 'WebDomain' => $domain );
+$m->field( 'Password' => $password );
+$m->click;
+
+is( $m->uri, $url . '/Install/Sendmail.html', 'mail page' );
+for my $field (qw/SendmailPath OwnerEmail/) {
+ ok( $m->current_form->find_input($field), "has field $field" );
+}
+
+$m->field( 'OwnerEmail' => '' );
+$m->click;
+$m->content_contains( "doesn&#39;t look like an email address",
+ 'got email error' );
+
+$m->field( 'SendmailPath' => '/fake/path/sendmail' );
+$m->click;
+$m->content_contains( "/fake/path/sendmail doesn&#39;t exist",
+ 'got sendmail error' );
+
+$m->field( 'SendmailPath' => $sendmail );
+$m->field( 'OwnerEmail' => $owner );
+$m->click;
+
+is( $m->uri, $url . '/Install/Global.html', 'global page' );
+for my $field (qw/CommentAddress CorrespondAddress/) {
+ ok( $m->current_form->find_input($field), "has field $field" );
+}
+
+$m->click;
+is( $m->uri, $url . '/Install/Initialize.html', 'init db page' );
+$m->back;
+
+is( $m->uri, $url . '/Install/Global.html', 'global page' );
+$m->field( 'CorrespondAddress' => 'reply' );
+$m->click;
+$m->content_contains( "doesn&#39;t look like an email address",
+ 'got email error' );
+$m->field( 'CommentAddress' => 'comment' );
+$m->click;
+$m->content_contains( "doesn&#39;t look like an email address",
+ 'got email error' );
+
+$m->field( 'CorrespondAddress' => 'reply@example.com' );
+$m->field( 'CommentAddress' => 'comment@example.com' );
+$m->click;
+
+is( $m->uri, $url . '/Install/Initialize.html', 'init db page' );
+$m->click;
+
+is( $m->uri, $url . '/Install/Finish.html', 'finish page' );
+$m->click;
+
+is( $m->uri, $url . '/', 'home page' );
+$m->login( 'root', $password );
+$m->content_contains( 'RT at a glance', 'logged in with newpass' );
+
+RT->LoadConfig;
+my $config = RT->Config;
+
+is( $config->Get('DatabaseType'), 'SQLite', 'DatabaseType in config' );
+is( $config->Get('DatabaseName'), $dbname, 'DatabaseName in config' );
+is( $config->Get('rtname'), $rtname, 'rtname in config' );
+is( $config->Get('WebDomain'), $domain, 'WebDomain email in config' );
+is( $config->Get('WebPort'), $port, 'WebPort email in config' );
+is( $config->Get('SendmailPath'), $sendmail, 'SendmailPath in config' );
+is( $config->Get('OwnerEmail'), $owner, 'OwnerEmail in config' );
+is( $config->Get('CorrespondAddress'),
+ $correspond, 'correspond address in config' );
+is( $config->Get('CommentAddress'), $comment, 'comment address in config' );
+
+unlink File::Spec->catfile( $RT::VarPath, $dbname );
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/language_update.t b/rt/t/web/language_update.t
new file mode 100644
index 000000000..35082f886
--- /dev/null
+++ b/rt/t/web/language_update.t
@@ -0,0 +1,22 @@
+use strict;
+use warnings;
+use RT::Test tests => 9;
+
+my ( $url, $m ) = RT::Test->started_ok;
+ok( $m->login(), 'logged in' );
+
+$m->follow_link_ok({text => 'About me'});
+$m->form_with_fields('Lang');
+$m->field(Lang => 'zh_TW');
+$m->submit;
+
+$m->text_contains(Encode::decode("UTF-8","並讓現存的 iCal feeds不再能用"), "successfully updated to zh_TW");
+$m->text_contains(Encode::decode("UTF-8","使用語言 的值從 (無) 改為 'zh_TW'"), "when updating to language zh_TW, results are in zh_TW");
+
+$m->form_with_fields('Lang');
+$m->field(Lang => 'en_us');
+$m->submit;
+
+$m->text_contains("breaking all existing iCal feeds", "successfully updated to en_us");
+$m->text_contains("Lang changed from 'zh_TW' to 'en_us'", "when updating to language en_us, results are in en_us");
+
diff --git a/rt/t/web/login.t b/rt/t/web/login.t
index d0213c373..4b3620d41 100644
--- a/rt/t/web/login.t
+++ b/rt/t/web/login.t
@@ -1,7 +1,9 @@
use strict;
use warnings;
-use RT::Test tests => 34;
+use RT::Test;
+
+RT::Config->Set(AllowLoginPasswordAutoComplete => 1);
my ( $baseurl, $m ) = RT::Test->started_ok;
@@ -17,6 +19,7 @@ diag "normal login";
$m->get($baseurl);
$m->title_is('Login');
is( $m->uri, $baseurl, "right url" );
+ $m->content_lacks('autocomplete="off"');
$m->submit_form(
form_id => 'login',
diff --git a/rt/t/web/mobile.t b/rt/t/web/mobile.t
new file mode 100644
index 000000000..3f32e49e6
--- /dev/null
+++ b/rt/t/web/mobile.t
@@ -0,0 +1,210 @@
+use strict;
+use warnings;
+use RT::Test tests => 170;
+
+my ( $url, $m ) = RT::Test->started_ok;
+my $root = RT::Test->load_or_create_user( Name => 'root' );
+
+diag "create another queue";
+my $test_queue = RT::Queue->new( $RT::SystemUser );
+ok( $test_queue->Create( Name => 'foo' ) );
+
+diag "create cf cfbar";
+my $cfbar = RT::CustomField->new( $RT::SystemUser );
+ok(
+ $cfbar->Create(
+ Name => 'cfbar',
+ Type => 'Freeform',
+ LookupType => 'RT::Queue-RT::Ticket'
+ )
+);
+
+$cfbar->AddToObject( $test_queue );
+
+diag "create some tickets to link";
+# yep, create 3 tickets for DependsOn
+my @tickets = map { { Subject => "link of $_" } }
+ qw/DependsOn DependsOn DependsOn DependedOnBy HasMember HasMember
+ MemberOf RefersTo RefersTo ReferredToBy/;
+RT::Test->create_tickets( { Status => 'resolved' }, @tickets );
+
+diag "test different mobile agents";
+my @agents = (
+ 'hiptop', 'Blazer', 'Novarra', 'Vagabond',
+ 'SonyEricsson', 'Symbian', 'NetFront', 'UP.Browser',
+ 'UP.Link', 'Windows CE', 'MIDP', 'J2ME',
+ 'DoCoMo', 'J-PHONE', 'PalmOS', 'PalmSource',
+ 'iPhone', 'iPod', 'AvantGo', 'Nokia',
+ 'Android', 'WebOS', 'S60'
+);
+
+for my $agent (@agents) {
+ $m->agent($agent);
+ $m->get_ok($url);
+ $m->content_contains( 'Not using a mobile browser',
+ "mobile login page for agent $agent" );
+}
+
+$m->submit_form( fields => { user => 'root', pass => 'password' } );
+is( $m->uri, $url . '/m/', 'logged in via mobile ui' );
+ok( $m->find_link( text => 'Home' ), 'has homepage link, so really logged in' );
+
+diag "create some tickets";
+$m->follow_link_ok( { text => 'New ticket' } );
+like( $m->uri, qr'/m/ticket/select_create_queue', 'queue select page' );
+$m->follow_link_ok( { text => 'General' } );
+like( $m->uri, qr'/m/ticket/create', 'ticket create page' );
+$m->submit_form(
+ fields => {
+ Subject => 'ticket1',
+ Content => 'content 1',
+ Status => 'open',
+ Cc => 'cc@example.com',
+ AdminCc => 'admincc@example.com',
+ InitialPriority => 13,
+ FinalPriority => 93,
+ TimeEstimated => 2,
+ 'TimeEstimated-TimeUnits' => 'hours',
+ TimeWorked => 30,
+ TimeLeft => 60,
+ Starts => '2011-01-11 11:11:11',
+ Due => '2011-02-12 12:12:12',
+ 'new-DependsOn' => '1 2 3',
+ 'DependsOn-new' => '4',
+ 'new-MemberOf' => '5 6',
+ 'MemberOf-new' => '7',
+ 'new-RefersTo' => '8 9',
+ 'RefersTo-new' => '10',
+ }
+);
+like( $m->uri, qr'/m/ticket/show', 'ticket show page' );
+$m->content_contains( 'ticket1', 'subject' );
+$m->content_contains( 'open', 'status' );
+$m->content_contains( 'cc@example.com', 'cc' );
+$m->content_contains( 'admincc@example.com', 'admincc' );
+$m->content_contains( '13/93', 'priority' );
+$m->content_contains( '2 hour', 'time estimates' );
+$m->content_contains( '30 min', 'time worked' );
+$m->content_contains( '60 min', 'time left' );
+$m->content_contains( 'Tue Jan 11 11:11:11', 'starts' );
+$m->content_contains( 'Sat Feb 12 12:12:12', 'due' );
+$m->content_like( qr/(link of DependsOn).*\1.*\1/s, 'depends on' );
+$m->content_contains( 'link of DependedOnBy', 'depended on by' );
+$m->content_like( qr/(link of HasMember).*\1/s, 'has member' );
+$m->content_contains( 'link of MemberOf', 'member of' );
+$m->content_like( qr/(link of RefersTo).*\1/s, 'refers to' );
+$m->content_contains( 'link of ReferredToBy', 'referred to by' );
+
+diag "test ticket reply";
+$m->follow_link_ok( { text => 'Reply' } );
+like( $m->uri, qr'/m/ticket/reply', 'ticket reply page' );
+$m->submit_form(
+ fields => {
+ UpdateContent => 'reply 1',
+ UpdateTimeWorked => '30',
+ UpdateStatus => 'resolved',
+ UpdateType => 'response',
+ },
+ button => 'SubmitTicket',
+);
+like( $m->uri, qr'/m/ticket/show', 'back to ticket show page' );
+$m->content_contains( '1 hour', 'time worked' );
+$m->content_contains( 'resolved', 'status' );
+$m->follow_link_ok( { text => 'Reply' } );
+like( $m->uri, qr'/m/ticket/reply', 'ticket reply page' );
+$m->submit_form(
+ fields => {
+ UpdateContent => 'reply 2',
+ UpdateSubject => 'ticket1',
+ UpdateStatus => 'open',
+ UpdateType => 'private',
+ },
+ button => 'SubmitTicket',
+);
+$m->no_warnings_ok;
+$m->content_contains( 'ticket1', 'subject' );
+$m->content_contains( 'open', 'status' );
+
+like( $m->uri, qr'/m/ticket/show', 'back to ticket show page' );
+
+diag "test ticket history";
+$m->follow_link_ok( { text => 'History' } );
+like( $m->uri, qr'/m/ticket/history', 'ticket history page' );
+$m->content_contains( 'content 1', 'has main content' );
+$m->content_contains( 'reply 1', 'has replied content' );
+$m->content_contains( 'reply 2', 'has replied content' );
+
+diag "create another ticket in queue foo";
+$m->follow_link_ok( { text => 'Home' } );
+is( $m->uri, "$url/m/", 'main mobile page' );
+$m->follow_link_ok( { text => 'New ticket' } );
+like( $m->uri, qr'/m/ticket/select_create_queue', 'queue select page' );
+$m->follow_link_ok( { text => 'foo' } );
+like( $m->uri, qr'/m/ticket/create', 'ticket create page' );
+$m->content_contains( 'cfbar', 'has cf name' );
+$m->content_contains( 'Object-RT::Ticket--CustomField-' . $cfbar->id . '-Value', 'has cf input name' );
+$m->submit_form(
+ fields => {
+ Subject => 'ticket2',
+ Content => 'content 2',
+ Owner => $root->id,
+ 'Object-RT::Ticket--CustomField-' . $cfbar->id . '-Value' => 'cfvalue',
+ }
+);
+$m->no_warnings_ok;
+like( $m->uri, qr'/m/ticket/show', 'ticket show page' );
+$m->content_contains( 'cfbar', 'has cf name' );
+$m->content_contains( 'cfvalue', 'has cf value' );
+
+$m->follow_link_ok( { text => 'Home' } );
+is( $m->uri, "$url/m/", 'main mobile page' );
+
+diag "test unowned tickets link";
+$m->follow_link_ok( { text => 'Unowned tickets' } );
+$m->content_contains( 'Found 1 ticket', 'found 1 ticket' );
+$m->content_contains( 'ticket1', 'has ticket1' );
+$m->content_lacks( 'ticket2', 'no ticket2' );
+$m->back;
+
+diag "test tickets I own link";
+$m->follow_link_ok( { text => 'Tickets I own' } );
+$m->content_contains( 'Found 1 ticket', 'found 1 ticket' );
+$m->content_lacks( 'ticket1', 'no ticket1' );
+ok( $m->find_link( text_regex => qr/ticket2/ ), 'has ticket2 link' );
+$m->back;
+
+diag "test all tickets link";
+$m->follow_link_ok( { text => 'All tickets' } );
+$m->content_contains( 'Found 12 tickets', 'found 12 tickets' );
+ok( $m->find_link( text_regex => qr/ticket1/ ), 'has ticket1 link' );
+ok( $m->find_link( text_regex => qr/ticket2/ ), 'has ticket2 link' );
+$m->back;
+
+diag "test bookmarked tickets link";
+my $ticket = RT::Ticket->new(RT::CurrentUser->new('root'));
+$ticket->Load(11);
+$root->ToggleBookmark($ticket);
+
+$m->follow_link_ok( { text => 'Bookmarked tickets' } );
+$m->content_contains( 'Found 1 ticket', 'found 1 ticket' );
+ok( $m->find_link( text_regex => qr/ticket1/ ), 'has ticket1 link' );
+$m->content_lacks( 'ticket2', 'no ticket2' );
+$m->back;
+
+diag "test tickets search";
+$m->submit_form( fields => { q => 'ticket2' } );
+$m->content_contains( 'Found 1 ticket', 'found 1 ticket' );
+$m->content_lacks( 'ticket1', 'no ticket1' );
+ok( $m->find_link( text_regex => qr/ticket2/ ), 'has ticket2 link' );
+$m->back;
+
+diag "test logout link";
+$m->follow_link_ok( { text => 'Logout' } );
+is( $m->uri, "$url/m/", 'still in mobile' );
+$m->submit_form( fields => { user => 'root', pass => 'password' } );
+
+diag "test notmobile link";
+$m->follow_link_ok( { text => 'Home' } );
+$m->follow_link_ok( { text => 'Not using a mobile browser?' } );
+is( $m->uri, $url . '/', 'got full ui' );
+
diff --git a/rt/t/web/offline.t b/rt/t/web/offline.t
deleted file mode 100644
index 06d51913a..000000000
--- a/rt/t/web/offline.t
+++ /dev/null
@@ -1,77 +0,0 @@
-use strict;
-use warnings;
-
-use RT::Test tests => 20;
-
-my ( $url, $m ) = RT::Test->started_ok;
-ok( $m->login, 'logged in' );
-
-{
- my $template = <<EOF;
-===Create-Ticket: ticket1
-Queue: General
-Subject: test
-Status: new
-EOF
- my $ticket = create_ticket_offline( $m, $template );
- ok $ticket->id, 'created a ticket with offline tool';
- is $ticket->QueueObj->Name, 'General', 'correct value';
- is $ticket->Subject, 'test', 'correct value';
- is $ticket->Status, 'new', 'correct value';
-}
-
-{
- my $template = <<'EOF';
-===Create-Ticket: ticket1
-Queue: General
-Subject: test
-Status: new
-Requestor: test@example.com
-EOF
- my $ticket = create_ticket_offline( $m, $template );
- ok $ticket->id, 'created a ticket with offline tool';
- is $ticket->RequestorAddresses, 'test@example.com', 'correct value';
-}
-
-{
- my $group = RT::Group->new(RT->SystemUser);
- my ($id, $msg) = $group->CreateUserDefinedGroup( Name => 'test' );
- ok $id, "created a user defined group";
-
- my $template = <<'EOF';
-===Create-Ticket: ticket1
-Queue: General
-Subject: test
-Status: new
-Requestor: test@example.com
-RequestorGroup: test
-EOF
- my $ticket = create_ticket_offline( $m, $template );
- ok $ticket->id, 'created a ticket with offline tool';
- ok grep(
- { $_->MemberId eq $group->id }
- @{ $ticket->Requestors->MembersObj->ItemsArrayRef }
- ), 'correct value' ;
- is $ticket->RequestorAddresses, 'test@example.com', 'correct value';
-}
-
-sub create_ticket_offline {
- my ($m, $template) = @_;
-
- $m->get_ok( $url . '/Tools/Offline.html' );
-
- $m->submit_form(
- form_name => 'TicketUpdate',
- fields => { string => $template },
- button => 'UpdateTickets',
- );
-
- my $ticket = RT::Ticket->new( RT->SystemUser );
- $m->content_like( qr/Ticket \d+ created/, 'found ticket created message' )
- or return $ticket;
-
- $ticket->Load( $m->content =~ /Ticket (\d+) created/ );
- return $ticket;
-}
-
-
diff --git a/rt/t/web/offline_messages_utf8.t b/rt/t/web/offline_messages_utf8.t
deleted file mode 100644
index 4cf6954bd..000000000
--- a/rt/t/web/offline_messages_utf8.t
+++ /dev/null
@@ -1,64 +0,0 @@
-use strict;
-use warnings;
-
-use RT::Test tests => 8;
-use RT::Ticket;
-
-my ( $url, $m ) = RT::Test->started_ok;
-$m->default_header( 'Accept-Language' => "zh-tw" );
-ok( $m->login, 'logged in' );
-
-my $ticket_id;
-my $template;
-
-{
-
- # test create message
- $template = <<EOF;
-===Create-Ticket: ticket1
-Queue: General
-Subject: test message
-Status: new
-Content:
-ENDOFCONTENT
-Due:
-TimeEstimated: 100
-TimeLeft: 100
-FinalPriority: 90
-EOF
-
- $m->get_ok( $url . '/Tools/Offline.html' );
-
- $m->submit_form(
- form_name => 'TicketUpdate',
- fields => { string => $template, },
- button => 'UpdateTickets',
- );
- my $content = Encode::encode("UTF-8", $m->content);
- ok( $content =~ m/申請單 #(\d+) 成功新增於 &#39;General&#39; 表單/, 'message is shown right' );
- $ticket_id = $1;
-}
-
-{
-
- # test update message
- $template = <<EOF;
-===Update-Ticket: 1
-Subject: test message update
-EOF
-
- $m->get_ok( $url . '/Tools/Offline.html' );
- $m->submit_form(
- form_name => 'TicketUpdate',
- fields => { string => $template, },
- button => 'UpdateTickets',
- );
-
- my $content = Encode::encode("UTF-8", $m->content);
- ok(
- $content =~
-qr/主題\s*的值從\s*&#39;test message&#39;\s*改為\s*&#39;test message update&#39;/,
- 'subject is updated'
- );
-}
-
diff --git a/rt/t/web/offline_utf8.t b/rt/t/web/offline_utf8.t
deleted file mode 100644
index aab3049a3..000000000
--- a/rt/t/web/offline_utf8.t
+++ /dev/null
@@ -1,53 +0,0 @@
-use strict;
-use warnings;
-
-use RT::Test tests => 9;
-
-use RT::Ticket;
-my $file = File::Spec->catfile( RT::Test->temp_directory, 'template' );
-open my $fh, '>', $file or die $!;
-my $template = Encode::decode("UTF-8",<<EOF);
-===Create-Ticket: ticket1
-Queue: General
-Subject: 标题
-Status: new
-Content:
-这是正文
-ENDOFCONTENT
-EOF
-
-print $fh Encode::encode("UTF-8",$template);
-close $fh;
-
-my ( $url, $m ) = RT::Test->started_ok;
-ok( $m->login, 'logged in' );
-
-$m->get_ok( $url . '/Tools/Offline.html' );
-
-$m->submit_form(
- form_name => 'TicketUpdate',
- fields => { Template => $file, },
- button => 'Parse',
-);
-
-$m->content_contains( Encode::decode("UTF-8",'这是正文'), 'content is parsed right' );
-
-$m->submit_form(
- form_name => 'TicketUpdate',
- button => 'UpdateTickets',
-
- # mimic what browsers do: they seems decoded $template
- fields => { string => $template },
-);
-
-$m->content_like( qr/Ticket \d+ created/, 'found ticket created message' );
-my ( $ticket_id ) = $m->content =~ /Ticket (\d+) created/;
-
-my $ticket = RT::Ticket->new( RT->SystemUser );
-$ticket->Load( $ticket_id );
-is( $ticket->Subject, Encode::decode("UTF-8",'标题'), 'subject in $ticket is right' );
-
-$m->goto_ticket($ticket_id);
-$m->content_contains( Encode::decode("UTF-8",'这是正文'),
- 'content is right in ticket display page' );
-
diff --git a/rt/t/web/owner_disabled_group_19221.t b/rt/t/web/owner_disabled_group_19221.t
index d41decfd2..b71fc5b3e 100644
--- a/rt/t/web/owner_disabled_group_19221.t
+++ b/rt/t/web/owner_disabled_group_19221.t
@@ -122,7 +122,7 @@ diag "Check WithMember and WithoutMember recursively";
{
my $with = RT::Groups->new( RT->SystemUser );
$with->WithMember( PrincipalId => $user->PrincipalObj->Id, Recursively => 1 );
- $with->Limit( FIELD => 'domain', OPERATOR => '=', VALUE => 'UserDefined' );
+ $with->LimitToUserDefinedGroups;
is_deeply(
[map {$_->Name} @{$with->ItemsArrayRef}],
['Disabled Group','Supergroup'],
@@ -131,7 +131,7 @@ diag "Check WithMember and WithoutMember recursively";
my $without = RT::Groups->new( RT->SystemUser );
$without->WithoutMember( PrincipalId => $user->PrincipalObj->Id, Recursively => 1 );
- $without->Limit( FIELD => 'domain', OPERATOR => '=', VALUE => 'UserDefined' );
+ $without->LimitToUserDefinedGroups;
is_deeply(
[map {$_->Name} @{$without->ItemsArrayRef}],
[],
diff --git a/rt/t/web/path-traversal.t b/rt/t/web/path-traversal.t
index 01302e672..8207265ef 100644
--- a/rt/t/web/path-traversal.t
+++ b/rt/t/web/path-traversal.t
@@ -8,35 +8,30 @@ ok($agent->login);
$agent->get("$baseurl/NoAuth/../Elements/HeaderJavascript");
is($agent->status, 400);
-$agent->warning_like(qr/Invalid request.*aborting/,);
+$agent->warning_like(qr/Invalid request.*aborting/);
$agent->get("$baseurl/NoAuth/../%45lements/HeaderJavascript");
is($agent->status, 400);
-$agent->warning_like(qr/Invalid request.*aborting/,);
+$agent->warning_like(qr/Invalid request.*aborting/);
$agent->get("$baseurl/NoAuth/%2E%2E/Elements/HeaderJavascript");
is($agent->status, 400);
-$agent->warning_like(qr/Invalid request.*aborting/,);
+$agent->warning_like(qr/Invalid request.*aborting/);
$agent->get("$baseurl/NoAuth/../../../etc/RT_Config.pm");
is($agent->status, 400);
-SKIP: {
- skip "Apache rejects busting up above / for us", 2 if $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/;
- $agent->warning_like(qr/Invalid request.*aborting/,);
-};
+$agent->warning_like(qr/Invalid request.*aborting/) unless $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/;
-$agent->get("$baseurl/NoAuth/css/web2/images/../../../../../../etc/RT_Config.pm");
-is($agent->status, 400);
-SKIP: {
- skip "Apache rejects busting up above / for us", 2 if $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/;
- $agent->warning_like(qr/Invalid request.*aborting/,);
-};
+$agent->get("$baseurl/static/css/web2/images/../../../../../../etc/RT_Config.pm");
+# Apache hardcodes a 400m but the static handler returns a 403 for traversal too high
+is($agent->status, $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/ ? 400 : 403);
# Do not reject a simple /. in the URL, for downloading uploaded
# dotfiles, for example.
$agent->get("$baseurl/Ticket/Attachment/28/9/.bashrc");
is($agent->status, 200); # Even for a file not found, we return 200
-$agent->content_contains("Bad attachment id");
+$agent->next_warning_like(qr/could not be loaded/, "couldn't loaded warning");
+$agent->content_like(qr/Attachment \S+ could not be loaded/);
# do not reject these URLs, even though they contain /. outside the path
$agent->get("$baseurl/index.html?ignored=%2F%2E");
diff --git a/rt/t/web/psgi-wrap.t b/rt/t/web/psgi-wrap.t
new file mode 100644
index 000000000..0e4b0532b
--- /dev/null
+++ b/rt/t/web/psgi-wrap.t
@@ -0,0 +1,15 @@
+use strict;
+use warnings;
+
+use RT::Test
+ tests => undef,
+ plugins => [qw(RT::Extension::PSGIWrap)];
+
+my ($base, $m) = RT::Test->started_ok;
+$m->login;
+ok(my $res = $m->get("/"));
+is($res->code, 200, 'Successful request to /');
+ok($res->header('X-RT-PSGIWrap'), 'X-RT-PSGIWrap header set from the plugin');
+
+undef $m;
+done_testing();
diff --git a/rt/t/web/query_builder.t b/rt/t/web/query_builder.t
index 3589c381a..dbe909939 100644
--- a/rt/t/web/query_builder.t
+++ b/rt/t/web/query_builder.t
@@ -196,7 +196,7 @@ diag "click advanced, enter 'C1 OR ( C2 AND C3 )', apply, aggregators should sta
# create a custom field with nonascii name and try to add a condition
{
my $cf = RT::CustomField->new( RT->SystemUser );
- $cf->LoadByName( Name => "\x{442}", Queue => 0 );
+ $cf->LoadByName( Name => "\x{442}", LookupType => RT::Ticket->CustomFieldLookupType, ObjectId => 0 );
if ( $cf->id ) {
is($cf->Type, 'Freeform', 'loaded and type is correct');
} else {
@@ -212,10 +212,10 @@ diag "click advanced, enter 'C1 OR ( C2 AND C3 )', apply, aggregators should sta
ok( $response->is_success, "Fetched " . $url."Search/Build.html" );
ok($agent->form_name('BuildQuery'), "found the form once");
- $agent->field("ValueOf'CF.{\x{442}}'", "\x{441}");
+ $agent->field("ValueOfCF.{\x{442}}", "\x{441}");
$agent->submit();
is( getQueryFromForm($agent),
- "'CF.{\x{442}}' LIKE '\x{441}'",
+ "CF.{\x{442}} LIKE '\x{441}'",
"no changes, no duplicate condition with badly encoded text"
);
diff --git a/rt/t/web/query_builder_queue_limits.t b/rt/t/web/query_builder_queue_limits.t
index 332cc939c..6bbf33386 100644
--- a/rt/t/web/query_builder_queue_limits.t
+++ b/rt/t/web/query_builder_queue_limits.t
@@ -71,9 +71,9 @@ $m->get_ok( $url . '/Search/Build.html' );
diag "check default statuses, cf and owners";
my $form = $m->form_name('BuildQuery');
ok( $form, 'found BuildQuery form' );
-ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf by default' );
-ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'no general_cf by default' );
-ok( !$form->find_input("ValueOf'CF.{foo_cf}'"), 'no foo_cf by default' );
+ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf by default' );
+ok( !$form->find_input("ValueOfCF.{general_cf}"), 'no general_cf by default' );
+ok( !$form->find_input("ValueOfCF.{foo_cf}"), 'no foo_cf by default' );
my $status_input = $form->find_input('ValueOfStatus');
my @statuses = sort $status_input->possible_values;
@@ -94,9 +94,9 @@ $m->submit_form(
);
$form = $m->form_name('BuildQuery');
-ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' );
-ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' );
-ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'still no general_cf' );
+ok( $form->find_input("ValueOfCF.{foo_cf}"), 'found foo_cf' );
+ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' );
+ok( !$form->find_input("ValueOfCF.{general_cf}"), 'still no general_cf' );
$status_input = $form->find_input('ValueOfStatus');
@statuses = sort $status_input->possible_values;
is_deeply(
@@ -119,9 +119,9 @@ $m->submit_form(
);
$form = $m->form_name('BuildQuery');
-ok( $form->find_input("ValueOf'CF.{general_cf}'"), 'found general_cf' );
-ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' );
-ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' );
+ok( $form->find_input("ValueOfCF.{general_cf}"), 'found general_cf' );
+ok( $form->find_input("ValueOfCF.{foo_cf}"), 'found foo_cf' );
+ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' );
$status_input = $form->find_input('ValueOfStatus');
@statuses = sort $status_input->possible_values;
is_deeply(
@@ -144,9 +144,9 @@ $m->submit_form(
);
$form = $m->form_name('BuildQuery');
-ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' );
-ok( !$form->find_input("ValueOf'CF.{foo_cf}'"), 'no foo_cf' );
-ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'no general_cf' );
+ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' );
+ok( !$form->find_input("ValueOfCF.{foo_cf}"), 'no foo_cf' );
+ok( !$form->find_input("ValueOfCF.{general_cf}"), 'no general_cf' );
$status_input = $form->find_input('ValueOfStatus');
@statuses = sort $status_input->possible_values;
is_deeply(
@@ -166,9 +166,9 @@ $m->submit_form(
fields => { Query => q{Queue = 'General' OR Queue = 'foo'} },
);
$form = $m->form_name('BuildQuery');
-ok( $form->find_input("ValueOf'CF.{general_cf}'"), 'found general_cf' );
-ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' );
-ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' );
+ok( $form->find_input("ValueOfCF.{general_cf}"), 'found general_cf' );
+ok( $form->find_input("ValueOfCF.{foo_cf}"), 'found foo_cf' );
+ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' );
$status_input = $form->find_input('ValueOfStatus');
@statuses = sort $status_input->possible_values;
is_deeply(
diff --git a/rt/t/web/query_log.t b/rt/t/web/query_log.t
index 89cca2d7b..cfb4d81d7 100644
--- a/rt/t/web/query_log.t
+++ b/rt/t/web/query_log.t
@@ -14,6 +14,5 @@ $root->LoadByEmail('root@localhost');
$m->get_ok("/Admin/Tools/Queries.html");
$m->text_contains("/index.html", "we include info about a page we hit while logging in");
$m->text_contains("Stack:", "stack traces");
-$m->text_like(qr{share/html/autohandler:\d+}, "stack trace includes mason components");
+$m->text_like(qr{/autohandler:\d+}, "stack trace includes mason components");
$m->text_contains("SELECT * FROM Principals WHERE id = '".$root->id."'", "we interpolate bind params");
-
diff --git a/rt/t/web/queue_create.t b/rt/t/web/queue_create.t
index 566876231..40f7b3b8b 100644
--- a/rt/t/web/queue_create.t
+++ b/rt/t/web/queue_create.t
@@ -13,7 +13,7 @@ my $queue_name = 'test queue';
my $queue_id;
diag "Create a queue";
{
- $m->follow_link( id => 'tools-config-queues-create');
+ $m->follow_link( id => 'admin-queues-create');
# Test queue form validation
$m->submit_form(
diff --git a/rt/t/web/redirect-after-login.t b/rt/t/web/redirect-after-login.t
index 35025a1e1..eb2718cf3 100644
--- a/rt/t/web/redirect-after-login.t
+++ b/rt/t/web/redirect-after-login.t
@@ -4,8 +4,6 @@ use warnings;
use RT::Test tests => 122;
-RT->Config->Set( GnuPG => Enable => 0 );
-
my ($baseurl, $agent) = RT::Test->started_ok;
my $url = $agent->rt_base_url;
@@ -226,7 +224,7 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) {
unlike($agent->content, qr/Your username or password is incorrect/, "didn't get any error message");
}
-# XXX TODO: we should also be testing WebExternalAuth here, but we don't have
+# XXX TODO: we should also be testing WebRemoteUserAuth here, but we don't have
# the framework for dealing with that
1;
diff --git a/rt/t/web/reminder-permissions.t b/rt/t/web/reminder-permissions.t
new file mode 100644
index 000000000..dd859cd33
--- /dev/null
+++ b/rt/t/web/reminder-permissions.t
@@ -0,0 +1,178 @@
+use strict;
+use warnings;
+use RT::Test tests => 40;
+
+my $user_a = RT::Test->load_or_create_user(
+ Name => 'user_a',
+ Password => 'password',
+);
+
+ok( $user_a && $user_a->id, 'created user_a' );
+ok(
+ RT::Test->add_rights(
+ {
+ Principal => $user_a,
+ Right => [qw/SeeQueue CreateTicket ShowTicket OwnTicket/]
+ },
+ ),
+ 'add basic rights for user_a'
+);
+
+ok(
+ RT::Test->add_rights(
+ {
+ Principal => 'Owner',
+ Right => [qw/ModifyTicket/],
+ },
+ ),
+ 'add basic rights for owner'
+);
+
+my $ticket = RT::Test->create_ticket(
+ Subject => 'test reminder permission',
+ Queue => 'General',
+);
+
+ok( $ticket->id, 'created a ticket' );
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+$m->login;
+
+my ( $root_reminder_id, $user_a_reminder_id );
+diag "create two reminders, with owner root and user_a, respectively";
+{
+ $m->goto_ticket( $ticket->id );
+ $m->text_contains( 'New reminder:', 'can create a new reminder' );
+ $m->form_name('UpdateReminders');
+ $m->field( 'NewReminder-Subject' => "root reminder" );
+ $m->submit;
+ $m->text_contains( "Reminder 'root reminder': Created",
+ 'created root reminder' );
+
+ $m->form_name('UpdateReminders');
+ $m->field( 'NewReminder-Subject' => "user_a reminder", );
+ $m->field( 'NewReminder-Owner' => $user_a->id, );
+ $m->submit;
+ $m->text_contains( "Reminder 'user_a reminder': Created",
+ 'created user_a reminder' );
+
+ my $reminders = RT::Reminders->new($user_a);
+ $reminders->Ticket( $ticket->id );
+ my $col = $reminders->Collection;
+ while ( my $c = $col->Next ) {
+ if ( $c->Subject eq 'root reminder' ) {
+ $root_reminder_id = $c->id;
+ }
+ elsif ( $c->Subject eq 'user_a reminder' ) {
+ $user_a_reminder_id = $c->id;
+ }
+ }
+}
+
+diag "check root_a can update user_a reminder but not root reminder";
+my $m_a = RT::Test::Web->new;
+{
+ ok( $m_a->login( user_a => 'password' ), 'logged in as user_a' );
+ $m_a->goto_ticket( $ticket->id );
+ $m_a->content_lacks( 'New reminder:', 'can not create a new reminder' );
+ $m_a->content_contains( 'root reminder', 'can see root reminder' );
+ $m_a->content_contains( 'user_a reminder', 'can see user_a reminder' );
+ $m_a->content_like(
+qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!,
+ "root reminder checkbox is disabled"
+ );
+
+ $m_a->form_name('UpdateReminders');
+ $m_a->tick( "Complete-Reminder-$user_a_reminder_id" => 1 );
+ $m_a->submit;
+ $m_a->text_contains(
+ "Reminder 'user_a reminder': Status changed from 'open' to 'resolved'",
+ 'complete user_a reminder' );
+
+ $m_a->follow_link_ok( { id => 'page-reminders' } );
+ $m_a->title_is( "Reminders for ticket #" . $ticket->id );
+ $m_a->content_contains( 'root reminder', 'can see root reminder' );
+ $m_a->content_contains( 'user_a reminder', 'can see user_a reminder' );
+ $m_a->content_lacks( 'New reminder:', 'can not create a new reminder' );
+ $m_a->content_like(
+qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!,
+ "root reminder checkbox is disabled"
+ );
+
+ $m_a->form_name('UpdateReminders');
+ $m_a->untick( "Complete-Reminder-$user_a_reminder_id", 1 );
+ $m_a->submit;
+ $m_a->text_contains(
+ "Reminder 'user_a reminder': Status changed from 'resolved' to 'open'",
+ 'reopen user_a reminder'
+ );
+
+}
+
+diag "set ticket owner to user_a to let user_a grant modify ticket right";
+{
+ $ticket->SetOwner( $user_a->id );
+
+ $m_a->goto_ticket( $ticket->id );
+ $m_a->content_contains( 'New reminder:', 'can create a new reminder' );
+ $m_a->content_like(
+qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!,
+ "root reminder checkbox is still disabled"
+ );
+ $m_a->form_name('UpdateReminders');
+ $m_a->field( 'NewReminder-Subject' => "user_a from display reminder" );
+ $m_a->submit;
+ $m_a->text_contains( "Reminder 'user_a from display reminder': Created",
+ 'created user_a from display reminder' );
+
+ $m_a->follow_link_ok( { id => 'page-reminders' } );
+ $m_a->title_is( "Reminders for ticket #" . $ticket->id );
+ $m_a->content_contains( 'New reminder:', 'can create a new reminder' );
+ $m_a->content_like(
+qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!,
+ "root reminder checkbox is still disabled"
+ );
+ $m_a->form_name('UpdateReminders');
+ $m_a->field( 'NewReminder-Subject' => "user_a from reminders reminder" );
+ $m_a->submit;
+ $m_a->text_contains( "Reminder 'user_a from reminders reminder': Created",
+ 'created user_a from reminders reminder' );
+}
+
+diag "grant user_a with ModifyTicket globally";
+{
+ ok(
+ RT::Test->add_rights(
+ {
+ Principal => $user_a,
+ Right => [qw/ModifyTicket/],
+ },
+ ),
+ 'add ModifyTicket rights to user_a'
+ );
+
+ $m_a->goto_ticket( $ticket->id );
+ $m_a->content_unlike(
+qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!,
+ "root reminder checkbox is enabled"
+ );
+ $m_a->form_name('UpdateReminders');
+ $m_a->tick( "Complete-Reminder-$root_reminder_id" => 1 );
+ $m_a->submit;
+ $m_a->text_contains(
+ "Reminder 'root reminder': Status changed from 'open' to 'resolved'",
+ 'complete root reminder' );
+
+ $m_a->follow_link_ok( { id => 'page-reminders' } );
+ $m_a->content_unlike(
+qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!,
+ "root reminder checkbox is enabled"
+ );
+ $m_a->form_name('UpdateReminders');
+ $m_a->untick( "Complete-Reminder-$root_reminder_id" => 1 );
+ $m_a->submit;
+ $m_a->text_contains(
+ "Reminder 'root reminder': Status changed from 'resolved' to 'open'",
+ 'reopen root reminder' );
+}
+
diff --git a/rt/t/web/reminders.t b/rt/t/web/reminders.t
index 510235156..98a8d6954 100644
--- a/rt/t/web/reminders.t
+++ b/rt/t/web/reminders.t
@@ -26,7 +26,7 @@ $m->goto_ticket($ticket->id);
$m->form_name('UpdateReminders');
$m->field( 'NewReminder-Subject' => "baby's first reminder" );
$m->submit;
-$m->content_contains("Reminder &#39;baby&#39;s first reminder&#39; added");
+$m->content_contains("Reminder &#39;baby&#39;s first reminder&#39;: Created");
$ticket->SetStatus('deleted');
is( $ticket->Status, 'deleted', 'deleted ticket' );
diff --git a/rt/t/web/remote_user.t b/rt/t/web/remote_user.t
index edad6ef95..c17a93379 100644
--- a/rt/t/web/remote_user.t
+++ b/rt/t/web/remote_user.t
@@ -1,36 +1,197 @@
use strict;
use warnings;
use RT;
-use RT::Test tests => 9;
-use MIME::Base64 qw//;
+use RT::Test plan => 'no_plan';
-RT->Config->Set( DevelMode => 0 );
-RT->Config->Set( WebExternalAuth => 1 );
+sub stop_server {
+ my $mech = shift;
-sub auth {
- return Authorization => "Basic " .
- MIME::Base64::encode( join(":", @_) );
+ # Ensure we're logged in for the final warnings check
+ $$mech->auth("root");
+
+ # Force the warnings check before we stop the server
+ undef $$mech;
+
+ RT::Test->stop_server;
+}
+
+diag "Continuous + Fallback";
+{
+ RT->Config->Set( DevelMode => 0 );
+ RT->Config->Set( WebRemoteUserAuth => 1 );
+ RT->Config->Set( WebRemoteUserAuthContinuous => 1 );
+ RT->Config->Set( WebFallbackToRTLogin => 1 );
+ RT->Config->Set( WebRemoteUserAutocreate => 0 );
+
+ my ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' );
+
+ diag "Internal auth";
+ {
+ # Empty REMOTE_USER
+ $m->auth("");
+
+ # First request gets the login form
+ $m->get_ok($url, "No basic auth is OK");
+ $m->content_like(qr/Login/, "Login form");
+
+ # Log in using RT's form
+ $m->submit_form_ok({
+ with_fields => {
+ user => 'root',
+ pass => 'password',
+ },
+ }, "Submitted login form");
+ ok $m->logged_in_as("root"), "Logged in as root";
+
+ # Still logged in on another request without REMOTE_USER
+ $m->follow_link_ok({ text => 'My Tickets' });
+ ok $m->logged_in_as("root"), "Logged in as root";
+
+ ok $m->logout, "Logged out";
+
+ # We're definitely logged out?
+ $m->get_ok($url);
+ $m->content_like(qr/Login/, "Login form");
+ }
+
+ diag "External auth";
+ {
+ # REMOTE_USER of root
+ $m->auth("root");
+
+ # Automatically logged in as root without Login page
+ $m->get_ok($url);
+ ok $m->logged_in_as("root"), "Logged in as root";
+
+ # Still logged in on another request
+ $m->follow_link_ok({ text => 'My Tickets' });
+ ok $m->logged_in_as("root"), "Still logged in as root";
+
+ # Drop credentials and...
+ $m->auth("");
+
+ # ...see if RT notices
+ $m->get($url);
+ is $m->status, 403, "403 Forbidden from RT";
+
+ # Next request gets us the login form
+ $m->get_ok($url);
+ $m->content_like(qr/Login/, "Login form");
+ }
+
+ diag "External auth with invalid user, login internally";
+ {
+ # REMOTE_USER of invalid
+ $m->auth("invalid");
+
+ # Login internally via the login link
+ $m->get("$url/Search/Build.html");
+ is $m->status, 403, "403 Forbidden";
+ $m->follow_link_ok({ url_regex => qr'NoAuth/Login\.html' }, "follow logout link");
+ $m->content_like(qr/Login/, "Login form");
+
+ # Log in using RT's form
+ $m->submit_form_ok({
+ with_fields => {
+ user => 'root',
+ pass => 'password',
+ },
+ }, "Submitted login form");
+ ok $m->logged_in_as("root"), "Logged in as root";
+ like $m->uri, qr'Search/Build\.html', "at our originally requested page";
+
+ # Still logged in on another request
+ $m->follow_link_ok({ text => 'Tools' });
+ ok $m->logged_in_as("root"), "Logged in as root";
+
+ ok $m->logout, "Logged out";
+
+ $m->next_warning_like(qr/Couldn't find internal user for 'invalid'/, "found warning for first request");
+ $m->next_warning_like(qr/Couldn't find internal user for 'invalid'/, "found warning for second request");
+ }
+
+ stop_server(\$m);
}
-my ( $url, $m ) = RT::Test->started_ok( basic_auth => 1 );
-$m->get($url);
-is($m->status, 401, "Initial request with no creds gets 401");
+diag "Fallback OFF";
+{
+ RT->Config->Set( DevelMode => 0 );
+ RT->Config->Set( WebRemoteUserAuth => 1 );
+ RT->Config->Set( WebRemoteUserContinuous => 0 );
+ RT->Config->Set( WebFallbackToRTLogin => 0 );
+ RT->Config->Set( WebRemoteUserAutocreate => 0 );
-$m->get($url, auth( root => "wrong" ));
-is($m->status, 401, "Request with wrong creds gets 401");
+ my ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' );
-$m->get($url, auth( root => "password" ));
-is($m->status, 200, "Request with right creds gets 200");
+ diag "No remote user";
+ {
+ $m->auth("");
+ $m->get($url);
+ is $m->status, 403, "Forbidden";
+ }
+
+ stop_server(\$m);
+}
-$m->content_like(
- qr{<span class="current-user">\Qroot\E</span>}i,
- "Has user on the page"
-);
-$m->content_unlike(qr/Logout/i, "Has no logout button, no WebFallbackToInternalAuth");
+diag "WebRemoteUserAutocreate";
+{
+ RT->Config->Set( DevelMode => 0 );
+ RT->Config->Set( WebRemoteUserAuth => 1 );
+ RT->Config->Set( WebRemoteUserContinuous => 1 );
+ RT->Config->Set( WebFallbackToRTLogin => 0 );
+ RT->Config->Set( WebRemoteUserAutocreate => 1 );
+ RT->Config->Set( UserAutocreateDefaultsOnLogin => { Organization => "BPS" } );
-$m->get($url);
-is($m->status, 401, "Subsequent requests without credentials aren't still logged in");
+ my ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' );
+ diag "New user";
+ {
+ $m->auth("anewuser");
+ $m->get_ok($url);
+ ok $m->logged_in_as("anewuser"), "Logged in as anewuser";
+
+ my $user = RT::User->new( RT->SystemUser );
+ $user->Load("anewuser");
+ ok $user->id, "Found newly created user";
+ is $user->Organization, "BPS", "Found Organization from UserAutocreateDefaultsOnLogin hash";
+ ok $user->Privileged, "Privileged by default";
+ }
+
+ stop_server(\$m);
+ RT->Config->Set(
+ UserAutocreateDefaultsOnLogin => {
+ Privileged => 0,
+ EmailAddress => 'foo@example.com',
+ },
+ );
+ ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' );
+
+ diag "Create unprivileged users";
+ {
+ $m->auth("unpriv");
+ $m->get_ok($url);
+ ok $m->logged_in_as("unpriv"), "Logged in as an unpriv user";
+ like $m->uri->path, RT->Config->Get('SelfServiceRegex'), "SelfService URL";
+
+ my $user = RT::User->new( RT->SystemUser );
+ $user->Load("unpriv");
+ ok $user->id, "Found newly created user";
+ ok !$user->Privileged, "Unprivileged per config";
+ is $user->EmailAddress, 'foo@example.com', "Email address per config";
+ }
+
+ diag "User creation failure";
+ {
+ $m->auth("conflicting");
+ $m->get($url);
+ is $m->status, 403, "Forbidden";
+ $m->next_warning_like(qr/Couldn't auto-create user 'conflicting' when attempting WebRemoteUser: Email address in use/, 'found failed auth warning');
+
+ my $user = RT::User->new( RT->SystemUser );
+ $user->Load("conflicting");
+ ok !$user->id, "Couldn't find conflicting user";
+ }
+
+ stop_server(\$m);
+}
-# Put the credentials back for the warnings check at the end
-$m->default_header( auth( root => "password" ));
diff --git a/rt/t/web/rest-search-group.t b/rt/t/web/rest-search-group.t
new file mode 100644
index 000000000..b62aa09e4
--- /dev/null
+++ b/rt/t/web/rest-search-group.t
@@ -0,0 +1,102 @@
+use strict;
+use warnings;
+use RT::Test tests => undef;
+
+my $group_foo = RT::Group->new($RT::SystemUser);
+$group_foo->CreateUserDefinedGroup( Name => 'foo' );
+
+my $group_bar = RT::Group->new($RT::SystemUser);
+$group_bar->CreateUserDefinedGroup( Name => 'bar' );
+
+my $group_baz = RT::Group->new($RT::SystemUser);
+$group_baz->CreateUserDefinedGroup( Name => 'baz' );
+$group_baz->SetDisabled(1);
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+
+ok( $m->login, 'logged in' );
+
+search_groups_ok(
+ { query => 'id = ' . $group_foo->id },
+ [ $group_foo->id . ': foo' ],
+ 'search by id'
+);
+
+search_groups_ok(
+ {
+ query => 'Name = ' . $group_foo->Name,
+ format => 's',
+ fields => 'id,name',
+ },
+ [ "id\tName", $group_foo->id . "\tfoo" ],
+ 'search by name with customized fields'
+);
+
+search_groups_ok(
+ { query => 'foo = 3' },
+ ['Invalid field specification: foo'],
+ 'invalid field'
+);
+
+search_groups_ok(
+ { query => 'id foo 3' },
+ ['Invalid operator specification: foo'],
+ 'invalid op'
+);
+
+search_groups_ok(
+ { query => '', orderby => 'id' },
+ [ $group_foo->id . ': foo', $group_bar->id . ': bar', ],
+ 'order by id'
+);
+
+search_groups_ok(
+ { query => '', orderby => 'name' },
+ [ $group_bar->id . ': bar', $group_foo->id . ': foo' ],
+ 'order by name'
+);
+
+search_groups_ok(
+ { query => '', orderby => '+name' },
+ [ $group_bar->id . ': bar', $group_foo->id . ': foo' ],
+ 'order by +name'
+);
+
+search_groups_ok(
+ { query => '', orderby => '-name' },
+ [ $group_foo->id . ': foo', $group_bar->id . ': bar' ],
+ 'order by -name'
+);
+
+search_groups_ok(
+ { query => 'Disabled = 0', orderby => 'id' },
+ [ $group_foo->id . ': foo', $group_bar->id . ': bar' ],
+ 'enabled groups'
+);
+
+search_groups_ok(
+ { query => 'Disabled = 1', orderby => 'id' },
+ [ $group_baz->id . ': baz' ],
+ 'disabled groups'
+);
+
+sub search_groups_ok {
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+ my $query = shift;
+ my $expected = shift;
+ my $name = shift || 'search groups';
+
+ my $uri = URI->new("$baseurl/REST/1.0/search/group");
+ $uri->query_form(%$query);
+ $m->get_ok($uri);
+
+ my @lines = split /\n/, $m->content;
+ shift @lines; # header
+ shift @lines; # empty line
+
+ is_deeply( \@lines, $expected, $name );
+
+}
+
+undef $m;
+done_testing();
diff --git a/rt/t/web/rest-search-queue.t b/rt/t/web/rest-search-queue.t
new file mode 100644
index 000000000..a827d8643
--- /dev/null
+++ b/rt/t/web/rest-search-queue.t
@@ -0,0 +1,104 @@
+use strict;
+use warnings;
+use RT::Test tests => undef;
+
+my $queue_foo = RT::Test->load_or_create_queue( Name => 'Foo' );
+my $queue_bar = RT::Test->load_or_create_queue( Name => 'Bar' );
+my $queue_baz = RT::Test->load_or_create_queue( Name => 'Baz' );
+$queue_baz->SetDisabled(1);
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+
+ok( $m->login, 'logged in' );
+
+search_queues_ok( { query => 'id = 1' }, ['1: General'], 'search id = 1' );
+search_queues_ok(
+ {
+ query => 'Name = General',
+ format => 's',
+ fields => 'id,name,description'
+ },
+ [ "id\tName\tDescription", "1\tGeneral\tThe default queue" ],
+ 'search by name with customized fields'
+);
+
+search_queues_ok(
+ { query => 'id > 10' },
+ ['No matching results.'],
+ 'no matching results'
+);
+
+search_queues_ok(
+ { query => 'foo = 3' },
+ ['Invalid field specification: foo'],
+ 'invalid field'
+);
+
+search_queues_ok(
+ { query => 'id foo 3' },
+ ['Invalid operator specification: foo'],
+ 'invalid op'
+);
+
+search_queues_ok(
+ { query => '', orderby => 'id' },
+ [ '1: General', $queue_foo->id . ': Foo', $queue_bar->id . ': Bar', ],
+ 'order by id'
+);
+
+search_queues_ok(
+ { query => '', orderby => 'name' },
+ [ $queue_bar->id . ': Bar', $queue_foo->id . ': Foo', '1: General', ],
+ 'order by name'
+);
+
+search_queues_ok(
+ { query => '', orderby => '+name' },
+ [ $queue_bar->id . ': Bar', $queue_foo->id . ': Foo', '1: General', ],
+ 'order by +name'
+);
+
+search_queues_ok(
+ { query => '', orderby => '-name' },
+ [ '1: General', $queue_foo->id . ': Foo', $queue_bar->id . ': Bar', ],
+ 'order by -name'
+);
+
+search_queues_ok(
+ { query => 'Disabled = 0', orderby => 'id' },
+ [ '1: General', $queue_foo->id . ': Foo', $queue_bar->id . ': Bar', ],
+ 'enabled queues'
+);
+
+search_queues_ok(
+ { query => 'Disabled = 1', orderby => 'id' },
+ [ $queue_baz->id . ': Baz', ],
+ 'disabled queues'
+);
+
+search_queues_ok(
+ { query => 'Disabled = 2', orderby => 'id' },
+ [ '2: ___Approvals', ],
+ 'special Approvals queue'
+);
+
+sub search_queues_ok {
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+ my $query = shift;
+ my $expected = shift;
+ my $name = shift || 'search queues';
+
+ my $uri = URI->new("$baseurl/REST/1.0/search/queue");
+ $uri->query_form(%$query);
+ $m->get_ok($uri);
+
+ my @lines = split /\n/, $m->content;
+ shift @lines; # header
+ shift @lines; # empty line
+
+ is_deeply( \@lines, $expected, $name );
+
+}
+
+undef $m;
+done_testing();
diff --git a/rt/t/web/rest-search-user.t b/rt/t/web/rest-search-user.t
new file mode 100644
index 000000000..84a967377
--- /dev/null
+++ b/rt/t/web/rest-search-user.t
@@ -0,0 +1,115 @@
+use strict;
+use warnings;
+use RT::Test tests => undef;
+
+my $root = RT::Test->load_or_create_user( Name => 'root', );
+my $user_foo = RT::Test->load_or_create_user(
+ Name => 'foo',
+ Password => 'password',
+);
+my $user_bar = RT::Test->load_or_create_user( Name => 'bar' );
+my $user_baz = RT::Test->load_or_create_user( Name => 'baz' );
+$user_baz->SetDisabled(1);
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+
+ok( $m->login, 'logged in' );
+
+search_users_ok(
+ { query => 'id = ' . $user_foo->id },
+ [ $user_foo->id . ': foo' ],
+ 'search by id'
+);
+
+search_users_ok(
+ {
+ query => 'Name = ' . $user_foo->Name,
+ format => 's',
+ fields => 'id,name'
+ },
+ [ "id\tName", $user_foo->id . "\tfoo" ],
+ 'search by name with customized fields'
+);
+
+
+search_users_ok(
+ { query => 'foo = 3' },
+ ['Invalid field specification: foo'],
+ 'invalid field'
+);
+
+search_users_ok(
+ { query => 'id foo 3' },
+ ['Invalid operator specification: foo'],
+ 'invalid op'
+);
+
+search_users_ok(
+ { query => 'password = foo' },
+ ['Invalid field specification: password'],
+ "can't search password"
+);
+
+search_users_ok(
+ { query => '', orderby => 'id' },
+ [ $root->id . ': root', $user_foo->id . ': foo', $user_bar->id . ': bar', ],
+ 'order by id'
+);
+
+search_users_ok(
+ { query => '', orderby => 'name' },
+ [ $user_bar->id . ': bar', $user_foo->id . ': foo', $root->id . ': root' ],
+ 'order by name'
+);
+
+search_users_ok(
+ { query => '', orderby => '+name' },
+ [ $user_bar->id . ': bar', $user_foo->id . ': foo', $root->id . ': root' ],
+ 'order by +name'
+);
+
+search_users_ok(
+ { query => '', orderby => '-name' },
+ [ $root->id . ': root', $user_foo->id . ': foo', $user_bar->id . ': bar' ],
+ 'order by -name'
+);
+
+search_users_ok(
+ { query => 'Disabled = 0', orderby => 'id' },
+ [ $root->id . ': root', $user_foo->id . ': foo', $user_bar->id . ': bar', ],
+ 'enabled users'
+);
+
+search_users_ok(
+ { query => 'Disabled = 1', orderby => 'id' },
+ [ $user_baz->id . ': baz', ],
+ 'disabled users'
+);
+
+ok( $m->login( 'foo', 'password', logout => 1 ), 'logged in as foo' );
+search_users_ok(
+ { query => 'id = ' . $user_foo->id },
+ [ 'Permission denied' ],
+ "can't search without permission"
+);
+
+sub search_users_ok {
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+ my $query = shift;
+ my $expected = shift;
+ my $name = shift || 'search users';
+
+ my $uri = URI->new("$baseurl/REST/1.0/search/user");
+ $uri->query_form(%$query);
+ $m->get_ok($uri);
+
+ my @lines = split /\n/, $m->content;
+ shift @lines; # header
+ shift @lines; # empty line
+
+ is_deeply( \@lines, $expected, $name );
+
+}
+
+undef $m;
+done_testing();
diff --git a/rt/t/web/rest.t b/rt/t/web/rest.t
index 3a84b2a01..8b8cbcb86 100644
--- a/rt/t/web/rest.t
+++ b/rt/t/web/rest.t
@@ -204,7 +204,7 @@ is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content)
$text = $m->content;
$text =~ s/.*?\n\n//;
$text =~ s/\n\n/\n/;
- $text =~ s{CF\.{severity}:.*\n}{}img;
+ $text =~ s{CF\.\{severity\}:.*\n}{}img;
$text .= "CF.{severity}: explosive, a bit\n";
$m->post(
"$baseurl/REST/1.0/ticket/edit",
@@ -234,7 +234,7 @@ is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content)
]
);
$text = $m->content;
- $text =~ s{CF\.{severity}:.*\n}{}img;
+ $text =~ s{CF\.\{severity\}:.*\n}{}img;
$text .= "CF.{severity}:\n";
$m->post(
"$baseurl/REST/1.0/ticket/edit",
@@ -301,7 +301,7 @@ is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content)
]
);
$text = $m->content;
- $text =~ s{CF\.{single}:.*\n}{}img;
+ $text =~ s{CF\.\{single\}:.*\n}{}img;
$text .= "CF.{single}: that\n";
$m->post(
"$baseurl/REST/1.0/ticket/edit",
diff --git a/rt/t/web/rest_user_cf.t b/rt/t/web/rest_user_cf.t
new file mode 100644
index 000000000..d9f4ea3ba
--- /dev/null
+++ b/rt/t/web/rest_user_cf.t
@@ -0,0 +1,26 @@
+use strict;
+use warnings;
+use RT::Interface::REST;
+
+use RT::Test tests => undef;
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+
+my $cf = RT::Test->load_or_create_custom_field(
+ Name => 'foo',
+ Type => 'Freeform',
+ LookupType => 'RT::User',
+);
+$cf->AddToObject(RT::User->new(RT->SystemUser));
+
+my $root = RT::User->new( RT->SystemUser );
+$root->Load('root');
+$root->AddCustomFieldValue( Field => 'foo', Value => 'blabla' );
+is( $root->FirstCustomFieldValue('foo'), 'blabla', 'cf is set' );
+
+ok( $m->login, 'logged in' );
+$m->post( "$baseurl/REST/1.0/show", [ id => 'user/12', ] );
+like( $m->content, qr/CF-foo: blabla/, 'found the cf' );
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/richtext-autohandler.t b/rt/t/web/richtext-autohandler.t
deleted file mode 100644
index 724a7b34c..000000000
--- a/rt/t/web/richtext-autohandler.t
+++ /dev/null
@@ -1,14 +0,0 @@
-use strict;
-use warnings;
-
-use RT::Test tests => 9;
-my ($baseurl, $agent) = RT::Test->started_ok;
-
-$agent->get("$baseurl/NoAuth/RichText/ckeditor/config.js");
-is($agent->status, 403);
-$agent->content_lacks("config.disableNativeSpellChecker");
-
-$agent->get_ok("/NoAuth/RichText/config.js");
-$agent->content_contains("config.disableNativeSpellChecker");
-
-$agent->warning_like(qr/Invalid request directly to the rich text editor/,);
diff --git a/rt/t/web/rights.t b/rt/t/web/rights.t
index 23b357f79..c7e8aac00 100644
--- a/rt/t/web/rights.t
+++ b/rt/t/web/rights.t
@@ -6,7 +6,7 @@ use RT::Test tests => 14;
my ($baseurl, $m) = RT::Test->started_ok;
ok $m->login, "logged in";
-$m->follow_link_ok({ id => 'tools-config-global-group-rights'});
+$m->follow_link_ok({ id => 'admin-global-group-rights'});
sub get_rights {
diff --git a/rt/t/web/rights1.t b/rt/t/web/rights1.t
index 63ddb38c4..2cc7689c6 100644
--- a/rt/t/web/rights1.t
+++ b/rt/t/web/rights1.t
@@ -29,9 +29,9 @@ $agent->login( $user_obj->Name, 'customer');
# Test for absence of Configure and Preferences tabs.
ok(!$agent->find_link( url => "$RT::WebPath/Admin/",
- text => 'Configuration'), "No config tab" );
+ text => 'Admin'), "No admin tab" );
ok(!$agent->find_link( url => "$RT::WebPath/User/Prefs.html",
- text => 'Preferences'), "No prefs pane" );
+ text => 'Preferences'), "No prefs pane" );
# Now test for their presence, one at a time. Sleep for a bit after
# ACL changes, thanks to the 10s ACL cache.
@@ -43,20 +43,20 @@ $agent->reload;
$agent->content_contains('Logout', "Reloaded page successfully");
ok($agent->find_link( url => "$RT::WebPath/Admin/",
- text => 'Configuration'), "Found config tab" );
+ text => 'Admin'), "Found admin tab" );
my ($revokeid,$revokemsg) =$user_obj->PrincipalObj->RevokeRight(Right => 'ShowConfigTab');
ok ($revokeid,$revokemsg);
($grantid,$grantmsg) =$user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf');
ok ($grantid,$grantmsg);
$agent->reload();
$agent->content_contains('Logout', "Reloaded page successfully");
-ok($agent->find_link(
- id => 'preferences-settings' ), "Found prefs pane" );
+ok($agent->find_link(
+ id => 'preferences-settings' ), "Found prefs pane" );
($revokeid,$revokemsg) = $user_obj->PrincipalObj->RevokeRight(Right => 'ModifySelf');
ok ($revokeid,$revokemsg);
# Good. Now load the search page and test Load/Save Search.
$agent->follow_link( url => "$RT::WebPath/Search/Build.html",
- text => 'Tickets');
+ text => 'Tickets');
is($agent->status, 200, "Fetched search builder page");
$agent->content_lacks("Load saved search", "No search loading box");
$agent->content_lacks("Saved searches", "No saved searches box");
@@ -79,23 +79,23 @@ $agent->content_like(qr/input\s+type=['"]submit['"][^>]+name=['"]SavedSearchSave
# via SelectOwner.
my $queue_obj = RT::Queue->new(RT->SystemUser);
-($ret, $msg) = $queue_obj->Create(Name => 'CustomerQueue-'.$$,
- Description => 'queue for SelectOwner testing');
+($ret, $msg) = $queue_obj->Create(Name => 'CustomerQueue-'.$$,
+ Description => 'queue for SelectOwner testing');
ok($ret, "SelectOwner test queue creation. $msg");
my $group_obj = RT::Group->new(RT->SystemUser);
($ret, $msg) = $group_obj->CreateUserDefinedGroup(Name => 'CustomerGroup-'.$$,
- Description => 'group for SelectOwner testing');
+ Description => 'group for SelectOwner testing');
ok($ret, "SelectOwner test group creation. $msg");
# Add our customer to the customer group, and give it queue rights.
($ret, $msg) = $group_obj->AddMember($user_obj->PrincipalObj->Id());
ok($ret, "Added customer to its group. $msg");
($grantid,$grantmsg) =$group_obj->PrincipalObj->GrantRight(Right => 'OwnTicket',
- Object => $queue_obj);
-
+ Object => $queue_obj);
+
ok($grantid,$grantmsg);
($grantid,$grantmsg) =$group_obj->PrincipalObj->GrantRight(Right => 'SeeQueue',
- Object => $queue_obj);
+ Object => $queue_obj);
ok ($grantid,$grantmsg);
# Now. When we look at the search page we should be able to see
# ourself in the list of possible owners.
diff --git a/rt/t/web/saved_search_chart.t b/rt/t/web/saved_search_chart.t
index 70111b97c..3737b512e 100644
--- a/rt/t/web/saved_search_chart.t
+++ b/rt/t/web/saved_search_chart.t
@@ -58,7 +58,7 @@ $m->submit_form(
form_name => 'SaveSearch',
fields => {
Query => 'id=2',
- PrimaryGroupBy => 'Status',
+ GroupBy => 'Status',
ChartStyle => 'pie',
},
button => 'SavedSearchSave',
@@ -67,13 +67,13 @@ $m->submit_form(
$m->content_contains("Chart first chart updated", 'found updated message' );
$m->content_contains("id=2", 'Query is updated' );
$m->content_like( qr/value="Status"\s+selected="selected"/,
- 'PrimaryGroupBy is updated' );
+ 'GroupBy is updated' );
$m->content_like( qr/value="pie"\s+selected="selected"/,
'ChartType is updated' );
ok( $search->Load($id) );
is( $search->SubValue('Query'), 'id=2', 'Query is indeed updated' );
-is( $search->SubValue('PrimaryGroupBy'),
- 'Status', 'PrimaryGroupBy is indeed updated' );
+is( $search->SubValue('GroupBy'),
+ 'Status', 'GroupBy is indeed updated' );
is( $search->SubValue('ChartStyle'), 'pie', 'ChartStyle is indeed updated' );
# finally, let's test delete
diff --git a/rt/t/web/saved_search_permissions.t b/rt/t/web/saved_search_permissions.t
index f61c931a0..e24ae6146 100644
--- a/rt/t/web/saved_search_permissions.t
+++ b/rt/t/web/saved_search_permissions.t
@@ -26,7 +26,7 @@ ok( $m->login( 'foo', 'foobar', logout => 1 ), 'logged in' );
$m->get_ok( $url . "/Search/Build.html?SavedSearchLoad=$id" );
my $message = qq{Can not load saved search "$id"};
-RT::Interface::Web::EscapeUTF8( \$message );
+RT::Interface::Web::EscapeHTML( \$message );
$m->content_contains( $message, 'user foo can not load saved search of root' );
$m->warning_like( qr/User #\d+ tried to load container user #\d+/,
diff --git a/rt/t/web/scrips.t b/rt/t/web/scrips.t
index 0ff46bf26..d669f4c4e 100644
--- a/rt/t/web/scrips.t
+++ b/rt/t/web/scrips.t
@@ -1,7 +1,9 @@
use strict;
use warnings;
-use RT::Test tests => 14;
+use RT::Test tests => undef;
+
+RT->Config->Set( UseTransactionBatch => 1 );
# TODO:
# Test the rest of the conditions.
@@ -9,10 +11,16 @@ use RT::Test tests => 14;
# Test templates?
# Test cleanup scripts.
+my $queue_g = RT::Test->load_or_create_queue( Name => 'General' );
+ok $queue_g && $queue_g->id, 'loaded or created queue';
+
+my $queue_r = RT::Test->load_or_create_queue( Name => 'Regression' );
+ok $queue_r && $queue_r->id, 'loaded or created queue';
+
my ($baseurl, $m) = RT::Test->started_ok;
ok $m->login, "logged in";
-$m->follow_link_ok({id => 'tools-config-global-scrips-create'});
+$m->follow_link_ok({id => 'admin-global-scrips-create'});
sub prepare_code_with_value {
my $value = shift;
@@ -47,16 +55,17 @@ sub prepare_code_with_value {
foreach my $data (@values_for_actions) {
my ($condition, $prepare_code_value) = @$data;
diag "Create Scrip (Cond #$condition)" if $ENV{TEST_VERBOSE};
- $m->follow_link_ok({id => 'tools-config-global-scrips-create'});
+ $m->follow_link_ok({id => 'admin-global-scrips-create'});
my $prepare_code = prepare_code_with_value($prepare_code_value);
- $m->form_name('ModifyScrip');
+ $m->form_name('CreateScrip');
$m->set_fields(
- 'Scrip-new-ScripCondition' => $condition,
- 'Scrip-new-ScripAction' => 15, # User Defined
- 'Scrip-new-Template' => 1, # Blank
- 'Scrip-new-CustomPrepareCode' => $prepare_code,
+ 'ScripCondition' => $condition,
+ 'ScripAction' => 'User Defined',
+ 'Template' => 'Blank',
+ 'CustomPrepareCode' => $prepare_code,
);
- $m->submit;
+ $m->click('Create');
+ $m->content_like(qr{Scrip Created});
}
my $ticket_obj = RT::Test->create_ticket(
@@ -76,7 +85,7 @@ sub prepare_code_with_value {
$m->submit_form(
form_name => 'ForwardMessage',
fields => {
- To => 'rt-test, rt-to@example.com',
+ To => 'rt-test@example.com, rt-to@example.com',
},
button => 'ForwardAndReturn'
);
@@ -92,7 +101,7 @@ sub prepare_code_with_value {
$m->submit_form(
form_name => 'ForwardMessage',
fields => {
- To => 'rt-test, rt-to@example.com',
+ To => 'rt-test@example.com, rt-to@example.com',
},
button => 'ForwardAndReturn'
);
@@ -101,3 +110,191 @@ sub prepare_code_with_value {
RT::Test->clean_caught_mails;
}
+
+note "check basics in scrip's admin interface";
+{
+ $m->follow_link_ok( { id => 'admin-global-scrips-create' } );
+ ok $m->form_name('CreateScrip');
+ is $m->value_name('Description'), '', 'empty value';
+ is $m->value_name('ScripAction'), '-', 'empty value';
+ is $m->value_name('ScripCondition'), '-', 'empty value';
+ is $m->value_name('Template'), '-', 'empty value';
+ $m->field('Description' => 'test');
+ $m->click('Create');
+ $m->content_contains("Action is mandatory argument");
+
+ ok $m->form_name('CreateScrip');
+ is $m->value_name('Description'), 'test', 'value stays on the page';
+ $m->select('ScripAction' => 'Notify Ccs');
+ $m->click('Create');
+ $m->content_contains("Template is mandatory argument");
+
+ ok $m->form_name('CreateScrip');
+ is $m->value_name('Description'), 'test', 'value stays on the page';
+ is $m->value_name('ScripAction'), 'Notify Ccs', 'value stays on the page';
+ $m->select('Template' => 'Blank');
+ $m->click('Create');
+ $m->content_contains("Condition is mandatory argument");
+
+ ok $m->form_name('CreateScrip');
+ is $m->value_name('Description'), 'test', 'value stays on the page';
+ is $m->value_name('ScripAction'), 'Notify Ccs', 'value stays on the page';
+ $m->select('ScripCondition' => 'On Close');
+ $m->click('Create');
+ $m->content_contains("Scrip Created");
+
+ ok $m->form_name('ModifyScrip');
+ is $m->value_name('Description'), 'test', 'correct value';
+ is $m->value_name('ScripCondition'), 'On Close', 'correct value';
+ is $m->value_name('ScripAction'), 'Notify Ccs', 'correct value';
+ is $m->value_name('Template'), 'Blank', 'correct value';
+ $m->field('Description' => 'test test');
+ $m->click('Update');
+ # regression
+ $m->content_lacks("Template is mandatory argument");
+
+ ok $m->form_name('ModifyScrip');
+ is $m->value_name('Description'), 'test test', 'correct value';
+ $m->content_contains("Description changed from", "found action result message");
+}
+
+note "check application in admin interface";
+{
+ $m->follow_link_ok({ id => 'admin-global-scrips-create' });
+ $m->submit_form_ok({
+ with_fields => {
+ Description => "testing application",
+ ScripCondition => "On Create",
+ ScripAction => "Open Tickets",
+ Template => "Blank",
+ },
+ button => 'Create',
+ }, "created scrip");
+ $m->content_contains("Scrip Created", "found result message");
+
+ my ($sid) = ($m->content =~ /Modify scrip #(\d+)/);
+ ok $sid, "found scrip id on the page";
+ RT::Test->object_scrips_are($sid, [0]);
+
+ $m->follow_link_ok({ id => 'page-applies-to' });
+ ok $m->form_name("AddRemoveScrip"), "found form";
+ $m->tick("RemoveScrip-$sid", 0);
+ $m->click_ok("Update", "update scrip application");
+ RT::Test->object_scrips_are($sid, []);
+
+ ok $m->form_name("AddRemoveScrip"), "found form";
+ $m->tick("AddScrip-$sid", 0);
+ $m->tick("AddScrip-$sid", $queue_g->id);
+ $m->click_ok("Update", "update scrip application");
+ RT::Test->object_scrips_are($sid, [0], [$queue_g->id, $queue_r->id]);
+}
+
+note "check templates in scrip's admin interface";
+{
+ my $template = RT::Template->new( RT->SystemUser );
+ my ($status, $msg) = $template->Create( Queue => $queue_g->id, Name => 'foo' );
+ ok $status, 'created a template';
+
+ my $templates = RT::Templates->new( RT->SystemUser );
+ $templates->LimitToGlobal;
+
+ my @default = (
+ '',
+ map $_->Name, @{$templates->ItemsArrayRef}
+ );
+
+ $m->follow_link_ok( { id => 'admin-global-scrips-create' } );
+ ok $m->form_name('CreateScrip');
+ my @templates = ($m->find_all_inputs( type => 'option', name => 'Template' ))[0]
+ ->possible_values;
+ is_deeply([sort @templates], [sort @default]);
+
+ $m->follow_link_ok( { id => 'admin-queues' } );
+ $m->follow_link_ok( { text => 'General' } );
+ $m->follow_link_ok( { id => 'page-scrips-create' } );
+
+ ok $m->form_name('CreateScrip');
+ @templates = ($m->find_all_inputs( type => 'option', name => 'Template' ))[0]
+ ->possible_values;
+ is_deeply([sort @templates], [sort @default, 'foo']);
+
+note "make sure we can not apply scrip to queue without required template";
+ $m->field('Description' => 'test template');
+ $m->select('ScripCondition' => 'On Close');
+ $m->select('ScripAction' => 'Notify Ccs');
+ $m->select('Template' => 'foo');
+ $m->click('Create');
+ $m->content_contains("Scrip Created");
+
+ $m->follow_link_ok( { id => 'page-applies-to' } );
+ my ($id) = ($m->content =~ /Modify associated objects for scrip #(\d+)/);
+ $m->form_name('AddRemoveScrip');
+ $m->tick('AddScrip-'.$id, $queue_r->id);
+ $m->click('Update');
+ $m->content_like(qr{No template foo in queue Regression or global});
+
+note "unapply the scrip from any queue";
+ $m->form_name('AddRemoveScrip');
+ $m->tick('RemoveScrip-'.$id, $queue_g->id);
+ $m->click('Update');
+ $m->content_like(qr{Object deleted});
+
+note "you can pick any template";
+ $m->follow_link_ok( { id => 'page-basics' } );
+ ok $m->form_name('ModifyScrip');
+ @templates = ($m->find_all_inputs( type => 'option', name => 'Template' ))[0]
+ ->possible_values;
+ is_deeply(
+ [sort @templates],
+ [sort do {
+ my $t = RT::Templates->new( RT->SystemUser );
+ $t->UnLimit;
+ ('', $t->DistinctFieldValues('Name'))
+ }],
+ );
+
+note "go to apply page and apply with template change";
+ $m->follow_link_ok( { id => 'page-applies-to' } );
+ $m->form_name('AddRemoveScrip');
+ $m->field('Template' => 'blank');
+ $m->tick('AddScrip-'.$id, $queue_g->id);
+ $m->tick('AddScrip-'.$id, $queue_r->id);
+ $m->click('Update');
+ $m->content_contains("Template: Template changed from ");
+ $m->content_contains("Object created");
+}
+
+note "apply scrip in different stage to different queues";
+{
+ $m->follow_link_ok( { id => 'admin-queues' } );
+ $m->follow_link_ok( { text => 'General' } );
+ $m->follow_link_ok( { id => 'page-scrips-create'});
+
+ ok $m->form_name('CreateScrip');
+ $m->field('Description' => 'test stage');
+ $m->select('ScripCondition' => 'On Close');
+ $m->select('ScripAction' => 'Notify Ccs');
+ $m->select('Template' => 'Blank');
+ $m->click('Create');
+ $m->content_contains("Scrip Created");
+
+ my ($sid) = ($m->content =~ /Modify scrip #(\d+)/);
+ ok $sid, "found scrip id on the page";
+
+ $m->follow_link_ok({ text => 'Applies to' });
+ ok $m->form_name('AddRemoveScrip');
+ $m->select('Stage' => 'Batch');
+ $m->tick( "AddScrip-$sid" => $queue_r->id );
+ $m->click('Update');
+ $m->content_contains("Object created");
+
+ $m->follow_link_ok({ text => 'General' });
+ $m->follow_link_ok({ id => 'page-scrips' });
+
+ my (@matches) = $m->content =~ /test stage/g;
+ # regression
+ is scalar @matches, 1, 'scrip mentioned only once';
+}
+
+undef $m;
+done_testing;
diff --git a/rt/t/web/search_bulk_update_links.t b/rt/t/web/search_bulk_update_links.t
index ffe2efe81..d9b477e03 100644
--- a/rt/t/web/search_bulk_update_links.t
+++ b/rt/t/web/search_bulk_update_links.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test tests => 47;
+use RT::Test tests => 46;
my ( $url, $m ) = RT::Test->started_ok;
ok( $m->login, 'logged in' );
@@ -79,7 +79,7 @@ $m->content_lacks( 'DeleteLink--', 'no delete link stuff' );
$m->form_name('BulkUpdate');
my @fields = qw/Owner AddRequestor DeleteRequestor AddCc DeleteCc AddAdminCc
DeleteAdminCc Subject Priority Queue Status Starts_Date Told_Date Due_Date
-Resolved_Date UpdateSubject UpdateContent/;
+UpdateSubject UpdateContent/;
for my $field ( @fields ) {
is( $m->value($field), '', "default $field is empty" );
}
diff --git a/rt/t/web/search_ical.t b/rt/t/web/search_ical.t
new file mode 100644
index 000000000..094d8a2de
--- /dev/null
+++ b/rt/t/web/search_ical.t
@@ -0,0 +1,196 @@
+use strict;
+use warnings;
+
+use Data::ICal;
+use RT::Test tests => 77;
+
+my $start_obj = RT::Date->new( RT->SystemUser );
+$start_obj->SetToNow;
+my $start = $start_obj->iCal( Time => 1);
+
+my $due_obj = RT::Date->new( RT->SystemUser );
+$due_obj->SetToNow;
+$due_obj->AddDays(2);
+my $due = $due_obj->iCal( Time => 1);
+
+diag 'Test iCal with date only';
+{
+ my ($baseurl, $agent) = RT::Test->started_ok;
+
+ my $ticket = RT::Ticket->new(RT->SystemUser);
+
+ for ( 1 .. 5 ) {
+ $ticket->Create(
+ Subject => 'Ticket ' . $_,
+ Queue => 'General',
+ Owner => 'root',
+ Requestor => 'ical@localhost',
+ Starts => $start_obj->ISO,
+ Due => $due_obj->ISO,
+ );
+ }
+
+ ok $agent->login('root', 'password'), 'logged in as root';
+
+ $agent->get_ok('/Search/Build.html');
+ $agent->form_name('BuildQuery');
+ $agent->field('idOp', '>');
+ $agent->field('ValueOfid', '0');
+ $agent->submit('DoSearch');
+ $agent->follow_link_ok({id => 'page-results'});
+
+ for ( 1 .. 5 ) {
+ $agent->content_contains('Ticket ' . $_);
+ }
+
+ $agent->follow_link_ok( { text => 'iCal' } );
+
+ is( $agent->content_type, 'text/calendar', 'content type is text/calendar' );
+
+ for ( 1 .. 5 ) {
+ $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/);
+ }
+
+ my $ical = Data::ICal->new(data => $agent->content);
+
+ my @entries = $ical->entries;
+ my $ical_count = @{$entries[0]};
+ is( $ical_count, 10, "Got $ical_count ical entries");
+
+ my $prop_ref = $entries[0]->[0]->properties;
+ my $start_as_root = RT::Date->new( RT::CurrentUser->new( 'root' ) );
+ $start_as_root->Unix( $start_obj->Unix );
+ my $start = $start_as_root->ISO( Time => 0, Timezone => 'user' );
+ $start =~ s/-//g;
+ is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date: $start");
+ like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE\:/, 'Got DATE value');
+
+ $prop_ref = $entries[0]->[1]->properties;
+ my $due_as_root = RT::Date->new( RT::CurrentUser->new( 'root' ) );
+ $due_as_root->Unix( $due_obj->Unix );
+ my $due = $due_as_root->ISO( Time => 0, Timezone => 'user' );
+ $due =~ s/-//g;
+ is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date: $due");
+ like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE\:/, 'Got DATE value');
+}
+
+RT::Test->stop_server;
+
+diag 'Test iCal with date and time with config option';
+{
+ RT->Config->Set(TimeInICal =>1);
+ my ($baseurl, $agent) = RT::Test->started_ok;
+
+ ok $agent->login('root', 'password'), 'logged in as root';
+
+ $agent->get_ok('/Search/Build.html');
+ $agent->form_name('BuildQuery');
+ $agent->field('idOp', '>');
+ $agent->field('ValueOfid', '0');
+ $agent->submit('DoSearch');
+ $agent->follow_link_ok({id => 'page-results'});
+
+ for ( 1 .. 5 ) {
+ $agent->content_contains('Ticket ' . $_);
+ }
+
+ my $link = $agent->find_link( text => 'iCal' ); # use $link later
+ $agent->get_ok($link->url);
+
+ is( $agent->content_type, 'text/calendar', 'content type is text/calendar' );
+
+ for ( 1 .. 5 ) {
+ $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/);
+ }
+
+ my $ical = Data::ICal->new(data => $agent->content);
+
+ my @entries = $ical->entries;
+ my $ical_count = @{$entries[0]};
+ is( $ical_count, 10, "Got $ical_count ical entries");
+
+ my $prop_ref = $entries[0]->[0]->properties;
+ $start =~ s/-//g;
+ is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date with time: $start");
+ like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value');
+
+ $prop_ref = $entries[0]->[1]->properties;
+ $due =~ s/-//g;
+ is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date with time: $due");
+ like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value');
+}
+
+RT::Test->stop_server;
+
+diag 'Test iCal with date and time using query param';
+{
+ RT->Config->Set(TimeInICal =>0);
+ my ($baseurl, $agent) = RT::Test->started_ok;
+
+ ok $agent->login('root', 'password'), 'logged in as root';
+
+ $agent->get_ok('/Search/Build.html');
+ $agent->form_name('BuildQuery');
+ $agent->field('idOp', '>');
+ $agent->field('ValueOfid', '0');
+ $agent->submit('DoSearch');
+ $agent->follow_link_ok({id => 'page-results'});
+
+ for ( 1 .. 5 ) {
+ $agent->content_contains('Ticket ' . $_);
+ }
+
+ my $link = $agent->find_link( text => 'iCal' );
+ $agent->get_ok($link->url . '?Time=1');
+
+ is( $agent->content_type, 'text/calendar', 'content type is text/calendar' );
+
+ for ( 1 .. 5 ) {
+ $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/);
+ }
+
+ my $ical = Data::ICal->new(data => $agent->content);
+
+ my @entries = $ical->entries;
+ my $ical_count = @{$entries[0]};
+ is( $ical_count, 10, "Got $ical_count ical entries");
+
+ my $prop_ref = $entries[0]->[0]->properties;
+ $start =~ s/-//g;
+ is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date with time: $start");
+ like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value');
+
+ $prop_ref = $entries[0]->[1]->properties;
+ $due =~ s/-//g;
+ is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date with time: $due");
+ like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value');
+
+ diag 'Test iCal with date and time in single events';
+
+ my $url = $link->url . '?SingleEvent=1&Time=1';
+ $agent->get_ok($url);
+
+ is( $agent->content_type, 'text/calendar', 'content type is text/calendar' );
+
+ for ( 1 .. 5 ) {
+ $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/);
+ }
+
+ $ical = Data::ICal->new(data => $agent->content);
+
+ @entries = $ical->entries;
+ $ical_count = @{$entries[0]};
+
+ # Only 5 entries in single event mode
+ is( $ical_count, 5, "Got $ical_count ical entries");
+
+ $prop_ref = $entries[0]->[0]->properties;
+ $start =~ s/-//g;
+ is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date with time: $start");
+ like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value');
+
+ $prop_ref = $entries[0]->[1]->properties;
+ $due =~ s/-//g;
+ is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date with time: $due");
+ like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value');
+}
diff --git a/rt/t/web/search_rss.t b/rt/t/web/search_rss.t
index 9a53a8d94..7f1fdc1c3 100644
--- a/rt/t/web/search_rss.t
+++ b/rt/t/web/search_rss.t
@@ -39,14 +39,11 @@ my $rss_content = $agent->content;
$agent->get_ok($rdf_path);
is($agent->content, $rss_content, 'old Results.rdf still works');
-SKIP: {
- eval { require XML::Simple; };
- skip 'no XML::Simple found', 6 if $@;
- my $rss = XML::Simple::XMLin( $rss_content );
- is( scalar @{ $rss->{item} }, 5, 'item number' );
- for ( 1 .. 5 ) {
- is( $rss->{item}[$_-1]{title}, 'Ticket ' . $_, 'title' . $_ );
- }
+use XML::Simple;
+my $rss = XML::Simple::XMLin( $rss_content );
+is( scalar @{ $rss->{item} }, 5, 'item number' );
+for ( 1 .. 5 ) {
+ is( $rss->{item}[$_-1]{title}, 'Ticket ' . $_, 'title' . $_ );
}
# not login at all
diff --git a/rt/t/web/search_simple.t b/rt/t/web/search_simple.t
index a1a3ce806..d7c47279f 100644
--- a/rt/t/web/search_simple.t
+++ b/rt/t/web/search_simple.t
@@ -44,14 +44,14 @@ my $t = RT::Ticket->new(RT->SystemUser);
{
my ($status, $msg) = $t->AddCustomFieldValue(
Field => $cf1->id,
- Value => 'Downtown');
+ Value => 'Downtown');
ok( $status, "Added CF value - $msg" );
}
{
my ($status, $msg) = $t->AddCustomFieldValue(
Field => $cf2->id,
- Value => 'Proxy');
+ Value => 'Proxy');
ok( $status, "Added CF value - $msg" );
}
diff --git a/rt/t/web/self_service.t b/rt/t/web/self_service.t
index adc90d776..7afc008c6 100644
--- a/rt/t/web/self_service.t
+++ b/rt/t/web/self_service.t
@@ -18,9 +18,8 @@ ok( $user_a && $user_a->id, 'loaded or created user' );
ok( ! $user_a->Privileged, 'user is not privileged' );
# Load Cc group
-my $Cc = RT::Group->new( RT->SystemUser );
-my($ok, $msg) = $Cc->LoadSystemRoleGroup( 'Cc' );
-ok($ok, $msg);
+my $Cc = RT::System->RoleGroup( 'Cc' );
+ok($Cc->id);
RT::Test->add_rights( { Principal => $Cc, Right => ['ShowTicket'] } );
my ($ticket) = RT::Test->create_ticket(
diff --git a/rt/t/web/googleish_search.t b/rt/t/web/simple_search.t
index a5f834eee..710efb1d1 100644
--- a/rt/t/web/googleish_search.t
+++ b/rt/t/web/simple_search.t
@@ -21,40 +21,41 @@ ok $two_words_queue && $two_words_queue->id, 'loaded or created a queue';
my $active = "( ".join( " OR ", map "Status = '$_'", RT::Queue->ActiveStatusArray())." )";
my $inactive = "( ".join( " OR ", map "Status = '$_'", RT::Queue->InactiveStatusArray())." )";
- require RT::Search::Googleish;
- my $parser = RT::Search::Googleish->new(
+ require RT::Search::Simple;
+ my $parser = RT::Search::Simple->new(
TicketsObj => $tickets,
Argument => '',
);
- is $parser->QueryToSQL("foo"), "$active AND ( Subject LIKE 'foo' )", "correct parsing";
+ is $parser->QueryToSQL("foo"), "( Subject LIKE 'foo' ) AND $active", "correct parsing";
+ is $parser->QueryToSQL("1 foo"), "( Subject LIKE 'foo' AND Subject LIKE '1' ) AND $active", "correct parsing";
is $parser->QueryToSQL("1"), "( Id = 1 )", "correct parsing";
is $parser->QueryToSQL("#1"), "( Id = 1 )", "correct parsing";
- is $parser->QueryToSQL("'1'"), "$active AND ( Subject LIKE '1' )", "correct parsing";
+ is $parser->QueryToSQL("'1'"), "( Subject LIKE '1' ) AND $active", "correct parsing";
is $parser->QueryToSQL("foo bar"),
- "$active AND ( Subject LIKE 'foo' AND Subject LIKE 'bar' )",
+ "( Subject LIKE 'foo' AND Subject LIKE 'bar' ) AND $active",
"correct parsing";
is $parser->QueryToSQL("'foo bar'"),
- "$active AND ( Subject LIKE 'foo bar' )",
+ "( Subject LIKE 'foo bar' ) AND $active",
"correct parsing";
is $parser->QueryToSQL("'foo \\' bar'"),
- "$active AND ( Subject LIKE 'foo \\' bar' )",
+ "( Subject LIKE 'foo \\' bar' ) AND $active",
"correct parsing";
is $parser->QueryToSQL('"foo \' bar"'),
- "$active AND ( Subject LIKE 'foo \\' bar' )",
+ "( Subject LIKE 'foo \\' bar' ) AND $active",
"correct parsing";
is $parser->QueryToSQL('"\f\o\o"'),
- "$active AND ( Subject LIKE '\\\\f\\\\o\\\\o' )",
+ "( Subject LIKE '\\\\f\\\\o\\\\o' ) AND $active",
"correct parsing";
is $parser->QueryToSQL("General"), "( Queue = 'General' ) AND $active", "correct parsing";
- is $parser->QueryToSQL("'Two Words'"), "$active AND ( Subject LIKE 'Two Words' )", "correct parsing";
+ is $parser->QueryToSQL("'Two Words'"), "( Subject LIKE 'Two Words' ) AND $active", "correct parsing";
is $parser->QueryToSQL("queue:'Two Words'"), "( Queue = 'Two Words' ) AND $active", "correct parsing";
is $parser->QueryToSQL("subject:'Two Words'"), "$active AND ( Subject LIKE 'Two Words' )", "correct parsing";
is $parser->QueryToSQL("me"), "( Owner.id = '__CurrentUser__' ) AND $active", "correct parsing";
- is $parser->QueryToSQL("'me'"), "$active AND ( Subject LIKE 'me' )", "correct parsing";
+ is $parser->QueryToSQL("'me'"), "( Subject LIKE 'me' ) AND $active", "correct parsing";
is $parser->QueryToSQL("owner:me"), "( Owner.id = '__CurrentUser__' ) AND $active", "correct parsing";
is $parser->QueryToSQL("owner:'me'"), "( Owner = 'me' ) AND $active", "correct parsing";
is $parser->QueryToSQL('owner:root@localhost'), "( Owner.EmailAddress = 'root\@localhost' ) AND $active", "Email address as owner";
@@ -164,7 +165,9 @@ for my $quote ( q{'}, q{"} ) {
Subject => qq!base${quote}ticket $$!,
Queue => 'general',
Owner => $user->Name,
- Requestor => qq!custom${quote}search\@localhost!,
+ ( $quote eq q{'}
+ ? (Requestor => qq!custom${quote}search\@localhost!)
+ : () ),
Content => qq!this is base${quote}ticket with quote inside!,
);
ok( $ticket_quote->id, 'created ticket with quote for custom search' );
@@ -188,7 +191,7 @@ for my $quote ( q{'}, q{"} ) {
$m->field( q => $q );
$m->submit;
my $escape_quote = $quote;
- RT::Interface::Web::EscapeUTF8(\$escape_quote);
+ RT::Interface::Web::EscapeHTML(\$escape_quote);
$m->content_contains( "base${escape_quote}ticket",
"base${quote}ticket is found" );
}
diff --git a/rt/t/web/smime/outgoing.t b/rt/t/web/smime/outgoing.t
new file mode 100644
index 000000000..21d2328f2
--- /dev/null
+++ b/rt/t/web/smime/outgoing.t
@@ -0,0 +1,384 @@
+use strict;
+use warnings;
+
+use RT::Test::SMIME tests => undef;
+my $test = 'RT::Test::SMIME';
+
+use RT::Action::SendEmail;
+use File::Temp qw(tempdir);
+
+use_ok('RT::Crypt::SMIME');
+
+RT::Test::SMIME->import_key('sender@example.com');
+
+my $user_email = 'root@example.com';
+{
+ my $user = RT::Test->load_or_create_user(
+ Name => $user_email, EmailAddress => $user_email
+ );
+ ok $user && $user->id, 'loaded or created user';
+ RT::Test::SMIME->import_key($user_email, $user);
+}
+
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'Regression',
+ CorrespondAddress => 'sender@example.com',
+ CommentAddress => 'sender@example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+RT::Test->set_rights(
+ Principal => 'Everyone',
+ Right => ['CreateTicket', 'ShowTicket', 'SeeQueue', 'ReplyToTicket', 'ModifyTicket'],
+);
+
+my ($baseurl, $m) = RT::Test->started_ok;
+ok $m->login, 'logged in';
+
+my @variants = (
+ {},
+ { Sign => 1 },
+ { Encrypt => 1 },
+ { Sign => 1, Encrypt => 1 },
+);
+
+# collect emails
+my %mail = (
+ plain => [],
+ signed => [],
+ encrypted => [],
+ signed_encrypted => [],
+);
+
+diag "check in read-only mode that queue's props influence create/update ticket pages" if $ENV{TEST_VERBOSE};
+{
+ foreach my $variant ( @variants ) {
+ set_queue_crypt_options( %$variant );
+ $m->goto_create_ticket( $queue );
+ $m->form_name('TicketCreate');
+ if ( $variant->{'Encrypt'} ) {
+ ok $m->value('Encrypt', 2), "encrypt tick box is checked";
+ } else {
+ ok !$m->value('Encrypt', 2), "encrypt tick box is unchecked";
+ }
+ if ( $variant->{'Sign'} ) {
+ ok $m->value('Sign', 2), "sign tick box is checked";
+ } else {
+ ok !$m->value('Sign', 2), "sign tick box is unchecked";
+ }
+ }
+
+ # to avoid encryption/signing during create
+ set_queue_crypt_options();
+
+ my $ticket = RT::Ticket->new( $RT::SystemUser );
+ my ($id) = $ticket->Create(
+ Subject => 'test',
+ Queue => $queue->id,
+ Requestor => $user_email,
+ );
+ ok $id, 'ticket created';
+
+ foreach my $variant ( @variants ) {
+ set_queue_crypt_options( %$variant );
+ $m->goto_ticket( $id );
+ $m->follow_link_ok({text => 'Reply'}, '-> reply');
+ $m->form_number(3);
+ if ( $variant->{'Encrypt'} ) {
+ ok $m->value('Encrypt', 2), "encrypt tick box is checked";
+ } else {
+ ok !$m->value('Encrypt', 2), "encrypt tick box is unchecked";
+ }
+ if ( $variant->{'Sign'} ) {
+ ok $m->value('Sign', 2), "sign tick box is checked";
+ } else {
+ ok !$m->value('Sign', 2), "sign tick box is unchecked";
+ }
+ }
+}
+
+# create a ticket for each combination
+foreach my $queue_set ( @variants ) {
+ set_queue_crypt_options( %$queue_set );
+ foreach my $ticket_set ( @variants ) {
+ create_a_ticket( %$ticket_set );
+ }
+}
+
+my $tid;
+{
+ my $ticket = RT::Ticket->new( $RT::SystemUser );
+ ($tid) = $ticket->Create(
+ Subject => 'test',
+ Queue => $queue->id,
+ Requestor => $user_email,
+ );
+ ok $tid, 'ticket created';
+}
+
+# again for each combination add a reply message
+foreach my $queue_set ( @variants ) {
+ set_queue_crypt_options( %$queue_set );
+ foreach my $ticket_set ( @variants ) {
+ update_ticket( $tid, %$ticket_set );
+ }
+}
+
+
+# ------------------------------------------------------------------------------
+# now delete all keys from the keyring and put back secret/pub pair for rt-test@
+# and only public key for sender@ so we can verify signatures and decrypt
+# like we are on another side recieving emails
+# ------------------------------------------------------------------------------
+
+my $keyring = $test->keyring_path;
+unlink $_ foreach glob( $keyring ."/*" );
+RT::Test::SMIME->import_key('sender@example.com.crt');
+RT::Test::SMIME->import_key($user_email);
+
+$queue = RT::Test->load_or_create_queue(
+ Name => 'Regression',
+ CorrespondAddress => $user_email,
+ CommentAddress => $user_email,
+);
+ok $queue && $queue->id, 'changed props of the queue';
+
+foreach my $mail ( map cleanup_headers($_), @{ $mail{'plain'} } ) {
+ my ($status, $id) = RT::Test->send_via_mailgate($mail);
+ is ($status >> 8, 0, "The mail gateway exited normally");
+ ok ($id, "got id of a newly created ticket - $id");
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $id );
+ ok ($tick->id, "loaded ticket #$id");
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
+
+ ok !$msg->GetHeader('X-RT-Privacy'), "RT's outgoing mail has no crypto";
+ is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Not encrypted',
+ "RT's outgoing mail looks not encrypted";
+ ok !$msg->GetHeader('X-RT-Incoming-Signature'),
+ "RT's outgoing mail looks not signed";
+
+ like $txn->Content, qr/Some content/, "RT's mail includes copy of ticket text";
+}
+
+foreach my $mail ( map cleanup_headers($_), @{ $mail{'signed'} } ) {
+ my ($status, $id) = RT::Test->send_via_mailgate($mail);
+ is ($status >> 8, 0, "The mail gateway exited normally");
+ ok ($id, "got id of a newly created ticket - $id");
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $id );
+ ok ($tick->id, "loaded ticket #$id");
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
+
+ is $msg->GetHeader('X-RT-Privacy'), 'SMIME',
+ "RT's outgoing mail has crypto" or exit 0;
+ is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Not encrypted',
+ "RT's outgoing mail looks not encrypted";
+ like $msg->GetHeader('X-RT-Incoming-Signature'),
+ qr/<sender\@example\.com>/,
+ "RT's outgoing mail looks signed";
+
+ like $attachments[0]->Content, qr/Some content/,
+ "RT's mail includes copy of ticket text";
+}
+
+foreach my $mail ( map cleanup_headers($_), @{ $mail{'encrypted'} } ) {
+ my ($status, $id) = RT::Test->send_via_mailgate($mail);
+ is ($status >> 8, 0, "The mail gateway exited normally");
+ ok ($id, "got id of a newly created ticket - $id");
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $id );
+ ok ($tick->id, "loaded ticket #$id");
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
+
+ is $msg->GetHeader('X-RT-Privacy'), 'SMIME',
+ "RT's outgoing mail has crypto";
+ is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Success',
+ "RT's outgoing mail looks encrypted";
+ ok !$msg->GetHeader('X-RT-Incoming-Signature'),
+ "RT's outgoing mail looks not signed";
+
+ like $attachments[0]->Content, qr/Some content/,
+ "RT's mail includes copy of ticket text";
+}
+
+foreach my $mail ( map cleanup_headers($_), @{ $mail{'signed_encrypted'} } ) {
+ my ($status, $id) = RT::Test->send_via_mailgate($mail);
+ is ($status >> 8, 0, "The mail gateway exited normally");
+ ok ($id, "got id of a newly created ticket - $id");
+
+ my $tick = RT::Ticket->new( $RT::SystemUser );
+ $tick->Load( $id );
+ ok ($tick->id, "loaded ticket #$id");
+
+ my $txn = $tick->Transactions->First;
+ my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
+
+ is $msg->GetHeader('X-RT-Privacy'), 'SMIME',
+ "RT's outgoing mail has crypto";
+ is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Success',
+ "RT's outgoing mail looks encrypted";
+ like $msg->GetHeader('X-RT-Incoming-Signature'),
+ qr/<sender\@example.com>/,
+ "RT's outgoing mail looks signed";
+
+ like $attachments[0]->Content, qr/Some content/,
+ "RT's mail includes copy of ticket text";
+}
+
+sub create_a_ticket {
+ my %args = (@_);
+
+ RT::Test->clean_caught_mails;
+
+ describe_options('creating a ticket: ', %args);
+
+ $m->goto_create_ticket( $queue );
+ $m->form_name('TicketCreate');
+ $m->field( Subject => 'test' );
+ $m->field( Requestors => $user_email );
+ $m->field( Content => 'Some content' );
+
+ foreach ( qw(Sign Encrypt) ) {
+ if ( $args{ $_ } ) {
+ $m->tick( $_ => 1 );
+ } else {
+ $m->untick( $_ => 1 );
+ }
+ }
+
+ $m->submit;
+ is $m->status, 200, "request successful";
+
+ unlike($m->content, qr/unable to sign outgoing email messages/);
+
+ $m->get_ok('/'); # ensure that the mail has been processed
+
+ my @mail = RT::Test->fetch_caught_mails;
+ check_text_emails( \%args, @mail );
+}
+
+sub update_ticket {
+ my $tid = shift;
+ my %args = (@_);
+
+ RT::Test->clean_caught_mails;
+
+ describe_options('updating ticket #'. $tid .': ', %args);
+
+ ok $m->goto_ticket( $tid ), "UI -> ticket #$tid";
+ $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' );
+ $m->form_number(3);
+ $m->field( UpdateContent => 'Some content' );
+
+ foreach ( qw(Sign Encrypt) ) {
+ if ( $args{ $_ } ) {
+ $m->tick( $_ => 1 );
+ } else {
+ $m->untick( $_ => 1 );
+ }
+ }
+
+ $m->click('SubmitTicket');
+ is $m->status, 200, "request successful";
+ $m->content_like(qr/Correspondence added/, 'Correspondence added');# or diag $m->content;
+
+ $m->get_ok('/'); # ensure that the mail has been processed
+
+ my @mail = RT::Test->fetch_caught_mails;
+ check_text_emails( \%args, @mail );
+}
+
+undef $m;
+done_testing;
+
+sub check_text_emails {
+ my %args = %{ shift @_ };
+ my @mail = @_;
+
+ describe_options('testing that we got at least one mail: ', %args);
+
+ ok scalar @mail, "got some mail";
+ for my $mail (@mail) {
+ if ( $args{'Encrypt'} ) {
+ unlike $mail, qr/Some content/, "outgoing email was encrypted";
+ } else {
+ like $mail, qr/Some content/, "outgoing email was not encrypted";
+ }
+
+ if ( $args{'Encrypt'} ) {
+ like $mail, qr/application\/(?:x-)?pkcs7-mime/, 'outgoing email was processed';
+ } elsif ( $args{'Sign'} ) {
+ like $mail, qr/(?:x-)?pkcs7-signature/, 'outgoing email was processed';
+ } else {
+ unlike $mail, qr/smime/, 'outgoing email was not processed';
+ }
+ }
+ if ( $args{'Sign'} && $args{'Encrypt'} ) {
+ push @{ $mail{'signed_encrypted'} }, @mail;
+ } elsif ( $args{'Sign'} ) {
+ push @{ $mail{'signed'} }, @mail;
+ } elsif ( $args{'Encrypt'} ) {
+ push @{ $mail{'encrypted'} }, @mail;
+ } else {
+ push @{ $mail{'plain'} }, @mail;
+ }
+}
+
+sub cleanup_headers {
+ my $mail = shift;
+ # strip id from subject to create new ticket
+ $mail =~ s/^(Subject:)\s*\[.*?\s+#\d+\]\s*/$1 /m;
+ # strip several headers
+ foreach my $field ( qw(Message-ID RT-Originator RT-Ticket X-RT-Loop-Prevention) ) {
+ $mail =~ s/^$field:.*?\n(?! |\t)//gmsi;
+ }
+ return $mail;
+}
+
+sub set_queue_crypt_options {
+ my %args = @_;
+
+ describe_options('setting queue options: ', %args);
+
+ $m->get_ok("/Admin/Queues/Modify.html?id=". $queue->id);
+ $m->form_with_fields('Sign', 'Encrypt');
+ foreach my $opt ('Sign', 'Encrypt') {
+ if ( $args{$opt} ) {
+ $m->tick($opt => 1);
+ } else {
+ $m->untick($opt => 1);
+ }
+ }
+ $m->submit;
+}
+
+sub describe_options {
+ return unless $ENV{'TEST_VERBOSE'};
+
+ my $msg = shift;
+ my %args = @_;
+ if ( $args{'Encrypt'} && $args{'Sign'} ) {
+ $msg .= 'encrypt and sign';
+ }
+ elsif ( $args{'Sign'} ) {
+ $msg .= 'sign';
+ }
+ elsif ( $args{'Encrypt'} ) {
+ $msg .= 'encrypt';
+ }
+ else {
+ $msg .= 'no encrypt and no sign';
+ }
+ diag $msg;
+}
+
diff --git a/rt/t/web/squish.t b/rt/t/web/squish.t
index ff43e74fb..9d1c01b1f 100644
--- a/rt/t/web/squish.t
+++ b/rt/t/web/squish.t
@@ -5,18 +5,18 @@ use RT::Test tests => 26;
RT->Config->Set( DevelMode => 0 );
RT->Config->Set( WebDefaultStylesheet => 'aileron' );
-RT->Config->Set( MasonLocalComponentRoot => RT::Test::get_abs_relocatable_dir('html') );
+RT->Config->Set( LocalStaticPath => RT::Test::get_abs_relocatable_dir('static') );
my ( $url, $m ) = RT::Test->started_ok;
$m->login;
diag "test squished files with devel mode disabled";
-$m->follow_link_ok( { url_regex => qr!aileron-squished-([a-f0-9]{32})\.css! },
+$m->follow_link_ok( { url_regex => qr!aileron/squished-([a-f0-9]{32})\.css! },
'follow squished css' );
$m->content_like( qr!/\*\* End of .*?.css \*/!, 'squished css' );
-$m->content_lacks( 'text-decoration: underline !important;',
- 'no print.css by default' );
+$m->content_lacks( 'counteract the titlebox',
+ 'no mobile.css by default' );
$m->back;
my ($js_link) =
@@ -29,16 +29,16 @@ RT::Test->stop_server;
diag "test squished files with customized files and devel mode disabled";
RT->AddJavaScript( 'not-by-default.js' );
-RT->AddStyleSheets( 'print.css' );
+RT->AddStyleSheets( 'mobile.css' );
( $url, $m ) = RT::Test->started_ok;
$m->login;
-$m->follow_link_ok( { url_regex => qr!aileron-squished-([a-f0-9]{32})\.css! },
+$m->follow_link_ok( { url_regex => qr!aileron/squished-([a-f0-9]{32})\.css! },
'follow squished css' );
$m->content_like( qr!/\*\* End of .*?.css \*/!, 'squished css' );
-$m->content_contains( 'text-decoration: underline !important;',
- 'has print.css' );
+$m->content_contains( 'counteract the titlebox',
+ 'has mobile.css' );
$m->back;
($js_link) =
diff --git a/rt/t/web/html/NoAuth/js/not-by-default.js b/rt/t/web/static/js/not-by-default.js
index 568f670ee..568f670ee 100644
--- a/rt/t/web/html/NoAuth/js/not-by-default.js
+++ b/rt/t/web/static/js/not-by-default.js
diff --git a/rt/t/web/template.t b/rt/t/web/template.t
index 4a2e6c13a..1a02dc98d 100644
--- a/rt/t/web/template.t
+++ b/rt/t/web/template.t
@@ -17,7 +17,7 @@ ok( RT::Test->set_rights(
ok $m->login('user_a', 'password'), 'logged in as user A';
# get to the templates screen
-$m->follow_link( text => 'Configuration' );
+$m->follow_link( text => 'Admin' );
$m->title_is(q{RT Administration}, 'admin screen');
$m->follow_link( text => 'Global' );
diff --git a/rt/t/web/ticket-create-utf8.t b/rt/t/web/ticket-create-utf8.t
index 107e41d71..ebb2d5eab 100644
--- a/rt/t/web/ticket-create-utf8.t
+++ b/rt/t/web/ticket-create-utf8.t
@@ -32,7 +32,7 @@ foreach my $test_str ( $ru_test, $l1_test ) {
$m->submit;
$m->content_like(
- qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i,
+ qr{<td\s+class="message-header-value\s*"[^>]*>\s*\Q$test_str\E\s*</td>}i,
'header on the page'
);
@@ -50,7 +50,7 @@ foreach my $test_str ( $ru_test, $l1_test ) {
$m->submit;
$m->content_like(
- qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i,
+ qr{<td\s+class="message-header-value\s*"[^>]*>\s*\Q$test_str\E\s*</td>}i,
'header on the page'
);
$m->content_contains(
@@ -73,7 +73,7 @@ foreach my $test_str ( $ru_test, $l1_test ) {
$m->submit;
$m->content_like(
- qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i,
+ qr{<td\s+class="message-header-value\s*"[^>]*>\s*\Q$test_str\E\s*</td>}i,
'header on the page'
);
$m->content_contains(
diff --git a/rt/t/web/ticket_forward.t b/rt/t/web/ticket_forward.t
index adf4d6f69..439242d48 100644
--- a/rt/t/web/ticket_forward.t
+++ b/rt/t/web/ticket_forward.t
@@ -36,19 +36,21 @@ diag "Forward Ticket" if $ENV{TEST_VERBOSE};
$m->submit_form(
form_name => 'ForwardMessage',
fields => {
- To => 'rt-test, rt-to@example.com',
- Cc => 'rt-cc@example.com',
+ To => '"Foo" <rt-foo@example.com>, rt-too@example.com',
+ Cc => 'rt-cc@example.com',
+ Bcc => 'root',
},
button => 'ForwardAndReturn'
);
- $m->content_contains( 'Sent email successfully', 'sent mail msg' );
$m->content_contains(
- 'Forwarded Ticket to rt-test, rt-to@example.com, rt-cc@example.com',
+ 'Forwarded Ticket to Foo &lt;rt-foo@example.com&gt;, &lt;rt-too@example.com&gt;, &lt;rt-cc@example.com&gt;, root &#40;Enoch Root&#41;',
'txn msg' );
my ($mail) = RT::Test->fetch_caught_mails;
like( $mail, qr!Subject: test forward!, 'Subject field' );
- like( $mail, qr!To: rt-test, rt-to\@example.com!, 'To field' );
+ like( $mail, qr!To: .*?rt-foo\@example.com!i, 'To field' );
+ like( $mail, qr!To: .*?rt-too\@example.com!i, 'To field' );
like( $mail, qr!Cc: rt-cc\@example.com!i, 'Cc field' );
+ like( $mail, qr!Bcc: root\@localhost!i, 'Bcc field' );
like( $mail, qr!This is a forward of ticket!, 'content' );
like( $mail, qr!this is an attachment!, 'att content' );
like( $mail, qr!$att_name!, 'att file name' );
@@ -60,22 +62,22 @@ diag "Forward Transaction" if $ENV{TEST_VERBOSE};
$m->submit_form(
form_name => 'ForwardMessage',
fields => {
- To => 'rt-test, rt-to@example.com',
+ To => 'rt-to@example.com, rt-too@example.com',
Cc => 'rt-cc@example.com',
- Bcc => 'rt-bcc@example.com'
+ Bcc => 'root'
},
button => 'ForwardAndReturn'
);
- $m->content_contains( 'Sent email successfully', 'sent mail msg' );
$m->content_like(
-qr/Forwarded Transaction #\d+ to rt-test, rt-to\@example.com, rt-cc\@example.com, rt-bcc\@example.com/,
+qr/Forwarded .*?Transaction #\d+.*? to &lt;rt-to\@example\.com&gt;, &lt;rt-too\@example\.com&gt;, &lt;rt-cc\@example\.com&gt;, root &#40;Enoch Root&#41;/,
'txn msg'
);
my ($mail) = RT::Test->fetch_caught_mails;
like( $mail, qr!Subject: test forward!, 'Subject field' );
- like( $mail, qr!To: rt-test, rt-to\@example.com!, 'To field' );
+ like( $mail, qr!To: .*rt-to\@example.com!i, 'To field' );
+ like( $mail, qr!To: .*rt-too\@example.com!i, 'To field' );
like( $mail, qr!Cc: rt-cc\@example.com!i, 'Cc field' );
- like( $mail, qr!Bcc: rt-bcc\@example.com!i, 'Bcc field' );
+ like( $mail, qr!Bcc: root\@localhost!i, 'Bcc field' );
like( $mail, qr!This is a forward of transaction!, 'content' );
like( $mail, qr!$att_name!, 'att file name' );
like( $mail, qr!this is an attachment!, 'att content' );
@@ -93,9 +95,8 @@ diag "Forward Ticket without content" if $ENV{TEST_VERBOSE};
fields => { To => 'rt-test@example.com', },
button => 'ForwardAndReturn'
);
- $m->content_contains( 'Sent email successfully', 'sent mail msg' );
my ($mail) = RT::Test->fetch_caught_mails;
- like( $mail, qr/Subject: Fwd: \[example\.com #\d\] test forward without content/, 'Subject field' );
+ like( $mail, qr/Subject: \[example\.com #\d\] Fwd: test forward without content/, 'Subject field' );
like( $mail, qr/To: rt-test\@example\.com/, 'To field' );
like( $mail, qr/This is a forward of ticket #\d/, 'content' );
}
@@ -107,7 +108,7 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO
$m->form_name('TicketCreate');
my $attach = $m->current_form->find_input('Attach');
- $attach->filename("awesome.patch");
+ $attach->filename('awesome.pátch');
$attach->headers('Content-Type' => 'text/x-diff');
$m->set_fields(
Subject => 'test forward, empty content but attachments',
@@ -122,8 +123,8 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO
Attach => RT::Test::get_relocatable_file('bpslogo.png', '..', 'data'), # an image!
);
$m->submit;
- $m->content_like( qr/Ticket \d+ created/i, 'created the ticket' );
- $m->content_like( qr/awesome\.patch/, 'uploaded patch file' );
+ $m->content_like( qr/Ticket \d+ created/i, 'created the ticket' );
+ $m->content_like( qr/awesome.p\%C3\%A1tch/, 'uploaded patch file' );
$m->content_like( qr/text\/x-diff/, 'uploaded patch file content type' );
$m->content_like( qr/bpslogo\.png/, 'uploaded image file' );
$m->content_like( qr/image\/png/, 'uploaded image file content type' );
@@ -137,13 +138,12 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO
},
button => 'ForwardAndReturn'
);
- $m->content_contains( 'Sent email successfully', 'sent mail msg' );
- $m->content_like( qr/Forwarded Transaction #\d+ to rt-test\@example\.com/, 'txn msg' );
+ $m->content_like( qr/Forwarded .*?Transaction #\d+.*? to &lt;rt-test\@example\.com&gt;/, 'txn msg' );
my ($mail) = RT::Test->fetch_caught_mails;
like( $mail, qr/Subject: test forward, empty content but attachments/, 'Subject field' );
like( $mail, qr/To: rt-test\@example.com/, 'To field' );
like( $mail, qr/This is a forward of transaction/, 'content' );
- like( $mail, qr/awesome\.patch/, 'att file name' );
+ like( $mail, qr/filename\*\=\"UTF\-8\'\'awesome.p\%C3\%A1tch\"/, 'att file name' );
like( $mail, qr/this is an attachment/, 'att content' );
like( $mail, qr/text\/x-diff/, 'att content type' );
like( $mail, qr/bpslogo\.png/, 'att image file name' );
@@ -153,7 +153,7 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO
diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_VERBOSE};
{
my $mime = MIME::Entity->build(
- From => 'test@example.com',
+ From => '"Tést" <test@example.com>',
Subject => 'attachments for everyone',
Type => 'multipart/mixed',
);
@@ -195,9 +195,8 @@ diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_V
},
button => 'ForwardAndReturn'
);
- $m->content_contains( 'Sent email successfully', 'sent mail msg' );
- $m->content_like( qr/Forwarded Transaction #\d+ to rt-test\@example\.com/, 'txn msg' );
-
+ $m->content_like( qr/Forwarded .*?Transaction #\d+.*? to &lt;rt-test\@example\.com&gt;/, 'txn msg' );
+
# Forward ticket
$m->follow_link_ok( { text => 'Forward', n => 1 }, 'follow 1st Forward' );
$m->submit_form(
@@ -207,15 +206,15 @@ diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_V
},
button => 'ForwardAndReturn'
);
- $m->content_contains( 'Sent email successfully', 'sent mail msg' );
- $m->content_like( qr/Forwarded Ticket to rt-test\@example\.com/, 'txn msg' );
+ $m->content_like( qr/Forwarded Ticket to &lt;rt-test\@example\.com&gt;/, 'txn msg' );
my ($forward_txn, $forward_ticket) = RT::Test->fetch_caught_mails;
- my $tag = qr/Fwd: \[example\.com #\d+\]/;
+ my $tag = qr/\[example\.com #\d+\] Fwd:/;
like( $forward_txn, qr/Subject: $tag attachments for everyone/, 'Subject field is from txn' );
like( $forward_txn, qr/This is a forward of transaction/, 'forward description' );
like( $forward_ticket, qr/Subject: $tag test forward, attachments but no "content"/, 'Subject field is from ticket' );
like( $forward_ticket, qr/This is a forward of ticket/, 'forward description' );
+ like( $forward_ticket, qr/From: \=\?UTF-8\?.* \<test\@example\.com\>/i );
for my $mail ($forward_txn, $forward_ticket) {
like( $mail, qr/To: rt-test\@example.com/, 'To field' );
@@ -259,7 +258,26 @@ diag "Forward Ticket Template with a Subject: line" if $ENV{TEST_VERBOSE};
);
my ($mail) = RT::Test->fetch_caught_mails;
- like($mail, qr/Subject: OVERRIDING SUBJECT/);
+ like($mail, qr/Subject: \[example.com #\d+\] OVERRIDING SUBJECT/);
+}
+
+diag "Forward Transaction with non-ascii subject" if $ENV{TEST_VERBOSE};
+{
+ $m->follow_link_ok( { text => 'Forward', n => 2 }, 'follow 2nd Forward' );
+ my $subject = Encode::decode("UTF-8", 'test non-ascii äöü');
+ $m->submit_form(
+ form_name => 'ForwardMessage',
+ fields => {
+ Subject => $subject,
+ To => 'rt-to@example.com',
+ },
+ button => 'ForwardAndReturn'
+ );
+ my ($mail) = RT::Test->fetch_caught_mails;
+ if ( $mail =~ /Subject: (.+)/ ) {
+ like( Encode::decode("UTF-8", RT::I18N::DecodeMIMEWordsToUTF8( $1, 'Subject' )), qr/$subject/, 'non-ascii subject' );
+ }
+ $m->content_contains( $subject, 'non-ascii subject got displayed correctly' );
}
undef $m;
diff --git a/rt/t/web/ticket_links.t b/rt/t/web/ticket_links.t
index efb615107..994630efd 100644
--- a/rt/t/web/ticket_links.t
+++ b/rt/t/web/ticket_links.t
@@ -52,7 +52,7 @@ for my $type ( "DependsOn", "MemberOf", "RefersTo" ) {
$m->submit;
$m->content_like(qr/Ticket \d+ created/, 'created ticket');
- $m->content_contains("Can&#39;t link to a deleted ticket");
+ $m->content_contains("Linking to a deleted ticket is not allowed");
$id = RT::Test->last_ticket->id;
}
@@ -75,7 +75,7 @@ for my $type ( "DependsOn", "MemberOf", "RefersTo" ) {
$m->field( "$type-$id", "$deleted_id $active_id $inactive_id" );
}
$m->submit;
- $m->content_contains("Can&#39;t link to a deleted ticket");
+ $m->content_contains("Linking to a deleted ticket is not allowed");
if ( $c eq 'base' ) {
$m->content_like(
@@ -165,7 +165,7 @@ for my $type ( "DependsOn", "MemberOf", "RefersTo" ) {
$m->content_lacks('hello test reminder subject');
if ($type eq 'RefersTo') {
$m->text_contains("$baseurl/test_ticket_reference");
- $m->text_contains("Article " . $article->Id . ': test article');
+ $m->text_contains("Article #" . $article->Id . ': test article');
}
}
}
diff --git a/rt/t/web/ticket_modify_all.t b/rt/t/web/ticket_modify_all.t
index 6d19b28e4..6b85d98cf 100644
--- a/rt/t/web/ticket_modify_all.t
+++ b/rt/t/web/ticket_modify_all.t
@@ -1,13 +1,15 @@
use strict;
use warnings;
-use RT::Test tests => 22;
+use RT::Test tests => undef;
my $ticket = RT::Test->create_ticket(
Subject => 'test bulk update',
Queue => 1,
);
+RT->Config->Set(AutocompleteOwners => 1);
+
my ( $url, $m ) = RT::Test->started_ok;
ok( $m->login, 'logged in' );
@@ -19,18 +21,12 @@ $m->submit_form(
button => 'SubmitTicket',
);
-$m->content_contains("Message recorded", 'updated ticket');
+$m->content_contains("Comments added", 'updated ticket');
$m->content_lacks("this is update content", 'textarea is clear');
$m->get_ok($url . '/Ticket/Display.html?id=' . $ticket->id );
$m->content_contains("this is update content", 'updated content in display page');
-# NOTE http://issues.bestpractical.com/Ticket/Display.html?id=18284
-RT::Test->stop_server;
-RT->Config->Set(AutocompleteOwners => 1);
-($url, $m) = RT::Test->started_ok;
-$m->login;
-
$m->get_ok($url . '/Ticket/ModifyAll.html?id=' . $ticket->id);
$m->form_name('TicketModifyAll');
@@ -57,10 +53,18 @@ $m->field('Told_Date' => "2015-01-01 00:00:00");
$m->click('SubmitTicket');
$m->text_contains("Last Contact: (Thu Jan 01 00:00:00 2015)", 'told date successfully updated');
-$m->form_name('TicketModifyAll');
-$m->field('Due_Date' => "2016-01-01 00:00:00");
-$m->click('SubmitTicket');
-$m->text_contains("Due: (Fri Jan 01 00:00:00 2016)", 'due date successfully updated');
+for my $unset ("0", "-", " ") {
+ $m->form_name('TicketModifyAll');
+ $m->field('Due_Date' => "2016-01-01 00:00:00");
+ $m->click('SubmitTicket');
+ $m->text_contains("Due: (Fri Jan 01 00:00:00 2016)", 'due date successfully updated');
+
+ $m->form_name('TicketModifyAll');
+ $m->field('Due_Date' => $unset);
+ $m->click('SubmitTicket');
+ $m->text_contains("Due: (Not set)", "due date successfully cleared with '$unset'");
+ $m->warning_like(qr/Couldn't parse date '-'/) if $unset eq "-";
+}
$m->get( $url . '/Ticket/ModifyAll.html?id=' . $ticket->id );
$m->form_name('TicketModifyAll');
@@ -76,8 +80,9 @@ $m->field(WatcherTypeEmail => 'Requestor');
$m->field(WatcherAddressEmail => 'root@localhost');
$m->click('SubmitTicket');
$m->text_contains(
- "root is already a Requestor for this ticket",
+ "root is already a Requestor",
'no duplicate watchers',
);
-# XXX TODO test other parts, i.e. links
+undef $m;
+done_testing;
diff --git a/rt/t/web/ticket_modify_people.t b/rt/t/web/ticket_modify_people.t
index 750be3f2c..cefbf915b 100644
--- a/rt/t/web/ticket_modify_people.t
+++ b/rt/t/web/ticket_modify_people.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test tests => 23;
+use RT::Test tests => 25;
my $root = RT::Test->load_or_create_user( Name => 'root' );
my $group_foo = RT::Group->new($RT::SystemUser);
@@ -80,7 +80,7 @@ ok(
$m->reload;
ok(
$m->find_link(
- text => 'Enoch Root',
+ text => 'root (Enoch Root)',
url_regex => qr!/Admin/Users/Modify\.html!,
),
'got link to modify user'
@@ -108,6 +108,16 @@ ok(
'got link to modify group'
);
+$m->submit_form_ok({
+ with_fields => {
+ WatcherTypeEmail1 => 'Cc',
+ WatcherAddressEmail1 => '"Foo Bar" <foo@example.com>',
+ },
+ button => 'SubmitTicket',
+}, "Added email with phrase as watcher");
+
+my $foo = RT::Test->load_or_create_user( EmailAddress => 'foo@example.com' );
+is $foo->RealName, "Foo Bar", "RealName matches";
# TODO test Add|Delete people
diff --git a/rt/t/web/ticket_owner.t b/rt/t/web/ticket_owner.t
index 81508534a..782e68f8d 100644
--- a/rt/t/web/ticket_owner.t
+++ b/rt/t/web/ticket_owner.t
@@ -2,7 +2,7 @@
use strict;
use warnings;
-use RT::Test nodata => 1, tests => 105;
+use RT::Test nodata => 1, tests => undef;
my $queue = RT::Test->load_or_create_queue( Name => 'Regression' );
ok $queue && $queue->id, 'loaded or created queue';
@@ -10,12 +10,18 @@ ok $queue && $queue->id, 'loaded or created queue';
my $user_a = RT::Test->load_or_create_user(
Name => 'user_a', Password => 'password',
);
-ok $user_a && $user_a->id, 'loaded or created user';
+ok $user_a && $user_a->id, 'loaded or created user: ' . $user_a->Name;
my $user_b = RT::Test->load_or_create_user(
Name => 'user_b', Password => 'password',
);
-ok $user_b && $user_b->id, 'loaded or created user';
+ok $user_b && $user_b->id, 'loaded or created user: ' . $user_b->Name;
+
+# To give ReassignTicket
+my $user_c = RT::Test->load_or_create_user(
+ Name => 'user_c', Password => 'password',
+);
+ok $user_c && $user_c->id, 'loaded or created user: ' . $user_c->Name;
my ($baseurl, $agent_a) = RT::Test->started_ok;
@@ -360,6 +366,7 @@ ok(
]
},
{ Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] },
+ { Principal => $user_c, Right => [qw(SeeQueue ShowTicket ReassignTicket)] },
),
'set rights'
);
@@ -383,10 +390,12 @@ diag
fields => { Owner => $user_a->id },
button => 'SubmitTicket',
);
- $agent_a->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message' );
+ like($agent_a->dom->at('.transaction.people .description')->all_text,
+ qr/user_a\s*-\s*Taken/, 'got user_a Taken message' );
$agent_b->goto_ticket($id);
- $agent_b->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message for user b ' );
+ like($agent_b->dom->at('.transaction.people .description')->all_text,
+ qr/user_a\s*-\s*Taken/, 'got user_a Taken message for user b' );
}
diag
@@ -410,9 +419,106 @@ diag
$agent_a->content_contains( 'Owner changed from Nobody to user_a',
'got set message in Basics' );
$agent_a->goto_ticket($id);
- $agent_a->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message' );
+ like($agent_a->dom->at('.transaction.people .description')->all_text,
+ qr/user_a\s*-\s*Taken/, 'got user_a Taken message' );
$agent_b->goto_ticket($id);
- $agent_b->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message for user b ' );
+ like($agent_b->dom->at('.transaction.people .description')->all_text,
+ qr/user_a\s*-\s*Taken/, 'got user_a Taken message for user b' );
+}
+
+my $agent_c = RT::Test::Web->new;
+ok $agent_c->login('user_c', 'password'), 'logged in as user C';
+
+diag "user can assign ticket to new owner with ReassignTicket right";
+{
+ my $ticket = RT::Ticket->new($user_a);
+ my ( $id, $txn, $msg ) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => 'test',
+ );
+ ok $id, 'created a ticket #' . $id or diag "error: $msg";
+ is $ticket->Owner, RT->Nobody->id, 'correct owner';
+
+ $agent_c->goto_ticket($id);
+ ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link';
+ ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link';
+
+ $agent_a->goto_ticket($id);
+ $agent_a->content_lacks('Taken', 'no Taken');
+ $agent_a->follow_link_ok( { text => 'Basics' }, 'Ticket -> Basics' );
+ $agent_a->submit_form(
+ form_name => 'TicketModify',
+ fields => { Owner => $user_a->id },
+ );
+ $agent_a->content_contains( 'Owner changed from Nobody to user_a',
+ 'got set message in Basics' );
+ $agent_a->goto_ticket($id);
+ like($agent_a->dom->at('.transaction.people .description')->all_text,
+ qr{user_a\s*-\s*Taken}, 'got user_a Taken message' );
+
+ $agent_c->goto_ticket($id);
+ ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link';
+ ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link';
+ $agent_c->follow_link_ok( { text => 'Basics' }, 'Ticket -> Basics' );
+ my $form = $agent_c->form_name('TicketModify');
+ is $form->value('Owner'), $user_a->id, 'correct owner selected';
+
+ ok grep($_ == $user_b->id, $form->find_input('Owner')->possible_values),
+ 'user B is listed as potential owner';
+ $agent_c->select('Owner', $user_b->id);
+ $agent_c->submit;
+ $agent_c->content_contains( 'Owner changed from user_a to user_b',
+ 'got set message in Basics' );
+ $agent_c->goto_ticket($id);
+ $agent_c->content_like( qr{Owner forcibly changed}, 'got owner forcibly changed message' );
+ ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link';
+}
+
+ok(
+ RT::Test->add_rights(
+ { Principal => $user_c, Right => [qw(OwnTicket)] },
+ ),
+ 'add rights'
+);
+diag "user can take/steal ticket with ReassignTicket+OwnTicket right";
+{
+ my $ticket = RT::Ticket->new($user_a);
+ my ( $id, $txn, $msg ) = $ticket->Create(
+ Queue => $queue->id,
+ Subject => 'test',
+ );
+ ok $id, 'created a ticket #' . $id or diag "error: $msg";
+ is $ticket->Owner, RT->Nobody->id, 'correct owner';
+
+ $agent_c->goto_ticket($id);
+ ok( ($agent_c->find_all_links( text => 'Take' ))[0], 'has Take link' );
+ ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link';
+
+ $agent_a->goto_ticket($id);
+ $agent_a->content_lacks('Taken', 'no Taken');
+ $agent_a->follow_link_ok( { text => 'Basics' }, 'Ticket -> Basics' );
+ $agent_a->submit_form(
+ form_name => 'TicketModify',
+ fields => { Owner => $user_a->id },
+ );
+ $agent_a->content_contains( 'Owner changed from Nobody to user_a',
+ 'got set message in Basics' );
+ $agent_a->goto_ticket($id);
+ like($agent_a->dom->at('.transaction.people .description')->all_text,
+ qr{user_a\s*-\s*Taken}, 'got user_a Taken message' );
+
+ $agent_c->goto_ticket($id);
+ ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link';
+ ok( ($agent_c->find_all_links( text => 'Steal' ))[0], 'has Steal link' );
+ $agent_c->follow_link_ok( { text => 'Steal' }, 'Ticket -> Steal' );
+ $agent_c->content_contains( 'Owner changed from user_a to user_c', 'steal message' );
+ ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link';
+ ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link';
}
+
+undef $agent_a;
+undef $agent_b;
+undef $agent_c;
+done_testing;
diff --git a/rt/t/web/ticket_preserve_basics.t b/rt/t/web/ticket_preserve_basics.t
new file mode 100644
index 000000000..145941407
--- /dev/null
+++ b/rt/t/web/ticket_preserve_basics.t
@@ -0,0 +1,110 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my $ticket = RT::Test->create_ticket(
+ Subject => 'test ticket basics',
+ Queue => 1,
+);
+
+my ( $url, $m ) = RT::Test->started_ok;
+ok( $m->login, 'logged in' );
+
+my $root = RT::Test->load_or_create_user( Name => 'root' );
+
+# Failing test where the time units are not preserved when you
+# click 'Add more files' on Display
+my @form_tries = (
+ {Subject => "hello rt"},
+ {Status => "open"},
+ {Owner => $root->id},
+
+ (
+ map +{
+ "Time$_" => undef,
+ "Time$_-TimeUnits" => 'hours',
+ }, qw/Estimated Worked Left/
+ ),
+ (
+ map +{
+ "Time$_" => '1',
+ "Time$_-TimeUnits" => 'hours',
+ }, qw/Estimated Worked Left/
+ ),
+
+ {InitialPriority => "10"},
+ {FinalPriority => "10"},
+);
+
+for my $try (@form_tries) {
+ $m->goto_create_ticket(1);
+ $m->form_name('TicketCreate');
+ $m->set_fields(%$try);
+ $m->click('AddMoreAttach');
+ $m->form_name('TicketCreate');
+ for my $field (keys %$try) {
+ is(
+ $m->value($field),
+ defined($try->{$field}) ? $try->{$field} : '',
+ "field $field is the same after the form was submitted"
+ );
+ }
+}
+
+# Test for time unit preservation in Jumbo
+for my $try (@form_tries) {
+ my $jumbo_ticket = RT::Test->create_ticket(
+ Subject => 'test jumbo ticket basics',
+ Queue => 1,
+ );
+
+ local($try->{Priority}) = delete local($try->{InitialPriority})
+ if exists $try->{InitialPriority};
+
+ $m->get( $url . "/Ticket/ModifyAll.html?id=" . $jumbo_ticket->id );
+ $m->form_name('TicketModifyAll');
+ $m->set_fields(%$try);
+ $m->click('AddMoreAttach');
+ $m->form_name('TicketModifyAll');
+ for my $field (keys %$try) {
+ is(
+ $m->value($field),
+ defined($try->{$field}) ? $try->{$field} : '',
+ "field $field is the same after the Jumbo form was submitted"
+ );
+ }
+}
+
+my $cf = RT::Test->load_or_create_custom_field(
+ Name => 'CF1',
+ Type => 'Freeform',
+ Pattern => '.', # mandatory
+ Queue => 'General',
+);
+
+# More time unit testing by a failing CF validation
+$m->get_ok($url.'/Admin/CustomFields/Objects.html?id='.$cf->id);
+$m->form_with_fields('UpdateObjs');
+$m->tick('AddCustomField-'.$cf->id => '0'); # Make CF global
+$m->click('UpdateObjs');
+$m->text_contains('Object created', 'CF applied globally');
+
+# Test for preservation when a ticket is submitted and CF validation fails
+for my $try (@form_tries) {
+ $m->goto_create_ticket(1);
+ $m->form_name('TicketCreate');
+ $m->set_fields(%$try);
+ $m->submit();
+ $m->form_name('TicketCreate');
+ for my $field (keys %$try) {
+ is(
+ $m->value($field),
+ defined($try->{$field}) ? $try->{$field} : '',
+ "field $field is the same after the form was submitted"
+ );
+ }
+}
+
+undef $m;
+done_testing();
diff --git a/rt/t/web/ticket_txn_content.t b/rt/t/web/ticket_txn_content.t
index c0cae976c..096d78e31 100644
--- a/rt/t/web/ticket_txn_content.t
+++ b/rt/t/web/ticket_txn_content.t
@@ -27,14 +27,14 @@ sub follow_parent_with_headers_link {
my $m = shift;
my $link = $m->find_link(@_)->url;
$link =~ s{/(\d+)$}{"/" . ($1-1)}e; # get the parent attach
- $m->get_ok($baseurl . $link);
+ $m->get_ok($link);
}
sub follow_with_headers_link {
my $m = shift;
my $link = $m->find_link(@_)->url;
$link =~ s{/\d+/(\d+)/.+$}{/WithHeaders/$1}; # frob into a with headers url
- $m->get_ok($baseurl . $link);
+ $m->get_ok($link);
}
for my $type ( 'text/plain', 'text/html' ) {
diff --git a/rt/t/web/user_update.t b/rt/t/web/user_update.t
index 54139d797..7be088b0a 100644
--- a/rt/t/web/user_update.t
+++ b/rt/t/web/user_update.t
@@ -8,7 +8,7 @@ ok( $m->login(), 'logged in' );
$m->follow_link_ok({text => 'About me'});
$m->submit_form_ok({ with_fields => { Lang => 'ja'} },
"Change to Japanese");
-$m->text_contains("Lang changed from (no value) to 'ja'");
+$m->text_contains(Encode::decode("UTF-8","Langは「(値なし)」から「'ja'」に変更されました"));
$m->text_contains(Encode::decode("UTF-8","実名"), "Page content is japanese");
# we only changed one field, and it wasn't the default, so this feedback is
@@ -19,9 +19,7 @@ $m->content_lacks("That is already the current value");
$m->submit_form_ok({ with_fields => { Lang => 'en_us'} },
"Change back to english");
-# This message shows up in Japanese
-# $m->text_contains("Lang changed from 'ja' to 'en_us'");
-$m->text_contains(Encode::decode("UTF-8","Langは「'ja'」から「'en_us'」に変更されました"));
+$m->text_contains("Lang changed from 'ja' to 'en_us'");
$m->text_contains("Real Name", "Page content is english");
# Check for a lack of spurious updates
@@ -30,11 +28,11 @@ $m->content_lacks("That is already the current value");
# Ensure that we can change the language back to the default.
$m->submit_form_ok({ with_fields => { Lang => 'ja'} },
"Back briefly to Japanese");
-$m->text_contains("Lang changed from 'en_us' to 'ja'");
+$m->text_contains(Encode::decode("UTF-8","Langは「'en_us'」から「'ja'」に変更されました"));
$m->text_contains(Encode::decode("UTF-8","実名"), "Page content is japanese");
$m->submit_form_ok({ with_fields => { Lang => ''} },
"And set to the default");
-$m->text_contains(Encode::decode("UTF-8","Langは「'ja'」から「(値なし)」に変更されました"));
+$m->text_contains("Lang changed from 'ja' to (no value)");
$m->text_contains("Real Name", "Page content is english");
undef $m;
diff --git a/rt/t/web/walk.t b/rt/t/web/walk.t
index 97aa36e12..2f7272739 100644
--- a/rt/t/web/walk.t
+++ b/rt/t/web/walk.t
@@ -53,7 +53,7 @@ my @links = (
'/Admin/Groups/Modify.html?id=' . $group->id,
'/Admin/Queues/Modify.html?id=' . $queue->id,
'/Admin/CustomFields/Modify.html?id=' . $cf->id,
- '/Admin/Global/Scrip.html?id=1',
+ '/Admin/Scrips/Modify.html?id=1',
'/Admin/Global/Template.html?Template=1',
'/Admin/Articles/Classes/Modify.html?id=' . $class->id,
'/Search/Build.html?Query=id<10',