github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/cmd/bancheck/config/config_test.go (about) 1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package config 15 16 import ( 17 "path/filepath" 18 "testing" 19 20 "github.com/google/go-cmp/cmp" 21 "github.com/google/go-cmp/cmp/cmpopts" 22 "golang.org/x/tools/go/analysis/analysistest" 23 ) 24 25 func TestReadConfigs(t *testing.T) { 26 tests := []struct { 27 desc string 28 files map[string]string 29 want *Config 30 }{ 31 { 32 desc: "file with empty definitions", 33 files: map[string]string{ 34 "file.json": ` 35 {} 36 `, 37 }, 38 want: &Config{}, 39 }, 40 { 41 desc: "file with unknown field", 42 files: map[string]string{ 43 "file.json": ` 44 { 45 "unknown": 1 46 } 47 `, 48 }, 49 want: &Config{}, 50 }, 51 { 52 desc: "file with banned import", 53 files: map[string]string{ 54 "file.json": ` 55 { 56 "imports": [{ 57 "name": "legacyconversions", 58 "msg": "Sample message", 59 "exemptions": [{ 60 "justification": "My justification", 61 "allowedPkg": "subdirs/vetted/..." 62 }] 63 }] 64 } 65 `, 66 }, 67 want: &Config{Imports: []BannedAPI{ 68 { 69 Name: "legacyconversions", 70 Msg: "Sample message", 71 Exemptions: []Exemption{ 72 { 73 Justification: "My justification", 74 AllowedPkg: "subdirs/vetted/...", 75 }, 76 }, 77 }}, 78 }, 79 }, 80 { 81 desc: "multiple files with imports", 82 files: map[string]string{ 83 "file1.json": ` 84 { 85 "imports": [{ 86 "name": "import1", 87 "msg": "msg1" 88 }] 89 } 90 `, 91 "file2.json": ` 92 { 93 "imports": [{ 94 "name": "import2", 95 "msg": "msg2" 96 }] 97 } 98 `, 99 }, 100 want: &Config{Imports: []BannedAPI{ 101 {Name: "import1", Msg: "msg1"}, {Name: "import2", Msg: "msg2"}, 102 }}, 103 }, 104 { 105 desc: "file with banned function", 106 files: map[string]string{ 107 "file.json": ` 108 { 109 "functions": [{ 110 "name": "safehttp.NewServeMuxConfig", 111 "msg": "Sample message", 112 "exemptions": [{ 113 "justification": "My justification", 114 "allowedPkg": "subdirs/vetted/..." 115 }] 116 }] 117 } 118 `, 119 }, 120 want: &Config{Functions: []BannedAPI{{ 121 Name: "safehttp.NewServeMuxConfig", 122 Msg: "Sample message", 123 Exemptions: []Exemption{ 124 { 125 Justification: "My justification", 126 AllowedPkg: "subdirs/vetted/...", 127 }, 128 }, 129 }}}, 130 }, 131 { 132 desc: "file with banned imports and functions", 133 files: map[string]string{ 134 "file.json": ` 135 { 136 "imports": [{ 137 "name": "legacyconversions", 138 "msg": "Sample message", 139 "exemptions": [{ 140 "justification": "My justification", 141 "allowedPkg": "subdirs/vetted/..." 142 }] 143 }], 144 "functions": [{ 145 "name": "safehttp.NewServeMuxConfig", 146 "msg": "Sample message", 147 "exemptions": [{ 148 "justification": "My justification", 149 "allowedPkg": "subdirs/vetted/..." 150 }] 151 }] 152 } 153 `, 154 }, 155 want: &Config{ 156 Imports: []BannedAPI{ 157 { 158 Name: "legacyconversions", 159 Msg: "Sample message", 160 Exemptions: []Exemption{ 161 { 162 Justification: "My justification", 163 AllowedPkg: "subdirs/vetted/...", 164 }, 165 }, 166 }}, 167 Functions: []BannedAPI{{ 168 Name: "safehttp.NewServeMuxConfig", 169 Msg: "Sample message", 170 Exemptions: []Exemption{ 171 { 172 Justification: "My justification", 173 AllowedPkg: "subdirs/vetted/...", 174 }, 175 }, 176 }}, 177 }, 178 }, 179 { 180 desc: "multiple files with functions", 181 files: map[string]string{ 182 "file1.json": ` 183 { 184 "functions": [{ 185 "name": "function1", 186 "msg": "msg1" 187 }] 188 } 189 `, 190 "file2.json": ` 191 { 192 "functions": [{ 193 "name": "function2", 194 "msg": "msg2" 195 }] 196 } 197 `, 198 }, 199 want: &Config{Functions: []BannedAPI{ 200 {Name: "function1", Msg: "msg1"}, {Name: "function2", Msg: "msg2"}, 201 }}, 202 }, 203 { 204 desc: "duplicate definitions", 205 files: map[string]string{ 206 "file1.json": ` 207 { 208 "functions": [{ 209 "name": "function", 210 "msg": "Banned by team x", 211 "exemptions": [{ 212 "justification": "My justification", 213 "allowedPkg": "subdirs/vetted/..." 214 }] 215 }] 216 } 217 `, 218 "file2.json": ` 219 { 220 "functions": [{ 221 "name": "function", 222 "msg": "Banned by team y", 223 "exemptions": [{ 224 "justification": "#yolo", 225 "allowedPkg": "otherdir/legacy/..." 226 }] 227 }] 228 } 229 `, 230 }, 231 want: &Config{ 232 Functions: []BannedAPI{ 233 { 234 Name: "function", 235 Msg: "Banned by team x", 236 Exemptions: []Exemption{ 237 { 238 Justification: "My justification", 239 AllowedPkg: "subdirs/vetted/...", 240 }, 241 }, 242 }, 243 { 244 Name: "function", 245 Msg: "Banned by team y", 246 Exemptions: []Exemption{ 247 { 248 Justification: "#yolo", 249 AllowedPkg: "otherdir/legacy/...", 250 }, 251 }, 252 }, 253 }, 254 }, 255 }, 256 } 257 258 for _, test := range tests { 259 t.Run(test.desc, func(t *testing.T) { 260 dir, cleanup, err := analysistest.WriteFiles(test.files) 261 if err != nil { 262 t.Fatalf("WriteFiles() returned err: %v", err) 263 } 264 defer cleanup() 265 var files []string 266 for f := range test.files { 267 path := filepath.Join(dir, "src", f) 268 files = append(files, path) 269 } 270 271 cfg, err := ReadConfigs(files) 272 273 if err != nil { 274 t.Errorf("ReadConfigs() got err: %v want: nil", err) 275 } 276 if diff := cmp.Diff(cfg, test.want, cmpopts.SortSlices(BannedAPICmp)); diff != "" { 277 t.Errorf("config mismatch (-want +got):\n%s", diff) 278 } 279 }) 280 } 281 } 282 283 var BannedAPICmp = func(b1, b2 BannedAPI) bool { return b1.Msg < b2.Msg } 284 285 func TestConfigErrors(t *testing.T) { 286 tests := []struct { 287 desc string 288 files map[string]string 289 fileName string 290 }{ 291 { 292 desc: "file does not exist", 293 files: map[string]string{}, 294 fileName: "nonexistent", 295 }, 296 { 297 desc: "file is a directory", 298 files: map[string]string{ 299 "dir/file.json": ``, 300 }, 301 fileName: "dir", 302 }, 303 { 304 desc: "file has invalid contents", 305 files: map[string]string{ 306 "file.json": ` 307 {"imports":"this should be an object"} 308 `, 309 }, 310 fileName: "file.json", 311 }, 312 } 313 314 for _, test := range tests { 315 t.Run(test.desc, func(t *testing.T) { 316 dir, cleanup, err := analysistest.WriteFiles(test.files) 317 if err != nil { 318 t.Fatalf("WriteFiles() got err: %v", err) 319 } 320 defer cleanup() 321 322 file := filepath.Join(dir, "src", test.fileName) 323 cfg, err := ReadConfigs([]string{file}) 324 325 if cfg != nil { 326 t.Errorf("ReadConfigs() got %v, wanted nil", cfg) 327 } 328 if err == nil { 329 t.Errorf("ReadConfigs() got %v, wanted error", cfg) 330 } 331 }) 332 } 333 }