diff options
Diffstat (limited to 'spec/models/info_request_spec.rb')
-rw-r--r-- | spec/models/info_request_spec.rb | 757 |
1 files changed, 742 insertions, 15 deletions
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 |