require 'spec_helper' feature "initiate application_data_backup retrieval job", :js => true do include HttpMockHelpers include DummyAppDb include AwsHelper before do create_application_data_backup_with_available_components remove_attached_files_from_filesystem visit admin_path end it "should initiate aws retrieval job and set status to pending" do expect{page.find('#initiate_retrieval').click; wait_for_ajax}.to change{ @application_data_backup.reload.retrieval_status }.from('available').to('pending') expect(initiate_retrieve_job).to have_been_requested.times(4) expect(page).to have_selector('#application_data_backups .application_data_backup .retrieval_pending') end end feature "retrieve application_data_backup from aws", :js => true do include HttpMockHelpers include DummyAppDb include AwsHelper context "retrieval job id has expired" do before do fetch_expired_archive # sets up the webmock method stub create_application_data_backup_with_ready_and_expired_components remove_attached_files_from_filesystem visit admin_path end it "should show failure message and reset application_data_backup status" do expect(page).to have_selector('#fetch_archive') page.find('#fetch_archive').click wait_for_ajax expect(fetch_expired_archive).to have_been_requested.once expect(fetch_file_archive_retrieval_job_output).to have_been_requested.times(2) expect(fetch_db_archive_retrieval_job_output).to have_been_requested.once expect(page).to have_selector("#fetch_fail_message", :text => "fail") end end context "retrieval job still valid" do before do fetch_file_archive_retrieval_job_output fetch_db_archive_retrieval_job_output create_application_data_backup_with_ready_components remove_attached_files_from_filesystem visit admin_path end it "should show failure message and reset application_data_backup status" do expect(page).to have_selector('#fetch_archive') page.find('#fetch_archive').click wait_for_ajax expect(fetch_file_archive_retrieval_job_output).to have_been_requested.times(3) expect(fetch_db_archive_retrieval_job_output).to have_been_requested.once expect(page).to have_selector(".restore_database") end end end feature "delete application_data_backup object and aws application_data_backup", :js => true do include HttpMockHelpers include DummyAppDb include AwsHelper before do create_application_data_backup_with_available_components visit admin_path end it "should send application_data_backup delete message to aws" do page.find('#delete_archive').click wait_for_ajax expect(delete_archive).to have_been_requested.times(4) expect(ApplicationDataBackup.count).to eq 0 expect(GlacierArchive.count).to eq 0 expect(page.all('#application_data_backups .application_data_backup').length).to be 0 end end feature "restore database from retrieved application_data_backup", :js => true do include HttpMockHelpers include AwsHelper before do create_application_data_backup_with_local_database_components create_compressed_archive(@application_data_backup.glacier_db_archive) change_database visit admin_path end context "when restoral is successful" do it "should restore the database" do page.find('.restore_database').click wait_for_ajax expect(ActiveRecord::Base.connection.execute("select * from test").first["foo"]).to eq 'bar' expect(flash_message).to eq "Database restored with the #{@application_data_backup.created_at.to_date.to_s} backup" expect(page).to have_selector('td#initiate_retrieval') end end context "when restoral fails" do before do allow_any_instance_of(ApplicationDatabase).to receive(:restore).and_return(false) end it "should restore the database" do page.find('.restore_database').click wait_for_ajax expect(ActiveRecord::Base.connection.execute("select * from test").first["foo"]).to eq 'bosh' # b/c restore failed expect(flash_message).to eq "Database restore failed" expect(page).to have_selector('td.restore_database') end end end feature "backup_now", :js => true do include HttpMockHelpers include AwsHelper include DummyAppDb before do visit admin_path end context "when no errors are generated by AWS Glacier" do it "should create a new application_data_backup" do expect{page.find('#backup_now').click; wait_for_ajax}.to change{ApplicationDataBackup.count}.from(0).to(1) expect( upload_archive_post ).to have_been_requested.times(4) expect(page).to have_selector("#application_data_backups .application_data_backup", :count => 1) end end context "when AWS responds with an error" do before do upload_archive_post_with_error_response # mock the errored response # Failed to create archive with: Aws::Glacier::Errors::InvalidParameterValueException: Invalid Content-Length: 0 end it "should not create a new application_data_backup" do expect{page.find('#backup_now').click; wait_for_ajax}.not_to change{ApplicationDataBackup.count} expect( upload_archive_post_with_error_response ).to have_been_requested.times(1) expect(page).not_to have_selector("#application_data_backups .application_data_backup") expect(flash_message).to eq "failed to create backup" end end context "when AWS sdk responds with an error" do before do allow_any_instance_of(Aws::Plugins::RequestSigner::Handler).to receive(:missing_credentials?).and_return(true) end it "should not create a new application_data_backup" do page.find('#backup_now').click wait_for_ajax expect(flash_message).to eq "failed to create backup" end end context "when there is a database configuration error" do before do allow(ActiveRecord::Base).to receive(:configurations).and_return({"test"=> {"host" => "bosh"}}) end it "should not create a new application_data_backup" do expect{page.find('#backup_now').click; wait_for_ajax}.not_to change{ApplicationDataBackup.count} expect( upload_archive_post_with_error_response ).not_to have_been_requested expect(page).not_to have_selector("#application_data_backups .application_data_backup") expect(flash_message).to eq "failed to create backup" end end context "when there is a missing database password file" do before do @config = ActiveRecord::Base.configurations[Rails.env].dup ActiveRecord::Base.configurations[Rails.env].merge!({"password" => "sekret"}) allow(File).to receive(:exists?) allow(File).to receive(:exists?).with("~/.pgpass").and_return(false) end after do ActiveRecord::Base.configurations[Rails.env] = @config end it "should not create a new application_data_backup" do expect{page.find('#backup_now').click; wait_for_ajax}.not_to change{ApplicationDataBackup.count} expect(page).not_to have_selector("#application_data_backups .application_data_backup") expect(flash_message).to eq "failed to create backup" expect(aws_log).to match /PgPass::FileMissing exception: #{File.expand_path("~/.pgpass")} file not found, cannot dump database contents/ end end context "when database password file has incorrect permissions" do before do @config = ActiveRecord::Base.configurations[Rails.env].dup ActiveRecord::Base.configurations[Rails.env].merge!({"password" => "sekret"}) allow(File).to receive(:exists?) allow(File).to receive(:exists?).with(File.expand_path("~/.pgpass")).and_return(true) status = Struct.new(:mode).new(33204) # octal: 100664 allow(File).to receive(:stat) allow(File).to receive(:stat).with(File.expand_path("~/.pgpass")).and_return(status) end after do ActiveRecord::Base.configurations[Rails.env] = @config end it "should not create a new application_data_backup" do expect{page.find('#backup_now').click; wait_for_ajax}.not_to change{ApplicationDataBackup.count} expect(page).not_to have_selector("#application_data_backups .application_data_backup") expect(flash_message).to eq "failed to create backup" expect(aws_log).to match /#{Regexp.escape PgPass::FilePermissionsError.new.message}/ end end end