// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not // use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, // either express or implied. See the License for the specific language governing // permissions and limitations under the License. // Package allowedvalueparamvalidator is responsible for validating parameter value // with the allowed values given in the document. package allowedvalueparamvalidator import ( "fmt" "math" "testing" "github.com/aws/amazon-ssm-agent/agent/contracts" "github.com/aws/amazon-ssm-agent/agent/framework/docparser/paramvalidator/utils" "github.com/aws/amazon-ssm-agent/agent/log" logmocks "github.com/aws/amazon-ssm-agent/agent/mocks/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) // allowedValueParameterTestSuite executes the test suite // containing tests related to allowed values param validators type allowedValueParameterTestSuite struct { suite.Suite allowedValValidator *allowedValueParamValidator testCaseList []*testCases log log.T } type testStatus string const ( success testStatus = "Success" failure testStatus = "Failure" ) type testCases struct { allowedVal []string testInput []interface{} testOutput testStatus name string parameterType utils.DocumentParamType defaultVal interface{} errorStringContains string } // TestAllowedValueParameterTestSuite executes the test suite containing tests related to allowed values param validators func TestAllowedValueParameterTestSuite(t *testing.T) { allowedValValueParamSuite := new(allowedValueParameterTestSuite) allowedValValueParamSuite.allowedValValidator = GetAllowedValueParamValidator() suite.Run(t, allowedValValueParamSuite) } func (suite *allowedValueParameterTestSuite) SetupTest() { suite.log = logmocks.NewMockLog() suite.testCaseList = make([]*testCases, 0) validAllowedValues := &testCases{ allowedVal: []string{"testValue1", "testValue2", "testValue3"}, testInput: []interface{}{ "testValue1", []string{"testValue1", "testValue2"}, []interface{}{"testValue1", "testValue2"}}, name: "TestValidAllowedValues", testOutput: success} suite.testCaseList = append(suite.testCaseList, validAllowedValues) mapListInAllowedList := &testCases{ allowedVal: []string{"{\"path\":\"test1\"}"}, testInput: []interface{}{ map[string]interface{}{"path": "test1"}, "{\"path\":\"test1\"}", []interface{}{map[string]interface{}{"path": "test1"}, map[string]interface{}{"path": "test1"}}, }, name: "TestMapListInAllowedList", errorStringContains: "parameter value /testValue1/ is not in the allowed list [testValue2 testValue3]", testOutput: success} suite.testCaseList = append(suite.testCaseList, mapListInAllowedList) mapListNotInAllowedList := &testCases{ allowedVal: []string{"{\"path\":\"test2\"}"}, testInput: []interface{}{ map[string]interface{}{"path": "test1"}, "{\"path\":\"test1\"}", []interface{}{map[string]interface{}{"path": "test1"}, map[string]interface{}{"path": "test1"}}, }, name: "TestMapListNotInAllowedList", errorStringContains: "is not in the allowed list", testOutput: failure} suite.testCaseList = append(suite.testCaseList, mapListNotInAllowedList) missingAllowedValues := &testCases{ allowedVal: []string{"testValue2", "testValue3"}, testInput: []interface{}{ "testValue1", []string{"testValue1", "testValue2"}, []interface{}{"testValue1", "testValue2"}}, name: "TestMissingAllowedValues", errorStringContains: "parameter value /testValue1/ is not in the allowed list [testValue2 testValue3]", testOutput: failure} suite.testCaseList = append(suite.testCaseList, missingAllowedValues) integerAllowedValues := &testCases{ allowedVal: []string{"3", "4"}, defaultVal: "1", testInput: []interface{}{float64(1)}, name: "TestIntegerAllowedValues", parameterType: utils.ParamTypeInteger, testOutput: success} suite.testCaseList = append(suite.testCaseList, integerAllowedValues) integerOutOfBoundaryLimit := &testCases{ allowedVal: []string{"1", "2"}, defaultVal: "2", testInput: []interface{}{float64(math.MaxInt32 + 1)}, name: "TestIntegerAllowedValues", parameterType: utils.ParamTypeInteger, errorStringContains: "out-of-boundary error", testOutput: failure} suite.testCaseList = append(suite.testCaseList, integerOutOfBoundaryLimit) integerNotAllowedValues := &testCases{ allowedVal: []string{"1", "2"}, defaultVal: "3", testInput: []interface{}{float64(1)}, name: "TestIntegerNotAllowedValues", parameterType: utils.ParamTypeInteger, testOutput: failure, errorStringContains: "parameter value /1/ not equal to default /3/ for Integer", } suite.testCaseList = append(suite.testCaseList, integerNotAllowedValues) booleanAllowedValues := &testCases{ allowedVal: []string{"false"}, defaultVal: false, testInput: []interface{}{false}, name: "TestBoolAllowedValues", parameterType: utils.ParamTypeBoolean, testOutput: success} suite.testCaseList = append(suite.testCaseList, booleanAllowedValues) booleanNotAllowedValues := &testCases{ allowedVal: []string{"false"}, defaultVal: "false", testInput: []interface{}{true}, name: "TestBoolNotAllowedValues", parameterType: utils.ParamTypeBoolean, testOutput: failure, errorStringContains: "parameter value /true/ not equal to default for Boolean", } suite.testCaseList = append(suite.testCaseList, booleanNotAllowedValues) } func (suite *allowedValueParameterTestSuite) TestValidate_MultipleTestCases() { log := logmocks.NewMockLog() for _, testCase := range suite.testCaseList { for _, input := range testCase.testInput { paramValue := input parameterConfig := contracts.Parameter{ AllowedVal: testCase.allowedVal, DefaultVal: testCase.defaultVal, ParamType: string(testCase.parameterType), } err := suite.allowedValValidator.Validate(log, paramValue, ¶meterConfig) if testCase.testOutput == success { assert.Nil(suite.T(), err, fmt.Sprintf("Testcase details: parameter %v, paramValue %v, testCaseName: %v", parameterConfig, paramValue, testCase.name)) } else { assert.Contains(suite.T(), err.Error(), testCase.errorStringContains) assert.NotNil(suite.T(), err, fmt.Sprintf("Testcase details: parameter %v, paramValue %v, testCaseName: %v", parameterConfig, paramValue, testCase.name)) } } } } func (suite *allowedValueParameterTestSuite) TestCheckValuesAllowed_Success_ValidInputs() { err := suite.allowedValValidator.isValueInAllowedList("test1", []string{"test1", "test2"}) assert.Nil(suite.T(), err) err = suite.allowedValValidator.isValueInAllowedList("test2", []string{"test2"}) assert.Nil(suite.T(), err) } func (suite *allowedValueParameterTestSuite) TestCheckValuesInAllowed_Success_ValidInputs() { err := suite.allowedValValidator.isValueInAllowedList("test3", []string{"test1", "test2"}) assert.NotNil(suite.T(), err) assert.Contains(suite.T(), err.Error(), "is not in the allowed list") err = suite.allowedValValidator.isValueInAllowedList("test2", []string{"test3"}) assert.Contains(suite.T(), err.Error(), "is not in the allowed list") assert.NotNil(suite.T(), err) } func (suite *allowedValueParameterTestSuite) TestVerifyAllowedValuesAfterMarshall_Success_ValidInputs() { log := logmocks.NewMockLog() input := make(map[string]map[string]struct{}) input["test"] = make(map[string]struct{}) err := suite.allowedValValidator.verifyAllowedValuesAfterMarshall(log, input, []string{"{\"test\":{}}"}) assert.Nil(suite.T(), err) } func (suite *allowedValueParameterTestSuite) TestVerifyAllowedValuesAfterMarshall_Failure_InValidInputs() { log := logmocks.NewMockLog() input := make(map[string]map[string]struct{}) input["test1"] = make(map[string]struct{}) err := suite.allowedValValidator.verifyAllowedValuesAfterMarshall(log, input, []string{"{\"testInvalid\":{}}"}) assert.NotNil(suite.T(), err) }