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  }