MT#11147 Create fake data for the devicemodels and pbxdevices tests

Change-Id: I782bd536fe6dc18d35857519f463329fba0c9da2
changes/90/1490/6
Irina Peshinskaya 11 years ago
parent a35314d0aa
commit 11979e04ad

@ -37,6 +37,17 @@ class_has 'query_params' => (
second => sub {},
},
},
{
param => 'handle',
description => 'Filter for billing profiles with a specific handle',
query => {
first => sub {
my $q = shift;
{ handle => { like => $q } };
},
second => sub {},
},
},
]},
);

@ -49,6 +49,17 @@ class_has 'query_params' => (
second => sub {},
},
},
{
param => 'external_id',
description => 'Filter for contracts with a specific external id',
query => {
first => sub {
my $q = shift;
{ 'me.external_id' => { like => $q } };
},
second => sub {},
},
},
]},
);

@ -38,6 +38,17 @@ class_has 'query_params' => (
second => sub {},
},
},
{
param => 'name',
description => 'Filter for profile sets with a specific name',
query => {
first => sub {
my $q = shift;
{ 'me.name' => { like => $q } };
},
second => sub {},
},
},
]},
);

@ -38,6 +38,17 @@ class_has 'query_params' => (
second => sub {},
},
},
{
param => 'name',
description => 'Filter for profile with a specific name',
query => {
first => sub {
my $q = shift;
{ 'me.name' => { like => $q } };
},
second => sub {},
},
},
]},
);

@ -1,4 +1,6 @@
package NGCP::Panel::Utils::Test::Collection;
package Test::Collection;
#later package should be split into 2: apiclient and testcollection
#testcollection will keep object of the apiclient
use strict;
use Test::More;
@ -8,6 +10,7 @@ use LWP::UserAgent;
use HTTP::Request::Common;
use Net::Domain qw(hostfqdn);
use URI;
use URI::Escape;
use Clone qw/clone/;
use Data::Dumper;
@ -22,7 +25,7 @@ has 'base_uri' => (
is => 'ro',
isa => 'Str',
default => $ENV{CATALYST_SERVER} || ('https://'.hostfqdn.':4443'),
);
);
has 'name' => (
is => 'rw',
isa => 'Str',
@ -37,11 +40,11 @@ has 'methods' => (
isa => 'HashRef',
default => sub { {
'collection' =>{
'all' => {map {$_ => 1} qw/GET HEAD OPTIONS POST/},
'all' => {map {$_ => 1} qw/GET HEAD OPTIONS POST/},
'allowed' => {map {$_ => 1} qw/GET HEAD OPTIONS POST/}, #some default
},
'item' =>{
'all' => {map {$_ => 1} qw/GET HEAD OPTIONS PUT PATCH POST DELETE/},
'all' => {map {$_ => 1} qw/GET HEAD OPTIONS PUT PATCH POST DELETE/},
'allowed' => {map {$_ => 1} qw/GET HEAD OPTIONS PUT PATCH DELETE/}, #some default
},
} },
@ -76,10 +79,19 @@ has 'DATA_CREATED' => (
isa => 'HashRef',
builder => 'clear_data_created',
);
has 'KEEP_CREATED' =>(
is => 'rw',
isa => 'Bool',
);
has 'URI_CUSTOM' =>(
is => 'rw',
isa => 'Str',
);
has 'QUERY_PARAMS' =>(
is => 'rw',
isa => 'Str',
default => '',
);
has 'URI_CUSTOM_STORE' =>(
is => 'rw',
isa => 'Str',
@ -99,9 +111,28 @@ has 'ENCODE_CONTENT' => (
isa => 'Str',
default => 'json',
);
sub set{
my $self = shift;
my %params = @_;
my $prev_state = {};
while (my ($variable, $value) = each %params){
$prev_state->{$variable} = $self->$variable;
$self->$variable($value);
}
return $prev_state;
}
sub get_cloned{
my $self = shift;
my @params = @_;
my $state = {};
foreach my $variable (@params){
$state->{$variable} = clone( $self->$variable );
}
return $state;
}
sub _init_ua {
my $self = shift;
my $valid_ssl_client_cert = $ENV{API_SSL_CLIENT_CERT} ||
my $valid_ssl_client_cert = $ENV{API_SSL_CLIENT_CERT} ||
"/etc/ngcp-panel/api_ssl/NGCP-API-client-certificate.pem";
my $valid_ssl_client_key = $ENV{API_SSL_CLIENT_KEY} ||
$valid_ssl_client_cert;
@ -118,7 +149,7 @@ sub _init_ua {
# SSL_verify_mode => 0x00,
#);
return $ua;
};
}
sub clear_data_created{
my($self) = @_;
$self->DATA_CREATED({
@ -133,6 +164,12 @@ sub form_data_item{
(defined $data_cb) and $data_cb->($self->DATA_ITEM,$data_cb_data);
return $self->DATA_ITEM;
}
sub get_id_from_created{
my($self, $created_info) = @_;
my $id = $created_info->{location} || '';
$id=~s/.*?\D(\d+)$/$1/gis;
return $id;
}
sub get_hal_name{
my($self) = @_;
return "ngcp:".$self->name;
@ -143,8 +180,13 @@ sub restore_uri_custom{
$self->URI_CUSTOM_STORE(undef);
}
sub get_uri_collection{
my($self) = @_;
return $self->base_uri."/api/".$self->name.($self->name ? "/" : "");
my($self,$name) = @_;
$name //= $self->name;
return $self->base_uri."/api/".$name.($name ? "/" : "").($self->QUERY_PARAMS ? "?".$self->QUERY_PARAMS : "");
}
sub get_uri_get{
my($self,$query_string) = @_;
return $self->base_uri."/api/".$self->name.'/?'.$query_string;
}
sub get_uri_firstitem{
my($self) = @_;
@ -174,17 +216,18 @@ sub encode_content{
'application/json-patch+json' => 1,
'json' => 1,
);
#print "content=$content;\n\n";
#print "1. content=$content;\n\n";
if($content){
if( $json_types{$type} && (('HASH' eq ref $content) ||('ARRAY' eq ref $content)) ){
return JSON::to_json($content);
}
}
#print "2. content=$content;\n\n";
return $content;
}
sub request{
my($self,$req) = @_;
#print $req->as_string;
#print $req->as_string;
$self->ua->request($req);
}
@ -193,8 +236,8 @@ sub get_request_put{
$uri ||= $self->get_uri_current;
#This is for multipart/form-data cases
$content = $self->encode_content($content, $self->content_type->{PUT});
my $req = POST $uri,
Content_Type => $self->content_type->{POST},
my $req = POST $uri,
Content_Type => $self->content_type->{POST},
$content ? ( Content => $content ) : ();
$req->method('PUT');
$req->header('Prefer' => 'return=representation');
@ -214,21 +257,21 @@ sub request_put{
my $req = $self->get_request_put( $content, $uri );
my $res = $self->request($req);
#print Dumper $res;
my $err = $res->decoded_content ? JSON::from_json($res->decoded_content) : '';
return wantarray ? ($res,$err,$req) : $res;
my $rescontent = $res->decoded_content ? JSON::from_json($res->decoded_content) : '';
return wantarray ? ($res,$rescontent,$req) : $res;
}
sub request_patch{
my($self,$content,$uri, $req) = @_;
my($self,$content, $uri, $req) = @_;
$uri ||= $self->get_uri_current;
$req ||= $self->get_request_patch($uri);
#patch is always a json
$content = $self->encode_content($content, $self->content_type->{PATCH});
$content and $req->content($content);
my $res = $self->request($req);
my $err = $res->decoded_content ? JSON::from_json($res->decoded_content) : '';
#print Dumper [$res,$err,$req];
return ($res,$err,$req);
my $rescontent = $res->decoded_content ? JSON::from_json($res->decoded_content) : '';
#print Dumper [$res,$rescontent,$req];
return wantarray ? ($res,$rescontent,$req) : $res;
}
sub request_post{
@ -241,16 +284,14 @@ sub request_post{
};
$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,
Content_Type => $self->content_type->{POST},
my $req = POST $self->get_uri_collection,
Content_Type => $self->content_type->{POST},
Content => $content;
my $res = $self->request($req);
my $err = $res->decoded_content ? JSON::from_json($res->decoded_content) : '';
return ($res,$err,$req);
my $rescontent = $res->decoded_content ? JSON::from_json($res->decoded_content) : '';
return wantarray ? ($res,$rescontent,$req) : $res;
};
sub request_options{
my ($self,$uri) = @_;
# OPTIONS tests
@ -260,6 +301,7 @@ sub request_options{
my $content = $res->decoded_content ? JSON::from_json($res->decoded_content) : '';
return($req,$res,$content);
}
sub request_delete{
my ($self,$uri) = @_;
# DELETE tests
@ -269,10 +311,15 @@ sub request_delete{
my $content = $res->decoded_content ? JSON::from_json($res->decoded_content) : '';
return($req,$res,$content);
}
############## end of test machine
############## start of test collection
sub check_options_collection{
my ($self) = @_;
my ($self, $uri) = @_;
# OPTIONS tests
my $req = HTTP::Request->new('OPTIONS', $self->get_uri_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");
$self->check_methods($res,'collection');
@ -297,34 +344,39 @@ sub check_methods{
ok(grep(/^$opt$/, @{ $opts->{methods} }), "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$/, @{ $opts->{methods} }), "check for absence of '$opt' in body");
}
}
}
sub check_create_correct{
my($self, $number, $uniquizer_cb, $keep_data) = @_;
if(!$keep_data){
my($self, $number, $uniquizer_cb, $keep_created) = @_;
if(!$keep_created && !$self->KEEP_CREATED){
$self->clear_data_created;
}
$self->DATA_CREATED->{ALL} //= {};
for(my $i = 1; $i <= $number; ++$i) {
my ($res, $err) = $self->request_post( $uniquizer_cb , undef, { i => $i} );
is($res->code, 201, "create test item $i");
my ($res, $content, $req) = $self->request_post( $uniquizer_cb , undef, { i => $i} );
is($res->code, 201, "create test item '".$self->name."' $i");
my $location = $res->header('Location');
if($location){
$self->DATA_CREATED->{ALL}->{$location} = $i;
$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 check_delete_use_created{
sub clear_test_data_all{
my($self,$uri) = @_;
my @uris = $uri ? ($uri) : keys $self->DATA_CREATED->{ALL};
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);
is($res->code, 204, "check delete item $del_uri");
}
}
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_list_collection{
my($self, $check_embedded_cb) = @_;
my $nexturi = $self->get_uri_collection."?page=1&rows=5";
@ -363,7 +415,7 @@ sub check_list_collection{
}
my $hal_name = $self->get_hal_name;
ok(((ref $list_collection->{_links}->{$hal_name} eq "ARRAY" ) ||
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");
my $check_embedded = sub {
@ -410,8 +462,8 @@ sub check_item_get{
my $req = HTTP::Request->new('GET', $uri);
my $res = $self->request($req);
is($res->code, 200, "fetch one item");
my $err = $res->decoded_content ? JSON::from_json($res->decoded_content) : '';
return wantarray ? ($res, $err, $req) : $res;
my $content = $res->decoded_content ? JSON::from_json($res->decoded_content) : '';
return wantarray ? ($res, $content, $req) : $res;
}
sub check_put_content_type_empty{
@ -476,11 +528,11 @@ sub check_put_bundle{
}
sub check_patch_correct{
my($self,$content) = @_;
my ($res,$mod_model,$req) = $self->request_patch( $content );
my ($res,$rescontent,$req) = $self->request_patch( $content );
is($res->code, 200, "check patched item");
is($mod_model->{_links}->{self}->{href}, $self->DATA_CREATED->{FIRST}, "check patched self link");
is($mod_model->{_links}->{collection}->{href}, '/api/'.$self->name.'/', "check patched collection link");
return ($res,$mod_model,$req);
is($rescontent->{_links}->{self}->{href}, $self->DATA_CREATED->{FIRST}, "check patched self link");
is($rescontent->{_links}->{collection}->{href}, '/api/'.$self->name.'/', "check patched collection link");
return ($res,$rescontent,$req);
}
sub check_patch_prefer_wrong{
@ -574,7 +626,7 @@ sub check_patch_bundle{
}
sub check_bundle{
my($self) = @_;
$self->check_options_collection;
$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);
@ -583,4 +635,9 @@ sub check_bundle{
$self->check_put_bundle;
$self->check_patch_bundle;
}
#utils
sub hash2params{
my($self,$hash) = @_;
return join '&', map {$_.'='.uri_escape($hash->{$_})} keys %{ $hash };
}
1;

@ -0,0 +1,330 @@
package Test::DeepHashUtils;
use 5.006;
use strict;
use warnings;
use Data::Dumper;
require Exporter;
our @ISA = qw(Exporter);
our %EXPORT_TAGS = ( 'all' => [ qw( reach slurp nest deepvalue ) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = ();
our $VERSION = '0.03';
my $C;
# Recursive version of C<each>;
sub reach {
my $ref = shift;
if (ref $ref eq 'HASH') {
if (defined $C->{$ref}{v}) {
if (ref $C->{$ref}{v} eq 'HASH' || ref $C->{$ref}{v} eq 'ARRAY') {
if (my @rec = reach($C->{$ref}{v})) {
if (defined $C->{$ref}{k}) {
return ($C->{$ref}{k},@rec);
}
if (defined $C->{$ref}{i}) {
return ($C->{$ref}{i},@rec);
}
return @rec;
}
}
undef $C->{$ref}{v};
}
if (my ($k,$v) = each %$ref) {
$C->{$ref}{v} = $v;
$C->{$ref}{k} = $k;
return ($k,reach($v));
}
return ();
} elsif (ref $ref eq 'ARRAY') {
if (defined $C->{$ref}{v}) {
if (ref $C->{$ref}{v} eq 'HASH' ||
ref $C->{$ref}{v} eq 'ARRAY') {
if (my @rec = reach($C->{$ref}{v})) {
if (defined $C->{$ref}{i}) {
return $C->{$ref}{i},@rec;
}
if (defined $C->{$ref}{k}) {
return $C->{$ref}{k},@rec;
}
return @rec;
}
}
undef $C->{$ref}{v};
}
if(!(defined $C->{$ref}{i})){
$C->{$ref}{i} = 0;
}else{
$C->{$ref}{i}++;
}
if (my $v = $ref->[$C->{$ref}{i}]) {
$C->{$ref}{v} = $v;
return ($C->{$ref}{i}, reach($v));
}
return ();
}
return $ref;
}
# run C<reach> over entire hash and return the final list of values at once
sub slurp {
my $ref = shift;
my @h;
while (my @a = reach($ref)) {
push @h,\@a;
}
return @h;
}
# Define nested hash keys from the given list of values
sub nest {
my $hr = shift;
my $ref = $hr;
while ( @_ ) {
my $key = shift @_;
if (@_ > 1) {
$ref = ('HASH' eq ref $ref ? $ref->{$key} : ('ARRAY' eq ref $ref ? $ref->[$key]:undef) ) ;
$ref ||= {};
} else {
my $value = shift;
if('HASH' eq ref $ref){
$ref->{$key} = $value;
}elsif('ARRAY' eq ref $ref){
$ref->[$key] = $value;
}
}
}
return $hr;
}
# Return value at the end of the given nested hash keys and/or array indexes
sub deepvalue {
my $hr = shift;
while (@_) {
my $key = shift;
if (ref $hr eq 'HASH') {
return unless ($hr = $hr->{$key});
} elsif (ref $hr eq 'ARRAY') {
return unless ($hr = $hr->[$key]);
} else {
return;
}
}
return $hr;
}
1;
__END__
=head1 NAME
Test::DeepHashUtils - functions for iterating over and working with nested hashes
=head1 SYNOPSIS
use Deep::Hash::Utils qw(reach slurp nest deepvalue);
my %hash = (
A => {
B => {
W => 1,
X => 2,
},
C => {
Y => 3,
Z => 4,
},
}
);
while (my @list = reach(\%hash)) {
print "@list";
}
for my $a (sort {$a->[2] cmp $b->[2]} slurp(\%hash)) {
print "@$a";
}
my %new_hash = ();
nest(\%new_hash,1,2,3,4,5);
my $value = deepvalue(\%new_hash,1,2,3,4);
=head1 DESCRIPTION
This module provides functions for accessing and modifying values in deeply nested data structures
=head3 C<reach>
reach HASHREF
Iterate over each nested data structure contained in the given hash. Returns an array of each nested key/value set.
Just as C<each> lets you iterate over the keys and values in a single hash, C<reach> provides an iterator over any recursively nested data structures.
This helps avoid the need to use layers of nested loops in order to iterate over all entities in nested hashes and arrays.
The reference passed to C<reach> can contain any combination of nested hashes and arrays. Hash keys and values will be ordered in the same manner as when using C<each>, C<keys>, or C<values>.
use Deep::Hash::Utils qw(reach slurp nest);
$\ = "\n";
my %hash = (
A => {
B => {
W => 1,
X => 2,
},
C => {
Y => 3,
Z => 4,
},
}
);
while (my @list = reach(\%hash)) {
print "@list";
}
__END__
Outputs:
A C Z 4
A C Y 3
A B W 1
A B X 2
=head3 C<slurp>
slurp HASHREF
Returns a list of arrays generated by C<reach> at once.
Use this if you want the same result of C<reach> with the ability to sort each layer of keys.
for my $a (sort {$a->[2] cmp $b->[2]} slurp(\%hash)) {
print "@$a";
}
__END__
Output:
A B W 1
A B X 2
A C Y 3
A C Z 4
=head3 C<nest>
nest HASHREF, LIST
define nested hash keys with a given list
use Data::Dumper;
my %new_hash = ();
nest(\%new_hash,1,2,3,4,5);
print Dumper \%new_hash;
__END__
Output:
$VAR1 = {
'1' => {
'2' => {
'3' => {
'4' => 5
}
}
}
};
=head3 C<deepvalue>
deepvalue HASHREF, LIST
retrieve deeply nested values with a list of keys:
my %new_hash = (
'1' => {
'2' => {
'3' => {
'4' => 5
}
}
}
);
print Dumper deepvalue(\%new_hash,1,2,3,4);
print Dumper deepvalue(\%new_hash,1,2);
__END__
Output:
$VAR1 = 5;
$VAR1 = {
'3' => {
'4' => 5
}
};
=head2 EXPORT
None by default.
=head1 REPOSITORY
L<https://github.com/neilbowers/perl-deep-hash-utils>
=head1 AUTHOR
Chris Becker, E<lt>clbecker@gmail.com<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2009 by Chris Becker
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

@ -0,0 +1,546 @@
package Test::FakeData;
use strict;
use Sipwise::Base;
use Test::Collection;
use JSON;
use Test::More;
use File::Basename;
use Data::Dumper;
use Test::DeepHashUtils qw(reach nest deepvalue);
use Clone qw/clone/;
has 'test_machine' =>(
is => 'rw',
isa => 'Test::Collection',
default => sub { Test::Collection->new () },
);
has 'created' => (
is => 'rw',
isa => 'HashRef',
default => sub { {} },
);
has 'loaded' => (
is => 'rw',
isa => 'HashRef',
default => sub { {} },
);
has 'data_default' => (
is => 'rw',
isa => 'HashRef',
builder => 'build_data_default',
);
has 'data' => (
is => 'rw',
isa => 'HashRef',
builder => 'build_data',
);
has 'FLAVOUR' => (
is => 'rw',
isa => 'Str',
);
#TODO: optimization - pre load and predelete should be done only for required collections and dependencies
has 'work_collections' => (
is => 'rw',
isa => 'ArrayRef',
default => sub { [] },
);
sub build_data_default{
return {
'products' => [
{
id => 1,
reseller_id => undef,
class => 'pstnpeering',
handle => 'PSTN_PEERING',
name => 'PSTN Peering',
on_sale => 1,
price => undef,
weight => undef,
billing_profile_id => undef,
},{
id => 2,
reseller_id => undef,
class => 'sippeering',
handle => 'SIP_PEERING',
name => 'PSTN Peering',
on_sale => 1,
price => undef,
weight => undef,
billing_profile_id => undef,
},{
id => 3,
reseller_id => undef,
class => 'reseller',
handle => 'VOIP_RESELLER',
name => 'VoIP Reseller',
on_sale => 1,
price => undef,
weight => undef,
billing_profile_id => undef,
},
],
'contracts' => {
id => 1,
customer_id => undef,
reseller_id => undef,
contact_id => undef,
order_id => undef,
status => 'active',
modify_timestamp => '0',
create_timestamp => '0',
activate_timestamp => '0',
terminate_timestamp => undef,
},
'resellers' => {
id => 1,
contract_id => 1,
name => 'default',
status => 'active',
},
'billing_mappings' => {
id => 1,
start_date => undef,
end_date => undef,
billing_profile_id => undef,
contract_id => 1,
product_id => 3,
},
'billing_profiles' => {
id => 1,
reseller_id => 1,
handle => 'default',
name => 'Default Billing Profile',
prepaid => 1,
interval_charge => 0,
interval_free_time => 0,
interval_free_cash => 0,
interval_unit => 'month',
interval_count => 1,
currency => undef,
vat_rate => 0,
vat_included => 0,
},
'billing_zones' => {
id => 1,
billing_profile_id => 1,
zone => 'Free Default Zone',
detail => 'All Destinations',
},
'billing_fees' => {
id => 1,
billing_profile_id => 1,
billing_zone_id => 1,
destination => '.*',
type => 'call',
onpeak_init_rate => 0,
onpeak_init_interval => 600,
onpeak_follow_rate => 0,
onpeak_follow_interval => 600,
offpeak_init_rate => 0,
offpeak_init_interval => 600,
offpeak_follow_rate => 0,
offpeak_follow_interval => 600,
},
'domains' => {
domain => 'voip.sipwise.local',
local => 1,
}
};
}
sub build_data{
my ($self) = @_;
my $data = {
'systemcontacts' => {
'data' => {
email => 'api_test_reseller@reseller.invalid',
firstname => 'api_test first',
lastname => 'api_test last',
},
'query' => ['email'],
'delete_potentially_dependent' => 1,
},
'customercontacts' => {
'data' => {
firstname => 'api_test cust_contact_first',
lastname => 'api_test cust_contact_last',
email => 'api_test_cust_contact@custcontact.invalid',
reseller_id => sub { return shift->create('resellers',@_); },
},
'query' => ['email'],
'delete_potentially_dependent' => 1,
},
'contracts' => {
'data' => {
contact_id => sub { return shift->create('systemcontacts',@_); },
status => 'active',
external_id => 'api_test',
#type => sub { return value_request('contracts','type',['reseller']); },
type => 'reseller',
billing_profile_id => sub { return shift->create('billingprofiles',@_); },
},
'default' => 'contracts',
'query' => ['external_id'],
'no_delete_available' => 1,
},
'resellers' => {
'data' => {
contract_id => sub { return shift->create('contracts', @_ ); },
name => 'api_test test reseller',
status => 'active',
},
'default' => 'resellers',
'query' => ['name'],
'no_delete_available' => 1,
},
'customers' => {
'data' => {
status => 'active',
contact_id => sub { return shift->create('customercontacts',@_); },
billing_profile_id => sub { return shift->create('billingprofiles',@_); },
max_subscribers => undef,
external_id => 'api_test customer',
type => 'pbxaccount',#sipaccount
},
'query' => ['external_id'],
'no_delete_available' => 1,
},
'billingprofiles' => {
'data' => {
name => 'api_test test profile',
handle => 'api_test_testprofile',
reseller_id => sub { return shift->create('resellers',@_); },
},
'default' => 'billing_profiles',
'query' => ['handle'],
'no_delete_available' => 1,
},
'subscribers' => {
'data' => {
administrative => 0,
customer_id => sub { return shift->create('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->create('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->create('subscriberprofiles',@_); },
status => 'active',
pbx_hunt_policy => 'parallel',
pbx_hunt_timeout => '15',
},
'query' => ['username'],
},
'domains' => {
'data' => {
domain => 'api_test_domain.api_test_domain',
reseller_id => sub { return shift->create('resellers',@_); },
},
'query' => ['domain'],
},
'subscriberprofilesets' => {
'data' => {
name => 'api_test_subscriberprofileset',
reseller_id => sub { return shift->create('resellers',@_); },
description => 'api_test_subscriberprofileset',
},
'query' => ['name'],
},
'subscriberprofiles' => {
'data' => {
name => 'api_test subscriberprofile',
profile_set_id => sub { return shift->create('subscriberprofilesets',@_); },
description => 'api_test subscriberprofile',
},
'query' => ['name'],
},
'pbxdevicemodels' => {
'data' => {
json => {
model => "api_test ATA111",
#reseller_id=1 is very default, as is seen from the base initial script
#reseller_id => "1",
reseller_id => sub { return shift->create('resellers',@_); },
vendor =>"Cisco",
#3.7relative tests
type => "phone",
connectable_models => [],
extensions_num => "2",
bootstrap_method => "http",
bootstrap_uri => "",
bootstrap_config_http_sync_method => "GET",
bootstrap_config_http_sync_params => "[% server.uri %]/\$MA",
bootstrap_config_http_sync_uri => "http=>//[% client.ip %]/admin/resync",
bootstrap_config_redirect_panasonic_password => "",
bootstrap_config_redirect_panasonic_user => "",
bootstrap_config_redirect_polycom_password => "",
bootstrap_config_redirect_polycom_profile => "",
bootstrap_config_redirect_polycom_user => "",
bootstrap_config_redirect_yealink_password => "",
bootstrap_config_redirect_yealink_user => "",
#TODO:implement checking against this number in the controller and api
#/3.7relative tests
"linerange"=>[
{
"keys" => [
{y => "390", labelpos => "left", x => "510"},
{y => "350", labelpos => "left", x => "510"}
],
can_private => "1",
can_shared => "0",
can_blf => "0",
name => "Phone Ports api_test",
#TODO: test duplicate creation #"id"=>1311,
},
{
"keys"=>[
{y => "390", labelpos => "left", x => "510"},
{y => "350", labelpos => "left", x => "510"}
],
can_private => "1",
can_shared => "0",
#TODO: If I'm right - now we don't check field values against this, because test for pbxdevice xreation is OK
can_blf => "0",
name => "Extra Ports api_test",
#TODO: test duplicate creation #"id"=>1311,
}
]
},
#TODO: can check big files
#front_image => [ dirname($0).'/resources/api_devicemodels_front_image.jpg' ],
front_image => [ dirname($0).'/resources/empty.txt' ],
},
'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);
},
'no_delete_available' => 1,
},
'pbxdeviceconfigs' => {
'data' => {
device_id => sub { return shift->create('pbxdevicemodels',@_); },
version => 'api_test 1.1',
content_type => 'text/plain',
},
'query' => ['version'],
'create_special'=> sub {
my ($self,$name) = @_;
my $prev_params = $self->test_machine->get_cloned('content_type','QUERY_PARAMS');
$self->test_machine->content_type->{POST} = $self->data->{$name}->{data}->{content_type};
$self->test_machine->QUERY_PARAMS($self->test_machine->hash2params($self->data->{$name}->{data}));
$self->test_machine->check_create_correct(1, sub {return 'test_api_empty_config';} );
$self->test_machine->set(%$prev_params);
},
'no_delete_available' => 1,
},
'pbxdeviceprofiles' => {
'data' => {
config_id => sub { return shift->create('pbxdeviceconfigs',@_); },
name => 'api_test profile 1.1',
},
'query' => ['name'],
'no_delete_available' => 1,
},
'pbxdevices' => {
'data' => {
profile_id => sub { return shift->create('pbxdeviceprofiles',@_); },
customer_id => sub { return shift->create('customers',@_); },
identifier => 'aaaabbbbcccc',
station_name => 'api_test_vun',
lines=>[{
linerange => 'Phone Ports api_test',
type => 'private',
key_num => '0',
subscriber_id => sub { return shift->create('subscribers',@_); },
extension_unit => '1',
extension_num => '1',#to handle some the same extensions devices
},{
linerange => 'Extra Ports api_test',
type => 'blf',
key_num => '1',
subscriber_id => sub { return shift->create('subscribers',@_); },
extension_unit => '2',
}],
},
'query' => ['station_name'],
},
};
foreach my $collection_name( keys %$data ){
if($self->FLAVOUR && exists $data->{$collection_name}->{flavour} && exists $data->{$collection_name}->{flavour}->{$self->FLAVOUR}){
$data = {%$data, %{$data->{$collection_name}->{flavour}->{$self->FLAVOUR}}},
}
}
$self->clear_db($data,[qw/contracts systemcontacts customercontacts/]);
#incorrect place, leave it for the next timeframe to work on it
$self->load_db($data);
return $data;
}
sub load_db{
my($self,$data) = @_;
$data //= $self->data;
foreach my $collection_name( keys %$data ){
#print "collection_name=$collection_name;\n";
if((!exists $self->loaded->{$collection_name}) && $data->{$collection_name}->{query}){
my(undef,$content) = $self->search_item($collection_name,$data);
if($content->{total_count}){
my $values = $content->{_embedded}->{$self->test_machine->get_hal_name};
$values = ('HASH' eq ref $values) ? [$values] : $values;
$self->loaded->{$collection_name} = [ map {
{
location => $_->{_links}->{self}->{href},
content => $_,
}
} @$values ];
}
}
}
return;
}
sub clear_db{
my($self,$data,$order_array) = @_;
$order_array //= [];
my $order_hash = {};
@$order_hash{(keys %$data)} = (0) x (keys %$data);
@$order_hash{@$order_array} = (1..$#$order_array+1);
my @undeletable_items = ();
foreach my $collection_name (sort {$order_hash->{$a} <=> $order_hash->{$b}} keys %$data ){
if((!$data->{$collection_name}->{query})){
next;
}
my(undef,$content) = $self->search_item($collection_name,$data);
if($content->{total_count}){
my $values = $content->{_links}->{$self->test_machine->get_hal_name};
$values =
('HASH' eq ref $values) ? [$values] : $values;
my @locations = map {$_->{href}} @$values;
if($data->{$collection_name}->{no_delete_available}){
push @undeletable_items, @locations;
}else{
if($data->{$collection_name}->{delete_potentially_dependent}){
foreach( @locations ){
if(!$self->test_machine->clear_test_data_dependent($_)){
push @undeletable_items, $_;
}
}
}else{
$self->test_machine->clear_test_data_all([ @locations ]);
}
}
}
}
if(@undeletable_items){
print "We have test items, which can't delete through API:\n";
print Dumper [ @undeletable_items ];
}
return;
}
sub search_item{
my($self,$collection_name,$data) = @_;
$data //= $self->data;
my $item = $data->{$collection_name};
if(!$item->{query}){
return;
}
$self->test_machine->name($collection_name);
my $query_string = join('&', map {
my @deep_keys = ('ARRAY' eq ref $_) ? @$_:($_);
my $field_name = ( @deep_keys > 1 ) ? shift @deep_keys : $deep_keys[0];
$field_name.'='.deepvalue($item->{data},@deep_keys);
} @{$item->{query}}
);
my($res, $content, $req) = $self->test_machine->check_item_get($self->test_machine->get_uri_get($query_string));
return ($res, $content, $req);
}
sub create{
my($self, $name, $parents_in, $field_path, $params) = @_;
$parents_in //= {};
if($self->loaded->{$name} || $self->created->{$name}){
return $self->get_id($name);
}
if($parents_in->{$name}){
if($self->data->{$name}->{default}){
$self->data->{$name}->{process_cycled} = {'parents'=>$parents_in,'field_path'=>$field_path};
return $self->data_default->{$self->data->{$name}->{default}}->{id};
}else{
die('Data absence', Dumper([$name,$parents_in]));
}
}
$self->process($name, $parents_in);
#create itself
my $data = clone($self->data->{$name}->{data});
$self->test_machine->set(
name => $name,
DATA_ITEM => $data,
);
if(exists $self->data->{$name}->{create_special} && 'CODE' eq ref $self->data->{$name}->{create_special}){
$self->data->{$name}->{create_special}->($self,$name);
}else{
$self->test_machine->check_create_correct(1);
}
$self->created->{$name} = [values %{$self->test_machine->DATA_CREATED->{ALL}}];
if($self->data->{$name}->{process_cycled}){
my %parents_cycled_ordered = reverse %{$self->data->{$name}->{process_cycled}->{parents}};
my $last_parent = -1 + ( scalar values (%parents_cycled_ordered) );
my $uri = $self->test_machine->get_uri_collection($parents_cycled_ordered{$last_parent}).$self->get_id($parents_cycled_ordered{$last_parent});
$self->test_machine->request_patch([ {
op => 'replace',
path => join('/',('',@{$self->data->{$name}->{process_cycled}->{field_path}})),
value => $self->get_id($name) } ],
$uri
);
delete $self->data->{$name}->{process_cycled};
}
return $self->get_id($name);
}
sub process{
my($self, $name, $parents_in) = @_;
$parents_in //= {};
my $parents = {%{$parents_in}};
$parents->{$name} //= scalar values %$parents_in;
while (my @keys_and_value = reach($self->data->{$name}->{data})){
my $field_value = pop @keys_and_value;
if('CODE' eq ref $field_value ){
my $value = $field_value->($self,$parents,[@keys_and_value]);
nest( $self->data->{$name}->{data}, @keys_and_value, $value );
}
}
return $self->data->{$name}->{data};
}
sub get_id{
my($self, $name) = @_;
my $id = $self->test_machine->get_id_from_created($self->created->{$name}->[0])
|| $self->test_machine->get_id_from_created($self->loaded->{$name}->[0]);
return $id
}
sub DEMOLISH{
my($self) = @_;
( 'ARRAY' eq ref$self->created ) and ( $self->test_machine->clear_test_data_all([ map {$_->{location}} @$self->created ]) );
}
1;
__END__
Further improvements:
Really it would be much more correct to use collection clases with ALL their own test machine initialization for data creation. just will call proper collection class. It will allow to keep data near releveant tests, and don't duplicate test_machine params in the FakeData.
Optimizations:
1.make wrapper for data creation/deletion for all tests.
2.Load/Clear only relevant tests data

@ -1,143 +0,0 @@
#use Sipwise::Base;
use strict;
#use Moose;
use Sipwise::Base;
extends 'NGCP::Panel::Utils::Test::Collection';
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 $test_machine = NGCP::Panel::Utils::Test::Collection->new(
name => 'pbxdevicemodels',
embedded => [qw/pbxdevicefirmwares/]
);
@{$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 PUT PATCH)};
#for item creation test purposes /post request data/
$test_machine->DATA_ITEM_STORE({
json => {
"model"=>"ATA111",
#should be some fake reseller - create reseller/customer/subscriber tests?
#"reseller_id"=>"1",
"vendor"=>"Cisco",
#3.7relative tests
"bootstrap_method"=>"http",
"bootstrap_uri"=>"",
"bootstrap_config_http_sync_method"=>"GET",
"bootstrap_config_http_sync_params"=>"[% server.uri %]/\$MA",
"bootstrap_config_http_sync_uri"=>"http=>//[% client.ip %]/admin/resync",
"bootstrap_config_redirect_panasonic_password"=>"",
"bootstrap_config_redirect_panasonic_user"=>"",
"bootstrap_config_redirect_polycom_password"=>"",
"bootstrap_config_redirect_polycom_profile"=>"",
"bootstrap_config_redirect_polycom_user"=>"",
"bootstrap_config_redirect_yealink_password"=>"",
"bootstrap_config_redirect_yealink_user"=>"",
"type"=>"phone",
#"connectable_models"=>[702,703,704],
"extensions_num"=>"2",
#/3.7relative tests
"linerange"=>[
{
"keys"=>[
{"y"=>"390","labelpos"=>"left","x"=>"510"},
{"y"=>"350","labelpos"=>"left","x"=>"510"}
],
"can_private"=>"1",
"can_shared"=>"0",
"can_blf"=>"0",
"name"=>"Phone Ports",
#test duplicate creation #"id"=>1311,
}
]
},
#can check big files
#'front_image' => [ dirname($0).'/resources/api_devicemodels_front_image.jpg' ],
'front_image' => [ dirname($0).'/resources/empty.txt' ],
});
##$test_machine->form_data_item( sub {$_[0]->{json}->{type} = "extension";} );
##$test_machine->check_create_correct( 1, sub{ $_[0]->{json}->{model} .= "Extension 2".$_[1]->{i}; } );
#$test_machine->check_get2put( sub {
# $_[0]->{connectable_models} = [670],
# $_[0] = {
# json => JSON::to_json($_[0]),
# 'front_image' => $test_machine->DATA_ITEM_STORE->{front_image}
# }; },
# $test_machine->get_uri_collection.'449'
#);
#
##test check_patch_prefer_wrong is broken
##$test_machine->name('billingprofiles');
##$test_machine->check_patch_prefer_wrong;
foreach my $type(qw/phone extension/){
#last;#skip classic tests
$test_machine->form_data_item( sub {$_[0]->{json}->{type} = $type;} );
# create 6 new billing models from DATA_ITEM
#$test_machine->check_create_correct( 6, sub{ $_[0]->{json}->{model} .= $type."TEST_".$_[1]->{i}; } );
#$test_machine->check_get2put( 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};});
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;});
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;});
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");
}
{
my (undef, $item_first_get) = $test_machine->check_item_get;
ok(exists $item_first_get->{reseller_id} && $item_first_get->{reseller_id}->is_int, "check existence of reseller_id");
foreach(qw/vendor model/){
ok(exists $item_first_get->{$_}, "check existence of $_");
}
# check if we have the proper links
# TODO: fees, reseller links
#ok(exists $new_contract->{_links}->{'ngcp:resellers'}, "check put presence of ngcp:resellers relation");
}
{
my $t = time;
my($res,$mod_model) = $test_machine->check_patch_correct( [ { op => 'replace', path => '/model', value => 'patched model '.$t } ] );
is($mod_model->{model}, "patched model $t", "check patched replace op");
}
{
my($res) = $test_machine->request_patch( [ { op => 'replace', path => '/reseller_id', value => undef } ] );
is($res->code, 422, "check patched undef reseller");
}
{
my($res) = $test_machine->request_patch( [ { op => 'replace', path => '/reseller_id', value => 99999 } ] );
is($res->code, 422, "check patched invalid reseller");
}
}
`echo 'delete from autoprov_devices where model like "%TEST\\_%" or model like "patched model%";'|mysql provisioning`;
done_testing;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,104 @@
#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;
#init test_machine
my $fake_data = Test::FakeData->new;
my $test_machine = Test::Collection->new(
name => 'pbxdevicemodels',
embedded => [qw/pbxdevicefirmwares/]
);
@{$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 PUT PATCH)};
$test_machine->KEEP_CREATED( 1 );
$test_machine->DATA_ITEM_STORE($fake_data->process('pbxdevicemodels'));
my $connactable_devices={};
foreach my $type(qw/extension phone/){
#last;#skip classic tests
$test_machine->form_data_item( sub {$_[0]->{json}->{type} = $type;} );
# create 3 & 3 new billing models from DATA_ITEM
$test_machine->check_create_correct( 3, sub{ $_[0]->{json}->{model} .= $type."TEST_".$_[1]->{i}; } );
#print Dumper $test_machine->DATA_CREATED->{ALL};
$connactable_devices->{$type}->{data} = [ values %{$test_machine->DATA_CREATED->{ALL}}];
$connactable_devices->{$type}->{ids} = [ map {$test_machine->get_id_from_created($_)} @{$connactable_devices->{$type}->{data}}];
}
sub get_connectable_type{
my $type = shift;
return ('extension' eq $type) ? 'phone' : 'extension';
}
foreach my $type(qw/extension phone/){
#last;#skip classic tests
$test_machine->form_data_item( sub {$_[0]->{json}->{type} = $type;} );
# create 3 next new models from DATA_ITEM
$test_machine->check_create_correct( 3, sub{
$_[0]->{json}->{model} .= $type."TEST_".($_[1]->{i} + 3);
$_[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_bundle();
# try to create model without reseller_id
{
my ($res, $err) = $test_machine->request_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;});
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;});
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");
}
{
my (undef, $item_first_get) = $test_machine->check_item_get;
ok(exists $item_first_get->{reseller_id} && $item_first_get->{reseller_id}->is_int, "check existence of the reseller_id");
foreach(qw/vendor model/){
ok(exists $item_first_get->{$_}, "check existence of $_");
}
# check if we have the proper links
}
{
my $t = time;
my($res,$mod_model) = $test_machine->check_patch_correct( [ { op => 'replace', path => '/model', value => 'patched model '.$t } ] );
is($mod_model->{model}, "patched model $t", "check patched replace op");
}
{
my($res) = $test_machine->request_patch( [ { op => 'replace', path => '/reseller_id', value => undef } ] );
is($res->code, 422, "check patched undef reseller");
}
{
my($res) = $test_machine->request_patch( [ { op => 'replace', path => '/reseller_id', value => 99999 } ] );
is($res->code, 422, "check patched invalid reseller");
}
}
#pbxdevicemodels doesn't have DELETE method
#`echo 'delete from autoprov_devices where model like "%api_test %" or model like "patched model%";'|mysql provisioning`;
done_testing;
# vim: set tabstop=4 expandtab:

@ -1,9 +1,9 @@
#use Sipwise::Base;
use strict;
#use Moose;
use Sipwise::Base;
extends 'NGCP::Panel::Utils::Test::Collection';
use Test::Collection;
use Test::FakeData;
use Net::Domain qw(hostfqdn);
use LWP::UserAgent;
use HTTP::Request::Common;
@ -14,44 +14,23 @@ use File::Basename;
use bignum qw/hex/;
#init test_machine
my $test_machine = NGCP::Panel::Utils::Test::Collection->new(
name => 'pbxdevices',
my $test_machine = Test::Collection->new(
name => 'pbxdevices',
embedded => [qw/pbxdeviceprofiles customers/]
);
$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)};
my $fake_data = Test::FakeData->new;
#for item creation test purposes /post request data/
$test_machine->DATA_ITEM_STORE({
#'profile_id' => '151',
#somehow should obtain/create test customer with the test subscriber - discuss with alex
#'customer_id' => '968',
'identifier' => 'aaaabbbbcccc',
'station_name' => 'abc',
'lines'=>[{
'linerange' => 'Phone Ports',
'type' => 'private',
'key_num' => '0',
#somehow should obtain/create test customer with the test subscriber - discuss with alex
#'subscriber_id' => '1198',
'extension_unit' => '1',
#'extension_num' => '1',#to handle some the same extensions devices
},{
'linerange' => 'Phone Ports',
'type' => 'private',
'key_num' => '1',
#somehow should obtain/create test customer with the test subscriber - discuss with alex
#'subscriber_id' => '1198',
'extension_unit' => '2',
}],
});
$test_machine->DATA_ITEM_STORE($fake_data->process('pbxdevices'));
$test_machine->form_data_item( );
# create 3 new billing models from DATA_ITEM
#$test_machine->check_create_correct( 3, sub{ $_[0]->{identifier} = sprintf('%x', (hex('0x'.$_[0]->{identifier}) + $_[1]->{i}) ); } );
#$test_machine->check_get2put( );
# 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_bundle();
$test_machine->check_delete_use_created();
$test_machine->clear_test_data_all();
done_testing;

Loading…
Cancel
Save