1
0
mirror of https://github.com/mgechev/revive.git synced 2025-11-23 22:04:49 +02:00
Files
revive/config/config_test.go
2025-11-06 04:05:40 -08:00

411 lines
13 KiB
Go

package config
import (
"path/filepath"
"strings"
"testing"
goversion "github.com/hashicorp/go-version"
"github.com/mgechev/revive/lint"
"github.com/mgechev/revive/rule"
)
func TestGetConfig(t *testing.T) {
t.Run("ok", func(t *testing.T) {
for name, tc := range map[string]struct {
confPath string
wantConfig lint.Config
}{
"default config": {
wantConfig: func() lint.Config {
c := defaultConfig()
normalizeConfig(c)
return *c
}(),
},
"non-reg issue #470": {
confPath: "issue-470.toml",
wantConfig: lint.Config{
Confidence: 0.8,
Severity: lint.SeverityWarning,
Rules: lint.RulesConfig{
"add-constant": {
Severity: lint.SeverityWarning,
Arguments: lint.Arguments{
map[string]any{
"maxLitCount": "3",
"allowStrs": `"`,
"allowFloats": "0.0,1.0,1.,2.0,2.",
"allowInts": "0,1,2",
},
},
},
},
},
},
"config from file issue #585": {
confPath: "issue-585.toml",
wantConfig: lint.Config{
Confidence: 0.0,
Severity: lint.SeverityWarning,
},
},
"config from file default confidence issue #585": {
confPath: "issue-585-defaultConfidence.toml",
wantConfig: lint.Config{
Confidence: 0.8,
Severity: lint.SeverityWarning,
},
},
"config from file goVersion": {
confPath: "goVersion.toml",
wantConfig: lint.Config{
Confidence: 0.8,
GoVersion: goversion.Must(goversion.NewSemver("1.20.0")),
},
},
"config from file ignoreGeneratedHeader": {
confPath: "ignoreGeneratedHeader.toml",
wantConfig: lint.Config{
Confidence: 0.8,
IgnoreGeneratedHeader: true,
},
},
"config from file enableDefault": {
confPath: "enableDefault.toml",
wantConfig: lint.Config{
Confidence: 0.8,
IgnoreGeneratedHeader: false,
EnableDefaultRules: true,
Rules: lint.RulesConfig{
"blank-imports": {},
"context-as-argument": {},
"context-keys-type": {},
"dot-imports": {},
"empty-block": {},
"error-naming": {},
"error-return": {},
"error-strings": {},
"errorf": {},
"exported": {},
"increment-decrement": {},
"indent-error-flow": {},
"package-comments": {},
"range": {},
"receiver-naming": {},
"redefines-builtin-id": {},
"superfluous-else": {},
"time-naming": {},
"unexported-return": {},
"unreachable-code": {},
"unused-parameter": {},
"var-declaration": {},
"var-naming": {},
},
},
},
} {
t.Run(name, func(t *testing.T) {
var cfgPath string
if tc.confPath != "" {
cfgPath = filepath.Join("testdata", tc.confPath)
}
cfg, err := GetConfig(cfgPath)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if cfg.IgnoreGeneratedHeader != tc.wantConfig.IgnoreGeneratedHeader {
t.Errorf("IgnoreGeneratedHeader: expected %v, got %v", tc.wantConfig.IgnoreGeneratedHeader, cfg.IgnoreGeneratedHeader)
}
if cfg.Confidence != tc.wantConfig.Confidence {
t.Errorf("Confidence: expected %v, got %v", tc.wantConfig.Confidence, cfg.Confidence)
}
if cfg.Severity != tc.wantConfig.Severity {
t.Errorf("Severity: expected %v, got %v", tc.wantConfig.Severity, cfg.Severity)
}
if cfg.EnableAllRules != tc.wantConfig.EnableAllRules {
t.Errorf("EnableAllRules: expected %v, got %v", tc.wantConfig.EnableAllRules, cfg.EnableAllRules)
}
if cfg.EnableDefaultRules != tc.wantConfig.EnableDefaultRules {
t.Errorf("EnableDefaultRules: expected %v, got %v", tc.wantConfig.EnableDefaultRules, cfg.EnableDefaultRules)
}
if cfg.ErrorCode != tc.wantConfig.ErrorCode {
t.Errorf("ErrorCode: expected %v, got %v", tc.wantConfig.ErrorCode, cfg.ErrorCode)
}
if cfg.WarningCode != tc.wantConfig.WarningCode {
t.Errorf("WarningCode: expected %v, got %v", tc.wantConfig.WarningCode, cfg.WarningCode)
}
if !tc.wantConfig.GoVersion.Equal(cfg.GoVersion) {
t.Errorf("GoVersion: expected %v, got %v", tc.wantConfig.GoVersion, cfg.GoVersion)
}
if len(cfg.Exclude) != len(tc.wantConfig.Exclude) {
t.Errorf("Exclude length: expected %v, got %v", len(tc.wantConfig.Exclude), len(cfg.Exclude))
} else {
for i, exclude := range tc.wantConfig.Exclude {
if cfg.Exclude[i] != exclude {
t.Errorf("Exclude[%d]: expected %v, got %v", i, exclude, cfg.Exclude[i])
}
}
}
if len(cfg.Rules) != len(tc.wantConfig.Rules) {
t.Errorf("Rules count: expected %v, got %v", len(tc.wantConfig.Rules), len(cfg.Rules))
}
for ruleName, wantRule := range tc.wantConfig.Rules {
gotRule, exists := cfg.Rules[ruleName]
if !exists {
t.Errorf("Rule %q: expected to exist, but not found", ruleName)
continue
}
if gotRule.Disabled != wantRule.Disabled {
t.Errorf("Rule %q Disabled: expected %v, got %v", ruleName, wantRule.Disabled, gotRule.Disabled)
}
if gotRule.Severity != wantRule.Severity {
t.Errorf("Rule %q Severity: expected %v, got %v", ruleName, wantRule.Severity, gotRule.Severity)
}
if len(gotRule.Arguments) != len(wantRule.Arguments) {
t.Errorf("Rule %q Arguments length: expected %v, got %v", ruleName, len(wantRule.Arguments), len(gotRule.Arguments))
}
if len(gotRule.Exclude) != len(wantRule.Exclude) {
t.Errorf("Rule %q Exclude length: expected %v, got %v", ruleName, len(wantRule.Exclude), len(gotRule.Exclude))
} else {
for i, wantExclude := range wantRule.Exclude {
if gotRule.Exclude[i] != wantExclude {
t.Errorf("Rule %q Exclude[%d]: expected %v, got %v", ruleName, i, wantExclude, gotRule.Exclude[i])
}
}
}
}
// Check for unexpected rules in actual config
for ruleName := range cfg.Rules {
if _, exists := tc.wantConfig.Rules[ruleName]; !exists {
t.Errorf("Rule %q: found in actual config but not expected", ruleName)
}
}
if len(cfg.Directives) != len(tc.wantConfig.Directives) {
t.Errorf("Directives count: expected %v, got %v", len(tc.wantConfig.Directives), len(cfg.Directives))
}
for directiveName, wantDirective := range tc.wantConfig.Directives {
gotDirective, exists := cfg.Directives[directiveName]
if !exists {
t.Errorf("Directive %q: expected to exist, but not found", directiveName)
continue
}
if gotDirective.Severity != wantDirective.Severity {
t.Errorf("Directive %q Severity: expected %v, got %v", directiveName, wantDirective.Severity, gotDirective.Severity)
}
}
// Check for unexpected directives in actual config
for directiveName := range cfg.Directives {
if _, exists := tc.wantConfig.Directives[directiveName]; !exists {
t.Errorf("Directive %q: found in actual config but not expected", directiveName)
}
}
})
}
t.Run("rule-level file filter excludes", func(t *testing.T) {
cfg, err := GetConfig("testdata/rule-level-exclude-850.toml")
if err != nil {
t.Fatal("should be valid config")
}
r1 := cfg.Rules["r1"]
if len(r1.Exclude) > 0 {
t.Fatal("r1 should have empty excludes")
}
r2 := cfg.Rules["r2"]
if len(r2.Exclude) != 1 {
t.Fatal("r2 should have exclude set")
}
if !r2.MustExclude("some/file.go") {
t.Fatal("r2 should be initialized and exclude some/file.go")
}
if r2.MustExclude("some/any-other.go") {
t.Fatal("r2 should not exclude some/any-other.go")
}
})
})
t.Run("failure", func(t *testing.T) {
for name, tc := range map[string]struct {
confPath string
wantError string
}{
"unknown file": {
confPath: "unknown",
wantError: "cannot read the config file",
},
"malformed file": {
confPath: "malformed.toml",
wantError: "cannot parse the config file",
},
} {
t.Run(name, func(t *testing.T) {
_, err := GetConfig(filepath.Join("testdata", tc.confPath))
if err != nil && !strings.Contains(err.Error(), tc.wantError) {
t.Errorf("Unexpected error: want %q, got: %q", tc.wantError, err)
}
})
}
})
}
func TestGetLintingRules(t *testing.T) {
tt := map[string]struct {
confPath string
wantRulesCount int
wantErr string
}{
"no rules": {
confPath: "noRules.toml",
wantRulesCount: 0,
},
"enableAllRules without disabled rules": {
confPath: "enableAll.toml",
wantRulesCount: len(allRules),
},
"enableAllRules with 2 disabled rules": {
confPath: "enableAllBut2.toml",
wantRulesCount: len(allRules) - 2,
},
"enableDefaultRules without disabled rules": {
confPath: "enableDefault.toml",
wantRulesCount: len(defaultRules),
},
"enableDefaultRules with 2 disabled rules": {
confPath: "enableDefaultBut2.toml",
wantRulesCount: len(defaultRules) - 2,
},
"enableDefaultRules plus 1 non-default rule": {
confPath: "enableDefaultPlus1.toml",
wantRulesCount: len(defaultRules) + 1,
},
"enableAllRules and enableDefaultRules both set": {
confPath: "enableAllAndDefault.toml",
wantRulesCount: len(allRules),
},
"enableDefaultRules plus rule already in defaults": {
confPath: "enableDefaultPlusDefaultRule.toml",
wantRulesCount: len(defaultRules),
},
"enableAllRules plus rule already in all": {
confPath: "enableAllWithRule.toml",
wantRulesCount: len(allRules),
},
"enable 2 rules": {
confPath: "enable2.toml",
wantRulesCount: 2,
},
"var-naming configure error": {
confPath: "varNamingConfigureError.toml",
wantErr: `cannot configure rule: "var-naming": invalid argument to the var-naming rule. Expecting a allowlist of type slice with initialisms, got string`,
},
}
for name, tc := range tt {
t.Run(name, func(t *testing.T) {
cfg, err := GetConfig(filepath.Join("testdata", tc.confPath))
if err != nil {
t.Fatalf("Unexpected error while loading conf: %v", err)
}
rules, err := GetLintingRules(cfg, []lint.Rule{})
if tc.wantErr != "" {
if err == nil || err.Error() != tc.wantErr {
t.Fatalf("Expected error %q, got %q", tc.wantErr, err)
}
return
}
switch {
case err != nil:
t.Fatalf("Unexpected error\n\t%v", err)
case len(rules) != tc.wantRulesCount:
t.Fatalf("Expected %v enabled linting rules got: %v", tc.wantRulesCount, len(rules))
}
})
}
}
func TestGetGlobalSeverity(t *testing.T) {
tt := map[string]struct {
confPath string
wantGlobalSeverity string
particularRule lint.Rule
wantParticularSeverity string
}{
"enable 2 rules with one specific severity": {
confPath: "testdata/enable2OneSpecificSeverity.toml",
wantGlobalSeverity: "warning",
particularRule: &rule.CyclomaticRule{},
wantParticularSeverity: "error",
},
"enableAllRules with one specific severity": {
confPath: "testdata/enableAllOneSpecificSeverity.toml",
wantGlobalSeverity: "error",
particularRule: &rule.DeepExitRule{},
wantParticularSeverity: "warning",
},
}
for name, tc := range tt {
t.Run(name, func(t *testing.T) {
cfg, err := GetConfig(tc.confPath)
if err != nil {
t.Fatalf("Unexpected error while loading conf: %v", err)
}
rules, err := GetLintingRules(cfg, []lint.Rule{})
if err != nil {
t.Fatalf("Unexpected error while loading conf: %v", err)
}
for _, r := range rules {
ruleName := r.Name()
ruleCfg := cfg.Rules[ruleName]
ruleSeverity := string(ruleCfg.Severity)
switch ruleName {
case tc.particularRule.Name():
if tc.wantParticularSeverity != ruleSeverity {
t.Fatalf("Expected Severity %v for rule %v, got %v", tc.wantParticularSeverity, ruleName, ruleSeverity)
}
default:
if tc.wantGlobalSeverity != ruleSeverity {
t.Fatalf("Expected Severity %v for rule %v, got %v", tc.wantGlobalSeverity, ruleName, ruleSeverity)
}
}
}
})
}
}
func TestGetFormatter(t *testing.T) {
t.Run("default formatter", func(t *testing.T) {
formatter, err := GetFormatter("")
if err != nil {
t.Fatalf("Unexpected error %q", err)
}
if formatter == nil || formatter.Name() != "default" {
t.Errorf("Expected formatter %q, got %v", "default", formatter)
}
})
t.Run("unknown formatter", func(t *testing.T) {
_, err := GetFormatter("unknown")
if err == nil || err.Error() != "unknown formatter unknown" {
t.Errorf("Expected error %q, got: %q", "unknown formatter unknown", err)
}
})
t.Run("checkstyle formatter", func(t *testing.T) {
formatter, err := GetFormatter("checkstyle")
if err != nil {
t.Fatalf("Unexpected error: %q", err)
}
if formatter == nil || formatter.Name() != "checkstyle" {
t.Errorf("Expected formatter %q, got %v", "checkstyle", formatter)
}
})
}