require 'test_helper'
require 'ostruct'
require 'yaml'

module InstanceAgent
  module Plugins
    module CodeDeployPlugin
      module ApplicationSpecification

        class ApplicationSpecificationTest < InstanceAgentTestCase
          def make_app_spec
            ApplicationSpecification.new(YAML.load(@app_spec_string), {:revision_id => @test_revision_id})
          end

          context 'The Application Specification' do
            setup do
              @test_revision_id = 'bar'
            end

            private

            context "With empty AppSpec" do
              setup do
                @app_spec_string = ""
              end

              should "raise an exception" do
                assert_raised_with_message("The deployment failed because the application specification file was empty. Make sure your AppSpec file defines at minimum the 'version' and 'os' properties.", AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With whitespace-only AppSpec" do
              setup do
                @app_spec_string = " \n"
              end

              should "raise an exception" do
                assert_raised_with_message("The deployment failed because the application specification file was empty. Make sure your AppSpec file defines at minimum the 'version' and 'os' properties.", AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With missing version" do
              setup do
                @app_spec_string = <<-END
              os: linux
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because an invalid version value () was entered in the application specification file. Make sure your AppSpec file specifies "0.0" as the version, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With invalid version" do
              setup do
                @app_spec_string = <<-END
              version: invalid
              os: linux
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because an invalid version value (invalid) was entered in the application specification file. Make sure your AppSpec file specifies "0.0" as the version, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With invalid file_exists_behavior" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              file_exists_behavior: invalid
              os: linux
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because an invalid file_exists_behavior value (invalid) was entered in the application specification file. Make sure your AppSpec file specifies one of DISALLOW,OVERWRITE,RETAIN as the file_exists_behavior, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With missing os" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because the application specification file specifies an unsupported operating system (). Specify either "linux" or "windows" in the os section of the AppSpec file, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With invalid os" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: unsupported
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because the application specification file specifies an unsupported operating system (unsupported). Specify either "linux" or "windows" in the os section of the AppSpec file, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With no hooks or files" do
              setup do
                @app_spec_string = "version: 0.0\nos: linux\n"
              end
              should "Return an empty hooks hash" do
                app_spec = make_app_spec
                assert_equal({}, app_spec.hooks)
              end
              should "Return an empty files array" do
                app_spec = make_app_spec
                assert_equal([], app_spec.files)
              end
            end

            context "With a single complete hook" do
              setup do
                #A single test script with all parameters
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              hooks:
                test_hook:
                  - location: test_location_1
                    runas: foo
                    timeout: 30
                END
              end
              should "Return a collection containing test script 1" do
                app_spec = make_app_spec
                assert_not_equal nil, app_spec.hooks
                assert_equal ['test_location_1'] , app_spec.hooks["test_hook"].map(&:location)
                assert_equal ['foo'] , app_spec.hooks["test_hook"].map(&:runas)
                assert_equal [30] , app_spec.hooks["test_hook"].map(&:timeout)
              end
            end

            context "With sudo hook" do
              setup do
                #A single test script with all parameters
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              hooks:
                test_hook:
                  - location: test_location_1
                    sudo: true
                    runas: foo
                    timeout: 30
                END
              end
              should "Return a collection containing test script 1" do
                app_spec = make_app_spec
                assert_not_equal nil, app_spec.hooks
                assert_equal ['test_location_1'] , app_spec.hooks["test_hook"].map(&:location)
                assert_equal [true], app_spec.hooks["test_hook"].map(&:sudo)
                assert_equal ['foo'] , app_spec.hooks["test_hook"].map(&:runas)
                assert_equal [30] , app_spec.hooks["test_hook"].map(&:timeout)
              end
            end

            context "With two complete hooks" do
              setup do
                #A pair of test scripts with all parameters
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              hooks:
                test_hook:
                  - location: test_location_1
                    runas: foo
                    timeout: 30
                  - location: test_location_2
                    runas: foo2
                    timeout: 30
                END
              end

              should "Return a collection containing test script 1 and test script 2" do
                app_spec = make_app_spec
                assert_not_equal nil, app_spec.hooks
                assert_equal ['test_location_1', 'test_location_2'] , app_spec.hooks["test_hook"].map(&:location)
              end
            end

            context "With partial hooks (just a runas)" do
              setup do
                #A test script with just a location
                #A test script with location and runas
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              hooks:
                test_hook:
                  - location: test_location
                  - location: test_location_lr
                    runas: foo3
                END
              end

              should "Return a collection containing the two scripts in order" do
                app_spec = make_app_spec()
                assert_not_equal nil, app_spec.hooks
                assert_equal [nil, 'foo3'] , app_spec.hooks["test_hook"].map(&:runas)
              end
            end

            context "With partial hooks (just a timeout)" do
              setup do
                #A test script with just a location
                #A test script with location and timeout
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              hooks:
                test_hook:
                  - location: test_location
                  - location: test_location_lt
                    timeout: 30
                END
              end

              should "Return a collection containing the two scripts in order" do
                app_spec = make_app_spec()
                assert_not_equal nil, app_spec.hooks
                assert_equal [3600, 30] , app_spec.hooks["test_hook"].map(&:timeout)
              end
            end

            context "With empty hook" do
              setup do
                #A test script without a location
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              hooks:
                empty_test_hook:
                test_hook:
                  - location: test_location
                    timeout: 30
                END
              end

              should "Return ignore empty hook" do
                app_spec = make_app_spec()
                assert_not_equal nil, app_spec.hooks
                assert_equal nil , app_spec.hooks["empty_test_hook"]
                assert_equal ['test_location'] , app_spec.hooks["test_hook"].map(&:location)
              end
            end

            context "With missing location data" do
              setup do
                #A test script without a location
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              hooks:
                test_hook:
                  - timeout: 30
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because the application specification file specifies a script with no location value. Specify the location in the hooks section of the AppSpec file, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With non numerical timeout data" do
              setup do
                #A test script with bad timeout data
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              hooks:
                test_hook:
                  - location: test_location
                    timeout: foo
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because an invalid timeout value was provided for a script in the application specification file. Make corrections in the hooks section of the AppSpec file, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "App spec has a file mapping" do
              context "file map contains a single file" do
                setup do
                  @app_spec_string = <<-END
                version: 0.0
                os: linux
                files:
                  - source: test_source
                    destination: test_destination
                  END
                end

                should "return a hash containing the file mapping objects" do
                  app_spec = make_app_spec
                  assert_not_equal nil, app_spec.files
                  assert_equal ['test_source'] , app_spec.files.map(&:source)
                  assert_equal ['test_destination'] , app_spec.files.map(&:destination)
                end
              end

              context "file map contains multiple files" do
                setup do
                  @app_spec_string = <<-END
                version: 0.0
                os: linux
                files:
                  - source: test_source
                    destination: test_destination
                  - source: test_source_2
                    destination: test_destination_2
                  END
                end

                should  "return a hash containing the file mapping objects" do
                  app_spec = make_app_spec
                  assert_not_equal nil, app_spec.files
                  assert_equal ['test_source', 'test_source_2'] , app_spec.files.map(&:source)
                  assert_equal ['test_destination','test_destination_2'] , app_spec.files.map(&:destination)
                end
              end

              context "file map is missing a destination" do
                setup do
                  @app_spec_string = <<-END
                version: 0.0
                os: linux
                files:
                  - source: test_source
                  END
                end

                should  "raise and AppSpecValidationException" do
                  assert_raised_with_message('The deployment failed because the application specification file specifies only a source file (test_source). Add the name of the destination file to the files section of the AppSpec file, and then try again.',AppSpecValidationException) do
                    make_app_spec()
                  end
                end
              end

              context "file map is missing a source" do
                setup do
                  @app_spec_string = <<-END
                version: 0.0
                os: linux
                files:
                  - destination: test_destination
                  END
                end

                should  "raise and AppSpecValidationException" do
                  assert_raised_with_message('The deployment failed because the application specification file specifies a destination file, but no source file. Update the files section of the AppSpec file, and then try again.',AppSpecValidationException) do
                    make_app_spec()
                  end
                end
              end
            end

            context "With permission without object set" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - pattern: test
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because a permission listed in the application specification file has no object value. Update the permissions section of the AppSpec file, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission pattern of **" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: '/'
                  pattern: '**'
                END
              end

              should "match all objects" do
                app_spec = make_app_spec()
                assert_equal '**', app_spec.permissions[0].pattern
              end
            end

            context "With multiple permissions" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: '/'
                - object: test
                - object: more
                END
              end

              should "match all objects" do
                app_spec = make_app_spec()
                assert_equal 3, app_spec.permissions.length
                assert_equal '/', app_spec.permissions[0].object
                assert_equal "test", app_spec.permissions[1].object
                assert_equal "more", app_spec.permissions[2].object
              end
            end

            context "With permissions with pattern" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: '/'
                  pattern: 'glob'
                END
              end

              should "raise when validated as file permission" do
                app_spec = make_app_spec()
                assert_raised_with_message('The deployment failed because the application specification file includes an object (/) with an invalid pattern (glob), such as a pattern for a file applied to a directory. Correct the permissions section of the AppSpec file, and then try again.',AppSpecValidationException) do
                  app_spec.permissions[0].validate_file_permission
                end
              end
            end

            context "With permissions with except" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: '/'
                  except:
                    - 'glob'
                END
              end

              should "raise when validated as file permission" do
                app_spec = make_app_spec()
                assert_raised_with_message('The deployment failed because the except parameter for a pattern in the permissions section (["glob"]) for the object named / contains an invalid format. Update the AppSpec file, and then try again.',AppSpecValidationException) do
                  app_spec.permissions[0].validate_file_permission
                end
              end
            end

            context "With permissions" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: '/'
                END
              end

              should "not raise when validated as file permission" do
                app_spec = make_app_spec()
                assert_nothing_raised do
                  app_spec.permissions[0].validate_file_permission
                end
              end
            end

            context "With permissions with pattern without file type" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: '/'
                  pattern: 'glob'
                  type:
                    - "directory"
                END
              end

              should "not raise when validated as file permission" do
                app_spec = make_app_spec()
                assert_nothing_raised do
                  app_spec.permissions[0].validate_file_permission
                end
              end
            end

            context "With permissions with acl without default ace" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: '/'
                  acls:
                    - 'user:name:rwx'
                END
              end

              should "be able to validate as a file acl" do
                app_spec = make_app_spec()
                assert_nothing_raised do
                  app_spec.permissions[0].validate_file_acl("test")
                end
              end
            end

            context "With permissions with acl with default ace" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: '/'
                  acls:
                    - 'd:user:name:rwx'
                END
              end

              should "be able to validate as a file acl" do
                app_spec = make_app_spec()
                assert_raised_with_message('The deployment failed because the -d parameter has been specified to apply an acl setting to a file. This parameter is supported for directories only. Update the AppSpec file, and then try again.',RuntimeError) do
                  app_spec.permissions[0].validate_file_acl("test")
                end
              end
            end

            context "With valid permission object" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test'
                  pattern: 'prefix*'
                  except: ['*ReadMe', '*.tmp']
                  type: ['file']
                  owner: 'bob'
                  group: 'dev'
                  mode: 6763
                  acls:
                    - 'u:henry:7'
                    - 'd:g:manager:rw'
                  context:
                    name: 'user_u'
                    type: 'unconfined_t'
                    range: 's3-s5:c0.c7,c13'
                END
              end

              should "match pattern when appropriate" do
                app_spec = make_app_spec()
                permission = app_spec.permissions[0]

                assert permission.matches_pattern?(File.expand_path("test/prefix")), "Should match test/prefix for pattern"
                assert permission.matches_pattern?(File.expand_path("test/prefix_matches")), "Should match test/prefix_matches for pattern"
                assert !permission.matches_pattern?(File.expand_path("test/prefix/does_not_match")), "Should not match test/prefix/does_not_match for pattern"
                assert !permission.matches_pattern?(File.expand_path("testprefix/")), "Should not match testprefix/ for pattern"
                assert !permission.matches_pattern?(File.expand_path("tst/prefix")), "Should not match tst/prefix for pattern"
                assert !permission.matches_pattern?(File.expand_path("test/not_prefix")), "Should not match test/not_prefix for pattern"
              end

              should "match except when appropriate" do
                app_spec = make_app_spec()
                permission = app_spec.permissions[0]

                assert permission.matches_except?(File.expand_path("test/this.tmp")), "Should match test/this.tmp for except"
                assert permission.matches_except?(File.expand_path("test/this_ReadMe")), "Should match test/this_ReadMe for except"
                assert !permission.matches_except?(File.expand_path("test/prefix/does_not_match.tmp")), "Should not match test/prefix/does_not_match.tmp for except"
                assert !permission.matches_except?(File.expand_path("testprefix/")), "Should not match testprefix/ for except"
                assert !permission.matches_except?(File.expand_path("tst/prefix")), "Should not match tst/prefix for except"
                assert !permission.matches_except?(File.expand_path("test/not_match")), "Should not match test/not_match for except"
              end

              should "set fields correctly" do
                app_spec = make_app_spec()
                permission = app_spec.permissions[0]
                assert_equal 'test', permission.object
                assert_equal 'prefix*', permission.pattern
                assert_equal ['*ReadMe', '*.tmp'], permission.except
                assert_equal ['file'], permission.type
                assert_equal 'bob', permission.owner
                assert_equal 'dev', permission.group

                mode = permission.mode
                assert_equal '6763', mode.mode
                assert_equal '3', mode.world
                assert_equal false, mode.world_readable
                assert_equal true, mode.world_writable
                assert_equal true, mode.world_executable
                assert_equal '6', mode.group
                assert_equal true, mode.group_readable
                assert_equal true, mode.group_writable
                assert_equal false, mode.group_executable
                assert_equal '7', mode.owner
                assert_equal true, mode.owner_readable
                assert_equal true, mode.owner_writable
                assert_equal true, mode.owner_executable
                assert_equal true, mode.setuid
                assert_equal true, mode.setgid
                assert_equal false, mode.sticky

                acl = permission.acls
                assert_equal 2, acl.aces.length
                ace = acl.aces[0]
                assert_equal false, ace.default
                assert_equal 'user', ace.type
                assert_equal 'henry', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[1]
                assert_equal true, ace.default
                assert_equal 'group', ace.type
                assert_equal 'manager', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal false, ace.execute

                context = permission.context
                assert_equal 'user_u', context.user
                assert_equal nil, context.role
                assert_equal 'unconfined_t', context.type

                range = context.range
                assert_equal 3, range.low_sensitivity
                assert_equal 5, range.high_sensitivity

                categories = range.categories
                assert_equal 9, categories.length
                [(0..7).to_a,13].flatten!.each do |category|
                  assert_equal true, categories.include?(category), "Unable to find expected category #{category}"
                end
              end
            end

            context "With permission with acl with ace with too few parts" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - '7'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because of a problem with the acls permission settings in the application specification file. Invalid acl entry (7).',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with acl with ace with too many parts" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'd:u:bob:7:rwx'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because of a problem with the acls permission settings in the application specification file. Invalid acl entry (d:u:bob:7:rwx).',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with acl with ace with invalid first part" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'invalid:u:bob:7:rwx'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because of a problem with the acls permission settings in the application specification file. Invalid acl entry (invalid:u:bob:7:rwx).',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with acl with ace with invalid second part" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'd:invalid:bob:7:rwx'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because of a problem with the acls permission settings in the application specification file. Invalid acl entry (d:invalid:bob:7:rwx).',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with acl with ace with default as first and second part" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'd:d:bob:7:rwx'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because of a problem with the acls permission settings in the application specification file. Invalid acl entry (d:d:bob:7:rwx).',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with acl with ace with mask with name" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'mask:name:rwx'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because of a problem with the acls permission settings in the application specification file. Invalid acl entry (mask:name:rwx).',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with acl with ace with other with name" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'd:other:name:rwx'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because of a problem with the acls permission settings in the application specification file. Invalid acl entry (d:other:name:rwx).',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with acl with ace with invalid permission character" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'user:bob:rwxd'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because the access control list (ACL) named user:bob:rwxd in the application specification file contains an invalid character (d). Correct the ACL in the hooks section of the AppSpec file, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with acl with valid ace with 4 parts" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'd:u:bob:rwx'
                    - 'default:g:dev:rw'
                END
              end

              should "generate correct fields" do
                app_spec = make_app_spec()

                acl = app_spec.permissions[0].acls
                assert_equal 2, acl.aces.length

                ace = acl.aces[0]
                assert_equal true, ace.default
                assert_equal 'user', ace.type
                assert_equal 'bob', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[1]
                assert_equal true, ace.default
                assert_equal 'group', ace.type
                assert_equal 'dev', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal false, ace.execute
              end
            end

            context "With permission with acl with valid ace with 3 parts" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'd:bob:rwx'
                    - 'default::rw'
                    - 'm::7'
                    - 'mask::7'
                    - 'g:dev:7'
                    - 'group:dev:7'
                    - 'u:bob:7'
                    - 'user:bob:7'
                    - 'u:mask:7'
                    - 'u:other:7'
                END
              end

              should "generate correct fields" do
                app_spec = make_app_spec()

                acl = app_spec.permissions[0].acls
                assert_equal 10, acl.aces.length

                ace = acl.aces[0]
                assert_equal true, ace.default
                assert_equal 'user', ace.type
                assert_equal 'bob', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[1]
                assert_equal true, ace.default
                assert_equal 'user', ace.type
                assert_equal '', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal false, ace.execute

                ace = acl.aces[2]
                assert_equal false, ace.default
                assert_equal 'mask', ace.type
                assert_equal '', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[3]
                assert_equal false, ace.default
                assert_equal 'mask', ace.type
                assert_equal '', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[4]
                assert_equal false, ace.default
                assert_equal 'group', ace.type
                assert_equal 'dev', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[5]
                assert_equal false, ace.default
                assert_equal 'group', ace.type
                assert_equal 'dev', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[6]
                assert_equal false, ace.default
                assert_equal 'user', ace.type
                assert_equal 'bob', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[7]
                assert_equal false, ace.default
                assert_equal 'user', ace.type
                assert_equal 'bob', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[8]
                assert_equal false, ace.default
                assert_equal 'user', ace.type
                assert_equal 'mask', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[9]
                assert_equal false, ace.default
                assert_equal 'user', ace.type
                assert_equal 'other', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute
              end
            end

            context "With permission with acl with valid ace with 2 parts" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'bob:0'
                    - 'm:7'
                    - 'mask:'
                END
              end

              should "generate correct fields" do
                app_spec = make_app_spec()

                acl = app_spec.permissions[0].acls
                assert_equal 3, acl.aces.length

                ace = acl.aces[0]
                assert_equal false, ace.default
                assert_equal 'user', ace.type
                assert_equal 'bob', ace.name
                assert_equal false, ace.read
                assert_equal false, ace.write
                assert_equal false, ace.execute

                ace = acl.aces[1]
                assert_equal false, ace.default
                assert_equal 'mask', ace.type
                assert_equal '', ace.name
                assert_equal true, ace.read
                assert_equal true, ace.write
                assert_equal true, ace.execute

                ace = acl.aces[2]
                assert_equal false, ace.default
                assert_equal 'mask', ace.type
                assert_equal '', ace.name
                assert_equal false, ace.read
                assert_equal false, ace.write
                assert_equal false, ace.execute
              end
            end

            context "With permission with context with invalid sensitivity range" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's3-s2:c0'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because of a problem with the SELinux range specified (s3-s2:c0) for the context parameter in the permissions section of the application specification file. Make corrections in the permissions section of the AppSpec file, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with missing sensitivity range part" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's3-:c0'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid range part s3-',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With single sensitivity" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    type: 'type'
                    range: 's5'
                END
              end

              should "generate correct fields" do
                app_spec = make_app_spec()

                assert_equal 5, app_spec.permissions[0].context.range.low_sensitivity
                assert_equal 5, app_spec.permissions[0].context.range.high_sensitivity
                assert_equal nil, app_spec.permissions[0].context.range.categories
              end
            end

            context "With permission with context with missing sensitivity" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: ':c0'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid range part :c0',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with missing sensitivity value" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid sensitivity s',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with negative sensitivity value" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's0-s-1'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid sensitivity s-1',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with invalid sensitivity" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 'sd3'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid sensitivity sd3',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with invalid sensitivity 2" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 'd3'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid sensitivity d3',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with invalid category range" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's0:c2.c1'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid category range c2.c1',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with missing category range part" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's0:c2.'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid range part c2.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With valid category" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    type: 'type'
                    range: 's0:c0.c1,c15,c7.c9'
                END
              end

              should "generate correct fields" do
                app_spec = make_app_spec()

                categories = app_spec.permissions[0].context.range.categories
                assert_equal 6, categories.length
                [(0..1).to_a, (7..9).to_a, 15].flatten!.each do |category|
                  assert_equal true, categories.include?(category), "Unable to find expected category #{category}"
                end
              end
            end

            context "With permission with context with missing category" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's0:'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid range part s0:',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with missing category value" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's0:c'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid category c',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with negative category value" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's0:c-1'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid category c-1',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with category value above 1023" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's0:c1024'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid category c1024',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context without type" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                END
              end

              should "raise an exception" do
                assert_raised_with_message("The deployment failed because the application specification file specifies an invalid context type ({\"name\"=>\"name\"}). Update the permissions section of the AppSpec file, and then try again.",AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with invalid category" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's0:cd3'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid category cd3',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with invalid category 2" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's0:d3'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('invalid category d3',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with context with duplicate categories" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  context:
                    name: 'name'
                    type: 'type'
                    range: 's0:c0.c2,c1'
                END
              end

              should "raise an exception" do
                assert_raised_with_message('duplicate categories',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with mode with 5 digits" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  mode: 12345
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because the length of a permissions mode (12345) in the application specification file is invalid. Permissions modes must be between one and four characters long. Update the permissions section of the AppSpec file, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with mode with 2 digits" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  mode: 12
                END
              end

              should "fill in needed zeros" do
                app_spec = make_app_spec()

                mode = app_spec.permissions[0].mode
                assert_equal '012', mode.mode
                assert_equal '2', mode.world
                assert_equal false, mode.world_readable
                assert_equal true, mode.world_writable
                assert_equal false, mode.world_executable
                assert_equal '1', mode.group
                assert_equal false, mode.group_readable
                assert_equal false, mode.group_writable
                assert_equal true, mode.group_executable
                assert_equal '0', mode.owner
                assert_equal false, mode.owner_readable
                assert_equal false, mode.owner_writable
                assert_equal false, mode.owner_executable
                assert_equal false, mode.setuid
                assert_equal false, mode.setgid
                assert_equal false, mode.sticky
              end
            end

            context "With permission with mode with invalid char" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  mode: 12a
                END
              end

              should "raise an exception" do
                assert_raised_with_message('The deployment failed because the permissions mode (12a) in the application specification file contains an invalid character (a). Update the permissions section of the AppSpec file, and then try again.',AppSpecValidationException) do
                  make_app_spec()
                end
              end
            end

            context "With permission with valid modes" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  mode: 7777
                - object: 'test1/'
                  mode: 0000
                - object: 'test2/'
                  mode: 777
                END
              end

              should "generate correct fields" do
                app_spec = make_app_spec()

                mode = app_spec.permissions[0].mode
                assert_equal '7777', mode.mode
                assert_equal '7', mode.world
                assert_equal true, mode.world_readable
                assert_equal true, mode.world_writable
                assert_equal true, mode.world_executable
                assert_equal '7', mode.group
                assert_equal true, mode.group_readable
                assert_equal true, mode.group_writable
                assert_equal true, mode.group_executable
                assert_equal '7', mode.owner
                assert_equal true, mode.owner_readable
                assert_equal true, mode.owner_writable
                assert_equal true, mode.owner_executable
                assert_equal true, mode.setuid
                assert_equal true, mode.setgid
                assert_equal true, mode.sticky

                mode = app_spec.permissions[1].mode
                assert_equal '000', mode.mode
                assert_equal '0', mode.world
                assert_equal false, mode.world_readable
                assert_equal false, mode.world_writable
                assert_equal false, mode.world_executable
                assert_equal '0', mode.group
                assert_equal false, mode.group_readable
                assert_equal false, mode.group_writable
                assert_equal false, mode.group_executable
                assert_equal '0', mode.owner
                assert_equal false, mode.owner_readable
                assert_equal false, mode.owner_writable
                assert_equal false, mode.owner_executable
                assert_equal false, mode.setuid
                assert_equal false, mode.setgid
                assert_equal false, mode.sticky

                mode = app_spec.permissions[2].mode
                assert_equal '777', mode.mode
                assert_equal '7', mode.world
                assert_equal true, mode.world_readable
                assert_equal true, mode.world_writable
                assert_equal true, mode.world_executable
                assert_equal '7', mode.group
                assert_equal true, mode.group_readable
                assert_equal true, mode.group_writable
                assert_equal true, mode.group_executable
                assert_equal '7', mode.owner
                assert_equal true, mode.owner_readable
                assert_equal true, mode.owner_writable
                assert_equal true, mode.owner_executable
                assert_equal false, mode.setuid
                assert_equal false, mode.setgid
                assert_equal false, mode.sticky
              end
            end

            context "When acl is present" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls: []
                END
                app_spec = make_app_spec()
                @acl = app_spec.permissions[0].acls
              end

              should "be able to add and clear additional aces" do
                @acl.add_ace("d:henry:4")
                @acl.clear_additional
              end

              should "be able to get an empty acl" do
                assert_equal [], @acl.get_acl
              end

              should "be able to get added aces in the acl" do
                @acl.add_ace("d:henry:4")
                assert_equal 1, @acl.get_acl.length
                @acl.clear_additional
              end

              should "not be able to get a default ace" do
                assert_equal nil, @acl.get_default_ace
              end

              should "be able to get an added default ace" do
                @acl.add_ace("d:henry:4")
                assert_not_nil @acl.get_default_ace
                @acl.clear_additional
              end

              should "not be able to get a default group ace" do
                assert_equal nil, @acl.get_default_group_ace
              end

              should "be able to get an added default group ace" do
                @acl.add_ace("d:g::4")
                assert_not_nil @acl.get_default_group_ace
                @acl.clear_additional
              end

              should "not have a base named ace" do
                assert !@acl.has_base_named?
              end

              should "have a base named ace when added" do
                @acl.add_ace("bob:4")
                assert @acl.has_base_named?
                @acl.clear_additional
              end

              should "not have a base mask ace" do
                assert !@acl.has_base_mask?
              end

              should "have a base mask ace when added" do
                @acl.add_ace("m:4")
                assert @acl.has_base_mask?
                @acl.clear_additional
              end

              should "not have a default ace" do
                assert !@acl.has_default?
              end

              should "have a default ace when added" do
                @acl.add_ace("d:bob:4")
                assert @acl.has_default?
                @acl.clear_additional
              end

              should "not have a default user ace" do
                assert !@acl.has_default_user?
              end

              should "have a default user ace when added" do
                @acl.add_ace("d::4")
                assert @acl.has_default_user?
                @acl.clear_additional
              end

              should "not have a default group ace" do
                assert !@acl.has_default_group?
              end

              should "have a default group ace when added" do
                @acl.add_ace("d:g::4")
                assert @acl.has_default_group?
                @acl.clear_additional
              end

              should "not have a default other ace" do
                assert !@acl.has_default_other?
              end

              should "have a default other ace when added" do
                @acl.add_ace("d:o:4")
                assert @acl.has_default_other?
                @acl.clear_additional
              end

              should "not have a default named ace" do
                assert !@acl.has_default_named?
              end

              should "have a default named ace when added" do
                @acl.add_ace("d:bob:4")
                assert @acl.has_default_named?
                @acl.clear_additional
              end

              should "not have a default mask ace" do
                assert !@acl.has_default_mask?
              end

              should "have a default mask ace when added" do
                @acl.add_ace("d:m:4")
                assert @acl.has_default_mask?
                @acl.clear_additional
              end
            end

            context "When acl is present with existing aces" do
              setup do
                @app_spec_string = <<-END
              version: 0.0
              os: linux
              permissions:
                - object: 'test/'
                  acls:
                    - 'bob:6'
                    - 'm:6'
                    - 'd:bob:0'
                    - 'd::3'
                    - 'd:g::4'
                    - 'd:o:3'
                    - 'd:m:7'
                END
                app_spec = make_app_spec()
                @acl = app_spec.permissions[0].acls
              end

              should "be able to get the acl" do
                assert_equal 7, @acl.get_acl.length
              end

              should "be able to get default ace" do
                assert_not_nil @acl.get_default_ace
              end

              should "be able to get default group ace" do
                assert_not_nil @acl.get_default_group_ace
              end

              should "have base named ace" do
                assert_not_nil @acl.has_base_named?
              end

              should "have base mask ace" do
                assert_not_nil @acl.has_base_mask?
              end

              should "have default ace" do
                assert_not_nil @acl.has_default?
              end

              should "have default user ace" do
                assert_not_nil @acl.has_default_user?
              end

              should "have default group ace" do
                assert_not_nil @acl.has_default_group?
              end

              should "have default other ace" do
                assert_not_nil @acl.has_default_other?
              end

              should "have default named ace" do
                assert_not_nil @acl.has_default_named?
              end

              should "have default mask ace" do
                assert_not_nil @acl.has_default_mask?
              end
            end
          end

          context "With a ContextInfo" do
            should "with a simple range" do
              info = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::ContextInfo.new({"type"=>"type","range"=>"s3"})
              assert_equal "s3", info.range.get_range
            end

            should "with a complex range" do
              info = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::ContextInfo.new({"type"=>"type","range"=>"s3-s7:c5,c8.c10,c17"})
              assert_equal "s3-s7:c5,c8.c10,c17", info.range.get_range
            end
          end

          context "With a ACEInfo" do
            should "not raise if made internal with base entries" do
              assert_nothing_raised do
                InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("u::7", true)
                InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("g::7", true)
                InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("o::7", true)
              end
            end

            should "raise when not internal and has base user" do
              assert_raised_with_message("The deployment failed because of a problem with the acls permission settings in the application specification file. Use mode to set the base acl entry (u::7). Update the permissions section of the AppSpec file, and then try again.",AppSpecValidationException) do
                InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("u::7")
              end
            end

            should "raise when not internal and has base group" do
              assert_raised_with_message("The deployment failed because of a problem with the acls permission settings in the application specification file. Use mode to set the base acl entry (g::7). Update the permissions section of the AppSpec file, and then try again.",AppSpecValidationException) do
                InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("g::7")
              end
            end

            should "raise when not internal and has base other" do
              assert_raised_with_message("The deployment failed because of a problem with the acls permission settings in the application specification file. Use mode to set the base acl entry (o:7). Update the permissions section of the AppSpec file, and then try again.",AppSpecValidationException) do
                InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("o:7")
              end
            end

            should "be able to get the ace" do
              assert_equal("default:user:bob:rwx", InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("d:u:bob:7").get_ace)
              assert_equal("mask::---", InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("m:0").get_ace)
            end
          end
        end
      end
    end
  end
end
