The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

#!perl
# vim: softtabstop=4 tabstop=4 shiftwidth=4 ft=perl expandtab smarttab
# This test is for testing RT::Client::REST with a real instance of RT.
# This is so that we can verify bug reports and compare functionality
# (and bugs) between different versions of RT.
use strict;
use File::Spec::Functions qw/ splitpath /;
BEGIN {
unless ( $ENV{RELEASE_TESTING} ) {
plan( skip_all => 'these tests are for release candidate testing' );
}
if ( grep { not defined $ENV{$_} } (qw(RTSERVER RTPASS RTUSER)) ) {
plan( skip_all => 'one of RTSERVER, RTPASS, or RTUSER is not set' );
}
}
{
# We will only use letters, because this string may be used for names of
# queues and users in RT and we don't want to fail because of RT rules.
my @chars = ( 'a' .. 'z', 'A' .. 'Z' );
sub random_string {
my $retval = '';
for ( 1 .. 10 ) {
$retval .= $chars[ int( rand( scalar(@chars) ) ) ];
}
return $retval;
}
}
plan 'no_plan';
use File::Temp qw(tempfile);
my $rt = RT::Client::REST->new( server => $ENV{RTSERVER}, );
ok( $rt, 'RT instance is created' );
# Log in with wrong credentials and see that we get expected error
{
my $e;
try {
$rt->login(
username => $ENV{RTUSER},
password => 'WRONG' . $ENV{RTPASS}
);
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('RT::Client::REST::AuthenticationFailureException') ) {
$e = $_;
}
else {
$_->rethrow;
}
};
ok( defined($e),
'Logging in with wrong credentials throws expected error' );
}
# Now log in successfully
{
my $e;
try {
$rt->login( username => $ENV{RTUSER}, password => $ENV{RTPASS} );
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('RT::Client::REST::Exception') ) {
$e = $_;
}
else {
$_->rethrow;
}
};
ok( !defined($e), 'login is successful' );
}
# Create a user
my $user_id;
my %user_props = (
name => random_string(),
password => random_string(),
comments => random_string(),
real_name => random_string(),
);
{
my ( $user, $e );
try {
$user = RT::Client::REST::User->new(
rt => $rt,
%user_props,
)->store;
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('RT::Client::REST::CouldNotCreateObjectException') ) {
$e = $_;
}
else {
$_->rethrow;
}
};
ok( defined($user),
"user $user_props{name} created successfully, id: "
. ( defined $user ? $user->id : 'UNDEF' ) );
ok( !defined($e), '...and no exception was thrown' );
$user_id = $user->id;
}
# Retrieve the user we just created and verify its properties
{
my $user = RT::Client::REST::User->new( rt => $rt, id => $user_id );
my $e;
try {
$user->retrieve;
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("fetching user threw $e");
}
else {
$_->rethrow;
}
};
ok( !defined($e), 'fetched user without exception being thrown' );
while ( my ( $prop, $val ) = each(%user_props) ) {
next if $prop eq 'password'; # This property comes back obfuscated
is( $user->$prop, $val, "user property `$prop' matches" );
}
}
# Create a queue
my $queue_name = 'A queue named ' . random_string();
my $queue_id;
my $queue;
{
my $e;
try {
$queue = RT::Client::REST::Queue->new(
rt => $rt,
name => $queue_name,
)->store;
$queue_id = $queue->id;
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("test queue store: $e");
}
else {
$_->rethrow;
}
};
ok( $queue, "Create test queue '$queue_name'" );
ok( !defined($e), 'created test queue without exception being thrown' );
}
{
my $e;
try {
$queue = RT::Client::REST::Queue->new(
rt => $rt,
id => $queue_id,
)->retrieve;
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("queue retrieve $e");
}
else {
$_->rethrow;
}
};
is( $queue->name, $queue_name, 'test queue name matches' );
# TODO: with 4.2.3, warning "Unknown key: disabled" is printed
}
# Create a ticket
my $ticket;
{
my $e;
my $subject = 'This is a subject ' . random_string();
try {
$ticket = RT::Client::REST::Ticket->new(
rt => $rt,
queue => $queue_id,
subject => $subject,
)->store( text => 'Some random text ' . random_string() );
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("ticket store: $e");
}
else {
$_->rethrow;
}
};
ok( defined($ticket),
"Created ticket '$subject' ID " . ( defined $ticket ? $ticket->id : 'UNDEF' ) );
ok( !defined($e), 'No exception thrown when ticket created' );
}
# Attach something to the ticket and verify its count and contents
{
my $att_contents = "dude this is a text attachment\n";
my ( $fh, $filename ) = tempfile;
$fh->print($att_contents);
$fh->close;
my $message = 'This is a message ' . random_string(),
my $e;
try {
$ticket->comment(
message => $message,
attachments => [$filename],
);
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("attach to ticket: $e");
}
else {
$_->rethrow;
}
};
ok( !defined($e), 'Create attachment and no exception thrown' );
unlink $filename;
$e = undef;
try {
my $atts = $ticket->attachments;
# XXX With RT 4.2.3, the count is 4. Is it the same with previous
# versions or is this a change in behavior?
is( $atts->count, 4, 'There are 4 attachment to ticket ' . $ticket->id );
my $att_iter = $atts->get_iterator;
my $basename = (splitpath($filename))[2];
my ($att) = grep { $_->file_name eq $basename } &$att_iter;
if ($att) {
ok(1, "Found attachment with filename: $basename");
is( $att->content, $att_contents, 'Attachment content matches' );
}
else {
ok(0, "Found attachment with filename: $basename");
}
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("attach to ticket: $e");
}
else {
$_->rethrow;
}
};
ok( !defined($e), 'listed attachments and no exception thrown' );
}
# Comment with HTML
{
my $message = sprintf('Some <b>html</b> message text <pre>%s</pre>', random_string());
my $e;
try {
$ticket->comment(
message => $message,
html => 1
);
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("attach to ticket: $e");
}
else {
$_->rethrow;
}
};
ok( !defined($e), 'Add html comment and no exception thrown' );
try {
my $atts = $ticket->attachments;
my $att_iter = $atts->get_iterator;
my $att = (&$att_iter)[-1];
if ($att) {
ok(1, 'Retrieved final attachment');
is( $att->content_type, 'text/html', 'Content-Type is text/html' );
}
else {
ok(0, 'Retrieved final attachment');
}
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("attach to ticket: $e");
}
else {
$_->rethrow;
}
};
ok( !defined($e), 'listed attachments and no exception thrown' );
}
# Search for tickets (with format s)
{
my (@results, $e);
try {
@results = $rt->search(
type => 'ticket',
query => "Queue='$queue_name'",
format => 's'
)
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("searching for tickets (with format s): $e");
}
else {
$_->rethrow;
}
};
ok( scalar @results > 0, 'Found some results (with format s)' );
is_deeply( \@results, [[ $ticket->id, $ticket->subject ]], 'Search results as expected (with format s)' );
ok( !defined($e), 'No exception thrown when searching tickets (with format s)' );
}
# Delete the ticket
{
my $e;
try {
$ticket->status('deleted');
$ticket->store;
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("delete ticket: $e");
}
else {
$_->rethrow;
}
};
ok( !defined($e), 'ticket deleted and no exception thrown' );
}
# TODO: RT 90112: Attachment retrieval returns wrongly decoded files
# Disable the queue
{
my $e;
try {
$queue->disabled(1);
$queue->store;
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ( $_->isa('Exception::Class::Base') ) {
$e = $_;
diag("disable test queue: $e");
}
else {
$_->rethrow;
}
};
ok( !defined($e), 'disabled queue without exception being thrown' );
}