diff options
Diffstat (limited to 't')
-rw-r--r-- | t/Mock/OpenIDConnect.pm | 77 | ||||
-rw-r--r-- | t/app/controller/auth_social.t | 198 | ||||
-rw-r--r-- | t/app/controller/index.t | 7 | ||||
-rw-r--r-- | t/app/controller/report_new_anon.t | 164 | ||||
-rw-r--r-- | t/app/model/user.t | 25 | ||||
-rw-r--r-- | t/cobrand/bucks.t | 2 | ||||
-rw-r--r-- | t/cobrand/westminster.t | 218 | ||||
-rw-r--r-- | t/open311.t | 30 |
8 files changed, 677 insertions, 44 deletions
diff --git a/t/Mock/OpenIDConnect.pm b/t/Mock/OpenIDConnect.pm new file mode 100644 index 000000000..079c354fc --- /dev/null +++ b/t/Mock/OpenIDConnect.pm @@ -0,0 +1,77 @@ +package t::Mock::OpenIDConnect; + +use JSON::MaybeXS; +use Web::Simple; +use DateTime; +use MIME::Base64 qw(encode_base64); +use MooX::Types::MooseLike::Base qw(:all); + +has json => ( + is => 'lazy', + default => sub { + JSON->new->pretty->allow_blessed->convert_blessed; + }, +); + +has returns_email => ( + is => 'rw', + isa => Bool, + default => 1, +); + +sub dispatch_request { + my $self = shift; + + sub (GET + /oauth2/v2.0/authorize + ?*) { + my ($self) = @_; + return [ 200, [ 'Content-Type' => 'text/html' ], [ 'OpenID Connect login page' ] ]; + }, + + sub (GET + /oauth2/v2.0/logout + ?*) { + my ($self) = @_; + return [ 200, [ 'Content-Type' => 'text/html' ], [ 'OpenID Connect logout page' ] ]; + }, + + sub (POST + /oauth2/v2.0/token + ?*) { + my ($self) = @_; + my $header = { + typ => "JWT", + alg => "RS256", + kid => "XXXfakeKEY1234", + }; + my $now = DateTime->now->epoch; + my $payload = { + exp => $now + 3600, + nbf => $now, + ver => "1.0", + iss => "https://login.example.org/12345-6789-4321-abcd-12309812309/v2.0/", + sub => "my_cool_user_id", + aud => "example_client_id", + iat => $now, + auth_time => $now, + given_name => "Andy", + family_name => "Dwyer", + tfp => "B2C_1_default", + extension_CrmContactId => "1c304134-ef12-c128-9212-123908123901", + nonce => 'MyAwesomeRandomValue', + }; + $payload->{emails} = ['oidc@example.org'] if $self->returns_email; + my $signature = "dummy"; + my $id_token = join(".", ( + encode_base64($self->json->encode($header), ''), + encode_base64($self->json->encode($payload), ''), + encode_base64($signature, '') + )); + my $data = { + id_token => $id_token, + token_type => "Bearer", + not_before => $now, + id_token_expires_in => 3600, + profile_info => encode_base64($self->json->encode({}), ''), + }; + my $json = $self->json->encode($data); + return [ 200, [ 'Content-Type' => 'application/json' ], [ $json ] ]; + }, +} + +__PACKAGE__->run_if_script; diff --git a/t/app/controller/auth_social.t b/t/app/controller/auth_social.t index 75eabfc43..160d46f8f 100644 --- a/t/app/controller/auth_social.t +++ b/t/app/controller/auth_social.t @@ -5,17 +5,18 @@ use JSON::MaybeXS; use t::Mock::Facebook; use t::Mock::Twitter; +use t::Mock::OpenIDConnect; use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; - + # disable info logs for this test run FixMyStreet::App->log->disable('info'); END { FixMyStreet::App->log->enable('info'); } my $body = $mech->create_body_ok(2504, 'Westminster Council'); -my ($report) = $mech->create_problems_for_body(1, $body->id, 'Test'); +my ($report) = $mech->create_problems_for_body(1, $body->id, 'My Test Report'); my $contact = $mech->create_contact_ok( body_id => $body->id, category => 'Damaged bin', email => 'BIN', @@ -30,44 +31,101 @@ my $contact2 = $mech->create_contact_ok( body_id => $body->id, category => 'Whatever', email => 'WHATEVER', ); -FixMyStreet::override_config { - FACEBOOK_APP_ID => 'facebook-app-id', - TWITTER_KEY => 'twitter-key', - ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], - MAPIT_URL => 'http://mapit.uk/', -}, sub { +my $resolver = Test::MockModule->new('Email::Valid'); +my $social = Test::MockModule->new('FixMyStreet::App::Controller::Auth::Social'); +$social->mock('generate_nonce', sub { 'MyAwesomeRandomValue' }); + +for my $test ( + { + type => 'facebook', + config => { + FACEBOOK_APP_ID => 'facebook-app-id', + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, + update => 1, + email => 'facebook@example.org', + uid => 123456789, + mock => 't::Mock::Facebook', + mock_hosts => ['www.facebook.com', 'graph.facebook.com'], + host => 'www.facebook.com', + error_callback => '/auth/Facebook?error_code=ERROR', + success_callback => '/auth/Facebook?code=response-code', + redirect_pattern => qr{facebook\.com.*dialog/oauth.*facebook-app-id}, +}, { + type => 'oidc', + config => { + ALLOWED_COBRANDS => [ { westminster => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + COBRAND_FEATURES => { + oidc_login => { + westminster => { + client_id => 'example_client_id', + secret => 'example_secret_key', + auth_uri => 'http://oidc.example.org/oauth2/v2.0/authorize', + token_uri => 'http://oidc.example.org/oauth2/v2.0/token', + logout_uri => 'http://oidc.example.org/oauth2/v2.0/logout', + display_name => 'MyWestminster' + } + } + } + }, + email => 'oidc@example.org', + uid => "westminster:example_client_id:my_cool_user_id", + mock => 't::Mock::OpenIDConnect', + mock_hosts => ['oidc.example.org'], + host => 'oidc.example.org', + error_callback => '/auth/OIDC?error=ERROR', + success_callback => '/auth/OIDC?code=response-code&state=login', + redirect_pattern => qr{oidc\.example\.org/oauth2/v2\.0/authorize}, + logout_redirect_pattern => qr{oidc\.example\.org/oauth2/v2\.0/logout}, + user_extras => [ + [westminster_account_id => "1c304134-ef12-c128-9212-123908123901"], + ], +} +) { -my $fb_email = 'facebook@example.org'; -my $fb_uid = 123456789; +FixMyStreet::override_config $test->{config}, sub { -my $resolver = Test::MockModule->new('Email::Valid'); -$resolver->mock('address', sub { 'facebook@example.org' }); +$resolver->mock('address', sub { $test->{email} }); -for my $fb_state ( 'refused', 'no email', 'existing UID', 'okay' ) { +for my $state ( 'refused', 'no email', 'existing UID', 'okay' ) { for my $page ( 'my', 'report', 'update' ) { - subtest "test FB '$fb_state' login for page '$page'" => sub { + next if $page eq 'update' && !$test->{update}; + + subtest "test $test->{type} '$state' login for page '$page'" => sub { # Lots of user changes happening here, make sure we don't confuse # Catalyst with a cookie session user that no longer exists $mech->log_out_ok; $mech->cookie_jar({}); - if ($fb_state eq 'existing UID') { - my $user = $mech->create_user_ok($fb_email); - $user->update({ facebook_id => $fb_uid }); + if ($state eq 'existing UID') { + my $user = $mech->create_user_ok($test->{email}); + if ($test->{type} eq 'facebook') { + $user->update({ facebook_id => $test->{uid} }); + } elsif ($test->{type} eq 'oidc') { + $user->update({ oidc_ids => [ $test->{uid} ] }); + } } else { - $mech->delete_user($fb_email); + $mech->delete_user($test->{email}); + } + if ($page eq 'my' && $state eq 'existing UID') { + $report->update({ user_id => FixMyStreet::App->model( 'DB::User' )->find( { email => $test->{email} } )->id }); + } else { + $report->update({ user_id => FixMyStreet::App->model( 'DB::User' )->find( { email => 'test@example.com' } )->id }); } - # Set up a mock to catch (most, see below) requests to Facebook - my $fb = t::Mock::Facebook->new; - $fb->returns_email(0) if $fb_state eq 'no email' || $fb_state eq 'existing UID'; - LWP::Protocol::PSGI->register($fb->to_psgi_app, host => 'www.facebook.com'); - LWP::Protocol::PSGI->register($fb->to_psgi_app, host => 'graph.facebook.com'); + # Set up a mock to catch (most, see below) requests to the OAuth API + my $mock_api = $test->{mock}->new; + $mock_api->returns_email(0) if $state eq 'no email' || $state eq 'existing UID'; + for my $host (@{ $test->{mock_hosts} }) { + LWP::Protocol::PSGI->register($mock_api->to_psgi_app, host => $host); + } # Due to https://metacpan.org/pod/Test::WWW::Mechanize::Catalyst#External-Redirects-and-allow_external - # the redirect to Facebook's OAuth page can mess up the session - # cookie. So let's pretend we always on www.facebook.com, which + # the redirect to the OAuth page can mess up the session + # cookie. So let's pretend we're always on the API host, which # sorts that out. - $mech->host('www.facebook.com'); + $mech->host($test->{host}); # Fetch the page with the form via which we wish to log in my $fields; @@ -91,20 +149,23 @@ for my $fb_state ( 'refused', 'no email', 'existing UID', 'okay' ) { update => 'Test update', }; } - $mech->submit_form(with_fields => $fields, button => 'facebook_sign_in'); + $mech->submit_form(with_fields => $fields, button => 'social_sign_in'); # As well as the cookie issue above, caused by this external # redirect rewriting the host, the redirect gets handled directly # by Catalyst, not our mocked handler, so will be a 404. Check # the redirect happened instead. - is $mech->res->previous->code, 302, 'FB button redirected'; - like $mech->res->previous->header('Location'), qr{facebook\.com.*dialog/oauth.*facebook-app-id}, 'FB redirect to oauth URL'; - - # Okay, now call the callback Facebook would send us to - if ($fb_state eq 'refused') { - $mech->get_ok('/auth/Facebook?error_code=ERROR'); + is $mech->res->previous->code, 302, "$test->{type} button redirected"; + like $mech->res->previous->header('Location'), $test->{redirect_pattern}, "$test->{type} redirect to oauth URL"; + + # Okay, now call the callback we'd be sent to + # NB: for OIDC these should be post_ok, but that doesn't work because + # the session cookie doesn't seem to be included (related to the + # cookie issue above perhaps). + if ($state eq 'refused') { + $mech->get_ok($test->{error_callback}); } else { - $mech->get_ok('/auth/Facebook?code=response-code'); + $mech->get_ok($test->{success_callback}); } # Check we're showing the right form, regardless of what came back @@ -115,14 +176,14 @@ for my $fb_state ( 'refused', 'no email', 'existing UID', 'okay' ) { $mech->content_contains('/report/update'); } - if ($fb_state eq 'refused') { - $mech->content_contains('Sorry, we could not log you in. Please fill in the form below.'); + if ($state eq 'refused') { + $mech->content_contains('Sorry, we could not log you in.'); $mech->not_logged_in_ok; - } elsif ($fb_state eq 'no email') { + } elsif ($state eq 'no email') { $mech->content_contains('We need your email address, please give it below.'); # We don't have an email, so check that we can still submit it, # and the ID carries through the confirmation - $fields->{username} = $fb_email; + $fields->{username} = $test->{email}; $fields->{name} = 'Ffion Tester' unless $page eq 'my'; $mech->submit_form(with_fields => $fields, $page eq 'my' ? (button => 'sign_in_by_code') : ()); $mech->content_contains('Nearly done! Now check your email'); @@ -131,26 +192,77 @@ for my $fb_state ( 'refused', 'no email', 'existing UID', 'okay' ) { $mech->clear_emails_ok; ok $url, "extracted confirm url '$url'"; - my $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $fb_email } ); + my $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $test->{email} } ); if ($page eq 'my') { is $user, undef, 'No user yet exists'; } else { - is $user->facebook_id, undef, 'User has no facebook ID'; + if ($test->{type} eq 'facebook') { + is $user->facebook_id, undef, 'User has no facebook ID'; + } elsif ($test->{type} eq 'oidc') { + is $user->oidc_ids, undef, 'User has no OIDC IDs'; + } } $mech->get_ok( $url ); - $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $fb_email } ); - is $user->facebook_id, $fb_uid, 'User now has correct facebook ID'; + $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $test->{email} } ); + if ($test->{type} eq 'facebook') { + is $user->facebook_id, $test->{uid}, 'User now has correct facebook ID'; + } elsif ($test->{type} eq 'oidc') { + is_deeply $user->oidc_ids, [ $test->{uid} ], 'User now has correct OIDC IDs'; + } + if ($test->{user_extras}) { + for my $extra (@{ $test->{user_extras} }) { + my ($k, $v) = @$extra; + is $user->get_extra_metadata($k), $v, "User has correct $k extra field"; + } + } } elsif ($page ne 'my') { # /my auth login goes directly there, no message like this $mech->content_contains('You have successfully signed in; please check and confirm your details are accurate'); $mech->logged_in_ok; + if ($test->{user_extras}) { + my $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $test->{email} } ); + for my $extra (@{ $test->{user_extras} }) { + my ($k, $v) = @$extra; + is $user->get_extra_metadata($k), $v, "User has correct $k extra field"; + } + } } else { is $mech->uri->path, '/my', 'Successfully on /my page'; + if ($test->{user_extras}) { + my $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $test->{email} } ); + for my $extra (@{ $test->{user_extras} }) { + my ($k, $v) = @$extra; + is $user->get_extra_metadata($k), $v, "User has correct $k extra field"; + } + } + if ($state eq 'existing UID') { + my $report_id = $report->id; + $mech->content_contains( $report->title ); + $mech->content_contains( "/report/$report_id" ); + } + } + + $mech->get('/auth/sign_out'); + if ($test->{type} eq 'oidc' && $state ne 'refused' && $state ne 'no email') { + # XXX the 'no email' situation is skipped because of some confusion + # with the hosts/sessions that I've not been able to get to the bottom of. + # The code does behave as expected when testing manually, however. + is $mech->res->previous->code, 302, "$test->{type} sign out redirected"; + like $mech->res->previous->header('Location'), $test->{logout_redirect_pattern}, "$test->{type} sign out redirect to oauth logout URL"; } + $mech->not_logged_in_ok; } } } +} +}; + +FixMyStreet::override_config { + TWITTER_KEY => 'twitter-key', + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', +}, sub { $resolver->mock('address', sub { 'twitter@example.org' }); @@ -204,7 +316,7 @@ for my $tw_state ( 'refused', 'existing UID', 'no email' ) { update => 'Test update', }; } - $mech->submit_form(with_fields => $fields, button => 'twitter_sign_in'); + $mech->submit_form(with_fields => $fields, button => 'social_sign_in'); # As well as the cookie issue above, caused by this external # redirect rewriting the host, the redirect gets handled directly diff --git a/t/app/controller/index.t b/t/app/controller/index.t index bd268b3d7..3f3aed48e 100644 --- a/t/app/controller/index.t +++ b/t/app/controller/index.t @@ -91,6 +91,13 @@ subtest "prefilters /around if user has categories" => sub { $mech->content_contains("Cows,Potholes"); }; +subtest "prefilters /around if filter_category given in URL" => sub { + $mech->get_ok('/?filter_category=MyUniqueTestCategory&filter_group=MyUniqueTestGroup'); + # NB can't use visible_form_values because fields are hidden + $mech->content_contains("MyUniqueTestCategory"); + $mech->content_contains("MyUniqueTestGroup"); +}; + END { done_testing(); } diff --git a/t/app/controller/report_new_anon.t b/t/app/controller/report_new_anon.t new file mode 100644 index 000000000..e9ce23c44 --- /dev/null +++ b/t/app/controller/report_new_anon.t @@ -0,0 +1,164 @@ +package FixMyStreet::Cobrand::AnonAllowed; +use parent 'FixMyStreet::Cobrand::FixMyStreet'; +sub allow_anonymous_reports { 1 } +sub anonymous_account { { email => 'anon@example.org', name => 'Anonymous' } } + +package FixMyStreet::Cobrand::AnonAllowedByButton; +use parent 'FixMyStreet::Cobrand::FixMyStreet'; +sub allow_anonymous_reports { 'button' } +sub anonymous_account { { email => 'anonbutton@example.org', name => 'Anonymous Button' } } + +package main; + +use FixMyStreet::TestMech; +use FixMyStreet::App; + +# disable info logs for this test run +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + +my $mech = FixMyStreet::TestMech->new; + +my $body = $mech->create_body_ok(2651, 'Edinburgh'); +my $contact1 = $mech->create_contact_ok( + body_id => $body->id, + category => 'Street lighting', + email => 'highways@example.com', +); +my $contact2 = $mech->create_contact_ok( + body_id => $body->id, + category => 'Trees', + email => 'trees@example.com', +); + +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'anonallowed', + MAPIT_URL => 'http://mapit.uk/', +}, sub { + +subtest "check form errors when anonymous account is on" => sub { + $mech->get_ok('/around'); + + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + $mech->submit_form_ok( { with_fields => { category => "Street lighting" } }, "submit form" ); + + my @errors = ( + 'Please enter a subject', + 'Please enter some details', + # No user errors + ); + is_deeply [ sort @{$mech->page_errors} ], [ sort @errors ], "check errors"; +}; + +subtest "test report creation anonymously" => sub { + $mech->get_ok('/around'); + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + $mech->submit_form_ok( + { + button => 'submit_register', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + name => 'Joe Bloggs', + may_show_name => '1', + category => 'Street lighting', + } + }, + "submit good details" + ); + $mech->content_contains('Thank you'); + + is_deeply $mech->page_errors, [], "check there were no errors"; + + my $report = FixMyStreet::DB->resultset("Problem")->first; + ok $report, "Found the report"; + + is $report->state, 'confirmed', "report confirmed"; + $mech->get_ok( '/report/' . $report->id ); + + is $report->bodies_str, $body->id; + is $report->name, 'Anonymous'; + is $report->anonymous, 0; # Doesn't change behaviour here, but uses anon account's name always + + my $alert = FixMyStreet::App->model('DB::Alert')->find( { + user => $report->user, + alert_type => 'new_updates', + parameter => $report->id, + } ); + is $alert, undef, "no alert created"; + + $mech->not_logged_in_ok; +}; + +}; + +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'anonallowedbybutton', + MAPIT_URL => 'http://mapit.uk/', +}, sub { + +subtest "test report creation anonymously by button" => sub { + $mech->get_ok('/around'); + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + $mech->submit_form_ok( + { + button => 'submit_register', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + name => 'Joe Bloggs', + may_show_name => '1', + category => 'Street lighting', + } + }, + "submit good details" + ); + + is_deeply $mech->page_errors, [ + 'Please enter your email' + ], "check there were no errors"; + + $mech->get_ok('/around'); + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + $mech->submit_form_ok( + { + button => 'report_anonymously', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + category => 'Street lighting', + } + }, + "submit good details" + ); + $mech->content_contains('Thank you'); + + is_deeply $mech->page_errors, [], "check there were no errors"; + + my $report = FixMyStreet::DB->resultset("Problem")->search({}, { order_by => { -desc => 'id' } })->first; + ok $report, "Found the report"; + + is $report->state, 'confirmed', "report confirmed"; + $mech->get_ok( '/report/' . $report->id ); + + is $report->bodies_str, $body->id; + is $report->name, 'Anonymous Button'; + is $report->anonymous, 1; # Doesn't change behaviour here, but uses anon account's name always + + my $alert = FixMyStreet::App->model('DB::Alert')->find( { + user => $report->user, + alert_type => 'new_updates', + parameter => $report->id, + } ); + is $alert, undef, "no alert created"; + + $mech->not_logged_in_ok; +}; + +}; + +done_testing(); diff --git a/t/app/model/user.t b/t/app/model/user.t index cbc0fe6cf..88b29ca84 100644 --- a/t/app/model/user.t +++ b/t/app/model/user.t @@ -75,6 +75,31 @@ subtest 'Check non-existent methods on user object die' => sub { ); }; +subtest 'OIDC ids can be manipulated correctly' => sub { + my $user = $problem->user; + + is $user->oidc_ids, undef, 'user starts with no OIDC ids'; + + $user->add_oidc_id("fixmystreet:1234:5678"); + is_deeply $user->oidc_ids, ["fixmystreet:1234:5678"], 'OIDC id added correctly'; + + $user->add_oidc_id("mycobrand:0123:abcd"); + is_deeply [ sort @{$user->oidc_ids} ], ["fixmystreet:1234:5678", "mycobrand:0123:abcd"], 'Second OIDC id added correctly'; + + $user->add_oidc_id("mycobrand:0123:abcd"); + is_deeply [ sort @{$user->oidc_ids} ], ["fixmystreet:1234:5678", "mycobrand:0123:abcd"], 'Adding existing OIDC id does not add duplicate'; + + $user->remove_oidc_id("mycobrand:0123:abcd"); + is_deeply $user->oidc_ids, ["fixmystreet:1234:5678"], 'OIDC id can be removed OK'; + + $user->remove_oidc_id("mycobrand:0123:abcd"); + is_deeply $user->oidc_ids, ["fixmystreet:1234:5678"], 'Removing non-existent OIDC id has no effect'; + + $user->remove_oidc_id("fixmystreet:1234:5678"); + is $user->oidc_ids, undef, 'Removing last OIDC id results in undef'; + +}; + done_testing(); sub create_update { diff --git a/t/cobrand/bucks.t b/t/cobrand/bucks.t index 6732eb29c..2d42dcd81 100644 --- a/t/cobrand/bucks.t +++ b/t/cobrand/bucks.t @@ -19,7 +19,7 @@ $mech->create_contact_ok(body_id => $district->id, category => 'Graffiti', email my $cobrand = Test::MockModule->new('FixMyStreet::Cobrand::Buckinghamshire'); $cobrand->mock('lookup_site_code', sub { - my ($self, $row, $buffer) = @_; + my ($self, $row) = @_; return "Road ID" if $row->latitude == 51.812244; }); diff --git a/t/cobrand/westminster.t b/t/cobrand/westminster.t new file mode 100644 index 000000000..2912f6ee5 --- /dev/null +++ b/t/cobrand/westminster.t @@ -0,0 +1,218 @@ +use CGI::Simple; +use Test::MockModule; +use FixMyStreet::TestMech; +use FixMyStreet::Script::Reports; + +ok( my $mech = FixMyStreet::TestMech->new, 'Created mech object' ); + +my $cobrand = Test::MockModule->new('FixMyStreet::Cobrand::Westminster'); +$cobrand->mock('lookup_site_code', sub { + my ($self, $row) = @_; + return "My USRN" if $row->latitude == 51.501009; +}); + +my $body = $mech->create_body_ok(2504, 'Westminster City Council', { + send_method => 'Open311', api_key => 'key', 'endpoint' => 'e', 'jurisdiction' => 'j' }); +my $superuser = $mech->create_user_ok( + 'superuser@example.com', + name => 'Test Superuser', + is_superuser => 1 +); +my $staff_user = $mech->create_user_ok( + 'westminster@example.com', + name => 'Test User', + from_body => $body +); +my ($report) = $mech->create_problems_for_body(1, $body->id, 'Title'); + +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'westminster', + MAPIT_URL => 'http://mapit.uk/', + COBRAND_FEATURES => { + oidc_login => { + westminster => { + client_id => 'example_client_id', + secret => 'example_secret_key', + auth_uri => 'http://oidc.example.org/oauth2/v2.0/authorize', + token_uri => 'http://oidc.example.org/oauth2/v2.0/token', + display_name => 'MyWestminster' + } + } + } +}, sub { + subtest 'Cobrand allows social auth' => sub { + my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker('westminster')->new(); + ok $cobrand->social_auth_enabled; + }; + + subtest 'Login button displayed correctly' => sub { + $mech->get_ok("/auth"); + $mech->content_contains("Sign in with MyWestminster"); + }; + + subtest 'Reports do not have update form' => sub { + $mech->get_ok('/report/' . $report->id); + $mech->content_lacks('Provide an update'); + }; +}; + +subtest 'Reports have an update form for superusers' => sub { + # Westminster cobrand disables email signin, so we have to + # login and *then* set the cobrand. + $mech->log_in_ok( $superuser->email ); + + FixMyStreet::override_config { + ALLOWED_COBRANDS => 'westminster', + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok('/report/' . $report->id); + $mech->content_contains('Provide an update'); + }; + + $mech->log_out_ok(); +}; + +subtest 'Reports have an update form for staff users' => sub { + $mech->log_in_ok( $staff_user->email ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => 'westminster', + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok('/report/' . $report->id); + $mech->content_contains('Provide an update'); + }; + $mech->log_out_ok(); +}; + +for ( + { + ALLOWED_COBRANDS => 'westminster', + MAPIT_URL => 'http://mapit.uk/', + COBRAND_FEATURES => { + oidc_login => { + westminster => 0 + } + } + }, + { + ALLOWED_COBRANDS => 'westminster', + MAPIT_URL => 'http://mapit.uk/', + COBRAND_FEATURES => { + oidc_login => { + hounslow => { + client_id => 'example_client_id', + secret => 'example_secret_key', + auth_uri => 'http://oidc.example.org/oauth2/v2.0/authorize', + token_uri => 'http://oidc.example.org/oauth2/v2.0/token', + display_name => 'MyHounslow' + } + } + } + }, + { + ALLOWED_COBRANDS => 'westminster', + MAPIT_URL => 'http://mapit.uk/', + } +) { + FixMyStreet::override_config $_, sub { + subtest 'Cobrand disallows social auth' => sub { + my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker('westminster')->new(); + ok !$cobrand->social_auth_enabled; + }; + + subtest 'Login button not displayed' => sub { + $mech->get_ok("/auth"); + $mech->content_lacks("Login with MyWestminster"); + }; + }; +} + +FixMyStreet::DB->resultset('Problem')->delete_all; +$mech->create_contact_ok(body_id => $body->id, category => 'Abandoned bike', email => "BIKE"); +($report) = $mech->create_problems_for_body(1, $body->id, 'Bike', { + category => "Abandoned bike", cobrand => 'westminster', + latitude => 51.501009, longitude => -0.141588, areas => '2504', +}); + +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'westminster' ], + MAPIT_URL => 'http://mapit.uk/', + STAGING_FLAGS => { send_reports => 1, skip_checks => 0 }, + COBRAND_FEATURES => { anonymous_account => { westminster => 'anon' } }, +}, sub { + subtest 'USRN set correctly' => sub { + my $test_data = FixMyStreet::Script::Reports::send(); + my $req = $test_data->{test_req_used}; + my $c = CGI::Simple->new($req->content); + is $c->param('service_code'), 'BIKE'; + is $c->param('attribute[USRN]'), 'My USRN'; + }; +}; + +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'westminster', + MAPIT_URL => 'http://mapit.uk/', +}, sub { + subtest 'No reporter alert created' => sub { + my $user = $mech->log_in_ok('test@example.org'); + $mech->get_ok('/'); + $mech->submit_form_ok( { with_fields => { pc => 'SW1A1AA' } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + $mech->submit_form_ok( { with_fields => { + title => 'Title', detail => 'Detail', category => 'Abandoned bike', name => 'Test Example', + } }, 'submitted okay' ); + is $user->alerts->count, 0; + }; +}; + +my $westminster = FixMyStreet::Cobrand::Westminster->new; +subtest 'correct config returned for USRN/UPRN lookup' => sub { + my $actual = $westminster->lookup_site_code_config('USRN'); + delete $actual->{accept_feature}; # is_deeply doesn't like code + is_deeply $actual, { + buffer => 1000, + proxy_url => "https://tilma.staging.mysociety.org/resource-proxy/proxy.php", + url => "https://westminster.assets/40/query", + property => 'USRN', + }; + $actual = $westminster->lookup_site_code_config('UPRN'); + delete $actual->{accept_feature}; # is_deeply doesn't like code + is_deeply $actual, { + buffer => 1000, + proxy_url => "https://tilma.staging.mysociety.org/resource-proxy/proxy.php", + url => "https://westminster.assets/25/query", + property => 'UPRN', + accept_types => { + Point => 1 + }, + }; +}; + +subtest 'nearest UPRN returns correct point' => sub { + my $cfg = { + accept_feature => sub { 1 }, + property => 'UPRN', + accept_types => { + Point => 1, + }, + }; + my $features = [ + # A couple of incorrect geometry types to check they're ignored... + { geometry => { type => 'Polygon' } }, + { geometry => { type => 'LineString', + coordinates => [ [ 527735, 181004 ], [ 527755, 181004 ] ] }, + properties => { fid => '20100024' } }, + # And two points which are further away than the above linestring, + # the second of which is the closest to our testing point. + { geometry => { type => 'Point', + coordinates => [ 527795, 181024 ] }, + properties => { UPRN => '10012387122' } }, + { geometry => { type => 'Point', + coordinates => [ 527739, 181009 ] }, + properties => { UPRN => '10012387123' } }, + ]; + is $westminster->_nearest_feature($cfg, 527745, 180994, $features), '10012387123'; +}; + + +done_testing(); diff --git a/t/open311.t b/t/open311.t index 73bb488d5..9524006b8 100644 --- a/t/open311.t +++ b/t/open311.t @@ -263,6 +263,36 @@ for my $test ( }; } +for my $test ( + { + desc => 'account_id handled correctly when present', + account_id => '1c304134-ef12-c128-9212-123908123901', + }, + { + desc => 'account_id handled correctly when 0', + account_id => '0' + }, + { + desc => 'account_id handled correctly when missing', + account_id => undef + } +) { + subtest $test->{desc} => sub { + $problem->extra( undef ); + my $extra = { + url => 'http://example.com/report/1', + defined $test->{account_id} ? ( account_id => $test->{account_id} ) : () + }; + + my $results = make_service_req( $problem, $extra, $problem->category, +'<?xml version="1.0" encoding="utf-8"?><service_requests><request><service_request_id>248</service_request_id></request></service_requests>' + ); + my $c = CGI::Simple->new( $results->{req}->content ); + + is $c->param( 'account_id' ), $test->{account_id}, 'correct account_id'; + }; +} + subtest 'test always_send_email' => sub { my $email = $user->email; $user->email(undef); |