require_relative '../../spec_helper'
require 'tempfile'

describe "Tempfile.create" do
  after :each do
    if @tempfile
      @tempfile.close
      File.unlink(@tempfile.path) if File.file?(@tempfile.path)
    end
  end

  it "returns a new, open regular File instance placed in tmpdir" do
    @tempfile = Tempfile.create
    # Unlike Tempfile.open this returns a true File,
    # but `.should.instance_of?(File)` would be true either way.
    @tempfile.instance_of?(File).should == true

    @tempfile.should_not.closed?
    File.file?(@tempfile.path).should == true

    @tempfile.path.should.start_with?(Dir.tmpdir)
    @tempfile.path.should_not == "#{Dir.tmpdir}/"
  end

  it "returns file in w+ mode" do
    @tempfile = Tempfile.create
    @tempfile << "Test!\nMore test!"
    @tempfile.rewind
    @tempfile.read.should == "Test!\nMore test!"

    # Not "a+" mode, which would write at the end of the file.
    @tempfile.rewind
    @tempfile.print "Trust"
    @tempfile.rewind
    @tempfile.read.should == "Trust\nMore test!"
  end

  platform_is_not :windows do
    it "returns a private, readable and writable file" do
      @tempfile = Tempfile.create
      stat = @tempfile.stat
      stat.should.readable?
      stat.should.writable?
      stat.should_not.executable?
      stat.should_not.world_readable?
      stat.should_not.world_writable?
    end
  end

  platform_is :windows do
    it "returns a public, readable and writable file" do
      @tempfile = Tempfile.create
      stat = @tempfile.stat
      stat.should.readable?
      stat.should.writable?
      stat.should_not.executable?
      stat.should.world_readable?
      stat.should.world_writable?
    end
  end

  context "when called with a block" do
    it "returns the value of the block" do
      value = Tempfile.create do |tempfile|
        tempfile << "Test!"
        "return"
      end
      value.should == "return"
    end

    it "closes and unlinks file after block execution" do
      Tempfile.create do |tempfile|
        @tempfile = tempfile
        @tempfile.should_not.closed?
        File.exist?(@tempfile.path).should == true
      end

      @tempfile.should.closed?
      File.exist?(@tempfile.path).should == false
    end
  end

  context "when called with a single positional argument" do
    it "uses a String as a prefix for the filename" do
      @tempfile = Tempfile.create("create_spec")
      @tempfile.path.should.start_with?("#{Dir.tmpdir}/create_spec")
      @tempfile.path.should_not == "#{Dir.tmpdir}/create_spec"
    end

    it "uses an array of one String as a prefix for the filename" do
      @tempfile = Tempfile.create(["create_spec"])
      @tempfile.path.should.start_with?("#{Dir.tmpdir}/create_spec")
      @tempfile.path.should_not == "#{Dir.tmpdir}/create_spec"
    end

    it "uses an array of two Strings as a prefix and suffix for the filename" do
      @tempfile = Tempfile.create(["create_spec", ".temp"])
      @tempfile.path.should.start_with?("#{Dir.tmpdir}/create_spec")
      @tempfile.path.should.end_with?(".temp")
    end

    it "ignores excessive array elements after the first two" do
      @tempfile = Tempfile.create(["create_spec", ".temp", :".txt"])
      @tempfile.path.should.start_with?("#{Dir.tmpdir}/create_spec")
      @tempfile.path.should.end_with?(".temp")
    end

    it "raises ArgumentError if passed something else than a String or an array of Strings" do
      -> { Tempfile.create(:create_spec) }.should.raise(ArgumentError, "unexpected prefix: :create_spec")
      -> { Tempfile.create([:create_spec]) }.should.raise(ArgumentError, "unexpected prefix: :create_spec")
      -> { Tempfile.create(["create_spec", :temp]) }.should.raise(ArgumentError, "unexpected suffix: :temp")
    end
  end

  context "when called with a second positional argument" do
    it "uses it as a directory for the tempfile" do
      @tempfile = Tempfile.create("create_spec", "./")
      @tempfile.path.should.start_with?("./create_spec")
    end

    it "raises TypeError if argument can not be converted to a String" do
      -> { Tempfile.create("create_spec", :temp) }.should.raise(TypeError, "no implicit conversion of Symbol into String")
    end
  end

  context "when called with a mode option" do
    it "ORs it with the default mode, forcing it to be readable and writable" do
      @tempfile = Tempfile.create(mode: File::RDONLY)
      @tempfile.puts "test"
      @tempfile.rewind
      @tempfile.read.should == "test\n"
    end

    it "raises NoMethodError if passed a String mode" do
      -> { Tempfile.create(mode: "wb") }.should.raise(NoMethodError, /undefined method ['`]|' for .+String/)
    end
  end

  ruby_version_is "3.4" do
    context "when called with anonymous: true" do
      it "returns an already unlinked File without a proper path" do
        @tempfile = Tempfile.create(anonymous: true)
        @tempfile.should_not.closed?
        @tempfile.path.should == "#{Dir.tmpdir}/"
        File.file?(@tempfile.path).should == false
      end

      it "unlinks file before calling the block" do
        Tempfile.create(anonymous: true) do |tempfile|
          @tempfile = tempfile
          @tempfile.should_not.closed?
          @tempfile.path.should == "#{Dir.tmpdir}/"
          File.file?(@tempfile.path).should == false
        end
        @tempfile.should.closed?
      end
    end

    context "when called with anonymous: false" do
      it "returns a usual File with a path" do
        @tempfile = Tempfile.create(anonymous: false)
        @tempfile.should_not.closed?
        @tempfile.path.should.start_with?(Dir.tmpdir)
        File.file?(@tempfile.path).should == true
      end
    end
  end

  context "when called with other options" do
    it "passes them along to File.open" do
      @tempfile = Tempfile.create(encoding: "IBM037:IBM037", binmode: true)
      @tempfile.external_encoding.should == Encoding.find("IBM037")
      @tempfile.binmode?.should == true
    end
  end
end
