diff --git a/.golangci.yml b/.golangci.yml index 148ded9..f58fc15 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,198 +1,201 @@ -run: - timeout: 1m - -linters-settings: - errcheck: - # report about not checking of errors in type assetions: `a := b.(MyStruct)`; - # default is false: such cases aren't reported by default. - check-type-assertions: true - - # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; - # default is false: such cases aren't reported by default. - check-blank: false - - # [deprecated] comma-separated list of pairs of the form pkg:regex - # the regex is used to ignore names within pkg. (default "fmt:.*"). - # see https://github.com/kisielk/errcheck#the-deprecated-method for details - # ignore: fmt:.*,io/ioutil:^Read.* - - # path to a file containing a list of functions to exclude from checking - # see https://github.com/kisielk/errcheck#excluding-functions for details - # exclude: /path/to/file.txt - - funlen: - lines: 50 - statements: 40 - - govet: - # report about shadowed variables - shadow: true - - # enable or disable analyzers by name - # enable: - # - atomicalign - enable-all: true - disable: - - fieldalignment - # disable-all: false - revive: - # minimal confidence for issues, default is 0.8 - min-confidence: 0.8 - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true - goimports: - # put imports beginning with prefix after 3rd-party packages; - # it's a comma-separated list of prefixes - local-prefixes: github.com/kinbiko/bugsnag - gocyclo: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - # This check is set to an unreasonably low number by most developers' - # standards to track the code standard over time - min-complexity: 10 - gocognit: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - # This check is a more useful cyclomatic complexity called cognitive complexity, - # where nested if/for is weighted more, and only one point regardless of - # cases in a switch. - min-complexity: 11 - dupl: - # tokens count to trigger issue, 150 by default - threshold: 100 - goconst: - # minimal length of string constant, 3 by default - min-len: 10 - # minimal occurrences count to trigger, 3 by default - min-occurrences: 3 - - # packages-with-error-messages: - # specify an error message to output when a blacklisted package is used - # github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" - misspell: - # Correct spellings using locale preferences for US or UK. - # Default is to use a neutral variety of English. - # Setting locale to US will correct the British spelling of 'colour' to 'color'. - locale: US - # ignore-words: - # - someword - lll: - # max line length, lines longer will be reported. Default is 120. - # '\t' is counted as 1 character by default, and can be changed with the tab-width option - line-length: 165 - # tab width in spaces. Default to 1. - tab-width: 4 - unused: - # treat code as a program (not a library) and report unused exported identifiers; default is false. - # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find funcs usages. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: false - unparam: - # Inspect exported functions, default is false. Set to true if no external program/library imports your code. - # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find external interfaces. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: true - nakedret: - # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 - # Naked returns can go plop itself - max-func-lines: 0 - prealloc: - # XXX: we don't recommend using this linter before doing performance profiling. - # For most programs usage of prealloc will be a premature optimization. - - # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. - # True by default. - simple: true - range-loops: true # Report preallocation suggestions on range loops, true by default - for-loops: false # Report preallocation suggestions on for loops, false by default - gocritic: - # Which checks should be enabled; can't be combined with 'disabled-checks'; - # See https://go-critic.github.io/overview#checks-overview - # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` - # By default list of stable checks is used. - # enabled-checks: - # - badCond - - # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty - # disabled-checks: - - # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks. - # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". - enabled-tags: - - diagnostic - - style - - performance - - settings: # settings passed to gocritic - captLocal: # must be valid enabled check name - paramsOnly: true - rangeValCopy: - sizeThreshold: 64 - godox: - # report any comments starting with keywords, this is useful for TODO or FIXME comments that - # might be left in the code accidentally and should be resolved before merging - keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting - - TODO - - FIXME - dogsled: - # checks assignments with too many blank identifiers; default is 2 - max-blank-identifiers: 2 - - whitespace: - multi-if: false # Enforces newlines (or comments) after every multi-line if statement - multi-func: false # Enforces newlines (or comments) after every multi-line function signature - +version: "2" linters: + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - canonicalheader + - containedctx + - contextcheck + - copyloopvar + - cyclop + - decorder + - dogsled + - dupl + - durationcheck + - err113 + - errchkjson + - errname + - errorlint + - exhaustive + - exhaustruct_v5 + - exptostd + - fatcontext + - forbidigo + - forcetypeassert + - funlen + - ginkgolinter + - gocheckcompilerdirectives + - gochecknoglobals + - gochecknoinits + - gochecksumtype + - gocognit + - goconst + - gocritic + - gocyclo + - godox + - goheader + - gomoddirectives + - gomodguard_v2 + - goprintffuncname + - gosec + - gosmopolitan + - grouper + - iface + - importas + - inamedparam + - interfacebloat + - intrange + - ireturn + - lll + - loggercheck + - maintidx + - makezero + - mirror + - misspell + - mnd + - musttag + - nakedret + - nestif + - nilerr + - nilnesserr + - nilnil + - noctx + - nolintlint + - nonamedreturns + - nosprintfhostport + - paralleltest + - perfsprint + - prealloc + - predeclared + - promlinter + - protogetter + - reassign + - recvcheck + - revive + - rowserrcheck + - sloglint + - spancheck + - sqlclosecheck + - staticcheck + - tagalign + - tagliatelle + - testifylint + - testpackage + - tparallel + - unconvert + - unparam + - usestdlibvars + - wastedassign + - whitespace + - wrapcheck + - zerologlint disable: - - wsl + - depguard - godot - nlreturn - - - depguard - - - gci # This conflicts with goimports - - varnamelen # This has too many false positives around indexes etc to be useful - presets: - - bugs - - complexity - - format - - performance - - style - - unused - fast: false - + - varnamelen + - wsl + settings: + dogsled: + max-blank-identifiers: 2 + dupl: + threshold: 100 + errcheck: + check-type-assertions: true + check-blank: false + funlen: + lines: 50 + statements: 40 + gocognit: + min-complexity: 11 + goconst: + min-len: 10 + min-occurrences: 3 + gocritic: + enabled-tags: + - diagnostic + - style + - performance + settings: + captLocal: + paramsOnly: true + rangeValCopy: + sizeThreshold: 64 + gocyclo: + min-complexity: 10 + godox: + keywords: + - TODO + - FIXME + govet: + disable: + - fieldalignment + enable-all: true + lll: + line-length: 165 + tab-width: 4 + misspell: + locale: US + nakedret: + max-func-lines: 0 + prealloc: + simple: true + range-loops: true + for-loops: false + unparam: + check-exported: true + whitespace: + multi-if: false + multi-func: false + exclusions: + generated: lax + rules: + - linters: + - cyclop + - dupl + - errcheck + - errchkjson + - exhaustivestruct + - forbidigo + - funlen + - gocognit + - goconst + - gocyclo + - gosmopolitan + - lll + - maintidx + - mnd + - paralleltest + - staticcheck + - testpackage + - varnamelen + path: _test\.go + - linters: + - err113 + path: \.go + paths: + - third_party$ + - builtin$ + - examples$ issues: - # Excluding configuration per-path, per-linter, per-text and per-source - exclude-rules: - # Exclude some linters from running on tests files. - - path: _test\.go - linters: - - cyclop - - dupl - - errcheck - - errchkjson - - exhaustivestruct - - forbidigo - - funlen - - gocognit - - gocyclo - - gomnd - - lll - - stylecheck - - testpackage - - varnamelen - - maintidx - - path: \.go - linters: - - err113 - - # Independently from option `exclude` we use default exclude patterns, - # it can be disabled by this option. To list all - # excluded by default patterns execute `golangci-lint run --help`. - # Default value for this option is true. - exclude-use-default: false - # Maximum issues count per one linter. Set to 0 to disable. Default is 50. max-issues-per-linter: 0 - # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. max-same-issues: 0 +formatters: + enable: + - gofmt + - gofumpt + - goimports + settings: + gofmt: + simplify: true + goimports: + local-prefixes: + - github.com/kinbiko/bugsnag + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/Makefile b/Makefile index dfa429b..75dbff5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -LINTER_VERSION := v1.61.0 +LINTER_VERSION := v2.13.2 .PHONY: check check: lint test @@ -19,6 +19,21 @@ test: coverage: go test -race -v -coverprofile=profile.cov -covermode=atomic ./... +# Download the release tarball directly. The upstream install.sh checksum +# lookup matches both *.tar.gz and *.tar.gz.sbom.json in v2.13.x. bin/linter: Makefile - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./bin $(LINTER_VERSION) - mv ./bin/golangci-lint ./bin/linter + mkdir -p ./bin + tmpdir=$$(mktemp -d) && \ + os=$$(go env GOOS) && \ + arch=$$(go env GOARCH) && \ + ver=$(LINTER_VERSION) && \ + vernum=$${ver#v} && \ + asset="golangci-lint-$${vernum}-$${os}-$${arch}.tar.gz" && \ + curl -sSfL "https://github.com/golangci/golangci-lint/releases/download/$${ver}/$${asset}" -o "$$tmpdir/$${asset}" && \ + curl -sSfL "https://github.com/golangci/golangci-lint/releases/download/$${ver}/golangci-lint-$${vernum}-checksums.txt" -o "$$tmpdir/checksums.txt" && \ + want=$$(grep " $${asset}$$" "$$tmpdir/checksums.txt" | awk '{print $$1}') && \ + got=$$(sha256sum "$$tmpdir/$${asset}" | awk '{print $$1}') && \ + test -n "$$want" && test "$$want" = "$$got" && \ + tar -xzf "$$tmpdir/$${asset}" -C "$$tmpdir" && \ + mv "$$tmpdir"/golangci-lint-*/golangci-lint ./bin/linter && \ + rm -rf "$$tmpdir" diff --git a/array.go b/array.go index f59fddd..cd9d8c5 100644 --- a/array.go +++ b/array.go @@ -7,7 +7,7 @@ import ( ) func (a *Asserter) checkArray(path string, act, exp []interface{}) { - a.tt.Helper() + a.Helper() if len(exp) > 0 && exp[0] == "<>" { a.checkArrayUnordered(path, act, exp[1:]) } else { @@ -17,14 +17,14 @@ func (a *Asserter) checkArray(path string, act, exp []interface{}) { //nolint:gocognit,gocyclo,cyclop // function is actually still readable func (a *Asserter) checkArrayUnordered(path string, act, exp []interface{}) { - a.tt.Helper() + a.Helper() if len(act) != len(exp) { - a.tt.Errorf("length of arrays at '%s' were different. Expected array to be of length %d, but contained %d element(s)", path, len(exp), len(act)) + a.Errorf("length of arrays at '%s' were different. Expected array to be of length %d, but contained %d element(s)", path, len(exp), len(act)) serializedAct, serializedExp := serialize(act), serialize(exp) if len(serializedAct+serializedExp) < maxMsgCharCount { - a.tt.Errorf("actual JSON at '%s' was: %+v, but expected JSON was: %+v, potentially in a different order", path, serializedAct, serializedExp) + a.Errorf("actual JSON at '%s' was: %+v, but expected JSON was: %+v, potentially in a different order", path, serializedAct, serializedExp) } else { - a.tt.Errorf("actual JSON at '%s' was:\n%+v\nbut expected JSON was:\n%+v,\npotentially in a different order", path, serializedAct, serializedExp) + a.Errorf("actual JSON at '%s' was:\n%+v\nbut expected JSON was:\n%+v,\npotentially in a different order", path, serializedAct, serializedExp) } return } @@ -39,9 +39,9 @@ func (a *Asserter) checkArrayUnordered(path string, act, exp []interface{}) { if !found { serializedEl := serialize(actEl) if len(serializedEl) < maxMsgCharCount { - a.tt.Errorf("actual JSON at '%s[%d]' contained an unexpected element: %s", path, i, serializedEl) + a.Errorf("actual JSON at '%s[%d]' contained an unexpected element: %s", path, i, serializedEl) } else { - a.tt.Errorf("actual JSON at '%s[%d]' contained an unexpected element:\n%s", path, i, serializedEl) + a.Errorf("actual JSON at '%s[%d]' contained an unexpected element:\n%s", path, i, serializedEl) } } } @@ -54,23 +54,23 @@ func (a *Asserter) checkArrayUnordered(path string, act, exp []interface{}) { if !found { serializedEl := serialize(expEl) if len(serializedEl) < maxMsgCharCount { - a.tt.Errorf("expected JSON at '%s[%d]': %s was missing from actual payload", path, i, serializedEl) + a.Errorf("expected JSON at '%s[%d]': %s was missing from actual payload", path, i, serializedEl) } else { - a.tt.Errorf("expected JSON at '%s[%d]':\n%s\nwas missing from actual payload", path, i, serializedEl) + a.Errorf("expected JSON at '%s[%d]':\n%s\nwas missing from actual payload", path, i, serializedEl) } } } } func (a *Asserter) checkArrayOrdered(path string, act, exp []interface{}) { - a.tt.Helper() + a.Helper() if len(act) != len(exp) { - a.tt.Errorf("length of arrays at '%s' were different. Expected array to be of length %d, but contained %d element(s)", path, len(exp), len(act)) + a.Errorf("length of arrays at '%s' were different. Expected array to be of length %d, but contained %d element(s)", path, len(exp), len(act)) serializedAct, serializedExp := serialize(act), serialize(exp) if len(serializedAct+serializedExp) < maxMsgCharCount { - a.tt.Errorf("actual JSON at '%s' was: %+v, but expected JSON was: %+v", path, serializedAct, serializedExp) + a.Errorf("actual JSON at '%s' was: %+v, but expected JSON was: %+v", path, serializedAct, serializedExp) } else { - a.tt.Errorf("actual JSON at '%s' was:\n%+v\nbut expected JSON was:\n%+v", path, serializedAct, serializedExp) + a.Errorf("actual JSON at '%s' was:\n%+v\nbut expected JSON was:\n%+v", path, serializedAct, serializedExp) } return } diff --git a/boolean.go b/boolean.go index 2503413..0b3820c 100644 --- a/boolean.go +++ b/boolean.go @@ -13,8 +13,8 @@ func extractBoolean(b string) (bool, error) { } func (a *Asserter) checkBoolean(path string, act, exp bool) { - a.tt.Helper() + a.Helper() if act != exp { - a.tt.Errorf("expected boolean at '%s' to be %v but was %v", path, exp, act) + a.Errorf("expected boolean at '%s' to be %v but was %v", path, exp, act) } } diff --git a/core.go b/core.go index 4eea7a0..1627beb 100644 --- a/core.go +++ b/core.go @@ -11,31 +11,31 @@ const maxMsgCharCount = 50 //nolint:gocyclo,cyclop // function is actually still readable func (a *Asserter) pathassertf(path, act, exp string) { - a.tt.Helper() + a.Helper() if act == exp { return } actType, err := findType(act) if err != nil { - a.tt.Errorf("'actual' JSON is not valid JSON: " + err.Error()) + a.Errorf("'actual' JSON is not valid JSON: " + err.Error()) return } expType, err := findType(exp) if err != nil { - a.tt.Errorf("'expected' JSON is not valid JSON: " + err.Error()) + a.Errorf("'expected' JSON is not valid JSON: " + err.Error()) return } // If we're only caring about the presence of the key, then don't bother checking any further if expPresence, _ := extractString(exp); expPresence == "<>" { if actType == jsonNull { - a.tt.Errorf(`expected the presence of any value at '%s', but was absent`, path) + a.Errorf(`expected the presence of any value at '%s', but was absent`, path) } return } if actType != expType { - a.tt.Errorf("actual JSON (%s) and expected JSON (%s) were of different types at '%s'", actType, expType, path) + a.Errorf("actual JSON (%s) and expected JSON (%s) were of different types at '%s'", actType, expType, path) return } switch actType { //nolint:exhaustive // already know it's valid JSON and not null diff --git a/exports.go b/exports.go index dc18ee5..077e810 100644 --- a/exports.go +++ b/exports.go @@ -124,13 +124,13 @@ The above will verify that "foo", "bar", and "baz" are exactly the elements in the payload, but will ignore the order in which they appear. */ func (a *Asserter) Assertf(actualJSON, expectedJSON string, fmtArgs ...interface{}) { - a.tt.Helper() + a.Helper() a.pathassertf("$", actualJSON, fmt.Sprintf(expectedJSON, fmtArgs...)) } // Assert works like Assertf, but does not accept fmt.Sprintf directives. // See Assert for details. func (a *Asserter) Assert(actualJSON, expectedJSON string) { - a.tt.Helper() + a.Helper() a.pathassertf("$", actualJSON, expectedJSON) } diff --git a/go.mod b/go.mod index d25a27c..0f92424 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/kinbiko/jsonassert -go 1.22 +go 1.27 diff --git a/number.go b/number.go index 7fa1085..49703dd 100644 --- a/number.go +++ b/number.go @@ -12,9 +12,9 @@ const ( ) func (a *Asserter) checkNumber(path string, act, exp float64) { - a.tt.Helper() + a.Helper() if diff := math.Abs(act - exp); diff > minDiff { - a.tt.Errorf("expected number at '%s' to be '%.7f' but was '%.7f'", path, exp, act) + a.Errorf("expected number at '%s' to be '%.7f' but was '%.7f'", path, exp, act) } } diff --git a/object.go b/object.go index f60bfc3..57a8536 100644 --- a/object.go +++ b/object.go @@ -6,15 +6,15 @@ import ( ) func (a *Asserter) checkObject(path string, act, exp map[string]interface{}) { - a.tt.Helper() + a.Helper() if len(act) != len(exp) { - a.tt.Errorf("expected %d keys at '%s' but got %d keys", len(exp), path, len(act)) + a.Errorf("expected %d keys at '%s' but got %d keys", len(exp), path, len(act)) } if unique := difference(act, exp); len(unique) != 0 { - a.tt.Errorf("unexpected object key(s) %+v found at '%s'", serialize(unique), path) + a.Errorf("unexpected object key(s) %+v found at '%s'", serialize(unique), path) } if unique := difference(exp, act); len(unique) != 0 { - a.tt.Errorf("expected object key(s) %+v missing at '%s'", serialize(unique), path) + a.Errorf("expected object key(s) %+v missing at '%s'", serialize(unique), path) } for key := range act { if contains(exp, key) { diff --git a/string.go b/string.go index bb0c524..8129b44 100644 --- a/string.go +++ b/string.go @@ -6,12 +6,12 @@ import ( ) func (a *Asserter) checkString(path, act, exp string) { - a.tt.Helper() + a.Helper() if act != exp { if len(exp+act) < maxMsgCharCount { - a.tt.Errorf("expected string at '%s' to be '%s' but was '%s'", path, exp, act) + a.Errorf("expected string at '%s' to be '%s' but was '%s'", path, exp, act) } else { - a.tt.Errorf("expected string at '%s' to be\n'%s'\nbut was\n'%s'", path, exp, act) + a.Errorf("expected string at '%s' to be\n'%s'\nbut was\n'%s'", path, exp, act) } } }