MT#14461 Adjust subscriber for faxes after subscriber creation

Fix faxserversettings PUT/PATCH processing found after faxes tests run

MT#15441 Tests for subscriber number changes

MT#12347 Test framework small fixes and additions

Some format changes and unused library removals
Add put_and_get method as common
Replace request_get & check 200 with check_item_get
Changes for API that doesn't return created object json after POST
Complete base_uri using replacement with normalize_uri

Change-Id: Ia31a97eb9511f1ca1de63c72bcbe380b89dfed34
changes/91/2891/2
Irina Peshinskaya 10 years ago committed by Rene Krenn
parent 98ac54d7cb
commit 57edaa4689

@ -11,6 +11,7 @@ use NGCP::Panel::Utils::ValidateJSON qw();
use NGCP::Panel::Utils::DateTime;
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
use Clone qw/clone/;
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::HTTPMethods;
@ -110,6 +111,7 @@ sub PATCH :Allow {
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, faxserversettings => $item);
my $old_resource = $self->hal_from_item($c, $item)->resource;
$old_resource = clone($old_resource);
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;

@ -1,18 +1,11 @@
use strict;
use warnings;
#use Moose;
use Sipwise::Base;
use Test::Collection;
use Test::FakeData;
use Net::Domain qw(hostfqdn);
use LWP::UserAgent;
use HTTP::Request::Common;
use JSON;
use Test::More;
use Data::Dumper;
#init test_machine
my $fake_data = Test::FakeData->new;
$fake_data->set_data_from_script({
@ -44,35 +37,35 @@ $test_machine->methods->{item}->{allowed} = {map {$_ => 1} qw(GET HEAD OPT
$test_machine->form_data_item( );
# create 3 new field billing fees from DATA_ITEM
$test_machine->check_create_correct( 3, sub{ $_[0]->{destination} .= $_[1]->{i} ; } );
$test_machine->check_get2put( );
$test_machine->check_get2put();
$test_machine->check_bundle();
# specific tests
# try to create fee without billing_profile_id
{
my ($res, $err) = $test_machine->request_post(sub{delete $_[0]->{billing_profile_id};});
my ($res, $err) = $test_machine->check_item_post(sub{delete $_[0]->{billing_profile_id};});
is($res->code, 422, "create billing zone without billing_profile_id");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Missing parameter 'billing_profile_id'/, "check error message in body");
}
# try to create fee with invalid billing_profile_id
{
my ($res, $err) = $test_machine->request_post(sub{$_[0]->{billing_profile_id} = 99999;});
my ($res, $err) = $test_machine->check_item_post(sub{$_[0]->{billing_profile_id} = 99999;});
is($res->code, 422, "create billing zone with invalid billing_profile_id");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'billing_profile_id'/, "check error message in body");
}
# try to create fee without billing_zone_id
{
my ($res, $err) = $test_machine->request_post(sub{delete $_[0]->{billing_zone_id};});
my ($res, $err) = $test_machine->check_item_post(sub{delete $_[0]->{billing_zone_id};});
is($res->code, 422, "create billing zone without billing_zone_id");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'billing_zone_id'/, "check error message in body");
}
# try to create fee with invalid billing_zone_id
{
my ($res, $err) = $test_machine->request_post(sub{$_[0]->{billing_zone_id} = 99999;});
my ($res, $err) = $test_machine->check_item_post(sub{$_[0]->{billing_zone_id} = 99999;});
is($res->code, 422, "create billing zone with invalid billing_zone_id");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'billing_zone_id'/, "check error message in body");
@ -80,7 +73,7 @@ $test_machine->check_bundle();
# try to create fee with implicit zone which already exists
{
my $t = time;
my ($res, $err) = $test_machine->request_post(sub{
my ($res, $err) = $test_machine->check_item_post(sub{
delete $_[0]->{billing_zone_id};
$_[0]->{billing_zone_zone} = 'apitestzone';
$_[0]->{billing_zone_detail} = 'api_test zone';
@ -88,14 +81,13 @@ $test_machine->check_bundle();
});
is($res->code, 201, "create profile fee with existing implicit zone");
my($z_fee,$req);
($res, $z_fee, $req) = $test_machine->request_get($test_machine->base_uri.$res->header('Location'));
is($res->code, 200, "fetch profile fee with existing implicit zone");
($res, $z_fee, $req) = $test_machine->check_item_get($test_machine->normalize_uri($res->header('Location')),"fetch profile fee with existing implicit zone");
ok(exists $z_fee->{billing_zone_id} && $z_fee->{billing_zone_id} == $test_machine->DATA_ITEM->{billing_zone_id}, "check if implicit zone returns the correct zone id");
}
# try to create fee with implicit zone which doesn't exist yet
{
my $t = 1 + time();
my ($res, $err) = $test_machine->request_post(sub{
my ($res, $err) = $test_machine->check_item_post(sub{
delete $_[0]->{billing_zone_id};
$_[0]->{billing_zone_zone} = 'apitestzone'.$t;
$_[0]->{billing_zone_detail} = 'api_test zone'.$t;
@ -103,14 +95,13 @@ $test_machine->check_bundle();
});
is($res->code, 201, "create profile fee with new implicit zone");
my($z_fee, $req, $content);
($res, $z_fee, $req) = $test_machine->request_get($test_machine->base_uri.$res->header('Location'));
is($res->code, 200, "fetch profile fee with new implicit zone");
($res, $z_fee, $req) = $test_machine->check_item_get($test_machine->normalize_uri($res->header('Location')),"fetch profile fee with new implicit zone");
ok(exists $z_fee->{billing_zone_id} && $z_fee->{billing_zone_id} > $test_machine->DATA_ITEM->{billing_zone_id}, "check if implicit zone returns a new zone id");
($req,$res,$content) = $test_machine->request_delete($test_machine->base_uri.$z_fee->{_links}->{'ngcp:billingzones'}->{href});
($req,$res,$content) = $test_machine->request_delete($test_machine->normalize_uri($z_fee->{_links}->{'ngcp:billingzones'}->{href}));
is($res->code, 204, "delete new implicit zone");
($res) = $test_machine->request_get($test_machine->base_uri.$z_fee->{_links}->{'self'}->{href});
($res) = $test_machine->request_get($test_machine->normalize_uri($z_fee->{_links}->{'self'}->{href}));
is($res->code, 404, "check if fee is deleted when zone is deleted");
}
@ -150,7 +141,7 @@ $test_machine->check_bundle();
$test_machine->clear_test_data_all();
{
my $uri = $test_machine->base_uri.'/api/billingzones/'.$test_machine->DATA_ITEM->{billing_zone_id};
my $uri = $test_machine->normalize_uri('/api/billingzones/'.$test_machine->DATA_ITEM->{billing_zone_id});
my($req,$res,$content) = $test_machine->request_delete($uri);
is($res->code, 204, "check delete of zone");
($res, $content, $req) = $test_machine->request_get($uri);

@ -30,7 +30,7 @@ $test_machine->form_data_item( );
# create 3 new billing zones from DATA_ITEM
$test_machine->check_create_correct( 3, sub{ $_[0]->{zone} .= $_[1]->{i} ; } );
$test_machine->check_get2put( );
$test_machine->check_get2put();
$test_machine->check_bundle();
$test_machine->clear_test_data_all();
done_testing;

@ -18,8 +18,7 @@ SKIP:
if($ENV{API_FORCE_SUBSCRIBER_ID}) {
$sub1_id = $ENV{API_FORCE_SUBSCRIBER_ID};
} else {
($res, $sub1) = $test_machine->request_get('/api/subscribers/?page=1&rows=1');
is($res->code, 200, "fetch a subscriber for testing");
($res, $sub1) = $test_machine->check_item_get('/api/subscribers/?page=1&rows=1',"fetch a subscriber for testing");
if ($sub1->{total_count} < 1) {
skip("Precondition not met: need a subscriber",1);
}
@ -27,14 +26,11 @@ SKIP:
}
cmp_ok ($sub1_id, '>', 0, "should be positive integer");
($res, $cl_collection) = $test_machine->request_get('/api/calllists/?page=1&rows=10&subscriber_id='.$sub1_id);
is($res->code, 200, "fetch calllists collection of subscriber ($sub1_id)");
($res, $cl_collection) = $test_machine->check_item_get('/api/calllists/?page=1&rows=10&subscriber_id='.$sub1_id,"fetch calllists collection of subscriber ($sub1_id)");
($res, $cl_collection_in) = $test_machine->request_get('/api/calllists/?page=1&rows=10&direction=in&subscriber_id='.$sub1_id);
is($res->code, 200, "fetch calllists collection of subscriber ($sub1_id) with direction filter in");
($res, $cl_collection_in) = $test_machine->check_item_get('/api/calllists/?page=1&rows=10&direction=in&subscriber_id='.$sub1_id,"fetch calllists collection of subscriber ($sub1_id) with direction filter in");
($res, $cl_collection_out) = $test_machine->request_get('/api/calllists/?page=1&rows=10&direction=out&subscriber_id='.$sub1_id);
is($res->code, 200, "fetch calllists collection of subscriber ($sub1_id) with direction filter out");
($res, $cl_collection_out) = $test_machine->check_item_get('/api/calllists/?page=1&rows=10&direction=out&subscriber_id='.$sub1_id, "fetch calllists collection of subscriber ($sub1_id) with direction filter out");
is($cl_collection_in->{total_count}+$cl_collection_out->{total_count}, $cl_collection->{total_count},
"Incoming and outgoing calls should add up to total number of calls");
@ -50,8 +46,7 @@ SKIP:
if($ENV{API_FORCE_CUSTOMER_ID}) {
$cust1_id = $ENV{API_FORCE_CUSTOMER_ID};
} else {
($res, $cust1) = $test_machine->request_get('/api/customers/?page=1&rows=1');
is($res->code, 200, "fetch a customer for testing");
($res, $cust1) = $test_machine->check_item_get('/api/customers/?page=1&rows=1',"fetch a customer for testing");
if ($cust1->{total_count} < 1) {
skip("Precondition not met: need a customer",1);
}
@ -59,14 +54,11 @@ SKIP:
}
cmp_ok ($cust1_id, '>', 0, "should be positive integer");
($res, $cl_collection) = $test_machine->request_get('/api/calllists/?page=1&rows=10&customer_id='.$cust1_id);
is($res->code, 200, "fetch calllists collection of customer ($cust1_id)");
($res, $cl_collection) = $test_machine->check_item_get('/api/calllists/?page=1&rows=10&customer_id='.$cust1_id,"fetch calllists collection of customer ($cust1_id)");
($res, $cl_collection_in) = $test_machine->request_get('/api/calllists/?page=1&rows=10&direction=in&customer_id='.$cust1_id);
is($res->code, 200, "fetch calllists collection of customer ($cust1_id) with direction filter in");
($res, $cl_collection_in) = $test_machine->check_item_get('/api/calllists/?page=1&rows=10&direction=in&customer_id='.$cust1_id,"fetch calllists collection of customer ($cust1_id) with direction filter in");
($res, $cl_collection_out) = $test_machine->request_get('/api/calllists/?page=1&rows=10&direction=out&customer_id='.$cust1_id);
is($res->code, 200, "fetch calllists collection of customer ($cust1_id) with direction filter out");
($res, $cl_collection_out) = $test_machine->check_item_get('/api/calllists/?page=1&rows=10&direction=out&customer_id='.$cust1_id,"fetch calllists collection of customer ($cust1_id) with direction filter out");
is($cl_collection_in->{total_count}+$cl_collection_out->{total_count}, $cl_collection->{total_count},
"Incoming and outgoing calls should add up to total number of calls");

@ -9,15 +9,12 @@ my $test_machine = Test::Collection->new(
);
diag('Note that the next tests require at least one subscriber to be present ' .
'and accessible to the current API user.');
'and accessible to the current API user.');
# fetch a cfdestinationset for testing that
{
my ($res, $content) = $test_machine->request_get('/api/cfdestinationsets/?page=1&rows=10');
is($res->code, 200, "fetch cfdestinationsets collection");
($res, $content) = $test_machine->request_get('/api/cftimesets/?page=1&rows=10');
is($res->code, 200, "fetch cftimesets collection");
my ($res, $content) = $test_machine->check_item_get('/api/cfdestinationsets/?page=1&rows=10', "fetch cfdestinationsets collection");
($res, $content) = $test_machine->check_item_get('/api/cftimesets/?page=1&rows=10', "fetch cftimesets collection");
}
# fetch a cfdestinationset being a reseller
@ -33,11 +30,9 @@ SKIP:
}
is($res->code, 200, "fetch cfdestinationsets collection as reseller");
($res, $cft_collection1) = $test_machine->request_get('/api/cftimesets/?page=1&rows=10');
is($res->code, 200, "fetch cftimesets collection as reseller");
($res, $cft_collection1) = $test_machine->check_item_get('/api/cftimesets/?page=1&rows=10', "fetch cftimesets collection as reseller");
($res, $sub1) = $test_machine->request_get('/api/subscribers/?page=1&rows=1');
is($res->code, 200, "fetch a subscriber of our reseller for testing");
($res, $sub1) = $test_machine->check_item_get('/api/subscribers/?page=1&rows=1',"fetch a subscriber of our reseller for testing");
if ($sub1->{total_count} < 1) {
skip("Precondition not met: need a subscriber",1);
}
@ -45,16 +40,14 @@ SKIP:
cmp_ok ($sub1_id, '>', 0, "should be positive integer");
($res, $cf_collection2) = $test_machine->request_get('/api/cfdestinationsets/?page=1&rows=10&subscriber_id='.$sub1_id);
is($res->code, 200, "fetch cfdestinationsets collection as reseller with subscriber filter");
($res, $cf_collection2) = $test_machine->check_item_get('/api/cfdestinationsets/?page=1&rows=10&subscriber_id='.$sub1_id, "fetch cfdestinationsets collection as reseller with subscriber filter");
cmp_ok($cf_collection1->{total_count}, '>=', $cf_collection2->{total_count},
"filtered collection (cfdestinationsets) should be smaller or equal");
# --------
($res, $cft_collection2) = $test_machine->request_get('/api/cftimesets/?page=1&rows=10&subscriber_id='.$sub1_id);
is($res->code, 200, "fetch cftimesets collection as reseller with subscriber filter");
($res, $cft_collection2) = $test_machine->check_item_get('/api/cftimesets/?page=1&rows=10&subscriber_id='.$sub1_id, "fetch cftimesets collection as reseller with subscriber filter");
cmp_ok($cft_collection1->{total_count}, '>=', $cft_collection2->{total_count},
"filtered collection (cftimesets) should be smaller or equal");

@ -1,18 +1,13 @@
#use Sipwise::Base;
use strict;
#use Moose;
use Sipwise::Base;
use Test::Collection;
use Test::FakeData;
use Net::Domain qw(hostfqdn);
use LWP::UserAgent;
use HTTP::Request::Common;
use JSON;
use Test::More;
use Data::Dumper;
use File::Basename;
#init test_machine
my $fake_data = Test::FakeData->new;
$fake_data->set_data_from_script({
@ -25,11 +20,11 @@ $fake_data->set_data_from_script({
faxfile => [ dirname($0).'/resources/empty.txt' ],
},
'create_special'=> sub {
my ($self,$name) = @_;
my $prev_params = $self->test_machine->get_cloned('content_type');
@{$self->test_machine->content_type}{qw/POST PUT/} = (('multipart/form-data') x 2);
$self->test_machine->check_create_correct(1);
$self->test_machine->set(%$prev_params);
my ($self,$name,$test_machine) = @_;
my $prev_params = $test_machine->get_cloned('content_type');
@{$test_machine->content_type}{qw/POST PUT/} = (('multipart/form-data') x 2);
$test_machine->check_create_correct(1);
$test_machine->set(%$prev_params);
},
'no_delete_available' => 1,
},
@ -38,14 +33,23 @@ my $test_machine = Test::Collection->new(
name => 'faxes',
embedded => [qw/subscribers/]
);
$test_machine->DATA_ITEM_STORE($fake_data->process('faxes'));
{
my $uri = $test_machine->normalize_uri('/api/faxserversettings/'.$test_machine->DATA_ITEM->{json}->{subscriber_id});
my($res,$faxserversettings,$req) = $test_machine->check_item_get($uri);
$faxserversettings->{active} = 1;
$faxserversettings->{password} = 'aaa111';
$test_machine->request_put($faxserversettings,$uri);
}
@{$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->form_data_item( );
$test_machine->check_create_correct( 1 );
$test_machine->form_data_item();
$test_machine->check_create_correct( 1, sub{ my $cmd = "echo 'aaa' > $_[0]->{faxfile}->[0]";`$cmd`; } );
#$test_machine->check_bundle();
#$test_machine->check_get2put( sub { $_[0] = { json => JSON::to_json($_[0]), 'faxfile' => $test_machine->DATA_ITEM_STORE->{faxfile} }; } );

@ -69,11 +69,11 @@ $fake_data->set_data_from_script({
},
'query' => [ ['model','json','model'] ],
'create_special'=> sub {
my ($self,$name) = @_;
my $prev_params = $self->test_machine->get_cloned('content_type');
@{$self->test_machine->content_type}{qw/POST PUT/} = (('multipart/form-data') x 2);
$self->test_machine->check_create_correct(1);
$self->test_machine->set(%$prev_params);
my ($self,$collection_name,$test_machine) = @_;
my $prev_params = $test_machine->get_cloned('content_type');
@{$test_machine->content_type}{qw/POST PUT/} = (('multipart/form-data') x 2);
$test_machine->check_create_correct(1);
$test_machine->set(%$prev_params);
},
'no_delete_available' => 1,
},
@ -111,27 +111,34 @@ foreach my $type(qw/extension phone/){
$_[0]->{json}->{model} .= $type."TEST_".($_[1]->{i} + 3).'_'.$time;
$_[0]->{json}->{connactable_devices} = $connactable_devices->{ get_connectable_type( $type) }->{ids};
} );
$test_machine->check_get2put( sub { $_[0] = { json => JSON::to_json($_[0]), 'front_image' => $test_machine->DATA_ITEM_STORE->{front_image} }; } );
$test_machine->check_get2put( {
'data_cb' => sub {
$_[0] = {
'json' => JSON::to_json($_[0]),
'front_image' => $test_machine->DATA_ITEM_STORE->{front_image}
};
}
} );
$test_machine->check_bundle();
# try to create model without reseller_id
{
my ($res, $err) = $test_machine->request_post(sub{delete $_[0]->{json}->{reseller_id};});
my ($res, $err) = $test_machine->check_item_post(sub{delete $_[0]->{json}->{reseller_id};});
is($res->code, 422, "create model without reseller_id");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='reseller_id'/, "check error message in body");
}
# try to create model with empty reseller_id
{
my ($res, $err) = $test_machine->request_post(sub{$_[0]->{json}->{reseller_id} = undef;});
my ($res, $err) = $test_machine->check_item_post(sub{$_[0]->{json}->{reseller_id} = undef;});
is($res->code, 422, "create model with empty reseller_id");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='reseller_id'/, "check error message in body");
}
# try to create model with invalid reseller_id
{
my ($res, $err) = $test_machine->request_post(sub{$_[0]->{json}->{reseller_id} = 99999;});
my ($res, $err) = $test_machine->check_item_post(sub{$_[0]->{json}->{reseller_id} = 99999;});
is($res->code, 422, "create model with invalid reseller_id");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid reseller_id/, "check error message in body");

@ -24,7 +24,7 @@ $fake_data->set_data_from_script({
profile_id => sub { return shift->get_id('pbxdeviceprofiles',@_); },
customer_id => sub { return shift->get_id('customers',@_); },
identifier => 'aaaabbbbcccc',
station_name => 'api_test_vun',
station_name => 'api_test_run',
lines=>[{
linerange => 'Phone Ports api_test',
type => 'private',
@ -51,7 +51,7 @@ $test_machine->DATA_ITEM_STORE($fake_data->process('pbxdevices'));
$test_machine->form_data_item( );
# create 3 new field pbx devices from DATA_ITEM
$test_machine->check_create_correct( 3, sub{ $_[0]->{identifier} = sprintf('%x', (hex('0x'.$_[0]->{identifier}) + $_[1]->{i}) ); } );
$test_machine->check_get2put( );
$test_machine->check_get2put();
$test_machine->check_bundle();
$test_machine->clear_test_data_all();

@ -1,8 +1,6 @@
#use Sipwise::Base;
use strict;
#use Moose;
use Sipwise::Base;
use Test::Collection;
use Test::FakeData;
use Test::More;
@ -53,34 +51,31 @@ $test_machine->clear_data_created();
$test_machine->check_create_correct( 1, sub{ $_[0]->{contact} .= time().'_'.$_[1]->{i} ; } );
{
my($res, $content) = $test_machine->request_post(sub{$_[0]->{q} = 2;$_[0]->{contact} .= time().'_MT14779_1' ;});
my($res, $content) = $test_machine->check_item_post(sub{$_[0]->{q} = 2;$_[0]->{contact} .= time().'_MT14779_1' ;});
$test_machine->http_code_msg(422, "check creation of the subscriber registration with q > 1. MT#14779",$res,$content);
}
{
my($res, $content) = $test_machine->request_post(sub{$_[0]->{q} = -2;$_[0]->{contact} .= time().'_MT14779_2' ;});
my($res, $content) = $test_machine->check_item_post(sub{$_[0]->{q} = -2;$_[0]->{contact} .= time().'_MT14779_2' ;});
$test_machine->http_code_msg(422, "check creation of the subscriber registration with q < -1. MT#14779",$res,$content);
}
{
# Default value should be used.
my($res, $content) = $test_machine->request_post(sub{delete $_[0]->{q};$_[0]->{contact} .= time().'_MT14779_3';});
my($res, $content) = $test_machine->check_item_post(sub{delete $_[0]->{q};$_[0]->{contact} .= time().'_MT14779_3';});
$test_machine->http_code_msg(201, "check creation of the subscriber registration without q. MT#14779.",$res,$content);
}
{
my($res, $content) = $test_machine->request_post(sub{delete $_[0]->{expires};$_[0]->{contact} .= time().'_MT14891_1';});
my($res, $content) = $test_machine->check_item_post(sub{delete $_[0]->{expires};$_[0]->{contact} .= time().'_MT14891_1';});
$test_machine->http_code_msg(422, "check creation of the subscriber registration without required expires. MT#14891.",$res,$content);
}
#api doesn't deny extra fields
#{
# my($res, $content) = $test_machine->request_post(sub{$_[0]->{user_agent} = 'Test User Agent';$_[0]->{contact} .= time().'_MT14789' ;});
# $test_machine->http_code_msg(422, "check creation of the subscriber registration without required expires. MT#14789.",$res,$content);
# my($res, $content) = $test_machine->check_item_post(sub{$_[0]->{user_agent} = 'Test User Agent';$_[0]->{contact} .= time().'_MT14789' ;});
# $test_machine->http_code_msg(422, "check creation of the subscriber registration with already removed "user_agent". MT#14789.",$res,$content);
#}
$test_machine->clear_test_data_all();
done_testing;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,124 @@
use strict;
use Test::Collection;
use Test::FakeData;
use Test::More;
use Data::Dumper;
#use NGCP::Panel::Utils::Subscriber;
my $test_machine = Test::Collection->new(
name => 'subscribers',
);
my $fake_data = Test::FakeData->new;
$test_machine->methods->{collection}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS POST)};
$test_machine->methods->{item}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS PUT PATCH DELETE)};
$fake_data->set_data_from_script({
'subscribers' => {
'data' => {
administrative => 0,
customer_id => sub { return shift->get_id('customers',@_); },
primary_number => { ac => 111, cc=> 111, sn => 111 },
alias_numbers => [ { ac => 11, cc=> 11, sn => 11 } ],
username => 'api_test_username',
password => 'api_test_password',
webusername => 'api_test_webusername',
webpassword => undef,
domain_id => sub { return shift->get_id('domains',@_); },,
#domain_id =>
email => undef,
external_id => undef,
is_pbx_group => 1,
is_pbx_pilot => 1,
pbx_extension => '111',
pbx_group_ids => [],
pbx_groupmember_ids => [],
profile_id => sub { return shift->get_id('subscriberprofiles',@_); },
status => 'active',
pbx_hunt_policy => 'parallel',
pbx_hunt_timeout => '15',
},
'query' => ['username'],
},
});
$test_machine->DATA_ITEM_STORE($fake_data->process('subscribers'));
$test_machine->form_data_item( );
# create new subscribers from DATA_ITEM. Item is not created in the fake_data->process.
$test_machine->check_create_correct( 1, sub{ $_[0]->{username} .= time().'_'.$_[1]->{i} ; } );
##$test_machine->check_bundle();
##$test_machine->check_get2put();
#------- MT#15441
{
my $intentional_cli = '111'.time();
my $intentional_primary_number = {
'cc' => '111',
'ac' => '222',
'sn' => '123'.time(),
};
#prepare preconditions: cli should differ from primary_nuber
my $subscriber = ($test_machine->get_created_first() || $fake_data->get_existent_item($test_machine->name) || $test_machine->get_item_hal());
$subscriber->{uri} = $subscriber->{location};
my ($preferences, $preferences_put) = ({'uri' => '/api/subscriberpreferences/'.$test_machine->get_id_from_created($subscriber)}) x 2;
(undef, $preferences->{content}) = $test_machine->check_item_get($preferences->{uri});
$preferences->{content}->{cli} = $intentional_cli;
(undef, $preferences_put->{content}) = $test_machine->request_put($preferences->{content},$preferences->{uri});
is($preferences_put->{content}->{cli}, $intentional_cli, "check that cli was updated on subscriberpreferences put: $preferences_put->{content}->{cli} == $intentional_cli");
my ($subscriber_put, $subscriber_get, $preferences_get);
#1
$subscriber->{content}->{primary_number} = $intentional_primary_number;
($subscriber_put,$subscriber_get,$preferences_get) = $test_machine->put_and_get($subscriber, $preferences_put);
is($preferences_get->{content}->{cli}, $intentional_cli, "check that cli was preserved on subscriber phones update: $preferences_get->{content}->{cli} == $intentional_cli");
#/1
#2
delete $subscriber->{content}->{primary_number};
($subscriber_put,$subscriber_get,$preferences_get) = $test_machine->put_and_get($subscriber, $preferences_put);
is($preferences_get->{content}->{cli}, $intentional_cli, "check that cli was preserved on subscriber phones update: $preferences_get->{content}->{cli} == $intentional_cli");
#/2
#now prepare preferences for zero situation, when synchronization will be restarted again
delete $preferences->{content}->{cli};
(undef, $preferences_put->{content}) = $test_machine->request_put($preferences->{content},$preferences->{uri});
is($preferences_put->{content}->{cli}, undef, "check that cli was deleted on subscriberpreferences put with empty cli");
#3
$subscriber->{content}->{primary_number} = $intentional_primary_number;
($subscriber_put,$subscriber_get,$preferences_get) = $test_machine->put_and_get($subscriber, $preferences_put);
is($preferences_get->{content}->{cli}, number_as_string($intentional_primary_number), "check that cli was created on subscriber phones update: $preferences_get->{content}->{cli} == ".number_as_string($intentional_primary_number) );
#/3
$intentional_primary_number = {
'cc' => '222',
'ac' => '333',
'sn' => '444'.time(),
};
#4
$subscriber->{content}->{primary_number} = $intentional_primary_number;
($subscriber_put,$subscriber_get,$preferences_get) = $test_machine->put_and_get($subscriber, $preferences_put);
is($preferences_get->{content}->{cli}, number_as_string($intentional_primary_number), "check that cli was updated on subscriber phones update: $preferences_get->{content}->{cli} == ".number_as_string($intentional_primary_number) );
#/4
#5
delete $subscriber->{content}->{primary_number};
($subscriber_put,$subscriber_get,$preferences_get) = $test_machine->put_and_get($subscriber, $preferences_put);
is($preferences_get->{content}->{cli}, undef, "check that cli was deleted on subscriber phones update");
#/5
}
$test_machine->clear_test_data_all();
done_testing;
sub number_as_string{
my ($number_row, %params) = @_;
return 'HASH' eq ref $number_row
? $number_row->{cc} . ($number_row->{ac} // '') . $number_row->{sn}
: $number_row->cc . ($number_row->ac // '') . $number_row->sn;
}
# vim: set tabstop=4 expandtab:

@ -1,17 +1,10 @@
use strict;
use warnings;
use Net::Domain qw(hostfqdn);
use LWP::UserAgent;
use HTTP::Request::Common;
use JSON;
use Test::More;
use Data::Dumper;
use File::Basename;
use bignum qw/hex/;
use Test::Collection;
use Test::FakeData;
use Test::More;
use Data::Dumper;
#init test_machine
my $test_machine = Test::Collection->new(
@ -37,15 +30,13 @@ $fake_data->set_data_from_script({
#for item creation test purposes /post request data/
$test_machine->DATA_ITEM_STORE($fake_data->process('trustedsources'));
$test_machine->form_data_item( );
# create 3 new field pbx devices from DATA_ITEM
$test_machine->check_create_correct( 3, sub{ $_[0]->{src_ip} = sprintf('1.2.3.%d', $_[1]->{i}); } );
$test_machine->check_get2put( );
$test_machine->check_get2put();
$test_machine->check_bundle();
$test_machine->clear_test_data_all();
done_testing;
# vim: set tabstop=4 expandtab:

@ -44,20 +44,20 @@ my $voucher_uri;
{
#todo: move request processing results to separate package inside collection, to don't return these chains
my($res_post,$result_item_post,$req_post,$content_post_in,$location_post,$content_get) = $test_machine->check_post2get();
$voucher_uri = $location_post;
my($res_put,$result_item_put,$req_put,$item_put_data,$get_res,$result_item_get,$get_req) = $test_machine->check_put2get(undef, undef, $voucher_uri);
my($post,$get_post) = $test_machine->check_post2get();
$voucher_uri = $post->{location};
my($put,$get_put) = $test_machine->check_put2get( {}, {'uri' => $voucher_uri} );
$voucher = $result_item_get;
$voucher = $get_put->{content};
my($res_patch,$patch_voucher) = $test_machine->request_patch( [ { op => 'replace', path => '/code', value => $result_item_put->{code} } ], $voucher_uri );
delete $result_item_put->{_links};
delete $result_item_put->{id};
my($res_patch,$patch_voucher) = $test_machine->request_patch( [ { op => 'replace', path => '/code', value => $put->{content}->{code} } ], $voucher_uri );
delete $put->{content}->{_links};
delete $put->{content}->{id};
delete $patch_voucher->{_links};
delete $patch_voucher->{id};
is_deeply($result_item_put, $patch_voucher, "check PATCHed voucher against POSTed voucher");
is_deeply($put->{content}, $patch_voucher, "check PATCHed voucher against POSTed voucher");
my($res,$result_item,$req) = $test_machine->request_post(undef,$voucher);
my($res,$result_item,$req) = $test_machine->request_post($voucher);
$test_machine->http_code_msg(422, "POST same voucher code again", $res, $result_item);
}
{

@ -12,7 +12,7 @@ use Net::Domain qw(hostfqdn);
use URI;
use URI::Escape;
use Clone qw/clone/;
use Test::HTTPRequestAsCurl;
use Data::Dumper;
@ -21,6 +21,12 @@ has 'local_test' => (
isa => 'Str',
default => $ENV{LOCAL_TEST} // '',
);
has 'DEBUG' => (
is => 'rw',
isa => 'Bool',
default => 0,
);
has 'catalyst_config' => (
is => 'rw',
isa => 'HashRef',
@ -107,6 +113,11 @@ has 'KEEP_CREATED' =>(
isa => 'Bool',
default => 1,
);
has 'DATA_LOADED' => (
is => 'rw',
isa => 'HashRef',
default => sub{{}},
);
has 'URI_CUSTOM' =>(
is => 'rw',
isa => 'Str',
@ -188,8 +199,8 @@ sub init_ua {
my $ua = LWP::UserAgent->new;
my $uri = $self->base_uri;
$uri =~ s/^https?:\/\///;
my($user,$pass) = $self->get_role_credentials();
$ua->credentials( $uri, 'api_admin_http', $user, $pass);
my($user,$pass,$role,$realm) = $self->get_role_credentials();
$ua->credentials( $uri, $realm, $user, $pass);
$ua->ssl_opts(
verify_hostname => 0,
SSL_verify_mode => 0,
@ -201,9 +212,9 @@ sub runas {
my($role_in,$uri) = @_;
$uri //= $self->base_uri;
$uri =~ s/^https?:\/\///;
my($user,$pass,$role) = $self->get_role_credentials($role_in);
my($user,$pass,$role,$realm) = $self->get_role_credentials($role_in);
$self->runas_role($role);
$self->ua->credentials( $uri, 'api_admin_http', $user, $pass);
$self->ua->credentials( $uri, $realm, $user, $pass);
}
sub get_role_credentials{
my $self = shift;
@ -217,7 +228,8 @@ sub get_role_credentials{
$user //= $ENV{API_USER_RESELLER} // 'api_test';
$pass //= $ENV{API_PASS_RESELLER} // 'api_test';
}
return($user,$pass,$role);
my $realm = 'api_admin_http';
return($user,$pass,$role,$realm);
}
sub clear_data_created{
my($self) = @_;
@ -252,39 +264,63 @@ sub restore_uri_custom{
sub get_uri_collection{
my($self,$name) = @_;
$name //= $self->name;
return $self->base_uri."/api/".$name.($name ? "/" : "").($self->QUERY_PARAMS ? "?".$self->QUERY_PARAMS : "");
return $self->normalize_uri("/api/".$name.($name ? "/" : "").($self->QUERY_PARAMS ? "?".$self->QUERY_PARAMS : ""));
}
sub get_uri_get{
my($self,$query_string, $name) = @_;
$name //= $self->name;
return $self->base_uri."/api/".$name.($query_string ? '/?' : '/' ).$query_string;
return $self->normalize_uri("/api/".$name.($query_string ? '/?' : '/' ).$query_string);
}
sub get_uri{
my($self,$add,$name) = @_;
$add //= '';
$name //= $self->name;
return $self->base_uri."/api/".$name.'/'.$add;
return $self->normalize_uri("/api/".$name.'/'.$add);
}
sub get_uri_item{
my($self,$name,$item) = @_;
my $resuri;
$item ||= $self->get_item_hal($name);
$resuri = $self->normalize_uri('/'.$item->{location});
return $resuri;
}
sub get_uri_firstitem{
sub get_item_hal{
my($self,$name) = @_;
if(!$self->DATA_CREATED->{FIRST}){
my($res,$list_collection,$req) = $self->check_item_get($self->get_uri_collection."?page=1&rows=1");
$name ||= $self->name;
my $resitem ;
if(( $name eq $self->name ) && $self->DATA_CREATED->{FIRST}){
$resitem = $self->get_created_first;
}
if($self->DATA_LOADED->{$name} && @{$self->DATA_LOADED->{$name}}){
$resitem = $self->DATA_LOADED->{$name}->[0];
}
if(!$resitem){
my ($reshal, $location);
my($res,$list_collection,$req) = $self->check_item_get($self->get_uri_collection($name)."?page=1&rows=1");
my $hal_name = $self->get_hal_name($name);
if(ref $list_collection->{_links}->{$hal_name} eq "HASH") {
$self->DATA_CREATED->{FIRST} = $list_collection->{_links}->{$hal_name}->{href};
$reshal = $list_collection;
$location = $reshal->{_links}->{$hal_name}->{href};
} else {
$self->DATA_CREATED->{FIRST} = $list_collection->{_embedded}->{$hal_name}->[0]->{_links}->{self}->{href};
$reshal = $list_collection->{_embedded}->{$hal_name}->[0];
$location = $reshal->{_links}->{self}->{href};
}
$resitem = { num => 1, content => $reshal, res => $res, req => $req, location => $location };
$self->DATA_LOADED->{$name} ||= [];
push @{$self->DATA_LOADED->{$name}}, $resitem;
}
$self->DATA_CREATED->{FIRST} //= '';
return $self->base_uri.'/'.$self->DATA_CREATED->{FIRST};
return $resitem;
}
sub get_created_first{
my($self) = @_;
return $self->DATA_CREATED->{ALL}->{$self->DATA_CREATED->{FIRST}};
}
sub get_uri_current{
my($self) = @_;
$self->URI_CUSTOM and return $self->URI_CUSTOM;
return $self->get_uri_firstitem;
return $self->get_uri_item;
}
sub encode_content{
my($self,$content, $type) = @_;
$type //= $self->ENCODE_CONTENT;
@ -304,15 +340,25 @@ sub encode_content{
}
sub request{
my($self,$req) = @_;
#print $req->as_string;
my $credentials = {};
(@$credentials{qw/user password/},undef,undef) = $self->get_role_credentials();
my $curl = Test::HTTPRequestAsCurl::as_curl($req, credentials => $credentials );
if($self->DEBUG){
print $req->as_string;
print "$curl\n\n";
}
my $res = $self->ua->request($req);
#draft of the debug mode
#if($res->code >= 400){
# print Dumper $req;
# print Dumper $res;
# print Dumper $self->get_response_content($res);
# die;
#}
if($self->DEBUG){
if($res->code >= 400){
print Dumper $req;
print Dumper $res;
print Dumper $self->get_response_content($res);
#die;
}
}
return $res;
}
@ -326,6 +372,7 @@ sub request_process{
sub get_request_put{
my($self,$content,$uri) = @_;
$uri ||= $self->get_uri_current;
$uri = $self->normalize_uri($uri);
#This is for multipart/form-data cases
$content = $self->encode_content($content, $self->content_type->{PUT});
my $req = POST $uri,
@ -338,6 +385,7 @@ sub get_request_put{
sub get_request_patch{
my($self,$uri) = @_;
$uri ||= $self->get_uri_current;
$uri = $self->normalize_uri($uri);
my $req = HTTP::Request->new('PATCH', $uri);
$req->header('Prefer' => 'return=representation');
$req->header('Content-Type' => $self->content_type->{PATCH} );
@ -346,7 +394,7 @@ sub get_request_patch{
sub request_put{
my($self,$content,$uri) = @_;
$uri ||= $self->get_uri_current;
my $req = $self->get_request_put( $content, $uri );
my $req = $self->get_request_put( $content, $self->normalize_uri($uri) );
my $res = $self->request($req);
my $rescontent = $self->get_response_content($res);
return wantarray ? ($res,$rescontent,$req) : $res;
@ -363,36 +411,28 @@ sub request_patch{
#print Dumper [$res,$rescontent,$req];
return wantarray ? ($res,$rescontent,$req) : $res;
}
sub process_data{
my($self, $data_cb, $data_in, $data_cb_data) = @_;
my $data = $data_in || clone($self->DATA_ITEM);
defined $data_cb and $data_cb->($data, $data_cb_data);
return $data;
}
sub request_post{
my($self, $data_cb, $data_in, $data_cb_data) = @_;
my $data = $self->process_data($data_cb, $data_in, $data_cb_data);
my $content = {
$data->{json} ? ( json => JSON::to_json(delete $data->{json}) ) : (),
%$data,
};
my($self, $content, $uri, $req) = @_;
$uri ||= $self->get_uri_collection;
$uri = $self->normalize_uri($uri);
$content = $self->encode_content($content, $self->content_type->{POST} );
#form-data is set automatically, despite on $self->content_type->{POST}
my $req = POST $self->get_uri_collection,
$req ||= POST $uri,
Content_Type => $self->content_type->{POST},
Content => $content;
$req->header('Prefer' => 'return=representation');
my $res = $self->request($req);
my $rescontent = $self->get_response_content($res);
return wantarray ? ($res,$rescontent,$req,$content) : $res;
};
sub request_options{
my ($self,$uri) = @_;
# OPTIONS tests
my $req = HTTP::Request->new('OPTIONS', $self->normalize_uri($uri));
my $res = $self->request($req);
my $content = $self->get_response_content($res);
return($req,$res,$content);
return($res,$content,$req);
}
sub request_delete{
@ -417,6 +457,9 @@ sub get_response_content{
if($res->decoded_content){
eval { $content = JSON::from_json($res->decoded_content); };
}
#print "get_response_content;\n";
#print Dumper [caller];
#print Dumper $content;
return $content;
}
sub normalize_uri{
@ -430,6 +473,8 @@ sub normalize_uri{
############## end of test machine
############## start of test collection
#---------------- options, methods, hal format
sub check_options_collection{
my ($self, $uri) = @_;
# OPTIONS tests
@ -463,43 +508,6 @@ sub check_methods{
}
}
}
sub check_create_correct{
my($self, $number, $uniquizer_cb) = @_;
if(!$self->KEEP_CREATED){
$self->clear_data_created;
}
$self->DATA_CREATED->{ALL} //= {};
for(my $i = 1; $i <= $number; ++$i) {
my ($res, $content, $req) = $self->request_post( $uniquizer_cb , undef, { i => $i} );
$self->http_code_msg(201, "create test item '".$self->name."' $i",$res,$content);
my $location = $res->header('Location');
if($location){
$self->DATA_CREATED->{ALL}->{$location} = { num => $i, content => $content, res => $res, req => $req, location => $location};
$self->DATA_CREATED->{FIRST} = $location unless $self->DATA_CREATED->{FIRST};
}
}
}
sub clear_test_data_all{
my($self,$uri) = @_;
my @uris = $uri ? (('ARRAY' eq ref $uri) ? @$uri : ($uri)) : keys %{ $self->DATA_CREATED->{ALL} };
foreach my $del_uri(@uris){
my($req,$res,$content) = $self->request_delete($self->base_uri.$del_uri);
$self->http_code_msg(204, "check delete item $del_uri",$res,$content);
}
$self->clear_data_created();
}
sub clear_test_data_dependent{
my($self,$uri) = @_;
my($req,$res,$content) = $self->request_delete($self->base_uri.$uri);
return ('204' eq $res->code);
}
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");
}
}
sub check_list_collection{
my($self, $check_embedded_cb) = @_;
@ -508,7 +516,7 @@ sub check_list_collection{
do {
#print "nexturi=$nexturi;\n";
my ($res,$list_collection) = $self->check_item_get($nexturi);
my $selfuri = $self->base_uri . $list_collection->{_links}->{self}->{href};
my $selfuri = $self->normalize_uri($list_collection->{_links}->{self}->{href});
is($selfuri, $nexturi, "check _links.self.href of collection");
my $colluri = URI->new($selfuri);
@ -531,7 +539,7 @@ sub check_list_collection{
}
if($list_collection->{_links}->{next}->{href}) {
$nexturi = $self->base_uri . $list_collection->{_links}->{next}->{href};
$nexturi = $self->normalize_uri($list_collection->{_links}->{next}->{href});
} else {
$nexturi = undef;
}
@ -575,17 +583,18 @@ sub check_created_listed{
}
}
sub check_item_get{
my($self,$uri) = @_;
$uri ||= $self->get_uri_current;
my $req = HTTP::Request->new('GET', $uri);
my $res = $self->request($req);
#print Dumper $res;
$self->http_code_msg(200, "fetch uri: $uri", $res);
my $content = $self->get_response_content($res);
return wantarray ? ($res, $content, $req) : $res;
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");
}
}
#------------------- put bundle -----------
#------------------- put bundle -----------
sub check_put_content_type_empty{
my($self) = @_;
# check if it fails without content type
@ -625,58 +634,6 @@ sub check_put_body_empty{
$self->http_code_msg(400, "check put no body", $res, $content);
}
sub check_get2put{
my($self, $put_data_cb, $uri) = @_;
#$req->remove_header('Prefer');
#$req->header('Prefer' => "return=representation");
# PUT same result again
my ($res_get, $result_item_get, $req_get) = $self->check_item_get($uri);
my $item_put_data = clone($result_item_get);
delete $item_put_data->{_links};
delete $item_put_data->{_embedded};
# check if put is ok
(defined $put_data_cb) and $put_data_cb->($item_put_data);
my ($res_put,$result_item_put,$req_put) = $self->request_put( $item_put_data, $uri );
$self->http_code_msg(200, "check_get2put: check put successful",$res_put, $result_item_put);
is_deeply($result_item_get, $result_item_put, "check_get2put: check put if unmodified put returns the same");
return ($res_put,$result_item_put,$req_put,$item_put_data);
}
sub check_put2get{
my($self, $put_data_in, $put_data_cb, $uri) = @_;
my $item_put_data = $self->process_data($put_data_cb, $put_data_in);
$item_put_data = JSON::to_json($item_put_data);
my ($res_put,$result_item_put,$req_put) = $self->request_put( $item_put_data, $uri );
$self->http_code_msg(200, "check_put2get: check put successful",$res_put, $result_item_put);
my ($res_get, $result_item_get, $req_get) = $self->check_item_get($uri);
delete $result_item_get->{_links};
delete $result_item_get->{_embedded};
my $item_id = delete $result_item_get->{id};
$item_put_data = JSON::from_json($item_put_data);
is_deeply($item_put_data, $result_item_get, "check_put2get: check PUTed item against POSTed item");
$result_item_get->{id} = $item_id;
return ($res_put,$result_item_put,$req_put,$item_put_data,$res_get, $result_item_get, $req_get);
}
sub check_post2get{
my($self, $post_data_in, $post_data_cb) = @_;
my ($res_post, $result_item_post, $req_post, $item_post_data ) = $self->request_post( $post_data_cb, $post_data_in );
$self->http_code_msg(201, "check_post2get: POST item '".$self->name."' for check_post2get", $res_post, $result_item_post);
my $location_post = $self->base_uri.($res_post->header('Location') // '');
my ($res_get, $result_item_get, $req_get) = $self->request_get( $location_post );
$self->http_code_msg(200, "check_post2get: fetch POSTed test '".$self->name."'", $res_get, $result_item_get);
delete $result_item_get->{_links};
my $item_id = delete $result_item_get->{id};
$item_post_data = JSON::from_json($item_post_data);
is_deeply($item_post_data, $result_item_get, "check_post2get: check POSTed '".$self->name."' against fetched");
$result_item_get->{id} = $item_id;
return ($res_post,$result_item_post,$req_post,$item_post_data, $location_post, $result_item_get);
}
sub check_put_bundle{
my($self) = @_;
$self->check_put_content_type_empty;
@ -684,6 +641,8 @@ sub check_put_bundle{
$self->check_put_prefer_wrong;
$self->check_put_body_empty;
}
#-------------------- patch bundle -------
sub check_patch_correct{
my($self,$content) = @_;
my ($res,$rescontent,$req) = $self->request_patch( $content );
@ -799,7 +758,161 @@ sub check_bundle{
}
}
}
#utils
sub check_item_get{
my($self, $uri, $msg) = @_;
$msg //= '';
$uri ||= $self->get_uri_current;
$uri = $self->normalize_uri($uri);
my ($res, $content, $req) = $self->request_get($uri);
$self->http_code_msg(200, $msg.($msg?": ":"")."fetch uri: $uri", $res);
return wantarray ? ($res, $content, $req) : $res;
}
sub process_data{
my($self, $data_cb, $data_in, $data_cb_data) = @_;
my $data = $data_in || clone($self->DATA_ITEM);
defined $data_cb and $data_cb->($data, $data_cb_data);
return $data;
}
sub get_item_post_content{
my($self, $data_cb, $data_in, $data_cb_data) = @_;
my $data = $self->process_data($data_cb, $data_in, $data_cb_data);
#print Dumper $data;
my $content = {
$data->{json} ? ( json => JSON::to_json(delete $data->{json}) ) : (),
%$data,
};
return $content;
}
sub check_item_post{
my($self, $data_cb, $data_in, $data_cb_data) = @_;
my $content = $self->get_item_post_content($data_cb, $data_in, $data_cb_data);
my ($res,$rescontent,$req) = $self->request_post($content);#,$uri,$req
return wantarray ? ($res,$rescontent,$req,$content) : $res;
};
sub check_create_correct{
my($self, $number, $uniquizer_cb) = @_;
if(!$self->KEEP_CREATED){
$self->clear_data_created;
}
$self->DATA_CREATED->{ALL} //= {};
for(my $i = 1; $i <= $number; ++$i) {
my ($res, $content, $req) = $self->check_item_post( $uniquizer_cb , undef, { i => $i } );
$self->http_code_msg(201, "create test item '".$self->name."' $i",$res,$content);
my $location = $res->header('Location');
if($location){
#some interfaces (e.g. subscribers) don't provide hal after creation - is it correct, by the way?
if(!$content){
my($res_get,$content_get,$content_req) = $self->check_item_get($location,"no object returned after POST");
if($content_get){
$content = $content_get;
}
}
$self->DATA_CREATED->{ALL}->{$location} = { num => $i, content => $content, res => $res, req => $req, location => $location};
$self->DATA_CREATED->{FIRST} = $location unless $self->DATA_CREATED->{FIRST};
}
}
}
sub clear_test_data_all{
my($self,$uri) = @_;
my @uris = $uri ? (('ARRAY' eq ref $uri) ? @$uri : ($uri)) : keys %{ $self->DATA_CREATED->{ALL} };
foreach my $del_uri(@uris){
$del_uri = $self->normalize_uri($del_uri);
my($req,$res,$content) = $self->request_delete($del_uri);
$self->http_code_msg(204, "check delete item $del_uri",$res,$content);
}
$self->clear_data_created();
}
sub clear_test_data_dependent{
my($self,$uri) = @_;
my($req,$res,$content) = $self->request_delete($self->normalize_uri($uri));
return ('204' eq $res->code);
}
sub check_get2put{
my($self, $put_in, $get_in) = @_;
my($put_out,$get_out);
$get_in //= {};
$get_in->{uri} //= $put_in->{uri};
$put_in->{uri} //= $get_in->{uri};
@$get_out{qw/response content request/} = $self->check_item_get($get_in->{uri});
$put_out->{content_in} = clone($get_out->{content});
delete $put_out->{content_in}->{_links};
delete $put_out->{content_in}->{_embedded};
# check if put is ok
(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");
return ($put_out,$get_out);
}
sub check_put2get{
my($self, $put_in, $get_in) = @_;
my($put_out,$get_out);
$get_in //= {};
$get_in->{uri} //= $put_in->{uri};
$put_in->{uri} //= $get_in->{uri};
$get_out->{uri} = $get_in->{uri};
$put_out->{content_in} = $self->process_data($put_in->{data_cb}, $put_in->{data_in});
$put_out->{content_in} = JSON::to_json($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_put2get: check put successful",$put_out->{response}, $put_out->{content});
@{$get_out}{qw/response content request/} = $self->check_item_get($get_out->{uri});
delete $get_out->{content}->{_links};
delete $get_out->{content}->{_embedded};
my $item_id = delete $get_out->{content}->{id};
$put_out->{content_in} = JSON::from_json($put_out->{content_in});
is_deeply($put_out->{content_in}, $get_out->{content}, "check_put2get: check PUTed item against POSTed item");
$get_out->{content}->{id} = $item_id;
return ($put_out,$get_out);
}
sub check_post2get{
my($self, $post_in, $get_in) = @_;
$get_in //= {};
#$post = {data_in=>,data_cb=>};
#$get = {uri=>}
#return
#$post={response,content,request,data,location}
#$get=={response,content,request,uri}
my($post_out,$get_out);
@{$post_out}{qw/response content request data/} = $self->check_item_post( $post_in->{data_cb}, $post_in->{data_in} );
$self->http_code_msg(201, "check_post2get: POST item '".$self->name."' for check_post2get", @{$post_out}{qw/response content/});
$post_out->{location} = $self->normalize_uri(($post_out->{response}->header('Location') // ''));
$get_out->{uri} = $get_in->{uri} // $post_out->{location};
@{$get_out}{qw/response content request/} = $self->check_item_get( $get_out->{uri}, "check_post2get: fetch POSTed test '".$self->name."'" );
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");
$get_out->{content}->{id} = $item_id;
return ($post_out, $get_out);
}
sub put_and_get{
my($self, $put_in, $get_in) = @_;
my($put_out,$put_get_out,$get_out);
@{$put_out}{qw/response content request/} = $self->request_put($put_in->{content},$put_in->{uri});
@{$put_get_out}{qw/response content request/} = $self->check_item_get($put_in->{uri});
@{$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");
return ($put_out,$put_get_out,$get_out);
}
####--------------------------utils
sub hash2params{
my($self,$hash) = @_;
return join '&', map {$_.'='.uri_escape($hash->{$_})} keys %{ $hash };

@ -229,32 +229,6 @@ sub build_data{
'no_delete_available' => 1,
'dependency_requires_recreation' => ['resellers'],
},
'subscribers' => {
'data' => {
administrative => 0,
customer_id => sub { return shift->get_id('customers',@_); },
primary_number => { ac => 111, cc=> 111, sn => 111 },
alias_numbers => [ { ac => 11, cc=> 11, sn => 11 } ],
username => 'api_test_username',
password => 'api_test_password',
webusername => 'api_test_webusername',
webpassword => undef,
domain_id => sub { return shift->get_id('domains',@_); },,
#domain_id =>
email => undef,
external_id => undef,
is_pbx_group => 1,
is_pbx_pilot => 1,
pbx_extension => '111',
pbx_group_ids => [],
pbx_groupmember_ids => [],
profile_id => sub { return shift->get_id('subscriberprofiles',@_); },
status => 'active',
pbx_hunt_policy => 'parallel',
pbx_hunt_timeout => '15',
},
'query' => ['username'],
},
'domains' => {
'data' => {
domain => 'api_test_domain.api_test_domain',
@ -286,12 +260,12 @@ sub build_data{
},
'query' => ['version'],
'create_special'=> sub {
my ($self,$collection_name) = @_;
my $prev_params = $self->test_machine->get_cloned('content_type','QUERY_PARAMS');
$self->test_machine->content_type->{POST} = $self->data->{$collection_name}->{data}->{content_type};
$self->test_machine->QUERY_PARAMS($self->test_machine->hash2params($self->data->{$collection_name}->{data}));
$self->test_machine->check_create_correct(1, sub {return 'test_api_empty_config';} );
$self->test_machine->set(%$prev_params);
my ($self,$collection_name,$test_machine) = @_;
my $prev_params = $test_machine->get_cloned('content_type','QUERY_PARAMS');
$test_machine->content_type->{POST} = $self->data->{$collection_name}->{data}->{content_type};
$test_machine->QUERY_PARAMS($test_machine->hash2params($self->data->{$collection_name}->{data}));
$test_machine->check_create_correct(1, sub {return 'test_api_empty_config';} );
$test_machine->set(%$prev_params);
},
'no_delete_available' => 1,
},
@ -345,11 +319,15 @@ sub load_db{
sub clear_db{
my($self,$data,$order_array,$collections_slice) = @_;
$data ||= $self->data;
$order_array //= [qw/contracts systemcontacts customercontacts/];
my $order_hash = {};
$collections_slice //= [keys %$data];
@$order_hash{(keys %$data)} = (0) x @$collections_slice;
@$order_hash{@$order_array} = (1..$#$order_array+1);
#print "clear_db;\n";
#print Dumper $collections_slice;
#print Dumper [caller];
foreach my $collection_name (sort {$order_hash->{$a} <=> $order_hash->{$b}} @$collections_slice ){
if(!$data->{$collection_name}->{query}){
next;
@ -360,7 +338,7 @@ sub clear_db{
$values = ('HASH' eq ref $values) ? [$values] : $values;
my @locations = map {$_->{href}} @$values;
if($data->{$collection_name}->{no_delete_available}){
@{$self->undeletable->{@locations}} = ($collection_name) x @locations;
@{$self->undeletable}{@locations} = ($collection_name) x @locations;
}else{
if($data->{$collection_name}->{delete_potentially_dependent}){
#no checking of deletion success will be done for items which may depend on not deletable ones
@ -372,6 +350,7 @@ sub clear_db{
}else{
$self->test_machine->clear_test_data_all([ @locations ]);
}
$self->clear_cached_data($collection_name);
}
}
}
@ -399,15 +378,19 @@ sub search_item{
$field_name.'='.uri_escape($search_value);
} @{$item->{query}}
);
my $name_prev = $self->test_machine->{name};
$self->test_machine->name($collection_name);
my($res, $content, $req) = $self->test_machine->check_item_get($self->test_machine->get_uri_get($query_string));
$name_prev and $self->test_machine->name($name_prev);
my($res, $content, $req) = $self->test_machine->check_item_get($self->test_machine->get_uri_get($query_string,$collection_name));
#time for memoize?
$self->searched->{$collection_name} = [$res, $content, $req];
return ($res, $content, $req);
}
sub clear_cached_data{
my($self, @collections) = @_;
#print Dumper [@collections];
delete @{$self->loaded}{@collections};
delete @{$self->created}{@collections};
delete @{$self->searched}{@collections};
}
sub set_data_from_script{
my($self, $data_in) = @_;
while (my($collection_name,$collection_data) = each %$data_in ){
@ -445,30 +428,16 @@ sub load_data_from_script{
}
}
sub process{
my($self, $collection_name, $parents_in) = @_;
$self->load_collection_data($collection_name);
$parents_in //= {};
my $parents = {%{$parents_in}};#copy
$parents->{$collection_name}->[0] //= scalar values %$parents_in;
while (my @keys_and_value = reach($self->data->{$collection_name}->{data})){
my $field_value = pop @keys_and_value;
if('CODE' eq ref $field_value ){
$parents->{$collection_name}->[1] = [@keys_and_value];
my $value = $field_value->($self,$parents,[@keys_and_value]);
#here we don't use get/set _collection_data_fields - we should refer directly to the {json}, if we have {json}
nest( $self->data->{$collection_name}->{data}, @keys_and_value, $value );
}
}
return $self->data->{$collection_name}->{data};
}
sub load_collection_data{
my($self, $collection_name) = @_;
#print "load_collection_data: $collection_name;\n";
#print Dumper [caller];
if(!$self->data->{$collection_name}){
$self->load_data_from_script($collection_name);
}
if(!$self->collection_id_exists($collection_name) ){
if(! ( $self->collection_id_exists($collection_name) ) ){
$self->clear_db(undef,undef,[$collection_name]);
#print "load_collection_data: $collection_name: collection_id_exists=".$self->collection_id_exists($collection_name)."; loaded:".($self->loaded->{$collection_name}?$self->loaded->{$collection_name}:"undef")."; created: ".($self->created->{$collection_name}?$self->created->{$collection_name}:"undef")." searched: ".($self->searched->{$collection_name}?$self->searched->{$collection_name}:"undef")." ;\n";
$self->load_db(undef,[$collection_name]);
}
}
@ -476,19 +445,67 @@ sub get_id{
my $self = shift;
#my( $collection_name, $parents_in, $params) = @_;
my( $collection_name ) = @_;
#print "get_id: $collection_name;\n";
#print Dumper [caller];
$self->load_collection_data($collection_name);
my $res_id;
if( $self->collection_id_exists($collection_name) ){
return $self->get_existent_id($collection_name);
$res_id = $self->get_existent_id($collection_name);
#print "get_id: $collection_name: id exists: $res_id ;\n";
}else{
$res_id = $self->create(@_);
#print "get_id: $collection_name: create: $res_id ;\n";
}
return $self->create(@_);
#print "get_id end: $collection_name:$res_id ;\n";
#print Dumper $collection_name;
#print Dumper [caller];
return $res_id;
}
sub get_existent_item{
my($self, $collection_name) = @_;
my $item = $self->created->{$collection_name}->[0]
|| $self->loaded->{$collection_name}->[0];
return $item
}
sub get_existent_id{
my($self, $collection_name) = @_;
my $id = $self->test_machine->get_id_from_created($self->created->{$collection_name}->[0])
|| $self->test_machine->get_id_from_created($self->loaded->{$collection_name}->[0]);
return $id
}
sub collection_id_exists{
my($self, $collection_name) = @_;
return (exists $self->loaded->{$collection_name}) || ( exists $self->created->{$collection_name});
}
sub set_collection_data_fields{
my($self, $collection_name, $fields) = @_;
@{ $self->data->{$collection_name}->{data}->{json} || $self->data->{$collection_name}->{data} }{keys %$fields} = values %$fields;
}
sub get_collection_data_fields{
my($self, $collection_name, @fields ) = @_;
my $data = $self->data->{$collection_name}->{data}->{json} || $self->data->{$collection_name}->{data};
my %res = map { $_ => $data->{$_} } @fields;
return wantarray ? %res : ( values %res )[0];
}
sub process{
my($self, $collection_name, $parents_in) = @_;
#print "process: $collection_name;\n";
#print Dumper [caller];
$self->load_collection_data($collection_name);
$parents_in //= {};
my $parents = {%{$parents_in}};#copy
$parents->{$collection_name}->[0] //= scalar values %$parents_in;
while (my @keys_and_value = reach($self->data->{$collection_name}->{data})){
my $field_value = pop @keys_and_value;
if('CODE' eq ref $field_value ){
$parents->{$collection_name}->[1] = [@keys_and_value];
my $value = $field_value->($self,$parents,[@keys_and_value]);
#here we don't use get/set _collection_data_fields - we should refer directly to the {json}, if we have {json}
nest( $self->data->{$collection_name}->{data}, @keys_and_value, $value );
}
}
return $self->data->{$collection_name}->{data};
}
sub create{
my($self, $collection_name, $parents_in, $params) = @_;
@ -505,18 +522,24 @@ sub create{
$self->process($collection_name, $parents_in);
#create itself
my $data = clone($self->data->{$collection_name}->{data});
$self->test_machine->set(
my $test_machine = clone $self->test_machine;
$test_machine->set(
name => $collection_name,
DATA_ITEM => $data,
);
if(exists $self->data->{$collection_name}->{create_special} && 'CODE' eq ref $self->data->{$collection_name}->{create_special}){
$self->data->{$collection_name}->{create_special}->($self,$collection_name);
$self->data->{$collection_name}->{create_special}->($self,$collection_name,$test_machine);
}else{
$self->test_machine->check_create_correct(1);
$test_machine->check_create_correct(1);
}
$self->created->{$collection_name} = [values %{$self->test_machine->DATA_CREATED->{ALL}}];
$self->created->{$collection_name} = [values %{$test_machine->DATA_CREATED->{ALL}}];
if($self->data->{$collection_name}->{process_cycled}){
undef $test_machine;
#parents is a flat description of the dependency hierarchy
#parent is just a collection which requires id of the current collection in its data
#parents = { $parent_collection_name => [ $number_of_parents_levels_before, [ @nested_keys in collection to set this collection value]] }
#so, last_parent is just a collection, which directly requires current collection item id
my $parents_cycled = $self->data->{$collection_name}->{process_cycled}->{parents};
my $last_parent = ( sort { $parents_cycled->{$b}->[0] <=> $parents_cycled->{$a}->[0] } keys %{$parents_cycled} )[0];
if(grep {$collection_name} @{$self->data->{$last_parent}->{dependency_requires_recreation}} ){
@ -528,11 +551,12 @@ sub create{
#so all we need - update "created" field for further get_existent_id, which will be aclled on exit from this "create" function
$self->create($last_parent,{%parents_temp} );
}else{
my $uri = $self->test_machine->get_uri_collection($last_parent).$self->get_existent_id($last_parent);
$self->test_machine->request_patch([ {
op => 'replace',
path => join('/',('',@{$parents_cycled->{$last_parent}->[1]})),
value => $self->get_existent_id($collection_name) } ],
my $uri = $test_machine->get_uri_collection($last_parent).$self->get_existent_id($last_parent);
$test_machine->request_patch([ {
op => 'replace',
path => join('/',('',@{$parents_cycled->{$last_parent}->[1]})),
value => $self->get_existent_id($collection_name) }
],
$uri
);
}
@ -540,20 +564,6 @@ sub create{
}
return $self->get_existent_id($collection_name);
}
sub set_collection_data_fields{
my($self, $collection_name, $fields) = @_;
@{ $self->data->{$collection_name}->{data}->{json} || $self->data->{$collection_name}->{data} }{keys %$fields} = values %$fields;
}
sub get_collection_data_fields{
my($self, $collection_name, @fields ) = @_;
my $data = $self->data->{$collection_name}->{data}->{json} || $self->data->{$collection_name}->{data};
my %res = map { $_ => $data->{$_} } @fields;
return wantarray ? %res : ( values %res )[0];
}
sub collection_id_exists{
my($self, $collection_name) = @_;
return exists $self->loaded->{$collection_name} || exists $self->created->{$collection_name}
}
sub DEMOLISH{
my($self) = @_;
( 'ARRAY' eq ref $self->created ) and ( $self->test_machine->clear_test_data_all([ map {$_->{location}} @$self->created ]) );
@ -561,7 +571,6 @@ sub DEMOLISH{
print "We have test items, which can't delete through API:\n";
print Dumper [ sort { $a cmp $b } keys %{$self->undeletable} ];
}
}
1;
__END__

@ -0,0 +1,138 @@
package Test::HTTPRequestAsCurl;
use 5.008005;
use strict;
use warnings;
our $VERSION = "0.03";
use Carp;
use String::ShellQuote qw/ shell_quote /;
#use Win32::ShellQuote qw/ cmd_escape /;
use Exporter::Shiny qw/ as_curl /;
sub as_curl {
my ($request, %params) = @_;
my $content = $request->content;
my @data = split '&', $content;
my $method = $request->method;
my $uri = $request->uri;
my $headers = $request->headers;
my $user = $headers->authorization_basic || ( $params{credentials} ? join(':', @{$params{credentials}}{qw/user password/}) : '');
#my @h = grep { $_ !~ /(authorization|content-length|content-type)/i }
my @h = grep { $_ !~ /(authorization|content-length)/i }
$headers->header_field_names;
my @cmd = (["curl"]);
push(@cmd, ["--request", $method, $uri]);
push(@cmd, ["--dump-header", "-"]);
push(@cmd, ["--insecure"]) if $user;
push(@cmd, ["--user", $user]) if $user;
push(@cmd, ["--header", "$_: " . $headers->header($_)]) for sort @h;
push(@cmd, ["--data", $_]) for sort @data;
#return map { @$_ } @cmd unless keys %params;
return _make_it_pretty(\@cmd, %params);
}
sub _make_it_pretty {
my ($cmd, %params) = @_;
$params{shell} = $params{shell} || _default_shell_escape();
#$params{newline} = $params{newline} || "\\ \n";
$params{newline} = $params{newline} || "";
my $string;
for my $part (@$cmd) {
#if ($params{shell} eq 'win32') {
# $string .= cmd_escape join " ", @$part;
# $string .= ' ^' . $params{newline};
#}
#els
if ($params{shell} eq 'bourne') {
$string .= shell_quote @$part;
$string .= ' ' . $params{newline};
}
else {
croak "this shell is not currently supported: $params{shell}";
}
}
return $string;
}
sub _default_shell_escape { $^O eq 'MSWin32' ? 'win32' : 'bourne' }
1;
__END__
=encoding utf-8
=head1 NAME
HTTP::Request::AsCurl - Generate a curl command from an HTTP::Request object.
=head1 SYNOPSIS
use HTTP::Request::Common;
use HTTP::Request::AsCurl qw/as_curl/;
my $request = POST('api.earth.defense/weapon1', {
target => 'mothership',
when => 'now'
});
system as_curl($request);
print as_curl($request, pretty => 1, newline => "\n", shell => 'bourne');
# curl \
# --request POST api.earth.defense/weapon1 \
# --dump-header - \
# --data target=mothership \
# --data when=now
=head1 DESCRIPTION
This module converts an HTTP::Request object to a curl command. It can be used
for debugging REST APIs.
It handles headers and basic authentication.
=head1 METHODS
=head2 as_curl($request, %params)
Accepts an HTTP::Request object and converts it to a curl command. If there
are no C<%params>, C<as_curl()> returns the cmd as an array suitable for being
passed to system().
If there are C<%params>, C<as_curl()> returns a formatted string. The string's
format defaults to using "\n" for newlines and escaping the curl command using
bourne shell rules unless you are on a win32 system in which case it defaults
to using win32 cmd.exe escaping rules.
Available params are as follows
newline: defaults to "\n"
shell: currently available options are 'bourne' and 'win32'
=head1 LICENSE
Copyright (C) Eric Johnson.
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=head1 AUTHOR
Eric Johnson E<lt>eric.git@iijo.orgE<gt>
=cut
Loading…
Cancel
Save