aboutsummaryrefslogtreecommitdiffstats
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/admin_censor_rule_controller_spec.rb4
-rw-r--r--spec/controllers/admin_general_controller_spec.rb5
-rw-r--r--spec/controllers/admin_incoming_message_controller_spec.rb155
-rw-r--r--spec/controllers/admin_outgoing_message_controller_spec.rb105
-rw-r--r--spec/controllers/admin_public_body_change_requests_controller_spec.rb35
-rw-r--r--spec/controllers/admin_public_body_controller_spec.rb502
-rw-r--r--spec/controllers/admin_request_controller_spec.rb114
-rw-r--r--spec/controllers/admin_track_controller_spec.rb2
-rw-r--r--spec/controllers/admin_user_controller_spec.rb25
-rw-r--r--spec/controllers/api_controller_spec.rb47
-rw-r--r--spec/controllers/comment_controller_spec.rb2
-rw-r--r--spec/controllers/general_controller_spec.rb221
-rw-r--r--spec/controllers/help_controller_spec.rb23
-rw-r--r--spec/controllers/info_request_batch_controller_spec.rb53
-rw-r--r--spec/controllers/public_body_change_requests_controller_spec.rb99
-rw-r--r--spec/controllers/public_body_controller_spec.rb304
-rw-r--r--spec/controllers/reports_controller_spec.rb104
-rw-r--r--spec/controllers/request_controller_spec.rb1409
-rw-r--r--spec/controllers/services_controller_spec.rb12
-rw-r--r--spec/controllers/track_controller_spec.rb69
-rw-r--r--spec/controllers/user_controller_spec.rb213
-rw-r--r--spec/factories.rb165
-rw-r--r--spec/fixtures/comments.yml15
-rw-r--r--spec/fixtures/files/blog_feed.atom39
-rw-r--r--spec/fixtures/files/document-pdf.email110
-rw-r--r--spec/fixtures/files/email-folding-example-1.txt.expected7
-rw-r--r--spec/fixtures/files/email-folding-example-10.txt12
-rw-r--r--spec/fixtures/files/email-folding-example-10.txt.expected13
-rw-r--r--spec/fixtures/files/email-folding-example-11.txt45
-rw-r--r--spec/fixtures/files/email-folding-example-11.txt.expected8
-rw-r--r--spec/fixtures/files/email-folding-example-2.txt.expected1
-rw-r--r--spec/fixtures/files/email-folding-example-3.txt.expected7
-rw-r--r--spec/fixtures/files/email-folding-example-5.txt.expected12
-rw-r--r--spec/fixtures/files/email-folding-example-7.txt.expected3
-rw-r--r--spec/fixtures/files/email-folding-example-8.txt.expected3
-rw-r--r--spec/fixtures/files/email-folding-example-9.txt.expected6
-rw-r--r--spec/fixtures/files/fake-authority-type.csv3
-rw-r--r--spec/fixtures/files/incoming-request-empty.email8
-rw-r--r--spec/fixtures/files/incoming-request-two-same-name.email4
-rw-r--r--spec/fixtures/files/inline-uuencode.email27
-rw-r--r--spec/fixtures/files/interesting.pdfbin0 -> 6280 bytes
-rw-r--r--spec/fixtures/files/malformed-to-and-cc.email11
-rw-r--r--spec/fixtures/files/mislabelled-as-iso-8859-1.email20
-rw-r--r--spec/fixtures/files/multipart-no-final-boundary.email21
-rw-r--r--spec/fixtures/files/nested-attachments-premature-end.email110
-rw-r--r--spec/fixtures/files/no-part-charset-random-data.email30
-rw-r--r--spec/fixtures/files/part-without-charset-in-content-type.email38
-rw-r--r--spec/fixtures/files/subject-bad-utf-8-trailing-base64.email5
-rw-r--r--spec/fixtures/files/subject-bad-utf-8-trailing-quoted-printable.email5
-rw-r--r--spec/fixtures/files/tnef-attachment-empty.email196
-rw-r--r--spec/fixtures/files/tnef-attachment-truncated.email34
-rw-r--r--spec/fixtures/files/unrecognized-encoding-mail.email36
-rw-r--r--spec/fixtures/foi_attachments.yml1
-rw-r--r--spec/fixtures/holidays.yml9
-rw-r--r--spec/fixtures/incoming_messages.yml22
-rw-r--r--spec/fixtures/info_request_events.yml28
-rw-r--r--spec/fixtures/info_requests.yml36
-rw-r--r--spec/fixtures/locale/en/app.po153
-rw-r--r--spec/fixtures/locale/en_GB/app.po152
-rw-r--r--spec/fixtures/locale/es/app.po270
-rw-r--r--spec/fixtures/outgoing_messages.yml29
-rw-r--r--spec/fixtures/public_bodies.yml85
-rw-r--r--spec/fixtures/public_body_translations.yml59
-rw-r--r--spec/fixtures/raw_emails.yml7
-rw-r--r--spec/fixtures/theme_views/core/application_mailer/core_only.html.erb (renamed from spec/fixtures/theme_views/core/application_mailer/core_only.rhtml)0
-rw-r--r--spec/fixtures/theme_views/core/application_mailer/multipart_core_only.html.erb (renamed from spec/fixtures/theme_views/core/application_mailer/multipart_core_only.rhtml)0
-rw-r--r--spec/fixtures/theme_views/core/application_mailer/simple.html.erb (renamed from spec/fixtures/theme_views/core/application_mailer/simple.rhtml)0
-rw-r--r--spec/fixtures/theme_views/theme_one/application_mailer/multipart_theme_only.html.erb (renamed from spec/fixtures/theme_views/theme_one/application_mailer/multipart_theme_only.rhtml)0
-rw-r--r--spec/fixtures/theme_views/theme_one/application_mailer/simple.html.erb (renamed from spec/fixtures/theme_views/theme_one/application_mailer/simple.rhtml)0
-rw-r--r--spec/fixtures/theme_views/theme_one/application_mailer/theme_only.html.erb (renamed from spec/fixtures/theme_views/theme_one/application_mailer/theme_only.rhtml)0
-rw-r--r--spec/fixtures/theme_views/theme_one/help/contact.es.html.erb1
-rw-r--r--spec/fixtures/theme_views/theme_one/help/contact.html.erb1
-rw-r--r--spec/fixtures/track_things.yml16
-rw-r--r--spec/fixtures/track_things_sent_emails.yml0
-rw-r--r--spec/fixtures/users.yml46
-rw-r--r--spec/helpers/link_to_helper_spec.rb75
-rw-r--r--spec/integration/admin_spec.rb79
-rw-r--r--spec/integration/alaveteli_dsl.rb68
-rw-r--r--spec/integration/cookie_stripping_spec.rb12
-rw-r--r--spec/integration/create_request_spec.rb45
-rw-r--r--spec/integration/download_request_spec.rb324
-rw-r--r--spec/integration/errors_spec.rb159
-rw-r--r--spec/integration/localisation_spec.rb88
-rw-r--r--spec/integration/request_controller_spec.rb39
-rw-r--r--spec/integration/search_request_spec.rb6
-rw-r--r--spec/integration/view_request_spec.rb165
-rw-r--r--spec/lib/ability_spec.rb51
-rw-r--r--spec/lib/alaveteli_external_command.rb23
-rwxr-xr-xspec/lib/alaveteli_external_command_scripts/error.sh4
-rwxr-xr-xspec/lib/alaveteli_external_command_scripts/segfault.sh3
-rw-r--r--spec/lib/basic_encoding_tests.rb157
-rw-r--r--spec/lib/confidence_intervals.rb30
-rw-r--r--spec/lib/i18n_interpolation.rb28
-rw-r--r--spec/lib/mail_handler/mail_handler_spec.rb126
-rw-r--r--spec/lib/sendmail_return_path_spec.rb83
-rw-r--r--spec/lib/theme_spec.rb25
-rw-r--r--spec/lib/timezone_fixes_spec.rb5
-rw-r--r--spec/lib/whatdotheyknow/strip_empty_sessions_spec.rb56
-rw-r--r--spec/mailers/application_mailer_spec.rb (renamed from spec/models/application_mailer_spec.rb)48
-rw-r--r--spec/mailers/info_request_batch_mailer.rb35
-rw-r--r--spec/mailers/outgoing_mailer_spec.rb133
-rw-r--r--spec/mailers/request_mailer_spec.rb (renamed from spec/models/request_mailer_spec.rb)90
-rw-r--r--spec/mailers/track_mailer_spec.rb (renamed from spec/models/track_mailer_spec.rb)57
-rw-r--r--spec/models/censor_rule_spec.rb39
-rw-r--r--spec/models/contact_mailer_spec.rb8
-rw-r--r--spec/models/foi_attachment_spec.rb17
-rw-r--r--spec/models/holiday_spec.rb9
-rw-r--r--spec/models/incoming_message_spec.rb241
-rw-r--r--spec/models/info_request_batch_spec.rb150
-rw-r--r--spec/models/info_request_event_spec.rb85
-rw-r--r--spec/models/info_request_spec.rb757
-rw-r--r--spec/models/mail_server_log_spec.rb23
-rw-r--r--spec/models/outgoing_mailer_spec.rb140
-rw-r--r--spec/models/outgoing_message_spec.rb132
-rw-r--r--spec/models/post_redirect_spec.rb16
-rw-r--r--spec/models/profile_photo_spec.rb32
-rw-r--r--spec/models/public_body_change_request_spec.rb139
-rw-r--r--spec/models/public_body_spec.rb280
-rw-r--r--spec/models/purge_request_spec.rb20
-rw-r--r--spec/models/raw_email_spec.rb7
-rw-r--r--spec/models/track_thing_spec.rb18
-rw-r--r--spec/models/track_things_sent_email_spec.rb13
-rw-r--r--spec/models/user_info_request_sent_alert_spec.rb11
-rw-r--r--spec/models/user_mailer_spec.rb8
-rw-r--r--spec/models/user_spec.rb80
-rw-r--r--spec/models/xapian_spec.rb150
-rw-r--r--spec/script/mailin_spec.rb24
-rw-r--r--spec/spec.opts3
-rw-r--r--spec/spec_helper.rb475
-rw-r--r--spec/support/email_helpers.rb23
-rw-r--r--spec/support/load_file_fixtures.rb8
-rw-r--r--spec/support/xapian_index.rb42
-rw-r--r--spec/views/public_body/show.html.erb_spec.rb (renamed from spec/views/public_body/show.rhtml_spec.rb)67
-rw-r--r--spec/views/reports/new.erb_spec.rb29
-rw-r--r--spec/views/request/_after_actions.html.erb_spec.rb (renamed from spec/views/request/_after_actions.rhtml_spec.rb)74
-rw-r--r--spec/views/request/_describe_state.html.erb_spec.rb (renamed from spec/views/request/_describe_state.rhtml_spec.rb)28
-rw-r--r--spec/views/request/list.rhtml_spec.rb51
-rw-r--r--spec/views/request/show.html.erb_spec.rb110
-rw-r--r--spec/views/request/show.rhtml_spec.rb128
-rw-r--r--spec/views/request_game/play.html.erb_spec.rb (renamed from spec/views/request_game/play.rhtml_spec.rb)14
140 files changed, 8178 insertions, 2699 deletions
diff --git a/spec/controllers/admin_censor_rule_controller_spec.rb b/spec/controllers/admin_censor_rule_controller_spec.rb
index 8893a858b..37ffd9764 100644
--- a/spec/controllers/admin_censor_rule_controller_spec.rb
+++ b/spec/controllers/admin_censor_rule_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminCensorRuleController, "when making censor rules from the admin interface" do
- integrate_views
+ render_views
before { basic_auth_login @request }
it "should create a censor rule and purge the corresponding request from varnish" do
@@ -10,7 +10,7 @@ describe AdminCensorRuleController, "when making censor rules from the admin int
:text => "meat",
:replacement => "tofu",
:last_edit_comment => "none",
- :info_request => ir
+ :info_request_id => ir
}
PurgeRequest.all().first.model_id.should == ir.id
end
diff --git a/spec/controllers/admin_general_controller_spec.rb b/spec/controllers/admin_general_controller_spec.rb
index dc1eb0d97..971960762 100644
--- a/spec/controllers/admin_general_controller_spec.rb
+++ b/spec/controllers/admin_general_controller_spec.rb
@@ -4,7 +4,7 @@ describe AdminGeneralController do
describe "when viewing front page of admin interface" do
- integrate_views
+ render_views
before { basic_auth_login @request }
it "should render the front page" do
@@ -14,8 +14,7 @@ describe AdminGeneralController do
it "should redirect to include trailing slash" do
get :index
- response.should redirect_to(:controller => 'admin_general',
- :action => 'index')
+ response.should redirect_to admin_general_index_url(:trailing_slash => true)
end
end
diff --git a/spec/controllers/admin_incoming_message_controller_spec.rb b/spec/controllers/admin_incoming_message_controller_spec.rb
new file mode 100644
index 000000000..21c744e5b
--- /dev/null
+++ b/spec/controllers/admin_incoming_message_controller_spec.rb
@@ -0,0 +1,155 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe AdminIncomingMessageController, "when administering incoming messages" do
+
+ describe 'when destroying an incoming message' do
+
+ before(:each) do
+ basic_auth_login @request
+ load_raw_emails_data
+ end
+
+ before do
+ @im = incoming_messages(:useless_incoming_message)
+ @controller.stub!(:expire_for_request)
+ end
+
+ it "destroys the raw email file" do
+ raw_email = @im.raw_email.filepath
+ assert_equal File.exists?(raw_email), true
+ post :destroy, :incoming_message_id => @im.id
+ assert_equal File.exists?(raw_email), false
+ end
+
+ it 'asks the incoming message to fully destroy itself' do
+ IncomingMessage.stub!(:find).and_return(@im)
+ @im.should_receive(:fully_destroy)
+ post :destroy, :incoming_message_id => @im.id
+ end
+
+ it 'expires the file cache for the associated info_request' do
+ @controller.should_receive(:expire_for_request).with(@im.info_request)
+ post :destroy, :incoming_message_id => @im.id
+ end
+
+ end
+
+ describe 'when redelivering an incoming message' do
+
+ before(:each) do
+ basic_auth_login @request
+ load_raw_emails_data
+ end
+
+ it 'expires the file cache for the previous request' do
+ current_info_request = info_requests(:fancy_dog_request)
+ destination_info_request = info_requests(:naughty_chicken_request)
+ incoming_message = incoming_messages(:useless_incoming_message)
+ @controller.should_receive(:expire_for_request).with(current_info_request)
+ post :redeliver, :redeliver_incoming_message_id => incoming_message.id,
+ :url_title => destination_info_request.url_title
+ end
+
+ it 'should succeed, even if a duplicate xapian indexing job is created' do
+
+ with_duplicate_xapian_job_creation do
+ current_info_request = info_requests(:fancy_dog_request)
+ destination_info_request = info_requests(:naughty_chicken_request)
+ incoming_message = incoming_messages(:useless_incoming_message)
+ post :redeliver, :redeliver_incoming_message_id => incoming_message.id,
+ :url_title => destination_info_request.url_title
+ end
+
+ end
+
+ end
+
+ describe 'when editing an incoming message' do
+
+ before do
+ @incoming = FactoryGirl.create(:incoming_message)
+ end
+
+ it 'should be successful' do
+ get :edit, :id => @incoming.id
+ response.should be_success
+ end
+
+ it 'should assign the incoming message to the view' do
+ get :edit, :id => @incoming.id
+ assigns[:incoming_message].should == @incoming
+ end
+
+ end
+
+ describe 'when updating an incoming message' do
+
+ before do
+ @incoming = FactoryGirl.create(:incoming_message, :prominence => 'normal')
+ @default_params = {:id => @incoming.id,
+ :incoming_message => {:prominence => 'hidden',
+ :prominence_reason => 'dull'} }
+ end
+
+ def make_request(params=@default_params)
+ post :update, params
+ end
+
+ it 'should save the prominence of the message' do
+ make_request
+ @incoming.reload
+ @incoming.prominence.should == 'hidden'
+ end
+
+ it 'should save a prominence reason for the message' do
+ make_request
+ @incoming.reload
+ @incoming.prominence_reason.should == 'dull'
+ end
+
+ it 'should log an "edit_incoming" event on the info_request' do
+ @controller.stub!(:admin_current_user).and_return("Admin user")
+ make_request
+ @incoming.reload
+ last_event = @incoming.info_request_events.last
+ last_event.event_type.should == 'edit_incoming'
+ last_event.params.should == { :incoming_message_id => @incoming.id,
+ :editor => "Admin user",
+ :old_prominence => "normal",
+ :prominence => "hidden",
+ :old_prominence_reason => nil,
+ :prominence_reason => "dull" }
+ end
+
+ it 'should expire the file cache for the info request' do
+ @controller.should_receive(:expire_for_request).with(@incoming.info_request)
+ make_request
+ end
+
+ context 'if the incoming message saves correctly' do
+
+ it 'should redirect to the admin info request view' do
+ make_request
+ response.should redirect_to admin_request_show_url(@incoming.info_request)
+ end
+
+ it 'should show a message that the incoming message has been updated' do
+ make_request
+ flash[:notice].should == 'Incoming message successfully updated.'
+ end
+
+ end
+
+ context 'if the incoming message is not valid' do
+
+ it 'should render the edit template' do
+ make_request({:id => @incoming.id,
+ :incoming_message => {:prominence => 'fantastic',
+ :prominence_reason => 'dull'}})
+ response.should render_template("edit")
+ end
+
+ end
+ end
+
+end
diff --git a/spec/controllers/admin_outgoing_message_controller_spec.rb b/spec/controllers/admin_outgoing_message_controller_spec.rb
new file mode 100644
index 000000000..0dde53b86
--- /dev/null
+++ b/spec/controllers/admin_outgoing_message_controller_spec.rb
@@ -0,0 +1,105 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe AdminOutgoingMessageController do
+
+ describe 'when editing an outgoing message' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ @outgoing = @info_request.outgoing_messages.first
+ end
+
+ it 'should be successful' do
+ get :edit, :id => @outgoing.id
+ response.should be_success
+ end
+
+ it 'should assign the incoming message to the view' do
+ get :edit, :id => @outgoing.id
+ assigns[:outgoing_message].should == @outgoing
+ end
+
+ end
+
+ describe 'when updating an outgoing message' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ @outgoing = @info_request.outgoing_messages.first
+ @default_params = {:id => @outgoing.id,
+ :outgoing_message => {:prominence => 'hidden',
+ :prominence_reason => 'dull',
+ :body => 'changed body'} }
+ end
+
+ def make_request(params=@default_params)
+ post :update, params
+ end
+
+ it 'should save a change to the body of the message' do
+ make_request
+ @outgoing.reload
+ @outgoing.body.should == 'changed body'
+ end
+
+ it 'should save the prominence of the message' do
+ make_request
+ @outgoing.reload
+ @outgoing.prominence.should == 'hidden'
+ end
+
+ it 'should save a prominence reason for the message' do
+ make_request
+ @outgoing.reload
+ @outgoing.prominence_reason.should == 'dull'
+ end
+
+ it 'should log an "edit_outgoing" event on the info_request' do
+ @controller.stub!(:admin_current_user).and_return("Admin user")
+ make_request
+ @info_request.reload
+ last_event = @info_request.info_request_events.last
+ last_event.event_type.should == 'edit_outgoing'
+ last_event.params.should == { :outgoing_message_id => @outgoing.id,
+ :editor => "Admin user",
+ :old_prominence => "normal",
+ :prominence => "hidden",
+ :old_prominence_reason => nil,
+ :old_body => 'Some information please',
+ :body => 'changed body',
+ :prominence_reason => "dull" }
+ end
+
+ it 'should expire the file cache for the info request' do
+ @controller.should_receive(:expire_for_request).with(@info_request)
+ make_request
+ end
+
+ context 'if the outgoing message saves correctly' do
+
+ it 'should redirect to the admin info request view' do
+ make_request
+ response.should redirect_to admin_request_show_url(@info_request)
+ end
+
+ it 'should show a message that the incoming message has been updated' do
+ make_request
+ flash[:notice].should == 'Outgoing message successfully updated.'
+ end
+
+ end
+
+ context 'if the incoming message is not valid' do
+
+ it 'should render the edit template' do
+ make_request({:id => @outgoing.id,
+ :outgoing_message => {:prominence => 'fantastic',
+ :prominence_reason => 'dull',
+ :body => 'Some information please'}})
+ response.should render_template("edit")
+ end
+
+ end
+ end
+
+end
diff --git a/spec/controllers/admin_public_body_change_requests_controller_spec.rb b/spec/controllers/admin_public_body_change_requests_controller_spec.rb
new file mode 100644
index 000000000..b478e851d
--- /dev/null
+++ b/spec/controllers/admin_public_body_change_requests_controller_spec.rb
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe AdminPublicBodyChangeRequestsController, "editing a change request" do
+
+ it "should render the edit template" do
+ change_request = FactoryGirl.create(:add_body_request)
+ get :edit, :id => change_request.id
+ response.should render_template("edit")
+ end
+
+end
+
+describe AdminPublicBodyChangeRequestsController, 'updating a change request' do
+
+ before do
+ @change_request = FactoryGirl.create(:add_body_request)
+ post :update, { :id => @change_request.id,
+ :response => 'Thanks but no',
+ :subject => 'Your request' }
+ end
+
+ it 'should close the change request' do
+ PublicBodyChangeRequest.find(@change_request.id).is_open.should == false
+ end
+
+ it 'should send a response email to the user who requested the change' do
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.subject.should == 'Your request'
+ mail.to.should == [@change_request.get_user_email]
+ mail.body.should =~ /Thanks but no/
+ end
+end
diff --git a/spec/controllers/admin_public_body_controller_spec.rb b/spec/controllers/admin_public_body_controller_spec.rb
index 504ddc5cc..095d23245 100644
--- a/spec/controllers/admin_public_body_controller_spec.rb
+++ b/spec/controllers/admin_public_body_controller_spec.rb
@@ -1,16 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-describe AdminPublicBodyController, "when administering public bodies" do
- integrate_views
-
- before do
- @old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
- end
-
- after do
- ActionController::Routing::Routes.filters = @old_filters
- end
+describe AdminPublicBodyController, "when showing the index of public bodies" do
+ render_views
it "shows the index page" do
get :index
@@ -21,28 +12,255 @@ describe AdminPublicBodyController, "when administering public bodies" do
assigns[:public_bodies].should == [ public_bodies(:humpadink_public_body) ]
end
+ it "searches for 'humpa' in another locale" do
+ get :index, {:query => "humpa", :locale => "es"}
+ assigns[:public_bodies].should == [ public_bodies(:humpadink_public_body) ]
+ end
+
+end
+
+describe AdminPublicBodyController, "when showing a public body" do
+ render_views
+
it "shows a public body" do
get :show, :id => 2
end
- it "creates a new public body" do
+ it "sets a using_admin flag" do
+ get :show, :id => 2
+ session[:using_admin].should == 1
+ end
+
+ it "shows a public body in another locale" do
+ get :show, {:id => 2, :locale => "es" }
+ end
+
+end
+
+describe AdminPublicBodyController, 'when showing the form for a new public body' do
+
+ it 'should assign a new public body to the view' do
+ get :new
+ assigns[:public_body].should be_a(PublicBody)
+ end
+
+ context 'when passed a change request id as a param' do
+ render_views
+
+ it 'should populate the name, email address and last edit comment on the public body' do
+ change_request = FactoryGirl.create(:add_body_request)
+ get :new, :change_request_id => change_request.id
+ assigns[:public_body].name.should == change_request.public_body_name
+ assigns[:public_body].request_email.should == change_request.public_body_email
+ assigns[:public_body].last_edit_comment.should match('Notes: Please')
+ end
+
+ it 'should assign a default response text to the view' do
+ change_request = FactoryGirl.create(:add_body_request)
+ get :new, :change_request_id => change_request.id
+ assigns[:change_request_user_response].should match("Thanks for your suggestion to add A New Body")
+ end
+ end
+
+end
+
+describe AdminPublicBodyController, "when creating a public body" do
+ render_views
+
+ it "creates a new public body in one locale" do
n = PublicBody.count
- post :create, { :public_body => { :name => "New Quango", :short_name => "", :tag_string => "blah", :request_email => 'newquango@localhost', :last_edit_comment => 'From test code' } }
+ post :create, { :public_body => { :name => "New Quango",
+ :short_name => "",
+ :tag_string => "blah",
+ :request_email => 'newquango@localhost',
+ :last_edit_comment => 'From test code' } }
+ PublicBody.count.should == n + 1
+
+ body = PublicBody.find_by_name("New Quango")
+ response.should redirect_to(:controller=>'admin_public_body', :action=>'show', :id=>body.id)
+ end
+
+ it "creates a new public body with multiple locales" do
+ n = PublicBody.count
+ post :create, {
+ :public_body => { :name => "New Quango",
+ :short_name => "",
+ :tag_string => "blah",
+ :request_email => 'newquango@localhost',
+ :last_edit_comment => 'From test code',
+ :translated_versions => [{ :locale => "es",
+ :name => "Mi Nuevo Quango",
+ :short_name => "",
+ :request_email => 'newquango@localhost' }]
+ }
+ }
PublicBody.count.should == n + 1
+
+ body = PublicBody.find_by_name("New Quango")
+ body.translations.map {|t| t.locale.to_s}.sort.should == ["en", "es"]
+ I18n.with_locale(:en) do
+ body.name.should == "New Quango"
+ body.url_name.should == "new_quango"
+ body.first_letter.should == "N"
+ end
+ I18n.with_locale(:es) do
+ body.name.should == "Mi Nuevo Quango"
+ body.url_name.should == "mi_nuevo_quango"
+ body.first_letter.should == "M"
+ end
+
+ response.should redirect_to(:controller=>'admin_public_body', :action=>'show', :id=>body.id)
end
+ context 'when the body is being created as a result of a change request' do
+
+ before do
+ @change_request = FactoryGirl.create(:add_body_request)
+ post :create, { :public_body => { :name => "New Quango",
+ :short_name => "",
+ :tag_string => "blah",
+ :request_email => 'newquango@localhost',
+ :last_edit_comment => 'From test code' },
+ :change_request_id => @change_request.id,
+ :subject => 'Adding a new body',
+ :response => 'The URL will be [Authority URL will be inserted here]'}
+ end
+
+ it 'should send a response to the requesting user' do
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.subject.should == 'Adding a new body'
+ mail.to.should == [@change_request.get_user_email]
+ mail.body.should =~ /The URL will be http:\/\/test.host\/body\/new_quango/
+ end
+
+ it 'should mark the change request as closed' do
+ PublicBodyChangeRequest.find(@change_request.id).is_open.should be_false
+ end
+
+ end
+
+end
+
+describe AdminPublicBodyController, "when editing a public body" do
+ render_views
+
it "edits a public body" do
get :edit, :id => 2
end
+ it "edits a public body in another locale" do
+ get :edit, {:id => 3, :locale => :en}
+
+ # When editing a body, the controller returns all available translations
+ assigns[:public_body].find_translation_by_locale("es").name.should == 'El Department for Humpadinking'
+ assigns[:public_body].name.should == 'Department for Humpadinking'
+ response.should render_template('edit')
+ end
+
+ context 'when passed a change request id as a param' do
+ render_views
+
+ before do
+ @change_request = FactoryGirl.create(:update_body_request)
+ get :edit, :id => @change_request.public_body_id, :change_request_id => @change_request.id
+ end
+
+ it 'should populate the email address and last edit comment on the public body' do
+ change_request = FactoryGirl.create(:update_body_request)
+ get :edit, :id => change_request.public_body_id, :change_request_id => change_request.id
+ assigns[:public_body].request_email.should == @change_request.public_body_email
+ assigns[:public_body].last_edit_comment.should match('Notes: Please')
+ end
+
+ it 'should assign a default response text to the view' do
+ assigns[:change_request_user_response].should match("Thanks for your suggestion to update the email address")
+ end
+ end
+
+end
+
+describe AdminPublicBodyController, "when updating a public body" do
+ render_views
+
it "saves edits to a public body" do
public_bodies(:humpadink_public_body).name.should == "Department for Humpadinking"
- post :update, { :id => 3, :public_body => { :name => "Renamed", :short_name => "", :tag_string => "some tags", :request_email => 'edited@localhost', :last_edit_comment => 'From test code' } }
- response.flash[:notice].should include('successful')
+ post :update, { :id => 3, :public_body => { :name => "Renamed",
+ :short_name => "",
+ :tag_string => "some tags",
+ :request_email => 'edited@localhost',
+ :last_edit_comment => 'From test code' } }
+ request.flash[:notice].should include('successful')
pb = PublicBody.find(public_bodies(:humpadink_public_body).id)
pb.name.should == "Renamed"
end
+ it "saves edits to a public body in another locale" do
+ I18n.with_locale(:es) do
+ pb = PublicBody.find(id=3)
+ pb.name.should == "El Department for Humpadinking"
+ post :update, {
+ :id => 3,
+ :public_body => {
+ :name => "Department for Humpadinking",
+ :short_name => "",
+ :tag_string => "some tags",
+ :request_email => 'edited@localhost',
+ :last_edit_comment => 'From test code',
+ :translated_versions => {
+ 3 => {:locale => "es",
+ :name => "Renamed",
+ :short_name => "",
+ :request_email => 'edited@localhost'}
+ }
+ }
+ }
+ request.flash[:notice].should include('successful')
+ end
+
+ pb = PublicBody.find(public_bodies(:humpadink_public_body).id)
+ I18n.with_locale(:es) do
+ pb.name.should == "Renamed"
+ end
+ I18n.with_locale(:en) do
+ pb.name.should == "Department for Humpadinking"
+ end
+ end
+
+ context 'when the body is being updated as a result of a change request' do
+
+ before do
+ @change_request = FactoryGirl.create(:update_body_request)
+ post :update, { :id => @change_request.public_body_id,
+ :public_body => { :name => "New Quango",
+ :short_name => "",
+ :request_email => 'newquango@localhost',
+ :last_edit_comment => 'From test code' },
+ :change_request_id => @change_request.id,
+ :subject => 'Body update',
+ :response => 'Done.'}
+ end
+
+ it 'should send a response to the requesting user' do
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.subject.should == 'Body update'
+ mail.to.should == [@change_request.get_user_email]
+ mail.body.should =~ /Done./
+ end
+
+ it 'should mark the change request as closed' do
+ PublicBodyChangeRequest.find(@change_request.id).is_open.should be_false
+ end
+
+ end
+end
+
+describe AdminPublicBodyController, "when destroying a public body" do
+ render_views
+
it "does not destroy a public body that has associated requests" do
id = public_bodies(:humpadink_public_body).id
n = PublicBody.count
@@ -58,94 +276,94 @@ describe AdminPublicBodyController, "when administering public bodies" do
PublicBody.count.should == n - 1
end
- it "sets a using_admin flag" do
- get :show, :id => 2
- session[:using_admin].should == 1
- end
+end
+
+describe AdminPublicBodyController, "when assigning public body tags" do
+ render_views
it "mass assigns tags" do
- n = PublicBody.count
+ condition = "public_body_translations.locale = ?"
+ n = PublicBody.joins(:translations).where([condition, "en"]).count
post :mass_tag_add, { :new_tag => "department", :table_name => "substring" }
- response.flash[:notice].should == "Added tag to table of bodies."
+ request.flash[:notice].should == "Added tag to table of bodies."
response.should redirect_to(:action=>'list')
PublicBody.find_by_tag("department").count.should == n
end
+end
- describe 'import_csv' do
+describe AdminPublicBodyController, "when importing a csv" do
+ render_views
- describe 'when handling a GET request' do
+ describe 'when handling a GET request' do
- it 'should get the page successfully' do
- get :import_csv
- response.should be_success
- end
+ it 'should get the page successfully' do
+ get :import_csv
+ response.should be_success
+ end
+
+ end
+
+ describe 'when handling a POST request' do
+ before do
+ PublicBody.stub!(:import_csv).and_return([[],[]])
+ @file_object = fixture_file_upload('/files/fake-authority-type.csv')
end
- describe 'when handling a POST request' do
+ it 'should handle a nil csv file param' do
+ post :import_csv, { :commit => 'Dry run' }
+ response.should be_success
+ end
+
+ describe 'if there is a csv file param' do
- before do
- PublicBody.stub!(:import_csv).and_return([[],[]])
- @file_object = mock("a file upload", :read => 'some contents',
- :original_filename => 'contents.txt')
+ it 'should try to get the contents and original name of a csv file param' do
+ @file_object.should_receive(:read).and_return('some contents')
+ post :import_csv, { :csv_file => @file_object,
+ :commit => 'Dry run'}
end
- it 'should handle a nil csv file param' do
- post :import_csv, { :commit => 'Dry run' }
- response.should be_success
+ it 'should assign the original filename to the view' do
+ post :import_csv, { :csv_file => @file_object,
+ :commit => 'Dry run'}
+ assigns[:original_csv_file].should == 'fake-authority-type.csv'
end
- describe 'if there is a csv file param' do
+ end
- it 'should try to get the contents and original name of a csv file param' do
- @file_object.should_receive(:read).and_return('some contents')
- post :import_csv, { :csv_file => @file_object,
- :commit => 'Dry run'}
- end
+ describe 'if there is no csv file param, but there are temporary_csv_file and
+ original_csv_file params' do
- it 'should assign the original filename to the view' do
- post :import_csv, { :csv_file => @file_object,
- :commit => 'Dry run'}
- assigns[:original_csv_file].should == 'contents.txt'
- end
+ it 'should try and get the file contents from a temporary file whose name
+ is passed as a param' do
+ @controller.should_receive(:retrieve_csv_data).with('csv_upload-2046-12-31-394')
+ post :import_csv, { :temporary_csv_file => 'csv_upload-2046-12-31-394',
+ :original_csv_file => 'original_contents.txt',
+ :commit => 'Dry run'}
+ end
+ it 'should raise an error on an invalid temp file name' do
+ params = { :temporary_csv_file => 'bad_name',
+ :original_csv_file => 'original_contents.txt',
+ :commit => 'Dry run'}
+ expected_error = "Invalid filename in upload_csv: bad_name"
+ lambda{ post :import_csv, params }.should raise_error(expected_error)
end
- describe 'if there is no csv file param, but there are temporary_csv_file and
- original_csv_file params' do
-
- it 'should try and get the file contents from a temporary file whose name
- is passed as a param' do
- @controller.should_receive(:retrieve_csv_data).with('csv_upload-2046-12-31-394')
- post :import_csv, { :temporary_csv_file => 'csv_upload-2046-12-31-394',
- :original_csv_file => 'original_contents.txt',
- :commit => 'Dry run'}
- end
-
- it 'should raise an error on an invalid temp file name' do
- params = { :temporary_csv_file => 'bad_name',
- :original_csv_file => 'original_contents.txt',
- :commit => 'Dry run'}
- expected_error = "Invalid filename in upload_csv: bad_name"
- lambda{ post :import_csv, params }.should raise_error(expected_error)
- end
-
- it 'should raise an error if the temp file does not exist' do
- temp_name = "csv_upload-20461231-394"
- params = { :temporary_csv_file => temp_name,
- :original_csv_file => 'original_contents.txt',
- :commit => 'Dry run'}
- expected_error = "Missing file in upload_csv: csv_upload-20461231-394"
- lambda{ post :import_csv, params }.should raise_error(expected_error)
- end
-
- it 'should assign the temporary filename to the view' do
- post :import_csv, { :csv_file => @file_object,
- :commit => 'Dry run'}
- temporary_filename = assigns[:temporary_csv_file]
- temporary_filename.should match(/csv_upload-#{Time.now.strftime("%Y%m%d")}-\d{1,5}/)
- end
+ it 'should raise an error if the temp file does not exist' do
+ temp_name = "csv_upload-20461231-394"
+ params = { :temporary_csv_file => temp_name,
+ :original_csv_file => 'original_contents.txt',
+ :commit => 'Dry run'}
+ expected_error = "Missing file in upload_csv: csv_upload-20461231-394"
+ lambda{ post :import_csv, params }.should raise_error(expected_error)
+ end
+ it 'should assign the temporary filename to the view' do
+ post :import_csv, { :csv_file => @file_object,
+ :commit => 'Dry run'}
+ temporary_filename = assigns[:temporary_csv_file]
+ temporary_filename.should match(/csv_upload-#{Time.now.strftime("%Y%m%d")}-\d{1,5}/)
end
end
@@ -154,7 +372,7 @@ end
describe AdminPublicBodyController, "when administering public bodies and paying attention to authentication" do
- integrate_views
+ render_views
before do
config = MySociety::Config.load_default()
@@ -215,6 +433,19 @@ describe AdminPublicBodyController, "when administering public bodies and paying
PublicBody.count.should == n - 1
end
+ it "doesn't let people with good emergency account credentials log in if the emergency user is disabled" do
+ setup_emergency_credentials('biz', 'fuz')
+ AlaveteliConfiguration.stub!(:disable_emergency_user).and_return(true)
+ n = PublicBody.count
+ basic_auth_login(@request, "biz", "fuz")
+ post :show, { :id => public_bodies(:humpadink_public_body).id, :emergency => 1}
+ session[:using_admin].should == nil
+ n = PublicBody.count
+ post :destroy, { :id => public_bodies(:forlorn_public_body).id }
+ session[:using_admin].should == nil
+ PublicBody.count.should == n
+ end
+
it "allows superusers to do stuff" do
session[:user_id] = users(:admin_user).id
@request.env["HTTP_AUTHORIZATION"] = ""
@@ -262,112 +493,3 @@ describe AdminPublicBodyController, "when administering public bodies and paying
end
end
-describe AdminPublicBodyController, "when administering public bodies with i18n" do
- integrate_views
-
- it "shows the index page" do
- get :index
- end
-
- it "searches for 'humpa'" do
- get :index, {:query => "humpa", :locale => "es"}
- assigns[:public_bodies].should == [ public_bodies(:humpadink_public_body) ]
- end
-
- it "shows a public body" do
- get :show, {:id => 2, :locale => "es" }
- end
-
- it "edits a public body" do
- get :edit, {:id => 3, :locale => :en}
-
- # When editing a body, the controller returns all available translations
- assigns[:public_body].translation("es").name.should == 'El Department for Humpadinking'
- assigns[:public_body].name.should == 'Department for Humpadinking'
- response.should render_template('edit')
- end
-
- it "saves edits to a public body" do
- PublicBody.with_locale(:es) do
- pb = PublicBody.find(id=3)
- pb.name.should == "El Department for Humpadinking"
- post :update, {
- :id => 3,
- :public_body => {
- :name => "Department for Humpadinking",
- :short_name => "",
- :tag_string => "some tags",
- :request_email => 'edited@localhost',
- :last_edit_comment => 'From test code',
- :translated_versions => {
- 3 => {:locale => "es", :name => "Renamed",:short_name => "", :request_email => 'edited@localhost'}
- }
- }
- }
- response.flash[:notice].should include('successful')
- end
-
- pb = PublicBody.find(public_bodies(:humpadink_public_body).id)
- PublicBody.with_locale(:es) do
- pb.name.should == "Renamed"
- end
- PublicBody.with_locale(:en) do
- pb.name.should == "Department for Humpadinking"
- end
- end
-
- it "destroy a public body" do
- n = PublicBody.count
- post :destroy, { :id => public_bodies(:forlorn_public_body).id }
- PublicBody.count.should == n - 1
- end
-
-end
-
-describe AdminPublicBodyController, "when creating public bodies with i18n" do
- integrate_views
-
- before do
- @old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
- end
-
- after do
- ActionController::Routing::Routes.filters = @old_filters
- end
-
- it "creates a new public body in one locale" do
- n = PublicBody.count
- post :create, { :public_body => { :name => "New Quango", :short_name => "", :tag_string => "blah", :request_email => 'newquango@localhost', :last_edit_comment => 'From test code' } }
- PublicBody.count.should == n + 1
-
- body = PublicBody.find_by_name("New Quango")
- response.should redirect_to(:controller=>'admin_public_body', :action=>'show', :id=>body.id)
- end
-
- it "creates a new public body with multiple locales" do
- n = PublicBody.count
- post :create, {
- :public_body => {
- :name => "New Quango", :short_name => "", :tag_string => "blah", :request_email => 'newquango@localhost', :last_edit_comment => 'From test code',
- :translated_versions => [{ :locale => "es", :name => "Mi Nuevo Quango", :short_name => "", :request_email => 'newquango@localhost' }]
- }
- }
- PublicBody.count.should == n + 1
-
- body = PublicBody.find_by_name("New Quango")
- body.translations.map {|t| t.locale.to_s}.sort.should == ["en", "es"]
- PublicBody.with_locale(:en) do
- body.name.should == "New Quango"
- body.url_name.should == "new_quango"
- body.first_letter.should == "N"
- end
- PublicBody.with_locale(:es) do
- body.name.should == "Mi Nuevo Quango"
- body.url_name.should == "mi_nuevo_quango"
- body.first_letter.should == "M"
- end
-
- response.should redirect_to(:controller=>'admin_public_body', :action=>'show', :id=>body.id)
- end
-end
diff --git a/spec/controllers/admin_request_controller_spec.rb b/spec/controllers/admin_request_controller_spec.rb
index 8a3934685..63b219c88 100644
--- a/spec/controllers/admin_request_controller_spec.rb
+++ b/spec/controllers/admin_request_controller_spec.rb
@@ -1,16 +1,11 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminRequestController, "when administering requests" do
- integrate_views
+ render_views
before { basic_auth_login @request }
before(:each) do
load_raw_emails_data
- @old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
- end
- after do
- ActionController::Routing::Routes.filters = @old_filters
end
it "shows the index/list page" do
@@ -39,7 +34,7 @@ describe AdminRequestController, "when administering requests" do
:awaiting_description => false,
:allow_new_responses_from => 'anybody',
:handle_rejected_responses => 'bounce' } }
- response.flash[:notice].should include('successful')
+ request.flash[:notice].should include('successful')
ir = InfoRequest.find(info_requests(:fancy_dog_request).id)
ir.title.should == "Renamed"
end
@@ -57,18 +52,6 @@ describe AdminRequestController, "when administering requests" do
end
- it "edits an outgoing message" do
- get :edit_outgoing, :id => outgoing_messages(:useless_outgoing_message)
- end
-
- it "saves edits to an outgoing_message" do
- outgoing_messages(:useless_outgoing_message).body.should include("fancy dog")
- post :update_outgoing, { :id => outgoing_messages(:useless_outgoing_message), :outgoing_message => { :body => "Why do you have such a delicious cat?" } }
- response.flash[:notice].should include('successful')
- ir = OutgoingMessage.find(outgoing_messages(:useless_outgoing_message).id)
- ir.body.should include("delicious cat")
- end
-
describe 'when fully destroying a request' do
it 'expires the file cache for that request' do
@@ -82,15 +65,10 @@ describe AdminRequestController, "when administering requests" do
end
describe AdminRequestController, "when administering the holding pen" do
- integrate_views
+ render_views
before(:each) do
basic_auth_login @request
load_raw_emails_data
- @old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
- end
- after do
- ActionController::Routing::Routes.filters = @old_filters
end
it "shows a rejection reason for an incoming message from an invalid address" do
@@ -99,57 +77,8 @@ describe AdminRequestController, "when administering the holding pen" do
ir.handle_rejected_responses = 'holding_pen'
ir.save!
receive_incoming_mail('incoming-request-plain.email', ir.incoming_email, "frob@nowhere.com")
- get :show_raw_email, :id => InfoRequest.holding_pen_request.get_last_response.raw_email.id
- response.should have_text(/Only the authority can reply to this request/)
- end
-
- it "allows redelivery even to a closed request" do
- ir = info_requests(:fancy_dog_request)
- ir.allow_new_responses_from = 'nobody'
- ir.handle_rejected_responses = 'holding_pen'
- ir.save!
- InfoRequest.holding_pen_request.incoming_messages.length.should == 0
- ir.incoming_messages.length.should == 1
- receive_incoming_mail('incoming-request-plain.email', ir.incoming_email, "frob@nowhere.com")
- InfoRequest.holding_pen_request.incoming_messages.length.should == 1
- new_im = InfoRequest.holding_pen_request.incoming_messages[0]
- ir.incoming_messages.length.should == 1
- post :redeliver_incoming, :redeliver_incoming_message_id => new_im.id, :url_title => ir.url_title
- ir = InfoRequest.find_by_url_title(ir.url_title)
- ir.incoming_messages.length.should == 2
- response.should redirect_to(:controller=>'admin_request', :action=>'show', :id=>101)
- InfoRequest.holding_pen_request.incoming_messages.length.should == 0
- end
-
- it "allows redelivery to more than one request" do
- ir1 = info_requests(:fancy_dog_request)
- ir1.allow_new_responses_from = 'nobody'
- ir1.handle_rejected_responses = 'holding_pen'
- ir1.save!
- ir1.incoming_messages.length.should == 1
- ir2 = info_requests(:another_boring_request)
- ir2.incoming_messages.length.should == 1
-
- receive_incoming_mail('incoming-request-plain.email', ir1.incoming_email, "frob@nowhere.com")
- InfoRequest.holding_pen_request.incoming_messages.length.should == 1
-
- new_im = InfoRequest.holding_pen_request.incoming_messages[0]
- post :redeliver_incoming, :redeliver_incoming_message_id => new_im.id, :url_title => "#{ir1.url_title},#{ir2.url_title}"
- ir1.reload
- ir1.incoming_messages.length.should == 2
- ir2.reload
- ir2.incoming_messages.length.should == 2
- response.should redirect_to(:controller=>'admin_request', :action=>'show', :id=>ir2.id)
- InfoRequest.holding_pen_request.incoming_messages.length.should == 0
- end
-
- it 'expires the file cache for the previous request' do
- current_info_request = info_requests(:fancy_dog_request)
- destination_info_request = info_requests(:naughty_chicken_request)
- incoming_message = incoming_messages(:useless_incoming_message)
- @controller.should_receive(:expire_for_request).with(current_info_request)
- post :redeliver_incoming, :redeliver_incoming_message_id => incoming_message.id,
- :url_title => destination_info_request.url_title
+ get :show_raw_email, :id => InfoRequest.holding_pen_request.get_last_public_response.raw_email.id
+ response.should contain "Only the authority can reply to this request"
end
it "guesses a misdirected request" do
@@ -159,41 +88,16 @@ describe AdminRequestController, "when administering the holding pen" do
ir.save!
mail_to = "request-#{ir.id}-asdfg@example.com"
receive_incoming_mail('incoming-request-plain.email', mail_to)
- interesting_email = InfoRequest.holding_pen_request.get_last_response.raw_email.id
+ interesting_email = InfoRequest.holding_pen_request.get_last_public_response.raw_email.id
# now we add another message to the queue, which we're not interested in
receive_incoming_mail('incoming-request-plain.email', ir.incoming_email, "")
InfoRequest.holding_pen_request.incoming_messages.length.should == 2
get :show_raw_email, :id => interesting_email
- response.should have_text(/Could not identify the request/)
+ response.should contain "Could not identify the request"
assigns[:info_requests][0].should == ir
end
- describe 'when destroying an incoming message' do
-
- before do
- @im = incoming_messages(:useless_incoming_message)
- @controller.stub!(:expire_for_request)
- end
-
- it "destroys the raw email file" do
- raw_email = @im.raw_email.filepath
- assert_equal File.exists?(raw_email), true
- post :destroy_incoming, :incoming_message_id => @im.id
- assert_equal File.exists?(raw_email), false
- end
-
- it 'asks the incoming message to fully destroy itself' do
- IncomingMessage.stub!(:find).and_return(@im)
- @im.should_receive(:fully_destroy)
- post :destroy_incoming, :incoming_message_id => @im.id
- end
- it 'expires the file cache for the associated info_request' do
- @controller.should_receive(:expire_for_request).with(@im.info_request)
- post :destroy_incoming, :incoming_message_id => @im.id
- end
-
- end
it "shows a suitable default 'your email has been hidden' message" do
ir = info_requests(:fancy_dog_request)
@@ -260,13 +164,13 @@ describe AdminRequestController, "when administering the holding pen" do
end
it 'should not send a notification email' do
- ContactMailer.should_not_receive(:deliver_from_admin_message)
+ ContactMailer.should_not_receive(:from_admin_message)
make_request
end
it 'should add a notice to the flash saying that the request has been hidden' do
make_request
- response.flash[:notice].should == "This external request has been hidden"
+ request.flash[:notice].should == "This external request has been hidden"
end
it 'should expire the file cache for the request' do
diff --git a/spec/controllers/admin_track_controller_spec.rb b/spec/controllers/admin_track_controller_spec.rb
index 728c79f1f..f2de6c0d3 100644
--- a/spec/controllers/admin_track_controller_spec.rb
+++ b/spec/controllers/admin_track_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminTrackController, "when administering tracks" do
- integrate_views
+ render_views
it "shows the list page" do
get :list
diff --git a/spec/controllers/admin_user_controller_spec.rb b/spec/controllers/admin_user_controller_spec.rb
index cf3665c9f..99894a414 100644
--- a/spec/controllers/admin_user_controller_spec.rb
+++ b/spec/controllers/admin_user_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminUserController, "when administering users" do
- integrate_views
+ render_views
it "shows the index/list page" do
get :index
@@ -15,13 +15,32 @@ describe AdminUserController, "when administering users" do
it "shows a user" do
get :show, :id => users(:bob_smith_user)
end
-
+
it "logs in as another user" do
get :login_as, :id => users(:bob_smith_user).id
post_redirect = PostRedirect.get_last_post_redirect
response.should redirect_to(:controller => 'user', :action => 'confirm', :email_token => post_redirect.email_token)
end
-
+
# See also "allows an admin to log in as another user" in spec/integration/admin_spec.rb
end
+describe AdminUserController, "when updating a user" do
+
+ it "saves a change to 'can_make_batch_requests'" do
+ user = FactoryGirl.create(:user)
+ user.can_make_batch_requests?.should be_false
+ post :update, {:id => user.id, :admin_user => {:can_make_batch_requests => '1',
+ :name => user.name,
+ :email => user.email,
+ :admin_level => user.admin_level,
+ :ban_text => user.ban_text,
+ :about_me => user.about_me,
+ :no_limit => user.no_limit}}
+ flash[:notice].should == 'User successfully updated.'
+ response.should be_redirect
+ user = User.find(user.id)
+ user.can_make_batch_requests?.should be_true
+ end
+
+end
diff --git a/spec/controllers/api_controller_spec.rb b/spec/controllers/api_controller_spec.rb
index 1c320f85c..6b02bd5b4 100644
--- a/spec/controllers/api_controller_spec.rb
+++ b/spec/controllers/api_controller_spec.rb
@@ -1,18 +1,6 @@
# coding: utf-8
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-def normalise_whitespace(s)
- s = s.gsub(/\A\s+|\s+\Z/, "")
- s = s.gsub(/\s+/, " ")
- return s
-end
-
-Spec::Matchers.define :be_equal_modulo_whitespace_to do |expected|
- match do |actual|
- normalise_whitespace(actual) == normalise_whitespace(expected)
- end
-end
-
describe ApiController, "when using the API" do
describe 'checking API keys' do
@@ -83,6 +71,9 @@ describe ApiController, "when using the API" do
new_request.last_event_forming_initial_request.outgoing_message.body.should == request_data["body"].strip
new_request.public_body_id.should == public_bodies(:geraldine_public_body).id
+ new_request.info_request_events.size.should == 1
+ new_request.info_request_events[0].event_type.should == 'sent'
+ new_request.info_request_events[0].calculated_state.should == 'waiting_response'
end
def _create_request
@@ -173,7 +164,7 @@ describe ApiController, "when using the API" do
"body" => "xxx"
}.to_json
- response.status.should == "500 Internal Server Error"
+ response.status.should == 500
ActiveSupport::JSON.decode(response.body)["errors"].should == [
"Request #{request_id} cannot be updated using the API"]
@@ -195,7 +186,7 @@ describe ApiController, "when using the API" do
"body" => "xxx"
}.to_json
- response.status.should == "500 Internal Server Error"
+ response.status.should == 500
ActiveSupport::JSON.decode(response.body)["errors"].should == [
"You do not own request #{request_id}"]
@@ -213,12 +204,12 @@ describe ApiController, "when using the API" do
"body" => "Are you joking, or are you serious?"
}.to_json,
:attachments => [
- fixture_file_upload("files/tfl.pdf")
+ fixture_file_upload("/files/tfl.pdf")
]
# Make sure it worked
- response.status.to_i.should == 500
+ response.status.should == 500
errors = ActiveSupport::JSON.decode(response.body)["errors"]
errors.should == ["You cannot attach files to messages in the 'request' direction"]
end
@@ -242,7 +233,7 @@ describe ApiController, "when using the API" do
"body" => response_body
}.to_json,
:attachments => [
- fixture_file_upload("files/tfl.pdf")
+ fixture_file_upload("/files/tfl.pdf")
]
# And make sure it worked
@@ -259,7 +250,7 @@ describe ApiController, "when using the API" do
attachments.size.should == 1
attachment = attachments[0]
attachment.filename.should == "tfl.pdf"
- attachment.body.should == load_file_fixture("tfl.pdf", as_binary=true)
+ attachment.body.should == load_file_fixture("tfl.pdf")
end
it "should show information about a request" do
@@ -279,6 +270,18 @@ describe ApiController, "when using the API" do
# check, which does not really test anything at all.
end
+ it 'should show information about an external request' do
+ info_request = info_requests(:external_request)
+ get :show_request,
+ :k => public_bodies(:geraldine_public_body).api_key,
+ :id => info_request.id
+
+ response.should be_success
+ assigns[:request].id.should == info_request.id
+ r = ActiveSupport::JSON.decode(response.body)
+ r["title"].should == info_request.title
+ end
+
it "should show an Atom feed of new request events" do
get :body_request_events,
:id => public_bodies(:geraldine_public_body).id,
@@ -286,7 +289,7 @@ describe ApiController, "when using the API" do
:feed_type => "atom"
response.should be_success
- response.should render_template("api/request_events.atom")
+ response.should render_template("api/request_events")
assigns[:events].size.should > 0
assigns[:events].each do |event|
event.info_request.public_body.should == public_bodies(:geraldine_public_body)
@@ -341,7 +344,7 @@ describe ApiController, "when using the API" do
:feed_type => "atom"
response.should be_success
- response.should render_template("api/request_events.atom")
+ response.should render_template("api/request_events")
assigns[:events].size.should > 0
assigns[:events].each do |event|
event.created_at.should >= Date.new(2010, 1, 1)
@@ -360,7 +363,7 @@ describe ApiController, "when using the API" do
"sent_at" => sent_at,
"body" => response_body
}.to_json
- response.status.should == "404 Not Found"
+ response.status.should == 404
ActiveSupport::JSON.decode(response.body)["errors"].should == ["Could not find request 123459876"]
end
@@ -376,7 +379,7 @@ describe ApiController, "when using the API" do
"sent_at" => sent_at,
"body" => response_body
}.to_json
- response.status.should == "500 Internal Server Error"
+ response.status.should == 500
ActiveSupport::JSON.decode(response.body)["errors"].should == ["Request #{request_id} cannot be updated using the API"]
end
end
diff --git a/spec/controllers/comment_controller_spec.rb b/spec/controllers/comment_controller_spec.rb
index 4a7acee23..c03615ce2 100644
--- a/spec/controllers/comment_controller_spec.rb
+++ b/spec/controllers/comment_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe CommentController, "when commenting on a request" do
- integrate_views
+ render_views
it "should give an error and render 'new' template when body text is just some whitespace" do
post :new, :url_title => info_requests(:naughty_chicken_request).url_title,
diff --git a/spec/controllers/general_controller_spec.rb b/spec/controllers/general_controller_spec.rb
index 642ed0e05..7590a5b42 100644
--- a/spec/controllers/general_controller_spec.rb
+++ b/spec/controllers/general_controller_spec.rb
@@ -12,30 +12,52 @@ describe GeneralController, "when trying to show the blog" do
it "should fail silently if the blog is returning an error" do
FakeWeb.register_uri(:get, %r|.*|, :body => "Error", :status => ["500", "Error"])
get :blog
- response.status.should == "200 OK"
+ response.status.should == 200
assigns[:blog_items].count.should == 0
end
end
describe GeneralController, 'when getting the blog feed' do
+ before do
+ AlaveteliConfiguration.stub!(:blog_feed).and_return("http://blog.example.com")
+ # Don't call out to external url during tests
+ controller.stub!(:quietly_try_to_open).and_return('')
+ end
+
it 'should add a lang param correctly to a url with no querystring' do
- Configuration.stub!(:blog_feed).and_return("http://blog.example.com")
get :blog
assigns[:feed_url].should == "http://blog.example.com?lang=en"
end
it 'should add a lang param correctly to a url with an existing querystring' do
- Configuration.stub!(:blog_feed).and_return("http://blog.example.com?alt=rss")
+ AlaveteliConfiguration.stub!(:blog_feed).and_return("http://blog.example.com?alt=rss")
get :blog
assigns[:feed_url].should == "http://blog.example.com?alt=rss&lang=en"
end
+ it 'should parse an item from an example feed' do
+ controller.stub!(:quietly_try_to_open).and_return(load_file_fixture("blog_feed.atom"))
+ get :blog
+ assigns[:blog_items].count.should == 1
+ end
+
+ context 'if no feed is configured' do
+
+ before do
+ AlaveteliConfiguration.stub!(:blog_feed).and_return('')
+ end
+
+ it 'should raise an ActiveRecord::RecordNotFound error' do
+ lambda{ get :blog }.should raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
end
describe GeneralController, "when showing the frontpage" do
- integrate_views
+ render_views
before do
public_body = mock_model(PublicBody, :name => "Example Public Body",
@@ -58,15 +80,14 @@ describe GeneralController, "when showing the frontpage" do
it "should render the front page with default language" do
get :frontpage
- response.should have_tag('html[lang="en"]')
+ response.should have_selector('html[lang="en"]')
end
it "should render the front page with default language" do
- old_default_locale = I18n.default_locale
- I18n.default_locale = "es"
- get :frontpage
- response.should have_tag('html[lang="es"]')
- I18n.default_locale = old_default_locale
+ with_default_locale("es") do
+ get :frontpage
+ response.should have_selector('html[lang="es"]')
+ end
end
it "should render the front page with default language and ignore the browser setting" do
@@ -74,11 +95,10 @@ describe GeneralController, "when showing the frontpage" do
config['USE_DEFAULT_BROWSER_LANGUAGE'] = false
accept_language = "en-GB,en-US;q=0.8,en;q=0.6"
request.env['HTTP_ACCEPT_LANGUAGE'] = accept_language
- old_default_locale = I18n.default_locale
- I18n.default_locale = "es"
- get :frontpage
- response.should have_tag('html[lang="es"]')
- I18n.default_locale = old_default_locale
+ with_default_locale("es") do
+ get :frontpage
+ response.should have_selector('html[lang="es"]')
+ end
end
it "should render the front page with browser-selected language when there's no default set" do
@@ -87,7 +107,7 @@ describe GeneralController, "when showing the frontpage" do
accept_language = "es-ES,en-GB,en-US;q=0.8,en;q=0.6"
request.env['HTTP_ACCEPT_LANGUAGE'] = accept_language
get :frontpage
- response.should have_tag('html[lang="es"]')
+ response.should have_selector('html[lang="es"]')
request.env['HTTP_ACCEPT_LANGUAGE'] = nil
end
@@ -97,145 +117,21 @@ describe GeneralController, "when showing the frontpage" do
response.should be_success
end
- describe 'when there is more than one locale' do
-
- describe 'when using the default locale' do
-
- before do
- @default_lang_home_link = /href=".*\/en\//
- @other_lang_home_link = /href=".*\/es\//
- @old_include_default_locale_in_urls = Configuration::include_default_locale_in_urls
- end
-
- def set_default_locale_in_urls(value)
- Configuration.stub!(:include_default_locale_in_urls).and_return(value)
- load Rails.root.join("config/initializers/fast_gettext.rb")
- end
-
- describe 'when the config value INCLUDE_DEFAULT_LOCALE_IN_URLS is false' do
-
- before do
- set_default_locale_in_urls(false)
- end
-
- it 'should generate URLs without a locale prepended' do
- get :frontpage
- response.should_not have_text(@default_lang_home_link)
- end
-
- it 'should render the front page in the default language when no locale param
- is present and the session locale is not the default' do
- get(:frontpage, {}, {:locale => 'es'})
- response.should_not have_text(@other_lang_home_link)
- end
- end
-
- it 'should generate URLs with a locale prepended when the config value
- INCLUDE_DEFAULT_LOCALE_IN_URLS is true' do
- set_default_locale_in_urls(true)
- get :frontpage
- response.should have_text(@default_lang_home_link)
- end
-
- after do
- set_default_locale_in_urls(@old_include_default_locale_in_urls)
- end
-
- end
- end
-
-
- describe "when using different locale settings" do
- home_link_regex = /href=".*\/en\//
-
- it "should generate URLs with a locale prepended when there's more than one locale set" do
- get :frontpage
- response.should have_text(home_link_regex)
- end
+ describe 'when using locales' do
it "should use our test PO files rather than the application one" do
- I18n.default_locale = :es
- get :frontpage
- response.should have_text(/XOXO/)
- I18n.default_locale = :en
- end
-
- it "should generate URLs that include the locale when using one that includes an underscore" do
- I18n.default_locale = :"en_GB"
- get :frontpage
- response.should have_text(/href="\/en_GB\//)
- I18n.default_locale = :en
- end
-
- it "should fall back to the language if the territory is unknown" do
- I18n.default_locale = :"en_US"
- get :frontpage
- response.should have_text(/href="\/en\//)
- response.should_not have_text(/href="\/en_US\//)
- I18n.default_locale = :en
- end
-
- it "should generate URLs without a locale prepended when there's only one locale set" do
- old_fgt_available_locales = FastGettext.default_available_locales
- old_i18n_available_locales = I18n.available_locales
- FastGettext.default_available_locales = I18n.available_locales = ['en']
-
- get :frontpage
- response.should_not have_text(home_link_regex)
-
- FastGettext.default_available_locales = old_fgt_available_locales
- I18n.available_locales = old_i18n_available_locales
- end
-
- end
-end
-describe GeneralController, "when showing the front page with fixture data" do
-
- describe 'when constructing the list of recent requests' do
-
- before(:each) do
- get_fixtures_xapian_index
+ get :frontpage, :locale => 'es'
+ response.body.should match /XOXO/
end
- describe 'when there are fewer than five successful requests' do
-
- it 'should list the most recently sent and successful requests by the creation date of the
- request event' do
- # Make sure the newest response is listed first even if a request
- # with an older response has a newer comment or was reclassified more recently:
- # https://github.com/mysociety/alaveteli/issues/370
- #
- # This is a deliberate behaviour change, in that the
- # previous behaviour (showing more-recently-reclassified
- # requests first) was intentional.
- get :frontpage
-
- request_events = assigns[:request_events]
- previous = nil
- request_events.each do |event|
- if previous
- previous.created_at.should be >= event.created_at
- end
- ['sent', 'response'].include?(event.event_type).should be_true
- if event.event_type == 'response'
- ['successful', 'partially_successful'].include?(event.calculated_state).should be_true
- end
- previous = event
- end
- end
- end
-
- it 'should coalesce duplicate requests' do
- get :frontpage
- assigns[:request_events].map(&:info_request).select{|x|x.url_title =~ /^spam/}.length.should == 1
- end
end
end
+
describe GeneralController, 'when using xapian search' do
- integrate_views
+ render_views
# rebuild xapian index after fixtures loaded
before(:each) do
@@ -249,7 +145,7 @@ describe GeneralController, 'when using xapian search' do
end
it "should find info request when searching for '\"fancy dog\"'" do
- get :search, :combined => ['"fancy dog"']
+ get :search, :combined => '"fancy dog"'
response.should render_template('search')
assigns[:xapian_requests].matches_estimated.should == 1
assigns[:xapian_requests].results.size.should == 1
@@ -259,7 +155,7 @@ describe GeneralController, 'when using xapian search' do
end
it "should find public body and incoming message when searching for 'geraldine quango'" do
- get :search, :combined => ['geraldine quango']
+ get :search, :combined => 'geraldine quango'
response.should render_template('search')
assigns[:xapian_requests].matches_estimated.should == 1
@@ -272,7 +168,7 @@ describe GeneralController, 'when using xapian search' do
end
it "should filter results based on end of URL being 'all'" do
- get :search, :combined => ['"bob"', "all"]
+ get :search, :combined => "bob/all"
assigns[:xapian_requests].results.map{|x| x[:model]}.should =~ [
info_request_events(:useless_outgoing_message_event),
info_request_events(:silly_outgoing_message_event),
@@ -284,14 +180,25 @@ describe GeneralController, 'when using xapian search' do
end
it "should filter results based on end of URL being 'users'" do
- get :search, :combined => ['"bob"', "users"]
+ get :search, :combined => "bob/users"
assigns[:xapian_requests].should == nil
assigns[:xapian_users].results.map{|x| x[:model]}.should == [users(:bob_smith_user)]
assigns[:xapian_bodies].should == nil
end
+ it 'should highlight words for a user-only request' do
+ get :search, :combined => "bob/users"
+ assigns[:highlight_words].should == ['bob']
+ end
+
+ it 'should show spelling corrections for a user-only request' do
+ get :search, :combined => "rob/users"
+ assigns[:spelling_correction].should == 'bob'
+ response.body.should include('did_you_mean')
+ end
+
it "should filter results based on end of URL being 'requests'" do
- get :search, :combined => ['"bob"', "requests"]
+ get :search, :combined => "bob/requests"
assigns[:xapian_requests].results.map{|x|x[:model]}.should =~ [
info_request_events(:useless_outgoing_message_event),
info_request_events(:silly_outgoing_message_event),
@@ -303,12 +210,17 @@ describe GeneralController, 'when using xapian search' do
end
it "should filter results based on end of URL being 'bodies'" do
- get :search, :combined => ['"quango"', "bodies"]
+ get :search, :combined => "quango/bodies"
assigns[:xapian_requests].should == nil
assigns[:xapian_users].should == nil
assigns[:xapian_bodies].results.map{|x|x[:model]}.should == [public_bodies(:geraldine_public_body)]
end
+ it 'should show "Browse all" link if there are no results for a search restricted to bodies' do
+ get :search, :combined => "noresultsshouldbefound/bodies"
+ response.body.should include('Browse all')
+ end
+
it "should show help when searching for nothing" do
get :search_redirect, :query => nil
response.should render_template('search')
@@ -317,7 +229,7 @@ describe GeneralController, 'when using xapian search' do
end
it "should not show unconfirmed users" do
- get :search, :combined => ["unconfirmed", "users"]
+ get :search, :combined => "unconfirmed/users"
response.should render_template('search')
assigns[:xapian_users].results.map{|x|x[:model]}.should == []
end
@@ -328,15 +240,14 @@ describe GeneralController, 'when using xapian search' do
u.save!
update_xapian_index
- get :search, :combined => ["unconfirmed", "users"]
+ get :search, :combined => "unconfirmed/users"
response.should render_template('search')
assigns[:xapian_users].results.map{|x|x[:model]}.should == [u]
end
it "should show tracking links for requests-only searches" do
- get :search, :combined => ['"bob"', "requests"]
+ get :search, :combined => "bob/requests"
response.body.should include('Track this search')
end
end
-
diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb
index 28fd08c80..cc024f840 100644
--- a/spec/controllers/help_controller_spec.rb
+++ b/spec/controllers/help_controller_spec.rb
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe HelpController, "when using help" do
- integrate_views
-
+ render_views
+
it "shows the about page" do
get :about
end
@@ -12,7 +13,7 @@ describe HelpController, "when using help" do
end
it "sends a contact message" do
- post :contact, { :contact => {
+ post :contact, { :contact => {
:name => "Vinny Vanilli",
:email => "vinny@localhost",
:subject => "Why do I have such an ace name?",
@@ -27,5 +28,21 @@ describe HelpController, "when using help" do
deliveries.clear
end
+ describe 'when requesting a page in a supported locale ' do
+
+ before do
+ # Prepend our fixture templates
+ fixture_theme_path = File.join(Rails.root, 'spec', 'fixtures', 'theme_views', 'theme_one')
+ controller.prepend_view_path fixture_theme_path
+ end
+
+ it 'should render the locale-specific template if available' do
+ get :contact, {:locale => 'es'}
+ response.body.should match('contáctenos theme one')
+ end
+
+ end
+
+
end
diff --git a/spec/controllers/info_request_batch_controller_spec.rb b/spec/controllers/info_request_batch_controller_spec.rb
new file mode 100644
index 000000000..d08f02e10
--- /dev/null
+++ b/spec/controllers/info_request_batch_controller_spec.rb
@@ -0,0 +1,53 @@
+# coding: utf-8
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe InfoRequestBatchController, "when showing a request" do
+
+ before do
+ @first_public_body = FactoryGirl.create(:public_body)
+ @second_public_body = FactoryGirl.create(:public_body)
+ @info_request_batch = FactoryGirl.create(:info_request_batch, :title => 'Matched title',
+ :body => 'Matched body',
+ :public_bodies => [@first_public_body,
+ @second_public_body])
+ @first_request = FactoryGirl.create(:info_request, :info_request_batch => @info_request_batch,
+ :public_body => @first_public_body)
+ @second_request = FactoryGirl.create(:info_request, :info_request_batch => @info_request_batch,
+ :public_body => @second_public_body)
+ @default_params = {:id => @info_request_batch.id}
+ end
+
+ def make_request(params=@default_params)
+ get :show, params
+ end
+
+ it 'should be successful' do
+ make_request
+ response.should be_success
+ end
+
+ it 'should assign an info_request_batch to the view' do
+ make_request
+ assigns[:info_request_batch].should == @info_request_batch
+ end
+
+ context 'when the batch has not been sent' do
+
+ it 'should assign public_bodies to the view' do
+ make_request
+ assigns[:public_bodies].should == [@first_public_body, @second_public_body]
+ end
+ end
+
+ context 'when the batch has been sent' do
+
+ it 'should assign info_requests to the view' do
+ @info_request_batch.sent_at = Time.now
+ @info_request_batch.save!
+ make_request
+ assigns[:info_requests].sort.should == [@first_request, @second_request]
+ end
+
+ end
+
+end
diff --git a/spec/controllers/public_body_change_requests_controller_spec.rb b/spec/controllers/public_body_change_requests_controller_spec.rb
new file mode 100644
index 000000000..7b878b893
--- /dev/null
+++ b/spec/controllers/public_body_change_requests_controller_spec.rb
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe PublicBodyChangeRequestsController, "making a new change request" do
+
+ it "should show the form" do
+ get :new
+ response.should render_template("new")
+ end
+
+end
+
+describe PublicBodyChangeRequestsController, "creating a change request" do
+
+ context 'when handling a request for a new authority' do
+
+ before do
+ @email = "test@example.com"
+ name = "Test User"
+ @change_request_params = {:user_email => @email,
+ :user_name => name,
+ :public_body_name => 'New Body',
+ :public_body_email => 'new_body@example.com',
+ :notes => 'Please',
+ :source => 'http://www.example.com'}
+ end
+
+ it "should send an email to the site contact address" do
+ post :create, {:public_body_change_request => @change_request_params}
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.subject.should =~ /Add authority - New Body/
+ mail.from.should include(@email)
+ mail.to.should include('postmaster@localhost')
+ mail.body.should include('new_body@example.com')
+ mail.body.should include('New Body')
+ mail.body.should include("Please")
+ mail.body.should include('http://test.host/admin/body/new?change_request_id=')
+ mail.body.should include('http://test.host/admin/change_request/edit/')
+ end
+
+ it 'should show a notice' do
+ post :create, {:public_body_change_request => @change_request_params}
+ expected_text = "Your request to add an authority has been sent. Thank you for getting in touch! We'll get back to you soon."
+ flash[:notice].should == expected_text
+ end
+
+ it 'should redirect to the frontpage' do
+ post :create, {:public_body_change_request => @change_request_params}
+ response.should redirect_to frontpage_url
+ end
+
+ end
+
+ context 'when handling a request for an update to an existing authority' do
+
+ before do
+ @email = "test@example.com"
+ name = "Test User"
+ @public_body = FactoryGirl.create(:public_body)
+ @change_request_params = {:user_email => @email,
+ :user_name => name,
+ :public_body_id => @public_body.id,
+ :public_body_email => 'new_body@example.com',
+ :notes => 'Please',
+ :source => 'http://www.example.com'}
+ end
+
+ it 'should send an email to the site contact address' do
+ post :create, {:public_body_change_request => @change_request_params}
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.subject.should =~ /Update email address - #{@public_body.name}/
+ mail.from.should include(@email)
+ mail.to.should include('postmaster@localhost')
+ mail.body.should include('new_body@example.com')
+ mail.body.should include(@public_body.name)
+ mail.body.should include("Please")
+ mail.body.should include("http://test.host/admin/body/edit/#{@public_body.id}?change_request_id=")
+ mail.body.should include('http://test.host/admin/change_request/edit/')
+ end
+
+ it 'should show a notice' do
+ post :create, {:public_body_change_request => @change_request_params}
+ expected_text = "Your request to update the address for #{@public_body.name} has been sent. Thank you for getting in touch! We'll get back to you soon."
+ flash[:notice].should == expected_text
+ end
+
+ it 'should redirect to the frontpage' do
+ post :create, {:public_body_change_request => @change_request_params}
+ response.should redirect_to frontpage_url
+ end
+
+ end
+
+
+end
diff --git a/spec/controllers/public_body_controller_spec.rb b/spec/controllers/public_body_controller_spec.rb
index 29ece18cb..63989baaa 100644
--- a/spec/controllers/public_body_controller_spec.rb
+++ b/spec/controllers/public_body_controller_spec.rb
@@ -1,8 +1,10 @@
# coding: utf-8
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+require 'nokogiri'
+
describe PublicBodyController, "when showing a body" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -43,28 +45,20 @@ describe PublicBodyController, "when showing a body" do
:conditions => ["public_body_id = ?", public_bodies(:humpadink_public_body).id])
end
- it "should assign the body using different locale from that used for url_name" do
- PublicBody.with_locale(:es) do
- get :show, {:url_name => "dfh", :view => 'all'}
- assigns[:public_body].notes.should == "Baguette"
- end
+ it "should display the body using same locale as that used in url_name" do
+ get :show, {:url_name => "edfh", :view => 'all', :locale => "es"}
+ response.should contain("Baguette")
end
- it "should assign the body using same locale as that used in url_name" do
- PublicBody.with_locale(:es) do
- get :show, {:url_name => "edfh", :view => 'all'}
- assigns[:public_body].notes.should == "Baguette"
- end
+ it 'should show public body names in the selected locale language if present for a locale with underscores' do
+ AlaveteliLocalization.set_locales('he_IL en', 'en')
+ get :show, {:url_name => 'dfh', :view => 'all', :locale => 'he_IL'}
+ response.should contain('Hebrew Humpadinking')
end
it "should redirect use to the relevant locale even when url_name is for a different locale" do
- old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
-
get :show, {:url_name => "edfh", :view => 'all'}
response.should redirect_to "http://test.host/body/dfh"
-
- ActionController::Routing::Routes.filters = old_filters
end
it "should redirect to newest name if you use historic name of public body in URL" do
@@ -79,37 +73,108 @@ describe PublicBodyController, "when showing a body" do
end
describe PublicBodyController, "when listing bodies" do
- integrate_views
+ render_views
it "should be successful" do
get :list
response.should be_success
end
- it "should list all bodies from default locale, even when there are no translations for selected locale" do
- PublicBody.with_locale(:en) do
- @english_only = PublicBody.new(:name => 'English only',
- :short_name => 'EO',
- :request_email => 'english@flourish.org',
- :last_edit_editor => 'test',
- :last_edit_comment => '')
- @english_only.save
- end
- PublicBody.with_locale(:es) do
- get :list
- assigns[:public_bodies].include?(@english_only).should == true
+ def make_single_language_example(locale)
+ result = nil
+ with_default_locale(locale) do
+ I18n.with_locale(locale) do
+ case locale
+ when :en
+ result = PublicBody.new(:name => 'English only',
+ :short_name => 'EO')
+ when :es
+ result = PublicBody.new(:name => 'Español Solamente',
+ :short_name => 'ES')
+ else
+ raise StandardError.new "Unknown locale #{locale}"
+ end
+ result.request_email = "#{locale}@example.org"
+ result.last_edit_editor = 'test'
+ result.last_edit_comment = ''
+ result.save
+ end
end
+ result
+ end
+
+ it "with no fallback, should only return bodies from the current locale" do
+ @english_only = make_single_language_example :en
+ @spanish_only = make_single_language_example :es
+ get :list, {:locale => 'es'}
+ assigns[:public_bodies].include?(@english_only).should == false
+ assigns[:public_bodies].include?(@spanish_only).should == true
+ end
+
+ it "if fallback is requested, should list all bodies from default locale, even when there are no translations for selected locale" do
+ AlaveteliConfiguration.stub!(:public_body_list_fallback_to_default_locale).and_return(true)
+ @english_only = make_single_language_example :en
+ get :list, {:locale => 'es'}
+ assigns[:public_bodies].include?(@english_only).should == true
+ end
+
+ it 'if fallback is requested, should still list public bodies only with translations in the current locale' do
+ AlaveteliConfiguration.stub!(:public_body_list_fallback_to_default_locale).and_return(true)
+ @spanish_only = make_single_language_example :es
+ get :list, {:locale => 'es'}
+ assigns[:public_bodies].include?(@spanish_only).should == true
+ end
+
+ it "if fallback is requested, make sure that there are no duplicates listed" do
+ AlaveteliConfiguration.stub!(:public_body_list_fallback_to_default_locale).and_return(true)
+ get :list, {:locale => 'es'}
+ pb_ids = assigns[:public_bodies].map { |pb| pb.id }
+ unique_pb_ids = pb_ids.uniq
+ pb_ids.sort.should === unique_pb_ids.sort
+ end
+
+ it 'should show public body names in the selected locale language if present' do
+ get :list, {:locale => 'es'}
+ response.should contain('El Department for Humpadinking')
+ end
+
+ it 'should not show the internal admin authority' do
+ PublicBody.internal_admin_body
+ get :list, {:locale => 'en'}
+ response.should_not contain('Internal admin authority')
+ end
+
+ it 'should order on the translated name, even with the fallback' do
+ # The names of each public body is in:
+ # <span class="head"><a>Public Body Name</a></span>
+ # ... eo extract all of those, and check that they are ordered:
+ AlaveteliConfiguration.stub!(:public_body_list_fallback_to_default_locale).and_return(true)
+ get :list, {:locale => 'es'}
+ parsed = Nokogiri::HTML(response.body)
+ public_body_names = parsed.xpath '//span[@class="head"]/a/text()'
+ public_body_names = public_body_names.map { |pb| pb.to_s }
+ public_body_names.should == public_body_names.sort
+ end
+
+ it 'should show public body names in the selected locale language if present for a locale with underscores' do
+ AlaveteliLocalization.set_locales('he_IL en', 'en')
+ get :list, {:locale => 'he_IL'}
+ response.should contain('Hebrew Humpadinking')
end
+
it "should list bodies in alphabetical order" do
# Note that they are alphabetised by localised name
get :list
response.should render_template('list')
- assigns[:public_bodies].should == PublicBody.all(
- :conditions => "id <> #{PublicBody.internal_admin_body.id}",
- :order => "(select name from public_body_translations where public_body_id=public_bodies.id and locale='en')")
+ assigns[:public_bodies].should == [ public_bodies(:other_public_body),
+ public_bodies(:humpadink_public_body),
+ public_bodies(:forlorn_public_body),
+ public_bodies(:geraldine_public_body),
+ public_bodies(:sensible_walks_public_body),
+ public_bodies(:silly_walks_public_body) ]
assigns[:tag].should == "all"
assigns[:description].should == ""
end
@@ -125,13 +190,13 @@ describe PublicBodyController, "when listing bodies" do
end
it "should list bodies in alphabetical order with different locale" do
- I18n.default_locale = :es
- get :list
- response.should render_template('list')
- assigns[:public_bodies].should == [ public_bodies(:geraldine_public_body), public_bodies(:humpadink_public_body) ]
- assigns[:tag].should == "all"
- assigns[:description].should == ""
- I18n.default_locale = :en
+ with_default_locale(:es) do
+ get :list
+ response.should render_template('list')
+ assigns[:public_bodies].should == [ public_bodies(:geraldine_public_body), public_bodies(:humpadink_public_body) ]
+ assigns[:tag].should == "all"
+ assigns[:description].should == ""
+ end
end
it "should list a tagged thing on the appropriate list page, and others on the other page, and all still on the all page" do
@@ -147,11 +212,20 @@ describe PublicBodyController, "when listing bodies" do
get :list, :tag => "other"
response.should render_template('list')
- assigns[:public_bodies].should =~ PublicBody.all(:conditions => "id not in (#{public_bodies(:humpadink_public_body).id}, #{PublicBody.internal_admin_body.id})")
+ assigns[:public_bodies].should == [ public_bodies(:other_public_body),
+ public_bodies(:forlorn_public_body),
+ public_bodies(:geraldine_public_body),
+ public_bodies(:sensible_walks_public_body),
+ public_bodies(:silly_walks_public_body) ]
get :list
response.should render_template('list')
- assigns[:public_bodies].should =~ PublicBody.all(:conditions => "id <> #{PublicBody.internal_admin_body.id}")
+ assigns[:public_bodies].should == [ public_bodies(:other_public_body),
+ public_bodies(:humpadink_public_body),
+ public_bodies(:forlorn_public_body),
+ public_bodies(:geraldine_public_body),
+ public_bodies(:sensible_walks_public_body),
+ public_bodies(:silly_walks_public_body) ]
end
it "should list a machine tagged thing, should get it in both ways" do
@@ -171,8 +245,18 @@ describe PublicBodyController, "when listing bodies" do
response.should render_template('list')
assigns[:public_bodies].should == [ public_bodies(:humpadink_public_body) ]
assigns[:tag].should == "eats_cheese:stilton"
+ end
+ it 'should return a "406 Not Acceptable" code if asked for a json version of a list' do
+ get :list, :format => 'json'
+ response.code.should == '406'
+ end
+ it "should list authorities starting with a multibyte first letter" do
+ get :list, {:tag => "å", :show_locale => 'cs'}
+ response.should render_template('list')
+ assigns[:public_bodies].should == [ public_bodies(:accented_public_body) ]
+ assigns[:tag].should == "Å"
end
end
@@ -191,9 +275,145 @@ describe PublicBodyController, "when showing JSON version for API" do
end
+describe PublicBodyController, "when asked to export public bodies as CSV" do
+
+ it "should return a valid CSV file with the right number of rows" do
+ get :list_all_csv
+ all_data = CSV.parse response.body
+ all_data.length.should == 8
+ # Check that the header has the right number of columns:
+ all_data[0].length.should == 11
+ # And an actual line of data:
+ all_data[1].length.should == 11
+ end
+
+end
+
+describe PublicBodyController, "when showing public body statistics" do
+
+ it "should render the right template with the right data" do
+ config = MySociety::Config.load_default()
+ config['MINIMUM_REQUESTS_FOR_STATISTICS'] = 1
+ config['PUBLIC_BODY_STATISTICS_PAGE'] = true
+ get :statistics
+ response.should render_template('public_body/statistics')
+ # There are 5 different graphs we're creating at the moment.
+ assigns[:graph_list].length.should == 5
+ # The first is the only one with raw values, the rest are
+ # percentages with error bars:
+ assigns[:graph_list].each_with_index do |graph, index|
+ if index == 0
+ graph['errorbars'].should be_false
+ graph['x_values'].length.should == 4
+ graph['x_values'].should == [0, 1, 2, 3]
+ graph['y_values'].should == [1, 2, 2, 4]
+ else
+ graph['errorbars'].should be_true
+ # Just check the first one:
+ if index == 1
+ graph['x_values'].should == [0, 1, 2, 3]
+ graph['y_values'].should == [0, 50, 100, 100]
+ end
+ # Check that at least every confidence interval value is
+ # a Float (rather than NilClass, say):
+ graph['cis_below'].each { |v| v.should be_instance_of(Float) }
+ graph['cis_above'].each { |v| v.should be_instance_of(Float) }
+ end
+ end
+ end
+
+end
+
+describe PublicBodyController, "when converting data for graphing" do
+
+ before(:each) do
+ @raw_count_data = PublicBody.get_request_totals(n=3,
+ highest=true,
+ minimum_requests=1)
+ @percentages_data = PublicBody.get_request_percentages(
+ column='info_requests_successful_count',
+ n=3,
+ highest=false,
+ minimum_requests=1)
+ end
+
+ it "should not include the real public body model instance" do
+ to_draw = controller.simplify_stats_for_graphs(@raw_count_data,
+ column='blah_blah',
+ percentages=false,
+ {} )
+ to_draw['public_bodies'][0].class.should == Hash
+ to_draw['public_bodies'][0].has_key?('request_email').should be_false
+ end
+
+ it "should generate the expected id" do
+ to_draw = controller.simplify_stats_for_graphs(@raw_count_data,
+ column='blah_blah',
+ percentages=false,
+ {:highest => true} )
+ to_draw['id'].should == "blah_blah-highest"
+ to_draw = controller.simplify_stats_for_graphs(@raw_count_data,
+ column='blah_blah',
+ percentages=false,
+ {:highest => false} )
+ to_draw['id'].should == "blah_blah-lowest"
+ end
+
+ it "should have exactly the expected keys" do
+ to_draw = controller.simplify_stats_for_graphs(@raw_count_data,
+ column='blah_blah',
+ percentages=false,
+ {} )
+ to_draw.keys.sort.should == ["errorbars", "id", "public_bodies",
+ "title", "tooltips", "totals",
+ "x_axis", "x_ticks", "x_values",
+ "y_axis", "y_max", "y_values"]
+
+ to_draw = controller.simplify_stats_for_graphs(@percentages_data,
+ column='whatever',
+ percentages=true,
+ {})
+ to_draw.keys.sort.should == ["cis_above", "cis_below",
+ "errorbars", "id", "public_bodies",
+ "title", "tooltips", "totals",
+ "x_axis", "x_ticks", "x_values",
+ "y_axis", "y_max", "y_values"]
+ end
+
+ it "should have values of the expected class and length" do
+ [controller.simplify_stats_for_graphs(@raw_count_data,
+ column='blah_blah',
+ percentages=false,
+ {}),
+ controller.simplify_stats_for_graphs(@percentages_data,
+ column='whatever',
+ percentages=true,
+ {})].each do |to_draw|
+ per_pb_keys = ["cis_above", "cis_below", "public_bodies",
+ "tooltips", "totals", "x_ticks", "x_values",
+ "y_values"]
+ # These should be all be arrays with one element per public body:
+ per_pb_keys.each do |key|
+ if to_draw.has_key? key
+ to_draw[key].class.should == Array
+ to_draw[key].length.should eq(3), "for key #{key}"
+ end
+ end
+ # Just check that the rest aren't of class Array:
+ to_draw.keys.each do |key|
+ unless per_pb_keys.include? key
+ to_draw[key].class.should_not eq(Array), "for key #{key}"
+ end
+ end
+ end
+ end
+
+end
+
+
describe PublicBodyController, "when doing type ahead searches" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
diff --git a/spec/controllers/reports_controller_spec.rb b/spec/controllers/reports_controller_spec.rb
new file mode 100644
index 000000000..fa8c72eaa
--- /dev/null
+++ b/spec/controllers/reports_controller_spec.rb
@@ -0,0 +1,104 @@
+require 'spec_helper'
+
+describe ReportsController, "when reporting a request when not logged in" do
+ it "should only allow logged-in users to report requests" do
+ post :create, :request_id => info_requests(:badger_request).url_title, :reason => "my reason"
+
+ flash[:notice].should =~ /You need to be logged in/
+ response.should redirect_to show_request_path(:url_title => info_requests(:badger_request).url_title)
+ end
+end
+
+describe ReportsController, "when reporting a request (logged in)" do
+ render_views
+
+ before do
+ @user = users(:robin_user)
+ session[:user_id] = @user.id
+ end
+
+ it "should 404 for non-existent requests" do
+ lambda {
+ post :create, :request_id => "hjksfdhjk_louytu_qqxxx"
+ }.should raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it "should mark a request as having been reported" do
+ ir = info_requests(:badger_request)
+ title = ir.url_title
+ ir.attention_requested.should == false
+
+ post :create, :request_id => title, :reason => "my reason"
+ response.should redirect_to show_request_path(:url_title => title)
+
+ ir.reload
+ ir.attention_requested.should == true
+ ir.described_state.should == "attention_requested"
+ end
+
+ it "should pass on the reason and message" do
+ info_request = mock_model(InfoRequest, :url_title => "foo", :attention_requested= => nil, :save! => nil)
+ InfoRequest.should_receive(:find_by_url_title!).with("foo").and_return(info_request)
+ info_request.should_receive(:report!).with("Not valid request", "It's just not", @user)
+ post :create, :request_id => "foo", :reason => "Not valid request", :message => "It's just not"
+ end
+
+ it "should not allow a request to be reported twice" do
+ title = info_requests(:badger_request).url_title
+
+ post :create, :request_id => title, :reason => "my reason"
+ response.should redirect_to show_request_url(:url_title => title)
+
+ post :create, :request_id => title, :reason => "my reason"
+ response.should redirect_to show_request_url(:url_title => title)
+ flash[:notice].should =~ /has already been reported/
+ end
+
+ it "should send an email from the reporter to admins" do
+ ir = info_requests(:badger_request)
+ title = ir.url_title
+ post :create, :request_id => title, :reason => "my reason"
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.subject.should =~ /attention_requested/
+ mail.from.should include(@user.email)
+ mail.body.should include(@user.name)
+ end
+
+ it "should force the user to pick a reason" do
+ info_request = mock_model(InfoRequest, :report! => nil, :url_title => "foo",
+ :report_reasons => ["Not FOIish enough"])
+ InfoRequest.should_receive(:find_by_url_title!).with("foo").and_return(info_request)
+
+ post :create, :request_id => "foo", :reason => ""
+ response.should render_template("new")
+ flash[:error].should == "Please choose a reason"
+ end
+end
+
+describe ReportsController, "#new_report_request" do
+ let(:info_request) { mock_model(InfoRequest, :url_title => "foo") }
+ before :each do
+ InfoRequest.should_receive(:find_by_url_title!).with("foo").and_return(info_request)
+ end
+
+ context "not logged in" do
+ it "should require the user to be logged in" do
+ get :new, :request_id => "foo"
+ response.should_not render_template("new")
+ end
+ end
+
+ context "logged in" do
+ before :each do
+ session[:user_id] = users(:bob_smith_user).id
+ end
+ it "should show the form" do
+ get :new, :request_id => "foo"
+ response.should render_template("new")
+ end
+ end
+end
+
+
diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb
index 6f25b605a..1e7df4536 100644
--- a/spec/controllers/request_controller_spec.rb
+++ b/spec/controllers/request_controller_spec.rb
@@ -2,7 +2,6 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe RequestController, "when listing recent requests" do
-
before(:each) do
load_raw_emails_data
get_fixtures_xapian_index
@@ -18,92 +17,8 @@ describe RequestController, "when listing recent requests" do
response.should render_template('list')
end
- it "should filter requests" do
- get :list, :view => 'all'
- assigns[:list_results].map(&:info_request).should =~ InfoRequest.all
-
- # default sort order is the request with the most recently created event first
- assigns[:list_results].map(&:info_request).should == InfoRequest.all(
- :order => "(select max(info_request_events.created_at) from info_request_events where info_request_events.info_request_id = info_requests.id) DESC")
-
- get :list, :view => 'successful'
- assigns[:list_results].map(&:info_request).should =~ InfoRequest.all(
- :conditions => "id in (
- select info_request_id
- from info_request_events
- where not exists (
- select *
- from info_request_events later_events
- where later_events.created_at > info_request_events.created_at
- and later_events.info_request_id = info_request_events.info_request_id
- and later_events.described_state is not null
- )
- and info_request_events.described_state in ('successful', 'partially_successful')
- )")
- end
-
- it "should filter requests by date" do
- # The semantics of the search are that it finds any InfoRequest
- # that has any InfoRequestEvent created in the specified range
-
- get :list, :view => 'all', :request_date_before => '13/10/2007'
- assigns[:list_results].map(&:info_request).should =~ InfoRequest.all(
- :conditions => "id in (select info_request_id from info_request_events where created_at < '2007-10-13'::date)")
-
- get :list, :view => 'all', :request_date_after => '13/10/2007'
- assigns[:list_results].map(&:info_request).should =~ InfoRequest.all(
- :conditions => "id in (select info_request_id from info_request_events where created_at > '2007-10-13'::date)")
-
- get :list, :view => 'all', :request_date_after => '13/10/2007', :request_date_before => '01/11/2007'
- assigns[:list_results].map(&:info_request).should =~ InfoRequest.all(
- :conditions => "id in (select info_request_id from info_request_events where created_at between '2007-10-13'::date and '2007-11-01'::date)")
- end
-
- it "should list internal_review requests as unresolved ones" do
- get :list, :view => 'awaiting'
-
- # This doesn’t precisely duplicate the logic of the actual
- # query, but it is close enough to give the same result with
- # the current set of test data.
- assigns[:list_results].should =~ InfoRequestEvent.all(
- :conditions => "described_state in (
- 'waiting_response', 'waiting_clarification',
- 'internal_review', 'gone_postal', 'error_message', 'requires_admin'
- ) and not exists (
- select *
- from info_request_events later_events
- where later_events.created_at > info_request_events.created_at
- and later_events.info_request_id = info_request_events.info_request_id
- )")
-
-
- get :list, :view => 'awaiting'
- assigns[:list_results].map(&:info_request).include?(info_requests(:fancy_dog_request)).should == false
-
- event = info_request_events(:useless_incoming_message_event)
- event.described_state = event.calculated_state = "internal_review"
- event.save!
- rebuild_xapian_index
-
- get :list, :view => 'awaiting'
- assigns[:list_results].map(&:info_request).include?(info_requests(:fancy_dog_request)).should == true
- end
-
- it "should assign the first page of results" do
- xap_results = mock_model(ActsAsXapian::Search,
- :results => (1..25).to_a.map { |m| { :model => m } },
- :matches_estimated => 1000000)
-
- InfoRequest.should_receive(:full_search).
- with([InfoRequestEvent]," (variety:sent OR variety:followup_sent OR variety:response OR variety:comment)", "created_at", anything, anything, anything, anything).
- and_return(xap_results)
- get :list, :view => 'all'
- assigns[:list_results].size.should == 25
- assigns[:show_no_more_than].should == RequestController::MAX_RESULTS
- end
-
it "should return 404 for pages we don't want to serve up" do
- xap_results = mock_model(ActsAsXapian::Search,
+ xap_results = mock(ActsAsXapian::Search,
:results => (1..25).to_a.map { |m| { :model => m } },
:matches_estimated => 1000000)
lambda {
@@ -120,8 +35,7 @@ describe RequestController, "when listing recent requests" do
end
describe RequestController, "when changing things that appear on the request page" do
-
- integrate_views
+ render_views
it "should purge the downstream cache when mail is received" do
ir = info_requests(:fancy_dog_request)
@@ -136,7 +50,7 @@ describe RequestController, "when changing things that appear on the request pag
it "should purge the downstream cache when a followup is made" do
session[:user_id] = users(:bob_smith_user).id
ir = info_requests(:fancy_dog_request)
- post :show_response, :outgoing_message => { :body => "What a useless response! You suck.", :what_doing => 'normal_sort' }, :id => ir.id, :incoming_message_id => incoming_messages(:useless_incoming_message), :submitted_followup => 1
+ post :show_response, :outgoing_message => { :body => "What a useless response! You suck.", :what_doing => 'normal_sort' }, :id => ir.id, :submitted_followup => 1
PurgeRequest.all().first.model_id.should == ir.id
end
it "should purge the downstream cache when the request is categorised" do
@@ -174,7 +88,7 @@ describe RequestController, "when changing things that appear on the request pag
ir.save!
PurgeRequest.all().first.model_id.should == ir.id
end
- it "should not create more than one entry for any given resourcce" do
+ it "should not create more than one entry for any given resource" do
ir = info_requests(:fancy_dog_request)
ir.prominence = 'hidden'
ir.save!
@@ -187,11 +101,10 @@ describe RequestController, "when changing things that appear on the request pag
end
describe RequestController, "when showing one request" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
- FileUtils.rm_rf File.join(File.dirname(__FILE__), "../../cache/zips")
end
it "should be successful" do
@@ -216,20 +129,20 @@ describe RequestController, "when showing one request" do
end
it "should redirect from a numeric URL to pretty one" do
- get :show, :url_title => info_requests(:naughty_chicken_request).id
+ get :show, :url_title => info_requests(:naughty_chicken_request).id.to_s
response.should redirect_to(:action => 'show', :url_title => info_requests(:naughty_chicken_request).url_title)
end
it 'should show actions the request owner can take' do
get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.should have_tag('div#owner_actions')
+ response.should have_selector('div#owner_actions')
end
describe 'when the request does allow comments' do
it 'should have a comment link' do
get :show, { :url_title => 'why_do_you_have_such_a_fancy_dog' },
{ :user_id => users(:admin_user).id }
- response.should have_tag('#anyone_actions', /Add an annotation/)
+ response.should have_selector('#anyone_actions', :content => "Add an annotation")
end
end
@@ -237,10 +150,40 @@ describe RequestController, "when showing one request" do
it 'should not have a comment link' do
get :show, { :url_title => 'spam_1' },
{ :user_id => users(:admin_user).id }
- response.should_not have_tag('#anyone_actions', /Add an annotation/)
+ response.should_not have_selector('#anyone_actions', :content => "Add an annotation")
end
end
+ context "when the request has not yet been reported" do
+ it "should allow the user to report" do
+ title = info_requests(:badger_request).url_title
+ get :show, :url_title => title
+ response.should_not contain("This request has been reported")
+ response.should contain("Offensive?")
+ end
+ end
+
+ context "when the request has been reported for admin attention" do
+ before :each do
+ info_requests(:fancy_dog_request).report!("", "", nil)
+ end
+ it "should inform the user" do
+ get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
+ response.should contain("This request has been reported")
+ response.should_not contain("Offensive?")
+ end
+
+ context "and then deemed okay and left to complete" do
+ before :each do
+ info_requests(:fancy_dog_request).set_described_state("successful")
+ end
+ it "should let the user know that the administrators have not hidden this request" do
+ get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
+ response.body.should =~ (/the site administrators.*have not hidden it/)
+ end
+ end
+ end
+
describe 'when the request is being viewed by an admin' do
describe 'if the request is awaiting description' do
@@ -254,13 +197,13 @@ describe RequestController, "when showing one request" do
it 'should show the describe state form' do
get :show, { :url_title => 'why_do_you_have_such_a_fancy_dog' },
{ :user_id => users(:admin_user).id }
- response.should have_tag('div.describe_state_form')
+ response.should have_selector('div.describe_state_form')
end
it 'should ask the user to use the describe state from' do
get :show, { :url_title => 'why_do_you_have_such_a_fancy_dog' },
{ :user_id => users(:admin_user).id }
- response.should have_tag('p#request_status', :text => /answer the question above/)
+ response.should have_selector('p#request_status', :content => "answer the question above")
end
end
@@ -278,7 +221,7 @@ describe RequestController, "when showing one request" do
it 'should give a link to requesting an internal review' do
get :show, { :url_title => 'why_do_you_have_such_a_fancy_dog' },
{ :user_id => users(:admin_user).id }
- response.should have_tag('p#request_status', :text =>/requesting an internal review/)
+ response.should have_selector('p#request_status', :content => "requesting an internal review")
end
end
@@ -296,7 +239,7 @@ describe RequestController, "when showing one request" do
it 'should give a link to make a followup' do
get :show, { :url_title => 'why_do_you_have_such_a_fancy_dog' },
{ :user_id => users(:admin_user).id }
- response.should have_tag('p#request_status a', :text =>/send a follow up message/)
+ response.should have_selector('p#request_status a', :content => "send a follow up message")
end
end
@@ -313,7 +256,7 @@ describe RequestController, "when showing one request" do
it 'should not display actions the request owner can take' do
get :show, :url_title => 'balalas'
- response.should_not have_tag('div#owner_actions')
+ response.should_not have_selector('div#owner_actions')
end
end
@@ -339,12 +282,12 @@ describe RequestController, "when showing one request" do
it 'should not show the describe state form' do
make_request
- response.should_not have_tag('div.describe_state_form')
+ response.should_not have_selector('div.describe_state_form')
end
it 'should not ask the user to use the describe state form' do
make_request
- response.should_not have_tag('p#request_status', :text => /answer the question above/)
+ response.should_not have_selector('p#request_status', :content => "answer the question above")
end
end
@@ -361,7 +304,7 @@ describe RequestController, "when showing one request" do
it 'should not give a link to requesting an internal review' do
make_request
- response.should_not have_tag('p#request_status', :text =>/requesting an internal review/)
+ response.should_not have_selector('p#request_status', :content => "requesting an internal review")
end
end
@@ -377,12 +320,12 @@ describe RequestController, "when showing one request" do
it 'should not give a link to make a followup' do
make_request
- response.should_not have_tag('p#request_status a', :text =>/send a follow up message/)
+ response.should_not have_selector('p#request_status a', :content => "send a follow up message")
end
it 'should not give a link to sign in (in the request status paragraph)' do
make_request
- response.should_not have_tag('p#request_status a', :text => /sign in/)
+ response.should_not have_selector('p#request_status a', :content => "sign in")
end
end
@@ -445,7 +388,7 @@ describe RequestController, "when showing one request" do
describe 'when handling incoming mail' do
- integrate_views
+ render_views
it "should receive incoming messages, send email to creator, and show them" do
ir = info_requests(:fancy_dog_request)
@@ -479,13 +422,13 @@ describe RequestController, "when showing one request" do
(assigns[:info_request_events].size - size_before).should == 1
ir.reload
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt'], :skip_cache => 1
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt', :skip_cache => 1
response.content_type.should == "text/plain"
- response.should have_text(/Second hello/)
+ response.should contain "Second hello"
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 3, :file_name => ['hello.txt'], :skip_cache => 1
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 3, :file_name => 'hello world.txt', :skip_cache => 1
response.content_type.should == "text/plain"
- response.should have_text(/First hello/)
+ response.should contain "First hello"
end
it 'should cache an attachment on a request with normal prominence' do
@@ -496,24 +439,23 @@ describe RequestController, "when showing one request" do
get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id,
:id => ir.id,
:part => 2,
- :file_name => ['hello.txt']
-
+ :file_name => 'hello world.txt'
end
it "should convert message body to UTF8" do
ir = info_requests(:fancy_dog_request)
receive_incoming_mail('iso8859_2_raw_email.email', ir.incoming_email)
get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.should have_text(/tënde/u)
+ response.should contain "tënde"
end
it "should generate valid HTML verson of plain text attachments" do
ir = info_requests(:fancy_dog_request)
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
ir.reload
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt.html'], :skip_cache => 1
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1
response.content_type.should == "text/html"
- response.should have_text(/Second hello/)
+ response.should contain "Second hello"
end
# This is a regression test for a bug where URLs of this form were causing 500 errors
@@ -532,11 +474,11 @@ describe RequestController, "when showing one request" do
ir.reload
ugly_id = "55195"
lambda {
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => ['hello.txt.html'], :skip_cache => 1
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1
}.should raise_error(ActiveRecord::RecordNotFound)
lambda {
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => ['hello.txt'], :skip_cache => 1
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello world.txt', :skip_cache => 1
}.should raise_error(ActiveRecord::RecordNotFound)
end
it "should return 404 when incoming message and request ids don't match" do
@@ -545,7 +487,7 @@ describe RequestController, "when showing one request" do
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
ir.reload
lambda {
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => wrong_id, :part => 2, :file_name => ['hello.txt.html'], :skip_cache => 1
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => wrong_id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1
}.should raise_error(ActiveRecord::RecordNotFound)
end
it "should return 404 for ugly URLs contain a request id that isn't an integer, even if the integer prefix refers to an actual request" do
@@ -555,11 +497,11 @@ describe RequestController, "when showing one request" do
ugly_id = "%d95" % [info_requests(:naughty_chicken_request).id]
lambda {
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => ['hello.txt.html'], :skip_cache => 1
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1
}.should raise_error(ActiveRecord::RecordNotFound)
lambda {
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => ['hello.txt'], :skip_cache => 1
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello world.txt', :skip_cache => 1
}.should raise_error(ActiveRecord::RecordNotFound)
end
it "should return 404 when incoming message and request ids don't match" do
@@ -568,7 +510,7 @@ describe RequestController, "when showing one request" do
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
ir.reload
lambda {
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => wrong_id, :part => 2, :file_name => ['hello.txt.html'], :skip_cache => 1
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => wrong_id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1
}.should raise_error(ActiveRecord::RecordNotFound)
end
@@ -576,41 +518,64 @@ describe RequestController, "when showing one request" do
ir = info_requests(:fancy_dog_request)
receive_incoming_mail('incoming-request-pdf-attachment.email', ir.incoming_email)
ir.reload
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['fs_50379341.pdf.html'], :skip_cache => 1
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'fs 50379341.pdf.html', :skip_cache => 1
response.content_type.should == "text/html"
- response.should have_text(/Walberswick Parish Council/)
+ response.should contain "Walberswick Parish Council"
end
- it "should not cause a reparsing of the raw email, even when the result would be a 404" do
+ it "should not cause a reparsing of the raw email, even when the attachment can't be found" do
ir = info_requests(:fancy_dog_request)
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
ir.reload
- attachment = IncomingMessage.get_attachment_by_url_part_number(ir.incoming_messages[1].get_attachments_for_display, 2)
- attachment.body.should have_text(/Second hello/)
+ attachment = IncomingMessage.get_attachment_by_url_part_number_and_filename(ir.incoming_messages[1].get_attachments_for_display, 2, 'hello world.txt')
+ attachment.body.should contain "Second hello"
# change the raw_email associated with the message; this only be reparsed when explicitly asked for
ir.incoming_messages[1].raw_email.data = ir.incoming_messages[1].raw_email.data.sub("Second", "Third")
- # asking for an attachment by the wrong filename results
- # in a 404 for browsing users. This shouldn't cause a
- # re-parse...
- lambda {
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt.baz.html'], :skip_cache => 1
- }.should raise_error(ActiveRecord::RecordNotFound)
+ # asking for an attachment by the wrong filename should result in redirecting
+ # back to the incoming message, but shouldn't cause a reparse:
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt.baz.html', :skip_cache => 1
+ response.status.should == 303
- attachment = IncomingMessage.get_attachment_by_url_part_number(ir.incoming_messages[1].get_attachments_for_display, 2)
- attachment.body.should have_text(/Second hello/)
+ attachment = IncomingMessage.get_attachment_by_url_part_number_and_filename(ir.incoming_messages[1].get_attachments_for_display, 2, 'hello world.txt')
+ attachment.body.should contain "Second hello"
# ...nor should asking for it by its correct filename...
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt.html'], :skip_cache => 1
- response.should_not have_text(/Third hello/)
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1
+ response.should_not contain "Third hello"
# ...but if we explicitly ask for attachments to be extracted, then they should be
force = true
ir.incoming_messages[1].parse_raw_email!(force)
- attachment = IncomingMessage.get_attachment_by_url_part_number(ir.incoming_messages[1].get_attachments_for_display, 2)
- attachment.body.should have_text(/Second hello/)
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt.html'], :skip_cache => 1
- response.should have_text(/Third hello/)
+ ir.reload
+ attachment = IncomingMessage.get_attachment_by_url_part_number_and_filename(ir.incoming_messages[1].get_attachments_for_display, 2, 'hello world.txt')
+ attachment.body.should contain "Third hello"
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1
+ response.should contain "Third hello"
+ end
+
+ it "should redirect to the incoming message if there's a wrong part number and an ambiguous filename" do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
+ ir.reload
+
+ im = ir.incoming_messages[1]
+
+ attachment = IncomingMessage.get_attachment_by_url_part_number_and_filename(im.get_attachments_for_display, 5, 'hello world.txt')
+ attachment.should be_nil
+
+ get :get_attachment_as_html, :incoming_message_id => im.id, :id => ir.id, :part => 5, :file_name => 'hello world.txt', :skip_cache => 1
+ response.status.should == 303
+ new_location = response.header['Location']
+ new_location.should match(/request\/#{ir.url_title}#incoming-#{im.id}/)
+ end
+
+ it "should find a uniquely named filename even if the URL part number was wrong" do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('incoming-request-pdf-attachment.email', ir.incoming_email)
+ ir.reload
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 5, :file_name => 'fs 50379341.pdf', :skip_cache => 1
+ response.content_type.should == "application/pdf"
end
it "should treat attachments with unknown extensions as binary" do
@@ -618,19 +583,17 @@ describe RequestController, "when showing one request" do
receive_incoming_mail('incoming-request-attachment-unknown-extension.email', ir.incoming_email)
ir.reload
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.qwglhm'], :skip_cache => 1
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello.qwglhm', :skip_cache => 1
response.content_type.should == "application/octet-stream"
- response.should have_text(/an unusual sort of file/)
+ response.should contain "an unusual sort of file"
end
it "should not download attachments with wrong file name" do
ir = info_requests(:fancy_dog_request)
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
- lambda {
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2,
- :file_name => ['http://trying.to.hack']
- }.should raise_error(ActiveRecord::RecordNotFound)
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'http://trying.to.hack'
+ response.status.should == 303
end
it "should censor attachments downloaded as binary" do
@@ -646,9 +609,9 @@ describe RequestController, "when showing one request" do
begin
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt'], :skip_cache => 1
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt', :skip_cache => 1
response.content_type.should == "text/plain"
- response.should have_text(/xxxxxx hello/)
+ response.should contain "xxxxxx hello"
ensure
ir.censor_rules.clear
end
@@ -668,9 +631,9 @@ describe RequestController, "when showing one request" do
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
ir.reload
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt'], :skip_cache => 1
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt', :skip_cache => 1
response.content_type.should == "text/plain"
- response.should have_text(/xxxxxx hello/)
+ response.should contain "xxxxxx hello"
ensure
ir.user.censor_rules.clear
end
@@ -692,205 +655,247 @@ describe RequestController, "when showing one request" do
get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
assert assigns[:info_request].info_request_events[3].incoming_message.get_attachments_for_display.count == 2
# the issue is that the info_request_events have got cached on them the old info_requests.
- # where i'm at: trying to replace those fields that got re-read from the raw email. however tests are failing in very strange ways. currently I don't appear to be getting any attachments parsed in at all when in the template (see "*****" in _correspondence.rhtml) but do when I'm in the code.
+ # where i'm at: trying to replace those fields that got re-read from the raw email. however tests are failing in very strange ways. currently I don't appear to be getting any attachments parsed in at all when in the template (see "*****" in _correspondence.html.erb) but do when I'm in the code.
# so at this point, assigns[:info_request].incoming_messages[1].get_attachments_for_display is returning stuff, but the equivalent thing in the template isn't.
# but something odd is that the above is return a whole load of attachments which aren't there in the controller
- response.body.should have_tag("p.attachment strong", /hello.txt/m)
+ response.body.should have_selector("p.attachment strong") do |s|
+ s.should contain /hello world.txt/m
+ end
censor_rule = CensorRule.new()
- censor_rule.text = "hello.txt"
+ # Note that the censor rule applies to the original filename,
+ # not the display_filename:
+ censor_rule.text = "hello-world.txt"
censor_rule.replacement = "goodbye.txt"
censor_rule.last_edit_editor = "unknown"
censor_rule.last_edit_comment = "none"
ir.censor_rules << censor_rule
begin
get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.body.should have_tag("p.attachment strong", /goodbye.txt/m)
+ response.body.should have_selector("p.attachment strong") do |s|
+ s.should contain /goodbye.txt/m
+ end
ensure
ir.censor_rules.clear
end
end
- describe 'when making a zipfile available' do
- it 'should return a 410 for a request that is hidden' do
- title = 'why_do_you_have_such_a_fancy_dog'
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'hidden'
- ir.save!
- get :download_entire_request, {:url_title => title}, { :user_id => ir.user.id }
- response.should render_template('request/hidden')
- response.code.should == '410'
- end
-
- it "should have a different zipfile URL when the request changes" do
- title = 'why_do_you_have_such_a_fancy_dog'
- ir = info_requests(:fancy_dog_request)
- session[:user_id] = ir.user.id # bob_smith_user
- get :download_entire_request, :url_title => title
- assigns[:url_path].should have_text(/#{title}.zip$/)
- old_path = assigns[:url_path]
- response.location.should have_text(/#{assigns[:url_path]}$/)
- zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", old_path)) { |zipfile|
- zipfile.count.should == 1 # just the message
- }
- receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
- get :download_entire_request, :url_title => title
- assigns[:url_path].should have_text(/#{title}.zip$/)
- old_path = assigns[:url_path]
- response.location.should have_text(/#{assigns[:url_path]}$/)
- zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", old_path)) { |zipfile|
- zipfile.count.should == 3 # the message plus two "hello.txt" files
- }
-
- # The path of the zip file is based on the hash of the timestamp of the last request
- # in the thread, so we wait for a second to make sure this one will have a different
- # timestamp than the previous.
- sleep 1
- receive_incoming_mail('incoming-request-attachment-unknown-extension.email', ir.incoming_email)
- get :download_entire_request, :url_title => title
- assigns[:url_path].should have_text(/#{title}.zip$/)
- assigns[:url_path].should_not == old_path
- response.location.should have_text(/#{assigns[:url_path]}/)
- zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", assigns[:url_path])) { |zipfile|
- zipfile.count.should == 4 # the message, two hello.txt plus the unknown attachment
- }
- end
-
- it 'should successfully make a zipfile for an external request' do
- info_request = info_requests(:external_request)
- get :download_entire_request, { :url_title => info_request.url_title },
- { :user_id => users(:bob_smith_user) }
- response.location.should have_text(/#{assigns[:url_path]}$/)
- end
- end
end
end
-describe RequestController, "when changing prominence of a request" do
+describe RequestController, "when handling prominence" do
- before(:each) do
- load_raw_emails_data
+ def expect_hidden(hidden_template)
+ response.content_type.should == "text/html"
+ response.should render_template(hidden_template)
+ response.code.should == '403'
end
- it "should not show hidden requests" do
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'hidden'
- ir.save!
+ context 'when the request is hidden' do
- get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.should render_template('hidden')
- end
+ before(:each) do
+ @info_request = FactoryGirl.create(:info_request_with_incoming_attachments,
+ :prominence => 'hidden')
+ end
- it "should not show hidden requests even if logged in as their owner" do
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'hidden'
- ir.save!
+ it "should not show request if you're not logged in" do
+ get :show, :url_title => @info_request.url_title
+ expect_hidden('hidden')
+ end
- session[:user_id] = ir.user.id # bob_smith_user
- get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.should render_template('hidden')
- end
+ it "should not show request even if logged in as their owner" do
+ session[:user_id] = @info_request.user.id
+ get :show, :url_title => @info_request.url_title
+ expect_hidden('hidden')
+ end
- it "should show hidden requests if logged in as super user" do
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'hidden'
- ir.save!
+ it 'should not show request if requested using json' do
+ session[:user_id] = @info_request.user.id
+ get :show, :url_title => @info_request.url_title, :format => 'json'
+ response.code.should == '403'
+ end
- session[:user_id] = users(:admin_user)
- get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.should render_template('show')
- end
+ it "should show request if logged in as super user" do
+ session[:user_id] = FactoryGirl.create(:admin_user)
+ get :show, :url_title => @info_request.url_title
+ response.should render_template('show')
+ end
- it "should not show requester_only requests if you're not logged in" do
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'requester_only'
- ir.save!
+ it "should not download attachments" do
+ incoming_message = @info_request.incoming_messages.first
+ get :get_attachment, :incoming_message_id => incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf',
+ :skip_cache => 1
+ expect_hidden('request/hidden')
+ end
+
+ it 'should not generate an HTML version of an attachment for a request whose prominence
+ is hidden even for an admin but should return a 404' do
+ session[:user_id] = FactoryGirl.create(:admin_user)
+ incoming_message = @info_request.incoming_messages.first
+ lambda do
+ get :get_attachment_as_html, :incoming_message_id => incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf'
+ end.should raise_error(ActiveRecord::RecordNotFound)
+ end
- get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.should render_template('hidden')
end
- it "should show requester_only requests to requester and admin if logged in" do
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'requester_only'
- ir.save!
+ context 'when the request is requester_only' do
- session[:user_id] = users(:silly_name_user).id
- get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.should render_template('hidden')
+ before(:each) do
+ @info_request = FactoryGirl.create(:info_request_with_incoming_attachments,
+ :prominence => 'requester_only')
+ end
- session[:user_id] = ir.user.id # bob_smith_user
- get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.should render_template('show')
+ it "should not show request if you're not logged in" do
+ get :show, :url_title => @info_request.url_title
+ expect_hidden('hidden')
+ end
- session[:user_id] = users(:admin_user).id
- get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.should render_template('show')
- end
+ it "should show request to requester and admin if logged in" do
+ session[:user_id] = FactoryGirl.create(:user).id
+ get :show, :url_title => @info_request.url_title
+ expect_hidden('hidden')
- it 'should not cache an attachment on a request whose prominence is requester_only when showing
- the request to the requester or admin' do
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'requester_only'
- ir.save!
- session[:user_id] = ir.user.id # bob_smith_user
- @controller.should_not_receive(:foi_fragment_cache_write)
- get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
+ session[:user_id] = @info_request.user.id
+ get :show, :url_title => @info_request.url_title
+ response.should render_template('show')
+
+ session[:user_id] = FactoryGirl.create(:admin_user).id
+ get :show, :url_title => @info_request.url_title
+ response.should render_template('show')
+ end
+
+ it 'should not cache an attachment when showing an attachment to the requester or admin' do
+ session[:user_id] = @info_request.user.id
+ incoming_message = @info_request.incoming_messages.first
+ @controller.should_not_receive(:foi_fragment_cache_write)
+ get :get_attachment, :incoming_message_id => incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf'
+ end
end
- it "should not download attachments if hidden" do
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'hidden'
- ir.save!
- receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
+ context 'when the incoming message has prominence hidden' do
+
+ before(:each) do
+ @incoming_message = FactoryGirl.create(:incoming_message_with_attachments,
+ :prominence => 'hidden')
+ @info_request = @incoming_message.info_request
+ end
+
+ it "should not download attachments for a non-logged in user" do
+ get :get_attachment, :incoming_message_id => @incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf',
+ :skip_cache => 1
+ expect_hidden('request/hidden_correspondence')
+ end
+
+ it 'should not download attachments for the request owner' do
+ session[:user_id] = @info_request.user.id
+ get :get_attachment, :incoming_message_id => @incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf',
+ :skip_cache => 1
+ expect_hidden('request/hidden_correspondence')
+ end
+
+ it 'should download attachments for an admin user' do
+ session[:user_id] = FactoryGirl.create(:admin_user).id
+ get :get_attachment, :incoming_message_id => @incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf',
+ :skip_cache => 1
+ response.content_type.should == 'application/pdf'
+ response.should be_success
+ end
+
+ it 'should not generate an HTML version of an attachment for a request whose prominence
+ is hidden even for an admin but should return a 404' do
+ session[:user_id] = FactoryGirl.create(:admin_user).id
+ lambda do
+ get :get_attachment_as_html, :incoming_message_id => @incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf',
+ :skip_cache => 1
+ end.should raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'should not cache an attachment when showing an attachment to the requester or admin' do
+ session[:user_id] = @info_request.user.id
+ @controller.should_not_receive(:foi_fragment_cache_write)
+ get :get_attachment, :incoming_message_id => @incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf'
+ end
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id,
- :id => ir.id,
- :part => 2,
- :skip_cache => 1
- response.content_type.should == "text/html"
- response.should_not have_text(/Second hello/)
- response.should render_template('request/hidden')
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id,
- :id => ir.id,
- :part => 3,
- :skip_cache => 1
- response.content_type.should == "text/html"
- response.should_not have_text(/First hello/)
- response.should render_template('request/hidden')
- response.code.should == '410'
end
- it 'should not generate an HTML version of an attachment whose prominence is hidden/requester
- only even for the requester or an admin but should return a 404' do
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'hidden'
- ir.save!
- receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
- session[:user_id] = users(:admin_user).id
- lambda do
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id,
- :id => ir.id,
- :part => 2,
- :file_name => ['hello.txt']
- end.should raise_error(ActiveRecord::RecordNotFound)
- end
-
- it 'should not generate an HTML version of an attachment whose prominence is hidden/requester
- only even for the requester or an admin but should return a 404' do
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'hidden'
- ir.save!
- receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
- session[:user_id] = users(:admin_user).id
- lambda do
- get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id,
- :id => ir.id,
- :part => 2,
- :file_name => ['hello.txt']
- end.should raise_error(ActiveRecord::RecordNotFound)
+ context 'when the incoming message has prominence requester_only' do
+
+ before(:each) do
+ @incoming_message = FactoryGirl.create(:incoming_message_with_attachments,
+ :prominence => 'requester_only')
+ @info_request = @incoming_message.info_request
+ end
+
+ it "should not download attachments for a non-logged in user" do
+ get :get_attachment, :incoming_message_id => @incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf',
+ :skip_cache => 1
+ expect_hidden('request/hidden_correspondence')
+ end
+
+ it 'should download attachments for the request owner' do
+ session[:user_id] = @info_request.user.id
+ get :get_attachment, :incoming_message_id => @incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf',
+ :skip_cache => 1
+ response.content_type.should == 'application/pdf'
+ response.should be_success
+ end
+
+ it 'should download attachments for an admin user' do
+ session[:user_id] = FactoryGirl.create(:admin_user).id
+ get :get_attachment, :incoming_message_id => @incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf',
+ :skip_cache => 1
+ response.content_type.should == 'application/pdf'
+ response.should be_success
+ end
+
+ it 'should not generate an HTML version of an attachment for a request whose prominence
+ is hidden even for an admin but should return a 404' do
+ session[:user_id] = FactoryGirl.create(:admin_user)
+ lambda do
+ get :get_attachment_as_html, :incoming_message_id => @incoming_message.id,
+ :id => @info_request.id,
+ :part => 2,
+ :file_name => 'interesting.pdf',
+ :skip_cache => 1
+ end.should raise_error(ActiveRecord::RecordNotFound)
+ end
+
end
end
@@ -902,11 +907,11 @@ end
# end
describe RequestController, "when searching for an authority" do
-
# Whether or not sign-in is required for this step is configurable,
# so we make sure we're logged in, just in case
before do
@user = users(:bob_smith_user)
+ get_fixtures_xapian_index
end
it "should return nothing for the empty query string" do
@@ -918,6 +923,7 @@ describe RequestController, "when searching for an authority" do
end
it "should return matching bodies" do
+
session[:user_id] = @user.id
get :select_authority, :query => "Quango"
@@ -942,7 +948,7 @@ describe RequestController, "when searching for an authority" do
end
describe RequestController, "when creating a new request" do
- integrate_views
+ render_views
before do
@user = users(:bob_smith_user)
@@ -973,6 +979,14 @@ describe RequestController, "when creating a new request" do
response.should render_template('new')
end
+ it 'should display one meaningful error message when no message body is added' do
+ post :new, :info_request => { :public_body_id => @body.id },
+ :outgoing_message => { :body => "" },
+ :submitted_new_request => 1, :preview => 1
+ assigns[:info_request].errors.full_messages.should_not include('Outgoing messages is invalid')
+ assigns[:outgoing_message].errors.full_messages.should include('Body Please enter your letter requesting information')
+ end
+
it "should give an error and render 'new' template when a summary isn't given" do
post :new, :info_request => { :public_body_id => @body.id },
:outgoing_message => { :body => "This is a silly letter. It is too short to be interesting." },
@@ -1034,7 +1048,7 @@ describe RequestController, "when creating a new request" do
response.should redirect_to show_new_request_url(:url_title => ir.url_title)
# This test uses an explicit path because it's relied in
# Google Analytics goals:
- response.redirected_to.should =~ /request\/why_is_your_quango_called_gerald\/new$/
+ response.redirect_url.should =~ /request\/why_is_your_quango_called_gerald\/new$/
end
it "should give an error if the same request is submitted twice" do
@@ -1178,7 +1192,7 @@ describe RequestController, "when making a new request" do
end
describe RequestController, "when viewing an individual response for reply/followup" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -1199,7 +1213,7 @@ describe RequestController, "when viewing an individual response for reply/follo
it "should offer the opportunity to reply to the main address" do
session[:user_id] = users(:bob_smith_user).id
get :show_response, :id => info_requests(:fancy_dog_request).id, :incoming_message_id => incoming_messages(:useless_incoming_message)
- response.body.should have_tag("div#other_recipients ul li", /the main FOI contact address for/)
+ response.body.should have_selector("div#other_recipients ul li", :content => "the main FOI contact address for")
end
it "should offer an opportunity to reply to another address" do
@@ -1209,17 +1223,32 @@ describe RequestController, "when viewing an individual response for reply/follo
ir.save!
receive_incoming_mail('incoming-request-plain.email', ir.incoming_email, "Frob <frob@bonce.com>")
get :show_response, :id => ir.id, :incoming_message_id => incoming_messages(:useless_incoming_message)
- response.body.should have_tag("div#other_recipients ul li", /Frob/)
+ response.body.should have_selector("div#other_recipients ul li", :content => "Frob")
end
- it "should not show individual responses if request hidden, even if request owner" do
- ir = info_requests(:fancy_dog_request)
- ir.prominence = 'hidden'
- ir.save!
+ context 'when a request is hidden' do
+
+ before do
+ ir = info_requests(:fancy_dog_request)
+ ir.prominence = 'hidden'
+ ir.save!
+
+ session[:user_id] = users(:bob_smith_user).id
+ end
+
+ it "should not show individual responses, even if request owner" do
+ get :show_response, :id => info_requests(:fancy_dog_request).id, :incoming_message_id => incoming_messages(:useless_incoming_message)
+ response.should render_template('request/hidden')
+ end
+
+ it 'should respond to a json request for a hidden request with a 403 code and no body' do
+ get :show_response, :id => info_requests(:fancy_dog_request).id,
+ :incoming_message_id => incoming_messages(:useless_incoming_message),
+ :format => 'json'
+
+ response.code.should == '403'
+ end
- session[:user_id] = users(:bob_smith_user).id
- get :show_response, :id => info_requests(:fancy_dog_request).id, :incoming_message_id => incoming_messages(:useless_incoming_message)
- response.should render_template('request/hidden')
end
describe 'when viewing a response for an external request' do
@@ -1248,8 +1277,7 @@ describe RequestController, "when classifying an information request" do
end
it 'should redirect to the request page' do
- post :describe_state, :id => @external_request.id,
- :submitted_describe_state => 1
+ post :describe_state, :id => @external_request.id
response.should redirect_to(:action => 'show',
:controller => 'request',
:url_title => @external_request.url_title)
@@ -1269,8 +1297,7 @@ describe RequestController, "when classifying an information request" do
def post_status(status)
post :describe_state, :incoming_message => { :described_state => status },
:id => @dog_request.id,
- :last_info_request_event_id => @dog_request.last_event_id_needing_description,
- :submitted_describe_state => 1
+ :last_info_request_event_id => @dog_request.last_event_id_needing_description
end
it "should require login" do
@@ -1280,6 +1307,7 @@ describe RequestController, "when classifying an information request" do
end
it 'should ask whether the request is old and unclassified' do
+ session[:user_id] = users(:silly_name_user).id
@dog_request.should_receive(:is_old_unclassified?)
post_status('rejected')
end
@@ -1294,7 +1322,9 @@ describe RequestController, "when classifying an information request" do
before do
@dog_request.stub!(:is_old_unclassified?).and_return(true)
- RequestMailer.stub!(:deliver_old_unclassified_updated)
+ mail_mock = mock("mail")
+ mail_mock.stub(:deliver)
+ RequestMailer.stub!(:old_unclassified_updated).and_return(mail_mock)
end
describe 'when the user is not logged in' do
@@ -1317,7 +1347,7 @@ describe RequestController, "when classifying an information request" do
it 'should classify the request' do
@dog_request.stub!(:calculate_status).and_return('rejected')
- @dog_request.should_receive(:set_described_state).with('rejected')
+ @dog_request.should_receive(:set_described_state).with('rejected', users(:silly_name_user), nil)
post_status('rejected')
end
@@ -1331,7 +1361,7 @@ describe RequestController, "when classifying an information request" do
end
it 'should send an email to the requester letting them know someone has updated the status of their request' do
- RequestMailer.should_receive(:deliver_old_unclassified_updated)
+ RequestMailer.should_receive(:old_unclassified_updated)
post_status('rejected')
end
@@ -1345,6 +1375,26 @@ describe RequestController, "when classifying an information request" do
flash[:notice].should == 'Thank you for updating this request!'
end
+ context "playing the classification game" do
+ before :each do
+ session[:request_game] = true
+ end
+
+ it "should continue the game after classifying a request" do
+ post_status("rejected")
+ flash[:notice].should =~ /There are some more requests below for you to classify/
+ response.should redirect_to categorise_play_url
+ end
+ end
+
+ it "should send a mail from the user who changed the state to requires_admin" do
+ post :describe_state, :incoming_message => { :described_state => "requires_admin", :message => "a message" }, :id => @dog_request.id, :incoming_message_id => incoming_messages(:useless_incoming_message), :last_info_request_event_id => @dog_request.last_event_id_needing_description
+
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.from_addrs.first.to_s.should == users(:silly_name_user).email
+ end
end
end
@@ -1360,7 +1410,7 @@ describe RequestController, "when classifying an information request" do
it 'should update the status of the request' do
@dog_request.stub!(:calculate_status).and_return('rejected')
- @dog_request.should_receive(:set_described_state).with('rejected')
+ @dog_request.should_receive(:set_described_state).with('rejected', @admin_user, nil)
post_status('rejected')
end
@@ -1382,7 +1432,9 @@ describe RequestController, "when classifying an information request" do
end
it 'should send an email to the requester letting them know someone has updated the status of their request' do
- RequestMailer.should_receive(:deliver_old_unclassified_updated)
+ mail_mock = mock("mail")
+ mail_mock.stub :deliver
+ RequestMailer.should_receive(:old_unclassified_updated).and_return(mail_mock)
post_status('rejected')
end
@@ -1411,17 +1463,17 @@ describe RequestController, "when classifying an information request" do
it 'should update the status of the request' do
@dog_request.stub!(:calculate_status).and_return('rejected')
- @dog_request.should_receive(:set_described_state).with('rejected')
+ @dog_request.should_receive(:set_described_state).with('rejected', @admin_user, nil)
post_status('rejected')
end
- it 'should not log a status update event' do
- @dog_request.should_not_receive(:log_event)
+ it 'should log a status update event' do
+ @dog_request.should_receive(:log_event)
post_status('rejected')
end
it 'should not send an email to the requester letting them know someone has updated the status of their request' do
- RequestMailer.should_not_receive(:deliver_old_unclassified_updated)
+ RequestMailer.should_not_receive(:old_unclassified_updated)
post_status('rejected')
end
@@ -1446,42 +1498,71 @@ describe RequestController, "when classifying an information request" do
@dog_request.stub!(:each).and_return([@dog_request])
end
+ it "should let you know when you forget to select a status" do
+ post :describe_state, :id => @dog_request.id,
+ :last_info_request_event_id => @dog_request.last_event_id_needing_description
+ response.should redirect_to show_request_url(:url_title => @dog_request.url_title)
+ flash[:error].should == _("Please choose whether or not you got some of the information that you wanted.")
+ end
+
+ it "should not change the status if the request has changed while viewing it" do
+ @dog_request.stub!(:last_event_id_needing_description).and_return(2)
+
+ post :describe_state, :incoming_message => { :described_state => "rejected" },
+ :id => @dog_request.id, :last_info_request_event_id => 1
+ response.should redirect_to show_request_url(:url_title => @dog_request.url_title)
+ flash[:error].should =~ /The request has been updated since you originally loaded this page/
+ end
+
it "should successfully classify response if logged in as user controlling request" do
post_status('rejected')
response.should redirect_to(:controller => 'help', :action => 'unhappy', :url_title => @dog_request.url_title)
@dog_request.reload
@dog_request.awaiting_description.should == false
@dog_request.described_state.should == 'rejected'
- @dog_request.get_last_response_event.should == info_request_events(:useless_incoming_message_event)
- @dog_request.get_last_response_event.calculated_state.should == 'rejected'
+ @dog_request.get_last_public_response_event.should == info_request_events(:useless_incoming_message_event)
+ @dog_request.info_request_events.last.event_type.should == "status_update"
+ @dog_request.info_request_events.last.calculated_state.should == 'rejected'
end
- it 'should not log a status update event' do
- @dog_request.should_not_receive(:log_event)
+ it 'should log a status update event' do
+ @dog_request.should_receive(:log_event)
post_status('rejected')
end
it 'should not send an email to the requester letting them know someone has updated the status of their request' do
- RequestMailer.should_not_receive(:deliver_old_unclassified_updated)
+ RequestMailer.should_not_receive(:old_unclassified_updated)
post_status('rejected')
end
- it "should send email when classified as requires_admin" do
- post :describe_state, :incoming_message => { :described_state => "requires_admin" }, :id => @dog_request.id, :incoming_message_id => incoming_messages(:useless_incoming_message), :last_info_request_event_id => @dog_request.last_event_id_needing_description, :submitted_describe_state => 1
- response.should redirect_to(:controller => 'help', :action => 'contact')
+ it "should go to the page asking for more information when classified as requires_admin" do
+ post :describe_state, :incoming_message => { :described_state => "requires_admin" }, :id => @dog_request.id, :incoming_message_id => incoming_messages(:useless_incoming_message), :last_info_request_event_id => @dog_request.last_event_id_needing_description
+ response.should redirect_to describe_state_message_url(:url_title => @dog_request.url_title, :described_state => "requires_admin")
@dog_request.reload
- @dog_request.awaiting_description.should == false
- @dog_request.described_state.should == 'requires_admin'
- @dog_request.get_last_response_event.calculated_state.should == 'requires_admin'
+ @dog_request.described_state.should_not == 'requires_admin'
- deliveries = ActionMailer::Base.deliveries
- deliveries.size.should == 1
- mail = deliveries[0]
- mail.body.should =~ /as needing admin/
- mail.from_addrs.first.to_s.should == @request_owner.name_and_email
+ ActionMailer::Base.deliveries.should be_empty
end
+ context "message is included when classifying as requires_admin" do
+ it "should send an email including the message" do
+ post :describe_state,
+ :incoming_message => {
+ :described_state => "requires_admin",
+ :message => "Something weird happened" },
+ :id => @dog_request.id,
+ :last_info_request_event_id => @dog_request.last_event_id_needing_description
+
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.body.should =~ /as needing admin/
+ mail.body.should =~ /Something weird happened/
+ end
+ end
+
+
it 'should say it is showing advice as to what to do next' do
post_status('rejected')
flash[:notice].should match(/Here is what to do now/)
@@ -1505,7 +1586,7 @@ describe RequestController, "when classifying an information request" do
end
end
- describe 'when redirecting after a successful status update by the request owner' do
+ describe 'after a successful status update by the request owner' do
before do
@request_owner = users(:bob_smith_user)
@@ -1513,11 +1594,6 @@ describe RequestController, "when classifying an information request" do
@dog_request = info_requests(:fancy_dog_request)
@dog_request.stub!(:each).and_return([@dog_request])
InfoRequest.stub!(:find).and_return(@dog_request)
- @old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
- end
- after do
- ActionController::Routing::Routes.filters = @old_filters
end
def request_url
@@ -1533,77 +1609,161 @@ describe RequestController, "when classifying an information request" do
response.should redirect_to("http://test.host/#{redirect_path}")
end
- it 'should redirect to the "request url" with a message in the right tense when status is updated to "waiting response" and the response is not overdue' do
- @dog_request.stub!(:date_response_required_by).and_return(Time.now.to_date+1)
- @dog_request.stub!(:date_very_overdue_after).and_return(Time.now.to_date+40)
+ context 'when status is updated to "waiting_response"' do
- expect_redirect("waiting_response", "request/#{@dog_request.url_title}")
- flash[:notice].should match(/should get a response/)
- end
+ it 'should redirect to the "request url" with a message in the right tense when
+ the response is not overdue' do
+ @dog_request.stub!(:date_response_required_by).and_return(Time.now.to_date+1)
+ @dog_request.stub!(:date_very_overdue_after).and_return(Time.now.to_date+40)
- it 'should redirect to the "request url" with a message in the right tense when status is updated to "waiting response" and the response is overdue' do
- @dog_request.stub!(:date_response_required_by).and_return(Time.now.to_date-1)
- @dog_request.stub!(:date_very_overdue_after).and_return(Time.now.to_date+40)
- expect_redirect('waiting_response', request_url)
- flash[:notice].should match(/should have got a response/)
- end
+ expect_redirect("waiting_response", "request/#{@dog_request.url_title}")
+ flash[:notice].should match(/should get a response/)
+ end
- it 'should redirect to the "request url" with a message in the right tense when status is updated to "waiting response" and the response is overdue' do
- @dog_request.stub!(:date_response_required_by).and_return(Time.now.to_date-2)
- @dog_request.stub!(:date_very_overdue_after).and_return(Time.now.to_date-1)
- expect_redirect('waiting_response', unhappy_url)
- flash[:notice].should match(/is long overdue/)
- flash[:notice].should match(/by more than 40 working days/)
- flash[:notice].should match(/within 20 working days/)
- end
+ it 'should redirect to the "request url" with a message in the right tense when
+ the response is overdue' do
+ @dog_request.stub!(:date_response_required_by).and_return(Time.now.to_date-1)
+ @dog_request.stub!(:date_very_overdue_after).and_return(Time.now.to_date+40)
+ expect_redirect('waiting_response', request_url)
+ flash[:notice].should match(/should have got a response/)
+ end
- it 'should redirect to the "request url" when status is updated to "not held"' do
- expect_redirect('not_held', request_url)
+ it 'should redirect to the "request url" with a message in the right tense when
+ the response is overdue' do
+ @dog_request.stub!(:date_response_required_by).and_return(Time.now.to_date-2)
+ @dog_request.stub!(:date_very_overdue_after).and_return(Time.now.to_date-1)
+ expect_redirect('waiting_response', unhappy_url)
+ flash[:notice].should match(/is long overdue/)
+ flash[:notice].should match(/by more than 40 working days/)
+ flash[:notice].should match(/within 20 working days/)
+ end
end
- it 'should redirect to the "request url" when status is updated to "successful"' do
- expect_redirect('successful', request_url)
- end
+ context 'when status is updated to "not held"' do
+
+ it 'should redirect to the "request url"' do
+ expect_redirect('not_held', request_url)
+ end
- it 'should redirect to the "unhappy url" when status is updated to "rejected"' do
- expect_redirect('rejected', "help/unhappy/#{@dog_request.url_title}")
end
- it 'should redirect to the "unhappy url" when status is updated to "partially successful"' do
- expect_redirect('partially_successful', "help/unhappy/#{@dog_request.url_title}")
+ context 'when status is updated to "successful"' do
+
+ it 'should redirect to the "request url"' do
+ expect_redirect('successful', request_url)
+ end
+
+ it 'should show a message including the donation url if there is one' do
+ AlaveteliConfiguration.stub!(:donation_url).and_return('http://donations.example.com')
+ post_status('successful')
+ flash[:notice].should match('make a donation')
+ flash[:notice].should match('http://donations.example.com')
+ end
+
+ it 'should show a message without reference to donations if there is no
+ donation url' do
+ AlaveteliConfiguration.stub!(:donation_url).and_return('')
+ post_status('successful')
+ flash[:notice].should_not match('make a donation')
+ end
+
end
- it 'should redirect to the "response url" when status is updated to "waiting clarification" and there is a last response' do
- incoming_message = mock_model(IncomingMessage)
- @dog_request.stub!(:get_last_response).and_return(incoming_message)
- expect_redirect('waiting_clarification', "request/#{@dog_request.id}/response/#{incoming_message.id}")
+ context 'when status is updated to "waiting clarification"' do
+
+ it 'should redirect to the "response url" when there is a last response' do
+ incoming_message = mock_model(IncomingMessage)
+ @dog_request.stub!(:get_last_public_response).and_return(incoming_message)
+ expect_redirect('waiting_clarification', "request/#{@dog_request.id}/response/#{incoming_message.id}")
+ end
+
+ it 'should redirect to the "response no followup url" when there are no events
+ needing description' do
+ @dog_request.stub!(:get_last_public_response).and_return(nil)
+ expect_redirect('waiting_clarification', "request/#{@dog_request.id}/response")
+ end
+
end
- it 'should redirect to the "response no followup url" when status is updated to "waiting clarification" and there are no events needing description' do
- @dog_request.stub!(:get_last_response).and_return(nil)
- expect_redirect('waiting_clarification', "request/#{@dog_request.id}/response")
+ context 'when status is updated to "rejected"' do
+
+ it 'should redirect to the "unhappy url"' do
+ expect_redirect('rejected', "help/unhappy/#{@dog_request.url_title}")
+ end
+
end
- it 'should redirect to the "respond to last url" when status is updated to "gone postal"' do
- expect_redirect('gone_postal', "request/#{@dog_request.id}/response/#{@dog_request.get_last_response.id}?gone_postal=1")
+ context 'when status is updated to "partially successful"' do
+
+ it 'should redirect to the "unhappy url"' do
+ expect_redirect('partially_successful', "help/unhappy/#{@dog_request.url_title}")
+ end
+
+ it 'should show a message including the donation url if there is one' do
+ AlaveteliConfiguration.stub!(:donation_url).and_return('http://donations.example.com')
+ post_status('successful')
+ flash[:notice].should match('make a donation')
+ flash[:notice].should match('http://donations.example.com')
+ end
+
+ it 'should show a message without reference to donations if there is no
+ donation url' do
+ AlaveteliConfiguration.stub!(:donation_url).and_return('')
+ post_status('successful')
+ flash[:notice].should_not match('make a donation')
+ end
+
end
- it 'should redirect to the "request url" when status is updated to "internal review"' do
- expect_redirect('internal_review', request_url)
+ context 'when status is updated to "gone postal"' do
+
+ it 'should redirect to the "respond to last url"' do
+ expect_redirect('gone_postal', "request/#{@dog_request.id}/response/#{@dog_request.get_last_public_response.id}?gone_postal=1")
+ end
+
end
- it 'should redirect to the "help general url" when status is updated to "requires admin"' do
- expect_redirect('requires_admin', "help/contact")
+ context 'when status updated to "internal review"' do
+
+ it 'should redirect to the "request url"' do
+ expect_redirect('internal_review', request_url)
+ end
+
end
- it 'should redirect to the "help general url" when status is updated to "error message"' do
- expect_redirect('error_message', "help/contact")
+ context 'when status is updated to "requires admin"' do
+
+ it 'should redirect to the "request url"' do
+ post :describe_state, :incoming_message => {
+ :described_state => 'requires_admin',
+ :message => "A message" },
+ :id => @dog_request.id,
+ :last_info_request_event_id => @dog_request.last_event_id_needing_description
+ response.should redirect_to show_request_url(:url_title => @dog_request.url_title)
+ end
+
end
- it 'should redirect to the "respond to last url url" when status is updated to "user_withdrawn"' do
- expect_redirect('user_withdrawn', "request/#{@dog_request.id}/response/#{@dog_request.get_last_response.id}")
+ context 'when status is updated to "error message"' do
+
+ it 'should redirect to the "request url"' do
+ post :describe_state, :incoming_message => {
+ :described_state => 'error_message',
+ :message => "A message" },
+ :id => @dog_request.id,
+ :last_info_request_event_id => @dog_request.last_event_id_needing_description
+ response.should redirect_to show_request_url(:url_title => @dog_request.url_title)
+ end
+
end
+ context 'when status is updated to "user_withdrawn"' do
+
+ it 'should redirect to the "respond to last url url" ' do
+ expect_redirect('user_withdrawn', "request/#{@dog_request.id}/response/#{@dog_request.get_last_public_response.id}")
+ end
+
+ end
end
end
@@ -1611,7 +1771,7 @@ describe RequestController, "when classifying an information request" do
end
describe RequestController, "when sending a followup message" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -1653,7 +1813,7 @@ describe RequestController, "when sending a followup message" do
# fake that this is a clarification
info_requests(:fancy_dog_request).set_described_state('waiting_clarification')
info_requests(:fancy_dog_request).described_state.should == 'waiting_clarification'
- info_requests(:fancy_dog_request).get_last_response_event.calculated_state.should == 'waiting_clarification'
+ info_requests(:fancy_dog_request).get_last_public_response_event.calculated_state.should == 'waiting_clarification'
# make the followup
session[:user_id] = users(:bob_smith_user).id
@@ -1664,14 +1824,14 @@ describe RequestController, "when sending a followup message" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /What a useless response! You suck./
- mail.to_addrs.first.to_s.should == "FOI Person <foiperson@localhost>"
+ mail.to_addrs.first.to_s.should == "foiperson@localhost"
response.should redirect_to(:action => 'show', :url_title => info_requests(:fancy_dog_request).url_title)
# and that the status changed
info_requests(:fancy_dog_request).reload
info_requests(:fancy_dog_request).described_state.should == 'waiting_response'
- info_requests(:fancy_dog_request).get_last_response_event.calculated_state.should == 'waiting_clarification'
+ info_requests(:fancy_dog_request).get_last_public_response_event.calculated_state.should == 'waiting_clarification'
end
it "should give an error if the same followup is submitted twice" do
@@ -1693,7 +1853,7 @@ end
# it can't check the URLs in the emails I don't think, ugh.
describe RequestController, "sending overdue request alerts" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -1711,9 +1871,9 @@ describe RequestController, "sending overdue request alerts" do
mail = chicken_mails[0]
mail.body.should =~ /promptly, as normally/
- mail.to_addrs.first.to_s.should == info_requests(:naughty_chicken_request).user.name_and_email
+ mail.to_addrs.first.to_s.should == info_requests(:naughty_chicken_request).user.email
- mail.body =~ /(http:\/\/.*\/c\/(.*))/
+ mail.body.to_s =~ /(http:\/\/.*\/c\/(.*))/
mail_url = $1
mail_token = $2
@@ -1740,7 +1900,7 @@ describe RequestController, "sending overdue request alerts" do
mail = chicken_mails[0]
mail.body.should =~ /promptly, as normally/
- mail.to_addrs.first.to_s.should == info_requests(:naughty_chicken_request).user.name_and_email
+ mail.to_addrs.first.to_s.should == info_requests(:naughty_chicken_request).user.email
end
it "should send not actually send the overdue alert if the user is banned but should
@@ -1768,9 +1928,9 @@ describe RequestController, "sending overdue request alerts" do
mail = chicken_mails[0]
mail.body.should =~ /required by law/
- mail.to_addrs.first.to_s.should == info_requests(:naughty_chicken_request).user.name_and_email
+ mail.to_addrs.first.to_s.should == info_requests(:naughty_chicken_request).user.email
- mail.body =~ /(http:\/\/.*\/c\/(.*))/
+ mail.body.to_s =~ /(http:\/\/.*\/c\/(.*))/
mail_url = $1
mail_token = $2
@@ -1838,7 +1998,7 @@ describe RequestController, "sending overdue request alerts" do
end
describe RequestController, "sending unclassified new response reminder alerts" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -1851,8 +2011,8 @@ describe RequestController, "sending unclassified new response reminder alerts"
deliveries.size.should == 3 # sufficiently late it sends reminders too
mail = deliveries[0]
mail.body.should =~ /To let everyone know/
- mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
- mail.body =~ /(http:\/\/.*\/c\/(.*))/
+ mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.email
+ mail.body.to_s =~ /(http:\/\/.*\/c\/(.*))/
mail_url = $1
mail_token = $2
@@ -1868,7 +2028,7 @@ describe RequestController, "sending unclassified new response reminder alerts"
end
describe RequestController, "clarification required alerts" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
end
@@ -1887,8 +2047,8 @@ describe RequestController, "clarification required alerts" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /asked you to explain/
- mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
- mail.body =~ /(http:\/\/.*\/c\/(.*))/
+ mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.email
+ mail.body.to_s =~ /(http:\/\/.*\/c\/(.*))/
mail_url = $1
mail_token = $2
@@ -1921,7 +2081,7 @@ describe RequestController, "clarification required alerts" do
end
describe RequestController, "comment alerts" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
end
@@ -1940,8 +2100,8 @@ describe RequestController, "comment alerts" do
deliveries = ActionMailer::Base.deliveries
mail = deliveries[0]
mail.body.should =~ /has annotated your/
- mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
- mail.body =~ /(http:\/\/.*)/
+ mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.email
+ mail.body.to_s =~ /(http:\/\/.*)/
mail_url = $1
mail_url.should match("/request/why_do_you_have_such_a_fancy_dog#comment-#{new_comment.id}")
@@ -1990,8 +2150,8 @@ describe RequestController, "comment alerts" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /There are 2 new annotations/
- mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
- mail.body =~ /(http:\/\/.*)/
+ mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.email
+ mail.body.to_s =~ /(http:\/\/.*)/
mail_url = $1
mail_url.should match("/request/why_do_you_have_such_a_fancy_dog#comment-#{comments(:silly_comment).id}")
@@ -2000,7 +2160,7 @@ describe RequestController, "comment alerts" do
end
describe RequestController, "when viewing comments" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
end
@@ -2008,22 +2168,26 @@ describe RequestController, "when viewing comments" do
it "should link to the user who submitted it" do
session[:user_id] = users(:bob_smith_user).id
get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.body.should have_tag("div#comment-1 h2", /Silly.*left an annotation/m)
- response.body.should_not have_tag("div#comment-1 h2", /You.*left an annotation/m)
+ response.body.should have_selector("div#comment-1 h2") do |s|
+ s.should contain /Silly.*left an annotation/m
+ s.should_not contain /You.*left an annotation/m
+ end
end
it "should link to the user who submitted to it, even if it is you" do
session[:user_id] = users(:silly_name_user).id
get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
- response.body.should have_tag("div#comment-1 h2", /Silly.*left an annotation/m)
- response.body.should_not have_tag("div#comment-1 h2", /You.*left an annotation/m)
+ response.body.should have_selector("div#comment-1 h2") do |s|
+ s.should contain /Silly.*left an annotation/m
+ s.should_not contain /You.*left an annotation/m
+ end
end
end
describe RequestController, "authority uploads a response from the web interface" do
- integrate_views
+ render_views
before(:each) do
# domain after the @ is used for authentication of FOI officers, so to test it
@@ -2061,7 +2225,7 @@ describe RequestController, "authority uploads a response from the web interface
session[:user_id] = @normal_user.id
# post up a photo of the parrot
- parrot_upload = fixture_file_upload('files/parrot.png','image/png')
+ parrot_upload = fixture_file_upload('/files/parrot.png','image/png')
post :upload_response, :url_title => 'why_do_you_have_such_a_fancy_dog',
:body => "Find attached a picture of a parrot",
:file_1 => parrot_upload,
@@ -2089,7 +2253,7 @@ describe RequestController, "authority uploads a response from the web interface
session[:user_id] = @foi_officer_user.id
# post up a photo of the parrot
- parrot_upload = fixture_file_upload('files/parrot.png','image/png')
+ parrot_upload = fixture_file_upload('/files/parrot.png','image/png')
post :upload_response, :url_title => 'why_do_you_have_such_a_fancy_dog',
:body => "Find attached a picture of a parrot",
:file_1 => parrot_upload,
@@ -2113,7 +2277,6 @@ describe RequestController, "authority uploads a response from the web interface
end
describe RequestController, "when showing JSON version for API" do
-
before(:each) do
load_raw_emails_data
end
@@ -2132,25 +2295,28 @@ describe RequestController, "when showing JSON version for API" do
end
describe RequestController, "when doing type ahead searches" do
+ render_views
- integrate_views
+ before :each do
+ get_fixtures_xapian_index
+ end
it "should return nothing for the empty query string" do
get :search_typeahead, :q => ""
- response.should render_template('request/_search_ahead.rhtml')
+ response.should render_template('request/_search_ahead')
assigns[:xapian_requests].should be_nil
end
it "should return a request matching the given keyword, but not users with a matching description" do
get :search_typeahead, :q => "chicken"
- response.should render_template('request/_search_ahead.rhtml')
+ response.should render_template('request/_search_ahead')
assigns[:xapian_requests].results.size.should == 1
assigns[:xapian_requests].results[0][:model].title.should == info_requests(:naughty_chicken_request).title
end
it "should return all requests matching any of the given keywords" do
get :search_typeahead, :q => "money dog"
- response.should render_template('request/_search_ahead.rhtml')
+ response.should render_template('request/_search_ahead')
assigns[:xapian_requests].results.map{|x|x[:model].info_request}.should =~ [
info_requests(:fancy_dog_request),
info_requests(:naughty_chicken_request),
@@ -2160,13 +2326,13 @@ describe RequestController, "when doing type ahead searches" do
it "should not return matches for short words" do
get :search_typeahead, :q => "a"
- response.should render_template('request/_search_ahead.rhtml')
+ response.should render_template('request/_search_ahead')
assigns[:xapian_requests].should be_nil
end
it "should do partial matches for longer words" do
get :search_typeahead, :q => "chick"
- response.should render_template('request/_search_ahead.rhtml')
+ response.should render_template('request/_search_ahead')
assigns[:xapian_requests].results.size.should ==1
end
@@ -2191,7 +2357,12 @@ describe RequestController, "when doing type ahead searches" do
end
describe RequestController, "when showing similar requests" do
- integrate_views
+ render_views
+
+ before do
+ get_fixtures_xapian_index
+ load_raw_emails_data
+ end
it "should work" do
get :similar, :url_title => info_requests(:badger_request).url_title
@@ -2223,93 +2394,7 @@ describe RequestController, "when showing similar requests" do
end
-
-describe RequestController, "when reporting a request when not logged in" do
- it "should only allow logged-in users to report requests" do
- get :report_request, :url_title => info_requests(:badger_request).url_title
- post_redirect = PostRedirect.get_last_post_redirect
- response.should redirect_to(:controller => 'user', :action => 'signin', :token => post_redirect.token)
- end
-end
-
-describe RequestController, "when reporting a request (logged in)" do
- integrate_views
-
- before do
- @user = users(:robin_user)
- session[:user_id] = @user.id
- end
-
- it "should 404 for non-existent requests" do
- lambda {
- post :report_request, :url_title => "hjksfdhjk_louytu_qqxxx"
- }.should raise_error(ActiveRecord::RecordNotFound)
- end
-
- it "should mark a request as having been reported" do
- ir = info_requests(:badger_request)
- title = ir.url_title
- get :show, :url_title => title
- assigns[:info_request].attention_requested.should == false
-
- post :report_request, :url_title => title
- response.should redirect_to(:action => :show, :url_title => title)
-
- get :show, :url_title => title
- response.should be_success
- assigns[:info_request].attention_requested.should == true
- assigns[:info_request].described_state.should == "attention_requested"
- end
-
- it "should not allow a request to be reported twice" do
- title = info_requests(:badger_request).url_title
-
- post :report_request, :url_title => title
- response.should redirect_to(:action => :show, :url_title => title)
- get :show, :url_title => title
- response.should be_success
- response.body.should include("has been reported")
-
- post :report_request, :url_title => title
- response.should redirect_to(:action => :show, :url_title => title)
- get :show, :url_title => title
- response.should be_success
- response.body.should include("has already been reported")
- end
-
- it "should let users know a request has been reported" do
- title = info_requests(:badger_request).url_title
- get :show, :url_title => title
- response.body.should include("Offensive?")
-
- post :report_request, :url_title => title
- response.should redirect_to(:action => :show, :url_title => title)
-
- get :show, :url_title => title
- response.body.should_not include("Offensive?")
- response.body.should include("This request has been reported")
-
- info_requests(:badger_request).set_described_state("successful")
- get :show, :url_title => title
- response.body.should_not include("This request has been reported")
- response.body.should =~ (/the site administrators.*have not hidden it/)
- end
-
- it "should send an email from the reporter to admins" do
- ir = info_requests(:badger_request)
- title = ir.url_title
- post :report_request, :url_title => title
- deliveries = ActionMailer::Base.deliveries
- deliveries.size.should == 1
- mail = deliveries[0]
- mail.subject.should =~ /attention_requested/
- mail.from.should include(@user.email)
- mail.body.should include(@user.name)
- end
-end
-
describe RequestController, "when caching fragments" do
-
it "should not fail with long filenames" do
long_name = "blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah.txt"
info_request = mock(InfoRequest, :user_can_view? => true,
@@ -2319,13 +2404,15 @@ describe RequestController, "when caching fragments" do
:info_request_id => 132,
:id => 44,
:get_attachments_for_display => nil,
- :html_mask_stuff! => nil)
+ :html_mask_stuff! => nil,
+ :user_can_view? => true,
+ :all_can_view? => true)
attachment = mock(FoiAttachment, :display_filename => long_name,
:body_as_html => ['some text', 'wrapper'])
IncomingMessage.stub!(:find).with("44").and_return(incoming_message)
- IncomingMessage.stub!(:get_attachment_by_url_part_number).and_return(attachment)
+ IncomingMessage.stub!(:get_attachment_by_url_part_number_and_filename).and_return(attachment)
InfoRequest.stub!(:find).with("132").and_return(info_request)
- params = { :file_name => [long_name],
+ params = { :file_name => long_name,
:controller => "request",
:action => "get_attachment_as_html",
:id => "132",
@@ -2336,4 +2423,268 @@ describe RequestController, "when caching fragments" do
end
+describe RequestController, "#new_batch" do
+
+ context "when batch requests is enabled" do
+
+ before do
+ AlaveteliConfiguration.stub!(:allow_batch_requests).and_return(true)
+ end
+
+ context "when the current user can make batch requests" do
+
+ before do
+ @user = FactoryGirl.create(:user, :can_make_batch_requests => true)
+ @public_body = FactoryGirl.create(:public_body)
+ @other_public_body = FactoryGirl.create(:public_body)
+ @public_body_ids = [@public_body.id, @other_public_body.id]
+ @default_post_params = { :info_request => { :title => "What does it all mean?",
+ :tag_string => "" },
+ :public_body_ids => @public_body_ids,
+ :outgoing_message => { :body => "This is a silly letter." },
+ :submitted_new_request => 1,
+ :preview => 1 }
+ end
+
+ it 'should be successful' do
+ get :new_batch, {:public_body_ids => @public_body_ids}, {:user_id => @user.id}
+ response.should be_success
+ end
+
+ it 'should render the "new" template' do
+ get :new_batch, {:public_body_ids => @public_body_ids}, {:user_id => @user.id}
+ response.should render_template('request/new')
+ end
+
+ it 'should redirect to "select_authorities" if no public_body_ids param is passed' do
+ get :new_batch, {}, {:user_id => @user.id}
+ response.should redirect_to select_authorities_path
+ end
+
+ it "should render 'preview' when given a good title and body" do
+ post :new_batch, @default_post_params, { :user_id => @user.id }
+ response.should render_template('preview')
+ end
+
+ it "should give an error and render 'new' template when a summary isn't given" do
+ @default_post_params[:info_request].delete(:title)
+ post :new_batch, @default_post_params, { :user_id => @user.id }
+ assigns[:info_request].errors[:title].should == ['Please enter a summary of your request']
+ response.should render_template('new')
+ end
+
+ it "should allow re-editing of a request" do
+ params = @default_post_params.merge(:preview => 0, :reedit => 1)
+ post :new_batch, params, { :user_id => @user.id }
+ response.should render_template('new')
+ end
+
+ context "on success" do
+
+ def make_request
+ @params = @default_post_params.merge(:preview => 0)
+ post :new_batch, @params, { :user_id => @user.id }
+ end
+
+ it 'should create an info request batch and redirect to the new batch on success' do
+ make_request
+ new_info_request_batch = assigns[:info_request_batch]
+ new_info_request_batch.should_not be_nil
+ response.should redirect_to(info_request_batch_path(new_info_request_batch))
+ end
+
+ it 'should prevent double submission of a batch request' do
+ make_request
+ post :new_batch, @params, { :user_id => @user.id }
+ response.should render_template('new')
+ assigns[:existing_batch].should_not be_nil
+ end
+
+ it 'should display a success notice' do
+ make_request
+ notice_text = "<p>Your Freedom of Information requests will be <strong>sent</strong> shortly!"
+ flash[:notice].should match notice_text
+ end
+
+ end
+
+ context "when the user is banned" do
+
+ before do
+ @user.ban_text = "bad behaviour"
+ @user.save!
+ end
+
+ it 'should show the "banned" template' do
+ post :new_batch, @default_post_params, { :user_id => @user.id }
+ response.should render_template('user/banned')
+ assigns[:details].should == 'bad behaviour'
+ end
+
+ end
+
+ end
+
+ context "when the current user can't make batch requests" do
+
+ render_views
+
+ before do
+ @user = FactoryGirl.create(:user)
+ end
+
+ it 'should return a 403 with an appropriate message' do
+ get :new_batch, {}, {:user_id => @user.id}
+ response.code.should == '403'
+ response.body.should match("Users cannot usually make batch requests to multiple authorities at once")
+ end
+
+ end
+
+ context 'when there is no logged-in user' do
+
+ it 'should return a redirect to the login page' do
+ get :new_batch
+ post_redirect = PostRedirect.get_last_post_redirect
+ response.should redirect_to(:controller => 'user', :action => 'signin', :token => post_redirect.token)
+ end
+ end
+
+
+ end
+
+ context "when batch requests is not enabled" do
+
+ it 'should return a 404' do
+ Rails.application.config.stub!(:consider_all_requests_local).and_return(false)
+ get :new_batch
+ response.code.should == '404'
+ end
+
+ end
+
+end
+
+describe RequestController, "#select_authorities" do
+
+ context "when batch requests is enabled" do
+
+ before do
+ get_fixtures_xapian_index
+ load_raw_emails_data
+ AlaveteliConfiguration.stub!(:allow_batch_requests).and_return(true)
+ end
+
+ context "when the current user can make batch requests" do
+
+ before do
+ @user = FactoryGirl.create(:user, :can_make_batch_requests => true)
+ end
+
+ context 'when asked for HTML' do
+
+ it 'should be successful' do
+ get :select_authorities, {}, {:user_id => @user.id}
+ response.should be_success
+ end
+
+ it 'should render the "select_authorities" template' do
+ get :select_authorities, {}, {:user_id => @user.id}
+ response.should render_template('request/select_authorities')
+ end
+
+ it 'should assign a list of search results to the view if passed a query' do
+ get :select_authorities, {:public_body_query => "Quango"}, {:user_id => @user.id}
+ assigns[:search_bodies].results.size.should == 1
+ assigns[:search_bodies].results[0][:model].name.should == public_bodies(:geraldine_public_body).name
+ end
+
+ it 'should assign a list of public bodies to the view if passed a list of ids' do
+ get :select_authorities, {:public_body_ids => [public_bodies(:humpadink_public_body).id]},
+ {:user_id => @user.id}
+ assigns[:public_bodies].size.should == 1
+ assigns[:public_bodies][0].name.should == public_bodies(:humpadink_public_body).name
+ end
+
+ it 'should subtract a list of public bodies to remove from the list of bodies assigned to
+ the view' do
+ get :select_authorities, {:public_body_ids => [public_bodies(:humpadink_public_body).id,
+ public_bodies(:geraldine_public_body).id],
+ :remove_public_body_ids => [public_bodies(:geraldine_public_body).id]},
+ {:user_id => @user.id}
+ assigns[:public_bodies].size.should == 1
+ assigns[:public_bodies][0].name.should == public_bodies(:humpadink_public_body).name
+ end
+
+ end
+
+ context 'when asked for JSON', :focus => true do
+
+ it 'should be successful' do
+ get :select_authorities, {:public_body_query => "Quan", :format => 'json'}, {:user_id => @user.id}
+ response.should be_success
+ end
+
+ it 'should return a list of public body names and ids' do
+ get :select_authorities, {:public_body_query => "Quan", :format => 'json'},
+ {:user_id => @user.id}
+
+ JSON(response.body).should == [{ 'id' => public_bodies(:geraldine_public_body).id,
+ 'name' => public_bodies(:geraldine_public_body).name }]
+ end
+
+ it 'should return an empty list if no search is passed' do
+ get :select_authorities, {:format => 'json' },{:user_id => @user.id}
+ JSON(response.body).should == []
+ end
+
+ it 'should return an empty list if there are no bodies' do
+ get :select_authorities, {:public_body_query => 'fknkskalnr', :format => 'json' },
+ {:user_id => @user.id}
+ JSON(response.body).should == []
+ end
+
+ end
+
+ end
+
+ context "when the current user can't make batch requests" do
+
+ render_views
+
+ before do
+ @user = FactoryGirl.create(:user)
+ end
+
+ it 'should return a 403 with an appropriate message' do
+ get :select_authorities, {}, {:user_id => @user.id}
+ response.code.should == '403'
+ response.body.should match("Users cannot usually make batch requests to multiple authorities at once")
+ end
+
+ end
+
+ context 'when there is no logged-in user' do
+
+ it 'should return a redirect to the login page' do
+ get :select_authorities
+ post_redirect = PostRedirect.get_last_post_redirect
+ response.should redirect_to(:controller => 'user', :action => 'signin', :token => post_redirect.token)
+ end
+ end
+
+
+ end
+
+ context "when batch requests is not enabled" do
+
+ it 'should return a 404' do
+ Rails.application.config.stub!(:consider_all_requests_local).and_return(false)
+ get :select_authorities
+ response.code.should == '404'
+ end
+
+ end
+
+end
diff --git a/spec/controllers/services_controller_spec.rb b/spec/controllers/services_controller_spec.rb
index a9950d520..14731f090 100644
--- a/spec/controllers/services_controller_spec.rb
+++ b/spec/controllers/services_controller_spec.rb
@@ -4,7 +4,7 @@ require 'fakeweb'
describe ServicesController, "when returning a message for people in other countries" do
- integrate_views
+ render_views
# store and restore the locale in the context of the test suite to isolate
# changes made in these tests
@@ -54,27 +54,27 @@ describe ServicesController, "when returning a message for people in other count
it "should return the 'another country' message if the service responds OK" do
config = MySociety::Config.load_default()
config['ISO_COUNTRY_CODE'] = "DE"
- Configuration.stub!(:gaze_url).and_return('http://denmark.com')
+ AlaveteliConfiguration.stub!(:gaze_url).and_return('http://denmark.com')
FakeWeb.register_uri(:get, %r|denmark.com|, :body => "DK")
get :other_country_message
response.should be_success
- response.body.should == 'Hello! We have an <a href="/help/alaveteli?country_name=Deutschland">important message</a> for visitors outside Deutschland <span class="close-button">X</span>'
+ response.body.should == 'Hello! We have an <a href="/help/alaveteli?country_name=Deutschland">important message</a> for visitors outside Deutschland'
end
it "should default to no message if the country_from_ip domain doesn't exist" do
- Configuration.stub!(:gaze_url).and_return('http://12123sdf14qsd.com')
+ AlaveteliConfiguration.stub!(:gaze_url).and_return('http://12123sdf14qsd.com')
get :other_country_message
response.should be_success
response.body.should == ''
end
it "should default to no message if the country_from_ip service doesn't exist" do
- Configuration.stub!(:gaze_url).and_return('http://www.google.com')
+ AlaveteliConfiguration.stub!(:gaze_url).and_return('http://www.google.com')
get :other_country_message
response.should be_success
response.body.should == ''
end
it "should default to no message if the country_from_ip service returns an error" do
FakeWeb.register_uri(:get, %r|500.com|, :body => "Error", :status => ["500", "Error"])
- Configuration.stub!(:gaze_url).and_return('http://500.com')
+ AlaveteliConfiguration.stub!(:gaze_url).and_return('http://500.com')
get :other_country_message
response.should be_success
response.body.should == ''
diff --git a/spec/controllers/track_controller_spec.rb b/spec/controllers/track_controller_spec.rb
index c785960b5..40865d2b9 100644
--- a/spec/controllers/track_controller_spec.rb
+++ b/spec/controllers/track_controller_spec.rb
@@ -10,7 +10,7 @@ describe TrackController, "when making a new track on a request" do
:tracking_user_id= => nil)
TrackThing.stub!(:create_track_for_request).and_return(@track_thing)
TrackThing.stub!(:create_track_for_search_query).and_return(@track_thing)
- TrackThing.stub!(:find_by_existing_track).and_return(nil)
+ TrackThing.stub!(:find_existing).and_return(nil)
InfoRequest.stub!(:find_by_url_title!) do |url_title|
if url_title == "myrequest"
@ir
@@ -49,15 +49,47 @@ describe TrackController, "when making a new track on a request" do
it "should save a search track and redirect to the right place" do
session[:user_id] = @user.id
@track_thing.should_receive(:save!)
- get :track_search_query, :query_array => ["bob variety:sent"], :feed => 'track'
+ get :track_search_query, :query_array => "bob variety:sent", :feed => 'track'
response.should redirect_to(:controller => 'general', :action => 'search', :combined => ["bob", "requests"])
end
end
+describe TrackController, "when unsubscribing from a track" do
+
+ before do
+ @track_thing = FactoryGirl.create(:track_thing)
+ end
+
+ it 'should destroy the track thing' do
+ get :update, {:track_id => @track_thing.id,
+ :track_medium => 'delete',
+ :r => 'http://example.com'},
+ {:user_id => @track_thing.tracking_user.id}
+ TrackThing.find(:first, :conditions => ['id = ? ', @track_thing.id]).should == nil
+ end
+
+ it 'should redirect to a URL on the site' do
+ get :update, {:track_id => @track_thing.id,
+ :track_medium => 'delete',
+ :r => '/'},
+ {:user_id => @track_thing.tracking_user.id}
+ response.should redirect_to('/')
+ end
+
+ it 'should not redirect to a url on another site' do
+ track_thing = FactoryGirl.create(:track_thing)
+ get :update, {:track_id => @track_thing.id,
+ :track_medium => 'delete',
+ :r => 'http://example.com/'},
+ {:user_id => @track_thing.tracking_user.id}
+ response.should redirect_to('/')
+ end
+
+end
+
describe TrackController, "when sending alerts for a track" do
- integrate_views
- include LinkToHelper # for main_url
+ render_views
before(:each) do
load_raw_emails_data
@@ -65,9 +97,6 @@ describe TrackController, "when sending alerts for a track" do
end
it "should send alerts" do
- # Don't do clever locale-insertion-unto-URL stuff
- old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
# set the time the comment event happened at to within the last week
ire = info_request_events(:silly_comment_event)
@@ -81,7 +110,7 @@ describe TrackController, "when sending alerts for a track" do
mail = deliveries[0]
mail.body.should =~ /Alter your subscription/
mail.to_addrs.first.to_s.should include(users(:silly_name_user).email)
- mail.body =~ /(http:\/\/.*\/c\/(.*))/
+ mail.body.to_s =~ /(http:\/\/.*\/c\/(.*))/
mail_url = $1
mail_token = $2
@@ -105,7 +134,7 @@ describe TrackController, "when sending alerts for a track" do
# Given we can't click the link, check the token is right instead
post_redirect = PostRedirect.find_by_email_token(mail_token)
- expected_url = main_url("/user/" + users(:silly_name_user).url_name + "#email_subscriptions") # XXX can't call URL making functions here, what is correct way to do this?
+ expected_url = show_user_url(:url_name => users(:silly_name_user).url_name, :anchor => "email_subscriptions")
post_redirect.uri.should == expected_url
# Check nothing more is delivered if we try again
@@ -113,9 +142,6 @@ describe TrackController, "when sending alerts for a track" do
TrackMailer.alert_tracks
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 0
-
- # Restore the routing filters
- ActionController::Routing::Routes.filters = old_filters
end
it "should send localised alerts" do
@@ -134,7 +160,7 @@ describe TrackController, "when sending alerts for a track" do
end
describe TrackController, "when viewing RSS feed for a track" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -146,6 +172,7 @@ describe TrackController, "when viewing RSS feed for a track" do
get :track_request, :feed => 'feed', :url_title => track_thing.info_request.url_title
response.should render_template('track/atom_feed')
+ response.content_type.should == 'application/atom+xml'
# XXX should check it is an atom.builder type being rendered, not sure how to
assigns[:xapian_object].matches_estimated.should == 3
@@ -160,11 +187,23 @@ describe TrackController, "when viewing RSS feed for a track" do
get :track_user, :feed => 'feed', :url_name => "there_is_no_such_user"
}.should raise_error(ActiveRecord::RecordNotFound)
end
+
+ it 'should return atom/xml for a feed url without format specified, even if the
+ requester prefers json' do
+
+ request.env['HTTP_ACCEPT'] = 'application/json,text/xml'
+ track_thing = track_things(:track_fancy_dog_request)
+
+ get :track_request, :feed => 'feed', :url_title => track_thing.info_request.url_title
+ response.should render_template('track/atom_feed')
+ response.content_type.should == 'application/atom+xml'
+ end
+
end
describe TrackController, "when viewing JSON version of a track feed" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -206,7 +245,7 @@ end
describe TrackController, "when tracking a public body" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
diff --git a/spec/controllers/user_controller_spec.rb b/spec/controllers/user_controller_spec.rb
index 23006803b..cf361d898 100644
--- a/spec/controllers/user_controller_spec.rb
+++ b/spec/controllers/user_controller_spec.rb
@@ -3,68 +3,128 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
# XXX Use route_for or params_from to check /c/ links better
# http://rspec.rubyforge.org/rspec-rails/1.1.12/classes/Spec/Rails/Example/ControllerExampleGroup.html
+describe UserController, "when redirecting a show request to a canonical url" do
+
+ it "should redirect to lower case name if given one with capital letters" do
+ get :show, :url_name => "Bob_Smith"
+ response.should redirect_to(:controller => 'user', :action => 'show', :url_name => "bob_smith")
+ end
+
+ it 'should redirect a long non-canonical name that has a numerical suffix,
+ retaining the suffix' do
+ get :show, :url_name => 'Bob_SmithBob_SmithBob_SmithBob_S_2'
+ response.should redirect_to(:controller => 'user',
+ :action => 'show',
+ :url_name => 'bob_smithbob_smithbob_smithbob_s_2')
+ end
+
+ it 'should not redirect a long canonical name that has a numerical suffix' do
+ User.stub!(:find).with(:first, anything()).and_return(mock_model(User,
+ :url_name => 'bob_smithbob_smithbob_smithbob_s_2',
+ :name => 'Bob Smith Bob Smith Bob Smith Bob Smith'))
+ User.stub!(:find).with(:all, anything()).and_return([])
+ get :show, :url_name => 'bob_smithbob_smithbob_smithbob_s_2'
+ response.should be_success
+ end
+
+end
describe UserController, "when showing a user" do
- integrate_views
+
before(:each) do
- load_raw_emails_data
- get_fixtures_xapian_index
+ @user = FactoryGirl.create(:user)
end
it "should be successful" do
- get :show, :url_name => "bob_smith"
+ get :show, :url_name => @user.url_name
response.should be_success
end
- it "should redirect to lower case name if given one with capital letters" do
- get :show, :url_name => "Bob_Smith"
- response.should redirect_to(:controller => 'user', :action => 'show', :url_name => "bob_smith")
- end
-
it "should render with 'show' template" do
- get :show, :url_name => "bob_smith"
+ get :show, :url_name => @user.url_name
response.should render_template('show')
end
- it "should distinguish between 'my profile' and 'my requests' for logged in users" do
- session[:user_id] = users(:bob_smith_user).id
- get :show, :url_name => "bob_smith", :view => 'requests'
- response.body.should_not include("Change your password")
- response.body.should match(/Your [0-9]+ Freedom of Information requests/)
- get :show, :url_name => "bob_smith", :view => 'profile'
- response.body.should include("Change your password")
- response.body.should_not match(/Your [0-9]+ Freedom of Information requests/)
+ it "should assign the user" do
+ get :show, :url_name => @user.url_name
+ assigns[:display_user].should == @user
end
- it "should assign the user" do
- get :show, :url_name => "bob_smith"
- assigns[:display_user].should == users(:bob_smith_user)
+ context "when viewing the user's own profile" do
+
+ render_views
+
+ def make_request
+ get :show, {:url_name => @user.url_name, :view => 'profile'}, {:user_id => @user.id}
+ end
+
+ it 'should not show requests, or batch requests, but should show account options' do
+ make_request
+ response.body.should_not match(/Freedom of Information requests made by you/)
+ assigns[:show_batches].should be_false
+ response.body.should include("Change your password")
+ end
+
end
- it "should search the user's contributions" do
- get :show, :url_name => "bob_smith"
- assigns[:xapian_requests].results.map{|x|x[:model].info_request}.should =~ InfoRequest.all(
- :conditions => "user_id = #{users(:bob_smith_user).id}")
+ context "when viewing a user's own requests" do
+
+ render_views
+
+ def make_request
+ get :show, {:url_name => @user.url_name, :view => 'requests'}, {:user_id => @user.id}
+ end
+
+ it 'should show requests, batch requests, but no account options' do
+ make_request
+ response.body.should match(/Freedom of Information requests made by you/)
+ assigns[:show_batches].should be_true
+ response.body.should_not include("Change your password")
+ end
- get :show, :url_name => "bob_smith", :user_query => "money"
- assigns[:xapian_requests].results.map{|x|x[:model].info_request}.should =~ [
- info_requests(:naughty_chicken_request),
- info_requests(:another_boring_request),
- ]
end
- it "should not show unconfirmed users" do
- begin
- get :show, :url_name => "unconfirmed_user"
- rescue => e
+end
+
+describe UserController, "when showing a user" do
+
+ context 'when using fixture data' do
+
+ before do
+ load_raw_emails_data
+ get_fixtures_xapian_index
end
- e.should be_an_instance_of(ActiveRecord::RecordNotFound)
+
+ it "should search the user's contributions" do
+ get :show, :url_name => "bob_smith"
+ assigns[:xapian_requests].results.map{|x|x[:model].info_request}.should =~ InfoRequest.all(
+ :conditions => "user_id = #{users(:bob_smith_user).id}")
+
+ get :show, :url_name => "bob_smith", :user_query => "money"
+ assigns[:xapian_requests].results.map{|x|x[:model].info_request}.should =~ [
+ info_requests(:naughty_chicken_request),
+ info_requests(:another_boring_request),
+ ]
+ end
+
+ it "should not show unconfirmed users" do
+ begin
+ get :show, :url_name => "unconfirmed_user"
+ rescue => e
+ end
+ e.should be_an_instance_of(ActiveRecord::RecordNotFound)
+ end
end
end
describe UserController, "when signing in" do
- integrate_views
+ render_views
+
+ before do
+ # Don't call out to external url during tests
+ controller.stub!(:country_from_ip).and_return('gb')
+ end
def get_last_postredirect
post_redirects = PostRedirect.find_by_sql("select * from post_redirects order by id desc limit 1")
@@ -74,7 +134,7 @@ describe UserController, "when signing in" do
it "should show sign in / sign up page" do
get :signin
- response.should have_tag("input#signin_token")
+ response.should have_selector("input#signin_token")
end
it "should create post redirect to / when you just go to /signin" do
@@ -100,9 +160,6 @@ describe UserController, "when signing in" do
end
it "should log in when you give right email/password, and redirect to where you were" do
- old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
-
get :signin, :r => "/list"
response.should render_template('sign')
post_redirect = get_last_postredirect
@@ -112,15 +169,10 @@ describe UserController, "when signing in" do
session[:user_id].should == users(:bob_smith_user).id
# response doesn't contain /en/ but redirect_to does...
response.should redirect_to(:controller => 'request', :action => 'list', :post_redirect => 1)
- response.should_not send_email
-
- ActionController::Routing::Routes.filters = old_filters
+ ActionMailer::Base.deliveries.should be_empty
end
it "should not log you in if you use an invalid PostRedirect token, and shouldn't give 500 error either" do
- old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
-
post_redirect = "something invalid"
lambda {
post :signin, { :user_signin => { :email => 'bob@localhost', :password => 'jonespassword' },
@@ -131,8 +183,6 @@ describe UserController, "when signing in" do
:token => post_redirect }
response.should render_template('sign')
assigns[:post_redirect].should == nil
-
- ActionController::Routing::Routes.filters = old_filters
end
# No idea how to test this in the test framework :(
@@ -152,65 +202,57 @@ describe UserController, "when signing in" do
:token => post_redirect.token
}
response.should render_template('confirm')
- response.should send_email
+ ActionMailer::Base.deliveries.should_not be_empty
end
it "should confirm your email, log you in and redirect you to where you were after you click an email link" do
- old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
-
get :signin, :r => "/list"
post_redirect = get_last_postredirect
post :signin, { :user_signin => { :email => 'unconfirmed@localhost', :password => 'jonespassword' },
:token => post_redirect.token
}
- response.should send_email
+ ActionMailer::Base.deliveries.should_not be_empty
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 1
mail = deliveries[0]
- mail.body =~ /(http:\/\/.*(\/c\/(.*)))/
+ mail.body.to_s =~ /(http:\/\/.*(\/c\/(.*)))/
mail_url = $1
mail_path = $2
mail_token = $3
# check is right confirmation URL
mail_token.should == post_redirect.email_token
- params_from(:get, mail_path).should == { :controller => 'user', :action => 'confirm', :email_token => mail_token }
+ Rails.application.routes.recognize_path(mail_path).should == { :controller => 'user', :action => 'confirm', :email_token => mail_token }
# check confirmation URL works
session[:user_id].should be_nil
get :confirm, :email_token => post_redirect.email_token
session[:user_id].should == users(:unconfirmed_user).id
response.should redirect_to(:controller => 'request', :action => 'list', :post_redirect => 1)
-
- ActionController::Routing::Routes.filters = old_filters
end
it "should keep you logged in if you click a confirmation link and are already logged in as an admin" do
- old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
-
get :signin, :r => "/list"
post_redirect = get_last_postredirect
post :signin, { :user_signin => { :email => 'unconfirmed@localhost', :password => 'jonespassword' },
:token => post_redirect.token
}
- response.should send_email
+ ActionMailer::Base.deliveries.should_not be_empty
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 1
mail = deliveries[0]
- mail.body =~ /(http:\/\/.*(\/c\/(.*)))/
+ mail.body.to_s =~ /(http:\/\/.*(\/c\/(.*)))/
mail_url = $1
mail_path = $2
mail_token = $3
# check is right confirmation URL
mail_token.should == post_redirect.email_token
- params_from(:get, mail_path).should == { :controller => 'user', :action => 'confirm', :email_token => mail_token }
+ Rails.application.routes.recognize_path(mail_path).should == { :controller => 'user', :action => 'confirm', :email_token => mail_token }
# Log in as an admin
session[:user_id] = users(:admin_user).id
@@ -222,19 +264,23 @@ describe UserController, "when signing in" do
# And the redirect should still work, of course
response.should redirect_to(:controller => 'request', :action => 'list', :post_redirect => 1)
- ActionController::Routing::Routes.filters = old_filters
end
end
describe UserController, "when signing up" do
- integrate_views
+ render_views
+
+ before do
+ # Don't call out to external url during tests
+ controller.stub!(:country_from_ip).and_return('gb')
+ end
it "should be an error if you type the password differently each time" do
post :signup, { :user_signup => { :email => 'new@localhost', :name => 'New Person',
:password => 'sillypassword', :password_confirmation => 'sillypasswordtwo' }
}
- assigns[:user_signup].errors[:password].should == 'Please enter the same password twice'
+ assigns[:user_signup].errors[:password].should == ['Please enter the same password twice']
end
it "should be an error to sign up with a misformatted email" do
@@ -285,7 +331,7 @@ describe UserController, "when signing up" do
end
describe UserController, "when signing out" do
- integrate_views
+ render_views
it "should log you out and redirect to the home page" do
session[:user_id] = users(:bob_smith_user).id
@@ -295,21 +341,16 @@ describe UserController, "when signing out" do
end
it "should log you out and redirect you to where you were" do
- old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
-
session[:user_id] = users(:bob_smith_user).id
get :signout, :r => '/list'
session[:user_id].should be_nil
response.should redirect_to(:controller => 'request', :action => 'list')
-
- ActionController::Routing::Routes.filters = old_filters
end
end
describe UserController, "when sending another user a message" do
- integrate_views
+ render_views
it "should redirect to signin page if you go to the contact form and aren't signed in" do
get :contact, :id => users(:silly_name_user)
@@ -337,16 +378,16 @@ describe UserController, "when sending another user a message" do
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 1
mail = deliveries[0]
- mail.body.should include("Bob Smith has used #{Configuration::site_name} to send you the message below")
+ mail.body.should include("Bob Smith has used #{AlaveteliConfiguration::site_name} to send you the message below")
mail.body.should include("Just a test!")
#mail.to_addrs.first.to_s.should == users(:silly_name_user).name_and_email # XXX fix some nastiness with quoting name_and_email
- mail.from_addrs.first.to_s.should == users(:bob_smith_user).name_and_email
+ mail.from_addrs.first.to_s.should == users(:bob_smith_user).email
end
end
describe UserController, "when changing password" do
- integrate_views
+ render_views
it "should show the email form when not logged in" do
get :signchangepassword
@@ -386,7 +427,7 @@ describe UserController, "when changing password" do
post :signchangepassword, { :user => { :password => 'ooo', :password_confirmation => 'ooo' },
:submitted_signchangepassword_do => 1
}
- users(:bob_smith_user).hashed_password.should != old_hash
+ users(:bob_smith_user).reload.hashed_password.should_not == old_hash
response.should redirect_to(:controller => 'user', :action => 'show', :url_name => users(:bob_smith_user).url_name)
end
@@ -416,7 +457,7 @@ describe UserController, "when changing password" do
end
describe UserController, "when changing email address" do
- integrate_views
+ render_views
it "should require login" do
get :signchangeemail
@@ -500,10 +541,10 @@ describe UserController, "when changing email address" do
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 1
mail = deliveries[0]
- mail.body.should include("confirm that you want to change")
+ mail.body.should include("confirm that you want to \nchange")
mail.to.should == [ 'newbob@localhost' ]
- mail.body =~ /(http:\/\/.*(\/c\/(.*)))/
+ mail.body.to_s =~ /(http:\/\/.*(\/c\/(.*)))/
mail_url = $1
mail_path = $2
mail_token = $3
@@ -561,16 +602,13 @@ describe UserController, "when changing email address" do
end
describe UserController, "when using profile photos" do
- integrate_views
+ render_views
before do
@user = users(:bob_smith_user)
- @uploadedfile = File.open(file_fixture_name("parrot.png"))
- @uploadedfile.stub!(:original_filename).and_return('parrot.png')
-
- @uploadedfile_2 = File.open(file_fixture_name("parrot.jpg"))
- @uploadedfile_2.stub!(:original_filename).and_return('parrot.jpg')
+ @uploadedfile = fixture_file_upload("/files/parrot.png")
+ @uploadedfile_2 = fixture_file_upload("/files/parrot.jpg")
end
it "should not let you change profile photo if you're not logged in as the user" do
@@ -631,9 +669,10 @@ describe UserController, "when showing JSON version for API" do
end
describe UserController, "when viewing the wall" do
- integrate_views
+ render_views
before(:each) do
+ load_raw_emails_data
get_fixtures_xapian_index
end
diff --git a/spec/factories.rb b/spec/factories.rb
new file mode 100644
index 000000000..8efc53033
--- /dev/null
+++ b/spec/factories.rb
@@ -0,0 +1,165 @@
+FactoryGirl.define do
+
+ sequence(:email) { |n| "person#{n}@example.com" }
+ sequence(:name) { |n| "Example Public Body #{n}" }
+ sequence(:short_name) { |n| "Example Body #{n}" }
+
+ factory :foi_attachment do
+ factory :body_text do
+ content_type 'text/plain'
+ body { 'hereisthetext' }
+ end
+ factory :pdf_attachment do
+ content_type 'application/pdf'
+ filename 'interesting.pdf'
+ body { load_file_fixture('interesting.pdf') }
+ end
+ end
+
+ factory :incoming_message do
+ info_request
+ raw_email
+ last_parsed { 1.week.ago }
+ sent_at { 1.week.ago }
+
+ after_create do |incoming_message, evaluator|
+ FactoryGirl.create(:body_text,
+ :incoming_message => incoming_message,
+ :url_part_number => 1)
+ end
+
+ factory :plain_incoming_message do
+ last_parsed { nil }
+ sent_at { nil }
+ after_create do |incoming_message, evaluator|
+ data = load_file_fixture('incoming-request-plain.email')
+ data.gsub!('EMAIL_FROM', 'Bob Responder <bob@example.com>')
+ incoming_message.raw_email.data = data
+ incoming_message.raw_email.save!
+ end
+ end
+
+ factory :incoming_message_with_attachments do
+ # foi_attachments_count is declared as an ignored attribute and available in
+ # attributes on the factory, as well as the callback via the evaluator
+ ignore do
+ foi_attachments_count 2
+ end
+
+ # the after(:create) yields two values; the incoming_message instance itself and the
+ # evaluator, which stores all values from the factory, including ignored
+ # attributes;
+ after_create do |incoming_message, evaluator|
+ evaluator.foi_attachments_count.times do |count|
+ FactoryGirl.create(:pdf_attachment,
+ :incoming_message => incoming_message,
+ :url_part_number => count+2)
+ end
+ end
+ end
+ end
+
+ factory :raw_email
+
+ factory :outgoing_message do
+ factory :initial_request do
+ ignore do
+ status 'ready'
+ message_type 'initial_request'
+ body 'Some information please'
+ what_doing 'normal_sort'
+ end
+ initialize_with { OutgoingMessage.new({ :status => status,
+ :message_type => message_type,
+ :body => body,
+ :what_doing => what_doing }) }
+ after_create do |outgoing_message|
+ outgoing_message.send_message
+ end
+ end
+ end
+
+ factory :info_request do
+ title "Example Title"
+ public_body
+ user
+
+ after_create do |info_request, evaluator|
+ FactoryGirl.create(:initial_request, :info_request => info_request)
+ end
+
+ factory :info_request_with_incoming do
+ after_create do |info_request, evaluator|
+ incoming_message = FactoryGirl.create(:incoming_message, :info_request => info_request)
+ info_request.log_event("response", {:incoming_message_id => incoming_message.id})
+ end
+ end
+
+ factory :info_request_with_plain_incoming do
+ after_create do |info_request, evaluator|
+ incoming_message = FactoryGirl.create(:plain_incoming_message, :info_request => info_request)
+ info_request.log_event("response", {:incoming_message_id => incoming_message.id})
+ end
+ end
+
+ factory :info_request_with_incoming_attachments do
+ after_create do |info_request, evaluator|
+ incoming_message = FactoryGirl.create(:incoming_message_with_attachments, :info_request => info_request)
+ info_request.log_event("response", {:incoming_message_id => incoming_message.id})
+ end
+ end
+
+ factory :external_request do
+ user nil
+ external_user_name 'External User'
+ external_url 'http://www.example.org/request/external'
+ end
+
+ end
+
+ factory :user do
+ name 'Example User'
+ email
+ salt "-6116981980.392287733335677"
+ hashed_password '6b7cd45a5f35fd83febc0452a799530398bfb6e8' # jonespassword
+ email_confirmed true
+ ban_text ""
+ factory :admin_user do
+ name 'Admin User'
+ admin_level 'super'
+ end
+ end
+
+ factory :public_body do
+ name
+ short_name
+ request_email 'request@example.com'
+ last_edit_editor "admin user"
+ last_edit_comment "Making an edit"
+ end
+
+ factory :track_thing do
+ association :tracking_user, :factory => :user
+ track_medium 'email_daily'
+ track_type 'search_query'
+ track_query 'Example Query'
+ end
+
+ factory :public_body_change_request do
+ user
+ source_url 'http://www.example.com'
+ notes 'Please'
+ public_body_email 'new@example.com'
+ factory :add_body_request do
+ public_body_name 'A New Body'
+ end
+ factory :update_body_request do
+ public_body
+ end
+ end
+ factory :info_request_batch do
+ title "Example title"
+ user
+ body "Some text"
+ end
+end
diff --git a/spec/fixtures/comments.yml b/spec/fixtures/comments.yml
index b73385a55..44db44d54 100644
--- a/spec/fixtures/comments.yml
+++ b/spec/fixtures/comments.yml
@@ -1,3 +1,18 @@
+# == Schema Information
+#
+# Table name: comments
+#
+# id :integer not null, primary key
+# user_id :integer not null
+# comment_type :string(255) default("internal_error"), not null
+# info_request_id :integer
+# body :text not null
+# visible :boolean default(TRUE), not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# locale :text default(""), not null
+#
+
silly_comment:
visible: t
updated_at: 2008-08-13 01:25:17.486939
diff --git a/spec/fixtures/files/blog_feed.atom b/spec/fixtures/files/blog_feed.atom
new file mode 100644
index 000000000..f49693938
--- /dev/null
+++ b/spec/fixtures/files/blog_feed.atom
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0"
+ xmlns:content="http://purl.org/rss/1.0/modules/content/"
+ xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
+ xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
+ >
+
+<channel>
+ <title>A Blog Feed</title>
+ <atom:link href="http://example.com/feed/" rel="self" type="application/rss+xml" />
+ <link>http://www.example.com</link>
+ <description>Stuff</description>
+ <lastBuildDate>Tue, 30 Apr 2013 14:34:15 +0000</lastBuildDate>
+ <language>en</language>
+ <sy:updatePeriod>hourly</sy:updatePeriod>
+ <sy:updateFrequency>1</sy:updateFrequency>
+ <generator>http://wordpress.org/?v=3.3.2</generator>
+ <item>
+ <title>Example Post</title>
+ <link>http://www.example.com/example-post</link>
+ <comments>http://www.example.com/example-post#comments</comments>
+ <pubDate>Mon, 01 Apr 2013 19:26:08 +0000</pubDate>
+ <dc:creator>Example Blogger</dc:creator>
+ <category><![CDATA[FOI]]></category>
+
+ <guid isPermaLink="false">http://www.example.com/?id=333</guid>
+ <description><![CDATA[An example post [...]]]></description>
+ <content:encoded><![CDATA[<h3>A blog post</h3>
+<p>Example post</p>
+]]></content:encoded>
+ <wfw:commentRss>http://www.example.com/feed/</wfw:commentRss>
+ <slash:comments>2</slash:comments>
+ </item>
+
+ </channel>
+</rss>
diff --git a/spec/fixtures/files/document-pdf.email b/spec/fixtures/files/document-pdf.email
new file mode 100644
index 000000000..f4fc6f0fe
--- /dev/null
+++ b/spec/fixtures/files/document-pdf.email
@@ -0,0 +1,110 @@
+From authority@example.org Tue Dec 3 11:13:02 2013
+Return-path: <authority@example.org>
+Envelope-to: requester@example.org
+Delivery-date: Tue, 03 Dec 2013 11:13:00 +0000
+From: Test Authority <authority@example.org>
+To: requester@example.org
+Subject: testing a PDF attachment with the wrong content-type
+Date: Tue, 03 Dec 2013 11:12:45 +0000
+Message-ID: <87li09xuasdfasdfpoija@blahblah>
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="=-=-="
+
+--=-=-=
+Content-Type: text/plain
+
+Here's a PDF attachement which has a document/pdf content-type,
+when it really should be application/pdf.
+
+
+--=-=-=
+Content-Type: application/pdf
+Content-Disposition: attachment; filename=tiny-example.pdf
+Content-Transfer-Encoding: base64
+Content-Description: a very small example PDF
+
+JVBERi0xLjUKJbXtrvsKMyAwIG9iago8PCAvTGVuZ3RoIDQgMCBSCiAgIC9GaWx0ZXIgL0ZsYXRl
+RGVjb2RlCj4+CnN0cmVhbQp4nCvkMlAAwaJ0Bf1EA4X0Yi6nEC5DA1M9IwNLYzNjsBwS18hIz9TI
+0tBSwdzIQs/Y3MLAzEQhJJdLP03XQBeoUCEkjStawyM1JydfUTM2xIvLNYQrkAsAJG8VmQplbmRz
+dHJlYW0KZW5kb2JqCjQgMCBvYmoKICAgOTIKZW5kb2JqCjIgMCBvYmoKPDwKICAgL0V4dEdTdGF0
+ZSA8PAogICAgICAvYTAgPDwgL0NBIDEgL2NhIDEgPj4KICAgPj4KICAgL0ZvbnQgPDwKICAgICAg
+L2YtMC0wIDUgMCBSCiAgID4+Cj4+CmVuZG9iago2IDAgb2JqCjw8IC9UeXBlIC9QYWdlCiAgIC9Q
+YXJlbnQgMSAwIFIKICAgL01lZGlhQm94IFsgMCAwIDU5NS4yNzU1NzQgODQxLjg4OTc3MSBdCiAg
+IC9Db250ZW50cyAzIDAgUgogICAvR3JvdXAgPDwKICAgICAgL1R5cGUgL0dyb3VwCiAgICAgIC9T
+IC9UcmFuc3BhcmVuY3kKICAgICAgL0kgdHJ1ZQogICAgICAvQ1MgL0RldmljZVJHQgogICA+Pgog
+ICAvUmVzb3VyY2VzIDIgMCBSCj4+CmVuZG9iago3IDAgb2JqCjw8IC9MZW5ndGggOCAwIFIKICAg
+L0ZpbHRlciAvRmxhdGVEZWNvZGUKICAgL0xlbmd0aDEgMzcxNgo+PgpzdHJlYW0KeJzlVnt0lMUV
+v/P9vtndZB/5drMbiUlww7ookJCQECCAzYJBBSrlETXYxgayxIjQJAQVTOOCFAREgwKLAtIUkSpE
+mlIkG2OtVN4xrZXH8Y0gFGkjRouoa5j0btCe0/boOW3/6OnpzM58cx9z5/7ud+fuR4KI4mkBgbxl
+s6dVvXnmiTlECBJpt5bdPddLd6TlE8mXiIQqr7p9dvXgu2cSmZmmbbfPml9etrfAzOtG1o9UzJgW
+7O56fQSRZRbzhlQwwxYx3cL0ZqavrJg9d97lhs76lgNM95pVWTaNKM7N9HtMp86eNq9KrzFVM93F
+tLdqzoyqEeaPeRmXyj5UkEblKqyXy83srZkuD9j0L8n0pbDIkKZT1p4jHYPIONJxpCM70Znu9Kc7
+08t16qpBStdpFTY7Pv9kjqkfCTrItkbIo2SljIDbskZ7VqeF8SYzUuWwOJFKus3o6sjh3yDKGnmK
+F9k7JtqFKEnMZaO5Th/PvoOHtHcPHbrY55A8enGDFowO0PZf5DBpVN39vh7Sa8lNKVQVuJI8Im6J
+Zan0PCNki0209mpxRWwrUlM8msVjofGaK2FMKp92vmOP05XPzp8633HKOMf9/DlmZAf6FaRVpTWk
+vZrWmSYLqEAUaAWeghSZYc6yZMVlxFdSpajUKj2VKXEl1aJEeNJ7i9ycIUM9DuHzktOg3BwyDxS+
+PiazHuraYWtvnrl/etmrd6rzar/o13VSmCPaU0vXtTi02259cf/gwdv7Z4hhIl4kimvVO3vW7ty+
+kV877eZpvsnNMe/TTGs1YaHrdHaaw9ORHbAbMiAnylJZJTulqSdKvt2RiMn9RcfX8biL42Gly2hc
+INHU4qIWW8S1olecK2ESXJ4xvXrgX4JunMsO+AqSa6nWFDKHLKG4UHzIWmsL2UOOUELICDlrXQ3J
+nclORtrH5HEnMdS8wX2vyokh9fWJzVrN6sZta1Y1Nq7qFC51rvNj9ZFw4viZgwfPfHBg/9kN6oDq
+UB8y+HzG6BbDOLU0kRdzlPMBFEd5gQQzLdIXahazFND5ahhd45usRcUtRN0vDZs6siMnn19U1qmu
+V7LFLrJ6rROtKPHnenycGvBB5LW1tbk3eZTizKhW68UM6mmCrcdumo10bQI/e5PBHAeFqFtMEdPE
+PHGfeFTbp73t7evN9g73Nqb36e6O3QFqEJNFKcvrvpInsjz/b/JvboLPeFusExvERu4NX/V93A+I
+Az0a8lv3/6tN/Ns7tb+j0DPr/5Ev/0eNszdCbdx301baILYwVc7sauY0aDtoMd3FnJdFm1imZTJv
+C3XSYdZ8gNqwVScxjnKZS/SG1Oi8KKKdbCOfb0a+2aSTPkHfqU/WI/oZvZ2G6jV6u16q14hcbJI3
+yy088rFXc3E9vYIi4jjV0PM4i1y8oBfqDjqOdmyl03xK7F22UT1tplr2xS0qKaTVapOZs1+20zru
+lSxv5yw9zN49LxbRUXoMunYDbRRHGVcbXaBFKNJCnBy5Wjn7v59ttfP+dVSj880V8aS0Acxj7/ms
+6T1zGjLl0Z7eybeslopos4nLktnHp8QitkW8LDpMq6iBDuMHqMZbYrHu05/Wb6D6SxFAKdWz7XWx
+PaZyMZ+xx3ptzLp2j14qttJZvdQ8nW3vjSHiM3dqkxlROb3A4x6TwZhGiMVYxp7GpGnUbh6nZ/F+
+tmCuY9RElcijmbyqpe20gzIRpnq21IPXNFRe4J0b9BOMuV48pF2gdhRSPyrXz3Gs+S+GwkTNZpPU
+oQnK8BpNmn9ssCkwqdh7YGp6ZsY/kF7D7G2iiU32+d5Id/fEYj1FTm2SqU3wW5p0v+/ENwlPZGaM
+n1jsbbo4pvArq2NKC5k3pZiXMYrZzB9T2COLHdok/fwbW9rkLavwLjeW+4YvN2YMz7xUI7Sie2dN
+WLj6hwkjP6UrLD053L47tfTr52fHurbb58TdxGRMeKmq8GyerdKI7Cc/OxadZJ/zT9XGxBlazil7
+UO+gai2fduunqRp5sdre0wp5NLChuTyaYk7Evld6rJhQRAOogiuzxjX58dipukdL4qce0RYEur9U
+iLrxhR+f5+CzMC448KnCeYW/+PGJAx+H0enHR8tHyY8UzoXxYRgdUfw5ij8pnB2OD0bjjMIfc3D6
+1BR5OoxTrHhqCt4/mSXfj+JkFk4ovKdwPAfvuvFOGG8rvOXCm3V4oxWvKxxj9WN1OHrkenm0Dkeu
+x+HXUuRhhddS8AeFVxV+r/A7hfYwXmnrLV9RaOuNQzk4qLBvsVPuS8XeJOxReFnhtwq7FV5S+I3C
+iwq/VnhBoVXheSdalvhli0KkuVVGFJp3lcjmVjQv0Hc955e7SgLd2BXQn/Njp8Kvwtih8EuFJoVf
+KGwP4lkHGrf5ZWMQ27a65DY/trrwDDv9TBRPK/xcYYvCUy5sVnhyk0M+mYNNDvwsiAZWaQjjpwob
+n7DJjQpP2LBhfbLcEMT6dYZcn4x1Bh6Px2MKa8N2uVYhbMca3rQmjNWrHHL11VjlwKNRPLKyVT6i
+sLK+RK5sxcoFev3DfllfgvqA/rAfDymseHCgXKHw4EAsZ5jLR2HZUqtc5sZSKx5gxgNBLOFILfFj
+sRM/UVh0v1MuUrjfiYUKCxRCCoHu++rq5H0KdXX4cRC1RR5Z68e9CvMV5jlwjw13x+MuhblR1EQx
+J4rqKKoUKhV+pDArHXcqzHSOljOn4A6FijrczkS5wgyFoEKZwnSFacNRGsVtNpQofF/hVoWpxfFy
+ahTF8bglKVnekoObFW7ik28ajSIPpghDTumFyW5MGpcoJynwN8j3FCbcaMgJCjca+K7CeJaMVxg3
+1pDjEjE2zS7HGrjBjusVrgtjTBiFCtdqmfLaKEa3YtR4BBQKFL5zjUt+x41rRibIa1wYOcIuRwa6
+EzDCjuEK+QrDhrrlsCiGDjHkUDeG5FnlEAN5VgzujVw7cgZZZY7CICuys6wy244sKwZmxsmBBjLj
+kJGDAf39ckAQ/fu5ZH8/+rlw9VV+efUoXOVHX79V9k2A34orFXwKfRKQzjjTXfAGcUUUvRlC7yDS
+7EjlCKYqpERx+WgkM5Gs0CuIyzhSlykk8aakZHgU3AqJCi5WcCk4GatzNIw6JAThULDbkqRdwcba
+tiRYFeINxClYWM2iYHbDFITOQp0zwAPmQnGVNaSWCWGAFEREBBc/JAb8LzT6bzvwrS3tr1SHeSwK
+ZW5kc3RyZWFtCmVuZG9iago4IDAgb2JqCiAgIDI0MDIKZW5kb2JqCjkgMCBvYmoKPDwgL0xlbmd0
+aCAxMCAwIFIKICAgL0ZpbHRlciAvRmxhdGVEZWNvZGUKPj4Kc3RyZWFtCnicXVAxbsQgEOx5xZZ3
+xQnbykUpkKXo0ri4JIqTB2BYHKQYEMaFf58FThcpBcwsuzOMll+Gl8HZBPw9ejViAmOdjrj6LSqE
+CWfrWNuBtirdqnKrRQbGSTzua8JlcMYzIYB/UHNNcYfDs/YTHhkA8LeoMVo3w+HrMtancQvhBxd0
+CRrW96DRkN1Vhle5IPAiPg2a+jbtJ5L9TXzuAaErdVsjKa9xDVJhlG5GJpqmB2FMz9Dpf71zVUxG
+fcvIxMMTTTYNAROP58IJiKvKVeamcvITXVs4Qfa+ueRf8jru8dUWIyUvOyuRc1jr8L7W4ENWlfML
+jQ547AplbmRzdHJlYW0KZW5kb2JqCjEwIDAgb2JqCiAgIDI0NgplbmRvYmoKMTEgMCBvYmoKPDwg
+L1R5cGUgL0ZvbnREZXNjcmlwdG9yCiAgIC9Gb250TmFtZSAvUlFaWlJTK0RlamFWdVNhbnMKICAg
+L0ZvbnRGYW1pbHkgKERlamFWdSBTYW5zKQogICAvRmxhZ3MgMzIKICAgL0ZvbnRCQm94IFsgLTEw
+MjAgLTQxNSAxNjgwIDExNjYgXQogICAvSXRhbGljQW5nbGUgMAogICAvQXNjZW50IDkyOAogICAv
+RGVzY2VudCAtMjM1CiAgIC9DYXBIZWlnaHQgMTE2NgogICAvU3RlbVYgODAKICAgL1N0ZW1IIDgw
+CiAgIC9Gb250RmlsZTIgNyAwIFIKPj4KZW5kb2JqCjUgMCBvYmoKPDwgL1R5cGUgL0ZvbnQKICAg
+L1N1YnR5cGUgL1RydWVUeXBlCiAgIC9CYXNlRm9udCAvUlFaWlJTK0RlamFWdVNhbnMKICAgL0Zp
+cnN0Q2hhciAzMgogICAvTGFzdENoYXIgMTExCiAgIC9Gb250RGVzY3JpcHRvciAxMSAwIFIKICAg
+L0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcKICAgL1dpZHRocyBbIDAgNDAwIDAgMCAwIDAgMCAw
+IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAg
+MCAwIDAgMCA3NTEgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAw
+IDAgMCAwIDAgMCA2MTUgMCAwIDAgMCAwIDAgMjc3IDAgMCA2MTEgXQogICAgL1RvVW5pY29kZSA5
+IDAgUgo+PgplbmRvYmoKMSAwIG9iago8PCAvVHlwZSAvUGFnZXMKICAgL0tpZHMgWyA2IDAgUiBd
+CiAgIC9Db3VudCAxCj4+CmVuZG9iagoxMiAwIG9iago8PCAvQ3JlYXRvciAoY2Fpcm8gMS4xMi4x
+NiAoaHR0cDovL2NhaXJvZ3JhcGhpY3Mub3JnKSkKICAgL1Byb2R1Y2VyIChjYWlybyAxLjEyLjE2
+IChodHRwOi8vY2Fpcm9ncmFwaGljcy5vcmcpKQo+PgplbmRvYmoKMTMgMCBvYmoKPDwgL1R5cGUg
+L0NhdGFsb2cKICAgL1BhZ2VzIDEgMCBSCj4+CmVuZG9iagp4cmVmCjAgMTQKMDAwMDAwMDAwMCA2
+NTUzNSBmIAowMDAwMDA0MDYyIDAwMDAwIG4gCjAwMDAwMDAyMDUgMDAwMDAgbiAKMDAwMDAwMDAx
+NSAwMDAwMCBuIAowMDAwMDAwMTg0IDAwMDAwIG4gCjAwMDAwMDM2NzkgMDAwMDAgbiAKMDAwMDAw
+MDMxNCAwMDAwMCBuIAowMDAwMDAwNTQyIDAwMDAwIG4gCjAwMDAwMDMwMzggMDAwMDAgbiAKMDAw
+MDAwMzA2MSAwMDAwMCBuIAowMDAwMDAzMzg1IDAwMDAwIG4gCjAwMDAwMDM0MDggMDAwMDAgbiAK
+MDAwMDAwNDEyNyAwMDAwMCBuIAowMDAwMDA0MjU3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUg
+MTQKICAgL1Jvb3QgMTMgMCBSCiAgIC9JbmZvIDEyIDAgUgo+PgpzdGFydHhyZWYKNDMxMAolJUVP
+Rgo=
+--=-=-=--
+
diff --git a/spec/fixtures/files/email-folding-example-1.txt.expected b/spec/fixtures/files/email-folding-example-1.txt.expected
index 801542288..45dabf156 100644
--- a/spec/fixtures/files/email-folding-example-1.txt.expected
+++ b/spec/fixtures/files/email-folding-example-1.txt.expected
@@ -8,3 +8,10 @@ On behalf of James Hall, Chief Executive
Identity and Passport Service
<<9032 C Pollard final response.doc>>
+FOLDED_QUOTED_SECTION
+
+FOLDED_QUOTED_SECTION
+
+
+FOLDED_QUOTED_SECTION
+
diff --git a/spec/fixtures/files/email-folding-example-10.txt b/spec/fixtures/files/email-folding-example-10.txt
index 0fabb7f9c..a0773e6ff 100644
--- a/spec/fixtures/files/email-folding-example-10.txt
+++ b/spec/fixtures/files/email-folding-example-10.txt
@@ -3,13 +3,13 @@ Department of Health, please visit the 'Contact us' page on the
Department’s website.
-----------------------------------------------------------------------------------------
-
- Apologies that you were not able to read our previous response of 4
- October. Please find the text of that email below.
-
+
+ Apologies that you were not able to read our previous response of 4
+ October. Please find the text of that email below.
+
Our ref: DE00000642471
-Dear Ms Peters Rock,
+Dear Ms Peters Rock,
You requested your correspondence to be treated under the Freedom of
Information Act.  However, as your correspondence asked for general
@@ -19,7 +19,7 @@ correspondence under the provisions of the Act.
I am sorry I cannot be more helpful.
-Yours sincerely,
+Yours sincerely,
Simon Dove
Customer Service Centre
Department of Health
diff --git a/spec/fixtures/files/email-folding-example-10.txt.expected b/spec/fixtures/files/email-folding-example-10.txt.expected
index e4f704c0e..5b609dc12 100644
--- a/spec/fixtures/files/email-folding-example-10.txt.expected
+++ b/spec/fixtures/files/email-folding-example-10.txt.expected
@@ -3,13 +3,13 @@ Department of Health, please visit the 'Contact us' page on the
Department’s website.
-----------------------------------------------------------------------------------------
-
- Apologies that you were not able to read our previous response of 4
- October. Please find the text of that email below.
-
+
+ Apologies that you were not able to read our previous response of 4
+ October. Please find the text of that email below.
+
Our ref: DE00000642471
-Dear Ms Peters Rock,
+Dear Ms Peters Rock,
You requested your correspondence to be treated under the Freedom of
Information Act.  However, as your correspondence asked for general
@@ -19,7 +19,8 @@ correspondence under the provisions of the Act.
I am sorry I cannot be more helpful.
-Yours sincerely,
+Yours sincerely,
Simon Dove
Customer Service Centre
Department of Health
+FOLDED_QUOTED_SECTION
diff --git a/spec/fixtures/files/email-folding-example-11.txt b/spec/fixtures/files/email-folding-example-11.txt
new file mode 100644
index 000000000..635d7aa4f
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-11.txt
@@ -0,0 +1,45 @@
+ Hello Example,
+
+ This is a reply to your test request Nov 28.
+
+ Regards.
+
+ On Thu, Nov 28, 2013 at 9:08 AM, Example User
+ <[1]request-x-xxx@xxx.com> wrote:
+
+ Dear Test Authority,
+
+ This is the request body.
+
+ Yours faithfully,
+
+ Example User
+
+ -------------------------------------------------------------------
+
+ Please use this email address for all replies to this request:
+ [2]request-x-xxx@xxx.com
+
+ Is [3]testauthority@example.com the wrong address for Freedom of
+ Information requests to AYG Test Authority? If so, please contact us
+ using this form:
+ [4]http://example.com/help/contact
+
+ Disclaimer: This message and any reply that you make will be published
+ on the internet. Our privacy and copyright policies:
+ [5]http://example.com/help/officers
+
+ If you find this service useful as an FOI officer, please ask your web
+ manager to link to us from your organisation's FOI page.
+
+ -------------------------------------------------------------------
+
+References
+
+ Visible links
+ 1. mailto:request-x-xxx@xxx.com
+ 2. mailto:request-x-xxx@xxx.com
+ 3. mailto:testauthority@example.com
+ 4. http://example.com/help/contact
+ 5. http://example.com/help/officers
+
diff --git a/spec/fixtures/files/email-folding-example-11.txt.expected b/spec/fixtures/files/email-folding-example-11.txt.expected
new file mode 100644
index 000000000..e336062c7
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-11.txt.expected
@@ -0,0 +1,8 @@
+ Hello Example,
+
+ This is a reply to your test request Nov 28.
+
+ Regards.
+
+
+FOLDED_QUOTED_SECTION
diff --git a/spec/fixtures/files/email-folding-example-2.txt.expected b/spec/fixtures/files/email-folding-example-2.txt.expected
index e52fbe443..df578390d 100644
--- a/spec/fixtures/files/email-folding-example-2.txt.expected
+++ b/spec/fixtures/files/email-folding-example-2.txt.expected
@@ -2,3 +2,4 @@ Preface to the message which we are not interested in
-----------------------------------------------------------------------------------------
Important message about cheese
+FOLDED_QUOTED_SECTION
diff --git a/spec/fixtures/files/email-folding-example-3.txt.expected b/spec/fixtures/files/email-folding-example-3.txt.expected
index e2cca4933..accb40150 100644
--- a/spec/fixtures/files/email-folding-example-3.txt.expected
+++ b/spec/fixtures/files/email-folding-example-3.txt.expected
@@ -3,3 +3,10 @@ Reference : T3241/8
Thank you for your e-mail enquiry of 12th February.
A reply is attached.
+FOLDED_QUOTED_SECTION
+
+FOLDED_QUOTED_SECTION
+
+
+FOLDED_QUOTED_SECTION
+
diff --git a/spec/fixtures/files/email-folding-example-5.txt.expected b/spec/fixtures/files/email-folding-example-5.txt.expected
index fbb0f0f50..46d7f731a 100644
--- a/spec/fixtures/files/email-folding-example-5.txt.expected
+++ b/spec/fixtures/files/email-folding-example-5.txt.expected
@@ -1,11 +1,11 @@
Hi Simon
-My apologies for timescale of response. The data forwarded is a public
-register, and is updated on a frequent and regular basis; your request
-unfortunately coincided with annual leave and a monthly update of the
-spreadsheet. As the definition of an HMO under the Housing Act 2004
-differs to that under planning legislation, I have forwarded this and
-your original request on to Andy England, Development Control Manager to
+My apologies for timescale of response. The data forwarded is a public
+register, and is updated on a frequent and regular basis; your request
+unfortunately coincided with annual leave and a monthly update of the
+spreadsheet. As the definition of an HMO under the Housing Act 2004
+differs to that under planning legislation, I have forwarded this and
+your original request on to Andy England, Development Control Manager to
respond independantly.
If I can be of further assistance please contact me
diff --git a/spec/fixtures/files/email-folding-example-7.txt.expected b/spec/fixtures/files/email-folding-example-7.txt.expected
index 0ef8fd82b..cb6961038 100644
--- a/spec/fixtures/files/email-folding-example-7.txt.expected
+++ b/spec/fixtures/files/email-folding-example-7.txt.expected
@@ -13,4 +13,5 @@ Telephone +44 (0) 116 2222222
Extn 5221 VM No. 8035
Fax + 44 (0) 116 2485217
-<<0001_00035908_Resp_12RESPONSE LETTER_20080408_112311_01.TIF>> \ No newline at end of file
+<<0001_00035908_Resp_12RESPONSE LETTER_20080408_112311_01.TIF>>
+FOLDED_QUOTED_SECTION
diff --git a/spec/fixtures/files/email-folding-example-8.txt.expected b/spec/fixtures/files/email-folding-example-8.txt.expected
index b5dc10c0d..e8c08f4ca 100644
--- a/spec/fixtures/files/email-folding-example-8.txt.expected
+++ b/spec/fixtures/files/email-folding-example-8.txt.expected
@@ -3,4 +3,5 @@ I will be out of the office starting 11/04/2008 and will not return until
I will respond to your message when I return. If you have any urgent
queries please ring 02085419088 for Legal Business Support queries or
-contact Eileen Perren for FOI or DP queries \ No newline at end of file
+contact Eileen Perren for FOI or DP queries
+FOLDED_QUOTED_SECTION
diff --git a/spec/fixtures/files/email-folding-example-9.txt.expected b/spec/fixtures/files/email-folding-example-9.txt.expected
index 2d2381a34..d222e9438 100644
--- a/spec/fixtures/files/email-folding-example-9.txt.expected
+++ b/spec/fixtures/files/email-folding-example-9.txt.expected
@@ -7,3 +7,9 @@ Yours sincerely
MICHAEL HEGARTY
FOI Officer
+FOLDED_QUOTED_SECTION
+
+FOLDED_QUOTED_SECTION
+
+
+FOLDED_QUOTED_SECTION
diff --git a/spec/fixtures/files/fake-authority-type.csv b/spec/fixtures/files/fake-authority-type.csv
index 4aa618ad1..a320941c7 100644
--- a/spec/fixtures/files/fake-authority-type.csv
+++ b/spec/fixtures/files/fake-authority-type.csv
@@ -1,3 +1,6 @@
,North West Fake Authority,north_west_foi@localhost
,Scottish Fake Authority,scottish_foi@localhost
,Fake Authority of Northern Ireland,ni_foi@localhost
+,Gobierno de Aragón,spain_foi@localhost
+,Nordic æøå,no_foi@localhost
+
diff --git a/spec/fixtures/files/incoming-request-empty.email b/spec/fixtures/files/incoming-request-empty.email
new file mode 100644
index 000000000..890a14f21
--- /dev/null
+++ b/spec/fixtures/files/incoming-request-empty.email
@@ -0,0 +1,8 @@
+From: EMAIL_FROM
+To: FOI Person <EMAIL_TO>
+Bcc:
+Subject: Re: Nothing to see here.
+Reply-To:
+In-Reply-To: <471f1eae5d1cb_7347..fdbe67386163@cat.tmail>
+
+
diff --git a/spec/fixtures/files/incoming-request-two-same-name.email b/spec/fixtures/files/incoming-request-two-same-name.email
index f1024d607..ecd322fe4 100644
--- a/spec/fixtures/files/incoming-request-two-same-name.email
+++ b/spec/fixtures/files/incoming-request-two-same-name.email
@@ -13,13 +13,13 @@ Content-Disposition: inline
--Q68bSM7Ycu6FN28Q
Content-Type: text/plain; charset=us-ascii
-Content-Disposition: attachment; filename="hello.txt"
+Content-Disposition: attachment; filename="hello-world.txt"
Second hello
--Q68bSM7Ycu6FN28Q
Content-Type: text/plain; charset=us-ascii
-Content-Disposition: attachment; filename="hello.txt"
+Content-Disposition: attachment; filename="hello-world.txt"
First hello
diff --git a/spec/fixtures/files/inline-uuencode.email b/spec/fixtures/files/inline-uuencode.email
new file mode 100644
index 000000000..3134ba3ad
--- /dev/null
+++ b/spec/fixtures/files/inline-uuencode.email
@@ -0,0 +1,27 @@
+From foo@bar Mon Jun 01 17:14:44 2009
+Return-path: <foo@bar>
+Envelope-to: foi@quux
+Delivery-date: Mon, 01 Jun 2009 17:14:44 +0100
+From: <foo@bar>
+To: <request-whatever@quux>
+Subject: something or other
+Date: Mon, 1 Jun 2009 17:14:37 +0100
+X-MimeOLE: Produced By Microsoft MimeOLE V6.00.3790.181
+Message-ID: <baz@xyzzy>
+
+Thanks for your email - here's a truncated attachment
+for you:
+
+**********************************************************************
+
+begin 666 ResponseT7363 9.doc
+MT,\1X*&Q&N$`````````````````````/@`#`/[_"0`&```````````````"
+M````) ``````````$ ``+@````$```#^____`````",```!L````________
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+#````
+`
+end
+
+The original of this email was scanned for viruses or something
+like that.
diff --git a/spec/fixtures/files/interesting.pdf b/spec/fixtures/files/interesting.pdf
new file mode 100644
index 000000000..17f543455
--- /dev/null
+++ b/spec/fixtures/files/interesting.pdf
Binary files differ
diff --git a/spec/fixtures/files/malformed-to-and-cc.email b/spec/fixtures/files/malformed-to-and-cc.email
new file mode 100644
index 000000000..4fbb6e21e
--- /dev/null
+++ b/spec/fixtures/files/malformed-to-and-cc.email
@@ -0,0 +1,11 @@
+From foo@bar Wed Mar 12 14:58:26 2008
+Return-path: <foo@bar>
+Subject: example email
+To: <bar@example.org
+Cc: baz@example.org>
+From: quux@example.org
+Date: Mon, 7 May 2012 12:47:06 +0100
+Mime-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+
+A very basic email, but with malformed To: and Cc: lines
diff --git a/spec/fixtures/files/mislabelled-as-iso-8859-1.email b/spec/fixtures/files/mislabelled-as-iso-8859-1.email
new file mode 100644
index 000000000..6c8e6109e
--- /dev/null
+++ b/spec/fixtures/files/mislabelled-as-iso-8859-1.email
@@ -0,0 +1,20 @@
+From foo@bar Thu Mar 01 15:02:33 2012
+Return-path: <foo@bar>
+Envelope-to: foi@quux
+Delivery-date: Thu, 01 Mar 2012 15:02:33 +0000
+Date: Thu, 01 Mar 2012 15:01:58 +0000
+Subject: some FOI request
+To: foi@quux
+From: foo@bar
+MIME-Version: 1.0
+Content-Type: text/plain; charset="iso-8859-1"
+Content-Transfer-Encoding: 7bit
+Message-Id: <2468@bar.local>
+
+Dear Whoever,
+
+THERE'S A DASH NEXT REQUEST FOR INFORMATION
+
+Best regards,
+Other Person
+
diff --git a/spec/fixtures/files/multipart-no-final-boundary.email b/spec/fixtures/files/multipart-no-final-boundary.email
new file mode 100644
index 000000000..9c16dad52
--- /dev/null
+++ b/spec/fixtures/files/multipart-no-final-boundary.email
@@ -0,0 +1,21 @@
+From foo@bar Thu Sep 13 10:34:44 2012
+Return-path: <foo@bar>
+Envelope-to: foi@example.org
+Delivery-date: Thu, 13 Sep 2012 10:34:44 +0100
+From: foo@bar
+To: foi@example.org
+Subject: an acknowledgement email
+Date: Thu, 13 Sep 2012 10:08:03 +0100
+Message-ID: <987654@foo.local>
+Content-Type: multipart/mixed; boundary="-----7D81B75CCC90D2974F7A1CBD"
+
+This is a multi-part message in MIME format.
+-------7D81B75CCC90D2974F7A1CBD
+Content-Type: text/html
+
+<div>
+ <p>
+ This is an acknowledgement of your email, that irritatingly
+ leaves out the final MIME boundary.
+ </p>
+<div>
diff --git a/spec/fixtures/files/nested-attachments-premature-end.email b/spec/fixtures/files/nested-attachments-premature-end.email
new file mode 100644
index 000000000..6b13808dc
--- /dev/null
+++ b/spec/fixtures/files/nested-attachments-premature-end.email
@@ -0,0 +1,110 @@
+From someone@example.org Mon May 15 13:10:29 2012
+Return-path: <someone@example.org>
+Envelope-to: foi@example.org
+Delivery-date: Mon, 15 May 2012 13:10:29 +0100
+Message-Id: <abcde@baz.local>
+Date: Mon, 15 May 2012 09:48:48 +0100
+From: "Example Person" <someone@example.org>
+To: <request@example.org>
+Subject: some FOI request or other
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="=__outer__="
+
+This is a MIME message. If you are reading this text, you may want to
+consider changing to a mail reader or gateway that understands how to
+properly handle MIME multipart messages.
+
+--=__outer__=
+Content-Type: multipart/alternative; boundary="=__inner__="
+
+--=__inner__=
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: quoted-printable
+X-MIME-Autoconverted: from 8bit to quoted-printable by something
+
+Hello
+=20
+Please find some information attached.
+=20
+
+--=__inner__=
+Content-Description: HTML
+Content-Type: text/html; charset="utf-8"
+Content-Transfer-Encoding: quoted-printable
+
+<html>
+ <head>
+ <title>some title text</title>
+ </head>
+ <body>
+ <p>blah blah blah</p>
+ </body>
+</html>
+
+--=__inner__=--
+
+--=__outer__=
+Content-Type: message/rfc822
+
+Return-path: <foo@bar>
+Date: Mon, 7 May 2012 12:47:06 +0100
+From: someone-else@example.org
+To: foi@example.org
+Message-Id: <56789@quux.local>
+Subject: a freedom of information requests
+Mime-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+
+ Dear Whoever,
+
+ Please could you let me know, um, whatever ...
+
+ Yours faithfully,
+
+ Whoever I Am
+
+--=__outer__=
+Content-Type: text/plain; charset=US-ASCII
+Content-Disposition: inline
+Content-Transfer-Encoding: quoted-printable
+
+ Dear Whowever,
+ =20
+ Please could you let me know, um, whatever ...
+ =20
+ Yours faithfully,
+ =20
+ Whoever I Am
+ =20
+
+--=__outer__=--
+
+--=__outer__=
+Content-Type: application/png; name="maroon-square.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="maroon-square.png"
+
+iVBORw0KGgoAAAANSUhEUgAAAEEAAABCCAYAAAAIY7vrAAAABmJLR0QA/wD/AP+g
+vaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QQeDSEx8qultwAAABl0
+RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAMzSURBVHja7VtL2psw
+DNS4rPv1Gj1Kt71Az9ZT9F7dN9MFGGThB/YfKDX2Kp8DRBpLowcKvn/5ShERiAgl
+srh8aT93tJzWdae8XR0CEICwUx59K54H4QFKp0Eg5alrAwEYIDx5DRAGCAOEAcIA
+QaUFfDoIHJawpEbOPd0dRPjJDWIUiEwt933+8es2Ovz++a3dCkREXmwD4ZbsVln6
+cLkef14duAMqAGCkY0A+jBNgXGFZU/eKa3fhZjlQqLhHKF9oFbpulE2Z/oFrXTd+
+nlOWkn1dMHXrAiWguq0iG9uk/REjBggPtgQOED781my4wwBhgDBAmPmUAwR0X0UO
+dxggnA8CO5xocU8HoAoEDwA6nOyCH+ZMKQ4zy+QbNBoUirquMPBJcgPyJkOi+c7S
+ohhn6ZctzDIrcFalIspYILG1et9WABUtt6WztLq+/0Amp9sCnsCBUhfvK4FLiRCA
+QwC7JABGTngrIIPnIjf6R5We0uxz3j+FbCvdy2nlY/IgcfrMRQuFHIC9Sap3AW8n
+2gZ+cZYCVn4LzBxxnykNgJpWN8lt7yw+QCMxan2s8lQXcNlDlpAW7YmIXMszTgoH
+rU91+8OFYXN9ikz/LyLgExSCDlaO+cdGsIEQkyUAIgFMKRTEn3vDjFFHwWSIzEQC
+cmN4IHVNGG2PQXhhsuRl3jihwQyB6H1274gV1BhKLKNt4ZEpkygeeoC+xytdK1cr
+oX0EACphnTZXbbLMmL/YBGo9lSU1OmBONMnTlQUqTa4y1VgAddg0hdTR04lyT0Xq
+8RYAyHVyBX6ET/9wTBD6TWVCMH5Qo3yhXju3bNY/BBMdsoLYBMmnzQdOP56O36s5
+40r1D7UWYV5dNT2nbxVBAHb43Y36CdbXfTii6isU/U7ZXLQ4w/V/wotFoilVF2kl
+w7YCDrIPkj4/G9fao7q0rYSSJdgeSqmQrCU+r/j8rOv/gpuKPm5Lffen5eN+ljeo
+rcfW0Om2Enm9KwDZAgrG98txX9cMe6X2E5SGU29VTE17lFAUkMybsXclndu31BGX
+hcgWv8oxonYtkf/jhc10WPGgm2IZncKlu+sg8vLm7hDSwk3f2/wFEzN3v6aAXQ0A
+AAAASUVORK5CYII=
+
+--=__outer__=--
+
diff --git a/spec/fixtures/files/no-part-charset-random-data.email b/spec/fixtures/files/no-part-charset-random-data.email
new file mode 100644
index 000000000..d51fd3f38
--- /dev/null
+++ b/spec/fixtures/files/no-part-charset-random-data.email
@@ -0,0 +1,30 @@
+From xxxx@yahoo.cn Mon Oct 08 14:01:34 2012
+Return-path: <xxxx@yahoo.cn>
+Envelope-to: foi@atlas.ukcod.org.uk
+Delivery-date: Mon, 08 Oct 2012 14:01:34 +0100
+Received: (qmail 63864 invoked from network); 8 Oct 2012 13:01:12 -0000
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.cn; s=s1024; t=1349701272; bh=T/mtlIYvhB/L5RO+CvTazeAdGf1n1zsGXBoA8EKGT9M=; h=Message-ID:X-Yahoo-Newman-Property:X-YMail-OSG:X-Yahoo-SMTP:Received:X-mailer:From:Subject:To:Content-Transfer-Encoding:Content-Type:Date; b=LYI/PXvA7DA746bmyprChUg7N8YDvN9XE/bhfTt5MW7siOmxHHzn1w+s5X33PvLI0x0UfJLo+MCkTnGPKnG5BYY38US8PkocJYyphrvF/eaUl3ALf8UvxHBOJX1iIi89Xp2NnfbS8lz9kZAWifb9GOnOA5/kLDcL5/WJXliit2k=
+Message-ID: <xxxx@xxxx.yahoo.com>
+X-Yahoo-Newman-Property: ymail-5
+X-YMail-OSG: nPs5jgsVM1myUoKjeEPTxxalz4BM6BZMEUYu.E8NPMPQyo_
+ Yej8T2WCTurn767NOwhuDIqNxC2QGZINqfjmKcdyW7a1P_Zxqr9GsjgxODci
+ ihwr7qYAGDDbcsrB.PX4epnJZHl3yAwoGW.1ReEZnXQANFcNep7.zNEbZ_2k
+ RU1IhI9aHYvxPxt5RWugwOoFRh9P8Ym35A88IMazNtVaBiBEXF6Vk8Aqr9XP
+ 3Vh9xOT9Pn6X8qOUjNXkdb3xB4S5AAIRSE9mqhL1KzHBwdVQs25IoM_2FV2b
+ gPsQGgL4_mwBH0WcEMhdj7Kn6Nfb44L.50E_V3DH.8P7KzDK8zNVXSbAqohX
+ Qi6MzUK2frr8IyZyYzHb.ekff7kAcJgUoHvhnyPar8tRYxhQT3_xsUTzsx8N
+ oWckVPh_i3OT7U4ObgekqgtteMoYqPH2eF1SZXamGBAs-
+X-Yahoo-SMTP: YUQHwRWswBDjbw_M.D6EP4KpT9khlJErDRBQi4ySZQ--
+X-mailer: MIME::Lite 3.027 (F2.74; T1.31; A2.07; B3.13; Q3.13)
+From: =?GB2312?B?zsJKaWFu?= Bing <xxxx@yahoo.cn>
+Subject: =?GB2312?B?yM7A1svJ?=
+To: FOI Person <EMAIL_TO>
+Content-Transfer-Encoding: base64
+Content-Type: text/plain
+Date: Tue, 9 Oct 2012 20:53:06 +0800
+
+HPBSqsndNBX+ER4hyBoPhhnclcWKVFgbevdD5cJvfI/ARbxRYqA28hZ49Pf6A/ks
+NdVh4N5VPgRs/7SHYPfw5625pZJYTLj6nVdYk76sxnjiiAmwCJWGjPoWvO7nHUBv
+fuLXtNVq5HmD0bWWjAbSk2n74PW7v5izbNO2fjHyiyX2CIof0rriXDmOldJqoebO
+ejybrjG+Tahpu3FF1Mw98HfswzkdB46u/izLCzdUQVM=
+
diff --git a/spec/fixtures/files/part-without-charset-in-content-type.email b/spec/fixtures/files/part-without-charset-in-content-type.email
new file mode 100644
index 000000000..439d52cc3
--- /dev/null
+++ b/spec/fixtures/files/part-without-charset-in-content-type.email
@@ -0,0 +1,38 @@
+From example@example.com Wed Sep 15 17:55:40 2010
+Return-path: <example@example.com>
+Envelope-to: example@example.com
+Delivery-date: Wed, 15 Sep 2010 17:55:40 +0100
+From: <example@example.com>
+To: <request-xxxxx@whatdotheyknow.com>
+Date: Wed, 15 Sep 2010 17:56:03 +0100
+Subject: FOI Internal Review response
+Thread-Topic: FOI Internal Review response
+Thread-Index: xxxxx
+Message-ID: <xxxxxx>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach: yes
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: multipart/mixed;
+ boundary="_002_E6527350F565F54A88C36C23F6C2B86702618AD0DF95SDCCPMSXMB5_"
+MIME-Version: 1.0
+
+--_002_E6527350F565F54A88C36C23F6C2B86702618AD0DF95SDCCPMSXMB5_
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+someencodedtext=
+
+--_002_E6527350F565F54A88C36C23F6C2B86702618AD0DF95SDCCPMSXMB5_
+Content-Type: document/pdf; name="document.pdf"
+Content-Description: document.pdf
+Content-Disposition: attachment; filename="document.pdf";
+ size=62103; creation-date="Wed, 15 Sep 2010 17:54:27 GMT";
+ modification-date="Wed, 15 Sep 2010 17:54:27 GMT"
+Content-Transfer-Encoding: base64
+
+somemoreencodedtext=
+
+--_002_E6527350F565F54A88C36C23F6C2B86702618AD0DF95SDCCPMSXMB5_--
+
diff --git a/spec/fixtures/files/subject-bad-utf-8-trailing-base64.email b/spec/fixtures/files/subject-bad-utf-8-trailing-base64.email
new file mode 100644
index 000000000..dad621877
--- /dev/null
+++ b/spec/fixtures/files/subject-bad-utf-8-trailing-base64.email
@@ -0,0 +1,5 @@
+From: foo@bar
+To: baz@quux
+Subject: =?UTF-8?B?aGVsbG/w?=
+
+Hello, this is the text of the email.
diff --git a/spec/fixtures/files/subject-bad-utf-8-trailing-quoted-printable.email b/spec/fixtures/files/subject-bad-utf-8-trailing-quoted-printable.email
new file mode 100644
index 000000000..b80deb4e8
--- /dev/null
+++ b/spec/fixtures/files/subject-bad-utf-8-trailing-quoted-printable.email
@@ -0,0 +1,5 @@
+From: foo@bar
+To: baz@quux
+Subject: =?UTF-8?Q?hello=F0=?=
+
+Hello, this is the text of the email.
diff --git a/spec/fixtures/files/tnef-attachment-empty.email b/spec/fixtures/files/tnef-attachment-empty.email
new file mode 100644
index 000000000..7967aa95b
--- /dev/null
+++ b/spec/fixtures/files/tnef-attachment-empty.email
@@ -0,0 +1,196 @@
+From hello@blah.local Fri Feb 21 16:23:14 2013
+Return-path: <bar@example.org>
+Envelope-to: foo@example.org
+Delivery-date: Fri, 21 Feb 2013 16:23:14 +0000
+Content-Type: multipart/mixed;
+ boundary="_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_"
+From: <bar@example.org>
+To: <foo@example.org>
+Sender: <hello@blah.local>
+Date: Fri, 21 Feb 2013 16:23:04 +0000
+Subject: here's a useless email
+Message-ID: <12345@blah.local>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator: <12345@blah.local>
+acceptlanguage: en-US, en-GB
+MIME-Version: 1.0
+
+--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: quoted-printable
+
+This attachment just has a body from one of the tests
+in the tnef package in Debian.
+
+--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_
+Content-Disposition: attachment; filename="winmail.dat"
+Content-Transfer-Encoding: base64
+Content-Type: application/ms-tnef; name="winmail.dat"
+
+eJ8+IiURAQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA5AQAAAAAAADoAAENgAQAAgAA
+AAIAAgABBYADAA4AAADVBwQAGQAKAA8AIwABADYBASCAAwAOAAAA1QcEABkACgAP
+ACQAAQA3AQEJgAEAIQAAADBEREEwRkNCQ0MwN0MxNDE5MkVFODZGQzQyRDE1Qjk1
+AGYHAQSQBgBkAgAAAQAAAA8AAAAfAAEwAQAAABAAAAAzAGsAdQBzAGUAcgAyAAAA
+HwACMAEAAAAGAAAARQBYAAAAAAAfAAMwAQAAAI4AAAAvAE8APQBCAFIALQBFAFgA
+QwBIAC0AVABFAFMAVAAvAE8AVQA9AEYASQBSAFMAVAAgAEEARABNAEkATgBJAFMA
+VABSAEEAVABJAFYARQAgAEcAUgBPAFUAUAAvAEMATgA9AFIARQBDAEkAUABJAEUA
+TgBUAFMALwBDAE4APQAzAGsAdQBzAGUAcgAyAAAAAAADAAAwAAAAAAMA/18AAAAA
+AwAVDAEAAAACAQswAQAAAEoAAABFWDovTz1CUi1FWENILVRFU1QvT1U9RklSU1Qg
+QURNSU5JU1RSQVRJVkUgR1JPVVAvQ049UkVDSVBJRU5UUy9DTj0zS1VTRVIyAAAA
+HwAgOgEAAAAQAAAAMwBrAHUAcwBlAHIAMgAAAAMA/V8BAAAACwBAOgAA+T8CAfdf
+AQAAAGMAAAAAAAAA3KdAyMBCEBq0uQgAKy/hggEAAAAAAAAAL289QlItRVhDSC1U
+RVNUL291PUZpcnN0IEFkbWluaXN0cmF0aXZlIEdyb3VwL2NuPVJlY2lwaWVudHMv
+Y249M2t1c2VyMgAAAwAAOQAAAAAfAP45AQAAAEoAAAAzAGsAdQBzAGUAcgAyAEAA
+YgByAGUAeABjAGgAYQBuAGcAZQAuAGQAbwBsAHAAaABpAG4AcwBlAGEAcgBjAGgA
+LgBjAG8AbQAAAAAAAwBxOgAAAAAfAPZfAQAAABAAAAAzAGsAdQBzAGUAcgAyAAAA
+m2sBA5AGAEwbAAAzAAAACwACAAEAAAAfABoAAQAAABIAAABJAFAATQAuAE4AbwB0
+AGUAAAAAAAMAJgAAAAAAAwA2AAAAAAAfADcAAQAAAB4AAABCAGkAbABsACAAbwBm
+ACAAUgBpAGcAaAB0AHMAAAAAAEAAOQBgQvtkuknFAR8APQABAAAAAgAAAAAAAAAC
+AUcAAQAAADgAAABjPXVzO2E9IDtwPUJSLUVYQ0gtVEVTVDtsPUJSLUVYQ0gtREVW
+MS0wNTA0MjUxNzE1MzZaLTE0AB8AcAABAAAAHgAAAEIAaQBsAGwAIABvAGYAIABS
+AGkAZwBoAHQAcwAAAAAAAgFxAAEAAAAWAAAAAcVJumT7yarjal9+TnmqsNvwaipi
+/QAAHwAaDAEAAAAQAAAAMwBrAHIAZQBsAGEAeQAAAB8AHQ4BAAAAHgAAAEIAaQBs
+AGwAIABvAGYAIABSAGkAZwBoAHQAcwAAAAAAAgETEAEAAADuFAAAPCFET0NUWVBF
+IEhUTUwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDQuMCBUcmFuc2l0aW9uYWwv
+L0VOIj4NCjxIVE1MPjxIRUFEPg0KPE1FVEEgaHR0cC1lcXVpdj1Db250ZW50LVR5
+cGUgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PXVzLWFzY2lpIj4NCjxNRVRB
+IGNvbnRlbnQ9Ik1TSFRNTCA2LjAwLjM3OTAuMTgzMCIgbmFtZT1HRU5FUkFUT1I+
+PC9IRUFEPg0KPEJPRFk+DQo8RElWPg0KPERJVj48Rk9OVCBmYWNlPUFyaWFsIHNp
+emU9Mj5USEUgQklMTCBPRiBSSUdIVFM8QlI+QW1lbmRtZW50cyAxLTEwIG9mIHRo
+ZSANCkNvbnN0aXR1dGlvbjwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+
+DQo8RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPlRoZSBDb252ZW50aW9ucyBv
+ZiBhIG51bWJlciBvZiB0aGUgU3RhdGVzIGhhdmluZywgDQphdCB0aGUgdGltZSBv
+ZiBhZG9wdGluZyB0aGUgQ29uc3RpdHV0aW9uLCBleHByZXNzZWQgYSBkZXNpcmUs
+IGluIG9yZGVyIHRvIA0KcHJldmVudCBtaXNjb25zdHJ1Y3Rpb24gb3IgYWJ1c2Ug
+b2YgaXRzIHBvd2VycywgdGhhdCBmdXJ0aGVyIGRlY2xhcmF0b3J5IGFuZCANCnJl
+c3RyaWN0aXZlIGNsYXVzZXMgc2hvdWxkIGJlIGFkZGVkLCBhbmQgYXMgZXh0ZW5k
+aW5nIHRoZSBncm91bmQgb2YgcHVibGljIA0KY29uZmlkZW5jZSBpbiB0aGUgR292
+ZXJubWVudCB3aWxsIGJlc3QgaW5zdXJlIHRoZSBiZW5lZmljZW50IGVuZHMgb2Yg
+aXRzIA0KaW5zdGl0dXRpb247IDxCUj5SZXNvbHZlZCwgYnkgdGhlIFNlbmF0ZSBh
+bmQgSG91c2Ugb2YgUmVwcmVzZW50YXRpdmVzIG9mIHRoZSANClVuaXRlZCBTdGF0
+ZXMgb2YgQW1lcmljYSwgaW4gQ29uZ3Jlc3MgYXNzZW1ibGVkLCB0d28tdGhpcmRz
+IG9mIGJvdGggSG91c2VzIA0KY29uY3VycmluZywgdGhhdCB0aGUgZm9sbG93aW5n
+IGFydGljbGVzIGJlIHByb3Bvc2VkIHRvIHRoZSBMZWdpc2xhdHVyZXMgb2YgdGhl
+IA0Kc2V2ZXJhbCBTdGF0ZXMsIGFzIGFtZW5kbWVudHMgdG8gdGhlIENvbnN0aXR1
+dGlvbiBvZiB0aGUgVW5pdGVkIFN0YXRlczsgYWxsIG9yIA0KYW55IG9mIHdoaWNo
+IGFydGljbGVzLCB3aGVuIHJhdGlmaWVkIGJ5IHRocmVlLWZvdXJ0aHMgb2YgdGhl
+IHNhaWQgTGVnaXNsYXR1cmVzLCANCnRvIGJlIHZhbGlkIHRvIGFsbCBpbnRlbnRz
+IGFuZCBwdXJwb3NlcyBhcyBwYXJ0IG9mIHRoZSBzYWlkIENvbnN0aXR1dGlvbiwg
+DQpuYW1lbHk6IDwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8RElW
+PjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPkFtZW5kbWVudCBJPC9GT05UPjwvRElW
+Pg0KPERJVj4mbmJzcDs8L0RJVj4NCjxESVY+PEZPTlQgZmFjZT1BcmlhbCBzaXpl
+PTI+Q29uZ3Jlc3Mgc2hhbGwgbWFrZSBubyBsYXcgcmVzcGVjdGluZyBhbiANCmVz
+dGFibGlzaG1lbnQgb2YgcmVsaWdpb24sIG9yIHByb2hpYml0aW5nIHRoZSBmcmVl
+IGV4ZXJjaXNlIHRoZXJlb2Y7IG9yIA0KYWJyaWRnaW5nIHRoZSBmcmVlZG9tIG9m
+IHNwZWVjaCwgb3Igb2YgdGhlIHByZXNzOyBvciB0aGUgcmlnaHQgb2YgdGhlIHBl
+b3BsZSANCnBlYWNlYWJseSB0byBhc3NlbWJsZSwgYW5kIHRvIHBldGl0aW9uIHRo
+ZSBnb3Zlcm5tZW50IGZvciBhIHJlZHJlc3Mgb2YgDQpncmlldmFuY2VzLiA8L0ZP
+TlQ+PC9ESVY+DQo8RElWPiZuYnNwOzwvRElWPg0KPERJVj48Rk9OVCBmYWNlPUFy
+aWFsIHNpemU9Mj5BbWVuZG1lbnQgSUk8L0ZPTlQ+PC9ESVY+DQo8RElWPiZuYnNw
+OzwvRElWPg0KPERJVj48Rk9OVCBmYWNlPUFyaWFsIHNpemU9Mj5BIHdlbGwgcmVn
+dWxhdGVkIG1pbGl0aWEsIGJlaW5nIG5lY2Vzc2FyeSB0byB0aGUgDQpzZWN1cml0
+eSBvZiBhIGZyZWUgc3RhdGUsIHRoZSByaWdodCBvZiB0aGUgcGVvcGxlIHRvIGtl
+ZXAgYW5kIGJlYXIgYXJtcywgc2hhbGwgDQpub3QgYmUgaW5mcmluZ2VkLiA8L0ZP
+TlQ+PC9ESVY+DQo8RElWPiZuYnNwOzwvRElWPg0KPERJVj48Rk9OVCBmYWNlPUFy
+aWFsIHNpemU9Mj5BbWVuZG1lbnQgSUlJPC9GT05UPjwvRElWPg0KPERJVj4mbmJz
+cDs8L0RJVj4NCjxESVY+PEZPTlQgZmFjZT1BcmlhbCBzaXplPTI+Tm8gc29sZGll
+ciBzaGFsbCwgaW4gdGltZSBvZiBwZWFjZSBiZSBxdWFydGVyZWQgaW4gDQphbnkg
+aG91c2UsIHdpdGhvdXQgdGhlIGNvbnNlbnQgb2YgdGhlIG93bmVyLCBub3IgaW4g
+dGltZSBvZiB3YXIsIGJ1dCBpbiBhIG1hbm5lciANCnRvIGJlIHByZXNjcmliZWQg
+YnkgbGF3LiA8L0ZPTlQ+PC9ESVY+DQo8RElWPiZuYnNwOzwvRElWPg0KPERJVj48
+Rk9OVCBmYWNlPUFyaWFsIHNpemU9Mj5BbWVuZG1lbnQgSVY8L0ZPTlQ+PC9ESVY+
+DQo8RElWPiZuYnNwOzwvRElWPg0KPERJVj48Rk9OVCBmYWNlPUFyaWFsIHNpemU9
+Mj5UaGUgcmlnaHQgb2YgdGhlIHBlb3BsZSB0byBiZSBzZWN1cmUgaW4gdGhlaXIg
+DQpwZXJzb25zLCBob3VzZXMsIHBhcGVycywgYW5kIGVmZmVjdHMsIGFnYWluc3Qg
+dW5yZWFzb25hYmxlIHNlYXJjaGVzIGFuZCANCnNlaXp1cmVzLCBzaGFsbCBub3Qg
+YmUgdmlvbGF0ZWQsIGFuZCBubyB3YXJyYW50cyBzaGFsbCBpc3N1ZSwgYnV0IHVw
+b24gcHJvYmFibGUgDQpjYXVzZSwgc3VwcG9ydGVkIGJ5IG9hdGggb3IgYWZmaXJt
+YXRpb24sIGFuZCBwYXJ0aWN1bGFybHkgZGVzY3JpYmluZyB0aGUgcGxhY2UgDQp0
+byBiZSBzZWFyY2hlZCwgYW5kIHRoZSBwZXJzb25zIG9yIHRoaW5ncyB0byBiZSBz
+ZWl6ZWQuIDwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8RElWPjxG
+T05UIGZhY2U9QXJpYWwgc2l6ZT0yPkFtZW5kbWVudCBWPC9GT05UPjwvRElWPg0K
+PERJVj4mbmJzcDs8L0RJVj4NCjxESVY+PEZPTlQgZmFjZT1BcmlhbCBzaXplPTI+
+Tm8gcGVyc29uIHNoYWxsIGJlIGhlbGQgdG8gYW5zd2VyIGZvciBhIGNhcGl0YWws
+IG9yIA0Kb3RoZXJ3aXNlIGluZmFtb3VzIGNyaW1lLCB1bmxlc3Mgb24gYSBwcmVz
+ZW50bWVudCBvciBpbmRpY3RtZW50IG9mIGEgZ3JhbmQganVyeSwgDQpleGNlcHQg
+aW4gY2FzZXMgYXJpc2luZyBpbiB0aGUgbGFuZCBvciBuYXZhbCBmb3JjZXMsIG9y
+IGluIHRoZSBtaWxpdGlhLCB3aGVuIGluIA0KYWN0dWFsIHNlcnZpY2UgaW4gdGlt
+ZSBvZiB3YXIgb3IgcHVibGljIGRhbmdlcjsgbm9yIHNoYWxsIGFueSBwZXJzb24g
+YmUgc3ViamVjdCANCmZvciB0aGUgc2FtZSBvZmZlbnNlIHRvIGJlIHR3aWNlIHB1
+dCBpbiBqZW9wYXJkeSBvZiBsaWZlIG9yIGxpbWI7IG5vciBzaGFsbCBiZSANCmNv
+bXBlbGxlZCBpbiBhbnkgY3JpbWluYWwgY2FzZSB0byBiZSBhIHdpdG5lc3MgYWdh
+aW5zdCBoaW1zZWxmLCBub3IgYmUgZGVwcml2ZWQgDQpvZiBsaWZlLCBsaWJlcnR5
+LCBvciBwcm9wZXJ0eSwgd2l0aG91dCBkdWUgcHJvY2VzcyBvZiBsYXc7IG5vciBz
+aGFsbCBwcml2YXRlIA0KcHJvcGVydHkgYmUgdGFrZW4gZm9yIHB1YmxpYyB1c2Us
+IHdpdGhvdXQganVzdCBjb21wZW5zYXRpb24uIDwvRk9OVD48L0RJVj4NCjxESVY+
+Jm5ic3A7PC9ESVY+DQo8RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPkFtZW5k
+bWVudCBWSTwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8RElWPjxG
+T05UIGZhY2U9QXJpYWwgc2l6ZT0yPkluIGFsbCBjcmltaW5hbCBwcm9zZWN1dGlv
+bnMsIHRoZSBhY2N1c2VkIHNoYWxsIA0KZW5qb3kgdGhlIHJpZ2h0IHRvIGEgc3Bl
+ZWR5IGFuZCBwdWJsaWMgdHJpYWwsIGJ5IGFuIGltcGFydGlhbCBqdXJ5IG9mIHRo
+ZSBzdGF0ZSANCmFuZCBkaXN0cmljdCB3aGVyZWluIHRoZSBjcmltZSBzaGFsbCBo
+YXZlIGJlZW4gY29tbWl0dGVkLCB3aGljaCBkaXN0cmljdCBzaGFsbCANCmhhdmUg
+YmVlbiBwcmV2aW91c2x5IGFzY2VydGFpbmVkIGJ5IGxhdywgYW5kIHRvIGJlIGlu
+Zm9ybWVkIG9mIHRoZSBuYXR1cmUgYW5kIA0KY2F1c2Ugb2YgdGhlIGFjY3VzYXRp
+b247IHRvIGJlIGNvbmZyb250ZWQgd2l0aCB0aGUgd2l0bmVzc2VzIGFnYWluc3Qg
+aGltOyB0byANCmhhdmUgY29tcHVsc29yeSBwcm9jZXNzIGZvciBvYnRhaW5pbmcg
+d2l0bmVzc2VzIGluIGhpcyBmYXZvciwgYW5kIHRvIGhhdmUgdGhlIA0KYXNzaXN0
+YW5jZSBvZiBjb3Vuc2VsIGZvciBoaXMgZGVmZW5zZS4gPC9GT05UPjwvRElWPg0K
+PERJVj4mbmJzcDs8L0RJVj4NCjxESVY+PEZPTlQgZmFjZT1BcmlhbCBzaXplPTI+
+QW1lbmRtZW50IFZJSTwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8
+RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPkluIHN1aXRzIGF0IGNvbW1vbiBs
+YXcsIHdoZXJlIHRoZSB2YWx1ZSBpbiANCmNvbnRyb3ZlcnN5IHNoYWxsIGV4Y2Vl
+ZCB0d2VudHkgZG9sbGFycywgdGhlIHJpZ2h0IG9mIHRyaWFsIGJ5IGp1cnkgc2hh
+bGwgYmUgDQpwcmVzZXJ2ZWQsIGFuZCBubyBmYWN0IHRyaWVkIGJ5IGEganVyeSwg
+c2hhbGwgYmUgb3RoZXJ3aXNlIHJlZXhhbWluZWQgaW4gYW55IA0KY291cnQgb2Yg
+dGhlIFVuaXRlZCBTdGF0ZXMsIHRoYW4gYWNjb3JkaW5nIHRvIHRoZSBydWxlcyBv
+ZiB0aGUgY29tbW9uIGxhdy4gDQo8L0ZPTlQ+PC9ESVY+DQo8RElWPiZuYnNwOzwv
+RElWPg0KPERJVj48Rk9OVCBmYWNlPUFyaWFsIHNpemU9Mj5BbWVuZG1lbnQgVklJ
+STwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8RElWPjxGT05UIGZh
+Y2U9QXJpYWwgc2l6ZT0yPkV4Y2Vzc2l2ZSBiYWlsIHNoYWxsIG5vdCBiZSByZXF1
+aXJlZCwgbm9yIGV4Y2Vzc2l2ZSANCmZpbmVzIGltcG9zZWQsIG5vciBjcnVlbCBh
+bmQgdW51c3VhbCBwdW5pc2htZW50cyBpbmZsaWN0ZWQuIDwvRk9OVD48L0RJVj4N
+CjxESVY+Jm5ic3A7PC9ESVY+DQo8RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0y
+PkFtZW5kbWVudCBJWDwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8
+RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPlRoZSBlbnVtZXJhdGlvbiBpbiB0
+aGUgQ29uc3RpdHV0aW9uLCBvZiBjZXJ0YWluIA0KcmlnaHRzLCBzaGFsbCBub3Qg
+YmUgY29uc3RydWVkIHRvIGRlbnkgb3IgZGlzcGFyYWdlIG90aGVycyByZXRhaW5l
+ZCBieSB0aGUgDQpwZW9wbGUuIDwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9E
+SVY+DQo8RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPkFtZW5kbWVudCBYPC9G
+T05UPjwvRElWPg0KPERJVj4mbmJzcDs8L0RJVj4NCjxESVY+PEZPTlQgZmFjZT1B
+cmlhbCBzaXplPTI+VGhlIHBvd2VycyBub3QgZGVsZWdhdGVkIHRvIHRoZSBVbml0
+ZWQgU3RhdGVzIGJ5IA0KdGhlIENvbnN0aXR1dGlvbiwgbm9yIHByb2hpYml0ZWQg
+YnkgaXQgdG8gdGhlIHN0YXRlcywgYXJlIHJlc2VydmVkIHRvIHRoZSBzdGF0ZXMg
+DQpyZXNwZWN0aXZlbHksIG9yIHRvIHRoZSBwZW9wbGUuIDwvRk9OVD48L0RJVj48
+L0RJVj48L0JPRFk+PC9IVE1MPg0KAAAfADUQAQAAAKIAAAA8ADQANQAyADAARgA2
+ADEANQAxAEQAQQBGADIAQQA0ADQAQgBBADgANwA4AEIARgAyAEYAMwA4ADAAMwA0
+ADgARQAyADYARQA1AEAAYgByAC0AZQB4AGMAaAAtAGQAZQB2ADEALgBiAHIAZQB4
+AGMAaABhAG4AZwBlAC4AZABvAGwAcABoAGkAbgBzAGUAYQByAGMAaAAuAGMAbwBt
+AD4AAAAAAAMAgBD/////HwDzEAEAAAAmAAAAQgBpAGwAbAAgAG8AZgAgAFIAaQBn
+AGgAdABzAC4ARQBNAEwAAAAAAAsA9BAAAAAACwD1EAAAAAALAPYQAAAAAEAABzBR
+lpFluknFAUAACDBRlpFluknFAQMA3j+fTgAAAwDxPwkEAAAfAPg/AQAAABAAAAAz
+AGsAcgBlAGwAYQB5AAAAAgH5PwEAAABjAAAAAAAAANynQMjAQhAatLkIACsv4YIB
+AAAAAAAAAC9PPUJSLUVYQ0gtVEVTVC9PVT1GSVJTVCBBRE1JTklTVFJBVElWRSBH
+Uk9VUC9DTj1SRUNJUElFTlRTL0NOPTNLUkVMQVkAAB8A+j8BAAAAEAAAADMAawBy
+AGUAbABhAHkAAAACAfs/AQAAAGMAAAAAAAAA3KdAyMBCEBq0uQgAKy/hggEAAAAA
+AAAAL089QlItRVhDSC1URVNUL09VPUZJUlNUIEFETUlOSVNUUkFUSVZFIEdST1VQ
+L0NOPVJFQ0lQSUVOVFMvQ049M0tSRUxBWQAAAwD9P+QEAAADABlAAAAAAAMAGkAA
+AAAAHwAwQAEAAAAQAAAAMwBLAFIARQBMAEEAWQAAAB8AMUABAAAAEAAAADMASwBS
+AEUATABBAFkAAAAfADhAAQAAABAAAAAzAEsAUgBFAEwAQQBZAAAAHwA5QAEAAAAQ
+AAAAMwBLAFIARQBMAEEAWQAAAAMAdkD/////AwACWQAAFgADAAlZAgAAAAsAhYEI
+IAYAAAAAAMAAAAAAAABGAAAAAA6FAAAAAAAAAwCdgQggBgAAAAAAwAAAAAAAAEYA
+AAAAUoUAAJjDAQAfAJ6BCCAGAAAAAADAAAAAAAAARgAAAABUhQAAAQAAAAoAAAAx
+ADEALgAwAAAAAAADAOmBCCAGAAAAAADAAAAAAAAARgAAAAABhQAAAAAAAAsA7oEI
+IAYAAAAAAMAAAAAAAABGAAAAAAOFAAAAAAAAAwD4gQggBgAAAAAAwAAAAAAAAEYA
+AAAAEIUAAAAAAAADAP+BCCAGAAAAAADAAAAAAAAARgAAAAAYhQAAAAAAAAsAIIII
+IAYAAAAAAMAAAAAAAABGAAAAAAaFAAAAAAAACwAkggggBgAAAAAAwAAAAAAAAEYA
+AAAAgoUAAAAAAAAfACaCCCAGAAAAAADAAAAAAAAARgAAAACDhQAAAQAAACYAAAA0
+ADAANQAxADMAMQA1ADEANwAtADIANQAwADQAMgAwADAANQAAAAAAAwBxggggBgAA
+AAAAwAAAAAAAAEYAAAAAk4UAAAAAAAALACkAAAAAAAsAIwAAAAAAAgF/AAEAAABR
+AAAAPDQ1MjBGNjE1MURBRjJBNDRCQTg3OEJGMkYzODAzNDhFMjZFNUBici1leGNo
+LWRldjEuYnJleGNoYW5nZS5kb2xwaGluc2VhcmNoLmNvbT4AAAAAC/o=
+
+--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_--
+
diff --git a/spec/fixtures/files/tnef-attachment-truncated.email b/spec/fixtures/files/tnef-attachment-truncated.email
new file mode 100644
index 000000000..365a5a442
--- /dev/null
+++ b/spec/fixtures/files/tnef-attachment-truncated.email
@@ -0,0 +1,34 @@
+From hello@blah.local Fri Feb 21 16:23:14 2013
+Return-path: <bar@example.org>
+Envelope-to: foo@example.org
+Delivery-date: Fri, 21 Feb 2013 16:23:14 +0000
+Content-Type: multipart/mixed;
+ boundary="_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_"
+From: <bar@example.org>
+To: <foo@example.org>
+Sender: <hello@blah.local>
+Date: Fri, 21 Feb 2013 16:23:04 +0000
+Subject: here's a useless email
+Message-ID: <12345@blah.local>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator: <12345@blah.local>
+acceptlanguage: en-US, en-GB
+MIME-Version: 1.0
+
+--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: quoted-printable
+
+Some introductory text here, before the malformed TNEF attachment.
+
+--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_
+Content-Disposition: attachment; filename="winmail.dat"
+Content-Transfer-Encoding: base64
+Content-Type: application/ms-tnef; name="winmail.dat"
+
+eJ8+IkV9AQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA5AQAAAAAAADoAAEJgAEAIQAAAEMyRUUzRUYx
+
+--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_--
+
diff --git a/spec/fixtures/files/unrecognized-encoding-mail.email b/spec/fixtures/files/unrecognized-encoding-mail.email
new file mode 100644
index 000000000..266a90fbc
--- /dev/null
+++ b/spec/fixtures/files/unrecognized-encoding-mail.email
@@ -0,0 +1,36 @@
+From xxx@example.com Fri Jun 21 07:50:52 2013
+Return-path: <xxx@example.com>
+Envelope-to: xxx@example.com
+Delivery-date: Fri, 21 Jun 2013 07:50:52 +0100
+Message-ID: <185C0D48380D7AE612DD38A527D5EAF2@tmvbalem>
+From: "cttlqvx" <xxx@example.com>
+To: <xxx@example.com>
+Subject: =?hz-gb-2312?B?fntPck9ISXpWQn59c3J5dW95d3MoQUQpICAgIA==?=
+Date: Fri, 21 Jun 2013 14:48:20 +0800
+MIME-Version: 1.0
+Content-Type: multipart/related;
+ type="multipart/alternative";
+ boundary="----=_NextPart_000_02ED_01A0462A.178683F0"
+X-Priority: 1
+X-MSMail-Priority: High
+X-Mailer: Microsoft Outlook Express 6.00.2900.5512
+X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.5512
+
+This is a multi-part message in MIME format.
+
+------=_NextPart_000_02ED_01A0462A.178683F0
+Content-Type: multipart/alternative;
+ boundary="----=_NextPart_001_09EC_01A0462A.178683F0"
+
+------=_NextPart_001_09EC_01A0462A.178683F0
+Content-Type: text/plain;
+ charset="hz-gb-2312"
+Content-Transfer-Encoding: base64
+
+
+------=_NextPart_001_09EC_01A0462A.178683F0
+Content-Type: text/html;
+ charset="hz-gb-2312"
+Content-Transfer-Encoding: base64
+
+------=_NextPart_001_09EC_01A0462A.178683F0--
diff --git a/spec/fixtures/foi_attachments.yml b/spec/fixtures/foi_attachments.yml
deleted file mode 100644
index 8b1378917..000000000
--- a/spec/fixtures/foi_attachments.yml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/spec/fixtures/holidays.yml b/spec/fixtures/holidays.yml
index 5a3d018bc..e6b8889c3 100644
--- a/spec/fixtures/holidays.yml
+++ b/spec/fixtures/holidays.yml
@@ -1,3 +1,12 @@
+# == Schema Information
+#
+# Table name: holidays
+#
+# id :integer not null, primary key
+# day :date
+# description :text
+#
+
date20071130:
id: '1'
day: '2007-11-30'
diff --git a/spec/fixtures/incoming_messages.yml b/spec/fixtures/incoming_messages.yml
index fca5c716c..0bf392be1 100644
--- a/spec/fixtures/incoming_messages.yml
+++ b/spec/fixtures/incoming_messages.yml
@@ -1,3 +1,25 @@
+# == Schema Information
+#
+# Table name: incoming_messages
+#
+# id :integer not null, primary key
+# info_request_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# raw_email_id :integer not null
+# cached_attachment_text_clipped :text
+# cached_main_body_text_folded :text
+# cached_main_body_text_unfolded :text
+# subject :text
+# mail_from_domain :text
+# valid_to_reply_to :boolean
+# last_parsed :datetime
+# mail_from :text
+# sent_at :datetime
+# prominence :string(255) default("normal"), not null
+# prominence_reason :text
+#
+
useless_incoming_message:
id: 1
info_request_id: 101
diff --git a/spec/fixtures/info_request_events.yml b/spec/fixtures/info_request_events.yml
index c1a00ad47..b2f40cc37 100644
--- a/spec/fixtures/info_request_events.yml
+++ b/spec/fixtures/info_request_events.yml
@@ -1,3 +1,20 @@
+# == Schema Information
+#
+# Table name: info_request_events
+#
+# id :integer not null, primary key
+# info_request_id :integer not null
+# event_type :text not null
+# params_yaml :text not null
+# created_at :datetime not null
+# described_state :string(255)
+# calculated_state :string(255)
+# last_described_at :datetime
+# incoming_message_id :integer
+# outgoing_message_id :integer
+# comment_id :integer
+#
+
useless_outgoing_message_event:
id: 900
params_yaml: "--- \n\
@@ -171,3 +188,14 @@ anonymous_external_outgoing_message_event:
created_at: 2009-01-03 02:23:45.6789100
described_state: waiting_response
calculated_state: waiting_response
+
+other_request_outgoing_message_event:
+ id: 916
+ params_yaml: "--- \n\
+ :outgoing_message_id: 10\n"
+ outgoing_message_id: 10
+ info_request_id: 111
+ event_type: sent
+ created_at: <%= Time.now %>
+ described_state: waiting_response
+ calculated_state: waiting_response
diff --git a/spec/fixtures/info_requests.yml b/spec/fixtures/info_requests.yml
index 9361ec486..d64807a49 100644
--- a/spec/fixtures/info_requests.yml
+++ b/spec/fixtures/info_requests.yml
@@ -1,3 +1,27 @@
+# == Schema Information
+#
+# Table name: info_requests
+#
+# id :integer not null, primary key
+# title :text not null
+# user_id :integer
+# public_body_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# described_state :string(255) not null
+# awaiting_description :boolean default(FALSE), not null
+# prominence :string(255) default("normal"), not null
+# url_title :text not null
+# law_used :string(255) default("foi"), not null
+# allow_new_responses_from :string(255) default("anybody"), not null
+# handle_rejected_responses :string(255) default("bounce"), not null
+# idhash :string(255) not null
+# external_user_name :string(255)
+# external_url :string(255)
+# attention_requested :boolean default(FALSE)
+# comments_allowed :boolean default(TRUE), not null
+#
+
fancy_dog_request:
id: 101
title: Why do you have & such a fancy dog?
@@ -107,3 +131,15 @@ anonymous_external_request:
awaiting_description: false
comments_allowed: true
idhash: 7654321a
+other_request:
+ id: 111
+ title: Another request
+ url_title: another_request
+ created_at: 2010-01-01 02:23:45.6789100
+ updated_at: 2010-01-01 02:23:45.6789100
+ public_body_id: 7
+ user_id: 6
+ described_state: waiting_response
+ awaiting_description: false
+ comments_allowed: true
+ idhash: b234567
diff --git a/spec/fixtures/locale/en/app.po b/spec/fixtures/locale/en/app.po
index 91af9b72b..6d5bca1f9 100644
--- a/spec/fixtures/locale/en/app.po
+++ b/spec/fixtures/locale/en/app.po
@@ -31,7 +31,7 @@ msgid ""
msgstr ""
#: app/views/comment/_comment_form.rhtml:16
-msgid " (<strong>no ranty</strong> politics, read our <a href=\"%s\">moderation policy</a>)"
+msgid " (<strong>no ranty</strong> politics, read our <a href=\"{{url}}\">moderation policy</a>)"
msgstr ""
#: app/views/request/upload_response.rhtml:40
@@ -71,7 +71,7 @@ msgstr ""
#: app/views/public_body/view_email.rhtml:30
msgid ""
-" If you know the address to use, then please <a href=\"%s\">send it to us</a>.\n"
+" If you know the address to use, then please <a href=\"{{url}}\">send it to us</a>.\n"
" You may be able to find the address on their website, or by phoning them up and asking."
msgstr ""
@@ -123,20 +123,20 @@ msgid " when you send this message."
msgstr ""
#: app/views/public_body/show.rhtml:87
-msgid "%d Freedom of Information request to %s"
-msgid_plural "%d Freedom of Information requests to %s"
+msgid "{{count}} Freedom of Information request to {{public_body_name}}"
+msgid_plural "{{count}} Freedom of Information requests to {{public_body_name}}"
msgstr[0] ""
msgstr[1] ""
#: app/views/general/frontpage.rhtml:43
-msgid "%d request"
-msgid_plural "%d requests"
+msgid "{{count}} request"
+msgid_plural "{{count}} requests"
msgstr[0] ""
msgstr[1] ""
#: app/views/public_body/_body_listing_single.rhtml:21
-msgid "%d request made."
-msgid_plural "%d requests made."
+msgid "{{count}} request made."
+msgid_plural "{{count}} requests made."
msgstr[0] ""
msgstr[1] ""
@@ -187,43 +187,37 @@ msgstr ""
msgid "3. Now check your request"
msgstr ""
-#: app/views/public_body/show.rhtml:56
-msgid "<a class=\"link_button_green\" href=\"{{url}}\">{{text}}</a>"
-msgstr ""
-
#: app/views/request/_after_actions.rhtml:9
-msgid "<a href=\"%s\">Add an annotation</a> (to help the requester or others)"
+msgid "<a href=\"{{url}}\">Add an annotation</a> (to help the requester or others)"
msgstr ""
#: app/views/public_body/list.rhtml:29
-msgid "<a href=\"%s\">Are we missing a public authority?</a>."
+msgid "Are we missing a public authority?"
msgstr ""
#: app/views/request/_sidebar.rhtml:39
-msgid ""
-"<a href=\"%s\">Are you the owner of\n"
-" any commercial copyright on this page?</a>"
+msgid "Are you the owner of any commercial copyright on this page?"
msgstr ""
#: app/views/general/search.rhtml:168
-msgid "<a href=\"%s\">Browse all</a> or <a href=\"%s\">ask us to add one</a>."
+msgid "<a href=\"{{browse_url}}\">Browse all</a> or <a href=\"{{add_url}}\">ask us to add one</a>."
msgstr ""
#: app/views/public_body/list.rhtml:51
-msgid "<a href=\"%s\">Can't find the one you want?</a>"
+msgid "Can't find the one you want?"
msgstr ""
#: app/views/user/show.rhtml:118
-msgid "<a href=\"%s\">Sign in</a> to change password, subscriptions and more ({{user_name}} only)"
+msgid "<a href=\"{{url}}\">Sign in</a> to change password, subscriptions and more ({{user_name}} only)"
msgstr ""
#: app/views/request/_followup.rhtml:66 app/views/request/_followup.rhtml:73
#: app/views/request/show.rhtml:83 app/views/request/show.rhtml:87
-msgid "<a href=\"%s\">details</a>"
+msgid "details"
msgstr ""
#: app/views/request/_followup.rhtml:101
-msgid "<a href=\"%s\">what's that?</a>"
+msgid "what's that?"
msgstr ""
#: app/controllers/request_game_controller.rb:23
@@ -293,11 +287,11 @@ msgid "<p>We're glad you got some of the information that you wanted. If you fou
msgstr ""
#: app/controllers/request_controller.rb:318
-msgid "<p>You do not need to include your email in the request in order to get a reply (<a href=\"%s\">details</a>).</p>"
+msgid "<p>You do not need to include your email in the request in order to get a reply (<a href=\"{{url}}\">details</a>).</p>"
msgstr ""
#: app/controllers/request_controller.rb:316
-msgid "<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"%s\">details</a>).</p>"
+msgid "<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"{{url}}\">details</a>).</p>"
msgstr ""
#: app/controllers/request_controller.rb:324
@@ -327,7 +321,7 @@ msgstr ""
#: app/views/request/new.rhtml:135
msgid ""
"<strong> Can I request information about myself?</strong>\n"
-"\t\t\t<a href=\"%s\">No! (Click here for details)</a>"
+"\t\t\t<a href=\"{{url}}\">No! (Click here for details)</a>"
msgstr ""
#: app/views/general/_advanced_search_tips.rhtml:12
@@ -413,7 +407,7 @@ msgstr ""
#: app/views/request/preview.rhtml:31
msgid ""
"<strong>Privacy note:</strong> If you want to request private information about\n"
-" yourself then <a href=\"%s\">click here</a>."
+" yourself then <a href=\"{{url}}\">click here</a>."
msgstr ""
#: app/views/user/set_crop_profile_photo.rhtml:35
@@ -576,7 +570,7 @@ msgstr ""
#: app/views/request/show_response.rhtml:29
msgid ""
"At the bottom of this page, write a reply to them trying to persuade them to scan it in\n"
-" (<a href=\"%s\">more details</a>)."
+" (<a href=\"{{url}}\">more details</a>)."
msgstr ""
#: app/views/request/upload_response.rhtml:33
@@ -921,7 +915,7 @@ msgstr ""
#: app/views/request/upload_response.rhtml:23
msgid ""
"Enter your response below. You may attach one file (use email, or \n"
-"<a href=\"%s\">contact us</a> if you need more)."
+"<a href=\"{{url}}\">contact us</a> if you need more)."
msgstr ""
#: app/models/info_request.rb:259 app/models/info_request.rb:277
@@ -950,16 +944,16 @@ msgstr ""
#: app/views/request/new.rhtml:128
msgid ""
-"Everything that you enter on this page \n"
+"Everything that you enter on this page\n"
" will be <strong>displayed publicly</strong> on\n"
-" this website forever (<a href=\"%s\">why?</a>)."
+" this website forever (<a href=\"{{url}}\">why?</a>)."
msgstr ""
#: app/views/request/new.rhtml:120
msgid ""
-"Everything that you enter on this page, including <strong>your name</strong>, \n"
+"Everything that you enter on this page, including <strong>your name</strong>,\n"
" will be <strong>displayed publicly</strong> on\n"
-" this website forever (<a href=\"%s\">why?</a>)."
+" this website forever (<a href=\"{{url}}\">why?</a>)."
msgstr ""
#: locale/model_attributes.rb:68
@@ -1007,7 +1001,7 @@ msgid "Failed to convert image to a PNG"
msgstr ""
#: app/models/profile_photo.rb:105
-msgid "Failed to convert image to the correct size: at %{cols}x%{rows}, need %{width}x%{height}"
+msgid "Failed to convert image to the correct size: at {{cols}}x{{rows}}, need {{width}}x{{height}}"
msgstr ""
#: app/views/general/search.rhtml:117
@@ -1018,7 +1012,7 @@ msgstr ""
msgid ""
"First, type in the <strong>name of the UK public authority</strong> you'd \n"
" like information from. <strong>By law, they have to respond</strong>\n"
-" (<a href=\"%s#%s\">why?</a>)."
+" (<a href=\"{{url}}\">why?</a>)."
msgstr ""
#: locale/model_attributes.rb:88
@@ -1147,7 +1141,7 @@ msgstr ""
msgid ""
"From the request page, try replying to a particular message, rather than sending\n"
" a general followup. If you need to make a general followup, and know\n"
-" an email which will go to the right place, please <a href=\"%s\">send it to us</a>."
+" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>."
msgstr ""
#: app/views/request/_correspondence.rhtml:12
@@ -1278,7 +1272,7 @@ msgid "I've received an <strong>error message</strong>"
msgstr ""
#: app/views/public_body/view_email.rhtml:28
-msgid "If the address is wrong, or you know a better address, please <a href=\"%s\">contact us</a>."
+msgid "If the address is wrong, or you know a better address, please <a href=\"{{url}}\">contact us</a>."
msgstr ""
#: app/views/request_mailer/stopped_responses.rhtml:10
@@ -1292,21 +1286,21 @@ msgstr ""
msgid ""
"If you are dissatisfied by the response you got from\n"
" the public authority, you have the right to\n"
-" complain (<a href=\"%s\">details</a>)."
+" complain (<a href=\"{{url}}\">details</a>)."
msgstr ""
#: app/views/user/no_cookies.rhtml:20
-msgid "If you are still having trouble, please <a href=\"%s\">contact us</a>."
+msgid "If you are still having trouble, please <a href=\"{{url}}\">contact us</a>."
msgstr ""
#: app/views/request/hidden.rhtml:15
-msgid "If you are the requester, then you may <a href=\"%s\">sign in</a> to view the request."
+msgid "If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the request."
msgstr ""
#: app/views/request/new.rhtml:123
msgid ""
"If you are thinking of using a pseudonym,\n"
-" please <a href=\"%s\">read this first</a>."
+" please <a href=\"{{url}}\">read this first</a>."
msgstr ""
#: app/views/request/show.rhtml:105
@@ -1423,10 +1417,6 @@ msgstr ""
msgid "InfoRequestEvent|Params yaml"
msgstr ""
-#: locale/model_attributes.rb:41
-msgid "InfoRequestEvent|Prominence"
-msgstr ""
-
#: locale/model_attributes.rb:102
msgid "InfoRequest|Allow new responses from"
msgstr ""
@@ -1497,7 +1487,7 @@ msgid "Joined {{site_name}} in"
msgstr ""
#: app/views/request/new.rhtml:106
-msgid "Keep it <strong>focused</strong>, you'll be more likely to get what you want (<a href=\"%s\">why?</a>)."
+msgid "Keep it <strong>focused</strong>, you'll be more likely to get what you want (<a href=\"{{url}}\">why?</a>)."
msgstr ""
#: app/views/request/_request_filter_form.rhtml:6
@@ -1837,7 +1827,7 @@ msgid "Please"
msgstr ""
#: app/views/user/no_cookies.rhtml:15
-msgid "Please <a href=\"%s\">get in touch</a> with us so we can fix it."
+msgid "Please <a href=\"{{url}}\">get in touch</a> with us so we can fix it."
msgstr ""
#: app/views/request/show.rhtml:52
@@ -1990,7 +1980,7 @@ msgid ""
msgstr ""
#: app/models/outgoing_message.rb:157
-msgid "Please sign at the bottom with your name, or alter the \"%{signoff}\" signature"
+msgid "Please sign at the bottom with your name, or alter the \"{{signoff}}\" signature"
msgstr ""
#: app/views/user/sign.rhtml:8
@@ -2337,8 +2327,9 @@ msgid "Search the site to find what you were looking for."
msgstr ""
#: app/views/public_body/show.rhtml:85
-msgid "Search within the %d Freedom of Information requests to %s"
-msgid_plural "Search within the %d Freedom of Information requests made to %s"
+msgid "Search within the {{count}} Freedom of Information requests to {{public_body_name}}"
+msgid_plural "Search within the {{count}} Freedom of Information requests to {{public_body_name}}"
+
msgstr[0] ""
msgstr[1] ""
@@ -2680,10 +2671,7 @@ msgid "The request was refused by the public authority"
msgstr ""
#: app/views/request/hidden.rhtml:9
-msgid ""
-"The request you have tried to view has been removed. There are\n"
-"various reasons why we might have done this, sorry we can't be more specific here. Please <a\n"
-" href=\"%s\">contact us</a> if you have any questions."
+msgid "The request you have tried to view has been removed. There are\\nvarious reasons why we might have done this, sorry we can't be more specific here. Please <a\\n href=\"{{url}}\">contact us</a> if you have any questions."
msgstr ""
#: app/views/general/_advanced_search_tips.rhtml:36
@@ -2692,14 +2680,14 @@ msgstr ""
#: app/views/request/_followup.rhtml:59
msgid ""
-"The response to your request has been <strong>delayed</strong>. You can say that, \n"
+"The response to your request has been <strong>delayed</strong>. You can say that,\n"
" by law, the authority should normally have responded\n"
" <strong>promptly</strong> and"
msgstr ""
#: app/views/request/_followup.rhtml:71
msgid ""
-"The response to your request is <strong>long overdue</strong>. You can say that, by \n"
+"The response to your request is <strong>long overdue</strong>. You can say that, by\n"
" law, under all circumstances, the authority should have responded\n"
" by now"
msgstr ""
@@ -2804,15 +2792,9 @@ msgstr ""
msgid "There are {{count}} new annotations on your {{info_request}} request. Follow this link to see what they wrote."
msgstr ""
-#: app/views/public_body/show.rhtml:7
-msgid "There is %d person following this authority"
-msgid_plural "There are %d people following this authority"
-msgstr[0] ""
-msgstr[1] ""
-
#: app/views/request/_sidebar.rhtml:5
-msgid "There is %d person following this request"
-msgid_plural "There are %d people following this request"
+msgid "There is {{count}} person following this request"
+msgid_plural "There are {{count}} people following this request"
msgstr[0] ""
msgstr[1] ""
@@ -2876,7 +2858,7 @@ msgstr ""
#: app/views/request/_hidden_correspondence.rhtml:23
msgid ""
"This comment has been hidden. See annotations to\n"
-" find out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response."
+" find out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response."
msgstr ""
#: app/views/request/new.rhtml:63
@@ -2906,7 +2888,7 @@ msgstr ""
#: app/views/request/_hidden_correspondence.rhtml:17
msgid ""
"This outgoing message has been hidden. See annotations to\n"
-"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response."
+"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response."
msgstr ""
#: app/views/request/_describe_state.rhtml:44
@@ -2919,14 +2901,14 @@ msgid "This person has made no Freedom of Information requests using this site."
msgstr ""
#: app/views/user/show.rhtml:149
-msgid "This person's %d Freedom of Information request"
-msgid_plural "This person's %d Freedom of Information requests"
+msgid "This person's {{count}} Freedom of Information request"
+msgid_plural "This person's {{count}} Freedom of Information requests"
msgstr[0] ""
msgstr[1] ""
#: app/views/user/show.rhtml:179
-msgid "This person's %d annotation"
-msgid_plural "This person's %d annotations"
+msgid "This person's {{count}} annotation"
+msgid_plural "This person's {{count}} annotations"
msgstr[0] ""
msgstr[1] ""
@@ -2965,7 +2947,7 @@ msgstr ""
#: app/views/request/show.rhtml:11
msgid ""
"This request is hidden, so that only you the requester can see it. Please\n"
-" <a href=\"%s\">contact us</a> if you are not sure why."
+" <a href=\"{{url}}\">contact us</a> if you are not sure why."
msgstr ""
#: app/views/request/_describe_state.rhtml:7
@@ -2976,7 +2958,7 @@ msgstr ""
#: app/views/request/_hidden_correspondence.rhtml:10
msgid ""
"This response has been hidden. See annotations to find out why.\n"
-" If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response."
+" If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response."
msgstr ""
#: app/views/request/details.rhtml:6
@@ -3202,7 +3184,7 @@ msgstr ""
msgid ""
"Unfortunately we don't know the FOI\n"
"email address for that authority, so we can't validate this.\n"
-"Please <a href=\"%s\">contact us</a> to sort it out."
+"Please <a href=\"{{url}}\">contact us</a> to sort it out."
msgstr ""
#: app/views/request/new_bad_contact.rhtml:5
@@ -3439,7 +3421,7 @@ msgid ""
msgstr ""
#: app/views/request/new_please_describe.rhtml:16
-msgid "When you're done, <strong>come back here</strong>, <a href=\"%s\">reload this page</a> and file your new request."
+msgid "When you're done, <strong>come back here</strong>, <a href=\"{{url}}\">reload this page</a> and file your new request."
msgstr ""
#: app/views/request/show_response.rhtml:13
@@ -3571,7 +3553,7 @@ msgstr ""
msgid ""
"You may be able to find\n"
" one on their website, or by phoning them up and asking. If you manage\n"
-" to find one, then please <a href=\"%s\">send it to us</a>."
+" to find one, then please <a href=\"{{url}}\">send it to us</a>."
msgstr ""
#: app/views/request/new_bad_contact.rhtml:6
@@ -3642,24 +3624,19 @@ msgid "You've now cleared your profile photo"
msgstr ""
#: app/views/user/show.rhtml:149
-msgid "Your %d Freedom of Information request"
-msgid_plural "Your %d Freedom of Information requests"
+msgid "Your {{count}} Freedom of Information request"
+msgid_plural "Your {{count}} Freedom of Information requests"
msgstr[0] ""
msgstr[1] ""
#: app/views/user/show.rhtml:179
-msgid "Your %d annotation"
-msgid_plural "Your %d annotations"
+msgid "Your {{count}} annotation"
+msgid_plural "Your {{count}} annotations"
msgstr[0] ""
msgstr[1] ""
#: app/views/user/_signup.rhtml:22
-msgid ""
-"Your <strong>name will appear publicly</strong> \n"
-" (<a href=\"%s\">why?</a>)\n"
-" on this website and in search engines. If you\n"
-" are thinking of using a pseudonym, please \n"
-" <a href=\"%s\">read this first</a>."
+msgid "Your <strong>name will appear publicly</strong>\\n (<a href=\"{{why_url}}\">why?</a>)\\n on this website and in search engines. If you\\n are thinking of using a pseudonym, please\\n <a href=\"{{help_url}}\">read this first</a>."
msgstr ""
#: app/views/user/show.rhtml:172
@@ -3682,7 +3659,7 @@ msgid "Your email subscriptions"
msgstr ""
#: app/controllers/request_controller.rb:598
-msgid "Your follow up has not been sent because this request has been stopped to prevent spam. Please <a href=\"%s\">contact us</a> if you really want to send a follow up message."
+msgid "Your follow up has not been sent because this request has been stopped to prevent spam. Please <a href=\"{{url}}\">contact us</a> if you really want to send a follow up message."
msgstr ""
#: app/controllers/request_controller.rb:626
@@ -3712,7 +3689,7 @@ msgstr ""
#: app/views/request/preview.rhtml:8
msgid ""
"Your name, request and any responses will appear in <strong>search engines</strong>\n"
-" (<a href=\"%s\">details</a>)."
+" (<a href=\"{{url}}\">details</a>)."
msgstr ""
#: app/views/user/_signup.rhtml:18
@@ -3746,7 +3723,7 @@ msgid "Your request:"
msgstr ""
#: app/views/request/upload_response.rhtml:8
-msgid "Your response will <strong>appear on the Internet</strong>, <a href=\"%s\">read why</a> and answers to other questions."
+msgid "Your response will <strong>appear on the Internet</strong>, <a href=\"{{url}}\">read why</a> and answers to other questions."
msgstr ""
#: app/views/comment/new.rhtml:63
@@ -3990,7 +3967,7 @@ msgid ""
"no longer exists. If you are trying to make\n"
" From the request page, try replying to a particular message, rather than sending\n"
" a general followup. If you need to make a general followup, and know\n"
-" an email which will go to the right place, please <a href=\"%s\">send it to us</a>."
+" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>."
msgstr ""
#: app/views/request/show.rhtml:72
diff --git a/spec/fixtures/locale/en_GB/app.po b/spec/fixtures/locale/en_GB/app.po
index 91af9b72b..f106273a0 100644
--- a/spec/fixtures/locale/en_GB/app.po
+++ b/spec/fixtures/locale/en_GB/app.po
@@ -31,7 +31,7 @@ msgid ""
msgstr ""
#: app/views/comment/_comment_form.rhtml:16
-msgid " (<strong>no ranty</strong> politics, read our <a href=\"%s\">moderation policy</a>)"
+msgid " (<strong>no ranty</strong> politics, read our <a href=\"{{url}}\">moderation policy</a>)"
msgstr ""
#: app/views/request/upload_response.rhtml:40
@@ -71,7 +71,7 @@ msgstr ""
#: app/views/public_body/view_email.rhtml:30
msgid ""
-" If you know the address to use, then please <a href=\"%s\">send it to us</a>.\n"
+" If you know the address to use, then please <a href=\"{{url}}\">send it to us</a>.\n"
" You may be able to find the address on their website, or by phoning them up and asking."
msgstr ""
@@ -123,20 +123,20 @@ msgid " when you send this message."
msgstr ""
#: app/views/public_body/show.rhtml:87
-msgid "%d Freedom of Information request to %s"
-msgid_plural "%d Freedom of Information requests to %s"
+msgid "{{count}} Freedom of Information request to {{public_body_name}}"
+msgid_plural "{{count}} Freedom of Information requests to {{public_body_name}}"
msgstr[0] ""
msgstr[1] ""
#: app/views/general/frontpage.rhtml:43
-msgid "%d request"
-msgid_plural "%d requests"
+msgid "{{count}} request"
+msgid_plural "{{count}} requests"
msgstr[0] ""
msgstr[1] ""
#: app/views/public_body/_body_listing_single.rhtml:21
-msgid "%d request made."
-msgid_plural "%d requests made."
+msgid "{{count}} request made."
+msgid_plural "{{count}} requests made."
msgstr[0] ""
msgstr[1] ""
@@ -187,43 +187,37 @@ msgstr ""
msgid "3. Now check your request"
msgstr ""
-#: app/views/public_body/show.rhtml:56
-msgid "<a class=\"link_button_green\" href=\"{{url}}\">{{text}}</a>"
-msgstr ""
-
#: app/views/request/_after_actions.rhtml:9
-msgid "<a href=\"%s\">Add an annotation</a> (to help the requester or others)"
+msgid "<a href=\"{{url}}\">Add an annotation</a> (to help the requester or others)"
msgstr ""
#: app/views/public_body/list.rhtml:29
-msgid "<a href=\"%s\">Are we missing a public authority?</a>."
+msgid "Are we missing a public authority?"
msgstr ""
#: app/views/request/_sidebar.rhtml:39
-msgid ""
-"<a href=\"%s\">Are you the owner of\n"
-" any commercial copyright on this page?</a>"
+msgid "Are you the owner of any commercial copyright on this page?"
msgstr ""
#: app/views/general/search.rhtml:168
-msgid "<a href=\"%s\">Browse all</a> or <a href=\"%s\">ask us to add one</a>."
+msgid "<a href=\"{{browse_url}}\">Browse all</a> or <a href=\"{{add_url}}\">ask us to add one</a>."
msgstr ""
#: app/views/public_body/list.rhtml:51
-msgid "<a href=\"%s\">Can't find the one you want?</a>"
+msgid "Can't find the one you want?"
msgstr ""
#: app/views/user/show.rhtml:118
-msgid "<a href=\"%s\">Sign in</a> to change password, subscriptions and more ({{user_name}} only)"
+msgid "<a href=\"{{url}}\">Sign in</a> to change password, subscriptions and more ({{user_name}} only)"
msgstr ""
#: app/views/request/_followup.rhtml:66 app/views/request/_followup.rhtml:73
#: app/views/request/show.rhtml:83 app/views/request/show.rhtml:87
-msgid "<a href=\"%s\">details</a>"
+msgid "details"
msgstr ""
#: app/views/request/_followup.rhtml:101
-msgid "<a href=\"%s\">what's that?</a>"
+msgid "what's that?"
msgstr ""
#: app/controllers/request_game_controller.rb:23
@@ -293,11 +287,11 @@ msgid "<p>We're glad you got some of the information that you wanted. If you fou
msgstr ""
#: app/controllers/request_controller.rb:318
-msgid "<p>You do not need to include your email in the request in order to get a reply (<a href=\"%s\">details</a>).</p>"
+msgid "<p>You do not need to include your email in the request in order to get a reply (<a href=\"{{url}}\">details</a>).</p>"
msgstr ""
#: app/controllers/request_controller.rb:316
-msgid "<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"%s\">details</a>).</p>"
+msgid "<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"{{url}}\">details</a>).</p>"
msgstr ""
#: app/controllers/request_controller.rb:324
@@ -327,7 +321,7 @@ msgstr ""
#: app/views/request/new.rhtml:135
msgid ""
"<strong> Can I request information about myself?</strong>\n"
-"\t\t\t<a href=\"%s\">No! (Click here for details)</a>"
+"\t\t\t<a href=\"{{url}}\">No! (Click here for details)</a>"
msgstr ""
#: app/views/general/_advanced_search_tips.rhtml:12
@@ -413,7 +407,7 @@ msgstr ""
#: app/views/request/preview.rhtml:31
msgid ""
"<strong>Privacy note:</strong> If you want to request private information about\n"
-" yourself then <a href=\"%s\">click here</a>."
+" yourself then <a href=\"{{url}}\">click here</a>."
msgstr ""
#: app/views/user/set_crop_profile_photo.rhtml:35
@@ -576,7 +570,7 @@ msgstr ""
#: app/views/request/show_response.rhtml:29
msgid ""
"At the bottom of this page, write a reply to them trying to persuade them to scan it in\n"
-" (<a href=\"%s\">more details</a>)."
+" (<a href=\"{{url}}\">more details</a>)."
msgstr ""
#: app/views/request/upload_response.rhtml:33
@@ -921,7 +915,7 @@ msgstr ""
#: app/views/request/upload_response.rhtml:23
msgid ""
"Enter your response below. You may attach one file (use email, or \n"
-"<a href=\"%s\">contact us</a> if you need more)."
+"<a href=\"{{url}}\">contact us</a> if you need more)."
msgstr ""
#: app/models/info_request.rb:259 app/models/info_request.rb:277
@@ -950,16 +944,16 @@ msgstr ""
#: app/views/request/new.rhtml:128
msgid ""
-"Everything that you enter on this page \n"
+"Everything that you enter on this page\n"
" will be <strong>displayed publicly</strong> on\n"
-" this website forever (<a href=\"%s\">why?</a>)."
+" this website forever (<a href=\"{{url}}\">why?</a>)."
msgstr ""
#: app/views/request/new.rhtml:120
msgid ""
-"Everything that you enter on this page, including <strong>your name</strong>, \n"
+"Everything that you enter on this page, including <strong>your name</strong>,\n"
" will be <strong>displayed publicly</strong> on\n"
-" this website forever (<a href=\"%s\">why?</a>)."
+" this website forever (<a href=\"{{url}}\">why?</a>)."
msgstr ""
#: locale/model_attributes.rb:68
@@ -1007,7 +1001,7 @@ msgid "Failed to convert image to a PNG"
msgstr ""
#: app/models/profile_photo.rb:105
-msgid "Failed to convert image to the correct size: at %{cols}x%{rows}, need %{width}x%{height}"
+msgid "Failed to convert image to the correct size: at {{cols}}x{{rows}}, need {{width}}x{{height}}"
msgstr ""
#: app/views/general/search.rhtml:117
@@ -1018,7 +1012,7 @@ msgstr ""
msgid ""
"First, type in the <strong>name of the UK public authority</strong> you'd \n"
" like information from. <strong>By law, they have to respond</strong>\n"
-" (<a href=\"%s#%s\">why?</a>)."
+" (<a href=\"{{url}}\">why?</a>)."
msgstr ""
#: locale/model_attributes.rb:88
@@ -1147,7 +1141,7 @@ msgstr ""
msgid ""
"From the request page, try replying to a particular message, rather than sending\n"
" a general followup. If you need to make a general followup, and know\n"
-" an email which will go to the right place, please <a href=\"%s\">send it to us</a>."
+" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>."
msgstr ""
#: app/views/request/_correspondence.rhtml:12
@@ -1278,7 +1272,7 @@ msgid "I've received an <strong>error message</strong>"
msgstr ""
#: app/views/public_body/view_email.rhtml:28
-msgid "If the address is wrong, or you know a better address, please <a href=\"%s\">contact us</a>."
+msgid "If the address is wrong, or you know a better address, please <a href=\"{{url}}\">contact us</a>."
msgstr ""
#: app/views/request_mailer/stopped_responses.rhtml:10
@@ -1292,21 +1286,21 @@ msgstr ""
msgid ""
"If you are dissatisfied by the response you got from\n"
" the public authority, you have the right to\n"
-" complain (<a href=\"%s\">details</a>)."
+" complain (<a href=\"{{url}}\">details</a>)."
msgstr ""
#: app/views/user/no_cookies.rhtml:20
-msgid "If you are still having trouble, please <a href=\"%s\">contact us</a>."
+msgid "If you are still having trouble, please <a href=\"{{url}}\">contact us</a>."
msgstr ""
#: app/views/request/hidden.rhtml:15
-msgid "If you are the requester, then you may <a href=\"%s\">sign in</a> to view the request."
+msgid "If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the request."
msgstr ""
#: app/views/request/new.rhtml:123
msgid ""
"If you are thinking of using a pseudonym,\n"
-" please <a href=\"%s\">read this first</a>."
+" please <a href=\"{{url}}\">read this first</a>."
msgstr ""
#: app/views/request/show.rhtml:105
@@ -1423,10 +1417,6 @@ msgstr ""
msgid "InfoRequestEvent|Params yaml"
msgstr ""
-#: locale/model_attributes.rb:41
-msgid "InfoRequestEvent|Prominence"
-msgstr ""
-
#: locale/model_attributes.rb:102
msgid "InfoRequest|Allow new responses from"
msgstr ""
@@ -1497,7 +1487,7 @@ msgid "Joined {{site_name}} in"
msgstr ""
#: app/views/request/new.rhtml:106
-msgid "Keep it <strong>focused</strong>, you'll be more likely to get what you want (<a href=\"%s\">why?</a>)."
+msgid "Keep it <strong>focused</strong>, you'll be more likely to get what you want (<a href=\"{{url}}\">why?</a>)."
msgstr ""
#: app/views/request/_request_filter_form.rhtml:6
@@ -1837,7 +1827,7 @@ msgid "Please"
msgstr ""
#: app/views/user/no_cookies.rhtml:15
-msgid "Please <a href=\"%s\">get in touch</a> with us so we can fix it."
+msgid "Please <a href=\"{{url}}\">get in touch</a> with us so we can fix it."
msgstr ""
#: app/views/request/show.rhtml:52
@@ -1990,7 +1980,7 @@ msgid ""
msgstr ""
#: app/models/outgoing_message.rb:157
-msgid "Please sign at the bottom with your name, or alter the \"%{signoff}\" signature"
+msgid "Please sign at the bottom with your name, or alter the \"{{signoff}}\" signature"
msgstr ""
#: app/views/user/sign.rhtml:8
@@ -2337,8 +2327,8 @@ msgid "Search the site to find what you were looking for."
msgstr ""
#: app/views/public_body/show.rhtml:85
-msgid "Search within the %d Freedom of Information requests to %s"
-msgid_plural "Search within the %d Freedom of Information requests made to %s"
+msgid "Search within the {{count}} Freedom of Information requests to {{public_body_name}}"
+msgid_plural "Search within the {{count}} Freedom of Information requests to {{public_body_name}}"
msgstr[0] ""
msgstr[1] ""
@@ -2680,10 +2670,7 @@ msgid "The request was refused by the public authority"
msgstr ""
#: app/views/request/hidden.rhtml:9
-msgid ""
-"The request you have tried to view has been removed. There are\n"
-"various reasons why we might have done this, sorry we can't be more specific here. Please <a\n"
-" href=\"%s\">contact us</a> if you have any questions."
+msgid "The request you have tried to view has been removed. There are\\nvarious reasons why we might have done this, sorry we can't be more specific here. Please <a\\n href=\"{{url}}\">contact us</a> if you have any questions."
msgstr ""
#: app/views/general/_advanced_search_tips.rhtml:36
@@ -2692,14 +2679,14 @@ msgstr ""
#: app/views/request/_followup.rhtml:59
msgid ""
-"The response to your request has been <strong>delayed</strong>. You can say that, \n"
+"The response to your request has been <strong>delayed</strong>. You can say that,\n"
" by law, the authority should normally have responded\n"
" <strong>promptly</strong> and"
msgstr ""
#: app/views/request/_followup.rhtml:71
msgid ""
-"The response to your request is <strong>long overdue</strong>. You can say that, by \n"
+"The response to your request is <strong>long overdue</strong>. You can say that, by\n"
" law, under all circumstances, the authority should have responded\n"
" by now"
msgstr ""
@@ -2804,15 +2791,9 @@ msgstr ""
msgid "There are {{count}} new annotations on your {{info_request}} request. Follow this link to see what they wrote."
msgstr ""
-#: app/views/public_body/show.rhtml:7
-msgid "There is %d person following this authority"
-msgid_plural "There are %d people following this authority"
-msgstr[0] ""
-msgstr[1] ""
-
#: app/views/request/_sidebar.rhtml:5
-msgid "There is %d person following this request"
-msgid_plural "There are %d people following this request"
+msgid "There is {{count}} person following this request"
+msgid_plural "There are {{count}} people following this request"
msgstr[0] ""
msgstr[1] ""
@@ -2876,7 +2857,7 @@ msgstr ""
#: app/views/request/_hidden_correspondence.rhtml:23
msgid ""
"This comment has been hidden. See annotations to\n"
-" find out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response."
+" find out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response."
msgstr ""
#: app/views/request/new.rhtml:63
@@ -2906,7 +2887,7 @@ msgstr ""
#: app/views/request/_hidden_correspondence.rhtml:17
msgid ""
"This outgoing message has been hidden. See annotations to\n"
-"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response."
+"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response."
msgstr ""
#: app/views/request/_describe_state.rhtml:44
@@ -2919,14 +2900,14 @@ msgid "This person has made no Freedom of Information requests using this site."
msgstr ""
#: app/views/user/show.rhtml:149
-msgid "This person's %d Freedom of Information request"
-msgid_plural "This person's %d Freedom of Information requests"
+msgid "This person's {{count}} Freedom of Information request"
+msgid_plural "This person's {{count}} Freedom of Information requests"
msgstr[0] ""
msgstr[1] ""
#: app/views/user/show.rhtml:179
-msgid "This person's %d annotation"
-msgid_plural "This person's %d annotations"
+msgid "This person's {{count}} annotation"
+msgid_plural "This person's {{count}} annotations"
msgstr[0] ""
msgstr[1] ""
@@ -2965,7 +2946,7 @@ msgstr ""
#: app/views/request/show.rhtml:11
msgid ""
"This request is hidden, so that only you the requester can see it. Please\n"
-" <a href=\"%s\">contact us</a> if you are not sure why."
+" <a href=\"{{url}}\">contact us</a> if you are not sure why."
msgstr ""
#: app/views/request/_describe_state.rhtml:7
@@ -2976,7 +2957,7 @@ msgstr ""
#: app/views/request/_hidden_correspondence.rhtml:10
msgid ""
"This response has been hidden. See annotations to find out why.\n"
-" If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response."
+" If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response."
msgstr ""
#: app/views/request/details.rhtml:6
@@ -3202,7 +3183,7 @@ msgstr ""
msgid ""
"Unfortunately we don't know the FOI\n"
"email address for that authority, so we can't validate this.\n"
-"Please <a href=\"%s\">contact us</a> to sort it out."
+"Please <a href=\"{{url}}\">contact us</a> to sort it out."
msgstr ""
#: app/views/request/new_bad_contact.rhtml:5
@@ -3439,7 +3420,7 @@ msgid ""
msgstr ""
#: app/views/request/new_please_describe.rhtml:16
-msgid "When you're done, <strong>come back here</strong>, <a href=\"%s\">reload this page</a> and file your new request."
+msgid "When you're done, <strong>come back here</strong>, <a href=\"{{url}}\">reload this page</a> and file your new request."
msgstr ""
#: app/views/request/show_response.rhtml:13
@@ -3571,7 +3552,7 @@ msgstr ""
msgid ""
"You may be able to find\n"
" one on their website, or by phoning them up and asking. If you manage\n"
-" to find one, then please <a href=\"%s\">send it to us</a>."
+" to find one, then please <a href=\"{{url}}\">send it to us</a>."
msgstr ""
#: app/views/request/new_bad_contact.rhtml:6
@@ -3642,24 +3623,19 @@ msgid "You've now cleared your profile photo"
msgstr ""
#: app/views/user/show.rhtml:149
-msgid "Your %d Freedom of Information request"
-msgid_plural "Your %d Freedom of Information requests"
+msgid "Your {{count}} Freedom of Information request"
+msgid_plural "Your {{count}} Freedom of Information requests"
msgstr[0] ""
msgstr[1] ""
#: app/views/user/show.rhtml:179
-msgid "Your %d annotation"
-msgid_plural "Your %d annotations"
+msgid "Your {{count}} annotation"
+msgid_plural "Your {{count}} annotations"
msgstr[0] ""
msgstr[1] ""
#: app/views/user/_signup.rhtml:22
-msgid ""
-"Your <strong>name will appear publicly</strong> \n"
-" (<a href=\"%s\">why?</a>)\n"
-" on this website and in search engines. If you\n"
-" are thinking of using a pseudonym, please \n"
-" <a href=\"%s\">read this first</a>."
+msgid "Your <strong>name will appear publicly</strong>\\n (<a href=\"{{why_url}}\">why?</a>)\\n on this website and in search engines. If you\\n are thinking of using a pseudonym, please\\n <a href=\"{{help_url}}\">read this first</a>."
msgstr ""
#: app/views/user/show.rhtml:172
@@ -3682,7 +3658,7 @@ msgid "Your email subscriptions"
msgstr ""
#: app/controllers/request_controller.rb:598
-msgid "Your follow up has not been sent because this request has been stopped to prevent spam. Please <a href=\"%s\">contact us</a> if you really want to send a follow up message."
+msgid "Your follow up has not been sent because this request has been stopped to prevent spam. Please <a href=\"{{url}}\">contact us</a> if you really want to send a follow up message."
msgstr ""
#: app/controllers/request_controller.rb:626
@@ -3712,7 +3688,7 @@ msgstr ""
#: app/views/request/preview.rhtml:8
msgid ""
"Your name, request and any responses will appear in <strong>search engines</strong>\n"
-" (<a href=\"%s\">details</a>)."
+" (<a href=\"{{url}}\">details</a>)."
msgstr ""
#: app/views/user/_signup.rhtml:18
@@ -3746,7 +3722,7 @@ msgid "Your request:"
msgstr ""
#: app/views/request/upload_response.rhtml:8
-msgid "Your response will <strong>appear on the Internet</strong>, <a href=\"%s\">read why</a> and answers to other questions."
+msgid "Your response will <strong>appear on the Internet</strong>, <a href=\"{{url}}\">read why</a> and answers to other questions."
msgstr ""
#: app/views/comment/new.rhtml:63
@@ -3990,7 +3966,7 @@ msgid ""
"no longer exists. If you are trying to make\n"
" From the request page, try replying to a particular message, rather than sending\n"
" a general followup. If you need to make a general followup, and know\n"
-" an email which will go to the right place, please <a href=\"%s\">send it to us</a>."
+" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>."
msgstr ""
#: app/views/request/show.rhtml:72
diff --git a/spec/fixtures/locale/es/app.po b/spec/fixtures/locale/es/app.po
index 4e54a1d40..2e0f4302a 100644
--- a/spec/fixtures/locale/es/app.po
+++ b/spec/fixtures/locale/es/app.po
@@ -1,7 +1,7 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
-#
+#
# Translators:
# David Cabo <david.cabo@gmail.com>, 2011, 2012.
# skenaja <alex@alexskene.com>, 2011.
@@ -35,9 +35,9 @@ msgstr " Esto aparecerá en tu perfil de {{site_name}}, para facilitar\n
#: app/views/comment/_comment_form.rhtml:16
msgid ""
-" (<strong>no ranty</strong> politics, read our <a href=\"%s\">moderation "
+" (<strong>no ranty</strong> politics, read our <a href=\"{{url}}\">moderation "
"policy</a>)"
-msgstr " (<strong>sin ataques políticos</strong>, lea nuestra <a href=\"%s\">política de moderación</a>)"
+msgstr " (<strong>sin ataques políticos</strong>, lea nuestra <a href=\"{{url}}\">política de moderación</a>)"
#: app/views/request/upload_response.rhtml:40
msgid ""
@@ -80,9 +80,9 @@ msgstr " Ideas sobre <strong>qué otra información pedir</strong> que el organi
#: app/views/public_body/view_email.rhtml:30
msgid ""
-" If you know the address to use, then please <a href=\"%s\">send it to us</a>.\n"
+" If you know the address to use, then please <a href=\"{{url}}\">send it to us</a>.\n"
" You may be able to find the address on their website, or by phoning them up and asking."
-msgstr " Si conoces la dirección a utilizar, entonces por favor <a href=\"%s\">envíanosla</a>.\n Puede que la encuentres en su página web, o llamándoles por teléfono y preguntando."
+msgstr " Si conoces la dirección a utilizar, entonces por favor <a href=\"{{url}}\">envíanosla</a>.\n Puede que la encuentres en su página web, o llamándoles por teléfono y preguntando."
#: app/views/user/set_profile_about_me.rhtml:26
msgid ""
@@ -140,22 +140,22 @@ msgid " when you send this message."
msgstr " cuando envió este mensaje."
#: app/views/public_body/show.rhtml:87
-msgid "%d Freedom of Information request to %s"
-msgid_plural "%d Freedom of Information requests to %s"
-msgstr[0] "%d solicitud de información a %s"
-msgstr[1] "%d solicitudes de información a %s"
+msgid "{{count}} Freedom of Information request to {{public_body_name}}"
+msgid_plural "{{count}} Freedom of Information requests to {{public_body_name}}"
+msgstr[0] "{{count}} solicitud de información a {{public_body_name}}"
+msgstr[1] "{{count}} solicitudes de información a {{public_body_name}}"
#: app/views/general/frontpage.rhtml:43
-msgid "%d request"
-msgid_plural "%d requests"
-msgstr[0] "%d solicitud"
-msgstr[1] "%d solicitudes"
+msgid "{{count}} request"
+msgid_plural "{{count}} requests"
+msgstr[0] "{{count}} solicitud"
+msgstr[1] "{{count}} solicitudes"
#: app/views/public_body/_body_listing_single.rhtml:21
-msgid "%d request made."
-msgid_plural "%d requests made."
-msgstr[0] "%d solicitud enviada."
-msgstr[1] "%d solicitudes enviadas."
+msgid "{{count}} request made."
+msgid_plural "{{count}} requests made."
+msgstr[0] "{{count}} solicitud enviada."
+msgstr[1] "{{count}} solicitudes enviadas."
#: app/views/request/new.rhtml:92
msgid "'Crime statistics by ward level for Wales'"
@@ -204,46 +204,40 @@ msgstr "2. Solicite información"
msgid "3. Now check your request"
msgstr "3. Revisa tu solicitud"
-#: app/views/public_body/show.rhtml:56
-msgid "<a class=\"link_button_green\" href=\"{{url}}\">{{text}}</a>"
-msgstr "<a class=\"link_button_green\" href=\"{{url}}\">{{text}}</a>"
-
#: app/views/request/_after_actions.rhtml:9
-msgid "<a href=\"%s\">Add an annotation</a> (to help the requester or others)"
-msgstr "<a href=\"%s\">Añade un comentario</a> (para ayudar al solicitante o a otros)"
+msgid "<a href=\"{{url}}\">Add an annotation</a> (to help the requester or others)"
+msgstr "<a href=\"{{url}}\">Añade un comentario</a> (para ayudar al solicitante o a otros)"
#: app/views/public_body/list.rhtml:29
-msgid "<a href=\"%s\">Are we missing a public authority?</a>."
-msgstr "<a href=\"%s\">¿Nos falta algún organismo público?</a>."
+msgid "Are we missing a public authority?"
+msgstr "¿Nos falta algún organismo público?."
#: app/views/request/_sidebar.rhtml:39
-msgid ""
-"<a href=\"%s\">Are you the owner of\n"
-" any commercial copyright on this page?</a>"
-msgstr "<a href=\"%s\">¿Posee el copyright\n de alguna información de esta página?</a>"
+msgid "Are you the owner of any commercial copyright on this page?"
+msgstr "¿Posee el copyright de alguna información de esta página?"
#: app/views/general/search.rhtml:168
-msgid "<a href=\"%s\">Browse all</a> or <a href=\"%s\">ask us to add one</a>."
-msgstr "<a href=\"%s\">Ver todas</a> o <a href=\"%s\">pídanos que añadamos una</a>."
+msgid "<a href=\"{{browse_url}}\">Browse all</a> or <a href=\"{{add_url}}\">ask us to add one</a>."
+msgstr "<a href=\"{{browse_url}}\">Ver todas</a> o <a href=\"{{add_url}}\">pídanos que añadamos una</a>."
#: app/views/public_body/list.rhtml:51
-msgid "<a href=\"%s\">Can't find the one you want?</a>"
-msgstr "<a href=\"%s\">¿No encuentra el que busca?</a>"
+msgid "Can't find the one you want?"
+msgstr "¿No encuentra el que busca?"
#: app/views/user/show.rhtml:118
msgid ""
-"<a href=\"%s\">Sign in</a> to change password, subscriptions and more "
+"<a href=\"{{url}}\">Sign in</a> to change password, subscriptions and more "
"({{user_name}} only)"
-msgstr "<a href=\"%s\">Abre una sesión</a> para cambiar tu contraseña, suscripciones... (sólo {{user_name}})"
+msgstr "<a href=\"{{url}}\">Abre una sesión</a> para cambiar tu contraseña, suscripciones... (sólo {{user_name}})"
#: app/views/request/_followup.rhtml:66 app/views/request/_followup.rhtml:73
#: app/views/request/show.rhtml:83 app/views/request/show.rhtml:87
-msgid "<a href=\"%s\">details</a>"
-msgstr "<a href=\"%s\">detalles</a>"
+msgid "details"
+msgstr "detalles"
#: app/views/request/_followup.rhtml:101
-msgid "<a href=\"%s\">what's that?</a>"
-msgstr "<a href=\"%s\">¿Qué es eso?</a>"
+msgid "what's that?"
+msgstr "¿Qué es eso?"
#: app/controllers/request_game_controller.rb:23
msgid ""
@@ -382,8 +376,8 @@ msgstr "<small>Si usas correo web o tiene filtros \"anti spam\", por favor compr
#: app/views/request/new.rhtml:135
msgid ""
"<strong> Can I request information about myself?</strong>\n"
-"\t\t\t<a href=\"%s\">No! (Click here for details)</a>"
-msgstr "<strong> ¿Puedo pedir información sobre mí?</strong>\n\t\t\t<a href=\"%s\">¡No! (Pulse aquí para más detalles)</a>"
+"\t\t\t<a href=\"{{url}}\">No! (Click here for details)</a>"
+msgstr "<strong> ¿Puedo pedir información sobre mí?</strong>\n\t\t\t<a href=\"{{url}}\">¡No! (Pulse aquí para más detalles)</a>"
#: app/views/general/_advanced_search_tips.rhtml:12
msgid ""
@@ -486,8 +480,8 @@ msgstr "<strong>Nota:</strong> Te estás enviando un mensaje a ti mismo, suponem
#: app/views/request/preview.rhtml:31
msgid ""
"<strong>Privacy note:</strong> If you want to request private information about\n"
-" yourself then <a href=\"%s\">click here</a>."
-msgstr "<strong>Nota sobre privacidad:</strong> Si quiere solicitar información privada\n sobre sí mismo entonces <a href=\"%s\">siga este enlace</a>."
+" yourself then <a href=\"{{url}}\">click here</a>."
+msgstr "<strong>Nota sobre privacidad:</strong> Si quiere solicitar información privada\n sobre sí mismo entonces <a href=\"{{url}}\">siga este enlace</a>."
#: app/views/user/set_crop_profile_photo.rhtml:35
msgid ""
@@ -667,8 +661,8 @@ msgstr "Pide documentos o información <strong>específica</strong>, esta web no
#: app/views/request/show_response.rhtml:29
msgid ""
"At the bottom of this page, write a reply to them trying to persuade them to scan it in\n"
-" (<a href=\"%s\">more details</a>)."
-msgstr "Al final de esta página, escribe una respuesta intentando convencerles de que lo escaneen\n (<a href=\"%s\">más detalles</a>)."
+" (<a href=\"{{url}}\">more details</a>)."
+msgstr "Al final de esta página, escribe una respuesta intentando convencerles de que lo escaneen\n (<a href=\"{{url}}\">más detalles</a>)."
#: app/views/request/upload_response.rhtml:33
msgid "Attachment (optional):"
@@ -1037,8 +1031,8 @@ msgstr "Introduzca las palabras que desee separadas por espacio, es decir <stron
#: app/views/request/upload_response.rhtml:23
msgid ""
"Enter your response below. You may attach one file (use email, or \n"
-"<a href=\"%s\">contact us</a> if you need more)."
-msgstr "Escribe tu solicitud a continuación. Puedes adjuntar un fichero (manda un correo,\n o <a href=\"%s\">contáctanos</a>, si necesita más)."
+"<a href=\"{{url}}\">contact us</a> if you need more)."
+msgstr "Escribe tu solicitud a continuación. Puedes adjuntar un fichero (manda un correo,\n o <a href=\"{{url}}\">contáctanos</a>, si necesita más)."
#: app/models/info_request.rb:259 app/models/info_request.rb:277
msgid "Environmental Information Regulations"
@@ -1066,17 +1060,17 @@ msgstr "Historial de eventos"
#: app/views/request/new.rhtml:128
msgid ""
-"Everything that you enter on this page \n"
+"Everything that you enter on this page\n"
" will be <strong>displayed publicly</strong> on\n"
-" this website forever (<a href=\"%s\">why?</a>)."
-msgstr "Todo lo que escriba en esta página \n estará <strong>disponible públicamente</strong> en\n está web para siempre (<a href=\"%s\">¿por qué?</a>)."
+" this website forever (<a href=\"{{url}}\">why?</a>)."
+msgstr "Todo lo que escriba en esta página \n estará <strong>disponible públicamente</strong> en\n está web para siempre (<a href=\"{{url}}\">¿por qué?</a>)."
#: app/views/request/new.rhtml:120
msgid ""
-"Everything that you enter on this page, including <strong>your name</strong>, \n"
+"Everything that you enter on this page, including <strong>your name</strong>,\n"
" will be <strong>displayed publicly</strong> on\n"
-" this website forever (<a href=\"%s\">why?</a>)."
-msgstr "Todo lo que escribas en esta página, incluyendo <strong>tu nombre</strong>, \n estará <strong>disponible públicamente</strong> en\n está web para siempre (<a href=\"%s\">¿por qué?</a>)."
+" this website forever (<a href=\"{{url}}\">why?</a>)."
+msgstr "Todo lo que escribas en esta página, incluyendo <strong>tu nombre</strong>, \n estará <strong>disponible públicamente</strong> en\n está web para siempre (<a href=\"{{url}}\">¿por qué?</a>)."
#: locale/model_attributes.rb:68
msgid "EximLogDone|Filename"
@@ -1123,10 +1117,8 @@ msgid "Failed to convert image to a PNG"
msgstr "Error al convertir la imagen a PNG"
#: app/models/profile_photo.rb:105
-msgid ""
-"Failed to convert image to the correct size: at %{cols}x%{rows}, need "
-"%{width}x%{height}"
-msgstr "Error al convertir la imagen al tamaño adecuado: es %{cols}x%{rows}, debería ser %{width}x%{height}"
+msgid "Failed to convert image to the correct size: at {{cols}}x{{rows}}, need {{width}}x{{height}}"
+msgstr "Error al convertir la imagen al tamaño adecuado: es {{cols}}x{{rows}}, debería ser {{width}}x{{height}}"
#: app/views/general/search.rhtml:117
msgid "Filter"
@@ -1136,8 +1128,8 @@ msgstr "Filtrar"
msgid ""
"First, type in the <strong>name of the UK public authority</strong> you'd \n"
" like information from. <strong>By law, they have to respond</strong>\n"
-" (<a href=\"%s#%s\">why?</a>)."
-msgstr "Primero, escribe el <strong>nombre de la institución</strong> a la que quieres pedir información. <strong>Están obligados a responder</strong> (<a href=\"%s#%s\">¿por qué?</a>)."
+" (<a href=\"{{url}}\">why?</a>)."
+msgstr "Primero, escribe el <strong>nombre de la institución</strong> a la que quieres pedir información. <strong>Están obligados a responder</strong> (<a href=\"{{url}}\">¿por qué?</a>)."
#: locale/model_attributes.rb:88
msgid "FoiAttachment|Charset"
@@ -1272,8 +1264,8 @@ msgstr "Solicitudes de información a"
msgid ""
"From the request page, try replying to a particular message, rather than sending\n"
" a general followup. If you need to make a general followup, and know\n"
-" an email which will go to the right place, please <a href=\"%s\">send it to us</a>."
-msgstr "Desde la página de la solicitud, intenta responder a un mensaje en concreto, en vez de\n responder a la solicitud en general. Si necesitas hacerlo y tienes una dirección de\n correo válida, por favor <a href=\"%s\">mándanosla</a>."
+" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>."
+msgstr "Desde la página de la solicitud, intenta responder a un mensaje en concreto, en vez de\n responder a la solicitud en general. Si necesitas hacerlo y tienes una dirección de\n correo válida, por favor <a href=\"{{url}}\">mándanosla</a>."
#: app/views/request/_correspondence.rhtml:12
#: app/views/request/_correspondence.rhtml:36
@@ -1409,8 +1401,8 @@ msgstr "He recibido un <strong>mensaje de error</strong>"
#: app/views/public_body/view_email.rhtml:28
msgid ""
"If the address is wrong, or you know a better address, please <a "
-"href=\"%s\">contact us</a>."
-msgstr "Si la dirección es incorrecta, o conoce una más actualizada, por favor <a href=\"%s\">contáctenos</a>."
+"href=\"{{url}}\">contact us</a>."
+msgstr "Si la dirección es incorrecta, o conoce una más actualizada, por favor <a href=\"{{url}}\">contáctenos</a>."
#: app/views/request_mailer/stopped_responses.rhtml:10
msgid ""
@@ -1423,24 +1415,24 @@ msgstr "Si no es correcto, o te gustaría enviar una respuesta a la solicitud\no
msgid ""
"If you are dissatisfied by the response you got from\n"
" the public authority, you have the right to\n"
-" complain (<a href=\"%s\">details</a>)."
-msgstr "Si no estás satisfecho con la respuesta que has recibido del\n organismo público, tienes derecho a\n apelar (<a href=\"%s\">detalles</a>)."
+" complain (<a href=\"{{url}}\">details</a>)."
+msgstr "Si no estás satisfecho con la respuesta que has recibido del\n organismo público, tienes derecho a\n apelar (<a href=\"{{url}}\">detalles</a>)."
#: app/views/user/no_cookies.rhtml:20
-msgid "If you are still having trouble, please <a href=\"%s\">contact us</a>."
-msgstr "Si aún tienes problemas, por favor <a href=\"%s\">contáctanos</a>."
+msgid "If you are still having trouble, please <a href=\"{{url}}\">contact us</a>."
+msgstr "Si aún tienes problemas, por favor <a href=\"{{url}}\">contáctanos</a>."
#: app/views/request/hidden.rhtml:15
msgid ""
-"If you are the requester, then you may <a href=\"%s\">sign in</a> to view "
+"If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view "
"the request."
-msgstr "Si la solicitud es tuya, puedes <a href=\"%s\">abrir una sesión</a> para verla."
+msgstr "Si la solicitud es tuya, puedes <a href=\"{{url}}\">abrir una sesión</a> para verla."
#: app/views/request/new.rhtml:123
msgid ""
"If you are thinking of using a pseudonym,\n"
-" please <a href=\"%s\">read this first</a>."
-msgstr "Si está pensando en utilizar un pseudónimo,\n por favor <a href=\"%s\">lea esto primero</a>."
+" please <a href=\"{{url}}\">read this first</a>."
+msgstr "Si está pensando en utilizar un pseudónimo,\n por favor <a href=\"{{url}}\">lea esto primero</a>."
#: app/views/request/show.rhtml:105
msgid "If you are {{user_link}}, please"
@@ -1561,10 +1553,6 @@ msgstr "InfoRequestEvent|Last described at"
msgid "InfoRequestEvent|Params yaml"
msgstr "InfoRequestEvent|Params yaml"
-#: locale/model_attributes.rb:41
-msgid "InfoRequestEvent|Prominence"
-msgstr "InfoRequestEvent|Prominence"
-
#: locale/model_attributes.rb:102
msgid "InfoRequest|Allow new responses from"
msgstr "InfoRequest|Allow new responses from"
@@ -1639,8 +1627,8 @@ msgstr "Registrado en {{site_name}} el"
#: app/views/request/new.rhtml:106
msgid ""
"Keep it <strong>focused</strong>, you'll be more likely to get what you want"
-" (<a href=\"%s\">why?</a>)."
-msgstr "Sea <strong>específico</strong>, tendrá más probabilidades de conseguir lo que quiere (<a href=\"%s\">¿por qué?</a>)."
+" (<a href=\"{{url}}\">why?</a>)."
+msgstr "Sea <strong>específico</strong>, tendrá más probabilidades de conseguir lo que quiere (<a href=\"{{url}}\">¿por qué?</a>)."
#: app/views/request/_request_filter_form.rhtml:6
msgid "Keywords"
@@ -1990,8 +1978,8 @@ msgid "Please"
msgstr "Por favor"
#: app/views/user/no_cookies.rhtml:15
-msgid "Please <a href=\"%s\">get in touch</a> with us so we can fix it."
-msgstr "Por favor <a href=\"%s\">contacta</a> con nosotros para que podamos arreglarlo."
+msgid "Please <a href=\"{{url}}\">get in touch</a> with us so we can fix it."
+msgstr "Por favor <a href=\"{{url}}\">contacta</a> con nosotros para que podamos arreglarlo."
#: app/views/request/show.rhtml:52
msgid ""
@@ -2154,10 +2142,8 @@ msgid ""
msgstr "Por favor elije estas solicitudes una a una, y <strong>haz que se sepa</strong>\nsi han tenido éxito o no."
#: app/models/outgoing_message.rb:157
-msgid ""
-"Please sign at the bottom with your name, or alter the \"%{signoff}\" "
-"signature"
-msgstr "Por favor, firma con tu nombre en la parte inferior, o cambia la firma \"%{signoff}\""
+msgid "Please sign at the bottom with your name, or alter the \"{{signoff}}\" signature"
+msgstr "Por favor, firma con tu nombre en la parte inferior, o cambia la firma \"{{signoff}}\""
#: app/views/user/sign.rhtml:8
msgid "Please sign in as "
@@ -2519,10 +2505,10 @@ msgid "Search the site to find what you were looking for."
msgstr "Buscar en esta web para encontrar lo que busca."
#: app/views/public_body/show.rhtml:85
-msgid "Search within the %d Freedom of Information requests to %s"
-msgid_plural "Search within the %d Freedom of Information requests made to %s"
-msgstr[0] "Busca en la %d solicitud de información hecha a %s"
-msgstr[1] "Busca en las %d solicitudes de información hechas a %s"
+msgid "Search within the {{count}} Freedom of Information requests to {{public_body_name}}"
+msgid_plural "Search within the {{count}} Freedom of Information requests to {{public_body_name}}"
+msgstr[0] "Busca en la {{count}} solicitud de información hecha a {{public_body_name}}"
+msgstr[1] "Busca en las {{count}} solicitudes de información hechas a {{public_body_name}}"
#: app/views/user/show.rhtml:132
msgid "Search your contributions"
@@ -2878,11 +2864,8 @@ msgid "The request was refused by the public authority"
msgstr "La solicitud ha sido rechazada por el organismo"
#: app/views/request/hidden.rhtml:9
-msgid ""
-"The request you have tried to view has been removed. There are\n"
-"various reasons why we might have done this, sorry we can't be more specific here. Please <a\n"
-" href=\"%s\">contact us</a> if you have any questions."
-msgstr "La solicitud que has intentado ver ha sido eliminada. Hay\nvarios posibles motivos para esto, pero no podemos ser más específicos aquí. Por favor <a\n href=\"%s\">contáctanos</a> si tiene cualquier pregunta."
+msgid "The request you have tried to view has been removed. There are\\nvarious reasons why we might have done this, sorry we can't be more specific here. Please <a\\n href=\"{{url}}\">contact us</a> if you have any questions."
+msgstr "La solicitud que has intentado ver ha sido eliminada. Hay\nvarios posibles motivos para esto, pero no podemos ser más específicos aquí. Por favor <a\n href=\"{{url}}\">contáctanos</a> si tiene cualquier pregunta."
#: app/views/general/_advanced_search_tips.rhtml:36
msgid "The requester has abandoned this request for some reason"
@@ -2890,14 +2873,14 @@ msgstr "El creador de la solicitud la ha cancelado por algún motivo"
#: app/views/request/_followup.rhtml:59
msgid ""
-"The response to your request has been <strong>delayed</strong>. You can say that, \n"
+"The response to your request has been <strong>delayed</strong>. You can say that,\n"
" by law, the authority should normally have responded\n"
" <strong>promptly</strong> and"
msgstr "La respuesta a tu solicitud ha sido <strong>retrasada</strong>.\n Por ley, el organismo debería normalmente haber respondido\n <strong>rápidamente</strong> y"
#: app/views/request/_followup.rhtml:71
msgid ""
-"The response to your request is <strong>long overdue</strong>. You can say that, by \n"
+"The response to your request is <strong>long overdue</strong>. You can say that, by\n"
" law, under all circumstances, the authority should have responded\n"
" by now"
msgstr "La respuesta a tu solicitud ha sido <strong>muy retrasada</strong>.\n Por ley, bajo cualquier circunstancia, el organismo ya debería\n haber respondido"
@@ -3016,17 +2999,11 @@ msgid ""
" this link to see what they wrote."
msgstr "Hay {{count}} comentarios en tu solicitud {{info_request}}. Sigue este enlace para leer lo que dicen."
-#: app/views/public_body/show.rhtml:7
-msgid "There is %d person following this authority"
-msgid_plural "There are %d people following this authority"
-msgstr[0] "Hay %d persona siguiendo a este organismo."
-msgstr[1] "Hay %d personas siguiendo a este organismo."
-
#: app/views/request/_sidebar.rhtml:5
-msgid "There is %d person following this request"
-msgid_plural "There are %d people following this request"
-msgstr[0] "Hay %d persona siguiendo esta solicitud."
-msgstr[1] "Hay %d personas siguiendo esta solicitud."
+msgid "There is {{count}} person following this request"
+msgid_plural "There are {{count}} people following this request"
+msgstr[0] "Hay {{count}} persona siguiendo esta solicitud."
+msgstr[1] "Hay {{count}} personas siguiendo esta solicitud."
#: app/views/user/show.rhtml:8
msgid ""
@@ -3099,8 +3076,8 @@ msgstr "Este organismo ya no existe, no pueden realizarse solicitudes de informa
#: app/views/request/_hidden_correspondence.rhtml:23
msgid ""
"This comment has been hidden. See annotations to\n"
-" find out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response."
-msgstr "Este respuesta está oculta. Revisa los comentarios\n para descubrir por qué. Si es tu solicitud, <a href=\"%s\">abre una sesión</a> para ver la respuesta."
+" find out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response."
+msgstr "Este respuesta está oculta. Revisa los comentarios\n para descubrir por qué. Si es tu solicitud, <a href=\"{{url}}\">abre una sesión</a> para ver la respuesta."
#: app/views/request/new.rhtml:63
msgid ""
@@ -3140,8 +3117,8 @@ msgstr "Esta es tu solicitud, por lo que recibirás correos automáticamente cua
#: app/views/request/_hidden_correspondence.rhtml:17
msgid ""
"This outgoing message has been hidden. See annotations to\n"
-"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response."
-msgstr "Este mensaje está oculto. Lee los comentarios\n\t\t\t\t\t\tpara descubrir por qué. Si es tu solicitud, <a href=\"%s\">abra una sesión</a> para ver la respuesta."
+"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response."
+msgstr "Este mensaje está oculto. Lee los comentarios\n\t\t\t\t\t\tpara descubrir por qué. Si es tu solicitud, <a href=\"{{url}}\">abra una sesión</a> para ver la respuesta."
#: app/views/request/_describe_state.rhtml:44
#: app/views/request/_other_describe_state.rhtml:40
@@ -3154,16 +3131,16 @@ msgid ""
msgstr "Esta persona no ha realizado solicitudes de información usando esta web."
#: app/views/user/show.rhtml:149
-msgid "This person's %d Freedom of Information request"
-msgid_plural "This person's %d Freedom of Information requests"
-msgstr[0] "Tu %d solicitud de información"
-msgstr[1] "Tus %d solicitudes de información"
+msgid "This person's {{count}} Freedom of Information request"
+msgid_plural "This person's {{count}} Freedom of Information requests"
+msgstr[0] "Tu {{count}} solicitud de información"
+msgstr[1] "Tus {{count}} solicitudes de información"
#: app/views/user/show.rhtml:179
-msgid "This person's %d annotation"
-msgid_plural "This person's %d annotations"
-msgstr[0] "Tu %d comentario"
-msgstr[1] "Tus %d comentarios"
+msgid "This person's {{count}} annotation"
+msgid_plural "This person's {{count}} annotations"
+msgstr[0] "Tu {{count}} comentario"
+msgstr[1] "Tus {{count}} comentarios"
#: app/views/user/show.rhtml:172
msgid "This person's annotations"
@@ -3204,8 +3181,8 @@ msgstr "Esta solicitud tiene visibilidad 'oculta'. Puedes verla sólo porque est
#: app/views/request/show.rhtml:11
msgid ""
"This request is hidden, so that only you the requester can see it. Please\n"
-" <a href=\"%s\">contact us</a> if you are not sure why."
-msgstr "Esta solicitud está oculta, por lo que sólo tú como creador puedes verla. Por favor\n <a href=\"%s\">contáctanos</a> si no estás seguro de por qué."
+" <a href=\"{{url}}\">contact us</a> if you are not sure why."
+msgstr "Esta solicitud está oculta, por lo que sólo tú como creador puedes verla. Por favor\n <a href=\"{{url}}\">contáctanos</a> si no estás seguro de por qué."
#: app/views/request/_describe_state.rhtml:7
#: app/views/request/_other_describe_state.rhtml:10
@@ -3215,8 +3192,8 @@ msgstr "Esta solicitud está todavía en proceso:"
#: app/views/request/_hidden_correspondence.rhtml:10
msgid ""
"This response has been hidden. See annotations to find out why.\n"
-" If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response."
-msgstr "Este respuesta está oculta. Revisa los comentarios\n para descubrir por qué. Si es tu solicitud, <a href=\"%s\">abre una sesión</a> para ver la respuesta."
+" If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response."
+msgstr "Este respuesta está oculta. Revisa los comentarios\n para descubrir por qué. Si es tu solicitud, <a href=\"{{url}}\">abre una sesión</a> para ver la respuesta."
#: app/views/request/details.rhtml:6
msgid ""
@@ -3450,8 +3427,8 @@ msgstr "Se encontró un tipo de resultado inesperado "
msgid ""
"Unfortunately we don't know the FOI\n"
"email address for that authority, so we can't validate this.\n"
-"Please <a href=\"%s\">contact us</a> to sort it out."
-msgstr "Desgraciadamente no tenemos la dirección\nde correo para este organismo, así que no podemos validarlo.\nPor favor <a href=\"%s\">contáctenos</a> para arreglarlo."
+"Please <a href=\"{{url}}\">contact us</a> to sort it out."
+msgstr "Desgraciadamente no tenemos la dirección\nde correo para este organismo, así que no podemos validarlo.\nPor favor <a href=\"{{url}}\">contáctenos</a> para arreglarlo."
#: app/views/request/new_bad_contact.rhtml:5
msgid ""
@@ -3699,9 +3676,9 @@ msgstr "Cuando reciba la respuesta en papel, por favor ayude\n a que
#: app/views/request/new_please_describe.rhtml:16
msgid ""
-"When you're done, <strong>come back here</strong>, <a href=\"%s\">reload "
+"When you're done, <strong>come back here</strong>, <a href=\"{{url}}\">reload "
"this page</a> and file your new request."
-msgstr "Cuando esté listo, <strong>vuelva aquí</strong>, <a href=\"%s\">recargue esta página</a> y cree una nueva solicitud."
+msgstr "Cuando esté listo, <strong>vuelva aquí</strong>, <a href=\"{{url}}\">recargue esta página</a> y cree una nueva solicitud."
#: app/views/request/show_response.rhtml:13
msgid "Which of these is happening?"
@@ -3840,8 +3817,8 @@ msgstr "Puede <strong>adjuntar ficheros</strong>. Si quiere adjuntar un fichero\
msgid ""
"You may be able to find\n"
" one on their website, or by phoning them up and asking. If you manage\n"
-" to find one, then please <a href=\"%s\">send it to us</a>."
-msgstr "Puede que encuentres una\n en su página web, o preguntando por teléfono. Si la consigues\n por favor <a href=\"%s\">envíanosla</a>."
+" to find one, then please <a href=\"{{url}}\">send it to us</a>."
+msgstr "Puede que encuentres una\n en su página web, o preguntando por teléfono. Si la consigues\n por favor <a href=\"{{url}}\">envíanosla</a>."
#: app/views/request/new_bad_contact.rhtml:6
msgid ""
@@ -3914,25 +3891,20 @@ msgid "You've now cleared your profile photo"
msgstr "Has borrado la foto de tu perfil"
#: app/views/user/show.rhtml:149
-msgid "Your %d Freedom of Information request"
-msgid_plural "Your %d Freedom of Information requests"
-msgstr[0] "Tu %d solicitud de información"
-msgstr[1] "Tus %d solicitudes de información"
+msgid "Your {{count}} Freedom of Information request"
+msgid_plural "Your {{count}} Freedom of Information requests"
+msgstr[0] "Tu {{count}} solicitud de información"
+msgstr[1] "Tus {{count}} solicitudes de información"
#: app/views/user/show.rhtml:179
-msgid "Your %d annotation"
-msgid_plural "Your %d annotations"
-msgstr[0] "Tu %d comentario"
-msgstr[1] "Tus %d comentarios"
+msgid "Your {{count}} annotation"
+msgid_plural "Your {{count}} annotations"
+msgstr[0] "Tu {{count}} comentario"
+msgstr[1] "Tus {{count}} comentarios"
#: app/views/user/_signup.rhtml:22
-msgid ""
-"Your <strong>name will appear publicly</strong> \n"
-" (<a href=\"%s\">why?</a>)\n"
-" on this website and in search engines. If you\n"
-" are thinking of using a pseudonym, please \n"
-" <a href=\"%s\">read this first</a>."
-msgstr "<strong>Tu nombre aparecerá públicamente</strong> \n (<a href=\"%s\">¿por qué?</a>)\n en esta web y en motores de búsqueda. Si estás\n pensando en utilizar un seudónimo, por favor \n <a href=\"%s\">lee esto primero</a>."
+msgid "Your <strong>name will appear publicly</strong> \\n (<a href=\"{{why_url}}\">why?</a>)\\n on this website and in search engines. If you\\n are thinking of using a pseudonym, please \\n <a href=\"{{help_url}}\">read this first</a>."
+msgstr "<strong>Tu nombre aparecerá públicamente</strong> \n (<a href=\"{{why_url}}\">¿por qué?</a>)\n en esta web y en motores de búsqueda. Si estás\n pensando en utilizar un seudónimo, por favor \n <a href=\"{{help_url}}\">lee esto primero</a>."
#: app/views/user/show.rhtml:172
msgid "Your annotations"
@@ -3956,9 +3928,9 @@ msgstr "Tus suscripciones de correo"
#: app/controllers/request_controller.rb:598
msgid ""
"Your follow up has not been sent because this request has been stopped to "
-"prevent spam. Please <a href=\"%s\">contact us</a> if you really want to "
+"prevent spam. Please <a href=\"{{url}}\">contact us</a> if you really want to "
"send a follow up message."
-msgstr "Tu respuesta no ha sido enviada porque esta solicitud ha sido bloqueada para evitar spam. Por favor <a href=\"%s\">contáctanos</a> si realmente quieres enviar una respuesta."
+msgstr "Tu respuesta no ha sido enviada porque esta solicitud ha sido bloqueada para evitar spam. Por favor <a href=\"{{url}}\">contáctanos</a> si realmente quieres enviar una respuesta."
#: app/controllers/request_controller.rb:626
msgid "Your follow up message has been sent on its way."
@@ -3990,8 +3962,8 @@ msgstr "Tu nombre y su comentario aparecerán en los <strong>motores de búsqued
#: app/views/request/preview.rhtml:8
msgid ""
"Your name, request and any responses will appear in <strong>search engines</strong>\n"
-" (<a href=\"%s\">details</a>)."
-msgstr "Tu nombre, tu solicitud y cualquier respuesta aparecerán en los <strong>motores de búsqueda</strong>\n (<a href=\"%s\">detalles</a>)."
+" (<a href=\"{{url}}\">details</a>)."
+msgstr "Tu nombre, tu solicitud y cualquier respuesta aparecerán en los <strong>motores de búsqueda</strong>\n (<a href=\"{{url}}\">detalles</a>)."
#: app/views/user/_signup.rhtml:18
msgid "Your name:"
@@ -4028,8 +4000,8 @@ msgstr "Tu solicitud:"
#: app/views/request/upload_response.rhtml:8
msgid ""
"Your response will <strong>appear on the Internet</strong>, <a "
-"href=\"%s\">read why</a> and answers to other questions."
-msgstr "Tu respuesta <strong>aparecerá en Internet</strong>, <a href=\"%s\">lee por qué</a> y respuestas a otras preguntas."
+"href=\"{{url}}\">read why</a> and answers to other questions."
+msgstr "Tu respuesta <strong>aparecerá en Internet</strong>, <a href=\"{{url}}\">lee por qué</a> y respuestas a otras preguntas."
#: app/views/comment/new.rhtml:63
msgid ""
@@ -4276,8 +4248,8 @@ msgid ""
"no longer exists. If you are trying to make\n"
" From the request page, try replying to a particular message, rather than sending\n"
" a general followup. If you need to make a general followup, and know\n"
-" an email which will go to the right place, please <a href=\"%s\">send it to us</a>."
-msgstr "ya no existe. \nDesde la página de la solicitud, intenta responder a un mensaje en concreto, en vez de\n responder a la solicitud en general. Si necesitas hacerlo y tienes una dirección de\n correo válida, por favor <a href=\"%s\">mándanosla</a>."
+" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>."
+msgstr "ya no existe. \nDesde la página de la solicitud, intenta responder a un mensaje en concreto, en vez de\n responder a la solicitud en general. Si necesitas hacerlo y tienes una dirección de\n correo válida, por favor <a href=\"{{url}}\">mándanosla</a>."
#: app/views/request/show.rhtml:72
msgid "normally"
diff --git a/spec/fixtures/outgoing_messages.yml b/spec/fixtures/outgoing_messages.yml
index 55df8473e..92de076c4 100644
--- a/spec/fixtures/outgoing_messages.yml
+++ b/spec/fixtures/outgoing_messages.yml
@@ -1,3 +1,21 @@
+# == Schema Information
+#
+# Table name: outgoing_messages
+#
+# id :integer not null, primary key
+# info_request_id :integer not null
+# body :text not null
+# status :string(255) not null
+# message_type :string(255) not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# last_sent_at :datetime
+# incoming_message_followup_id :integer
+# what_doing :string(255) not null
+# prominence :string(255) default("normal"), not null
+# prominence_reason :text
+#
+
useless_outgoing_message:
id: 1
info_request_id: 101
@@ -108,3 +126,14 @@ anonymous_external_outgoing_message:
updated_at: 2009-01-12 01:56:58.586598
what_doing: normal_sort
+other_outgoing_message:
+ id: 10
+ info_request_id: 111
+ message_type: initial_request
+ status: sent
+ body: "Just another request"
+ last_sent_at: <%= Time.now %>
+ created_at: 2009-01-12 01:56:58.586598
+ updated_at: 2009-01-12 01:56:58.586598
+ what_doing: normal_sort
+
diff --git a/spec/fixtures/public_bodies.yml b/spec/fixtures/public_bodies.yml
index 615c4bcb6..d0eb572b3 100644
--- a/spec/fixtures/public_bodies.yml
+++ b/spec/fixtures/public_bodies.yml
@@ -1,3 +1,30 @@
+# == Schema Information
+#
+# Table name: public_bodies
+#
+# id :integer not null, primary key
+# name :text not null
+# short_name :text not null
+# request_email :text not null
+# version :integer not null
+# last_edit_editor :string(255) not null
+# last_edit_comment :text not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# url_name :text not null
+# home_page :text default(""), not null
+# notes :text default(""), not null
+# first_letter :string(255) not null
+# publication_scheme :text default(""), not null
+# api_key :string(255) not null
+# info_requests_count :integer default(0), not null
+# disclosure_log :text default(""), not null
+# info_requests_successful_count :integer
+# info_requests_not_held_count :integer
+# info_requests_overdue_count :integer
+# info_requests_visible_classified_count :integer
+#
+
geraldine_public_body:
name: The Geraldine Quango
first_letter: T
@@ -12,6 +39,10 @@ geraldine_public_body:
created_at: 2007-10-24 10:51:01.161639
api_key: 1
info_requests_count: 4
+ info_requests_visible_classified_count: 4
+ info_requests_successful_count: 0
+ info_requests_not_held_count: 0
+ info_requests_overdue_count: 3
humpadink_public_body:
name: "Department for Humpadinking"
first_letter: D
@@ -27,6 +58,10 @@ humpadink_public_body:
notes: An albatross told me!!!
api_key: 2
info_requests_count: 2
+ info_requests_visible_classified_count: 2
+ info_requests_successful_count: 1
+ info_requests_not_held_count: 0
+ info_requests_overdue_count: 1
forlorn_public_body:
name: "Department of Loneliness"
first_letter: D
@@ -42,6 +77,10 @@ forlorn_public_body:
notes: A very lonely public body that no one has corresponded with
api_key: 3
info_requests_count: 0
+ info_requests_visible_classified_count: 0
+ info_requests_successful_count: 0
+ info_requests_not_held_count: 0
+ info_requests_overdue_count: 0
silly_walks_public_body:
id: 5
version: 1
@@ -57,6 +96,10 @@ silly_walks_public_body:
notes: You know the one.
api_key: 4
info_requests_count: 2
+ info_requests_visible_classified_count: 2
+ info_requests_successful_count: 2
+ info_requests_not_held_count: 0
+ info_requests_overdue_count: 0
sensible_walks_public_body:
id: 6
version: 1
@@ -72,3 +115,45 @@ sensible_walks_public_body:
created_at: 2008-10-25 10:51:01.161639
api_key: 5
info_requests_count: 1
+ info_requests_visible_classified_count: 1
+ info_requests_successful_count: 1
+ info_requests_not_held_count: 0
+ info_requests_overdue_count: 0
+other_public_body:
+ id: 7
+ version: 1
+ name: 'Another Public Body'
+ first_letter: A
+ request_email: other@localhost
+ short_name: Another Public Body
+ url_name: another_public_body
+ notes: More notes
+ updated_at: 2008-10-25 10:51:01.161639
+ last_edit_comment: Another edit
+ last_edit_editor: louise
+ created_at: 2008-10-25 10:51:01.161639
+ api_key: 6
+ info_requests_count: 0
+ info_requests_visible_classified_count: 0
+ info_requests_successful_count: 0
+ info_requests_not_held_count: 0
+ info_requests_overdue_count: 0
+accented_public_body:
+ id: 8
+ version: 1
+ name: 'Åčçèñtéd Authority'
+ first_letter: Å
+ request_email: accented@localhost
+ short_name: 'Åčçèñtéd Authority'
+ url_name: accented_authority
+ notes: This is to test unicode handling in body names
+ updated_at: 2008-10-25 10:51:01.161639
+ last_edit_comment: Another edit
+ last_edit_editor: louise
+ created_at: 2008-10-25 10:51:01.161639
+ api_key: 7
+ info_requests_count: 0
+ info_requests_visible_classified_count: 0
+ info_requests_successful_count: 0
+ info_requests_not_held_count: 0
+ info_requests_overdue_count: 0
diff --git a/spec/fixtures/public_body_translations.yml b/spec/fixtures/public_body_translations.yml
index 24b14c470..225bd74e2 100644
--- a/spec/fixtures/public_body_translations.yml
+++ b/spec/fixtures/public_body_translations.yml
@@ -10,6 +10,8 @@ geraldine_es_public_body_translation:
notes: ""
publication_scheme: ""
disclosure_log: ""
+ created_at: 2007-10-24 10:51:01.161639
+ updated_at: 2007-10-24 10:51:01.161639
geraldine_en_public_body_translation:
name: Geraldine Quango
@@ -23,6 +25,8 @@ geraldine_en_public_body_translation:
notes: ""
publication_scheme: ""
disclosure_log: ""
+ created_at: 2007-10-24 10:51:01.161639
+ updated_at: 2007-10-24 10:51:01.161639
humpadink_es_public_body_translation:
name: "El Department for Humpadinking"
@@ -36,6 +40,8 @@ humpadink_es_public_body_translation:
notes: Baguette
publication_scheme: ""
disclosure_log: ""
+ created_at: 2007-10-24 10:51:01.161639
+ updated_at: 2007-10-24 10:51:01.161639
humpadink_en_public_body_translation:
name: "Department for Humpadinking"
@@ -49,6 +55,8 @@ humpadink_en_public_body_translation:
notes: An albatross told me!!!
publication_scheme: ""
disclosure_log: ""
+ created_at: 2007-10-24 10:51:01.161639
+ updated_at: 2007-10-24 10:51:01.161639
forlorn_en_public_body_translation:
name: "Department of Loneliness"
@@ -62,6 +70,8 @@ forlorn_en_public_body_translation:
notes: A very lonely public body that no one has corresponded with
publication_scheme: ""
disclosure_log: ""
+ created_at: 2007-10-24 10:51:01.161639
+ updated_at: 2007-10-24 10:51:01.161639
silly_walks_en_public_body_translation:
id: 6
@@ -75,6 +85,8 @@ silly_walks_en_public_body_translation:
notes: You know the one.
publication_scheme: ""
disclosure_log: ""
+ created_at: 2007-10-24 10:51:01.161639
+ updated_at: 2007-10-24 10:51:01.161639
sensible_walks_en_public_body_translation:
id: 7
@@ -88,3 +100,50 @@ sensible_walks_en_public_body_translation:
notes: I bet you’ve never heard of it.
publication_scheme: ""
disclosure_log: ""
+ created_at: 2008-10-25 10:51:01.161639
+ updated_at: 2008-10-25 10:51:01.161639
+
+other_public_body_translation:
+ id: 8
+ public_body_id: 7
+ locale: en
+ name: "Another Public Body"
+ first_letter: A
+ request_email: other@localhost
+ short_name: Another Public Body
+ url_name: another_public_body
+ notes: More notes
+ publication_scheme: ""
+ disclosure_log: ""
+ created_at: 2008-10-25 10:51:01.161639
+ updated_at: 2008-10-25 10:51:01.161639
+
+humpadink_he_IL_public_body_translation:
+ name: "Hebrew Humpadinking"
+ first_letter: D
+ request_email: humpadink-requests@localhost
+ id: 9
+ public_body_id: 3
+ short_name: DfH
+ url_name: dfh
+ locale: he_IL
+ notes: An albatross told me!!!
+ publication_scheme: ""
+ disclosure_log: ""
+ created_at: 2007-10-24 10:51:01.161639
+ updated_at: 2007-10-24 10:51:01.161639
+
+accented_public_body_translation:
+ id: 10
+ public_body_id: 8
+ locale: cs
+ name: "Åčçèñtéd Authority"
+ first_letter: 'Å'
+ request_email: accented@localhost
+ short_name: "Åčçèñtéd Authority"
+ url_name: accented_authority
+ notes: This is to test unicode handling in body names
+ publication_scheme: ""
+ disclosure_log: ""
+ created_at: 2008-10-25 10:51:01.161639
+ updated_at: 2008-10-25 10:51:01.161639
diff --git a/spec/fixtures/raw_emails.yml b/spec/fixtures/raw_emails.yml
index ad2bc0a63..efdadde7b 100644
--- a/spec/fixtures/raw_emails.yml
+++ b/spec/fixtures/raw_emails.yml
@@ -1,3 +1,10 @@
+# == Schema Information
+#
+# Table name: raw_emails
+#
+# id :integer not null, primary key
+#
+
# The actual email messages are in fixtures/files/raw_emails
#
# Note that the words "money" and "bob" are used in some tests
diff --git a/spec/fixtures/theme_views/core/application_mailer/core_only.rhtml b/spec/fixtures/theme_views/core/application_mailer/core_only.html.erb
index 53b7798ec..53b7798ec 100644
--- a/spec/fixtures/theme_views/core/application_mailer/core_only.rhtml
+++ b/spec/fixtures/theme_views/core/application_mailer/core_only.html.erb
diff --git a/spec/fixtures/theme_views/core/application_mailer/multipart_core_only.rhtml b/spec/fixtures/theme_views/core/application_mailer/multipart_core_only.html.erb
index 646a349f8..646a349f8 100644
--- a/spec/fixtures/theme_views/core/application_mailer/multipart_core_only.rhtml
+++ b/spec/fixtures/theme_views/core/application_mailer/multipart_core_only.html.erb
diff --git a/spec/fixtures/theme_views/core/application_mailer/simple.rhtml b/spec/fixtures/theme_views/core/application_mailer/simple.html.erb
index a3937c940..a3937c940 100644
--- a/spec/fixtures/theme_views/core/application_mailer/simple.rhtml
+++ b/spec/fixtures/theme_views/core/application_mailer/simple.html.erb
diff --git a/spec/fixtures/theme_views/theme_one/application_mailer/multipart_theme_only.rhtml b/spec/fixtures/theme_views/theme_one/application_mailer/multipart_theme_only.html.erb
index d6423fbb4..d6423fbb4 100644
--- a/spec/fixtures/theme_views/theme_one/application_mailer/multipart_theme_only.rhtml
+++ b/spec/fixtures/theme_views/theme_one/application_mailer/multipart_theme_only.html.erb
diff --git a/spec/fixtures/theme_views/theme_one/application_mailer/simple.rhtml b/spec/fixtures/theme_views/theme_one/application_mailer/simple.html.erb
index ad43e0c87..ad43e0c87 100644
--- a/spec/fixtures/theme_views/theme_one/application_mailer/simple.rhtml
+++ b/spec/fixtures/theme_views/theme_one/application_mailer/simple.html.erb
diff --git a/spec/fixtures/theme_views/theme_one/application_mailer/theme_only.rhtml b/spec/fixtures/theme_views/theme_one/application_mailer/theme_only.html.erb
index 865445815..865445815 100644
--- a/spec/fixtures/theme_views/theme_one/application_mailer/theme_only.rhtml
+++ b/spec/fixtures/theme_views/theme_one/application_mailer/theme_only.html.erb
diff --git a/spec/fixtures/theme_views/theme_one/help/contact.es.html.erb b/spec/fixtures/theme_views/theme_one/help/contact.es.html.erb
new file mode 100644
index 000000000..a294c8aa1
--- /dev/null
+++ b/spec/fixtures/theme_views/theme_one/help/contact.es.html.erb
@@ -0,0 +1 @@
+contáctenos theme one
diff --git a/spec/fixtures/theme_views/theme_one/help/contact.html.erb b/spec/fixtures/theme_views/theme_one/help/contact.html.erb
new file mode 100644
index 000000000..428c7368d
--- /dev/null
+++ b/spec/fixtures/theme_views/theme_one/help/contact.html.erb
@@ -0,0 +1 @@
+Contact us
diff --git a/spec/fixtures/track_things.yml b/spec/fixtures/track_things.yml
index 1c4b323fd..61f1ed532 100644
--- a/spec/fixtures/track_things.yml
+++ b/spec/fixtures/track_things.yml
@@ -1,3 +1,19 @@
+# == Schema Information
+#
+# Table name: track_things
+#
+# id :integer not null, primary key
+# tracking_user_id :integer not null
+# track_query :string(255) not null
+# info_request_id :integer
+# tracked_user_id :integer
+# public_body_id :integer
+# track_medium :string(255) not null
+# track_type :string(255) default("internal_error"), not null
+# created_at :datetime
+# updated_at :datetime
+#
+
track_fancy_dog_request:
id: "10"
track_query: request:why_do_you_have_such_a_fancy_dog
diff --git a/spec/fixtures/track_things_sent_emails.yml b/spec/fixtures/track_things_sent_emails.yml
deleted file mode 100644
index e69de29bb..000000000
--- a/spec/fixtures/track_things_sent_emails.yml
+++ /dev/null
diff --git a/spec/fixtures/users.yml b/spec/fixtures/users.yml
index d6391c5e8..9eb91ee9c 100644
--- a/spec/fixtures/users.yml
+++ b/spec/fixtures/users.yml
@@ -1,4 +1,28 @@
-bob_smith_user:
+# == Schema Information
+#
+# Table name: users
+#
+# id :integer not null, primary key
+# email :string(255) not null
+# name :string(255) not null
+# hashed_password :string(255) not null
+# salt :string(255) not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# email_confirmed :boolean default(FALSE), not null
+# url_name :text not null
+# last_daily_track_email :datetime default(2000-01-01 00:00:00 UTC)
+# admin_level :string(255) default("none"), not null
+# ban_text :text default(""), not null
+# about_me :text default(""), not null
+# locale :string(255)
+# email_bounced_at :datetime
+# email_bounce_message :text default(""), not null
+# no_limit :boolean default(FALSE), not null
+# receive_email_alerts :boolean default(TRUE), not null
+#
+
+bob_smith_user:
id: "1"
name: Bob Smith
url_name: bob_smith
@@ -13,7 +37,7 @@ bob_smith_user:
locale: 'en'
about_me: 'I like making requests about fancy dogs and naughty chickens and stuff.'
receive_email_alerts: true
-silly_name_user:
+silly_name_user:
id: "2"
name: "Silly <em>Name</em>"
url_name: silly_emnameem
@@ -28,7 +52,7 @@ silly_name_user:
locale: 'en'
about_me: ''
receive_email_alerts: true
-admin_user:
+admin_user:
id: "3"
name: Joe Admin
url_name: joe_admin
@@ -43,7 +67,7 @@ admin_user:
locale: ''
about_me: ''
receive_email_alerts: true
-unconfirmed_user:
+unconfirmed_user:
id: "4"
name: "Unconfirmed"
url_name: unconfirmed
@@ -71,3 +95,17 @@ robin_user:
ban_text: ''
about_me: 'I am the best'
receive_email_alerts: true
+another_user:
+ id: 6
+ name: Another User
+ url_name: another_user
+ email: another@localhost
+ salt: "-6116981980.392287733335677"
+ hashed_password: 6b7cd45a5f35fd83febc0452a799530398bfb6e8 # jonespassword
+ updated_at: 2012-01-01 10:39:15.491593
+ created_at: 2012-01-01 10:39:15.491593
+ email_confirmed: true
+ admin_level: 'none'
+ ban_text: ''
+ about_me: 'Just another user'
+ receive_email_alerts: true
diff --git a/spec/helpers/link_to_helper_spec.rb b/spec/helpers/link_to_helper_spec.rb
index 030fd612d..2259db6c2 100644
--- a/spec/helpers/link_to_helper_spec.rb
+++ b/spec/helpers/link_to_helper_spec.rb
@@ -8,70 +8,65 @@ describe LinkToHelper do
before do
@mock_request = mock_model(InfoRequest, :url_title => 'test_title')
- @old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
end
- after do
- ActionController::Routing::Routes.filters = @old_filters
- end
-
it 'should return a path like /request/test_title' do
- request_url(@mock_request).should == '/request/test_title'
+ request_path(@mock_request).should == '/request/test_title'
end
it 'should return a path including any extra parameters passed' do
- request_url(@mock_request, {:update_status => 1}).should == '/request/test_title?update_status=1'
+ request_path(@mock_request, {:update_status => 1}).should == '/request/test_title?update_status=1'
end
end
- describe "when appending something to a URL" do
- it 'should append to things without query strings' do
- main_url('/a', '.json').should == 'http://test.host/a.json'
- end
- it 'should append to things with query strings' do
- main_url('/a?z=1', '.json').should == 'http://test.host/a.json?z=1'
- end
- it 'should fail silently with invalid URLs' do
- main_url('/a?z=9%', '.json').should == 'http://test.host/a?z=9%'
- end
- end
-
- describe 'when displaying a user admin link for a request' do
+ describe 'when displaying a user link for a request' do
- it 'should return the text "An anonymous user (external)" in the case where there is no external username' do
- info_request = mock_model(InfoRequest, :external_user_name => nil,
- :is_external? => true)
- user_admin_link_for_request(info_request).should == 'Anonymous user (external)'
- end
+ context "for external requests" do
+ before do
+ @info_request = mock_model(InfoRequest, :external_user_name => nil,
+ :is_external? => true)
+ end
- end
+ it 'should return the text "Anonymous user" with a link to the privacy help pages when there is no external username' do
+ request_user_link(@info_request).should == '<a href="/help/privacy#anonymous">Anonymous user</a>'
+ end
- describe 'admin_url' do
- context 'with no ADMIN_BASE_URL set' do
- it 'should prepend the admin general index path to a simple string' do
- admin_url('unclassified').should == 'http://test.host/en/admin/unclassified'
+ it 'should return a link with an alternative text if requested' do
+ request_user_link(@info_request, 'other text').should == '<a href="/help/privacy#anonymous">other text</a>'
end
- it 'should prepend the admin general index path to a deeper URL' do
- admin_url('request/show/123').should == 'http://test.host/en/admin/request/show/123'
+ it 'should display an absolute link if requested' do
+ request_user_link_absolute(@info_request).should == '<a href="http://test.host/help/privacy#anonymous">Anonymous user</a>'
end
end
- context 'with ADMIN_BASE_URL set' do
- before(:each) do
- Configuration::should_receive(:admin_base_url).and_return('https://www.example.com/secure/alaveteli-admin/')
+ context "for normal requests" do
+
+ before do
+ @info_request = FactoryGirl.build(:info_request)
end
- it 'should prepend the admin base URL to a simple string' do
- admin_url('unclassified').should == 'https://www.example.com/secure/alaveteli-admin/unclassified'
+ it 'should display a relative link by default' do
+ request_user_link(@info_request).should == '<a href="/user/example_user">Example User</a>'
end
- it 'should prepend the admin base URL to a deeper URL' do
- admin_url('request/show/123').should == 'https://www.example.com/secure/alaveteli-admin/request/show/123'
+ it 'should display an absolute link if requested' do
+ request_user_link_absolute(@info_request).should == '<a href="http://test.host/user/example_user">Example User</a>'
end
+
+ end
+
+ end
+
+ describe 'when displaying a user admin link for a request' do
+
+ it 'should return the text "An anonymous user (external)" in the case where there is no external username' do
+ info_request = mock_model(InfoRequest, :external_user_name => nil,
+ :is_external? => true)
+ user_admin_link_for_request(info_request).should == 'Anonymous user (external)'
end
+
end
describe 'simple_date' do
diff --git a/spec/integration/admin_spec.rb b/spec/integration/admin_spec.rb
index e148ea3ca..8e6351d2c 100644
--- a/spec/integration/admin_spec.rb
+++ b/spec/integration/admin_spec.rb
@@ -1,21 +1,74 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-
-require "base64"
+require File.expand_path(File.dirname(__FILE__) + '/alaveteli_dsl')
describe "When administering the site" do
+
+ before do
+ AlaveteliConfiguration.stub!(:skip_admin_auth).and_return(false)
+ end
+
it "allows an admin to log in as another user" do
# First log in as Joe Admin
- admin_user = users(:admin_user)
- admin_user.email_confirmed = true
- admin_user.save!
- post_via_redirect "/profile/sign_in", :user_signin => {:email => admin_user.email, :password => "jonespassword"}
- response.should be_success
-
+ confirm(:admin_user)
+ admin = login(:admin_user)
+
# Now fetch the "log in as" link to log in as Bob
- get_via_redirect "/admin/user/login_as/#{users(:bob_smith_user).id}", nil, {
- "Authorization" => "Basic " + Base64.encode64("#{Configuration::admin_username}:#{Configuration::admin_password}").strip
- }
- response.should be_success
- session[:user_id].should == users(:bob_smith_user).id
+ admin.get_via_redirect "/en/admin/user/login_as/#{users(:bob_smith_user).id}"
+ admin.response.should be_success
+ admin.session[:user_id].should == users(:bob_smith_user).id
+ end
+
+ it 'does not allow a non-admin user to login as another user' do
+ robin = login(:robin_user)
+ robin.get_via_redirect "/en/admin/user/login_as/#{users(:bob_smith_user).id}"
+ robin.response.should be_success
+ robin.session[:user_id].should_not == users(:bob_smith_user).id
+ end
+
+ it "allows redelivery of an incoming message to a closed request" do
+ confirm(:admin_user)
+ admin = login(:admin_user)
+ ir = info_requests(:fancy_dog_request)
+ close_request(ir)
+ InfoRequest.holding_pen_request.incoming_messages.length.should == 0
+ ir.incoming_messages.length.should == 1
+ receive_incoming_mail('incoming-request-plain.email', ir.incoming_email, "frob@nowhere.com")
+ InfoRequest.holding_pen_request.incoming_messages.length.should == 1
+ new_im = InfoRequest.holding_pen_request.incoming_messages[0]
+ ir.incoming_messages.length.should == 1
+ post_params = {'redeliver_incoming_message_id' => new_im.id,
+ 'url_title' => ir.url_title}
+ admin.post '/en/admin/incoming/redeliver', post_params
+ admin.response.location.should == 'http://www.example.com/en/admin/request/show/101'
+ ir = InfoRequest.find_by_url_title(ir.url_title)
+ ir.incoming_messages.length.should == 2
+
+ InfoRequest.holding_pen_request.incoming_messages.length.should == 0
+ end
+
+ it "allows redelivery of an incoming message to more than one request" do
+ confirm(:admin_user)
+ admin = login(:admin_user)
+
+ ir1 = info_requests(:fancy_dog_request)
+ close_request(ir1)
+ ir1.incoming_messages.length.should == 1
+ ir2 = info_requests(:another_boring_request)
+ ir2.incoming_messages.length.should == 1
+
+ receive_incoming_mail('incoming-request-plain.email', ir1.incoming_email, "frob@nowhere.com")
+ InfoRequest.holding_pen_request.incoming_messages.length.should == 1
+
+ new_im = InfoRequest.holding_pen_request.incoming_messages[0]
+ post_params = {'redeliver_incoming_message_id' => new_im.id,
+ 'url_title' => "#{ir1.url_title},#{ir2.url_title}"}
+ admin.post '/en/admin/incoming/redeliver', post_params
+ ir1.reload
+ ir1.incoming_messages.length.should == 2
+ ir2.reload
+ ir2.incoming_messages.length.should == 2
+ admin.response.location.should == 'http://www.example.com/en/admin/request/show/106'
+ InfoRequest.holding_pen_request.incoming_messages.length.should == 0
end
+
end
diff --git a/spec/integration/alaveteli_dsl.rb b/spec/integration/alaveteli_dsl.rb
new file mode 100644
index 000000000..119bb05a0
--- /dev/null
+++ b/spec/integration/alaveteli_dsl.rb
@@ -0,0 +1,68 @@
+module AlaveteliDsl
+
+ def browses_request(url_title)
+ get "/request/#{url_title}"
+ assert_response :success
+ end
+
+ def creates_request_unregistered
+ params = { :info_request => { :public_body_id => public_bodies(:geraldine_public_body).id,
+ :title => "Why is your quango called Geraldine?",
+ :tag_string => "" },
+ :outgoing_message => { :body => "This is a silly letter. It is too short to be interesting." },
+ :submitted_new_request => 1,
+ :preview => 0
+ }
+
+ # Initially we are not logged in. Try to create a new request.
+ post "/new", params
+ # We expect to be redirected to the login page
+ post_redirect = PostRedirect.get_last_post_redirect
+ response.should redirect_to(:controller => 'user', :action => 'signin', :token => post_redirect.token)
+ follow_redirect!
+ response.should render_template("user/sign")
+ response.body.should match(/To send your FOI request, please sign in or make a new account./)
+ end
+
+end
+
+def login(user)
+ open_session do |sess|
+ # Make sure we get a fresh empty session - there seems to be some
+ # problem with session leakage otherwise
+ sess.reset!
+ sess.extend(AlaveteliDsl)
+
+ if user.is_a? User
+ u = user
+ else
+ u = users(user)
+ end
+ sess.visit signin_path
+ sess.fill_in "Your e-mail:", :with => u.email
+ sess.fill_in "Password:", :with => "jonespassword"
+ sess.click_button "Sign in"
+ assert sess.session[:user_id] == u.id
+ end
+end
+
+def without_login
+ open_session do |sess|
+ sess.extend(AlaveteliDsl)
+ end
+end
+
+def confirm(user)
+ u = users(user)
+ u.email_confirmed = true
+ u.save!
+end
+
+def close_request(request)
+ request.allow_new_responses_from = 'nobody'
+ request.handle_rejected_responses = 'holding_pen'
+ request.save!
+end
+
+
+
diff --git a/spec/integration/cookie_stripping_spec.rb b/spec/integration/cookie_stripping_spec.rb
new file mode 100644
index 000000000..897899fd5
--- /dev/null
+++ b/spec/integration/cookie_stripping_spec.rb
@@ -0,0 +1,12 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+require File.expand_path(File.dirname(__FILE__) + '/alaveteli_dsl')
+
+describe 'when making stripping cookies' do
+
+ it 'should not set a cookie when no significant session data is set' do
+ get 'country_message'
+ response.headers['Set-Cookie'].should be_blank
+ end
+
+end
+
diff --git a/spec/integration/create_request_spec.rb b/spec/integration/create_request_spec.rb
index 4efbf94ee..84fad12f9 100644
--- a/spec/integration/create_request_spec.rb
+++ b/spec/integration/create_request_spec.rb
@@ -1,51 +1,36 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+require File.expand_path(File.dirname(__FILE__) + '/alaveteli_dsl')
describe "When creating requests" do
- def create_request_unregistered
- params = { :info_request => { :public_body_id => public_bodies(:geraldine_public_body).id,
- :title => "Why is your quango called Geraldine?",
- :tag_string => "" },
- :outgoing_message => { :body => "This is a silly letter. It is too short to be interesting." },
- :submitted_new_request => 1,
- :preview => 0
- }
-
- # Initially we are not logged in. Try to create a new request.
- post "/new", params
- # We expect to be redirected to the login page
- post_redirect = PostRedirect.get_last_post_redirect
- response.should redirect_to(:controller => 'user', :action => 'signin', :token => post_redirect.token)
- follow_redirect!
- response.should render_template("user/sign")
- response.body.should match(/To send your FOI request, please sign in or make a new account./)
- end
+
it "should associate the request with the requestor, even if it is approved by an admin" do
+
+ unregistered = without_login
# This is a test for https://github.com/mysociety/alaveteli/issues/446
- create_request_unregistered
+ unregistered.creates_request_unregistered
post_redirect = PostRedirect.get_last_post_redirect
# Now log in as an unconfirmed user.
- post "/profile/sign_in", :user_signin => {:email => users(:unconfirmed_user).email, :password => "jonespassword"}, :token => post_redirect.token
+ unregistered.post "/profile/sign_in", :user_signin => {:email => users(:unconfirmed_user).email, :password => "jonespassword"}, :token => post_redirect.token
# This will trigger a confirmation mail. Get the PostRedirect for later.
- response.should render_template("user/confirm")
+ unregistered.response.body.should match('Now check your email!')
post_redirect = PostRedirect.get_last_post_redirect
+
# Now log in as an admin user, then follow the confirmation link in the email that was sent to the unconfirmed user
- admin_user = users(:admin_user)
- admin_user.email_confirmed = true
- admin_user.save!
- post_via_redirect "/profile/sign_in", :user_signin => {:email => admin_user.email, :password => "jonespassword"}
- response.should be_success
- get "/c/" + post_redirect.email_token
- follow_redirect!
- response.location.should =~ %r(/request/(.+)/new)
- response.location =~ %r(/request/(.+)/new)
+ confirm(:admin_user)
+ admin = login(:admin_user)
+ admin.get "/c/" + post_redirect.email_token
+ admin.follow_redirect!
+ admin.response.location.should =~ %r(/request/(.+)/new)
+ admin.response.location =~ %r(/request/(.+)/new)
url_title = $1
info_request = InfoRequest.find_by_url_title(url_title)
info_request.should_not be_nil
# Make sure the request is still owned by the user who made it, not the admin who confirmed it
info_request.user_id.should == users(:unconfirmed_user).id
+
end
end
diff --git a/spec/integration/download_request_spec.rb b/spec/integration/download_request_spec.rb
new file mode 100644
index 000000000..638198cde
--- /dev/null
+++ b/spec/integration/download_request_spec.rb
@@ -0,0 +1,324 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+require File.expand_path(File.dirname(__FILE__) + '/alaveteli_dsl')
+
+describe 'when making a zipfile available' do
+
+ after do
+ FileUtils.rm_rf(InfoRequest.download_zip_dir)
+ end
+
+ def inspect_zip_download(session, info_request)
+ session.get_via_redirect "request/#{info_request.url_title}/download"
+ session.response.should be_success
+ Tempfile.open('download') do |f|
+ f.binmode
+ f.write(session.response.body)
+ f.flush
+ Zip::ZipFile::open(f.path) do |zip|
+ yield zip
+ end
+ end
+ end
+
+ def sleep_and_receive_mail(name, info_request)
+ # The path of the zip file is based on the hash of the timestamp of the last request
+ # in the thread, so we wait for a second to make sure this one will have a different
+ # timestamp than the previous.
+ sleep 1
+ receive_incoming_mail(name, info_request.incoming_email)
+ end
+
+ context 'when an html to pdf converter is supplied' do
+
+ before do
+ # We want to test the contents of the pdf, and we don't know whether a particular
+ # instance will have a working html_to_pdf tool, so just copy the HTML rendered
+ # to the PDF file for the purposes of checking it doesn't contain anything that
+ # shouldn't be there.
+ AlaveteliConfiguration.stub!(:html_to_pdf_command).and_return('/bin/cp')
+ end
+
+ context 'when an incoming message is made "requester_only"' do
+
+ it 'should not include the incoming message or attachments in a download of the entire request
+ by a non-request owner but should retain them for owner and admin' do
+
+ # Non-owner can download zip with incoming and attachments
+ non_owner = login(FactoryGirl.create(:user))
+ info_request = FactoryGirl.create(:info_request_with_incoming_attachments)
+
+ inspect_zip_download(non_owner, info_request) do |zip|
+ zip.count.should == 3
+ zip.read('correspondence.pdf').should match('hereisthetext')
+ end
+
+ # Admin makes the incoming message requester only
+ admin = login(FactoryGirl.create(:admin_user))
+ post_data = {:incoming_message => {:prominence => 'requester_only',
+ :prominence_reason => 'boring'}}
+ admin.post_via_redirect "/en/admin/incoming/update/#{info_request.incoming_messages.first.id}", post_data
+ admin.response.should be_success
+
+ # Admin retains the requester only things
+ inspect_zip_download(admin, info_request) do |zip|
+ zip.count.should == 3
+ zip.read('correspondence.pdf').should match('hereisthetext')
+ end
+
+ # Zip for non owner is now without requester_only things
+ inspect_zip_download(non_owner, info_request) do |zip|
+ zip.count.should == 1
+ correspondence_text = zip.read('correspondence.pdf')
+ correspondence_text.should_not match('hereisthetext')
+ expected_text = "This message has been hidden.\n boring"
+ correspondence_text.should match(expected_text)
+ end
+
+ # Requester retains the requester only things
+ owner = login(info_request.user)
+ inspect_zip_download(owner, info_request) do |zip|
+ zip.count.should == 3
+ zip.read('correspondence.pdf').should match('hereisthetext')
+ end
+
+ end
+
+ end
+
+ context 'when an outgoing message is made "requester_only"' do
+
+ it 'should not include the outgoing message in a download of the entire request
+ by a non-request owner but should retain them for owner and admin' do
+
+ # Non-owner can download zip with outgoing
+ non_owner = login(FactoryGirl.create(:user))
+ info_request = FactoryGirl.create(:info_request)
+
+ inspect_zip_download(non_owner, info_request) do |zip|
+ zip.count.should == 1
+ zip.read('correspondence.pdf').should match('Some information please')
+ end
+
+ # Admin makes the incoming message requester only
+ admin = login(FactoryGirl.create(:admin_user))
+ post_data = {:outgoing_message => {:prominence => 'requester_only',
+ :prominence_reason => 'boring',
+ :body => 'Some information please'}}
+ admin.post_via_redirect "/en/admin/outgoing/update/#{info_request.outgoing_messages.first.id}", post_data
+ admin.response.should be_success
+
+ # Admin retains the requester only things
+ inspect_zip_download(admin, info_request) do |zip|
+ zip.count.should == 1
+ zip.read('correspondence.pdf').should match('Some information please')
+ end
+
+ # Zip for non owner is now without requester_only things
+ inspect_zip_download(non_owner, info_request) do |zip|
+ zip.count.should == 1
+ correspondence_text = zip.read('correspondence.pdf')
+ correspondence_text.should_not match('Some information please')
+ expected_text = "This message has been hidden.\n boring"
+ correspondence_text.should match(expected_text)
+ end
+
+ # Requester retains the requester only things
+ owner = login(info_request.user)
+ inspect_zip_download(owner, info_request) do |zip|
+ zip.count.should == 1
+ zip.read('correspondence.pdf').should match('Some information please')
+ end
+
+ end
+
+ end
+
+ end
+
+ context 'when no html to pdf converter is supplied' do
+
+ before do
+ AlaveteliConfiguration.stub!(:html_to_pdf_command).and_return('')
+ end
+
+ it "should update the contents of the zipfile when the request changes" do
+
+ info_request = FactoryGirl.create(:info_request_with_incoming)
+ request_owner = login(info_request.user)
+ inspect_zip_download(request_owner, info_request) do |zip|
+ zip.count.should == 1 # just the message
+ expected = 'This is a plain-text version of the Freedom of Information request "Example Title"'
+ zip.read('correspondence.txt').should match expected
+ end
+
+ sleep_and_receive_mail('incoming-request-two-same-name.email', info_request)
+
+ inspect_zip_download(request_owner, info_request) do |zip|
+ zip.count.should == 3 # the message plus two "hello-world.txt" files
+ zip.read('2_2_hello world.txt').should match('Second hello')
+ zip.read('2_3_hello world.txt').should match('First hello')
+ end
+
+ sleep_and_receive_mail('incoming-request-attachment-unknown-extension.email', info_request)
+
+ inspect_zip_download(request_owner, info_request) do |zip|
+ zip.count.should == 4 # the message plus two "hello-world.txt" files, and the new attachment
+ zip.read('3_2_hello.qwglhm').should match('This is an unusual')
+ end
+ end
+
+ context 'when a request is "requester_only"' do
+
+ before do
+ @non_owner = login(FactoryGirl.create(:user))
+ @info_request = FactoryGirl.create(:info_request_with_incoming,
+ :prominence => 'requester_only')
+ @request_owner = login(@info_request.user)
+ @admin = login(FactoryGirl.create(:admin_user))
+ end
+
+
+ it 'should allow a download of the request by the request owner and admin only' do
+ # Requester can access the zip
+ inspect_zip_download(@request_owner, @info_request) do |zip|
+ zip.count.should == 1
+ zip.read('correspondence.txt').should match('hereisthetext')
+ end
+ # Non-owner can't
+ @non_owner.get_via_redirect "request/#{@info_request.url_title}/download"
+ @non_owner.response.code.should == '403'
+ # Admin can
+ inspect_zip_download(@admin, @info_request) do |zip|
+ zip.count.should == 1
+ zip.read('correspondence.txt').should match('hereisthetext')
+ end
+ end
+ end
+
+ context 'when a request is "hidden"' do
+
+ it 'should not allow a download of the request by an admin only' do
+ @non_owner = login(FactoryGirl.create(:user))
+ @info_request = FactoryGirl.create(:info_request_with_incoming,
+ :prominence => 'hidden')
+ @request_owner = login(@info_request.user)
+ @admin = login(FactoryGirl.create(:admin_user))
+
+ # Requester can't access the zip
+ @request_owner.get_via_redirect "request/#{@info_request.url_title}/download"
+ @request_owner.response.code.should == '403'
+ # Non-owner can't
+ @non_owner.get_via_redirect "request/#{@info_request.url_title}/download"
+ @non_owner.response.code.should == '403'
+ # Admin can
+ inspect_zip_download(@admin, @info_request) do |zip|
+ zip.count.should == 1
+ zip.read('correspondence.txt').should match('hereisthetext')
+ end
+ end
+
+ end
+
+ context 'when an incoming message is made "requester_only"' do
+
+ it 'should not include the incoming message or attachments in a download of the entire request
+ by a non-request owner but should retain them for owner and admin' do
+
+ # Non-owner can download zip with outgoing
+ non_owner = login(FactoryGirl.create(:user))
+ info_request = FactoryGirl.create(:info_request_with_incoming_attachments)
+
+ inspect_zip_download(non_owner, info_request) do |zip|
+ zip.count.should == 3
+ zip.read('correspondence.txt').should match('hereisthetext')
+ end
+
+ # Admin makes the incoming message requester only
+ admin = login(FactoryGirl.create(:admin_user))
+ post_data = {:incoming_message => {:prominence => 'requester_only',
+ :prominence_reason => 'boring'}}
+ admin.post_via_redirect "/en/admin/incoming/update/#{info_request.incoming_messages.first.id}", post_data
+ admin.response.should be_success
+
+ # Admin retains the requester only things
+ inspect_zip_download(admin, info_request) do |zip|
+ zip.count.should == 3
+ zip.read('correspondence.txt').should match('hereisthetext')
+ end
+
+ # Zip for non owner is now without requester_only things
+ inspect_zip_download(non_owner, info_request) do |zip|
+ zip.count.should == 1
+ correspondence_text = zip.read('correspondence.txt')
+ correspondence_text.should_not match('hereisthetext')
+ expected_text = 'This message has been hidden. boring'
+ correspondence_text.should match(expected_text)
+ end
+
+ # Requester retains the requester only things
+ owner = login(info_request.user)
+ inspect_zip_download(owner, info_request) do |zip|
+ zip.count.should == 3
+ zip.read('correspondence.txt').should match('hereisthetext')
+ end
+
+ end
+
+ end
+
+ context 'when an outgoing message is made "requester_only"' do
+
+ it 'should not include the outgoing message in a download of the entire request
+ by a non-request owner but should retain them for owner and admin' do
+
+ # Non-owner can download zip with incoming and attachments
+ non_owner = login(FactoryGirl.create(:user))
+ info_request = FactoryGirl.create(:info_request)
+
+ inspect_zip_download(non_owner, info_request) do |zip|
+ zip.count.should == 1
+ zip.read('correspondence.txt').should match('Some information please')
+ end
+
+ # Admin makes the incoming message requester only
+ admin = login(FactoryGirl.create(:admin_user))
+ post_data = {:outgoing_message => {:prominence => 'requester_only',
+ :prominence_reason => 'boring',
+ :body => 'Some information please'}}
+ admin.post_via_redirect "/en/admin/outgoing/update/#{info_request.outgoing_messages.first.id}", post_data
+ admin.response.should be_success
+
+ # Admin retains the requester only things
+ inspect_zip_download(admin, info_request) do |zip|
+ zip.count.should == 1
+ zip.read('correspondence.txt').should match('Some information please')
+ end
+
+ # Zip for non owner is now without requester_only things
+ inspect_zip_download(non_owner, info_request) do |zip|
+ zip.count.should == 1
+ correspondence_text = zip.read('correspondence.txt')
+ correspondence_text.should_not match('Some information please')
+ expected_text = 'This message has been hidden. boring'
+ correspondence_text.should match(expected_text)
+ end
+
+ # Requester retains the requester only things
+ owner = login(info_request.user)
+ inspect_zip_download(owner, info_request) do |zip|
+ zip.count.should == 1
+ zip.read('correspondence.txt').should match('Some information please')
+ end
+
+ end
+
+ end
+
+ it 'should successfully make a zipfile for an external request' do
+ external_request = FactoryGirl.create(:external_request)
+ user = login(FactoryGirl.create(:user))
+ inspect_zip_download(user, external_request){ |zip| zip.count.should == 1 }
+ end
+ end
+
+end
diff --git a/spec/integration/errors_spec.rb b/spec/integration/errors_spec.rb
index a44ed7051..17a0153c2 100644
--- a/spec/integration/errors_spec.rb
+++ b/spec/integration/errors_spec.rb
@@ -1,53 +1,130 @@
+# -*- coding: utf-8 -*-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-describe "When rendering errors" do
+describe "When errors occur" do
- before(:each) do
- load_raw_emails_data
- ActionController::Base.consider_all_requests_local = false
+ def set_consider_all_requests_local(value)
+ @requests_local = Rails.application.config.consider_all_requests_local
+ Rails.application.config.consider_all_requests_local = value
end
- after(:each) do
- ActionController::Base.consider_all_requests_local = true
+ def restore_consider_all_requests_local
+ Rails.application.config.consider_all_requests_local = @requests_local
end
- it "should render a 404 for unrouteable URLs" do
- get("/frobsnasm")
- response.body.should include("The page doesn't exist")
- response.code.should == "404"
- end
- it "should render a 404 for users that don't exist" do
- get("/user/wobsnasm")
- response.body.should include("The page doesn't exist")
- response.code.should == "404"
- end
- it "should render a 404 for bodies that don't exist" do
- get("/body/wobsnasm")
- response.body.should include("The page doesn't exist")
- response.code.should == "404"
+ before(:each) do
+ # This should happen automatically before each test but doesn't with these integration
+ # tests for some reason.
+ ActionMailer::Base.deliveries = []
end
- it "should render a 500 for general errors" do
- ir = info_requests(:naughty_chicken_request)
- # Set an invalid state for the request. Note that update_attribute doesn't run the validations
- ir.update_attribute(:described_state, "crotchety")
- get("/request/#{ir.url_title}")
- response.code.should == "500"
+
+ after(:each) do
+ restore_consider_all_requests_local
end
- it "should render a 403 for attempts at directory listing for attachments" do
- # make a fake cache
- foi_cache_path = File.expand_path(File.join(File.dirname(__FILE__), '../../cache'))
- FileUtils.mkdir_p(File.join(foi_cache_path, "views/en/request/101/101/response/1/attach/html/1"))
- get("/request/101/response/1/attach/html/1/" )
- response.body.should include("Directory listing not allowed")
- response.code.should == "403"
- get("/request/101/response/1/attach/html" )
- response.body.should include("Directory listing not allowed")
- response.code.should == "403"
+
+ context 'when considering all requests local (by default all in development)' do
+
+ before(:each) { set_consider_all_requests_local(true) }
+
+ it 'should show a full trace for general errors' do
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/request/example")
+ response.body.should have_selector('div[id=traces]')
+ response.body.should match('An example error')
+ end
+
end
- it "should render a 404 for non-existent 'details' pages for requests" do
- get("/details/request/wobble" )
- response.body.should include("The page doesn't exist")
- response.code.should == "404"
+
+ context 'when not considering all requests local' do
+
+ before(:each) { set_consider_all_requests_local(false) }
+
+ it "should render a 404 for unrouteable URLs using the general/exception_caught template" do
+ get("/frobsnasm")
+ response.should render_template('general/exception_caught')
+ response.code.should == "404"
+ end
+
+ it "should render a 404 for users or bodies that don't exist using the general/exception_caught
+ template" do
+ ['/user/wobsnasm', '/body/wobsnasm'].each do |non_existent_url|
+ get(non_existent_url)
+ response.should render_template('general/exception_caught')
+ response.code.should == "404"
+ end
+ end
+
+ it "should render a 500 for general errors using the general/exception_caught template" do
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/request/example")
+ response.should render_template('general/exception_caught')
+ response.body.should match('An example error')
+ response.code.should == "500"
+ end
+
+ it 'should render a 500 for json errors' do
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/request/example.json")
+ response.code.should == '500'
+ end
+
+ it 'should render a 404 for a non-found xml request' do
+ get("/frobsnasm.xml")
+ response.code.should == '404'
+ end
+
+ it 'should notify of a general error' do
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/request/example")
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.body.should =~ /An example error/
+ end
+
+ it 'should log a general error' do
+ Rails.logger.should_receive(:fatal)
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/request/example")
+ end
+
+ it 'should assign the locale for the general/exception_caught template' do
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/es/request/example")
+ response.should render_template('general/exception_caught')
+ response.body.should match('Lo sentimos, hubo un problema procesando esta página')
+ response.body.should match('An example error')
+ end
+
+ it "should render a 403 with text body for attempts at directory listing for attachments" do
+ # make a fake cache
+ foi_cache_path = File.expand_path(File.join(File.dirname(__FILE__), '../../cache'))
+ FileUtils.mkdir_p(File.join(foi_cache_path, "views/en/request/101/101/response/1/attach/html/1"))
+ get("/request/101/response/1/attach/html/1/" )
+ response.body.should include("Directory listing not allowed")
+ response.code.should == "403"
+ get("/request/101/response/1/attach/html" )
+ response.body.should include("Directory listing not allowed")
+ response.code.should == "403"
+ end
+
+ it "return a 403 for a JSON PermissionDenied error" do
+ InfoRequest.stub!(:find_by_url_title!).and_raise(ApplicationController::PermissionDenied)
+ get("/request/example.json")
+ response.code.should == '403'
+ end
+
+ context "in the admin interface" do
+
+ it 'should show a full trace for general errors' do
+ InfoRequest.stub!(:find).and_raise("An example error")
+ get("/admin/request/show/333")
+ response.body.should have_selector('div[id=traces]')
+ response.body.should match('An example error')
+ end
+
+ end
+
end
-end
+end
diff --git a/spec/integration/localisation_spec.rb b/spec/integration/localisation_spec.rb
new file mode 100644
index 000000000..4f6b61ae1
--- /dev/null
+++ b/spec/integration/localisation_spec.rb
@@ -0,0 +1,88 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe "when generating urls" do
+
+ before do
+ @home_link_regex = /href=".*\/en\//
+ end
+
+ it "should generate URLs that include the locale when using one that includes an underscore" do
+ get('/en_GB')
+ response.body.should match /href="\/en_GB\//
+ end
+
+ it "should fall back to the language if the territory is unknown" do
+ AlaveteliLocalization.set_locales(available_locales='es en', default_locale='en')
+ get('/', {}, {'HTTP_ACCEPT_LANGUAGE' => 'en_US'})
+ response.body.should match /href="\/en\//
+ response.body.should_not match /href="\/en_US\//
+ end
+
+ it "should generate URLs without a locale prepended when there's only one locale set" do
+ AlaveteliLocalization.set_locales(available_locales='en', default_locale='en')
+ get('/')
+ response.should_not contain @home_link_regex
+ end
+
+ it 'should redirect requests for a public body in a locale to the canonical name in that locale' do
+ get('/es/body/dfh')
+ response.should redirect_to "/es/body/edfh"
+ end
+
+ it 'should remember a filter view when redirecting a public body request to the canonical name' do
+ get('/es/body/tgq/successful')
+ response.should redirect_to "/es/body/etgq/successful"
+ end
+
+ describe 'when there is more than one locale' do
+
+ before do
+ AlaveteliLocalization.set_locales(available_locales='es en', default_locale='en')
+ end
+
+ it "should generate URLs with a locale prepended when there's more than one locale set" do
+ get('/')
+ response.body.should match @home_link_regex
+ end
+
+ describe 'when using the default locale' do
+
+ before do
+ @default_lang_home_link = /href=".*\/en\//
+ @other_lang_home_link = /href=".*\/es\//
+ @old_include_default_locale_in_urls = AlaveteliConfiguration::include_default_locale_in_urls
+ end
+
+ describe 'when the config value INCLUDE_DEFAULT_LOCALE_IN_URLS is false' do
+
+ before do
+ AlaveteliLocalization.set_default_locale_urls(false)
+ end
+
+ it 'should generate URLs without a locale prepended' do
+ get '/'
+ response.should_not contain @default_lang_home_link
+ end
+
+ it 'should render the front page in the default language when no locale param
+ is present and the session locale is not the default' do
+ get('/', {:locale => 'es'})
+ response.should_not contain @other_lang_home_link
+ end
+ end
+
+ it 'should generate URLs with a locale prepended when the config value
+ INCLUDE_DEFAULT_LOCALE_IN_URLS is true' do
+ AlaveteliLocalization.set_default_locale_urls(true)
+ get '/'
+ response.body.should match /#{@default_lang_home_link}/
+ end
+
+ after do
+ AlaveteliLocalization.set_default_locale_urls(@old_include_default_locale_in_urls)
+ end
+
+ end
+ end
+
+end
diff --git a/spec/integration/request_controller_spec.rb b/spec/integration/request_controller_spec.rb
new file mode 100644
index 000000000..f5de692b8
--- /dev/null
+++ b/spec/integration/request_controller_spec.rb
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+require File.expand_path(File.dirname(__FILE__) + '/alaveteli_dsl')
+
+describe RequestController, "when classifying an information request" do
+
+ describe 'when the request is internal' do
+
+ before(:each) do
+ load_raw_emails_data
+ @dog_request = info_requests(:fancy_dog_request)
+ # This should happen automatically before each test but doesn't with these integration
+ # tests for some reason.
+ ActionMailer::Base.deliveries = []
+ end
+
+ describe 'when logged in as the requestor' do
+
+ before :each do
+ @bob = login(:bob_smith_user)
+ end
+
+ it "should send an email including the message" do
+ @bob.visit describe_state_message_path(:url_title => @dog_request.url_title,
+ :described_state => "requires_admin")
+ @bob.fill_in "Please tell us more:", :with => "Okay. I don't quite understand."
+ @bob.click_button "Submit status and send message"
+
+ @bob.response.should contain "Thank you! We'll look into what happened and try and fix it up."
+
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.body.should =~ /as needing admin/
+ mail.body.should =~ /Okay. I don't quite understand./
+ end
+ end
+ end
+end
diff --git a/spec/integration/search_request_spec.rb b/spec/integration/search_request_spec.rb
index c564032a6..23a62e97b 100644
--- a/spec/integration/search_request_spec.rb
+++ b/spec/integration/search_request_spec.rb
@@ -13,10 +13,8 @@ describe "When searching" do
end
it "should redirect requests with search in query string to URL-based page" do
- url = '/search/all?query=bob'
- request_via_redirect("post", url)
- response.request.url.should_not include(url)
- response.request.url.should include("/search/bob/all")
+ post '/search/all?query=bob'
+ response.should redirect_to "/en/search/bob/all"
end
it "should correctly execute simple search" do
diff --git a/spec/integration/view_request_spec.rb b/spec/integration/view_request_spec.rb
index 442721890..814e20fb3 100644
--- a/spec/integration/view_request_spec.rb
+++ b/spec/integration/view_request_spec.rb
@@ -1,16 +1,169 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+require File.expand_path(File.dirname(__FILE__) + '/alaveteli_dsl')
describe "When viewing requests" do
- before(:each) do
- load_raw_emails_data
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ @unregistered = without_login
end
it "should not make endlessly recursive JSON <link>s" do
- @dog_request = info_requests(:fancy_dog_request)
- get "request/#{@dog_request.url_title}?unfold=1"
- response.body.should_not include("dog?unfold=1.json")
- response.body.should include("dog.json?unfold=1")
+ @unregistered.browses_request("#{@info_request.url_title}?unfold=1")
+ @unregistered.response.body.should_not include("#{@info_request.url_title}?unfold=1.json")
+ @unregistered.response.body.should include("#{@info_request.url_title}.json?unfold=1")
+ end
+
+ it 'should not raise a routing error when making a json link for a request with an
+ "action" querystring param' do
+ @unregistered.browses_request("#{@info_request.url_title}?action=add")
+ end
+
+ context 'when a response has prominence "normal"' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request_with_incoming)
+ end
+
+ it 'should show the message itself to any user' do
+
+ # unregistered
+ unregistered = without_login
+ unregistered.browses_request(@info_request.url_title)
+ unregistered.response.body.should include("hereisthetext")
+ unregistered.response.body.should_not include("This message has been hidden.")
+ unregistered.response.body.should_not include("sign in</a> to view the message.")
+
+ # requester
+ owner = login(@info_request.user)
+ owner.browses_request(@info_request.url_title)
+ owner.response.body.should include("hereisthetext")
+ owner.response.body.should_not include("This message has been hidden.")
+
+ # admin
+ admin_user = login(FactoryGirl.create(:admin_user))
+ admin_user.browses_request(@info_request.url_title)
+ admin_user.response.body.should include("hereisthetext")
+ admin_user.response.body.should_not include("This message has prominence \'hidden\'.")
+
+ end
+
+ end
+
+ context 'when a response has prominence "hidden"' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request_with_incoming)
+ message = @info_request.incoming_messages.first
+ message.prominence = 'hidden'
+ message.prominence_reason = 'It is too irritating.'
+ message.save!
+ end
+
+ it 'should show a hidden notice, not the message, to an unregistered user or the requester and
+ the message itself to an admin ' do
+
+ # unregistered
+ unregistered = without_login
+ unregistered.browses_request(@info_request.url_title)
+ unregistered.response.body.should include("This message has been hidden.")
+ unregistered.response.body.should include("It is too irritating.")
+ unregistered.response.body.should_not include("sign in</a> to view the message.")
+ unregistered.response.body.should_not include("hereisthetext")
+
+ # requester
+ owner = login(@info_request.user)
+ owner.browses_request(@info_request.url_title)
+ owner.response.body.should include("This message has been hidden.")
+ owner.response.body.should include("It is too irritating")
+ owner.response.body.should_not include("hereisthetext")
+
+ # admin
+ admin_user = login(FactoryGirl.create(:admin_user))
+ admin_user.browses_request(@info_request.url_title)
+ admin_user.response.body.should include('hereisthetext')
+ admin_user.response.body.should include("This message has prominence \'hidden\'.")
+ admin_user.response.body.should include("It is too irritating.")
+ admin_user.response.body.should include("You can only see it because you are logged in as a super user.")
+
+ end
+
+ end
+
+ context 'when a response has prominence "requester_only"' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request_with_incoming)
+ message = @info_request.incoming_messages.first
+ message.prominence = 'requester_only'
+ message.prominence_reason = 'It is too irritating.'
+ message.save!
+ end
+
+ it 'should show a hidden notice with login link to an unregistered user, and the message itself
+ with a hidden note to the requester or an admin' do
+
+ # unregistered
+ unregistered = without_login
+ unregistered.browses_request(@info_request.url_title)
+ unregistered.response.body.should include("This message has been hidden.")
+ unregistered.response.body.should include("It is too irritating")
+ unregistered.response.body.should include("sign in</a> to view the message.")
+ unregistered.response.body.should_not include("hereisthetext")
+
+ # requester
+ owner = login(@info_request.user)
+ owner.browses_request(@info_request.url_title)
+ owner.response.body.should include("hereisthetext")
+ owner.response.body.should include("This message is hidden, so that only you, the requester, can see it.")
+ owner.response.body.should include("It is too irritating.")
+
+ # admin
+ admin_user = login(FactoryGirl.create(:admin_user))
+ admin_user.browses_request(@info_request.url_title)
+ admin_user.response.body.should include('hereisthetext')
+ admin_user.response.body.should_not include("This message has been hidden.")
+ admin_user.response.body.should include("This message is hidden, so that only you, the requester, can see it.")
+ end
+
+ end
+
+ context 'when an outgoing message has prominence "requester_only"' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ message = @info_request.outgoing_messages.first
+ message.prominence = 'requester_only'
+ message.prominence_reason = 'It is too irritating.'
+ message.save!
+ end
+
+ it 'should show a hidden notice with login link to an unregistered user, and the message itself
+ with a hidden note to the requester or an admin' do
+
+ # unregistered
+ unregistered = without_login
+ unregistered.browses_request(@info_request.url_title)
+ unregistered.response.body.should include("This message has been hidden.")
+ unregistered.response.body.should include("It is too irritating")
+ unregistered.response.body.should include("sign in</a> to view the message.")
+ unregistered.response.body.should_not include("Some information please")
+
+ # requester
+ owner = login(@info_request.user)
+ owner.browses_request(@info_request.url_title)
+ owner.response.body.should include("Some information please")
+ owner.response.body.should include("This message is hidden, so that only you, the requester, can see it.")
+ owner.response.body.should include("It is too irritating.")
+
+ # admin
+ admin_user = login(FactoryGirl.create(:admin_user))
+ admin_user.browses_request(@info_request.url_title)
+ admin_user.response.body.should include('Some information please')
+ admin_user.response.body.should_not include("This message has been hidden.")
+ admin_user.response.body.should include("This message is hidden, so that only you, the requester, can see it.")
+ end
+
end
end
diff --git a/spec/lib/ability_spec.rb b/spec/lib/ability_spec.rb
new file mode 100644
index 000000000..f075d0f32
--- /dev/null
+++ b/spec/lib/ability_spec.rb
@@ -0,0 +1,51 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe Ability do
+ describe ".can_update_request_state?" do
+ context "old and unclassified request" do
+ let(:request) { mock_model(InfoRequest, :is_old_unclassified? => true) }
+
+ context "logged out" do
+ let(:user) { nil }
+ before(:each) { request.stub!(:is_owning_user?).and_return(false) }
+ it { Ability::can_update_request_state?(user, request).should be_false }
+ end
+
+ context "logged in but not owner of request" do
+ let(:user) { mock_model(User) }
+ before(:each) { request.stub!(:is_owning_user?).and_return(false) }
+
+ it { Ability::can_update_request_state?(user, request).should be_true }
+ end
+ end
+
+ context "new request" do
+ let(:request) { mock_model(InfoRequest, :is_old_unclassified? => false) }
+
+ context "logged out" do
+ let(:user) { nil }
+ before(:each) { request.stub!(:is_owning_user?).and_return(false) }
+
+ it { Ability::can_update_request_state?(user, request).should be_false }
+ end
+
+ context "logged in" do
+ let(:user) { mock_model(User) }
+
+ # An owner of a request can also be someone with admin powers
+ context "as owner of request" do
+ before(:each) { request.stub!(:is_owning_user?).and_return(true) }
+
+ it { Ability::can_update_request_state?(user, request).should be_true }
+ end
+
+ context "but not owner of request" do
+ before(:each) { request.stub!(:is_owning_user?).and_return(false) }
+
+ it { Ability::can_update_request_state?(user, request).should be_false }
+ end
+ end
+ end
+ end
+end
+
diff --git a/spec/lib/alaveteli_external_command.rb b/spec/lib/alaveteli_external_command.rb
new file mode 100644
index 000000000..18afeda33
--- /dev/null
+++ b/spec/lib/alaveteli_external_command.rb
@@ -0,0 +1,23 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+require 'alaveteli_external_command'
+
+script_dir = File.join(File.dirname(__FILE__), 'alaveteli_external_command_scripts')
+segfault_script = File.join(script_dir, 'segfault.sh')
+error_script = File.join(script_dir, 'error.sh')
+
+describe "when running external commands" do
+
+ it "should detect a non-zero exit status" do
+ $stderr.should_receive(:puts).with(/Error from/)
+ t = AlaveteliExternalCommand.run(error_script)
+ assert_nil t
+ end
+
+ it "should detect when an external command crashes" do
+ $stderr.should_receive(:puts).with(/exited abnormally/)
+ t = AlaveteliExternalCommand.run(segfault_script)
+ assert_nil t
+ end
+
+end
diff --git a/spec/lib/alaveteli_external_command_scripts/error.sh b/spec/lib/alaveteli_external_command_scripts/error.sh
new file mode 100755
index 000000000..65e74b3c6
--- /dev/null
+++ b/spec/lib/alaveteli_external_command_scripts/error.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo "this is my error message" >&1
+exit 1
diff --git a/spec/lib/alaveteli_external_command_scripts/segfault.sh b/spec/lib/alaveteli_external_command_scripts/segfault.sh
new file mode 100755
index 000000000..f96ba5be8
--- /dev/null
+++ b/spec/lib/alaveteli_external_command_scripts/segfault.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+kill -11 $$
diff --git a/spec/lib/basic_encoding_tests.rb b/spec/lib/basic_encoding_tests.rb
new file mode 100644
index 000000000..35d35fd4a
--- /dev/null
+++ b/spec/lib/basic_encoding_tests.rb
@@ -0,0 +1,157 @@
+# -*- coding: utf-8 -*-
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+def bytes_to_binary_string( bytes, claimed_encoding = nil )
+ claimed_encoding ||= 'ASCII-8BIT'
+ bytes_string = bytes.pack('c*')
+ if RUBY_VERSION.to_f >= 1.9
+ bytes_string.force_encoding! claimed_encoding
+ end
+ bytes_string
+end
+
+random_string = bytes_to_binary_string [ 0x0f, 0x58, 0x1c, 0x8f, 0xa4, 0xcf,
+ 0xf6, 0x8c, 0x9d, 0xa7, 0x06, 0xd9,
+ 0xf7, 0x90, 0x6c, 0x6f]
+
+windows_1252_string = bytes_to_binary_string [ 0x44, 0x41, 0x53, 0x48, 0x20,
+ 0x96, 0x20, 0x44, 0x41, 0x53,
+ 0x48 ]
+
+# It's a shame this example is so long, but if we don't take enough it
+# gets misinterpreted as Shift_JIS
+
+gb_18030_bytes = [ 0xb9, 0xf3, 0xb9, 0xab, 0xcb, 0xbe, 0xb8, 0xba, 0xd4, 0xf0,
+ 0xc8, 0xcb, 0x28, 0xbe, 0xad, 0xc0, 0xed, 0x2f, 0xb2, 0xc6,
+ 0xce, 0xf1, 0x29, 0xc4, 0xfa, 0xba, 0xc3, 0xa3, 0xba, 0x0d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0xb1, 0xbe, 0xb9, 0xab, 0xcb, 0xbe, 0xd4,
+ 0xda, 0x31, 0x39, 0x39, 0x37, 0xc4, 0xea, 0xb3, 0xc9, 0xc1,
+ 0xa2, 0xb9, 0xfa, 0xbc, 0xd2, 0xb9, 0xa4, 0xc9, 0xcc, 0xd7,
+ 0xa2, 0xb2, 0xe1, 0x2e, 0xca, 0xb5, 0xc1, 0xa6, 0xd0, 0xdb,
+ 0xba, 0xf1, 0xa1, 0xa3, 0xd3, 0xd0, 0xb6, 0xc0, 0xc1, 0xa2,
+ 0xcb, 0xb0, 0xce, 0xf1, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xd7, 0xa8, 0xd2, 0xb5,
+ 0xc8, 0xcb, 0xd4, 0xb1, 0x3b, 0xd4, 0xda, 0xc8, 0xab, 0xb9,
+ 0xfa, 0xb8, 0xf7, 0xb3, 0xc7, 0xca, 0xd0, 0xc9, 0xe8, 0xc1,
+ 0xa2, 0xb7, 0xd6, 0xb9, 0xab, 0xcb, 0xbe, 0xa3, 0xa8, 0xd5,
+ 0xe3, 0xbd, 0xad, 0xa1, 0xa2, 0xc9, 0xcf, 0xba, 0xa3, 0xa1,
+ 0xa2, 0xb9, 0xe3, 0xd6, 0xdd, 0xa1, 0xa2, 0xbd, 0xad, 0xcb,
+ 0xd5, 0xb5, 0xc8, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0xb5, 0xd8, 0xb7, 0xbd, 0xa3,
+ 0xa9, 0xd2, 0xf2, 0xbd, 0xf8, 0xcf, 0xee, 0xbd, 0xcf, 0xb6,
+ 0xe0, 0xcf, 0xd6, 0xcd, 0xea, 0xb3, 0xc9, 0xb2, 0xbb, 0xc1,
+ 0xcb, 0xc3, 0xbf, 0xd4, 0xc2, 0xcf, 0xfa, 0xca, 0xdb, 0xb6,
+ 0xee, 0xb6, 0xc8, 0xa1, 0xa3, 0xc3, 0xbf, 0xd4, 0xc2, 0xd3,
+ 0xd0, 0xd2, 0xbb, 0xb2, 0xbf, 0xb7, 0xd6, 0x0d, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xd4,
+ 0xf6, 0xd6, 0xb5, 0xb6, 0x90, 0xa3, 0xa8, 0x36, 0x2d, 0x37,
+ 0x25, 0xd7, 0xf3, 0xd3, 0xd2, 0x29, 0xba, 0xcd, 0xc6, 0xd5,
+ 0xc6, 0xb1, 0xa3, 0xa8, 0x30, 0x2e, 0x35, 0x25, 0x2d, 0x32,
+ 0x25, 0x20, 0xd7, 0xf3, 0xd3, 0xd2, 0xa3, 0xa9, 0xd3, 0xc5,
+ 0xbb, 0xdd, 0xb4, 0xfa, 0xbf, 0xaa, 0xbb, 0xf2, 0xba, 0xcf,
+ 0xd7, 0xf7, 0xa3, 0xac, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb5, 0xe3, 0xca, 0xfd,
+ 0xbd, 0xcf, 0xb5, 0xcd, 0xa1, 0xa3, 0xb4, 0xfa, 0xc0, 0xed,
+ 0xb7, 0xb6, 0xce, 0xa7, 0xc8, 0xe7, 0xcf, 0xc2, 0xa3, 0xba,
+ 0x0d, 0x0a ]
+
+gb_18030_spam_string = bytes_to_binary_string gb_18030_bytes
+
+describe "normalize_string_to_utf8" do
+
+ describe "when passed uniterpretable character data" do
+
+ it "should reject it as invalid" do
+
+ expect {
+ normalize_string_to_utf8 random_string
+ }.to raise_error(EncodingNormalizationError)
+
+ expect {
+ normalize_string_to_utf8 random_string, 'UTF-8'
+ }.to raise_error(EncodingNormalizationError)
+
+ end
+ end
+
+ describe "when passed unlabelled Windows 1252 data" do
+
+ it "should correctly convert it to UTF-8" do
+
+ normalized = normalize_string_to_utf8 windows_1252_string
+
+ normalized.should == "DASH – DASH"
+
+ end
+
+ end
+
+ describe "when passed GB 18030 data" do
+
+ it "should correctly convert it to UTF-8 if unlabelled" do
+
+ normalized = normalize_string_to_utf8 gb_18030_spam_string
+
+ normalized.should start_with("贵公司负责人")
+
+ end
+
+ end
+
+end
+
+describe "convert_string_to_utf8_or_binary" do
+
+ describe "when passed uniterpretable character data" do
+
+ it "should return it as a binary string" do
+
+ converted = convert_string_to_utf8_or_binary random_string
+ converted.should == random_string
+
+ if RUBY_VERSION.to_f >= 1.9
+ converted.encoding.should == 'ASCII-8BIT'
+ end
+
+ converted = convert_string_to_utf8_or_binary random_string,'UTF-8'
+ converted.should == random_string
+
+ if RUBY_VERSION.to_f >= 1.9
+ converted.encoding.should == 'ASCII-8BIT'
+ end
+
+ end
+ end
+
+ describe "when passed unlabelled Windows 1252 data" do
+
+ it "should correctly convert it to UTF-8" do
+
+ converted = convert_string_to_utf8_or_binary windows_1252_string
+
+ converted.should == "DASH – DASH"
+
+ if RUBY_VERSION.to_f >= 1.9
+ converted.encoding.should == 'UTF-8'
+ end
+ end
+
+ end
+
+ describe "when passed GB 18030 data" do
+
+ it "should correctly convert it to UTF-8 if unlabelled" do
+
+ converted = convert_string_to_utf8_or_binary gb_18030_spam_string
+
+ converted.should start_with("贵公司负责人")
+
+ if RUBY_VERSION.to_f >= 1.9
+ converted.encoding.should == 'UTF-8'
+ end
+ end
+
+ end
+
+end
diff --git a/spec/lib/confidence_intervals.rb b/spec/lib/confidence_intervals.rb
new file mode 100644
index 000000000..cb8717f3d
--- /dev/null
+++ b/spec/lib/confidence_intervals.rb
@@ -0,0 +1,30 @@
+require 'confidence_intervals'
+
+describe "ci_bounds" do
+
+ describe "when passed all successes" do
+ it "should never return a high CI above 1" do
+ ci = ci_bounds 16, 16, 0.01
+ ci[1].should be <= 1
+ end
+ end
+
+ describe "when passed all failures" do
+ it "should never return a low CI below 0" do
+ ci = ci_bounds 0, 10, 0.05
+ ci[0].should be >= 0
+ end
+ end
+
+ describe "when passed 4 out of 10 successes (with 0.05 power)" do
+ it "should return the correct Wilson's interval" do
+ # The expected results here were taken from an online
+ # calculator:
+ # http://www.vassarstats.net/prop1.html
+ ci = ci_bounds 7, 10, 0.05
+ ci[0].should be_within(0.001).of(0.3968)
+ ci[1].should be_within(0.001).of(0.8922)
+ end
+ end
+
+end
diff --git a/spec/lib/i18n_interpolation.rb b/spec/lib/i18n_interpolation.rb
index e8d046757..b07cf1e9a 100644
--- a/spec/lib/i18n_interpolation.rb
+++ b/spec/lib/i18n_interpolation.rb
@@ -12,7 +12,35 @@ describe "when using i18n" do
it "should assume that simple translations are always html safe" do
_("Hello").should be_html_safe
end
+end
+
+describe "n_" do
+ it "should return the translated singular" do
+ FastGettext.should_receive(:n_).with("Apple", "Apples", 1).and_return("Apfel")
+ n_("Apple", "Apples", 1).should == "Apfel"
+ end
+
+ it "should return the translated plural" do
+ FastGettext.should_receive(:n_).with("Apple", "Apples", 3).and_return("Äpfel")
+ n_("Apple", "Apples", 3).should == "Äpfel"
+ end
+ it "should return the translated singular interpolated" do
+ FastGettext.should_receive(:n_).with("I eat {{count}} apple", "I eat {{count}} apples", 1).
+ and_return("Ich esse {{count}} Apfel")
+ n_("I eat {{count}} apple", "I eat {{count}} apples", 1, :count => 1).should == "Ich esse 1 Apfel"
+ end
+
+ it "should return the translated plural interpolated" do
+ FastGettext.should_receive(:n_).with("I eat {{count}} apple", "I eat {{count}} apples", 3).
+ and_return("Ich esse {{count}} Äpfel")
+ n_("I eat {{count}} apple", "I eat {{count}} apples", 3, :count => 3).should == "Ich esse 3 Äpfel"
+ end
+
+ it "should always be html safe when there is no interpolation" do
+ FastGettext.should_receive(:n_).with("Apple", "Apples", 1).and_return("Apfel")
+ n_("Apple", "Apples", 1).should be_html_safe
+ end
end
describe "gettext_interpolate" do
diff --git a/spec/lib/mail_handler/mail_handler_spec.rb b/spec/lib/mail_handler/mail_handler_spec.rb
index 48c32e2bc..ffc40ced9 100644
--- a/spec/lib/mail_handler/mail_handler_spec.rb
+++ b/spec/lib/mail_handler/mail_handler_spec.rb
@@ -9,23 +9,74 @@ end
describe 'when creating a mail object from raw data' do
+ it "should be able to parse a large email without raising an exception", :focus => true do
+ m = Mail.new
+ m.add_file(:filename => "attachment.data", :content => "a" * (8 * 1024 * 1024))
+ raw_email = "From jamis_buck@byu.edu Mon May 2 16:07:05 2005\r\n#{m.to_s}"
+ lambda { Mail::Message.new(raw_email) }.should_not raise_error
+ end
+
it 'should correctly parse a multipart email with a linebreak in the boundary' do
mail = get_fixture_mail('space-boundary.email')
mail.parts.size.should == 2
mail.multipart?.should == true
end
+ it "should not fail on invalid byte sequence in content-disposition header", :focus => true do
+ part = Mail::Part.new("Content-Disposition: inline; filename=a\xB8z\r\n\r\nThis is the body text.")
+ lambda { part.inline? }.should_not raise_error
+ end
+
it 'should parse multiple to addresses with unqoted display names' do
mail = get_fixture_mail('multiple-unquoted-display-names.email')
mail.to.should == ["request-66666-caa77777@whatdotheyknow.com", "foi@example.com"]
end
+ it 'should return nil for malformed To: and Cc: lines' do
+ mail = get_fixture_mail('malformed-to-and-cc.email')
+ mail.to.should == nil
+ mail.cc.should == nil
+ end
+
it 'should convert an iso8859 email to utf8' do
mail = get_fixture_mail('iso8859_2_raw_email.email')
- mail.subject.should have_text(/gjatë/u)
+ mail.subject.should match /gjatë/u
MailHandler.get_part_body(mail).is_utf8?.should == true
end
+ it 'should not be confused by subject lines with malformed UTF-8 at the end' do
+ # The base64 subject line was generated with:
+ # printf "hello\360" | base64
+ # ... and wrapping the result in '=?UTF-8?B?' and '?='
+ mail = get_fixture_mail('subject-bad-utf-8-trailing-base64.email')
+ mail.subject.should == 'hello'
+ # The quoted printable subject line was generated with:
+ # printf "hello\360" | qprint -b -e
+ # ... and wrapping the result in '=?UTF-8?Q?' and '?='
+ mail = get_fixture_mail('subject-bad-utf-8-trailing-quoted-printable.email')
+ mail.subject.should == 'hello'
+ end
+
+ it 'should convert a Windows-1252 body mislabelled as ISO-8859-1 to UTF-8' do
+ mail = get_fixture_mail('mislabelled-as-iso-8859-1.email')
+ body = MailHandler.get_part_body(mail)
+ body.is_utf8?.should == true
+ # This email is broken in at least these two ways:
+ # 1. It contains a top bit set character (0x96) despite the
+ # "Content-Transfer-Encoding: 7bit"
+ # 2. The charset in the Content-Type header is "iso-8859-1"
+ # but 0x96 is actually a Windows-1252 en dash, which would
+ # be Unicode codepoint 2013. It should be possible to
+ # spot the mislabelling, since 0x96 isn't a valid
+ # ISO-8859-1 character.
+ body.should match(/ \xe2\x80\x93 /)
+ end
+
+ it 'should not error on a subject line with an encoding encoding not recognized by iconv' do
+ mail = get_fixture_mail('unrecognized-encoding-mail.email')
+ lambda{ mail.subject }.should_not raise_error
+ end
+
end
describe 'when asked for the from name' do
@@ -275,6 +326,12 @@ end
describe 'when getting attachment attributes' do
+ it 'should handle a mail with a non-multipart part with no charset in the Content-Type header' do
+ mail = get_fixture_mail('part-without-charset-in-content-type.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes.size.should == 2
+ end
+
it 'should get two attachment parts from a multipart mail with text and html alternatives
and an image' do
mail = get_fixture_mail('quoted-subject-iso8859-1.email')
@@ -282,6 +339,13 @@ describe 'when getting attachment attributes' do
attributes.size.should == 2
end
+ it 'should get one attachment from a multipart mail with text and HTML alternatives, which should be UTF-8' do
+ mail = get_fixture_mail('iso8859_2_raw_email.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes.length.should == 1
+ attributes[0][:body].is_utf8?.should == true
+ end
+
it 'should expand a mail attached as text' do
# Note that this spec will only pass using Tmail in the timezone set as datetime headers
# are rendered out in the local time - using the Mail gem this is not necessary
@@ -304,6 +368,58 @@ describe 'when getting attachment attributes' do
attributes = MailHandler.get_attachment_attributes(mail)
end
+ it 'should ignore truncated TNEF attachment' do
+ mail = get_fixture_mail('tnef-attachment-truncated.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes.length.should == 2
+ end
+
+ it 'should ignore anything beyond the final MIME boundary' do
+ pending do
+ # This example raw email has a premature closing boundary for
+ # the outer multipart/mixed - my reading of RFC 1521 is that
+ # the "epilogue" beyond that should be ignored.
+ # See https://github.com/mysociety/alaveteli/issues/922 for
+ # more discussion.
+ mail = get_fixture_mail('nested-attachments-premature-end.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes.length.should == 3
+ end
+ end
+
+ it 'should cope with a missing final MIME boundary' do
+ mail = get_fixture_mail('multipart-no-final-boundary.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes.length.should == 1
+ attributes[0][:body].should match(/This is an acknowledgement of your email/)
+ attributes[0][:content_type].should == "text/plain"
+ attributes[0][:url_part_number].should == 1
+ end
+
+ it 'should ignore a TNEF attachment with no usable contents' do
+ # FIXME: "no usable contents" is slightly misleading. The
+ # attachment in this example email does have usable content in
+ # the body of the TNEF attachment, but the invocation of tnef
+ # historically used to unpack these attachments doesn't add
+ # the --save-body parameter, so that they have been ignored so
+ # far. We probably should include the body from such
+ # attachments, but, at the moment, with the pending upgrade to
+ # Rails 3, we just want to check that the behaviour is the
+ # same as before.
+ mail = get_fixture_mail('tnef-attachment-empty.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes.length.should == 2
+ # This is the size of the TNEF-encoded attachment; currently,
+ # we expect the code just to return this without decoding:
+ attributes[1][:body].length.should == 7769
+ end
+
+ it 'should treat a document/pdf attachment as application/pdf' do
+ mail = get_fixture_mail('document-pdf.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes[1][:content_type].should == "application/pdf"
+ end
+
it 'should produce a consistent set of url_part_numbers, content_types, within_rfc822_subjects
and filenames from an example mail with lots of attachments' do
mail = get_fixture_mail('many-attachments-date-header.email')
@@ -385,3 +501,11 @@ describe 'when getting attachment attributes' do
end
end
end
+
+describe 'when getting the address part from an address string' do
+
+ it 'should handle non-ascii characters in the name input' do
+ address = "\"Someone’s name\" <test@example.com>"
+ MailHandler.address_from_string(address).should == 'test@example.com'
+ end
+end
diff --git a/spec/lib/sendmail_return_path_spec.rb b/spec/lib/sendmail_return_path_spec.rb
deleted file mode 100644
index 137869b6e..000000000
--- a/spec/lib/sendmail_return_path_spec.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-# This is a test of the monkey patches in sendmail_return_path.rb
-
-require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-
-describe "when sending email with an altered return path" do
- before(:each) { ActionMailer::Base.deliveries = [] }
-
- it "should default to delivery method test" do
- ActionMailer::Base.delivery_method.should == :test
- end
-
- it "should let the helper change the method" do
- with_delivery_method :smtp do
- ActionMailer::Base.delivery_method.should == :smtp
- end
- ActionMailer::Base.delivery_method.should == :test
- end
-
- # Documentation for fancy mock functions: http://rspec.info/documentation/mocks/message_expectations.html
- it "should set the return path when sending email using SMTP" do
- mock_smtp = mock("smtp")
- mock_smtp_session = mock("smtp_session")
-
- mock_smtp.should_receive(:start).once.and_yield(mock_smtp_session)
- # the second parameter to the SMTP session is the sender (return path)
- mock_smtp_session.should_receive(:sendmail).once.with(anything(), "test@localhost", anything())
-
- Net::SMTP.stub!(:new).and_return(mock_smtp)
-
- with_delivery_method :smtp do
- ContactMailer.deliver_to_admin_message(
- "Mr. Test", "test@localhost", "Test script spec/lib/sendmail_return_path_spec.rb",
- "This is just a test for a test script", nil, nil, nil
- )
- end
-
- deliveries = ActionMailer::Base.deliveries
- deliveries.size.should == 0
- end
-
- it "should set the return path when sending email using sendmail" do
- with_stub_popen do
- IO.should_receive(:popen).once.with('/usr/sbin/sendmail -i -t -f "test@localhost"', "w+")
- with_delivery_method :sendmail do
- ContactMailer.deliver_to_admin_message(
- "Mr. Test", "test@localhost", "Test script spec/lib/sendmail_return_path_spec.rb",
- "This is just a test for a test script", nil, nil, nil
- )
- end
- end
-
- deliveries = ActionMailer::Base.deliveries
- deliveries.size.should == 0
- end
-
-
- protected
- # Change the way Rails delivers memory, just for current scope
- def with_delivery_method(new_delivery_method)
- old_delivery_method, ActionMailer::Base.delivery_method = ActionMailer::Base.delivery_method, new_delivery_method
- yield
- ensure
- ActionMailer::Base.delivery_method = old_delivery_method
- end
-
- # By default, we can't stub popen, presumably because it is a builtin written in C.
- # Replace it entirely with a normal method that just calls the C one, so we can stub it -
- # this leaves IO working afterwards (for other tests that run in the same instance).
- def with_stub_popen()
- IO.class_eval "@orig_popen = self.method(:popen); def self.popen(a, b, &c); @orig_popen.call(a, b, &c); end"
- begin
- yield
- ensure
- # in theory would undo the popen alterations and return IO to a pristine state, but
- # don't know how to (much fiddling with alias bind and the like didn't help). It
- # doesn't matter - the new popen should behave just the same.
- end
- end
-
-
-end
-
-
diff --git a/spec/lib/theme_spec.rb b/spec/lib/theme_spec.rb
new file mode 100644
index 000000000..829c1a269
--- /dev/null
+++ b/spec/lib/theme_spec.rb
@@ -0,0 +1,25 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe "theme_url_to_theme_name" do
+
+ it "should deal with a typical bare repo URL" do
+ url = 'git://wherever/blah-theme.git'
+ theme_url_to_theme_name(url).should == 'blah-theme'
+ end
+
+ it "should deal with a typical bare repo URL with trailing slashes" do
+ url = 'ssh://wherever/blah-theme.git//'
+ theme_url_to_theme_name(url).should == 'blah-theme'
+ end
+
+ it "should deal with a typical non-bare repo URL" do
+ url = '/home/whoever/themes/blah-theme'
+ theme_url_to_theme_name(url).should == 'blah-theme'
+ end
+
+ it "should deal with a typical non-bare repo URL with a trailing slash" do
+ url = '/home/whoever/themes/blah-theme/'
+ theme_url_to_theme_name(url).should == 'blah-theme'
+ end
+
+end
diff --git a/spec/lib/timezone_fixes_spec.rb b/spec/lib/timezone_fixes_spec.rb
index 9d6ade526..8a9a3bf31 100644
--- a/spec/lib/timezone_fixes_spec.rb
+++ b/spec/lib/timezone_fixes_spec.rb
@@ -3,6 +3,11 @@
# We use MailServerLogDone here just as a totally random model that has a datetime type.
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+# In Rails 3 the monkeypatch that these tests are testing is not necessary. So,
+# since these tests are testing the Rails internals you could argue that they shouldn't
+# be here. Well, you're right. But let's leave them in for the time being until the upgrade is finished.
+# Then, we should probably delete this whole file
+
describe "when doing things with timezones" do
it "should preserve time objects with local time conversion to default timezone UTC
diff --git a/spec/lib/whatdotheyknow/strip_empty_sessions_spec.rb b/spec/lib/whatdotheyknow/strip_empty_sessions_spec.rb
index 9bd5ccb93..fcd729b48 100644
--- a/spec/lib/whatdotheyknow/strip_empty_sessions_spec.rb
+++ b/spec/lib/whatdotheyknow/strip_empty_sessions_spec.rb
@@ -1,71 +1,71 @@
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
describe WhatDoTheyKnow::StripEmptySessions do
-
+
def make_response(session_data, response_headers)
app = lambda do |env|
env['rack.session'] = session_data
- return [200, response_headers, ['content']]
+ return [200, response_headers, ['content']]
end
strip_empty_sessions = WhatDoTheyKnow::StripEmptySessions
app = strip_empty_sessions.new(app, {:key => 'mykey', :path => '', :httponly => true})
response = Rack::MockRequest.new(app).get('/', 'HTTP_ACCEPT' => 'text/html')
end
-
- it 'should not prevent a cookie being set if there is data in the session' do
- session_data = { :some_real_data => 'important',
- :session_id => 'my_session_id',
- :_csrf_token => 'hi_there' }
- application_response_headers = { 'Content-Type' => 'text/html',
+
+ it 'should not prevent a cookie being set if there is data in the session' do
+ session_data = { 'some_real_data' => 'important',
+ 'session_id' => 'my_session_id',
+ '_csrf_token' => 'hi_there' }
+ application_response_headers = { 'Content-Type' => 'text/html',
'Set-Cookie' => 'mykey=f274c61a35320c52d45e9f8d7d4e2649; path=/; HttpOnly'}
response = make_response(session_data, application_response_headers)
response.headers['Set-Cookie'].should == 'mykey=f274c61a35320c52d45e9f8d7d4e2649; path=/; HttpOnly'
end
- describe 'if there is no meaningful data in the session' do
+ describe 'if there is no meaningful data in the session' do
- before do
- @session_data = { :session_id => 'my_session_id',
- :_csrf_token => 'hi_there' }
+ before do
+ @session_data = { 'session_id' => 'my_session_id',
+ '_csrf_token' => 'hi_there' }
end
-
- it 'should not strip any other header' do
+
+ it 'should not strip any other header' do
application_response_headers = { 'Content-Type' => 'text/html',
'Set-Cookie' => 'mykey=f274c61a35320c52d45e9f8d7d4e2649; path=/; HttpOnly'}
response = make_response(@session_data, application_response_headers)
response.headers['Content-Type'].should == 'text/html'
end
-
- it 'should strip the session cookie setting header ' do
- application_response_headers = { 'Content-Type' => 'text/html',
+
+ it 'should strip the session cookie setting header ' do
+ application_response_headers = { 'Content-Type' => 'text/html',
'Set-Cookie' => 'mykey=f274c61a35320c52d45e9f8d7d4e2649; path=/; HttpOnly'}
response = make_response(@session_data, application_response_headers)
response.headers['Set-Cookie'].should == ""
end
-
- it 'should strip the session cookie setting header even with a locale' do
- @session_data[:locale] = 'en'
- application_response_headers = { 'Content-Type' => 'text/html',
+
+ it 'should strip the session cookie setting header even with a locale' do
+ @session_data['locale'] = 'en'
+ application_response_headers = { 'Content-Type' => 'text/html',
'Set-Cookie' => 'mykey=f274c61a35320c52d45e9f8d7d4e2649; path=/; HttpOnly'}
response = make_response(@session_data, application_response_headers)
response.headers['Set-Cookie'].should == ""
end
- it 'should not strip the session cookie setting for admins' do
- @session_data[:using_admin] = 1
- application_response_headers = { 'Content-Type' => 'text/html',
+ it 'should not strip the session cookie setting for admins' do
+ @session_data['using_admin'] = 1
+ application_response_headers = { 'Content-Type' => 'text/html',
'Set-Cookie' => 'mykey=f274c61a35320c52d45e9f8d7d4e2649; path=/; HttpOnly'}
response = make_response(@session_data, application_response_headers)
response.headers['Set-Cookie'].should == "mykey=f274c61a35320c52d45e9f8d7d4e2649; path=/; HttpOnly"
end
-
- it 'should strip the session cookie setting header (but no other cookie setting header) if there is more than one' do
- application_response_headers = { 'Content-Type' => 'text/html',
+
+ it 'should strip the session cookie setting header (but no other cookie setting header) if there is more than one' do
+ application_response_headers = { 'Content-Type' => 'text/html',
'Set-Cookie' => ['mykey=f274c61a35320c52d45e9f8d7d4e2649; path=/; HttpOnly',
'other=mydata']}
response = make_response(@session_data, application_response_headers)
response.headers['Set-Cookie'].should == ['other=mydata']
end
-
+
end
end
diff --git a/spec/models/application_mailer_spec.rb b/spec/mailers/application_mailer_spec.rb
index acf5f43bc..718ac47fb 100644
--- a/spec/models/application_mailer_spec.rb
+++ b/spec/mailers/application_mailer_spec.rb
@@ -8,13 +8,12 @@ describe ApplicationMailer do
def set_base_views
ApplicationMailer.class_eval do
@previous_view_paths = self.view_paths.dup
- self.view_paths.clear
- self.view_paths << File.join(Rails.root, 'spec', 'fixtures', 'theme_views', 'core')
+ self.view_paths = [File.join(Rails.root, 'spec', 'fixtures', 'theme_views', 'core')]
end
end
def add_mail_methods(method_names)
- method_names.each{ |method_name| ApplicationMailer.send(:define_method, method_name){} }
+ method_names.each{ |method_name| ApplicationMailer.send(:define_method, method_name){ mail() } }
end
def remove_mail_methods(method_names)
@@ -27,13 +26,13 @@ describe ApplicationMailer do
def prepend_theme_views(theme_name)
ApplicationMailer.class_eval do
- view_paths.unshift File.join(Rails.root, 'spec', 'fixtures', 'theme_views', theme_name)
+ prepend_view_path File.join(Rails.root, 'spec', 'fixtures', 'theme_views', theme_name)
end
end
def append_theme_views(theme_name)
ApplicationMailer.class_eval do
- view_paths << File.join(Rails.root, 'spec', 'fixtures', 'theme_views', theme_name)
+ append_view_path File.join(Rails.root, 'spec', 'fixtures', 'theme_views', theme_name)
end
end
@@ -45,11 +44,8 @@ describe ApplicationMailer do
def create_multipart_method(method_name)
ApplicationMailer.send(:define_method, method_name) do
- attachment :content_type => 'message/rfc822',
- :body => 'xxx',
- :filename => "original.eml",
- :transfer_encoding => '7bit',
- :content_disposition => 'inline'
+ attachments['original.eml'] = 'xxx'
+ mail
end
end
@@ -62,32 +58,32 @@ describe ApplicationMailer do
it 'should render a theme template in preference to a core template' do
prepend_theme_views('theme_one')
- @mail = ApplicationMailer.create_simple()
+ @mail = ApplicationMailer.simple
@mail.body.should match('Theme simple')
end
it 'should render the template provided by the theme if no template is available in core' do
prepend_theme_views('theme_one')
- @mail = ApplicationMailer.create_theme_only()
+ @mail = ApplicationMailer.theme_only
@mail.body.should match('Theme only')
end
it 'should render the template provided by core if there is no theme template' do
prepend_theme_views('theme_one')
- @mail = ApplicationMailer.create_core_only()
+ @mail = ApplicationMailer.core_only
@mail.body.should match('Core only')
end
- it 'should raise an error if the template is in neither core nor theme' do
+ it 'should render an empty body if the template is in neither core nor theme' do
prepend_theme_views('theme_one')
- expected_error = 'Missing template application_mailer/neither.erb in view path'
- lambda{ ApplicationMailer.create_neither() }.should raise_error(/#{expected_error}/)
+ @mail = ApplicationMailer.neither
+ @mail.body.should be_empty
end
it 'should render a multipart email using a theme template' do
prepend_theme_views('theme_one')
create_multipart_method('multipart_theme_only')
- @mail = ApplicationMailer.create_multipart_theme_only()
+ @mail = ApplicationMailer.multipart_theme_only
@mail.parts.size.should == 2
message_part = @mail.parts[0].to_s
message_part.should match("Theme multipart")
@@ -96,7 +92,7 @@ describe ApplicationMailer do
it 'should render a multipart email using a core template' do
prepend_theme_views('theme_one')
create_multipart_method('multipart_core_only')
- @mail = ApplicationMailer.create_multipart_core_only()
+ @mail = ApplicationMailer.multipart_core_only
@mail.parts.size.should == 2
message_part = @mail.parts[0].to_s
message_part.should match("Core multipart")
@@ -108,32 +104,32 @@ describe ApplicationMailer do
it 'should render a core template in preference to a theme template' do
append_theme_views('theme_one')
- @mail = ApplicationMailer.create_simple()
+ @mail = ApplicationMailer.simple
@mail.body.should match('Core simple')
end
it 'should render the template provided by the theme if no template is available in core' do
append_theme_views('theme_one')
- @mail = ApplicationMailer.create_theme_only()
+ @mail = ApplicationMailer.theme_only
@mail.body.should match('Theme only')
end
it 'should render the template provided by core if there is no theme template' do
append_theme_views('theme_one')
- @mail = ApplicationMailer.create_core_only()
+ @mail = ApplicationMailer.core_only
@mail.body.should match('Core only')
end
- it 'should raise an error if the template is in neither core nor theme' do
+ it 'should render an empty body if the template is in neither core nor theme' do
append_theme_views('theme_one')
- expected_error = 'Missing template application_mailer/neither.erb in view path'
- lambda{ ApplicationMailer.create_neither() }.should raise_error(/#{expected_error}/)
+ @mail = ApplicationMailer.neither
+ @mail.body.should be_empty
end
it 'should render a multipart email using a core template' do
append_theme_views('theme_one')
create_multipart_method('multipart_core_only')
- @mail = ApplicationMailer.create_multipart_core_only()
+ @mail = ApplicationMailer.multipart_core_only
@mail.parts.size.should == 2
message_part = @mail.parts[0].to_s
message_part.should match("Core multipart")
@@ -142,7 +138,7 @@ describe ApplicationMailer do
it 'should render a multipart email using a theme template' do
append_theme_views('theme_one')
create_multipart_method('multipart_theme_only')
- @mail = ApplicationMailer.create_multipart_theme_only()
+ @mail = ApplicationMailer.multipart_theme_only
@mail.parts.size.should == 2
message_part = @mail.parts[0].to_s
message_part.should match("Theme multipart")
diff --git a/spec/mailers/info_request_batch_mailer.rb b/spec/mailers/info_request_batch_mailer.rb
new file mode 100644
index 000000000..19791e163
--- /dev/null
+++ b/spec/mailers/info_request_batch_mailer.rb
@@ -0,0 +1,35 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe InfoRequestBatchMailer do
+
+ describe 'when sending batch sent notification' do
+
+ before do
+ @user = FactoryGirl.create(:user)
+ @info_request_batch = FactoryGirl.create(:info_request_batch)
+ @public_body = FactoryGirl.create(:public_body)
+ @unrequestable = [@public_body]
+ @mail = InfoRequestBatchMailer.batch_sent(@info_request_batch, @unrequestable, @user)
+ end
+
+ it 'renders the subject' do
+ @mail.subject.should == 'Your batch request "Example title" has been sent'
+ end
+
+ it 'renders the receiver email' do
+ @mail.to.should == [@user.email]
+ end
+
+ it 'renders the sender email' do
+ @mail.from.should == ['postmaster@localhost']
+ end
+
+ it 'assigns @unrequestable' do
+ @mail.body.encoded.should match(@public_body.name)
+ end
+
+ it 'assigns @url' do
+ @mail.body.encoded.should match("http://test.host/en/c/")
+ end
+ end
+end
diff --git a/spec/mailers/outgoing_mailer_spec.rb b/spec/mailers/outgoing_mailer_spec.rb
new file mode 100644
index 000000000..a11d56dd3
--- /dev/null
+++ b/spec/mailers/outgoing_mailer_spec.rb
@@ -0,0 +1,133 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe OutgoingMailer, " when working out follow up names and addresses" do
+
+ before do
+ @info_request = mock_model(InfoRequest,
+ :recipient_name_and_email => 'test <test@example.com>',
+ :recipient_email => 'test@example.com')
+ @info_request.stub_chain(:public_body, :name).and_return("Test Authority")
+ @incoming_message = mock_model(IncomingMessage,
+ :from_email => 'specific@example.com',
+ :safe_mail_from => 'Specific Person')
+ end
+
+ def expect_address(info_request, incoming_message, expected_result)
+ mail = create_message_from(from_line)
+ name = MailHandler.get_from_name(mail)
+ email = MailHandler.get_from_address(mail)
+ address = MailHandler.address_from_name_and_email(name, email).to_s
+ [name, email, address].should == expected_result
+ end
+
+ describe 'if there is no incoming message being replied to' do
+
+ it 'should return the name and email address of the public body' do
+ OutgoingMailer.name_and_email_for_followup(@info_request, nil).should == 'test <test@example.com>'
+ OutgoingMailer.name_for_followup(@info_request, nil).should == 'Test Authority'
+ OutgoingMailer.email_for_followup(@info_request, nil).should == 'test@example.com'
+ end
+
+ end
+
+ describe 'if the incoming message being replied to is not valid to reply to' do
+
+ before do
+ @incoming_message.stub!(:valid_to_reply_to?).and_return(false)
+ end
+
+ it 'should return the safe name and email address of the public body' do
+ OutgoingMailer.name_and_email_for_followup(@info_request, @incoming_message).should == 'test <test@example.com>'
+ OutgoingMailer.name_for_followup(@info_request, @incoming_message).should == 'Test Authority'
+ OutgoingMailer.email_for_followup(@info_request, @incoming_message).should == 'test@example.com'
+ end
+ end
+
+ describe 'if the incoming message is valid to reply to' do
+
+ before do
+ @incoming_message.stub!(:valid_to_reply_to?).and_return(true)
+ end
+
+ it 'should return the name and email address from the incoming message' do
+ OutgoingMailer.name_and_email_for_followup(@info_request, @incoming_message).should == 'Specific Person <specific@example.com>'
+ OutgoingMailer.name_for_followup(@info_request, @incoming_message).should == 'Specific Person'
+ OutgoingMailer.email_for_followup(@info_request, @incoming_message).should == 'specific@example.com'
+ end
+
+ it 'should return the name of the public body if the incoming message does not have
+ a safe name' do
+ @incoming_message.stub!(:safe_mail_from).and_return(nil)
+ OutgoingMailer.name_for_followup(@info_request, @incoming_message).should == 'Test Authority'
+ end
+
+ end
+
+end
+
+describe OutgoingMailer, "when working out follow up subjects" do
+
+ before(:each) do
+ load_raw_emails_data
+ end
+
+ it "should prefix the title with 'Freedom of Information request -' for initial requests" do
+ ir = info_requests(:fancy_dog_request)
+ im = ir.incoming_messages[0]
+
+ ir.email_subject_request.should == "Freedom of Information request - Why do you have & such a fancy dog?"
+ end
+
+ it "should use 'Re:' and inital request subject for followups which aren't replies to particular messages" do
+ ir = info_requests(:fancy_dog_request)
+ om = outgoing_messages(:useless_outgoing_message)
+
+ OutgoingMailer.subject_for_followup(ir, om).should == "Re: Freedom of Information request - Why do you have & such a fancy dog?"
+ end
+
+ it "should prefix with Re: the subject of the message being replied to" do
+ ir = info_requests(:fancy_dog_request)
+ im = ir.incoming_messages[0]
+ om = outgoing_messages(:useless_outgoing_message)
+ om.incoming_message_followup = im
+
+ OutgoingMailer.subject_for_followup(ir, om).should == "Re: Geraldine FOI Code AZXB421"
+ end
+
+ it "should not add Re: prefix if there already is such a prefix" do
+ ir = info_requests(:fancy_dog_request)
+ im = ir.incoming_messages[0]
+ om = outgoing_messages(:useless_outgoing_message)
+ om.incoming_message_followup = im
+
+ im.raw_email.data = im.raw_email.data.sub("Subject: Geraldine FOI Code AZXB421", "Subject: Re: Geraldine FOI Code AZXB421")
+ OutgoingMailer.subject_for_followup(ir, om).should == "Re: Geraldine FOI Code AZXB421"
+ end
+
+ it "should not add Re: prefix if there already is a lower case re: prefix" do
+ ir = info_requests(:fancy_dog_request)
+ im = ir.incoming_messages[0]
+ om = outgoing_messages(:useless_outgoing_message)
+ om.incoming_message_followup = im
+
+ im.raw_email.data = im.raw_email.data.sub("Subject: Geraldine FOI Code AZXB421", "Subject: re: Geraldine FOI Code AZXB421")
+ im.parse_raw_email! true
+
+ OutgoingMailer.subject_for_followup(ir, om).should == "re: Geraldine FOI Code AZXB421"
+ end
+
+ it "should use 'Re:' and initial request subject when replying to failed delivery notifications" do
+ ir = info_requests(:fancy_dog_request)
+ im = ir.incoming_messages[0]
+ om = outgoing_messages(:useless_outgoing_message)
+ om.incoming_message_followup = im
+
+ im.raw_email.data = im.raw_email.data.sub("foiperson@localhost", "postmaster@localhost")
+ im.raw_email.data = im.raw_email.data.sub("Subject: Geraldine FOI Code AZXB421", "Subject: Delivery Failed")
+ im.parse_raw_email! true
+
+ OutgoingMailer.subject_for_followup(ir, om).should == "Re: Freedom of Information request - Why do you have & such a fancy dog?"
+ end
+end
+
+
diff --git a/spec/models/request_mailer_spec.rb b/spec/mailers/request_mailer_spec.rb
index 20d401a63..516d13127 100644
--- a/spec/models/request_mailer_spec.rb
+++ b/spec/mailers/request_mailer_spec.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe RequestMailer, " when receiving incoming mail" do
@@ -27,13 +28,13 @@ describe RequestMailer, " when receiving incoming mail" do
receive_incoming_mail('incoming-request-plain.email', 'dummy@localhost')
ir.incoming_messages.size.should == 1
InfoRequest.holding_pen_request.incoming_messages.size.should == 1
- last_event = InfoRequest.holding_pen_request.incoming_messages[0].info_request.get_last_event
+ last_event = InfoRequest.holding_pen_request.incoming_messages[0].info_request.info_request_events.last
last_event.params[:rejected_reason].should == "Could not identify the request from the email address"
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 1
mail = deliveries[0]
- mail.to.should == [ Configuration::contact_email ]
+ mail.to.should == [ AlaveteliConfiguration::contact_email ]
deliveries.clear
end
@@ -47,13 +48,13 @@ describe RequestMailer, " when receiving incoming mail" do
receive_incoming_mail('incoming-request-plain.email', ir.incoming_email, "")
ir.incoming_messages.size.should == 1
InfoRequest.holding_pen_request.incoming_messages.size.should == 1
- last_event = InfoRequest.holding_pen_request.incoming_messages[0].info_request.get_last_event
+ last_event = InfoRequest.holding_pen_request.incoming_messages[0].info_request.info_request_events.last
last_event.params[:rejected_reason].should =~ /there is no "From" address/
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 1
mail = deliveries[0]
- mail.to.should == [ Configuration::contact_email ]
+ mail.to.should == [ AlaveteliConfiguration::contact_email ]
deliveries.clear
end
@@ -67,13 +68,13 @@ describe RequestMailer, " when receiving incoming mail" do
receive_incoming_mail('incoming-request-plain.email', ir.incoming_email, "frob@nowhere.com")
ir.incoming_messages.size.should == 1
InfoRequest.holding_pen_request.incoming_messages.size.should == 1
- last_event = InfoRequest.holding_pen_request.incoming_messages[0].info_request.get_last_event
+ last_event = InfoRequest.holding_pen_request.incoming_messages[0].info_request.info_request_events.last
last_event.params[:rejected_reason].should =~ /Only the authority can reply/
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 1
mail = deliveries[0]
- mail.to.should == [ Configuration::contact_email ]
+ mail.to.should == [ AlaveteliConfiguration::contact_email ]
deliveries.clear
end
@@ -98,7 +99,7 @@ describe RequestMailer, " when receiving incoming mail" do
mail.multipart?.should == true
mail.parts.size.should == 2
message_part = mail.parts[0].to_s
- bounced_mail = MailHandler.mail_from_raw_email(mail.parts[1].body)
+ bounced_mail = MailHandler.mail_from_raw_email(mail.parts[1].body.to_s)
bounced_mail.to.should == [ ir.incoming_email ]
bounced_mail.from.should == [ 'geraldinequango@localhost' ]
bounced_mail.body.include?("That's so totally a rubbish question").should be_true
@@ -152,14 +153,14 @@ describe RequestMailer, " when receiving incoming mail" do
receive_incoming_mail('incoming-request-plain.email', ir.incoming_email)
ir.incoming_messages.size.should == 1
InfoRequest.holding_pen_request.incoming_messages.size.should == 1 # arrives in holding pen
- last_event = InfoRequest.holding_pen_request.incoming_messages[0].info_request.get_last_event
+ last_event = InfoRequest.holding_pen_request.incoming_messages[0].info_request.info_request_events.last
last_event.params[:rejected_reason].should =~ /allow new responses from nobody/
# should be a message to admin regarding holding pen
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 1
mail = deliveries[0]
- mail.to.should == [ Configuration::contact_email ]
+ mail.to.should == [ AlaveteliConfiguration::contact_email ]
deliveries.clear
end
@@ -203,15 +204,17 @@ describe RequestMailer, "when sending reminders to requesters to classify a resp
before do
Time.stub!(:now).and_return(Time.utc(2007, 11, 12, 23, 59))
@mock_event = mock_model(InfoRequestEvent)
- @mock_response = mock_model(IncomingMessage)
+ @mock_response = mock_model(IncomingMessage, :user_can_view? => true)
@mock_user = mock_model(User)
- @mock_request = mock_model(InfoRequest, :get_last_response_event_id => @mock_event.id,
- :get_last_response => @mock_response,
+ @mock_request = mock_model(InfoRequest, :get_last_public_response_event_id => @mock_event.id,
+ :get_last_public_response => @mock_response,
:user_id => 2,
:url_title => 'test_title',
:user => @mock_user)
InfoRequest.stub!(:find).and_return([@mock_request])
- RequestMailer.stub!(:deliver_new_response_reminder_alert)
+ mail_mock = mock("mail")
+ mail_mock.stub(:deliver)
+ RequestMailer.stub(:new_response_reminder_alert).and_return(mail_mock)
@sent_alert = mock_model(UserInfoRequestSentAlert, :user= =>nil,
:info_request= => nil,
:alert_type= => nil,
@@ -227,10 +230,12 @@ describe RequestMailer, "when sending reminders to requesters to classify a resp
it 'should ask for all requests that are awaiting description and whose latest response is older
than the number of days given and that are not the holding pen' do
expected_conditions = [ "awaiting_description = ?
- AND (SELECT created_at
- FROM info_request_events
- WHERE info_request_events.info_request_id = info_requests.id
- AND info_request_events.event_type = 'response'
+ AND (SELECT info_request_events.created_at
+ FROM info_request_events, incoming_messages
+ WHERE info_request_events.info_request_id = info_requests.id
+ AND info_request_events.event_type = 'response'
+ AND incoming_messages.id = info_request_events.incoming_message_id
+ AND incoming_messages.prominence = 'normal'
ORDER BY created_at desc LIMIT 1) < ?
AND url_title != 'holding_pen'
AND user_id IS NOT NULL".split(' ').join(' '),
@@ -243,13 +248,13 @@ describe RequestMailer, "when sending reminders to requesters to classify a resp
query_params[:conditions].should == expected_conditions
query_params[:include].should == [ :user ]
query_params[:order].should == 'info_requests.id'
- end
+ end.and_return [@mock_request]
send_alerts
end
it 'should raise an error if a request does not have a last response event id' do
- @mock_request.stub!(:get_last_response_event_id).and_return(nil)
+ @mock_request.stub!(:get_last_public_response_event_id).and_return(nil)
expected_message = "internal error, no last response while making alert new response reminder, request id #{@mock_request.id}"
lambda{ send_alerts }.should raise_error(expected_message)
end
@@ -268,7 +273,7 @@ describe RequestMailer, "when sending reminders to requesters to classify a resp
end
it 'should not send the reminder' do
- RequestMailer.should_not_receive(:deliver_new_response_reminder_alert)
+ RequestMailer.should_not_receive(:new_response_reminder_alert)
send_alerts
end
@@ -286,13 +291,13 @@ describe RequestMailer, "when sending reminders to requesters to classify a resp
mock_sent_alert.should_receive(:info_request=).with(@mock_request)
mock_sent_alert.should_receive(:user=).with(@mock_user)
mock_sent_alert.should_receive(:alert_type=).with('new_response_reminder_1')
- mock_sent_alert.should_receive(:info_request_event_id=).with(@mock_request.get_last_response_event_id)
+ mock_sent_alert.should_receive(:info_request_event_id=).with(@mock_request.get_last_public_response_event_id)
mock_sent_alert.should_receive(:save!)
send_alerts
end
it 'should send the reminder' do
- RequestMailer.should_receive(:deliver_new_response_reminder_alert)
+ RequestMailer.should_receive(:new_response_reminder_alert)
send_alerts
end
end
@@ -310,7 +315,7 @@ describe RequestMailer, 'when sending mail when someone has updated an old uncla
:public_body => @public_body,
:display_status => 'Refused.',
:url_title => 'test_request')
- @mail = RequestMailer.create_old_unclassified_updated(@info_request)
+ @mail = RequestMailer.old_unclassified_updated(@info_request)
end
it 'should have the subject "Someone has updated the status of your request"' do
@@ -327,6 +332,27 @@ describe RequestMailer, 'when sending mail when someone has updated an old uncla
end
+describe RequestMailer, 'when generating a fake response for an upload' do
+
+ before do
+ @foi_officer = mock_model(User, :name_and_email => "FOI officer's name and email")
+ @request_user = mock_model(User)
+ @public_body = mock_model(PublicBody, :name => 'Test public body')
+ @info_request = mock_model(InfoRequest, :user => @request_user,
+ :email_subject_followup => 'Re: Freedom of Information - Test request',
+ :incoming_name_and_email => 'Someone <someone@example.org>')
+ end
+
+ it 'should should generate a "fake response" email with a reasonable subject line' do
+ fake_email = RequestMailer.fake_response(@info_request,
+ @foi_officer,
+ "The body of the email...",
+ "blah.txt",
+ "The content of blah.txt")
+ fake_email.subject.should == "Re: Freedom of Information - Test request"
+ end
+
+end
describe RequestMailer, 'when sending a new response email' do
@@ -343,7 +369,7 @@ describe RequestMailer, 'when sending a new response email' do
end
it 'should not error when sending mails requests with characters requiring quoting in the subject' do
- @mail = RequestMailer.create_new_response(@info_request, @incoming_message)
+ @mail = RequestMailer.new_response(@info_request, @incoming_message)
end
end
@@ -361,20 +387,14 @@ describe RequestMailer, 'requires_admin' do
end
it 'body should contain the full admin URL' do
- mail = RequestMailer.deliver_requires_admin(@info_request)
+ mail = RequestMailer.requires_admin(@info_request).deliver
mail.body.should include('http://test.host/en/admin/request/show/123')
end
- context 'has an ADMIN_BASE_URL set' do
- before(:each) do
- Configuration::should_receive(:admin_base_url).and_return('http://our.proxy.server/admin/alaveteli/')
- end
-
- it 'body should contain the full admin URL' do
- mail = RequestMailer.deliver_requires_admin(@info_request)
-
- mail.body.should include('http://our.proxy.server/admin/alaveteli/request/show/123')
- end
+ it "body should contain the message from the user" do
+ mail = RequestMailer.requires_admin(@info_request, nil, "Something has gone wrong").deliver
+ mail.body.should include 'Something has gone wrong'
end
+
end
diff --git a/spec/models/track_mailer_spec.rb b/spec/mailers/track_mailer_spec.rb
index 9bf03c3d0..e8094b692 100644
--- a/spec/models/track_mailer_spec.rb
+++ b/spec/mailers/track_mailer_spec.rb
@@ -5,13 +5,15 @@ describe TrackMailer do
describe 'when sending email alerts for tracked things' do
before do
- TrackMailer.stub!(:deliver_event_digest)
+ mail_mock = mock("mail")
+ mail_mock.stub(:deliver)
+ TrackMailer.stub!(:event_digest).and_return(mail_mock)
Time.stub!(:now).and_return(Time.utc(2007, 11, 12, 23, 59))
end
it 'should ask for all the users whose last daily track email was sent more than a day ago' do
expected_conditions = [ "last_daily_track_email < ?", Time.utc(2007, 11, 11, 23, 59)]
- User.should_receive(:find).with(:all, :conditions => expected_conditions).and_return([])
+ User.should_receive(:find_each).with(:conditions => expected_conditions)
TrackMailer.alert_tracks
end
@@ -24,7 +26,7 @@ describe TrackMailer do
:url_name => 'test-name',
:get_locale => 'en',
:should_be_emailed? => true)
- User.stub!(:find).and_return([@user])
+ User.stub!(:find_each).and_yield(@user)
@user.stub!(:receive_email_alerts).and_return(true)
@user.stub!(:no_xapian_reindex=)
end
@@ -67,11 +69,15 @@ describe TrackMailer do
@xapian_search = mock('xapian search', :results => [])
@found_event = mock_model(InfoRequestEvent, :described_at => @track_thing.created_at + 1.day)
@search_result = {:model => @found_event}
- InfoRequest.stub!(:full_search).and_return(@xapian_search)
+ ActsAsXapian::Search.stub!(:new).and_return(@xapian_search)
end
it 'should ask for the events returned by the tracking query' do
- InfoRequest.should_receive(:full_search).with([InfoRequestEvent], 'test query', 'described_at', true, nil, 100, 1).and_return(@xapian_search)
+ ActsAsXapian::Search.should_receive(:new).with([InfoRequestEvent], 'test query',
+ :sort_by_prefix => 'described_at',
+ :sort_by_ascending => true,
+ :collapse_by_prefix => nil,
+ :limit => 100).and_return(@xapian_search)
TrackMailer.alert_tracks
end
@@ -79,21 +85,21 @@ describe TrackMailer do
sent_email = mock_model(TrackThingsSentEmail, :info_request_event_id => @found_event.id)
@track_things_sent_emails_array.stub!(:find).and_return([sent_email]) # this is for the date range find (created in last 14 days)
@xapian_search.stub!(:results).and_return([@search_result])
- TrackMailer.should_not_receive(:deliver_event_digest)
+ TrackMailer.should_not_receive(:event_digest)
TrackMailer.alert_tracks
end
it 'should not include in the email any events not sent in a previous tracking email that were described before the track was set up' do
@found_event.stub!(:described_at).and_return(@track_thing.created_at - 1.day)
@xapian_search.stub!(:results).and_return([@search_result])
- TrackMailer.should_not_receive(:deliver_event_digest)
+ TrackMailer.should_not_receive(:event_digest)
TrackMailer.alert_tracks
end
it 'should include in the email any events that the user has not been sent a tracking email on that have been described since the track was set up' do
@found_event.stub!(:described_at).and_return(@track_thing.created_at + 1.day)
@xapian_search.stub!(:results).and_return([@search_result])
- TrackMailer.should_receive(:deliver_event_digest)
+ TrackMailer.should_receive(:event_digest)
TrackMailer.alert_tracks
end
@@ -122,14 +128,14 @@ describe TrackMailer do
:save! => true,
:url_name => 'test-name',
:should_be_emailed? => false)
- User.stub!(:find).and_return([@user])
+ User.stub!(:find_each).and_yield(@user)
@user.stub!(:receive_email_alerts).and_return(true)
@user.stub!(:no_xapian_reindex=)
end
it 'should not ask for any daily track things for the user' do
expected_conditions = [ "tracking_user_id = ? and track_medium = ?", @user.id, 'email_daily' ]
- TrackThing.should_not_receive(:find).with(:all, :conditions => expected_conditions).and_return([])
+ TrackThing.should_not_receive(:find).with(:all, :conditions => expected_conditions)
TrackMailer.alert_tracks
end
@@ -137,7 +143,7 @@ describe TrackMailer do
@user.stub(:should_be_emailed?).and_return(true)
@user.stub(:receive_email_alerts).and_return(false)
expected_conditions = [ "tracking_user_id = ? and track_medium = ?", @user.id, 'email_daily' ]
- TrackThing.should_not_receive(:find).with(:all, :conditions => expected_conditions).and_return([])
+ TrackThing.should_not_receive(:find).with(:all, :conditions => expected_conditions)
TrackMailer.alert_tracks
end
@@ -160,20 +166,19 @@ describe TrackMailer do
describe 'delivering the email' do
- before do
+ before :each do
@post_redirect = mock_model(PostRedirect, :save! => true,
:email_token => "token")
PostRedirect.stub!(:new).and_return(@post_redirect)
ActionMailer::Base.deliveries = []
+ @user = mock_model(User,
+ :name_and_email => MailHandler.address_from_name_and_email('Tippy Test', 'tippy@localhost'),
+ :url_name => 'tippy_test'
+ )
+ TrackMailer.event_digest(@user, []).deliver # no items in it email for minimal test
end
it 'should deliver one email, with right headers' do
- @user = mock_model(User,
- :name_and_email => MailHandler.address_from_name_and_email('Tippy Test', 'tippy@localhost'),
- :url_name => 'tippy_test'
- )
-
- TrackMailer.deliver_event_digest(@user, []) # no items in it email for minimal test
deliveries = ActionMailer::Base.deliveries
if deliveries.size > 1 # debugging if there is an error
deliveries.each do |d|
@@ -190,6 +195,22 @@ describe TrackMailer do
deliveries.clear
end
+
+ context "force ssl is off" do
+ # Force SSL is off in the tests. Since the code that selectively switches the protocols
+ # is in the initialiser for Rails it's hard to test. Hmmm...
+ # We could AlaveteliConfiguration.stub!(:force_ssl).and_return(true) but the config/environment.rb
+ # wouldn't get reloaded
+
+ it "should have http links in the email" do
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+
+ mail.body.should include("http://")
+ mail.body.should_not include("https://")
+ end
+ end
end
end
diff --git a/spec/models/censor_rule_spec.rb b/spec/models/censor_rule_spec.rb
index c11b05a03..5b41cc0d4 100644
--- a/spec/models/censor_rule_spec.rb
+++ b/spec/models/censor_rule_spec.rb
@@ -1,3 +1,20 @@
+# == Schema Information
+#
+# Table name: censor_rules
+#
+# id :integer not null, primary key
+# info_request_id :integer
+# user_id :integer
+# public_body_id :integer
+# text :text not null
+# replacement :text not null
+# last_edit_editor :string(255) not null
+# last_edit_comment :text not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# regexp :boolean
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe CensorRule, "substituting things" do
@@ -73,10 +90,10 @@ end
describe 'when validating rules' do
- describe 'should be invalid without text' do
+ it 'should be invalid without text' do
censor_rule = CensorRule.new
censor_rule.valid?.should == false
- censor_rule.errors.on(:text).should == "can't be blank"
+ censor_rule.errors[:text].should == ["can't be blank"]
end
describe 'when validating a regexp rule' do
@@ -96,7 +113,7 @@ describe 'when validating rules' do
it 'should add an error message to the text field with the regexp error message' do
Regexp.stub!(:new).and_raise(RegexpError.new("very bad regexp"))
@censor_rule.valid?.should == false
- @censor_rule.errors.on(:text).should == "very bad regexp"
+ @censor_rule.errors[:text].should == ["very bad regexp"]
end
end
@@ -106,7 +123,7 @@ describe 'when validating rules' do
it 'should not add any error message to the text field' do
Regexp.stub!(:new)
@censor_rule.valid?
- @censor_rule.errors.on(:text).should == nil
+ @censor_rule.errors[:text].should == []
end
end
@@ -134,15 +151,21 @@ describe 'when validating rules' do
it 'should not allow a global text censor rule (without user_id, request_id or public_body_id)' do
@censor_rule.valid?.should == false
- @expected_error = 'Censor must apply to an info request a user or a body; is invalid'
- @censor_rule.errors.full_messages.should == [@expected_error]
+
+ expected_error = ["Rule must apply to an info request, a user or a body"]
+ @censor_rule.errors[:user].should == expected_error
+ @censor_rule.errors[:info_request].should == expected_error
+ @censor_rule.errors[:public_body].should == expected_error
end
it 'should not allow a global regex censor rule (without user_id, request_id or public_body_id)' do
@censor_rule.regexp = true
@censor_rule.valid?.should == false
- @expected_error = 'Censor must apply to an info request a user or a body; is invalid'
- @censor_rule.errors.full_messages.should == [@expected_error]
+
+ expected_error = ["Rule must apply to an info request, a user or a body"]
+ @censor_rule.errors[:user].should == expected_error
+ @censor_rule.errors[:info_request].should == expected_error
+ @censor_rule.errors[:public_body].should == expected_error
end
end
diff --git a/spec/models/contact_mailer_spec.rb b/spec/models/contact_mailer_spec.rb
deleted file mode 100644
index 202e45758..000000000
--- a/spec/models/contact_mailer_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-
-describe ContactMailer, " when blah" do
- before do
- end
-end
-
-
diff --git a/spec/models/foi_attachment_spec.rb b/spec/models/foi_attachment_spec.rb
index 537a3363c..882723d1e 100644
--- a/spec/models/foi_attachment_spec.rb
+++ b/spec/models/foi_attachment_spec.rb
@@ -1,6 +1,21 @@
+# == Schema Information
+#
+# Table name: foi_attachments
+#
+# id :integer not null, primary key
+# content_type :text
+# filename :text
+# charset :text
+# display_size :text
+# url_part_number :integer
+# within_rfc822_subject :text
+# incoming_message_id :integer
+# hexdigest :string(32)
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-describe FoiAttachment, " when calculating due date" do
+describe FoiAttachment do
before(:each) do
load_raw_emails_data
diff --git a/spec/models/holiday_spec.rb b/spec/models/holiday_spec.rb
index 5d3f76d24..89849abb7 100644
--- a/spec/models/holiday_spec.rb
+++ b/spec/models/holiday_spec.rb
@@ -1,3 +1,12 @@
+# == Schema Information
+#
+# Table name: holidays
+#
+# id :integer not null, primary key
+# day :date
+# description :text
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe Holiday, " when calculating due date" do
diff --git a/spec/models/incoming_message_spec.rb b/spec/models/incoming_message_spec.rb
index f53a5856a..f06dcbeeb 100644
--- a/spec/models/incoming_message_spec.rb
+++ b/spec/models/incoming_message_spec.rb
@@ -1,6 +1,140 @@
# coding: utf-8
+# == Schema Information
+#
+# Table name: incoming_messages
+#
+# id :integer not null, primary key
+# info_request_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# raw_email_id :integer not null
+# cached_attachment_text_clipped :text
+# cached_main_body_text_folded :text
+# cached_main_body_text_unfolded :text
+# subject :text
+# mail_from_domain :text
+# valid_to_reply_to :boolean
+# last_parsed :datetime
+# mail_from :text
+# sent_at :datetime
+# prominence :string(255) default("normal"), not null
+# prominence_reason :text
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+describe IncomingMessage, 'when validating' do
+
+ it 'should be valid with valid prominence values' do
+ ['hidden', 'requester_only', 'normal'].each do |prominence|
+ incoming_message = IncomingMessage.new(:raw_email => RawEmail.new,
+ :info_request => InfoRequest.new,
+ :prominence => prominence)
+ incoming_message.valid?.should be_true
+ end
+ end
+
+ it 'should not be valid with an invalid prominence value' do
+ incoming_message = IncomingMessage.new(:raw_email => RawEmail.new,
+ :info_request => InfoRequest.new,
+ :prominence => 'norman')
+ incoming_message.valid?.should be_false
+ end
+
+end
+
+describe IncomingMessage, 'when getting a response event' do
+
+ it 'should return an event with event_type "response"' do
+ incoming_message = IncomingMessage.new
+ ['comment', 'response'].each do |event_type|
+ incoming_message.info_request_events << InfoRequestEvent.new(:event_type => event_type)
+ end
+ incoming_message.response_event.event_type.should == 'response'
+ end
+
+end
+
+describe IncomingMessage, 'when asked if a user can view it' do
+
+ before do
+ @user = mock_model(User)
+ @info_request = mock_model(InfoRequest)
+ @incoming_message = IncomingMessage.new(:info_request => @info_request)
+ end
+
+ context 'if the prominence is hidden' do
+
+ before do
+ @incoming_message.prominence = 'hidden'
+ end
+
+ it 'should return true if the user can view hidden things' do
+ User.stub!(:view_hidden?).with(@user).and_return(true)
+ @incoming_message.user_can_view?(@user).should be_true
+ end
+
+ it 'should return false if the user cannot view hidden things' do
+ User.stub!(:view_hidden?).with(@user).and_return(false)
+ @incoming_message.user_can_view?(@user).should be_false
+ end
+
+ end
+
+ context 'if the prominence is requester_only' do
+
+ before do
+ @incoming_message.prominence = 'requester_only'
+ end
+
+ it 'should return true if the user owns the associated request' do
+ @info_request.stub!(:is_owning_user?).with(@user).and_return(true)
+ @incoming_message.user_can_view?(@user).should be_true
+ end
+
+ it 'should return false if the user does not own the associated request' do
+ @info_request.stub!(:is_owning_user?).with(@user).and_return(false)
+ @incoming_message.user_can_view?(@user).should be_false
+ end
+ end
+
+ context 'if the prominence is normal' do
+
+ before do
+ @incoming_message.prominence = 'normal'
+ end
+
+ it 'should return true' do
+ @incoming_message.user_can_view?(@user).should be_true
+ end
+
+ end
+
+end
+
+describe 'when asked if it is indexed by search' do
+
+ before do
+ @incoming_message = IncomingMessage.new
+ end
+
+ it 'should return false if it has prominence "hidden"' do
+ @incoming_message.prominence = 'hidden'
+ @incoming_message.indexed_by_search?.should be_false
+ end
+
+ it 'should return false if it has prominence "requester_only"' do
+ @incoming_message.prominence = 'requester_only'
+ @incoming_message.indexed_by_search?.should be_false
+ end
+
+ it 'should return true if it has prominence "normal"' do
+ @incoming_message.prominence = 'normal'
+ @incoming_message.indexed_by_search?.should be_true
+ end
+
+end
+
describe IncomingMessage, " when dealing with incoming mail" do
before(:each) do
@@ -27,11 +161,11 @@ describe IncomingMessage, " when dealing with incoming mail" do
end
it "should correctly fold various types of footer" do
- Dir.glob(File.join(Spec::Runner.configuration.fixture_path, "files", "email-folding-example-*.txt")).each do |file|
+ Dir.glob(File.join(RSpec.configuration.fixture_path, "files", "email-folding-example-*.txt")).each do |file|
message = File.read(file)
parsed = IncomingMessage.remove_quoted_sections(message)
expected = File.read("#{file}.expected")
- parsed.should include(expected)
+ parsed.should be_equal_modulo_whitespace_to expected
end
end
@@ -59,12 +193,19 @@ describe IncomingMessage, " when dealing with incoming mail" do
message.subject.should == "Câmara Responde: Banco de ideias"
end
- it 'should not error on display of a message which has no charset set on the body part and
- is not good utf-8' do
+ it 'should deal with GB18030 text even if the charset is missing' do
ir = info_requests(:fancy_dog_request)
receive_incoming_mail('no-part-charset-bad-utf8.email', ir.incoming_email)
message = ir.incoming_messages[1]
message.parse_raw_email!
+ message.get_main_body_text_internal.should include("贵公司负责人")
+ end
+
+ it 'should not error on display of a message which has no charset set on the body part and is not good UTF-8' do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('no-part-charset-random-data.email', ir.incoming_email)
+ message = ir.incoming_messages[1]
+ message.parse_raw_email!
message.get_main_body_text_internal.should include("The above text was badly encoded")
end
@@ -108,6 +249,16 @@ describe IncomingMessage, " when dealing with incoming mail" do
end
+ it 'should handle a main body part that is just quoted content in an email that has
+ no subject' do
+ i = IncomingMessage.new
+ i.stub!(:get_main_body_text_unfolded).and_return("some quoting")
+ i.stub!(:get_main_body_text_folded).and_return("FOLDED_QUOTED_SECTION")
+ i.stub!(:subject).and_return(nil)
+ i.get_body_for_html_display
+ end
+
+
end
describe IncomingMessage, " display attachments" do
@@ -311,7 +462,18 @@ describe IncomingMessage, " when censoring data" do
data.should == "His email was x\000x\000x\000@\000x\000x\000x\000.\000x\000x\000x\000, indeed"
end
-
+ it 'should handle multibyte characters correctly' do
+ orig_data = 'á'
+ data = orig_data.dup
+ @regex_censor_rule = CensorRule.new()
+ @regex_censor_rule.text = 'á'
+ @regex_censor_rule.regexp = true
+ @regex_censor_rule.replacement = 'cat'
+ @regex_censor_rule.last_edit_editor = 'unknown'
+ @regex_censor_rule.last_edit_comment = 'none'
+ @im.info_request.censor_rules << @regex_censor_rule
+ lambda{ @im.binary_mask_stuff!(data, "text/plain") }.should_not raise_error
+ end
def pdf_replacement_test(use_ghostscript_compression)
config = MySociety::Config.load_default()
@@ -353,7 +515,7 @@ describe IncomingMessage, " when censoring data" do
end
it "should apply hard-coded privacy rules to HTML files" do
- data = "http://#{Configuration::domain}/c/cheese"
+ data = "http://#{AlaveteliConfiguration::domain}/c/cheese"
@im.html_mask_stuff!(data)
data.should == "[WDTK login link]"
end
@@ -405,12 +567,24 @@ describe IncomingMessage, " when uudecoding bad messages" do
im.stub!(:mail).and_return(mail)
im.extract_attachments!
+ im.reload
attachments = im.foi_attachments
attachments.size.should == 2
attachments[1].filename.should == 'moo.txt'
im.get_attachments_for_display.size.should == 1
end
+ it "should still work when parsed from the raw email" do
+ raw_email = load_file_fixture 'inline-uuencode.email'
+ mail = MailHandler.mail_from_raw_email(raw_email)
+ im = incoming_messages :useless_incoming_message
+ im.stub!(:raw_email).and_return(raw_email)
+ im.stub!(:mail).and_return(mail)
+ im.parse_raw_email!
+ attachments = im.foi_attachments
+ attachments.size.should == 2
+ end
+
it "should apply censor rules" do
mail = get_fixture_mail('incoming-request-bad-uuencoding.email')
@@ -523,3 +697,58 @@ describe IncomingMessage, "when TNEF attachments are attached to messages" do
end
end
+describe IncomingMessage, "when extracting attachments" do
+
+ before do
+ load_raw_emails_data
+ end
+
+ it 'handles the case where reparsing changes the body of the main part
+ and the cached attachment has been deleted' do
+ # original set of attachment attributes
+ attachment_attributes = { :url_part_number => 1,
+ :within_rfc822_subject => nil,
+ :content_type => "text/plain",
+ :charset => nil,
+ :body => "No way!\n",
+ :hexdigest => "0c8b1b0f5cb9c94ed15a180e73b5c7d1",
+ :filename => nil }
+
+ # Make a small change in the body returned for the attachment
+ new_attachment_attributes = attachment_attributes.merge(:body => "No way!",
+ :hexdigest => "74d2c0a41e074f9cebe49324d5b47414")
+
+
+ # Simulate parsing with the original attachments
+ MailHandler.stub!(:get_attachment_attributes).and_return([attachment_attributes])
+ incoming_message = incoming_messages(:useless_incoming_message)
+
+ # Extract the attachments
+ incoming_message.extract_attachments!
+
+ # delete the cached file for the main body part
+ main = incoming_message.get_main_body_text_part
+ main.delete_cached_file!
+
+ # Simulate reparsing with the slightly changed body
+ MailHandler.stub!(:get_attachment_attributes).and_return([new_attachment_attributes])
+
+ # Re-extract the attachments
+ incoming_message.extract_attachments!
+
+ attachments = incoming_message.foi_attachments
+ attachments.size.should == 1
+ attachments.first.hexdigest.should == "74d2c0a41e074f9cebe49324d5b47414"
+ attachments.first.body.should == 'No way!'
+ end
+
+ it 'makes invalid utf-8 encoded attachment text valid when string responds to encode' do
+ if String.method_defined?(:encode)
+ im = incoming_messages(:useless_incoming_message)
+ im.stub!(:extract_text).and_return("\xBF")
+
+ im._get_attachment_text_internal.valid_encoding?.should be_true
+ end
+ end
+
+end
diff --git a/spec/models/info_request_batch_spec.rb b/spec/models/info_request_batch_spec.rb
new file mode 100644
index 000000000..53158ebe2
--- /dev/null
+++ b/spec/models/info_request_batch_spec.rb
@@ -0,0 +1,150 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe InfoRequestBatch, "when validating" do
+
+ before do
+ @info_request_batch = FactoryGirl.build(:info_request_batch)
+ end
+
+ it 'should require a user' do
+ @info_request_batch.user = nil
+ @info_request_batch.valid?.should be_false
+ @info_request_batch.errors.full_messages.should == ["User can't be blank"]
+ end
+
+ it 'should require a title' do
+ @info_request_batch.title = nil
+ @info_request_batch.valid?.should be_false
+ @info_request_batch.errors.full_messages.should == ["Title can't be blank"]
+ end
+
+ it 'should require a body' do
+ @info_request_batch.body = nil
+ @info_request_batch.valid?.should be_false
+ @info_request_batch.errors.full_messages.should == ["Body can't be blank"]
+ end
+
+end
+
+describe InfoRequestBatch, "when finding an existing batch" do
+
+ before do
+ @first_body = FactoryGirl.create(:public_body)
+ @second_body = FactoryGirl.create(:public_body)
+ @info_request_batch = FactoryGirl.create(:info_request_batch, :title => 'Matched title',
+ :body => 'Matched body',
+ :public_bodies => [@first_body,
+ @second_body])
+ end
+
+ it 'should return a batch with the same user, title and body sent to one of the same public bodies' do
+ InfoRequestBatch.find_existing(@info_request_batch.user,
+ @info_request_batch.title,
+ @info_request_batch.body,
+ [@first_body]).should_not be_nil
+ end
+
+ it 'should not return a batch with the same title and body sent to another public body' do
+ InfoRequestBatch.find_existing(@info_request_batch.user,
+ @info_request_batch.title,
+ @info_request_batch.body,
+ [FactoryGirl.create(:public_body)]).should be_nil
+ end
+
+ it 'should not return a batch sent the same public bodies with a different title and body' do
+ InfoRequestBatch.find_existing(@info_request_batch.user,
+ 'Other title',
+ 'Other body',
+ [@first_body]).should be_nil
+ end
+
+ it 'should not return a batch sent to one of the same public bodies with the same title and body by
+ a different user' do
+ InfoRequestBatch.find_existing(FactoryGirl.create(:user),
+ @info_request_batch.title,
+ @info_request_batch.body,
+ [@first_body]).should be_nil
+ end
+end
+
+describe InfoRequestBatch, "when creating a batch", :focus => true do
+
+ before do
+ @title = 'A test title'
+ @body = "Dear [Authority name],\nA message\nYours faithfully,\nRequester"
+ @first_public_body = FactoryGirl.create(:public_body)
+ @second_public_body = FactoryGirl.create(:public_body)
+ @user = FactoryGirl.create(:user)
+ @info_request_batch = InfoRequestBatch.create!({:title => @title,
+ :body => @body,
+ :public_bodies => [@first_public_body,
+ @second_public_body],
+ :user => @user})
+ end
+
+ it 'should substitute authority name for the placeholder in each request' do
+ unrequestable = @info_request_batch.create_batch!
+ [@first_public_body, @second_public_body].each do |public_body|
+ request = @info_request_batch.info_requests.detect do |info_request|
+ info_request.public_body == public_body
+ end
+ expected = "Dear #{public_body.name},\nA message\nYours faithfully,\nRequester"
+ request.outgoing_messages.first.body.should == expected
+ end
+ end
+
+ it 'should send requests to requestable public bodies, and return a list of unrequestable ones' do
+ @first_public_body.stub(:is_requestable?).and_return(false)
+ unrequestable = @info_request_batch.create_batch!
+ unrequestable.should == [@first_public_body]
+ @info_request_batch.info_requests.size.should == 1
+ request = @info_request_batch.info_requests.first
+ request.outgoing_messages.first.status.should == 'sent'
+ end
+
+ it 'should set the sent_at value of the info request batch' do
+ @info_request_batch.create_batch!
+ @info_request_batch.sent_at.should_not be_nil
+ end
+
+end
+
+describe InfoRequestBatch, "when sending batches" do
+
+ before do
+ @title = 'A test title'
+ @body = "Dear [Authority name],\nA message\nYours faithfully,\nRequester"
+ @first_public_body = FactoryGirl.create(:public_body)
+ @second_public_body = FactoryGirl.create(:public_body)
+ @user = FactoryGirl.create(:user)
+ @info_request_batch = InfoRequestBatch.create!({:title => @title,
+ :body => @body,
+ :public_bodies => [@first_public_body,
+ @second_public_body],
+ :user => @user})
+ @sent_batch = InfoRequestBatch.create!({:title => @title,
+ :body => @body,
+ :public_bodies => [@first_public_body,
+ @second_public_body],
+ :user => @user,
+ :sent_at => Time.now})
+ end
+
+ it 'should send requests and notifications for only unsent batch requests' do
+ InfoRequestBatch.send_batches
+ ActionMailer::Base.deliveries.size.should == 3
+ first_email = ActionMailer::Base.deliveries.first
+ first_email.to.should == [@first_public_body.request_email]
+ first_email.subject.should == 'Freedom of Information request - A test title'
+
+ second_email = ActionMailer::Base.deliveries.second
+ second_email.to.should == [@second_public_body.request_email]
+ second_email.subject.should == 'Freedom of Information request - A test title'
+
+ third_email = ActionMailer::Base.deliveries.third
+ third_email.to.should == [@user.email]
+ third_email.subject.should == 'Your batch request "A test title" has been sent'
+ end
+
+end
+
diff --git a/spec/models/info_request_event_spec.rb b/spec/models/info_request_event_spec.rb
index 796f8b840..53c83bd46 100644
--- a/spec/models/info_request_event_spec.rb
+++ b/spec/models/info_request_event_spec.rb
@@ -1,3 +1,21 @@
+# coding: utf-8
+# == Schema Information
+#
+# Table name: info_request_events
+#
+# id :integer not null, primary key
+# info_request_id :integer not null
+# event_type :text not null
+# params_yaml :text not null
+# created_at :datetime not null
+# described_state :string(255)
+# calculated_state :string(255)
+# last_described_at :datetime
+# incoming_message_id :integer
+# outgoing_message_id :integer
+# comment_id :integer
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe InfoRequestEvent do
@@ -14,6 +32,64 @@ describe InfoRequestEvent do
end
+ describe 'when deciding if it is indexed by search' do
+
+ before do
+ @comment = mock_model(Comment)
+ @incoming_message = mock_model(IncomingMessage)
+ @outgoing_message = mock_model(OutgoingMessage)
+ @info_request = mock_model(InfoRequest, :indexed_by_search? => true)
+ end
+
+ it 'should return false for a comment that is not visible' do
+ @comment.stub!(:visible).and_return(false)
+ @info_request_event = InfoRequestEvent.new(:event_type => 'comment',
+ :comment => @comment,
+ :info_request => @info_request)
+ @info_request_event.indexed_by_search?.should be_false
+ end
+
+ it 'should return true for a comment that is visible' do
+ @comment.stub!(:visible).and_return(true)
+ @info_request_event = InfoRequestEvent.new(:event_type => 'comment',
+ :comment => @comment,
+ :info_request => @info_request)
+ @info_request_event.indexed_by_search?.should be_true
+ end
+
+ it 'should return false for an incoming message that is not indexed by search' do
+ @incoming_message.stub!(:indexed_by_search?).and_return false
+ @info_request_event = InfoRequestEvent.new(:event_type => 'response',
+ :incoming_message => @incoming_message,
+ :info_request => @info_request)
+ @info_request_event.indexed_by_search?.should be_false
+ end
+
+ it 'should return true for an incoming message that is indexed by search' do
+ @incoming_message.stub!(:indexed_by_search?).and_return true
+ @info_request_event = InfoRequestEvent.new(:event_type => 'response',
+ :incoming_message => @incoming_message,
+ :info_request => @info_request)
+ @info_request_event.indexed_by_search?.should be_true
+ end
+
+ it 'should return false for an outgoing message that is not indexed by search' do
+ @outgoing_message.stub!(:indexed_by_search?).and_return false
+ @info_request_event = InfoRequestEvent.new(:event_type => 'followup_sent',
+ :outgoing_message => @outgoing_message,
+ :info_request => @info_request)
+ @info_request_event.indexed_by_search?.should be_false
+ end
+
+ it 'should return true for an outgoing message that is indexed by search' do
+ @outgoing_message.stub!(:indexed_by_search?).and_return true
+ @info_request_event = InfoRequestEvent.new(:event_type => 'followup_sent',
+ :outgoing_message => @outgoing_message,
+ :info_request => @info_request)
+ @info_request_event.indexed_by_search?.should be_true
+ end
+ end
+
describe 'after saving' do
it 'should mark the model for reindexing in xapian if there is no no_xapian_reindex flag on the object' do
@@ -21,7 +97,7 @@ describe InfoRequestEvent do
:event_type => 'sent',
:params => {})
event.should_receive(:xapian_mark_needs_index)
- event.run_callbacks(:after_save)
+ event.run_callbacks(:save)
end
end
@@ -118,6 +194,13 @@ describe InfoRequestEvent do
@info_request_event.same_email_as_previous_send?.should be_true
end
+ it 'should handle non-ascii characters in the name input' do
+ address = "\"Someone’s name\" <test@example.com>"
+ @info_request_event.stub!(:params).and_return(:email => address)
+ @info_request_event.stub_chain(:info_request, :get_previous_email_sent_to).and_return(address)
+ @info_request_event.same_email_as_previous_send?.should be_true
+ end
+
end
end
diff --git a/spec/models/info_request_spec.rb b/spec/models/info_request_spec.rb
index 544852f91..9766f928f 100644
--- a/spec/models/info_request_spec.rb
+++ b/spec/models/info_request_spec.rb
@@ -1,7 +1,66 @@
+# encoding: utf-8
+# == Schema Information
+#
+# Table name: info_requests
+#
+# id :integer not null, primary key
+# title :text not null
+# user_id :integer
+# public_body_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# described_state :string(255) not null
+# awaiting_description :boolean default(FALSE), not null
+# prominence :string(255) default("normal"), not null
+# url_title :text not null
+# law_used :string(255) default("foi"), not null
+# allow_new_responses_from :string(255) default("anybody"), not null
+# handle_rejected_responses :string(255) default("bounce"), not null
+# idhash :string(255) not null
+# external_user_name :string(255)
+# external_url :string(255)
+# attention_requested :boolean default(FALSE)
+# comments_allowed :boolean default(TRUE), not null
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe InfoRequest do
+ describe 'when validating' do
+
+ it 'should accept a summary with ascii characters' do
+ info_request = InfoRequest.new(:title => 'abcde')
+ info_request.valid?
+ info_request.errors[:title].should be_empty
+ end
+
+ it 'should accept a summary with unicode characters' do
+ info_request = InfoRequest.new(:title => 'кажете')
+ info_request.valid?
+ info_request.errors[:title].should be_empty
+ end
+
+ it 'should not accept a summary with no ascii or unicode characters' do
+ info_request = InfoRequest.new(:title => '55555')
+ info_request.valid?
+ info_request.errors[:title].should_not be_empty
+ end
+
+ it 'should require a public body id by default' do
+ info_request = InfoRequest.new
+ info_request.valid?
+ info_request.errors[:public_body_id].should_not be_empty
+ end
+
+ it 'should not require a public body id if it is a batch request template' do
+ info_request = InfoRequest.new
+ info_request.is_batch_request_template = true
+ info_request.valid?
+ info_request.errors[:public_body_id].should be_empty
+ end
+ end
+
describe 'when generating a user name slug' do
before do
@@ -156,6 +215,7 @@ describe InfoRequest do
end
it "should cope with indexing after item is deleted" do
+ load_raw_emails_data
IncomingMessage.find(:all).each{|x| x.parse_raw_email!}
rebuild_xapian_index
# delete event from underneath indexing; shouldn't cause error
@@ -375,10 +435,12 @@ describe InfoRequest do
it 'should add extra conditions if supplied' do
expected_conditions = ["awaiting_description = ?
- AND (SELECT created_at
- FROM info_request_events
+ AND (SELECT info_request_events.created_at
+ FROM info_request_events, incoming_messages
WHERE info_request_events.info_request_id = info_requests.id
AND info_request_events.event_type = 'response'
+ AND incoming_messages.id = info_request_events.incoming_message_id
+ AND incoming_messages.prominence = 'normal'
ORDER BY created_at desc LIMIT 1) < ?
AND url_title != 'holding_pen'
AND user_id IS NOT NULL
@@ -393,21 +455,25 @@ describe InfoRequest do
InfoRequest.find_old_unclassified({:conditions => ["prominence != 'backpage'"]})
end
- it 'should ask the database for requests that are awaiting description, have a last response older
+ it 'should ask the database for requests that are awaiting description, have a last public response older
than 21 days old, have a user, are not the holding pen and are not backpaged' do
expected_conditions = ["awaiting_description = ?
- AND (SELECT created_at
- FROM info_request_events
+ AND (SELECT info_request_events.created_at
+ FROM info_request_events, incoming_messages
WHERE info_request_events.info_request_id = info_requests.id
AND info_request_events.event_type = 'response'
+ AND incoming_messages.id = info_request_events.incoming_message_id
+ AND incoming_messages.prominence = 'normal'
ORDER BY created_at desc LIMIT 1) < ?
AND url_title != 'holding_pen'
AND user_id IS NOT NULL".split(' ').join(' '),
true, Time.now - 21.days]
- expected_select = "*, (SELECT created_at
- FROM info_request_events
+ expected_select = "*, (SELECT info_request_events.created_at
+ FROM info_request_events, incoming_messages
WHERE info_request_events.info_request_id = info_requests.id
AND info_request_events.event_type = 'response'
+ AND incoming_messages.id = info_request_events.incoming_message_id
+ AND incoming_messages.prominence = 'normal'
ORDER BY created_at desc LIMIT 1)
AS last_response_time".split(' ').join(' ')
InfoRequest.should_receive(:find) do |all, query_params|
@@ -422,12 +488,55 @@ describe InfoRequest do
end
+ describe 'when asked for random old unclassified requests with normal prominence' do
+
+ it "should not return requests that don't have normal prominence" do
+ dog_request = info_requests(:fancy_dog_request)
+ old_unclassified = InfoRequest.get_random_old_unclassified(1, :conditions => ["prominence = 'normal'"])
+ old_unclassified.length.should == 1
+ old_unclassified.first.should == dog_request
+ dog_request.prominence = 'requester_only'
+ dog_request.save!
+ old_unclassified = InfoRequest.get_random_old_unclassified(1, :conditions => ["prominence = 'normal'"])
+ old_unclassified.length.should == 0
+ dog_request.prominence = 'hidden'
+ dog_request.save!
+ old_unclassified = InfoRequest.get_random_old_unclassified(1, :conditions => ["prominence = 'normal'"])
+ old_unclassified.length.should == 0
+ end
+
+ end
+
+ describe 'when asked to count old unclassified requests with normal prominence' do
+
+ it "should not return requests that don't have normal prominence" do
+ dog_request = info_requests(:fancy_dog_request)
+ old_unclassified = InfoRequest.count_old_unclassified(:conditions => ["prominence = 'normal'"])
+ old_unclassified.should == 1
+ dog_request.prominence = 'requester_only'
+ dog_request.save!
+ old_unclassified = InfoRequest.count_old_unclassified(:conditions => ["prominence = 'normal'"])
+ old_unclassified.should == 0
+ dog_request.prominence = 'hidden'
+ dog_request.save!
+ old_unclassified = InfoRequest.count_old_unclassified(:conditions => ["prominence = 'normal'"])
+ old_unclassified.should == 0
+ end
+
+ end
+
describe 'when an instance is asked if it is old and unclassified' do
before do
Time.stub!(:now).and_return(Time.utc(2007, 11, 9, 23, 59))
- @mock_comment_event = safe_mock_model(InfoRequestEvent, :created_at => Time.now - 23.days, :event_type => 'comment')
- @mock_response_event = safe_mock_model(InfoRequestEvent, :created_at => Time.now - 22.days, :event_type => 'response')
+ @mock_comment_event = mock_model(InfoRequestEvent, :created_at => Time.now - 23.days,
+ :event_type => 'comment',
+ :response? => false)
+ mock_incoming_message = mock_model(IncomingMessage, :all_can_view? => true)
+ @mock_response_event = mock_model(InfoRequestEvent, :created_at => Time.now - 22.days,
+ :event_type => 'response',
+ :response? => true,
+ :incoming_message => mock_incoming_message)
@info_request = InfoRequest.new(:prominence => 'normal',
:awaiting_description => true,
:info_request_events => [@mock_response_event, @mock_comment_event])
@@ -457,16 +566,16 @@ describe InfoRequest do
describe 'when applying censor rules' do
before do
- @global_rule = safe_mock_model(CensorRule, :apply_to_text! => nil,
+ @global_rule = mock_model(CensorRule, :apply_to_text! => nil,
:apply_to_binary! => nil)
- @user_rule = safe_mock_model(CensorRule, :apply_to_text! => nil,
+ @user_rule = mock_model(CensorRule, :apply_to_text! => nil,
:apply_to_binary! => nil)
- @request_rule = safe_mock_model(CensorRule, :apply_to_text! => nil,
+ @request_rule = mock_model(CensorRule, :apply_to_text! => nil,
:apply_to_binary! => nil)
- @body_rule = safe_mock_model(CensorRule, :apply_to_text! => nil,
+ @body_rule = mock_model(CensorRule, :apply_to_text! => nil,
:apply_to_binary! => nil)
- @user = safe_mock_model(User, :censor_rules => [@user_rule])
- @body = safe_mock_model(PublicBody, :censor_rules => [@body_rule])
+ @user = mock_model(User, :censor_rules => [@user_rule])
+ @body = mock_model(PublicBody, :censor_rules => [@body_rule])
@info_request = InfoRequest.new(:prominence => 'normal',
:awaiting_description => true,
:title => 'title')
@@ -504,6 +613,12 @@ describe InfoRequest do
@info_request.apply_censor_rules_to_text!(@text)
end
+ it 'should not raise an error if the request is a batch request template' do
+ @info_request.stub!(:public_body).and_return(nil)
+ @info_request.is_batch_request_template = true
+ lambda{ @info_request.apply_censor_rules_to_text!(@text) }.should_not raise_error
+ end
+
end
context 'when applying censor rules to binary files' do
@@ -562,7 +677,619 @@ describe InfoRequest do
@info_request.prominence = 'requester_only'
@info_request.all_can_view?.should == false
end
+ end
+
+ describe 'when asked for the last public response event' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request_with_incoming)
+ @incoming_message = @info_request.incoming_messages.first
+ end
+
+ it 'should not return an event with a hidden prominence message' do
+ @incoming_message.prominence = 'hidden'
+ @incoming_message.save!
+ @info_request.get_last_public_response_event.should == nil
+ end
+
+ it 'should not return an event with a requester_only prominence message' do
+ @incoming_message.prominence = 'requester_only'
+ @incoming_message.save!
+ @info_request.get_last_public_response_event.should == nil
+ end
+ it 'should return an event with a normal prominence message' do
+ @incoming_message.prominence = 'normal'
+ @incoming_message.save!
+ @info_request.get_last_public_response_event.should == @incoming_message.response_event
+ end
+ end
+
+ describe 'when asked for the last public outgoing event' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ @outgoing_message = @info_request.outgoing_messages.first
+ end
+
+ it 'should not return an event with a hidden prominence message' do
+ @outgoing_message.prominence = 'hidden'
+ @outgoing_message.save!
+ @info_request.get_last_public_outgoing_event.should == nil
+ end
+
+ it 'should not return an event with a requester_only prominence message' do
+ @outgoing_message.prominence = 'requester_only'
+ @outgoing_message.save!
+ @info_request.get_last_public_outgoing_event.should == nil
+ end
+
+ it 'should return an event with a normal prominence message' do
+ @outgoing_message.prominence = 'normal'
+ @outgoing_message.save!
+ @info_request.get_last_public_outgoing_event.should == @outgoing_message.info_request_events.first
+ end
+
+ end
+
+ describe 'when asked who can be sent a followup' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request_with_plain_incoming)
+ @incoming_message = @info_request.incoming_messages.first
+ @public_body = @info_request.public_body
+ end
+
+ it 'should not include details from a hidden prominence response' do
+ @incoming_message.prominence = 'hidden'
+ @incoming_message.save!
+ @info_request.who_can_followup_to.should == [[@public_body.name,
+ @public_body.request_email,
+ nil]]
+ end
+
+ it 'should not include details from a requester_only prominence response' do
+ @incoming_message.prominence = 'requester_only'
+ @incoming_message.save!
+ @info_request.who_can_followup_to.should == [[@public_body.name,
+ @public_body.request_email,
+ nil]]
+ end
+
+ it 'should include details from a normal prominence response' do
+ @incoming_message.prominence = 'normal'
+ @incoming_message.save!
+ @info_request.who_can_followup_to.should == [[@public_body.name,
+ @public_body.request_email,
+ nil],
+ ['Bob Responder',
+ "bob@example.com",
+ @incoming_message.id]]
+ end
+
+ end
+
+ describe 'when generating json for the api' do
+
+ before do
+ @user = mock_model(User, :json_for_api => { :id => 20,
+ :url_name => 'alaveteli_user',
+ :name => 'Alaveteli User',
+ :ban_text => '',
+ :about_me => 'Hi' })
+ end
+
+ it 'should return full user info for an internal request' do
+ @info_request = InfoRequest.new(:user => @user)
+ @info_request.user_json_for_api.should == { :id => 20,
+ :url_name => 'alaveteli_user',
+ :name => 'Alaveteli User',
+ :ban_text => '',
+ :about_me => 'Hi' }
+ end
end
+ describe 'when working out a subject for request emails' do
+
+ it 'should create a standard request subject' do
+ info_request = FactoryGirl.build(:info_request)
+ expected_text = "Freedom of Information request - #{info_request.title}"
+ info_request.email_subject_request.should == expected_text
+ end
+
+ end
+
+ describe 'when working out a subject for a followup emails' do
+
+ it "should not be confused by an nil subject in the incoming message" do
+ ir = info_requests(:fancy_dog_request)
+ im = mock_model(IncomingMessage,
+ :subject => nil,
+ :valid_to_reply_to? => true)
+ subject = ir.email_subject_followup im
+ subject.should match(/^Re: Freedom of Information request.*fancy dog/)
+ end
+
+ it "should return a hash with the user's name for an external request" do
+ @info_request = InfoRequest.new(:external_url => 'http://www.example.com',
+ :external_user_name => 'External User')
+ @info_request.user_json_for_api.should == {:name => 'External User'}
+ end
+
+ it 'should return "Anonymous user" for an anonymous external user' do
+ @info_request = InfoRequest.new(:external_url => 'http://www.example.com')
+ @info_request.user_json_for_api.should == {:name => 'Anonymous user'}
+ end
+ end
+ describe "#set_described_state and #log_event" do
+ context "a request" do
+ let(:request) { InfoRequest.create!(:title => "my request",
+ :public_body => public_bodies(:geraldine_public_body),
+ :user => users(:bob_smith_user)) }
+
+ context "a series of events on a request" do
+ it "should have sensible events after the initial request has been made" do
+ # An initial request is sent
+ # The logic that changes the status when a message is sent is mixed up
+ # in OutgoingMessage#send_message. So, rather than extract it (or call it)
+ # let's just duplicate what it does here for the time being.
+ request.log_event('sent', {})
+ request.set_described_state('waiting_response')
+
+ events = request.info_request_events
+ events.count.should == 1
+ events[0].event_type.should == "sent"
+ events[0].described_state.should == "waiting_response"
+ events[0].calculated_state.should == "waiting_response"
+ end
+
+ it "should have sensible events after a response is received to a request" do
+ # An initial request is sent
+ request.log_event('sent', {})
+ request.set_described_state('waiting_response')
+ # A response is received
+ # This is normally done in InfoRequest#receive
+ request.awaiting_description = true
+ request.log_event("response", {})
+
+ events = request.info_request_events
+ events.count.should == 2
+ events[0].event_type.should == "sent"
+ events[0].described_state.should == "waiting_response"
+ events[0].calculated_state.should == "waiting_response"
+ events[1].event_type.should == "response"
+ events[1].described_state.should be_nil
+ # TODO: Should calculated_status in this situation be "waiting_classification"?
+ # This would allow searches like "latest_status: waiting_classification" to be
+ # available to the user in "Advanced search"
+ events[1].calculated_state.should be_nil
+ end
+
+ it "should have sensible events after a request is classified by the requesting user" do
+ # An initial request is sent
+ request.log_event('sent', {})
+ request.set_described_state('waiting_response')
+ # A response is received
+ request.awaiting_description = true
+ request.log_event("response", {})
+ # The request is classified by the requesting user
+ # This is normally done in RequestController#describe_state
+ request.log_event("status_update", {})
+ request.set_described_state("waiting_response")
+
+ events = request.info_request_events
+ events.count.should == 3
+ events[0].event_type.should == "sent"
+ events[0].described_state.should == "waiting_response"
+ events[0].calculated_state.should == "waiting_response"
+ events[1].event_type.should == "response"
+ events[1].described_state.should be_nil
+ events[1].calculated_state.should == 'waiting_response'
+ events[2].event_type.should == "status_update"
+ events[2].described_state.should == "waiting_response"
+ events[2].calculated_state.should == "waiting_response"
+ end
+
+ it "should have sensible events after a normal followup is sent" do
+ # An initial request is sent
+ request.log_event('sent', {})
+ request.set_described_state('waiting_response')
+ # A response is received
+ request.awaiting_description = true
+ request.log_event("response", {})
+ # The request is classified by the requesting user
+ request.log_event("status_update", {})
+ request.set_described_state("waiting_response")
+ # A normal follow up is sent
+ # This is normally done in OutgoingMessage#send_message
+ request.log_event('followup_sent', {})
+ request.set_described_state('waiting_response')
+
+ events = request.info_request_events
+ events.count.should == 4
+ events[0].event_type.should == "sent"
+ events[0].described_state.should == "waiting_response"
+ events[0].calculated_state.should == "waiting_response"
+ events[1].event_type.should == "response"
+ events[1].described_state.should be_nil
+ events[1].calculated_state.should == 'waiting_response'
+ events[2].event_type.should == "status_update"
+ events[2].described_state.should == "waiting_response"
+ events[2].calculated_state.should == "waiting_response"
+ events[3].event_type.should == "followup_sent"
+ events[3].described_state.should == "waiting_response"
+ events[3].calculated_state.should == "waiting_response"
+ end
+
+ it "should have sensible events after a user classifies the request after a follow up" do
+ # An initial request is sent
+ request.log_event('sent', {})
+ request.set_described_state('waiting_response')
+ # A response is received
+ request.awaiting_description = true
+ request.log_event("response", {})
+ # The request is classified by the requesting user
+ request.log_event("status_update", {})
+ request.set_described_state("waiting_response")
+ # A normal follow up is sent
+ request.log_event('followup_sent', {})
+ request.set_described_state('waiting_response')
+ # The request is classified by the requesting user
+ request.log_event("status_update", {})
+ request.set_described_state("waiting_response")
+
+ events = request.info_request_events
+ events.count.should == 5
+ events[0].event_type.should == "sent"
+ events[0].described_state.should == "waiting_response"
+ events[0].calculated_state.should == "waiting_response"
+ events[1].event_type.should == "response"
+ events[1].described_state.should be_nil
+ events[1].calculated_state.should == 'waiting_response'
+ events[2].event_type.should == "status_update"
+ events[2].described_state.should == "waiting_response"
+ events[2].calculated_state.should == "waiting_response"
+ events[3].event_type.should == "followup_sent"
+ events[3].described_state.should == "waiting_response"
+ events[3].calculated_state.should == "waiting_response"
+ events[4].event_type.should == "status_update"
+ events[4].described_state.should == "waiting_response"
+ events[4].calculated_state.should == "waiting_response"
+ end
+ end
+
+ context "another series of events on a request" do
+ it "should have sensible event states" do
+ # An initial request is sent
+ request.log_event('sent', {})
+ request.set_described_state('waiting_response')
+ # An internal review is requested
+ request.log_event('followup_sent', {})
+ request.set_described_state('internal_review')
+
+ events = request.info_request_events
+ events.count.should == 2
+ events[0].event_type.should == "sent"
+ events[0].described_state.should == "waiting_response"
+ events[0].calculated_state.should == "waiting_response"
+ events[1].event_type.should == "followup_sent"
+ events[1].described_state.should == "internal_review"
+ events[1].calculated_state.should == "internal_review"
+ end
+
+ it "should have sensible event states" do
+ # An initial request is sent
+ request.log_event('sent', {})
+ request.set_described_state('waiting_response')
+ # An internal review is requested
+ request.log_event('followup_sent', {})
+ request.set_described_state('internal_review')
+ # The user marks the request as rejected
+ request.log_event("status_update", {})
+ request.set_described_state("rejected")
+
+ events = request.info_request_events
+ events.count.should == 3
+ events[0].event_type.should == "sent"
+ events[0].described_state.should == "waiting_response"
+ events[0].calculated_state.should == "waiting_response"
+ events[1].event_type.should == "followup_sent"
+ events[1].described_state.should == "internal_review"
+ events[1].calculated_state.should == "internal_review"
+ events[2].event_type.should == "status_update"
+ events[2].described_state.should == "rejected"
+ events[2].calculated_state.should == "rejected"
+ end
+ end
+
+ context "another series of events on a request" do
+ it "should have sensible event states" do
+ # An initial request is sent
+ request.log_event('sent', {})
+ request.set_described_state('waiting_response')
+ # The user marks the request as successful (I know silly but someone did
+ # this in https://www.whatdotheyknow.com/request/family_support_worker_redundanci)
+ request.log_event("status_update", {})
+ request.set_described_state("successful")
+
+ events = request.info_request_events
+ events.count.should == 2
+ events[0].event_type.should == "sent"
+ events[0].described_state.should == "waiting_response"
+ events[0].calculated_state.should == "waiting_response"
+ events[1].event_type.should == "status_update"
+ events[1].described_state.should == "successful"
+ events[1].calculated_state.should == "successful"
+ end
+
+ it "should have sensible event states" do
+ # An initial request is sent
+ request.log_event('sent', {})
+ request.set_described_state('waiting_response')
+
+ # A response is received
+ request.awaiting_description = true
+ request.log_event("response", {})
+
+ # The user marks the request as successful
+ request.log_event("status_update", {})
+ request.set_described_state("successful")
+
+ events = request.info_request_events
+ events.count.should == 3
+ events[0].event_type.should == "sent"
+ events[0].described_state.should == "waiting_response"
+ events[0].calculated_state.should == "waiting_response"
+ events[1].event_type.should == "response"
+ events[1].described_state.should be_nil
+ events[1].calculated_state.should == "successful"
+ events[2].event_type.should == "status_update"
+ events[2].described_state.should == "successful"
+ events[2].calculated_state.should == "successful"
+ end
+ end
+
+ context "another series of events on a request" do
+ it "should have sensible event states" do
+ # An initial request is sent
+ request.log_event('sent', {})
+ request.set_described_state('waiting_response')
+ # An admin sets the status of the request to 'gone postal' using
+ # the admin interface
+ request.log_event("edit", {})
+ request.set_described_state("gone_postal")
+
+ events = request.info_request_events
+ events.count.should == 2
+ events[0].event_type.should == "sent"
+ events[0].described_state.should == "waiting_response"
+ events[0].calculated_state.should == "waiting_response"
+ events[1].event_type.should == "edit"
+ events[1].described_state.should == "gone_postal"
+ events[1].calculated_state.should == "gone_postal"
+ end
+ end
+ end
+ end
+
+ describe 'when saving an info_request' do
+
+ before do
+ @info_request = InfoRequest.new(:external_url => 'http://www.example.com',
+ :external_user_name => 'Example User',
+ :title => 'Some request or other',
+ :public_body => public_bodies(:geraldine_public_body))
+ end
+
+ it "should call purge_in_cache and update_counter_cache" do
+ @info_request.should_receive(:purge_in_cache)
+ # Twice - once for save, once for destroy:
+ @info_request.should_receive(:update_counter_cache).twice
+ @info_request.save!
+ @info_request.destroy
+ end
+
+ end
+
+ describe 'when destroying an info_request' do
+
+ before do
+ @info_request = InfoRequest.new(:external_url => 'http://www.example.com',
+ :external_user_name => 'Example User',
+ :title => 'Some request or other',
+ :public_body => public_bodies(:geraldine_public_body))
+ end
+
+ it "should call update_counter_cache" do
+ @info_request.save!
+ @info_request.should_receive(:update_counter_cache)
+ @info_request.destroy
+ end
+
+ end
+
+ describe 'when changing a described_state' do
+
+ it "should change the counts on its PublicBody without saving a new version" do
+ pb = public_bodies(:geraldine_public_body)
+ old_version_count = pb.versions.count
+ old_successful_count = pb.info_requests_successful_count
+ old_not_held_count = pb.info_requests_not_held_count
+ ir = InfoRequest.new(:external_url => 'http://www.example.com',
+ :external_user_name => 'Example User',
+ :title => 'Some request or other',
+ :described_state => 'partially_successful',
+ :public_body => pb)
+ ir.save!
+ pb.info_requests_successful_count.should == (old_successful_count + 1)
+ ir.described_state = 'not_held'
+ ir.save!
+ pb.reload
+ pb.info_requests_successful_count.should == old_successful_count
+ pb.info_requests_not_held_count.should == (old_not_held_count + 1)
+ ir.described_state = 'successful'
+ ir.save!
+ pb.reload
+ pb.info_requests_successful_count.should == (old_successful_count + 1)
+ pb.info_requests_not_held_count.should == old_not_held_count
+ ir.destroy
+ pb.reload
+ pb.info_requests_successful_count.should == old_successful_count
+ pb.info_requests_successful_count.should == old_not_held_count
+ pb.versions.count.should == old_version_count
+ end
+
+ end
+
+ describe InfoRequest, 'when getting similar requests' do
+
+ before(:each) do
+ get_fixtures_xapian_index
+ end
+
+ it 'should return similar requests' do
+ similar, more = info_requests(:spam_1_request).similar_requests(1)
+ similar.results.first[:model].info_request.should == info_requests(:spam_2_request)
+ end
+
+ it 'should return a flag set to true' do
+ similar, more = info_requests(:spam_1_request).similar_requests(1)
+ more.should be_true
+ end
+
+ end
+
+ describe InfoRequest, 'when constructing the list of recent requests' do
+
+ before(:each) do
+ get_fixtures_xapian_index
+ end
+
+ describe 'when there are fewer than five successful requests' do
+
+ it 'should list the most recently sent and successful requests by the creation date of the
+ request event' do
+ # Make sure the newest response is listed first even if a request
+ # with an older response has a newer comment or was reclassified more recently:
+ # https://github.com/mysociety/alaveteli/issues/370
+ #
+ # This is a deliberate behaviour change, in that the
+ # previous behaviour (showing more-recently-reclassified
+ # requests first) was intentional.
+ request_events, request_events_all_successful = InfoRequest.recent_requests
+ previous = nil
+ request_events.each do |event|
+ if previous
+ previous.created_at.should be >= event.created_at
+ end
+ ['sent', 'response'].include?(event.event_type).should be_true
+ if event.event_type == 'response'
+ ['successful', 'partially_successful'].include?(event.calculated_state).should be_true
+ end
+ previous = event
+ end
+ end
+ end
+
+ it 'should coalesce duplicate requests' do
+ request_events, request_events_all_successful = InfoRequest.recent_requests
+ request_events.map(&:info_request).select{|x|x.url_title =~ /^spam/}.length.should == 1
+ end
+ end
+
+ describe InfoRequest, "when constructing a list of requests by query" do
+
+ before(:each) do
+ get_fixtures_xapian_index
+ end
+
+ def apply_filters(filters)
+ results = InfoRequest.request_list(filters, page=1, per_page=100, max_results=100)
+ results[:results].map(&:info_request)
+ end
+
+ it "should filter requests" do
+ apply_filters(:latest_status => 'all').should =~ InfoRequest.all
+
+ # default sort order is the request with the most recently created event first
+ apply_filters(:latest_status => 'all').should == InfoRequest.all(
+ :order => "(SELECT max(info_request_events.created_at)
+ FROM info_request_events
+ WHERE info_request_events.info_request_id = info_requests.id)
+ DESC")
+
+ apply_filters(:latest_status => 'successful').should =~ InfoRequest.all(
+ :conditions => "id in (
+ SELECT info_request_id
+ FROM info_request_events
+ WHERE NOT EXISTS (
+ SELECT *
+ FROM info_request_events later_events
+ WHERE later_events.created_at > info_request_events.created_at
+ AND later_events.info_request_id = info_request_events.info_request_id
+ AND later_events.described_state IS NOT null
+ )
+ AND info_request_events.described_state IN ('successful', 'partially_successful')
+ )")
+
+ end
+
+ it "should filter requests by date" do
+ # The semantics of the search are that it finds any InfoRequest
+ # that has any InfoRequestEvent created in the specified range
+ filters = {:latest_status => 'all', :request_date_before => '13/10/2007'}
+ apply_filters(filters).should =~ InfoRequest.all(
+ :conditions => "id IN (SELECT info_request_id
+ FROM info_request_events
+ WHERE created_at < '2007-10-13'::date)")
+
+ filters = {:latest_status => 'all', :request_date_after => '13/10/2007'}
+ apply_filters(filters).should =~ InfoRequest.all(
+ :conditions => "id IN (SELECT info_request_id
+ FROM info_request_events
+ WHERE created_at > '2007-10-13'::date)")
+
+ filters = {:latest_status => 'all',
+ :request_date_after => '13/10/2007',
+ :request_date_before => '01/11/2007'}
+ apply_filters(filters).should =~ InfoRequest.all(
+ :conditions => "id IN (SELECT info_request_id
+ FROM info_request_events
+ WHERE created_at BETWEEN '2007-10-13'::date
+ AND '2007-11-01'::date)")
+ end
+
+
+ it "should list internal_review requests as unresolved ones" do
+
+ # This doesn’t precisely duplicate the logic of the actual
+ # query, but it is close enough to give the same result with
+ # the current set of test data.
+ results = apply_filters(:latest_status => 'awaiting')
+ results.should =~ InfoRequest.all(
+ :conditions => "id IN (SELECT info_request_id
+ FROM info_request_events
+ WHERE described_state in (
+ 'waiting_response', 'waiting_clarification',
+ 'internal_review', 'gone_postal', 'error_message', 'requires_admin'
+ ) and not exists (
+ select *
+ from info_request_events later_events
+ where later_events.created_at > info_request_events.created_at
+ and later_events.info_request_id = info_request_events.info_request_id
+ ))")
+
+
+ results.include?(info_requests(:fancy_dog_request)).should == false
+
+ event = info_request_events(:useless_incoming_message_event)
+ event.described_state = event.calculated_state = "internal_review"
+ event.save!
+ rebuild_xapian_index
+ results = apply_filters(:latest_status => 'awaiting')
+ results.include?(info_requests(:fancy_dog_request)).should == true
+ end
+
+
+ end
end
diff --git a/spec/models/mail_server_log_spec.rb b/spec/models/mail_server_log_spec.rb
index d0a1d202f..67709b130 100644
--- a/spec/models/mail_server_log_spec.rb
+++ b/spec/models/mail_server_log_spec.rb
@@ -1,9 +1,22 @@
+# == Schema Information
+#
+# Table name: mail_server_logs
+#
+# id :integer not null, primary key
+# mail_server_log_done_id :integer
+# info_request_id :integer
+# order :integer not null
+# line :text not null
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe MailServerLog do
describe ".load_file" do
before :each do
- Configuration.stub!(:incoming_email_domain).and_return("example.com")
+ AlaveteliConfiguration.stub!(:incoming_email_domain).and_return("example.com")
File.stub_chain(:stat, :mtime).and_return(DateTime.new(2012, 10, 10))
end
@@ -64,8 +77,8 @@ describe MailServerLog do
describe ".email_addresses_on_line" do
before :each do
- Configuration.stub!(:incoming_email_domain).and_return("example.com")
- Configuration.stub!(:incoming_email_prefix).and_return("foi+")
+ AlaveteliConfiguration.stub!(:incoming_email_domain).and_return("example.com")
+ AlaveteliConfiguration.stub!(:incoming_email_prefix).and_return("foi+")
end
it "recognises a single incoming email" do
@@ -106,7 +119,7 @@ describe MailServerLog do
# Postfix logs for a single email go over multiple lines. They are all tied together with the Queue ID.
# See http://onlamp.com/onlamp/2004/01/22/postfix.html
it "loads the postfix log and untangles seperate email transactions using the queue ID" do
- Configuration.stub!(:incoming_email_domain).and_return("example.com")
+ AlaveteliConfiguration.stub!(:incoming_email_domain).and_return("example.com")
log.stub!(:rewind)
ir1 = info_requests(:fancy_dog_request)
ir2 = info_requests(:naughty_chicken_request)
@@ -135,7 +148,7 @@ describe MailServerLog do
describe ".scan_for_postfix_queue_ids" do
it "returns the queue ids of interest with the connected email addresses" do
- Configuration.stub!(:incoming_email_domain).and_return("example.com")
+ AlaveteliConfiguration.stub!(:incoming_email_domain).and_return("example.com")
MailServerLog.scan_for_postfix_queue_ids(log).should == {
"CB55836EE58C" => ["request-14-e0e09f97@example.com"],
"9634B16F7F7" => ["request-10-1234@example.com"]
diff --git a/spec/models/outgoing_mailer_spec.rb b/spec/models/outgoing_mailer_spec.rb
deleted file mode 100644
index 5d1ea2dfb..000000000
--- a/spec/models/outgoing_mailer_spec.rb
+++ /dev/null
@@ -1,140 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-
-describe OutgoingMailer, " when working out follow up addresses" do
- # This is done with fixtures as the code is a bit tangled with the way it
- # calls TMail. XXX untangle it and make these tests spread out and using
- # mocks. Put parts of the tests in spec/lib/tmail_extensions.rb
- before(:each) do
- load_raw_emails_data
- end
-
- it "should parse them right" do
- ir = info_requests(:fancy_dog_request)
- im = ir.incoming_messages[0]
-
- # check the basic entry in the fixture is fine
- OutgoingMailer.name_and_email_for_followup(ir, im).should == "FOI Person <foiperson@localhost>"
- OutgoingMailer.name_for_followup(ir, im).should == "FOI Person"
- OutgoingMailer.email_for_followup(ir, im).should == "foiperson@localhost"
- end
-
- it "should work when there is only an email address" do
- ir = info_requests(:fancy_dog_request)
- im = ir.incoming_messages[0]
-
- im.raw_email.data = im.raw_email.data.sub("\"FOI Person\" <foiperson@localhost>", "foiperson@localhost")
- im.parse_raw_email! true
-
- # check the basic entry in the fixture is fine
- OutgoingMailer.name_and_email_for_followup(ir, im).should == "foiperson@localhost"
- OutgoingMailer.name_for_followup(ir, im).should == "Geraldine Quango"
- OutgoingMailer.email_for_followup(ir, im).should == "foiperson@localhost"
- end
-
- it "should quote funny characters" do
- ir = info_requests(:fancy_dog_request)
- im = ir.incoming_messages[0]
-
- im.raw_email.data = im.raw_email.data.sub("FOI Person", "FOI [ Person")
- im.parse_raw_email! true
-
- # check the basic entry in the fixture is fine
- OutgoingMailer.name_and_email_for_followup(ir, im).should == "\"FOI [ Person\" <foiperson@localhost>"
- OutgoingMailer.name_for_followup(ir, im).should == "FOI [ Person"
- OutgoingMailer.email_for_followup(ir, im).should == "foiperson@localhost"
- end
-
- it "should quote quotes" do
- ir = info_requests(:fancy_dog_request)
- im = ir.incoming_messages[0]
-
- im.raw_email.data = im.raw_email.data.sub("FOI Person", "FOI \\\" Person")
- im.parse_raw_email! true
-
- # check the basic entry in the fixture is fine
- OutgoingMailer.name_and_email_for_followup(ir, im).should == "\"FOI \\\" Person\" <foiperson@localhost>"
- OutgoingMailer.name_for_followup(ir, im).should == "FOI \" Person"
- OutgoingMailer.email_for_followup(ir, im).should == "foiperson@localhost"
- end
-
- it "should quote @ signs" do
- ir = info_requests(:fancy_dog_request)
- im = ir.incoming_messages[0]
-
- im.raw_email.data = im.raw_email.data.sub("FOI Person", "FOI @ Person")
- im.parse_raw_email! true
-
- # check the basic entry in the fixture is fine
- OutgoingMailer.name_and_email_for_followup(ir, im).should == "\"FOI @ Person\" <foiperson@localhost>"
- OutgoingMailer.name_for_followup(ir, im).should == "FOI @ Person"
- OutgoingMailer.email_for_followup(ir, im).should == "foiperson@localhost"
- end
-
-end
-
-describe OutgoingMailer, "when working out follow up subjects" do
-
- before(:each) do
- load_raw_emails_data
- end
-
- it "should prefix the title with 'Freedom of Information request -' for initial requests" do
- ir = info_requests(:fancy_dog_request)
- im = ir.incoming_messages[0]
-
- ir.email_subject_request.should == "Freedom of Information request - Why do you have & such a fancy dog?"
- end
-
- it "should use 'Re:' and inital request subject for followups which aren't replies to particular messages" do
- ir = info_requests(:fancy_dog_request)
- om = outgoing_messages(:useless_outgoing_message)
-
- OutgoingMailer.subject_for_followup(ir, om).should == "Re: Freedom of Information request - Why do you have & such a fancy dog?"
- end
-
- it "should prefix with Re: the subject of the message being replied to" do
- ir = info_requests(:fancy_dog_request)
- im = ir.incoming_messages[0]
- om = outgoing_messages(:useless_outgoing_message)
- om.incoming_message_followup = im
-
- OutgoingMailer.subject_for_followup(ir, om).should == "Re: Geraldine FOI Code AZXB421"
- end
-
- it "should not add Re: prefix if there already is such a prefix" do
- ir = info_requests(:fancy_dog_request)
- im = ir.incoming_messages[0]
- om = outgoing_messages(:useless_outgoing_message)
- om.incoming_message_followup = im
-
- im.raw_email.data = im.raw_email.data.sub("Subject: Geraldine FOI Code AZXB421", "Subject: Re: Geraldine FOI Code AZXB421")
- OutgoingMailer.subject_for_followup(ir, om).should == "Re: Geraldine FOI Code AZXB421"
- end
-
- it "should not add Re: prefix if there already is a lower case re: prefix" do
- ir = info_requests(:fancy_dog_request)
- im = ir.incoming_messages[0]
- om = outgoing_messages(:useless_outgoing_message)
- om.incoming_message_followup = im
-
- im.raw_email.data = im.raw_email.data.sub("Subject: Geraldine FOI Code AZXB421", "Subject: re: Geraldine FOI Code AZXB421")
- im.parse_raw_email! true
-
- OutgoingMailer.subject_for_followup(ir, om).should == "re: Geraldine FOI Code AZXB421"
- end
-
- it "should use 'Re:' and initial request subject when replying to failed delivery notifications" do
- ir = info_requests(:fancy_dog_request)
- im = ir.incoming_messages[0]
- om = outgoing_messages(:useless_outgoing_message)
- om.incoming_message_followup = im
-
- im.raw_email.data = im.raw_email.data.sub("foiperson@localhost", "postmaster@localhost")
- im.raw_email.data = im.raw_email.data.sub("Subject: Geraldine FOI Code AZXB421", "Subject: Delivery Failed")
- im.parse_raw_email! true
-
- OutgoingMailer.subject_for_followup(ir, om).should == "Re: Freedom of Information request - Why do you have & such a fancy dog?"
- end
-end
-
-
diff --git a/spec/models/outgoing_message_spec.rb b/spec/models/outgoing_message_spec.rb
index 51bb6fdf5..a3e2d1c68 100644
--- a/spec/models/outgoing_message_spec.rb
+++ b/spec/models/outgoing_message_spec.rb
@@ -1,3 +1,21 @@
+# == Schema Information
+#
+# Table name: outgoing_messages
+#
+# id :integer not null, primary key
+# info_request_id :integer not null
+# body :text not null
+# status :string(255) not null
+# message_type :string(255) not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# last_sent_at :datetime
+# incoming_message_followup_id :integer
+# what_doing :string(255) not null
+# prominence :string(255) default("normal"), not null
+# prominence_reason :text
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe OutgoingMessage, " when making an outgoing message" do
@@ -16,7 +34,7 @@ describe OutgoingMessage, " when making an outgoing message" do
it "should not index the email addresses" do
# also used for track emails
@outgoing_message.get_text_for_indexing.should_not include("foo@bar.com")
- end
+ end
it "should not display email addresses on page" do
@outgoing_message.get_body_for_html_display.should_not include("foo@bar.com")
@@ -33,10 +51,112 @@ describe OutgoingMessage, " when making an outgoing message" do
it "should work out a salutation" do
@om.get_salutation.should == "Dear Geraldine Quango,"
end
+
+ it 'should produce the expected text for an internal review request' do
+ public_body = mock_model(PublicBody, :name => 'A test public body')
+ info_request = mock_model(InfoRequest, :public_body => public_body,
+ :url_title => 'a_test_title',
+ :title => 'A test title',
+ :apply_censor_rules_to_text! => nil,
+ :is_batch_request_template? => false)
+ outgoing_message = OutgoingMessage.new({
+ :status => 'ready',
+ :message_type => 'followup',
+ :what_doing => 'internal_review',
+ :info_request => info_request
+ })
+ expected_text = "I am writing to request an internal review of A test public body's handling of my FOI request 'A test title'."
+ outgoing_message.body.should include(expected_text)
+ end
+
+ context "when associated with a batch template request" do
+
+ it 'should produce a salutation with a placeholder' do
+ @om.info_request.is_batch_request_template = true
+ @om.get_salutation.should == 'Dear [Authority name],'
+ end
+ end
+
+
+ describe 'when asked if a user can view it' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ @outgoing_message = @info_request.outgoing_messages.first
+ end
+
+ context 'if the prominence is hidden' do
+
+ before do
+ @outgoing_message.prominence = 'hidden'
+ end
+
+ it 'should return true for an admin user' do
+ @outgoing_message.user_can_view?(FactoryGirl.create(:admin_user)).should be_true
+ end
+
+ it 'should return false for a non-admin user' do
+ @outgoing_message.user_can_view?(FactoryGirl.create(:user)).should be_false
+ end
+
+ end
+
+ context 'if the prominence is requester_only' do
+
+ before do
+ @outgoing_message.prominence = 'requester_only'
+ end
+
+ it 'should return true if the user owns the associated request' do
+ @outgoing_message.user_can_view?(@info_request.user).should be_true
+ end
+
+ it 'should return false if the user does not own the associated request' do
+ @outgoing_message.user_can_view?(FactoryGirl.create(:user)).should be_false
+ end
+ end
+
+ context 'if the prominence is normal' do
+
+ before do
+ @outgoing_message.prominence = 'normal'
+ end
+
+ it 'should return true for a non-admin user' do
+ @outgoing_message.user_can_view?(FactoryGirl.create(:user)).should be_true
+ end
+
+ end
+
+ end
+
+ describe 'when asked if it is indexed by search' do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ @outgoing_message = @info_request.outgoing_messages.first
+ end
+
+ it 'should return false if it has prominence "hidden"' do
+ @outgoing_message.prominence = 'hidden'
+ @outgoing_message.indexed_by_search?.should be_false
+ end
+
+ it 'should return false if it has prominence "requester_only"' do
+ @outgoing_message.prominence = 'requester_only'
+ @outgoing_message.indexed_by_search?.should be_false
+ end
+
+ it 'should return true if it has prominence "normal"' do
+ @outgoing_message.prominence = 'normal'
+ @outgoing_message.indexed_by_search?.should be_true
+ end
+
+ end
end
-describe IncomingMessage, " when censoring data" do
+describe OutgoingMessage, " when censoring data" do
before do
@om = outgoing_messages(:useless_outgoing_message)
@@ -56,4 +176,12 @@ describe IncomingMessage, " when censoring data" do
end
end
+describe OutgoingMessage, "when validating the format of the message body" do
+
+ it 'should handle a salutation with a bracket in it' do
+ outgoing_message = FactoryGirl.build(:initial_request)
+ outgoing_message.stub!(:get_salutation).and_return("Dear Bob (Robert,")
+ lambda{ outgoing_message.valid? }.should_not raise_error(RegexpError)
+ end
+end
diff --git a/spec/models/post_redirect_spec.rb b/spec/models/post_redirect_spec.rb
index 5f51b6de5..73740e914 100644
--- a/spec/models/post_redirect_spec.rb
+++ b/spec/models/post_redirect_spec.rb
@@ -1,3 +1,19 @@
+# == Schema Information
+#
+# Table name: post_redirects
+#
+# id :integer not null, primary key
+# token :text not null
+# uri :text not null
+# post_params_yaml :text
+# created_at :datetime not null
+# updated_at :datetime not null
+# email_token :text not null
+# reason_params_yaml :text
+# user_id :integer
+# circumstance :text default("normal"), not null
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe PostRedirect, " when constructing" do
diff --git a/spec/models/profile_photo_spec.rb b/spec/models/profile_photo_spec.rb
index 892cccd08..e70f474a0 100644
--- a/spec/models/profile_photo_spec.rb
+++ b/spec/models/profile_photo_spec.rb
@@ -1,11 +1,21 @@
+# == Schema Information
+#
+# Table name: profile_photos
+#
+# id :integer not null, primary key
+# data :binary not null
+# user_id :integer
+# draft :boolean default(FALSE), not null
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-describe ProfilePhoto, "when constructing a new photo" do
+describe ProfilePhoto, "when constructing a new photo" do
- before do
+ before do
@mock_user = mock_model(User)
end
-
+
it 'should take no image as invalid' do
profile_photo = ProfilePhoto.new(:data => nil, :user => @mock_user)
profile_photo.valid?.should == false
@@ -16,7 +26,15 @@ describe ProfilePhoto, "when constructing a new photo" do
profile_photo.valid?.should == false
end
- it 'should accept and convert a PNG to right size' do
+ it 'should translate a no image error message' do
+ I18n.with_locale(:es) do
+ profile_photo = ProfilePhoto.new(:data => nil, :user => @mock_user)
+ profile_photo.valid?.should == false
+ profile_photo.errors[:data].should == ['Por favor elige el fichero que contiene tu foto']
+ end
+ end
+
+ it 'should accept and convert a PNG to right size' do
data = load_file_fixture("parrot.png")
profile_photo = ProfilePhoto.new(:data => data, :user => @mock_user)
profile_photo.valid?.should == true
@@ -25,7 +43,7 @@ describe ProfilePhoto, "when constructing a new photo" do
profile_photo.image.rows.should == 96
end
- it 'should accept and convert a JPEG to right format and size' do
+ it 'should accept and convert a JPEG to right format and size' do
data = load_file_fixture("parrot.jpg")
profile_photo = ProfilePhoto.new(:data => data, :user => @mock_user)
profile_photo.valid?.should == true
@@ -34,7 +52,7 @@ describe ProfilePhoto, "when constructing a new photo" do
profile_photo.image.rows.should == 96
end
- it 'should accept a draft PNG and not resize it' do
+ it 'should accept a draft PNG and not resize it' do
data = load_file_fixture("parrot.png")
profile_photo = ProfilePhoto.new(:data => data, :draft => true)
profile_photo.valid?.should == true
@@ -43,6 +61,6 @@ describe ProfilePhoto, "when constructing a new photo" do
profile_photo.image.rows.should == 289
end
-
+
end
diff --git a/spec/models/public_body_change_request_spec.rb b/spec/models/public_body_change_request_spec.rb
new file mode 100644
index 000000000..0c4cea67b
--- /dev/null
+++ b/spec/models/public_body_change_request_spec.rb
@@ -0,0 +1,139 @@
+# == Schema Information
+#
+# Table name: public_body_change_requests
+#
+# id :integer not null, primary key
+# user_email :string(255)
+# user_name :string(255)
+# user_id :integer
+# public_body_name :text
+# public_body_id :integer
+# public_body_email :string(255)
+# source_url :text
+# notes :text
+# is_open :boolean default(TRUE), not null
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe PublicBodyChangeRequest, 'when validating' do
+
+ it 'should not be valid without a public body name' do
+ change_request = PublicBodyChangeRequest.new()
+ change_request.valid?.should be_false
+ change_request.errors[:public_body_name].should == ['Please enter the name of the authority']
+ end
+
+ it 'should not be valid without a user name if there is no user' do
+ change_request = PublicBodyChangeRequest.new(:public_body_name => 'New Body')
+ change_request.valid?.should be_false
+ change_request.errors[:user_name].should == ['Please enter your name']
+ end
+
+ it 'should not be valid without a user email address if there is no user' do
+ change_request = PublicBodyChangeRequest.new(:public_body_name => 'New Body')
+ change_request.valid?.should be_false
+ change_request.errors[:user_email].should == ['Please enter your email address']
+ end
+
+ it 'should be valid with a user and no name or email address' do
+ user = FactoryGirl.build(:user)
+ change_request = PublicBodyChangeRequest.new(:user => user,
+ :public_body_name => 'New Body')
+ change_request.valid?.should be_true
+ end
+
+ it 'should validate the format of a user email address entered' do
+ change_request = PublicBodyChangeRequest.new(:public_body_name => 'New Body',
+ :user_email => '@example.com')
+ change_request.valid?.should be_false
+ change_request.errors[:user_email].should == ["Your email doesn't look like a valid address"]
+ end
+
+ it 'should validate the format of a public body email address entered' do
+ change_request = PublicBodyChangeRequest.new(:public_body_name => 'New Body',
+ :public_body_email => '@example.com')
+ change_request.valid?.should be_false
+ change_request.errors[:public_body_email].should == ["The authority email doesn't look like a valid address"]
+ end
+
+end
+
+describe PublicBodyChangeRequest, 'get_user_name' do
+
+ it 'should return the user_name field if there is no user association' do
+ change_request = PublicBodyChangeRequest.new(:user_name => 'Test User')
+ change_request.get_user_name.should == 'Test User'
+ end
+
+ it 'should return the name of the associated user if there is one' do
+ user = FactoryGirl.build(:user)
+ change_request = PublicBodyChangeRequest.new(:user => user)
+ change_request.get_user_name.should == user.name
+ end
+
+end
+
+
+describe PublicBodyChangeRequest, 'get_user_email' do
+
+ it 'should return the user_email field if there is no user association' do
+ change_request = PublicBodyChangeRequest.new(:user_email => 'user@example.com')
+ change_request.get_user_email.should == 'user@example.com'
+ end
+
+ it 'should return the email of the associated user if there is one' do
+ user = FactoryGirl.build(:user)
+ change_request = PublicBodyChangeRequest.new(:user => user)
+ change_request.get_user_email.should == user.email
+ end
+
+end
+
+
+describe PublicBodyChangeRequest, 'get_public_body_name' do
+
+ it 'should return the public_body_name field if there is no public body association' do
+ change_request = PublicBodyChangeRequest.new(:public_body_name => 'Test Authority')
+ change_request.get_public_body_name.should == 'Test Authority'
+ end
+
+ it 'should return the name of the associated public body if there is one' do
+ public_body = FactoryGirl.build(:public_body)
+ change_request = PublicBodyChangeRequest.new(:public_body => public_body)
+ change_request.get_public_body_name.should == public_body.name
+ end
+
+end
+
+describe PublicBodyChangeRequest, 'when creating a comment for the associated public body' do
+
+ it 'should include requesting user, source_url and notes' do
+ change_request = PublicBodyChangeRequest.new(:user_name => 'Test User',
+ :user_email => 'test@example.com',
+ :source_url => 'http://www.example.com',
+ :notes => 'Some notes')
+ expected = "Requested by: Test User (test@example.com)\nSource URL: http://www.example.com\nNotes: Some notes"
+ change_request.comment_for_public_body.should == expected
+ end
+
+end
+
+describe PublicBodyChangeRequest, 'when creating a default subject for a response email' do
+
+ it 'should create an appropriate subject for a request to add a body' do
+ change_request = PublicBodyChangeRequest.new(:public_body_name => 'Test Body')
+ change_request.default_response_subject.should == 'Your request to add Test Body to Alaveteli'
+ end
+
+ it 'should create an appropriate subject for a request to update an email address' do
+ public_body = FactoryGirl.build(:public_body)
+ change_request = PublicBodyChangeRequest.new(:public_body => public_body)
+ change_request.default_response_subject.should == "Your request to update #{public_body.name} on Alaveteli"
+
+ end
+
+end
+
diff --git a/spec/models/public_body_spec.rb b/spec/models/public_body_spec.rb
index c2e0a6353..dc09bdfa6 100644
--- a/spec/models/public_body_spec.rb
+++ b/spec/models/public_body_spec.rb
@@ -1,3 +1,31 @@
+# encoding: UTF-8
+# == Schema Information
+#
+# Table name: public_bodies
+#
+# id :integer not null, primary key
+# name :text not null
+# short_name :text not null
+# request_email :text not null
+# version :integer not null
+# last_edit_editor :string(255) not null
+# last_edit_comment :text not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# url_name :text not null
+# home_page :text default(""), not null
+# notes :text default(""), not null
+# first_letter :string(255) not null
+# publication_scheme :text default(""), not null
+# api_key :string(255) not null
+# info_requests_count :integer default(0), not null
+# disclosure_log :text default(""), not null
+# info_requests_successful_count :integer
+# info_requests_not_held_count :integer
+# info_requests_overdue_count :integer
+# info_requests_visible_classified_count :integer
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe PublicBody, " using tags" do
@@ -135,40 +163,84 @@ describe PublicBody, " when saving" do
@public_body = PublicBody.new
end
+ def set_default_attributes(public_body)
+ public_body.name = "Testing Public Body"
+ public_body.short_name = "TPB"
+ public_body.request_email = "request@localhost"
+ public_body.last_edit_editor = "*test*"
+ public_body.last_edit_comment = "This is a test"
+ end
+
it "should not be valid without setting some parameters" do
@public_body.should_not be_valid
end
it "should not be valid with misformatted request email" do
- @public_body.name = "Testing Public Body"
- @public_body.short_name = "TPB"
+ set_default_attributes(@public_body)
@public_body.request_email = "requestBOOlocalhost"
- @public_body.last_edit_editor = "*test*"
- @public_body.last_edit_comment = "This is a test"
@public_body.should_not be_valid
@public_body.should have(1).errors_on(:request_email)
end
it "should save" do
- @public_body.name = "Testing Public Body"
- @public_body.short_name = "TPB"
- @public_body.request_email = "request@localhost"
- @public_body.last_edit_editor = "*test*"
- @public_body.last_edit_comment = "This is a test"
+ set_default_attributes(@public_body)
@public_body.save!
end
it "should update first_letter" do
- @public_body.name = "Testing Public Body"
- @public_body.short_name = "TPB"
- @public_body.request_email = "request@localhost"
- @public_body.last_edit_editor = "*test*"
- @public_body.last_edit_comment = "This is a test"
-
+ set_default_attributes(@public_body)
@public_body.first_letter.should be_nil
@public_body.save!
@public_body.first_letter.should == 'T'
end
+
+ it "should update first letter, even if it's a multibyte character" do
+ pb = PublicBody.new(:name => 'åccents, lower-case',
+ :short_name => 'ALC',
+ :request_email => 'foo@localhost',
+ :last_edit_editor => 'test',
+ :last_edit_comment => '')
+ pb.first_letter.should be_nil
+ pb.save!
+ pb.first_letter.should == 'Å'
+ end
+
+ it "should save the name when renaming an existing public body" do
+ public_body = public_bodies(:geraldine_public_body)
+ public_body.name = "Mark's Public Body"
+ public_body.save!
+
+ public_body.name.should == "Mark's Public Body"
+ end
+
+ it 'should update the right translation when in a locale with an underscore' do
+ AlaveteliLocalization.set_locales('he_IL', 'he_IL')
+ public_body = public_bodies(:humpadink_public_body)
+ translation_count = public_body.translations.size
+ public_body.name = 'Renamed'
+ public_body.save!
+ public_body.translations.size.should == translation_count
+ end
+
+ it 'should not create a new version when nothing has changed' do
+ @public_body.versions.size.should == 0
+ set_default_attributes(@public_body)
+ @public_body.save!
+ @public_body.versions.size.should == 1
+ @public_body.save!
+ @public_body.versions.size.should == 1
+ end
+
+ it 'should create a new version if something has changed' do
+ @public_body.versions.size.should == 0
+ set_default_attributes(@public_body)
+ @public_body.save!
+ @public_body.versions.size.should == 1
+ @public_body.name = 'Test'
+ @public_body.save!
+ @public_body.versions.size.should == 2
+ end
+
end
describe PublicBody, "when searching" do
@@ -210,7 +282,7 @@ describe PublicBody, "when searching" do
end
it "should cope with same url_name across multiple locales" do
- PublicBody.with_locale(:es) do
+ I18n.with_locale(:es) do
# use the unique spanish name to retrieve and edit
body = PublicBody.find_by_url_name_with_historic('etgq')
body.short_name = 'tgq' # Same as english version
@@ -228,10 +300,41 @@ describe PublicBody, "when searching" do
end
end
+describe PublicBody, "when asked for the internal_admin_body" do
+ before(:each) do
+ # Make sure that there's no internal_admin_body before each of
+ # these tests:
+ PublicBody.connection.delete("DELETE FROM public_bodies WHERE url_name = 'internal_admin_body'")
+ PublicBody.connection.delete("DELETE FROM public_body_translations WHERE url_name = 'internal_admin_body'")
+ end
+
+ it "should create the internal_admin_body if it didn't exist" do
+ iab = PublicBody.internal_admin_body
+ iab.should_not be_nil
+ end
+
+ it "should find the internal_admin_body even if the default locale has changed since it was created" do
+ with_default_locale("en") do
+ I18n.with_locale(:en) do
+ iab = PublicBody.internal_admin_body
+ iab.should_not be_nil
+ end
+ end
+ with_default_locale("es") do
+ I18n.with_locale(:es) do
+ iab = PublicBody.internal_admin_body
+ iab.should_not be_nil
+ end
+ end
+ end
+
+end
+
+
describe PublicBody, " when dealing public body locales" do
it "shouldn't fail if it internal_admin_body was created in a locale other than the default" do
# first time, do it with the non-default locale
- PublicBody.with_locale(:es) do
+ I18n.with_locale(:es) do
PublicBody.internal_admin_body
end
@@ -252,22 +355,24 @@ describe PublicBody, " when loading CSV files" do
errors.should == []
notes.size.should == 2
notes[0].should == "line 1: creating new authority 'aBody' (locale: en):\n\t{\"name\":\"aBody\"}"
- notes[1].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( [A-Za-z ]+\n)*You may want to delete them manually.\n/
+ notes[1].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
end
it "should do a dry run successfully" do
original_count = PublicBody.count
- csv_contents = load_file_fixture("fake-authority-type.csv")
+ csv_contents = normalize_string_to_utf8(load_file_fixture("fake-authority-type.csv"))
errors, notes = PublicBody.import_csv(csv_contents, '', 'replace', true, 'someadmin') # true means dry run
errors.should == []
- notes.size.should == 4
- notes[0..2].should == [
+ notes.size.should == 6
+ notes[0..4].should == [
"line 1: creating new authority 'North West Fake Authority' (locale: en):\n\t\{\"name\":\"North West Fake Authority\",\"request_email\":\"north_west_foi@localhost\"\}",
"line 2: creating new authority 'Scottish Fake Authority' (locale: en):\n\t\{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\"\}",
"line 3: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t\{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\"\}",
+ "line 4: creating new authority 'Gobierno de Aragón' (locale: en):\n\t\{\"name\":\"Gobierno de Arag\\u00f3n\",\"request_email\":\"spain_foi@localhost\"}",
+ "line 5: creating new authority 'Nordic æøå' (locale: en):\n\t{\"name\":\"Nordic \\u00e6\\u00f8\\u00e5\",\"request_email\":\"no_foi@localhost\"}"
]
- notes[3].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( [A-Za-z ]+\n)*You may want to delete them manually.\n/
+ notes[5].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
PublicBody.count.should == original_count
end
@@ -275,34 +380,38 @@ describe PublicBody, " when loading CSV files" do
it "should do full run successfully" do
original_count = PublicBody.count
- csv_contents = load_file_fixture("fake-authority-type.csv")
+ csv_contents = normalize_string_to_utf8(load_file_fixture("fake-authority-type.csv"))
errors, notes = PublicBody.import_csv(csv_contents, '', 'replace', false, 'someadmin') # false means real run
errors.should == []
- notes.size.should == 4
- notes[0..2].should == [
+ notes.size.should == 6
+ notes[0..4].should == [
"line 1: creating new authority 'North West Fake Authority' (locale: en):\n\t\{\"name\":\"North West Fake Authority\",\"request_email\":\"north_west_foi@localhost\"\}",
"line 2: creating new authority 'Scottish Fake Authority' (locale: en):\n\t\{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\"\}",
"line 3: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t\{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\"\}",
+ "line 4: creating new authority 'Gobierno de Aragón' (locale: en):\n\t\{\"name\":\"Gobierno de Arag\\u00f3n\",\"request_email\":\"spain_foi@localhost\"}",
+ "line 5: creating new authority 'Nordic æøå' (locale: en):\n\t{\"name\":\"Nordic \\u00e6\\u00f8\\u00e5\",\"request_email\":\"no_foi@localhost\"}"
]
- notes[3].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( [A-Za-z ]+\n)*You may want to delete them manually.\n/
+ notes[5].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
- PublicBody.count.should == original_count + 3
+ PublicBody.count.should == original_count + 5
end
it "should do imports without a tag successfully" do
original_count = PublicBody.count
- csv_contents = load_file_fixture("fake-authority-type.csv")
+ csv_contents = normalize_string_to_utf8(load_file_fixture("fake-authority-type.csv"))
errors, notes = PublicBody.import_csv(csv_contents, '', 'replace', false, 'someadmin') # false means real run
errors.should == []
- notes.size.should == 4
- notes[0..2].should == [
+ notes.size.should == 6
+ notes[0..4].should == [
"line 1: creating new authority 'North West Fake Authority' (locale: en):\n\t\{\"name\":\"North West Fake Authority\",\"request_email\":\"north_west_foi@localhost\"\}",
"line 2: creating new authority 'Scottish Fake Authority' (locale: en):\n\t\{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\"\}",
"line 3: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t\{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\"\}",
+ "line 4: creating new authority 'Gobierno de Aragón' (locale: en):\n\t\{\"name\":\"Gobierno de Arag\\u00f3n\",\"request_email\":\"spain_foi@localhost\"}",
+ "line 5: creating new authority 'Nordic æøå' (locale: en):\n\t{\"name\":\"Nordic \\u00e6\\u00f8\\u00e5\",\"request_email\":\"no_foi@localhost\"}"
]
- notes[3].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( [A-Za-z ]+\n)*You may want to delete them manually.\n/
- PublicBody.count.should == original_count + 3
+ notes[5].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
+ PublicBody.count.should == original_count + 5
end
it "should handle a field list and fields out of order" do
@@ -317,7 +426,7 @@ describe PublicBody, " when loading CSV files" do
"line 3: creating new authority 'Scottish Fake Authority' (locale: en):\n\t\{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\",\"home_page\":\"http://scottish.org\",\"tag_string\":\"scottish\"\}",
"line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t\{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\",\"tag_string\":\"fake aTag\"\}",
]
- notes[3].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( [A-Za-z ]+\n)*You may want to delete them manually.\n/
+ notes[3].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
PublicBody.count.should == original_count
end
@@ -374,11 +483,11 @@ describe PublicBody, " when loading CSV files" do
"line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\",\"tag_string\":\"fake aTag\"}",
"line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: es):\n\t{\"name\":\"Autoridad Irlandesa\"}",
]
- notes[6].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( [A-Za-z ]+\n)*You may want to delete them manually.\n/
+ notes[6].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
PublicBody.count.should == original_count + 3
- # XXX Not sure why trying to do a PublicBody.with_locale fails here. Seems related to
+ # XXX Not sure why trying to do a I18n.with_locale fails here. Seems related to
# the way categories are loaded every time from the PublicBody class. For now we just
# test some translation was done.
body = PublicBody.find_by_name('North West Fake Authority')
@@ -400,10 +509,24 @@ describe PublicBody, " when loading CSV files" do
"line 3: creating new authority 'Scottish Fake Authority' (locale: en):\n\t{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\",\"home_page\":\"http://scottish.org\",\"tag_string\":\"scottish\"}",
"line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\",\"tag_string\":\"fake aTag\"}",
]
- notes[3].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( [A-Za-z ]+\n)*You may want to delete them manually.\n/
+ notes[3].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
PublicBody.count.should == original_count
end
+
+ it "should be able to load CSV from a file as well as a string" do
+ # Essentially the same code is used for import_csv_from_file
+ # as import_csv, so this is just a basic check that
+ # import_csv_from_file can load from a file at all. (It would
+ # be easy to introduce a regression that broke this, because
+ # of the confusing change in behaviour of CSV.parse between
+ # Ruby 1.8 and 1.9.)
+ original_count = PublicBody.count
+ filename = file_fixture_name('fake-authority-type-with-field-names.csv')
+ PublicBody.import_csv_from_file(filename, '', 'replace', false, 'someadmin')
+ PublicBody.count.should == original_count + 3
+ end
+
end
describe PublicBody do
@@ -456,8 +579,91 @@ end
describe PublicBody, " when override all public body request emails set" do
it "should return the overridden request email" do
- MySociety::Config.should_receive(:get).with("OVERRIDE_ALL_PUBLIC_BODY_REQUEST_EMAILS", "").twice.and_return("catch_all_test_email@foo.com")
+ AlaveteliConfiguration.should_receive(:override_all_public_body_request_emails).twice.and_return("catch_all_test_email@foo.com")
@geraldine = public_bodies(:geraldine_public_body)
@geraldine.request_email.should == "catch_all_test_email@foo.com"
end
end
+
+describe PublicBody, "when calculating statistics" do
+
+ it "should not include unclassified or hidden requests in percentages" do
+ with_hidden_and_successful_requests do
+ totals_data = PublicBody.get_request_totals(n=3,
+ highest=true,
+ minimum_requests=1)
+ # For the total number of requests, we still include
+ # hidden or unclassified requests:
+ totals_data['public_bodies'][-1].name.should == "Geraldine Quango"
+ totals_data['totals'][-1].should == 4
+
+ # However, for percentages, don't include the hidden or
+ # unclassified requests. So, for the Geraldine Quango
+ # we've made sure that there are only two visible and
+ # classified requests, one of which is successful, so the
+ # percentage should be 50%:
+
+ percentages_data = PublicBody.get_request_percentages(column='info_requests_successful_count',
+ n=3,
+ highest=false,
+ minimum_requests=1)
+ geraldine_index = percentages_data['public_bodies'].index do |pb|
+ pb.name == "Geraldine Quango"
+ end
+
+ percentages_data['y_values'][geraldine_index].should == 50
+ end
+ end
+
+ it "should only return totals for those with at least a minimum number of requests" do
+ minimum_requests = 1
+ with_enough_info_requests = PublicBody.where(["info_requests_count >= ?",
+ minimum_requests]).length
+ all_data = PublicBody.get_request_totals 4, true, minimum_requests
+ all_data['public_bodies'].length.should == with_enough_info_requests
+ end
+
+ it "should only return percentages for those with at least a minimum number of requests" do
+ with_hidden_and_successful_requests do
+ # With minimum requests at 3, this should return nil
+ # (corresponding to zero public bodies) since the only
+ # public body with just more than 3 info requests (The
+ # Geraldine Quango) has a hidden and an unclassified
+ # request within this block:
+ minimum_requests = 3
+ with_enough_info_requests = PublicBody.where(["info_requests_visible_classified_count >= ?",
+ minimum_requests]).length
+ all_data = PublicBody.get_request_percentages(column='info_requests_successful_count',
+ n=10,
+ true,
+ minimum_requests)
+ all_data.should be_nil
+ end
+ end
+
+ it "should only return those with at least a minimum number of requests, but not tagged 'test'" do
+ hpb = PublicBody.find_by_name 'Department for Humpadinking'
+
+ original_tag_string = hpb.tag_string
+ hpb.add_tag_if_not_already_present 'test'
+
+ begin
+ minimum_requests = 1
+ with_enough_info_requests = PublicBody.where(["info_requests_count >= ?", minimum_requests])
+ all_data = PublicBody.get_request_totals 4, true, minimum_requests
+ all_data['public_bodies'].length.should == 3
+ ensure
+ hpb.tag_string = original_tag_string
+ end
+ end
+
+end
+
+describe PublicBody, 'when asked for popular bodies' do
+
+ it 'should return bodies correctly when passed the hyphenated version of the locale' do
+ AlaveteliConfiguration.stub!(:frontpage_publicbody_examples).and_return('')
+ PublicBody.popular_bodies('he-IL').should == [public_bodies(:humpadink_public_body)]
+ end
+
+end
diff --git a/spec/models/purge_request_spec.rb b/spec/models/purge_request_spec.rb
index 94fe01317..02b3d685d 100644
--- a/spec/models/purge_request_spec.rb
+++ b/spec/models/purge_request_spec.rb
@@ -1,12 +1,24 @@
+# == Schema Information
+#
+# Table name: purge_requests
+#
+# id :integer not null, primary key
+# url :string(255)
+# created_at :datetime not null
+# model :string(255) not null
+# model_id :integer not null
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
require 'fakeweb'
-describe PurgeRequest, "purging things" do
+describe PurgeRequest, "purging things" do
before do
+ PurgeRequest.destroy_all
FakeWeb.last_request = nil
end
- it 'should issue purge requests to the server' do
+ it 'should issue purge requests to the server' do
req = PurgeRequest.new(:url => "/begone_from_here",
:model => "don't care",
:model_id => "don't care")
@@ -16,7 +28,7 @@ describe PurgeRequest, "purging things" do
PurgeRequest.all().count.should == 0
end
- it 'should fail silently for a misconfigured server' do
+ it 'should fail silently for a misconfigured server' do
FakeWeb.register_uri(:get, %r|brokenv|, :body => "BROKEN")
config = MySociety::Config.load_default()
config['VARNISH_HOST'] = "brokencache"
@@ -29,4 +41,4 @@ describe PurgeRequest, "purging things" do
PurgeRequest.all().count.should == 0
end
end
-
+
diff --git a/spec/models/raw_email_spec.rb b/spec/models/raw_email_spec.rb
index ff2830a62..f86b35e99 100644
--- a/spec/models/raw_email_spec.rb
+++ b/spec/models/raw_email_spec.rb
@@ -1,3 +1,10 @@
+# == Schema Information
+#
+# Table name: raw_emails
+#
+# id :integer not null, primary key
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe User, "manipulating a raw email" do
diff --git a/spec/models/track_thing_spec.rb b/spec/models/track_thing_spec.rb
index c42eb5e8b..1c582564b 100644
--- a/spec/models/track_thing_spec.rb
+++ b/spec/models/track_thing_spec.rb
@@ -1,3 +1,19 @@
+# == Schema Information
+#
+# Table name: track_things
+#
+# id :integer not null, primary key
+# tracking_user_id :integer not null
+# track_query :string(255) not null
+# info_request_id :integer
+# tracked_user_id :integer
+# public_body_id :integer
+# track_medium :string(255) not null
+# track_type :string(255) default("internal_error"), not null
+# created_at :datetime
+# updated_at :datetime
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe TrackThing, "when tracking changes" do
@@ -23,7 +39,7 @@ describe TrackThing, "when tracking changes" do
it "will find existing tracks which are the same" do
track_thing = TrackThing.create_track_for_search_query('fancy dog')
- found_track = TrackThing.find_by_existing_track(users(:silly_name_user), track_thing)
+ found_track = TrackThing.find_existing(users(:silly_name_user), track_thing)
found_track.should == @track_thing
end
diff --git a/spec/models/track_things_sent_email_spec.rb b/spec/models/track_things_sent_email_spec.rb
index 6166f42ab..4675d0847 100644
--- a/spec/models/track_things_sent_email_spec.rb
+++ b/spec/models/track_things_sent_email_spec.rb
@@ -1,3 +1,16 @@
+# == Schema Information
+#
+# Table name: track_things_sent_emails
+#
+# id :integer not null, primary key
+# track_thing_id :integer not null
+# info_request_event_id :integer
+# user_id :integer
+# public_body_id :integer
+# created_at :datetime
+# updated_at :datetime
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe TrackThingsSentEmail, "when tracking things sent email" do
diff --git a/spec/models/user_info_request_sent_alert_spec.rb b/spec/models/user_info_request_sent_alert_spec.rb
index 971c5b8c1..69be1092b 100644
--- a/spec/models/user_info_request_sent_alert_spec.rb
+++ b/spec/models/user_info_request_sent_alert_spec.rb
@@ -1,3 +1,14 @@
+# == Schema Information
+#
+# Table name: user_info_request_sent_alerts
+#
+# id :integer not null, primary key
+# user_id :integer not null
+# info_request_id :integer not null
+# alert_type :string(255) not null
+# info_request_event_id :integer
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe UserInfoRequestSentAlert, " when blah" do
diff --git a/spec/models/user_mailer_spec.rb b/spec/models/user_mailer_spec.rb
deleted file mode 100644
index 0792aaab2..000000000
--- a/spec/models/user_mailer_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-
-describe UserMailer, " when blah" do
- before do
- end
-end
-
-
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index e31c3f1b5..b6f48dad3 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1,3 +1,27 @@
+# == Schema Information
+#
+# Table name: users
+#
+# id :integer not null, primary key
+# email :string(255) not null
+# name :string(255) not null
+# hashed_password :string(255) not null
+# salt :string(255) not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# email_confirmed :boolean default(FALSE), not null
+# url_name :text not null
+# last_daily_track_email :datetime default(2000-01-01 00:00:00 UTC)
+# admin_level :string(255) default("none"), not null
+# ban_text :text default(""), not null
+# about_me :text default(""), not null
+# locale :string(255)
+# email_bounced_at :datetime
+# email_bounce_message :text default(""), not null
+# no_limit :boolean default(FALSE), not null
+# receive_email_alerts :boolean default(TRUE), not null
+#
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe User, "making up the URL name" do
@@ -27,11 +51,22 @@ describe User, "showing the name" do
@user.name.should == 'Some Name'
end
- it 'should show if user has been banned' do
- @user.ban_text = "Naughty user"
- @user.name.should == 'Some Name (Account suspended)'
+ describe 'if user has been banned' do
+
+ before do
+ @user.ban_text = "Naughty user"
+ end
+
+ it 'should show an "Account suspended" suffix' do
+ @user.name.should == 'Some Name (Account suspended)'
+ end
+
+ it 'should return a string when the user has been banned, not a SafeBuffer' do
+ @user.name.class.should == String
+ end
end
+
end
describe User, " when authenticating" do
@@ -152,10 +187,10 @@ end
describe User, "when reindexing referencing models" do
before do
- @request_event = safe_mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
- @request = safe_mock_model(InfoRequest, :info_request_events => [@request_event])
- @comment_event = safe_mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
- @comment = safe_mock_model(Comment, :info_request_events => [@comment_event])
+ @request_event = mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
+ @request = mock_model(InfoRequest, :info_request_events => [@request_event])
+ @comment_event = mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
+ @comment = mock_model(Comment, :info_request_events => [@comment_event])
@user = User.new(:comments => [@comment], :info_requests => [@request])
end
@@ -302,3 +337,34 @@ describe User, "when emails have bounced" do
user.email_bounce_message.should == "The reason we think the email bounced (e.g. a bounce message)"
end
end
+
+describe User, "when calculating if a user has exceeded the request limit" do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ @user = @info_request.user
+ end
+
+ it 'should return false if no request limit is set' do
+ AlaveteliConfiguration.stub!(:max_requests_per_user_per_day).and_return nil
+ @user.exceeded_limit?.should be_false
+ end
+
+ it 'should return false if the user has not submitted more than the limit' do
+ AlaveteliConfiguration.stub!(:max_requests_per_user_per_day).and_return(2)
+ @user.exceeded_limit?.should be_false
+ end
+
+ it 'should return true if the user has submitted more than the limit' do
+ AlaveteliConfiguration.stub!(:max_requests_per_user_per_day).and_return(0)
+ @user.exceeded_limit?.should be_true
+ end
+
+ it 'should return false if the user is allowed to make batch requests' do
+ @user.can_make_batch_requests = true
+ AlaveteliConfiguration.stub!(:max_requests_per_user_per_day).and_return(0)
+ @user.exceeded_limit?.should be_false
+ end
+
+
+end
diff --git a/spec/models/xapian_spec.rb b/spec/models/xapian_spec.rb
index 8c99d550f..a1e060d8e 100644
--- a/spec/models/xapian_spec.rb
+++ b/spec/models/xapian_spec.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe User, " when indexing users with Xapian" do
@@ -8,8 +9,7 @@ describe User, " when indexing users with Xapian" do
end
it "should search by name" do
- # def InfoRequest.full_search(models, query, order, ascending, collapse, per_page, page)
- xapian_object = InfoRequest.full_search([User], "Silly", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([User], "Silly", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model].should == users(:silly_name_user)
end
@@ -17,8 +17,7 @@ describe User, " when indexing users with Xapian" do
it "should search by 'about me' text" do
user = users(:bob_smith_user)
- # def InfoRequest.full_search(models, query, order, ascending, collapse, per_page, page)
- xapian_object = InfoRequest.full_search([User], "stuff", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([User], "stuff", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model].should == user
@@ -26,10 +25,10 @@ describe User, " when indexing users with Xapian" do
user.save!
update_xapian_index
- xapian_object = InfoRequest.full_search([User], "stuff", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([User], "stuff", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([User], "aardvark", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([User], "aardvark", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model].should == user
end
@@ -42,26 +41,26 @@ describe PublicBody, " when indexing public bodies with Xapian" do
end
it "should search index the main name field" do
- xapian_object = InfoRequest.full_search([PublicBody], "humpadinking", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "humpadinking", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model].should == public_bodies(:humpadink_public_body)
end
it "should search index the notes field" do
- xapian_object = InfoRequest.full_search([PublicBody], "albatross", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "albatross", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model].should == public_bodies(:humpadink_public_body)
end
it "should delete public bodies from the index when they are destroyed" do
- xapian_object = InfoRequest.full_search([PublicBody], "albatross", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "albatross", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model].should == public_bodies(:humpadink_public_body)
public_bodies(:forlorn_public_body).destroy
update_xapian_index
- xapian_object = InfoRequest.full_search([PublicBody], "lonely", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "lonely", :limit => 100)
xapian_object.results.should == []
end
@@ -75,13 +74,13 @@ describe PublicBody, " when indexing requests by body they are to" do
end
it "should find requests to the body" do
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_from:tgq", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_from:tgq", :limit => 100)
xapian_object.results.size.should == PublicBody.find_by_url_name("tgq").info_requests.map(&:info_request_events).flatten.size
end
it "should update index correctly when URL name of body changes" do
# initial search
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_from:tgq", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_from:tgq", :limit => 100)
xapian_object.results.size.should == PublicBody.find_by_url_name("tgq").info_requests.map(&:info_request_events).flatten.size
models_found_before = xapian_object.results.map { |x| x[:model] }
@@ -93,9 +92,9 @@ describe PublicBody, " when indexing requests by body they are to" do
update_xapian_index
# check we get results expected
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_from:tgq", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_from:tgq", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_from:gq", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_from:gq", :limit => 100)
xapian_object.results.size.should == PublicBody.find_by_url_name("gq").info_requests.map(&:info_request_events).flatten.size
models_found_after = xapian_object.results.map { |x| x[:model] }
@@ -113,11 +112,11 @@ describe PublicBody, " when indexing requests by body they are to" do
update_xapian_index
# check we get results expected
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_from:tgq", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_from:tgq", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_from:gq", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_from:gq", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_from:" + body.url_name, 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_from:#{body.url_name}", :limit => 100)
xapian_object.results.size.should == public_bodies(:geraldine_public_body).info_requests.map(&:info_request_events).flatten.size
models_found_after = xapian_object.results.map { |x| x[:model] }
end
@@ -130,13 +129,14 @@ describe User, " when indexing requests by user they are from" do
end
it "should find requests from the user" do
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_by:bob_smith", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_by:bob_smith",
+ :sort_by_prefix => 'created_at', :sort_by_ascending => true, :limit => 100)
xapian_object.results.map{|x|x[:model]}.should =~ InfoRequestEvent.all(:conditions => "info_request_id in (select id from info_requests where user_id = #{users(:bob_smith_user).id})")
end
it "should find just the sent message events from a particular user" do
- # def InfoRequest.full_search(models, query, order, ascending, collapse, per_page, page)
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_by:bob_smith variety:sent", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_by:bob_smith variety:sent",
+ :sort_by_prefix => 'created_at', :sort_by_ascending => true, :limit => 100)
xapian_object.results.map{|x|x[:model]}.should =~ InfoRequestEvent.all(:conditions => "info_request_id in (select id from info_requests where user_id = #{users(:bob_smith_user).id}) and event_type = 'sent'")
xapian_object.results[2][:model].should == info_request_events(:useless_outgoing_message_event)
xapian_object.results[1][:model].should == info_request_events(:silly_outgoing_message_event)
@@ -150,8 +150,9 @@ describe User, " when indexing requests by user they are from" do
update_xapian_index
- # def InfoRequest.full_search(models, query, order, ascending, collapse, per_page, page)
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_by:bob_smith", 'created_at', true, 'request_collapse', 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_by:bob_smith",
+ :sort_by_prefix => 'created_at', :sort_by_ascending => true,
+ :collapse_by_prefix => 'request_collapse', :limit => 100)
xapian_object.results.map{|x|x[:model].info_request}.should =~ InfoRequest.all(:conditions => "user_id = #{users(:bob_smith_user).id}")
end
@@ -172,8 +173,7 @@ describe User, " when indexing requests by user they are from" do
update_xapian_index
- # def InfoRequest.full_search(models, query, order, ascending, collapse, per_page, page)
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_by:john_k", 'created_at', true, 'request_collapse', 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_by:john_k", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model].should == info_request_events(:silly_outgoing_message_event)
end
@@ -181,7 +181,8 @@ describe User, " when indexing requests by user they are from" do
it "should update index correctly when URL name of user changes" do
# initial search
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_by:bob_smith", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_by:bob_smith",
+ :sort_by_prefix => 'created_at', :sort_by_ascending => true, :limit => 100)
xapian_object.results.map{|x|x[:model]}.should =~ InfoRequestEvent.all(:conditions => "info_request_id in (select id from info_requests where user_id = #{users(:bob_smith_user).id})")
models_found_before = xapian_object.results.map { |x| x[:model] }
@@ -193,9 +194,10 @@ describe User, " when indexing requests by user they are from" do
update_xapian_index
# check we get results expected
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_by:bob_smith", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_by:bob_smith", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "requested_by:robert_smith", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "requested_by:robert_smith",
+ :sort_by_prefix => 'created_at', :sort_by_ascending => true, :limit => 100)
models_found_after = xapian_object.results.map { |x| x[:model] }
models_found_before.should == models_found_after
end
@@ -208,13 +210,13 @@ describe User, " when indexing comments by user they are by" do
end
it "should find requests from the user" do
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "commented_by:silly_emnameem", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "commented_by:silly_emnameem", :limit => 100)
xapian_object.results.size.should == 1
end
it "should update index correctly when URL name of user changes" do
# initial search
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "commented_by:silly_emnameem", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "commented_by:silly_emnameem", :limit => 100)
xapian_object.results.size.should == 1
models_found_before = xapian_object.results.map { |x| x[:model] }
@@ -226,9 +228,9 @@ describe User, " when indexing comments by user they are by" do
update_xapian_index
# check we get results expected
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "commented_by:silly_emnameem", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "commented_by:silly_emnameem", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "commented_by:silly_name", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "commented_by:silly_name", :limit => 100)
xapian_object.results.size.should == 1
models_found_after = xapian_object.results.map { |x| x[:model] }
@@ -243,7 +245,7 @@ describe InfoRequest, " when indexing requests by their title" do
end
it "should find events for the request" do
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "request:how_much_public_money_is_wasted_o", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "request:how_much_public_money_is_wasted_o", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model] == info_request_events(:silly_outgoing_message_event)
end
@@ -257,9 +259,9 @@ describe InfoRequest, " when indexing requests by their title" do
update_xapian_index
# check we get results expected
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "request:how_much_public_money_is_wasted_o", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "request:how_much_public_money_is_wasted_o", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "request:really_naughty", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "request:really_naughty", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model] == info_request_events(:silly_outgoing_message_event)
end
@@ -277,11 +279,11 @@ describe InfoRequest, " when indexing requests by tag" do
ir.save!
update_xapian_index
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "tag:bunnyrabbit", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "tag:bunnyrabbit", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model] == info_request_events(:silly_outgoing_message_event)
- xapian_object = InfoRequest.full_search([InfoRequestEvent], "tag:orangeaardvark", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], "tag:orangeaardvark", :limit => 100)
xapian_object.results.size.should == 0
end
end
@@ -298,14 +300,14 @@ describe PublicBody, " when indexing authorities by tag" do
body.save!
update_xapian_index
- xapian_object = InfoRequest.full_search([PublicBody], "tag:mice", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "tag:mice", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model] == public_bodies(:geraldine_public_body)
- xapian_object = InfoRequest.full_search([PublicBody], "tag:mice:3", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "tag:mice:3", :limit => 100)
xapian_object.results.size.should == 1
xapian_object.results[0][:model] == public_bodies(:geraldine_public_body)
- xapian_object = InfoRequest.full_search([PublicBody], "tag:orangeaardvark", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "tag:orangeaardvark", :limit => 100)
xapian_object.results.size.should == 0
end
end
@@ -327,11 +329,11 @@ describe PublicBody, " when only indexing selected things on a rebuild" do
values = false
texts = false
rebuild_xapian_index(terms, values, texts, dropfirst)
- xapian_object = InfoRequest.full_search([PublicBody], "tag:mice", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "tag:mice", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([PublicBody], "frobzn", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "frobzn", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([PublicBody], "variety:authority", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "variety:authority", :limit => 100)
xapian_object.results.map{|x|x[:model]}.should =~ PublicBody.all
# only reindex 'tag' and text
dropfirst = true
@@ -339,32 +341,78 @@ describe PublicBody, " when only indexing selected things on a rebuild" do
values = false
texts = true
rebuild_xapian_index(terms, values, texts, dropfirst)
- xapian_object = InfoRequest.full_search([PublicBody], "tag:mice", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "tag:mice", :limit => 100)
xapian_object.results.size.should == 1
- xapian_object = InfoRequest.full_search([PublicBody], "frobzn", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "frobzn", :limit => 100)
xapian_object.results.size.should == 1
- xapian_object = InfoRequest.full_search([PublicBody], "variety:authority", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "variety:authority", :limit => 100)
xapian_object.results.size.should == 0
# only reindex 'variety' term, but keeping the existing data in-place
dropfirst = false
terms = "V"
texts = false
rebuild_xapian_index(terms, values, texts, dropfirst)
- xapian_object = InfoRequest.full_search([PublicBody], "tag:mice", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "tag:mice", :limit => 100)
xapian_object.results.size.should == 1
- xapian_object = InfoRequest.full_search([PublicBody], "frobzn", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "frobzn", :limit => 100)
xapian_object.results.size.should == 1
- xapian_object = InfoRequest.full_search([PublicBody], "variety:authority", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "variety:authority", :limit => 100)
xapian_object.results.map{|x|x[:model]}.should =~ PublicBody.all
# only reindex 'variety' term, blowing away existing data
dropfirst = true
rebuild_xapian_index(terms, values, texts, dropfirst)
- xapian_object = InfoRequest.full_search([PublicBody], "tag:mice", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "tag:mice", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([PublicBody], "frobzn", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "frobzn", :limit => 100)
xapian_object.results.size.should == 0
- xapian_object = InfoRequest.full_search([PublicBody], "variety:authority", 'created_at', true, nil, 100, 1)
+ xapian_object = ActsAsXapian::Search.new([PublicBody], "variety:authority", :limit => 100)
xapian_object.results.map{|x|x[:model]}.should =~ PublicBody.all
end
end
+# I would expect ActsAsXapian to have some tests under lib/acts_as_xapian, but
+# it looks like this is not the case. Putting a test here instead.
+describe ActsAsXapian::Search, "#words_to_highlight" do
+ before(:each) do
+ load_raw_emails_data
+ get_fixtures_xapian_index
+ end
+
+ it "should return a list of words used in the search" do
+ s = ActsAsXapian::Search.new([PublicBody], "albatross words", :limit => 100)
+ s.words_to_highlight.should == ["albatross", "words"]
+ end
+
+ it "should remove any operators" do
+ s = ActsAsXapian::Search.new([PublicBody], "albatross words tag:mice", :limit => 100)
+ s.words_to_highlight.should == ["albatross", "words"]
+ end
+
+ # This is the current behaviour but it seems a little simplistic to me
+ it "should separate punctuation" do
+ s = ActsAsXapian::Search.new([PublicBody], "The doctor's patient", :limit => 100)
+ s.words_to_highlight.should == ["The", "doctor", "s", "patient"]
+ end
+
+ it "should handle non-ascii characters" do
+ s = ActsAsXapian::Search.new([PublicBody], "adatigénylés words tag:mice", :limit => 100)
+ s.words_to_highlight.should == ["adatigénylés", "words"]
+ end
+
+end
+
+describe InfoRequestEvent, " when faced with a race condition during xapian_mark_needs_index" do
+
+ before(:each) do
+ load_raw_emails_data
+ get_fixtures_xapian_index
+ end
+
+ it 'should not raise an error but should fail silently' do
+ with_duplicate_xapian_job_creation do
+ ir = info_requests(:naughty_chicken_request)
+ ir.reindex_request_events
+ end
+ end
+
+end
diff --git a/spec/script/mailin_spec.rb b/spec/script/mailin_spec.rb
index d80789635..46ad39f7f 100644
--- a/spec/script/mailin_spec.rb
+++ b/spec/script/mailin_spec.rb
@@ -4,7 +4,11 @@ require "external_command"
def mailin_test(email_filename)
Dir.chdir Rails.root do
xc = ExternalCommand.new("script/mailin")
- xc.run(load_file_fixture(email_filename))
+ mail = load_file_fixture(email_filename)
+ ir = info_requests(:other_request)
+ mail.gsub!('EMAIL_TO', ir.incoming_email)
+ mail.gsub!('EMAIL_FROM', 'responder@localhost')
+ xc.run(mail)
xc.err.should == ""
return xc
end
@@ -12,10 +16,24 @@ end
describe "When importing mail into the application" do
+ # Turn off transactional fixtures for this suite - incoming message is imported
+ # outside the transaction via ExternalCommand, so needs to be destroyed outside the
+ # transaction
+ self.use_transactional_fixtures = false
+
it "should not produce any output and should return a 0 code on importing a plain email" do
- r = mailin_test("incoming-request-plain.email")
+ r = mailin_test("incoming-request-empty.email")
r.status.should == 0
r.out.should == ""
end
-end \ No newline at end of file
+ # Destroy the incoming message so that it doesn't affect other tests
+ after do
+ ir = info_requests(:other_request)
+ incoming_message = ir.incoming_messages[0]
+ incoming_message.fully_destroy
+ # And get rid of any remaining purge requests
+ PurgeRequest.destroy_all
+ end
+
+end
diff --git a/spec/spec.opts b/spec/spec.opts
deleted file mode 100644
index ff5051c37..000000000
--- a/spec/spec.opts
+++ /dev/null
@@ -1,3 +0,0 @@
---colour
---format
-specdoc
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 561a75da6..dc5a0d6eb 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,292 +1,235 @@
-# This file is copied to ~/spec when you run 'ruby script/generate rspec'
-# from the project root directory.
-ENV["RAILS_ENV"] = 'test'
-require File.expand_path(File.join('..', '..', 'config', 'environment'), __FILE__)
-require 'spec/autorun'
-require 'spec/rails'
-
-# set a default username and password so we can test
-config = MySociety::Config.load_default()
-config['ADMIN_USERNAME'] = 'foo'
-config['ADMIN_PASSWORD'] = 'baz'
-
-# tests assume 20 days
-config['REPLY_LATE_AFTER_DAYS'] = 20
-
-# register a fake Varnish server
-require 'fakeweb'
-FakeWeb.register_uri(:purge, %r|varnish.localdomain|, :body => "OK")
-
-# Uncomment the next line to use webrat's matchers
-#require 'webrat/integrations/rspec-rails'
-
-# Use test-specific translations
-FastGettext.add_text_domain 'app', :path => File.join(File.dirname(__FILE__), 'fixtures', 'locale'), :type => :po
-FastGettext.default_text_domain = 'app'
-Spec::Runner.configure do |config|
- # If you're not using ActiveRecord you should remove these
- # lines, delete config/database.yml and disable :active_record
- # in your config/boot.rb
-
- # fixture_path must end in a separator
- config.fixture_path = File.join(Rails.root, 'spec', 'fixtures') + File::SEPARATOR
- config.global_fixtures = :users,
- :public_bodies,
- :public_body_translations,
- :public_body_versions,
- :info_requests,
- :raw_emails,
- :incoming_messages,
- :outgoing_messages,
- :comments,
- :info_request_events,
- :track_things,
- :foi_attachments,
- :has_tag_string_tags,
- :holidays,
- :track_things_sent_emails
-
- # This section makes the garbage collector run less often to speed up tests
- last_gc_run = Time.now
-
- config.before(:each) do
- GC.disable
- end
-
- config.after(:each) do
- if Time.now - last_gc_run > 4
- GC.enable
- GC.start
- last_gc_run = Time.now
+require 'rubygems'
+require 'spork'
+
+#uncomment the following line to use spork with the debugger
+#require 'spork/ext/ruby-debug'
+require 'simplecov'
+require 'coveralls'
+# Generate coverage locally in html as well as in coveralls.io
+SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
+ SimpleCov::Formatter::HTMLFormatter,
+ Coveralls::SimpleCov::Formatter
+]
+SimpleCov.start('rails') do
+ add_filter 'commonlib'
+ add_filter 'vendor/plugins'
+ add_filter 'lib/strip_attributes'
+ add_filter 'lib/has_tag_string'
+ add_filter 'lib/acts_as_xapian'
+ add_filter 'lib/themes'
+end
+
+Spork.prefork do
+ # Loading more in this block will cause your tests to run faster. However,
+ # if you change any configuration or code from libraries loaded here, you'll
+ # need to restart spork for it take effect.
+
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
+ ENV["RAILS_ENV"] ||= 'test'
+ require File.expand_path("../../config/environment", __FILE__)
+ require 'rspec/rails'
+ require 'rspec/autorun'
+
+ # Requires supporting ruby files with custom matchers and macros, etc,
+ # in spec/support/ and its subdirectories.
+ Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
+
+ # Use test-specific translations
+ AlaveteliLocalization.set_default_text_domain('app', File.join(File.dirname(__FILE__), 'fixtures', 'locale'))
+
+ RSpec.configure do |config|
+ # ## Mock Framework
+ #
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
+ #
+ # config.mock_with :mocha
+ # config.mock_with :flexmock
+ # config.mock_with :rr
+
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
+
+ # The order (!) of this is important thanks to foreign keys
+ config.global_fixtures = :users,
+ :public_bodies,
+ :public_body_translations,
+ :public_body_versions,
+ :info_requests,
+ :raw_emails,
+ :incoming_messages,
+ :outgoing_messages,
+ :comments,
+ :info_request_events,
+ :track_things,
+ :has_tag_string_tags,
+ :holidays
+
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
+ # examples within a transaction, remove the following line or assign false
+ # instead of true.
+ config.use_transactional_fixtures = true
+
+ # If true, the base class of anonymous controllers will be inferred
+ # automatically. This will be the default behavior in future versions of
+ # rspec-rails.
+ config.infer_base_class_for_anonymous_controllers = false
+
+ # Run specs in random order to surface order dependencies. If you find an
+ # order dependency and want to debug it, you can fix the order by providing
+ # the seed, which is printed after each run.
+ # --seed 1234
+ config.order = "random"
+
+ # This is a workaround for a strange thing where ActionMailer::Base.deliveries isn't being
+ # cleared out correctly in controller specs. So, do it here for everything.
+ config.before(:each) do
+ ActionMailer::Base.deliveries = []
end
- end
-
- # == Fixtures
- #
- # You can declare fixtures for each example_group like this:
- # describe "...." do
- # fixtures :table_a, :table_b
- #
- # Alternatively, if you prefer to declare them only once, you can
- # do so right here. Just uncomment the next line and replace the fixture
- # names with your fixtures.
- #
- # config.global_fixtures = :table_a, :table_b
- #
- # If you declare global fixtures, be aware that they will be declared
- # for all of your examples, even those that don't use them.
- #
- # You can also declare which fixtures to use (for example fixtures for test/fixtures):
- #
- # config.fixture_path = Rails.root + '/spec/fixtures/'
- #
- # == Mock Framework
- #
- # RSpec uses its own mocking framework by default. If you prefer to
- # use mocha, flexmock or RR, uncomment the appropriate line:
- #
- # config.mock_with :mocha
- # config.mock_with :flexmock
- # config.mock_with :rr
- #
- # == Notes
- #
- # For more information take a look at Spec::Runner::Configuration and Spec::Runner
-end
-# XXX No idea what namespace/class/module to put this in
-def receive_incoming_mail(email_name, email_to, email_from = 'geraldinequango@localhost')
- email_name = file_fixture_name(email_name)
- content = File.read(email_name)
- content.gsub!('EMAIL_TO', email_to)
- content.gsub!('EMAIL_FROM', email_from)
- RequestMailer.receive(content)
-end
+ # Any test that messes with the locale needs to restore the state afterwards so that it
+ # doesn't interfere with any subsequent tests. This is made more complicated by the
+ # ApplicationController#set_gettext_locale which sets the locale and so you may be setting
+ # the locale in your tests and not even realising it. So, let's make things easier for
+ # ourselves and just always restore the locale for all tests.
+ config.after(:each) do
+ AlaveteliLocalization.set_locales(AlaveteliConfiguration::available_locales,
+ AlaveteliConfiguration::default_locale)
+ end
-def file_fixture_name(file_name)
- return File.join(Spec::Runner.configuration.fixture_path, "files", file_name)
-end
+ # Turn routing-filter off in functional and unit tests as per
+ # https://github.com/svenfuchs/routing-filter/blob/master/README.markdown#testing
+ config.before(:each) do
+ RoutingFilter.active = false if [:controller, :helper, :model].include? example.metadata[:type]
+ end
-def load_file_fixture(file_name, as_binary=false)
- file_name = file_fixture_name(file_name)
- content = File.open(file_name, 'r') do |file|
- if as_binary
- file.set_encoding(Encoding::BINARY) if file.respond_to?(:set_encoding)
- end
- file.read
+ config.after(:each) do
+ RoutingFilter.active = true if [:controller, :helper, :model].include? example.metadata[:type]
end
- return content
-end
-def parse_all_incoming_messages
- IncomingMessage.find(:all).each{ |x| x.parse_raw_email! }
-end
+ # This section makes the garbage collector run less often to speed up tests
+ last_gc_run = Time.now
-def load_raw_emails_data
- raw_emails_yml = File.join(Spec::Runner.configuration.fixture_path, "raw_emails.yml")
- for raw_email_id in YAML::load_file(raw_emails_yml).map{|k,v| v["id"]} do
- raw_email = RawEmail.find(raw_email_id)
- raw_email.data = load_file_fixture("raw_emails/%d.email" % [raw_email_id])
+ config.before(:each) do
+ GC.disable
end
-end
-# Rebuild the current xapian index
-def rebuild_xapian_index(terms = true, values = true, texts = true, dropfirst = true)
- if dropfirst
- begin
- ActsAsXapian.readable_init
- FileUtils.rm_r(ActsAsXapian.db_path)
- rescue RuntimeError
- end
- ActsAsXapian.writable_init
- ActsAsXapian.writable_db.close
+ config.after(:each) do
+ if Time.now - last_gc_run > 4
+ GC.enable
+ GC.start
+ last_gc_run = Time.now
+ end
end
- parse_all_incoming_messages
- # safe_rebuild=true, which involves forking to avoid memory leaks, doesn't work well with rspec.
- # unsafe is significantly faster, and we can afford possible memory leaks while testing.
- models = [PublicBody, User, InfoRequestEvent]
- ActsAsXapian.rebuild_index(models, verbose=false, terms, values, texts, safe_rebuild=false)
-end
-
-# Create a clean xapian index based on the fixture files and the raw_email data.
-def create_fixtures_xapian_index
- load_raw_emails_data
- rebuild_xapian_index
-end
-
-def update_xapian_index
- ActsAsXapian.update_index(flush_to_disk=false, verbose=false)
-end
+ end
-# Copy the xapian index created in create_fixtures_xapian_index to a temporary
-# copy at the same level and point xapian at the copy
-def get_fixtures_xapian_index()
- # Create a base index for the fixtures if not already created
- $existing_xapian_db ||= create_fixtures_xapian_index
- # Store whatever the xapian db path is originally
- $original_xapian_path ||= ActsAsXapian.db_path
- path_array = $original_xapian_path.split(File::Separator)
- path_array.pop
- temp_path = File.join(path_array, 'test.temp')
- FileUtils.remove_entry_secure(temp_path, force=true)
- FileUtils.cp_r($original_xapian_path, temp_path)
- ActsAsXapian.db_path = temp_path
-end
+ # XXX No idea what namespace/class/module to put this in
+ # Create a clean xapian index based on the fixture files and the raw_email data.
+ def create_fixtures_xapian_index
+ load_raw_emails_data
+ rebuild_xapian_index
+ end
-# Validate an entire HTML page
-def validate_html(html)
- $tempfilecount = $tempfilecount + 1
- tempfilename = File.join(Dir::tmpdir, "railshtmlvalidate."+$$.to_s+"."+$tempfilecount.to_s+".html")
- File.open(tempfilename, "w+") do |f|
- f.puts html
- end
- if not system($html_validation_script, *($html_validation_script_options +[tempfilename]))
- raise "HTML validation error in " + tempfilename + " HTTP status: " + @response.response_code.to_s
- end
- File.unlink(tempfilename)
- return true
-end
+ # Use the before create job hook to simulate a race condition with
+ # another process by creating an acts_as_xapian_job record for the
+ # same model:
+ def with_duplicate_xapian_job_creation
+ InfoRequestEvent.module_eval do
+ def xapian_before_create_job_hook(action, model, model_id)
+ ActsAsXapian::ActsAsXapianJob.create!(:model => model,
+ :model_id => model_id,
+ :action => action)
+ end
+ end
+ yield
+ ensure
+ InfoRequestEvent.module_eval do
+ def xapian_before_create_job_hook(action, model, model_id)
+ end
+ end
+ end
-# Validate HTML fragment by wrapping it as the <body> of a page
-def validate_as_body(html)
- validate_html('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' +
- "<html><head><title>Test</title></head><body>#{html}</body></html>")
-end
+ def with_env_tz(new_tz = 'US/Eastern')
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+ yield
+ ensure
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+ end
-def basic_auth_login(request, username = nil, password = nil)
- username = Configuration::admin_username if username.nil?
- password = Configuration::admin_password if password.nil?
- request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("#{username}:#{password}")
-end
+ def with_active_record_default_timezone(zone)
+ old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
+ yield
+ ensure
+ ActiveRecord::Base.default_timezone = old_zone
+ end
-# Monkeypatch! Validate HTML in tests.
-$html_validation_script_found = false
-Configuration::utility_search_path.each do |d|
- $html_validation_script = File.join(d, "validate")
- $html_validation_script_options = ["--charset=utf-8"]
- if File.file? $html_validation_script and File.executable? $html_validation_script
- $html_validation_script_found = true
- break
+ # To test the statistics calculations, it's helpful to have the
+ # request fixtures in different states, but changing the fixtures
+ # themselves disrupts many other tests. This function takes a
+ # block, and runs that block with the info requests for the
+ # Geraldine Quango altered so that one is hidden and there's a
+ # successful one.
+ def with_hidden_and_successful_requests
+ external = info_requests(:external_request)
+ chicken = info_requests(:naughty_chicken_request)
+ old_external_prominence = external.prominence
+ old_chicken_described_state = chicken.described_state
+ begin
+ external.prominence = 'hidden'
+ external.save!
+ chicken.described_state = 'successful'
+ chicken.save!
+ yield
+ ensure
+ external.prominence = old_external_prominence
+ external.save!
+ chicken.described_state = old_chicken_described_state
+ chicken.save!
end
-end
-if $tempfilecount.nil?
- $tempfilecount = 0
- if $html_validation_script_found
- module ActionController
- module TestProcess
- # Hook into the process function, so can automatically get HTML after each request
- alias :original_process :process
- def is_fragment
- # XXX there must be a better way of doing this!
- return @request.query_parameters["action"] == "search_typeahead"
- end
- def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
- self.original_process(action, parameters, session, flash, http_method)
- # don't validate auto-generated HTML
- return if @request.query_parameters["action"] == "get_attachment_as_html"
- # XXX Is there a better way to check this than calling a private method?
- return unless @response.template.controller.instance_eval { integrate_views? }
- # And then if HTML, not a redirect (302, 301)
- if @response.content_type == "text/html" && ! [301,302,401].include?(@response.response_code)
- if !is_fragment
- validate_html(@response.body)
- else
- # it's a partial
- validate_as_body(@response.body)
- end
- end
- end
- end
- end
- else
- puts "WARNING: HTML validation script " + $html_validation_script + " not found"
- end
-end
+ end
-# to_ary differs in Ruby 1.8 and 1.9
-# @see http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
-def safe_mock_model(model, args = {})
- mock = mock_model(model, args)
- mock.should_receive(:to_ary).any_number_of_times
- mock
-end
+ # Reset the default locale, making sure that the previous default locale
+ # is also cleared from the fallbacks
+ def with_default_locale(locale)
+ original_default_locale = I18n.default_locale
+ original_fallbacks = I18n.fallbacks
+ I18n.fallbacks = nil
+ I18n.default_locale = locale
+ yield
+ ensure
+ I18n.fallbacks = original_fallbacks
+ I18n.default_locale = original_default_locale
+ end
-def get_fixture_mail(filename)
- MailHandler.mail_from_raw_email(load_file_fixture(filename))
-end
+ def load_test_categories
+ PublicBodyCategories.add(:en, [
+ "Local and regional",
+ [ "local_council", "Local councils", "a local council" ],
+ "Miscellaneous",
+ [ "other", "Miscellaneous", "miscellaneous" ],])
+ end
-def load_test_categories
- PublicBodyCategories.add(:en, [
- "Local and regional",
- [ "local_council", "Local councils", "a local council" ],
- "Miscellaneous",
- [ "other", "Miscellaneous", "miscellaneous" ],])
+ def basic_auth_login(request, username = nil, password = nil)
+ username = AlaveteliConfiguration::admin_username if username.nil?
+ password = AlaveteliConfiguration::admin_password if password.nil?
+ request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("#{username}:#{password}")
+ end
end
-
-# Monkeypatch applicationcontroller because the `render_to_string`
-# method in the original breaks all the rspec test assertions such as
-# `should render_template('foo')`. Same problem as
-# http://stackoverflow.com/questions/8174415/is-it-possible-to-assert-template-or-render-template-against-the-same-partial-wi
-# - a bug in either Rails or Rspec I don't have the time to fix :(
-
-class ApplicationController < ActionController::Base
- def set_popup_banner
- @popup_banner = nil
- end
+Spork.each_run do
+ FactoryGirl.definition_file_paths = [ Rails.root.join('spec', 'factories') ]
+ FactoryGirl.reload
+ # This code will be run each time you run your specs.
end
-
-def with_env_tz(new_tz = 'US/Eastern')
- old_tz, ENV['TZ'] = ENV['TZ'], new_tz
- yield
-ensure
- old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+def normalise_whitespace(s)
+ s = s.gsub(/\A\s+|\s+\Z/, "")
+ s = s.gsub(/\s+/, " ")
+ return s
end
-def with_active_record_default_timezone(zone)
- old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
- yield
-ensure
- ActiveRecord::Base.default_timezone = old_zone
+RSpec::Matchers.define :be_equal_modulo_whitespace_to do |expected|
+ match do |actual|
+ normalise_whitespace(actual) == normalise_whitespace(expected)
+ end
end
+
diff --git a/spec/support/email_helpers.rb b/spec/support/email_helpers.rb
new file mode 100644
index 000000000..252b1f137
--- /dev/null
+++ b/spec/support/email_helpers.rb
@@ -0,0 +1,23 @@
+def load_raw_emails_data
+ raw_emails_yml = File.join(RSpec.configuration.fixture_path, "raw_emails.yml")
+ for raw_email_id in YAML::load_file(raw_emails_yml).map{|k,v| v["id"]} do
+ raw_email = RawEmail.find(raw_email_id)
+ raw_email.data = load_file_fixture("raw_emails/%d.email" % [raw_email_id])
+ end
+end
+
+def receive_incoming_mail(email_name, email_to, email_from = 'geraldinequango@localhost')
+ email_name = file_fixture_name(email_name)
+ content = File.open(email_name, 'rb') { |f| f.read }
+ content.gsub!('EMAIL_TO', email_to)
+ content.gsub!('EMAIL_FROM', email_from)
+ RequestMailer.receive(content)
+end
+
+def get_fixture_mail(filename)
+ MailHandler.mail_from_raw_email(load_file_fixture(filename))
+end
+
+def parse_all_incoming_messages
+ IncomingMessage.find(:all).each{ |x| x.parse_raw_email! }
+end
diff --git a/spec/support/load_file_fixtures.rb b/spec/support/load_file_fixtures.rb
new file mode 100644
index 000000000..a54505e99
--- /dev/null
+++ b/spec/support/load_file_fixtures.rb
@@ -0,0 +1,8 @@
+def file_fixture_name(file_name)
+ return File.join(RSpec.configuration.fixture_path, "files", file_name)
+end
+
+def load_file_fixture(file_name)
+ file_name = file_fixture_name(file_name)
+ return File.open(file_name, 'rb') { |f| f.read }
+end
diff --git a/spec/support/xapian_index.rb b/spec/support/xapian_index.rb
new file mode 100644
index 000000000..344c28ebb
--- /dev/null
+++ b/spec/support/xapian_index.rb
@@ -0,0 +1,42 @@
+# Rebuild the current xapian index
+def rebuild_xapian_index(terms = true, values = true, texts = true, dropfirst = true)
+ if dropfirst
+ begin
+ ActsAsXapian.readable_init
+ FileUtils.rm_r(ActsAsXapian.db_path)
+ rescue RuntimeError
+ end
+ ActsAsXapian.writable_init
+ ActsAsXapian.writable_db.close
+ end
+ parse_all_incoming_messages
+ # safe_rebuild=true, which involves forking to avoid memory leaks, doesn't work well with rspec.
+ # unsafe is significantly faster, and we can afford possible memory leaks while testing.
+ models = [PublicBody, User, InfoRequestEvent]
+ ActsAsXapian.rebuild_index(models, verbose=false, terms, values, texts, safe_rebuild=false)
+end
+
+def update_xapian_index
+ ActsAsXapian.update_index(flush_to_disk=false, verbose=false)
+end
+
+# Copy the xapian index created in create_fixtures_xapian_index to a temporary
+# copy at the same level and point xapian at the copy
+def get_fixtures_xapian_index()
+ # Create a base index for the fixtures if not already created
+ $existing_xapian_db ||= create_fixtures_xapian_index
+ # Store whatever the xapian db path is originally
+ $original_xapian_path ||= ActsAsXapian.db_path
+ path_array = $original_xapian_path.split(File::Separator)
+ path_array.pop
+ temp_path = File.join(path_array, 'test.temp')
+ FileUtils.remove_entry_secure(temp_path, force=true)
+ FileUtils.cp_r($original_xapian_path, temp_path)
+ ActsAsXapian.db_path = temp_path
+end
+
+# Create a clean xapian index based on the fixture files and the raw_email data.
+def create_fixtures_xapian_index
+ load_raw_emails_data
+ rebuild_xapian_index
+end
diff --git a/spec/views/public_body/show.rhtml_spec.rb b/spec/views/public_body/show.html.erb_spec.rb
index b68b3f43b..0559fc8ef 100644
--- a/spec/views/public_body/show.rhtml_spec.rb
+++ b/spec/views/public_body/show.html.erb_spec.rb
@@ -1,6 +1,6 @@
require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
-describe "when viewing a body" do
+describe "public_body/show" do
before do
@pb = mock_model(PublicBody,
:name => 'Test Quango',
@@ -17,64 +17,57 @@ describe "when viewing a body" do
@pb.stub!(:is_requestable?).and_return(true)
@pb.stub!(:has_notes?).and_return(false)
@pb.stub!(:has_tag?).and_return(false)
- @xap = mock_model(ActsAsXapian::Search, :matches_estimated => 2)
+ @xap = mock(ActsAsXapian::Search, :matches_estimated => 2)
@xap.stub!(:results).and_return([
{ :model => mock_event },
{ :model => mock_event }
])
- assigns[:public_body] = @pb
- assigns[:track_thing] = mock_model(TrackThing,
- :track_type => 'public_body_updates', :public_body => @pb, :params => {})
- assigns[:xapian_requests] = @xap
- assigns[:page] = 1
- assigns[:per_page] = 10
- # work round a bug in ActionController::TestRequest; allows request.query_string to work in the template
- request.env["REQUEST_URI"] = ""
+ assign(:public_body, @pb)
+ assign(:track_thing, mock_model(TrackThing,
+ :track_type => 'public_body_updates', :public_body => @pb, :params => {}))
+ assign(:xapian_requests, @xap)
+ assign(:page, 1)
+ assign(:per_page, 10)
end
it "should be successful" do
- render "public_body/show"
- response.should be_success
- end
-
- it "should be valid HTML" do
- render "public_body/show"
- validate_as_body response.body
+ render
+ controller.response.should be_success
end
it "should show the body's name" do
- render "public_body/show"
- response.should have_tag("h1", "Test Quango")
+ render
+ response.should have_selector('h1', :content => "Test Quango")
end
it "should tell total number of requests" do
- render "public_body/show"
- response.should include_text("4 Freedom of Information requests")
+ render
+ response.should match "4 Freedom of Information requests"
end
it "should cope with no results" do
@pb.stub!(:info_requests).and_return([])
- render "public_body/show"
- response.should have_tag("p", /Nobody has made any Freedom of Information requests/m)
+ render
+ response.should have_selector('p', :content => "Nobody has made any Freedom of Information requests")
end
it "should cope with Xapian being down" do
- assigns[:xapian_requests] = nil
- render "public_body/show"
- response.should have_tag("p", /The search index is currently offline/m)
+ assign(:xapian_requests, nil)
+ render
+ response.should match "The search index is currently offline"
end
it "should link to Charity Commission site if we have numbers to do so" do
@pb.stub!(:has_tag?).and_return(true)
@pb.stub!(:get_tag_values).and_return(['98765', '12345'])
- render "public_body/show"
- response.should have_tag("div#header_right") do
- with_tag("a[href*=?]", /charity-commission.gov.uk.*RegisteredCharityNumber=98765$/)
+ render
+ response.should have_selector("div#header_right") do
+ have_selector "a", :href => /charity-commission.gov.uk.*RegisteredCharityNumber=98765$/
end
- response.should have_tag("div#header_right") do
- with_tag("a[href*=?]", /charity-commission.gov.uk.*RegisteredCharityNumber=12345$/)
+ response.should have_selector("div#header_right") do
+ have_selector "a", :href => /www.charity-commission.gov.uk.*RegisteredCharityNumber=12345$/
end
end
@@ -82,17 +75,17 @@ describe "when viewing a body" do
@pb.stub!(:has_tag?).and_return(true)
@pb.stub!(:get_tag_values).and_return(['SC1234'])
- render "public_body/show"
- response.should have_tag("div#header_right") do
- with_tag("a[href*=?]", /www.oscr.org.uk.*id=SC1234$/)
+ render
+ response.should have_selector("div#header_right") do
+ have_selector "a", :href => /www.oscr.org.uk.*id=SC1234$/
end
end
it "should not link to Charity Commission site if we don't have number" do
- render "public_body/show"
- response.should have_tag("div#header_right") do
- without_tag("a[href*=?]", /charity-commission.gov.uk/)
+ render
+ response.should have_selector("div#header_right") do
+ have_selector "a", :href => /charity-commission.gov.uk/
end
end
diff --git a/spec/views/reports/new.erb_spec.rb b/spec/views/reports/new.erb_spec.rb
new file mode 100644
index 000000000..66b738261
--- /dev/null
+++ b/spec/views/reports/new.erb_spec.rb
@@ -0,0 +1,29 @@
+require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
+
+describe 'reports/new.html.erb' do
+ let(:info_request) { mock_model(InfoRequest, :url_title => "foo", :report_reasons => ["Weird"]) }
+ before :each do
+ assign(:info_request, info_request)
+ end
+
+ it "should show a form" do
+ render
+ rendered.should have_selector("form")
+ end
+
+ context "request has already been reported" do
+ before :each do
+ info_request.stub!(:attention_requested).and_return(true)
+ end
+
+ it "should not show a form" do
+ render
+ rendered.should_not have_selector("form")
+ end
+
+ it "should say it's already been reported" do
+ render
+ rendered.should contain("This request has already been reported")
+ end
+ end
+end
diff --git a/spec/views/request/_after_actions.rhtml_spec.rb b/spec/views/request/_after_actions.html.erb_spec.rb
index 548990c9f..833323d68 100644
--- a/spec/views/request/_after_actions.rhtml_spec.rb
+++ b/spec/views/request/_after_actions.html.erb_spec.rb
@@ -15,51 +15,27 @@ describe 'when displaying actions that can be taken with regard to a request' do
:comments_allowed? => true,
:url_title => 'test_request',
:all_can_view? => true)
- assigns[:info_request] = @mock_request
- end
-
- def do_render
- render :partial => 'request/after_actions'
- end
-
- def expect_owner_div
- do_render
- response.should have_tag('div#owner_actions'){ yield }
- end
-
- def expect_anyone_div
- do_render
- response.should have_tag('div#anyone_actions'){ yield }
- end
-
- def expect_owner_link(text)
- expect_owner_div{ with_tag('a', :text => text) }
- end
-
- def expect_no_owner_link(text)
- expect_owner_div{ without_tag('a', :text => text) }
- end
-
- def expect_anyone_link(text)
- expect_anyone_div{ with_tag('a', :text => text) }
- end
-
- def expect_no_anyone_link(text)
- expect_anyone_div{ without_tag('a', :text => text) }
+ assign :info_request, @mock_request
end
describe 'if the request is old and unclassified' do
before do
- assigns[:old_unclassified] = true
+ assign :old_unclassified, true
end
it 'should not display a link for the request owner to update the status of the request' do
- expect_no_owner_link('Update the status of this request')
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#owner_actions') do |div|
+ div.should_not have_selector('a', :content => 'Update the status of this request')
+ end
end
it 'should display a link for anyone to update the status of the request' do
- expect_anyone_link('Update the status of this request')
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#anyone_actions') do |div|
+ div.should have_selector('a', :content => 'Update the status of this request')
+ end
end
end
@@ -67,35 +43,37 @@ describe 'when displaying actions that can be taken with regard to a request' do
describe 'if the request is not old and unclassified' do
before do
- assigns[:old_unclassified] = false
+ assign :old_unclassified, false
end
it 'should display a link for the request owner to update the status of the request' do
- expect_owner_link('Update the status of this request')
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#owner_actions') do |div|
+ div.should have_selector('a', :content => 'Update the status of this request')
+ end
end
it 'should not display a link for anyone to update the status of the request' do
- expect_no_anyone_link('Update the status of this request')
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#anyone_actions') do |div|
+ div.should_not have_selector('a', :content => 'Update the status of this request')
+ end
end
end
it 'should display a link for the request owner to request a review' do
- expect_owner_link('Request an internal review')
- end
-
- describe 'if the request is viewable by all' do
-
- it 'should display the link to download the entire request' do
- expect_anyone_link('Download a zip file of all correspondence')
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#owner_actions') do |div|
+ div.should have_selector('a', :content => 'Request an internal review')
end
end
- describe 'if the request is not viewable by all' do
- it 'should not display the link to download the entire request' do
- @mock_request.stub!(:all_can_view?).and_return(false)
- expect_no_anyone_link('Download a zip file of all correspondence')
+ it 'should display the link to download the entire request' do
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#anyone_actions') do |div|
+ div.should have_selector('a', :content => 'Download a zip file of all correspondence')
end
end
diff --git a/spec/views/request/_describe_state.rhtml_spec.rb b/spec/views/request/_describe_state.html.erb_spec.rb
index 18778d5d2..88dea53c5 100644
--- a/spec/views/request/_describe_state.rhtml_spec.rb
+++ b/spec/views/request/_describe_state.html.erb_spec.rb
@@ -4,12 +4,12 @@ describe 'when showing the form for describing the state of a request' do
def expect_radio_button(value)
do_render
- response.should have_tag("input[type=radio][value=#{value}]")
+ response.should have_selector('input', :type => 'radio', :value => value)
end
def expect_no_radio_button(value)
do_render
- response.should_not have_tag("input[type=radio][value=#{value}]")
+ response.should_not have_selector('input', :type => 'radio', :value => value)
end
def do_render
@@ -24,25 +24,25 @@ describe 'when showing the form for describing the state of a request' do
:user_name => @mock_user.name,
:is_external? => false
)
- assigns[:info_request] = @mock_request
+ assign :info_request, @mock_request
end
describe 'if the user is a regular user (not the request owner)' do
before do
- assigns[:is_owning_user] = false
+ assign :is_owning_user, false
end
describe 'if the request is not old and unclassified' do
it 'should not show the form' do
do_render
- response.should_not have_tag('h2', :text => 'What best describes the status of this request now?')
+ response.should_not have_selector('h2', :content => 'What best describes the status of this request now?')
end
it 'should give a link to login' do
do_render
- response.should have_tag('a', :text => 'sign in')
+ response.should have_selector('a', :content => 'sign in')
end
end
@@ -50,22 +50,22 @@ describe 'when showing the form for describing the state of a request' do
describe 'if the request is old and unclassified' do
before do
- assigns[:old_unclassified] = true
+ assign :old_unclassified, true
end
it 'should not show the form' do
do_render
- response.should_not have_tag('h2', :text => 'What best describes the status of this request now?')
+ response.should_not have_selector('h2', :content => 'What best describes the status of this request now?')
end
it 'should show the form for someone else to classify the request' do
do_render
- response.should have_tag('h2', :text => /We need your help/)
+ response.should have_selector('h2', :content => 'We need your help')
end
it 'should not give a link to login' do
do_render
- response.should_not have_tag('a', :text => 'sign in')
+ response.should_not have_selector('a', :content => 'sign in')
end
end
@@ -74,7 +74,7 @@ describe 'when showing the form for describing the state of a request' do
describe 'if showing the form to the user owning the request' do
before do
- assigns[:is_owning_user] = true
+ assign :is_owning_user, true
end
describe 'when the request is not in internal review' do
@@ -100,7 +100,7 @@ describe 'when showing the form for describing the state of a request' do
describe 'when the user has asked to update the status of the request' do
before do
- assigns[:update_status] = true
+ assign :update_status, true
end
it 'should show a radio button to set the status to "internal_review"' do
@@ -129,7 +129,7 @@ describe 'when showing the form for describing the state of a request' do
it 'should show the text "The review has finished and overall:"' do
do_render
- response.should have_tag('p', :text => 'The review has finished and overall:')
+ response.should have_selector('p', :content => 'The review has finished and overall:')
end
end
@@ -170,4 +170,4 @@ describe 'when showing the form for describing the state of a request' do
end
end
-end \ No newline at end of file
+end
diff --git a/spec/views/request/list.rhtml_spec.rb b/spec/views/request/list.rhtml_spec.rb
deleted file mode 100644
index 137bc359d..000000000
--- a/spec/views/request/list.rhtml_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
-
-describe "when listing recent requests" do
-
- before do
- assigns[:page] = 1
- assigns[:per_page] = 10
- # work round a bug in ActionController::TestRequest; allows request.query_string to work in the template
- request.env["REQUEST_URI"] = ""
- end
-
- def make_mock_event
- return mock_model(InfoRequestEvent,
- :info_request => mock_model(InfoRequest,
- :title => 'Title',
- :url_title => 'title',
- :display_status => 'awaiting_response',
- :calculate_status => 'awaiting_response',
- :public_body => mock_model(PublicBody, :name => 'Test Quango', :url_name => 'testquango'),
- :user => mock_model(User, :name => 'Test User', :url_name => 'testuser'),
- :is_external? => false
- ),
- :incoming_message => nil, :is_incoming_message? => false,
- :outgoing_message => nil, :is_outgoing_message? => false,
- :comment => nil, :is_comment? => false,
- :event_type => 'sent',
- :created_at => Time.now - 4.days,
- :search_text_main => ''
- )
- end
-
- it "should be successful" do
- assigns[:list_results] = [ make_mock_event, make_mock_event ]
- assigns[:matches_estimated] = 2
- assigns[:show_no_more_than] = 100
- render "request/list"
- response.should have_tag("div.request_listing")
- response.should_not have_tag("p", /No requests of this sort yet/m)
- end
-
- it "should cope with no results" do
- assigns[:list_results] = [ ]
- assigns[:matches_estimated] = 0
- assigns[:show_no_more_than] = 0
- render "request/list"
- response.should have_tag("p", /No requests of this sort yet/m)
- response.should_not have_tag("div.request_listing")
- end
-
-end
-
diff --git a/spec/views/request/show.html.erb_spec.rb b/spec/views/request/show.html.erb_spec.rb
new file mode 100644
index 000000000..6e63b9b43
--- /dev/null
+++ b/spec/views/request/show.html.erb_spec.rb
@@ -0,0 +1,110 @@
+require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
+
+describe 'request/show' do
+
+ before do
+ @mock_body = mock_model(PublicBody, :name => 'test body',
+ :url_name => 'test_body',
+ :is_school? => false)
+ @mock_user = mock_model(User, :name => 'test user',
+ :url_name => 'test_user',
+ :profile_photo => nil)
+ @mock_request = mock_model(InfoRequest, :title => 'test request',
+ :awaiting_description => false,
+ :law_used_with_a => 'A Freedom of Information request',
+ :law_used_full => 'Freedom of Information',
+ :public_body => @mock_body,
+ :user => @mock_user,
+ :user_name => @mock_user.name,
+ :is_external? => false,
+ :calculate_status => 'waiting_response',
+ :date_response_required_by => Date.today,
+ :prominence => 'normal',
+ :comments_allowed? => true,
+ :all_can_view? => true,
+ :url_title => 'test_request')
+ end
+
+ def request_page
+ assign :info_request, @mock_request
+ assign :info_request_events, []
+ assign :status, @mock_request.calculate_status
+ render
+ end
+
+ describe 'when a status update has been requested' do
+
+ before do
+ assign :update_status, true
+ end
+
+ it 'should show the first form for describing the state of the request' do
+ request_page
+ response.should have_selector("div.describe_state_form#describe_state_form_1")
+ end
+
+ end
+
+ describe 'when it is awaiting a description' do
+
+ before do
+ @mock_request.stub!(:awaiting_description).and_return(true)
+ end
+
+ it 'should show the first form for describing the state of the request' do
+ request_page
+ response.should have_selector("div.describe_state_form#describe_state_form_1")
+ end
+
+ it 'should show the second form for describing the state of the request' do
+ request_page
+ response.should have_selector("div.describe_state_form#describe_state_form_2")
+ end
+
+ end
+
+ describe 'when the user is the request owner' do
+
+ before do
+ assign :is_owning_user, true
+ end
+
+ describe 'when the request status is "waiting clarification"' do
+
+ before do
+ @mock_request.stub!(:calculate_status).and_return('waiting_clarification')
+ end
+
+ describe 'when there is a last response' do
+
+ before do
+ @mock_response = mock_model(IncomingMessage)
+ @mock_request.stub!(:get_last_public_response).and_return(@mock_response)
+ end
+
+
+ it 'should show a link to follow up the last response with clarification' do
+ request_page
+ expected_url = "/en/request/#{@mock_request.id}/response/#{@mock_response.id}#followup"
+ response.should have_selector("a", :href => expected_url, :content => 'send a follow up message')
+ end
+
+ end
+
+ describe 'when there is no last response' do
+
+ before do
+ @mock_request.stub!(:get_last_public_response).and_return(nil)
+ end
+
+
+ it 'should show a link to follow up the request without reference to a specific response' do
+ request_page
+ expected_url = "/en/request/#{@mock_request.id}/response#followup"
+ response.should have_selector("a", :href => expected_url, :content => 'send a follow up message')
+ end
+ end
+ end
+
+ end
+end
diff --git a/spec/views/request/show.rhtml_spec.rb b/spec/views/request/show.rhtml_spec.rb
deleted file mode 100644
index 4429e9e58..000000000
--- a/spec/views/request/show.rhtml_spec.rb
+++ /dev/null
@@ -1,128 +0,0 @@
-require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
-
-describe 'when viewing an information request' do
-
- before do
- @mock_body = mock_model(PublicBody, :name => 'test body',
- :url_name => 'test_body',
- :is_school? => false)
- @mock_user = mock_model(User, :name => 'test user',
- :url_name => 'test_user',
- :profile_photo => nil)
- @mock_request = mock_model(InfoRequest, :title => 'test request',
- :awaiting_description => false,
- :law_used_with_a => 'A Freedom of Information request',
- :law_used_full => 'Freedom of Information',
- :public_body => @mock_body,
- :user => @mock_user,
- :user_name => @mock_user.name,
- :is_external? => false,
- :calculate_status => 'waiting_response',
- :date_response_required_by => Date.today,
- :prominence => 'normal')
- end
-
- def request_page
- assigns[:info_request] = @mock_request
- assigns[:info_request_events] = []
- assigns[:status] = @mock_request.calculate_status
- template.stub!(:render_partial)
- render 'request/show'
- end
-
- it 'should show the sidebar' do
- template.should_receive(:render_partial).with(:partial => 'sidebar', :locals => {})
- request_page
- end
-
- it 'should show the actions people can take' do
- template.should_receive(:render_partial).with(:partial => 'after_actions', :locals => {})
- request_page
- end
-
- describe 'when a status update has been requested' do
-
- before do
- assigns[:update_status] = true
- end
-
- it 'should show the first form for describing the state of the request' do
- request_page
- response.should have_tag("div.describe_state_form#describe_state_form_1")
- end
-
- end
-
- describe 'when it is awaiting a description' do
-
- before do
- @mock_request.stub!(:awaiting_description).and_return(true)
- end
-
- it 'should show the first form for describing the state of the request' do
- request_page
- response.should have_tag("div.describe_state_form#describe_state_form_1")
- end
-
- it 'should show the second form for describing the state of the request' do
- request_page
- response.should have_tag("div.describe_state_form#describe_state_form_2")
- end
-
- end
-
- describe 'when the user is the request owner' do
-
- before do
- assigns[:is_owning_user] = true
- end
-
- describe 'when the request status is "waiting clarification"' do
-
- before do
- @mock_request.stub!(:calculate_status).and_return('waiting_clarification')
- end
-
- describe 'when there is a last response' do
-
- before do
- @mock_response = mock_model(IncomingMessage)
- @mock_request.stub!(:get_last_response).and_return(@mock_response)
- @old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
- end
- after do
- ActionController::Routing::Routes.filters = @old_filters
- end
-
-
- it 'should show a link to follow up the last response with clarification' do
- request_page
- expected_url = "http://test.host/request/#{@mock_request.id}/response/#{@mock_response.id}#followup"
- response.should have_tag("a[href=#{expected_url}]", :text => 'send a follow up message')
- end
-
- end
-
- describe 'when there is no last response' do
-
- before do
- @mock_request.stub!(:get_last_response).and_return(nil)
- @old_filters = ActionController::Routing::Routes.filters
- ActionController::Routing::Routes.filters = RoutingFilter::Chain.new
- end
- after do
- ActionController::Routing::Routes.filters = @old_filters
- end
-
-
- it 'should show a link to follow up the request without reference to a specific response' do
- request_page
- expected_url = "http://test.host/request/#{@mock_request.id}/response#followup"
- response.should have_tag("a[href=#{expected_url}]", :text => 'send a follow up message')
- end
- end
- end
-
- end
-end
diff --git a/spec/views/request_game/play.rhtml_spec.rb b/spec/views/request_game/play.html.erb_spec.rb
index 24fb6d75d..b5cf57c23 100644
--- a/spec/views/request_game/play.rhtml_spec.rb
+++ b/spec/views/request_game/play.html.erb_spec.rb
@@ -1,6 +1,6 @@
require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
-describe 'when viewing the request game' do
+describe 'request_game/play' do
before do
@mock_body = mock_model(PublicBody, :name => 'test body',
@@ -22,15 +22,15 @@ describe 'when viewing the request game' do
:initial_request_text => 'hi there',
:display_status => 'Awaiting categorisation',
:created_at => Time.now)
- assigns[:league_table_28_days] = []
- assigns[:league_table_all_time] = []
- assigns[:requests] = [@mock_request]
- assigns[:play_urls] = true
+ assign :league_table_28_days, []
+ assign :league_table_all_time, []
+ assign :requests, [@mock_request]
+ assign :play_urls, true
end
it 'should show the correct url for a request' do
- render "request_game/play"
- response.should include_text("/categorise/request/a_test_request")
+ render
+ response.should include("/categorise/request/a_test_request")
end