github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/lint/rules/values_test.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package rules 18 19 import ( 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 27 "github.com/stefanmcshane/helm/internal/test/ensure" 28 ) 29 30 var nonExistingValuesFilePath = filepath.Join("/fake/dir", "values.yaml") 31 32 const testSchema = ` 33 { 34 "$schema": "http://json-schema.org/draft-07/schema#", 35 "title": "helm values test schema", 36 "type": "object", 37 "additionalProperties": false, 38 "required": [ 39 "username", 40 "password" 41 ], 42 "properties": { 43 "username": { 44 "description": "Your username", 45 "type": "string" 46 }, 47 "password": { 48 "description": "Your password", 49 "type": "string" 50 } 51 } 52 } 53 ` 54 55 func TestValidateValuesYamlNotDirectory(t *testing.T) { 56 _ = os.Mkdir(nonExistingValuesFilePath, os.ModePerm) 57 defer os.Remove(nonExistingValuesFilePath) 58 59 err := validateValuesFileExistence(nonExistingValuesFilePath) 60 if err == nil { 61 t.Errorf("validateValuesFileExistence to return a linter error, got no error") 62 } 63 } 64 65 func TestValidateValuesFileWellFormed(t *testing.T) { 66 badYaml := ` 67 not:well[]{}formed 68 ` 69 tmpdir := ensure.TempFile(t, "values.yaml", []byte(badYaml)) 70 defer os.RemoveAll(tmpdir) 71 valfile := filepath.Join(tmpdir, "values.yaml") 72 if err := validateValuesFile(valfile, map[string]interface{}{}); err == nil { 73 t.Fatal("expected values file to fail parsing") 74 } 75 } 76 77 func TestValidateValuesFileSchema(t *testing.T) { 78 yaml := "username: admin\npassword: swordfish" 79 tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml)) 80 defer os.RemoveAll(tmpdir) 81 createTestingSchema(t, tmpdir) 82 83 valfile := filepath.Join(tmpdir, "values.yaml") 84 if err := validateValuesFile(valfile, map[string]interface{}{}); err != nil { 85 t.Fatalf("Failed validation with %s", err) 86 } 87 } 88 89 func TestValidateValuesFileSchemaFailure(t *testing.T) { 90 // 1234 is an int, not a string. This should fail. 91 yaml := "username: 1234\npassword: swordfish" 92 tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml)) 93 defer os.RemoveAll(tmpdir) 94 createTestingSchema(t, tmpdir) 95 96 valfile := filepath.Join(tmpdir, "values.yaml") 97 98 err := validateValuesFile(valfile, map[string]interface{}{}) 99 if err == nil { 100 t.Fatal("expected values file to fail parsing") 101 } 102 103 assert.Contains(t, err.Error(), "Expected: string, given: integer", "integer should be caught by schema") 104 } 105 106 func TestValidateValuesFileSchemaOverrides(t *testing.T) { 107 yaml := "username: admin" 108 overrides := map[string]interface{}{ 109 "password": "swordfish", 110 } 111 tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml)) 112 defer os.RemoveAll(tmpdir) 113 createTestingSchema(t, tmpdir) 114 115 valfile := filepath.Join(tmpdir, "values.yaml") 116 if err := validateValuesFile(valfile, overrides); err != nil { 117 t.Fatalf("Failed validation with %s", err) 118 } 119 } 120 121 func TestValidateValuesFile(t *testing.T) { 122 tests := []struct { 123 name string 124 yaml string 125 overrides map[string]interface{} 126 errorMessage string 127 }{ 128 { 129 name: "value added", 130 yaml: "username: admin", 131 overrides: map[string]interface{}{"password": "swordfish"}, 132 }, 133 { 134 name: "value not overridden", 135 yaml: "username: admin\npassword:", 136 overrides: map[string]interface{}{"username": "anotherUser"}, 137 errorMessage: "Expected: string, given: null", 138 }, 139 { 140 name: "value overridden", 141 yaml: "username: admin\npassword:", 142 overrides: map[string]interface{}{"username": "anotherUser", "password": "swordfish"}, 143 }, 144 } 145 146 for _, tt := range tests { 147 t.Run(tt.name, func(t *testing.T) { 148 tmpdir := ensure.TempFile(t, "values.yaml", []byte(tt.yaml)) 149 defer os.RemoveAll(tmpdir) 150 createTestingSchema(t, tmpdir) 151 152 valfile := filepath.Join(tmpdir, "values.yaml") 153 154 err := validateValuesFile(valfile, tt.overrides) 155 156 switch { 157 case err != nil && tt.errorMessage == "": 158 t.Errorf("Failed validation with %s", err) 159 case err == nil && tt.errorMessage != "": 160 t.Error("expected values file to fail parsing") 161 case err != nil && tt.errorMessage != "": 162 assert.Contains(t, err.Error(), tt.errorMessage, "Failed with unexpected error") 163 } 164 }) 165 } 166 } 167 168 func createTestingSchema(t *testing.T, dir string) string { 169 t.Helper() 170 schemafile := filepath.Join(dir, "values.schema.json") 171 if err := ioutil.WriteFile(schemafile, []byte(testSchema), 0700); err != nil { 172 t.Fatalf("Failed to write schema to tmpdir: %s", err) 173 } 174 return schemafile 175 }