Elive/t/soap-participant-list.t
#!perl -T
use warnings; use strict;
use Test::More tests => 38;
use Test::Fatal;
use Test::Warn;
use List::Util;
use lib '.';
use t::Elive;
use Carp;
use version;
use Elive;
use Elive::Entity::ParticipantList;
use Elive::Entity::Meeting;
use Elive::Entity::User;
use Elive::Entity::Group;
use Elive::Util;
use Try::Tiny;
our $t = Test::More->builder;
our $class = 'Elive::Entity::Meeting' ;
use Pod::Usage;
use Getopt::Long qw{};
=head1 NAME
soap-participant-list.t - elm 2.x participant tests (setParticipant etc)
=head1 SYNOPSIS
prove --lib -v soap-participant-list.t :: \ #<opts>
-[no]unknowns # include unknown users in the stress test
-timeout=sec # set a timeout on the soap call
-participant_limit=n # max no. of participants in the stress-test
=cut
my $unknowns = 1;
my $participant_limit;
my $timeout_sec = $ENV{ELIVE_TEST_PARTICIPANT_TIMEOUT} || 120;
Getopt::Long::GetOptions('u|unknowns!' => \$unknowns,
't|timeout=i' => \$timeout_sec,
'p|participant_limit=i' => \$participant_limit,
) or pod2usage(2);
our $connection;
SKIP: {
my %result = t::Elive->test_connection( only => 'real', timeout => $timeout_sec);
my $auth = $result{auth};
skip ($result{reason} || 'skipping live tests', 38)
unless $auth;
my $connection_class = $result{class};
my $connection = $connection_class->connect(@$auth);
Elive->connection($connection);
#
# - ELM 3.3.4 / 10.0.2 - includes significant bug fixes
my $server_details = Elive->server_details
or die "unable to get server details - are all services running?";
our $version = version->declare( $server_details->version )->numify;
our $version_10_0_1 = version->declare( '10.0.1' )->numify;
our $elm_3_3_4_or_higher = $version >= $version_10_0_1;
our $version_11_1_2 = version->declare( '11.1.2' )->numify;
our $elm_3_5_0_or_higher = $version >= $version_11_1_2;
$participant_limit ||= $ENV{ELIVE_TEST_PARTICIPANT_LIMIT};
# older ELMs choke quite easily
$participant_limit ||= $elm_3_5_0_or_higher? 500: 200;
my $meeting_start = time();
my $meeting_end = $meeting_start + 900;
$meeting_start .= '000';
$meeting_end .= '000';
my %meeting_data = (
name => 'test meeting, generated by t/soap-participant-list.t',
facilitatorId => Elive->login->userId,
password => 'test', # what else?
start => $meeting_start,
end => $meeting_end,
privateMeeting => 1,
);
my ($meeting) = $class->insert(\%meeting_data);
isa_ok($meeting, $class, 'meeting');
my $participants_deep_ref = [{user => Elive->login->userId,
role => 2}];
#
# NB. not neccessary to insert prior to update, but since we allow it
is(
exception {my $_p = Elive::Entity::ParticipantList->insert(
{meetingId => $meeting->meetingId,
participants => $participants_deep_ref});
note ("participants: ".$_p->participants->stringify);
} => undef,
'insert of participant deep list - lives');
my $participant_list = Elive::Entity::ParticipantList->retrieve($meeting->meetingId);
isa_ok($participant_list, 'Elive::Entity::ParticipantList', 'participant_list');
is($participant_list->participants->stringify, Elive->login->userId.'=2',
'participant deep list - set correctly');
$participant_list->participants([Elive->login->userId.'=1']);
is($participant_list->participants->stringify, Elive->login->userId.'=1',
'participant shallow list - set correctly');
is( exception {$participant_list->update()} => undef, 'participant list update - lives');
is($participant_list->participants->stringify, Elive->login->userId.'=2',
'participant list update - has reset moderator privileges');
$participant_list->update({participants => Elive->login->userId.'=2'});
note ("participants=".$participant_list->participants->stringify);
is($participant_list->participants->stringify, Elive->login->userId.'=2',
'participant string list - set correctly');
#
# lets grab some volunteers from the audience!
#
my ($participant1, $participant2, @participants);
is( exception {
#
# for datasets with 1000s of entries
($participant1,$participant2, @participants) = grep {$_->userId ne $meeting->facilitatorId} @{ Elive::Entity::User->list(filter => 'lastName = Sm*') };
# for middle sized datasets
($participant1,$participant2, @participants) = grep {$_->userId ne $meeting->facilitatorId} @{ Elive::Entity::User->list(filter => 'lastName = S*') }
unless @participants+2 >= $participant_limit;
#
# for modest test datasets
($participant1,$participant2, @participants) = grep {$_->userId ne $meeting->facilitatorId} @{ Elive::Entity::User->list() }
unless @participants+2 >= $participant_limit;
} => undef,
'get_users - lives');
#
# only want a handful
#
my @participants_sample = @participants > 10
? @participants[0 .. 9]
: @participants;
if ($participant2) {
$participant_list->participants->add($participant1->userId.'=3');
is( exception {$participant_list->update} => undef, 'adding participant - lives');
ok(!$meeting->is_moderator( $participant1), '!is_moderator($participant1)');
ok((grep {$_->user->userId eq $participant1->userId} @{ $participant_list->participants }), 'participant 1 found in participant list');
ok((grep {$_->user->userId eq $participant1->userId && !$_->is_moderator} @{ $participant_list->participants }), 'participant 1 is not a moderator');
$participant_list->participants->add($participant2->userId.'=3');
$participant_list->update();
TODO: {
#
# is_participant() give variable results on various ELM versions
# ELM 3.0 - 3.3.4 under LDAP - best to treat is as broken
#
local($TODO) = 'reliable - is_participant()';
ok($meeting->is_participant( $participant1), 'is_participant($participant1)');
ok($meeting->is_participant( $participant2), 'is_participant($participant2)');
}
ok(!$meeting->is_moderator( $participant2), '!is_moderator($participant2)');
ok((grep {$_->user->userId eq $participant2->userId} @{ $participant_list->participants }), 'participant 2 found in participant list');
ok((grep {$_->user->userId eq $participant2->userId && ! $_->is_moderator} @{ $participant_list->participants }), 'participant 2 is not a moderator');
}
else {
$t->skip('unable to find any other users to act as participants(?)',)
for (1..9);
}
# indirect detection of LDAP integration. We can expect the
# userId to be mapped to the loginName
my $looks_like_ldap = $participant2 && $participant2->userId eq $participant2->loginName;
if ($looks_like_ldap && $elm_3_5_0_or_higher) {
#
# Check the following newer features
# 1. passing participants by name
# 2. case insensitivity of usernames
# NB under LDAP loginName :== userId
$participant_list->reset();
$participant_list->participants->add(Elive::Entity::User->quote($participant1->loginName).'=3');
is(exception {$participant_list->update()} => undef, 'adding participant by loginName - lives');
ok((grep {$_->user->userId eq $participant1->userId} @{ $participant_list->participants }), 'participant by loginName - found in participant list');
my $login_name_toggled = join('', map {$_ =~ m{[A-Z]}? lc: uc} split('', $participant2->loginName));
$participant_list->reset();
$participant_list->participants->add(Elive::Entity::User->quote($login_name_toggled).'=3');
is(exception {$participant_list->update()} => undef, 'participant by loginName (case insensitive - lives');
ok((grep {$_->user->userId eq $participant2->userId} @{ $participant_list->participants }), 'participant by loginName (case insensitive) - found in participant list');
}
else {
$t->skip("skipping ldap specific tests")
for (1..4);
}
$participant_list->reset();
if (@participants) {
is( exception {$participant_list->update({participants => \@participants_sample}) => undef,
} => undef, 'setting up a larger meeting - lives');
}
else {
$t->skip('insufficent users to run large meeting tests');
}
ok($meeting->is_participant( Elive->login), 'is_participant($moderator)');
ok($meeting->is_moderator( Elive->login), 'is_moderator($moderator)');
my $gate_crasher = 'gate_crasher_'.t::Elive::generate_id();
ok(!$meeting->is_participant( $gate_crasher ), '!is_participant($gate_crasher)');
ok(!$meeting->is_moderator( $gate_crasher ), '!is_moderator($gate_crasher)');
isnt( exception {
$participant_list->participants->add($gate_crasher.'=3');
$participant_list->update($gate_crasher.'=3');
} => undef,
'add of unknown participant - dies');
is( exception {$participant_list->update({participants => []})} => undef,
'clearing participants - lives');
my $p = $participant_list->participants;
#
# check our reset policy. Updating/creating an empty participant
# list is effectively the same as a reset. Ie, we end up with
# the facilitator as the sole participant, with a role of moderator (2).
#
is(@$p, 1, 'participant_list reset - single participant');
is($p->[0]->user && $p->[0]->user->userId, $meeting->facilitatorId,
'participant_list reset - single participant is the facilitator');
ok($p->[0]->is_moderator,
'participant_list reset - single participant has moderator role');
if (! $elm_3_3_4_or_higher ) {
#
# The next test verifies bug fixes under ELM 3.3.4/10.0.2. It probably
# wont work with 10.0.1 or earlier.
#
$t->skip('skipping participant long-list test for Elluminate < v10.0.2')
for (1..2);
}
elsif ( !$participant2 ) {
$t->skip('not enough participants to run long-list test')
for (1..2);
}
else {
#
# stress test underlying setParticipantList command we need to do a direct SOAP
# call to bypass overly helpful readback checks and removal of duplicates.
#
my @big_user_list;
my %expected_users = ($meeting->facilitatorId => 1);
MAKE_BIG_LIST:
while (1) {
foreach ($participant1, $participant2, @participants) {
#
# include a smattering of unknown users
#
if ($unknowns && rand() < .1) {
#
# include a smattering of random unknown users
#
my $unknown_user = t::Elive::generate_id();
push (@big_user_list, $unknown_user);
}
$expected_users{$_->userId}++;
push (@big_user_list, $_->userId);
last MAKE_BIG_LIST
if @big_user_list >= $participant_limit;
}
}
note sprintf('stress testing with %d participants (timeout %d sec)...', scalar @big_user_list, $timeout_sec);
#
# low level test that the setParticipantList adapter will accept
# a long list. was a problem prior to elm 3.3.4
#
do {
is( exception {
my $participants_str = join(';',
Elive->login->userId.'=2',
map {$_.'=3'} @big_user_list
);
note "participants in: $participants_str";
# this can hang - add a timeout
local $SIG{ALRM} = sub { die "test failed to complete after $timeout_sec seconds\n" };
alarm $timeout_sec;
my $som = $connection->call('setParticipantList' => (meetingId => $meeting->meetingId, users => $participants_str));
alarm 0;
$connection->_check_for_errors( $som );
} => undef,
'participants long-list test - lives'
);
#
# refetch the participant list and check that all real users
# are present
#
$participant_list = Elive::Entity::ParticipantList->retrieve($meeting->meetingId);
my $participants = $participant_list->participants;
my @actual_users = sort map {$_->user->userId} @$participants;
note "actual users: @actual_users";
my %users_out;
@users_out{ @actual_users } = undef;
is_deeply(\@actual_users, [sort keys %expected_users], "participant list as expected (no repeats or unknown users)")
or do {
foreach (sort keys %expected_users) {
diag "\t - user $_ has not been accepted as a participant"
unless exists $users_out{ $_ }
}
foreach (sort keys %users_out) {
diag "\t - user $_ has popped up, out of nowhere!?"
unless exists $expected_users{ $_ }
}
};
}
}
my $group;
my @groups;
my $group_member;
#
# test groups of participants
#
is( exception {
@groups = @{ Elive::Entity::Group->list() } } => undef,
'list all groups - lives');
splice(@groups, 10) if @groups > 10;
my $invited_guest = 'Robert(bob@acme.org)';
if (Elive->debug) {
$t->skip('debugging enable - wont check for warnings');
}
else {
warnings_like(sub {$participant_list->update({ participants => [$participant1, $invited_guest]})}, qr{ignoring}, 'participant guest - "ignored" warning under elm 2.x');
}
#
# you've got to refetch the group to populate the list of recipients
($group) = List::Util::first {$_->retrieve($_); @{ $_->members } } @groups;
if ($group) {
if (Elive->debug) {
$t->skip('debugging enable - wont check for warnings');
}
else {
note "using group ".$group->name;
warnings_like(sub {$participant_list->update({ participants => [$participant1, $group]})}, qr{client side expansion}, 'participant groups - expansion warning under elm 2.x');
}
}
else {
$t->skip('no candidates found for group tests');
}
#
# tidy up
#
is( exception {$meeting->delete} => undef, 'meeting deletion - lives');
}
Elive->disconnect;