diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2914942..1e993d8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -142,7 +142,6 @@ Describe a new checker in [checkers section](./README.md#checkers).
- [http-sugar](#http-sugar)
- [require-len](#require-len)
- [suite-test-name](#suite-test-name)
-- [useless-assert](#useless-assert)
---
@@ -367,26 +366,5 @@ func (s *HandlersSuite) Test_UsecaseSuccess()
---
-### useless-assert
-
-Support more complex cases, e.g.
-
-```go
-body, err := io.ReadAll(rr.Body)
-require.NoError(t, err)
-require.NoError(t, err) ❌
-
-expectedJSON, err := json.Marshal(expected)
-require.NoError(t, err)
-require.JSONEq(t, string(expectedJSON), string(body))
-```
-
-```go
-require.NoError(t, err)
-assert.ErrorContains(t, err, "user") ❌
-```
-
----
-
Any other figments of your imagination are welcome 🙏
List of `testify` functions [here](https://pkg.go.dev/github.com/stretchr/testify@master/assert#pkg-functions).
diff --git a/README.md b/README.md
index 1496d40..b1c6380 100644
--- a/README.md
+++ b/README.md
@@ -96,6 +96,7 @@ https://golangci-lint.run/usage/linters/#testifylint
| [compares](#compares) | ✅ | ✅ |
| [contains](#contains) | ✅ | ✅ |
| [empty](#empty) | ✅ | ✅ |
+| [encoded-compare](#encoded-compare) | ✅ | ✅ |
| [error-is-as](#error-is-as) | ✅ | 🤏 |
| [error-nil](#error-nil) | ✅ | ✅ |
| [expected-actual](#expected-actual) | ✅ | ✅ |
@@ -274,6 +275,39 @@ assert.NotEmpty(t, err)
---
+### encoded-compare
+
+```go
+❌
+assert.Equal(t, `{"foo": "bar"}`, body)
+assert.EqualValues(t, `{"foo": "bar"}`, body)
+assert.Exactly(t, `{"foo": "bar"}`, body)
+assert.Equal(t, expectedJSON, resultJSON)
+assert.Equal(t, expBodyConst, w.Body.String())
+assert.Equal(t, fmt.Sprintf(`{"value":"%s"}`, hexString), result)
+assert.Equal(t, "{}", json.RawMessage(resp))
+assert.Equal(t, expJSON, strings.Trim(string(resultJSONBytes), "\n")) // + Replace, ReplaceAll, TrimSpace
+
+assert.Equal(t, expectedYML, conf)
+
+✅
+assert.JSONEq(t, `{"foo": "bar"}`, body)
+assert.YAMLEq(t, expectedYML, conf)
+```
+
+**Autofix**: true.
+**Enabled by default**: true.
+**Reason**: Protection from bugs and more appropriate `testify` API with clearer failure message.
+
+`encoded-compare` detects JSON-style string constants (usable in `fmt.Sprintf` also) and JSON-style/YAML-style named
+variables. If variable is converted to `json.RawMessage`, then it is considered JSON unconditionally.
+
+When fixing, `encoded-compare` removes unnecessary conversions to `[]byte`, `string`, `json.RawMessage` and calls of
+`strings.Replace`, `strings.ReplaceAll`, `strings.Trim`, `strings.TrimSpace`, and adds a conversion to `string` when
+needed.
+
+---
+
### error-is-as
```go
@@ -654,7 +688,7 @@ assert.Equal(t, (chan Event)(nil), eventsChan)
assert.NotEqual(t, (chan Event)(nil), eventsChan)
```
-But in the case of `Equal`, `NotEqual` and `Exactly` type casting approach still doesn't work for the function type.
+But in the case of `Equal`, `NotEqual` and `Exactly` type conversion approach still doesn't work for the function type.
The best option here is to just use `Nil` / `NotNil` (see [details](https://github.com/stretchr/testify/issues/1524)).
@@ -884,22 +918,51 @@ a [checkers.AdvancedChecker](https://github.com/Antonboom/testifylint/blob/67632
### useless-assert
-Currently the checker guards against assertion of the same variable:
+The checker guards against assertion of the same variable:
```go
-❌
+assert.Contains(t, tt.value, tt.value)
+assert.ElementsMatch(t, tt.value, tt.value)
assert.Equal(t, tt.value, tt.value)
-assert.ElementsMatch(t, users, users)
-// And so on...
+assert.EqualExportedValues(t, tt.value, tt.value)
+// And other assert functions...
+
assert.True(t, num > num)
+assert.True(t, num < num)
+assert.True(t, num >= num)
+assert.True(t, num <= num)
+assert.True(t, num == num)
+assert.True(t, num != num)
+
+assert.False(t, num > num)
+assert.False(t, num < num)
+assert.False(t, num >= num)
+assert.False(t, num <= num)
assert.False(t, num == num)
+assert.False(t, num != num)
```
-More complex cases are [open for contribution](CONTRIBUTING.md#useless-assert).
+And against these meaningless assertions:
+```go
+assert.Empty(t, "")
+assert.False(t, false)
+assert.Implements(t, (*any)(nil), new(Conn))
+assert.Negative(t, -42)
+assert.Nil(t, nil)
+assert.NoError(t, nil)
+assert.NotEmpty(t, "value")
+assert.NotZero(t, 42)
+assert.NotZero(t, "value")
+assert.Positive(t, 42)
+assert.True(t, true)
+assert.Zero(t, 0)
+assert.Zero(t, "")
+assert.Zero(t, nil)
+```
**Autofix**: false.
**Enabled by default**: true.
-**Reason**: Protection from bugs and possible dead code.
+**Reason**: Protection from bugs and dead code.
---
diff --git a/analyzer/analyzer_test.go b/analyzer/analyzer_test.go
index 281f352..695c4e3 100644
--- a/analyzer/analyzer_test.go
+++ b/analyzer/analyzer_test.go
@@ -13,6 +13,29 @@ import (
func TestTestifyLint(t *testing.T) {
t.Parallel()
+ for _, checker := range checkers.All() {
+ checker := checker
+
+ t.Run(checker, func(t *testing.T) {
+ t.Parallel()
+
+ anlzr := analyzer.New()
+ if err := anlzr.Flags.Set("disable-all", "true"); err != nil {
+ t.Fatal(err)
+ }
+ if err := anlzr.Flags.Set("enable", checker); err != nil {
+ t.Fatal(err)
+ }
+
+ pkg := filepath.Join("checkers-default", checker)
+ analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), anlzr, pkg)
+ })
+ }
+}
+
+func TestTestifyLint_NotDefaultCases(t *testing.T) {
+ t.Parallel()
+
cases := []struct {
dir string
flags map[string]string
@@ -53,6 +76,20 @@ func TestTestifyLint(t *testing.T) {
"expected-actual.pattern": "goldenValue",
},
},
+ {
+ dir: "formatter-issue170",
+ flags: map[string]string{
+ "disable-all": "true",
+ "enable": checkers.NewFormatter().Name(),
+ },
+ },
+ {
+ dir: "formatter-issue170-suite",
+ flags: map[string]string{
+ "disable-all": "true",
+ "enable": checkers.NewFormatter().Name(),
+ },
+ },
{
dir: "formatter-not-defaults",
flags: map[string]string{
@@ -129,26 +166,3 @@ func TestTestifyLint(t *testing.T) {
})
}
}
-
-func TestTestifyLint_CheckersDefault(t *testing.T) {
- t.Parallel()
-
- for _, checker := range checkers.All() {
- checker := checker
-
- t.Run(checker, func(t *testing.T) {
- t.Parallel()
-
- anlzr := analyzer.New()
- if err := anlzr.Flags.Set("disable-all", "true"); err != nil {
- t.Fatal(err)
- }
- if err := anlzr.Flags.Set("enable", checker); err != nil {
- t.Fatal(err)
- }
-
- pkg := filepath.Join("checkers-default", checker)
- analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), anlzr, pkg)
- })
- }
-}
diff --git a/analyzer/checkers_factory_test.go b/analyzer/checkers_factory_test.go
index 6533494..28bfb1d 100644
--- a/analyzer/checkers_factory_test.go
+++ b/analyzer/checkers_factory_test.go
@@ -25,6 +25,7 @@ func Test_newCheckers(t *testing.T) {
checkers.NewErrorNil(),
checkers.NewNilCompare(),
checkers.NewErrorIsAs(),
+ checkers.NewEncodedCompare(),
checkers.NewExpectedActual(),
checkers.NewRegexp(),
checkers.NewSuiteExtraAssertCall(),
@@ -43,6 +44,7 @@ func Test_newCheckers(t *testing.T) {
checkers.NewErrorNil(),
checkers.NewNilCompare(),
checkers.NewErrorIsAs(),
+ checkers.NewEncodedCompare(),
checkers.NewExpectedActual(),
checkers.NewRegexp(),
checkers.NewSuiteExtraAssertCall(),
diff --git a/analyzer/testdata/src/checkers-default/empty/empty_test.go b/analyzer/testdata/src/checkers-default/empty/empty_test.go
index d20e16a..88c624e 100644
--- a/analyzer/testdata/src/checkers-default/empty/empty_test.go
+++ b/analyzer/testdata/src/checkers-default/empty/empty_test.go
@@ -38,8 +38,12 @@ func TestEmptyChecker(t *testing.T) {
assert.Greaterf(t, 1, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
assert.Zero(t, len(elems)) // want "empty: use assert\\.Empty"
assert.Zerof(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, len(elems)) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Zero(t, len([]string{"e"})) // want "empty: use assert\\.Empty"
+ assert.Zerof(t, len([]string{"e"}), "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, len(elems)) // want "empty: remove unnecessary len"
+ assert.Emptyf(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: remove unnecessary len"
+ assert.Empty(t, len([]string{"e"})) // want "empty: remove unnecessary len"
+ assert.Emptyf(t, len([]string{"e"}), "msg with args %d %s", 42, "42") // want "empty: remove unnecessary len"
assert.Less(t, len(elems), 0) // want "empty: use assert\\.Empty"
assert.Lessf(t, len(elems), 0, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
assert.Greater(t, 0, len(elems)) // want "empty: use assert\\.Empty"
@@ -73,8 +77,12 @@ func TestEmptyChecker(t *testing.T) {
assert.Positivef(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
assert.NotZero(t, len(elems)) // want "empty: use assert\\.NotEmpty"
assert.NotZerof(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
- assert.NotEmpty(t, len(elems)) // want "empty: use assert\\.NotEmpty"
- assert.NotEmptyf(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotZero(t, len([]string{"e"})) // want "empty: use assert\\.NotEmpty"
+ assert.NotZerof(t, len([]string{"e"}), "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, len(elems)) // want "empty: remove unnecessary len"
+ assert.NotEmptyf(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: remove unnecessary len"
+ assert.NotEmpty(t, len([]string{"e"})) // want "empty: remove unnecessary len"
+ assert.NotEmptyf(t, len([]string{"e"}), "msg with args %d %s", 42, "42") // want "empty: remove unnecessary len"
// Valid.
assert.NotEmpty(t, elems)
diff --git a/analyzer/testdata/src/checkers-default/empty/empty_test.go.golden b/analyzer/testdata/src/checkers-default/empty/empty_test.go.golden
index e6eb42c..958a3a1 100644
--- a/analyzer/testdata/src/checkers-default/empty/empty_test.go.golden
+++ b/analyzer/testdata/src/checkers-default/empty/empty_test.go.golden
@@ -14,40 +14,44 @@ func TestEmptyChecker(t *testing.T) {
// assert.Empty cases.
{
// Invalid.
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
- assert.Empty(t, elems) // want "empty: use assert\\.Empty"
- assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, []string{"e"}) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, []string{"e"}, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: remove unnecessary len"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: remove unnecessary len"
+ assert.Empty(t, []string{"e"}) // want "empty: remove unnecessary len"
+ assert.Emptyf(t, []string{"e"}, "msg with args %d %s", 42, "42") // want "empty: remove unnecessary len"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
+ assert.Empty(t, elems) // want "empty: use assert\\.Empty"
+ assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf"
// Valid.
assert.Empty(t, elems)
@@ -57,24 +61,28 @@ func TestEmptyChecker(t *testing.T) {
// assert.NotEmpty cases.
{
// Invalid.
- assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
- assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
- assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
- assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
- assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
- assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
- assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
- assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
- assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
- assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
- assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
- assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
- assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
- assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
- assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
- assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
- assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
- assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
+ assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
+ assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
+ assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
+ assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
+ assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
+ assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
+ assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty"
+ assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, []string{"e"}) // want "empty: use assert\\.NotEmpty"
+ assert.NotEmptyf(t, []string{"e"}, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf"
+ assert.NotEmpty(t, elems) // want "empty: remove unnecessary len"
+ assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: remove unnecessary len"
+ assert.NotEmpty(t, []string{"e"}) // want "empty: remove unnecessary len"
+ assert.NotEmptyf(t, []string{"e"}, "msg with args %d %s", 42, "42") // want "empty: remove unnecessary len"
// Valid.
assert.NotEmpty(t, elems)
diff --git a/analyzer/testdata/src/checkers-default/encoded-compare/encoded_compare_test.go b/analyzer/testdata/src/checkers-default/encoded-compare/encoded_compare_test.go
new file mode 100644
index 0000000..d4c9c6c
--- /dev/null
+++ b/analyzer/testdata/src/checkers-default/encoded-compare/encoded_compare_test.go
@@ -0,0 +1,198 @@
+// Code generated by testifylint/internal/testgen. DO NOT EDIT.
+
+package encodedcompare
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEncodedCompareChecker(t *testing.T) {
+ var respBody, raw, hexString, toJSON, expJSON, resultJSON, jsonb, resJson string
+ var conf, expectedYAML, expYaml, ymlResult, yamlResult, expYML, outputYaml string
+ var respBytes, resultJSONBytes []byte
+ w := httptest.NewRecorder()
+ var batch interface{ ParentSummary() []byte }
+ var res [1]struct{ Data []byte }
+ var output bytes.Buffer
+
+ const expBody = `{"status":"healthy","message":"","peer_count":1}`
+
+ // Invalid.
+ {
+ assert.Equal(t, `{"name":"name","value":1000}`, respBody) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, `{"name":"name","value":1000}`, respBody, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, expBody, respBody) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, expBody, respBody, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, `{"status":404,"message":"abc"}`, string(respBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, `{"status":404,"message":"abc"}`, string(respBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, `{"message":"success"}`, w.Body.String()) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, `{"message":"success"}`, w.Body.String(), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, "{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", string(w.Body.Bytes())) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, "{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", string(w.Body.Bytes()), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, "{\n\t\"msg\": \"hello world\"\n}", respBody) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, "{\n\t\"msg\": \"hello world\"\n}", respBody, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, fmt.Sprintf(`{"value":"%s","valuePtr":"%s"}`, hexString, hexString), string(respBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, fmt.Sprintf(`{"value":"%s","valuePtr":"%s"}`, hexString, hexString), string(respBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, `[{"@id":"a","b":[{"@id":"c"}]}]`, toJSON) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, `[{"@id":"a","b":[{"@id":"c"}]}]`, toJSON, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, "{\"FirstName\":\"john\",\"LastName\":\"doe\",\"Age\":26,\"Height\":182.88}", string(resJson)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, "{\"FirstName\":\"john\",\"LastName\":\"doe\",\"Age\":26,\"Height\":182.88}", string(resJson), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, expJSON, resultJSON) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, expJSON, resultJSON, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, jsonb, respBody) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, jsonb, respBody, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, respBody, jsonb) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, respBody, jsonb, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, expJSON, resJson) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, expJSON, resJson, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, expectedYAML, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.Equalf(t, expectedYAML, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Equal(t, expYaml, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.Equalf(t, expYaml, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Equal(t, ymlResult, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.Equalf(t, ymlResult, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Equal(t, yamlResult, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.Equalf(t, yamlResult, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Equal(t, expYML, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.Equalf(t, expYML, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Equal(t, conf, expectedYAML) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.Equalf(t, conf, expectedYAML, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Equal(t, outputYaml, string(output.Bytes())) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.Equalf(t, outputYaml, string(output.Bytes()), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Equal(t, json.RawMessage(`{"uuid": "b65b1a22-db6d-4f5a-9b3d-7302368a82e6"}`), batch.ParentSummary()) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, json.RawMessage(`{"uuid": "b65b1a22-db6d-4f5a-9b3d-7302368a82e6"}`), batch.ParentSummary(), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, res[0].Data, json.RawMessage([]byte(`{"name":"new"}`))) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, res[0].Data, json.RawMessage([]byte(`{"name":"new"}`)), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, json.RawMessage(raw), json.RawMessage(resultJSONBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, json.RawMessage(raw), json.RawMessage(resultJSONBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, json.RawMessage(raw), raw) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, json.RawMessage(raw), raw, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, json.RawMessage("{}"), respBody) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, json.RawMessage("{}"), respBody, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, respBody, json.RawMessage("null")) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, respBody, json.RawMessage("null"), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, json.RawMessage(`["more","raw","things"]`), resultJSONBytes) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, json.RawMessage(`["more","raw","things"]`), resultJSONBytes, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, "{}", string(resultJSONBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, "{}", string(resultJSONBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, []byte(expJSON), resultJSONBytes) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, []byte(expJSON), resultJSONBytes, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, []byte(expYaml), respBytes) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.Equalf(t, []byte(expYaml), respBytes, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Equal(t, expJSON, strings.Trim(string(resultJSONBytes), "\n")) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, expJSON, strings.Trim(string(resultJSONBytes), "\n"), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, raw, strings.Replace(jsonb, "\n", "", -1)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, raw, strings.Replace(jsonb, "\n", "", -1), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, `{"status":"healthy","message":"","peer_count":1}`, strings.ReplaceAll(string(respBytes), " ", "")) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, `{"status":"healthy","message":"","peer_count":1}`, strings.ReplaceAll(string(respBytes), " ", ""), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, `{"foo":"bar"}`, strings.TrimSpace(w.Body.String())) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, `{"foo":"bar"}`, strings.TrimSpace(w.Body.String()), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, `{"bar":"foo"}`, strings.TrimSpace(string(w.Body.Bytes()))) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Equalf(t, `{"bar":"foo"}`, strings.TrimSpace(string(w.Body.Bytes())), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Equal(t, strings.TrimSpace(strings.ReplaceAll(expYaml, "\t", " ")), strings.TrimSpace(string(respBytes))) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.Equalf(t, strings.TrimSpace(strings.ReplaceAll(expYaml, "\t", " ")), strings.TrimSpace(string(respBytes)), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.EqualValues(t, expJSON, resJson) // want "encoded-compare: use assert\\.JSONEq"
+ assert.EqualValuesf(t, expJSON, resJson, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.Exactly(t, expJSON, resJson) // want "encoded-compare: use assert\\.JSONEq"
+ assert.Exactlyf(t, expJSON, resJson, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.EqualValues(t, expYaml, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.EqualValuesf(t, expYaml, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Exactly(t, expYaml, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.Exactlyf(t, expYaml, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Equal(t, ` // want "encoded-compare: use assert\.JSONEq"
+{
+ "id": 123,
+ "method": "get_prop",
+ "params": ["power","sat"]
+}
+`, raw)
+ assert.Equal(t, ` // want "encoded-compare: use assert\.JSONEq"
+{
+ "id": 123,
+ "method": "get_prop",
+ "params": ["power","sat"]
+}
+`, raw, "msg with args %d %s", 42, "42")
+ }
+
+ // Valid.
+ {
+ assert.JSONEq(t, `{"name":"name","value":1000}`, respBody)
+ assert.JSONEqf(t, `{"name":"name","value":1000}`, respBody, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, expJSON, resultJSON)
+ assert.JSONEqf(t, expJSON, resultJSON, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, `{"foo":"bar"}`, `{"foo":"bar"}`)
+ assert.JSONEqf(t, `{"foo":"bar"}`, `{"foo":"bar"}`, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, `{"message":"success"}`, w.Body.String())
+ assert.JSONEqf(t, `{"message":"success"}`, w.Body.String(), "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, fmt.Sprintf(`{"value":"%s"}`, hexString), resJson)
+ assert.JSONEqf(t, fmt.Sprintf(`{"value":"%s"}`, hexString), resJson, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, "{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", w.Body.String())
+ assert.JSONEqf(t, "{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", w.Body.String(), "msg with args %d %s", 42, "42")
+ assert.YAMLEq(t, expYaml, conf)
+ assert.YAMLEqf(t, expYaml, conf, "msg with args %d %s", 42, "42")
+ }
+
+ // Ignored.
+ {
+ assert.Equal(t, "{{ .StepName }}", "use", "command name incorrect")
+ assert.Equalf(t, "{{ .StepName }}", "use", "command name incorrect", "msg with args %d %s", 42, "42")
+ assert.Equal(t, json.RawMessage{}, respBody)
+ assert.Equalf(t, json.RawMessage{}, respBody, "msg with args %d %s", 42, "42")
+ assert.Equal(t, json.RawMessage(nil), respBody)
+ assert.Equalf(t, json.RawMessage(nil), respBody, "msg with args %d %s", 42, "42")
+ assert.Equal(t, raw, raw)
+ assert.Equalf(t, raw, raw, "msg with args %d %s", 42, "42")
+ assert.EqualValues(t, raw, raw)
+ assert.EqualValuesf(t, raw, raw, "msg with args %d %s", 42, "42")
+ assert.Exactly(t, raw, raw)
+ assert.Exactlyf(t, raw, raw, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, raw, raw)
+ assert.JSONEqf(t, raw, raw, "msg with args %d %s", 42, "42")
+ assert.Equal(t, string(respBytes), raw)
+ assert.Equalf(t, string(respBytes), raw, "msg with args %d %s", 42, "42")
+ assert.EqualValues(t, raw, string(respBytes))
+ assert.EqualValuesf(t, raw, string(respBytes), "msg with args %d %s", 42, "42")
+ assert.Exactly(t, string(respBytes), raw)
+ assert.Exactlyf(t, string(respBytes), raw, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, raw, string(respBytes))
+ assert.JSONEqf(t, raw, string(respBytes), "msg with args %d %s", 42, "42")
+ assert.NotEqual(t, raw, resultJSON)
+ assert.NotEqualf(t, raw, resultJSON, "msg with args %d %s", 42, "42")
+ assert.NotEqualValues(t, resultJSON, resultJSON)
+ assert.NotEqualValuesf(t, resultJSON, resultJSON, "msg with args %d %s", 42, "42")
+ assert.YAMLEq(t, `
+kind: Kustomization
+apiVersion: kustomize.config.k8s.io/v1beta1
+images:
+ - name: foo
+ newName: bar
+ - name: bar
+ newName: baz
+ newTag: "123"
+`, conf)
+ assert.YAMLEqf(t, `
+kind: Kustomization
+apiVersion: kustomize.config.k8s.io/v1beta1
+images:
+ - name: foo
+ newName: bar
+ - name: bar
+ newName: baz
+ newTag: "123"
+`, conf, "msg with args %d %s", 42, "42")
+ assert.YAMLEq(t, "kind: Kustomization", "kind: Kustomization")
+ assert.YAMLEqf(t, "kind: Kustomization", "kind: Kustomization", "msg with args %d %s", 42, "42")
+ assert.YAMLEq(t, raw, conf)
+ assert.YAMLEqf(t, raw, conf, "msg with args %d %s", 42, "42")
+ assert.YAMLEq(t, raw, string(respBytes))
+ assert.YAMLEqf(t, raw, string(respBytes), "msg with args %d %s", 42, "42")
+ }
+}
diff --git a/analyzer/testdata/src/checkers-default/encoded-compare/encoded_compare_test.go.golden b/analyzer/testdata/src/checkers-default/encoded-compare/encoded_compare_test.go.golden
new file mode 100644
index 0000000..f031d71
--- /dev/null
+++ b/analyzer/testdata/src/checkers-default/encoded-compare/encoded_compare_test.go.golden
@@ -0,0 +1,198 @@
+// Code generated by testifylint/internal/testgen. DO NOT EDIT.
+
+package encodedcompare
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEncodedCompareChecker(t *testing.T) {
+ var respBody, raw, hexString, toJSON, expJSON, resultJSON, jsonb, resJson string
+ var conf, expectedYAML, expYaml, ymlResult, yamlResult, expYML, outputYaml string
+ var respBytes, resultJSONBytes []byte
+ w := httptest.NewRecorder()
+ var batch interface{ ParentSummary() []byte }
+ var res [1]struct{ Data []byte }
+ var output bytes.Buffer
+
+ const expBody = `{"status":"healthy","message":"","peer_count":1}`
+
+ // Invalid.
+ {
+ assert.JSONEq(t, `{"name":"name","value":1000}`, respBody) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, `{"name":"name","value":1000}`, respBody, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, expBody, respBody) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, expBody, respBody, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, `{"status":404,"message":"abc"}`, string(respBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, `{"status":404,"message":"abc"}`, string(respBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, `{"message":"success"}`, w.Body.String()) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, `{"message":"success"}`, w.Body.String(), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, "{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", w.Body.String()) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, "{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", w.Body.String(), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, "{\n\t\"msg\": \"hello world\"\n}", respBody) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, "{\n\t\"msg\": \"hello world\"\n}", respBody, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, fmt.Sprintf(`{"value":"%s","valuePtr":"%s"}`, hexString, hexString), string(respBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, fmt.Sprintf(`{"value":"%s","valuePtr":"%s"}`, hexString, hexString), string(respBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, `[{"@id":"a","b":[{"@id":"c"}]}]`, toJSON) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, `[{"@id":"a","b":[{"@id":"c"}]}]`, toJSON, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, "{\"FirstName\":\"john\",\"LastName\":\"doe\",\"Age\":26,\"Height\":182.88}", resJson) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, "{\"FirstName\":\"john\",\"LastName\":\"doe\",\"Age\":26,\"Height\":182.88}", resJson, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, expJSON, resultJSON) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, expJSON, resultJSON, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, jsonb, respBody) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, jsonb, respBody, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, respBody, jsonb) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, respBody, jsonb, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, expJSON, resJson) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, expJSON, resJson, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.YAMLEq(t, expectedYAML, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, expectedYAML, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.YAMLEq(t, expYaml, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, expYaml, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.YAMLEq(t, ymlResult, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, ymlResult, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.YAMLEq(t, yamlResult, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, yamlResult, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.YAMLEq(t, expYML, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, expYML, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.YAMLEq(t, conf, expectedYAML) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, conf, expectedYAML, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.YAMLEq(t, outputYaml, output.String()) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, outputYaml, output.String(), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.JSONEq(t, `{"uuid": "b65b1a22-db6d-4f5a-9b3d-7302368a82e6"}`, string(batch.ParentSummary())) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, `{"uuid": "b65b1a22-db6d-4f5a-9b3d-7302368a82e6"}`, string(batch.ParentSummary()), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, string(res[0].Data), `{"name":"new"}`) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, string(res[0].Data), `{"name":"new"}`, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, raw, string(resultJSONBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, raw, string(resultJSONBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, raw, raw) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, raw, raw, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, "{}", respBody) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, "{}", respBody, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, respBody, "null") // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, respBody, "null", "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, `["more","raw","things"]`, string(resultJSONBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, `["more","raw","things"]`, string(resultJSONBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, "{}", string(resultJSONBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, "{}", string(resultJSONBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, expJSON, string(resultJSONBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, expJSON, string(resultJSONBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.YAMLEq(t, expYaml, string(respBytes)) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, expYaml, string(respBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.JSONEq(t, expJSON, string(resultJSONBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, expJSON, string(resultJSONBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, raw, jsonb) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, raw, jsonb, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, `{"status":"healthy","message":"","peer_count":1}`, string(respBytes)) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, `{"status":"healthy","message":"","peer_count":1}`, string(respBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, `{"foo":"bar"}`, w.Body.String()) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, `{"foo":"bar"}`, w.Body.String(), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, `{"bar":"foo"}`, w.Body.String()) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, `{"bar":"foo"}`, w.Body.String(), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.YAMLEq(t, expYaml, string(respBytes)) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, expYaml, string(respBytes), "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.JSONEq(t, expJSON, resJson) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, expJSON, resJson, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.JSONEq(t, expJSON, resJson) // want "encoded-compare: use assert\\.JSONEq"
+ assert.JSONEqf(t, expJSON, resJson, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.JSONEqf"
+ assert.YAMLEq(t, expYaml, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, expYaml, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.YAMLEq(t, expYaml, conf) // want "encoded-compare: use assert\\.YAMLEq"
+ assert.YAMLEqf(t, expYaml, conf, "msg with args %d %s", 42, "42") // want "encoded-compare: use assert\\.YAMLEqf"
+ assert.Equal(t, ` // want "encoded-compare: use assert\.JSONEq"
+{
+ "id": 123,
+ "method": "get_prop",
+ "params": ["power","sat"]
+}
+`, raw)
+ assert.Equal(t, ` // want "encoded-compare: use assert\.JSONEq"
+{
+ "id": 123,
+ "method": "get_prop",
+ "params": ["power","sat"]
+}
+`, raw, "msg with args %d %s", 42, "42")
+ }
+
+ // Valid.
+ {
+ assert.JSONEq(t, `{"name":"name","value":1000}`, respBody)
+ assert.JSONEqf(t, `{"name":"name","value":1000}`, respBody, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, expJSON, resultJSON)
+ assert.JSONEqf(t, expJSON, resultJSON, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, `{"foo":"bar"}`, `{"foo":"bar"}`)
+ assert.JSONEqf(t, `{"foo":"bar"}`, `{"foo":"bar"}`, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, `{"message":"success"}`, w.Body.String())
+ assert.JSONEqf(t, `{"message":"success"}`, w.Body.String(), "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, fmt.Sprintf(`{"value":"%s"}`, hexString), resJson)
+ assert.JSONEqf(t, fmt.Sprintf(`{"value":"%s"}`, hexString), resJson, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, "{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", w.Body.String())
+ assert.JSONEqf(t, "{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", w.Body.String(), "msg with args %d %s", 42, "42")
+ assert.YAMLEq(t, expYaml, conf)
+ assert.YAMLEqf(t, expYaml, conf, "msg with args %d %s", 42, "42")
+ }
+
+ // Ignored.
+ {
+ assert.Equal(t, "{{ .StepName }}", "use", "command name incorrect")
+ assert.Equalf(t, "{{ .StepName }}", "use", "command name incorrect", "msg with args %d %s", 42, "42")
+ assert.Equal(t, json.RawMessage{}, respBody)
+ assert.Equalf(t, json.RawMessage{}, respBody, "msg with args %d %s", 42, "42")
+ assert.Equal(t, json.RawMessage(nil), respBody)
+ assert.Equalf(t, json.RawMessage(nil), respBody, "msg with args %d %s", 42, "42")
+ assert.Equal(t, raw, raw)
+ assert.Equalf(t, raw, raw, "msg with args %d %s", 42, "42")
+ assert.EqualValues(t, raw, raw)
+ assert.EqualValuesf(t, raw, raw, "msg with args %d %s", 42, "42")
+ assert.Exactly(t, raw, raw)
+ assert.Exactlyf(t, raw, raw, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, raw, raw)
+ assert.JSONEqf(t, raw, raw, "msg with args %d %s", 42, "42")
+ assert.Equal(t, string(respBytes), raw)
+ assert.Equalf(t, string(respBytes), raw, "msg with args %d %s", 42, "42")
+ assert.EqualValues(t, raw, string(respBytes))
+ assert.EqualValuesf(t, raw, string(respBytes), "msg with args %d %s", 42, "42")
+ assert.Exactly(t, string(respBytes), raw)
+ assert.Exactlyf(t, string(respBytes), raw, "msg with args %d %s", 42, "42")
+ assert.JSONEq(t, raw, string(respBytes))
+ assert.JSONEqf(t, raw, string(respBytes), "msg with args %d %s", 42, "42")
+ assert.NotEqual(t, raw, resultJSON)
+ assert.NotEqualf(t, raw, resultJSON, "msg with args %d %s", 42, "42")
+ assert.NotEqualValues(t, resultJSON, resultJSON)
+ assert.NotEqualValuesf(t, resultJSON, resultJSON, "msg with args %d %s", 42, "42")
+ assert.YAMLEq(t, `
+kind: Kustomization
+apiVersion: kustomize.config.k8s.io/v1beta1
+images:
+ - name: foo
+ newName: bar
+ - name: bar
+ newName: baz
+ newTag: "123"
+`, conf)
+ assert.YAMLEqf(t, `
+kind: Kustomization
+apiVersion: kustomize.config.k8s.io/v1beta1
+images:
+ - name: foo
+ newName: bar
+ - name: bar
+ newName: baz
+ newTag: "123"
+`, conf, "msg with args %d %s", 42, "42")
+ assert.YAMLEq(t, "kind: Kustomization", "kind: Kustomization")
+ assert.YAMLEqf(t, "kind: Kustomization", "kind: Kustomization", "msg with args %d %s", 42, "42")
+ assert.YAMLEq(t, raw, conf)
+ assert.YAMLEqf(t, raw, conf, "msg with args %d %s", 42, "42")
+ assert.YAMLEq(t, raw, string(respBytes))
+ assert.YAMLEqf(t, raw, string(respBytes), "msg with args %d %s", 42, "42")
+ }
+}
diff --git a/analyzer/testdata/src/checkers-default/useless-assert/useless_assert_test.go b/analyzer/testdata/src/checkers-default/useless-assert/useless_assert_test.go
index 8781eeb..14e4af1 100644
--- a/analyzer/testdata/src/checkers-default/useless-assert/useless_assert_test.go
+++ b/analyzer/testdata/src/checkers-default/useless-assert/useless_assert_test.go
@@ -15,6 +15,7 @@ func TestUselessAssertChecker(t *testing.T) {
var elapsed time.Time
var str string
var num int
+ var b bool
var tc testCase
// Invalid.
@@ -61,6 +62,104 @@ func TestUselessAssertChecker(t *testing.T) {
assert.IsTypef(t, (*testCase)(nil), (*testCase)(nil), "msg") // want "useless-assert: asserting of the same variable"
assert.IsTypef(t, (*testCase)(nil), (*testCase)(nil), "msg with arg %d", 42) // want "useless-assert: asserting of the same variable"
assert.IsTypef(t, (*testCase)(nil), (*testCase)(nil), "msg with args %d %s", 42, "42") // want "useless-assert: asserting of the same variable"
+ assert.Empty(t, "") // want "useless-assert: meaningless assertion"
+ assert.Empty(t, "", "msg") // want "useless-assert: meaningless assertion"
+ assert.Empty(t, "", "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Empty(t, "", "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Emptyf(t, "", "msg") // want "useless-assert: meaningless assertion"
+ assert.Emptyf(t, "", "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Emptyf(t, "", "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.False(t, false) // want "useless-assert: meaningless assertion"
+ assert.False(t, false, "msg") // want "useless-assert: meaningless assertion"
+ assert.False(t, false, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.False(t, false, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Falsef(t, false, "msg") // want "useless-assert: meaningless assertion"
+ assert.Falsef(t, false, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Falsef(t, false, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Implements(t, (*any)(nil), new(testing.T)) // want "useless-assert: meaningless assertion"
+ assert.Implements(t, (*any)(nil), new(testing.T), "msg") // want "useless-assert: meaningless assertion"
+ assert.Implements(t, (*any)(nil), new(testing.T), "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Implements(t, (*any)(nil), new(testing.T), "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Implementsf(t, (*any)(nil), new(testing.T), "msg") // want "useless-assert: meaningless assertion"
+ assert.Implementsf(t, (*any)(nil), new(testing.T), "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Implementsf(t, (*any)(nil), new(testing.T), "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Negative(t, -42) // want "useless-assert: meaningless assertion"
+ assert.Negative(t, -42, "msg") // want "useless-assert: meaningless assertion"
+ assert.Negative(t, -42, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Negative(t, -42, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Negativef(t, -42, "msg") // want "useless-assert: meaningless assertion"
+ assert.Negativef(t, -42, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Negativef(t, -42, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Nil(t, nil) // want "useless-assert: meaningless assertion"
+ assert.Nil(t, nil, "msg") // want "useless-assert: meaningless assertion"
+ assert.Nil(t, nil, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Nil(t, nil, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Nilf(t, nil, "msg") // want "useless-assert: meaningless assertion"
+ assert.Nilf(t, nil, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Nilf(t, nil, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.NoError(t, nil) // want "useless-assert: meaningless assertion"
+ assert.NoError(t, nil, "msg") // want "useless-assert: meaningless assertion"
+ assert.NoError(t, nil, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.NoError(t, nil, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.NoErrorf(t, nil, "msg") // want "useless-assert: meaningless assertion"
+ assert.NoErrorf(t, nil, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.NoErrorf(t, nil, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.NotEmpty(t, "value") // want "useless-assert: meaningless assertion"
+ assert.NotEmpty(t, "value", "msg") // want "useless-assert: meaningless assertion"
+ assert.NotEmpty(t, "value", "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.NotEmpty(t, "value", "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.NotEmptyf(t, "value", "msg") // want "useless-assert: meaningless assertion"
+ assert.NotEmptyf(t, "value", "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.NotEmptyf(t, "value", "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.NotZero(t, 42) // want "useless-assert: meaningless assertion"
+ assert.NotZero(t, 42, "msg") // want "useless-assert: meaningless assertion"
+ assert.NotZero(t, 42, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.NotZero(t, 42, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.NotZerof(t, 42, "msg") // want "useless-assert: meaningless assertion"
+ assert.NotZerof(t, 42, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.NotZerof(t, 42, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.NotZero(t, "value") // want "useless-assert: meaningless assertion"
+ assert.NotZero(t, "value", "msg") // want "useless-assert: meaningless assertion"
+ assert.NotZero(t, "value", "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.NotZero(t, "value", "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.NotZerof(t, "value", "msg") // want "useless-assert: meaningless assertion"
+ assert.NotZerof(t, "value", "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.NotZerof(t, "value", "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Positive(t, 42) // want "useless-assert: meaningless assertion"
+ assert.Positive(t, 42, "msg") // want "useless-assert: meaningless assertion"
+ assert.Positive(t, 42, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Positive(t, 42, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Positivef(t, 42, "msg") // want "useless-assert: meaningless assertion"
+ assert.Positivef(t, 42, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Positivef(t, 42, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.True(t, true) // want "useless-assert: meaningless assertion"
+ assert.True(t, true, "msg") // want "useless-assert: meaningless assertion"
+ assert.True(t, true, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.True(t, true, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Truef(t, true, "msg") // want "useless-assert: meaningless assertion"
+ assert.Truef(t, true, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Truef(t, true, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Zero(t, 0) // want "useless-assert: meaningless assertion"
+ assert.Zero(t, 0, "msg") // want "useless-assert: meaningless assertion"
+ assert.Zero(t, 0, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Zero(t, 0, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Zerof(t, 0, "msg") // want "useless-assert: meaningless assertion"
+ assert.Zerof(t, 0, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Zerof(t, 0, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Zero(t, "") // want "useless-assert: meaningless assertion"
+ assert.Zero(t, "", "msg") // want "useless-assert: meaningless assertion"
+ assert.Zero(t, "", "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Zero(t, "", "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Zerof(t, "", "msg") // want "useless-assert: meaningless assertion"
+ assert.Zerof(t, "", "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Zerof(t, "", "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Zero(t, nil) // want "useless-assert: meaningless assertion"
+ assert.Zero(t, nil, "msg") // want "useless-assert: meaningless assertion"
+ assert.Zero(t, nil, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Zero(t, nil, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
+ assert.Zerof(t, nil, "msg") // want "useless-assert: meaningless assertion"
+ assert.Zerof(t, nil, "msg with arg %d", 42) // want "useless-assert: meaningless assertion"
+ assert.Zerof(t, nil, "msg with args %d %s", 42, "42") // want "useless-assert: meaningless assertion"
assert.Contains(t, value, value) // want "useless-assert: asserting of the same variable"
assert.Containsf(t, value, value, "msg with args %d %s", 42, "42") // want "useless-assert: asserting of the same variable"
assert.ElementsMatch(t, value, value) // want "useless-assert: asserting of the same variable"
@@ -186,6 +285,90 @@ func TestUselessAssertChecker(t *testing.T) {
assert.IsTypef(t, tc, testCase{}, "msg")
assert.IsTypef(t, tc, testCase{}, "msg with arg %d", 42)
assert.IsTypef(t, tc, testCase{}, "msg with args %d %s", 42, "42")
+ assert.Empty(t, str)
+ assert.Empty(t, str, "msg")
+ assert.Empty(t, str, "msg with arg %d", 42)
+ assert.Empty(t, str, "msg with args %d %s", 42, "42")
+ assert.Emptyf(t, str, "msg")
+ assert.Emptyf(t, str, "msg with arg %d", 42)
+ assert.Emptyf(t, str, "msg with args %d %s", 42, "42")
+ assert.False(t, b)
+ assert.False(t, b, "msg")
+ assert.False(t, b, "msg with arg %d", 42)
+ assert.False(t, b, "msg with args %d %s", 42, "42")
+ assert.Falsef(t, b, "msg")
+ assert.Falsef(t, b, "msg with arg %d", 42)
+ assert.Falsef(t, b, "msg with args %d %s", 42, "42")
+ assert.Implements(t, (*testing.TB)(nil), new(testing.T))
+ assert.Implements(t, (*testing.TB)(nil), new(testing.T), "msg")
+ assert.Implements(t, (*testing.TB)(nil), new(testing.T), "msg with arg %d", 42)
+ assert.Implements(t, (*testing.TB)(nil), new(testing.T), "msg with args %d %s", 42, "42")
+ assert.Implementsf(t, (*testing.TB)(nil), new(testing.T), "msg")
+ assert.Implementsf(t, (*testing.TB)(nil), new(testing.T), "msg with arg %d", 42)
+ assert.Implementsf(t, (*testing.TB)(nil), new(testing.T), "msg with args %d %s", 42, "42")
+ assert.Negative(t, num)
+ assert.Negative(t, num, "msg")
+ assert.Negative(t, num, "msg with arg %d", 42)
+ assert.Negative(t, num, "msg with args %d %s", 42, "42")
+ assert.Negativef(t, num, "msg")
+ assert.Negativef(t, num, "msg with arg %d", 42)
+ assert.Negativef(t, num, "msg with args %d %s", 42, "42")
+ assert.Nil(t, new(testCase))
+ assert.Nil(t, new(testCase), "msg")
+ assert.Nil(t, new(testCase), "msg with arg %d", 42)
+ assert.Nil(t, new(testCase), "msg with args %d %s", 42, "42")
+ assert.Nilf(t, new(testCase), "msg")
+ assert.Nilf(t, new(testCase), "msg with arg %d", 42)
+ assert.Nilf(t, new(testCase), "msg with args %d %s", 42, "42")
+ assert.NoError(t, err)
+ assert.NoError(t, err, "msg")
+ assert.NoError(t, err, "msg with arg %d", 42)
+ assert.NoError(t, err, "msg with args %d %s", 42, "42")
+ assert.NoErrorf(t, err, "msg")
+ assert.NoErrorf(t, err, "msg with arg %d", 42)
+ assert.NoErrorf(t, err, "msg with args %d %s", 42, "42")
+ assert.NotEmpty(t, str)
+ assert.NotEmpty(t, str, "msg")
+ assert.NotEmpty(t, str, "msg with arg %d", 42)
+ assert.NotEmpty(t, str, "msg with args %d %s", 42, "42")
+ assert.NotEmptyf(t, str, "msg")
+ assert.NotEmptyf(t, str, "msg with arg %d", 42)
+ assert.NotEmptyf(t, str, "msg with args %d %s", 42, "42")
+ assert.Positive(t, num)
+ assert.Positive(t, num, "msg")
+ assert.Positive(t, num, "msg with arg %d", 42)
+ assert.Positive(t, num, "msg with args %d %s", 42, "42")
+ assert.Positivef(t, num, "msg")
+ assert.Positivef(t, num, "msg with arg %d", 42)
+ assert.Positivef(t, num, "msg with args %d %s", 42, "42")
+ assert.True(t, b)
+ assert.True(t, b, "msg")
+ assert.True(t, b, "msg with arg %d", 42)
+ assert.True(t, b, "msg with args %d %s", 42, "42")
+ assert.Truef(t, b, "msg")
+ assert.Truef(t, b, "msg with arg %d", 42)
+ assert.Truef(t, b, "msg with args %d %s", 42, "42")
+ assert.Zero(t, num)
+ assert.Zero(t, num, "msg")
+ assert.Zero(t, num, "msg with arg %d", 42)
+ assert.Zero(t, num, "msg with args %d %s", 42, "42")
+ assert.Zerof(t, num, "msg")
+ assert.Zerof(t, num, "msg with arg %d", 42)
+ assert.Zerof(t, num, "msg with args %d %s", 42, "42")
+ assert.Zero(t, str)
+ assert.Zero(t, str, "msg")
+ assert.Zero(t, str, "msg with arg %d", 42)
+ assert.Zero(t, str, "msg with args %d %s", 42, "42")
+ assert.Zerof(t, str, "msg")
+ assert.Zerof(t, str, "msg with arg %d", 42)
+ assert.Zerof(t, str, "msg with args %d %s", 42, "42")
+ assert.Zero(t, new(testCase))
+ assert.Zero(t, new(testCase), "msg")
+ assert.Zero(t, new(testCase), "msg with arg %d", 42)
+ assert.Zero(t, new(testCase), "msg with args %d %s", 42, "42")
+ assert.Zerof(t, new(testCase), "msg")
+ assert.Zerof(t, new(testCase), "msg with arg %d", 42)
+ assert.Zerof(t, new(testCase), "msg with args %d %s", 42, "42")
}
}
diff --git a/analyzer/testdata/src/debug/useless_assert_test.go b/analyzer/testdata/src/debug/useless_assert_test.go
new file mode 100644
index 0000000..f54e1b6
--- /dev/null
+++ b/analyzer/testdata/src/debug/useless_assert_test.go
@@ -0,0 +1,25 @@
+package debug
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestUselessAsserts(t *testing.T) {
+ assert.Empty(t, "")
+ assert.False(t, false)
+ assert.Implements(t, (*any)(nil), new(testing.T))
+ assert.Negative(t, -42)
+ assert.Nil(t, nil)
+ assert.NoError(t, nil)
+ assert.NotEmpty(t, "value")
+ assert.NotZero(t, 42)
+ assert.NotZero(t, "value")
+ assert.Zero(t, nil)
+ assert.Positive(t, 42)
+ assert.True(t, true)
+ assert.Zero(t, 0)
+ assert.Zero(t, "")
+ assert.Zero(t, nil)
+}
diff --git a/analyzer/testdata/src/formatter-issue170-suite/suie_only_test.go b/analyzer/testdata/src/formatter-issue170-suite/suie_only_test.go
new file mode 100644
index 0000000..e73ca94
--- /dev/null
+++ b/analyzer/testdata/src/formatter-issue170-suite/suie_only_test.go
@@ -0,0 +1,24 @@
+package formatterissue170
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+)
+
+func TestFormatter(t *testing.T) {
+ suite.Run(t, new(FormatterSuite))
+}
+
+type FormatterSuite struct {
+ suite.Suite
+}
+
+func (s *FormatterSuite) TestFormatter() {
+ s.True(false, fmt.Sprintf("expected %v, got %v", true, false)) // want "formatter: remove unnecessary fmt\\.Sprintf"
+}
+
+func (s FormatterSuite) TestFormatterValueRecv() {
+ s.False(true, fmt.Sprintf("expected %v, got %v", true, false)) // want "formatter: remove unnecessary fmt\\.Sprintf"
+}
diff --git a/analyzer/testdata/src/formatter-issue170-suite/suie_only_test.go.golden b/analyzer/testdata/src/formatter-issue170-suite/suie_only_test.go.golden
new file mode 100644
index 0000000..32c6600
--- /dev/null
+++ b/analyzer/testdata/src/formatter-issue170-suite/suie_only_test.go.golden
@@ -0,0 +1,24 @@
+package formatterissue170
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+)
+
+func TestFormatter(t *testing.T) {
+ suite.Run(t, new(FormatterSuite))
+}
+
+type FormatterSuite struct {
+ suite.Suite
+}
+
+func (s *FormatterSuite) TestFormatter() {
+ s.True(false, "expected %v, got %v", true, false) // want "formatter: remove unnecessary fmt\\.Sprintf"
+}
+
+func (s FormatterSuite) TestFormatterValueRecv() {
+ s.False(true, "expected %v, got %v", true, false) // want "formatter: remove unnecessary fmt\\.Sprintf"
+}
diff --git a/analyzer/testdata/src/formatter-issue170/require_only_test.go b/analyzer/testdata/src/formatter-issue170/require_only_test.go
new file mode 100644
index 0000000..5957a21
--- /dev/null
+++ b/analyzer/testdata/src/formatter-issue170/require_only_test.go
@@ -0,0 +1,12 @@
+package formatterissue170
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestFormatter(t *testing.T) {
+ require.True(t, false, fmt.Sprintf("expected %v, got %v", true, false)) // want "formatter: remove unnecessary fmt\\.Sprintf"
+}
diff --git a/analyzer/testdata/src/formatter-issue170/require_only_test.go.golden b/analyzer/testdata/src/formatter-issue170/require_only_test.go.golden
new file mode 100644
index 0000000..26a1e49
--- /dev/null
+++ b/analyzer/testdata/src/formatter-issue170/require_only_test.go.golden
@@ -0,0 +1,12 @@
+package formatterissue170
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestFormatter(t *testing.T) {
+ require.True(t, false, "expected %v, got %v", true, false) // want "formatter: remove unnecessary fmt\\.Sprintf"
+}
diff --git a/internal/analysisutil/encoded.go b/internal/analysisutil/encoded.go
new file mode 100644
index 0000000..cafc283
--- /dev/null
+++ b/internal/analysisutil/encoded.go
@@ -0,0 +1,46 @@
+package analysisutil
+
+import "strings"
+
+var whitespaceRemover = strings.NewReplacer("\n", "", "\\n", "", "\t", "", "\\t", "", " ", "")
+
+// IsJSONLike returns true if the string has JSON format features.
+// A positive result can be returned for invalid JSON as well.
+func IsJSONLike(s string) bool {
+ s = whitespaceRemover.Replace(unescape(s))
+
+ var startMatch bool
+ for _, prefix := range []string{
+ `{{`, `{[`, `{"`,
+ `[{{`, `[{[`, `[{"`,
+ } {
+ if strings.HasPrefix(s, prefix) {
+ startMatch = true
+ break
+ }
+ }
+ if !startMatch {
+ return false
+ }
+
+ for _, keyValue := range []string{`":{`, `":[`, `":"`} {
+ if strings.Contains(s, keyValue) {
+ return true
+ }
+ }
+ return false
+
+ // NOTE(a.telyshev): We do not check the end of the string, because this is usually a field for typos.
+ // And one of the reasons for using JSON-specific assertions is to catch typos like this.
+}
+
+func unescape(s string) string {
+ s = strings.ReplaceAll(s, `\"`, `"`)
+ s = unquote(s, `"`)
+ s = unquote(s, "`")
+ return s
+}
+
+func unquote(s string, q string) string {
+ return strings.TrimLeft(strings.TrimRight(s, q), q)
+}
diff --git a/internal/analysisutil/encoded_test.go b/internal/analysisutil/encoded_test.go
new file mode 100644
index 0000000..afd2706
--- /dev/null
+++ b/internal/analysisutil/encoded_test.go
@@ -0,0 +1,64 @@
+package analysisutil_test
+
+import (
+ "testing"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+func TestIsJSONLike(t *testing.T) {
+ cases := []struct {
+ in string
+ expected bool
+ }{
+ {
+ in: `[{"name": "values-files", "array": ["values-dev.yaml"]}, {"name": "helm-parameters", "map": {"image.tag": "v1.2.3"}}]`,
+ expected: true,
+ },
+ {
+ in: `{"labels":{"aaa":"111"},"annotations":{"ccc":"333"}}`,
+ expected: true,
+ },
+ {
+ in: "{\"message\":\"No user was found in the LDAP server(s) with that username\"}",
+ expected: true,
+ },
+ {
+ in: `"{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}"`,
+ expected: true,
+ },
+ {
+ in: `"{\"message\":\"No user was found in the LDAP server(s) with that username\"}"`,
+ expected: true,
+ },
+ {
+ in: `{"uuid": "b65b1a22-db6d-4f5a-9b3d-7302368a82e6"}`,
+ expected: true,
+ },
+ {
+ in: `apiVersion: 3`,
+ expected: false,
+ },
+ {
+ in: `[{}]`,
+ expected: false,
+ },
+ {
+ in: `{{ .TemplateVar }}`,
+ expected: false,
+ },
+ {
+ in: `{{-.TemplateVar}}`,
+ expected: false,
+ },
+ }
+
+ for _, tt := range cases {
+ t.Run("", func(t *testing.T) {
+ isJSON := analysisutil.IsJSONLike(tt.in)
+ if isJSON != tt.expected {
+ t.FailNow()
+ }
+ })
+ }
+}
diff --git a/internal/checkers/blank_import.go b/internal/checkers/blank_import.go
index 403691e..56cd64e 100644
--- a/internal/checkers/blank_import.go
+++ b/internal/checkers/blank_import.go
@@ -53,7 +53,7 @@ func (checker BlankImport) Check(pass *analysis.Pass, _ *inspector.Inspector) (d
}
msg := fmt.Sprintf("avoid blank import of %s as it does nothing", pkg)
- diagnostics = append(diagnostics, *newDiagnostic(checker.Name(), imp, msg, nil))
+ diagnostics = append(diagnostics, *newDiagnostic(checker.Name(), imp, msg))
}
}
return diagnostics
diff --git a/internal/checkers/bool_compare.go b/internal/checkers/bool_compare.go
index d125c43..67959b6 100644
--- a/internal/checkers/bool_compare.go
+++ b/internal/checkers/bool_compare.go
@@ -49,13 +49,11 @@ func (checker BoolCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis.
}
survivingArg = newBoolCast(survivingArg)
}
- return newUseFunctionDiagnostic(checker.Name(), call, proposed,
- newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{
- Pos: replaceStart,
- End: replaceEnd,
- NewText: analysisutil.NodeBytes(pass.Fset, survivingArg),
- }),
- )
+ return newUseFunctionDiagnostic(checker.Name(), call, proposed, analysis.TextEdit{
+ Pos: replaceStart,
+ End: replaceEnd,
+ NewText: analysisutil.NodeBytes(pass.Fset, survivingArg),
+ })
}
newUseTrueDiagnostic := func(survivingArg ast.Expr, replaceStart, replaceEnd token.Pos) *analysis.Diagnostic {
@@ -74,7 +72,7 @@ func (checker BoolCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis.
survivingArg = newBoolCast(survivingArg)
}
return newDiagnostic(checker.Name(), call, "need to simplify the assertion",
- &analysis.SuggestedFix{
+ analysis.SuggestedFix{
Message: "Simplify the assertion",
TextEdits: []analysis.TextEdit{{
Pos: replaceStart,
@@ -106,7 +104,7 @@ func (checker BoolCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis.
case xor(t1, t2):
survivingArg, _ := anyVal([]bool{t1, t2}, arg2, arg1)
if call.Fn.NameFTrimmed == "Exactly" && !isBuiltinBool(pass, survivingArg) {
- // NOTE(a.telyshev): `Exactly` assumes no type casting.
+ // NOTE(a.telyshev): `Exactly` assumes no type conversion.
return nil
}
return newUseTrueDiagnostic(survivingArg, arg1.Pos(), arg2.End())
@@ -114,7 +112,7 @@ func (checker BoolCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis.
case xor(f1, f2):
survivingArg, _ := anyVal([]bool{f1, f2}, arg2, arg1)
if call.Fn.NameFTrimmed == "Exactly" && !isBuiltinBool(pass, survivingArg) {
- // NOTE(a.telyshev): `Exactly` assumes no type casting.
+ // NOTE(a.telyshev): `Exactly` assumes no type conversion.
return nil
}
return newUseFalseDiagnostic(survivingArg, arg1.Pos(), arg2.End())
diff --git a/internal/checkers/checkers_registry.go b/internal/checkers/checkers_registry.go
index 993c42f..f881be4 100644
--- a/internal/checkers/checkers_registry.go
+++ b/internal/checkers/checkers_registry.go
@@ -17,6 +17,7 @@ var registry = checkersRegistry{
{factory: asCheckerFactory(NewErrorNil), enabledByDefault: true},
{factory: asCheckerFactory(NewNilCompare), enabledByDefault: true},
{factory: asCheckerFactory(NewErrorIsAs), enabledByDefault: true},
+ {factory: asCheckerFactory(NewEncodedCompare), enabledByDefault: true},
{factory: asCheckerFactory(NewExpectedActual), enabledByDefault: true},
{factory: asCheckerFactory(NewRegexp), enabledByDefault: true},
{factory: asCheckerFactory(NewSuiteExtraAssertCall), enabledByDefault: true},
diff --git a/internal/checkers/checkers_registry_test.go b/internal/checkers/checkers_registry_test.go
index ecdbefd..08c3a39 100644
--- a/internal/checkers/checkers_registry_test.go
+++ b/internal/checkers/checkers_registry_test.go
@@ -45,6 +45,7 @@ func TestAll(t *testing.T) {
"error-nil",
"nil-compare",
"error-is-as",
+ "encoded-compare",
"expected-actual",
"regexp",
"suite-extra-assert-call",
@@ -81,6 +82,7 @@ func TestEnabledByDefault(t *testing.T) {
"error-nil",
"nil-compare",
"error-is-as",
+ "encoded-compare",
"expected-actual",
"regexp",
"suite-extra-assert-call",
diff --git a/internal/checkers/compares.go b/internal/checkers/compares.go
index bdde03d..f0c4013 100644
--- a/internal/checkers/compares.go
+++ b/internal/checkers/compares.go
@@ -61,7 +61,9 @@ func (checker Compares) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia
return nil
}
- if isPointer(pass, be.X) && isPointer(pass, be.Y) {
+ _, xp := isPointer(pass, be.X)
+ _, yp := isPointer(pass, be.Y)
+ if xp && yp {
switch proposedFn {
case "Equal":
proposedFn = "Same"
@@ -72,12 +74,11 @@ func (checker Compares) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia
a, b := be.X, be.Y
return newUseFunctionDiagnostic(checker.Name(), call, proposedFn,
- newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{
+ analysis.TextEdit{
Pos: be.X.Pos(),
End: be.Y.End(),
NewText: formatAsCallArgs(pass, a, b),
- }),
- )
+ })
}
var tokenToProposedFnInsteadOfTrue = map[token.Token]string{
diff --git a/internal/checkers/contains.go b/internal/checkers/contains.go
index afbb0d2..07f76c6 100644
--- a/internal/checkers/contains.go
+++ b/internal/checkers/contains.go
@@ -63,10 +63,9 @@ func (checker Contains) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia
}
return newUseFunctionDiagnostic(checker.Name(), call, proposed,
- newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{
+ analysis.TextEdit{
Pos: call.Args[0].Pos(),
End: call.Args[0].End(),
NewText: formatAsCallArgs(pass, ce.Args[0], ce.Args[1]),
- }),
- )
+ })
}
diff --git a/internal/checkers/empty.go b/internal/checkers/empty.go
index eafecb6..71657fe 100644
--- a/internal/checkers/empty.go
+++ b/internal/checkers/empty.go
@@ -53,25 +53,28 @@ func (checker Empty) checkEmpty(pass *analysis.Pass, call *CallMeta) *analysis.D
newUseEmptyDiagnostic := func(replaceStart, replaceEnd token.Pos, replaceWith ast.Expr) *analysis.Diagnostic {
const proposed = "Empty"
return newUseFunctionDiagnostic(checker.Name(), call, proposed,
- newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{
+ analysis.TextEdit{
Pos: replaceStart,
End: replaceEnd,
NewText: analysisutil.NodeBytes(pass.Fset, replaceWith),
- }),
- )
+ })
}
if len(call.Args) == 0 {
return nil
}
-
a := call.Args[0]
+
switch call.Fn.NameFTrimmed {
- case "Zero", "Empty":
- lenArg, ok := isBuiltinLenCall(pass, a)
- if ok {
+ case "Zero":
+ if lenArg, ok := isBuiltinLenCall(pass, a); ok {
return newUseEmptyDiagnostic(a.Pos(), a.End(), lenArg)
}
+
+ case "Empty":
+ if lenArg, ok := isBuiltinLenCall(pass, a); ok {
+ return newRemoveLenDiagnostic(pass, checker.Name(), call, a, lenArg)
+ }
}
if len(call.Args) < 2 {
@@ -120,25 +123,28 @@ func (checker Empty) checkNotEmpty(pass *analysis.Pass, call *CallMeta) *analysi
newUseNotEmptyDiagnostic := func(replaceStart, replaceEnd token.Pos, replaceWith ast.Expr) *analysis.Diagnostic {
const proposed = "NotEmpty"
return newUseFunctionDiagnostic(checker.Name(), call, proposed,
- newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{
+ analysis.TextEdit{
Pos: replaceStart,
End: replaceEnd,
NewText: analysisutil.NodeBytes(pass.Fset, replaceWith),
- }),
- )
+ })
}
if len(call.Args) == 0 {
return nil
}
-
a := call.Args[0]
+
switch call.Fn.NameFTrimmed {
- case "NotZero", "NotEmpty", "Positive":
- lenArg, ok := isBuiltinLenCall(pass, a)
- if ok {
+ case "NotZero", "Positive":
+ if lenArg, ok := isBuiltinLenCall(pass, a); ok {
return newUseNotEmptyDiagnostic(a.Pos(), a.End(), lenArg)
}
+
+ case "NotEmpty":
+ if lenArg, ok := isBuiltinLenCall(pass, a); ok {
+ return newRemoveLenDiagnostic(pass, checker.Name(), call, a, lenArg)
+ }
}
if len(call.Args) < 2 {
diff --git a/internal/checkers/encoded_compare.go b/internal/checkers/encoded_compare.go
new file mode 100644
index 0000000..53c74ac
--- /dev/null
+++ b/internal/checkers/encoded_compare.go
@@ -0,0 +1,101 @@
+package checkers
+
+import (
+ "go/ast"
+
+ "golang.org/x/tools/go/analysis"
+)
+
+// EncodedCompare detects situations like
+//
+// assert.Equal(t, `{"foo": "bar"}`, body)
+// assert.EqualValues(t, `{"foo": "bar"}`, body)
+// assert.Exactly(t, `{"foo": "bar"}`, body)
+// assert.Equal(t, expectedJSON, resultJSON)
+// assert.Equal(t, expBodyConst, w.Body.String())
+// assert.Equal(t, fmt.Sprintf(`{"value":"%s"}`, hexString), result)
+// assert.Equal(t, "{}", json.RawMessage(resp))
+// assert.Equal(t, expJSON, strings.Trim(string(resultJSONBytes), "\n")) // + Replace, ReplaceAll, TrimSpace
+//
+// assert.Equal(t, expectedYML, conf)
+//
+// and requires
+//
+// assert.JSONEq(t, `{"foo": "bar"}`, body)
+// assert.YAMLEq(t, expectedYML, conf)
+type EncodedCompare struct{}
+
+// NewEncodedCompare constructs EncodedCompare checker.
+func NewEncodedCompare() EncodedCompare { return EncodedCompare{} }
+func (EncodedCompare) Name() string { return "encoded-compare" }
+
+func (checker EncodedCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic {
+ switch call.Fn.NameFTrimmed {
+ case "Equal", "EqualValues", "Exactly":
+ default:
+ return nil
+ }
+
+ if len(call.Args) < 2 {
+ return nil
+ }
+ lhs, rhs := call.Args[0], call.Args[1]
+
+ a, aIsExplicitJSON := checker.unwrap(pass, call.Args[0])
+ b, bIsExplicitJSON := checker.unwrap(pass, call.Args[1])
+
+ var proposed string
+ switch {
+ case aIsExplicitJSON, bIsExplicitJSON, isJSONStyleExpr(pass, a), isJSONStyleExpr(pass, b):
+ proposed = "JSONEq"
+ case isYAMLStyleExpr(a), isYAMLStyleExpr(b):
+ proposed = "YAMLEq"
+ }
+
+ if proposed != "" {
+ return newUseFunctionDiagnostic(checker.Name(), call, proposed,
+ analysis.TextEdit{
+ Pos: lhs.Pos(),
+ End: lhs.End(),
+ NewText: formatWithStringCastForBytes(pass, a),
+ },
+ analysis.TextEdit{
+ Pos: rhs.Pos(),
+ End: rhs.End(),
+ NewText: formatWithStringCastForBytes(pass, b),
+ },
+ )
+ }
+ return nil
+}
+
+// unwrap unwraps expression from string, []byte, strings.Replace(All), strings.Trim(Space) and json.RawMessage conversions.
+// Returns true in the second argument, if json.RawMessage was in the chain.
+func (checker EncodedCompare) unwrap(pass *analysis.Pass, e ast.Expr) (ast.Expr, bool) {
+ ce, ok := e.(*ast.CallExpr)
+ if !ok {
+ return e, false
+ }
+ if len(ce.Args) == 0 {
+ return e, false
+ }
+
+ if isJSONRawMessageCast(pass, ce) {
+ if isNil(ce.Args[0]) { // NOTE(a.telyshev): Ignore json.RawMessage(nil) case.
+ return checker.unwrap(pass, ce.Args[0])
+ }
+
+ v, _ := checker.unwrap(pass, ce.Args[0])
+ return v, true
+ }
+
+ if isIdentWithName("string", ce.Fun) ||
+ isByteArray(ce.Fun) ||
+ isStringsReplaceCall(pass, ce) ||
+ isStringsReplaceAllCall(pass, ce) ||
+ isStringsTrimCall(pass, ce) ||
+ isStringsTrimSpaceCall(pass, ce) {
+ return checker.unwrap(pass, ce.Args[0])
+ }
+ return e, false
+}
diff --git a/internal/checkers/error_is_as.go b/internal/checkers/error_is_as.go
index ab92c2e..f2812c9 100644
--- a/internal/checkers/error_is_as.go
+++ b/internal/checkers/error_is_as.go
@@ -67,12 +67,11 @@ func (checker ErrorIsAs) Check(pass *analysis.Pass, call *CallMeta) *analysis.Di
}
if proposed != "" {
return newUseFunctionDiagnostic(checker.Name(), call, proposed,
- newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{
+ analysis.TextEdit{
Pos: ce.Pos(),
End: ce.End(),
NewText: formatAsCallArgs(pass, ce.Args[0], ce.Args[1]),
- }),
- )
+ })
}
case "False":
@@ -91,12 +90,11 @@ func (checker ErrorIsAs) Check(pass *analysis.Pass, call *CallMeta) *analysis.Di
if isErrorsIsCall(pass, ce) {
const proposed = "NotErrorIs"
return newUseFunctionDiagnostic(checker.Name(), call, proposed,
- newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{
+ analysis.TextEdit{
Pos: ce.Pos(),
End: ce.End(),
NewText: formatAsCallArgs(pass, ce.Args[0], ce.Args[1]),
- }),
- )
+ })
}
case "ErrorAs":
@@ -127,15 +125,15 @@ func (checker ErrorIsAs) Check(pass *analysis.Pass, call *CallMeta) *analysis.Di
pt, ok := tv.Type.Underlying().(*types.Pointer)
if !ok {
- return newDiagnostic(checker.Name(), call, defaultReport, nil)
+ return newDiagnostic(checker.Name(), call, defaultReport)
}
if pt.Elem() == errorType {
- return newDiagnostic(checker.Name(), call, errorPtrReport, nil)
+ return newDiagnostic(checker.Name(), call, errorPtrReport)
}
_, isInterface := pt.Elem().Underlying().(*types.Interface)
if !isInterface && !types.Implements(pt.Elem(), errorIface) {
- return newDiagnostic(checker.Name(), call, defaultReport, nil)
+ return newDiagnostic(checker.Name(), call, defaultReport)
}
}
return nil
diff --git a/internal/checkers/error_nil.go b/internal/checkers/error_nil.go
index ea61c49..b9f28df 100644
--- a/internal/checkers/error_nil.go
+++ b/internal/checkers/error_nil.go
@@ -85,12 +85,11 @@ func (checker ErrorNil) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia
if proposedFn != "" {
return newUseFunctionDiagnostic(checker.Name(), call, proposedFn,
- newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{
+ analysis.TextEdit{
Pos: call.Args[0].Pos(),
End: replacementEndPos,
NewText: analysisutil.NodeBytes(pass.Fset, survivingArg),
- }),
- )
+ })
}
return nil
}
diff --git a/internal/checkers/expected_actual.go b/internal/checkers/expected_actual.go
index 77784dc..351d675 100644
--- a/internal/checkers/expected_actual.go
+++ b/internal/checkers/expected_actual.go
@@ -87,7 +87,7 @@ func (checker ExpectedActual) Check(pass *analysis.Pass, call *CallMeta) *analys
first, second := call.Args[0], call.Args[1]
if checker.isWrongExpectedActualOrder(pass, first, second) {
- return newDiagnostic(checker.Name(), call, "need to reverse actual and expected values", &analysis.SuggestedFix{
+ return newDiagnostic(checker.Name(), call, "need to reverse actual and expected values", analysis.SuggestedFix{
Message: "Reverse actual and expected values",
TextEdits: []analysis.TextEdit{
{
diff --git a/internal/checkers/float_compare.go b/internal/checkers/float_compare.go
index 7436f9c..6bc22cd 100644
--- a/internal/checkers/float_compare.go
+++ b/internal/checkers/float_compare.go
@@ -44,7 +44,7 @@ func (checker FloatCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis
if call.Fn.IsFmt {
format = "use %s.InEpsilonf (or InDeltaf)"
}
- return newDiagnostic(checker.Name(), call, fmt.Sprintf(format, call.SelectorXStr), nil)
+ return newDiagnostic(checker.Name(), call, fmt.Sprintf(format, call.SelectorXStr))
}
return nil
}
diff --git a/internal/checkers/formatter.go b/internal/checkers/formatter.go
index 3401bb0..896b6bf 100644
--- a/internal/checkers/formatter.go
+++ b/internal/checkers/formatter.go
@@ -1,8 +1,6 @@
package checkers
import (
- "fmt"
- "go/ast"
"go/types"
"strconv"
@@ -60,7 +58,7 @@ func (checker Formatter) Check(pass *analysis.Pass, call *CallMeta) (result *ana
}
func (checker Formatter) checkNotFmtAssertion(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic {
- msgAndArgsPos, ok := isPrintfLikeCall(pass, call, call.Fn.Signature)
+ msgAndArgsPos, ok := isPrintfLikeCall(pass, call)
if !ok {
return nil
}
@@ -71,21 +69,15 @@ func (checker Formatter) checkNotFmtAssertion(pass *analysis.Pass, call *CallMet
msgAndArgs := call.ArgsRaw[msgAndArgsPos]
if args, ok := isFmtSprintfCall(pass, msgAndArgs); ok {
if checker.requireFFuncs {
- msg := fmt.Sprintf("remove unnecessary fmt.Sprintf and use %s.%s", call.SelectorXStr, fFunc)
- return newDiagnostic(checker.Name(), call, msg,
- newSuggestedFuncReplacement(call, fFunc, analysis.TextEdit{
- Pos: msgAndArgs.Pos(),
- End: msgAndArgs.End(),
- NewText: formatAsCallArgs(pass, args...),
- }),
- )
+ return newRemoveFnAndUseDiagnostic(pass, checker.Name(), call, fFunc,
+ "fmt.Sprintf", msgAndArgs, args...)
}
return newRemoveSprintfDiagnostic(pass, checker.Name(), call, msgAndArgs, args)
}
}
if checker.requireFFuncs {
- return newUseFunctionDiagnostic(checker.Name(), call, fFunc, newSuggestedFuncReplacement(call, fFunc))
+ return newUseFunctionDiagnostic(checker.Name(), call, fFunc)
}
return nil
}
@@ -109,7 +101,7 @@ func (checker Formatter) checkFmtAssertion(pass *analysis.Pass, call *CallMeta)
defer func() { pass.Report = report }()
pass.Report = func(d analysis.Diagnostic) {
- result = newDiagnostic(checker.Name(), call, d.Message, nil)
+ result = newDiagnostic(checker.Name(), call, d.Message)
}
format, err := strconv.Unquote(analysisutil.NodeString(pass.Fset, msg))
@@ -121,21 +113,51 @@ func (checker Formatter) checkFmtAssertion(pass *analysis.Pass, call *CallMeta)
return result
}
-func isPrintfLikeCall(pass *analysis.Pass, call *CallMeta, sig *types.Signature) (int, bool) {
- msgAndArgsPos := getMsgAndArgsPosition(sig)
+func isPrintfLikeCall(pass *analysis.Pass, call *CallMeta) (int, bool) {
+ msgAndArgsPos := getMsgAndArgsPosition(call.Fn.Signature)
if msgAndArgsPos < 0 {
return -1, false
}
- fmtFn := analysisutil.ObjectOf(pass.Pkg, testify.AssertPkgPath, call.Fn.Name+"f")
- if fmtFn == nil {
- // NOTE(a.telyshev): No formatted analogue of assertion.
+ if !assertHasFormattedAnalogue(pass, call) {
return -1, false
}
return msgAndArgsPos, len(call.ArgsRaw) > msgAndArgsPos
}
+func assertHasFormattedAnalogue(pass *analysis.Pass, call *CallMeta) bool {
+ if fn := analysisutil.ObjectOf(pass.Pkg, testify.AssertPkgPath, call.Fn.Name+"f"); fn != nil {
+ return true
+ }
+
+ if fn := analysisutil.ObjectOf(pass.Pkg, testify.RequirePkgPath, call.Fn.Name+"f"); fn != nil {
+ return true
+ }
+
+ recv := call.Fn.Signature.Recv()
+ if recv == nil {
+ return false
+ }
+
+ recvT := recv.Type()
+ if ptr, ok := recv.Type().(*types.Pointer); ok {
+ recvT = ptr.Elem()
+ }
+
+ suite, ok := recvT.(*types.Named)
+ if !ok {
+ return false
+ }
+ for i := 0; i < suite.NumMethods(); i++ {
+ if suite.Method(i).Name() == call.Fn.Name+"f" {
+ return true
+ }
+ }
+
+ return false
+}
+
func getMsgAndArgsPosition(sig *types.Signature) int {
params := sig.Params()
if params.Len() < 1 {
@@ -162,26 +184,3 @@ func getMsgPosition(sig *types.Signature) int {
}
return -1
}
-
-func isFmtSprintfCall(pass *analysis.Pass, expr ast.Expr) ([]ast.Expr, bool) {
- ce, ok := expr.(*ast.CallExpr)
- if !ok {
- return nil, false
- }
-
- se, ok := ce.Fun.(*ast.SelectorExpr)
- if !ok {
- return nil, false
- }
-
- sprintfObj := analysisutil.ObjectOf(pass.Pkg, "fmt", "Sprintf")
- if sprintfObj == nil {
- return nil, false
- }
-
- if !analysisutil.IsObj(pass.TypesInfo, se.Sel, sprintfObj) {
- return nil, false
- }
-
- return ce.Args, true
-}
diff --git a/internal/checkers/go_require.go b/internal/checkers/go_require.go
index 060c960..8b0d399 100644
--- a/internal/checkers/go_require.go
+++ b/internal/checkers/go_require.go
@@ -142,11 +142,11 @@ func (checker GoRequire) Check(pass *analysis.Pass, inspector *inspector.Inspect
if testifyCall != nil {
switch checker.checkCall(testifyCall) {
case goRequireVerdictRequire:
- d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireCallReportFormat, "require"), nil)
+ d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireCallReportFormat, "require"))
diagnostics = append(diagnostics, *d)
case goRequireVerdictAssertFailNow:
- d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireCallReportFormat, testifyCall), nil)
+ d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireCallReportFormat, testifyCall))
diagnostics = append(diagnostics, *d)
case goRequireVerdictNoExit:
@@ -163,7 +163,7 @@ func (checker GoRequire) Check(pass *analysis.Pass, inspector *inspector.Inspect
if v := checker.checkFunc(pass, calledFd, testsDecls, processedFuncs); v != goRequireVerdictNoExit {
caller := analysisutil.NodeString(pass.Fset, ce.Fun)
- d := newDiagnostic(checker.Name(), ce, fmt.Sprintf(goRequireFnReportFormat, caller), nil)
+ d := newDiagnostic(checker.Name(), ce, fmt.Sprintf(goRequireFnReportFormat, caller))
diagnostics = append(diagnostics, *d)
}
}
@@ -198,11 +198,11 @@ func (checker GoRequire) checkHTTPHandlers(pass *analysis.Pass, insp *inspector.
switch checker.checkCall(testifyCall) {
case goRequireVerdictRequire:
- d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireHTTPHandlerReportFormat, "require"), nil)
+ d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireHTTPHandlerReportFormat, "require"))
diagnostics = append(diagnostics, *d)
case goRequireVerdictAssertFailNow:
- d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireHTTPHandlerReportFormat, testifyCall), nil)
+ d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireHTTPHandlerReportFormat, testifyCall))
diagnostics = append(diagnostics, *d)
case goRequireVerdictNoExit:
diff --git a/internal/checkers/helpers_basic_type.go b/internal/checkers/helpers_basic_type.go
index 432a303..9b43e91 100644
--- a/internal/checkers/helpers_basic_type.go
+++ b/internal/checkers/helpers_basic_type.go
@@ -1,10 +1,10 @@
package checkers
import (
- "fmt"
"go/ast"
"go/token"
"go/types"
+ "strconv"
"golang.org/x/tools/go/analysis"
)
@@ -56,9 +56,29 @@ func isTypedIntNumber(e ast.Expr, v int, types ...string) bool {
return false
}
-func isIntNumber(e ast.Expr, v int) bool {
+func isIntNumber(e ast.Expr, rhs int) bool {
+ lhs, ok := isIntBasicLit(e)
+ return ok && (lhs == rhs)
+}
+
+func isNegativeIntNumber(e ast.Expr) bool {
+ v, ok := isIntBasicLit(e)
+ return ok && v < 0
+}
+
+func isPositiveIntNumber(e ast.Expr) bool {
+ v, ok := isIntBasicLit(e)
+ return ok && v > 0
+}
+
+func isEmptyStringLit(e ast.Expr) bool {
+ bl, ok := e.(*ast.BasicLit)
+ return ok && bl.Kind == token.STRING && bl.Value == `""`
+}
+
+func isNotEmptyStringLit(e ast.Expr) bool {
bl, ok := e.(*ast.BasicLit)
- return ok && bl.Kind == token.INT && bl.Value == fmt.Sprintf("%d", v)
+ return ok && bl.Kind == token.STRING && bl.Value != `""`
}
func isBasicLit(e ast.Expr) bool {
@@ -66,9 +86,27 @@ func isBasicLit(e ast.Expr) bool {
return ok
}
-func isIntBasicLit(e ast.Expr) bool {
+func isIntBasicLit(e ast.Expr) (int, bool) {
+ if un, ok := e.(*ast.UnaryExpr); ok {
+ if un.Op == token.SUB {
+ v, ok := isIntBasicLit(un.X)
+ return -1 * v, ok
+ }
+ }
+
bl, ok := e.(*ast.BasicLit)
- return ok && bl.Kind == token.INT
+ if !ok {
+ return 0, false
+ }
+ if bl.Kind != token.INT {
+ return 0, false
+ }
+
+ v, err := strconv.Atoi(bl.Value)
+ if err != nil {
+ return 0, false
+ }
+ return v, true
}
func isUntypedConst(pass *analysis.Pass, e ast.Expr) bool {
@@ -98,12 +136,37 @@ func isUnderlying(pass *analysis.Pass, e ast.Expr, flag types.BasicInfo) bool {
return ok && (bt.Info()&flag > 0)
}
-func isPointer(pass *analysis.Pass, e ast.Expr) bool {
- _, ok := pass.TypesInfo.TypeOf(e).(*types.Pointer)
- return ok
+func isPointer(pass *analysis.Pass, e ast.Expr) (types.Type, bool) {
+ ptr, ok := pass.TypesInfo.TypeOf(e).(*types.Pointer)
+ if !ok {
+ return nil, false
+ }
+ return ptr.Elem(), true
+}
+
+// isByteArray returns true if expression is `[]byte` itself.
+func isByteArray(e ast.Expr) bool {
+ at, ok := e.(*ast.ArrayType)
+ return ok && isIdentWithName("byte", at.Elt)
+}
+
+// hasBytesType returns true if the expression is of `[]byte` type.
+func hasBytesType(pass *analysis.Pass, e ast.Expr) bool {
+ t := pass.TypesInfo.TypeOf(e)
+ if t == nil {
+ return false
+ }
+
+ sl, ok := t.(*types.Slice)
+ if !ok {
+ return false
+ }
+
+ el, ok := sl.Elem().(*types.Basic)
+ return ok && el.Kind() == types.Uint8
}
-// untype returns v from type(v) expression or v itself if there is no type cast.
+// untype returns v from type(v) expression or v itself if there is no type conversion.
func untype(e ast.Expr) ast.Expr {
ce, ok := e.(*ast.CallExpr)
if !ok || len(ce.Args) != 1 {
diff --git a/internal/checkers/helpers_diagnostic.go b/internal/checkers/helpers_diagnostic.go
index 1f5c019..f12d87a 100644
--- a/internal/checkers/helpers_diagnostic.go
+++ b/internal/checkers/helpers_diagnostic.go
@@ -7,11 +7,32 @@ import (
"golang.org/x/tools/go/analysis"
)
+func newRemoveFnAndUseDiagnostic(
+ pass *analysis.Pass,
+ checker string,
+ call *CallMeta,
+ proposedFn string,
+ removedFn string,
+ removedFnPos analysis.Range,
+ removedFnArgs ...ast.Expr,
+) *analysis.Diagnostic {
+ f := proposedFn
+ if call.Fn.IsFmt {
+ f += "f"
+ }
+ msg := fmt.Sprintf("remove unnecessary %s and use %s.%s", removedFn, call.SelectorXStr, f)
+
+ return newDiagnostic(checker, call, msg,
+ newSuggestedFuncRemoving(pass, removedFn, removedFnPos, removedFnArgs...),
+ newSuggestedFuncReplacement(call, proposedFn),
+ )
+}
+
func newUseFunctionDiagnostic(
checker string,
call *CallMeta,
proposedFn string,
- fix *analysis.SuggestedFix,
+ additionalEdits ...analysis.TextEdit,
) *analysis.Diagnostic {
f := proposedFn
if call.Fn.IsFmt {
@@ -19,17 +40,18 @@ func newUseFunctionDiagnostic(
}
msg := fmt.Sprintf("use %s.%s", call.SelectorXStr, f)
- return newDiagnostic(checker, call, msg, fix)
+ return newDiagnostic(checker, call, msg,
+ newSuggestedFuncReplacement(call, proposedFn, additionalEdits...))
}
-func newRemoveSprintfDiagnostic(
+func newRemoveLenDiagnostic(
pass *analysis.Pass,
checker string,
call *CallMeta,
fnPos analysis.Range,
- fnArgs []ast.Expr,
+ fnArg ast.Expr,
) *analysis.Diagnostic {
- return newRemoveFnDiagnostic(pass, checker, call, "fmt.Sprintf", fnPos, fnArgs...)
+ return newRemoveFnDiagnostic(pass, checker, call, "len", fnPos, fnArg)
}
func newRemoveMustCompileDiagnostic(
@@ -42,6 +64,16 @@ func newRemoveMustCompileDiagnostic(
return newRemoveFnDiagnostic(pass, checker, call, "regexp.MustCompile", fnPos, fnArg)
}
+func newRemoveSprintfDiagnostic(
+ pass *analysis.Pass,
+ checker string,
+ call *CallMeta,
+ fnPos analysis.Range,
+ fnArgs []ast.Expr,
+) *analysis.Diagnostic {
+ return newRemoveFnDiagnostic(pass, checker, call, "fmt.Sprintf", fnPos, fnArgs...)
+}
+
func newRemoveFnDiagnostic(
pass *analysis.Pass,
checker string,
@@ -50,23 +82,15 @@ func newRemoveFnDiagnostic(
fnPos analysis.Range,
fnArgs ...ast.Expr,
) *analysis.Diagnostic {
- return newDiagnostic(checker, call, "remove unnecessary "+fnName, &analysis.SuggestedFix{
- Message: fmt.Sprintf("Remove `%s`", fnName),
- TextEdits: []analysis.TextEdit{
- {
- Pos: fnPos.Pos(),
- End: fnPos.End(),
- NewText: formatAsCallArgs(pass, fnArgs...),
- },
- },
- })
+ return newDiagnostic(checker, call, "remove unnecessary "+fnName,
+ newSuggestedFuncRemoving(pass, fnName, fnPos, fnArgs...))
}
func newDiagnostic(
checker string,
rng analysis.Range,
msg string,
- fix *analysis.SuggestedFix,
+ fixes ...analysis.SuggestedFix,
) *analysis.Diagnostic {
d := analysis.Diagnostic{
Pos: rng.Pos(),
@@ -74,21 +98,39 @@ func newDiagnostic(
Category: checker,
Message: checker + ": " + msg,
}
- if fix != nil {
- d.SuggestedFixes = []analysis.SuggestedFix{*fix}
+ if len(fixes) != 0 {
+ d.SuggestedFixes = fixes
}
return &d
}
+func newSuggestedFuncRemoving(
+ pass *analysis.Pass,
+ fnName string,
+ fnPos analysis.Range,
+ fnArgs ...ast.Expr,
+) analysis.SuggestedFix {
+ return analysis.SuggestedFix{
+ Message: fmt.Sprintf("Remove `%s`", fnName),
+ TextEdits: []analysis.TextEdit{
+ {
+ Pos: fnPos.Pos(),
+ End: fnPos.End(),
+ NewText: formatAsCallArgs(pass, fnArgs...),
+ },
+ },
+ }
+}
+
func newSuggestedFuncReplacement(
call *CallMeta,
proposedFn string,
additionalEdits ...analysis.TextEdit,
-) *analysis.SuggestedFix {
+) analysis.SuggestedFix {
if call.Fn.IsFmt {
proposedFn += "f"
}
- return &analysis.SuggestedFix{
+ return analysis.SuggestedFix{
Message: fmt.Sprintf("Replace `%s` with `%s`", call.Fn.Name, proposedFn),
TextEdits: append([]analysis.TextEdit{
{
diff --git a/internal/checkers/helpers_encoded.go b/internal/checkers/helpers_encoded.go
new file mode 100644
index 0000000..35a497a
--- /dev/null
+++ b/internal/checkers/helpers_encoded.go
@@ -0,0 +1,40 @@
+package checkers
+
+import (
+ "go/ast"
+ "go/token"
+ "regexp"
+
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+var (
+ jsonIdentRe = regexp.MustCompile(`json|JSON|Json`)
+ yamlIdentRe = regexp.MustCompile(`yaml|YAML|Yaml|yml|YML|Yml`)
+)
+
+func isJSONStyleExpr(pass *analysis.Pass, e ast.Expr) bool {
+ if isIdentNamedAfterPattern(jsonIdentRe, e) {
+ return true
+ }
+
+ if t, ok := pass.TypesInfo.Types[e]; ok && t.Value != nil {
+ return analysisutil.IsJSONLike(t.Value.String())
+ }
+
+ if bl, ok := e.(*ast.BasicLit); ok {
+ return bl.Kind == token.STRING && analysisutil.IsJSONLike(bl.Value)
+ }
+
+ if args, ok := isFmtSprintfCall(pass, e); ok {
+ return isJSONStyleExpr(pass, args[0])
+ }
+
+ return false
+}
+
+func isYAMLStyleExpr(e ast.Expr) bool {
+ return isIdentNamedAfterPattern(yamlIdentRe, e)
+}
diff --git a/internal/checkers/helpers_format.go b/internal/checkers/helpers_format.go
index 765fce5..d69c428 100644
--- a/internal/checkers/helpers_format.go
+++ b/internal/checkers/helpers_format.go
@@ -3,6 +3,7 @@ package checkers
import (
"bytes"
"go/ast"
+ "strings"
"golang.org/x/tools/go/analysis"
@@ -24,3 +25,37 @@ func formatAsCallArgs(pass *analysis.Pass, args ...ast.Expr) []byte {
}
return buf.Bytes()
}
+
+func formatWithStringCastForBytes(pass *analysis.Pass, e ast.Expr) []byte {
+ if !hasBytesType(pass, e) {
+ return analysisutil.NodeBytes(pass.Fset, e)
+ }
+
+ if se, ok := isBufferBytesCall(pass, e); ok {
+ return []byte(analysisutil.NodeString(pass.Fset, se) + ".String()")
+ }
+ return []byte("string(" + analysisutil.NodeString(pass.Fset, e) + ")")
+}
+
+func isBufferBytesCall(pass *analysis.Pass, e ast.Expr) (ast.Node, bool) {
+ ce, ok := e.(*ast.CallExpr)
+ if !ok {
+ return nil, false
+ }
+
+ se, ok := ce.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return nil, false
+ }
+
+ if !isIdentWithName("Bytes", se.Sel) {
+ return nil, false
+ }
+ if t := pass.TypesInfo.TypeOf(se.X); t != nil {
+ // NOTE(a.telyshev): This is hack, because `bytes` package can be not imported,
+ // and we cannot do "true" comparison with `Buffer` object.
+ return se.X, strings.TrimPrefix(t.String(), "*") == "bytes.Buffer"
+ }
+
+ return nil, false
+}
diff --git a/internal/checkers/helpers_interface.go b/internal/checkers/helpers_interface.go
index b0c0d13..ad39c72 100644
--- a/internal/checkers/helpers_interface.go
+++ b/internal/checkers/helpers_interface.go
@@ -15,8 +15,11 @@ func isEmptyInterface(pass *analysis.Pass, expr ast.Expr) bool {
if !ok {
return false
}
+ return isEmptyInterfaceType(t.Type)
+}
- iface, ok := t.Type.Underlying().(*types.Interface)
+func isEmptyInterfaceType(t types.Type) bool {
+ iface, ok := t.Underlying().(*types.Interface)
return ok && iface.NumMethods() == 0
}
diff --git a/internal/checkers/helpers_pkg_func.go b/internal/checkers/helpers_pkg_func.go
index a81cbc6..daf3093 100644
--- a/internal/checkers/helpers_pkg_func.go
+++ b/internal/checkers/helpers_pkg_func.go
@@ -8,6 +8,18 @@ import (
"github.com/Antonboom/testifylint/internal/analysisutil"
)
+func isFmtSprintfCall(pass *analysis.Pass, e ast.Expr) ([]ast.Expr, bool) {
+ ce, ok := e.(*ast.CallExpr)
+ if !ok {
+ return nil, false
+ }
+ return ce.Args, isPkgFnCall(pass, ce, "fmt", "Sprintf")
+}
+
+func isJSONRawMessageCast(pass *analysis.Pass, ce *ast.CallExpr) bool {
+ return isPkgFnCall(pass, ce, "encoding/json", "RawMessage")
+}
+
func isRegexpMustCompileCall(pass *analysis.Pass, ce *ast.CallExpr) bool {
return isPkgFnCall(pass, ce, "regexp", "MustCompile")
}
@@ -16,16 +28,32 @@ func isStringsContainsCall(pass *analysis.Pass, ce *ast.CallExpr) bool {
return isPkgFnCall(pass, ce, "strings", "Contains")
}
+func isStringsReplaceCall(pass *analysis.Pass, ce *ast.CallExpr) bool {
+ return isPkgFnCall(pass, ce, "strings", "Replace")
+}
+
+func isStringsReplaceAllCall(pass *analysis.Pass, ce *ast.CallExpr) bool {
+ return isPkgFnCall(pass, ce, "strings", "ReplaceAll")
+}
+
+func isStringsTrimCall(pass *analysis.Pass, ce *ast.CallExpr) bool {
+ return isPkgFnCall(pass, ce, "strings", "Trim")
+}
+
+func isStringsTrimSpaceCall(pass *analysis.Pass, ce *ast.CallExpr) bool {
+ return isPkgFnCall(pass, ce, "strings", "TrimSpace")
+}
+
func isPkgFnCall(pass *analysis.Pass, ce *ast.CallExpr, pkg, fn string) bool {
se, ok := ce.Fun.(*ast.SelectorExpr)
if !ok {
return false
}
- errorsIsObj := analysisutil.ObjectOf(pass.Pkg, pkg, fn)
- if errorsIsObj == nil {
+ fnObj := analysisutil.ObjectOf(pass.Pkg, pkg, fn)
+ if fnObj == nil {
return false
}
- return analysisutil.IsObj(pass.TypesInfo, se.Sel, errorsIsObj)
+ return analysisutil.IsObj(pass.TypesInfo, se.Sel, fnObj)
}
diff --git a/internal/checkers/len.go b/internal/checkers/len.go
index 4733056..c240a61 100644
--- a/internal/checkers/len.go
+++ b/internal/checkers/len.go
@@ -31,17 +31,16 @@ func (checker Len) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnost
a, b := call.Args[0], call.Args[1]
if lenArg, expectedLen, ok := xorLenCall(pass, a, b); ok {
- if expectedLen == b && !isIntBasicLit(expectedLen) {
+ if _, ok := isIntBasicLit(expectedLen); (expectedLen == b) && !ok {
// https://github.com/Antonboom/testifylint/issues/9
return nil
}
return newUseFunctionDiagnostic(checker.Name(), call, proposedFn,
- newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{
+ analysis.TextEdit{
Pos: a.Pos(),
End: b.End(),
NewText: formatAsCallArgs(pass, lenArg, expectedLen),
- }),
- )
+ })
}
case "True":
@@ -50,14 +49,16 @@ func (checker Len) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnost
}
expr := call.Args[0]
- if lenArg, expectedLen, ok := isLenEquality(pass, expr); ok && isIntBasicLit(expectedLen) {
+ if lenArg, expectedLen, ok := isLenEquality(pass, expr); ok {
+ if _, ok := isIntBasicLit(expectedLen); !ok {
+ return nil
+ }
return newUseFunctionDiagnostic(checker.Name(), call, proposedFn,
- newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{
+ analysis.TextEdit{
Pos: expr.Pos(),
End: expr.End(),
NewText: formatAsCallArgs(pass, lenArg, expectedLen),
- }),
- )
+ })
}
}
return nil
diff --git a/internal/checkers/negative_postive.go b/internal/checkers/negative_positive.go
similarity index 96%
rename from internal/checkers/negative_postive.go
rename to internal/checkers/negative_positive.go
index 274021f..a61bbdf 100644
--- a/internal/checkers/negative_postive.go
+++ b/internal/checkers/negative_positive.go
@@ -48,12 +48,11 @@ func (checker NegativePositive) checkNegative(pass *analysis.Pass, call *CallMet
newUseNegativeDiagnostic := func(replaceStart, replaceEnd token.Pos, replaceWith ast.Expr) *analysis.Diagnostic {
const proposed = "Negative"
return newUseFunctionDiagnostic(checker.Name(), call, proposed,
- newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{
+ analysis.TextEdit{
Pos: replaceStart,
End: replaceEnd,
NewText: analysisutil.NodeBytes(pass.Fset, replaceWith),
- }),
- )
+ })
}
// NOTE(a.telyshev): We ignore uint-asserts as being no sense for assert.Negative.
@@ -114,12 +113,11 @@ func (checker NegativePositive) checkPositive(pass *analysis.Pass, call *CallMet
newUsePositiveDiagnostic := func(replaceStart, replaceEnd token.Pos, replaceWith ast.Expr) *analysis.Diagnostic {
const proposed = "Positive"
return newUseFunctionDiagnostic(checker.Name(), call, proposed,
- newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{
+ analysis.TextEdit{
Pos: replaceStart,
End: replaceEnd,
NewText: analysisutil.NodeBytes(pass.Fset, replaceWith),
- }),
- )
+ })
}
switch call.Fn.NameFTrimmed {
diff --git a/internal/checkers/nil_compare.go b/internal/checkers/nil_compare.go
index 47c4a73..fc1adb7 100644
--- a/internal/checkers/nil_compare.go
+++ b/internal/checkers/nil_compare.go
@@ -47,10 +47,9 @@ func (checker NilCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis.D
}
return newUseFunctionDiagnostic(checker.Name(), call, proposedFn,
- newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{
+ analysis.TextEdit{
Pos: call.Args[0].Pos(),
End: call.Args[1].End(),
NewText: analysisutil.NodeBytes(pass.Fset, survivingArg),
- }),
- )
+ })
}
diff --git a/internal/checkers/require_error.go b/internal/checkers/require_error.go
index 2fa8452..e4e30aa 100644
--- a/internal/checkers/require_error.go
+++ b/internal/checkers/require_error.go
@@ -134,7 +134,7 @@ func (checker RequireError) Check(pass *analysis.Pass, inspector *inspector.Insp
}
diagnostics = append(diagnostics,
- *newDiagnostic(checker.Name(), c.testifyCall, requireErrorReport, nil))
+ *newDiagnostic(checker.Name(), c.testifyCall, requireErrorReport))
}
}
diff --git a/internal/checkers/suite_broken_parallel.go b/internal/checkers/suite_broken_parallel.go
index f830fd2..4374c93 100644
--- a/internal/checkers/suite_broken_parallel.go
+++ b/internal/checkers/suite_broken_parallel.go
@@ -68,7 +68,7 @@ func (checker SuiteBrokenParallel) Check(pass *analysis.Pass, insp *inspector.In
}
nextLine := pass.Fset.Position(ce.Pos()).Line + 1
- d := newDiagnostic(checker.Name(), ce, report, &analysis.SuggestedFix{
+ d := newDiagnostic(checker.Name(), ce, report, analysis.SuggestedFix{
Message: fmt.Sprintf("Remove `%s` call", analysisutil.NodeString(pass.Fset, ce)),
TextEdits: []analysis.TextEdit{
{
diff --git a/internal/checkers/suite_dont_use_pkg.go b/internal/checkers/suite_dont_use_pkg.go
index 6150ae7..4fbfbe7 100644
--- a/internal/checkers/suite_dont_use_pkg.go
+++ b/internal/checkers/suite_dont_use_pkg.go
@@ -60,7 +60,7 @@ func (checker SuiteDontUsePkg) Check(pass *analysis.Pass, call *CallMeta) *analy
}
msg := fmt.Sprintf("use %s.%s", newSelector, call.Fn.Name)
- return newDiagnostic(checker.Name(), call, msg, &analysis.SuggestedFix{
+ return newDiagnostic(checker.Name(), call, msg, analysis.SuggestedFix{
Message: fmt.Sprintf("Replace `%s` with `%s`", call.SelectorXStr, newSelector),
TextEdits: []analysis.TextEdit{
// Replace package function with suite method.
diff --git a/internal/checkers/suite_extra_assert_call.go b/internal/checkers/suite_extra_assert_call.go
index 9adfe51..fdea324 100644
--- a/internal/checkers/suite_extra_assert_call.go
+++ b/internal/checkers/suite_extra_assert_call.go
@@ -61,7 +61,7 @@ func (checker SuiteExtraAssertCall) Check(pass *analysis.Pass, call *CallMeta) *
}
msg := fmt.Sprintf("use an explicit %s.Assert().%s", analysisutil.NodeString(pass.Fset, x), call.Fn.Name)
- return newDiagnostic(checker.Name(), call, msg, &analysis.SuggestedFix{
+ return newDiagnostic(checker.Name(), call, msg, analysis.SuggestedFix{
Message: "Add `Assert()` call",
TextEdits: []analysis.TextEdit{{
Pos: x.End(),
@@ -85,7 +85,7 @@ func (checker SuiteExtraAssertCall) Check(pass *analysis.Pass, call *CallMeta) *
}
msg := fmt.Sprintf("need to simplify the assertion to %s.%s", analysisutil.NodeString(pass.Fset, se.X), call.Fn.Name)
- return newDiagnostic(checker.Name(), call, msg, &analysis.SuggestedFix{
+ return newDiagnostic(checker.Name(), call, msg, analysis.SuggestedFix{
Message: "Remove `Assert()` call",
TextEdits: []analysis.TextEdit{{
Pos: se.Sel.Pos(),
diff --git a/internal/checkers/suite_subtest_run.go b/internal/checkers/suite_subtest_run.go
index 67d9c25..525d5ff 100644
--- a/internal/checkers/suite_subtest_run.go
+++ b/internal/checkers/suite_subtest_run.go
@@ -53,7 +53,7 @@ func (checker SuiteSubtestRun) Check(pass *analysis.Pass, insp *inspector.Inspec
if implementsTestifySuite(pass, tCallSel.X) && implementsTestingT(pass, tCall) {
msg := fmt.Sprintf("use %s.Run to run subtest", analysisutil.NodeString(pass.Fset, tCallSel.X))
- diagnostics = append(diagnostics, *newDiagnostic(checker.Name(), ce, msg, nil))
+ diagnostics = append(diagnostics, *newDiagnostic(checker.Name(), ce, msg))
}
})
return diagnostics
diff --git a/internal/checkers/suite_thelper.go b/internal/checkers/suite_thelper.go
index 5945529..a229e43 100644
--- a/internal/checkers/suite_thelper.go
+++ b/internal/checkers/suite_thelper.go
@@ -51,7 +51,7 @@ func (checker SuiteTHelper) Check(pass *analysis.Pass, inspector *inspector.Insp
}
msg := fmt.Sprintf("suite helper method must start with " + helperCallStr)
- d := newDiagnostic(checker.Name(), fd, msg, &analysis.SuggestedFix{
+ d := newDiagnostic(checker.Name(), fd, msg, analysis.SuggestedFix{
Message: fmt.Sprintf("Insert `%s`", helperCallStr),
TextEdits: []analysis.TextEdit{
{
diff --git a/internal/checkers/useless_assert.go b/internal/checkers/useless_assert.go
index 6f206d0..045706e 100644
--- a/internal/checkers/useless_assert.go
+++ b/internal/checkers/useless_assert.go
@@ -10,15 +10,40 @@ import (
// UselessAssert detects useless asserts like
//
-// 1) Asserting of the same variable
-//
+// assert.Contains(t, tt.value, tt.value)
+// assert.ElementsMatch(t, tt.value, tt.value)
// assert.Equal(t, tt.value, tt.value)
-// assert.ElementsMatch(t, users, users)
+// assert.EqualExportedValues(t, tt.value, tt.value)
// ...
+//
// assert.True(t, num > num)
+// assert.True(t, num < num)
+// assert.True(t, num >= num)
+// assert.True(t, num <= num)
+// assert.True(t, num == num)
+// assert.True(t, num != num)
+//
+// assert.False(t, num > num)
+// assert.False(t, num < num)
+// assert.False(t, num >= num)
+// assert.False(t, num <= num)
// assert.False(t, num == num)
+// assert.False(t, num != num)
//
-// 2) Open for contribution...
+// assert.Empty(t, "")
+// assert.False(t, false)
+// assert.Implements(t, (*any)(nil), new(Conn))
+// assert.Negative(t, -42)
+// assert.Nil(t, nil)
+// assert.NoError(t, nil)
+// assert.NotEmpty(t, "value")
+// assert.NotZero(t, 42)
+// assert.NotZero(t, "value")
+// assert.Positive(t, 42)
+// assert.True(t, true)
+// assert.Zero(t, 0)
+// assert.Zero(t, "")
+// assert.Zero(t, nil)
type UselessAssert struct{}
// NewUselessAssert constructs UselessAssert checker.
@@ -26,6 +51,58 @@ func NewUselessAssert() UselessAssert { return UselessAssert{} }
func (UselessAssert) Name() string { return "useless-assert" }
func (checker UselessAssert) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic {
+ if d := checker.checkSameVars(pass, call); d != nil {
+ return d
+ }
+
+ var isMeaningless bool
+ switch call.Fn.NameFTrimmed {
+ case "Empty":
+ isMeaningless = (len(call.Args) >= 1) && isEmptyStringLit(call.Args[0])
+
+ case "False":
+ isMeaningless = (len(call.Args) >= 1) && isUntypedFalse(pass, call.Args[0])
+
+ case "Implements":
+ if len(call.Args) < 2 {
+ return nil
+ }
+
+ elem, ok := isPointer(pass, call.Args[0])
+ isMeaningless = ok && isEmptyInterfaceType(elem)
+
+ case "Negative":
+ isMeaningless = (len(call.Args) >= 1) && isNegativeIntNumber(call.Args[0])
+
+ case "Nil", "NoError":
+ isMeaningless = (len(call.Args) >= 1) && isNil(call.Args[0])
+
+ case "NotEmpty":
+ isMeaningless = (len(call.Args) >= 1) && isNotEmptyStringLit(call.Args[0])
+
+ case "NotZero":
+ isMeaningless = (len(call.Args) >= 1) &&
+ (isNotEmptyStringLit(call.Args[0]) ||
+ isNegativeIntNumber(call.Args[0]) || isPositiveIntNumber(call.Args[0]))
+
+ case "Positive":
+ isMeaningless = (len(call.Args) >= 1) && isPositiveIntNumber(call.Args[0])
+
+ case "True":
+ isMeaningless = (len(call.Args) >= 1) && isUntypedTrue(pass, call.Args[0])
+
+ case "Zero":
+ isMeaningless = (len(call.Args) >= 1) &&
+ (isZero(call.Args[0]) || isEmptyStringLit(call.Args[0]) || isNil(call.Args[0]))
+ }
+
+ if isMeaningless {
+ return newDiagnostic(checker.Name(), call, "meaningless assertion")
+ }
+ return nil
+}
+
+func (checker UselessAssert) checkSameVars(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic {
var first, second ast.Node
switch call.Fn.NameFTrimmed {
@@ -82,7 +159,7 @@ func (checker UselessAssert) Check(pass *analysis.Pass, call *CallMeta) *analysi
}
if analysisutil.NodeString(pass.Fset, first) == analysisutil.NodeString(pass.Fset, second) {
- return newDiagnostic(checker.Name(), call, "asserting of the same variable", nil)
+ return newDiagnostic(checker.Name(), call, "asserting of the same variable")
}
return nil
}
diff --git a/internal/testgen/gen_empty.go b/internal/testgen/gen_empty.go
index 0d1b89e..21e115f 100644
--- a/internal/testgen/gen_empty.go
+++ b/internal/testgen/gen_empty.go
@@ -15,8 +15,9 @@ func (EmptyTestsGenerator) Checker() checkers.Checker {
func (g EmptyTestsGenerator) TemplateData() any {
var (
- checker = g.Checker().Name()
- report = checker + ": use %s.%s"
+ checker = g.Checker().Name()
+ report = checker + ": use %s.%s"
+ reportRemoveLen = checker + ": remove unnecessary len"
)
type test struct {
@@ -60,7 +61,9 @@ func (g EmptyTestsGenerator) TemplateData() any {
{Fn: "Less", Argsf: "len(elems), 1", ReportMsgf: report, ProposedFn: "Empty", ProposedArgsf: "elems"},
{Fn: "Greater", Argsf: "1, len(elems)", ReportMsgf: report, ProposedFn: "Empty", ProposedArgsf: "elems"},
{Fn: "Zero", Argsf: "len(elems)", ReportMsgf: report, ProposedFn: "Empty", ProposedArgsf: "elems"},
- {Fn: "Empty", Argsf: "len(elems)", ReportMsgf: report, ProposedFn: "Empty", ProposedArgsf: "elems"},
+ {Fn: "Zero", Argsf: `len([]string{"e"})`, ReportMsgf: report, ProposedFn: "Empty", ProposedArgsf: `[]string{"e"}`},
+ {Fn: "Empty", Argsf: "len(elems)", ReportMsgf: reportRemoveLen, ProposedArgsf: "elems"},
+ {Fn: "Empty", Argsf: `len([]string{"e"})`, ReportMsgf: reportRemoveLen, ProposedArgsf: `[]string{"e"}`},
// Bullshit, but supported by the checker:
// n < 0, n <= 0
@@ -88,7 +91,9 @@ func (g EmptyTestsGenerator) TemplateData() any {
{Fn: "Greater", Argsf: "len(elems), 0", ReportMsgf: report, ProposedFn: "NotEmpty", ProposedArgsf: "elems"},
{Fn: "Positive", Argsf: "len(elems)", ReportMsgf: report, ProposedFn: "NotEmpty", ProposedArgsf: "elems"},
{Fn: "NotZero", Argsf: "len(elems)", ReportMsgf: report, ProposedFn: "NotEmpty", ProposedArgsf: "elems"},
- {Fn: "NotEmpty", Argsf: "len(elems)", ReportMsgf: report, ProposedFn: "NotEmpty", ProposedArgsf: "elems"},
+ {Fn: "NotZero", Argsf: `len([]string{"e"})`, ReportMsgf: report, ProposedFn: "NotEmpty", ProposedArgsf: `[]string{"e"}`},
+ {Fn: "NotEmpty", Argsf: "len(elems)", ReportMsgf: reportRemoveLen, ProposedArgsf: "elems"},
+ {Fn: "NotEmpty", Argsf: `len([]string{"e"})`, ReportMsgf: reportRemoveLen, ProposedArgsf: `[]string{"e"}`},
},
ValidAssertions: []Assertion{
{Fn: "NotEmpty", Argsf: "elems"},
diff --git a/internal/testgen/gen_encoded_compare.go b/internal/testgen/gen_encoded_compare.go
new file mode 100644
index 0000000..f0e2276
--- /dev/null
+++ b/internal/testgen/gen_encoded_compare.go
@@ -0,0 +1,340 @@
+package main
+
+import (
+ "strings"
+ "text/template"
+
+ "github.com/Antonboom/testifylint/internal/checkers"
+)
+
+type EncodedCompareTestsGenerator struct{}
+
+func (EncodedCompareTestsGenerator) Checker() checkers.Checker {
+ return checkers.NewEncodedCompare()
+}
+
+func (g EncodedCompareTestsGenerator) TemplateData() any {
+ var (
+ checker = g.Checker().Name()
+ report = checker + ": use %s.%s"
+ )
+
+ const multiLineJSON = `{
+ "id": 123,
+ "method": "get_prop",
+ "params": ["power","sat"]
+}
+`
+ multiLineCase := "assert.Equal(t, ` // want \"encoded-compare: use assert\\.JSONEq\"\n" + multiLineJSON + "`, raw)"
+ multiLineCase += "\nassert.Equal(t, ` // want \"encoded-compare: use assert\\.JSONEq\"\n" + multiLineJSON +
+ "`" + `, raw, "msg with args %d %s", 42, "42")`
+
+ const multiLineYAML = `
+kind: Kustomization
+apiVersion: kustomize.config.k8s.io/v1beta1
+images:
+ - name: foo
+ newName: bar
+ - name: bar
+ newName: baz
+ newTag: "123"
+`
+
+ return struct {
+ CheckerName CheckerName
+ InvalidAssertions []Assertion
+ MultiLineJSONCase string
+ ValidAssertions []Assertion
+ IgnoredAssertions []Assertion
+ }{
+ CheckerName: CheckerName(checker),
+ InvalidAssertions: []Assertion{
+ // Raw strings cases.
+ {
+ Fn: "Equal", Argsf: "`{\"name\":\"name\",\"value\":1000}`, respBody",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: "expBody, respBody",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: "`{\"status\":404,\"message\":\"abc\"}`, string(respBytes)",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: "`{\"message\":\"success\"}`, w.Body.String()",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: `"{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", string(w.Body.Bytes())`,
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: `"{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", w.Body.String()`,
+ },
+ {
+ Fn: "Equal", Argsf: `"{\n\t\"msg\": \"hello world\"\n}", respBody`,
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: "fmt.Sprintf(`{\"value\":\"%s\",\"valuePtr\":\"%s\"}`, hexString, hexString), string(respBytes)",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+
+ // Variable name cases.
+ {
+ Fn: "Equal", Argsf: "`[{\"@id\":\"a\",\"b\":[{\"@id\":\"c\"}]}]`, toJSON",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: `"{\"FirstName\":\"john\",\"LastName\":\"doe\",\"Age\":26,\"Height\":182.88}", string(resJson)`,
+ ProposedArgsf: `"{\"FirstName\":\"john\",\"LastName\":\"doe\",\"Age\":26,\"Height\":182.88}", resJson`,
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: "expJSON, resultJSON",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: "jsonb, respBody",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: "respBody, jsonb",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: "expJSON, resJson",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+
+ {
+ Fn: "Equal", Argsf: "expectedYAML, conf",
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ },
+ {
+ Fn: "Equal", Argsf: "expYaml, conf",
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ },
+ {
+ Fn: "Equal", Argsf: "ymlResult, conf",
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ },
+ {
+ Fn: "Equal", Argsf: "yamlResult, conf",
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ },
+ {
+ Fn: "Equal", Argsf: "expYML, conf",
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ },
+ {
+ Fn: "Equal", Argsf: "conf, expectedYAML",
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ },
+ {
+ Fn: "Equal", Argsf: "outputYaml, string(output.Bytes())",
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ ProposedArgsf: "outputYaml, output.String()",
+ },
+
+ // Type conversion cases.
+ {
+ Fn: "Equal", Argsf: "json.RawMessage(`{\"uuid\": \"b65b1a22-db6d-4f5a-9b3d-7302368a82e6\"}`), batch.ParentSummary()",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "`{\"uuid\": \"b65b1a22-db6d-4f5a-9b3d-7302368a82e6\"}`, string(batch.ParentSummary())",
+ },
+ {
+ Fn: "Equal", Argsf: "res[0].Data, json.RawMessage([]byte(`{\"name\":\"new\"}`))",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "string(res[0].Data), `{\"name\":\"new\"}`",
+ },
+ {
+ Fn: "Equal", Argsf: "json.RawMessage(raw), json.RawMessage(resultJSONBytes)",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "raw, string(resultJSONBytes)",
+ },
+ {
+ Fn: "Equal", Argsf: "json.RawMessage(raw), raw",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "raw, raw",
+ },
+ {
+ Fn: "Equal", Argsf: `json.RawMessage("{}"), respBody`,
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: `"{}", respBody`,
+ },
+ {
+ Fn: "Equal", Argsf: `respBody, json.RawMessage("null")`,
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: `respBody, "null"`,
+ },
+ {
+ Fn: "Equal", Argsf: "json.RawMessage(`[\"more\",\"raw\",\"things\"]`), resultJSONBytes",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "`[\"more\",\"raw\",\"things\"]`, string(resultJSONBytes)",
+ },
+
+ {
+ Fn: "Equal", Argsf: `"{}", string(resultJSONBytes)`,
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Equal", Argsf: "[]byte(expJSON), resultJSONBytes",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "expJSON, string(resultJSONBytes)",
+ },
+ {
+ Fn: "Equal", Argsf: "[]byte(expYaml), respBytes",
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ ProposedArgsf: "expYaml, string(respBytes)",
+ },
+
+ // Replace/Trim cases.
+ {
+ Fn: "Equal", Argsf: `expJSON, strings.Trim(string(resultJSONBytes), "\n")`,
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "expJSON, string(resultJSONBytes)",
+ },
+ {
+ Fn: "Equal", Argsf: `raw, strings.Replace(jsonb, "\n", "", -1)`,
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "raw, jsonb",
+ },
+ {
+ Fn: "Equal", Argsf: "`{\"status\":\"healthy\",\"message\":\"\",\"peer_count\":1}`," +
+ " strings.ReplaceAll(string(respBytes), \" \", \"\")",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "`{\"status\":\"healthy\",\"message\":\"\",\"peer_count\":1}`, string(respBytes)",
+ },
+ {
+ Fn: "Equal", Argsf: "`{\"foo\":\"bar\"}`, strings.TrimSpace(w.Body.String())",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "`{\"foo\":\"bar\"}`, w.Body.String()",
+ },
+ {
+ Fn: "Equal", Argsf: "`{\"bar\":\"foo\"}`, strings.TrimSpace(string(w.Body.Bytes()))",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ ProposedArgsf: "`{\"bar\":\"foo\"}`, w.Body.String()",
+ },
+ {
+ Fn: "Equal", Argsf: `strings.TrimSpace(strings.ReplaceAll(expYaml, "\t", " ")), strings.TrimSpace(string(respBytes))`,
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ ProposedArgsf: "expYaml, string(respBytes)",
+ },
+
+ // Other Equal* cases.
+ {
+ Fn: "EqualValues", Argsf: "expJSON, resJson",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "Exactly", Argsf: "expJSON, resJson",
+ ReportMsgf: report, ProposedFn: "JSONEq",
+ },
+ {
+ Fn: "EqualValues", Argsf: "expYaml, conf",
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ },
+ {
+ Fn: "Exactly", Argsf: "expYaml, conf",
+ ReportMsgf: report, ProposedFn: "YAMLEq",
+ },
+ },
+ MultiLineJSONCase: multiLineCase,
+ ValidAssertions: []Assertion{
+ {Fn: "JSONEq", Argsf: "`{\"name\":\"name\",\"value\":1000}`, respBody"},
+ {Fn: "JSONEq", Argsf: "expJSON, resultJSON"},
+ {Fn: "JSONEq", Argsf: "`{\"foo\":\"bar\"}`, `{\"foo\":\"bar\"}`"},
+ {Fn: "JSONEq", Argsf: "`{\"message\":\"success\"}`, w.Body.String()"},
+ {Fn: "JSONEq", Argsf: "fmt.Sprintf(`{\"value\":\"%s\"}`, hexString), resJson"},
+ {Fn: "JSONEq", Argsf: `"{\n \"first\": \"Tobi\",\n \"last\": \"Ferret\"\n}", w.Body.String()`},
+
+ {Fn: "YAMLEq", Argsf: "expYaml, conf"},
+ },
+ IgnoredAssertions: []Assertion{
+ {Fn: "Equal", Argsf: `"{{ .StepName }}", "use", "command name incorrect"`},
+ {Fn: "Equal", Argsf: "json.RawMessage{}, respBody"},
+ {Fn: "Equal", Argsf: "json.RawMessage(nil), respBody"},
+
+ {Fn: "Equal", Argsf: "raw, raw"},
+ {Fn: "EqualValues", Argsf: "raw, raw"},
+ {Fn: "Exactly", Argsf: "raw, raw"},
+ {Fn: "JSONEq", Argsf: "raw, raw"},
+
+ {Fn: "Equal", Argsf: "string(respBytes), raw"},
+ {Fn: "EqualValues", Argsf: "raw, string(respBytes)"},
+ {Fn: "Exactly", Argsf: "string(respBytes), raw"},
+ {Fn: "JSONEq", Argsf: "raw, string(respBytes)"},
+
+ {Fn: "NotEqual", Argsf: "raw, resultJSON"},
+ {Fn: "NotEqualValues", Argsf: "resultJSON, resultJSON"},
+
+ {Fn: "YAMLEq", Argsf: "`" + multiLineYAML + "`, conf"}, // Not supported.
+ {Fn: "YAMLEq", Argsf: `"kind: Kustomization", "kind: Kustomization"`}, // Not supported.
+ {Fn: "YAMLEq", Argsf: "raw, conf"},
+ {Fn: "YAMLEq", Argsf: "raw, string(respBytes)"},
+ },
+ }
+}
+
+func (EncodedCompareTestsGenerator) ErroredTemplate() Executor {
+ return template.Must(template.New("EncodedCompareTestsGenerator.ErroredTemplate").
+ Funcs(fm).
+ Parse(encodedCompareTestTmpl))
+}
+
+func (EncodedCompareTestsGenerator) GoldenTemplate() Executor {
+ return template.Must(template.New("EncodedCompareTestsGenerator.GoldenTemplate").
+ Funcs(fm).
+ Parse(strings.ReplaceAll(encodedCompareTestTmpl, "NewAssertionExpander", "NewAssertionExpander.AsGolden")))
+}
+
+const encodedCompareTestTmpl = header + `
+
+package {{ .CheckerName.AsPkgName }}
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func {{ .CheckerName.AsTestName }}(t *testing.T) {
+ var respBody, raw, hexString, toJSON, expJSON, resultJSON, jsonb, resJson string
+ var conf, expectedYAML, expYaml, ymlResult, yamlResult, expYML, outputYaml string
+ var respBytes, resultJSONBytes []byte
+ w := httptest.NewRecorder()
+ var batch interface { ParentSummary() []byte }
+ var res [1]struct{ Data []byte }
+ var output bytes.Buffer
+
+ const expBody = ` + "`{\"status\":\"healthy\",\"message\":\"\",\"peer_count\":1}`" + `
+
+ // Invalid.
+ {
+ {{- range $ai, $assrn := $.InvalidAssertions }}
+ {{ NewAssertionExpander.Expand $assrn "assert" "t" nil }}
+ {{- end }}
+ {{ .MultiLineJSONCase }}
+ }
+
+ // Valid.
+ {
+ {{- range $ai, $assrn := $.ValidAssertions }}
+ {{ NewAssertionExpander.Expand $assrn "assert" "t" nil }}
+ {{- end }}
+ }
+
+ // Ignored.
+ {
+ {{- range $ai, $assrn := $.IgnoredAssertions }}
+ {{ NewAssertionExpander.Expand $assrn "assert" "t" nil }}
+ {{- end }}
+ }
+}
+`
diff --git a/internal/testgen/gen_useless_assert.go b/internal/testgen/gen_useless_assert.go
index 5394c63..fc27f93 100644
--- a/internal/testgen/gen_useless_assert.go
+++ b/internal/testgen/gen_useless_assert.go
@@ -16,6 +16,7 @@ func (g UselessAssertTestsGenerator) TemplateData() any {
var (
checker = g.Checker().Name()
sameVarReport = checker + ": asserting of the same variable"
+ defaultReport = checker + ": meaningless assertion"
)
var twoSideAssertions []Assertion
@@ -86,6 +87,21 @@ func (g UselessAssertTestsGenerator) TemplateData() any {
{Fn: "Equal", Argsf: "tc.A(), tc.A()", ReportMsgf: sameVarReport},
{Fn: "Equal", Argsf: "testCase{}.A().B().C(), testCase{}.A().B().C()", ReportMsgf: sameVarReport},
{Fn: "IsType", Argsf: "(*testCase)(nil), (*testCase)(nil)", ReportMsgf: sameVarReport},
+
+ {Fn: "Empty", Argsf: `""`, ReportMsgf: defaultReport},
+ {Fn: "False", Argsf: "false", ReportMsgf: defaultReport},
+ {Fn: "Implements", Argsf: "(*any)(nil), new(testing.T)", ReportMsgf: defaultReport},
+ {Fn: "Negative", Argsf: "-42", ReportMsgf: defaultReport},
+ {Fn: "Nil", Argsf: "nil", ReportMsgf: defaultReport},
+ {Fn: "NoError", Argsf: "nil", ReportMsgf: defaultReport},
+ {Fn: "NotEmpty", Argsf: `"value"`, ReportMsgf: defaultReport},
+ {Fn: "NotZero", Argsf: `42`, ReportMsgf: defaultReport},
+ {Fn: "NotZero", Argsf: `"value"`, ReportMsgf: defaultReport},
+ {Fn: "Positive", Argsf: "42", ReportMsgf: defaultReport},
+ {Fn: "True", Argsf: "true", ReportMsgf: defaultReport},
+ {Fn: "Zero", Argsf: "0", ReportMsgf: defaultReport},
+ {Fn: "Zero", Argsf: `""`, ReportMsgf: defaultReport},
+ {Fn: "Zero", Argsf: "nil", ReportMsgf: defaultReport},
},
InvalidAssertions: twoSideAssertions,
ValidAssertions: []Assertion{
@@ -94,6 +110,19 @@ func (g UselessAssertTestsGenerator) TemplateData() any {
{Fn: "Equal", Argsf: `tc.A(), "tc.A()"`},
{Fn: "Equal", Argsf: "testCase{}.A().B().C(), tc.A().B().C()"},
{Fn: "IsType", Argsf: "tc, testCase{}"},
+
+ {Fn: "Empty", Argsf: "str"},
+ {Fn: "False", Argsf: "b"},
+ {Fn: "Implements", Argsf: "(*testing.TB)(nil), new(testing.T)"},
+ {Fn: "Negative", Argsf: "num"},
+ {Fn: "Nil", Argsf: "new(testCase)"},
+ {Fn: "NoError", Argsf: "err"},
+ {Fn: "NotEmpty", Argsf: "str"},
+ {Fn: "Positive", Argsf: "num"},
+ {Fn: "True", Argsf: "b"},
+ {Fn: "Zero", Argsf: "num"},
+ {Fn: "Zero", Argsf: "str"},
+ {Fn: "Zero", Argsf: "new(testCase)"},
},
}
}
@@ -126,6 +155,7 @@ func {{ .CheckerName.AsTestName }}(t *testing.T) {
var elapsed time.Time
var str string
var num int
+ var b bool
var tc testCase
// Invalid.
diff --git a/internal/testgen/main.go b/internal/testgen/main.go
index c3984b6..5836b6a 100644
--- a/internal/testgen/main.go
+++ b/internal/testgen/main.go
@@ -29,6 +29,7 @@ var checkerTestsGenerators = []CheckerTestsGenerator{
ComparesTestsGenerator{},
ContainsTestsGenerator{},
EmptyTestsGenerator{},
+ EncodedCompareTestsGenerator{},
ErrorNilTestsGenerator{},
ErrorIsAsTestsGenerator{},
ExpectedActualTestsGenerator{},