require File.join(File.dirname(__FILE__),'spec_helper') describe "SourceSync" do it_behaves_like "SharedRhoconnectHelper", :rhoconnect_data => true do before(:each) do @s = Source.load(@s_fields[:name],@s_params) @model = Rhoconnect::Model::Base.create(@s) end let(:mock_schema) { {"property" => { "name" => "string", "brand" => "string" }, "version" => "1.0"} } describe "SourceSync query methods" do before (:each) do rh = lambda { @model.query(params[:query])} @model = Rhoconnect::Model::Base.create(@s) @ssq = Rhoconnect::Handler::Query::Engine.new(@model, rh, {}) end it "should create Rhoconnect::Handler::Query::Engine" do @ssq.model.is_a?(SampleAdapter).should == true end it "should fail to create Rhoconnect::Handler::Query::Engine with InvalidArgumentError without source" do lambda { Rhoconnect::Handler::Query::Engine.new(nil, nil, {}) }.should raise_error(ArgumentError, 'Unknown source') end it "should fail to create Rhoconnect::Handler::Query::Engine with InvalidArgumentError without proc handler" do lambda { Rhoconnect::Handler::Query::Engine.new(@model, nil) }.should raise_error(ArgumentError, 'Invalid sync handler') end it "should raise LoginException if login fails" do msg = "Error logging in" @u.login = nil @ssq.should_receive(:log).with("Model raised login exception: #{msg}") @ssq.should_receive(:log).with(anything) @ssq.do_sync verify_doc_result(@s, :errors => {'login-error'=>{'message'=>msg}}) end it "should raise LogoffException if logoff fails" do msg = "Error logging off" @ssq.should_receive(:log).with("Model raised logoff exception: #{msg}") @ssq.should_receive(:log).with(anything) set_test_data('test_db_storage',{},msg,'logoff error') @ssq.do_sync verify_doc_result(@s, :errors => {'logoff-error'=>{'message'=>msg}}) end it "should hold on read on subsequent call of process if default poll interval is not exprired" do expected = {'1'=>@product1} set_state('test_db_storage' => expected) @ssq.do_sync set_state('test_db_storage' => {'2'=>@product2}) @ssq.do_sync verify_doc_result(@s, :md => expected) end it "should read on every subsequent call of process if poll interval is set to 0" do expected = {'2'=>@product2} @s.poll_interval = 0 set_state('test_db_storage' => {'1'=>@product1}) @ssq.do_sync set_state('test_db_storage' => expected) @ssq.do_sync verify_doc_result(@s, :md => expected) end it "should never call read on any call of process if poll interval is set to -1" do @s.poll_interval = -1 set_state('test_db_storage' => {'1'=>@product1}) @ssq.do_sync verify_doc_result(@s, :md => {}) end it "should process model metadata" do mock_metadata_method([SampleAdapter, SimpleAdapter]) do expected = {'1'=>@product1,'2'=>@product2} set_state('test_db_storage' => expected) @ssq.do_sync verify_doc_result(@s, {:md => expected, :metadata => "{\"foo\":\"bar\"}", :metadata_sha1 => "a5e744d0164540d33b1d7ea616c28f2fa97e754a"}) end end it "should process model schema" do mock_schema_method([SampleAdapter]) do expected = {'1'=>@product1,'2'=>@product2} set_state('test_db_storage' => expected) @ssq.do_sync verify_doc_result(@s, :md => expected) JSON.parse(@s.get_value(:schema)).should == mock_schema @s.get_value(:schema_sha1).should == get_sha1(mock_schema['version']) end end it "should raise exception if model schema has no version key/value pair" do mock_schema_no_version_method([SampleAdapter]) do expected = {'1'=>@product1,'2'=>@product2} set_state('test_db_storage' => expected) @ssq.do_sync errors = {} @s.lock(:errors) { errors = @s.get_data(:errors) } errors.empty?().should == false errors["query-error"]["message"].should == "Mandatory version key is not defined in model schema method" end end it "should process model with stash" do expected = {'1'=>@product1,'2'=>@product2} set_state('test_db_storage' => expected) @ssq.params[:query] = {'stash_result' => true} @ssq.do_sync #@ssq.model.should_receive(:stash_result).once verify_doc_result(@s, {:md => expected, :md_size => expected.size.to_s}) end it "should process model with pass_through set" do expected = {'1'=>@product1,'2'=>@product2} set_state('test_db_storage' => expected) @s.pass_through = 'true' @ssq.do_sync.should == expected verify_doc_result(@s, {:md => {}, :md_size => nil}) @s.pass_through = nil end it "should call methods in model" do mock_metadata_method([SampleAdapter, SimpleAdapter]) do expected = {'1'=>@product1,'2'=>@product2} metadata = "{\"foo\":\"bar\"}" @ssq.model.should_receive(:login).once.with(no_args()).and_return(true) @ssq.model.should_receive(:metadata).once.with(no_args()).and_return(metadata) @ssq.model.should_receive(:query).once.with(nil).and_return(expected) @ssq.model.should_receive(:sync).once.with(no_args()).and_return(true) @ssq.model.should_receive(:logoff).once.with(no_args()).and_return(nil) @ssq.do_sync end end it "should do query with no exception" do verify_read_operation('query') end it "should do query with no exception pass through" do verify_read_operation_pass_through('query') end it "should do query with exception raised" do verify_read_operation_with_error('query') end it "should do query with exception raised and update refresh time only after retries limit is exceeded" do @s.retry_limit = 1 msg = "Error during query" set_test_data('test_db_storage',{},msg,"query error") res = @ssq.do_sync verify_doc_result(@s, {:md => {}, :errors => {'query-error'=>{'message'=>msg}}}) # 1) if retry_limit is set to N - then, first N retries should not update refresh_time @s.read_state.retry_counter.should == 1 @s.read_state.refresh_time.should <= Time.now.to_i # try once more and fail again set_test_data('test_db_storage',{},msg,"query error") res = @ssq.do_sync verify_doc_result(@s, {:md => {}, :errors => {'query-error'=>{'message'=>msg}}}) # 2) if retry_limit is set to N and number of retries exceeded it - update refresh_time @s.read_state.retry_counter.should == 0 @s.read_state.refresh_time.should > Time.now.to_i end it "should do query with exception raised and restore state with succesfull retry" do @s.retry_limit = 1 msg = "Error during query" set_test_data('test_db_storage',{},msg,"query error") res = @ssq.do_sync verify_doc_result(@s, {:md => {}, :errors => {'query-error'=>{'message'=>msg}}}) # 1) if retry_limit is set to N - then, first N retries should not update refresh_time @s.read_state.retry_counter.should == 1 @s.read_state.refresh_time.should <= Time.now.to_i # try once more (with success) expected = {'1'=>@product1,'2'=>@product2} set_test_data('test_db_storage',expected) @ssq.do_sync verify_doc_result(@s, {:md => expected, :errors => {}}) @s.read_state.retry_counter.should == 0 @s.read_state.refresh_time.should > Time.now.to_i end it "should reset the retry counter if prev_refresh_time was set more than poll_interval secs ago" do @s.retry_limit = 3 @s.poll_interval = 2 msg = "Error during query" set_test_data('test_db_storage',{},msg,"query error") res = @ssq.do_sync verify_doc_result(@s, {:md => {}, :errors => {'query-error'=>{'message'=>msg}}}) # 1) if retry_limit is set to N - then, first N retries should not update refresh_time @s.read_state.retry_counter.should == 1 @s.read_state.refresh_time.should <= Time.now.to_i # 2) Make another error - results are the same set_test_data('test_db_storage',{},msg,"query error") res = @ssq.do_sync verify_doc_result(@s, {:md => {}, :errors => {'query-error'=>{'message'=>msg}}}) @s.read_state.retry_counter.should == 2 @s.read_state.refresh_time.should <= Time.now.to_i # wait until time interval exprires and prev_refresh_time is too old - # this should reset the counter on next request with error # and do not update refresh_time sleep(3) set_test_data('test_db_storage',{},msg,"query error") res = @ssq.do_sync verify_doc_result(@s, {:md => {}, :errors => {'query-error'=>{'message'=>msg}}}) @s.read_state.retry_counter.should == 1 @s.read_state.refresh_time.should <= Time.now.to_i end it "should do query with exception raised and update refresh time if retry_limit is 0" do @s.retry_limit = 0 msg = "Error during query" set_test_data('test_db_storage',{},msg,"query error") res = @ssq.do_sync verify_doc_result(@s, {:md => {}, :errors => {'query-error'=>{'message'=>msg}}}) # if poll_interval is set to 0 - refresh time should be updated @s.read_state.retry_counter.should == 0 @s.read_state.refresh_time.should > Time.now.to_i end it "should do query with exception raised and update refresh time if poll_interval == 0" do @s.retry_limit = 1 @s.poll_interval = 0 msg = "Error during query" set_test_data('test_db_storage',{},msg,"query error") prev_refresh_time = @s.read_state.refresh_time # make sure refresh time is expired sleep(1) res = @ssq.do_sync verify_doc_result(@s, {:md => {}, :errors => {'query-error'=>{'message'=>msg}}}) # if poll_interval is set to 0 - refresh time should be updated @s.read_state.retry_counter.should == 0 @s.read_state.refresh_time.should > prev_refresh_time end end describe "push_notify" do it "should do push_notify for source after push_objects if enabled" do @s.push_notify = 'true' data = {'1' => @product1, '2' => @product2, '3' => @product3} ping_params = {'user_id'=>["testuser"], 'sources'=>["SampleAdapter"]} PingJob.should_receive(:perform).once.with(ping_params) po_handler = lambda { @model.push_objects(params) } push_objects_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(po_handler, @model, {'objects' => data}) push_objects_handler.run end it "should do push_notify for source after push_deletes if enabled" do @s.push_notify = 'true' u2 = User.create(:login => 'user2') @a.users << u2.id data = {'1' => @product1, '2' => @product2, '3' => @product3} ping_params = {'user_id'=>["testuser"], 'sources'=>["SampleAdapter"]} PingJob.should_receive(:perform).once.with(ping_params) pd_handler = lambda { @model.push_deletes(params) } push_deletes_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(pd_handler, @model, {'objects' => data}) push_deletes_handler.run end it "should not do push_notify for user source if not enabled (default)" do u2 = User.create(:login => 'user2') @a.users << u2.id data = {'1' => @product1} PingJob.should_receive(:perform).never po_handler = lambda { @model.push_objects(params) } push_objects_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(po_handler, @model, {'objects' => data}) push_objects_handler.run end it "should not do push_notify for app source" do @s.partition = :app u2 = User.create(:login => 'user2') @a.users << u2.id data = {'1' => @product1} PingJob.should_receive(:perform).never po_handler = lambda { @model.push_objects(params) } push_objects_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(po_handler, @model, {'objects' => data}) push_objects_handler.run end end describe "create" do before (:each) do rh = lambda { @model.create(params[:create_object])} @ssc = Rhoconnect::Handler::Changes::Engine.new(['create'], @model, rh, {}) end it "should do create where adapter.create returns nil" do set_source_queue_state(@s, {:create => {'2'=>@product2}}, @c.id, true) @ssc.create verify_source_queue_data(@s, :create => []) verify_doc_result(@c, {:create_errors => {}, :create_links => {}}) end it "should do create where adapter.create returns object link" do @product4['link'] = 'test link' set_source_queue_state(@s, {:create => {'4'=>@product4}},@c.id,true) @ssc.create verify_source_queue_data(@s, :create => []) verify_doc_result(@c, {:create_errors => {}, :create_links => {'4'=>{'l'=>'backend_id'}}}) end it "should raise exception on adapter.create" do msg = "Error creating record" data = add_error_object({'4'=>@product4,'2'=>@product2},msg) set_source_queue_state(@s, {:create => data},@c.id, true) @ssc.create verify_doc_result(@c, :create_errors => {"#{ERROR}-error"=>{"message"=>msg},ERROR=>data[ERROR]}) end end describe "update" do before (:each) do rh = lambda { @model.update(params[:update_object])} @ssu = Rhoconnect::Handler::Changes::Engine.new(['update'], @model, rh, {}) end it "should do update with no errors" do set_source_queue_state(@s, {:update => {'4'=> { 'price' => '199.99' }}},@c.id,true) @ssu.update verify_source_queue_data(@s, :update => []) verify_doc_result(@c, :update_errors => {}) end it "should do update with errors" do msg = "Error updating record" data = add_error_object({'4'=> { 'price' => '199.99' }},msg) set_source_queue_state(@s, {:update => data},@c.id,true) set_doc_state(@c, :cd => { ERROR => { 'price' => '99.99' } } ) @ssu.update update_data,client_ids = @s.get_queue(:update) update_data.should == [{'4'=> { 'price' => '199.99'}}] client_ids.should == [@c.id] verify_doc_result(@c, {:update_errors => {"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]}, :update_rollback => {ERROR=>{"price"=>"99.99"}}}) end end describe "delete" do before (:each) do rh = lambda { @model.update(params[:delete_object])} @ssd = Rhoconnect::Handler::Changes::Engine.new(['delete'], @model, rh, {}) end it "should do delete with no errors" do set_source_queue_state(@s, {:delete => {'4'=>@product4}}, @c.id, true) set_doc_state(@s, :md => {'4'=>@product4,'3'=>@product3}) set_doc_state(@c, :cd => {'4'=>@product4,'3'=>@product3}) @ssd.delete verify_source_queue_data(@s, :delete => []) verify_doc_result(@c, :delete_errors => {}) verify_doc_result(@s, :md => {'3'=>@product3}) verify_doc_result(@c, :cd => {'3'=>@product3}) end it "should do delete with errors" do msg = "Error delete record" data = add_error_object({'2'=>@product2},msg) set_source_queue_state(@s, {:delete => data}, @c.id, true) @ssd.delete # FIXME: Failed for jruby, ruby 1.9.2 # verify_result(@c.docname(:delete_errors) => # {"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]}, # @c.docname(:delete) => {'2'=>@product2}) # Failure/Error: {"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]}, # Verifying `client:application:testuser:b020a633ac2c43f7b0d30ef92dd43886:SampleAdapter:delete` # expected: {"2"=>{"name"=>"G2", "brand"=>"Android", "price"=>"99.99"}} # got: {} (using ==) # Failure/Error: @c.docname(:delete) => {'2'=>@product2}) # Verifying `client:application:testuser:26a072a1da0d4bc18f69376e3229ab30:SampleAdapter:delete` # expected: {"2"=>{"name"=>"G2", "brand"=>"Android", "price"=>"99.99"}} # got: {} (using ==) # But this one works everywhere !!! verify_doc_result(@c, :delete_errors => {"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]}) end end describe "cud" do before (:each) do rh = lambda { @model.send(params[:operation].to_sym, params["#{params[:operation]}_object".to_sym]) } @sscud = Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, rh, {}) end it "should fail to create Rhoconnect::Handler::Changes::Engine with InvalidArgumentError without source" do lambda { Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], nil, nil, {}) }.should raise_error(ArgumentError, 'Unknown source') end it "should fail to create Rhoconnect::Handler::Changes::Engine with InvalidArgumentError without proc handler" do lambda { Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, nil) }.should raise_error(ArgumentError, 'Invalid CUD handler') end it "should create Rhoconnect::Handler::Changes::Engine" do @sscud.model.is_a?(SampleAdapter).should == true end it "should do process_cud" do @sscud.should_receive(:auth_method).twice.and_return(true) @sscud.should_receive(:create).once @sscud.should_receive(:update).once @sscud.should_receive(:delete).once @sscud.do_cud end end describe "cud conflicts" do before (:each) do rh = lambda { @model.send(params[:operation].to_sym, params["#{params[:operation]}_object".to_sym]) } @sscud = Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, rh, {}) end it "should detect create conflict and skip the duplicate record creation, but properly update the links" do set_source_queue_state(@s, {:create => {'4'=> { 'name' => 'Android', 'link' => '1' }}},@c.id,true) set_source_queue_state(@s, {:create => {'5'=> { 'name' => 'Android', 'link' => '1', 'duplicate_of_cid' => @c.id, 'duplicate_of_key' => '4', 'duplicate_of_queue_index' => '0' }}},@c.id,true) @sscud.do_cud verify_source_queue_data(@s, :create => []) verify_doc_result(@c, :create_errors => {}) verify_doc_result(@c, :create_links => {'4'=> { 'l' => 'backend_id' }, '5' => { 'l' => 'backend_id'}}) end it "should detect create conflict and skip the duplicate record creation, but properly update the errors page" do create_doc1 = { 'name' => 'wrongname', 'link' => '1', 'an_attribute' => "Create Sample Adapter Error" } create_doc2 = { 'name' => 'wrongname', 'link' => '1', 'duplicate_of_cid' => @c.id, 'duplicate_of_key' => '4', 'duplicate_of_queue_index' => '0'} set_source_queue_state(@s, {:create => {'4' => create_doc1}},@c.id,true) set_source_queue_state(@s, {:create => {'5' => create_doc2}},@c.id,true) @sscud.do_cud verify_source_queue_data(@s, :create => []) verify_doc_result(@c, :create_errors => {"4-error"=>{"message"=>"Create Sample Adapter Error"}, '4' => create_doc1, "5-error"=>{"message"=>"Create Sample Adapter Error"}, '5' => create_doc2}) verify_doc_result(@c, :create_links => {}) end it "should detect create conflict and force and error" do set_source_queue_state(@s, {:create => {'4'=> { 'name' => 'Android', 'link' => true }}},@c.id,true) set_source_queue_state(@s, {:create => {'5'=> { 'name' => 'Android', 'link' => '1', 'force_duplicate_error' => '1' }}},@c.id,true) @sscud.do_cud verify_source_queue_data(@s, :create => []) verify_doc_result(@c, :create_errors => {"5-error"=>{"message"=>"Error during create: object confict detected"}, "5"=>{"name"=>"Android", "link"=>"1", 'force_duplicate_error' => '1'}} ) end it "should detect update conflict and skip the duplicate record update" do set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}}) set_source_queue_state(@s, {:update => {'4'=> { 'name' => 'Android' }}},@c.id,true) set_source_queue_state(@s, {:update => {'4'=> { 'name' => 'InvalidName', 'duplicate_of_cid' => @c.id, 'duplicate_of_key' => '4', 'duplicate_of_queue_index' => '0'}}},@c.id,true) @sscud.do_cud verify_source_queue_data(@s, :update => []) verify_doc_result(@c, :update_errors => {}) verify_doc_result(@c, :update_rollback => {}) end it "should detect update conflict and force an error on duplicate record update" do set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}}) set_source_queue_state(@s, {:update => {'4'=> { 'name' => 'Android' }}},@c.id,true) set_source_queue_state(@s, {:update => {'4'=> { 'name' => 'ErrorName', 'force_duplicate_error' => '1' }}},@c.id,true) @sscud.do_cud verify_source_queue_data(@s, :update => []) verify_doc_result(@c, :update_errors => {"4-error"=>{"message"=>"Error during update: object confict detected"}, "4"=>{"name"=>"ErrorName", 'force_duplicate_error' => '1'}}) verify_doc_result(@c, :update_rollback => {'4'=> {'name' => 'Apple'}}) end it "should detect delete conflict and skip the duplicate record delete" do set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}}) set_doc_state(@c, :cd_size => 1) set_source_queue_state(@s, {:delete => {'4'=> { 'name' => 'Apple' }}},@c.id,true) set_source_queue_state(@s, {:delete => {'4'=> { 'name' => 'Apple', 'duplicate_of_cid' => @c.id, 'duplicate_of_key' => '4', 'duplicate_of_queue_index' => '0'}}},@c.id,true) @sscud.do_cud verify_source_queue_data(@s, :delete => []) verify_doc_result(@c, :cd => {}) verify_doc_result(@c, :cd_size => '0') verify_doc_result(@c, :delete_errors => {}) end it "should detect delete conflict and force an error on duplicate record delete" do set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}}) set_doc_state(@c, :cd_size => 1) set_source_queue_state(@s, {:delete => {'4'=> { 'name' => 'Apple' }}},@c.id,true) set_source_queue_state(@s, {:delete => {'4'=> { 'name' => 'Apple', 'force_duplicate_error' => '1'}}},@c.id,true) @sscud.do_cud verify_source_queue_data(@s, :delete => []) verify_doc_result(@c, :delete_errors => {"4-error"=>{"message"=>"Error during delete: object confict detected"}, "4"=>{"name"=>"Apple", 'force_duplicate_error' => '1'}}) end end describe "search" do before (:each) do rh = lambda { @model.search(params[:search]) } @sss = Rhoconnect::Handler::Search::Engine.new(@model, @c, rh, {}) end it "should do search with no exception" do verify_read_operation('search') end it "should do search with no exception pass through" do verify_read_operation_pass_through('search') end it "should do search with exception raised" do verify_read_operation_with_error('search') end end describe "app-level partitioning" do it "should create app-level masterdoc with '__shared__' docname" do @s1 = Source.load(@s_fields[:name],@s_params) @s1.partition = :app rh = lambda { @model.query(params[:query]) } @model1 = Rhoconnect::Model::Base.create(@s1) @ssq = Rhoconnect::Handler::Query::Engine.new(@model1, rh, {}) expected = {'1'=>@product1,'2'=>@product2} set_state('test_db_storage' => expected) @ssq.do_sync verify_doc_result(@s1, :md => expected) Store.get_store(0).keys("read_state:#{test_app_name}:__shared__*").sort.should == [ "read_state:#{test_app_name}:__shared__:SampleAdapter:refresh_time", "read_state:#{test_app_name}:__shared__:SampleAdapter:prev_refresh_time", "read_state:#{test_app_name}:__shared__:SampleAdapter:rho__id", "read_state:#{test_app_name}:__shared__:SampleAdapter:retry_counter"].sort end end def verify_read_operation(operation) expected = {'1'=>@product1,'2'=>@product2} set_test_data('test_db_storage',expected) @s.put_data(:errors, {"#{operation}-error"=>{'message'=>'failed'}},true) if operation == 'query' @ssq.run_query.should == true verify_doc_result(@s, {:md => expected, :errors => {}}) else @sss.run_search.should == true verify_doc_result(@c, {:search => expected, :search_errors => {}}) end end def verify_read_operation_pass_through(operation) expected = {'1'=>@product1,'2'=>@product2} set_test_data('test_db_storage',expected) @s.put_data(:errors, {"#{operation}-error"=>{'message'=>'failed'}},true) @s.pass_through = 'true' if operation == 'query' @ssq.run_query.should == expected verify_doc_result(@s, {:md => {}, :errors => {}}) else @sss.run_search.should == expected verify_doc_result(@c, {:search => {}, :search_errors => {}}) end end def verify_read_operation_with_error(operation) msg = "Error during #{operation}" set_test_data('test_db_storage',{},msg,"#{operation} error") if operation == 'query' @ssq.should_receive(:log).with("Model raised #{operation} exception: #{msg}") @ssq.should_receive(:log).with(anything) @ssq.run_query.should == true verify_doc_result(@s, {:md => {}, :errors => {'query-error'=>{'message'=>msg}}}) else @sss.should_receive(:log).with("Model raised #{operation} exception: #{msg}") @sss.should_receive(:log).with(anything) @sss.run_search.should == true verify_doc_result(@c, {:search => {}, :search_errors => {'search-error'=>{'message'=>msg}}}) end end describe "Jobs" do it "should enqueue process_cud SourceJob" do @s.cud_queue = :cud rh = lambda { @model.send(params[:operation].to_sym, params["#{params[:operation]}_object".to_sym]) } @sscud = Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, rh, {}) @sscud.do_cud Resque.peek(:cud).should == {"args"=> ["cud", @s.name, @a.name, @u.login, nil], "class"=>"Rhoconnect::SourceJob"} end it "should enqueue process_query SourceJob" do @s.query_queue = :abc rh = lambda { @model.query(params[:query]) } @ssq = Rhoconnect::Handler::Query::Engine.new(@model, rh, { :query => {'foo'=>'bar'} }) @ssq.do_sync Resque.peek(:abc).should == {"args"=> ["query", @s.name, @a.name, @u.login, {'foo'=>'bar'}], "class"=>"Rhoconnect::SourceJob"} end end end end