You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ngcp-panel/t/lib/NGCP/Test/Resource.pm

922 lines
30 KiB

package NGCP::Test::Resource;
use strict;
use warnings;
use Moose;
use Test::More;
use Test::Deep qw/cmp_bag/;
use Clone qw/clone/;
use Data::Dumper;
use JSON qw/from_json to_json/;
use NGCP::Test::Patch;
has '_test' => (
isa => 'Object',
is => 'ro',
);
has 'client' => (
isa => 'Object',
is => 'rw',
);
has 'resource' => (
isa => 'Str',
is => 'ro',
);
has 'data' => (
isa => 'Maybe[HashRef]',
is => 'ro',
);
has 'allowed' => (
isa => 'HashRef',
is => 'ro',
default => sub { { collection => [], item => [] } },
);
has 'autodelete_created_items' => (
isa => 'Bool',
is => 'rw',
default => 1,
);
has 'print_summary_on_finish' => (
isa => 'Bool',
is => 'rw',
default => 0,
);
has 'test_count' => (
isa => 'Int',
is => 'rw',
default => 0,
);
has 'created_items' => (
isa => 'ArrayRef',
is => 'rw',
default => sub {[]}
);
has 'requests' => (
is => 'rw',
isa => 'ArrayRef',
default => sub {[]},
);
sub BUILD {
my $self = shift;
unless($self->client) {
die "Missing parameter 'client' when creating " . __PACKAGE__ . "\n";
}
unless($self->resource) {
die "Missing parameter 'resource' when creating " . __PACKAGE__ . "\n";
}
}
sub _process_input {
my ($self, $args) = @_;
my @data = ();
my @qparams= ();
my @expected = ();
if(exists $args->{data_replace}) {
my $ref = ref $args->{data_replace};
if($ref eq 'HASH') {
push @data, ($args->{data_replace});
} elsif($ref eq 'ARRAY') {
@data = @{ $args->{data_replace} };
} else {
die "Invalid 'data_replace' type, must be hashref or arrayref\n";
}
} else {
@data = ($self->data);
}
if(exists $args->{expected_result}) {
my $ref = ref $args->{expected_result};
if($ref eq 'HASH') {
@expected = map { $args->{expected_result} } (1 .. @data);
} elsif($ref eq 'ARRAY') {
@expected = @{ $args->{expected_result} };
unless($#expected == $#data) {
die "Number of elements in 'data_replace' doesn't match number of elements in 'expected_result'\n";
}
} else {
die "Invalid 'data_replace' type, must be hashref or arrayref\n";
}
} else {
die "Missing argument 'expected_result'\n";
}
if(exists $args->{query_params}) {
my $ref = ref $args->{query_params};
if($ref eq 'HASH') {
push @qparams, ($args->{query_params});
} elsif($ref eq 'ARRAY') {
@qparams = @{ $args->{query_params} };
} else {
die "Invalid 'query_params' type, must be hashref or arrayref\n";
}
}
return { data => \@data, expected => \@expected, qparams => \@qparams };
}
sub _get_replaced_data {
my ($self, $data, $repl) = @_;
my $allrepl = [];
if(ref $repl eq "HASH") {
push @{ $allrepl }, $repl;
} elsif(ref $repl eq "ARRAY") {
$allrepl = $repl;
} else {
die "All data_replace elements must be hashref or arrayref\n";
}
my $d = clone($data);
foreach my $repl(@{ $allrepl }) {
my $key = $repl->{field};
my $val = $repl->{value};
if(defined $key) {
my @parts = split(/\./, $key);
my $tmp = \$d;
for(my $i = 0; $i < @parts; ++$i) {
if($repl->{delete} && $i == @parts - 1) {
last;
}
$tmp = \$$$tmp{$parts[$i]};
}
if($repl->{delete}) {
delete $$tmp->{$parts[$#parts]};
} else {
$$tmp = $val;
}
}
}
return $d;
}
sub _apply_patch {
my ($self, $data, $json_patch) = @_;
my $p = NGCP::Test::Patch->new;
my $res = $p->apply_patch($data, $json_patch);
return $res;
}
sub _get_patch_data {
my ($self, $data, $repl) = @_;
# TODO: support dot-style notation for nested fields
my $key = $repl->{field};
my $path = "/$key";
$path =~ s/\./\//g;
my $val = $repl->{value};
my $op = $repl->{op};
my $d = {
op => $op,
path => $path,
};
if($op eq "remove") {
# no value to be set
} elsif($op eq "copy" || $op eq "move") {
# value is the "from" path
my $from = "/$val";
$from =~ s/\./\//g;
$d->{from} = $from;
} else {
$d->{value} = $val;
}
$d = [$d];
return $d;
}
sub _test_fields {
my ($self, $skip, $expected, $ref, $item, $name) = @_;
#print "++++ testing fields for $name\n";
#print Dumper $ref;
#print Dumper $item;
$ref //= {};
$skip //= [];
my %skip = ();
foreach my $s(@{ $skip }) {
my @f = split /\./, $s;
my $elem = \%skip;
foreach my $p(@f) {
$elem->{$p} = {};
$elem = $elem->{$p};
}
}
my %expect = map { $_ => 0 } @{ $expected // [] };
foreach my $field(keys %{ $ref }) {
next if($field eq "_links");
if(defined $expected && exists $expect{$field}) {
ok(exists $item->{$field}, "$name - expected field $field seen");
$self->_inc_test_count;
$expect{$field} = 1;
}
next if(exists $skip{$field} && !keys %{ $skip{$field} });
if(defined $expected && !exists($expect{$field})) {
next;
}
if(ref $ref->{$field} eq "ARRAY") {
if(exists $skip{$field} && keys %{ $skip{$field} } && @{ $ref->{$field} } && ref $ref->{$field}->[0] eq "HASH") {
for(my $i = 0; $i < @{ $ref->{$field} }; ++$i) {
my $refelem = $ref->{$field}->[$i];
my $elem = $item->{$field}->[$i];
my $k = (keys %{ $skip{$field} })[0];
my $refold = delete $refelem->{$k};
my $old = delete $elem->{$k};
$self->_test->debug("compare content of $field\n");
$self->_test->debug("element: " . Dumper $elem);
$self->_test->debug("reference element: " . Dumper $refelem);
is_deeply($elem, $refelem, $name . " - content of $field");
$self->_inc_test_count;
$refelem->{$k} = $refold;
$elem->{$k} = $old;
}
is(@{ $ref->{$field} }, @{ $item->{$field} }, "$name - element count of $field");
$self->_inc_test_count;
} else {
$self->_test->debug("compare content of $field via bag comparison\n");
$self->_test->debug("element: " . Dumper $item->{$field});
$self->_test->debug("reference element: " . Dumper $ref->{$field});
cmp_bag($item->{$field}, $ref->{$field}, $name . " - content of $field");
$self->_inc_test_count;
}
} elsif(ref $ref->{$field} eq "HASH") {
if(exists $skip{$field} && keys %{ $skip{$field} }) {
my $k = (keys %{ $skip{$field} })[0];
my $refold = delete $ref->{$field}->{$k};
my $old = delete $item->{$field}->{$k};
is_deeply($item->{$field}, $ref->{$field}, $name . " - content of $field");
$self->_inc_test_count;
$ref->{$field}->{$k} = $refold;
$item->{$field}->{$k} = $old;
} else {
is_deeply($item->{$field}, $ref->{$field}, $name . " - content of $field");
$self->_inc_test_count;
}
} elsif(ref $ref->{$field} ne "") {
is_deeply($item->{$field}, $ref->{$field}, $name . " - content of $field");
$self->_inc_test_count;
} else {
is($item->{$field}, $ref->{$field}, $name . " - content of $field");
$self->_inc_test_count;
}
}
if(defined $expected) {
foreach my $field(keys %{ $item }) {
next if($field eq "_links");
ok(exists $expect{$field}, "$name - field $field expected");
$self->_inc_test_count;
delete $expect{$field};
}
foreach my $field(keys %expect) {
delete $expect{$field} if($expect{$field} == 1);
}
}
if(!ok(keys %expect == 0, "$name - all expected fields seen")) {
diag("the following expected fields were not seen: " . join(', ', keys %expect));
diag("existing fields were: " . join(', ', keys %{ $item }));
}
$self->_inc_test_count;
}
sub test_allowed_methods {
my ($self, $item) = @_;
# while at it, test without item as well
if(defined $item) {
$self->test_allowed_methods();
}
my $res;
my $expected;
my $name = $self->resource . ' - allowed methods';
if(defined $item) {
$res = $self->client->_options('api/'.$self->resource.'/'.$item->{id});
$name .= " - item";
$expected = $self->allowed->{item};
} else {
$res = $self->client->_options('api/'.$self->resource);
$name .= " - collection";
$expected = $self->allowed->{collection};
}
is($res->code, 200, "$name - options request");
$self->_inc_test_count;
return unless($res->is_success);
my $data = from_json($res->decoded_content);
my $methods = $data->{methods};
my @hopts = split /\s*,\s*/, $res->header('Allow');
is(@hopts, @{ $expected }, "$name - correct amount of allowed methods in header");
$self->_inc_test_count;
is(@{ $methods }, @{ $expected }, "$name - correct amount of allowed methods in body");
$self->_inc_test_count;
while((my $opt = shift @{ $expected })) {
ok(grep { /^$opt$/ } @hopts, "$name - '$opt' in Allow header");
$self->_inc_test_count;
ok(grep { /^$opt$/ } @{ $methods }, "$name - '$opt' in body");
$self->_inc_test_count;
}
is(@{ $expected }, 0, "$name - no leftover in allowed methods");
$self->_inc_test_count;
}
sub test_empty_bodies {
my ($self, $item) = @_;
# while at it, test without item as well
if(defined $item) {
$self->test_empty_bodies();
}
my $res;
my $name = $self->resource . ' - missing body';
if(defined $item) {
$name .= " - put";
$res = $self->client->_put('api/'.$self->resource.'/'.$item->{id}, undef);
is($res->code, 400, $name);
$self->_inc_test_count;
$name .= " - patch";
$res = $self->client->_patch('api/'.$self->resource.'/'.$item->{id}, undef);
is($res->code, 400, $name);
$self->_inc_test_count;
} else {
$name .= " - post";
$res = $self->client->_post('api/'.$self->resource, undef);
is($res->code, 400, $name);
$self->_inc_test_count;
}
}
sub test_invalid_headers {
my ($self, $item) = @_;
# while at it, test without item as well
if(defined $item) {
$self->test_invalid_headers();
}
my $res;
my $data;
my $testname = $self->resource . ' - invalid headers';
my $name;
if(defined $item) {
$name = "$testname - put without content-type";
$res = $self->client->_put('api/'.$self->resource.'/'.$item->{id}, $item, '', undef);
is($res->code, 415, "$name - code");
$self->_inc_test_count;
like($res->message, qr/Unsupported Media Type/, "$name - status");
$self->_inc_test_count;
$data = from_json($res->decoded_content);
like($data->{message}, qr/'undefined', accepting application\/json only/, "$name - error");
$self->_inc_test_count;
$name = "$testname - put with invalid content-type";
$res = $self->client->_put('api/'.$self->resource.'/'.$item->{id}, $item, 'something/invalid', undef);
is($res->code, 415, "$name - code");
$self->_inc_test_count;
like($res->message, qr/Unsupported Media Type/, "$name - status");
$self->_inc_test_count;
$data = from_json($res->decoded_content);
like($data->{message}, qr/'something\/invalid', accepting application\/json only/, "$name - error");
$self->_inc_test_count;
$name = "$testname - patch without content-type";
$res = $self->client->_patch('api/'.$self->resource.'/'.$item->{id}, $item, '', undef);
is($res->code, 415, "$name - code");
$self->_inc_test_count;
like($res->message, qr/Unsupported Media Type/, "$name - status");
$self->_inc_test_count;
$data = from_json($res->decoded_content);
like($data->{message}, qr/'undefined', accepting application\/json-patch\+json only/, "$name - error");
$self->_inc_test_count;
$name = "$testname - patch with invalid content-type";
$res = $self->client->_patch('api/'.$self->resource.'/'.$item->{id}, $item, 'application/json', undef);
is($res->code, 415, "$name - code");
$self->_inc_test_count;
like($res->message, qr/Unsupported Media Type/, "$name - status");
$self->_inc_test_count;
$data = from_json($res->decoded_content);
like($data->{message}, qr/'application\/json', accepting application\/json-patch\+json only/, "$name - error");
$self->_inc_test_count;
$name = "$testname - put without prefer";
$res = $self->client->_put('api/'.$self->resource.'/'.$item->{id}, $item, undef, '');
is($res->code, 204, "$name - code");
$self->_inc_test_count;
like($res->message, qr/No Content/, "$name - status");
$self->_inc_test_count;
is($res->decoded_content, '', "$name - no content");
$self->_inc_test_count;
$name = "$testname - put with invalid prefer";
$res = $self->client->_put('api/'.$self->resource.'/'.$item->{id}, $item, undef, 'something=invalid');
is($res->code, 400, "$name - code");
$self->_inc_test_count;
like($res->message, qr/Bad Request/, "$name - status");
$self->_inc_test_count;
$data = from_json($res->decoded_content);
like($data->{message}, qr/Header 'Prefer' must be either 'return=minimal' or 'return=representation'/, "$name - error");
$self->_inc_test_count;
$name = "$testname - patch without prefer";
$res = $self->client->_patch('api/'.$self->resource.'/'.$item->{id}, [], undef, '');
is($res->code, 204, "$name - code");
$self->_inc_test_count;
like($res->message, qr/No Content/, "$name - status");
$self->_inc_test_count;
is($res->decoded_content, '', "$name - no content");
$self->_inc_test_count;
$name = "$testname - patch with invalid prefer";
$res = $self->client->_patch('api/'.$self->resource.'/'.$item->{id}, $item, undef, 'something=invalid');
is($res->code, 400, "$name - code");
$self->_inc_test_count;
like($res->message, qr/Bad Request/, "$name - status");
$self->_inc_test_count;
$data = from_json($res->decoded_content);
like($data->{message}, qr/Header 'Prefer' must be either 'return=minimal' or 'return=representation'/, "$name - error");
$self->_inc_test_count;
} else {
$item = { test => 'test' };
$name = "$testname - post without content-type";
$res = $self->client->_post('api/'.$self->resource, $item, '', undef);
is($res->code, 415, "$name - code");
$self->_inc_test_count;
like($res->message, qr/Unsupported Media Type/, "$name - status");
$self->_inc_test_count;
$data = from_json($res->decoded_content);
like($data->{message}, qr/Unsupported media type 'undefined', accepting application\/json only/, "$name - error");
$self->_inc_test_count;
$name = "$testname - post with invalid content-type";
$res = $self->client->_post('api/'.$self->resource, $item, 'something/invalid', undef);
is($res->code, 415, "$name - code");
$self->_inc_test_count;
like($res->message, qr/Unsupported Media Type/, "$name - status");
$self->_inc_test_count;
$data = from_json($res->decoded_content);
like($data->{message}, qr/'something\/invalid', accepting application\/json only/, "$name - error");
$self->_inc_test_count;
$name = "$testname - post without prefer";
$res = $self->client->_post('api/'.$self->resource, {}, undef, '');
is($res->code, 422, "$name - code");
$self->_inc_test_count;
like($res->message, qr/Unprocessable Entity/, "$name - status");
$self->_inc_test_count;
$data = from_json($res->decoded_content);
# we assume here that at least ANY field must be existent in a post
like($data->{message}, qr/Validation failed/, "$name - error");
$self->_inc_test_count;
$name = "$testname - post with invalid prefer";
$res = $self->client->_post('api/'.$self->resource, $item, undef, 'something=invalid');
is($res->code, 400, "$name - code");
$self->_inc_test_count;
like($res->message, qr/Bad Request/, "$name - status");
$self->_inc_test_count;
$data = from_json($res->decoded_content);
like($data->{message}, qr/Header 'Prefer' must be either 'return=minimal' or 'return=representation'/, "$name - error");
$self->_inc_test_count;
}
}
sub _test_link {
my ($self, $href, $name) = @_;
my $res = $self->client->_get($href);
$self->_push_request($name, $res);
return $res->is_success;
}
sub _test_item {
my ($self, $item, $args, $name, $ref) = @_;
ok(!exists $item->{_embedded}, "$name - absence of _embedded in item");
$self->_inc_test_count;
ok(!exists $item->{total_count}, "$name - absence of total_count in item");
$self->_inc_test_count;
$self->_test_fields($args->{skip_test_fields}, $args->{expected_fields}, $ref, $item, $name);
my %links = map { $_ => 0 } @{ $args->{expected_links} };
foreach my $link(keys %{ $item->{_links} }) {
next if(grep { /^$link$/ } qw/self curies profile collection/);
ok(exists $links{$link}, "$name - existence of link $link in reference");
$self->_inc_test_count;
delete $links{$link};
ok($self->_test_link($item->{_links}->{$link}->{href}, $name), "$name - follow link to $link");
$self->_inc_test_count;
}
if(!ok(keys %links == 0, "$name - missing links")) {
diag("missing links: " . join(", ", keys %links));
}
$self->_inc_test_count;
}
sub test_get {
my ($self, %args) = @_;
my $testname = $self->resource . ' - ' . ($args{name} // 'test_get');
my $ref = $args{item};
my $input = $self->_process_input(\%args);
unless(@{ $input->{qparams} }) {
$input->{qparams} = [{}];
}
my @qparams = @{ $input->{qparams} };
my @expected = @{ $input->{expected} };
my @items = ();
if($ref && @qparams != 1) {
die "When providing a reference 'item', query_params must contain one row only\n";
}
for(my $i = 0; $i < @qparams; ++$i) {
my $e = $expected[$i];
my $q = $qparams[$i];
my $name = "$testname $i";
my $uri = 'api/'.$self->resource . ($ref ? "/$$ref{id}" : "");
my @tmpq = ();
foreach my $k(keys %{ $q }) {
push @tmpq, "$k=$$q{$k}";
}
if(@tmpq) {
$uri .= '?' . join('&', @tmpq);
}
my $res = $self->client->_get($uri);
is($res->code, $e->{code}, $name . " - code");
$self->_inc_test_count;
if(exists $e->{reason_re}) {
like($res->message, qr/$e->{reason_re}/, $name . " - message");
$self->_inc_test_count;
} elsif(exists $e->{error_re}) {
ok(!$res->is_success, $name . " - is an error");
$self->_inc_test_count;
my $item = from_json($res->decoded_content);
like($item->{message}, qr/$e->{error_re}/, $name . " - error message");
$self->_inc_test_count;
}
if($res->code =~ /^2\d\d$/) {
my $item = from_json($res->decoded_content);
push @items, $item;
if($ref) {
$self->_test_item($item, \%args, $name, $ref);
} else {
ok(exists $item->{total_count}, "$name - presence of total_count in collection");
$self->_inc_test_count;
if($item->{total_count}) {
ok(exists $item->{_embedded}, "$name - presence of _embedded in collection on data");
$self->_inc_test_count;
}
if(exists $args{expected_count}) {
is($item->{total_count}, $args{expected_count}, "$name - total_count value");
$self->_inc_test_count;
}
foreach my $subitem(@{ $item->{_embedded}->{"ngcp:".$self->resource} }) {
$self->_test_item($subitem, \%args, $name, undef);
}
}
}
}
return \@items;
}
sub test_post {
my ($self, %args) = @_;
my $testname = $self->resource . ' - ' . ($args{name} // 'test_post');
my $input = $self->_process_input(\%args);
my @data = @{ $input->{data} };
my @expected = @{ $input->{expected} };
for(my $i = 0; $i < @data; ++$i) {
my $d = $self->_get_replaced_data($self->data, $data[$i]);
my $e = $expected[$i];
my $name = "$testname $i";
# fire request and test result
my $res = $self->client->_post('api/'.$self->resource, $d);
$self->_push_request($name, $res);
is($res->code, $e->{code}, $name . " - code");
$self->_inc_test_count;
if(exists $e->{reason_re}) {
like($res->message, qr/$e->{reason_re}/, $name . " - message");
$self->_inc_test_count;
} elsif(exists $e->{error_re}) {
ok(!$res->is_success, $name . " - is an error");
$self->_inc_test_count;
my $item = from_json($res->decoded_content);
like($item->{message}, qr/$e->{error_re}/, $name . " - error message");
$self->_inc_test_count;
}
# copy created item into cache
if($res->code =~ /^2\d\d$/) {
my $item;
if($res->decoded_content) {
# TODO: do we need to extract from JSON::HAL?
$item = from_json($res->decoded_content);
} else {
my $id = $res->header('Location');
$id =~ s/^.+\/([^\/]+)$/$1/;
$res = $self->client->_get('api/'.$self->resource.'/'.$id);
$self->_push_request($name, $res);
unless($res->is_success) {
die "Failed to re-fetch created resource " . $self->resource . " with id $id\n";
}
# TODO: do we need to extract from JSON::HAL?
$item = from_json($res->decoded_content);
}
$self->push_created_item($item);
$self->_test_fields($args{skip_test_fields}, $args{expected_fields}, $d, $item, $name);
}
}
}
sub test_put {
my ($self, %args) = @_;
my $testname = $self->resource . ' - ' . ($args{name} // 'test_put');
my $item = $args{item};
my $input = $self->_process_input(\%args);
my @data = @{ $input->{data} };
my @expected = @{ $input->{expected} };
my @items = ();
for(my $i = 0; $i < @data; ++$i) {
my $d = $self->_get_replaced_data($item, $data[$i]);
$self->_test->debug("replaced data: " . Dumper $d);
my $e = $expected[$i];
my $name = "$testname $i";
# fire request and test result
my $res = $self->client->_put('api/'.$self->resource.'/'.$d->{id}, $d);
$self->_push_request($name, $res);
is($res->code, $e->{code}, $name . " - code");
$self->_inc_test_count;
if(exists $e->{reason_re}) {
like($res->message, qr/$e->{reason_re}/, $name . " - message");
$self->_inc_test_count;
} elsif(exists $e->{error_re}) {
ok(!$res->is_success, $name . " - is an error");
$self->_inc_test_count;
my $item = from_json($res->decoded_content);
like($item->{message}, qr/$e->{error_re}/, $name . " - error message");
$self->_inc_test_count;
}
if($res->code =~ /^2\d\d$/ && $res->decoded_content) {
# TODO: do we need to extract from JSON::HAL?
my $resitem = from_json($res->decoded_content);
push @items, $resitem;
$self->_test_fields($args{skip_test_fields}, $args{expected_fields}, $d, $resitem, $name);
} else {
push @items, $item;
}
}
return \@items;
}
sub test_patch {
my ($self, %args) = @_;
my $testname = $self->resource . ' - ' . ($args{name} // 'test_patch');
my $item = $args{item};
my $input = $self->_process_input(\%args);
my @data = @{ $input->{data} };
my @expected = @{ $input->{expected} };
my @items = ();
for(my $i = 0; $i < @data; ++$i) {
my $d = $self->_get_patch_data($item, $data[$i]);
my $refd = $self->_apply_patch($item, to_json($d));
my $e = $expected[$i];
my $name = "$testname $i";
# fire request and test result
my $res = $self->client->_patch('api/'.$self->resource.'/'.$item->{id}, $d);
$self->_push_request($name, $res);
is($res->code, $e->{code}, $name . " - code");
$self->_inc_test_count;
if(exists $e->{reason_re}) {
like($res->message, qr/$e->{reason_re}/, $name . " - message");
$self->_inc_test_count;
} elsif(exists $e->{error_re}) {
ok(!$res->is_success, $name . " - is an error");
$self->_inc_test_count;
my $item = from_json($res->decoded_content);
like($item->{message}, qr/$e->{error_re}/, $name . " - error message");
$self->_inc_test_count;
}
if($res->code =~ /^2\d\d$/ && $res->decoded_content) {
# TODO: do we need to extract from JSON::HAL?
my $resitem = from_json($res->decoded_content);
push @items, $resitem;
$self->_test_fields($args{skip_test_fields}, $args{expected_fields}, $refd, $resitem, $name);
} else {
push @items, $item;
}
}
return \@items;
}
sub test_delete {
my ($self, %args) = @_;
my $testname = $self->resource . ' - ' . ($args{name} // 'test_delete');
my $item = $args{item};
my $input = $self->_process_input(\%args);
my @data = @{ $input->{data} };
my @expected = @{ $input->{expected} };
my @items = ();
for(my $i = 0; $i < @data; ++$i) {
my $d = $self->_get_replaced_data($item, $data[$i]);
my $e = $expected[$i];
my $name = "$testname $i";
# fire request and test result
my $res = $self->client->_delete('api/'.$self->resource.'/'.$d->{id});
$self->_push_request($name, $res);
is($res->code, $e->{code}, $name . " - code");
$self->_inc_test_count;
if(exists $e->{reason_re}) {
like($res->message, qr/$e->{reason_re}/, $name . " - message");
$self->_inc_test_count;
} elsif(exists $e->{error_re}) {
ok(!$res->is_success, $name . " - is an error");
$self->_inc_test_count;
my $item = from_json($res->decoded_content);
like($item->{message}, qr/$e->{error_re}/, $name . " - error message");
$self->_inc_test_count;
}
}
}
=pod
# TODO: not needed?
sub _update_created_item {
my ($self, $item) = @_;
for(my $i = 0; $i < $self->count_created_items(); ++$i) {
if($self->created_items->[$i]->{id} eq $item->{id}) {
print "found old item in cache\n";
@{$self->created_items}[$i] = $item;
}
}
}
=cut
sub _inc_test_count {
my ($self) = @_;
$self->_test->inc_test_count();
}
sub push_created_item {
my ($self, $item) = @_;
push @{ $self->created_items }, $item;
}
sub pop_created_item {
my ($self) = @_;
my $item = pop @{ $self->created_items };
return $item;
}
sub shift_created_item {
my ($self) = @_;
my $item = shift @{ $self->created_items };
return $item;
}
sub count_created_items {
my ($self) = @_;
return 0 + @{ $self->created_items };
}
sub print_summary {
my ($self) = @_;
diag("Performed Requests:");
my $i = 0;
foreach my $r(@{ $self->requests }) {
$i++;
diag("$$r{testname}:");
diag("$$r{method} $$r{uri}");
diag("$$r{code} $$r{message}");
diag("rtt: $$r{rtt}");
diag("");
}
diag("Total: $i");
}
sub _push_request {
my ($self, $name, $res) = @_;
push @{ $self->requests }, {
testname => $name,
method => $res->request->method,
uri => $res->request->uri,
code => $res->code,
message => $res->message,
rtt => $self->client->last_rtt,
};
}
sub DEMOLISH {
my ($self) = @_;
my @failed = ();
my @threads = ();
if($self->autodelete_created_items) {
while((my $item = $self->pop_created_item())) {
my $id = $item->{id};
my $url = 'api/'.$self->resource.'/'.$id;
my $res = $self->client->_delete($url);
unless($res->is_success) {
my $data = from_json($res->decoded_content);
if($res->code == 404) {
$res = $self->client->_patch($url, [{
op => 'replace',
path => '/status',
value => 'terminated'
}]);
unless($res->is_success) {
diag("Failed to both auto-delete or auto-terminate '$url': $$data{message}");
push @failed, $id;
} else {
#print "+++++ $url successfully terminated\n";
}
}
} else {
#print "+++++ $url successfully deleted\n";
}
$self->_push_request('autodelete', $res);
}
if(@failed) {
diag("failed to auto-delete the following " .
$self->resource . ": " . (join ', ', @failed));
}
}
if($self->print_summary_on_finish) {
$self->print_summary();
}
}
1;
__END__
=head1 NAME
NGCP::Test::Resource
=head1 DESCRIPTION
NGCP Panel test resource
=head1 AUTHOR
Sipwise Development Team
=head1 LICENSE
This library is free software. You can redistribute it and/or modify
it under the same terms as Perl itself.
=cut