diff --git a/Makefile b/Makefile index 28b7f570080ef..2fe2a8485401c 100644 --- a/Makefile +++ b/Makefile @@ -252,7 +252,7 @@ integration_test: # we launch the server and run tests within same container # we also need dynamicfront for some assets to exists # this is the place where variables are important - ${DOCKER_COMPOSE_TEST} up -d memcached postgres mongodb backend dynamicfront incron + ${DOCKER_COMPOSE_TEST} up -d memcached postgres mongodb backend dynamicfront incron minion # note: we need the -T option for ci (non tty environment) ${DOCKER_COMPOSE_TEST} exec ${COVER_OPTS} -T backend prove -l -r tests/integration ${DOCKER_COMPOSE_TEST} stop @@ -271,9 +271,9 @@ test-unit: guard-test ${DOCKER_COMPOSE_TEST} run --rm backend perl ${args} tests/unit/${test} # usage: make test-int test=test-name.t -test-int: guard-test # usage: make test-one test=test-file.t +test-int: guard-test # usage: make test-int test=test-file.t @echo "🥫 Running test: 'tests/integration/${test}' …" - ${DOCKER_COMPOSE_TEST} up -d memcached postgres mongodb backend dynamicfront incron + ${DOCKER_COMPOSE_TEST} up -d memcached postgres mongodb backend dynamicfront incron minion ${DOCKER_COMPOSE_TEST} exec backend perl ${args} tests/integration/${test} # better shutdown, for if we do a modification of the code, we need a restart ${DOCKER_COMPOSE_TEST} stop backend diff --git a/cgi/import_file_job_status.pl b/cgi/import_file_job_status.pl index 49f10379b7e48..48708ceb9a5a3 100755 --- a/cgi/import_file_job_status.pl +++ b/cgi/import_file_job_status.pl @@ -97,12 +97,12 @@ if (not $data{error}) { - my $job = $minion->job($job_id); + my $job = get_minion()->job($job_id); # Get Minion::Job object without making any changes to the actual job or return undef if job does not exist. # Check job info $log->debug("import_file_job_status.pl - get job_info", {data => \%data}) if $log->is_debug(); - $data{job_info} = $minion->job($job_id)->info; + $data{job_info} = get_minion()->job($job_id)->info; } my $data = encode_json(\%data); diff --git a/cgi/import_file_process.pl b/cgi/import_file_process.pl index ce365841282ed..516faadccd501 100755 --- a/cgi/import_file_process.pl +++ b/cgi/import_file_process.pl @@ -194,7 +194,7 @@ $args_ref->{no_source} = 1; } -my $job_id = $minion->enqueue(import_csv_file => [$args_ref] => {queue => $server_options{minion_local_queue}}); +my $job_id = get_minion()->enqueue(import_csv_file => [$args_ref] => {queue => $server_options{minion_local_queue}}); $import_files_ref->{$file_id}{imports}{$import_id}{job_id} = $job_id; diff --git a/cgi/import_products_categories_from_public_database.pl b/cgi/import_products_categories_from_public_database.pl index f4eb66436aa78..85f670fb72d2c 100755 --- a/cgi/import_products_categories_from_public_database.pl +++ b/cgi/import_products_categories_from_public_database.pl @@ -78,7 +78,9 @@ import_id => $import_id, }; - my $job_id = $minion->enqueue(import_products_categories_from_public_database => [$args_ref] => + my $job_id + = get_minion() + ->enqueue(import_products_categories_from_public_database => [$args_ref] => {queue => $server_options{minion_local_queue}}); $template_data_ref->{import_id} = $import_id; diff --git a/cgi/minion_job_status.pl b/cgi/minion_job_status.pl index ebd963b6d030d..0fdfae419e79e 100755 --- a/cgi/minion_job_status.pl +++ b/cgi/minion_job_status.pl @@ -65,13 +65,13 @@ if (not $data{error}) { - my $job = $minion->job($job_id); + my $job = get_minion()->job($job_id); # Get Minion::Job object without making any changes to the actual job or return undef if job does not exist. # Check job info $log->debug("minion_job_status.pl - get job_info", {data => \%data}) if $log->is_debug(); - $data{job_info} = $minion->job($job_id)->info; + $data{job_info} = get_minion()->job($job_id)->info; } my $data = encode_json(\%data); diff --git a/lib/ProductOpener/APITest.pm b/lib/ProductOpener/APITest.pm index b282a7870753e..5a6bb497f6619 100644 --- a/lib/ProductOpener/APITest.pm +++ b/lib/ProductOpener/APITest.pm @@ -53,6 +53,7 @@ BEGIN { &execute_api_tests &wait_server &fake_http_server + &get_minion_jobs ); # symbols to export on request %EXPORT_TAGS = (all => [@EXPORT_OK]); } @@ -61,8 +62,9 @@ use vars @EXPORT_OK; use ProductOpener::TestDefaults qw/:all/; use ProductOpener::Test qw/:all/; -use ProductOpener::Mail qw/ $LOG_EMAIL_START $LOG_EMAIL_END /; +use ProductOpener::Mail qw/$LOG_EMAIL_START $LOG_EMAIL_END/; use ProductOpener::Store qw/store retrieve/; +use ProductOpener::Producers qw/get_minion/; use Test::More; use LWP::UserAgent; @@ -74,6 +76,7 @@ use Carp qw/confess/; use Clone qw/clone/; use File::Tail; use Test::Fake::HTTPD; +use Minion; # Constants of the test website main domain and url # Should be used internally only (see: construct_test_url to build urls in tests) @@ -768,4 +771,62 @@ sub fake_http_server ($port, $dump_path, $responses_ref) { return $httpd; } +=head2 get_minion_jobs($task_name, $created_after_ts, $max_waiting_time) +Subprogram which wait till the minion finished its job or +if it takes too much time + +=head3 Arguments + +=head4 $task_name +The name of the task + +=head4 $created_after_ts +The timestamp of the creation of the task + +=head4 $max_waiting_time +The max waiting time for this given task + +=head3 Returns +Returns a list of jobs associated with the task_name + +=cut + +sub get_minion_jobs ($task_name, $created_after_ts, $max_waiting_time) { + my $waited = 0; # counting the waiting time + my %run_jobs = (); + while (($waited < $max_waiting_time) and (!(scalar %run_jobs))) { + my $jobs = get_minion()->jobs({tasks => [$task_name]}); + # iterate on job + while (my $job = $jobs->next) { + next if (defined $run_jobs{$job->{id}}); + # only those who were created after the timestamp + if ($job->{created} > $created_after_ts) { + # retrieving the job id + my $job_id = $job->{id}; + # we have a dict, get the object to be consistent with case when we wait + my $job = get_minion()->job($job_id); + # retrieving the job state + my $job_state = $job->info->{state}; + # waiting the job to be done + while (($job_state eq "active") or ($job_state eq "inactive")) { + sleep(2); + $waited += 2; + # reload to get updated state + $job = get_minion()->job($job_id); + $job_state = $job->info->{state}; + } + $run_jobs{$job_id} = $job; + } + } + if (!(scalar %run_jobs)) { + # try to wait for jobs + sleep(2); + $waited += 2; + } + } + # sort by creation date to have jobs in predictable order + my @all_jobs = sort {$_->info->{created}} (values %run_jobs); + return \@all_jobs; +} + 1; diff --git a/lib/ProductOpener/Producers.pm b/lib/ProductOpener/Producers.pm index d87063cca8386..8a821ab2c5c16 100644 --- a/lib/ProductOpener/Producers.pm +++ b/lib/ProductOpener/Producers.pm @@ -46,7 +46,7 @@ BEGIN { use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS); @EXPORT_OK = qw( - $minion + &get_minion &load_csv_or_excel_file @@ -96,14 +96,30 @@ use Text::CSV(); use Minion; # Minion backend +my $minion; -if (not defined $server_options{minion_backend}) { +=head2 get_minion() +Function to get the backend minion - print STDERR "No Minion backend configured in lib/ProductOpener/Config2.pm\n"; -} -else { - print STDERR "Initializing Minion backend configured in lib/ProductOpener/Config2.pm\n"; - $minion = Minion->new(%{$server_options{minion_backend}}); +=head3 Arguments +None + +=head3 Return values +The backend minion $minion + +=cut + +sub get_minion() { + if (not defined $minion) { + if (not defined $server_options{minion_backend}) { + print STDERR "No Minion backend configured in lib/ProductOpener/Config2.pm\n"; + } + else { + print STDERR "Initializing Minion backend configured in lib/ProductOpener/Config2.pm\n"; + $minion = Minion->new(%{$server_options{minion_backend}}); + } + } + return $minion; } =head1 FUNCTIONS @@ -1840,18 +1856,22 @@ sub export_and_import_to_public_database ($args_ref) { # Local export my $local_export_job_id - = $minion->enqueue(export_csv_file => [$args_ref] => {queue => $server_options{minion_local_queue}}); + = get_minion()->enqueue(export_csv_file => [$args_ref] => {queue => $server_options{minion_local_queue}}); $args_ref->{export_job_id} = $local_export_job_id; # Remote import - my $remote_import_job_id = $minion->enqueue(import_csv_file => [$args_ref] => + my $remote_import_job_id + = get_minion() + ->enqueue(import_csv_file => [$args_ref] => {queue => $server_options{minion_export_queue}, parents => [$local_export_job_id]}); # Local export status update - my $local_export_status_job_id = $minion->enqueue(update_export_status_for_csv_file => [$args_ref] => + my $local_export_status_job_id + = get_minion() + ->enqueue(update_export_status_for_csv_file => [$args_ref] => {queue => $server_options{minion_local_queue}, parents => [$remote_import_job_id]}); $exports_ref->{$export_id}{local_export_job_id} = $local_export_job_id; @@ -2009,7 +2029,7 @@ sub update_export_status_for_csv_file_task ($job, $args_ref) { } sub queue_job { ## no critic (Subroutines::RequireArgUnpacking) - return $minion->enqueue(@_); + return get_minion()->enqueue(@_); } 1; diff --git a/lib/startup_apache2.pl b/lib/startup_apache2.pl index 410e5c80ad323..a760770aa464b 100755 --- a/lib/startup_apache2.pl +++ b/lib/startup_apache2.pl @@ -93,7 +93,6 @@ use ProductOpener::MainCountries qw/:all/; use ProductOpener::PackagerCodes qw/:all/; use ProductOpener::API qw/:all/; -use ProductOpener::APITest qw/:all/; use ProductOpener::APIProductRead qw/:all/; use ProductOpener::APIProductWrite qw/:all/; use ProductOpener::APITaxonomySuggestions qw/:all/; diff --git a/tests/integration/delete_user.t b/tests/integration/delete_user.t new file mode 100644 index 0000000000000..30c785314b4f5 --- /dev/null +++ b/tests/integration/delete_user.t @@ -0,0 +1,103 @@ +#!/usr/bin/perl -w + +use ProductOpener::PerlStandards; + +use Test::More; +use ProductOpener::APITest qw/:all/; +use ProductOpener::Test qw/:all/; +use ProductOpener::TestDefaults qw/:all/; +use ProductOpener::Users qw/:all/; +use ProductOpener::Producers qw/:all/; + +remove_all_users(); +wait_application_ready(); + +#new common user agent +my $ua = new_client(); +my %create_client_args = (%default_user_form, (email => 'bob@test.com')); +create_user($ua, \%create_client_args); + +#new admin user agent +my $admin = new_client(); +create_user($admin, \%admin_user_form); + +#common ua add a new product then delete the account while being still logged in +edit_product($ua, \%default_product); + +my $url_userid = construct_test_url("/cgi/user.pl?type=edit&userid=tests", "world"); +my $url_delete = construct_test_url("/cgi/user.pl", "world"); +my $response_edit = $ua->get($url_userid); + +my %delete_form = ( + name => 'Test', + email => 'bob@test.com', + password => '', + confirm_password => '', + delete => 'on', + action => 'process', + type => 'edit', + userid => 'tests' +); + +#checking if the delete button exist +like($response_edit->content, qr/Delete the user/, "the delete button does exist"); + +#deleting the account +my $before_delete_ts = time(); +my $response_delete = $ua->post($url_delete, \%delete_form); +#checking if we are redirected to the account deleted page +like($response_delete->content, qr/User is being deleted\. This may take a few minutes\./, "the account was deleted"); + +#waiting the deletion task to be done (weirdly enough it is not useful anymore..) +my $max_time = 60; +my $jobs_ref = get_minion_jobs("delete_user", $before_delete_ts, $max_time); + +is(scalar @{$jobs_ref}, 1, "One delete_user was triggered"); +my $delete_job_state = $jobs_ref->[0]->info->{state}; +is($delete_job_state, "finished", "delete_user finished without errors"); + +#user sign out of its account +my %signout_form = ( + length => "logout", + ".submit" => "Sign out" +); +my $url_signout = construct_test_url("/cgi/session.pl", "world"); +my $response_signout = $ua->post($url_signout, \%signout_form); + +like($response_signout->content, qr/See you soon\!/, "the user signed out"); + +#admin ua checking if the account is well deleted +my $response_userid = $admin->get($url_userid); +#checking if the edit page of the common ua is well deleted +like($response_userid->content, qr/Invalid user\./, "the userid edit page is well deleted"); + +my $url_email = construct_test_url('/cgi/user.pl?type=edit&userid=bob@test.com', "world"); +my $response_email = $admin->get($url_email); +#checking if the edit page of the common ua is well deleted +like($response_email->content, qr/Invalid user\./, "the email edit page is well deleted"); + +my $url_contributor = construct_test_url("/contributor/tests", "world"); +my $response_contributor = $admin->get($url_contributor); +#checking if the edit page of the common ua is well deleted +like($response_contributor->content, qr/Unknown user\./, "the contributor page of the ua is well deleted"); + +#checking if an ua can reconnect with the deleted account ids +my $url_login = construct_test_url("/cgi/login.pl", "world"); +my %login_form = ( + user_id => "tests", + password => "testtest", + submit => "Sign in" +); +my $response_login = $ua->post($url_login, \%login_form); +like( + $response_login->content, + qr/Incorrect user name or password\./, + "an user can't login with the deleted account ids" +); + +#checking if the added product has been anonymized +my $url_product = construct_test_url("/cgi/product.pl?type=edit&code=2000000000001", "world"); +my $response_product = $admin->get($url_product); +like($response_product->content, qr/\/editor\/anonymous/, "the product has been anonymized"); + +done_testing();