mirror of https://github.com/openssl/tools
Move OpenSSL-Query from omc-tools
Fixes #134 Co-authored-by: Matthias St. Pierre <matthias.st.pierre@ncp-e.com> Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com> Reviewed-by: Paul Dale <pauli@openssl.org> (Merged from https://github.com/openssl/tools/pull/170)
This commit is contained in:
parent
cfc0cf920f
commit
e31cc76864
|
@ -0,0 +1,10 @@
|
|||
lib/OpenSSL/Query.pm
|
||||
lib/OpenSSL/Query/PersonREST.pm
|
||||
lib/OpenSSL/Query/REST.pm
|
||||
lib/OpenSSL/Query/ClaREST.pm
|
||||
Makefile.PL
|
||||
MANIFEST
|
||||
META.yml
|
||||
README.md
|
||||
t/query.t
|
||||
t/00-load.t
|
|
@ -0,0 +1,44 @@
|
|||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
use inc::Module::Install;
|
||||
|
||||
name 'OpenSSL-Query';
|
||||
module_name 'OpenSSL::Query';
|
||||
version '1.2';
|
||||
abstract '';
|
||||
author q{Richard Levitte <levitte@openssl.org>};
|
||||
license 'apache';
|
||||
|
||||
perl_version 5.006;
|
||||
|
||||
tests_recursive('t');
|
||||
|
||||
resources (
|
||||
license => 'http://www.apache.org/licenses/LICENSE-2.0',
|
||||
repository => 'https://github.com/openssl/tools.git',
|
||||
bugtracker => 'https://github.com/openssl/tools/issues',
|
||||
);
|
||||
|
||||
configure_requires (
|
||||
'Module::Install' => 0,
|
||||
);
|
||||
|
||||
build_requires (
|
||||
'Test::More' => 0,
|
||||
);
|
||||
|
||||
requires (
|
||||
'Module::Load::Conditional' => 0,
|
||||
'Class::Method::Modifiers' => 0,
|
||||
'File::Spec' => 0,
|
||||
'URI::Encode' => 0,
|
||||
Moo => 0,
|
||||
Carp => 0,
|
||||
'LWP::UserAgent' => 0,
|
||||
'LWP::Protocol::https' =>0,
|
||||
);
|
||||
|
||||
install_as_site;
|
||||
auto_install;
|
||||
WriteAll;
|
|
@ -0,0 +1,74 @@
|
|||
OpenSSL::Query
|
||||
==============
|
||||
|
||||
A module to query certain information about OpenSSL committers as well
|
||||
as members of the OMC (OpenSSL Management Committee). These data are
|
||||
usually interesting for other programs that need to verify identities,
|
||||
whether a certain person holds a CLA, that sort of thing.
|
||||
|
||||
OpenSSL::Query is built to be able to handle several implementations
|
||||
for access to the databases that hold the data. The default
|
||||
implementation uses a RESTful API with JSON encoded responses,
|
||||
OpenSSL::Query::REST.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
OpenSSL::Query requires the following modules to build:
|
||||
|
||||
- Module::Install (debian package libmodule-install-perl)
|
||||
|
||||
OpenSSL::Query requires these extra modules to run:
|
||||
|
||||
- Class::Method::Modifiers (debian package libclass-method-modifiers-perl)
|
||||
- Moo (debian package libmoo-perl)
|
||||
- URI::Encode (debian package liburi-encode-perl)
|
||||
- LWP::UserAgent
|
||||
- LWP::Protocol::https
|
||||
|
||||
Any other module OpenSSL::Query depends on should be part of core
|
||||
perl.
|
||||
|
||||
On Debian, you can use the following command to install the required packages:
|
||||
|
||||
sudo apt install libmodule-install-perl libclass-method-modifiers-perl libmoo-perl liburi-encode-perl
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
perl Makefile.PL
|
||||
make && sudo make install
|
||||
|
||||
Local installation
|
||||
------------
|
||||
|
||||
For a local installation, you might want to consider using local::lib
|
||||
(debian package liblocal-lib-perl). In that case, running Makefile.PL
|
||||
is slightly different:
|
||||
|
||||
perl -Mlocal::lib Makefile.PL
|
||||
|
||||
Other than that, follow the instructions in "Installation" above.
|
||||
|
||||
To get the paths right permanently, you might want to consider adding
|
||||
this in your `.bash_profile`, `.bashrc` och corresponding shell init
|
||||
script:
|
||||
|
||||
eval "`perl -I$HOME/perl5/lib/perl5 -Mlocal::lib`"
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
Testing is done like this:
|
||||
|
||||
make test
|
||||
|
||||
However, it requires the additional Perl module Dancer2 and
|
||||
plackup (debian packages libdancer2-perl and libplack-perl)
|
||||
and that a temporary query service is started as well.
|
||||
This is part of QueryApp, and is started like this:
|
||||
|
||||
here=`pwd` # the directory OpenSSL-Query/
|
||||
cd ../QueryApp # Or wherever you have it checked out
|
||||
PERSONDB=./t/query_data/pdb.yaml CLADB=./t/query_data/cdb.txt \
|
||||
PERL5LIB=./lib:$here/lib plackup bin/query.psgi
|
|
@ -0,0 +1,160 @@
|
|||
#! /usr/bin/env perl
|
||||
#
|
||||
# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the OpenSSL license (the "License"). You may not use
|
||||
# this file except in compliance with the License. You can obtain a copy
|
||||
# in the file LICENSE in the source distribution or at
|
||||
# https://www.openssl.org/source/license.html
|
||||
|
||||
use strict;
|
||||
|
||||
package OpenSSL::Query;
|
||||
use Carp;
|
||||
|
||||
our %register_impl = ();
|
||||
|
||||
sub import {
|
||||
my $class = shift;
|
||||
my $regtype = undef;
|
||||
my $regname = undef;
|
||||
my $regprio = 999; # Bottom feeders
|
||||
|
||||
while (scalar @_ > 0) {
|
||||
my $arg = shift;
|
||||
if ($arg eq '-register-cla') {
|
||||
$regtype = 'cla';
|
||||
$regname = shift;
|
||||
} elsif ($arg eq '-register-person') {
|
||||
$regtype = 'person';
|
||||
$regname = shift;
|
||||
} elsif ($arg eq '-priority') {
|
||||
$regprio = shift;
|
||||
} else {
|
||||
croak "Unknown argument $arg";
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!defined($regtype) || !defined($regname)) {
|
||||
croak "No proper module registration";
|
||||
}
|
||||
$register_impl{$regtype}->{$regprio}->{$regname} = 1;
|
||||
}
|
||||
|
||||
sub _new_type {
|
||||
my $self = shift;
|
||||
my $type = shift;
|
||||
my @args = @_;
|
||||
|
||||
my @packages =
|
||||
map { (sort keys %{$register_impl{$type}->{$_}}) }
|
||||
sort keys %{$register_impl{$type}};
|
||||
my @objs = ();
|
||||
while (@packages) {
|
||||
my $obj = (shift @packages)->new(@args);
|
||||
push @objs, $obj if $obj;
|
||||
}
|
||||
|
||||
croak "No implementation for $type queries" unless @objs;
|
||||
|
||||
return @objs;
|
||||
}
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my @args = @_;
|
||||
|
||||
my $self = {};
|
||||
bless $self, $class;
|
||||
|
||||
foreach (('person', 'cla')) {
|
||||
$self->{$_} = [ $self->_new_type($_, @args) ];
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub _perform {
|
||||
my $self = shift;
|
||||
my $type = shift;
|
||||
my $sub = shift;
|
||||
|
||||
my @errors = ();
|
||||
foreach (@{$self->{$type}}) {
|
||||
my @result = eval { $sub->($_, @_); };
|
||||
return wantarray ? @result : $result[0] unless $@;
|
||||
push @errors, $@;
|
||||
}
|
||||
|
||||
croak join("\n", @errors);
|
||||
}
|
||||
|
||||
# Person methods
|
||||
sub list_people {
|
||||
my $self = shift;
|
||||
|
||||
$self->_perform('person',
|
||||
sub { my $obj = shift;
|
||||
return $obj->list_people(@_) },
|
||||
@_);
|
||||
}
|
||||
|
||||
sub find_person {
|
||||
my $self = shift;
|
||||
|
||||
$self->_perform('person',
|
||||
sub { my $obj = shift;
|
||||
return wantarray
|
||||
? ($obj->find_person(@_))
|
||||
: $obj->find_person(@_); },
|
||||
@_);
|
||||
}
|
||||
|
||||
sub find_person_tag {
|
||||
my $self = shift;
|
||||
|
||||
$self->_perform('person',
|
||||
sub { my $obj = shift;
|
||||
return $obj->find_person_tag(@_) },
|
||||
@_);
|
||||
}
|
||||
|
||||
sub is_member_of {
|
||||
my $self = shift;
|
||||
|
||||
$self->_perform('person',
|
||||
sub { my $obj = shift;
|
||||
return $obj->is_member_of(@_) },
|
||||
@_);
|
||||
}
|
||||
|
||||
# Group methods
|
||||
sub members_of {
|
||||
my $self = shift;
|
||||
|
||||
$self->_perform('person',
|
||||
sub { my $obj = shift;
|
||||
return $obj->members_of(@_) },
|
||||
@_);
|
||||
}
|
||||
|
||||
# Cla methods
|
||||
sub has_cla {
|
||||
my $self = shift;
|
||||
|
||||
$self->_perform('cla',
|
||||
sub { my $obj = shift;
|
||||
return $obj->has_cla(@_) },
|
||||
@_);
|
||||
}
|
||||
|
||||
sub list_clas {
|
||||
my $self = shift;
|
||||
|
||||
$self->_perform('cla',
|
||||
sub { my $obj = shift;
|
||||
return $obj->list_clas(@_) },
|
||||
@_);
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,53 @@
|
|||
#! /usr/bin/env perl
|
||||
#
|
||||
# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the OpenSSL license (the "License"). You may not use
|
||||
# this file except in compliance with the License. You can obtain a copy
|
||||
# in the file LICENSE in the source distribution or at
|
||||
# https://www.openssl.org/source/license.html
|
||||
|
||||
use strict;
|
||||
|
||||
package OpenSSL::Query::ClaREST;
|
||||
use Carp;
|
||||
use Moo;
|
||||
use OpenSSL::Query qw(-register-cla OpenSSL::Query::ClaREST -priority 1);
|
||||
use HTTP::Status qw(:is);
|
||||
use LWP::UserAgent;
|
||||
use URI::Encode qw(uri_encode uri_decode);
|
||||
use JSON::PP;
|
||||
use Data::Dumper;
|
||||
|
||||
has base_url => ( is => 'ro', default => 'https://api.openssl.org' );
|
||||
has _clahandler => ( is => 'ro', builder => 1 );
|
||||
|
||||
sub _build__clahandler {
|
||||
my $ua = LWP::UserAgent->new( keep_alive => 1 );
|
||||
$ua->env_proxy;
|
||||
return $ua;
|
||||
}
|
||||
|
||||
sub has_cla {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
if ($id =~ m|<(\S+\@\S+)>|) { $id = $1; }
|
||||
croak "Malformed input ID" unless $id =~ m|^\S+(\@\S+)$|;
|
||||
|
||||
my $ua = $self->_clahandler;
|
||||
my $json = $ua->get($self->base_url . '/0/HasCLA/'
|
||||
. uri_encode($id, {encode_reserved => 1}));
|
||||
croak "Server error: ", $json->message if is_server_error($json->code);
|
||||
return $json->code == 200;
|
||||
}
|
||||
|
||||
sub list_clas {
|
||||
my $self = shift;
|
||||
|
||||
my $ua = $self->_clahandler;
|
||||
my $json = $ua->get($self->base_url . '/0/CLAs');
|
||||
croak "Server error: ", $json->message if is_server_error($json->code);
|
||||
return $json->code == 200;
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,125 @@
|
|||
#! /usr/bin/env perl
|
||||
#
|
||||
# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the OpenSSL license (the "License"). You may not use
|
||||
# this file except in compliance with the License. You can obtain a copy
|
||||
# in the file LICENSE in the source distribution or at
|
||||
# https://www.openssl.org/source/license.html
|
||||
|
||||
use strict;
|
||||
|
||||
package OpenSSL::Query::PersonREST;
|
||||
use Carp;
|
||||
use Moo;
|
||||
use OpenSSL::Query qw(-register-person OpenSSL::Query::PersonREST -priority 1);
|
||||
use LWP::UserAgent;
|
||||
use HTTP::Status qw(:is);
|
||||
use URI::Encode qw(uri_encode uri_decode);
|
||||
use JSON::PP;
|
||||
use Data::Dumper;
|
||||
|
||||
has base_url => ( is => 'ro', default => 'https://api.openssl.org' );
|
||||
has _personhandler => ( is => 'lazy', builder => 1 );
|
||||
|
||||
sub _build__personhandler {
|
||||
my $ua = LWP::UserAgent->new( keep_alive => 1 );
|
||||
$ua->env_proxy;
|
||||
return $ua;
|
||||
}
|
||||
|
||||
sub list_people {
|
||||
my $self = shift;
|
||||
|
||||
my $ua = $self->_personhandler;
|
||||
my $json = $ua->get($self->base_url . '/0/People');
|
||||
croak "Server error: ", $json->message if is_server_error($json->code);
|
||||
return () unless $json->code == 200;
|
||||
|
||||
my $decoded = decode_json $json->decoded_content;
|
||||
|
||||
return @$decoded;
|
||||
}
|
||||
|
||||
sub _id_encode {
|
||||
my $id = shift;
|
||||
|
||||
return $id if ref($id) eq "";
|
||||
croak "Malformed input ID" if ref($id) ne "HASH" || scalar keys %$id != 1;
|
||||
|
||||
my $tag = (keys %$id)[0];
|
||||
return $tag . ':' . $id->{$tag};
|
||||
}
|
||||
|
||||
sub find_person {
|
||||
my $self = shift;
|
||||
my $id = _id_encode(shift);
|
||||
|
||||
my $ua = $self->_personhandler;
|
||||
my $json = $ua->get($self->base_url . '/0/Person/'
|
||||
. uri_encode($id, {encode_reserved => 1}));
|
||||
croak "Server error: ", $json->message if is_server_error($json->code);
|
||||
return () unless $json->code == 200;
|
||||
|
||||
my $decoded = decode_json $json->decoded_content;
|
||||
|
||||
return wantarray ? %$decoded : scalar keys %$decoded > 0;
|
||||
}
|
||||
|
||||
sub find_person_tag {
|
||||
my $self = shift;
|
||||
my $id = _id_encode(shift);
|
||||
my $tag = shift;
|
||||
|
||||
my $ua = $self->_personhandler;
|
||||
my $json = $ua->get($self->base_url
|
||||
. '/0/Person/'
|
||||
. uri_encode($id, {encode_reserved => 1})
|
||||
. '/ValueOfTag/'
|
||||
. uri_encode ($tag, {encode_reserved => 1}));
|
||||
croak "Server error: ", $json->message if is_server_error($json->code);
|
||||
return undef unless $json->code == 200;
|
||||
|
||||
my $decoded = decode_json $json->decoded_content;
|
||||
|
||||
return $decoded->[0];
|
||||
}
|
||||
|
||||
sub is_member_of {
|
||||
my $self = shift;
|
||||
my $id = _id_encode(shift);
|
||||
my $group = shift;
|
||||
|
||||
my $ua = $self->_personhandler;
|
||||
my $json = $ua->get($self->base_url
|
||||
. '/0/Person/'
|
||||
. uri_encode($id, {encode_reserved => 1})
|
||||
. '/IsMemberOf/'
|
||||
. uri_encode ($group, {encode_reserved => 1}));
|
||||
croak "Server error: ", $json->message if is_server_error($json->code);
|
||||
return 0 unless $json->code == 200;
|
||||
|
||||
my $decoded = decode_json $json->decoded_content;
|
||||
|
||||
return $decoded->[0];
|
||||
}
|
||||
|
||||
# Group methods
|
||||
sub members_of {
|
||||
my $self = shift;
|
||||
my $group = shift;
|
||||
|
||||
my $ua = $self->_personhandler;
|
||||
my $json = $ua->get($self->base_url
|
||||
. '/0/Group/'
|
||||
. uri_encode($group, {encode_reserved => 1})
|
||||
. '/Members');
|
||||
croak "Server error: ", $json->message if is_server_error($json->code);
|
||||
return () unless $json->code == 200;
|
||||
|
||||
my $decoded = decode_json $json->decoded_content;
|
||||
|
||||
return @$decoded;
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,15 @@
|
|||
#! /usr/bin/env perl
|
||||
#
|
||||
# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the OpenSSL license (the "License"). You may not use
|
||||
# this file except in compliance with the License. You can obtain a copy
|
||||
# in the file LICENSE in the source distribution or at
|
||||
# https://www.openssl.org/source/license.html
|
||||
|
||||
use strict;
|
||||
|
||||
package OpenSSL::Query::REST;
|
||||
use OpenSSL::Query::PersonREST;
|
||||
use OpenSSL::Query::ClaREST;
|
||||
1;
|
|
@ -0,0 +1,13 @@
|
|||
#!perl -T
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
|
||||
plan tests => 1;
|
||||
|
||||
BEGIN {
|
||||
use_ok( 'OpenSSL::Query::REST' ) || print "Bail out!\n";
|
||||
}
|
||||
|
||||
#note( "Testing OpenSSL::Query $OpenSSL::Query::VERSION, Perl $], $^X" );
|
|
@ -0,0 +1,147 @@
|
|||
#! /usr/bin/env perl
|
||||
|
||||
# This means that 'dance' at the end of query.psgi will not start a built in
|
||||
# service, but will simply return a coderef. This is useful to run this with
|
||||
# diverse dispatchers as well as tests.
|
||||
BEGIN { $ENV{DANCER_APPHANDLER} = 'PSGI';}
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
use OpenSSL::Query::REST;
|
||||
use Data::Dumper;
|
||||
|
||||
plan tests => 16;
|
||||
|
||||
SKIP: {
|
||||
my $query;
|
||||
|
||||
skip "No object for further operations", 8
|
||||
unless ok( $query = eval { OpenSSL::Query->new(base_url => $ENV{BASE_URL}
|
||||
// "http://localhost:5000") },
|
||||
'Creating a OpenSSL::Query object' );
|
||||
|
||||
# print STDERR Dumper($query);
|
||||
subtest 'Request of identity list' => sub {
|
||||
plan tests => 1;
|
||||
|
||||
my @res = $query->list_people();
|
||||
ok( scalar @res > 0, 'We got a list' );
|
||||
note( Dumper( [ @res ] ) );
|
||||
};
|
||||
|
||||
subtest 'Request of person data for Ray Bradbury' => sub {
|
||||
plan tests => 2;
|
||||
|
||||
my $res1 = $query->find_person( 'Ray Bradbury' );
|
||||
ok( $res1, 'Ray Bradbury is present' );
|
||||
note( $res1 );
|
||||
|
||||
my %res2 = $query->find_person( 'Ray Bradbury' );
|
||||
ok(scalar keys %res2 > 1, "Got Ray Bradbury's data" );
|
||||
note( Dumper( { %res2 } ) );
|
||||
};
|
||||
|
||||
subtest 'Request of person data for Ray Bradbury as full name' => sub {
|
||||
plan tests => 2;
|
||||
|
||||
my $res1 = $query->find_person( { fullname => 'Ray Bradbury' } );
|
||||
ok( $res1, 'Ray Bradbury is present' );
|
||||
note( $res1 );
|
||||
|
||||
my %res2 = $query->find_person( 'Ray Bradbury' );
|
||||
ok(scalar keys %res2 > 1, "Got Ray Bradbury's data" );
|
||||
note( Dumper( { %res2 } ) );
|
||||
};
|
||||
|
||||
subtest 'Request of membership in specific group for Ray Bradbury' => sub {
|
||||
plan tests => 1;
|
||||
my $res = $query->is_member_of( 'Ray Bradbury', 'scifi' );
|
||||
ok( $res, "Ray Bradbury is member of scifi since ".( $res ? $res : "(unknown)" ) );
|
||||
note( $res );
|
||||
};
|
||||
|
||||
subtest 'Request of membership in specific group for Ray Bradbury as full name' => sub {
|
||||
plan tests => 1;
|
||||
my $res = $query->is_member_of( { fullname => 'Ray Bradbury' }, 'scifi' );
|
||||
ok( $res, "Ray Bradbury is member of scifi since ".( $res ? $res : "(unknown)" ) );
|
||||
note( $res );
|
||||
};
|
||||
|
||||
subtest 'Request of "author" tag value for Ray Bradbury' => sub {
|
||||
plan tests => 1;
|
||||
my $res = $query->find_person_tag( 'Ray Bradbury', 'author' );
|
||||
ok( $res, "The 'author' tag for Ray Bradbury is ".( $res ? $res : "(unknown)" ) );
|
||||
note( Dumper $res );
|
||||
};
|
||||
|
||||
subtest 'Request of "author" tag value for Ray Bradbury as full name' => sub {
|
||||
plan tests => 1;
|
||||
my $res = $query->find_person_tag( { fullname => 'Ray Bradbury' }, 'author' );
|
||||
ok( $res, "The 'author' tag for Ray Bradbury is ".( $res ? $res : "(unknown)" ) );
|
||||
note( Dumper $res );
|
||||
};
|
||||
|
||||
subtest 'Request of CLA status for Ray Bradbury' => sub {
|
||||
plan tests => 1;
|
||||
my $res = $query->has_cla( 'ray@Ourplace.com' );
|
||||
ok( $res, 'Ray Bradbury has CLA as ray@Ourplace.com' );
|
||||
note( $res );
|
||||
};
|
||||
|
||||
subtest 'Request of membership in the group "writers"' => sub {
|
||||
plan tests => 1;
|
||||
my @res = $query->members_of( 'writers' );
|
||||
ok( @res, 'Finding members of "writers"' );
|
||||
note( Dumper @res );
|
||||
};
|
||||
|
||||
subtest 'Request of person data for Jay Luser' => sub {
|
||||
plan tests => 2;
|
||||
|
||||
my $res1 = $query->find_person( 'Jay Luser' );
|
||||
ok( !$res1, 'Jay Luser is not present' );
|
||||
note( $res1 );
|
||||
|
||||
my %res2 = $query->find_person( 'Jay Luser' );
|
||||
ok( !%res2, "Failed getting Jay Luser's data" );
|
||||
};
|
||||
|
||||
subtest 'Request of membership in specific group for Jay Luser' => sub {
|
||||
plan tests => 1;
|
||||
my $res = $query->is_member_of( 'Jay Luser', 'scifi' );
|
||||
ok( !$res, 'Jay Luser is not member of scifi' );
|
||||
note( $res );
|
||||
};
|
||||
|
||||
subtest 'Request of "author" tag value for Jay Luser' => sub {
|
||||
plan tests => 1;
|
||||
my $res = $query->find_person_tag( 'Jay Luser', 'author' );
|
||||
ok( !$res, "No 'author' tag for Jay Luser" );
|
||||
note( $res );
|
||||
};
|
||||
|
||||
subtest 'Request of CLA status for Jay Luser' => sub {
|
||||
plan tests => 1;
|
||||
my $res = $query->has_cla( 'jluser@ourplace.com' );
|
||||
ok( !$res, 'Jay Luser has no CLA' );
|
||||
note( $res );
|
||||
};
|
||||
|
||||
subtest 'Request of membership in the group "couchpotatoes"' => sub {
|
||||
plan tests => 1;
|
||||
my @res = $query->members_of( 'couchpotatoes' );
|
||||
ok( !@res, 'No members in "couchpotatoes"' );
|
||||
note( @res );
|
||||
};
|
||||
|
||||
subtest 'Request all existing CLAs' => sub {
|
||||
plan tests => 1;
|
||||
my @res = $query->list_clas();
|
||||
ok( @res, 'We got CLAs' );
|
||||
note( @res );
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
1;
|
Loading…
Reference in New Issue