Skip to content

Commit

Permalink
test: adding an integration test for the account deletion feature (#8723
Browse files Browse the repository at this point in the history
)

adding a test for the user deletion feature in which :

- a common user is created
- the user adds a product then deletes his account
- an admin user is created and checks if the user account is well deleted (checking the edit preferences page and contributor page)
- the admin also checks if the product added by the user is anonymized (the product should display "anonymous" as creator)
- checking that no one can log in with the user logs

---------

Co-authored-by: Stéphane Gigandet <[email protected]>
Co-authored-by: Alex Garel <[email protected]>
  • Loading branch information
3 people authored Aug 31, 2023
1 parent 878d48c commit 33602a8
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 22 deletions.
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions cgi/import_file_job_status.pl
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion cgi/import_file_process.pl
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
4 changes: 3 additions & 1 deletion cgi/import_products_categories_from_public_database.pl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions cgi/minion_job_status.pl
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
63 changes: 62 additions & 1 deletion lib/ProductOpener/APITest.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
}
Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;
42 changes: 31 additions & 11 deletions lib/ProductOpener/Producers.pm
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ BEGIN {
use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS);
@EXPORT_OK = qw(
$minion
&get_minion
&load_csv_or_excel_file
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
1 change: 0 additions & 1 deletion lib/startup_apache2.pl
Original file line number Diff line number Diff line change
Expand Up @@ -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/;
Expand Down
103 changes: 103 additions & 0 deletions tests/integration/delete_user.t
Original file line number Diff line number Diff line change
@@ -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 => '[email protected]'));
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 => '[email protected]',
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&[email protected]', "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();

0 comments on commit 33602a8

Please sign in to comment.