require File.expand_path('../../spec_helper', __FILE__)
require "minitest-chef-handler/resources"

describe MiniTest::Chef::Assertions do
  include MiniTest::Chef::Assertions

  before do
    Etc.stubs(:getpwnam).returns stub(:name => "me")
    Etc.stubs(:getgrnam).returns stub(:name => "family")
  end

  def resource_for(hash)
    Class.new do
      hash.each_pair do |key,value|
        define_method(key) { value }
      end
    end.new
  end

  def file(path)
    file = ::Chef::Resource::File.new(path)
    def file.mode; 0755;end
    def file.owner; "xxx";end
    def file.group; "xxx";end
    file
  end

  module MiniTest::Chef::Assertions
    class File
      def self.read(path, to=-1)
        '# Generated by Chef'[0..to]
      end

      def self.mtime(path)
        Time.now.utc
      end

      def self.exists?(path)
        ["/etc", "/etc/foo", "/etc/baz", "/etc/gar"].include?(path)
      end

      def self.directory?(path)
        ['/etc', '/etc/gar'].include?(path)
      end

      def self.file?(path)
        ['/etc/foo'].include?(path)
      end

      def self.symlink?(path)
        ['/etc/baz', '/etc/gar'].include?(path)
      end
    end
  end

  it "verifies that cron entries exist" do
    cron = resource_for({:name => 'foo', :command => 'ls'})
    assert_cron_exists(cron).must_equal cron
    assert_triggered "Expected cron entry 'foo' to exist" do
      assert_cron_exists(resource_for({:name => 'foo', :command => nil}))
    end
  end

  it "verifies that cron entries do not exist" do
    cron = resource_for({:name => 'foo', :command => nil})
    refute_cron_exists(cron).must_equal cron
    assert_triggered "Expected cron entry 'foo' to not exist" do
      refute_cron_exists(resource_for({:name => 'foo', :command => 'ls'}))
    end
  end

  it "verifies that groups exist" do
    group = resource_for({:name => 'foo', :gid => 100})
    assert_group_exists(group).must_equal group
    assert_triggered "Expected group 'foo' to exist" do
      assert_group_exists(resource_for({:name => 'foo', :gid => nil}))
    end
  end

  it "verifies that groups do not exist" do
    group = resource_for({:name => 'foo', :gid => nil})
    refute_group_exists(group).must_equal group
    assert_triggered "Expected group 'foo' to not exist" do
      refute_group_exists(resource_for({:name => 'foo', :gid => 100}))
    end
  end

  it "verifies that network interfaces exist" do
    ifconfig = resource_for({:name => 'foo', :device => '/dev/eth0'})
    assert_ifconfig_exists(ifconfig).must_equal ifconfig
    assert_triggered "Expected network interface 'foo' to exist" do
      assert_ifconfig_exists(resource_for({:name => 'foo', :device => nil}))
    end
  end

  it "verifies that network interfaces do not exist" do
    ifconfig = resource_for({:name => 'foo', :device => nil})
    refute_ifconfig_exists(ifconfig).must_equal ifconfig
    assert_triggered "Expected network interface 'foo' to not exist" do
      refute_ifconfig_exists(resource_for({:name => 'foo', :device => '/dev/eth0'}))
    end
  end

  it "verifies that filesystem links exist" do
    link = resource_for({:name => 'foo', :to => 'bar'})
    assert_link_exists(link).must_equal link
    assert_triggered "Expected link 'foo' to exist" do
      assert_link_exists(resource_for({:name => 'foo', :to => nil}))
    end
  end

  it "verifies that filesystem links do not exist" do
    link = resource_for({:name => 'foo', :to => nil})
    refute_link_exists(link).must_equal link
    assert_triggered "Expected link 'foo' to not exist" do
      refute_link_exists(resource_for({:name => 'foo', :to => 'bar'}))
    end
  end

  it "verifies that users exist" do
    user = resource_for({:name => 'foo', :uid => 200})
    assert_user_exists(user).must_equal user
    assert_triggered "Expected user 'foo' to exist" do
      assert_user_exists(resource_for({:name => 'foo', :uid => nil}))
    end
  end

  it "verifies that users do not exist" do
    user = resource_for({:name => 'foo', :uid => nil})
    refute_user_exists(user).must_equal user
    assert_triggered "Expected user 'foo' to not exist" do
      refute_user_exists(resource_for({:name => 'foo', :uid => 200}))
    end
  end

  it "verifies that services are enabled" do
    service = resource_for({:name => 'bar', :enabled => true})
    assert_enabled(service).must_equal service
    assert_triggered "Expected service 'bar' to be enabled" do
      assert_enabled(resource_for({:name => 'bar', :enabled => false}))
    end
  end

  it "verifies that services are not enabled" do
    service = resource_for({:name => 'bar', :enabled => false})
    refute_enabled(service).must_equal service
    assert_triggered "Expected service 'bar' to not be enabled" do
      refute_enabled(resource_for({:name => 'bar', :enabled => true}))
    end
  end

  it "verifies that a group contains the expected members" do
    group = resource_for({:name => 'foo', :members => ['bar', 'baz']})
    assert_group_includes('bar', group).must_equal group
    assert_group_includes(['bar'], group).must_equal group
    assert_group_includes(['bar', 'baz'], group).must_equal group
    assert_triggered "Expected group 'foo' to include members: bar" do
      assert_group_includes('bar',
        resource_for({:name => 'foo', :members => ['baz']}))
    end
    assert_triggered "Expected group 'foo' to include members: bar, bert" do
      assert_group_includes(['bar', 'bert'],
        resource_for({:name => 'foo', :members => ['baz']}))
    end
    assert_triggered "Expected group 'foo' to include members: bar, bert" do
      assert_group_includes(['bar', 'bert'],
        resource_for({:name => 'foo', :members => ['bar', 'baz']}))
    end
  end

  it "verifies that a group does not contain the expected members" do
    group = resource_for({:name => 'foo', :members => ['bar', 'baz']})
    refute_group_includes('fum', group).must_equal group
    refute_group_includes(['fum'], group).must_equal group
    refute_group_includes(['fum', 'baz'], group).must_equal group
    assert_triggered "Expected group 'foo' not to include members: bar" do
      refute_group_includes('bar',
        resource_for({:name => 'foo', :members => ['bar']}))
    end
    assert_triggered "Expected group 'foo' not to include members: bar" do
      refute_group_includes(['bar'],
        resource_for({:name => 'foo', :members => ['bar', 'bert']}))
    end
    assert_triggered "Expected group 'foo' not to include members: bar, bert" do
      refute_group_includes(['bar', 'bert'],
        resource_for({:name => 'foo', :members => ['bar', 'bert']}))
    end
  end

  it "ignores order when asserting group membership" do
    group = resource_for({:name => 'foo', :members => ['bar', 'baz']})
    assert_group_includes(['bar', 'baz'], group).must_equal group
    assert_group_includes(['baz', 'bar'], group).must_equal group
  end

  it "ignores order when refuting group membership" do
    group = resource_for({:name => 'foo', :members => ['bar', 'baz']})
    assert_triggered "Expected group 'foo' not to include members: bar, baz" do
      refute_group_includes(['bar', 'baz'], group).must_equal group
    end
    assert_triggered "Expected group 'foo' not to include members: baz, bar" do
      refute_group_includes(['baz', 'bar'], group).must_equal group
    end
  end

  it "verifies that a file includes the specified content" do
    file = resource_for({:path => '/etc/bar'})
    assert_includes_content(file, 'Chef').must_equal file
    assert_triggered "Expected file '/etc/bar' to include the specified content" do
      assert_includes_content(resource_for({:path => '/etc/bar'}), 'chef')
    end
  end

  it "verifies that a file does not include the specified content" do
    file = resource_for({:path => '/etc/bar'})
    refute_includes_content(file, 'chef').must_equal file
    assert_triggered "Expected file '/etc/bar' not to include the specified content" do
      refute_includes_content(resource_for({:path => '/etc/bar'}), 'Chef')
    end
  end

  it "verifies that a package is installed" do
    package = resource_for({:name => 'bash', :version => '4.2-0ubuntu4'})
    assert_installed(package).must_equal package
    assert_triggered "Expected package 'bash' to be installed" do
      assert_installed(resource_for({:name => 'bash', :version => nil}))
    end
  end

  it "verifies that a package is not installed" do
    package = resource_for({:name => 'bash', :version => nil})
    refute_installed(package).must_equal package
    assert_triggered "Expected package 'bash' to not be installed" do
      refute_installed(resource_for({:name => 'bash', :version => '4.2-0ubuntu4'}))
    end
  end

  it "verifies that a file matches the specified content" do
    file = resource_for({:path => '/etc/bar'})
    assert_matches_content(file, /Chef$/).must_equal file
    assert_triggered "Expected the contents of file '/etc/bar' to match the regular expression '^Chef'" do
      assert_matches_content(resource_for({:path => '/etc/bar'}), '^Chef')
    end
  end

  it "verifies that a file does not match the specified content" do
    file = resource_for({:path => '/etc/bar'})
    refute_matches_content(file, /^Chef/).must_equal file
    assert_triggered "Expected the contents of file '/etc/bar' not to match the regular expression 'Chef$'" do
      refute_matches_content(resource_for({:path => '/etc/bar'}), 'Chef$')
    end
  end

  it "verifies that a file was modified after the specified time" do
    file = resource_for({:path => '/etc/bar'})
    the_distant_future = Time.utc(Time.now.utc.year + 30)
    assert_modified_after(file, Time.at(0).utc).must_equal file
    assert_triggered "Expected the file '/etc/bar' to have been modified after '#{the_distant_future}'" do
      assert_modified_after(resource_for({:path => '/etc/bar'}), the_distant_future)
    end
  end

  it "verifies that a file was not modified after the specified time" do
    file = resource_for({:path => '/etc/bar'})
    the_distant_future = Time.utc(Time.now.utc.year + 30)
    refute_modified_after(file, the_distant_future).must_equal file
    assert_triggered "Expected the file '/etc/bar' not to have been modified after '#{Time.at(0).utc}'" do
      refute_modified_after(resource_for({:path => '/etc/bar'}), Time.at(0).utc)
    end
  end

  it "verifies that a mount point is mounted" do
    mount = resource_for({:name => '/mnt/foo', :mounted => true})
    assert_mounted(mount).must_equal mount
    assert_triggered "Expected mount point '/mnt/foo' to be mounted" do
      assert_mounted(resource_for({:name => '/mnt/foo', :mounted => false}))
    end
  end

  it "verifies that a mount point is not mounted" do
    mount = resource_for({:name => '/mnt/foo', :mounted => false})
    refute_mounted(mount).must_equal mount
    assert_triggered "Expected mount point '/mnt/foo' to not be mounted" do
      refute_mounted(resource_for({:name => '/mnt/foo', :mounted => true}))
    end
  end

  it "verifies that a mount point is enabled" do
    mount = resource_for({:name => '/mnt/foo', :enabled => true})
    assert_mount_enabled(mount).must_equal mount
    assert_triggered "Expected mount point '/mnt/foo' to be enabled" do
      assert_mount_enabled(resource_for({:name => '/mnt/foo', :enabled => false}))
    end
  end

  it "verifies that a mount point is not enabled" do
    mount = resource_for({:name => '/mnt/foo', :enabled => false})
    refute_mount_enabled(mount).must_equal mount
    assert_triggered "Expected mount point '/mnt/foo' to not be enabled" do
      refute_mount_enabled(resource_for({:name => '/mnt/foo', :enabled => true}))
    end
  end

  it "verifies that a file path exists" do
    file = resource_for({:path => '/etc/foo'})
    assert_path_exists(file).must_equal file
    assert_triggered "Expected path '/etc/bar' to exist" do
      assert_path_exists(resource_for({:path => '/etc/bar'}))
    end
  end

  it "verifies that a file path does not exist" do
    file = resource_for({:path => '/etc/bar'})
    refute_path_exists(file).must_equal file
    assert_triggered "Expected path '/etc/foo' not to exist" do
      refute_path_exists(resource_for({:path => '/etc/foo'}))
    end
  end

  it "verifies that a service is running" do
    service = resource_for({:name => 'httpd', :running => true})
    assert_running(service).must_equal service
    assert_triggered "Expected service 'httpd' to be running" do
      assert_running(resource_for({:name => 'httpd', :running => false}))
    end
  end

  it "verifies that a service is not running" do
    service = resource_for({:name => 'httpd', :running => false})
    refute_running(service).must_equal service
    assert_triggered "Expected service 'httpd' not to be running" do
      refute_running(resource_for({:name => 'httpd', :running => true}))
    end
  end

  describe "#assert_acl" do
    it "does not blow up if everything is correct" do
      assert_acl("/etc", "me", "family", 0755)
    end

    it "verifies that it has the correct owner" do
      assert_triggered "The file /etc does not have the expected owner.\nExpected: \"foo\"\n  Actual: \"me\"" do
        assert_acl("/etc", "foo", "bar", 0755)
      end
    end

    it "verifies that it has the correct group" do
      assert_triggered "The file /etc does not have the expected group.\nExpected: \"bar\"\n  Actual: \"family\"" do
        assert_acl("/etc", "me", "bar", 0755)
      end
    end

    it "verifies that it has the correct mode" do
      assert_triggered "The file /etc does not have the expected mode.\nExpected: \"750\"\n  Actual: \"755\"" do
        assert_acl("/etc", "me", "family", 0750)
      end
    end
  end

  describe "#assert_directory" do
    it "does not blow up if everything is correct" do
      assert_directory("/etc", "me", "family", 0755)
    end

    it "verifies that it is a directory" do
      assert_triggered "Expected /etc/foo to be a directory" do
        assert_directory("/etc/foo", "foo", "bar", 0755)
      end
    end

    it "verifies that it has the correct acl" do
      assert_triggered "The file /etc does not have the expected owner.\nExpected: \"foo\"\n  Actual: \"me\"" do
        assert_directory("/etc", "foo", "bar", 0755)
      end
    end
  end

  describe "#assert_file" do
    it "does not blow up if everything is correct" do
      assert_file("/etc/foo", "me", "family", 0755)
    end

    it "verifies that it is a file" do
      assert_triggered "Expected /etc to be a file" do
        assert_file("/etc", "foo", "bar", 0755)
      end
    end

    it "verifies that it has the correct acl" do
      assert_triggered "The file /etc/foo does not have the expected owner.\nExpected: \"foo\"\n  Actual: \"me\"" do
        assert_file("/etc/foo", "foo", "bar", 0755)
      end
    end
  end

  describe "#assert_symlinked_file" do
    it "does not blow up if everything is correct" do
      assert_symlinked_file("/etc/baz", "me", "family", 0755)
    end

    it "verifies that it is a symlink" do
      assert_triggered "Expected /etc/foo to be a symlink" do
        assert_symlinked_file("/etc/foo", "foo", "bar", 0755)
      end
    end

    it "verifies that it has the correct acl" do
      assert_triggered "The file /etc/baz does not have the expected owner.\nExpected: \"foo\"\n  Actual: \"me\"" do
        assert_symlinked_file("/etc/baz", "foo", "bar", 0755)
      end
    end
  end

  describe "#assert_symlinked_directory" do
    def assert_sh(*args)
    end

    it "does not blow up if everything is correct" do
      assert_symlinked_directory("/etc/gar", "me", "family", 0755)
    end

    it "verifies that it is a symlink" do
      assert_triggered "Expected /etc/foo to be a symlink" do
        assert_symlinked_directory("/etc/foo", "foo", "bar", 0755)
      end
    end

    it "verifies that it has the correct acl" do
      assert_triggered "The file /etc/gar does not have the expected owner.\nExpected: \"foo\"\n  Actual: \"me\"" do
        assert_symlinked_directory("/etc/gar", "foo", "bar", 0755)
      end
    end
  end

  describe "#assert_logrotate" do
    def assert_sh(*args)
    end

    def file(path)
      file = super
      def file.mode; 0644;end
      file
    end

    it "does not blow up if everything is correct" do
      Etc.stubs(:getpwnam).returns stub(:name => "root")
      Etc.stubs(:getgrnam).returns stub(:name => "root")
      assert_logrotate("/etc/foo")
    end
  end

  describe "#assert_sh" do
    it "does not blow up if everything is correct" do
      assert_sh("true")
    end

    it "fails when command fails" do
      assert_triggered "Expected echo ABC && false to succeed, but failed with: ABC" do
        assert_sh("echo ABC && false")
      end
    end
  end

  describe "#refute_sh" do
    it "succeeds when the command fails" do
      refute_sh("false")
    end

    it "fails when the command succeeds" do
      assert_triggered "Expected true not to succeed, but succeeded with: ABC" do
        refute_sh("echo ABC && true")
      end
    end
  end
end
