#!/usr/bin/ruby -I./lib/ -I../lib/


require 'test/unit'
require 'custodian/parser'




#
# Unit test for our parser.
#
class TestCustodianParser < Test::Unit::TestCase




  #
  # Create the test suite environment: NOP.
  #
  def setup
  end




  #
  # Destroy the test suite environment: NOP.
  #
  def teardown
  end




  #
  #  Test we can create a new parser object - specifically
  # that it throws exceptions if it is not given a filename
  # that exists.
  #
  def test_init

    #
    #  Constructor
    #
    assert_nothing_raised do
      Custodian::Parser.new()
    end
  end


  def test_period
    parser = Custodian::Parser.new()
    result = parser.parse_line( "example.vm.bytemark.co.uk must run ping except between 00-23" )
    assert( result.nil? )
  end



  #
  #  Test the different kinds of parsing:
  #
  #  1.  By string - single line.
  #  2.  By array - with multiple lines.
  #  3.  By lines - with newlines.
  #
  def test_parsing_api

    #
    #  1.  By string.
    #
    parser = Custodian::Parser.new()

    #  1.a.  Comment lines return nil.
    result = parser.parse_line( "# this is a comment" )
    assert( result.nil? )

    #  1.b.  Non-strings are an error
    assert_raise ArgumentError do
      result = parser.parse_line( nil )
    end


    #  1.c.  Adding a test will return an array of test-objects.
    result = parser.parse_line( "smtp.bytemark.co.uk must run smtp on 25 otherwise 'failure'." );
    assert( !result.nil? )
    assert( result.kind_of? Array )
    assert( result.size == 1 )


    #
    # 2.  By array.
    #
    parser = Custodian::Parser.new()
    #  2.a.  Comment lines return nil.
    tmp    = Array.new()
    tmp.push( "# This is a comment.." )
    assert( parser.parse_lines( tmp ).nil? )

    #  2.b.  Adding a test will return an array of test-objects.
    tmp = Array.new()
    tmp.push( "smtp.bytemark.co.uk must run ssh on 22 otherwise 'oops'." );
    ret = parser.parse_lines( tmp )
    assert( ret.kind_of? Array );
    assert( ret.size == 1 )

    #
    # 3.  By lines
    #
    parser = Custodian::Parser.new()
    #  3.a.  Comment lines return nil.
    str =<<EOF
# This is a comment
# This is also a fine comment
EOF
    assert( parser.parse_lines( str ).nil? )

    #  3.b.  Adding a test will return an array of test-objects.
    str = <<EOF
smtp.bytemark.co.uk must run smtp on 25.
google.com must run ping otherwise 'internet broken?'.
EOF
    ret = parser.parse_lines( str )
    assert( ret.kind_of? Array );
    assert( ret.size == 1 )

  end




  #
  #  Test that we can define macros.
  #
  def test_macros_lines

    parser = Custodian::Parser.new()

    #
    #  Input text
    #
    text =<<EOF
FOO is  kvm1.vm.bytemark.co.uk.
TEST is kvm2.vm.bytemark.co.uk.
EOF

    #
    # Test the parser with this text
    #
    parser.parse_lines( text )


    #
    #  We should now have two macros.
    #
    macros = parser.macros
    assert( ! macros.empty? )
    assert( macros.size() == 2 )
  end




  #
  #  Test that we can define macros.
  #
  def test_macros_array

    parser = Custodian::Parser.new()

    #
    #  Input text
    #
    text = Array.new()
    text.push( "FOO  is  kvm1.vm.bytemark.co.uk." );
    text.push( "FOO2 is  kvm2.vm.bytemark.co.uk." );

    #
    # Test the parser with this text
    #
    parser.parse_lines( text )


    #
    #  We should now have two macros.
    #
    macros = parser.macros
    assert( ! macros.empty? )
    assert( macros.size() == 2 )
  end




  #
  # Duplicate macros are a bug
  #
  def test_duplicate_macros

    parser = Custodian::Parser.new()

    #
    #  Input text to parse.
    #
    text = Array.new()
    text.push( "FOO is kvm1.vm.bytemark.co.uk." );
    text.push( "FOO is kvm2.vm.bytemark.co.uk." );

    #
    # Test the parser with this text
    #
    assert_raise ArgumentError do
      parser.parse_lines( text )
    end


    #
    #  We should now have one macro.
    #
    macros = parser.macros
    assert( ! macros.empty? )
    assert( macros.size() == 1 )
  end




  #
  # Test the expansion of macros.
  #
  def test_macro_expansion

    #
    #  Create a parser - validate it is free of macros.
    #
    parser = Custodian::Parser.new()
    macros = parser.macros
    assert( macros.empty? )

    #
    #  Expand a line - which should result in no change
    # as the line does not involve a known-macro
    #
    in_txt  = "example.bytemark.co.uk must run smtp."
    out_txt = parser.expand_macro( in_txt )

    #
    #  The difference is the return value will be an array
    #
    assert( out_txt.kind_of? Array )
    assert( out_txt.size() == 1 )
    assert( out_txt[0] == in_txt )


    #
    #  Now define a macro
    #
    parser.parse_line( "TARGET is example1.bytemark.co.uk and example2.bytemark.co.uk." )
    macros = parser.macros
    assert( !macros.empty? )

    #
    # Now we have a two-host macro, repeat the expansion
    #
    ret = parser.expand_macro( "TARGET must run smtp on 25." )

    #
    # The result should be an array
    #
    assert( ret.kind_of? Array )
    assert_equal( ret.size(), 2 )
    assert( ret[0] =~ /example1/)
    assert( ret[1] =~ /example2/)

  end


  #
  # Test that the parser works for HTTP-redirection
  #
  def test_http_redirection

    #
    # test data
    #
    data = {
      "http://example must run http."                         => true,
      "http://example must run http with status 200."         => true,
      "http://example must run http with content 'bar'."      => true,
      "http://example must run http following redirects."     => true,
      "http://example must run http not following redirects." => false,
      "http://example must run http not following redirect."  => false,
    }

    data.each do |str,follow|
      assert_nothing_raised do

        #
        # Create the new parser
        #
        obj = Custodian::TestFactory.create( str )

        assert( !obj.nil? )
        assert( obj.kind_of? Array )
        assert( obj.size == 1 )
        assert_equal( obj[0].to_s, str )

        if ( follow )
          assert( obj[0].follow_redirects? )
        else
          assert( ! obj[0].follow_redirects? )
        end
      end
    end
  end



  #
  # Test that the parser works for cache-busting.
  #
  def test_http_cache_busting

    #
    # test data
    #
    data = {
      "http://example must run http."                         => true,
      "http://example must run http with status 200."         => true,
      "http://example must run http with content 'bar'."      => true,
      "http://example must run http without cache busting."   => false,
    }

    data.each do |str,cb|
      assert_nothing_raised do

        #
        # Create the new parser
        #
        obj = Custodian::TestFactory.create( str )

        assert( !obj.nil? )
        assert( obj.kind_of? Array )
        assert( obj.size == 1 )
        assert_equal( obj[0].to_s, str )

        if ( cb )
          assert( obj[0].cache_busting? )
        else
          assert( ! obj[0].cache_busting? )
        end
      end
    end
  end




  #
  # Test that the text we're going to use in alerters is present.
  #
  def test_alert_text

    #
    # test data
    #
    data = {
      "foo must run rsync."                     => nil,
      "foo must run redis."                     => nil,
      "foo must not run ping."                  => nil,
      "foo must not run ssh otherwise 'fail'"   => "fail",
      "foo must not run ssh otherwise 'fail'."  => "fail",
      "foo must run redis otherwise 'memorystorage service is dead'" => "memorystorage service is dead",
      "foo must run ping otherwise 'don't you love me?'" => "don"
    }

    #
    #  For each test
    #
    data.each do |str,fail|
      assert_nothing_raised do

        #
        # Create the new parser
        #
        obj = Custodian::TestFactory.create( str )

        assert( !obj.nil? )
        assert( obj.kind_of? Array )
        assert( obj.size == 1 )
        assert_equal( obj[0].to_s, str )

        if ( fail.nil? )
          assert( obj[0].get_notification_text().nil? )
        else
          assert_equal( obj[0].get_notification_text(), fail )
        end

      end
    end
  end
end