diff options
Diffstat (limited to 'spec')
19 files changed, 1079 insertions, 168 deletions
diff --git a/spec/controllers/admin_public_body_controller_spec.rb b/spec/controllers/admin_public_body_controller_spec.rb index be33802c5..504ddc5cc 100644 --- a/spec/controllers/admin_public_body_controller_spec.rb +++ b/spec/controllers/admin_public_body_controller_spec.rb @@ -166,6 +166,13 @@ describe AdminPublicBodyController, "when administering public bodies and paying config['SKIP_ADMIN_AUTH'] = true end + def setup_emergency_credentials(username, password) + config = MySociety::Config.load_default() + config['SKIP_ADMIN_AUTH'] = false + config['ADMIN_USERNAME'] = username + config['ADMIN_PASSWORD'] = password + @request.env["HTTP_AUTHORIZATION"] = "" + end it "disallows non-authenticated users to do anything" do @request.env["HTTP_AUTHORIZATION"] = "" @@ -180,19 +187,14 @@ describe AdminPublicBodyController, "when administering public bodies and paying config = MySociety::Config.load_default() config['SKIP_ADMIN_AUTH'] = true @request.env["HTTP_AUTHORIZATION"] = "" - n = PublicBody.count post :destroy, { :id => public_bodies(:forlorn_public_body).id } PublicBody.count.should == n - 1 session[:using_admin].should == 1 end - it "doesn't let people with bad credentials log in" do - config = MySociety::Config.load_default() - config['SKIP_ADMIN_AUTH'] = false - config['ADMIN_USERNAME'] = 'biz' - config['ADMIN_PASSWORD'] = 'fuz' - @request.env["HTTP_AUTHORIZATION"] = "" + it "doesn't let people with bad emergency account credentials log in" do + setup_emergency_credentials('biz', 'fuz') n = PublicBody.count basic_auth_login(@request, "baduser", "badpassword") post :destroy, { :id => public_bodies(:forlorn_public_body).id } @@ -201,12 +203,8 @@ describe AdminPublicBodyController, "when administering public bodies and paying session[:using_admin].should == nil end - it "allows people with good credentials log in using HTTP Basic Auth" do - config = MySociety::Config.load_default() - config['SKIP_ADMIN_AUTH'] = false - config['ADMIN_USERNAME'] = 'biz' - config['ADMIN_PASSWORD'] = 'fuz' - @request.env["HTTP_AUTHORIZATION"] = "" + it "allows people with good emergency account credentials log in using HTTP Basic Auth" do + setup_emergency_credentials('biz', 'fuz') n = PublicBody.count basic_auth_login(@request, "biz", "fuz") post :show, { :id => public_bodies(:humpadink_public_body).id, :emergency => 1} @@ -235,6 +233,33 @@ describe AdminPublicBodyController, "when administering public bodies and paying PublicBody.count.should == n session[:using_admin].should == nil end + + describe 'when asked for the admin current user' do + + it 'returns the emergency account name for someone who logged in with the emergency account' do + setup_emergency_credentials('biz', 'fuz') + basic_auth_login(@request, "biz", "fuz") + post :show, { :id => public_bodies(:humpadink_public_body).id, :emergency => 1 } + controller.send(:admin_current_user).should == 'biz' + end + + it 'returns the current user url_name for a superuser' do + session[:user_id] = users(:admin_user).id + @request.env["HTTP_AUTHORIZATION"] = "" + post :show, { :id => public_bodies(:humpadink_public_body).id } + controller.send(:admin_current_user).should == users(:admin_user).url_name + end + + it 'returns the REMOTE_USER value from the request environment when skipping admin auth' do + config = MySociety::Config.load_default() + config['SKIP_ADMIN_AUTH'] = true + @request.env["HTTP_AUTHORIZATION"] = "" + @request.env["REMOTE_USER"] = "i_am_admin" + post :show, { :id => public_bodies(:humpadink_public_body).id } + controller.send(:admin_current_user).should == "i_am_admin" + end + + end end describe AdminPublicBodyController, "when administering public bodies with i18n" do diff --git a/spec/controllers/api_controller_spec.rb b/spec/controllers/api_controller_spec.rb index ded9a040a..8d8a39950 100644 --- a/spec/controllers/api_controller_spec.rb +++ b/spec/controllers/api_controller_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') def normalise_whitespace(s) @@ -167,7 +168,7 @@ describe ApiController, "when using the API" do OutgoingMessage.count.should == n_outgoing_messages end - it "should not allow other people’s requests to be updated" do + it "should not allow other people's requests to be updated" do request_id = _create_request n_incoming_messages = IncomingMessage.count n_outgoing_messages = OutgoingMessage.count diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index f16cee312..08e68d85e 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -2,41 +2,41 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') require 'fakeweb' describe ApplicationController, "when accessing third party services" do + before (:each) do FakeWeb.clean_registry end + after (:each) do FakeWeb.clean_registry end + it "should succeed if the service responds OK" do - config = MySociety::Config.load_default() - config['GAZE_URL'] = 'http://denmark.com' + Configuration.stub!(:gaze_url).and_return('http://denmark.com') FakeWeb.register_uri(:get, %r|denmark.com|, :body => "DK") country = self.controller.send :country_from_ip country.should == "DK" end it "should fail silently if the country_from_ip domain doesn't exist" do - config = MySociety::Config.load_default() - config['GAZE_URL'] = 'http://12123sdf14qsd.com' + Configuration.stub!(:gaze_url).and_return('http://12123sdf14qsd.com') country = self.controller.send :country_from_ip - country.should == config['ISO_COUNTRY_CODE'] + country.should == Configuration.iso_country_code end it "should fail silently if the country_from_ip service doesn't exist" do - config = MySociety::Config.load_default() - config['GAZE_URL'] = 'http://www.google.com' + Configuration.stub!(:gaze_url).and_return('http://www.google.com') country = self.controller.send :country_from_ip - country.should == config['ISO_COUNTRY_CODE'] + country.should == Configuration.iso_country_code end it "should fail silently if the country_from_ip service returns an error" do FakeWeb.register_uri(:get, %r|500.com|, :body => "Error", :status => ["500", "Error"]) - config = MySociety::Config.load_default() - config['GAZE_URL'] = 'http://500.com' + Configuration.stub!(:gaze_url).and_return('http://500.com') country = self.controller.send :country_from_ip - country.should == config['ISO_COUNTRY_CODE'] + country.should == Configuration.iso_country_code end end describe ApplicationController, "when caching fragments" do + it "should not fail with long filenames" do long_name = "blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah.txt" path = self.controller.send(:foi_fragment_cache_path, long_name) diff --git a/spec/controllers/general_controller_spec.rb b/spec/controllers/general_controller_spec.rb index de8dd8422..935f8eab6 100644 --- a/spec/controllers/general_controller_spec.rb +++ b/spec/controllers/general_controller_spec.rb @@ -17,12 +17,38 @@ describe GeneralController, "when trying to show the blog" do end end -describe GeneralController, "when searching" do +describe GeneralController, 'when getting the blog feed' do + + 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") + get :blog + assigns[:feed_url].should == "http://blog.example.com?alt=rss&lang=en" + end + +end + +describe GeneralController, "when showing the frontpage" do + integrate_views - before(:each) do - load_raw_emails_data - rebuild_xapian_index + before do + public_body = mock_model(PublicBody, :name => "Example Public Body", + :url_name => 'example_public_body') + info_request = mock_model(InfoRequest, :public_body => public_body, + :title => 'Example Request', + :url_title => 'example_request') + info_request_event = mock_model(InfoRequestEvent, :created_at => Time.now, + :info_request => info_request, + :described_at => Time.now, + :search_text_main => 'example text') + xapian_result = mock('xapian result', :results => [{:model => info_request_event}]) + controller.stub!(:perform_search).and_return(xapian_result) end it "should render the front page successfully" do @@ -71,11 +97,6 @@ describe GeneralController, "when searching" do response.should be_success end - it "should redirect from search query URL to pretty URL" do - post :search_redirect, :query => "mouse" # query hidden in POST parameters - response.should redirect_to(:action => 'search', :combined => "mouse", :view => "all") # URL /search/:query/all - 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 @@ -117,23 +138,41 @@ describe GeneralController, "when searching" do 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 - load_raw_emails_data - rebuild_xapian_index + rebuild_xapian_index end - it 'should list the newest successful request first' do - # Make sure the newest is listed first even if an older one - # 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 - assigns[:request_events].first.info_request.should == info_requests(:another_boring_request) + 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 @@ -142,37 +181,44 @@ describe GeneralController, "when searching" do end end - describe 'when using xapian search' do +end - # rebuild xapian index after fixtures loaded - before(:each) do - load_raw_emails_data - rebuild_xapian_index - end +describe GeneralController, 'when using xapian search' do - it "should find info request when searching for '\"fancy dog\"'" do - get :search, :combined => ['"fancy dog"'] - response.should render_template('search') - assigns[:xapian_requests].matches_estimated.should == 1 - assigns[:xapian_requests].results.size.should == 1 - assigns[:xapian_requests].results[0][:model].should == info_request_events(:useless_outgoing_message_event) + integrate_views - assigns[:xapian_requests].words_to_highlight == ["fancy", "dog"] - end + # rebuild xapian index after fixtures loaded + before(:each) do + load_raw_emails_data + rebuild_xapian_index + end - it "should find public body and incoming message when searching for 'geraldine quango'" do - get :search, :combined => ['geraldine quango'] - response.should render_template('search') + it "should redirect from search query URL to pretty URL" do + post :search_redirect, :query => "mouse" # query hidden in POST parameters + response.should redirect_to(:action => 'search', :combined => "mouse", :view => "all") # URL /search/:query/all + end + + it "should find info request when searching for '\"fancy dog\"'" do + get :search, :combined => ['"fancy dog"'] + response.should render_template('search') + assigns[:xapian_requests].matches_estimated.should == 1 + assigns[:xapian_requests].results.size.should == 1 + assigns[:xapian_requests].results[0][:model].should == info_request_events(:useless_outgoing_message_event) + + assigns[:xapian_requests].words_to_highlight == ["fancy", "dog"] + end - assigns[:xapian_requests].matches_estimated.should == 1 - assigns[:xapian_requests].results.size.should == 1 - assigns[:xapian_requests].results[0][:model].should == info_request_events(:useless_incoming_message_event) + it "should find public body and incoming message when searching for 'geraldine quango'" do + get :search, :combined => ['geraldine quango'] + response.should render_template('search') - assigns[:xapian_bodies].matches_estimated.should == 1 - assigns[:xapian_bodies].results.size.should == 1 - assigns[:xapian_bodies].results[0][:model].should == public_bodies(:geraldine_public_body) - end + assigns[:xapian_requests].matches_estimated.should == 1 + assigns[:xapian_requests].results.size.should == 1 + assigns[:xapian_requests].results[0][:model].should == info_request_events(:useless_incoming_message_event) + assigns[:xapian_bodies].matches_estimated.should == 1 + assigns[:xapian_bodies].results.size.should == 1 + assigns[:xapian_bodies].results[0][:model].should == public_bodies(:geraldine_public_body) end it "should filter results based on end of URL being 'all'" do diff --git a/spec/controllers/public_body_controller_spec.rb b/spec/controllers/public_body_controller_spec.rb index 9eca43aee..d12818a1c 100644 --- a/spec/controllers/public_body_controller_spec.rb +++ b/spec/controllers/public_body_controller_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe PublicBodyController, "when showing a body" do diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb index 77f43b618..b0223588e 100644 --- a/spec/controllers/request_controller_spec.rb +++ b/spec/controllers/request_controller_spec.rb @@ -245,7 +245,7 @@ describe RequestController, "when showing one request" do response.should have_tag('#anyone_actions', /Add an annotation/) end end - + describe 'when the request does not allow comments' do it 'should not have a comment link' do get :show, { :url_title => 'spam_1' }, @@ -253,7 +253,7 @@ describe RequestController, "when showing one request" do response.should_not have_tag('#anyone_actions', /Add an annotation/) end end - + describe 'when the request is being viewed by an admin' do describe 'if the request is awaiting description' do @@ -1709,15 +1709,17 @@ describe RequestController, "sending overdue request alerts" do mail.to_addrs.first.to_s.should == info_requests(:naughty_chicken_request).user.name_and_email end - it "should send not actually send the overdue alert if the user is banned" do + it "should send not actually send the overdue alert if the user is banned but should + record it as sent" do user = info_requests(:naughty_chicken_request).user user.ban_text = 'Banned' user.save! - + UserInfoRequestSentAlert.find_all_by_user_id(user.id).count.should == 0 RequestMailer.alert_overdue_requests deliveries = ActionMailer::Base.deliveries deliveries.size.should == 0 + UserInfoRequestSentAlert.find_all_by_user_id(user.id).count.should > 0 end it "should send a very overdue alert mail to creators of very overdue requests" do @@ -1746,6 +1748,59 @@ describe RequestController, "sending overdue request alerts" do assigns[:info_request].should == info_requests(:naughty_chicken_request) end + it "should not resend alerts to people who've already received them" do + chicken_request = info_requests(:naughty_chicken_request) + chicken_request.outgoing_messages[0].last_sent_at = Time.now() - 60.days + chicken_request.outgoing_messages[0].save! + RequestMailer.alert_overdue_requests + chicken_mails = ActionMailer::Base.deliveries.select{|x| x.body =~ /chickens/} + chicken_mails.size.should == 1 + RequestMailer.alert_overdue_requests + chicken_mails = ActionMailer::Base.deliveries.select{|x| x.body =~ /chickens/} + chicken_mails.size.should == 1 + end + + it 'should send alerts for requests where the last event forming the initial request is a followup + being sent following a request for clarification' do + chicken_request = info_requests(:naughty_chicken_request) + chicken_request.outgoing_messages[0].last_sent_at = Time.now() - 60.days + chicken_request.outgoing_messages[0].save! + RequestMailer.alert_overdue_requests + chicken_mails = ActionMailer::Base.deliveries.select{|x| x.body =~ /chickens/} + chicken_mails.size.should == 1 + + # Request is waiting clarification + chicken_request.set_described_state('waiting_clarification') + + # Followup message is sent + outgoing_message = OutgoingMessage.new(:status => 'ready', + :message_type => 'followup', + :info_request_id => chicken_request.id, + :body => 'Some text', + :what_doing => 'normal_sort') + outgoing_message.send_message + outgoing_message.save! + + chicken_request = InfoRequest.find(chicken_request.id) + + # Last event forming the request is now the followup + chicken_request.last_event_forming_initial_request.event_type.should == 'followup_sent' + + # This isn't overdue, so no email + RequestMailer.alert_overdue_requests + chicken_mails = ActionMailer::Base.deliveries.select{|x| x.body =~ /chickens/} + chicken_mails.size.should == 1 + + # Make the followup older + outgoing_message.last_sent_at = Time.now() - 60.days + outgoing_message.save! + + # Now it should be alerted on + RequestMailer.alert_overdue_requests + chicken_mails = ActionMailer::Base.deliveries.select{|x| x.body =~ /chickens/} + chicken_mails.size.should == 2 + end + end describe RequestController, "sending unclassified new response reminder alerts" do diff --git a/spec/fixtures/files/forward-quoting-example-2.email b/spec/fixtures/files/forward-quoting-example-2.email new file mode 100644 index 000000000..936de3ddb --- /dev/null +++ b/spec/fixtures/files/forward-quoting-example-2.email @@ -0,0 +1,318 @@ +From foi@example.com Wed May 28 14:14:39 2008 +To: FOI Person <EMAIL_TO> +Subject: RE: Freedom of Information request +Date: Wed, 28 May 2008 14:16:55 +0100 +MIME-Version: 1.0 +Content-Type: multipart/alternative; + boundary="----=_NextPart_000_008E_01C8C0CD.7BC69660" + +This is a multi-part message in MIME format. + +------=_NextPart_000_008E_01C8C0CD.7BC69660 +Content-Type: text/plain; + charset="us-ascii" +Content-Transfer-Encoding: 7bit + +Dear Mr Smith + +Some information + +Yours sincerely + +Foi Officer + +Please note that my email address has changed to foi@example.com + + +From: Foi Officer [mailto:foi@example.com] On Behalf Of Foi Officer +Sent: 19 May 2008 12:24 +To: Bob Smith +Subject: RE: Freedom of Information request + +Dear Mr Smith + +Some information + +Please note that my email address has changed to foi@example.com + + +-----Original Message----- +From: Bob Smith [mailto:request-xxx-xxxxx@whatdotheyknow.com] +Sent: 18 May 2008 14:22 +To: FOI requests at Example +Subject: Freedom of Information request + Dear Sir or Madam, + + Some information? + + Yours faithfully, + Bob Smith + ------------------------------------------------------------------- + Is this the wrong address for Freedom of Information + requests to Example? If so please let us know + by emailing xxx@whatdotheyknow.com - we'll make sure future ones go + to the right place. + ------------------------------------------------------------------- + + +------=_NextPart_000_008E_01C8C0CD.7BC69660 +Content-Type: text/html; + charset="us-ascii" +Content-Transfer-Encoding: 7bit + +<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:st1="urn:schemas-microsoft-com:office:smarttags" xmlns="http://www.w3.org/TR/REC-html40"> + +<head> +<meta http-equiv=Content-Type content="text/html; charset=us-ascii"> +<meta name=ProgId content=Word.Document> +<meta name=Generator content="Microsoft Word 11"> +<meta name=Originator content="Microsoft Word 11"> +<link rel=File-List href="cid:filelist.xml@01C8C0CD.7B3EA1B0"> +<link rel=Edit-Time-Data href="cid:editdata.mso"> +<!--[if !mso]> +<style> +v\:* {behavior:url(#default#VML);} +o\:* {behavior:url(#default#VML);} +w\:* {behavior:url(#default#VML);} +.shape {behavior:url(#default#VML);} +</style> +<![endif]--> +<title>RE: Freedom of Information request </title> +<o:SmartTagType namespaceuri="urn:schemas-microsoft-com:office:smarttags" + name="Street"/> +<o:SmartTagType namespaceuri="urn:schemas-microsoft-com:office:smarttags" + name="address"/> +<!--[if gte mso 9]><xml> + <o:OfficeDocumentSettings> + <o:DoNotRelyOnCSS/> + </o:OfficeDocumentSettings> +</xml><![endif]--><!--[if gte mso 9]><xml> + <w:WordDocument> + <w:DisplayBackgroundShape/> + <w:SpellingState>Clean</w:SpellingState> + <w:GrammarState>Clean</w:GrammarState> + <w:DocumentKind>DocumentEmail</w:DocumentKind> + <w:EnvelopeVis/> + <w:ValidateAgainstSchemas/> + <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> + <w:IgnoreMixedContent>false</w:IgnoreMixedContent> + <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> + <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> + </w:WordDocument> +</xml><![endif]--><!--[if gte mso 9]><xml> + <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> + </w:LatentStyles> +</xml><![endif]--><!--[if !mso]> +<style> +st1\:*{behavior:url(#default#ieooui) } +</style> +<![endif]--> +<style> +<!-- + /* Font Definitions */ + @font-face + {font-family:Tahoma; + panose-1:2 11 6 4 3 5 4 4 2 4; + mso-font-charset:0; + mso-generic-font-family:swiss; + mso-font-pitch:variable; + mso-font-signature:1627421319 -2147483648 8 0 66047 0;} + /* Style Definitions */ + p.MsoNormal, li.MsoNormal, div.MsoNormal + {mso-style-parent:""; + margin:0in; + margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:12.0pt; + font-family:"Times New Roman"; + mso-fareast-font-family:"Times New Roman";} +a:link, span.MsoHyperlink + {color:blue; + text-decoration:underline; + text-underline:single;} +a:visited, span.MsoHyperlinkFollowed + {color:blue; + text-decoration:underline; + text-underline:single;} +p + {mso-margin-top-alt:auto; + margin-right:0in; + mso-margin-bottom-alt:auto; + margin-left:0in; + mso-pagination:widow-orphan; + font-size:12.0pt; + font-family:"Times New Roman"; + mso-fareast-font-family:"Times New Roman";} +span.EmailStyle18 + {mso-style-type:personal-reply; + mso-style-noshow:yes; + mso-ansi-font-size:10.0pt; + mso-bidi-font-size:10.0pt; + font-family:Arial; + mso-ascii-font-family:Arial; + mso-hansi-font-family:Arial; + mso-bidi-font-family:Arial; + color:navy;} +span.SpellE + {mso-style-name:""; + mso-spl-e:yes;} +span.GramE + {mso-style-name:""; + mso-gram-e:yes;} +@page Section1 + {size:595.3pt 841.9pt; + margin:1.0in 1.0in 1.0in 1.0in; + mso-header-margin:35.3pt; + mso-footer-margin:35.3pt; + mso-paper-source:0;} +div.Section1 + {page:Section1;} +--> +</style> +<!--[if gte mso 10]> +<style> + /* Style Definitions */ + table.MsoNormalTable + {mso-style-name:"Table Normal"; + mso-tstyle-rowband-size:0; + mso-tstyle-colband-size:0; + mso-style-noshow:yes; + mso-style-parent:""; + mso-padding-alt:0in 5.4pt 0in 5.4pt; + mso-para-margin:0in; + mso-para-margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:10.0pt; + font-family:"Times New Roman"; + mso-ansi-language:#0400; + mso-fareast-language:#0400; + mso-bidi-language:#0400;} +</style> +<![endif]--><!--[if gte mso 9]><xml> + <o:shapedefaults v:ext="edit" spidmax="1026" /> +</xml><![endif]--><!--[if gte mso 9]><xml> + <o:shapelayout v:ext="edit"> + <o:idmap v:ext="edit" data="1" /> + </o:shapelayout></xml><![endif]--> +</head> + +<body lang=EN-GB link=blue vlink=blue style='tab-interval:.5in'> + +<div class=Section1> + +<p class=MsoNormal><font size=2 color=navy face=Arial><span style='font-size: +10.0pt;font-family:Arial;color:navy'>Dear Mr Smith<o:p></o:p></span></font></p> + +<p class=MsoNormal><font size=2 color=navy face=Arial><span style='font-size: +10.0pt;font-family:Arial;color:navy'><o:p> </o:p></span></font></p> + +<p class=MsoNormal><font size=2 color=navy face=Arial><span style='font-size: +10.0pt;font-family:Arial;color:navy'>Some information.</p> + +<div> + +<p class=MsoNormal><strong><b><font size=2 color=navy face=Arial><span +style='font-size:10.0pt;font-family:Arial;color:navy;mso-no-proof:yes'>Please +note that my email address has changed to <a href="mailto:foi@example.com">foi@example.com</a></span></font></b></strong><font +color=navy><span style='color:navy;mso-no-proof:yes'><o:p></o:p></span></font></p> + +<p class=MsoNormal><font size=3 color=navy face="Times New Roman"><span +style='font-size:12.0pt;color:navy;mso-no-proof:yes'> <o:p></o:p></span></font></p> + +</div> + +<div> + +<div class=MsoNormal align=center style='text-align:center'><font size=3 +face="Times New Roman"><span lang=EN-US style='font-size:12.0pt;mso-ansi-language: +EN-US'> + +<hr size=2 width="100%" align=center tabindex=-1> + +</span></font></div> + +<p class=MsoNormal><b><font size=2 face=Tahoma><span lang=EN-US +style='font-size:10.0pt;font-family:Tahoma;mso-ansi-language:EN-US;font-weight: +bold'>From:</span></font></b><font size=2 face=Tahoma><span lang=EN-US +style='font-size:10.0pt;font-family:Tahoma;mso-ansi-language:EN-US'> Foi Officer +[mailto:foi@example.com] <b><span style='font-weight:bold'>On Behalf Of </span></b>Foi +Officer<br> +<b><span style='font-weight:bold'>Sent:</span></b> 19 May 2008 12:24<br> +<b><span style='font-weight:bold'>To:</span></b> Bob Smith<br> +<b><span style='font-weight:bold'>Subject:</span></b> RE: Freedom of +Information request </span></font><span lang=EN-US style='mso-ansi-language: +EN-US'><o:p></o:p></span></p> + +</div> + +<p class=MsoNormal><font size=3 face="Times New Roman"><span style='font-size: +12.0pt'><o:p> </o:p></span></font></p> + +<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>Dear Mr +Smith</span></font> <o:p></o:p></p> + +<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>Some information. </span></font><o:p></o:p></p> + + +<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>Yours +sincerely</span></font> <o:p></o:p></p> + +<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>Foi Officer</span></font> <o:p></o:p></p> + + +<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>-----Original +Message-----</span></font> <br> +<font size=2><span style='font-size:10.0pt'>From: Bob Smith [<a +href="mailto:request-xxx-xxxx@whatdotheyknow.com">mailto:request-xxx-xxxxx@whatdotheyknow.com</a>] +</span></font><br> +<font size=2><span style='font-size:10.0pt'>Sent: 18 May 2008 14:22</span></font> +<br> +<font size=2><span style='font-size:10.0pt'>To: FOI requests at Example</span></font> <br> +<font size=2><span style='font-size:10.0pt'>Subject: Freedom of Information +request</span></font> +<o:p></o:p></p> + +<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'> +Dear Sir or Madam,</span></font> <o:p></o:p></p> + +<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'> +Some information?</span></font> <font +color=navy><span style='color:navy'><span +style='mso-spacerun:yes'> </span></span></font><o:p></o:p></p> + +p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'> +Yours faithfully,</span></font> <o:p></o:p></p> + +<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'> +Bob Smith</span></font> <o:p></o:p></p> + +<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'> +-------------------------------------------------------------------</span></font> +<br> +<font size=2><span style='font-size:10.0pt'> Is +this the wrong address for Freedom of Information</span></font> <br> +<font size=2><span style='font-size:10.0pt'> requests +to Example? If so please let us know</span></font> <br> +<font size=2><span style='font-size:10.0pt'> by +emailing xxxx@whatdotheyknow.com - we'll make sure future ones go</span></font> +<br> +<font size=2><span style='font-size:10.0pt'> to the +right place.</span></font> <br> +<font size=2><span style='font-size:10.0pt'> +-------------------------------------------------------------------</span></font> +<o:p></o:p></p> + +<p class=MsoNormal><font size=3 face="Times New Roman"><span style='font-size: +12.0pt'><o:p> </o:p></span></font></p> + +</div> + + + + +</FONT></DIV></body></html> + +------=_NextPart_000_008E_01C8C0CD.7BC69660-- + + + diff --git a/spec/fixtures/files/forward-quoting-example.email b/spec/fixtures/files/forward-quoting-example.email new file mode 100644 index 000000000..80b29c533 --- /dev/null +++ b/spec/fixtures/files/forward-quoting-example.email @@ -0,0 +1,73 @@ +From foi@example.com Thu Apr 17 16:45:16 2008 +Return-path: <foi@example.com> +Envelope-to: request-xxx-xxxxx@whatdotheyknow.com +Delivery-date: Thu, 17 Apr 2008 16:45:16 +0100 +To: FOI Person <EMAIL_TO> +Subject: Re: Freedom of Information request +MIME-Version: 1.0 +From: FOI Officer <foi@example.com> +Date: Thu, 17 Apr 2008 16:44:53 +0100 +Content-Type: multipart/alternative; + boundary="=_alternative 0056816D8025742E_=" + +This is a multipart message in MIME format. + +--=_alternative 0056816D8025742E_= +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: 7bit + +17/04/08 + +Dear Mr Requester + +Some text. + +Bob Smith <request-xxx-xxxxx@whatdotheyknow.com> +17/04/08 15:57 + +To +FOI requests <foi@example.com> +cc + +Subject +Freedom of Information request + + + +--=_alternative 0056816D8025742E_= +Content-Type: text/html; charset="us-ascii" +Content-Transfer-Encoding: 7bit + + +<br><font size=2 face="sans-serif">17/04/08</font> +<br> +<br><font size=2 face="sans-serif">Dear Mr Requester</font> +<br> +<br><font size=2 face="Arial"><b>Some text.</font> +<br> +<br> +<br> +<table width=100%> +<tr valign=top> +<td width=40%><font size=1 face="sans-serif"><b> Bob Smith <request-xxx-xxxxx@whatdotheyknow.com></b> +</font> +<p><font size=1 face="sans-serif">17/04/08 15:57</font> +<td width=59%> +<table width=100%> +<tr> +<td> +<div align=right><font size=1 face="sans-serif">To</font></div> +<td valign=top><font size=1 face="sans-serif">FOI requests at <foi@example.com></font> +<tr> +<td> +<div align=right><font size=1 face="sans-serif">cc</font></div> +<td valign=top> +<tr> +<td> +<div align=right><font size=1 face="sans-serif">Subject</font></div> +<td valign=top><font size=1 face="sans-serif">Freedom of Information request</font></table> +<br> + + +--=_alternative 0056816D8025742E_=-- + diff --git a/spec/fixtures/files/lotus-notes-quoting.email b/spec/fixtures/files/lotus-notes-quoting.email new file mode 100644 index 000000000..863a2dfef --- /dev/null +++ b/spec/fixtures/files/lotus-notes-quoting.email @@ -0,0 +1,112 @@ +From foi@example.com Thu Mar 06 17:28:24 2008 +To: FOI Person <EMAIL_TO> +Subject: Re: Freedom of Information request +MIME-Version: 1.0 +X-Mailer: Lotus Notes Release 6.5.6 March 06, 2007 +From: foi <foi@example.com> +Date: Thu, 6 Mar 2008 17:31:51 +0000 +Content-Type: multipart/alternative; boundary="=_alternative 005FFC2E80257404_=" + +This is a multipart message in MIME format. +--=_alternative 005FFC2E80257404_= +Content-Type: text/plain; charset="ISO-8859-1" +Content-Transfer-Encoding: quoted-printable + +Dear Requester, + +Some text. + +Yours sincerely + + +FOI Team + + + +Bob Smith <request-xx-xxxxx@whatdotheyknow.com>=20 +Sent by: Bob Smith <request-xx-xxxxx@whatdotheyknow.com> +06/03/08 10:00 +Please respond to +Bob Smith <request-xx-xxxxx@whatdotheyknow.com> + + +To +FOI requests <foi@example.com> +cc + +Subject +Freedom of Information request + + + + + + + Original text + + + + +--=_alternative 005FFC2E80257404_= +Content-Type: text/html; charset="ISO-8859-1" +Content-Transfer-Encoding: quoted-printable + + +<div> +<br><font size=3D2 face=3D"Arial">Dear Requester,</font> +<br> +<br><font size=3D2 face=3D"Arial">Some text. </font> +<br> +<br><font size=3D2 face=3D"Arial">Yours sincerely</font> +<br> +<br> +<div> +<br><font size=3D2 face=3D"Arial">FOI Team</font> +<p> +<br> +<br> +<table width=3D100%> +<tr valign=3Dtop> +<td width=3D40%><font size=3D1 face=3D"sans-serif"><b>Bob Smith <re= +quest-xx-xxxxx@whatdotheyknow.com></b> +</font> +<br><font size=3D1 face=3D"sans-serif">Sent by: Bob Smith <request-= +bounce-xx-xxxxx@whatdotheyknow.com></font> +<p><font size=3D1 face=3D"sans-serif">06/03/08 10:00</font> +<table border> +<tr valign=3Dtop> +<td bgcolor=3Dwhite> +<div align=3Dcenter><font size=3D1 face=3D"sans-serif">Please respond to<br> +Bob Smith <request-xx-xxxxx@whatdotheyknow.com></font></div><= +/table> +<br> +<td width=3D59%> +<table width=3D100%> +<tr valign=3Dtop> +<td> +<div align=3Dright><font size=3D1 face=3D"sans-serif">To</font></div> +<td><font size=3D1 face=3D"sans-serif">FOI requests +<foi@example.com></font> +<tr valign=3Dtop> +<td> +<div align=3Dright><font size=3D1 face=3D"sans-serif">cc</font></div> +<td> +<tr valign=3Dtop> +<td> +<div align=3Dright><font size=3D1 face=3D"sans-serif">Subject</font></div> +<td><font size=3D1 face=3D"sans-serif">Freedom of Information request</font></table> +<br> +<table> +<tr valign=3Dtop> +<td> +<td></table> +<br></table> +<br> +<br> +<br><tt><font size=3D2> Original text<br> +<br> +<br> +</font></tt> +<br></div></div></div> +--=_alternative 005FFC2E80257404_=-- + diff --git a/spec/fixtures/files/space-boundary.email b/spec/fixtures/files/space-boundary.email new file mode 100644 index 000000000..37d65dbb5 --- /dev/null +++ b/spec/fixtures/files/space-boundary.email @@ -0,0 +1,37 @@ +From responder@example.com Wed Sep 03 10:44:18 2008 +Return-path: <responder@example.com> +Envelope-to: foi@sandwich.ukcod.org.uk +Delivery-date: Wed, 03 Sep 2008 10:44:18 +0100 +X-MimeOLE: Produced By Microsoft Exchange V6.5 +Content-class: urn:content-classes:message +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary= + "----_=_NextPart_001_01C90DA9.9B158D0D" +Subject: A subject +Date: Wed, 3 Sep 2008 10:44:05 +0100 +Message-ID: <D403C5D38B9BD6468928738951034DDE85A457@aglnex02.attorneygeneral.gsi.gov.uk> +X-MS-Has-Attach: yes +X-MS-TNEF-Correlator: +Thread-Topic: Freedom of Information request - An information request about + stuff +Thread-Index: AckNqOAr459KZB17Q1OIy1jxNVd9xAAAAnVAAAAWJ+A= +From: "A responder" <responder@example.com> +To: FOI Person <EMAIL_TO> + +This is a multi-part message in MIME format. + +------_=_NextPart_001_01C90DA9.9B158D0D +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: quoted-printable + +=20Apologies.=20Herewith. + +------_=_NextPart_001_01C90DA9.9B158D0D +Content-Type: application/octet-stream; name="Mr Gradwick.pdf" +Content-Transfer-Encoding: base64 +Content-Description: Mr Gradwick.pdf +Content-Disposition: attachment; filename="Mr Gradwick.pdf" + +xxx + +------_=_NextPart_001_01C90DA9.9B158D0D-- diff --git a/spec/fixtures/public_body_translations.yml b/spec/fixtures/public_body_translations.yml index f3453e853..24b14c470 100644 --- a/spec/fixtures/public_body_translations.yml +++ b/spec/fixtures/public_body_translations.yml @@ -9,6 +9,7 @@ geraldine_es_public_body_translation: locale: es notes: "" publication_scheme: "" + disclosure_log: "" geraldine_en_public_body_translation: name: Geraldine Quango @@ -21,6 +22,7 @@ geraldine_en_public_body_translation: locale: en notes: "" publication_scheme: "" + disclosure_log: "" humpadink_es_public_body_translation: name: "El Department for Humpadinking" @@ -33,6 +35,7 @@ humpadink_es_public_body_translation: locale: es notes: Baguette publication_scheme: "" + disclosure_log: "" humpadink_en_public_body_translation: name: "Department for Humpadinking" @@ -45,6 +48,7 @@ humpadink_en_public_body_translation: locale: en notes: An albatross told me!!! publication_scheme: "" + disclosure_log: "" forlorn_en_public_body_translation: name: "Department of Loneliness" @@ -57,6 +61,7 @@ forlorn_en_public_body_translation: locale: en notes: A very lonely public body that no one has corresponded with publication_scheme: "" + disclosure_log: "" silly_walks_en_public_body_translation: id: 6 @@ -69,6 +74,7 @@ silly_walks_en_public_body_translation: url_name: msw notes: You know the one. publication_scheme: "" + disclosure_log: "" sensible_walks_en_public_body_translation: id: 7 @@ -81,3 +87,4 @@ sensible_walks_en_public_body_translation: url_name: sensible_walks notes: I bet you’ve never heard of it. publication_scheme: "" + disclosure_log: "" diff --git a/spec/integration/errors_spec.rb b/spec/integration/errors_spec.rb index 4424b58c6..a44ed7051 100644 --- a/spec/integration/errors_spec.rb +++ b/spec/integration/errors_spec.rb @@ -13,15 +13,17 @@ describe "When rendering errors" do it "should render a 404 for unrouteable URLs" do get("/frobsnasm") + response.body.should include("The page doesn't exist") response.code.should == "404" - response.body.should include("The page doesn't exist") 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" end it "should render a 500 for general errors" do @@ -36,12 +38,15 @@ describe "When rendering errors" do 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.code.should == "403" + response.body.should include("Directory listing not allowed") + response.code.should == "403" 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" end end diff --git a/spec/lib/timezone_fixes_spec.rb b/spec/lib/timezone_fixes_spec.rb index eef7e865c..a2bea5f64 100644 --- a/spec/lib/timezone_fixes_spec.rb +++ b/spec/lib/timezone_fixes_spec.rb @@ -1,68 +1,100 @@ # This is a test of the monkey patches in timezone_fixes.rb -# We use EximLogDone here just as a totally random model that has a datetime type. - +# We use MailServerLogDone here just as a totally random model that has a datetime type. require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe "when doing things with timezones" do - it "should preserve time objects with local time conversion to default timezone UTC" do + it "should preserve time objects with local time conversion to default timezone UTC + and return them as UTC times when config.time_zone is UTC" do with_env_tz 'America/New_York' do with_active_record_default_timezone :utc do time = Time.local(2000) - exim_log_done = EximLogDone.create('last_stat' => time, 'filename' => 'dummy') - saved_time = EximLogDone.find(exim_log_done.id).last_stat + mail_server_log_done = MailServerLogDone.create('last_stat' => time, 'filename' => 'dummy') + raw_saved_time = MailServerLogDone.find(mail_server_log_done.id).read_attribute(:last_stat) + saved_time = MailServerLogDone.find(mail_server_log_done.id).last_stat assert_equal time, saved_time + assert_equal saved_time, raw_saved_time + # Time is created in EST by local method (using ENV['TZ']) assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "EST"], time.to_a + # Due to :utc active_record_default_timezone, everything saved as UTC + assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], raw_saved_time.to_a + # As config.time_zone is UTC (from config default), times returned in UTC assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a end end end - it "should preserve time objects with time with zone conversion to default timezone UTC" do + it "should preserve time objects with time with zone conversion to default timezone UTC + and return them as local times in the zone set by Time.use_zone" do + with_env_tz 'America/New_York' do with_active_record_default_timezone :utc do Time.use_zone 'Central Time (US & Canada)' do time = Time.zone.local(2000) - exim_log_done = EximLogDone.create('last_stat' => time, 'filename' => 'dummy') - saved_time = EximLogDone.find(exim_log_done.id).last_stat + mail_server_log_done = MailServerLogDone.create('last_stat' => time, 'filename' => 'dummy') + raw_saved_time = MailServerLogDone.find(mail_server_log_done.id).read_attribute(:last_stat) + saved_time = MailServerLogDone.find(mail_server_log_done.id).last_stat assert_equal time, saved_time + assert_equal saved_time, raw_saved_time + # Time is created in CST by Time.local (as Time.zone has been set) assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a - assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a + # Due to :utc active_record_default_timezone, everything saved as UTC + assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], raw_saved_time.to_a + # Times returned in CST due to Time.use_zone and ActiveRecord::time_zone_aware_attributes + # being true + assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], saved_time.to_a end end end end - # XXX Couldn't get this test to work - but the other tests seem to detect presence of - # the monkey patch, so they will do for now. - #it "should preserve time objects with UTC time conversion to default timezone local" do - # with_env_tz 'America/New_York' do - # time = Time.utc(2000) - # exim_log_done = EximLogDone.create('last_stat' => time, 'filename' => 'dummy') - # saved_time = EximLogDone.find(exim_log_done.id).last_stat - # assert_equal time, saved_time - # assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a - # assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a - # end - #end + it "should preserve time objects with UTC time conversion to default timezone local + and return then as UTC times when config.time_zone is UTC" do + with_env_tz 'America/New_York' do + with_active_record_default_timezone :local do + time = Time.utc(2000) + mail_server_log_done = MailServerLogDone.create('last_stat' => time, 'filename' => 'dummy') + raw_saved_time = MailServerLogDone.find(mail_server_log_done.id).read_attribute(:last_stat) + saved_time = MailServerLogDone.find(mail_server_log_done.id).last_stat + assert_equal time, saved_time + assert_equal saved_time, raw_saved_time + # Time is created in UTC by Time.utc method + assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a + # Due to :local active_record_default_timezone, saved as EST + assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], raw_saved_time.to_a + # As config.time_zone is UTC (from config default), times returned in UTC + assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a + end + end + end - it "should preserve time objects with time with zone conversion to default timezone local" do + it "should preserve time objects with time with zone conversion to default timezone local + and return them as local times in the zone set by Time.use_zone" do with_env_tz 'America/New_York' do with_active_record_default_timezone :local do Time.use_zone 'Central Time (US & Canada)' do time = Time.zone.local(2000) - exim_log_done = EximLogDone.create('last_stat' => time, 'filename' => 'dummy') - saved_time = EximLogDone.find(exim_log_done.id).last_stat + mail_server_log_done = MailServerLogDone.create('last_stat' => time, 'filename' => 'dummy') + raw_saved_time = MailServerLogDone.find(mail_server_log_done.id).read_attribute(:last_stat) + saved_time = MailServerLogDone.find(mail_server_log_done.id).last_stat assert_equal time, saved_time + assert_equal saved_time, raw_saved_time + # Time is created in CST by Time.zone.local assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a - assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], saved_time.to_a + # Due to :local active_record_default_timezone, saved as EST + assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], raw_saved_time.to_a + # Due to Time.use_zone, and ActiveRecord::time_zone_aware_attributes + # being true, time returned in CST + assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], saved_time.to_a end end end end + protected + def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz yield diff --git a/spec/models/incoming_message_spec.rb b/spec/models/incoming_message_spec.rb index cf84b4595..b038c43d9 100644 --- a/spec/models/incoming_message_spec.rb +++ b/spec/models/incoming_message_spec.rb @@ -12,6 +12,14 @@ describe IncomingMessage, " when dealing with incoming mail" do ActionMailer::Base.deliveries.clear end + it 'should correctly parse multipart mails with a linebreak in the boundary marker' do + ir = info_requests(:fancy_dog_request) + receive_incoming_mail('space-boundary.email', ir.incoming_email) + message = ir.incoming_messages[1] + message.mail.parts.size.should == 2 + message.mail.multipart?.should == true + end + it "should return the mail Date header date for sent at" do @im.parse_raw_email!(true) @im.reload @@ -60,7 +68,6 @@ describe IncomingMessage, " when dealing with incoming mail" do is not good utf-8' do ir = info_requests(:fancy_dog_request) receive_incoming_mail('no-part-charset-bad-utf8.email', ir.incoming_email) - puts ir.incoming_messages.inspect message = ir.incoming_messages[1] message.parse_raw_email! message.get_main_body_text_internal.should include("The above text was badly encoded") @@ -125,21 +132,45 @@ end describe IncomingMessage, " folding quoted parts of emails" do - it "cope with [ in user names properly" do - @user = mock_model(User) - @user.stub!(:name).and_return("Sir [ Bobble") - @info_request = mock_model(InfoRequest) - @info_request.stub!(:user).and_return(@user) - @info_request.stub!(:user_name).and_return(@user.name) + it 'should fold an example lotus notes quoted part converted from HTML correctly' do + ir = info_requests(:fancy_dog_request) + receive_incoming_mail('lotus-notes-quoting.email', ir.incoming_email) + message = ir.incoming_messages[1] + message.get_main_body_text_folded.should match(/FOLDED_QUOTED_SECTION/) + end + it 'should fold a plain text lotus notes quoted part correctly' do + text = "FOI Team\n\n\nInfo Requester <xxx@whatdotheyknow.com>=20\nSent by: Info Requester <request-bounce-xxxxx@whatdotheyknow.com>\n06/03/08 10:00\nPlease respond to\nInfo Requester <request-xxxx@whatdotheyknow.com>" @incoming_message = IncomingMessage.new() - @incoming_message.info_request = @info_request + @incoming_message.stub_chain(:info_request, :user_name).and_return("Info Requester") + @incoming_message.remove_lotus_quoting(text).should match(/FOLDED_QUOTED_SECTION/) + end + it "cope with [ in user names properly" do + @incoming_message = IncomingMessage.new() + @incoming_message.stub_chain(:info_request, :user_name).and_return("Sir [ Bobble") # this gives a warning if [ is in the name text = @incoming_message.remove_lotus_quoting("Sir [ Bobble \nSent by: \n") text.should == "\n\nFOLDED_QUOTED_SECTION" end + it 'should fold an example of another kind of forward quoting' do + ir = info_requests(:fancy_dog_request) + receive_incoming_mail('forward-quoting-example.email', ir.incoming_email) + message = ir.incoming_messages[1] + message.get_main_body_text_folded.should match(/FOLDED_QUOTED_SECTION/) + end + + it 'should fold a further example of forward quoting' do + ir = info_requests(:fancy_dog_request) + receive_incoming_mail('forward-quoting-example-2.email', ir.incoming_email) + message = ir.incoming_messages[1] + body_text = message.get_main_body_text_folded + body_text.should match(/FOLDED_QUOTED_SECTION/) + # check that the quoted section incorporates both quoted messages + body_text.should_not match('Subject: RE: Freedom of Information request') + end + end describe IncomingMessage, " checking validity to reply to" do diff --git a/spec/models/info_request_spec.rb b/spec/models/info_request_spec.rb index 204c600d9..2aeac2fec 100644 --- a/spec/models/info_request_spec.rb +++ b/spec/models/info_request_spec.rb @@ -441,16 +441,16 @@ describe InfoRequest do describe 'when applying censor rules' do before do - @global_rule = mock_model(CensorRule, :apply_to_text! => nil, + @global_rule = safe_mock_model(CensorRule, :apply_to_text! => nil, :apply_to_binary! => nil) - @user_rule = mock_model(CensorRule, :apply_to_text! => nil, + @user_rule = safe_mock_model(CensorRule, :apply_to_text! => nil, :apply_to_binary! => nil) - @request_rule = mock_model(CensorRule, :apply_to_text! => nil, + @request_rule = safe_mock_model(CensorRule, :apply_to_text! => nil, :apply_to_binary! => nil) - @body_rule = mock_model(CensorRule, :apply_to_text! => nil, + @body_rule = safe_mock_model(CensorRule, :apply_to_text! => nil, :apply_to_binary! => nil) - @user = mock_model(User, :censor_rules => [@user_rule]) - @body = mock_model(PublicBody, :censor_rules => [@body_rule]) + @user = safe_mock_model(User, :censor_rules => [@user_rule]) + @body = safe_mock_model(PublicBody, :censor_rules => [@body_rule]) @info_request = InfoRequest.new(:prominence => 'normal', :awaiting_description => true, :title => 'title') diff --git a/spec/models/mail_server_log_spec.rb b/spec/models/mail_server_log_spec.rb new file mode 100644 index 000000000..d0a1d202f --- /dev/null +++ b/spec/models/mail_server_log_spec.rb @@ -0,0 +1,166 @@ +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") + File.stub_chain(:stat, :mtime).and_return(DateTime.new(2012, 10, 10)) + end + + let(:log) {[ + "This is a line of a logfile relevant to foi+request-1234@example.com", + "This is the second line for the same foi+request-1234@example.com email address" + ]} + + let(:ir) { info_requests(:fancy_dog_request) } + + it "loads relevant lines of an uncompressed exim log file" do + File.should_receive(:open).with("/var/log/exim4/exim-mainlog-2012-10-10", "r").and_return(log) + InfoRequest.should_receive(:find_by_incoming_email).with("request-1234@example.com").twice.and_return(ir) + MailServerLog.load_file("/var/log/exim4/exim-mainlog-2012-10-10") + + ir.mail_server_logs.count.should == 2 + log = ir.mail_server_logs[0] + log.order.should == 1 + log.line.should == "This is a line of a logfile relevant to foi+request-1234@example.com" + + log = ir.mail_server_logs[1] + log.order.should == 2 + log.line.should == "This is the second line for the same foi+request-1234@example.com email address" + end + + it "doesn't load the log file twice if it's unchanged" do + File.should_receive(:open).with("/var/log/exim4/exim-mainlog-2012-10-10", "r").once.and_return([]) + + MailServerLog.load_file("/var/log/exim4/exim-mainlog-2012-10-10") + MailServerLog.load_file("/var/log/exim4/exim-mainlog-2012-10-10") + end + + it "loads the log file again if it's changed" do + File.should_receive(:open).with("/var/log/exim4/exim-mainlog-2012-10-10", "r").twice.and_return([]) + MailServerLog.load_file("/var/log/exim4/exim-mainlog-2012-10-10") + File.stub_chain(:stat, :mtime).and_return(DateTime.new(2012, 10, 11)) + MailServerLog.load_file("/var/log/exim4/exim-mainlog-2012-10-10") + end + + it "doesn't end up with two copies of each line when the same file is actually loaded twice" do + File.should_receive(:open).with("/var/log/exim4/exim-mainlog-2012-10-10", "r").twice.and_return(log) + InfoRequest.should_receive(:find_by_incoming_email).with("request-1234@example.com").any_number_of_times.and_return(ir) + + MailServerLog.load_file("/var/log/exim4/exim-mainlog-2012-10-10") + ir.mail_server_logs.count.should == 2 + + File.stub_chain(:stat, :mtime).and_return(DateTime.new(2012, 10, 11)) + MailServerLog.load_file("/var/log/exim4/exim-mainlog-2012-10-10") + ir.mail_server_logs.count.should == 2 + end + + it "easily handles gzip compress log files" do + File.should_not_receive(:open) + Zlib::GzipReader.should_receive(:open).with("/var/log/exim4/exim-mainlog-2012-10-10.gz").and_return([]) + MailServerLog.load_file("/var/log/exim4/exim-mainlog-2012-10-10.gz") + end + end + + 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+") + end + + it "recognises a single incoming email" do + MailServerLog.email_addresses_on_line("a random log line foi+request-14-e0e09f97@example.com has an email").should == + ["foi+request-14-e0e09f97@example.com"] + end + + it "recognises two email addresses on the same line" do + MailServerLog.email_addresses_on_line("two email addresses here foi+request-10-1234@example.com and foi+request-14-e0e09f97@example.com").should == + ["foi+request-10-1234@example.com", "foi+request-14-e0e09f97@example.com"] + end + + it "returns an empty array when there is an email address from a different domain" do + MailServerLog.email_addresses_on_line("other foi+request-10-1234@foo.com").should be_empty + end + + it "ignores an email with a different prefix" do + MailServerLog.email_addresses_on_line("foitest+request-14-e0e09f97@example.com").should be_empty + end + + it "ignores an email where the . is substituted for something else" do + MailServerLog.email_addresses_on_line("foi+request-14-e0e09f97@exampledcom").should be_empty + end + end + + context "Postfix" do + let(:log) {[ +"Oct 3 16:39:35 host postfix/pickup[2257]: CB55836EE58C: uid=1003 from=<foitest+request-14-e0e09f97@example.com>", +"Oct 3 16:39:35 host postfix/cleanup[7674]: CB55836EE58C: message-id=<ogm-15+506bdda7a4551-20ee@example.com>", +"Oct 3 16:39:35 host postfix/qmgr[1673]: 9634B16F7F7: from=<foitest+request-10-1234@example.com>, size=368, nrcpt=1 (queue active)", +"Oct 3 16:39:35 host postfix/qmgr[15615]: CB55836EE58C: from=<foitest+request-14-e0e09f97@example.com>, size=1695, nrcpt=1 (queue active)", +"Oct 3 16:39:38 host postfix/smtp[7676]: CB55836EE58C: to=<foi@some.gov.au>, relay=aspmx.l.google.com[74.125.25.27]:25, delay=2.5, delays=0.13/0.02/1.7/0.59, dsn=2.0.0, status=sent (250 2.0.0 OK 1349246383 j9si1676296paw.328)", +"Oct 3 16:39:38 host postfix/smtp[1681]: 9634B16F7F7: to=<kdent@example.com>, relay=none, delay=46, status=deferred (connect to 216.150.150.131[216.150.150.131]: No route to host)", +"Oct 3 16:39:38 host postfix/qmgr[15615]: CB55836EE58C: removed", + ]} + + describe ".load_postfix_log_data" 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") + log.stub!(:rewind) + ir1 = info_requests(:fancy_dog_request) + ir2 = info_requests(:naughty_chicken_request) + InfoRequest.should_receive(:find_by_incoming_email).with("request-14-e0e09f97@example.com").any_number_of_times.and_return(ir1) + InfoRequest.should_receive(:find_by_incoming_email).with("request-10-1234@example.com").any_number_of_times.and_return(ir2) + MailServerLog.load_postfix_log_data(log, MailServerLogDone.new(:filename => "foo", :last_stat => DateTime.now)) + # TODO: Check that each log line is attached to the correct request + ir1.mail_server_logs.count.should == 5 + ir1.mail_server_logs[0].order.should == 1 + ir1.mail_server_logs[0].line.should == "Oct 3 16:39:35 host postfix/pickup[2257]: CB55836EE58C: uid=1003 from=<foitest+request-14-e0e09f97@example.com>" + ir1.mail_server_logs[1].order.should == 2 + ir1.mail_server_logs[1].line.should == "Oct 3 16:39:35 host postfix/cleanup[7674]: CB55836EE58C: message-id=<ogm-15+506bdda7a4551-20ee@example.com>" + ir1.mail_server_logs[2].order.should == 4 + ir1.mail_server_logs[2].line.should == "Oct 3 16:39:35 host postfix/qmgr[15615]: CB55836EE58C: from=<foitest+request-14-e0e09f97@example.com>, size=1695, nrcpt=1 (queue active)" + ir1.mail_server_logs[3].order.should == 5 + ir1.mail_server_logs[3].line.should == "Oct 3 16:39:38 host postfix/smtp[7676]: CB55836EE58C: to=<foi@some.gov.au>, relay=aspmx.l.google.com[74.125.25.27]:25, delay=2.5, delays=0.13/0.02/1.7/0.59, dsn=2.0.0, status=sent (250 2.0.0 OK 1349246383 j9si1676296paw.328)" + ir1.mail_server_logs[4].order.should == 7 + ir1.mail_server_logs[4].line.should == "Oct 3 16:39:38 host postfix/qmgr[15615]: CB55836EE58C: removed" + ir2.mail_server_logs.count.should == 2 + ir2.mail_server_logs[0].order.should == 3 + ir2.mail_server_logs[0].line.should == "Oct 3 16:39:35 host postfix/qmgr[1673]: 9634B16F7F7: from=<foitest+request-10-1234@example.com>, size=368, nrcpt=1 (queue active)" + ir2.mail_server_logs[1].order.should == 6 + ir2.mail_server_logs[1].line.should == "Oct 3 16:39:38 host postfix/smtp[1681]: 9634B16F7F7: to=<kdent@example.com>, relay=none, delay=46, status=deferred (connect to 216.150.150.131[216.150.150.131]: No route to host)" + end + end + + 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") + MailServerLog.scan_for_postfix_queue_ids(log).should == { + "CB55836EE58C" => ["request-14-e0e09f97@example.com"], + "9634B16F7F7" => ["request-10-1234@example.com"] + } + end + end + + describe ".extract_postfix_queue_id_from_syslog_line" do + it "returns nil if there is no queue id" do + MailServerLog.extract_postfix_queue_id_from_syslog_line("Oct 7 07:16:48 kedumba postfix/smtp[14294]: connect to mail.neilcopp.com.au[110.142.151.66]:25: Connection refused").should be_nil + end + end + + describe ".request_postfix_sent?" do + it "returns true when the logs say the message was sent" do + ir = info_requests(:fancy_dog_request) + ir.mail_server_logs.create!(:line => "Oct 10 16:58:38 kedumba postfix/smtp[26358]: A664436F218D: to=<contact@openaustraliafoundation.org.au>, relay=aspmx.l.google.com[74.125.25.26]:25, delay=2.7, delays=0.16/0.02/1.8/0.67, dsn=2.0.0, status=sent (250 2.0.0 OK 1349848723 e6si653316paw.346)", :order => 1) + MailServerLog.request_postfix_sent?(ir).should be_true + end + + it "returns false when the logs say the message hasn't been sent" do + ir = info_requests(:fancy_dog_request) + ir.mail_server_logs.create!(:line => "Oct 10 13:22:49 kedumba postfix/smtp[11876]: 6FB9036F1307: to=<foo@example.com>, relay=mta7.am0.yahoodns.net[74.6.136.244]:25, delay=1.5, delays=0.03/0/0.48/1, dsn=5.0.0, status=bounced (host mta7.am0.yahoodns.net[74.6.136.244] said: 554 delivery error: dd Sorry your message to foo@example.com cannot be delivered. This account has been disabled or discontinued [#102]. - mta1272.mail.sk1.yahoo.com (in reply to end of DATA command))", :order => 1) + MailServerLog.request_postfix_sent?(ir).should be_false + end + end + end +end diff --git a/spec/models/track_thing_spec.rb b/spec/models/track_thing_spec.rb index 345629bd6..c42eb5e8b 100644 --- a/spec/models/track_thing_spec.rb +++ b/spec/models/track_thing_spec.rb @@ -35,10 +35,11 @@ describe TrackThing, "when tracking changes" do end it "will make some sane descriptions of search-based tracks" do - tests = [['bob variety:user', "users matching text 'bob'"], - ['bob (variety:sent OR variety:followup_sent OR variety:response OR variety:comment) (latest_status:successful OR latest_status:partially_successful OR latest_status:rejected OR latest_status:not_held)', "comments or requests which are successful or unsuccessful matching text 'bob'"], - ['(latest_status:waiting_response OR latest_status:waiting_clarification OR waiting_classification:true)', 'requests which are awaiting a response']] - for query, description in tests + tests = { 'bob variety:user' => "users matching text 'bob'", + 'bob (variety:sent OR variety:followup_sent OR variety:response OR variety:comment) (latest_status:successful OR latest_status:partially_successful OR latest_status:rejected OR latest_status:not_held)' => "comments or requests which are successful or unsuccessful matching text 'bob'", + '(latest_status:waiting_response OR latest_status:waiting_clarification OR waiting_classification:true)' => 'requests which are awaiting a response', + ' (variety:sent OR variety:followup_sent OR variety:response OR variety:comment)' => 'all requests or comments' } + tests.each do |query, description| track_thing = TrackThing.create_track_for_search_query(query) track_thing.track_query_description.should == description end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 03b2f34f9..e31c3f1b5 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,42 +1,42 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') -describe User, "making up the URL name" do +describe User, "making up the URL name" do before do @user = User.new end - it 'should remove spaces, and make lower case' do + it 'should remove spaces, and make lower case' do @user.name = 'Some Name' @user.url_name.should == 'some_name' end - it 'should not allow a numeric name' do + it 'should not allow a numeric name' do @user.name = '1234' @user.url_name.should == 'user' end end -describe User, "showing the name" do +describe User, "showing the name" do before do @user = User.new @user.name = 'Some Name ' end - it 'should strip whitespace' do + it 'should strip whitespace' do @user.name.should == 'Some Name' end - it 'should show if user has been banned' do + it 'should show if user has been banned' do @user.ban_text = "Naughty user" @user.name.should == 'Some Name (Account suspended)' end end - + describe User, " when authenticating" do before do - @empty_user = User.new + @empty_user = User.new @full_user = User.new @full_user.name = "Sensible User" @@ -71,7 +71,7 @@ end describe User, " when saving" do before do - @user = User.new + @user = User.new end it "should not save without setting some parameters" do @@ -80,7 +80,7 @@ describe User, " when saving" do it "should not save with misformatted email" do @user.name = "Mr. Silly" - @user.password = "insecurepassword" + @user.password = "insecurepassword" @user.email = "mousefooble" @user.should have(1).error_on(:email) end @@ -88,58 +88,58 @@ describe User, " when saving" do it "should not allow an email address as a name" do @user.name = "silly@example.com" @user.email = "silly@example.com" - @user.password = "insecurepassword" + @user.password = "insecurepassword" @user.should have(1).error_on(:name) end it "should not save with no password" do @user.name = "Mr. Silly" - @user.password = "" + @user.password = "" @user.email = "silly@localhost" @user.should have(1).error_on(:hashed_password) end it "should save with reasonable name, password and email" do @user.name = "Mr. Reasonable" - @user.password = "insecurepassword" + @user.password = "insecurepassword" @user.email = "reasonable@localhost" @user.save! end it "should let you make two users with same name" do @user.name = "Mr. Flobble" - @user.password = "insecurepassword" + @user.password = "insecurepassword" @user.email = "flobble@localhost" @user.save! - @user2 = User.new + @user2 = User.new @user2.name = "Mr. Flobble" - @user2.password = "insecurepassword" + @user2.password = "insecurepassword" @user2.email = "flobble2@localhost" @user2.save! end - + it 'should mark the model for reindexing in xapian if the no_xapian_reindex flag is set to false' do @user.name = "Mr. First" - @user.password = "insecurepassword" + @user.password = "insecurepassword" @user.email = "reasonable@localhost" @user.no_xapian_reindex = false @user.should_receive(:xapian_mark_needs_index) @user.save! end - + it 'should mark the model for reindexing in xapian if the no_xapian_reindex flag is not set' do @user.name = "Mr. Second" - @user.password = "insecurepassword" + @user.password = "insecurepassword" @user.email = "reasonable@localhost" @user.no_xapian_reindex = nil @user.should_receive(:xapian_mark_needs_index) @user.save! end - - it 'should not mark the model for reindexing in xapian if the no_xapian_reindex flag is set' do + + it 'should not mark the model for reindexing in xapian if the no_xapian_reindex flag is set' do @user.name = "Mr. Third" - @user.password = "insecurepassword" + @user.password = "insecurepassword" @user.email = "reasonable@localhost" @user.no_xapian_reindex = true @user.should_not_receive(:xapian_mark_needs_index) @@ -149,47 +149,47 @@ describe User, " when saving" do end -describe User, "when reindexing referencing models" do +describe User, "when reindexing referencing models" do - before 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]) @user = User.new(:comments => [@comment], :info_requests => [@request]) end - - it 'should reindex events associated with that user\'s comments when URL changes' do + + it 'should reindex events associated with that user\'s comments when URL changes' do @user.stub!(:changes).and_return({'url_name' => 1}) @comment_event.should_receive(:xapian_mark_needs_index) @user.reindex_referencing_models end - - it 'should reindex events associated with that user\'s requests when URL changes' do + + it 'should reindex events associated with that user\'s requests when URL changes' do @user.stub!(:changes).and_return({'url_name' => 1}) @request_event.should_receive(:xapian_mark_needs_index) @user.reindex_referencing_models end - - describe 'when no_xapian_reindex is set' do - before do + + describe 'when no_xapian_reindex is set' do + before do @user.no_xapian_reindex = true end - - it 'should not reindex events associated with that user\'s comments when URL changes' do + + it 'should not reindex events associated with that user\'s comments when URL changes' do @user.stub!(:changes).and_return({'url_name' => 1}) @comment_event.should_not_receive(:xapian_mark_needs_index) @user.reindex_referencing_models end - - it 'should not reindex events associated with that user\'s requests when URL changes' do + + it 'should not reindex events associated with that user\'s requests when URL changes' do @user.stub!(:changes).and_return({'url_name' => 1}) @request_event.should_not_receive(:xapian_mark_needs_index) @user.reindex_referencing_models end - + end - + end describe User, "when checking abilities" do @@ -208,26 +208,26 @@ describe User, "when checking abilities" do end -describe User, 'when asked if a user owns every request' do - - before do +describe User, 'when asked if a user owns every request' do + + before do @mock_user = mock_model(User) end - - it 'should return false if no user is passed' do + + it 'should return false if no user is passed' do User.owns_every_request?(nil).should be_false end - - it 'should return true if the user has "requires admin" power' do + + it 'should return true if the user has "requires admin" power' do @mock_user.stub!(:owns_every_request?).and_return true User.owns_every_request?(@mock_user).should be_true end - - it 'should return false if the user does not have "requires admin" power' do + + it 'should return false if the user does not have "requires admin" power' do @mock_user.stub!(:owns_every_request?).and_return false User.owns_every_request?(@mock_user).should be_false end - + end describe User, " when making name and email address" do @@ -296,7 +296,7 @@ describe User, "when emails have bounced" do it "should record bounces" do User.record_bounce_for_email("bob@localhost", "The reason we think the email bounced (e.g. a bounce message)") - + user = User.find_user_by_email("bob@localhost") user.email_bounced_at.should_not be_nil user.email_bounce_message.should == "The reason we think the email bounced (e.g. a bounce message)" diff --git a/spec/views/public_body/show.rhtml_spec.rb b/spec/views/public_body/show.rhtml_spec.rb index a42516d72..b68b3f43b 100644 --- a/spec/views/public_body/show.rhtml_spec.rb +++ b/spec/views/public_body/show.rhtml_spec.rb @@ -11,6 +11,7 @@ describe "when viewing a body" do :eir_only? => nil, :info_requests => [1, 2, 3, 4], # out of sync with Xapian :publication_scheme => '', + :disclosure_log => '', :calculated_home_page => '') @pb.stub!(:override_request_email).and_return(nil) @pb.stub!(:is_requestable?).and_return(true) |