From 7dfc5fda21d2920613938be85cc763e572ce298e Mon Sep 17 00:00:00 2001 From: Rene Krenn Date: Fri, 18 Sep 2015 03:31:45 +0200 Subject: [PATCH] MT#14255 panel UI problem setting subscriber 'lock' detail Change-Id: I4873b36526152da64334a8359ce8bfe531ce42a6 --- lib/NGCP/Panel/Controller/API/Subscribers.pm | 45 +++++- .../Panel/Controller/API/SubscribersItem.pm | 18 +++ lib/NGCP/Panel/Controller/Subscriber.pm | 49 ++++-- lib/NGCP/Panel/Role/API.pm | 8 +- lib/NGCP/Panel/Role/API/Contracts.pm | 1 + lib/NGCP/Panel/Role/API/Customers.pm | 1 + lib/NGCP/Panel/Role/API/Subscribers.pm | 4 + t/api-rest/api-balanceintervals.t | 145 ++++++++++++++++-- 8 files changed, 234 insertions(+), 37 deletions(-) diff --git a/lib/NGCP/Panel/Controller/API/Subscribers.pm b/lib/NGCP/Panel/Controller/API/Subscribers.pm index bae1c44597..359e556958 100644 --- a/lib/NGCP/Panel/Controller/API/Subscribers.pm +++ b/lib/NGCP/Panel/Controller/API/Subscribers.pm @@ -187,6 +187,30 @@ class_has 'query_params' => ( }, }, }, + { + param => 'reseller_id', + description => 'Filter for subscribers of customers belonging to a specific reseller', + query => { + first => sub { + my $q = shift; + { 'contract.contact.reseller_id' => $q }; + }, + second => sub { + { join => 'contract.contact' }; + }, + }, + }, + { + param => 'contact_id', + description => 'Filter for subscribers of contracts with a specific contact id', + query => { + first => sub { + my $q = shift; + { 'contract.contact_id' => $q }; + }, + second => sub {}, + }, + }, ]}, ); @@ -222,12 +246,24 @@ sub GET :Allow { my ($self, $c) = @_; my $page = $c->request->params->{page} // 1; my $rows = $c->request->params->{rows} // 10; + my $schema = $c->model('DB'); + $schema->set_transaction_isolation('READ COMMITTED'); + my $guard = $schema->txn_scope_guard; { - my $subscribers = $self->item_rs($c); - (my $total_count, $subscribers) = $self->paginate_order_collection($c, $subscribers); - my (@embedded, @links); + my $subscribers_rs = $self->item_rs($c); + (my $total_count, $subscribers_rs) = $self->paginate_order_collection($c, $subscribers_rs); + my $subscribers = NGCP::Panel::Utils::ProfilePackages::lock_contracts(c => $c, + rs => $subscribers_rs, + contract_id_field => 'contract_id'); + my $now = NGCP::Panel::Utils::DateTime::current_local; + my (@embedded, @links, %contract_map); my $form = $self->get_form($c); - for my $subscriber ($subscribers->all) { + for my $subscriber (@$subscribers) { + my $contract = $subscriber->contract; + my $balance = NGCP::Panel::Utils::ProfilePackages::get_contract_balance(c => $c, + contract => $contract, + now => $now) if !exists $contract_map{$contract->id}; #apply underrun lock level + $contract_map{$contract->id} = 1; my $resource = $self->resource_from_item($c, $subscriber, $form); push @embedded, $self->hal_from_item($c, $subscriber, $resource, $form); push @links, Data::HAL::Link->new( @@ -235,6 +271,7 @@ sub GET :Allow { href => sprintf('%s%d', $self->dispatch_path, $subscriber->id), ); } + $self->delay_commit($c,$guard); push @links, Data::HAL::Link->new( relation => 'curies', diff --git a/lib/NGCP/Panel/Controller/API/SubscribersItem.pm b/lib/NGCP/Panel/Controller/API/SubscribersItem.pm index 16baff0fc5..9078be9286 100644 --- a/lib/NGCP/Panel/Controller/API/SubscribersItem.pm +++ b/lib/NGCP/Panel/Controller/API/SubscribersItem.pm @@ -9,6 +9,7 @@ use HTTP::Status qw(:constants); use MooseX::ClassAttribute qw(class_has); use NGCP::Panel::Utils::ValidateJSON qw(); use NGCP::Panel::Utils::DateTime; +use NGCP::Panel::Utils::ProfilePackages qw(); use Path::Tiny qw(path); use Safe::Isa qw($_isa); BEGIN { extends 'Catalyst::Controller::ActionRole'; } @@ -52,14 +53,22 @@ sub auto :Private { sub GET :Allow { my ($self, $c, $id) = @_; + $c->model('DB')->set_transaction_isolation('READ COMMITTED'); + my $guard = $c->model('DB')->txn_scope_guard; { last unless $self->valid_id($c, $id); my $subscriber = $self->item_by_id($c, $id); last unless $self->resource_exists($c, subscriber => $subscriber); + my $balance = NGCP::Panel::Utils::ProfilePackages::get_contract_balance(c => $c, + contract => $subscriber->contract, + ); #apply underrun lock level + + my $form = $self->get_form($c); my $resource = $self->resource_from_item($c, $subscriber, $form); my $hal = $self->hal_from_item($c, $subscriber, $resource, $form); + $guard->commit; #potential db write ops in hal_from my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new( (map { # XXX Data::HAL must be able to generate links with multiple relations @@ -97,6 +106,7 @@ sub OPTIONS :Allow { sub PUT :Allow { my ($self, $c, $id) = @_; my $schema = $c->model('DB'); + $schema->set_transaction_isolation('READ COMMITTED'); my $guard = $schema->txn_scope_guard; { my $preference = $self->require_preference($c); @@ -104,6 +114,9 @@ sub PUT :Allow { my $subscriber = $self->item_by_id($c, $id); last unless $self->resource_exists($c, subscriber => $subscriber); + my $balance = NGCP::Panel::Utils::ProfilePackages::get_contract_balance(c => $c, + contract => $subscriber->contract, + ); #apply underrun lock level my $resource = $self->get_valid_put_data( c => $c, id => $id, @@ -112,6 +125,7 @@ sub PUT :Allow { last unless $resource; my $r = $self->prepare_resource($c, $schema, $resource, $subscriber); last unless $r; + $resource = $r->{resource}; my $form = $self->get_form($c); @@ -145,6 +159,7 @@ sub PUT :Allow { sub PATCH :Allow { my ($self, $c, $id) = @_; my $schema = $c->model('DB'); + $schema->set_transaction_isolation('READ COMMITTED'); my $guard = $schema->txn_scope_guard; { my $preference = $self->require_preference($c); @@ -152,6 +167,9 @@ sub PATCH :Allow { my $subscriber = $self->item_by_id($c, $id); last unless $self->resource_exists($c, subscriber => $subscriber); + my $balance = NGCP::Panel::Utils::ProfilePackages::get_contract_balance(c => $c, + contract => $subscriber->contract, + ); #apply underrun lock level my $json = $self->get_valid_patch_data( c => $c, id => $id, diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index 3b9dae068d..a125fdd53b 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -1997,16 +1997,13 @@ sub preferences_callforward_delete :Chained('base') :PathPart('preferences/callf $c->uri_for_action('/subscriber/preferences', [$c->req->captures->[0]])); } -sub load_preference_list :Private { +sub underrun_catchup :Private { my ($self, $c) = @_; - - my $reseller_id = $c->stash->{subscriber}->contract->contact->reseller_id; - try { my $schema = $c->model('DB'); $schema->set_transaction_isolation('READ COMMITTED'); $schema->txn_do(sub { - NGCP::Panel::Utils::ProfilePackages::underrun_lock_subscriber(c => $c, subscriber => $c->stash->{subscriber}); + NGCP::Panel::Utils::ProfilePackages::get_contract_balance(c => $c, contract => $c->stash->{subscriber}->contract); }); } catch($e) { NGCP::Panel::Utils::Message->error( @@ -2016,8 +2013,16 @@ sub load_preference_list :Private { desc => $c->loc('Failed to check and apply underrun subscriber lock level'), ); $c->response->redirect($c->uri_for()); - return; - } + #return; + } +} + +sub load_preference_list :Private { + my ($self, $c) = @_; + + my $reseller_id = $c->stash->{subscriber}->contract->contact->reseller_id; + + $self->underrun_catchup($c); my $usr_pref_values = $c->model('DB') ->resultset('voip_preferences') @@ -2117,6 +2122,8 @@ sub master :Chained('base') :PathPart('details') :CaptureArgs(0) { template => 'subscriber/master.tt', ); + $self->underrun_catchup($c); + $c->stash->{prov_lock} = NGCP::Panel::Utils::Preferences::get_usr_preference_rs( c => $c, attribute => 'lock', @@ -2127,6 +2134,8 @@ sub master :Chained('base') :PathPart('details') :CaptureArgs(0) { sub details :Chained('master') :PathPart('') :Args(0) :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRole(reseller) :AllowedRole('subscriberadmin') { my ($self, $c) = @_; + $self->underrun_catchup($c); + $c->stash->{prov_lock} = NGCP::Panel::Utils::Preferences::get_usr_preference_rs( c => $c, attribute => 'lock', @@ -2284,6 +2293,7 @@ sub edit_master :Chained('master') :PathPart('edit') :Args(0) :Does(ACL) :ACLDet } my $schema = $c->model('DB'); try { + $schema->set_transaction_isolation('READ COMMITTED'); $schema->txn_do(sub { my $email = delete $form->params->{email}; @@ -2561,15 +2571,22 @@ sub edit_master :Chained('master') :PathPart('edit') :Args(0) :Does(ACL) :ACLDet } $form->values->{lock} ||= 0; - if($lock->first) { - if ($form->values->{lock} == 0) { - $lock->delete; - } else { - $lock->first->update({ value => $form->values->{lock} }); - } - } elsif($form->values->{lock} > 0) { - $lock->create({ value => $form->values->{lock} }); - } + NGCP::Panel::Utils::ProfilePackages::get_contract_balance(c => $c, contract => $subscriber->contract); + NGCP::Panel::Utils::Subscriber::lock_provisoning_voip_subscriber( + c => $c, + prov_subscriber => $subscriber->provisioning_voip_subscriber, + level => $form->values->{lock}, + ) if ($subscriber->provisioning_voip_subscriber); + + #if($lock->first) { + # if ($form->values->{lock} == 0) { + # $lock->delete; + # } else { + # $lock->first->update({ value => $form->values->{lock} }); + # } + #} elsif($form->values->{lock} > 0) { + # $lock->create({ value => $form->values->{lock} }); + #} }); delete $c->session->{created_objects}->{group}; NGCP::Panel::Utils::Message->info( diff --git a/lib/NGCP/Panel/Role/API.pm b/lib/NGCP/Panel/Role/API.pm index 299d5ad176..0e15a4040d 100644 --- a/lib/NGCP/Panel/Role/API.pm +++ b/lib/NGCP/Panel/Role/API.pm @@ -437,7 +437,7 @@ sub apply_patch { $entity = $coderef->('JSON::Pointer', $entity, $op->{path}, $op->{value}); } } else { - $pe->rethrow; + die($pe); #->rethrow; } } } elsif ('remove' eq $_) { @@ -450,7 +450,7 @@ sub apply_patch { $entity = $coderef->('JSON::Pointer', $entity, $op->{path}); } } else { - $pe->rethrow; + die($pe); #->rethrow; } } } elsif ('move' eq $_ or 'copy' eq $_) { @@ -463,7 +463,7 @@ sub apply_patch { $entity = $coderef->('JSON::Pointer', $entity, $op->{from}, $op->{path}); } } else { - $pe->rethrow; + die($pe); #->rethrow; } } } elsif ('test' eq $_) { @@ -478,7 +478,7 @@ sub apply_patch { unless $coderef->('JSON::Pointer', $entity, $op->{path}, $op->{value}); } } else { - $pe->rethrow; + die($pe); #->rethrow; } } } diff --git a/lib/NGCP/Panel/Role/API/Contracts.pm b/lib/NGCP/Panel/Role/API/Contracts.pm index 8d644261d1..d96dd0f3a8 100644 --- a/lib/NGCP/Panel/Role/API/Contracts.pm +++ b/lib/NGCP/Panel/Role/API/Contracts.pm @@ -102,6 +102,7 @@ sub hal_from_contract { Data::HAL::Link->new(relation => 'ngcp:systemcontacts', href => sprintf("/api/systemcontacts/%d", $contract->contact->id)), @profile_links, @network_links, + Data::HAL::Link->new(relation => 'ngcp:balanceintervals', href => sprintf("/api/balanceintervals/%d", $contract->id)), $self->get_journal_relation_link($contract->id), ], relation => 'ngcp:'.$self->resource_name, diff --git a/lib/NGCP/Panel/Role/API/Customers.pm b/lib/NGCP/Panel/Role/API/Customers.pm index 1335e2861a..893d8390b0 100644 --- a/lib/NGCP/Panel/Role/API/Customers.pm +++ b/lib/NGCP/Panel/Role/API/Customers.pm @@ -100,6 +100,7 @@ sub hal_from_customer { @network_links, $customer->profile_package_id ? Data::HAL::Link->new(relation => 'ngcp:profilepackages', href => sprintf("/api/profilepackages/%d", $customer->profile_package_id)) : (), Data::HAL::Link->new(relation => 'ngcp:customerbalances', href => sprintf("/api/customerbalances/%d", $customer->id)), + Data::HAL::Link->new(relation => 'ngcp:balanceintervals', href => sprintf("/api/balanceintervals/%d", $customer->id)), $customer->invoice_template ? (Data::HAL::Link->new(relation => 'ngcp:invoicetemplates', href => sprintf("/api/invoicetemplates/%d", $customer->invoice_template_id))) : (), $customer->subscriber_email_template_id ? (Data::HAL::Link->new(relation => 'ngcp:subscriberemailtemplates', href => sprintf("/api/emailtemplates/%d", $customer->subscriber_email_template_id))) : (), $customer->passreset_email_template_id ? (Data::HAL::Link->new(relation => 'ngcp:passresetemailtemplates', href => sprintf("/api/emailtemplates/%d", $customer->passreset_email_template_id))) : (), diff --git a/lib/NGCP/Panel/Role/API/Subscribers.pm b/lib/NGCP/Panel/Role/API/Subscribers.pm index d3fcb898a5..8bd4467909 100644 --- a/lib/NGCP/Panel/Role/API/Subscribers.pm +++ b/lib/NGCP/Panel/Role/API/Subscribers.pm @@ -152,6 +152,10 @@ sub item_rs { $item_rs = $c->model('DB')->resultset('voip_subscribers') ->search({ 'me.status' => { '!=' => 'terminated' } }); if($c->user->roles eq "admin") { + $item_rs = $item_rs->search(undef, + { + join => { 'contract' => 'contact' }, #for filters + }); } elsif($c->user->roles eq "reseller") { $item_rs = $item_rs->search({ 'contact.reseller_id' => $c->user->reseller_id, diff --git a/t/api-rest/api-balanceintervals.t b/t/api-rest/api-balanceintervals.t index 73a0c15b6a..a267531822 100644 --- a/t/api-rest/api-balanceintervals.t +++ b/t/api-rest/api-balanceintervals.t @@ -188,6 +188,21 @@ $res = $ua->request($req); is($res->code, 200, "fetch customer contact 5"); my $custcontact5 = JSON::from_json($res->decoded_content); +$req = HTTP::Request->new('POST', $uri.'/api/customercontacts/'); +$req->header('Content-Type' => 'application/json'); +$req->content(JSON::to_json({ + firstname => "cust_contact_9_first", + lastname => "cust_contact_9_last", + email => "cust_contact9\@custcontact.invalid", + reseller_id => $default_reseller_id, +})); +$res = $ua->request($req); +is($res->code, 201, "create customer contact 9"); +$req = HTTP::Request->new('GET', $uri.'/'.$res->header('Location')); +$res = $ua->request($req); +is($res->code, 200, "fetch customer contact 9"); +my $custcontact9 = JSON::from_json($res->decoded_content); + $req = HTTP::Request->new('POST', $uri.'/api/domains/'); $req->header('Content-Type' => 'application/json'); $req->content(JSON::to_json({ @@ -874,7 +889,7 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { if (_get_allow_delay_commit()) { _set_time(NGCP::Panel::Utils::DateTime::current_local->subtract(months => 3)); - _create_customers_threaded(3,undef,undef,$custcontact5); + _create_customers_threaded(3,undef,undef,$custcontact9); _create_customers_threaded(3,undef,undef,$custcontact2); _set_time(); @@ -883,7 +898,7 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { my $t_a = threads->create(\&_fetch_customerbalances_worker,$delay,'id','asc',$custcontact2); my $t_b = threads->create(\&_fetch_customerbalances_worker,$delay,'id','desc',$custcontact2); - my $t_c = threads->create(\&_fetch_customerbalances_worker,$delay,'id','asc',$custcontact5); + my $t_c = threads->create(\&_fetch_customerbalances_worker,$delay,'id','asc',$custcontact9); my $intervals_a = $t_a->join(); my $intervals_b = $t_b->join(); my $intervals_c = $t_c->join(); @@ -891,7 +906,7 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { #my $got_a = [ sort { $a->{id} <=> $b->{id} } @{ $intervals_b->{_embedded}->{'ngcp:balanceintervals'} } ]; #$a->{contract_id} is($intervals_a->{total_count},3,"check total count of thread a results"); is($intervals_b->{total_count},3,"check total count of thread b results"); - is($intervals_c->{total_count},scalar (grep { $_->{contact_id} == $custcontact5->{id} } values %customer_map),"check total count of thread c results"); + is($intervals_c->{total_count},scalar (grep { $_->{contact_id} == $custcontact9->{id} } values %customer_map),"check total count of thread c results"); my $got_asc = $intervals_a->{_embedded}->{'ngcp:customerbalances'}; my $got_desc = $intervals_b->{_embedded}->{'ngcp:customerbalances'}; if (!is_deeply($got_desc,[ reverse @{ $got_asc } ],'compare customerbalances collection results of threaded requests deeply')) { @@ -921,7 +936,7 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { if (_get_allow_delay_commit()) { _set_time(NGCP::Panel::Utils::DateTime::current_local->subtract(months => 3)); - _create_customers_threaded(3,undef,undef,$custcontact5); + _create_customers_threaded(3,undef,undef,$custcontact9); _create_customers_threaded(3,undef,undef,$custcontact3); _set_time(); @@ -930,14 +945,14 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { my $t_a = threads->create(\&_fetch_intervals_worker,$delay,'id','asc',$custcontact3); my $t_b = threads->create(\&_fetch_intervals_worker,$delay,'id','desc',$custcontact3); - my $t_c = threads->create(\&_fetch_intervals_worker,$delay,'id','desc',$custcontact5); + my $t_c = threads->create(\&_fetch_intervals_worker,$delay,'id','desc',$custcontact9); my $intervals_a = $t_a->join(); my $intervals_b = $t_b->join(); my $intervals_c = $t_c->join(); my $t2 = time; is($intervals_a->{total_count},3,"check total count of thread a results"); is($intervals_b->{total_count},3,"check total count of thread b results"); - is($intervals_c->{total_count},scalar (grep { $_->{contact_id} == $custcontact5->{id} } values %customer_map),"check total count of thread c results"); + is($intervals_c->{total_count},scalar (grep { $_->{contact_id} == $custcontact9->{id} } values %customer_map),"check total count of thread c results"); #my $got_a = [ sort { $a->{id} <=> $b->{id} } @{ $intervals_b->{_embedded}->{'ngcp:balanceintervals'} } ]; #$a->{contract_id} my $got_asc = $intervals_a->{_embedded}->{'ngcp:balanceintervals'}; my $got_desc = $intervals_b->{_embedded}->{'ngcp:balanceintervals'}; @@ -970,21 +985,21 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { if (_get_allow_delay_commit()) { my $package = _create_profile_package('create','month',1,initial_balance => 1, carry_over_mode => 'discard', underrun_lock_threshold => 1, underrun_lock_level => 4); _set_time(NGCP::Panel::Utils::DateTime::from_string('2015-05-17 13:00:00')); - _create_customers_threaded(3,2,$package,$custcontact5); + _create_customers_threaded(3,2,$package,$custcontact9); _create_customers_threaded(3,2,$package,$custcontact4); my $t1 = time; - my $delay = 10.0; #15.0; #10.0; #2.0; + my $delay = 15.0; #15.0; #10.0; #2.0; my $t_a = threads->create(\&_fetch_preferences_worker,$delay,'id','asc',$custcontact4); my $t_b = threads->create(\&_fetch_preferences_worker,$delay,'id','desc',$custcontact4); - my $t_c = threads->create(\&_fetch_preferences_worker,$delay,'id','desc',$custcontact5); + my $t_c = threads->create(\&_fetch_preferences_worker,$delay,'id','desc',$custcontact9); my $prefs_a = $t_a->join(); my $prefs_b = $t_b->join(); my $prefs_c = $t_c->join(); my $t2 = time; is($prefs_a->{total_count},2*3,"check total count of thread a results"); is($prefs_b->{total_count},2*3,"check total count of thread b results"); - is($prefs_c->{total_count},scalar (grep { $customer_map{$_->{customer_id}}->{contact_id} == $custcontact5->{id} } values %subscriber_map),"check total count of thread c results"); + is($prefs_c->{total_count},scalar (grep { $customer_map{$_->{customer_id}}->{contact_id} == $custcontact9->{id} } values %subscriber_map),"check total count of thread c results"); my $got_asc = $prefs_a->{_embedded}->{'ngcp:subscriberpreferences'}; my $got_desc = $prefs_b->{_embedded}->{'ngcp:subscriberpreferences'}; if (!is_deeply($got_desc,[ reverse @{ $got_asc } ],'compare subscriber preference collection results of threaded requests deeply')) { @@ -1002,14 +1017,14 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { $t_a = threads->create(\&_fetch_preferences_worker,$delay,'id','asc',$custcontact4); $t_b = threads->create(\&_fetch_preferences_worker,$delay,'id','desc',$custcontact4); - $t_c = threads->create(\&_fetch_preferences_worker,$delay,'id','desc',$custcontact5); + $t_c = threads->create(\&_fetch_preferences_worker,$delay,'id','desc',$custcontact9); $prefs_a = $t_a->join(); $prefs_b = $t_b->join(); $prefs_c = $t_c->join(); $t2 = time; is($prefs_a->{total_count},2*3,"check total count of thread a results"); is($prefs_b->{total_count},2*3,"check total count of thread b results"); - is($prefs_c->{total_count},scalar (grep { $customer_map{$_->{customer_id}}->{contact_id} == $custcontact5->{id} } values %subscriber_map),"check total count of thread c results"); + is($prefs_c->{total_count},scalar (grep { $customer_map{$_->{customer_id}}->{contact_id} == $custcontact9->{id} } values %subscriber_map),"check total count of thread c results"); $got_asc = $prefs_a->{_embedded}->{'ngcp:subscriberpreferences'}; $got_desc = $prefs_b->{_embedded}->{'ngcp:subscriberpreferences'}; if (!is_deeply($got_desc,[ reverse @{ $got_asc } ],'compare subscriber preference collection results of threaded requests deeply')) { @@ -1053,12 +1068,99 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { } else { diag('allow_delay_commit not set, skipping ...'); } + + if (_get_allow_delay_commit()) { + my $package = _create_profile_package('create','month',1,initial_balance => 1, carry_over_mode => 'discard', underrun_lock_threshold => 1, underrun_lock_level => 4); + _set_time(NGCP::Panel::Utils::DateTime::from_string('2015-05-17 13:00:00')); + _create_customers_threaded(3,2,$package,$custcontact9); + _create_customers_threaded(3,2,$package,$custcontact5); + + my $t1 = time; + my $delay = 15.0; #15.0; #10.0; #2.0; + my $t_a = threads->create(\&_fetch_subscribers_worker,$delay,'id','asc',$custcontact5); + my $t_b = threads->create(\&_fetch_subscribers_worker,$delay,'id','desc',$custcontact5); + my $t_c = threads->create(\&_fetch_subscribers_worker,$delay,'id','desc',$custcontact9); + my $subs_a = $t_a->join(); + my $subs_b = $t_b->join(); + my $subs_c = $t_c->join(); + my $t2 = time; + is($subs_a->{total_count},2*3,"check total count of thread a results"); + is($subs_b->{total_count},2*3,"check total count of thread b results"); + is($subs_c->{total_count},scalar (grep { $customer_map{$_->{customer_id}}->{contact_id} == $custcontact9->{id} } values %subscriber_map),"check total count of thread c results"); + my $got_asc = $subs_a->{_embedded}->{'ngcp:subscribers'}; + my $got_desc = $subs_b->{_embedded}->{'ngcp:subscribers'}; + if (!is_deeply($got_desc,[ reverse @{ $got_asc } ],'compare subscriber collection results of threaded requests deeply')) { + diag(Dumper({asc => $got_asc, desc => $got_desc})); + } + ok($t2 - $t1 > 2*$delay,'expected delay to assume subscribers requests were processed after another'); + ok($t2 - $t1 < 3*$delay,'expected delay to assume only required contracts were locked'); + for (my $i = 0; $i < 2*3; $i++) { + is($got_desc->[$i]->{lock},undef,"check if subscriber is unlocked initially"); + } + + _set_time(NGCP::Panel::Utils::DateTime::from_string('2015-06-18 13:00:00')); + + $t1 = time; + + $t_a = threads->create(\&_fetch_subscribers_worker,$delay,'id','asc',$custcontact5); + $t_b = threads->create(\&_fetch_subscribers_worker,$delay,'id','desc',$custcontact5); + $t_c = threads->create(\&_fetch_subscribers_worker,$delay,'id','desc',$custcontact9); + $subs_a = $t_a->join(); + $subs_b = $t_b->join(); + $subs_c = $t_c->join(); + $t2 = time; + is($subs_a->{total_count},2*3,"check total count of thread a results"); + is($subs_b->{total_count},2*3,"check total count of thread b results"); + is($subs_c->{total_count},scalar (grep { $customer_map{$_->{customer_id}}->{contact_id} == $custcontact9->{id} } values %subscriber_map),"check total count of thread c results"); + $got_asc = $subs_a->{_embedded}->{'ngcp:subscribers'}; + $got_desc = $subs_b->{_embedded}->{'ngcp:subscribers'}; + if (!is_deeply($got_desc,[ reverse @{ $got_asc } ],'compare subscriber collection results of threaded requests deeply')) { + diag(Dumper({asc => $got_asc, desc => $got_desc})); + } + ok($t2 - $t1 > 2*$delay,'expected delay to assume subscribers requests were processed after another'); + ok($t2 - $t1 < 3*$delay,'expected delay to assume only required contracts were locked'); + for (my $i = 0; $i < 2*3; $i++) { + is($got_desc->[$i]->{lock},4,"check if subscriber is locked now"); + } + + $t1 = time; + $t_a = threads->create(\&_fetch_subscribers_worker,$delay,'id','asc',$custcontact5); + $t_b = threads->create(\&_fetch_subscribers_worker,$delay,'id','desc',$custcontact5); + $t_c = threads->create(\&_fetch_subscribers_worker,$delay,'id','desc',$custcontact5); + $subs_a = $t_a->join(); + $subs_b = $t_b->join(); + $subs_c = $t_c->join(); + $t2 = time; + + is($subs_a->{total_count},2*3,"check total count of thread a results"); + is($subs_b->{total_count},2*3,"check total count of thread b results"); + is($subs_c->{total_count},2*3,"check total count of thread c results"); + ok($t2 - $t1 > 3*$delay,'expected delay to assume subscribers requests were processed after another'); + + $t1 = time; + $t_a = threads->create(\&_fetch_subscribers_worker,$delay,'id','asc',$custcontact5); + sleep($delay/2.0); + my $last_customer_id = shift(@{[sort {$b <=> $a} keys %customer_map]}); + _check_interval_history($customer_map{$last_customer_id},[ + { start => '2015-05-17 00:00:00', stop => '2015-06-16 23:59:59', cash => 0.01, package_id => $package->{id}, profile => $billingprofile->{id} }, + { start => '2015-06-17 00:00:00', stop => '2015-07-16 23:59:59', cash => 0, package_id => $package->{id}, profile => $billingprofile->{id} }, + ]); + $t2 = time; + $t_a->join(); + + ok($t2 - $t1 > $delay,'expected delay to assume subscribers request locks contracts and an simultaneous access to contract id ' . $last_customer_id . ' is serialized'); + + _set_time(); + + } else { + diag('allow_delay_commit not set, skipping ...'); + } } else { diag('allow_fake_client_time not set, skipping ...'); } -for my $custcontact ($custcontact1,$custcontact2,$custcontact3,$custcontact4,$custcontact5) { +for my $custcontact ($custcontact1,$custcontact2,$custcontact3,$custcontact4,$custcontact9) { { #test balanceintervals root collection and item _create_customers_threaded(3,undef,undef,$custcontact); # unless _get_allow_fake_client_time() && $enable_profile_packages; @@ -1692,6 +1794,8 @@ sub _create_subscriber { is($res->code, 201, "POST test subscriber"); my $request = $req; $req = HTTP::Request->new('GET', $uri.'/'.$res->header('Location')); + $req->header('X-Fake-Clienttime' => _get_fake_clienttime_now()); + $req->header('X-Request-Identifier' => $req_identifier) if $req_identifier; $res = $ua->request($req); is($res->code, 200, "fetch POSTed test subscriber"); my $subscriber = JSON::from_json($res->decoded_content); @@ -1799,6 +1903,21 @@ sub _fetch_preferences_worker { return $result; } +sub _fetch_subscribers_worker { + my ($delay,$sort_column,$dir,$custcontact) = @_; + diag("starting thread " . threads->tid() . " ..."); + $req = HTTP::Request->new('GET', $uri.'/api/subscribers/?order_by='.$sort_column.'&order_by_direction='.$dir.'&contact_id='.$custcontact->{id}.'&rows='.(scalar keys %subscriber_map)); + $req->header('X-Fake-Clienttime' => _get_fake_clienttime_now()); + $req->header('X-Request-Identifier' => $req_identifier) if $req_identifier; + $req->header('X-Delay-Commit' => $delay); + $res = $ua->request($req); + is($res->code, 200, "thread " . threads->tid() . ": concurrent fetch subscribers of contracts of contact id ".$custcontact->{id} . " in " . $dir . " order"); + my $result = JSON::from_json($res->decoded_content); + #is($result->{total_count},(scalar keys %subscriber_map),"check total count"); + diag("finishing thread " . threads->tid() . " ..."); + return $result; +} + sub _record_request { my ($label,$request,$req_data,$res_data) = @_; if ($tb) {