require 'spec_helper' require_dependency 'jobs/base' describe Jobs::Exporter do before do Jobs::Exporter.any_instance.stubs(:log).returns(true) Jobs::Exporter.any_instance.stubs(:create_tar_file).returns(true) Export::JsonEncoder.any_instance.stubs(:tmp_directory).returns( File.join(Rails.root, 'tmp', 'exporter_spec') ) Discourse.stubs(:enable_maintenance_mode).returns(true) Discourse.stubs(:disable_maintenance_mode).returns(true) end describe "execute" do context 'when no export or import is running' do before do @streams = {} Export::JsonEncoder.any_instance.stubs(:stream_creator).returns(lambda {|filename| @streams[File.basename(filename, '.*')] = StringIO.new }) Jobs::Exporter.any_instance.stubs(:ordered_models_for_export).returns([]) Export.stubs(:is_export_running?).returns(false) Export.stubs(:is_import_running?).returns(false) @exporter_args = {} end it "should indicate that an export is running" do seq = sequence('call sequence') Export.expects(:set_export_started).in_sequence(seq).at_least_once Export.expects(:set_export_is_not_running).in_sequence(seq).at_least_once Jobs::Exporter.new.execute( @exporter_args ) end it "should put the site in maintenance mode when it starts" do encoder = stub_everything Export::JsonEncoder.stubs(:new).returns(encoder) seq = sequence('export-sequence') Discourse.expects(:enable_maintenance_mode).in_sequence(seq).at_least_once encoder.expects(:write_schema_info).in_sequence(seq).at_least_once Jobs::Exporter.new.execute( @exporter_args ) end it "should take the site out of maintenance mode when it ends" do encoder = stub_everything Export::JsonEncoder.stubs(:new).returns(encoder) seq = sequence('export-sequence') encoder.expects(:write_schema_info).in_sequence(seq).at_least_once Discourse.expects(:disable_maintenance_mode).in_sequence(seq).at_least_once Jobs::Exporter.new.execute( @exporter_args ) end describe "without specifying a format" do it "should use json as the default" do Export::JsonEncoder.expects(:new).returns( stub_everything ) Jobs::Exporter.new.execute( @exporter_args.reject { |key, val| key == :format } ) end end describe "specifying an invalid format" do it "should raise an exception and not flag that an export has started" do Jobs::Exporter.expects(:set_export_started).never expect { Jobs::Exporter.new.execute( @exporter_args.merge( format: :interpretive_dance ) ) }.to raise_error(Export::FormatInvalidError) end end context "using json format" do before do @exporter_args = {format: :json} end it "should export metadata" do version = '201212121212' encoder = stub_everything encoder.expects(:write_schema_info).with do |arg| arg[:source].should == 'discourse' arg[:version].should == version end Export::JsonEncoder.stubs(:new).returns(encoder) Export.stubs(:current_schema_version).returns(version) Jobs::Exporter.new.execute( @exporter_args ) end describe "exporting tables" do before do # Create some real database records @user1, @user2 = Fabricate(:user), Fabricate(:user) @topic1 = Fabricate(:topic, user: @user1) @topic2 = Fabricate(:topic, user: @user2) @topic3 = Fabricate(:topic, user: @user1) @post1 = Fabricate(:post, topic: @topic1, user: @user1) @post1 = Fabricate(:post, topic: @topic3, user: @user1) @reply1 = Fabricate(:basic_reply, user: @user2, topic: @topic3) @reply1.save_reply_relationships @reply2 = Fabricate(:basic_reply, user: @user1, topic: @topic1) @reply2.save_reply_relationships @reply3 = Fabricate(:basic_reply, user: @user1, topic: @topic3) @reply3.save_reply_relationships end it "should export all rows from the topics table in ascending id order" do Jobs::Exporter.any_instance.stubs(:ordered_models_for_export).returns([Topic]) Jobs::Exporter.new.execute( @exporter_args ) topics = JSON.parse( @streams['topics'].string ) topics.should have(3).rows topics.map{|row| row[0].to_i}.sort.should == [@topic1.id, @topic2.id, @topic3.id].sort end it "should export all rows from the post_replies table in ascending order by post_id, reply_id" do # because post_replies doesn't have an id column, so order by one of its indexes Jobs::Exporter.any_instance.stubs(:ordered_models_for_export).returns([PostReply]) Jobs::Exporter.new.execute( @exporter_args ) post_replies = JSON.parse( @streams['post_replies'].string ) post_replies.map{|row| row[1].to_i}.sort.should == [@reply1.id, @reply2.id, @reply3.id].sort end it "should export column names for each table" do Jobs::Exporter.any_instance.stubs(:ordered_models_for_export).returns([Topic, TopicUser, PostReply]) Jobs::Exporter.new.execute( @exporter_args ) json = JSON.parse( @streams['schema'].string ) json['topics'].should have_key('fields') json['topic_users'].should have_key('fields') json['post_replies'].should have_key('fields') json['topics']['fields'].should == Topic.columns.map(&:name) json['topic_users']['fields'].should == TopicUser.columns.map(&:name) json['post_replies']['fields'].should == PostReply.columns.map(&:name) end end end context "when it finishes successfully" do context "and no user was given" do it "should not send a notification to anyone" do expect { Jobs::Exporter.new.execute( @exporter_args ) }.to_not change { Notification.count } end end context "and a user was given" do before do @user = Fabricate(:user) @admin = Fabricate(:admin) end it "should send a notification to the user who started the export" do ActiveRecord::Base.observers.enable :all expect { Jobs::Exporter.new.execute( @exporter_args.merge( user_id: @user.id ) ) }.to change { Notification.count }.by(1) end end end end context 'when an export is already running' do before do Export.expects(:is_export_running?).returns(true) end it "should not start an export and raise an exception" do Export.expects(:set_export_started).never Jobs::Exporter.any_instance.expects(:start_export).never expect { Jobs::Exporter.new.execute({}) }.to raise_error(Export::ExportInProgressError) end end context 'when an import is running' do before do Import.expects(:is_import_running?).returns(true) end it "should not start an export and raise an exception" do Export.expects(:set_export_started).never Jobs::Exporter.any_instance.expects(:start_export).never expect { Jobs::Exporter.new.execute({}) }.to raise_error(Import::ImportInProgressError) end end end end