diff options
Diffstat (limited to 't')
-rw-r--r-- | t/app/controller/about.t | 2 | ||||
-rw-r--r-- | t/app/controller/admin.t | 49 | ||||
-rw-r--r-- | t/app/controller/alert_new.t | 32 | ||||
-rw-r--r-- | t/app/controller/dashboard.t | 639 | ||||
-rw-r--r-- | t/app/controller/questionnaire.t | 132 | ||||
-rw-r--r-- | t/app/controller/report_import.t | 2 | ||||
-rw-r--r-- | t/app/controller/report_new.t | 228 | ||||
-rw-r--r-- | t/app/controller/report_updates.t | 20 | ||||
-rw-r--r-- | t/app/controller/reports.t | 4 | ||||
-rw-r--r-- | t/app/model/problem.t | 130 | ||||
-rw-r--r-- | t/app/uri_for.t | 57 | ||||
-rw-r--r-- | t/cobrand/get_council_sender.t | 30 | ||||
-rw-r--r-- | t/open311.t | 451 | ||||
-rw-r--r-- | t/open311/getservicerequestupdates.t | 537 | ||||
-rw-r--r-- | t/open311/populate-service-list.t | 298 |
15 files changed, 2472 insertions, 139 deletions
diff --git a/t/app/controller/about.t b/t/app/controller/about.t index ea7b1af20..4e49cdac9 100644 --- a/t/app/controller/about.t +++ b/t/app/controller/about.t @@ -13,7 +13,7 @@ $mech->content_contains('html class="no-js" lang="en-gb"'); SKIP: { skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 8 ) - unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{emptyhomes}; + unless FixMyStreet::Cobrand->exists('emptyhomes'); # check that geting the page as EHA produces a different page ok $mech->host("reportemptyhomes.co.uk"), 'change host to reportemptyhomes'; diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t index d8a1c24a1..09d99cfdf 100644 --- a/t/app/controller/admin.t +++ b/t/app/controller/admin.t @@ -105,34 +105,39 @@ subtest 'check summary counts' => sub { $mech->content_contains( "$q_count questionnaires sent" ); - ok $mech->host('barnet.fixmystreet.com'); + SKIP: { + skip( "Need 'barnet' in ALLOWED_COBRANDS config", 7 ) + unless FixMyStreet::Cobrand->exists('barnet'); - $mech->get_ok('/admin'); - $mech->title_like(qr/Summary/); + ok $mech->host('barnet.fixmystreet.com'); - my ($num_live) = $mech->content =~ /(\d+)<\/strong> live problems/; - my ($num_alerts) = $mech->content =~ /(\d+) confirmed alerts/; - my ($num_qs) = $mech->content =~ /(\d+) questionnaires sent/; + $mech->get_ok('/admin'); + $mech->title_like(qr/Summary/); - $report->council(2489); - $report->cobrand('barnet'); - $report->update; + my ($num_live) = $mech->content =~ /(\d+)<\/strong> live problems/; + my ($num_alerts) = $mech->content =~ /(\d+) confirmed alerts/; + my ($num_qs) = $mech->content =~ /(\d+) questionnaires sent/; - $alert->cobrand('barnet'); - $alert->update; + $report->council(2489); + $report->cobrand('barnet'); + $report->update; - $mech->get_ok('/admin'); + $alert->cobrand('barnet'); + $alert->update; - $mech->content_contains( ($num_live+1) . "</strong> live problems" ); - $mech->content_contains( ($num_alerts+1) . " confirmed alerts" ); - $mech->content_contains( ($num_qs+1) . " questionnaires sent" ); + $mech->get_ok('/admin'); - $report->council(2504); - $report->cobrand(''); - $report->update; + $mech->content_contains( ($num_live+1) . "</strong> live problems" ); + $mech->content_contains( ($num_alerts+1) . " confirmed alerts" ); + $mech->content_contains( ($num_qs+1) . " questionnaires sent" ); + + $report->council(2504); + $report->cobrand(''); + $report->update; - $alert->cobrand(''); - $alert->update; + $alert->cobrand(''); + $alert->update; + } FixMyStreet::App->model('DB::Problem')->search( { council => 1 } )->update( { council => 2489 } ); ok $mech->host('fixmystreet.com'); @@ -222,6 +227,8 @@ subtest 'check open311 configuring' => sub { api_key => 'api key', endpoint => 'http://example.com/open311', jurisdiction => 'mySociety', + send_comments => 0, + send_method => 'Open311', } } ); @@ -244,6 +251,8 @@ subtest 'check open311 configuring' => sub { api_key => 'new api key', endpoint => 'http://example.org/open311', jurisdiction => 'open311', + send_comments => 0, + send_method => 'Open311', } } ); diff --git a/t/app/controller/alert_new.t b/t/app/controller/alert_new.t index 3a4c2ef81..7ba887824 100644 --- a/t/app/controller/alert_new.t +++ b/t/app/controller/alert_new.t @@ -142,25 +142,18 @@ foreach my $test ( } foreach my $test ( - { - email => 'test-new@example.com', - type => 'area', - content => 'your alert will not be activated', - email_text => 'confirm the alert', - uri => -'/alert/subscribe?type=local&rznvy=test-new@example.com&feed=area:1000:A_Location', - param1 => 1000 - } + { exist => 0 }, + { exist => 1 }, ) { - subtest "use existing unlogged in user in a alert" => sub { + subtest "use existing unlogged in user in a alert ($test->{exist})" => sub { $mech->log_out_ok(); - my $type = $test->{type} . '_problems'; + my $type = 'area_problems'; my $user = FixMyStreet::App->model('DB::User') - ->find_or_create( { email => $test->{email} } ); + ->find_or_create( { email => 'test-new@example.com' } ); my $alert = FixMyStreet::App->model('DB::Alert')->find( { @@ -169,24 +162,26 @@ foreach my $test ( } ); # clear existing data so we can be sure we're creating it - ok $alert->delete() if $alert; + ok $alert->delete() if $alert && !$test->{exist}; - $mech->get_ok( $test->{uri} ); + $mech->get_ok( '/alert/subscribe?type=local&rznvy=test-new@example.com&feed=area:1000:A_Location' ); $alert = FixMyStreet::App->model('DB::Alert')->find( { user => $user, alert_type => $type, - parameter => $test->{param1}, - parameter2 => $test->{param2}, - confirmed => 0, + parameter => 1000, + parameter2 => undef, + confirmed => $test->{exist}, } ); $mech->content_contains( 'Now check your email' ); + $alert->confirm(); ok $alert, 'New alert created with existing user'; - $mech->delete_user($user); + + $mech->delete_user($user) if $test->{exist}; }; } @@ -445,6 +440,7 @@ subtest "Test normal alert signups and that alerts are sent" => sub { ok $update, "created test update - $update_id"; FixMyStreet::App->model('DB::AlertType')->email_alerts(); + # TODO Note the below will fail if the db has an existing alert that matches $mech->email_count_is(3); my @emails = $mech->get_email; my $count; diff --git a/t/app/controller/dashboard.t b/t/app/controller/dashboard.t new file mode 100644 index 000000000..7033fa02c --- /dev/null +++ b/t/app/controller/dashboard.t @@ -0,0 +1,639 @@ +use strict; +use warnings; +use Test::More; + +use FixMyStreet::TestMech; +use Web::Scraper; + +my $mech = FixMyStreet::TestMech->new; + +my $test_user = 'council_user@example.com'; +my $test_pass = 'password'; +my $test_council = 2651; +my $test_ward = 20723; + +$mech->delete_user( $test_user ); +my $user = FixMyStreet::App->model('DB::User')->create( { + email => $test_user, + password => $test_pass, +} ); + +my $p_user = FixMyStreet::App->model('DB::User')->find_or_create( { + email => 'p_user@example.com' +} ); + +$mech->not_logged_in_ok; +$mech->get_ok('/dashboard'); + +$mech->content_contains( 'sign in' ); + +$mech->submit_form( + with_fields => { email => $test_user, password_sign_in => $test_pass } +); + +is $mech->status, '404', 'If not council user get 404'; + +$user->from_council( $test_council ); +$user->update; + +$mech->log_out_ok; +$mech->get_ok('/dashboard'); +$mech->submit_form_ok( { + with_fields => { email => $test_user, password_sign_in => $test_pass } +} ); + +$mech->content_contains( 'Summary Statistics for City of Edinburgh' ); + +FixMyStreet::App->model('DB::Contact')->search( { area_id => $test_council } ) + ->delete; + +delete_problems(); + +my @cats = qw( Grafitti Litter Potholes ); +for my $contact ( @cats ) { + FixMyStreet::App->model('DB::Contact')->create( + { + area_id => $test_council, + category => $contact, + email => "$contact\@example.org", + confirmed => 1, + whenedited => DateTime->now, + deleted => 0, + editor => 'test', + note => 'test', + } + ); +} + +$mech->get_ok('/dashboard'); + +my $categories = scraper { + process "select[name=category] > option", 'cats[]' => 'TEXT', + process "select[name=ward] > option", 'wards[]' => 'TEXT', + process "table[id=overview] > tr", 'rows[]' => scraper { + process 'td', 'cols[]' => 'TEXT' + }, + process "tr[id=total] > td", 'totals[]' => 'TEXT', + process "tr[id=fixed_council] > td", 'council[]' => 'TEXT', + process "tr[id=fixed_user] > td", 'user[]' => 'TEXT', + process "tr[id=total_fixed] > td", 'total_fixed[]' => 'TEXT', + process "tr[id=in_progress] > td", 'in_progress[]' => 'TEXT', + process "tr[id=planned] > td", 'planned[]' => 'TEXT', + process "tr[id=investigating] > td", 'investigating[]' => 'TEXT', + process "tr[id=marked] > td", 'marked[]' => 'TEXT', + process "tr[id=avg_marked] > td", 'avg_marked[]' => 'TEXT', + process "tr[id=avg_fixed] > td", 'avg_fixed[]' => 'TEXT', + process "tr[id=not_marked] > td", 'not_marked[]' => 'TEXT', + process "tr[id=closed] > td", 'closed[]' => 'TEXT', + process "table[id=reports] > tr > td", 'report_lists[]' => scraper { + process 'ul > li', 'reports[]' => 'TEXT' + }, +}; + +my $expected_cats = [ 'All', '-- Pick a category --', @cats, 'Other' ]; +my $res = $categories->scrape( $mech->content ); +is_deeply( $res->{cats}, $expected_cats, 'correct list of categories' ); + +foreach my $row ( @{ $res->{rows} }[1 .. 11] ) { + foreach my $col ( @{ $row->{cols} } ) { + is $col, 0; + } +} + +for my $reports ( @{ $res->{report_lists} } ) { + is_deeply $reports, {}, 'No reports'; +} + +foreach my $test ( + { + desc => 'confirmed today with no state', + dt => DateTime->now, + counts => [1,1,1,1], + report_counts => [1, 0, 0], + }, + { + desc => 'confirmed last 7 days with no state', + dt => DateTime->now->subtract( days => 6, hours => 23 ), + counts => [1,2,2,2], + report_counts => [2, 0, 0], + }, + { + desc => 'confirmed last 8 days with no state', + dt => DateTime->now->subtract( days => 8 ), + counts => [1,2,3,3], + report_counts => [2, 1, 0], + }, + { + desc => 'confirmed last 4 weeks with no state', + dt => DateTime->now->subtract( weeks => 2 ), + counts => [1,2,4,4], + report_counts => [2, 1, 1], + }, + { + desc => 'confirmed this year with no state', + dt => DateTime->now->subtract( weeks => 7 ), + counts => [1,2,4,5], + report_counts => [2, 1, 1], + }, +) { + subtest $test->{desc} => sub { + make_problem( { state => 'confirmed', conf_dt => $test->{dt} } ); + + $mech->get_ok('/dashboard'); + $res = $categories->scrape( $mech->content ); + + check_row( $res, 'totals', $test->{counts} ); + check_row( $res, 'not_marked', $test->{counts} ); + + check_report_counts( $res, $test->{report_counts} ); + }; +} + +delete_problems(); + +foreach my $test ( + { + desc => 'user fixed today', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'fixed - user', + counts => { + totals => [1,1,1,1], + user => [1,1,1,1], + council => [0,0,0,0], + avg_fixed => [0,0,0,0], + total_fixed => [1,1,1,1], + } + }, + { + desc => 'council fixed today', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'fixed - council', + counts => { + totals => [2,2,2,2], + user => [1,1,1,1], + council => [1,1,1,1], + avg_fixed => [1,1,1,1], + total_fixed => [2,2,2,2], + } + }, + { + desc => 'marked investigating today', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'investigating', + counts => { + totals => [3,3,3,3], + user => [1,1,1,1], + council => [1,1,1,1], + total_fixed => [2,2,2,2], + avg_marked => [1,1,1,1], + investigating => [1,1,1,1], + marked => [1,1,1,1] + } + }, + { + desc => 'marked in progress today', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'in progress', + counts => { + totals => [4,4,4,4], + user => [1,1,1,1], + council => [1,1,1,1], + total_fixed => [2,2,2,2], + avg_marked => [1,1,1,1], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + marked => [2,2,2,2] + } + }, + { + desc => 'marked as planned today', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'planned', + counts => { + totals => [5,5,5,5], + user => [1,1,1,1], + council => [1,1,1,1], + total_fixed => [2,2,2,2], + avg_marked => [1,1,1,1], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + planned => [1,1,1,1], + marked => [3,3,3,3] + } + }, + { + desc => 'marked as planned today, confirmed a week ago', + confirm_dt => DateTime->now->subtract( days => 8 ), + mark_dt => DateTime->now, + state => 'planned', + counts => { + totals => [5,5,6,6], + user => [1,1,1,1], + council => [1,1,1,1], + total_fixed => [2,2,2,2], + avg_marked => [3,3,3,3], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + planned => [2,2,2,2], + marked => [4,4,4,4] + } + }, + { + desc => 'marked as council fixed today, confirmed a week ago', + confirm_dt => DateTime->now->subtract( days => 8 ), + mark_dt => DateTime->now, + state => 'fixed - council', + counts => { + totals => [5,5,7,7], + user => [1,1,1,1], + council => [2,2,2,2], + total_fixed => [3,3,3,3], + avg_fixed => [5,5,5,5], + avg_marked => [3,3,3,3], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + planned => [2,2,2,2], + marked => [4,4,4,4] + } + }, + { + desc => 'marked as council fixed a week ago, confirmed 3 weeks ago', + confirm_dt => DateTime->now->subtract( days => 21), + mark_dt => DateTime->now->subtract( days => 8 ), + state => 'fixed - council', + counts => { + totals => [5,5,8,8], + user => [1,1,1,1], + council => [2,2,3,3], + total_fixed => [3,3,4,4], + avg_fixed => [5,5,7,7], + avg_marked => [3,3,3,3], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + planned => [2,2,2,2], + marked => [4,4,4,4] + } + }, + { + desc => 'marked as user fixed 6 weeks ago, confirmed 7 weeks ago', + confirm_dt => DateTime->now->subtract( weeks => 6 ), + mark_dt => DateTime->now->subtract( weeks => 7 ), + state => 'fixed - user', + counts => { + totals => [5,5,8,9], + user => [1,1,1,2], + council => [2,2,3,3], + total_fixed => [3,3,4,5], + avg_fixed => [5,5,7,7], + avg_marked => [3,3,3,3], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + planned => [2,2,2,2], + marked => [4,4,4,4] + } + }, + { + desc => 'marked as closed', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'closed', + counts => { + totals => [6,6,9,10], + user => [1,1,1,2], + council => [2,2,3,3], + total_fixed => [3,3,4,5], + avg_fixed => [5,5,7,7], + avg_marked => [2,2,2,2], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + planned => [2,2,2,2], + closed => [1,1,1,1], + marked => [5,5,5,5] + } + }, +) { + subtest $test->{desc} => sub { + make_problem( + { + state => $test->{state}, + conf_dt => $test->{confirm_dt}, + mark_dt => $test->{mark_dt}, + } + ); + + $mech->get_ok('/dashboard'); + $res = $categories->scrape( $mech->content ); + + foreach my $row ( keys %{ $test->{counts} } ) { + check_row( $res, $row, $test->{counts}->{$row} ); + } + }; +} + +delete_problems(); + +for my $test ( + { + desc => 'Selecting no category does nothing', + p1 => { + state => 'confirmed', + conf_dt => DateTime->now(), + category => 'Potholes', + }, + p2 => { + state => 'confirmed', + conf_dt => DateTime->now(), + category => 'Litter', + }, + category => '', + counts => { + totals => [2,2,2,2], + }, + counts_after => { + totals => [2,2,2,2], + }, + report_counts => [2,0,0], + report_counts_after => [2,0,0], + }, + { + desc => 'Limit display by category', + category => 'Potholes', + counts => { + totals => [2,2,2,2], + }, + counts_after => { + totals => [1,1,1,1], + }, + report_counts => [2,0,0], + report_counts_after => [1,0,0], + }, + { + desc => 'Limit display for category with no entries', + category => 'Grafitti', + counts => { + totals => [2,2,2,2], + }, + counts_after => { + totals => [0,0,0,0], + }, + report_counts => [2,0,0], + report_counts_after => [0,0,0], + }, + { + desc => 'Limit display by category for council fixed', + p1 => { + state => 'fixed - council', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + mark_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Potholes', + }, + p2 => { + state => 'fixed - council', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + mark_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Litter', + }, + category => 'Potholes', + counts => { + council => [0,0,2,2], + totals => [2,2,4,4], + }, + counts_after => { + council => [0,0,1,1], + totals => [1,1,2,2], + }, + report_counts => [2,2,0], + report_counts_after => [1,1,0], + }, + { + desc => 'Limit display by category for user fixed', + p1 => { + state => 'fixed - user', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + mark_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Potholes', + }, + p2 => { + state => 'fixed - user', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + mark_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Litter', + }, + category => 'Potholes', + counts => { + user => [0,0,2,2], + council => [0,0,2,2], + totals => [2,2,6,6], + }, + counts_after => { + user => [0,0,1,1], + council => [0,0,1,1], + totals => [1,1,3,3], + }, + report_counts => [2,4,0], + report_counts_after => [1,2,0], + }, + { + desc => 'Limit display by ward', + p1 => { + state => 'confirmed', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Potholes', + # in real life it has commas around it and the search + # uses them + areas => ',20720,', + }, + p2 => { + state => 'fixed - council', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + mark_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Litter', + areas => ',20720,', + }, + ward => 20720, + counts => { + user => [0,0,2,2], + council => [0,0,3,3], + totals => [2,2,8,8], + }, + counts_after => { + user => [0,0,0,0], + council => [0,0,1,1], + totals => [0,0,2,2], + }, + report_counts => [2,6,0], + report_counts_after => [0,2,0], + }, +) { + subtest $test->{desc} => sub { + make_problem( $test->{p1} ) if $test->{p1}; + make_problem( $test->{p2} ) if $test->{p2}; + + $mech->get_ok('/dashboard'); + + $res = $categories->scrape( $mech->content ); + + foreach my $row ( keys %{ $test->{counts} } ) { + check_row( $res, $row, $test->{counts}->{$row} ); + } + + check_report_counts( $res, $test->{report_counts} ); + + $mech->submit_form_ok( { + with_fields => { + category => $test->{category}, + ward => $test->{ward}, + } + } ); + + $res = $categories->scrape( $mech->content ); + + foreach my $row ( keys %{ $test->{counts_after} } ) { + check_row( $res, $row, $test->{counts_after}->{$row} ); + } + check_report_counts( $res, $test->{report_counts_after} ); + }; +} + +delete_problems(); + +for my $test ( + { + desc => 'Selecting no state does nothing', + p1 => { + state => 'fixed - user', + conf_dt => DateTime->now(), + category => 'Potholes', + }, + p2 => { + state => 'confirmed', + conf_dt => DateTime->now(), + category => 'Litter', + }, + state => '', + report_counts => [2,0,0], + report_counts_after => [2,0,0], + }, + { + desc => 'limit by state works', + state => 'fixed', + report_counts => [2,0,0], + report_counts_after => [1,0,0], + }, + { + desc => 'All fixed states count as fixed', + p1 => { + state => 'fixed - council', + conf_dt => DateTime->now(), + category => 'Potholes', + }, + p2 => { + state => 'fixed', + conf_dt => DateTime->now(), + category => 'Potholes', + }, + state => 'fixed', + report_counts => [4,0,0], + report_counts_after => [3,0,0], + }, +) { + subtest $test->{desc} => sub { + make_problem( $test->{p1} ) if $test->{p1}; + make_problem( $test->{p2} ) if $test->{p2}; + + $mech->get_ok('/dashboard'); + + $res = $categories->scrape( $mech->content ); + + check_report_counts( $res, $test->{report_counts} ); + + $mech->submit_form_ok( { + with_fields => { + state => $test->{state}, + } + } ); + + $res = $categories->scrape( $mech->content ); + + check_report_counts( $res, $test->{report_counts_after} ); + }; +} + +sub make_problem { + my $args = shift; + + my $p = FixMyStreet::App->model('DB::Problem')->create( { + title => 'a problem', + name => 'a user', + anonymous => 1, + detail => 'some detail', + state => $args->{state}, + confirmed => $args->{conf_dt}, + whensent => $args->{conf_dt}, + lastupdate => $args->{mark_dt} || $args->{conf_dt}, + council => $test_council, + postcode => 'EH99 1SP', + latitude => '51', + longitude => '1', + areas => $args->{areas} || $test_ward, + used_map => 0, + user_id => $p_user->id, + category => $args->{category} || 'Other', + } ); + + if ( $args->{state} ne 'confirmed' ) { + my $c = FixMyStreet::App->model('DB::Comment')->create( { + problem => $p, + user_id => $p_user->id, + state => 'confirmed', + problem_state => $args->{state} =~ /^fixed - user|fixed$/ ? undef : $args->{state}, + confirmed => $args->{mark_dt}, + text => 'an update', + mark_fixed => $args->{state} =~ /fixed/ ? 1 : 0, + anonymous => 1, + } ); + } +} + +sub check_row { + my $res = shift; + my $row = shift; + my $totals = shift; + + is $res->{ $row }->[0], $totals->[0], "Correct count in $row for WTD"; + is $res->{ $row }->[1], $totals->[1], "Correct count in $row for last 7 days"; + is $res->{ $row }->[2], $totals->[2], "Correct count in $row for last 4 weeks"; + is $res->{ $row }->[3], $totals->[3], "Correct count in $row for YTD"; +} + +sub check_report_counts { + my $res = shift; + my $counts = shift; + + for my $i ( 0 .. 2 ) { + if ( $counts->[$i] == 0 ) { + is_deeply $res->{report_lists}->[$i], {}, "No reports for column $i"; + } else { + if ( ref( $res->{report_lists}->[$i]->{reports} ) eq 'ARRAY' ) { + is scalar @{ $res->{report_lists}->[$i]->{reports} }, $counts->[$i], "Correct report count for column $i"; + } else { + fail "Correct report count for column $i ( no reports )"; + } + } + } +} + +sub delete_problems { + FixMyStreet::App->model('DB::Comment') + ->search( { 'problem.council' => $test_council }, { join => 'problem' } ) + ->delete; + FixMyStreet::App->model('DB::Problem') + ->search( { council => $test_council } )->delete(); +} + +done_testing; diff --git a/t/app/controller/questionnaire.t b/t/app/controller/questionnaire.t index 0e2a71184..2d4fdb711 100644 --- a/t/app/controller/questionnaire.t +++ b/t/app/controller/questionnaire.t @@ -322,67 +322,79 @@ for my $test ( }; } -# EHA extra checking -ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes'; - -# Reset, and all the questionaire sending function - FIXME should it detect site itself somehow? -$report->send_questionnaire( 1 ); -$report->update; -$questionnaire->delete; -FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( { - site => 'emptyhomes' -} ); -$email = $mech->get_email; -ok $email, "got an email"; -$mech->clear_emails_ok; - -like $email->body, qr/fill in this short questionnaire/i, "got questionnaire email"; -($token) = $email->body =~ m{http://.*?/Q/(\S+)}; -ok $token, "extracted questionnaire token '$token'"; - -$mech->get_ok("/Q/" . $token); -$mech->content_contains( 'should have reported what they have done' ); - -# Test already answered the ever reported question, so not shown again -$dt = $dt->add( weeks => 4 ); -my $questionnaire2 = FixMyStreet::App->model('DB::Questionnaire')->find_or_create( - { - problem_id => $report->id, - whensent => $dt->ymd . ' ' . $dt->hms, - ever_reported => 1, - } -); -ok $questionnaire2, 'added another questionnaire'; -ok $mech->host("fixmystreet.com"), 'change host to fixmystreet'; -$mech->get_ok("/Q/" . $token); -$mech->title_like( qr/Questionnaire/ ); -$mech->content_contains( 'Has this problem been fixed?' ); -$mech->content_lacks( 'ever reported' ); - -# EHA extra checking -ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes'; -$mech->get_ok("/Q/" . $token); -$mech->content_contains( 'made a lot of progress' ); - -$token = FixMyStreet::App->model("DB::Token")->find( { scope => 'questionnaire', token => $token } ); -ok $token, 'found token for questionnaire'; -$questionnaire = FixMyStreet::App->model('DB::Questionnaire')->find( { id => $token->data } ); -ok $questionnaire, 'found questionnaire'; - -# I18N Unicode extra testing using FiksGataMi -$report->send_questionnaire( 1 ); -$report->cobrand( 'fiksgatami' ); -$report->update; -$questionnaire->delete; -$questionnaire2->delete; -FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( { site => 'fixmystreet' } ); # It's either fixmystreet or emptyhomes -$email = $mech->get_email; -ok $email, "got an email"; -$mech->clear_emails_ok; +SKIP: { + skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 18 ) + unless FixMyStreet::Cobrand->exists('emptyhomes'); + + # EHA extra checking + ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes'; + + # Reset, and all the questionaire sending function - FIXME should it detect site itself somehow? + $report->send_questionnaire( 1 ); + $report->update; + $questionnaire->delete; + + FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( { + site => 'emptyhomes' + } ); + $email = $mech->get_email; + ok $email, "got an email"; + $mech->clear_emails_ok; + + like $email->body, qr/fill in this short questionnaire/i, "got questionnaire email"; + ($token) = $email->body =~ m{http://.*?/Q/(\S+)}; + ok $token, "extracted questionnaire token '$token'"; + + $mech->get_ok("/Q/" . $token); + $mech->content_contains( 'should have reported what they have done' ); + + # Test already answered the ever reported question, so not shown again + $dt = $dt->add( weeks => 4 ); + my $questionnaire2 = FixMyStreet::App->model('DB::Questionnaire')->find_or_create( + { + problem_id => $report->id, + whensent => $dt->ymd . ' ' . $dt->hms, + ever_reported => 1, + } + ); + ok $questionnaire2, 'added another questionnaire'; + ok $mech->host("fixmystreet.com"), 'change host to fixmystreet'; + $mech->get_ok("/Q/" . $token); + $mech->title_like( qr/Questionnaire/ ); + $mech->content_contains( 'Has this problem been fixed?' ); + $mech->content_lacks( 'ever reported' ); + + # EHA extra checking + ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes'; + $mech->get_ok("/Q/" . $token); + $mech->content_contains( 'made a lot of progress' ); + + $token = FixMyStreet::App->model("DB::Token")->find( { scope => 'questionnaire', token => $token } ); + ok $token, 'found token for questionnaire'; + $questionnaire = FixMyStreet::App->model('DB::Questionnaire')->find( { id => $token->data } ); + ok $questionnaire, 'found questionnaire'; + + $questionnaire2->delete; +} -like $email->body, qr/Testing =96 Detail/, 'email contains encoded character from user'; -like $email->body, qr/sak p=E5 FiksGataMi/, 'email contains encoded character from template'; -is $email->header('Content-Type'), 'text/plain; charset="windows-1252"', 'email is in right encoding'; +SKIP: { + skip( "Need 'fiksgatami' in ALLOWED_COBRANDS config", 5 ) + unless FixMyStreet::Cobrand->exists('fiksgatami'); + + # I18N Unicode extra testing using FiksGataMi + $report->send_questionnaire( 1 ); + $report->cobrand( 'fiksgatami' ); + $report->update; + $questionnaire->delete; + FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( { site => 'fixmystreet' } ); # It's either fixmystreet or emptyhomes + $email = $mech->get_email; + ok $email, "got an email"; + $mech->clear_emails_ok; + + like $email->body, qr/Testing =96 Detail/, 'email contains encoded character from user'; + like $email->body, qr/sak p=E5 FiksGataMi/, 'email contains encoded character from template'; + is $email->header('Content-Type'), 'text/plain; charset="windows-1252"', 'email is in right encoding'; +} $mech->delete_user('test@example.com'); done_testing(); diff --git a/t/app/controller/report_import.t b/t/app/controller/report_import.t index 934bf0346..eb686b44e 100644 --- a/t/app/controller/report_import.t +++ b/t/app/controller/report_import.t @@ -266,7 +266,7 @@ subtest "Submit a correct entry (with location) to cobrand" => sub { SKIP: { skip( "Need 'fiksgatami' in ALLOWED_COBRANDS config", 20 ) - unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{fiksgatami}; + unless FixMyStreet::Cobrand->exists('fiksgatami'); mySociety::MaPit::configure('http://mapit.nuug.no/'); ok $mech->host("fiksgatami.no"), 'change host to fiksgatami'; diff --git a/t/app/controller/report_new.t b/t/app/controller/report_new.t index 625c7531f..29fb650e5 100644 --- a/t/app/controller/report_new.t +++ b/t/app/controller/report_new.t @@ -6,10 +6,14 @@ use utf8; use FixMyStreet::TestMech; use Web::Scraper; +use Path::Class; my $mech = FixMyStreet::TestMech->new; $mech->get_ok('/report/new'); +my $sample_file = file(__FILE__)->parent->file("sample.jpg")->stringify; +ok -e $sample_file, "sample file $sample_file exists"; + subtest "test that bare requests to /report/new get redirected" => sub { $mech->get_ok('/report/new'); @@ -48,9 +52,23 @@ my $contact3 = FixMyStreet::App->model('DB::Contact')->find_or_create( { category => 'Trees', email => 'trees@example.com', } ); +my $contact4 = FixMyStreet::App->model('DB::Contact')->find_or_create( { + %contact_params, + area_id => 2482, # Bromley + category => 'Trees', + email => 'trees@example.com', +} ); +my $contact5 = FixMyStreet::App->model('DB::Contact')->find_or_create( { + %contact_params, + area_id => 2651, # Edinburgh + category => 'Trees', + email => 'trees@example.com', +} ); ok $contact1, "created test contact 1"; ok $contact2, "created test contact 2"; ok $contact3, "created test contact 3"; +ok $contact4, "created test contact 4"; +ok $contact5, "created test contact 5"; # test that the various bit of form get filled in and errors correctly # generated. @@ -282,6 +300,69 @@ foreach my $test ( }, errors => [ 'Please enter a subject', 'Please enter some details', ], }, + { + msg => 'non-photo upload gives error', + pc => 'SW1A 1AA', + fields => { + title => 'Title', + detail => 'Detail', + photo => [ [ undef, 'bad.txt', Content => 'This is not a JPEG', Content_Type => 'text/plain' ], 1 ], + name => 'Bob Jones', + may_show_name => '1', + email => 'bob@example.com', + phone => '', + category => 'Street lighting', + password_sign_in => '', + password_register => '', + remember_me => undef, + }, + changes => { + photo => '', + }, + errors => [ "Please upload a JPEG image only" ], + }, + { + msg => 'bad photo upload gives error', + pc => 'SW1A 1AA', + fields => { + title => 'Title', + detail => 'Detail', + photo => [ [ undef, 'fake.jpeg', Content => 'This is not a JPEG', Content_Type => 'image/jpeg' ], 1 ], + name => 'Bob Jones', + may_show_name => '1', + email => 'bob@example.com', + phone => '', + category => 'Street lighting', + password_sign_in => '', + password_register => '', + remember_me => undef, + }, + changes => { + photo => '', + }, + errors => [ "That image doesn't appear to have uploaded correctly (Please upload a JPEG image only ), please try again." ], + }, + { + msg => 'photo with octet-stream gets through okay', + pc => 'SW1A 1AA', + fields => { + title => '', + detail => 'Detail', + photo => [ [ $sample_file, undef, Content_Type => 'application/octet-stream' ], 1 ], + name => 'Bob Jones', + may_show_name => '1', + email => 'bob@example.com', + phone => '', + category => 'Street lighting', + password_sign_in => '', + password_register => '', + remember_me => undef, + }, + changes => { + photo => '', + }, + errors => [ "Please enter a subject" ], + }, ) { subtest "check form errors where $test->{msg}" => sub { @@ -293,7 +374,7 @@ foreach my $test ( is_deeply $mech->form_errors, [], "no errors for pc '$test->{pc}'"; # click through to the report page - $mech->follow_link_ok( { text => 'skip this step', }, + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); # submit the main form @@ -473,7 +554,7 @@ subtest "test password errors for a user who is signing in as they report" => su "submit location" ); # click through to the report page - $mech->follow_link_ok( { text => 'Skip this step', }, + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); $mech->submit_form_ok( @@ -520,7 +601,7 @@ subtest "test report creation for a user who is signing in as they report" => su "submit location" ); # click through to the report page - $mech->follow_link_ok( { text => 'Skip this step', }, + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); $mech->submit_form_ok( @@ -614,7 +695,7 @@ foreach my $test ( "submit location" ); # click through to the report page - $mech->follow_link_ok( { text => 'Skip this step', }, + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); # check that the fields are correctly prefilled @@ -717,7 +798,7 @@ subtest "check that a lat/lon off coast leads to /around" => sub { is $mech->uri->path, '/around', "redirected to '/around'"; is_deeply # - $mech->page_errors, + $mech->form_errors, [ 'That spot does not appear to be covered by a council. If you have' . ' tried to report an issue past the shoreline, for example, please' . ' specify the closest point on land.' ], # @@ -725,8 +806,145 @@ subtest "check that a lat/lon off coast leads to /around" => sub { }; +for my $test ( + { + desc => 'user title not set if not bromley problem', + host => 'http://www.fixmystreet.com', + postcode => 'EH99 1SP', + fms_extra_title => '', + extra => undef, + user_title => undef, + }, + { + desc => 'title shown for bromley problem on main site', + host => 'http://www.fixmystreet.com', + postcode => 'BR1 3UH', + fms_extra_title => 'MR', + extra => [ + { + name => 'fms_extra_title', + value => 'MR', + description => 'FMS_EXTRA_TITLE', + }, + ], + user_title => 'MR', + }, + { + desc => + 'title, first and last name shown for bromley problem on cobrand', + host => 'http://bromley.fixmystreet.com', + postcode => 'BR1 3UH', + first_name => 'Test', + last_name => 'User', + fms_extra_title => 'MR', + extra => [ + { + name => 'fms_extra_title', + value => 'MR', + description => 'FMS_EXTRA_TITLE', + }, + { + name => 'first_name', + value => 'Test', + description => 'FIRST_NAME', + }, + { + name => 'last_name', + value => 'User', + description => 'LAST_NAME', + }, + ], + user_title => 'MR', + }, + ) +{ + subtest $test->{desc} => sub { + $mech->host( $test->{host} ); + + $mech->log_out_ok; + $mech->clear_emails_ok; + + $mech->get_ok('/'); + $mech->submit_form_ok( { with_fields => { pc => $test->{postcode}, } }, + "submit location" ); + $mech->follow_link_ok( + { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" + ); + + my $fields = $mech->visible_form_values('mapSkippedForm'); + if ( $test->{fms_extra_title} ) { + ok exists( $fields->{fms_extra_title} ), 'user title field displayed'; + } else { + ok !exists( $fields->{fms_extra_title} ), 'user title field not displayed'; + } + if ( $test->{first_name} ) { + ok exists( $fields->{first_name} ), 'first name field displayed'; + ok exists( $fields->{last_name} ), 'last name field displayed'; + ok !exists( $fields->{name} ), 'no name field displayed'; + } + else { + ok !exists( $fields->{first_name} ), + 'first name field not displayed'; + ok !exists( $fields->{last_name} ), 'last name field not displayed'; + ok exists( $fields->{name} ), 'name field displayed'; + } + + my $submission_fields = { + title => "Test Report", + detail => 'Test report details.', + photo => '', + email => 'firstlast@example.com', + may_show_name => '1', + phone => '07903 123 456', + category => 'Trees', + password_register => '', + }; + + $submission_fields->{fms_extra_title} = $test->{fms_extra_title} + if $test->{fms_extra_title}; + + if ( $test->{first_name} ) { + $submission_fields->{first_name} = $test->{first_name}; + $submission_fields->{last_name} = $test->{last_name}; + } + else { + $submission_fields->{name} = 'Test User'; + } + + $mech->submit_form_ok( { with_fields => $submission_fields }, + "submit good details" ); + + my $email = $mech->get_email; + ok $email, "got an email"; + like $email->body, qr/confirm the problem/i, "confirm the problem"; + + my ($url) = $email->body =~ m{(https?://\S+)}; + ok $url, "extracted confirm url '$url'"; + + # confirm token in order to update the user details + $mech->get_ok($url); + + my $user = + FixMyStreet::App->model('DB::User') + ->find( { email => 'firstlast@example.com' } ); + + my $report = $user->problems->first; + ok $report, "Found the report"; + my $extras = $report->extra; + 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; + }; +} + $contact1->delete; $contact2->delete; $contact3->delete; +$contact4->delete; +$contact5->delete; done_testing(); diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t index 9c2b0861b..0337a881b 100644 --- a/t/app/controller/report_updates.t +++ b/t/app/controller/report_updates.t @@ -872,7 +872,11 @@ for my $test ( is_deeply $values, $test->{initial_values}, 'initial form values'; - is $mech->extract_problem_banner->{text}, $test->{initial_banner}, 'initial banner'; + if ( !defined( $test->{initial_banner} ) ) { + is $mech->extract_problem_banner->{text}, undef, 'initial banner'; + } else { + like $mech->extract_problem_banner->{text}, qr/@{[ $test->{initial_banner} ]}/i, 'initial banner'; + } $mech->submit_form_ok( { @@ -883,7 +887,11 @@ for my $test ( is $mech->uri->path, "/report/" . $report_id, "redirected to report page"; - is $mech->extract_problem_banner->{text}, $test->{endstate_banner}, 'submitted banner'; + if ( !defined( $test->{endstate_banner} ) ) { + is $mech->extract_problem_banner->{text}, undef, 'endstate banner'; + } else { + like $mech->extract_problem_banner->{text}, qr/@{[ $test->{endstate_banner} ]}/i, 'endstate banner'; + } $mech->email_count_is(0); @@ -1029,8 +1037,12 @@ foreach my $test ( is_deeply $values, $test->{initial_values}, 'initial form values'; - is $mech->extract_problem_banner->{text}, $test->{initial_banner}, - 'initial banner'; + if ( !defined( $test->{initial_banner} ) ) { + is $mech->extract_problem_banner->{text}, undef, 'initial banner'; + } else { + like $mech->extract_problem_banner->{text}, qr/@{[ $test->{initial_banner} ]}/i, + 'initial banner'; + } $mech->submit_form_ok( { with_fields => $test->{fields}, }, 'submit update' ); diff --git a/t/app/controller/reports.t b/t/app/controller/reports.t index 58803d778..801dbeddb 100644 --- a/t/app/controller/reports.t +++ b/t/app/controller/reports.t @@ -17,14 +17,14 @@ $mech->follow_link_ok( { text_regex => qr/Birmingham/ } ); SKIP: { skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 8 ) - unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{emptyhomes}; + unless FixMyStreet::Cobrand->exists('emptyhomes'); ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes'; $mech->get_ok('/reports'); # EHA lacks one column the others have $mech->content_lacks('state unknown'); skip( "Need 'fiksgatami' in ALLOWED_COBRANDS config", 8 ) - unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{fiksgatami}; + unless FixMyStreet::Cobrand->exists('fiksgatami'); mySociety::MaPit::configure('http://mapit.nuug.no/'); ok $mech->host("fiksgatami.no"), 'change host to fiksgatami'; $mech->get_ok('/reports'); diff --git a/t/app/model/problem.t b/t/app/model/problem.t index 638e89200..a7415851b 100644 --- a/t/app/model/problem.t +++ b/t/app/model/problem.t @@ -7,6 +7,7 @@ use Test::More; use FixMyStreet; use FixMyStreet::App; +use FixMyStreet::TestMech; use mySociety::Locale; mySociety::Locale::gettext_domain('FixMyStreet'); @@ -16,8 +17,8 @@ my $problem_rs = FixMyStreet::App->model('DB::Problem'); my $problem = $problem_rs->new( { postcode => 'EH99 1SP', - latitude => 1, - longitude => 1, + latitude => '51.5016605453401', + longitude => '-0.142497580865087', areas => 1, title => '', detail => '', @@ -345,6 +346,131 @@ for my $test ( }; } +my $mech = FixMyStreet::TestMech->new(); + +FixMyStreet::App->model('DB::Contact')->find_or_create( + { + area_id => 2651, + category => 'potholes', + email => 'test@example.org', + confirmed => 1, + deleted => 0, + editor => 'test', + whenedited => \'ms_current_timestamp()', + note => '', + } +); + +FixMyStreet::App->model('DB::Contact')->find_or_create( + { + area_id => 2226, + category => 'potholes', + email => '2226@example.org', + confirmed => 1, + deleted => 0, + editor => 'test', + whenedited => \'ms_current_timestamp()', + note => '', + } +); + +FixMyStreet::App->model('DB::Contact')->find_or_create( + { + area_id => 2326, + category => 'potholes', + email => '2326@example.org', + confirmed => 1, + deleted => 0, + editor => 'test', + whenedited => \'ms_current_timestamp()', + note => '', + } +); + +foreach my $test ( { + desc => 'sends an email', + unset_whendef => 1, + email_count => 1, + email => 'system_user@example.com', + name => 'Andrew Smith', + dear => qr'Dear City of Edinburgh Council', + to => qr'City of Edinburgh Council', + council => 2651, + }, + { + desc => 'no email sent if no unsent problems', + unset_whendef => 0, + email_count => 0, + email => 'system_user@example.com', + name => 'Andrew Smith', + council => 2651, + }, + { + desc => 'email to two tier council', + unset_whendef => 1, + email_count => 1, + email => 'system_user@example.com', + name => 'Andrew Smith', + to => qr'Gloucestershire County Council.*Cheltenham Borough Council', + dear => qr'Dear Gloucestershire County Council and Cheltenham Borough', + council => '2226,2326', + multiple => 1, + }, + { + desc => 'email to two tier council with one missing details', + unset_whendef => 1, + email_count => 1, + email => 'system_user@example.com', + name => 'Andrew Smith', + to => qr'Gloucestershire County Council', + dear => qr'Dear Gloucestershire County Council,', + council => '2226|2649', + missing => qr'problem might be the responsibility of Fife.*Council'ms, + }, +) { + subtest $test->{ desc } => sub { + $mech->clear_emails_ok; + + FixMyStreet::App->model('DB::Problem')->search( + { + whensent => undef + } + )->update( { whensent => \'ms_current_timestamp()' } ); + + $problem->discard_changes; + $problem->update( { + council => $test->{ council }, + state => 'confirmed', + confirmed => \'ms_current_timestamp()', + whensent => $test->{ unset_whendef } ? undef : \'ms_current_timestamp()', + category => 'potholes', + name => $test->{ name }, + } ); + + FixMyStreet::App->model('DB::Problem')->send_reports(); + + $mech->email_count_is( $test->{ email_count } ); + if ( $test->{ email_count } ) { + my $email = $mech->get_email; + like $email->header('To'), $test->{ to }, 'to line looks correct'; + is $email->header('From'), sprintf('"%s" <%s>', $test->{ name }, $test->{ email } ), 'from line looks correct'; + like $email->header('Subject'), qr/A Title/, 'subject line looks correct'; + like $email->body, qr/A user of FixMyStreet/, 'email body looks a bit like a report'; + like $email->body, qr/Subject: A Title/, 'more email body checking'; + like $email->body, $test->{ dear }, 'Salutation looks correct'; + + if ( $test->{multiple} ) { + like $email->body, qr/This email has been sent to several councils /, 'multiple council text correct'; + } elsif ( $test->{ missing } ) { + like $email->body, $test->{ missing }, 'missing council information correct'; + } + + $problem->discard_changes; + ok defined( $problem->whensent ), 'whensent set'; + } + }; +} + $problem->comments->delete; $problem->delete; $user->delete; diff --git a/t/app/uri_for.t b/t/app/uri_for.t index 51a6e8a0e..eecf30e32 100644 --- a/t/app/uri_for.t +++ b/t/app/uri_for.t @@ -78,31 +78,36 @@ is( 'FiksGataMi url with lat not zoom' ); -like( - $reh_en_c->uri_for_email( '/foo' ), - qr{^http://en.}, - 'adds en to retain language' -); - -# instantiate this here otherwise sets locale to cy and breaks test -# above -my $reh_cy_c = FixMyStreet::App->new( - { - request => Catalyst::Request->new( - { - base => URI->new('http://cy.reportemptyhomes.com/'), - uri => URI->new('http://cy.reportemptyhomes.com/test_namespace') - } - ), - namespace => 'test_namespace', - } -); -$reh_cy_c->setup_request(); - -like( - $reh_cy_c->uri_for_email( '/foo' ), - qr{^http://cy.}, - 'retains language' -); +SKIP: { + skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 2 ) + unless FixMyStreet::Cobrand->exists('emptyhomes'); + + like( + $reh_en_c->uri_for_email( '/foo' ), + qr{^http://en.}, + 'adds en to retain language' + ); + + # instantiate this here otherwise sets locale to cy and breaks test + # above + my $reh_cy_c = FixMyStreet::App->new( + { + request => Catalyst::Request->new( + { + base => URI->new('http://cy.reportemptyhomes.com/'), + uri => URI->new('http://cy.reportemptyhomes.com/test_namespace') + } + ), + namespace => 'test_namespace', + } + ); + $reh_cy_c->setup_request(); + + like( + $reh_cy_c->uri_for_email( '/foo' ), + qr{^http://cy.}, + 'retains language' + ); +} done_testing(); diff --git a/t/cobrand/get_council_sender.t b/t/cobrand/get_council_sender.t new file mode 100644 index 000000000..9004a47f5 --- /dev/null +++ b/t/cobrand/get_council_sender.t @@ -0,0 +1,30 @@ +use strict; +use warnings; + +use Test::More; + +use mySociety::Locale; +use FixMyStreet::App; + +use_ok 'FixMyStreet::Cobrand'; + +mySociety::Locale::gettext_domain( 'FixMyStreet' ); + +my $c = FixMyStreet::Cobrand::FixMyStreet->new(); + + +is $c->get_council_sender( '1000', { type => 'DIS' } ), 'Email', 'defaults to email'; +is $c->get_council_sender( '1000', { type => 'LBO' } ), 'London', 'returns london report it if London borough'; + +my $conf = FixMyStreet::App->model('DB::Open311Conf')->find_or_create( + area_id => 1000, + endpoint => '', + send_method => 'TestMethod' +); + +is $c->get_council_sender( '1000', { type => 'LBO' } ), 'TestMethod', 'uses send_method in preference to London'; +is $c->get_council_sender( '1000', { type => 'DIS' } ), 'TestMethod', 'uses send_method in preference to Email'; + +$conf->delete; + +done_testing(); diff --git a/t/open311.t b/t/open311.t index 30de330b6..814b11f82 100644 --- a/t/open311.t +++ b/t/open311.t @@ -5,6 +5,10 @@ use warnings; use Test::More; use Test::Warn; use FixMyStreet::App; +use CGI::Simple; +use HTTP::Response; +use DateTime; +use DateTime::Format::W3CDTF; use FindBin; use lib "$FindBin::Bin/../perllib"; @@ -39,4 +43,451 @@ my $expected_error = qr{.*request failed: 500 Can.t connect to 192.168.50.1:80 \ warning_like {$o2->send_service_request( $p, { url => 'http://example.com/' }, 1 )} $expected_error, 'warning generated on failed call'; +my $dt = DateTime->now(); + +my $user = FixMyStreet::App->model('DB::User')->new( { + name => 'Test User', + email => 'test@example.com', +} ); + +my $problem = FixMyStreet::App->model('DB::Problem')->new( { + id => 80, + external_id => 81, + state => 'confirmed', + title => 'a problem', + detail => 'problem detail', + category => 'pothole', + latitude => 1, + longitude => 2, + user => $user, +} ); + +subtest 'posting service request' => sub { + my $extra = { + url => 'http://example.com/report/1', + }; + + 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>' ); + + is $results->{ res }, 248, 'got request id'; + + my $req = $o->test_req_used; + + my $description = <<EOT; +title: a problem + +detail: problem detail + +url: http://example.com/report/1 + +Submitted via FixMyStreet +EOT +; + + my $c = CGI::Simple->new( $results->{ req }->content ); + + is $c->param('email'), $user->email, 'correct email'; + is $c->param('first_name'), 'Test', 'correct first name'; + is $c->param('last_name'), 'User', 'correct last name'; + is $c->param('lat'), 1, 'latitide correct'; + is $c->param('long'), 2, 'longitude correct'; + is $c->param('description'), $description, 'description correct'; + is $c->param('service_code'), 'pothole', 'service code correct'; +}; + +subtest 'posting service request with basic_description' => sub { + my $extra = { + url => 'http://example.com/report/1', + }; + + 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>', + { basic_description => 1 }, + ); + + is $results->{ res }, 248, 'got request id'; + + my $req = $o->test_req_used; + + my $c = CGI::Simple->new( $results->{ req }->content ); + + is $c->param('description'), $problem->detail, 'description correct'; +}; + +for my $test ( + { + desc => 'extra values in service request', + extra => [ + { + name => 'title', + value => 'A title', + } + ], + params => [ + [ 'attribute[title]', 'A title', 'extra paramater used correctly' ] + ] + }, + { + desc => 'first and last names in extra used correctly', + extra => [ + { + name => 'first_name', + value => 'First', + }, + { + name => 'last_name', + value => 'Last', + }, + ], + params => [ + [ 'first_name', 'First', 'first name correct' ], + [ 'last_name', 'Last', 'last name correct' ], + [ 'attribute[first_name]', undef, 'no first_name attribute param' ], + [ 'attribute[last_name]', 'Last', 'last_name attribute param correct' ], + ], + }, + { + title => 'magic fms_extra parameters handled correctly', + extra => [ + { + name => 'fms_extra_title', + value => 'Extra title', + } + ], + params => [ + [ + 'attribute[title]', + 'Extra title', + 'fms_extra extra param used correctly' + ] + ], + }, + ) +{ + subtest $test->{desc} => sub { + $problem->extra( $test->{extra} ); + + my $extra = { url => 'http://example.com/report/1', }; + + 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 $req = $o->test_req_used; + my $c = CGI::Simple->new( $results->{req}->content ); + + for my $param ( @{ $test->{params} } ) { + is $c->param( $param->[0] ), $param->[1], $param->[2]; + } + }; +} + +my $comment = FixMyStreet::App->model('DB::Comment')->new( { + id => 38362, + user => $user, + problem => $problem, + anonymous => 0, + text => 'this is a comment', + confirmed => $dt, + extra => { title => 'Mr', email_alerts_requested => 0 }, +} ); + +subtest 'basic request update post parameters' => sub { + my $results = make_update_req( $comment, '<?xml version="1.0" encoding="utf-8"?><service_request_updates><request_update><update_id>248</update_id></request_update></service_request_updates>' ); + + is $results->{ res }, 248, 'got update id'; + + my $req = $o->test_req_used; + + my $c = CGI::Simple->new( $results->{ req }->content ); + + is $c->param('description'), 'this is a comment', 'email correct'; + is $c->param('email'), 'test@example.com', 'email correct'; + is $c->param('status'), 'OPEN', 'status correct'; + is $c->param('service_request_id_ext'), 80, 'external request id correct'; + is $c->param('service_request_id'), 81, 'request id correct'; + is $c->param('public_anonymity_required'), 'FALSE', 'anon status correct'; + is $c->param('updated_datetime'), DateTime::Format::W3CDTF->format_datetime($dt), 'correct date'; + is $c->param('title'), 'Mr', 'correct title'; + is $c->param('last_name'), 'User', 'correct first name'; + is $c->param('first_name'), 'Test', 'correct second name'; + is $c->param('email_alerts_requested'), 'FALSE', 'email alerts flag correct'; + is $c->param('media_url'), undef, 'no media url'; +}; + +subtest 'check media url set' => sub { + $comment->photo(1); + $comment->cobrand('fixmystreet'); + + my $results = make_update_req( $comment, '<?xml version="1.0" encoding="utf-8"?><service_request_updates><request_update><update_id>248</update_id></request_update></service_request_updates>' ); + + is $results->{ res }, 248, 'got update id'; + + my $req = $o->test_req_used; + + my $c = CGI::Simple->new( $results->{ req }->content ); + my $expected_path = '/c/' . $comment->id . '.full.jpeg'; + like $c->param('media_url'), qr/$expected_path/, 'image url included'; +}; + +foreach my $test ( + { + desc => 'comment with fixed state sends status of CLOSED', + state => 'fixed', + anon => 0, + status => 'CLOSED', + }, + { + desc => 'comment with fixed - user state sends status of CLOSED', + state => 'fixed - user', + anon => 0, + status => 'CLOSED', + }, + { + desc => 'comment with fixed - council state sends status of CLOSED', + state => 'fixed - council', + anon => 0, + status => 'CLOSED', + }, + { + desc => 'comment with closed state sends status of CLOSED', + state => 'closed', + anon => 0, + status => 'CLOSED', + }, + { + desc => 'comment with investigating state sends status of OPEN', + state => 'investigating', + anon => 0, + status => 'OPEN', + }, + { + desc => 'comment with planned state sends status of OPEN', + state => 'planned', + anon => 0, + status => 'OPEN', + }, + { + desc => 'comment with in progress state sends status of OPEN', + state => 'in progress', + anon => 0, + status => 'OPEN', + }, + { + desc => 'anonymous commment sets public_anonymity_required to true', + state => 'confirmed', + anon => 1, + status => 'OPEN', + }, +) { + subtest $test->{desc} => sub { + $comment->problem->state( $test->{state} ); + $comment->anonymous( $test->{anon} ); + + my $results = make_update_req( $comment, '<?xml version="1.0" encoding="utf-8"?><service_request_updates><request_update><update_id>248</update_id></request_update></service_request_updates>' ); + + my $c = CGI::Simple->new( $results->{ req }->content ); + is $c->param('status'), $test->{status}, 'correct status'; + is $c->param('public_anonymity_required'), $test->{anon} ? 'TRUE' : 'FALSE', 'correct anonymity'; + }; +} + + +for my $test ( + { + desc => 'update name name taken from comment over user', + comment_name => 'First Last', + user_name => 'Personal Family', + extra => undef, + first_name => 'First', + last_name => 'Last' + }, + { + desc => 'update name name taken from user if no comment name', + comment_name => '', + user_name => 'Personal Family', + extra => undef, + first_name => 'Personal', + last_name => 'Family' + }, + { + desc => 'update name taken from extra if available', + comment_name => 'First Last', + user_name => 'Personal Family', + extra => { first_name => 'Forename', last_name => 'Surname' }, + first_name => 'Forename', + last_name => 'Surname' + }, + ) +{ + subtest $test->{desc} => sub { + $comment->name( $test->{comment_name} ); + $user->name( $test->{user_name} ); + $comment->extra( $test->{ extra } ); + + my $results = make_update_req( $comment, +'<?xml version="1.0" encoding="utf-8"?><service_request_updates><request_update><update_id>248</update_id></request_update></service_request_updates>' + ); + + my $c = CGI::Simple->new( $results->{req}->content ); + is $c->param('first_name'), $test->{first_name}, 'first name correct'; + is $c->param('last_name'), $test->{last_name}, 'last name correct'; + }; +} + +for my $test ( + { + desc => 'use lat long forces lat long even if map not used', + use_latlong => 1, + postcode => 'EH99 1SP', + used_map => 0, + includes_latlong => 1, + }, + { + desc => 'no use lat long and no map sends address instead of lat long', + use_latlong => 0, + postcode => 'EH99 1SP', + used_map => 0, + includes_latlong => 0, + }, + { + desc => 'no use lat long but used map sends lat long', + use_latlong => 0, + postcode => 'EH99 1SP', + used_map => 1, + includes_latlong => 1, + }, + { + desc => 'no use lat long, no map and no postcode sends lat long', + use_latlong => 0, + postcode => '', + used_map => 0, + includes_latlong => 1, + }, + { + desc => 'no use lat long, no map and no postcode sends lat long', + use_latlong => 0, + notpinpoint => 1, + postcode => '', + used_map => 0, + includes_latlong => 0, + } +) { + subtest $test->{desc} => sub { + my $extra = { url => 'http://example.com/report/1', }; + $problem->used_map( $test->{used_map} ); + $problem->postcode( $test->{postcode} ); + + 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>', + { always_send_latlong => $test->{use_latlong}, + send_notpinpointed => $test->{notpinpoint} }, + ); + + is $results->{ res }, 248, 'got request id'; + + my $c = CGI::Simple->new( $results->{ req }->content ); + + if ( $test->{notpinpoint} ) { + is $c->param('lat'), undef, 'no latitude'; + is $c->param('long'), undef, 'no longitude'; + is $c->param('address_string'), undef, 'no address'; + is $c->param('address_id'), '#NOTPINPOINTED#', 'has not pinpointed'; + } elsif ( $test->{includes_latlong} ) { + ok $c->param('lat'), 'has latitude'; + ok $c->param('long'), 'has longitude'; + is $c->param('address_string'), undef, 'no address'; + } else { + is $c->param('lat'), undef, 'no latitude'; + is $c->param('long'), undef, 'no latitude'; + is $c->param('address_string'), $test->{postcode}, 'has address'; + } + }; +} + +subtest 'No update id in reponse' => sub { + my $results; + warning_like { + $results = make_update_req( $comment, '<?xml version="1.0" encoding="utf-8"?><service_request_updates><request_update><update_id></update_id></request_update></service_request_updates>' ) + } qr/Failed to submit comment \d+ over Open311/, 'correct error message'; + + is $results->{ res }, 0, 'No update_id is a failure'; +}; + +subtest 'error reponse' => sub { + my $results; + warning_like { + $results = make_update_req( $comment, '<?xml version="1.0" encoding="utf-8"?><errors><error><code>400</code><description>There was an error</description</error></errors>' ) + } qr/Failed to submit comment \d+ over Open311.*There was an error/, 'correct error messages'; + + is $results->{ res }, 0, 'error in response is a failure'; +}; + done_testing(); + +sub make_update_req { + my $comment = shift; + my $xml = shift; + + return make_req( + { + object => $comment, + xml => $xml, + method => 'post_service_request_update', + path => 'update.xml', + } + ); +} + +sub make_service_req { + my $problem = shift; + my $extra = shift; + my $service_code = shift; + my $xml = shift; + my $open311_args = shift || {}; + + return make_req( + { + object => $problem, + xml => $xml, + method => 'send_service_request', + path => 'requests.xml', + method_args => [ $extra, $service_code ], + open311_conf => $open311_args, + } + ); +} + +sub make_req { + my $args = shift; + + my $object = $args->{object}; + my $xml = $args->{xml}; + my $method = $args->{method}; + my $path = $args->{path}; + my %open311_conf = %{ $args->{open311_conf} || {} }; + my @args = @{ $args->{method_args} || [] }; + + $open311_conf{'test_mode'} = 1; + $open311_conf{'end_point'} = 'http://localhost/o311'; + my $o = + Open311->new( %open311_conf ); + + my $test_res = HTTP::Response->new(); + $test_res->code(200); + $test_res->message('OK'); + $test_res->content($xml); + + $o->test_get_returns( { $path => $test_res } ); + + my $res = $o->$method($object, @args); + + my $req = $o->test_req_used; + + return { res => $res, req => $req }; +} diff --git a/t/open311/getservicerequestupdates.t b/t/open311/getservicerequestupdates.t new file mode 100644 index 000000000..7ec8d5ae0 --- /dev/null +++ b/t/open311/getservicerequestupdates.t @@ -0,0 +1,537 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use Test::More; +use CGI::Simple; + +use FindBin; +use lib "$FindBin::Bin/../perllib"; +use lib "$FindBin::Bin/../commonlib/perllib"; + +use_ok( 'Open311' ); + +use_ok( 'Open311::GetServiceRequestUpdates' ); +use DateTime; +use FixMyStreet::App; + +my $user = FixMyStreet::App->model('DB::User')->find_or_create( + { + email => 'system_user@example.com' + } +); + +my $requests_xml = qq{<?xml version="1.0" encoding="utf-8"?> +<service_requests_updates> +<request_update> +<update_id>638344</update_id> +<service_request_id>1</service_request_id> +<service_request_id_ext>1</service_request_id_ext> +<status>open</status> +<description>This is a note</description> +UPDATED_DATETIME +</request_update> +</service_requests_updates> +}; + + +my $dt = DateTime->now; + +# basic xml -> perl object tests +for my $test ( + { + desc => 'basic parsing - element missing', + updated_datetime => '', + res => { update_id => 638344, service_request_id => 1, service_request_id_ext => 1, + status => 'open', description => 'This is a note' }, + }, + { + desc => 'basic parsing - empty element', + updated_datetime => '<updated_datetime />', + res => { update_id => 638344, service_request_id => 1, service_request_id_ext => 1, + status => 'open', description => 'This is a note', updated_datetime => {} } , + }, + { + desc => 'basic parsing - element with no content', + updated_datetime => '<updated_datetime></updated_datetime>', + res => { update_id => 638344, service_request_id => 1, service_request_id_ext => 1, + status => 'open', description => 'This is a note', updated_datetime => {} } , + }, + { + desc => 'basic parsing - element with content', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + res => { update_id => 638344, service_request_id => 1, service_request_id_ext => 1, + status => 'open', description => 'This is a note', updated_datetime => $dt } , + }, +) { + subtest $test->{desc} => sub { + my $local_requests_xml = $requests_xml; + $local_requests_xml =~ s/UPDATED_DATETIME/$test->{updated_datetime}/; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $local_requests_xml } ); + + my $res = $o->get_service_request_updates; + is_deeply $res->[0], $test->{ res }, 'result looks correct'; + + }; +} + +my $problem_rs = FixMyStreet::App->model('DB::Problem'); +my $problem = $problem_rs->new( + { + postcode => 'EH99 1SP', + latitude => 1, + longitude => 1, + areas => 1, + title => '', + detail => '', + used_map => 1, + user_id => 1, + name => '', + state => 'confirmed', + service => '', + cobrand => 'default', + cobrand_data => '', + user => $user, + created => DateTime->now()->subtract( days => 1 ), + lastupdate => DateTime->now()->subtract( days => 1 ), + anonymous => 1, + external_id => time(), + council => 2482, + } +); + +$problem->insert; + +for my $test ( + { + desc => 'element with content', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + description => 'This is a note', + external_id => 638344, + start_state => 'confirmed', + close_comment => 0, + mark_fixed=> 0, + mark_open => 0, + problem_state => undef, + end_state => 'confirmed', + }, + { + desc => 'comment closes report', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + description => 'This is a note', + external_id => 638344, + start_state => 'confirmed', + close_comment => 1, + mark_fixed=> 0, + mark_open => 0, + problem_state => 'fixed - council', + end_state => 'fixed - council', + }, + { + desc => 'comment re-opens fixed report', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + description => 'This is a note', + external_id => 638344, + start_state => 'fixed - user', + close_comment => 0, + mark_fixed => 0, + mark_open => 0, + problem_state => 'confirmed', + end_state => 'confirmed', + }, + { + desc => 'comment re-opens closed report', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + description => 'This is a note', + external_id => 638344, + start_state => 'closed', + close_comment => 0, + mark_fixed => 0, + mark_open => 0, + problem_state => 'confirmed', + end_state => 'confirmed', + }, + { + desc => 'comment leaves report closed', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + description => 'This is a note', + external_id => 638344, + start_state => 'closed', + close_comment => 1, + mark_fixed => 0, + mark_open => 0, + end_state => 'closed', + }, +) { + subtest $test->{desc} => sub { + my $local_requests_xml = $requests_xml; + $local_requests_xml =~ s/UPDATED_DATETIME/$test->{updated_datetime}/; + $local_requests_xml =~ s#<service_request_id>\d+</service_request_id>#<service_request_id>@{[$problem->external_id]}</service_request_id>#; + $local_requests_xml =~ s#<service_request_id_ext>\d+</service_request_id_ext>#<service_request_id_ext>@{[$problem->id]}</service_request_id_ext>#; + $local_requests_xml =~ s#<status>\w+</status>#<status>closed</status># if $test->{close_comment}; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $local_requests_xml } ); + + $problem->comments->delete; + $problem->lastupdate( DateTime->now()->subtract( days => 1 ) ); + $problem->state( $test->{start_state} ); + $problem->update; + + my $council_details = { areaid => 2482 }; + my $update = Open311::GetServiceRequestUpdates->new( system_user => $user ); + $update->update_comments( $o, $council_details ); + + is $problem->comments->count, 1, 'comment count'; + $problem->discard_changes; + + my $c = FixMyStreet::App->model('DB::Comment')->search( { external_id => $test->{external_id} } )->first; + ok $c, 'comment exists'; + is $c->text, $test->{description}, 'text correct'; + is $c->mark_fixed, $test->{mark_fixed}, 'mark_closed correct'; + is $c->problem_state, $test->{problem_state}, 'problem_state correct'; + is $c->mark_open, $test->{mark_open}, 'mark_open correct'; + is $problem->state, $test->{end_state}, 'correct problem state'; + }; +} + + +foreach my $test ( + { + desc => 'date for comment correct', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + external_id => 638344, + }, +) { + subtest $test->{desc} => sub { + my $dt = DateTime->now(); + $dt->subtract( minutes => 10 ); + my $local_requests_xml = $requests_xml; + + my $updated = sprintf( '<updated_datetime>%s</updated_datetime>', $dt ); + $local_requests_xml =~ s/UPDATED_DATETIME/$updated/; + $local_requests_xml =~ s#<service_request_id>\d+</service_request_id>#<service_request_id>@{[$problem->external_id]}</service_request_id>#; + $local_requests_xml =~ s#<service_request_id_ext>\d+</service_request_id_ext>#<service_request_id_ext>@{[$problem->id]}</service_request_id_ext>#; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $local_requests_xml } ); + + $problem->comments->delete; + + my $council_details = { areaid => 2482 }; + my $update = Open311::GetServiceRequestUpdates->new( system_user => $user ); + $update->update_comments( $o, $council_details ); + + my $comment = $problem->comments->first; + is $comment->created, $dt, 'created date set to date from XML'; + is $comment->confirmed, $dt, 'confirmed date set to date from XML'; + }; +} + +my $problem2 = $problem_rs->new( + { + postcode => 'EH99 1SP', + latitude => 1, + longitude => 1, + areas => 1, + title => '', + detail => '', + used_map => 1, + user_id => 1, + name => '', + state => 'confirmed', + service => '', + cobrand => 'default', + cobrand_data => '', + user => $user, + created => DateTime->now(), + lastupdate => DateTime->now(), + anonymous => 1, + external_id => $problem->external_id, + council => 2651, + } +); + +$problem2->insert(); +$problem->comments->delete; +$problem2->comments->delete; + +for my $test ( + { + desc => 'identical external_ids on problem resolved using council', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + external_id => 638344, + area_id => 2651, + request_id => $problem2->external_id, + request_id_ext => $problem2->id, + p1_comments => 0, + p2_comments => 1, + }, + { + desc => 'identical external_ids on comments resolved', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + external_id => 638344, + area_id => 2482, + request_id => $problem->external_id, + request_id_ext => $problem->id, + p1_comments => 1, + p2_comments => 1, + }, +) { + subtest $test->{desc} => sub { + my $local_requests_xml = $requests_xml; + $local_requests_xml =~ s/UPDATED_DATETIME/$test->{updated_datetime}/; + $local_requests_xml =~ s#<service_request_id>\d+</service_request_id>#<service_request_id>$test->{request_id}</service_request_id>#; + $local_requests_xml =~ s#<service_request_id_ext>\d+</service_request_id_ext>#<service_request_id_ext>$test->{request_id_ext}</service_request_id_ext>#; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $local_requests_xml } ); + + + my $council_details = { areaid => $test->{area_id} }; + my $update = Open311::GetServiceRequestUpdates->new( system_user => $user ); + $update->update_comments( $o, $council_details ); + + is $problem->comments->count, $test->{p1_comments}, 'comment count for first problem'; + is $problem2->comments->count, $test->{p2_comments}, 'comment count for second problem'; + }; +} + +subtest 'using start and end date' => sub { + my $local_requests_xml = $requests_xml; + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $local_requests_xml } ); + + my $start_dt = DateTime->now(); + $start_dt->subtract( days => 1 ); + my $end_dt = DateTime->now(); + + + my $update = Open311::GetServiceRequestUpdates->new( + system_user => $user, + start_date => $start_dt, + ); + + my $res = $update->update_comments( $o ); + is $res, 0, 'returns 0 if start but no end date'; + + $update = Open311::GetServiceRequestUpdates->new( + system_user => $user, + end_date => $end_dt, + ); + + $res = $update->update_comments( $o ); + is $res, 0, 'returns 0 if end but no start date'; + + $update = Open311::GetServiceRequestUpdates->new( + system_user => $user, + start_date => $start_dt, + end_date => $end_dt, + ); + + my $council_details = { areaid => 2482 }; + $update->update_comments( $o, $council_details ); + + my $start = $start_dt . ''; + my $end = $end_dt . ''; + + my $uri = URI->new( $o->test_uri_used ); + my $c = CGI::Simple->new( $uri->query ); + + is $c->param('start_date'), $start, 'start date used'; + is $c->param('end_date'), $end, 'end date used'; +}; + +subtest 'check that existing comments are not duplicated' => sub { + my $requests_xml = qq{<?xml version="1.0" encoding="utf-8"?> + <service_requests_updates> + <request_update> + <update_id>638344</update_id> + <service_request_id>@{[ $problem->external_id ]}</service_request_id> + <service_request_id_ext>@{[ $problem->id ]}</service_request_id_ext> + <status>open</status> + <description>This is a note</description> + <updated_datetime>UPDATED_DATETIME</updated_datetime> + </request_update> + <request_update> + <update_id>638354</update_id> + <service_request_id>@{[ $problem->external_id ]}</service_request_id> + <service_request_id_ext>@{[ $problem->id ]}</service_request_id_ext> + <status>open</status> + <description>This is a different note</description> + <updated_datetime>UPDATED_DATETIME2</updated_datetime> + </request_update> + </service_requests_updates> + }; + + $problem->comments->delete; + + my $comment = FixMyStreet::App->model('DB::Comment')->new( + { + problem => $problem, + external_id => 638344, + text => 'This is a note', + user => $user, + state => 'confirmed', + mark_fixed => 0, + anonymous => 0, + confirmed => $dt, + } + ); + $comment->insert; + + is $problem->comments->count, 1, 'one comment before fetching updates'; + + $requests_xml =~ s/UPDATED_DATETIME2/$dt/; + $requests_xml =~ s/UPDATED_DATETIME/@{[ $comment->confirmed ]}/; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $requests_xml } ); + + my $update = Open311::GetServiceRequestUpdates->new( + system_user => $user, + ); + + my $council_details = { areaid => 2482 }; + $update->update_comments( $o, $council_details ); + + $problem->discard_changes; + is $problem->comments->count, 2, 'two comments after fetching updates'; + + $update->update_comments( $o, $council_details ); + $problem->discard_changes; + is $problem->comments->count, 2, 're-fetching updates does not add comments'; + + $problem->comments->delete; + $update->update_comments( $o, $council_details ); + $problem->discard_changes; + is $problem->comments->count, 2, 'if comments are deleted then they are added'; +}; + +foreach my $test ( { + desc => 'check that closed and then open comment results in correct state', + dt1 => $dt->subtract( hours => 1 ), + dt2 => $dt, + }, + { + desc => 'check that old comments do not change problem status', + dt1 => $dt->subtract( hours => 2 ), + dt2 => $dt, + } +) { + subtest $test->{desc} => sub { + my $requests_xml = qq{<?xml version="1.0" encoding="utf-8"?> + <service_requests_updates> + <request_update> + <update_id>638344</update_id> + <service_request_id>@{[ $problem->external_id ]}</service_request_id> + <service_request_id_ext>@{[ $problem->id ]}</service_request_id_ext> + <status>closed</status> + <description>This is a note</description> + <updated_datetime>UPDATED_DATETIME</updated_datetime> + </request_update> + <request_update> + <update_id>638354</update_id> + <service_request_id>@{[ $problem->external_id ]}</service_request_id> + <service_request_id_ext>@{[ $problem->id ]}</service_request_id_ext> + <status>open</status> + <description>This is a different note</description> + <updated_datetime>UPDATED_DATETIME2</updated_datetime> + </request_update> + </service_requests_updates> + }; + + $problem->comments->delete; + $problem->state( 'confirmed' ); + $problem->lastupdate( $dt->subtract( hours => 3 ) ); + $problem->update; + + $requests_xml =~ s/UPDATED_DATETIME/$test->{dt1}/; + $requests_xml =~ s/UPDATED_DATETIME2/$test->{dt2}/; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $requests_xml } ); + + my $update = Open311::GetServiceRequestUpdates->new( + system_user => $user, + ); + + my $council_details = { areaid => 2482 }; + $update->update_comments( $o, $council_details ); + + $problem->discard_changes; + is $problem->comments->count, 2, 'two comments after fetching updates'; + is $problem->state, 'confirmed', 'correct problem status'; + }; +} + +foreach my $test ( { + desc => 'normally alerts are not suppressed', + suppress_alerts => 0, + }, + { + desc => 'alerts suppressed if suppress_alerts set', + suppress_alerts => 1, + } +) { + subtest $test->{desc} => sub { + my $requests_xml = qq{<?xml version="1.0" encoding="utf-8"?> + <service_requests_updates> + <request_update> + <update_id>638344</update_id> + <service_request_id>@{[ $problem->external_id ]}</service_request_id> + <service_request_id_ext>@{[ $problem->id ]}</service_request_id_ext> + <status>closed</status> + <description>This is a note</description> + <updated_datetime>UPDATED_DATETIME</updated_datetime> + </request_update> + </service_requests_updates> + }; + + $problem->comments->delete; + $problem->state( 'confirmed' ); + $problem->lastupdate( $dt->subtract( hours => 3 ) ); + $problem->update; + + my $alert = FixMyStreet::App->model('DB::Alert')->find_or_create( { + alert_type => 'new_updates', + parameter => $problem->id, + confirmed => 1, + user_id => $problem->user->id, + } ); + + $requests_xml =~ s/UPDATED_DATETIME/$dt/; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $requests_xml } ); + + my $update = Open311::GetServiceRequestUpdates->new( + system_user => $user, + suppress_alerts => $test->{suppress_alerts}, + ); + + my $council_details = { areaid => 2482 }; + $update->update_comments( $o, $council_details ); + $problem->discard_changes; + + my $alerts_sent = FixMyStreet::App->model('DB::AlertSent')->search( + { + alert_id => $alert->id, + parameter => $problem->comments->first->id, + } + ); + + if ( $test->{suppress_alerts} ) { + ok $alerts_sent->count(), 'alerts suppressed'; + } else { + is $alerts_sent->count(), 0, 'alerts not suppressed'; + } + + $alerts_sent->delete; + $alert->delete; + } +} + +$problem2->comments->delete(); +$problem->comments->delete(); +$problem2->delete; +$problem->delete; +$user->comments->delete; +$user->problems->delete; +$user->delete; + +done_testing(); diff --git a/t/open311/populate-service-list.t b/t/open311/populate-service-list.t index bdb7404f9..fbd729f3a 100644 --- a/t/open311/populate-service-list.t +++ b/t/open311/populate-service-list.t @@ -203,6 +203,304 @@ subtest 'check conflicting contacts not changed' => sub { is $contact_count, 4, 'correct number of contacts'; }; +subtest 'check meta data population' => sub { + my $processor = Open311::PopulateServiceList->new( council_list => [] ); + + my $meta_xml = '<?xml version="1.0" encoding="utf-8"?> +<service_definition> + <service_code>100</service_code> + <attributes> + <attribute> + <variable>true</variable> + <code>type</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Type of bin</datatype_description> + <order>1</order> + <description>Type of bin</description> + </attribute> + </attributes> +</service_definition> + '; + + my $contact = FixMyStreet::App->model('DB::Contact')->find_or_create( + { + area_id => 1, + email => '001', + category => 'Bins left out 24x7', + confirmed => 1, + deleted => 0, + editor => $0, + whenedited => \'ms_current_timestamp()', + note => 'test contact', + } + ); + + my $o = Open311->new( + jurisdiction => 'mysociety', + endpoint => 'http://example.com', + test_mode => 1, + test_get_returns => { 'services/100.xml' => $meta_xml } + ); + + my $council = FixMyStreet::App->model('DB::Open311conf')->new( { + area_id => 2482 + } ); + + $processor->_current_open311( $o ); + $processor->_current_council( $council ); + $processor->_current_service( { service_code => 100 } ); + + $processor->_add_meta_to_contact( $contact ); + + my $extra = [ { + variable => 'true', + code => 'type', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + } ]; + + $contact->discard_changes; + + is_deeply $contact->extra, $extra, 'meta data saved'; +}; + +subtest 'check attribute ordering' => sub { + my $processor = Open311::PopulateServiceList->new( council_list => [] ); + + my $meta_xml = '<?xml version="1.0" encoding="utf-8"?> +<service_definition> + <service_code>100</service_code> + <attributes> + <attribute> + <variable>true</variable> + <code>type</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Type of bin</datatype_description> + <order>1</order> + <description>Type of bin</description> + </attribute> + <attribute> + <variable>true</variable> + <code>colour</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Colour of bin</datatype_description> + <order>3</order> + <description>Colour of bin</description> + </attribute> + <attribute> + <variable>true</variable> + <code>size</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Size of bin</datatype_description> + <order>2</order> + <description>Size of bin</description> + </attribute> + </attributes> +</service_definition> + '; + + my $contact = FixMyStreet::App->model('DB::Contact')->find_or_create( + { + area_id => 1, + email => '001', + category => 'Bins left out 24x7', + confirmed => 1, + deleted => 0, + editor => $0, + whenedited => \'ms_current_timestamp()', + note => 'test contact', + } + ); + + my $o = Open311->new( + jurisdiction => 'mysociety', + endpoint => 'http://example.com', + test_mode => 1, + test_get_returns => { 'services/100.xml' => $meta_xml } + ); + + my $council = FixMyStreet::App->model('DB::Open311conf')->new( { + area_id => 1 + } ); + + $processor->_current_open311( $o ); + $processor->_current_council( $council ); + $processor->_current_service( { service_code => 100 } ); + + $processor->_add_meta_to_contact( $contact ); + + my $extra = [ + { + variable => 'true', + code => 'type', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + }, + { + variable => 'true', + code => 'size', + datatype => 'string', + required => 'true', + datatype_description => 'Size of bin', + order => 2, + description => 'Size of bin' + + }, + { + variable => 'true', + code => 'colour', + datatype => 'string', + required => 'true', + datatype_description => 'Colour of bin', + order => 3, + description => 'Colour of bin' + + }, + ]; + + $contact->discard_changes; + + is_deeply $contact->extra, $extra, 'meta data re-ordered correctly'; +}; + +subtest 'check bromely skip code' => sub { + my $processor = Open311::PopulateServiceList->new( council_list => [] ); + + my $meta_xml = '<?xml version="1.0" encoding="utf-8"?> +<service_definition> + <service_code>100</service_code> + <attributes> + <attribute> + <variable>true</variable> + <code>type</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Type of bin</datatype_description> + <order>1</order> + <description>Type of bin</description> + </attribute> + <attribute> + <variable>true</variable> + <code>title</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Type of bin</datatype_description> + <order>1</order> + <description>Type of bin</description> + </attribute> + <attribute> + <variable>true</variable> + <code>report_url</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Type of bin</datatype_description> + <order>1</order> + <description>Type of bin</description> + </attribute> + </attributes> +</service_definition> + '; + + my $contact = FixMyStreet::App->model('DB::Contact')->find_or_create( + { + area_id => 1, + email => '001', + category => 'Bins left out 24x7', + confirmed => 1, + deleted => 0, + editor => $0, + whenedited => \'ms_current_timestamp()', + note => 'test contact', + } + ); + + my $o = Open311->new( + jurisdiction => 'mysociety', + endpoint => 'http://example.com', + test_mode => 1, + test_get_returns => { 'services/100.xml' => $meta_xml } + ); + + my $council = FixMyStreet::App->model('DB::Open311conf')->new( { + area_id => 2482 + } ); + + $processor->_current_open311( $o ); + $processor->_current_council( $council ); + $processor->_current_service( { service_code => 100 } ); + + $processor->_add_meta_to_contact( $contact ); + + my $extra = [ { + variable => 'true', + code => 'type', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + } ]; + + $contact->discard_changes; + + is_deeply $contact->extra, $extra, 'only non std bromley meta data saved'; + + $council->area_id(1); + + $processor->_current_council( $council ); + $processor->_add_meta_to_contact( $contact ); + + $extra = [ + { + variable => 'true', + code => 'type', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + }, + { + variable => 'true', + code => 'title', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + }, + { + variable => 'true', + code => 'report_url', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + }, + ]; + + $contact->discard_changes; + + is_deeply $contact->extra, $extra, 'all meta data saved for non bromley'; +}; + sub get_standard_xml { return qq{<?xml version="1.0" encoding="utf-8"?> <services> |