diff options
Diffstat (limited to 't/app/controller')
22 files changed, 2320 insertions, 1758 deletions
diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t index 398ce8ea6..b69a711c8 100644 --- a/t/app/controller/admin.t +++ b/t/app/controller/admin.t @@ -1,27 +1,14 @@ use FixMyStreet::TestMech; -# avoid wide character warnings from the category change message -use open ':std', ':encoding(UTF-8)'; my $mech = FixMyStreet::TestMech->new; my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); -my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2'); - my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); -my $oxfordshirecontact = $mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Potholes', email => 'potholes@example.com' ); -$mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Traffic lights', email => 'lights@example.com' ); my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); -my $oxford = $mech->create_body_ok(2421, 'Oxford City Council'); -$mech->create_contact_ok( body_id => $oxford->id, category => 'Graffiti', email => 'graffiti@example.net' ); - -my $bromley = $mech->create_body_ok(2482, 'Bromley Council'); - -my $user3; - my $dt = DateTime->new( year => 2011, month => 04, @@ -56,6 +43,8 @@ my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( whensent => $dt->ymd . ' ' . $dt->hms, } ); +my $report_id = $report->id; +ok $report, "created test report - $report_id"; my $alert = FixMyStreet::App->model('DB::Alert')->find_or_create( { @@ -138,1433 +127,6 @@ subtest 'check summary counts' => sub { ok $mech->host('www.fixmystreet.com'); }; -# This override is wrapped around ALL the /admin/body tests -FixMyStreet::override_config { - MAPIT_URL => 'http://mapit.uk/', - MAPIT_TYPES => [ 'UTA' ], - BASE_URL => 'http://www.example.org', -}, sub { - -my $body = $mech->create_body_ok(2650, 'Aberdeen City Council'); -$mech->get_ok('/admin/body/' . $body->id); -$mech->content_contains('Aberdeen City Council'); -$mech->content_like(qr{AB\d\d}); -$mech->content_contains("http://www.example.org/around"); - -subtest 'check contact creation' => sub { - $mech->get_ok('/admin/body/' . $body->id); - - $mech->submit_form_ok( { with_fields => { - category => 'test category', - email => 'test@example.com', - note => 'test note', - non_public => undef, - state => 'unconfirmed', - } } ); - - $mech->content_contains( 'test category' ); - $mech->content_contains( 'test@example.com' ); - $mech->content_contains( '<td>test note' ); - $mech->content_like( qr/<td>\s*unconfirmed\s*<\/td>/ ); # No private - - $mech->submit_form_ok( { with_fields => { - category => 'private category', - email => 'test@example.com', - note => 'test note', - non_public => 'on', - } } ); - - $mech->content_contains( 'private category' ); - $mech->content_like( qr{test\@example.com\s*</td>\s*<td>\s*confirmed\s*<br>\s*<small>\s*Private\s*</small>\s*</td>} ); - - $mech->submit_form_ok( { with_fields => { - category => 'test/category', - email => 'test@example.com', - note => 'test/note', - non_public => 'on', - } } ); - $mech->get_ok('/admin/body/' . $body->id . '/test/category'); - $mech->content_contains('<h1>test/category</h1>'); -}; - -subtest 'check contact editing' => sub { - $mech->get_ok('/admin/body/' . $body->id .'/test%20category'); - - $mech->submit_form_ok( { with_fields => { - email => 'test2@example.com', - note => 'test2 note', - non_public => undef, - } } ); - - $mech->content_contains( 'test category' ); - $mech->content_like( qr{test2\@example.com\s*</td>\s*<td>\s*unconfirmed\s*</td>} ); - $mech->content_contains( '<td>test2 note' ); - - $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); - $mech->submit_form_ok( { with_fields => { - email => 'test2@example.com, test3@example.com', - note => 'test3 note', - } } ); - - $mech->content_contains( 'test2@example.com,test3@example.com' ); - - $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); - $mech->content_contains( '<td><strong>test2@example.com,test3@example.com' ); - - $mech->submit_form_ok( { with_fields => { - email => 'test2@example.com', - note => 'test2 note', - non_public => 'on', - } } ); - - $mech->content_like( qr{test2\@example.com\s*</td>\s*<td>\s*unconfirmed\s*<br>\s*<small>\s*Private\s*</small>\s*</td>} ); - - $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); - $mech->content_contains( '<td><strong>test2@example.com' ); -}; - -subtest 'check contact updating' => sub { - $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); - $mech->content_like(qr{test2\@example.com</strong>[^<]*</td>[^<]*<td>unconfirmed}s); - - $mech->get_ok('/admin/body/' . $body->id); - - $mech->form_number( 1 ); - $mech->tick( 'confirmed', 'test category' ); - $mech->submit_form_ok({form_number => 1}); - - $mech->content_like(qr'test2@example.com</td>[^<]*<td>\s*confirmed's); - $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); - $mech->content_like(qr{test2\@example.com[^<]*</td>[^<]*<td><strong>confirmed}s); -}; - -$body->update({ send_method => undef }); - -subtest 'check open311 configuring' => sub { - $mech->get_ok('/admin/body/' . $body->id); - $mech->content_lacks('Council contacts configured via Open311'); - - $mech->form_number(3); - $mech->submit_form_ok( - { - with_fields => { - api_key => 'api key', - endpoint => 'http://example.com/open311', - jurisdiction => 'mySociety', - send_comments => 0, - send_method => 'Open311', - } - } - ); - $mech->content_contains('Council contacts configured via Open311'); - $mech->content_contains('Values updated'); - - my $conf = FixMyStreet::App->model('DB::Body')->find( $body->id ); - is $conf->endpoint, 'http://example.com/open311', 'endpoint configured'; - is $conf->api_key, 'api key', 'api key configured'; - is $conf->jurisdiction, 'mySociety', 'jurisdiction configures'; - - $mech->form_number(3); - $mech->submit_form_ok( - { - with_fields => { - api_key => 'new api key', - endpoint => 'http://example.org/open311', - jurisdiction => 'open311', - send_comments => 0, - send_method => 'Open311', - } - } - ); - - $mech->content_contains('Values updated'); - - $conf = FixMyStreet::App->model('DB::Body')->find( $body->id ); - is $conf->endpoint, 'http://example.org/open311', 'endpoint updated'; - is $conf->api_key, 'new api key', 'api key updated'; - is $conf->jurisdiction, 'open311', 'jurisdiction configures'; -}; - -subtest 'check text output' => sub { - $mech->get_ok('/admin/body/' . $body->id . '?text=1'); - is $mech->content_type, 'text/plain'; - $mech->content_contains('test category'); - $mech->content_lacks('<body'); -}; - - -}; # END of override wrap - - -my $log_entries = FixMyStreet::App->model('DB::AdminLog')->search( - { - object_type => 'problem', - object_id => $report->id - }, - { - order_by => { -desc => 'id' }, - } -); - -is $log_entries->count, 0, 'no admin log entries'; - -my $report_id = $report->id; -ok $report, "created test report - $report_id"; - -foreach my $test ( - { - description => 'edit report title', - fields => { - title => 'Report to Edit', - detail => 'Detail for Report to Edit', - state => 'confirmed', - name => 'Test User', - username => $user->email, - anonymous => 0, - flagged => undef, - non_public => undef, - }, - changes => { title => 'Edited Report', }, - log_entries => [qw/edit/], - resend => 0, - }, - { - description => 'edit report description', - fields => { - title => 'Edited Report', - detail => 'Detail for Report to Edit', - state => 'confirmed', - name => 'Test User', - username => $user->email, - anonymous => 0, - flagged => undef, - non_public => undef, - }, - changes => { detail => 'Edited Detail', }, - log_entries => [qw/edit edit/], - resend => 0, - }, - { - description => 'edit report user name', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'confirmed', - name => 'Test User', - username => $user->email, - anonymous => 0, - flagged => undef, - non_public => undef, - }, - changes => { name => 'Edited User', }, - log_entries => [qw/edit edit edit/], - resend => 0, - user => $user, - }, - { - description => 'edit report set flagged true', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'confirmed', - name => 'Edited User', - username => $user->email, - anonymous => 0, - flagged => undef, - non_public => undef, - }, - changes => { - flagged => 'on', - }, - log_entries => [qw/edit edit edit edit/], - resend => 0, - user => $user, - }, - { - description => 'edit report user email', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'confirmed', - name => 'Edited User', - username => $user->email, - anonymous => 0, - flagged => 'on', - non_public => undef, - }, - changes => { username => $user2->email, }, - log_entries => [qw/edit edit edit edit edit/], - resend => 0, - user => $user2, - }, - { - description => 'change state to unconfirmed', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'confirmed', - name => 'Edited User', - username => $user2->email, - anonymous => 0, - flagged => 'on', - non_public => undef, - }, - expect_comment => 1, - changes => { state => 'unconfirmed' }, - log_entries => [qw/edit state_change edit edit edit edit edit/], - resend => 0, - }, - { - description => 'change state to confirmed', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'unconfirmed', - name => 'Edited User', - username => $user2->email, - anonymous => 0, - flagged => 'on', - non_public => undef, - }, - expect_comment => 1, - changes => { state => 'confirmed' }, - log_entries => [qw/edit state_change edit state_change edit edit edit edit edit/], - resend => 0, - }, - { - description => 'change state to fixed', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'confirmed', - name => 'Edited User', - username => $user2->email, - anonymous => 0, - flagged => 'on', - non_public => undef, - }, - expect_comment => 1, - changes => { state => 'fixed' }, - log_entries => - [qw/edit state_change edit state_change edit state_change edit edit edit edit edit/], - resend => 0, - }, - { - description => 'change state to hidden', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'fixed', - name => 'Edited User', - username => $user2->email, - anonymous => 0, - flagged => 'on', - non_public => undef, - }, - expect_comment => 1, - changes => { state => 'hidden' }, - log_entries => [ - qw/edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ - ], - resend => 0, - }, - { - description => 'edit and change state', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'hidden', - name => 'Edited User', - username => $user2->email, - anonymous => 0, - flagged => 'on', - non_public => undef, - }, - expect_comment => 1, - changes => { - state => 'confirmed', - anonymous => 1, - }, - log_entries => [ - qw/edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ - ], - resend => 0, - }, - { - description => 'resend', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'confirmed', - name => 'Edited User', - username => $user2->email, - anonymous => 1, - flagged => 'on', - non_public => undef, - }, - changes => {}, - log_entries => [ - qw/resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ - ], - resend => 1, - }, - { - description => 'non public', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'confirmed', - name => 'Edited User', - username => $user2->email, - anonymous => 1, - flagged => 'on', - non_public => undef, - }, - changes => { - non_public => 'on', - }, - log_entries => [ - qw/edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ - ], - resend => 0, - }, - { - description => 'change state to investigating as body superuser', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'confirmed', - name => 'Edited User', - username => $user2->email, - anonymous => 1, - flagged => 'on', - non_public => 'on', - }, - expect_comment => 1, - user_body => $oxfordshire, - changes => { state => 'investigating' }, - log_entries => [ - qw/edit state_change edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ - ], - resend => 0, - }, - { - description => 'change state to in progess and change category as body superuser', - fields => { - title => 'Edited Report', - detail => 'Edited Detail', - state => 'investigating', - name => 'Edited User', - username => $user2->email, - anonymous => 1, - flagged => 'on', - non_public => 'on', - }, - expect_comment => 1, - expected_text => '*Category changed from ‘Other’ to ‘Potholes’*', - user_body => $oxfordshire, - changes => { state => 'in progress', category => 'Potholes' }, - log_entries => [ - qw/edit state_change edit state_change edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ - ], - resend => 0, - }, - ) -{ - subtest $test->{description} => sub { - $report->comments->delete; - $log_entries->reset; - - if ( $test->{user_body} ) { - $superuser->from_body( $test->{user_body}->id ); - $superuser->update; - } - - $mech->get_ok("/admin/report_edit/$report_id"); - - @{$test->{fields}}{'external_id', 'external_body', 'external_team', 'category'} = (13, "", "", "Other"); - is_deeply( $mech->visible_form_values(), $test->{fields}, 'initial form values' ); - - my $new_fields = { - %{ $test->{fields} }, - %{ $test->{changes} }, - }; - - if ( $test->{resend} ) { - $mech->click_ok( 'resend' ); - } else { - $mech->submit_form_ok( { with_fields => $new_fields }, 'form_submitted' ); - } - - is_deeply( $mech->visible_form_values(), $new_fields, 'changed form values' ); - is $log_entries->count, scalar @{$test->{log_entries}}, 'log entry count'; - is $log_entries->next->action, $_, 'log entry added' for @{ $test->{log_entries} }; - - $report->discard_changes; - - if ($report->state eq 'confirmed' && $report->whensent) { - $mech->content_contains( 'type="submit" name="resend"', 'resend button' ); - } else { - $mech->content_lacks( 'type="submit" name="resend"', 'no resend button' ); - } - - $test->{changes}->{flagged} = 1 if $test->{changes}->{flagged}; - $test->{changes}->{non_public} = 1 if $test->{changes}->{non_public}; - - is $report->$_, $test->{changes}->{$_}, "$_ updated" for grep { $_ ne 'username' } keys %{ $test->{changes} }; - - if ( $test->{user} ) { - is $report->user->id, $test->{user}->id, 'user changed'; - } - - if ( $test->{resend} ) { - $mech->content_contains( 'That problem will now be resent' ); - is $report->whensent, undef, 'mark report to resend'; - } - - if ( $test->{expect_comment} ) { - my $comment = $report->comments->first; - ok $comment, 'report status change creates comment'; - is $report->comments->count, 1, 'report only has one comment'; - if ($test->{expected_text}) { - is $comment->text, $test->{expected_text}, 'comment has expected text'; - } else { - is $comment->text, '', 'comment has no text'; - } - if ( $test->{user_body} ) { - ok $comment->get_extra_metadata('is_body_user'), 'body user metadata set'; - ok !$comment->get_extra_metadata('is_superuser'), 'superuser metadata not set'; - is $comment->name, $test->{user_body}->name, 'comment name is body name'; - } else { - ok !$comment->get_extra_metadata('is_body_user'), 'body user metadata not set'; - ok $comment->get_extra_metadata('is_superuser'), 'superuser metadata set'; - is $comment->name, _('an administrator'), 'comment name is admin'; - } - } else { - is $report->comments->count, 0, 'report has no comments'; - } - - $superuser->from_body(undef); - $superuser->update; - }; -} - -FixMyStreet::override_config { - ALLOWED_COBRANDS => 'fixmystreet', -}, sub { - -subtest 'change report category' => sub { - my ($ox_report) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Unsure', { - category => 'Potholes', - areas => ',2237,2421,', # Cached used by categories_for_point... - latitude => 51.7549262252, - longitude => -1.25617899435, - whensent => \'current_timestamp', - }); - $mech->get_ok("/admin/report_edit/" . $ox_report->id); - - $mech->submit_form_ok( { with_fields => { category => 'Traffic lights' } }, 'form_submitted' ); - $ox_report->discard_changes; - is $ox_report->category, 'Traffic lights'; - isnt $ox_report->whensent, undef; - is $ox_report->comments->count, 1, "Comment created for update"; - is $ox_report->comments->first->text, '*Category changed from ‘Potholes’ to ‘Traffic lights’*', 'Comment text correct'; - - $mech->submit_form_ok( { with_fields => { category => 'Graffiti' } }, 'form_submitted' ); - $ox_report->discard_changes; - is $ox_report->category, 'Graffiti'; - is $ox_report->whensent, undef; -}; - -}; - -subtest 'change email to new user' => sub { - $log_entries->delete; - $mech->get_ok("/admin/report_edit/$report_id"); - my $fields = { - title => $report->title, - detail => $report->detail, - state => $report->state, - name => $report->name, - username => $report->user->email, - category => 'Potholes', - anonymous => 1, - flagged => 'on', - non_public => 'on', - external_id => '13', - external_body => '', - external_team => '', - }; - - is_deeply( $mech->visible_form_values(), $fields, 'initial form values' ); - - my $changes = { - username => 'test3@example.com' - }; - - $user3 = FixMyStreet::App->model('DB::User')->find( { email => 'test3@example.com' } ); - - ok !$user3, 'user not in database'; - - my $new_fields = { - %{ $fields }, - %{ $changes }, - }; - - $mech->submit_form_ok( - { - with_fields => $new_fields, - } - ); - - is $log_entries->count, 1, 'created admin log entries'; - is $log_entries->first->action, 'edit', 'log action'; - is_deeply( $mech->visible_form_values(), $new_fields, 'changed form values' ); - - $user3 = FixMyStreet::App->model('DB::User')->find( { email => 'test3@example.com' } ); - - $report->discard_changes; - - ok $user3, 'new user created'; - is $report->user_id, $user3->id, 'user changed to new user'; -}; - -subtest 'adding email to abuse list from report page' => sub { - my $email = $report->user->email; - - my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); - $abuse->delete if $abuse; - - $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->content_contains('Ban user'); - - $mech->click_ok('banuser'); - - $mech->content_contains('User added to abuse list'); - $mech->content_contains('<small>User in abuse table</small>'); - - $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); - ok $abuse, 'entry created in abuse table'; - - $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->content_contains('<small>User in abuse table</small>'); -}; - -subtest 'remove user from abuse list from edit user page' => sub { - my $abuse = FixMyStreet::App->model('DB::Abuse')->find_or_create( { email => $user->email } ); - $mech->get_ok( '/admin/user_edit/' . $user->id ); - $mech->content_contains('User in abuse table'); - - $mech->click_ok('unban'); - - $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $user->email } ); - ok !$abuse, 'record removed from abuse table'; -}; - -subtest 'remove user with phone account from abuse list from edit user page' => sub { - my $abuse_user = $mech->create_user_ok('01234 456789'); - my $abuse = FixMyStreet::App->model('DB::Abuse')->find_or_create( { email => $abuse_user->phone } ); - $mech->get_ok( '/admin/user_edit/' . $abuse_user->id ); - $mech->content_contains('User in abuse table'); - my $abuse_found = FixMyStreet::App->model('DB::Abuse')->find( { email => $abuse_user->phone } ); - ok $abuse_found, 'user in abuse table'; - - $mech->click_ok('unban'); - - $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $user->phone } ); - ok !$abuse, 'record removed from abuse table'; -}; - -subtest 'no option to remove user already in abuse list' => sub { - my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $user->email } ); - $abuse->delete if $abuse; - $mech->get_ok( '/admin/user_edit/' . $user->id ); - $mech->content_lacks('User in abuse table'); -}; - -subtest 'flagging user from report page' => sub { - $report->user->flagged(0); - $report->user->update; - - $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->content_contains('Flag user'); - - $mech->click_ok('flaguser'); - - $mech->content_contains('User flagged'); - $mech->content_contains('Remove flag'); - - $report->discard_changes; - ok $report->user->flagged, 'user flagged'; - - $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->content_contains('Remove flag'); -}; - -subtest 'unflagging user from report page' => sub { - $report->user->flagged(1); - $report->user->update; - - $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->content_contains('Remove flag'); - - $mech->click_ok('removeuserflag'); - - $mech->content_contains('User flag removed'); - $mech->content_contains('Flag user'); - - $report->discard_changes; - ok !$report->user->flagged, 'user not flagged'; - - $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->content_contains('Flag user'); -}; - -$log_entries->delete; - -my $update = FixMyStreet::App->model('DB::Comment')->create( - { - text => 'this is an update', - user => $user, - state => 'confirmed', - problem => $report, - mark_fixed => 0, - anonymous => 1, - } -); - -$log_entries = FixMyStreet::App->model('DB::AdminLog')->search( - { - object_type => 'update', - object_id => $update->id - }, - { - order_by => { -desc => 'id' }, - } -); - -is $log_entries->count, 0, 'no admin log entries'; - -for my $test ( - { - desc => 'edit update text', - fields => { - text => 'this is an update', - state => 'confirmed', - name => '', - anonymous => 1, - username => 'test@example.com', - }, - changes => { - text => 'this is a changed update', - }, - log_count => 1, - log_entries => [qw/edit/], - }, - { - desc => 'edit update name', - fields => { - text => 'this is a changed update', - state => 'confirmed', - name => '', - anonymous => 1, - username => 'test@example.com', - }, - changes => { - name => 'A User', - }, - log_count => 2, - log_entries => [qw/edit edit/], - }, - { - desc => 'edit update anonymous', - fields => { - text => 'this is a changed update', - state => 'confirmed', - name => 'A User', - anonymous => 1, - username => 'test@example.com', - }, - changes => { - anonymous => 0, - }, - log_count => 3, - log_entries => [qw/edit edit edit/], - }, - { - desc => 'edit update user', - fields => { - text => 'this is a changed update', - state => 'confirmed', - name => 'A User', - anonymous => 0, - username => 'test@example.com', - }, - changes => { - username => 'test2@example.com', - }, - log_count => 4, - log_entries => [qw/edit edit edit edit/], - user => $user2, - }, - { - desc => 'edit update state', - fields => { - text => 'this is a changed update', - state => 'confirmed', - name => 'A User', - anonymous => 0, - username => 'test2@example.com', - }, - changes => { - state => 'unconfirmed', - }, - log_count => 5, - log_entries => [qw/state_change edit edit edit edit/], - }, - { - desc => 'edit update state and text', - fields => { - text => 'this is a changed update', - state => 'unconfirmed', - name => 'A User', - anonymous => 0, - username => 'test2@example.com', - }, - changes => { - text => 'this is a twice changed update', - state => 'confirmed', - }, - log_count => 7, - log_entries => [qw/edit state_change state_change edit edit edit edit/], - }, -) { - subtest $test->{desc} => sub { - $log_entries->reset; - $mech->get_ok('/admin/update_edit/' . $update->id ); - - is_deeply $mech->visible_form_values, $test->{fields}, 'initial form values'; - - my $to_submit = { - %{ $test->{fields} }, - %{ $test->{changes} } - }; - - $mech->submit_form_ok( { with_fields => $to_submit } ); - - is_deeply $mech->visible_form_values, $to_submit, 'submitted form values'; - - is $log_entries->count, $test->{log_count}, 'number of log entries'; - is $log_entries->next->action, $_, 'log action' for @{ $test->{log_entries} }; - - $update->discard_changes; - - is $update->$_, $test->{changes}->{$_} for grep { $_ ne 'username' } keys %{ $test->{changes} }; - if ( $test->{changes}{state} && $test->{changes}{state} eq 'confirmed' ) { - isnt $update->confirmed, undef; - } - - if ( $test->{user} ) { - is $update->user->id, $test->{user}->id, 'update user'; - } - }; -} - -my $westminster = $mech->create_body_ok(2504, 'Westminster City Council'); -$report->bodies_str($westminster->id); -$report->update; - -for my $test ( - { - desc => 'user is problem owner', - problem_user => $user, - update_user => $user, - update_fixed => 0, - update_reopen => 0, - update_state => undef, - user_body => undef, - content => 'user is problem owner', - }, - { - desc => 'user is body user', - problem_user => $user, - update_user => $user2, - update_fixed => 0, - update_reopen => 0, - update_state => undef, - user_body => $westminster->id, - content => 'user is from same council as problem - ' . $westminster->id, - }, - { - desc => 'update changed problem state', - problem_user => $user, - update_user => $user2, - update_fixed => 0, - update_reopen => 0, - update_state => 'planned', - user_body => $westminster->id, - content => 'Update changed problem state to planned', - }, - { - desc => 'update marked problem as fixed', - problem_user => $user, - update_user => $user3, - update_fixed => 1, - update_reopen => 0, - update_state => undef, - user_body => undef, - content => 'Update marked problem as fixed', - }, - { - desc => 'update reopened problem', - problem_user => $user, - update_user => $user, - update_fixed => 0, - update_reopen => 1, - update_state => undef, - user_body => undef, - content => 'Update reopened problem', - }, -) { - subtest $test->{desc} => sub { - $report->user( $test->{problem_user} ); - $report->update; - - $update->user( $test->{update_user} ); - $update->problem_state( $test->{update_state} ); - $update->mark_fixed( $test->{update_fixed} ); - $update->mark_open( $test->{update_reopen} ); - $update->update; - - $test->{update_user}->from_body( $test->{user_body} ); - $test->{update_user}->update; - - $mech->get_ok('/admin/update_edit/' . $update->id ); - $mech->content_contains( $test->{content} ); - }; -} - -subtest 'editing update email creates new user if required' => sub { - my $user = FixMyStreet::App->model('DB::User')->find( { email => 'test4@example.com' } ); - - $user->delete if $user; - - my $fields = { - text => 'this is a changed update', - state => 'hidden', - name => 'A User', - anonymous => 0, - username => 'test4@example.com', - }; - - $mech->submit_form_ok( { with_fields => $fields } ); - - $user = FixMyStreet::App->model('DB::User')->find( { email => 'test4@example.com' } ); - - is_deeply $mech->visible_form_values, $fields, 'submitted form values'; - - ok $user, 'new user created'; - - $update->discard_changes; - is $update->user->id, $user->id, 'update set to new user'; -}; - -subtest 'adding email to abuse list from update page' => sub { - my $email = $update->user->email; - - my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); - $abuse->delete if $abuse; - - $mech->get_ok( '/admin/update_edit/' . $update->id ); - $mech->content_contains('Ban user'); - - $mech->click_ok('banuser'); - - $mech->content_contains('User added to abuse list'); - $mech->content_contains('<small>User in abuse table</small>'); - - $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); - ok $abuse, 'entry created in abuse table'; - - $mech->get_ok( '/admin/update_edit/' . $update->id ); - $mech->content_contains('<small>User in abuse table</small>'); -}; - -subtest 'flagging user from update page' => sub { - $update->user->flagged(0); - $update->user->update; - - $mech->get_ok( '/admin/update_edit/' . $update->id ); - $mech->content_contains('Flag user'); - - $mech->click_ok('flaguser'); - - $mech->content_contains('User flagged'); - $mech->content_contains('Remove flag'); - - $update->discard_changes; - ok $update->user->flagged, 'user flagged'; - - $mech->get_ok( '/admin/update_edit/' . $update->id ); - $mech->content_contains('Remove flag'); -}; - -subtest 'unflagging user from update page' => sub { - $update->user->flagged(1); - $update->user->update; - - $mech->get_ok( '/admin/update_edit/' . $update->id ); - $mech->content_contains('Remove flag'); - - $mech->click_ok('removeuserflag'); - - $mech->content_contains('User flag removed'); - $mech->content_contains('Flag user'); - - $update->discard_changes; - ok !$update->user->flagged, 'user not flagged'; - - $mech->get_ok( '/admin/update_edit/' . $update->id ); - $mech->content_contains('Flag user'); -}; - -subtest 'hiding comment marked as fixed reopens report' => sub { - $update->mark_fixed( 1 ); - $update->update; - - $report->state('fixed - user'); - $report->update; - - my $fields = { - text => 'this is a changed update', - state => 'hidden', - name => 'A User', - anonymous => 0, - username => 'test2@example.com', - }; - - $mech->submit_form_ok( { with_fields => $fields } ); - - $report->discard_changes; - is $report->state, 'confirmed', 'report reopened'; - $mech->content_contains('Problem marked as open'); -}; - -$log_entries->delete; - -subtest 'report search' => sub { - $update->state('confirmed'); - $update->user($report->user); - $update->update; - - $mech->get_ok('/admin/reports'); - $mech->get_ok('/admin/reports?search=' . $report->id ); - - $mech->content_contains( $report->title ); - my $r_id = $report->id; - $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); - - $mech->get_ok('/admin/reports?search=' . $report->external_id); - $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); - - $mech->get_ok('/admin/reports?search=ref:' . $report->external_id); - $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); - - $mech->get_ok('/admin/reports?search=' . $report->user->email); - - my $u_id = $update->id; - $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); - $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id#update_$u_id"[^>]*>$u_id</a>} ); - - $update->state('hidden'); - $update->update; - - $mech->get_ok('/admin/reports?search=' . $report->user->email); - $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td> \s* $u_id \s* </td>}xs ); - - $report->state('hidden'); - $report->update; - - $mech->get_ok('/admin/reports?search=' . $report->user->email); - $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td[^>]*> \s* $r_id \s* </td>}xs ); - - $report->state('fixed - user'); - $report->update; - - $mech->get_ok('/admin/reports?search=' . $report->user->email); - $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); -}; - -subtest 'search abuse' => sub { - $mech->get_ok( '/admin/users?search=example' ); - $mech->content_like(qr{test4\@example.com.*</td>\s*<td>.*?</td>\s*<td>User in abuse table}s); -}; - -subtest 'show flagged entries' => sub { - $report->flagged( 1 ); - $report->update; - - $user->flagged( 1 ); - $user->update; - - $mech->get_ok('/admin/flagged'); - $mech->content_contains( $report->title ); - $mech->content_contains( $user->email ); -}; - -my $haringey = $mech->create_body_ok(2509, 'Haringey Borough Council'); - -subtest 'user search' => sub { - $mech->get_ok('/admin/users'); - $mech->get_ok('/admin/users?search=' . $user->name); - - $mech->content_contains( $user->name); - my $u_id = $user->id; - $mech->content_like( qr{user_edit/$u_id">Edit</a>} ); - - $mech->get_ok('/admin/users?search=' . $user->email); - - $mech->content_like( qr{user_edit/$u_id">Edit</a>} ); - - $user->from_body($haringey->id); - $user->update; - $mech->get_ok('/admin/users?search=' . $haringey->id ); - $mech->content_contains('Haringey'); -}; - -subtest 'search does not show user from another council' => sub { - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'oxfordshire' ], - }, sub { - $mech->get_ok('/admin/users'); - $mech->get_ok('/admin/users?search=' . $user->name); - - $mech->content_contains( "Searching found no users." ); - - $mech->get_ok('/admin/users?search=' . $user->email); - $mech->content_contains( "Searching found no users." ); - }; -}; - -subtest 'user_edit does not show user from another council' => sub { - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'oxfordshire' ], - }, sub { - $mech->get('/admin/user_edit/' . $user->id); - ok !$mech->res->is_success(), "want a bad response"; - is $mech->res->code, 404, "got 404"; - }; -}; - -$log_entries = FixMyStreet::App->model('DB::AdminLog')->search( - { - object_type => 'user', - object_id => $user->id - }, - { - order_by => { -desc => 'id' }, - } -); - -is $log_entries->count, 0, 'no admin log entries'; - -$user->flagged( 0 ); -$user->update; - -my $southend = $mech->create_body_ok(2607, 'Southend-on-Sea Borough Council'); - -for my $test ( - { - desc => 'add user - blank form', - fields => { - email => '', email_verified => 0, - phone => '', phone_verified => 0, - }, - error => ['Please verify at least one of email/phone', 'Please enter a name'], - }, - { - desc => 'add user - blank, verify phone', - fields => { - email => '', email_verified => 0, - phone => '', phone_verified => 1, - }, - error => ['Please enter a valid email or phone number', 'Please enter a name'], - }, - { - desc => 'add user - bad email', - fields => { - name => 'Norman', - email => 'bademail', email_verified => 0, - phone => '', phone_verified => 0, - }, - error => ['Please enter a valid email'], - }, - { - desc => 'add user - bad phone', - fields => { - name => 'Norman', - phone => '01214960000000', phone_verified => 1, - }, - error => ['Please check your phone number is correct'], - }, - { - desc => 'add user - landline', - fields => { - name => 'Norman Name', - phone => '+441214960000', - phone_verified => 1, - }, - error => ['Please enter a mobile number'], - }, - { - desc => 'add user - good details', - fields => { - name => 'Norman Name', - phone => '+61491570156', - phone_verified => 1, - }, - }, -) { - subtest $test->{desc} => sub { - $mech->get_ok('/admin/users'); - $mech->submit_form_ok( { with_fields => $test->{fields} } ); - if ($test->{error}) { - $mech->content_contains($_) for @{$test->{error}}; - } else { - $mech->content_contains('Updated'); - } - }; -} - -my %default_perms = ( - "permissions[moderate]" => undef, - "permissions[planned_reports]" => undef, - "permissions[report_edit]" => undef, - "permissions[report_edit_category]" => undef, - "permissions[report_edit_priority]" => undef, - "permissions[report_inspect]" => undef, - "permissions[report_instruct]" => undef, - "permissions[contribute_as_another_user]" => undef, - "permissions[contribute_as_anonymous_user]" => undef, - "permissions[contribute_as_body]" => undef, - "permissions[view_body_contribute_details]" => undef, - "permissions[user_edit]" => undef, - "permissions[user_manage_permissions]" => undef, - "permissions[user_assign_body]" => undef, - "permissions[user_assign_areas]" => undef, - "permissions[template_edit]" => undef, - "permissions[responsepriority_edit]" => undef, - "permissions[category_edit]" => undef, - trusted_bodies => undef, -); - -# Start this section with user having no name -# Regression test for mysociety/fixmystreetforcouncils#250 -$user->update({ name => '' }); - -FixMyStreet::override_config { - MAPIT_URL => 'http://mapit.uk/', -}, sub { - for my $test ( - { - desc => 'edit user name', - fields => { - name => '', - email => 'test@example.com', - body => $haringey->id, - phone => '', - flagged => undef, - is_superuser => undef, - area_id => '', - %default_perms, - }, - changes => { - name => 'Changed User', - }, - log_count => 1, - log_entries => [qw/edit/], - }, - { - desc => 'edit user email', - fields => { - name => 'Changed User', - email => 'test@example.com', - body => $haringey->id, - phone => '', - flagged => undef, - is_superuser => undef, - area_id => '', - %default_perms, - }, - changes => { - email => 'changed@example.com', - }, - log_count => 2, - log_entries => [qw/edit edit/], - }, - { - desc => 'edit user body', - fields => { - name => 'Changed User', - email => 'changed@example.com', - body => $haringey->id, - phone => '', - flagged => undef, - is_superuser => undef, - area_id => '', - %default_perms, - }, - changes => { - body => $southend->id, - }, - log_count => 3, - log_entries => [qw/edit edit edit/], - }, - { - desc => 'edit user flagged', - fields => { - name => 'Changed User', - email => 'changed@example.com', - body => $southend->id, - phone => '', - flagged => undef, - is_superuser => undef, - area_id => '', - %default_perms, - }, - changes => { - flagged => 'on', - }, - log_count => 4, - log_entries => [qw/edit edit edit edit/], - }, - { - desc => 'edit user remove flagged', - fields => { - name => 'Changed User', - email => 'changed@example.com', - body => $southend->id, - phone => '', - flagged => 'on', - is_superuser => undef, - area_id => '', - %default_perms, - }, - changes => { - flagged => undef, - }, - log_count => 4, - log_entries => [qw/edit edit edit edit/], - }, - { - desc => 'edit user add is_superuser', - fields => { - name => 'Changed User', - email => 'changed@example.com', - body => $southend->id, - phone => '', - flagged => undef, - is_superuser => undef, - area_id => '', - %default_perms, - }, - changes => { - is_superuser => 'on', - }, - removed => [ - keys %default_perms, - ], - log_count => 5, - log_entries => [qw/edit edit edit edit edit/], - }, - { - desc => 'edit user remove is_superuser', - fields => { - name => 'Changed User', - email => 'changed@example.com', - body => $southend->id, - phone => '', - flagged => undef, - is_superuser => 'on', - area_id => '', - }, - changes => { - is_superuser => undef, - }, - added => { - %default_perms, - }, - log_count => 5, - log_entries => [qw/edit edit edit edit edit/], - }, - ) { - subtest $test->{desc} => sub { - $mech->get_ok( '/admin/user_edit/' . $user->id ); - - my $visible = $mech->visible_form_values; - is_deeply $visible, $test->{fields}, 'expected user'; - - my $expected = { - %{ $test->{fields} }, - %{ $test->{changes} } - }; - - $mech->submit_form_ok( { with_fields => $expected } ); - - # Some actions cause visible fields to be added/removed - foreach my $x (@{ $test->{removed} }) { - delete $expected->{$x}; - } - if ( $test->{added} ) { - $expected = { - %$expected, - %{ $test->{added} } - }; - } - - $visible = $mech->visible_form_values; - is_deeply $visible, $expected, 'user updated'; - - $mech->content_contains( 'Updated!' ); - }; - } -}; - -FixMyStreet::override_config { - MAPIT_URL => 'http://mapit.uk/', - SMS_AUTHENTICATION => 1, -}, sub { - subtest "Test edit user add verified phone" => sub { - $mech->get_ok( '/admin/user_edit/' . $user->id ); - $mech->submit_form_ok( { with_fields => { - phone => '+61491570157', - phone_verified => 1, - } } ); - $mech->content_contains( 'Updated!' ); - }; - - subtest "Test changing user to an existing one" => sub { - my $existing_user = $mech->create_user_ok('existing@example.com', name => 'Existing User'); - $mech->create_problems_for_body(2, 2514, 'Title', { user => $existing_user }); - my $count = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id })->count; - $mech->get_ok( '/admin/user_edit/' . $user->id ); - $mech->submit_form_ok( { with_fields => { email => 'existing@example.com' } }, 'submit email change' ); - is $mech->uri->path, '/admin/user_edit/' . $existing_user->id, 'redirected'; - my $p = FixMyStreet::DB->resultset('Problem')->search({ user_id => $existing_user->id })->count; - is $p, $count + 2, 'reports merged'; - }; - -}; - -subtest "Test setting a report from unconfirmed to something else doesn't cause a front end error" => sub { - $report->update( { confirmed => undef, state => 'unconfirmed', non_public => 0 } ); - $mech->get_ok("/admin/report_edit/$report_id"); - $mech->submit_form_ok( { with_fields => { state => 'investigating' } } ); - $report->discard_changes; - ok( $report->confirmed, 'report has a confirmed timestamp' ); - $mech->get_ok("/report/$report_id"); -}; - subtest "Check admin_base_url" => sub { my $rs = FixMyStreet::App->model('DB::Problem'); my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($report->cobrand)->new(); @@ -1578,10 +140,6 @@ subtest "Check admin_base_url" => sub { $mech->log_out_ok; subtest "Users without from_body can't access admin" => sub { - $user = FixMyStreet::App->model('DB::User')->find( { email => 'existing@example.com' } ); - $user->from_body( undef ); - $user->update; - $mech->log_in_ok( $user->email ); ok $mech->get('/admin'); @@ -1629,229 +187,4 @@ subtest "Users with from_body can't access fixmystreet.com admin" => sub { }; }; -subtest "response templates can be added" => sub { - is $oxfordshire->response_templates->count, 0, "No response templates yet"; - $mech->log_in_ok( $superuser->email ); - $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); - - my $fields = { - title => "Report acknowledgement", - text => "Thank you for your report. We will respond shortly.", - auto_response => undef, - "contacts[".$oxfordshirecontact->id."]" => 1, - }; - $mech->submit_form_ok( { with_fields => $fields } ); - - is $oxfordshire->response_templates->count, 1, "Response template was added"; -}; - -subtest "response templates are included on page" => sub { - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'oxfordshire' ], - }, sub { - $report->update({ category => $oxfordshirecontact->category, bodies_str => $oxfordshire->id }); - $mech->log_in_ok( $oxfordshireuser->email ); - - $mech->get_ok("/report/" . $report->id); - $mech->content_contains( $oxfordshire->response_templates->first->text ); - - $mech->log_out_ok; - }; -}; - -subtest "auto-response templates that duplicate a single category can't be added" => sub { - $mech->delete_response_template($_) for $oxfordshire->response_templates; - my $template = $oxfordshire->response_templates->create({ - title => "Report fixed - potholes", - text => "Thank you for your report. This problem has been fixed.", - auto_response => 1, - state => 'fixed - council', - }); - $template->contact_response_templates->find_or_create({ - contact_id => $oxfordshirecontact->id, - }); - is $oxfordshire->response_templates->count, 1, "Initial response template was created"; - - - $mech->log_in_ok( $superuser->email ); - $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); - - # This response template has the same category & state as an existing one - # so won't be allowed. - my $fields = { - title => "Report marked fixed - potholes", - text => "Thank you for your report. This pothole has been fixed.", - auto_response => 'on', - state => 'fixed - council', - "contacts[".$oxfordshirecontact->id."]" => 1, - }; - $mech->submit_form_ok( { with_fields => $fields } ); - is $mech->uri->path, '/admin/templates/' . $oxfordshire->id . '/new', 'not redirected'; - $mech->content_contains( 'Please correct the errors below' ); - $mech->content_contains( 'There is already an auto-response template for this category/state.' ); - - is $oxfordshire->response_templates->count, 1, "Duplicate response template wasn't added"; -}; - -subtest "auto-response templates that duplicate all categories can't be added" => sub { - $mech->delete_response_template($_) for $oxfordshire->response_templates; - $oxfordshire->response_templates->create({ - title => "Report investigating - all cats", - text => "Thank you for your report. This problem has been fixed.", - auto_response => 1, - state => 'fixed - council', - }); - is $oxfordshire->response_templates->count, 1, "Initial response template was created"; - - - $mech->log_in_ok( $superuser->email ); - $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); - - # There's already a response template for all categories and this state, so - # this new template won't be allowed. - my $fields = { - title => "Report investigating - single cat", - text => "Thank you for your report. This problem has been fixed.", - auto_response => 'on', - state => 'fixed - council', - "contacts[".$oxfordshirecontact->id."]" => 1, - }; - $mech->submit_form_ok( { with_fields => $fields } ); - is $mech->uri->path, '/admin/templates/' . $oxfordshire->id . '/new', 'not redirected'; - $mech->content_contains( 'Please correct the errors below' ); - $mech->content_contains( 'There is already an auto-response template for this category/state.' ); - - - is $oxfordshire->response_templates->count, 1, "Duplicate response template wasn't added"; -}; - -subtest "all-category auto-response templates that duplicate a single category can't be added" => sub { - $mech->delete_response_template($_) for $oxfordshire->response_templates; - my $template = $oxfordshire->response_templates->create({ - title => "Report fixed - potholes", - text => "Thank you for your report. This problem has been fixed.", - auto_response => 1, - state => 'fixed - council', - }); - $template->contact_response_templates->find_or_create({ - contact_id => $oxfordshirecontact->id, - }); - is $oxfordshire->response_templates->count, 1, "Initial response template was created"; - - - $mech->log_in_ok( $superuser->email ); - $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); - - # This response template is implicitly for all categories, but there's - # already a template for a specific category in this state, so it won't be - # allowed. - my $fields = { - title => "Report marked fixed - all cats", - text => "Thank you for your report. This problem has been fixed.", - auto_response => 'on', - state => 'fixed - council', - }; - $mech->submit_form_ok( { with_fields => $fields } ); - is $mech->uri->path, '/admin/templates/' . $oxfordshire->id . '/new', 'not redirected'; - $mech->content_contains( 'Please correct the errors below' ); - $mech->content_contains( 'There is already an auto-response template for this category/state.' ); - - is $oxfordshire->response_templates->count, 1, "Duplicate response template wasn't added"; -}; - - - -$mech->log_in_ok( $superuser->email ); - -subtest "response priorities can be added" => sub { - is $oxfordshire->response_priorities->count, 0, "No response priorities yet"; - $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id . "/new" ); - - my $fields = { - name => "Cat 1A", - description => "Fixed within 24 hours", - deleted => undef, - is_default => undef, - "contacts[".$oxfordshirecontact->id."]" => 1, - }; - $mech->submit_form_ok( { with_fields => $fields } ); - - is $oxfordshire->response_priorities->count, 1, "Response priority was added to body"; - is $oxfordshirecontact->response_priorities->count, 1, "Response priority was added to contact"; -}; - -subtest "response priorities can set to default" => sub { - my $priority_id = $oxfordshire->response_priorities->first->id; - is $oxfordshire->response_priorities->count, 1, "Response priority exists"; - $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id . "/$priority_id" ); - - my $fields = { - name => "Cat 1A", - description => "Fixed within 24 hours", - deleted => undef, - is_default => 1, - "contacts[".$oxfordshirecontact->id."]" => 1, - }; - $mech->submit_form_ok( { with_fields => $fields } ); - - is $oxfordshire->response_priorities->count, 1, "Still one response priority"; - is $oxfordshirecontact->response_priorities->count, 1, "Still one response priority"; - ok $oxfordshire->response_priorities->first->is_default, "Response priority set to default"; -}; - -subtest "response priorities can be listed" => sub { - $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); - - $mech->content_contains( $oxfordshire->response_priorities->first->name ); - $mech->content_contains( $oxfordshire->response_priorities->first->description ); -}; - -subtest "response priorities are limited by body" => sub { - my $bromleypriority = $bromley->response_priorities->create( { - deleted => 0, - name => "Bromley Cat 0", - } ); - - is $bromley->response_priorities->count, 1, "Response priority was added to Bromley"; - is $oxfordshire->response_priorities->count, 1, "Response priority wasn't added to Oxfordshire"; - - $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); - $mech->content_lacks( $bromleypriority->name ); - - $mech->get_ok( "/admin/responsepriorities/" . $bromley->id ); - $mech->content_contains( $bromleypriority->name ); -}; - -$mech->log_out_ok; - -subtest "response priorities can't be viewed across councils" => sub { - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'oxfordshire' ], - }, sub { - $oxfordshireuser->user_body_permissions->create({ - body => $oxfordshire, - permission_type => 'responsepriority_edit', - }); - $mech->log_in_ok( $oxfordshireuser->email ); - $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); - $mech->content_contains( $oxfordshire->response_priorities->first->name ); - - - $mech->get( "/admin/responsepriorities/" . $bromley->id ); - ok !$mech->res->is_success(), "want a bad response"; - is $mech->res->code, 404, "got 404"; - - my $bromley_priority_id = $bromley->response_priorities->first->id; - $mech->get( "/admin/responsepriorities/" . $bromley->id . "/" . $bromley_priority_id ); - ok !$mech->res->is_success(), "want a bad response"; - is $mech->res->code, 404, "got 404"; - }; -}; - -subtest "smoke view some stats pages" => sub { - $mech->log_in_ok( $superuser->email ); - $mech->get_ok('/admin/stats/fix-rate'); - $mech->get_ok('/admin/stats/questionnaire'); -}; - done_testing(); diff --git a/t/app/controller/admin/bodies.t b/t/app/controller/admin/bodies.t new file mode 100644 index 000000000..9bdf8fb9a --- /dev/null +++ b/t/app/controller/admin/bodies.t @@ -0,0 +1,165 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); +$mech->log_in_ok( $superuser->email ); + +# This override is wrapped around ALL the /admin/body tests +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + MAPIT_TYPES => [ 'UTA' ], + BASE_URL => 'http://www.example.org', +}, sub { + +my $body = $mech->create_body_ok(2650, 'Aberdeen City Council'); +$mech->get_ok('/admin/body/' . $body->id); +$mech->content_contains('Aberdeen City Council'); +$mech->content_like(qr{AB\d\d}); +$mech->content_contains("http://www.example.org/around"); + +subtest 'check contact creation' => sub { + $mech->get_ok('/admin/body/' . $body->id); + + $mech->submit_form_ok( { with_fields => { + category => 'test category', + email => 'test@example.com', + note => 'test note', + non_public => undef, + state => 'unconfirmed', + } } ); + + $mech->content_contains( 'test category' ); + $mech->content_contains( 'test@example.com' ); + $mech->content_contains( '<td>test note' ); + $mech->content_like( qr/<td>\s*unconfirmed\s*<\/td>/ ); # No private + + $mech->submit_form_ok( { with_fields => { + category => 'private category', + email => 'test@example.com', + note => 'test note', + non_public => 'on', + } } ); + + $mech->content_contains( 'private category' ); + $mech->content_like( qr{test\@example.com\s*</td>\s*<td>\s*confirmed\s*<br>\s*<small>\s*Private\s*</small>\s*</td>} ); + + $mech->submit_form_ok( { with_fields => { + category => 'test/category', + email => 'test@example.com', + note => 'test/note', + non_public => 'on', + } } ); + $mech->get_ok('/admin/body/' . $body->id . '/test/category'); + $mech->content_contains('<h1>test/category</h1>'); +}; + +subtest 'check contact editing' => sub { + $mech->get_ok('/admin/body/' . $body->id .'/test%20category'); + + $mech->submit_form_ok( { with_fields => { + email => 'test2@example.com', + note => 'test2 note', + non_public => undef, + } } ); + + $mech->content_contains( 'test category' ); + $mech->content_like( qr{test2\@example.com\s*</td>\s*<td>\s*unconfirmed\s*</td>} ); + $mech->content_contains( '<td>test2 note' ); + + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->submit_form_ok( { with_fields => { + email => 'test2@example.com, test3@example.com', + note => 'test3 note', + } } ); + + $mech->content_contains( 'test2@example.com,test3@example.com' ); + + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->content_contains( '<td><strong>test2@example.com,test3@example.com' ); + + $mech->submit_form_ok( { with_fields => { + email => 'test2@example.com', + note => 'test2 note', + non_public => 'on', + } } ); + + $mech->content_like( qr{test2\@example.com\s*</td>\s*<td>\s*unconfirmed\s*<br>\s*<small>\s*Private\s*</small>\s*</td>} ); + + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->content_contains( '<td><strong>test2@example.com' ); +}; + +subtest 'check contact updating' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->content_like(qr{test2\@example.com</strong>[^<]*</td>[^<]*<td>unconfirmed}s); + + $mech->get_ok('/admin/body/' . $body->id); + + $mech->form_number( 1 ); + $mech->tick( 'confirmed', 'test category' ); + $mech->submit_form_ok({form_number => 1}); + + $mech->content_like(qr'test2@example.com</td>[^<]*<td>\s*confirmed's); + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->content_like(qr{test2\@example.com[^<]*</td>[^<]*<td><strong>confirmed}s); +}; + +$body->update({ send_method => undef }); + +subtest 'check open311 configuring' => sub { + $mech->get_ok('/admin/body/' . $body->id); + $mech->content_lacks('Council contacts configured via Open311'); + + $mech->form_number(3); + $mech->submit_form_ok( + { + with_fields => { + api_key => 'api key', + endpoint => 'http://example.com/open311', + jurisdiction => 'mySociety', + send_comments => 0, + send_method => 'Open311', + } + } + ); + $mech->content_contains('Council contacts configured via Open311'); + $mech->content_contains('Values updated'); + + my $conf = FixMyStreet::App->model('DB::Body')->find( $body->id ); + is $conf->endpoint, 'http://example.com/open311', 'endpoint configured'; + is $conf->api_key, 'api key', 'api key configured'; + is $conf->jurisdiction, 'mySociety', 'jurisdiction configures'; + + $mech->form_number(3); + $mech->submit_form_ok( + { + with_fields => { + api_key => 'new api key', + endpoint => 'http://example.org/open311', + jurisdiction => 'open311', + send_comments => 0, + send_method => 'Open311', + } + } + ); + + $mech->content_contains('Values updated'); + + $conf = FixMyStreet::App->model('DB::Body')->find( $body->id ); + is $conf->endpoint, 'http://example.org/open311', 'endpoint updated'; + is $conf->api_key, 'new api key', 'api key updated'; + is $conf->jurisdiction, 'open311', 'jurisdiction configures'; +}; + +subtest 'check text output' => sub { + $mech->get_ok('/admin/body/' . $body->id . '?text=1'); + is $mech->content_type, 'text/plain'; + $mech->content_contains('test category'); + $mech->content_lacks('<body'); +}; + + +}; # END of override wrap + +done_testing(); diff --git a/t/app/controller/admin_defecttypes.t b/t/app/controller/admin/defecttypes.t index e7d0e42af..e7d0e42af 100644 --- a/t/app/controller/admin_defecttypes.t +++ b/t/app/controller/admin/defecttypes.t diff --git a/t/app/controller/admin_permissions.t b/t/app/controller/admin/permissions.t index 7944cc0b1..7944cc0b1 100644 --- a/t/app/controller/admin_permissions.t +++ b/t/app/controller/admin/permissions.t diff --git a/t/app/controller/admin/priorities.t b/t/app/controller/admin/priorities.t new file mode 100644 index 000000000..4eff20be7 --- /dev/null +++ b/t/app/controller/admin/priorities.t @@ -0,0 +1,102 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $oxfordshirecontact = $mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Potholes', email => 'potholes@example.com' ); +my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); + +my $bromley = $mech->create_body_ok(2482, 'Bromley Council'); + +$mech->log_in_ok( $superuser->email ); + +subtest "response priorities can be added" => sub { + is $oxfordshire->response_priorities->count, 0, "No response priorities yet"; + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id . "/new" ); + + my $fields = { + name => "Cat 1A", + description => "Fixed within 24 hours", + deleted => undef, + is_default => undef, + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + + is $oxfordshire->response_priorities->count, 1, "Response priority was added to body"; + is $oxfordshirecontact->response_priorities->count, 1, "Response priority was added to contact"; +}; + +subtest "response priorities can set to default" => sub { + my $priority_id = $oxfordshire->response_priorities->first->id; + is $oxfordshire->response_priorities->count, 1, "Response priority exists"; + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id . "/$priority_id" ); + + my $fields = { + name => "Cat 1A", + description => "Fixed within 24 hours", + deleted => undef, + is_default => 1, + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + + is $oxfordshire->response_priorities->count, 1, "Still one response priority"; + is $oxfordshirecontact->response_priorities->count, 1, "Still one response priority"; + ok $oxfordshire->response_priorities->first->is_default, "Response priority set to default"; +}; + +subtest "response priorities can be listed" => sub { + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); + + $mech->content_contains( $oxfordshire->response_priorities->first->name ); + $mech->content_contains( $oxfordshire->response_priorities->first->description ); +}; + +subtest "response priorities are limited by body" => sub { + my $bromleypriority = $bromley->response_priorities->create( { + deleted => 0, + name => "Bromley Cat 0", + } ); + + is $bromley->response_priorities->count, 1, "Response priority was added to Bromley"; + is $oxfordshire->response_priorities->count, 1, "Response priority wasn't added to Oxfordshire"; + + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); + $mech->content_lacks( $bromleypriority->name ); + + $mech->get_ok( "/admin/responsepriorities/" . $bromley->id ); + $mech->content_contains( $bromleypriority->name ); +}; + +$mech->log_out_ok; + +subtest "response priorities can't be viewed across councils" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'responsepriority_edit', + }); + $mech->log_in_ok( $oxfordshireuser->email ); + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); + $mech->content_contains( $oxfordshire->response_priorities->first->name ); + + + $mech->get( "/admin/responsepriorities/" . $bromley->id ); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404"; + + my $bromley_priority_id = $bromley->response_priorities->first->id; + $mech->get( "/admin/responsepriorities/" . $bromley->id . "/" . $bromley_priority_id ); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404"; + }; +}; + +done_testing(); diff --git a/t/app/controller/admin/report_edit.t b/t/app/controller/admin/report_edit.t new file mode 100644 index 000000000..a8a0bd143 --- /dev/null +++ b/t/app/controller/admin/report_edit.t @@ -0,0 +1,556 @@ +use FixMyStreet::TestMech; +# avoid wide character warnings from the category change message +use open ':std', ':encoding(UTF-8)'; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); +my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2'); +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $oxfordshirecontact = $mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Potholes', email => 'potholes@example.com' ); +$mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Traffic lights', email => 'lights@example.com' ); + +my $oxford = $mech->create_body_ok(2421, 'Oxford City Council'); +$mech->create_contact_ok( body_id => $oxford->id, category => 'Graffiti', email => 'graffiti@example.net' ); + +my $dt = DateTime->new( + year => 2011, + month => 04, + day => 16, + hour => 15, + minute => 47, + second => 23 +); + +my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( + { + postcode => 'SW1A 1AA', + bodies_str => '2504', + areas => ',105255,11806,11828,2247,2504,', + category => 'Other', + title => 'Report to Edit', + detail => 'Detail for Report to Edit', + used_map => 't', + name => 'Test User', + anonymous => 'f', + external_id => '13', + state => 'confirmed', + confirmed => $dt->ymd . ' ' . $dt->hms, + lang => 'en-gb', + service => '', + cobrand => '', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.5016605453401', + longitude => '-0.142497580865087', + user_id => $user->id, + whensent => $dt->ymd . ' ' . $dt->hms, + } +); + +$mech->log_in_ok( $superuser->email ); + +my $log_entries = FixMyStreet::App->model('DB::AdminLog')->search( + { + object_type => 'problem', + object_id => $report->id + }, + { + order_by => { -desc => 'id' }, + } +); + +is $log_entries->count, 0, 'no admin log entries'; + +my $report_id = $report->id; +ok $report, "created test report - $report_id"; + +foreach my $test ( + { + description => 'edit report title', + fields => { + title => 'Report to Edit', + detail => 'Detail for Report to Edit', + state => 'confirmed', + name => 'Test User', + username => $user->email, + anonymous => 0, + flagged => undef, + non_public => undef, + }, + changes => { title => 'Edited Report', }, + log_entries => [qw/edit/], + resend => 0, + }, + { + description => 'edit report description', + fields => { + title => 'Edited Report', + detail => 'Detail for Report to Edit', + state => 'confirmed', + name => 'Test User', + username => $user->email, + anonymous => 0, + flagged => undef, + non_public => undef, + }, + changes => { detail => 'Edited Detail', }, + log_entries => [qw/edit edit/], + resend => 0, + }, + { + description => 'edit report user name', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Test User', + username => $user->email, + anonymous => 0, + flagged => undef, + non_public => undef, + }, + changes => { name => 'Edited User', }, + log_entries => [qw/edit edit edit/], + resend => 0, + user => $user, + }, + { + description => 'edit report set flagged true', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user->email, + anonymous => 0, + flagged => undef, + non_public => undef, + }, + changes => { + flagged => 'on', + }, + log_entries => [qw/edit edit edit edit/], + resend => 0, + user => $user, + }, + { + description => 'edit report user email', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user->email, + anonymous => 0, + flagged => 'on', + non_public => undef, + }, + changes => { username => $user2->email, }, + log_entries => [qw/edit edit edit edit edit/], + resend => 0, + user => $user2, + }, + { + description => 'change state to unconfirmed', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user2->email, + anonymous => 0, + flagged => 'on', + non_public => undef, + }, + expect_comment => 1, + changes => { state => 'unconfirmed' }, + log_entries => [qw/edit state_change edit edit edit edit edit/], + resend => 0, + }, + { + description => 'change state to confirmed', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'unconfirmed', + name => 'Edited User', + username => $user2->email, + anonymous => 0, + flagged => 'on', + non_public => undef, + }, + expect_comment => 1, + changes => { state => 'confirmed' }, + log_entries => [qw/edit state_change edit state_change edit edit edit edit edit/], + resend => 0, + }, + { + description => 'change state to fixed', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user2->email, + anonymous => 0, + flagged => 'on', + non_public => undef, + }, + expect_comment => 1, + changes => { state => 'fixed' }, + log_entries => + [qw/edit state_change edit state_change edit state_change edit edit edit edit edit/], + resend => 0, + }, + { + description => 'change state to hidden', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'fixed', + name => 'Edited User', + username => $user2->email, + anonymous => 0, + flagged => 'on', + non_public => undef, + }, + expect_comment => 1, + changes => { state => 'hidden' }, + log_entries => [ + qw/edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 0, + }, + { + description => 'edit and change state', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'hidden', + name => 'Edited User', + username => $user2->email, + anonymous => 0, + flagged => 'on', + non_public => undef, + }, + expect_comment => 1, + changes => { + state => 'confirmed', + anonymous => 1, + }, + log_entries => [ + qw/edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 0, + }, + { + description => 'resend', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user2->email, + anonymous => 1, + flagged => 'on', + non_public => undef, + }, + changes => {}, + log_entries => [ + qw/resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 1, + }, + { + description => 'non public', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user2->email, + anonymous => 1, + flagged => 'on', + non_public => undef, + }, + changes => { + non_public => 'on', + }, + log_entries => [ + qw/edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 0, + }, + { + description => 'change state to investigating as body superuser', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user2->email, + anonymous => 1, + flagged => 'on', + non_public => 'on', + }, + expect_comment => 1, + user_body => $oxfordshire, + changes => { state => 'investigating' }, + log_entries => [ + qw/edit state_change edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 0, + }, + { + description => 'change state to in progess and change category as body superuser', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'investigating', + name => 'Edited User', + username => $user2->email, + anonymous => 1, + flagged => 'on', + non_public => 'on', + }, + expect_comment => 1, + expected_text => '*Category changed from ‘Other’ to ‘Potholes’*', + user_body => $oxfordshire, + changes => { state => 'in progress', category => 'Potholes' }, + log_entries => [ + qw/edit state_change edit state_change edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 0, + }, + ) +{ + subtest $test->{description} => sub { + $report->comments->delete; + $log_entries->reset; + + if ( $test->{user_body} ) { + $superuser->from_body( $test->{user_body}->id ); + $superuser->update; + } + + $mech->get_ok("/admin/report_edit/$report_id"); + + @{$test->{fields}}{'external_id', 'external_body', 'external_team', 'category'} = (13, "", "", "Other"); + is_deeply( $mech->visible_form_values(), $test->{fields}, 'initial form values' ); + + my $new_fields = { + %{ $test->{fields} }, + %{ $test->{changes} }, + }; + + if ( $test->{resend} ) { + $mech->click_ok( 'resend' ); + } else { + $mech->submit_form_ok( { with_fields => $new_fields }, 'form_submitted' ); + } + + is_deeply( $mech->visible_form_values(), $new_fields, 'changed form values' ); + is $log_entries->count, scalar @{$test->{log_entries}}, 'log entry count'; + is $log_entries->next->action, $_, 'log entry added' for @{ $test->{log_entries} }; + + $report->discard_changes; + + if ($report->state eq 'confirmed' && $report->whensent) { + $mech->content_contains( 'type="submit" name="resend"', 'resend button' ); + } else { + $mech->content_lacks( 'type="submit" name="resend"', 'no resend button' ); + } + + $test->{changes}->{flagged} = 1 if $test->{changes}->{flagged}; + $test->{changes}->{non_public} = 1 if $test->{changes}->{non_public}; + + is $report->$_, $test->{changes}->{$_}, "$_ updated" for grep { $_ ne 'username' } keys %{ $test->{changes} }; + + if ( $test->{user} ) { + is $report->user->id, $test->{user}->id, 'user changed'; + } + + if ( $test->{resend} ) { + $mech->content_contains( 'That problem will now be resent' ); + is $report->whensent, undef, 'mark report to resend'; + } + + if ( $test->{expect_comment} ) { + my $comment = $report->comments->first; + ok $comment, 'report status change creates comment'; + is $report->comments->count, 1, 'report only has one comment'; + if ($test->{expected_text}) { + is $comment->text, $test->{expected_text}, 'comment has expected text'; + } else { + is $comment->text, '', 'comment has no text'; + } + if ( $test->{user_body} ) { + ok $comment->get_extra_metadata('is_body_user'), 'body user metadata set'; + ok !$comment->get_extra_metadata('is_superuser'), 'superuser metadata not set'; + is $comment->name, $test->{user_body}->name, 'comment name is body name'; + } else { + ok !$comment->get_extra_metadata('is_body_user'), 'body user metadata not set'; + ok $comment->get_extra_metadata('is_superuser'), 'superuser metadata set'; + is $comment->name, _('an administrator'), 'comment name is admin'; + } + } else { + is $report->comments->count, 0, 'report has no comments'; + } + + $superuser->from_body(undef); + $superuser->update; + }; +} + +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'fixmystreet', +}, sub { + +subtest 'change report category' => sub { + my ($ox_report) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Unsure', { + category => 'Potholes', + areas => ',2237,2421,', # Cached used by categories_for_point... + latitude => 51.7549262252, + longitude => -1.25617899435, + whensent => \'current_timestamp', + }); + $mech->get_ok("/admin/report_edit/" . $ox_report->id); + + $mech->submit_form_ok( { with_fields => { category => 'Traffic lights' } }, 'form_submitted' ); + $ox_report->discard_changes; + is $ox_report->category, 'Traffic lights'; + isnt $ox_report->whensent, undef; + is $ox_report->comments->count, 1, "Comment created for update"; + is $ox_report->comments->first->text, '*Category changed from ‘Potholes’ to ‘Traffic lights’*', 'Comment text correct'; + + $mech->submit_form_ok( { with_fields => { category => 'Graffiti' } }, 'form_submitted' ); + $ox_report->discard_changes; + is $ox_report->category, 'Graffiti'; + is $ox_report->whensent, undef; +}; + +}; + +subtest 'change email to new user' => sub { + $log_entries->delete; + $mech->get_ok("/admin/report_edit/$report_id"); + my $fields = { + title => $report->title, + detail => $report->detail, + state => $report->state, + name => $report->name, + username => $report->user->email, + category => 'Potholes', + anonymous => 1, + flagged => 'on', + non_public => 'on', + external_id => '13', + external_body => '', + external_team => '', + }; + + is_deeply( $mech->visible_form_values(), $fields, 'initial form values' ); + + my $changes = { + username => 'test3@example.com' + }; + + my $user3 = FixMyStreet::App->model('DB::User')->find( { email => 'test3@example.com' } ); + + ok !$user3, 'user not in database'; + + my $new_fields = { + %{ $fields }, + %{ $changes }, + }; + + $mech->submit_form_ok( + { + with_fields => $new_fields, + } + ); + + is $log_entries->count, 1, 'created admin log entries'; + is $log_entries->first->action, 'edit', 'log action'; + is_deeply( $mech->visible_form_values(), $new_fields, 'changed form values' ); + + $user3 = FixMyStreet::App->model('DB::User')->find( { email => 'test3@example.com' } ); + + $report->discard_changes; + + ok $user3, 'new user created'; + is $report->user_id, $user3->id, 'user changed to new user'; +}; + +subtest 'adding email to abuse list from report page' => sub { + my $email = $report->user->email; + + my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); + $abuse->delete if $abuse; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Ban user'); + + $mech->click_ok('banuser'); + + $mech->content_contains('User added to abuse list'); + $mech->content_contains('<small>User in abuse table</small>'); + + $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); + ok $abuse, 'entry created in abuse table'; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('<small>User in abuse table</small>'); +}; + +subtest 'flagging user from report page' => sub { + $report->user->flagged(0); + $report->user->update; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Flag user'); + + $mech->click_ok('flaguser'); + + $mech->content_contains('User flagged'); + $mech->content_contains('Remove flag'); + + $report->discard_changes; + ok $report->user->flagged, 'user flagged'; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Remove flag'); +}; + +subtest 'unflagging user from report page' => sub { + $report->user->flagged(1); + $report->user->update; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Remove flag'); + + $mech->click_ok('removeuserflag'); + + $mech->content_contains('User flag removed'); + $mech->content_contains('Flag user'); + + $report->discard_changes; + ok !$report->user->flagged, 'user not flagged'; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Flag user'); +}; + +subtest "Test setting a report from unconfirmed to something else doesn't cause a front end error" => sub { + $report->update( { confirmed => undef, state => 'unconfirmed', non_public => 0 } ); + $mech->get_ok("/admin/report_edit/$report_id"); + $mech->submit_form_ok( { with_fields => { state => 'investigating' } } ); + $report->discard_changes; + ok( $report->confirmed, 'report has a confirmed timestamp' ); + $mech->get_ok("/report/$report_id"); +}; + +done_testing(); diff --git a/t/app/controller/admin_reportextrafields.t b/t/app/controller/admin/reportextrafields.t index fb06665f4..fb06665f4 100644 --- a/t/app/controller/admin_reportextrafields.t +++ b/t/app/controller/admin/reportextrafields.t diff --git a/t/app/controller/admin/search.t b/t/app/controller/admin/search.t new file mode 100644 index 000000000..497ac9fd6 --- /dev/null +++ b/t/app/controller/admin/search.t @@ -0,0 +1,121 @@ +use FixMyStreet::TestMech; +# avoid wide character warnings from the category change message +use open ':std', ':encoding(UTF-8)'; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $oxfordshirecontact = $mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Potholes', email => 'potholes@example.com' ); +$mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Traffic lights', email => 'lights@example.com' ); +my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); + +my $oxford = $mech->create_body_ok(2421, 'Oxford City Council'); +$mech->create_contact_ok( body_id => $oxford->id, category => 'Graffiti', email => 'graffiti@example.net' ); + +my $bromley = $mech->create_body_ok(2482, 'Bromley Council'); + +my $user3 = FixMyStreet::App->model('DB::User')->create( { email => 'test3@example.com' } ); + +my $dt = DateTime->new( + year => 2011, + month => 04, + day => 16, + hour => 15, + minute => 47, + second => 23 +); + +my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( + { + postcode => 'SW1A 1AA', + bodies_str => '2504', + areas => ',105255,11806,11828,2247,2504,', + category => 'Other', + title => 'Report to Edit', + detail => 'Detail for Report to Edit', + used_map => 't', + name => 'Test User', + anonymous => 'f', + external_id => '13', + state => 'confirmed', + confirmed => $dt->ymd . ' ' . $dt->hms, + lang => 'en-gb', + service => '', + cobrand => '', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.5016605453401', + longitude => '-0.142497580865087', + user_id => $user->id, + whensent => $dt->ymd . ' ' . $dt->hms, + } +); +my $report_id = $report->id; +ok $report, "created test report - $report_id"; + +$mech->log_in_ok( $superuser->email ); + +subtest 'show flagged entries' => sub { + $report->flagged( 1 ); + $report->update; + $mech->get_ok('/admin/flagged'); + $mech->content_contains( $report->title ); +}; + +my $update = FixMyStreet::App->model('DB::Comment')->create( + { + text => 'this is an update', + user => $user, + state => 'confirmed', + problem => $report, + mark_fixed => 0, + anonymous => 1, + } +); + +subtest 'report search' => sub { + $mech->get_ok('/admin/reports'); + $mech->get_ok('/admin/reports?search=' . $report->id ); + + $mech->content_contains( $report->title ); + my $r_id = $report->id; + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + + $mech->get_ok('/admin/reports?search=' . $report->external_id); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + + $mech->get_ok('/admin/reports?search=ref:' . $report->external_id); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + + $mech->get_ok('/admin/reports?search=' . $report->user->email); + + my $u_id = $update->id; + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id#update_$u_id"[^>]*>$u_id</a>} ); + + $update->state('hidden'); + $update->update; + + $mech->get_ok('/admin/reports?search=' . $report->user->email); + $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td> \s* $u_id \s* </td>}xs ); + + $report->state('hidden'); + $report->update; + + $mech->get_ok('/admin/reports?search=' . $report->user->email); + $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td[^>]*> \s* $r_id \s* </td>}xs ); + + $report->state('fixed - user'); + $report->update; + + $mech->get_ok('/admin/reports?search=' . $report->user->email); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); +}; + +done_testing(); diff --git a/t/app/controller/admin_states.t b/t/app/controller/admin/states.t index 60ffe5b88..60ffe5b88 100644 --- a/t/app/controller/admin_states.t +++ b/t/app/controller/admin/states.t diff --git a/t/app/controller/admin/stats.t b/t/app/controller/admin/stats.t new file mode 100644 index 000000000..dae51d31f --- /dev/null +++ b/t/app/controller/admin/stats.t @@ -0,0 +1,12 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +subtest "smoke view some stats pages" => sub { + $mech->log_in_ok( $superuser->email ); + $mech->get_ok('/admin/stats/fix-rate'); + $mech->get_ok('/admin/stats/questionnaire'); +}; + +done_testing(); diff --git a/t/app/controller/admin/templates.t b/t/app/controller/admin/templates.t new file mode 100644 index 000000000..179a1afcb --- /dev/null +++ b/t/app/controller/admin/templates.t @@ -0,0 +1,183 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $oxfordshirecontact = $mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Potholes', email => 'potholes@example.com' ); +my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); + +my $dt = DateTime->new( + year => 2011, + month => 04, + day => 16, + hour => 15, + minute => 47, + second => 23 +); + +my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( + { + postcode => 'SW1A 1AA', + bodies_str => '2504', + areas => ',105255,11806,11828,2247,2504,', + category => 'Other', + title => 'Report to Edit', + detail => 'Detail for Report to Edit', + used_map => 't', + name => 'Test User', + anonymous => 'f', + external_id => '13', + state => 'confirmed', + confirmed => $dt->ymd . ' ' . $dt->hms, + lang => 'en-gb', + service => '', + cobrand => '', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.5016605453401', + longitude => '-0.142497580865087', + user_id => $user->id, + whensent => $dt->ymd . ' ' . $dt->hms, + } +); + +$mech->log_in_ok( $superuser->email ); + +my $report_id = $report->id; +ok $report, "created test report - $report_id"; + +subtest "response templates can be added" => sub { + is $oxfordshire->response_templates->count, 0, "No response templates yet"; + $mech->log_in_ok( $superuser->email ); + $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); + + my $fields = { + title => "Report acknowledgement", + text => "Thank you for your report. We will respond shortly.", + auto_response => undef, + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + + is $oxfordshire->response_templates->count, 1, "Response template was added"; +}; + +subtest "response templates are included on page" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $report->update({ category => $oxfordshirecontact->category, bodies_str => $oxfordshire->id }); + $mech->log_in_ok( $oxfordshireuser->email ); + + $mech->get_ok("/report/" . $report->id); + $mech->content_contains( $oxfordshire->response_templates->first->text ); + + $mech->log_out_ok; + }; +}; + +subtest "auto-response templates that duplicate a single category can't be added" => sub { + $mech->delete_response_template($_) for $oxfordshire->response_templates; + my $template = $oxfordshire->response_templates->create({ + title => "Report fixed - potholes", + text => "Thank you for your report. This problem has been fixed.", + auto_response => 1, + state => 'fixed - council', + }); + $template->contact_response_templates->find_or_create({ + contact_id => $oxfordshirecontact->id, + }); + is $oxfordshire->response_templates->count, 1, "Initial response template was created"; + + + $mech->log_in_ok( $superuser->email ); + $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); + + # This response template has the same category & state as an existing one + # so won't be allowed. + my $fields = { + title => "Report marked fixed - potholes", + text => "Thank you for your report. This pothole has been fixed.", + auto_response => 'on', + state => 'fixed - council', + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + is $mech->uri->path, '/admin/templates/' . $oxfordshire->id . '/new', 'not redirected'; + $mech->content_contains( 'Please correct the errors below' ); + $mech->content_contains( 'There is already an auto-response template for this category/state.' ); + + is $oxfordshire->response_templates->count, 1, "Duplicate response template wasn't added"; +}; + +subtest "auto-response templates that duplicate all categories can't be added" => sub { + $mech->delete_response_template($_) for $oxfordshire->response_templates; + $oxfordshire->response_templates->create({ + title => "Report investigating - all cats", + text => "Thank you for your report. This problem has been fixed.", + auto_response => 1, + state => 'fixed - council', + }); + is $oxfordshire->response_templates->count, 1, "Initial response template was created"; + + + $mech->log_in_ok( $superuser->email ); + $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); + + # There's already a response template for all categories and this state, so + # this new template won't be allowed. + my $fields = { + title => "Report investigating - single cat", + text => "Thank you for your report. This problem has been fixed.", + auto_response => 'on', + state => 'fixed - council', + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + is $mech->uri->path, '/admin/templates/' . $oxfordshire->id . '/new', 'not redirected'; + $mech->content_contains( 'Please correct the errors below' ); + $mech->content_contains( 'There is already an auto-response template for this category/state.' ); + + + is $oxfordshire->response_templates->count, 1, "Duplicate response template wasn't added"; +}; + +subtest "all-category auto-response templates that duplicate a single category can't be added" => sub { + $mech->delete_response_template($_) for $oxfordshire->response_templates; + my $template = $oxfordshire->response_templates->create({ + title => "Report fixed - potholes", + text => "Thank you for your report. This problem has been fixed.", + auto_response => 1, + state => 'fixed - council', + }); + $template->contact_response_templates->find_or_create({ + contact_id => $oxfordshirecontact->id, + }); + is $oxfordshire->response_templates->count, 1, "Initial response template was created"; + + + $mech->log_in_ok( $superuser->email ); + $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); + + # This response template is implicitly for all categories, but there's + # already a template for a specific category in this state, so it won't be + # allowed. + my $fields = { + title => "Report marked fixed - all cats", + text => "Thank you for your report. This problem has been fixed.", + auto_response => 'on', + state => 'fixed - council', + }; + $mech->submit_form_ok( { with_fields => $fields } ); + is $mech->uri->path, '/admin/templates/' . $oxfordshire->id . '/new', 'not redirected'; + $mech->content_contains( 'Please correct the errors below' ); + $mech->content_contains( 'There is already an auto-response template for this category/state.' ); + + is $oxfordshire->response_templates->count, 1, "Duplicate response template wasn't added"; +}; + +done_testing(); diff --git a/t/app/controller/admin_translations.t b/t/app/controller/admin/translations.t index f5c32baa6..f5c32baa6 100644 --- a/t/app/controller/admin_translations.t +++ b/t/app/controller/admin/translations.t diff --git a/t/app/controller/admin/update_edit.t b/t/app/controller/admin/update_edit.t new file mode 100644 index 000000000..6ddbdbdfc --- /dev/null +++ b/t/app/controller/admin/update_edit.t @@ -0,0 +1,383 @@ +use FixMyStreet::TestMech; +# avoid wide character warnings from the category change message +use open ':std', ':encoding(UTF-8)'; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $user3 = FixMyStreet::App->model('DB::User')->create( { email => 'test3@example.com' } ); + +my $dt = DateTime->new( + year => 2011, + month => 04, + day => 16, + hour => 15, + minute => 47, + second => 23 +); + +my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( + { + postcode => 'SW1A 1AA', + bodies_str => '2504', + areas => ',105255,11806,11828,2247,2504,', + category => 'Other', + title => 'Report to Edit', + detail => 'Detail for Report to Edit', + used_map => 't', + name => 'Test User', + anonymous => 'f', + external_id => '13', + state => 'confirmed', + confirmed => $dt->ymd . ' ' . $dt->hms, + lang => 'en-gb', + service => '', + cobrand => '', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.5016605453401', + longitude => '-0.142497580865087', + user_id => $user->id, + whensent => $dt->ymd . ' ' . $dt->hms, + } +); + +$mech->log_in_ok( $superuser->email ); + +my $report_id = $report->id; +ok $report, "created test report - $report_id"; + +my $update = FixMyStreet::App->model('DB::Comment')->create( + { + text => 'this is an update', + user => $user, + state => 'confirmed', + problem => $report, + mark_fixed => 0, + anonymous => 1, + } +); + +my $log_entries = FixMyStreet::App->model('DB::AdminLog')->search( + { + object_type => 'update', + object_id => $update->id + }, + { + order_by => { -desc => 'id' }, + } +); + +is $log_entries->count, 0, 'no admin log entries'; + +for my $test ( + { + desc => 'edit update text', + fields => { + text => 'this is an update', + state => 'confirmed', + name => '', + anonymous => 1, + username => 'test@example.com', + }, + changes => { + text => 'this is a changed update', + }, + log_count => 1, + log_entries => [qw/edit/], + }, + { + desc => 'edit update name', + fields => { + text => 'this is a changed update', + state => 'confirmed', + name => '', + anonymous => 1, + username => 'test@example.com', + }, + changes => { + name => 'A User', + }, + log_count => 2, + log_entries => [qw/edit edit/], + }, + { + desc => 'edit update anonymous', + fields => { + text => 'this is a changed update', + state => 'confirmed', + name => 'A User', + anonymous => 1, + username => 'test@example.com', + }, + changes => { + anonymous => 0, + }, + log_count => 3, + log_entries => [qw/edit edit edit/], + }, + { + desc => 'edit update user', + fields => { + text => 'this is a changed update', + state => 'confirmed', + name => 'A User', + anonymous => 0, + username => 'test@example.com', + }, + changes => { + username => 'test2@example.com', + }, + log_count => 4, + log_entries => [qw/edit edit edit edit/], + user => $user2, + }, + { + desc => 'edit update state', + fields => { + text => 'this is a changed update', + state => 'confirmed', + name => 'A User', + anonymous => 0, + username => 'test2@example.com', + }, + changes => { + state => 'unconfirmed', + }, + log_count => 5, + log_entries => [qw/state_change edit edit edit edit/], + }, + { + desc => 'edit update state and text', + fields => { + text => 'this is a changed update', + state => 'unconfirmed', + name => 'A User', + anonymous => 0, + username => 'test2@example.com', + }, + changes => { + text => 'this is a twice changed update', + state => 'confirmed', + }, + log_count => 7, + log_entries => [qw/edit state_change state_change edit edit edit edit/], + }, +) { + subtest $test->{desc} => sub { + $log_entries->reset; + $mech->get_ok('/admin/update_edit/' . $update->id ); + + is_deeply $mech->visible_form_values, $test->{fields}, 'initial form values'; + + my $to_submit = { + %{ $test->{fields} }, + %{ $test->{changes} } + }; + + $mech->submit_form_ok( { with_fields => $to_submit } ); + + is_deeply $mech->visible_form_values, $to_submit, 'submitted form values'; + + is $log_entries->count, $test->{log_count}, 'number of log entries'; + is $log_entries->next->action, $_, 'log action' for @{ $test->{log_entries} }; + + $update->discard_changes; + + is $update->$_, $test->{changes}->{$_} for grep { $_ ne 'username' } keys %{ $test->{changes} }; + if ( $test->{changes}{state} && $test->{changes}{state} eq 'confirmed' ) { + isnt $update->confirmed, undef; + } + + if ( $test->{user} ) { + is $update->user->id, $test->{user}->id, 'update user'; + } + }; +} + +my $westminster = $mech->create_body_ok(2504, 'Westminster City Council'); +$report->bodies_str($westminster->id); +$report->update; + +for my $test ( + { + desc => 'user is problem owner', + problem_user => $user, + update_user => $user, + update_fixed => 0, + update_reopen => 0, + update_state => undef, + user_body => undef, + content => 'user is problem owner', + }, + { + desc => 'user is body user', + problem_user => $user, + update_user => $user2, + update_fixed => 0, + update_reopen => 0, + update_state => undef, + user_body => $westminster->id, + content => 'user is from same council as problem - ' . $westminster->id, + }, + { + desc => 'update changed problem state', + problem_user => $user, + update_user => $user2, + update_fixed => 0, + update_reopen => 0, + update_state => 'planned', + user_body => $westminster->id, + content => 'Update changed problem state to planned', + }, + { + desc => 'update marked problem as fixed', + problem_user => $user, + update_user => $user3, + update_fixed => 1, + update_reopen => 0, + update_state => undef, + user_body => undef, + content => 'Update marked problem as fixed', + }, + { + desc => 'update reopened problem', + problem_user => $user, + update_user => $user, + update_fixed => 0, + update_reopen => 1, + update_state => undef, + user_body => undef, + content => 'Update reopened problem', + }, +) { + subtest $test->{desc} => sub { + $report->user( $test->{problem_user} ); + $report->update; + + $update->user( $test->{update_user} ); + $update->problem_state( $test->{update_state} ); + $update->mark_fixed( $test->{update_fixed} ); + $update->mark_open( $test->{update_reopen} ); + $update->update; + + $test->{update_user}->from_body( $test->{user_body} ); + $test->{update_user}->update; + + $mech->get_ok('/admin/update_edit/' . $update->id ); + $mech->content_contains( $test->{content} ); + }; +} + +subtest 'editing update email creates new user if required' => sub { + my $user = FixMyStreet::App->model('DB::User')->find( { email => 'test4@example.com' } ); + + $user->delete if $user; + + my $fields = { + text => 'this is a changed update', + state => 'confirmed', + name => 'A User', + anonymous => 0, + username => 'test4@example.com', + }; + + $mech->submit_form_ok( { with_fields => $fields } ); + + $user = FixMyStreet::App->model('DB::User')->find( { email => 'test4@example.com' } ); + + is_deeply $mech->visible_form_values, $fields, 'submitted form values'; + + ok $user, 'new user created'; + + $update->discard_changes; + is $update->user->id, $user->id, 'update set to new user'; +}; + +subtest 'adding email to abuse list from update page' => sub { + my $email = $update->user->email; + + my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); + $abuse->delete if $abuse; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Ban user'); + + $mech->click_ok('banuser'); + + $mech->content_contains('User added to abuse list'); + $mech->content_contains('<small>User in abuse table</small>'); + + $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); + ok $abuse, 'entry created in abuse table'; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('<small>User in abuse table</small>'); +}; + +subtest 'flagging user from update page' => sub { + $update->user->flagged(0); + $update->user->update; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Flag user'); + + $mech->click_ok('flaguser'); + + $mech->content_contains('User flagged'); + $mech->content_contains('Remove flag'); + + $update->discard_changes; + ok $update->user->flagged, 'user flagged'; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Remove flag'); +}; + +subtest 'unflagging user from update page' => sub { + $update->user->flagged(1); + $update->user->update; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Remove flag'); + + $mech->click_ok('removeuserflag'); + + $mech->content_contains('User flag removed'); + $mech->content_contains('Flag user'); + + $update->discard_changes; + ok !$update->user->flagged, 'user not flagged'; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Flag user'); +}; + +subtest 'hiding comment marked as fixed reopens report' => sub { + $update->mark_fixed( 1 ); + $update->update; + + $report->state('fixed - user'); + $report->update; + + my $fields = { + text => 'this is a changed update', + state => 'hidden', + name => 'A User', + anonymous => 0, + username => 'test2@example.com', + }; + + $mech->submit_form_ok( { with_fields => $fields } ); + + $report->discard_changes; + is $report->state, 'confirmed', 'report reopened'; + $mech->content_contains('Problem marked as open'); +}; + +done_testing(); diff --git a/t/app/controller/admin/users.t b/t/app/controller/admin/users.t new file mode 100644 index 000000000..8759e260d --- /dev/null +++ b/t/app/controller/admin/users.t @@ -0,0 +1,453 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $haringey = $mech->create_body_ok(2509, 'Haringey Borough Council'); +my $southend = $mech->create_body_ok(2607, 'Southend-on-Sea Borough Council'); + +$mech->log_in_ok( $superuser->email ); + +subtest 'search abuse' => sub { + my $abuse = FixMyStreet::App->model('DB::Abuse')->find_or_create( { email => $user->email } ); + $mech->get_ok( '/admin/users?search=example' ); + $mech->content_like(qr{test\@example.com.*</td>\s*<td>.*?</td>\s*<td>User in abuse table}s); +}; + +subtest 'remove user from abuse list from edit user page' => sub { + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->content_contains('User in abuse table'); + + $mech->click_ok('unban'); + + my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $user->email } ); + ok !$abuse, 'record removed from abuse table'; +}; + +subtest 'remove user with phone account from abuse list from edit user page' => sub { + my $abuse_user = $mech->create_user_ok('01234 456789'); + my $abuse = FixMyStreet::App->model('DB::Abuse')->find_or_create( { email => $abuse_user->phone } ); + $mech->get_ok( '/admin/user_edit/' . $abuse_user->id ); + $mech->content_contains('User in abuse table'); + my $abuse_found = FixMyStreet::App->model('DB::Abuse')->find( { email => $abuse_user->phone } ); + ok $abuse_found, 'user in abuse table'; + + $mech->click_ok('unban'); + + $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $user->phone } ); + ok !$abuse, 'record removed from abuse table'; +}; + +subtest 'no option to remove user already in abuse list' => sub { + my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $user->email } ); + $abuse->delete if $abuse; + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->content_lacks('User in abuse table'); +}; + +subtest 'show flagged entries' => sub { + $user->flagged( 1 ); + $user->update; + + $mech->get_ok('/admin/flagged'); + $mech->content_contains( $user->email ); + + $user->flagged( 0 ); + $user->update; +}; + +subtest 'user search' => sub { + $mech->get_ok('/admin/users'); + $mech->get_ok('/admin/users?search=' . $user->name); + + $mech->content_contains( $user->name); + my $u_id = $user->id; + $mech->content_like( qr{user_edit/$u_id">Edit</a>} ); + + $mech->get_ok('/admin/users?search=' . $user->email); + + $mech->content_like( qr{user_edit/$u_id">Edit</a>} ); + + $user->from_body($haringey->id); + $user->update; + $mech->get_ok('/admin/users?search=' . $haringey->id ); + $mech->content_contains('Haringey'); +}; + +subtest 'search does not show user from another council' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $mech->get_ok('/admin/users'); + $mech->get_ok('/admin/users?search=' . $user->name); + + $mech->content_contains( "Searching found no users." ); + + $mech->get_ok('/admin/users?search=' . $user->email); + $mech->content_contains( "Searching found no users." ); + }; +}; + +subtest 'user_edit does not show user from another council' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $mech->get('/admin/user_edit/' . $user->id); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404"; + }; +}; + +for my $test ( + { + desc => 'add user - blank form', + fields => { + email => '', email_verified => 0, + phone => '', phone_verified => 0, + }, + error => ['Please verify at least one of email/phone', 'Please enter a name'], + }, + { + desc => 'add user - blank, verify phone', + fields => { + email => '', email_verified => 0, + phone => '', phone_verified => 1, + }, + error => ['Please enter a valid email or phone number', 'Please enter a name'], + }, + { + desc => 'add user - bad email', + fields => { + name => 'Norman', + email => 'bademail', email_verified => 0, + phone => '', phone_verified => 0, + }, + error => ['Please enter a valid email'], + }, + { + desc => 'add user - bad phone', + fields => { + name => 'Norman', + phone => '01214960000000', phone_verified => 1, + }, + error => ['Please check your phone number is correct'], + }, + { + desc => 'add user - landline', + fields => { + name => 'Norman Name', + phone => '+441214960000', + phone_verified => 1, + }, + error => ['Please enter a mobile number'], + }, + { + desc => 'add user - good details', + fields => { + name => 'Norman Name', + phone => '+61491570156', + phone_verified => 1, + }, + }, +) { + subtest $test->{desc} => sub { + $mech->get_ok('/admin/users'); + $mech->submit_form_ok( { with_fields => $test->{fields} } ); + if ($test->{error}) { + $mech->content_contains($_) for @{$test->{error}}; + } else { + $mech->content_contains('Updated'); + } + }; +} + +my %default_perms = ( + "permissions[moderate]" => undef, + "permissions[planned_reports]" => undef, + "permissions[report_edit]" => undef, + "permissions[report_edit_category]" => undef, + "permissions[report_edit_priority]" => undef, + "permissions[report_inspect]" => undef, + "permissions[report_instruct]" => undef, + "permissions[contribute_as_another_user]" => undef, + "permissions[contribute_as_anonymous_user]" => undef, + "permissions[contribute_as_body]" => undef, + "permissions[view_body_contribute_details]" => undef, + "permissions[user_edit]" => undef, + "permissions[user_manage_permissions]" => undef, + "permissions[user_assign_body]" => undef, + "permissions[user_assign_areas]" => undef, + "permissions[template_edit]" => undef, + "permissions[responsepriority_edit]" => undef, + "permissions[category_edit]" => undef, + trusted_bodies => undef, +); + +# Start this section with user having no name +# Regression test for mysociety/fixmystreetforcouncils#250 +$user->update({ name => '' }); + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', +}, sub { + for my $test ( + { + desc => 'edit user name', + fields => { + name => '', + email => 'test@example.com', + email_verified => 1, + body => $haringey->id, + phone => '', + phone_verified => undef, + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + name => 'Changed User', + }, + log_count => 1, + log_entries => [qw/edit/], + }, + { + desc => 'edit user email', + fields => { + name => 'Changed User', + email => 'test@example.com', + email_verified => 1, + body => $haringey->id, + phone => '', + phone_verified => undef, + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + email => 'changed@example.com', + }, + log_count => 2, + log_entries => [qw/edit edit/], + }, + { + desc => 'edit user body', + fields => { + name => 'Changed User', + email => 'changed@example.com', + email_verified => 1, + body => $haringey->id, + phone => '', + phone_verified => undef, + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + body => $southend->id, + }, + log_count => 3, + log_entries => [qw/edit edit edit/], + }, + { + desc => 'edit user flagged', + fields => { + name => 'Changed User', + email => 'changed@example.com', + email_verified => 1, + body => $southend->id, + phone => '', + phone_verified => undef, + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + flagged => 'on', + }, + log_count => 4, + log_entries => [qw/edit edit edit edit/], + }, + { + desc => 'edit user remove flagged', + fields => { + name => 'Changed User', + email => 'changed@example.com', + email_verified => 1, + body => $southend->id, + phone => '', + phone_verified => undef, + flagged => 'on', + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + flagged => undef, + }, + log_count => 4, + log_entries => [qw/edit edit edit edit/], + }, + { + desc => 'edit user add is_superuser', + fields => { + name => 'Changed User', + email => 'changed@example.com', + email_verified => 1, + body => $southend->id, + phone => '', + phone_verified => undef, + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + is_superuser => 'on', + }, + removed => [ + keys %default_perms, + ], + log_count => 5, + log_entries => [qw/edit edit edit edit edit/], + }, + { + desc => 'edit user remove is_superuser', + fields => { + name => 'Changed User', + email => 'changed@example.com', + email_verified => 1, + body => $southend->id, + phone => '', + phone_verified => undef, + flagged => undef, + is_superuser => 'on', + area_id => '', + }, + changes => { + is_superuser => undef, + }, + added => { + %default_perms, + }, + log_count => 5, + log_entries => [qw/edit edit edit edit edit/], + }, + ) { + subtest $test->{desc} => sub { + $mech->get_ok( '/admin/user_edit/' . $user->id ); + + my $visible = $mech->visible_form_values; + is_deeply $visible, $test->{fields}, 'expected user'; + + my $expected = { + %{ $test->{fields} }, + %{ $test->{changes} } + }; + + $mech->submit_form_ok( { with_fields => $expected } ); + + # Some actions cause visible fields to be added/removed + foreach my $x (@{ $test->{removed} }) { + delete $expected->{$x}; + } + if ( $test->{added} ) { + $expected = { + %$expected, + %{ $test->{added} } + }; + } + + $visible = $mech->visible_form_values; + is_deeply $visible, $expected, 'user updated'; + + $mech->content_contains( 'Updated!' ); + }; + } +}; + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + SMS_AUTHENTICATION => 1, +}, sub { + subtest "Test edit user add verified phone" => sub { + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->submit_form_ok( { with_fields => { + phone => '+61491570157', + phone_verified => 1, + } } ); + $mech->content_contains( 'Updated!' ); + }; + + subtest "Test changing user to an existing one" => sub { + my $existing_user = $mech->create_user_ok('existing@example.com', name => 'Existing User'); + $mech->create_problems_for_body(2, 2514, 'Title', { user => $existing_user }); + my $count = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id })->count; + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->submit_form_ok( { with_fields => { email => 'existing@example.com' } }, 'submit email change' ); + is $mech->uri->path, '/admin/user_edit/' . $existing_user->id, 'redirected'; + my $p = FixMyStreet::DB->resultset('Problem')->search({ user_id => $existing_user->id })->count; + is $p, $count + 2, 'reports merged'; + }; + +}; + +$user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +subtest "Anonymizing user from admin" => sub { + $mech->create_problems_for_body(4, 2237, 'Title'); + my $count_p = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id })->count; + my $count_u = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id })->count; + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->submit_form_ok({ button => 'anon_everywhere' }); + my $c = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id, anonymous => 1 })->count; + is $c, $count_p; + $c = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id, anonymous => 1 })->count; + is $c, $count_u; +}; + +subtest "Hiding user's reports from admin" => sub { + my $count_p = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id })->count; + my $count_u = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id })->count; + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->submit_form_ok({ button => 'hide_everywhere' }); + my $c = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id, state => 'hidden' })->count; + is $c, $count_p; + $c = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id, state => 'hidden' })->count; + is $c, $count_u; +}; + +subtest "Logging user out" => sub { + my $mech2 = FixMyStreet::TestMech->new; + $mech2->log_in_ok($user->email); + $mech2->logged_in_ok; + + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->submit_form_ok({ button => 'logout_everywhere' }, 'Logging user out'); + $mech2->not_logged_in_ok; +}; + +subtest "Removing account from admin" => sub { + $mech->create_problems_for_body(4, 2237, 'Title'); + my $count_p = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id })->count; + my $count_u = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id })->count; + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->submit_form_ok({ button => 'remove_account' }, 'Removing account'); + my $c = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id, anonymous => 1, name => '' })->count; + is $c, $count_p, 'All reports anon/nameless'; + $c = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id, anonymous => 1, name => '' })->count; + is $c, $count_u, 'All updates anon/nameless'; + $user->discard_changes; + is $user->name, '', 'Name gone'; + is $user->password, '', 'Password gone'; + is $user->email, 'removed-' . $user->id . '@example.org', 'Email gone' +}; + +done_testing(); diff --git a/t/app/controller/around.t b/t/app/controller/around.t index d1254edb7..618998513 100644 --- a/t/app/controller/around.t +++ b/t/app/controller/around.t @@ -137,25 +137,28 @@ subtest 'check non public reports are not displayed on around page' => sub { }; -subtest 'check category and status filtering works on /around?ajax' => sub { +subtest 'check category and status filtering works on /around' => sub { + my $body = $mech->create_body_ok(2237, "Oxfordshire"); + my $categories = [ 'Pothole', 'Vegetation', 'Flytipping' ]; my $params = { - postcode => 'OX1 1ND', - latitude => 51.7435918829363, - longitude => -1.23201966270446, + postcode => 'OX20 1SZ', + latitude => 51.754926, + longitude => -1.256179, }; my $bbox = ($params->{longitude} - 0.01) . ',' . ($params->{latitude} - 0.01) . ',' . ($params->{longitude} + 0.01) . ',' . ($params->{latitude} + 0.01); # Create one open and one fixed report in each category foreach my $category ( @$categories ) { + $mech->create_contact_ok( category => $category, body_id => $body->id, email => "$category\@example.org" ); foreach my $state ( 'confirmed', 'fixed' ) { my %report_params = ( %$params, category => $category, state => $state, ); - $mech->create_problems_for_body( 1, 2237, 'Around page', \%report_params ); + $mech->create_problems_for_body( 1, $body->id, 'Around page', \%report_params ); } } @@ -163,6 +166,14 @@ subtest 'check category and status filtering works on /around?ajax' => sub { my $pins = $json->{pins}; is scalar @$pins, 6, 'correct number of reports when no filters'; + # Regression test for filter_category in /around URL + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok( '/around?filter_category=Pothole&bbox=' . $bbox ); + $mech->content_contains('<option value="Pothole" selected>'); + }; + $json = $mech->get_ok_json( '/around?ajax=1&filter_category=Pothole&bbox=' . $bbox ); $pins = $json->{pins}; is scalar @$pins, 2, 'correct number of Pothole reports'; diff --git a/t/app/controller/auth.t b/t/app/controller/auth.t index 8d60137a2..8cc7e4154 100644 --- a/t/app/controller/auth.t +++ b/t/app/controller/auth.t @@ -5,7 +5,7 @@ my $mech = FixMyStreet::TestMech->new; my $test_email = 'test@example.com'; my $test_email3 = 'newuser@example.org'; -my $test_password = 'foobar'; +my $test_password = 'foobar123'; END { done_testing(); @@ -276,3 +276,49 @@ subtest "check logging in with token" => sub { $mech->delete_header('Authorization'); }; + +subtest 'check password length/common' => sub { + $mech->get_ok('/auth'); + $mech->submit_form_ok({ + form_name => 'general_auth', + fields => { username => $test_email, password_register => 'short' }, + button => 'sign_in_by_code', + }); + $mech->content_contains("Please make sure your password is at least"); + $mech->submit_form_ok({ + form_name => 'general_auth', + fields => { username => $test_email, password_register => 'common' }, + button => 'sign_in_by_code', + }); + $mech->content_contains("Please choose a less commonly-used password"); +}; + +subtest 'check common password AJAX call' => sub { + $mech->post_ok('/auth/common_password', { password_register => 'password' }); + $mech->content_contains("Please choose a less commonly-used password"); + $mech->post_ok('/auth/common_password', { password_register => 'squirblewirble' }); + $mech->content_contains("true"); +}; + +subtest "Test two-factor authentication login" => sub { + use Auth::GoogleAuth; + my $auth = Auth::GoogleAuth->new; + my $code = $auth->code; + my $wrong_code = $auth->code(undef, time() - 120); + + my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + $user->is_superuser(1); + $user->password('password'); + $user->set_extra_metadata('2fa_secret', $auth->secret32); + $user->update; + + $mech->get_ok('/auth'); + $mech->submit_form_ok( + { with_fields => { username => $test_email, password_sign_in => 'password' } }, + "sign in using form" ); + $mech->content_contains('Please generate a two-factor code'); + $mech->submit_form_ok({ with_fields => { '2fa_code' => $wrong_code } }, "provide wrong 2FA code" ); + $mech->content_contains('Try again'); + $mech->submit_form_ok({ with_fields => { '2fa_code' => $code } }, "provide correct 2FA code" ); + $mech->logged_in_ok; +}; diff --git a/t/app/controller/auth_profile.t b/t/app/controller/auth_profile.t index 74edccfe6..4be1be12c 100644 --- a/t/app/controller/auth_profile.t +++ b/t/app/controller/auth_profile.t @@ -8,7 +8,7 @@ LWP::Protocol::PSGI->register($twilio->to_psgi_app, host => 'api.twilio.com'); my $test_email = 'test@example.com'; my $test_email2 = 'test@example.net'; -my $test_password = 'foobar'; +my $test_password = 'foobar123'; END { done_testing(); @@ -75,12 +75,18 @@ subtest "Test change password page" => sub { { form_name => 'change_password', fields => - { new_password => $test_password, confirm => $test_password, }, + { new_password => 'new_password', confirm => 'new_password', }, }, - "change_password with '$test_password' and '$test_password'" + "change_password with 'new_password' and 'new_password'" ); is $mech->uri->path, '/auth/change_password', "still on change password page"; + $mech->content_contains('check your email'); + + $link = $mech->get_link_from_email; + $mech->get_ok($link); + is $mech->uri->path, '/my', "redirected to /my"; + $mech->content_contains( 'password has been changed', "found password changed" ); @@ -88,6 +94,73 @@ subtest "Test change password page" => sub { ok $user->password, "user now has a password"; }; +# Change password, when already got one +subtest "Test change password page with current password" => sub { + $mech->get_ok('/auth/change_password'); + + ok my $form = $mech->form_name('change_password'), + "found change password form"; + is_deeply [ sort grep { $_ } map { $_->name } $form->inputs ], # + [ 'confirm', 'current_password', 'new_password', 'token' ], + "check we got expected fields (ie not old_password)"; + + # check the various ways the form can be wrong + for my $test ( + { current => '', new => '', conf => '', err => 'check the passwords', }, + { current => 'new_password', new => '', conf => '', err => 'enter a password', }, + { current => 'new_password', new => 'secret', conf => '', err => 'do not match', }, + { current => 'new_password', new => '', conf => 'secret', err => 'do not match', }, + { current => 'new_password', new => 'secret', conf => 'not_secret', err => 'do not match', }, + ) + { + $mech->get_ok('/auth/change_password'); + $mech->content_lacks( $test->{err}, "did not find expected error" ); + $mech->submit_form_ok( + { + form_name => 'change_password', + fields => + { current_password => $test->{current}, new_password => $test->{new}, confirm => $test->{conf}, }, + }, + "change_password with '$test->{current}', '$test->{new}' and '$test->{conf}'" + ); + $mech->content_contains( $test->{err}, "found expected error" ); + } + + my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + ok $user, "got a user"; + + $mech->get_ok('/auth/change_password'); + $mech->submit_form_ok( + { + form_name => 'change_password', + fields => + { current_password => 'new_password', new_password => $test_password, confirm => $test_password }, + }, + "change_password with 'new_password' and '$test_password'" + ); + is $mech->uri->path, '/auth/change_password', + "still on change password page"; + $mech->content_contains( 'password has been changed', + "found password changed" ); + + $user->discard_changes(); + ok $user->password, "user now has a password"; +}; + +subtest 'check password length/common' => sub { + $mech->get_ok('/auth/change_password'); + $mech->submit_form_ok({ + form_name => 'change_password', + fields => { current_password => $test_password, new_password => 'short', confirm => 'short' }, + }); + $mech->content_contains("Please make sure your password is at least"); + $mech->submit_form_ok({ + form_name => 'change_password', + fields => { current_password => $test_password, new_password => 'common', confirm => 'common' }, + }); + $mech->content_contains("Please choose a less commonly-used password"); +}; + subtest "Test change email page" => sub { $mech->create_problems_for_body(1, 2514, 'Title1', { user => FixMyStreet::DB->resultset('User')->find( { email => $test_email } ) } ); @@ -274,7 +347,7 @@ subtest "Test superuser can access generate token page" => sub { }, }); - $mech->content_lacks('Generate token'); + $mech->content_lacks('Security'); $mech->get('/auth/generate_token'); is $mech->res->code, 403, "access denied"; @@ -282,7 +355,7 @@ subtest "Test superuser can access generate token page" => sub { ok $user->update({ is_superuser => 1 }), 'user is superuser'; $mech->get_ok('/my'); - $mech->content_contains('Generate token'); + $mech->content_contains('Security'); $mech->get_ok('/auth/generate_token'); }; @@ -299,7 +372,7 @@ subtest "Test staff user can access generate token page" => sub { }, }); - $mech->content_lacks('Generate token'); + $mech->content_lacks('Security'); my $body = $mech->create_body_ok(2237, 'Oxfordshire'); @@ -309,7 +382,7 @@ subtest "Test staff user can access generate token page" => sub { ok $user->update({ from_body => $body }), 'user is staff user'; $mech->get_ok('/my'); - $mech->content_contains('Generate token'); + $mech->content_contains('Security'); $mech->get_ok('/auth/generate_token'); }; @@ -333,7 +406,7 @@ subtest "Test generate token page" => sub { $mech->follow_link_ok({url => '/auth/generate_token'}); $mech->content_lacks('Token:'); $mech->submit_form_ok( - { with_fields => { generate_token => 'Generate token' } }, + { button => 'generate_token' }, "submit generate token form" ); $mech->content_contains( 'Your token has been generated', "token generated" ); @@ -352,4 +425,28 @@ subtest "Test generate token page" => sub { $mech->log_out_ok; $mech->add_header('Authorization', "Bearer $token"); $mech->logged_in_ok; -} +}; + +subtest "Test two-factor authentication admin" => sub { + my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + ok $user->update({ is_superuser => 1 }), 'user set to superuser'; + + $mech->log_in_ok($test_email); + $mech->get_ok('/auth/generate_token'); + ok !$user->get_extra_metadata('2fa_secret'); + + $mech->submit_form_ok({ button => 'toggle_2fa' }, "submit 2FA activation"); + $mech->content_contains('has been activated', "2FA activated"); + + $user->discard_changes(); + my $token = $user->get_extra_metadata('2fa_secret'); + ok $token, '2FA secret set'; + + $mech->content_contains($token, 'secret displayed'); + + $mech->get_ok('/auth/generate_token'); + $mech->content_lacks($token, 'secret no longer displayed'); + + $mech->submit_form_ok({ button => 'toggle_2fa' }, "submit 2FA deactivation"); + $mech->content_contains('has been deactivated', "2FA deactivated"); +}; diff --git a/t/app/controller/report_as_other.t b/t/app/controller/report_as_other.t index 91644e8ce..e8f65eb7b 100644 --- a/t/app/controller/report_as_other.t +++ b/t/app/controller/report_as_other.t @@ -39,7 +39,7 @@ subtest "Body user, has permission to add report as council" => sub { }; my @users; -subtest "Body user, has permission to add report as another user" => sub { +subtest "Body user, has permission to add report as another user with email" => sub { my $report = add_report( 'contribute_as_another_user', form_as => 'another_user', @@ -57,7 +57,49 @@ subtest "Body user, has permission to add report as another user" => sub { push @users, $report->user; }; -subtest "Body user, has permission to add report as another (existing) user" => sub { +subtest "Body user, has permission to add report as another user with mobile phone number" => sub { + my $report = add_report( + 'contribute_as_another_user', + form_as => 'another_user', + title => "Test Report", + detail => 'Test report details.', + category => 'Potholes', + name => 'Another User', + username => '07906 111111', + ); + is $report->name, 'Another User', 'report name is given name'; + is $report->user->name, 'Another User', 'user name matches'; + is $report->user->phone, '+447906111111', 'user phone correct'; + is $report->user->phone_verified, 1, 'user phone verified'; + is $report->user->email, undef, 'user email correct'; + is $report->user->email_verified, 0, 'user email not verified'; + isnt $report->user->id, $user->id, 'user does not match'; + $mech->email_count_is(0); + push @users, $report->user; +}; + +subtest "Body user, has permission to add report as another user with landline number" => sub { + my $report = add_report( + 'contribute_as_another_user', + form_as => 'another_user', + title => "Test Report", + detail => 'Test report details.', + category => 'Potholes', + name => 'Another User', + username => '01685 222222', + ); + is $report->name, 'Another User', 'report name is given name'; + is $report->user->name, 'Another User', 'user name matches'; + is $report->user->phone, '+441685222222', 'user phone correct'; + is $report->user->phone_verified, 1, 'user phone verified'; + is $report->user->email, undef, 'user email correct'; + is $report->user->email_verified, 0, 'user email not verified'; + isnt $report->user->id, $user->id, 'user does not match'; + $mech->email_count_is(0); + push @users, $report->user; +}; + +subtest "Body user, has permission to add report as another (existing) user with email" => sub { $mech->create_user_ok('existing@example.net', name => 'Existing User'); my $report = add_report( 'contribute_as_another_user', @@ -76,6 +118,25 @@ subtest "Body user, has permission to add report as another (existing) user" => push @users, $report->user; }; +subtest "Body user, has permission to add report as another (existing) user with phone" => sub { + $mech->create_user_ok('+447906333333', name => 'Existing User'); + my $report = add_report( + 'contribute_as_another_user', + form_as => 'another_user', + title => "Test Report", + detail => 'Test report details.', + category => 'Potholes', + name => 'Existing Yooser', + username => '07906 333333', + ); + is $report->name, 'Existing Yooser', 'report name is given name'; + is $report->user->name, 'Existing User', 'user name remains same'; + is $report->user->phone, '+447906333333', 'user phone correct'; + isnt $report->user->id, $user->id, 'user does not match'; + $mech->email_count_is(0); + push @users, $report->user; +}; + subtest "Body user, has permission to add report as anonymous user" => sub { my $report = add_report( 'contribute_as_anonymous_user', @@ -102,7 +163,7 @@ subtest "Body user, has permission to add update as council" => sub { is $update->anonymous, 0, 'update not anonymous'; }; -subtest "Body user, has permission to add update as another user" => sub { +subtest "Body user, has permission to add update as another user with email" => sub { my $update = add_update( 'contribute_as_another_user', form_as => 'another_user', @@ -118,7 +179,39 @@ subtest "Body user, has permission to add update as another user" => sub { push @users, $update->user; }; -subtest "Body user, has permission to add update as another (existing) user" => sub { +subtest "Body user, has permission to add update as another user with mobile phone" => sub { + my $update = add_update( + 'contribute_as_another_user', + form_as => 'another_user', + update => 'Test Update', + name => 'Another User', + username => '07906 444444', + ); + is $update->name, 'Another User', 'update name is given name'; + is $update->user->name, 'Another User', 'user name matches'; + is $update->user->phone, '+447906444444', 'user phone correct'; + isnt $update->user->id, $user->id, 'user does not match'; + $mech->email_count_is(0); + push @users, $update->user; +}; + +subtest "Body user, has permission to add update as another user with landline phone" => sub { + my $update = add_update( + 'contribute_as_another_user', + form_as => 'another_user', + update => 'Test Update', + name => 'Another User', + username => '01685 555555', + ); + is $update->name, 'Another User', 'update name is given name'; + is $update->user->name, 'Another User', 'user name matches'; + is $update->user->phone, '+441685555555', 'user phone correct'; + isnt $update->user->id, $user->id, 'user does not match'; + $mech->email_count_is(0); + push @users, $update->user; +}; + +subtest "Body user, has permission to add update as another (existing) user with email" => sub { my $update = add_update( 'contribute_as_another_user', form_as => 'another_user', @@ -133,6 +226,21 @@ subtest "Body user, has permission to add update as another (existing) user" => like $mech->get_text_body_from_email, qr/Your update has been logged/; }; +subtest "Body user, has permission to add update as another (existing) user with phone" => sub { + my $update = add_update( + 'contribute_as_another_user', + form_as => 'another_user', + update => 'Test Update', + name => 'Existing Yooser', + username => '07906 333333', + ); + is $update->name, 'Existing Yooser', 'update name is given name'; + is $update->user->name, 'Existing User', 'user name remains same'; + is $update->user->phone, '+447906333333', 'user phone correct'; + isnt $update->user->id, $user->id, 'user does not match'; + $mech->email_count_is(0); +}; + subtest "Body user, has permission to add update as anonymous user" => sub { my $update = add_update( 'contribute_as_anonymous_user', @@ -155,6 +263,7 @@ sub start_report { FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'fixmystreet' ], MAPIT_URL => 'http://mapit.uk/', + PHONE_COUNTRY => 'GB', }, sub { $mech->get_ok('/report/new?latitude=51.7549262252&longitude=-1.25617899435'); }; @@ -166,6 +275,7 @@ sub add_report { FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'fixmystreet' ], MAPIT_URL => 'http://mapit.uk/', + PHONE_COUNTRY => 'GB', }, sub { dropdown_shown(1); $mech->submit_form_ok({ @@ -184,16 +294,28 @@ sub start_update { $_->delete for $user->user_body_permissions; $user->user_body_permissions->create({ body => $body, permission_type => $permission }) if $permission; - $mech->get_ok('/report/' . $report_to_update->id); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + MAPIT_URL => 'http://mapit.uk/', + PHONE_COUNTRY => 'GB', + }, sub { + $mech->get_ok('/report/' . $report_to_update->id); + }; } sub add_update { my ($permission, %fields) = @_; start_update($permission); - dropdown_shown(1, 'updateForm'); - $mech->submit_form_ok({ - with_fields => \%fields, - }, "submit details"); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + MAPIT_URL => 'http://mapit.uk/', + PHONE_COUNTRY => 'GB', + }, sub { + dropdown_shown(1, 'updateForm'); + $mech->submit_form_ok({ + with_fields => \%fields, + }, "submit details"); + }; $mech->content_contains('Thank you for updating this issue'); my $update = FixMyStreet::DB->resultset("Comment")->search(undef, { order_by => { -desc => 'id' } })->first; ok $update, "Found the update"; diff --git a/t/app/controller/report_new.t b/t/app/controller/report_new.t index e0fe205bd..3c120b0b0 100644 --- a/t/app/controller/report_new.t +++ b/t/app/controller/report_new.t @@ -705,7 +705,11 @@ subtest "test password errors for a user who is signing in as they report" => su ], "check there were errors"; }; -subtest "test report creation for a user who is signing in as they report" => sub { +foreach my $test ( + { two_factor => 0, desc => '', }, + { two_factor => 1, desc => ' with two-factor', }, +) { + subtest "test report creation for a user who is signing in as they report$test->{desc}" => sub { $mech->log_out_ok; $mech->cookie_jar({}); $mech->clear_emails_ok; @@ -722,6 +726,15 @@ subtest "test report creation for a user who is signing in as they report" => su password => 'secret2', } ), "set user details"; + my $auth; + if ($test->{two_factor}) { + use Auth::GoogleAuth; + $auth = Auth::GoogleAuth->new; + $user->is_superuser(1); + $user->set_extra_metadata('2fa_secret', $auth->generate_secret32); + $user->update; + } + # submit initial pc form $mech->get_ok('/around'); FixMyStreet::override_config { @@ -742,7 +755,7 @@ subtest "test report creation for a user who is signing in as they report" => su title => 'Test Report', detail => 'Test report details.', photo1 => '', - username => 'test-2@example.com', + username => $test_email, password_sign_in => 'secret2', category => 'Street lighting', } @@ -750,6 +763,15 @@ subtest "test report creation for a user who is signing in as they report" => su "submit good details" ); + if ($test->{two_factor}) { + my $code = $auth->code; + my $wrong_code = $auth->code(undef, time() - 120); + $mech->content_contains('Please generate a two-factor code'); + $mech->submit_form_ok({ with_fields => { '2fa_code' => $wrong_code } }, "provide wrong 2FA code" ); + $mech->content_contains('Try again'); + $mech->submit_form_ok({ with_fields => { '2fa_code' => $code } }, "provide correct 2FA code" ); + } + # check that we got the message expected $mech->content_contains( 'You have successfully signed in; please check and confirm your details are accurate:' ); @@ -768,7 +790,10 @@ subtest "test report creation for a user who is signing in as they report" => su my $report = $user->problems->first; ok $report, "Found the report"; - $mech->content_contains('Thank you for reporting this issue'); + if (!$test->{two_factor}) { + # The superuser account will be immediately redirected + $mech->content_contains('Thank you for reporting this issue'); + } # Check the report has been assigned appropriately is $report->bodies_str, $body_ids{2651}; @@ -793,7 +818,8 @@ subtest "test report creation for a user who is signing in as they report" => su # cleanup $mech->delete_user($user) -}; + }; +} #### test report creation for user with account and logged in my ($saved_lat, $saved_lon); @@ -1236,9 +1262,7 @@ for my $test ( is $user->title, $test->{'user_title'}, 'user title correct'; is_deeply $extras, $test->{extra}, 'extra contains correct values'; - $user->problems->delete; - $user->alerts->delete; - $user->delete; + $mech->delete_user($user); }; } @@ -1705,9 +1729,7 @@ subtest "extra google analytics code displayed on email confirmation problem cre $mech->content_contains( "'id': 'report/" . $report->id . "'", 'extra google code present' ); - $user->problems->delete; - $user->alerts->delete; - $user->delete; + $mech->delete_user($user); }; }; diff --git a/t/app/controller/report_new_text.t b/t/app/controller/report_new_text.t index 734b9dbb4..cb07e57ee 100644 --- a/t/app/controller/report_new_text.t +++ b/t/app/controller/report_new_text.t @@ -45,8 +45,8 @@ foreach my $test ( password_register => '', password_sign_in => '', }, changes => { - username => '+44 121 496 0000', - phone => '+44 121 496 0000', + username => '0121 496 0000', + phone => '0121 496 0000', }, errors => [ 'Please enter a mobile number', ], }, diff --git a/t/app/controller/report_update_text.t b/t/app/controller/report_update_text.t index 45b4e78c2..a3b767221 100644 --- a/t/app/controller/report_update_text.t +++ b/t/app/controller/report_update_text.t @@ -95,7 +95,7 @@ for my $test ( password_sign_in => '', }, changes => { - username => '+44 121 496 0000', + username => '0121 496 0000', }, field_errors => [ 'Please enter a mobile number' ] }, diff --git a/t/app/controller/rss.t b/t/app/controller/rss.t index 171121eaa..a8101b593 100644 --- a/t/app/controller/rss.t +++ b/t/app/controller/rss.t @@ -14,28 +14,19 @@ my $user1 = $mech->create_user_ok('reporter-rss@example.com', name => 'Reporter my $dt_parser = FixMyStreet::App->model('DB')->schema->storage->datetime_parser; -my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { +my ($report) = $mech->create_problems_for_body(1, 2651, '', { postcode => 'eh1 1BB', - bodies_str => '2651', areas => ',11808,135007,14419,134935,2651,20728,', category => 'Street lighting', title => '&Test’i<n>g \'☃"', detail => 'Testing Detail', - used_map => 1, name => $user1->name, - anonymous => 0, - state => 'confirmed', confirmed => $dt_parser->format_datetime($dt), lastupdate => $dt_parser->format_datetime($dt), whensent => $dt_parser->format_datetime($dt->clone->add( minutes => 5 )), - lang => 'en-gb', - service => '', - cobrand => 'default', - cobrand_data => '', - send_questionnaire => 1, latitude => '55.951963', longitude => '-3.189944', - user_id => $user1->id, + user => $user1, } ); $mech->host('www.fixmystreet.com'); @@ -127,54 +118,19 @@ $report->delete(); my $council = $mech->create_body_ok(2333, 'Hart Council'); my $county = $mech->create_body_ok(2227, 'Hampshire Council'); -my $now = DateTime->now(); -my $report_to_council = FixMyStreet::App->model('DB::Problem')->find_or_create( - { - postcode => 'GU51 4AE', - bodies_str => $council->id, +my ($report_to_council) = $mech->create_problems_for_body(1, $council->id, '', { + user => $user1, areas => ',2333,2227,', - category => 'Other', - title => 'council report', - detail => 'Test 2 Detail', - used_map => 't', - name => 'Test User', - anonymous => 'f', - state => 'closed', - confirmed => $now->ymd . ' ' . $now->hms, - lang => 'en-gb', - service => '', - cobrand => 'default', - cobrand_data => '', - send_questionnaire => 't', latitude => '51.279616', longitude => '-0.846040', - user_id => $user1->id, - } -); +}); -my $report_to_county_council = FixMyStreet::App->model('DB::Problem')->find_or_create( - { - postcode => 'GU51 4AE', - bodies_str => $county->id, +my ($report_to_county_council) = $mech->create_problems_for_body(1, $county->id, '', { + user => $user1, areas => ',2333,2227,', - category => 'Other', - title => 'county report', - detail => 'Test 2 Detail', - used_map => 't', - name => 'Test User', - anonymous => 'f', - state => 'closed', - confirmed => $now->ymd . ' ' . $now->hms, - lang => 'en-gb', - service => '', - cobrand => 'default', - cobrand_data => '', - send_questionnaire => 't', latitude => '51.279616', longitude => '-0.846040', - user_id => $user1->id, - } -); +}); subtest "check RSS feeds on cobrand have correct URLs for non-cobrand reports" => sub { $mech->host('hart.fixmystreet.com'); |