MT#17173 Mass collections tests

Change-Id: I377cfec13a7f18dc6e1688cd3b57dbc4d11210ad
(cherry picked from commit 8d18584276558e3fed70c1043850e42d102139f9)
changes/42/4942/95
Irina Peshinskaya 9 years ago
parent 69966040c6
commit ad62a1a5a1

@ -0,0 +1,161 @@
package NGCP::Panel::Controller::API::MetaConfigDefs;
use NGCP::Panel::Utils::Generic qw(:all);
use boolean qw(true);
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::Preferences;
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
use JSON::Types qw();
use Config::General;
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
sub allowed_methods{
return [qw/GET OPTIONS HEAD/];
}
use base qw/Catalyst::Controller NGCP::Panel::Role::API/;
sub resource_name{
return 'metaconfigdefs';
}
sub dispatch_path{
return '/api/metaconfigdefs/';
}
sub relation{
return 'http://purl.org/sipwise/ngcp-api/#rel-metaconfigdefs';
}
__PACKAGE__->config(
action => {
map { $_ => {
ACLDetachTo => '/api/root/invalid_user',
AllowedRole => [qw/admin reseller/],#left adminand reseller, as test can run as reseller too. Just don't return full config
Args => 0,
Does => [qw(ACL CheckTrailingSlash RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
} } @{ __PACKAGE__->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
);
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
}
sub GET :Allow {
my ($self, $c) = @_;
{
my @links;
push @links,
Data::HAL::Link->new(
relation => 'curies',
href => 'http://purl.org/sipwise/ngcp-api/#rel-{rel}',
name => 'ngcp',
templated => true,
),
Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'),
Data::HAL::Link->new(relation => 'self', href => sprintf('%s', $self->dispatch_path));
my $hal = Data::HAL->new(
links => [@links],
);
#my $resource = $c->config;
my $catalyst_config = Config::General->new($c->config->{'Plugin::ConfigLoader'}->{file});
my %config_internal = $catalyst_config->getall();
my %config;
$config{file} = $c->config->{'Plugin::ConfigLoader'}->{file};
$config{numbermanagement}->{auto_sync_cli} = $config_internal{numbermanagement}->{auto_sync_cli};
$config{numbermanagement}->{auto_allow_cli} = $config_internal{numbermanagement}->{auto_allow_cli};
$config{features} = $config_internal{features};
my $meta = {
collections => {
#name => {
#module => '',
#allowed_methods => [],
#module_item =>''
#allowed_methods_item => [],
#query_params => [],
#allowed_roles => [],
#container_item_id => '',
#unique_fields => [['table.field','table1.fields1']],
#}
},
};
(my($files,$modules,$collections)) = NGCP::Panel::Utils::API::get_collections('NGCP::Panel::Controller::API::MetaConfigDefs');
for ( my $i=0; $i < $#$collections; $i++)
{
my $collection = $collections->[$i];
my $module = $modules->[$i];
my $module_item = $module.'Item';
my $roles = $module->can('config') ? $module->config->{action}->{OPTIONS}->{AllowedRole}:[];
(!(ref $roles eq 'ARRAY')) and $roles = [$roles];
$meta->{collections}->{$collection} = {
module => $module,
allowed_methods => $module->can('config') ? $module->config->{action} : {},
query_params => $module->can('query_params') ? [map {$_->{param}} @{$module->query_params}] : [],
allowed_roles => [$roles],
module_item => $module_item->can('config') ? $module_item : '',
allowed_methods_item => $module_item->can('config') ? $module_item->config->{action} : {},
#container_item_id => '',
#unique_fields => [['table.field','table1.fields1']],
};
}
my $resource = { config => \%config, meta => $meta };
$hal->resource($resource);
my $response = HTTP::Response->new(HTTP_OK, undef,
HTTP::Headers->new($hal->http_headers(skip_links => 1)), $hal->as_json);
$c->response->headers($response->headers);
$c->response->body($response->content);
return;
}
return;
}
sub HEAD :Allow {
my ($self, $c) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub OPTIONS :Allow {
my ($self, $c) = @_;
my $allowed_methods = $self->allowed_methods_filtered($c);
$c->response->headers(HTTP::Headers->new(
Allow => join(', ', @{ $allowed_methods }),
Accept_Post => 'application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name,
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
}
no Moose;
1;
# vim: set tabstop=4 expandtab:

@ -11,6 +11,7 @@ use HTTP::Status qw(:constants);
use File::Find::Rule;
use JSON qw(to_json);
use Safe::Isa qw($_isa);
use NGCP::Panel::Utils::API;
use parent qw/Catalyst::Controller NGCP::Panel::Role::API/;
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::CheckTrailingSlash;
@ -59,10 +60,11 @@ sub GET : Allow {
"PeeringServerPreferenceDefs" => 1,
"PbxDevicePreferenceDefs" => 1,
"PbxDeviceProfilePreferenceDefs" => 1,
"MetaConfigDefs" => 1,
};
my @colls = $self->get_collections;
foreach my $coll(@colls) {
my $colls = NGCP::Panel::Utils::API::get_collections_files;
foreach my $coll(@$colls) {
my $mod = $coll;
$mod =~ s/^.+\/([a-zA-Z0-9_]+)\.pm$/$1/;
next if(exists $blacklist->{$mod});
@ -224,36 +226,14 @@ sub OPTIONS : Allow {
return;
}
sub get_collections {
my ($self) = @_;
# figure out base path of our api modules
my $libpath = $INC{"NGCP/Panel/Controller/API/Root.pm"};
$libpath =~ s/Root\.pm$//;
# find all modules not called Root.pm and *Item.pm
# (which should then be just collections)
my $rootrule = File::Find::Rule->new->name('Root.pm');
my $itemrule = File::Find::Rule->new->name('*Item.pm');
my $rule = File::Find::Rule->new
->mindepth(1)
->maxdepth(1)
->name('*.pm')
->not($rootrule)
->not($itemrule);
my @colls = $rule->in($libpath);
return @colls;
}
sub collections_link_headers : Private {
my ($self) = @_;
my @colls = $self->get_collections;
my $colls = NGCP::Panel::Utils::API::get_collections_files;
# create Link header for each of the collections
my @links = ();
foreach my $mod(@colls) {
foreach my $mod(@$colls) {
# extract file base from path (e.g. Foo from lib/something/Foo.pm)
$mod =~ s/^.+\/([a-zA-Z0-9_]+)\.pm$/$1/;
my $rel = lc $mod;

@ -0,0 +1,68 @@
package NGCP::Panel::Utils::API;
use strict;
use warnings;
use File::Find::Rule;
sub get_collections {
my @files = @{get_collections_files()};
my(@collections, @packages, @modules);
foreach my $mod(@files) {
# extract file base from path (e.g. Foo from lib/something/Foo.pm)
$mod =~ s/^.+\/([a-zA-Z0-9_]+)\.pm$/$1/;
my $package = 'NGCP::Panel::Controller::API::'.$mod;
my $rel = lc $mod;
$mod = 'NGCP::Panel::Controller::API::'.$mod;
push @modules, $mod;
push @packages, $package;
push @collections, $rel;
}
return \@files, \@packages, \@collections, \@modules;
}
sub get_collections_files {
my($library,$libpath) = @_;
if(!$libpath){
# figure out base path of our api modules
$library ||= "NGCP/Panel/Controller/API/Root.pm";
$libpath = $INC{$library};
$libpath =~ s/\/[^\/]+$/\//;
}
# find all modules not called Root.pm and *Item.pm
# (which should then be just collections)
my $rootrule = File::Find::Rule->new->name('Root.pm');
my $itemrule = File::Find::Rule->new->name('*Item.pm');
my $rule = File::Find::Rule->new
->mindepth(1)
->maxdepth(1)
->name('*.pm')
->not($rootrule)
->not($itemrule);
my @colls = $rule->in($libpath);
return \@colls;
}
1;
=head1 NAME
NGCP::Panel::Utils::API
=head1 DESCRIPTION
A helper to manipulate REST API related data
=head1 METHODS
=head1 AUTHOR
Irina Peshinskaya
=head1 LICENSE
This library is free software. You can redistribute it and/or modify
it under the same terms as Perl itself.
=cut
# vim: set tabstop=4 expandtab:

@ -35,7 +35,7 @@ NGCP::Panel::Utils::API::Subscribers
=head1 DESCRIPTION
A temporary helper to manipulate subscribers related data in REST API moules
A temporary helper to manipulate subscribers related data in REST API modules
=head1 METHODS

@ -0,0 +1,26 @@
package Parent;
our $VERSION = 1.23;
sub VERSION { $VERSION }
sub child_version { $_[0]->VERSION }
package Child;
use base qw(Parent);
our $VERSION = 5.43;
sub VERSION { $VERSION }
sub new { bless {}, $_[0]; }
sub parent_version { $_[0]->SUPER::VERSION }
print "Child version is ", Child->VERSION, "\n"; # 5.43
my $child = Child->new;
print "Child version: ", $child->VERSION, "\n"; # 5.43
print "Parent version: ", $child->parent_version, "\n"; # 1.23
print "Child version: ", $child->child_version, "\n"; # 5.43

@ -0,0 +1,153 @@
use strict;
use Test::Collection;
use Test::FakeData;
use Test::More;
use Data::Dumper;
use Getopt::Long;
use File::Find::Rule;
use File::Basename;
use Clone qw/clone/;
my $opt = {
'collections' => {},
};
my $opt_in = {};
GetOptions($opt_in,
"help|h|?" ,
"collections:s" ,
"ignore-existence" ,
) or pod2usage(2);
my @opt_keys = keys %$opt_in;
@{$opt}{ map{my $k=$_;$k=~s/\-/_/;$k;} @opt_keys } = map {my $v = $opt_in->{$_}; $v={ map {$_=>1;} split(/[^[:alnum:]]+/,$v ) }; $v;} @opt_keys ;
print Dumper $opt;
pod2usage(1) if $opt->{help};
pod2usage(1) unless( 1
# defined $opt->{collections} && defined $opt->{etc}
);
my $test_machine = Test::Collection->new('name'=>'','ALLOW_EMPTY_COLLECTION' => 1);
my $fake_data = Test::FakeData->new;
$test_machine->clear_cache;
my $remote_config = $test_machine->init_catalyst_config;
print Dumper $remote_config ;
my $data = $remote_config->{meta}->{'collections'};
my %test_exclude = (
'metaconfigdefs' => 1,
'subscriberpreferencedefs' => 1,
'customerpreferencedefs' => 1,
'domainpreferencedefs' => 1,
'peeringserverpreferencedefs' => 1,
'profilepreferencedefs' => 1,
'subscriberpreferences' => 1,
'customerpreferences' => 1,
'domainpreferences' => 1,
'peeringserverpreferences' => 1,
'profilepreferences' => 1,
'pbxdevicepreferencedefs' => 1,
'pbxdeviceprofilepreferencedefs' => 1,
#defs and preferences are tested in context of preferences
'pbxdevicefirmwares' => 1, #too hard, fails with timeout on get
#falis with: not ok 163 - ccmapentries: check_get2put: check put successful (Unprocessable Entity: Validation failed. field='mappings', input='ARRAY(0x1a53f278)', errors='Mappings field is required')
'ccmapentries' => 1,
#fails with:
# got: 'https://127.0.0.1:1443/api/customerzonecosts/?page=1&rows=5&start=2016-10-01T000000&end=2016-10-31T235959'
# expected: 'https://127.0.0.1:1443/api/customerzonecosts/?page=1&rows=5'
'customerzonecosts' => 1,
#fails with: Unsupported media type 'application/json', accepting text/plain or text/xml only.)
'pbxdeviceconfigs' => 1,
#fails with: not ok 1131 - rtcapps: check_get2put: check put successful (Unprocessable Entity: Invalid field 'apps'. Must be an array.)
'rtcapps' => 1,
#fails with: not ok 1176 - rtcnetworks: check_get2put: check put successful (Unprocessable Entity: Invalid field 'networks'. Must be an array.)
'rtcnetworks' => 1,
);
my %test_exists;
{
my $dir = dirname($0);
my $rule = File::Find::Rule->new
->mindepth(1)
->maxdepth(1)
->name('api-*.t');
%test_exists = map {$_=~s/\Q$dir\/\E//;$_ => 1} $rule->in($dir);
}
my $res = {
'collections_no_get' => [],
'collections_empty' => [],
'collections_not_empty' => [],
'strange_item_actions' => {},
'no_module_item' => [],
'tests_exists' => \%test_exists,
'tests_exists_skipped' => [],
'checked' => [],
'tests_exclude' => \%test_exclude,
'opt' => $opt
};
foreach my $collection ( sort grep{(! ( scalar keys $opt->{collections} ) ) || $opt->{collections}->{$_} } keys %{$data} ){
if(!$opt->{collections}->{$collection}){
if($test_exists{'api-'.$collection.'.t'} && !$opt->{ignore_existence}){
push @{$res->{'tests_exists_skipped'}}, $collection;
#we will not test the same twice
next;
}
next if $test_exclude{$collection};
}
#print Dumper $data->{$collection}->{allowed_methods_item};
#print Dumper $collection;
my $item_allowed_actions = { allowed => {} };
if($data->{$collection}->{module_item}){
if(ref $data->{$collection}->{allowed_methods_item} eq 'HASH'){
$item_allowed_actions = { allowed => { map { $_ => 1 } keys %{$data->{$collection}->{allowed_methods_item}} }};
}else{
$res->{'strange_item_actions'}->{$collection} = $data->{$collection}->{allowed_methods_item};
}
}else{
push @{$res->{'no_module_item'}}, $collection;
}
push @{$res->{'checked'}}, $collection;
$test_machine->name($collection);
$test_machine->NO_ITEM_MODULE($data->{$collection}->{module_item} ? 0 : 1 );
{
$test_machine->methods({
collection => { allowed => { map { $_ => 1 } keys %{$data->{$collection}->{allowed_methods}} }},
item => $item_allowed_actions,
});
}
$test_machine->check_bundle();
if($test_machine->{methods}->{collection}->{allowed}->{GET}){
my $item = $test_machine->get_item_hal();
#if($item->{content}->{total_count}){
if(!$test_machine->IS_EMPTY_COLLECTION){
push @{$res->{'collections_not_empty'}}, $collection;
if($data->{$collection}->{allowed_methods}->{POST}){
my $item_post = clone($item);
delete $item_post->{content}->{id};
$test_machine->DATA_ITEM_STORE($item_post->{content});
$test_machine->form_data_item();
#test_machine->check_create_correct( 1 );
}
if($test_machine->{methods}->{item}->{allowed}->{PUT}){
$test_machine->check_get2put();
}
}else{
push @{$res->{'collections_empty'}}, $collection;
}
}else{
push @{$res->{'collections_no_get'}}, $collection;
}
}
$test_machine->clear_test_data_all();
done_testing;
undef $fake_data;
undef $test_machine;
print Dumper $res;
# vim: set tabstop=4 expandtab:

@ -34,53 +34,46 @@ my $test_machine = Test::Collection->new(
embedded_resources => [qw/subscribers/]
);
{
my ($res, $content, $req) = $test_machine->check_item_post();
if(422 == $res->code){
$test_machine->http_code_msg(422, "check faxserver feature state: disabled", $res, $content);
my $inactive_feature_msg = "Faxserver feature is not active";
if( $content->{message} =~ /$inactive_feature_msg/ ){
#some weird construction of the tests, but in case of inactive faxes feature and inactive faxes for the userboth response code will be 422.
#if feature is inactive on the application level - there is nothing to test more
#so added this pseudo test just to place it here. Really don't like it.
ok($content->{message} =~ /$inactive_feature_msg/, "check error message in body: $inactive_feature_msg");
done_testing;
exit();
}
}
}
my $remote_config = $test_machine->init_catalyst_config;
if( !$remote_config->{config}->{features}->{faxserver} ){
$remote_config->{config}->{features}->{faxserver} //= 0;
is($remote_config->{config}->{features}->{faxserver},0,"axserver feature isn't enabled");
}else{
@{$test_machine->content_type}{qw/POST PUT/} = (('multipart/form-data') x 2);
$test_machine->methods->{collection}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS POST)};
$test_machine->methods->{item}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS)};
$test_machine->DATA_ITEM_STORE($fake_data->process('faxes'));
$test_machine->form_data_item();
@{$test_machine->content_type}{qw/POST PUT/} = (('multipart/form-data') x 2);
$test_machine->methods->{collection}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS POST)};
$test_machine->methods->{item}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS)};
$test_machine->DATA_ITEM_STORE($fake_data->process('faxes'));
$test_machine->form_data_item();
{
my $test_machine_aux = Test::Collection->new(name => 'faxserversettings');
my $uri = $test_machine_aux->get_uri($test_machine->DATA_ITEM->{json}->{subscriber_id});
my($res,$faxserversettings,$req) = $test_machine_aux->check_item_get($uri);
$faxserversettings->{active} = 1;
$faxserversettings->{password} = 'aaa111';
$test_machine_aux->request_put($faxserversettings,$uri);
}
$test_machine->resource_fill_file($test_machine->DATA_ITEM->{faxfile}->[0]);
$test_machine->check_create_correct( 1 );
$test_machine->resource_clear_file($test_machine->DATA_ITEM->{faxfile}->[0]);
{
my $test_machine_aux = Test::Collection->new(name => 'faxserversettings');
my $uri = $test_machine_aux->get_uri($test_machine->DATA_ITEM->{json}->{subscriber_id});
my($res,$faxserversettings,$req) = $test_machine_aux->check_item_get($uri);
$faxserversettings->{active} = 1;
$faxserversettings->{password} = 'aaa111';
$test_machine_aux->request_put($faxserversettings,$uri);
delete $test_machine->DATA_ITEM->{faxfile};
$test_machine->DATA_ITEM->{json}->{data}="äöüß";
$test_machine->form_data_item();
$test_machine->check_create_correct( 1 );
}
$test_machine->resource_fill_file($test_machine->DATA_ITEM->{faxfile}->[0]);
$test_machine->check_create_correct( 1 );
$test_machine->resource_clear_file($test_machine->DATA_ITEM->{faxfile}->[0]);
delete $test_machine->DATA_ITEM->{faxfile};
$test_machine->DATA_ITEM->{json}->{data}="äöüß";
$test_machine->form_data_item();
$test_machine->check_create_correct( 1 );
#$test_machine->check_bundle();
#$test_machine->check_get2put( sub { $_[0] = { json => JSON::to_json($_[0]), 'faxfile' => $test_machine->DATA_ITEM_STORE->{faxfile} }; } );
$test_machine->clear_test_data_all();
done_testing;
undef $fake_data;
undef $test_machine;
# vim: set tabstop=4 expandtab:

@ -87,6 +87,7 @@ $ua->credentials($netloc, "api_admin_http", $user, $pass);
lnpnumbers => 1,
mailtofaxsettings => 1,
maliciouscalls => 1,
metaconfigdefs => 1,
ncoslevels => 1,
ncoslnpcarriers => 1,
ncospatterns => 1,

@ -18,6 +18,7 @@ use Test::HTTPRequestAsCurl;
use Data::Dumper;
use File::Slurp qw/write_file/;
use Storable;
use Carp qw(cluck longmess shortmess);
has 'data_cache_file' => (
is => 'ro',
@ -47,6 +48,21 @@ has 'DEBUG_ONLY' => (
isa => 'Bool',
default => 0,
);
has 'ALLOW_EMPTY_COLLECTION' => (
is => 'rw',
isa => 'Bool',
default => 0,
);
has 'IS_EMPTY_COLLECTION' => (
is => 'rw',
isa => 'Bool',
default => 0,
);
has 'NO_ITEM_MODULE' => (
is => 'rw',
isa => 'Bool',
default => 0,
);
has 'catalyst_config' => (
is => 'rw',
isa => 'HashRef',
@ -198,30 +214,26 @@ sub get_cloned{
}
sub init_catalyst_config{
my $self = shift;
my $catalyst_config;
my $panel_config;
if ($self->{local_test}) {
for my $path(qw#../ngcp_panel.conf ngcp_panel.conf#) {
if(-f $path) {
$panel_config = $path;
last;
}
my $config;
my $restored;
if($self->cache_data){
$restored = $self->get_cached_data;
if($restored->{loaded} && $restored->{loaded}->{metaconfigdefs}){
$config = $restored->{loaded}->{metaconfigdefs}->{content};
}
$panel_config //= dirname($0).'/../../ngcp_panel.conf';
} else {
#taken 1:1 from /lib/NGCP/Panel.pm
for my $path(qw#/etc/ngcp-panel/ngcp_panel.conf etc/ngcp_panel.conf ngcp_panel.conf#) {
if(-f $path) {
$panel_config = $path;
last;
}
}
if(!$config){
my($res,$list_collection,$req) = $self->check_item_get($self->normalize_uri('/api/metaconfigdefs/'));
my $location;
($config,$location) = $self->get_hal_from_collection($list_collection);
if($self->cache_data){
$restored->{loaded} //= {};
$restored->{loaded}->{metaconfigdefs} = { content => $config, location => $location };
store $restored, $self->data_cache_file;
}
$panel_config //= dirname($0).'/ngcp_panel.conf';
}
$catalyst_config = Config::General->new($panel_config);
my %config = $catalyst_config->getall();
$self->{catalyst_config} = \%config;
$self->{panel_config} = $panel_config;
$self->{catalyst_config} = $config;
$self->{panel_config} = $config->{file};
return $self->{catalyst_config};
}
sub init_ua {
@ -336,12 +348,16 @@ sub get_item_hal{
if(!$resitem){
my ($reshal, $location,$total_count);
$uri //= $self->get_uri_collection($name)."?page=1&rows=1";
#print "uri=$uri;";
my($res,$list_collection,$req) = $self->check_item_get($self->normalize_uri($uri));
($reshal,$location,$total_count) = $self->get_hal_from_collection($list_collection,$name);
if($total_count || ('HASH' eq ref $reshal->{content} && $reshal->{content}->{total_count})){
$self->IS_EMPTY_COLLECTION(0);
$resitem = { num => 1, content => $reshal, res => $res, req => $req, location => $location, total_count => $total_count };
$self->DATA_LOADED->{$name} ||= [];
push @{$self->DATA_LOADED->{$name}}, $resitem;
}else{
$self->IS_EMPTY_COLLECTION(1);
}
}
return $resitem;
@ -553,7 +569,7 @@ sub get_response_content{
}
sub normalize_uri{
my($self,$uri) = @_;
$uri ||= $self->get_uri_current;
$uri ||= $self->get_uri_current // '';
if($uri !~/^http/i){
$uri = $self->base_uri.$uri;
}
@ -570,30 +586,32 @@ sub check_options_collection{
$uri //= $self->get_uri_collection;
my $req = HTTP::Request->new('OPTIONS', $uri );
my $res = $self->request($req);
is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-".$self->name, "check Accept-Post header in options response");
is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-".$self->name, "$self->{name}: check Accept-Post header in options response");
$self->check_methods($res,'collection');
}
sub check_options_item{
my ($self,$uri) = @_;
# OPTIONS tests
$uri ||= $self->get_uri_current;
my $req = HTTP::Request->new('OPTIONS', $uri);
my $res = $self->request($req);
$self->check_methods($res,'item');
if(!$self->IS_EMPTY_COLLECTION){
my $req = HTTP::Request->new('OPTIONS', $uri);
my $res = $self->request($req);
$self->check_methods($res,'item');
}
}
sub check_methods{
my($self, $res, $area) = @_;
my $opts = $self->get_response_content($res);
$self->http_code_msg(200, "check $area options request", $res,$opts);
my @hopts = split /\s*,\s*/, $res->header('Allow');
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "$self->{name}: check for valid 'methods' in body");
foreach my $opt(keys %{$self->methods->{$area}->{all}} ) {
if(exists $self->methods->{$area}->{allowed}->{$opt}){
ok(grep(/^$opt$/, @hopts), "check for existence of '$opt' in Allow header");
ok(grep(/^$opt$/, @{ $opts->{methods} }), "check for existence of '$opt' in body");
ok(grep(/^$opt$/, @hopts), "$self->{name}: check for existence of '$opt' in Allow header");
ok(grep(/^$opt$/, @{ $opts->{methods} }), "$self->{name}: check for existence of '$opt' in body");
}else{
ok(!grep(/^$opt$/, @hopts), "check for absence of '$opt' in Allow header");
ok(!grep(/^$opt$/, @{ $opts->{methods} }), "check for absence of '$opt' in body");
ok(!grep(/^$opt$/, @hopts), "$self->{name}: check for absence of '$opt' in Allow header");
ok(!grep(/^$opt$/, @{ $opts->{methods} }), "$self->{name}: check for absence of '$opt' in body");
}
}
}
@ -606,25 +624,26 @@ sub check_list_collection{
#print "nexturi=$nexturi;\n";
my ($res,$list_collection) = $self->check_item_get($nexturi);
my $selfuri = $self->normalize_uri($list_collection->{_links}->{self}->{href});
is($selfuri, $nexturi, "check _links.self.href of collection");
is($selfuri, $nexturi, "$self->{name}: check _links.self.href of collection");
my $colluri = URI->new($selfuri);
ok($list_collection->{total_count} > 0, "check 'total_count' of collection");
if(($list_collection->{total_count} && $list_collection->{total_count} > 0 ) || !$self->ALLOW_EMPTY_COLLECTION){
ok($list_collection->{total_count} > 0, "$self->{name}: check 'total_count' of collection");
}
my %q = $colluri->query_form;
ok(exists $q{page} && exists $q{rows}, "check existence of 'page' and 'row' in 'self'");
ok(exists $q{page} && exists $q{rows}, "$self->{name}: check existence of 'page' and 'row' in 'self'");
my $page = int($q{page});
my $rows = int($q{rows});
ok($rows != 0, "check existance of the 'rows'");
if($page == 1) {
ok(!exists $list_collection->{_links}->{prev}->{href}, "check absence of 'prev' on first page");
ok(!exists $list_collection->{_links}->{prev}->{href}, "$self->{name}: check absence of 'prev' on first page");
} else {
ok(exists $list_collection->{_links}->{prev}->{href}, "check existence of 'prev'");
ok(exists $list_collection->{_links}->{prev}->{href}, "$self->{name}: check existence of 'prev'");
}
if(($rows != 0) && ($list_collection->{total_count} / $rows) <= $page) {
ok(!exists $list_collection->{_links}->{next}->{href}, "check absence of 'next' on last page");
ok(!exists $list_collection->{_links}->{next}->{href}, "$self->{name}: check absence of 'next' on last page");
} else {
ok(exists $list_collection->{_links}->{next}->{href}, "check existence of 'next'");
ok(exists $list_collection->{_links}->{next}->{href}, "$self->{name}: check existence of 'next'");
}
if($list_collection->{_links}->{next}->{href}) {
@ -634,8 +653,10 @@ sub check_list_collection{
}
my $hal_name = $self->get_hal_name;
ok(((ref $list_collection->{_links}->{$hal_name} eq "ARRAY" ) ||
(ref $list_collection->{_links}->{$hal_name} eq "HASH" ) ), "check if 'ngcp:".$self->name."' is array/hash-ref");
if(($list_collection->{total_count} && $list_collection->{total_count} > 0 ) || !$self->ALLOW_EMPTY_COLLECTION){
ok(((ref $list_collection->{_links}->{$hal_name} eq "ARRAY" ) ||
(ref $list_collection->{_links}->{$hal_name} eq "HASH" ) ), "$self->{name}: check if 'ngcp:".$self->name."' is array/hash-ref");
}
# it is really strange - we check that the only element of the _links will be hash - and after this treat _embedded as hash too
@ -665,7 +686,7 @@ sub check_created_listed{
foreach (@$listed){
delete $created_items->{$_};
}
is(scalar(keys %{$created_items}), 0, "check if all created test items have been foundin the list");
is(scalar(keys %{$created_items}), 0, "$self->{name}: check if all created test items have been foundin the list");
if(scalar(keys %{$created_items})){
print Dumper $created_items;
print Dumper $listed;
@ -676,7 +697,7 @@ sub check_embedded {
my($self, $embedded, $check_embedded_cb) = @_;
defined $check_embedded_cb and $check_embedded_cb->($embedded);
foreach my $embedded_name(@{$self->embedded_resources}){
ok(exists $embedded->{_links}->{'ngcp:'.$embedded_name}, "check presence of ngcp:$embedded_name relation");
ok(exists $embedded->{_links}->{'ngcp:'.$embedded_name}, "$self->{name}: check presence of ngcp:$embedded_name relation");
}
}
@ -736,8 +757,8 @@ sub check_patch_correct{
my($self,$content) = @_;
my ($res,$rescontent,$req) = $self->request_patch( $content );
$self->http_code_msg(200, "check patched item", $res, $rescontent);
is($rescontent->{_links}->{self}->{href}, $self->uri2location($req->uri), "check patched self link");
is($rescontent->{_links}->{collection}->{href}, '/api/'.$self->name.'/', "check patched collection link");
is($rescontent->{_links}->{self}->{href}, $self->uri2location($req->uri), "$self->{name}: check patched self link");
is($rescontent->{_links}->{collection}->{href}, '/api/'.$self->name.'/', "$self->{name}: check patched collection link");
return ($res,$rescontent,$req);
}
@ -770,7 +791,7 @@ sub check_patch_body_empty{
my($self) = @_;
my ($res,$content,$req) = $self->request_patch;
$self->http_code_msg(400, "check patch missing body", $res, $content);
like($content->{message}, qr/is missing a message body/, "check patch missing body response");
like($content->{message}, qr/is missing a message body/, "$self->{name}: check patch missing body response");
}
sub check_patch_body_notarray{
@ -779,7 +800,7 @@ sub check_patch_body_notarray{
{ foo => 'bar' },
);
$self->http_code_msg(400, "check patch no array body", $res, $content);
like($content->{message}, qr/must be an array/, "check patch missing body response");
like($content->{message}, qr/must be an array/, "$self->{name}: check patch missing body response");
}
sub check_patch_op_missed{
@ -788,7 +809,7 @@ sub check_patch_op_missed{
[{ foo => 'bar' }],
);
$self->http_code_msg(400, "check patch no op in body", $res, $content);
like($content->{message}, qr/must have an 'op' field/, "check patch no op in body response");
like($content->{message}, qr/must have an 'op' field/, "$self->{name}: check patch no op in body response");
}
sub check_patch_op_wrong{
@ -797,7 +818,7 @@ sub check_patch_op_wrong{
[{ op => 'bar' }],
);
$self->http_code_msg(400, "check patch invalid op in body", $res, $content);
like($content->{message}, qr/Invalid PATCH op /, "check patch no op in body response");
like($content->{message}, qr/Invalid PATCH op /, "$self->{name}: check patch no op in body response");
}
sub check_patch_opreplace_paramsmiss{
@ -806,7 +827,7 @@ sub check_patch_opreplace_paramsmiss{
[{ op => 'replace' }],
);
$self->http_code_msg(400, "check patch missing fields for op", $res, $content);
like($content->{message}, qr/Missing PATCH keys /, "check patch missing fields for op response");
like($content->{message}, qr/Missing PATCH keys /, "$self->{name}: check patch missing fields for op response");
}
sub check_patch_opreplace_paramsextra{
@ -815,7 +836,7 @@ sub check_patch_opreplace_paramsextra{
[{ op => 'replace', path => '/foo', value => 'bar', invalid => 'sna' }],
);
$self->http_code_msg(400, "check patch extra fields for op", $res, $content);
like($content->{message}, qr/Invalid PATCH key /, "check patch extra fields for op response");
like($content->{message}, qr/Invalid PATCH key /, "$self->{name}: check patch extra fields for op response");
}
sub check_patch_path_wrong{
my($self) = @_;
@ -842,16 +863,21 @@ sub check_bundle{
my($self) = @_;
$self->check_options_collection();
# iterate over collection to check next/prev links and status
my $listed = $self->check_list_collection();
$self->check_created_listed($listed);
my $listed=[];
if($self->methods->{collection}->{allowed}->{GET}){
$listed = $self->check_list_collection();
$self->check_created_listed($listed);
}
# test model item
if(@$listed){
if(@$listed && !$self->NO_ITEM_MODULE){
$self->check_options_item;
if(exists $self->methods->{'item'}->{allowed}->{'PUT'}){
$self->check_put_bundle;
}
if(exists $self->methods->{'item'}->{allowed}->{'PATCH'}){
$self->check_patch_bundle;
if(!$self->IS_EMPTY_COLLECTION){
if(exists $self->methods->{'item'}->{allowed}->{'PUT'}){
$self->check_put_bundle;
}
if(exists $self->methods->{'item'}->{allowed}->{'PATCH'}){
$self->check_patch_bundle;
}
}
}
}
@ -958,7 +984,7 @@ sub check_get2put{
(defined $put_in->{data_cb}) and $put_in->{data_cb}->($put_out->{content_in});
@{$put_out}{qw/response content request/} = $self->request_put( $put_out->{content_in}, $put_in->{uri} );
$self->http_code_msg(200, "check_get2put: check put successful", $put_out->{response}, $put_out->{content} );
is_deeply($get_out->{content}, $put_out->{content}, "check_get2put: check put if unmodified put returns the same");
is_deeply($get_out->{content}, $put_out->{content}, "$self->{name}: check_get2put: check put if unmodified put returns the same");
return ($put_out,$get_out);
}
@ -1013,7 +1039,7 @@ sub check_post2get{
delete $get_out->{content}->{_links};
my $item_id = delete $get_out->{content}->{id};
is_deeply($post_out->{data}, $get_out->{content}, "check_post2get: check POSTed '".$self->name."' against fetched");
is_deeply($post_out->{data}, $get_out->{content}, "$self->{name}: check_post2get: check POSTed '".$self->name."' against fetched");
$get_out->{content}->{id} = $item_id;
return ($post_out, $get_out);
@ -1026,12 +1052,11 @@ sub put_and_get{
@{$get_out}{qw/response content request/} = $self->check_item_get($get_in->{uri});
delete $put_get_out->{content_in}->{_links};
delete $put_get_out->{content_in}->{_embedded};
is_deeply($put_in->{content}, $put_get_out->{content}, "check that '$put_in->{uri}' was updated on put");
is_deeply($put_in->{content}, $put_get_out->{content}, "$self->{name}: check that '$put_in->{uri}' was updated on put;");
return ($put_out,$put_get_out,$get_out);
}
####--------------------------utils
sub hash2params{
my($self,$hash) = @_;
return join '&', map {$_.'='.uri_escape($hash->{$_})} keys %{ $hash };
@ -1060,6 +1085,8 @@ sub uri2location{
sub http_code_msg{
my($self,$code,$message,$res,$content) = @_;
my $message_res;
my $name = $self->{name} // '';
$message //= '';
if ( ($res->code < 300) || ( $code >= 300 ) ) {
my $res_message = $res->message // '';
my $content_message = 'HASH' eq ref $content ? $content->{message} // '' : '' ;
@ -1067,9 +1094,9 @@ sub http_code_msg{
} else {
$content //= $self->get_response_content($res);
if (defined $content && $content && defined $content->{message}) {
$message_res = $message . ' (' . $res->message . ': ' . $content->{message} . ')';
$message_res = "$name: ".$message . ' (' . $res->message . ': ' . $content->{message} . ')';
} else {
$message_res = $message . ' (' . $res->message . ')';
$message_res = "$name: ".$message . ' (' . $res->message . ')';
}
}
$code and is($res->code, $code, $message_res);
@ -1098,4 +1125,26 @@ sub replace_cached_data{
store $restored,$self->data_cache_file;
return $restored;
}
sub get_cached_data{
my($self) = @_;
return (-e $self->data_cache_file) ? retrieve($self->data_cache_file) : {};
}
sub replace_cached_data{
my($self,$data_callback,$restored) = @_;
$restored //= $self->get_cached_data;
$data_callback->($restored);
store $restored,$self->data_cache_file;
return $restored;
}
sub clear_cache{
my($self) = @_;
if(-e $self->data_cache_file){
my $cmd = "rm ".$self->data_cache_file;
`$cmd`;
}
}
1;

Loading…
Cancel
Save