github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/internal/linter/config/externalConfigProvider_test.go (about)

     1  package config_test
     2  
     3  import (
     4  	"os"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/yoheimuta/protolint/internal/setting_test"
     9  
    10  	"github.com/yoheimuta/protolint/internal/linter/config"
    11  )
    12  
    13  func TestGetExternalConfig(t *testing.T) {
    14  	for _, test := range []struct {
    15  		name               string
    16  		inputFilePath      string
    17  		inputDirPath       string
    18  		cwdPath            string
    19  		wantExternalConfig *config.ExternalConfig
    20  		wantExistErr       bool
    21  	}{
    22  		{
    23  			name:         "invalid config file",
    24  			inputDirPath: setting_test.TestDataPath("invalidconfig"),
    25  			wantExistErr: true,
    26  		},
    27  		{
    28  			name: "not found a config file",
    29  		},
    30  		{
    31  			name:         "valid config file",
    32  			inputDirPath: setting_test.TestDataPath("validconfig"),
    33  			wantExternalConfig: &config.ExternalConfig{
    34  				SourcePath: setting_test.TestDataPath("validconfig", "protolint.yaml"),
    35  				Lint: config.Lint{
    36  					Ignores: []config.Ignore{
    37  						{
    38  							ID: "ENUM_FIELD_NAMES_UPPER_SNAKE_CASE",
    39  							Files: []string{
    40  								"path/to/foo.proto",
    41  								"path/to/bar.proto",
    42  							},
    43  						},
    44  						{
    45  							ID: "ENUM_NAMES_UPPER_CAMEL_CASE",
    46  							Files: []string{
    47  								"path/to/foo.proto",
    48  							},
    49  						},
    50  					},
    51  					Rules: struct {
    52  						NoDefault  bool     `yaml:"no_default" json:"no_default" toml:"no_default"`
    53  						AllDefault bool     `yaml:"all_default" json:"all_default" toml:"all_default"`
    54  						Add        []string `yaml:"add" json:"add" toml:"add"`
    55  						Remove     []string `yaml:"remove" json:"remove" toml:"remove"`
    56  					}{
    57  						NoDefault: true,
    58  						Add: []string{
    59  							"FIELD_NAMES_LOWER_SNAKE_CASE",
    60  							"MESSAGE_NAMES_UPPER_CAMEL_CASE",
    61  						},
    62  						Remove: []string{
    63  							"RPC_NAMES_UPPER_CAMEL_CASE",
    64  						},
    65  					},
    66  					RulesOption: config.RulesOption{
    67  						MaxLineLength: config.MaxLineLengthOption{
    68  							MaxChars: 80,
    69  							TabChars: 2,
    70  						},
    71  						Indent: config.IndentOption{
    72  							Style:   "\t",
    73  							Newline: "\n",
    74  						},
    75  					},
    76  				},
    77  			},
    78  		},
    79  		{
    80  			name:         "load .protolint.yaml",
    81  			inputDirPath: setting_test.TestDataPath("validconfig", "hidden"),
    82  			wantExternalConfig: &config.ExternalConfig{
    83  				SourcePath: setting_test.TestDataPath("validconfig", "hidden", ".protolint.yaml"),
    84  				Lint: config.Lint{
    85  					RulesOption: config.RulesOption{
    86  						Indent: config.IndentOption{
    87  							Style:   "\t",
    88  							Newline: "\n",
    89  						},
    90  					},
    91  				},
    92  			},
    93  		},
    94  		{
    95  			name:          "load my_protolint.yaml",
    96  			inputFilePath: setting_test.TestDataPath("validconfig", "particular_name", "my_protolint.yaml"),
    97  			wantExternalConfig: &config.ExternalConfig{
    98  				SourcePath: setting_test.TestDataPath("validconfig", "particular_name", "my_protolint.yaml"),
    99  				Lint: config.Lint{
   100  					RulesOption: config.RulesOption{
   101  						Indent: config.IndentOption{
   102  							Style:   "\t",
   103  							Newline: "\n",
   104  						},
   105  					},
   106  				},
   107  			},
   108  		},
   109  		{
   110  			name:         "load protolint.yml",
   111  			inputDirPath: setting_test.TestDataPath("validconfig", "yml"),
   112  			wantExternalConfig: &config.ExternalConfig{
   113  				SourcePath: setting_test.TestDataPath("validconfig", "yml", "protolint.yml"),
   114  				Lint: config.Lint{
   115  					RulesOption: config.RulesOption{
   116  						Indent: config.IndentOption{
   117  							Style:   "\t",
   118  							Newline: "\n",
   119  						},
   120  					},
   121  				},
   122  			},
   123  		},
   124  		{
   125  			name:         "load .protolint.yml",
   126  			inputDirPath: setting_test.TestDataPath("validconfig", "yml_hidden"),
   127  			wantExternalConfig: &config.ExternalConfig{
   128  				SourcePath: setting_test.TestDataPath("validconfig", "yml_hidden", ".protolint.yml"),
   129  				Lint: config.Lint{
   130  					RulesOption: config.RulesOption{
   131  						Indent: config.IndentOption{
   132  							Style:   "\t",
   133  							Newline: "\n",
   134  						},
   135  					},
   136  				},
   137  			},
   138  		},
   139  		{
   140  			name:    "load .protolint.yml at cwd automatically",
   141  			cwdPath: setting_test.TestDataPath("validconfig", "default"),
   142  			wantExternalConfig: &config.ExternalConfig{
   143  				SourcePath: ".protolint.yml",
   144  				Lint: config.Lint{
   145  					RulesOption: config.RulesOption{
   146  						Indent: config.IndentOption{
   147  							Style:   "\t",
   148  							Newline: "\n",
   149  						},
   150  					},
   151  				},
   152  			},
   153  		},
   154  		{
   155  			name:    "prefer .protolint.yml at cwd to one at its parent dir",
   156  			cwdPath: setting_test.TestDataPath("validconfig", "default", "child"),
   157  			wantExternalConfig: &config.ExternalConfig{
   158  				SourcePath: ".protolint.yaml",
   159  				Lint: config.Lint{
   160  					RulesOption: config.RulesOption{
   161  						Indent: config.IndentOption{
   162  							Style:   "\t",
   163  							Newline: "\n",
   164  						},
   165  					},
   166  				},
   167  			},
   168  		},
   169  		{
   170  			name:    "locate .protolint.yml at the parent when not found at cwd",
   171  			cwdPath: setting_test.TestDataPath("validconfig", "default", "empty_child"),
   172  			wantExternalConfig: &config.ExternalConfig{
   173  				SourcePath: setting_test.TestDataPath("validconfig", "default", ".protolint.yml"),
   174  				Lint: config.Lint{
   175  					RulesOption: config.RulesOption{
   176  						Indent: config.IndentOption{
   177  							Style:   "\t",
   178  							Newline: "\n",
   179  						},
   180  					},
   181  				},
   182  			},
   183  		},
   184  		{
   185  			name:    "locate .protolint.yml at the grand parent when not found at cwd",
   186  			cwdPath: setting_test.TestDataPath("validconfig", "default", "empty_child", "empty_grand_child"),
   187  			wantExternalConfig: &config.ExternalConfig{
   188  				SourcePath: setting_test.TestDataPath("validconfig", "default", ".protolint.yml"),
   189  				Lint: config.Lint{
   190  					RulesOption: config.RulesOption{
   191  						Indent: config.IndentOption{
   192  							Style:   "\t",
   193  							Newline: "\n",
   194  						},
   195  					},
   196  				},
   197  			},
   198  		},
   199  		{
   200  			name:         "not found a config file even so inputDirPath is set",
   201  			inputDirPath: setting_test.TestDataPath("validconfig", "particular_name"),
   202  			wantExistErr: true,
   203  		},
   204  		{
   205  			name:          "not found a config file even so inputFilePath is set",
   206  			inputFilePath: setting_test.TestDataPath("validconfig", "particular_name", "not_found.yaml"),
   207  			wantExistErr:  true,
   208  		},
   209  		{
   210  			name:    "found a package.json with 'protolint' in it",
   211  			cwdPath: setting_test.TestDataPath("js_config", "package"),
   212  			wantExternalConfig: &config.ExternalConfig{
   213  				SourcePath: "package.json",
   214  				Lint: config.Lint{
   215  					RulesOption: config.RulesOption{
   216  						Indent: config.IndentOption{
   217  							Style:   "\t",
   218  							Newline: "\n",
   219  						},
   220  					},
   221  				},
   222  			},
   223  		},
   224  		{
   225  			name:               "found a package.json without 'protolint' in it",
   226  			cwdPath:            setting_test.TestDataPath("js_config", "package_no_protolint"),
   227  			wantExternalConfig: nil,
   228  		},
   229  		{
   230  			name:               "found a pyproject.toml without 'tools.protolint' in it",
   231  			cwdPath:            setting_test.TestDataPath("py_project", "pyproject_no_protolint"),
   232  			wantExternalConfig: nil,
   233  		},
   234  		{
   235  			name:               "found a pyproject.toml without 'tools' in it",
   236  			cwdPath:            setting_test.TestDataPath("py_project", "pyproject_no_tools"),
   237  			wantExternalConfig: nil,
   238  		},
   239  		{
   240  			name:    "found a package.json with 'protolint' and other stuff in it",
   241  			cwdPath: setting_test.TestDataPath("js_config", "non_pure_package"),
   242  			wantExternalConfig: &config.ExternalConfig{
   243  				SourcePath: "package.json",
   244  				Lint: config.Lint{
   245  					RulesOption: config.RulesOption{
   246  						Indent: config.IndentOption{
   247  							Style:   "\t",
   248  							Newline: "\n",
   249  						},
   250  					},
   251  				},
   252  			},
   253  		},
   254  		{
   255  			name:    "found a pyproject.toml with 'tools.protolint' and other stuff in it",
   256  			cwdPath: setting_test.TestDataPath("py_project", "with_pyproject"),
   257  			wantExternalConfig: &config.ExternalConfig{
   258  				SourcePath: "pyproject.toml",
   259  				Lint: config.Lint{
   260  					RulesOption: config.RulesOption{
   261  						Indent: config.IndentOption{
   262  							Style:   "\t",
   263  							Newline: "\n",
   264  						},
   265  					},
   266  				},
   267  			},
   268  		},
   269  		{
   270  			name:    "found a package.json with 'protolint' and other stuff in it, but superseded by sibling protolint.yaml",
   271  			cwdPath: setting_test.TestDataPath("js_config", "package_with_yaml"),
   272  			wantExternalConfig: &config.ExternalConfig{
   273  				SourcePath: "protolint.yaml",
   274  				Lint: config.Lint{
   275  					RulesOption: config.RulesOption{
   276  						Indent: config.IndentOption{
   277  							Style:   "\t",
   278  							Newline: "\n",
   279  						},
   280  					},
   281  				},
   282  			},
   283  		},
   284  		{
   285  			name:    "found a pyproject.toml with 'toolsprotolint' and other stuff in it, but superseded by sibling protolint.yaml",
   286  			cwdPath: setting_test.TestDataPath("py_project", "project_with_yaml"),
   287  			wantExternalConfig: &config.ExternalConfig{
   288  				SourcePath: "protolint.yaml",
   289  				Lint: config.Lint{
   290  					RulesOption: config.RulesOption{
   291  						Indent: config.IndentOption{
   292  							Style:   "\t",
   293  							Newline: "\n",
   294  						},
   295  					},
   296  				},
   297  			},
   298  		},
   299  		{
   300  			name:    "found a package.json with 'protolint' and other stuff in it, but superseded by parent protolint.yaml",
   301  			cwdPath: setting_test.TestDataPath("js_config", "package_with_yaml_parent", "child"),
   302  			wantExternalConfig: &config.ExternalConfig{
   303  				SourcePath: setting_test.TestDataPath("js_config", "package_with_yaml_parent", "protolint.yaml"),
   304  				Lint: config.Lint{
   305  					RulesOption: config.RulesOption{
   306  						Indent: config.IndentOption{
   307  							Style:   "\t",
   308  							Newline: "\n",
   309  						},
   310  					},
   311  				},
   312  			},
   313  		},
   314  		{
   315  			name:    "found a pyproject.toml with 'toolsprotolint' and other stuff in it, but superseded by parent protolint.yaml",
   316  			cwdPath: setting_test.TestDataPath("py_project", "project_with_yaml_parent", "child"),
   317  			wantExternalConfig: &config.ExternalConfig{
   318  				SourcePath: setting_test.TestDataPath("py_project", "project_with_yaml_parent", "protolint.yaml"),
   319  				Lint: config.Lint{
   320  					RulesOption: config.RulesOption{
   321  						Indent: config.IndentOption{
   322  							Style:   "\t",
   323  							Newline: "\n",
   324  						},
   325  					},
   326  				},
   327  			},
   328  		},
   329  	} {
   330  		test := test
   331  		t.Run(test.name, func(t *testing.T) {
   332  			if len(test.cwdPath) != 0 {
   333  				err := os.Chdir(test.cwdPath)
   334  				if err != nil {
   335  					t.Errorf("got err %v", err)
   336  					return
   337  				}
   338  			}
   339  
   340  			got, err := config.GetExternalConfig(test.inputFilePath, test.inputDirPath)
   341  			if test.wantExistErr {
   342  				if err == nil {
   343  					t.Errorf("got err nil, but want err")
   344  				}
   345  				return
   346  			}
   347  			if err != nil {
   348  				t.Errorf("got err %v, but want nil", err)
   349  				return
   350  			}
   351  
   352  			if !reflect.DeepEqual(got, test.wantExternalConfig) {
   353  				t.Errorf("got %v, but want %v", got, test.wantExternalConfig)
   354  			}
   355  		})
   356  	}
   357  }