#! /usr/bin/env ruby
require 'spec_helper'

describe "the generate function" do
  include PuppetSpec::Files

  before :all do
    Puppet::Parser::Functions.autoloader.loadall
  end

  let :node     do Puppet::Node.new('localhost') end
  let :compiler do Puppet::Parser::Compiler.new(node) end
  let :scope    do Puppet::Parser::Scope.new(compiler) end

  it "should exist" do
    Puppet::Parser::Functions.function("generate").should == "function_generate"
  end

  it "accept a fully-qualified path as a command" do
    command = File.expand_path('/command/foo')
    Dir.expects(:chdir).with(File.dirname(command)).returns("yay")
    scope.function_generate([command]).should == "yay"
  end

  it "should not accept a relative path as a command" do
    lambda { scope.function_generate(["command"]) }.should raise_error(Puppet::ParseError)
  end

  it "should not accept a command containing illegal characters" do
    lambda { scope.function_generate([File.expand_path('/##/command')]) }.should raise_error(Puppet::ParseError)
  end

  it "should not accept a command containing spaces" do
    lambda { scope.function_generate([File.expand_path('/com mand')]) }.should raise_error(Puppet::ParseError)
  end

  it "should not accept a command containing '..'" do
    command = File.expand_path("/command/../")
    lambda { scope.function_generate([command]) }.should raise_error(Puppet::ParseError)
  end

  it "should execute the generate script with the correct working directory" do
    command = File.expand_path("/command")
    Dir.expects(:chdir).with(File.dirname(command)).returns("yay")
    scope.function_generate([command]).should == 'yay'
  end

  describe "on Windows", :if => Puppet.features.microsoft_windows? do
    it "should accept the tilde in the path" do
      command = "C:/DOCUME~1/ADMINI~1/foo.bat"
      Dir.expects(:chdir).with(File.dirname(command)).returns("yay")
      scope.function_generate([command]).should == 'yay'
    end

    it "should accept lower-case drive letters" do
      command = 'd:/command/foo'
      Dir.expects(:chdir).with(File.dirname(command)).returns("yay")
      scope.function_generate([command]).should == 'yay'
    end

    it "should accept upper-case drive letters" do
      command = 'D:/command/foo'
      Dir.expects(:chdir).with(File.dirname(command)).returns("yay")
      scope.function_generate([command]).should == 'yay'
    end

    it "should accept forward and backslashes in the path" do
      command = 'D:\command/foo\bar'
      Dir.expects(:chdir).with(File.dirname(command)).returns("yay")
      scope.function_generate([command]).should == 'yay'
    end

    it "should reject colons when not part of the drive letter" do
      lambda { scope.function_generate(['C:/com:mand']) }.should raise_error(Puppet::ParseError)
    end

    it "should reject root drives" do
      lambda { scope.function_generate(['C:/']) }.should raise_error(Puppet::ParseError)
    end
  end

  describe "on non-Windows", :as_platform => :posix do
    it "should reject backslashes" do
      lambda { scope.function_generate(['/com\\mand']) }.should raise_error(Puppet::ParseError)
    end

    it "should accept plus and dash" do
      command = "/var/folders/9z/9zXImgchH8CZJh6SgiqS2U+++TM/-Tmp-/foo"
      Dir.expects(:chdir).with(File.dirname(command)).returns("yay")
      scope.function_generate([command]).should == 'yay'
    end
  end

  let :command do
    cmd = tmpfile('function_generate')

    if Puppet.features.microsoft_windows?
      cmd += '.bat'
      text = '@echo off' + "\n" + 'echo a-%1 b-%2'
    else
      text = '#!/bin/sh' + "\n" + 'echo a-$1 b-$2'
    end

    File.open(cmd, 'w') {|fh| fh.puts text }
    File.chmod 0700, cmd
    cmd
  end

  it "should call generator with no arguments" do
    scope.function_generate([command]).should == "a- b-\n"
  end

  it "should call generator with one argument" do
    scope.function_generate([command, 'one']).should == "a-one b-\n"
  end

  it "should call generator with wo arguments" do
    scope.function_generate([command, 'one', 'two']).should == "a-one b-two\n"
  end

  it "should fail if generator is not absolute" do
    expect { scope.function_generate(['boo']) }.to raise_error Puppet::ParseError
  end

  it "should fail if generator fails" do
    expect { scope.function_generate(['/boo']) }.to raise_error Puppet::ParseError
  end
end
