<commit_before>require "spec_helper"
require "timeout"
require "fileutils"

describe "paperclip-storage-ftp", :integration => true do

  before(:all) do
    require "support/integration/ftp_server"
    require "support/integration/user"
    FtpServer.start
  end

  before(:each) do
    FtpServer.clear
  end

  let(:file) { File.new(File.expand_path("../support/integration/avatar.jpg", __FILE__), "rb") }
  let(:user) { User.new }

  let(:uploaded_file_server1)         { FtpServer::USER1_PATH + "/original/avatar.jpg" }
  let(:uploaded_file_server1_medium)  { FtpServer::USER1_PATH + "/medium/avatar.jpg"   }
  let(:uploaded_file_server1_thumb)   { FtpServer::USER1_PATH + "/thumb/avatar.jpg"    }
  let(:uploaded_file_server2)         { FtpServer::USER2_PATH + "/original/avatar.jpg" }
  let(:uploaded_file_server2_medium)  { FtpServer::USER2_PATH + "/medium/avatar.jpg"   }
  let(:uploaded_file_server2_thumb)   { FtpServer::USER2_PATH + "/thumb/avatar.jpg"    }

  let(:uploaded_file_server1_other)   { FtpServer::USER1_PATH + "/original/foo.txt" }

  it "stores the attachment on the ftp servers" do
    user.avatar = file
    user.save!

    File.exist?(uploaded_file_server1).should be true
    File.exist?(uploaded_file_server1_medium).should be true
    File.exist?(uploaded_file_server1_thumb).should be true
    File.exist?(uploaded_file_server2).should be true
    File.exist?(uploaded_file_server2_medium).should be true
    File.exist?(uploaded_file_server2_thumb).should be true

    file.size.should == File.size(uploaded_file_server1)
    file.size.should == File.size(uploaded_file_server2)
  end

  it "deletes an attachment from the ftp servers" do
    user.avatar = file
    user.save!

    user.destroy

    File.exist?(uploaded_file_server1).should be false
    File.exist?(uploaded_file_server1_medium).should be false
    File.exist?(uploaded_file_server1_thumb).should be false

    File.exist?(uploaded_file_server2).should be false
    File.exist?(uploaded_file_server2_medium).should be false
    File.exist?(uploaded_file_server2_thumb).should be false
  end

  it "removes empty parent directories after image deletion" do
    user.avatar = file
    user.save!

    user.destroy

    Dir.exist?(File.dirname(uploaded_file_server1)).should be false
    Dir.exist?(File.dirname(uploaded_file_server2)).should be false
  end

  it "does not remove parent directories which are not empty" do
    user.avatar = file
    user.save!

    FileUtils.touch(uploaded_file_server1_other)

    user.destroy

    File.exist?(uploaded_file_server1_other).should be true
  end

  it "survives temporarily closed ftp connections" do
    user.avatar = file
    user.save!

    user.avatar = nil
    user.save!

    FtpServer.restart

    user.avatar = file
    user.save!

    File.exist?(uploaded_file_server1).should be true
    File.exist?(uploaded_file_server2).should be true
  end

  it "allows ignoring failed connections" do
    user = UserIgnoringFailingConnection.new
    user.avatar = file
    expect{ user.save! }.to_not raise_error

    File.exist?(uploaded_file_server1).should be true
    File.exist?(uploaded_file_server1_medium).should be true
    File.exist?(uploaded_file_server1_thumb).should be true
    File.exist?(uploaded_file_server2).should be false
    File.exist?(uploaded_file_server2_medium).should be false
    File.exist?(uploaded_file_server2_thumb).should be false
  end

  it "raises a SystemCallError when not ignoring failed connections" do
    user = UserNotIgnoringFailingConnection.new
    user.avatar = file
    expect{ user.save! }.to raise_error(SystemCallError)
  end

  unless ENV['TRAVIS']
    it "allows setting a connect timeout" do
      user = UserWithConnectTimeout.new
      user.avatar = file

      # Wrap the expectation in a timeout block to make
      # sure we don't accidentally get a passing test by waiting
      # for the Errno::ETIMEDOUT raised by the OS (usually in the
      # seconds or minutes range)
      Timeout.timeout(UserWithConnectTimeout::TIMEOUT + 1) do
        expect { user.save! }.to raise_error(Errno::ETIMEDOUT)
      end
    end
  end

  context "performance" do
    let(:user) { UserWithOneServerAndDeepPath.new }
    let(:padded_user_id) { user.id.to_s.rjust(3, "0") }
    let(:uploaded_file_deep_path) { FtpServer::USER1_PATH + "/img/user_with_one_server_and_deep_paths/avatars/000/000/#{padded_user_id}/original/avatar.jpg" }

    it "triggers minimal amount of ftp commands by using #put_files internally" do
      expect_any_instance_of(Net::FTP).to receive(:nlst).exactly(7).times.and_call_original
      expect_any_instance_of(Net::FTP).to receive(:mkdir).exactly(9).times.and_call_original
      user.avatar = file
      user.save!
      File.exist?(uploaded_file_deep_path).should be true
    end

    context "legacy" do
      it "triggers plenty of MKDIR commands when a) not using #put_files and b) existence is not checked beforehand" do
        expect_any_instance_of(Hash).to receive(:length).exactly(4).times.and_return(1) # ugly hack to enforce legacy behaviour
        expect_any_instance_of(Paperclip::Storage::Ftp::Server).to receive(:file_exists?).exactly(3).times.and_return(false)

        expect_any_instance_of(Net::FTP).to_not receive(:mktree)
        expect_any_instance_of(Net::FTP).to_not receive(:nlst)
        expect_any_instance_of(Net::FTP).to receive(:mkdir).exactly(24).times.and_call_original

        user.avatar = file
        user.save!
      end

      it "triggers plenty of ftp commands when not using #put_files" do
        expect_any_instance_of(Hash).to receive(:length).exactly(4).times.and_return(1) # ugly hack to enforce legacy behaviour

        expect_any_instance_of(Net::FTP).to_not receive(:mktree)
        expect_any_instance_of(Net::FTP).to receive(:nlst).exactly(3).times.and_call_original
        expect_any_instance_of(Net::FTP).to receive(:mkdir).exactly(24).times.and_call_original

        user.avatar = file
        user.save!
      end
    end
  end
end
<commit_msg>remove integration specs for legacy behaviour<commit_after>require "spec_helper"
require "timeout"
require "fileutils"

describe "paperclip-storage-ftp", :integration => true do

  before(:all) do
    require "support/integration/ftp_server"
    require "support/integration/user"
    FtpServer.start
  end

  before(:each) do
    FtpServer.clear
  end

  let(:file) { File.new(File.expand_path("../support/integration/avatar.jpg", __FILE__), "rb") }
  let(:user) { User.new }

  let(:uploaded_file_server1)         { FtpServer::USER1_PATH + "/original/avatar.jpg" }
  let(:uploaded_file_server1_medium)  { FtpServer::USER1_PATH + "/medium/avatar.jpg"   }
  let(:uploaded_file_server1_thumb)   { FtpServer::USER1_PATH + "/thumb/avatar.jpg"    }
  let(:uploaded_file_server2)         { FtpServer::USER2_PATH + "/original/avatar.jpg" }
  let(:uploaded_file_server2_medium)  { FtpServer::USER2_PATH + "/medium/avatar.jpg"   }
  let(:uploaded_file_server2_thumb)   { FtpServer::USER2_PATH + "/thumb/avatar.jpg"    }

  let(:uploaded_file_server1_other)   { FtpServer::USER1_PATH + "/original/foo.txt" }

  it "stores the attachment on the ftp servers" do
    user.avatar = file
    user.save!

    File.exist?(uploaded_file_server1).should be true
    File.exist?(uploaded_file_server1_medium).should be true
    File.exist?(uploaded_file_server1_thumb).should be true
    File.exist?(uploaded_file_server2).should be true
    File.exist?(uploaded_file_server2_medium).should be true
    File.exist?(uploaded_file_server2_thumb).should be true

    file.size.should == File.size(uploaded_file_server1)
    file.size.should == File.size(uploaded_file_server2)
  end

  it "deletes an attachment from the ftp servers" do
    user.avatar = file
    user.save!

    user.destroy

    File.exist?(uploaded_file_server1).should be false
    File.exist?(uploaded_file_server1_medium).should be false
    File.exist?(uploaded_file_server1_thumb).should be false

    File.exist?(uploaded_file_server2).should be false
    File.exist?(uploaded_file_server2_medium).should be false
    File.exist?(uploaded_file_server2_thumb).should be false
  end

  it "removes empty parent directories after image deletion" do
    user.avatar = file
    user.save!

    user.destroy

    Dir.exist?(File.dirname(uploaded_file_server1)).should be false
    Dir.exist?(File.dirname(uploaded_file_server2)).should be false
  end

  it "does not remove parent directories which are not empty" do
    user.avatar = file
    user.save!

    FileUtils.touch(uploaded_file_server1_other)

    user.destroy

    File.exist?(uploaded_file_server1_other).should be true
  end

  it "survives temporarily closed ftp connections" do
    user.avatar = file
    user.save!

    user.avatar = nil
    user.save!

    FtpServer.restart

    user.avatar = file
    user.save!

    File.exist?(uploaded_file_server1).should be true
    File.exist?(uploaded_file_server2).should be true
  end

  it "allows ignoring failed connections" do
    user = UserIgnoringFailingConnection.new
    user.avatar = file
    expect{ user.save! }.to_not raise_error

    File.exist?(uploaded_file_server1).should be true
    File.exist?(uploaded_file_server1_medium).should be true
    File.exist?(uploaded_file_server1_thumb).should be true
    File.exist?(uploaded_file_server2).should be false
    File.exist?(uploaded_file_server2_medium).should be false
    File.exist?(uploaded_file_server2_thumb).should be false
  end

  it "raises a SystemCallError when not ignoring failed connections" do
    user = UserNotIgnoringFailingConnection.new
    user.avatar = file
    expect{ user.save! }.to raise_error(SystemCallError)
  end

  unless ENV['TRAVIS']
    it "allows setting a connect timeout" do
      user = UserWithConnectTimeout.new
      user.avatar = file

      # Wrap the expectation in a timeout block to make
      # sure we don't accidentally get a passing test by waiting
      # for the Errno::ETIMEDOUT raised by the OS (usually in the
      # seconds or minutes range)
      Timeout.timeout(UserWithConnectTimeout::TIMEOUT + 1) do
        expect { user.save! }.to raise_error(Errno::ETIMEDOUT)
      end
    end
  end

  context "performance" do
    let(:user) { UserWithOneServerAndDeepPath.new }
    let(:padded_user_id) { user.id.to_s.rjust(3, "0") }
    let(:uploaded_file_deep_path) { FtpServer::USER1_PATH + "/img/user_with_one_server_and_deep_paths/avatars/000/000/#{padded_user_id}/original/avatar.jpg" }

    it "triggers minimal amount of ftp commands" do
      expect_any_instance_of(Net::FTP).to receive(:nlst).exactly(7).times.and_call_original
      expect_any_instance_of(Net::FTP).to receive(:mkdir).exactly(9).times.and_call_original
      user.avatar = file
      user.save!
      File.exist?(uploaded_file_deep_path).should be true
    end
  end
end

{'fraction_non_alphanumeric': 0.0987563334868724, 'fraction_numerical': 0.01050207277752188, 'mean_word_length': 3.8791011235955057, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7625916', 'n_tokens_mistral': 3632, 'n_tokens_neox': 3553, 'n_words': 723}