Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ run:
linters:
default: all
disable:
- nilnil
- tparallel
- gocyclo
- errcheck
- err113
- errorlint
- nilnil
- noinlineerr
- wsl_v5
- funcorder
Expand All @@ -34,23 +34,29 @@ linters:
- varnamelen
- wrapcheck
settings:
gocognit:
min-complexity: 50
funlen:
statements: 65
lines: 120
dupl:
threshold: 100
errcheck:
check-type-assertions: true
check-blank: true
gocyclo:
min-complexity: 20
cyclop:
max-complexity: 15
misspell:
locale: US
unparam:
check-exported: true
cyclop:
max-complexity: 25
exclusions:
generated: lax
rules:
- linters:
- tparallel
- gosec
- dupl
- funlen
Expand Down
4 changes: 2 additions & 2 deletions cmd/jsoncompact/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
)

// The function is a bit lengthy, but I'm not sure if it would be more approachable divided in several functions.
func main() { //nolint
func main() {

Check notice on line 19 in cmd/jsoncompact/main.go

View workflow job for this annotation

GitHub Actions / test (stable)

9 statement(s) on lines 19:35 are not covered by tests.
var (
input, output string
length int
Expand All @@ -40,7 +40,7 @@

input = flag.Arg(0)
if input == "" {
_, _ = fmt.Fprintln(flag.CommandLine.Output(), "Missing input path argument, use `-` for stdin.") //nolint
_, _ = fmt.Fprintln(flag.CommandLine.Output(), "Missing input path argument, use `-` for stdin.")

Check notice on line 43 in cmd/jsoncompact/main.go

View workflow job for this annotation

GitHub Actions / test (stable)

3 statement(s) on lines 42:47 are not covered by tests.
flag.Usage()

return
Expand Down
247 changes: 247 additions & 0 deletions compare.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
package assertjson

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"

"github.com/bool64/shared"
"github.com/swaggest/assertjson/diff"
)

func (c Comparer) varCollected(s string, v interface{}) bool {
if c.Vars != nil && c.Vars.IsVar(s) {
if _, found := c.Vars.Get(s); !found {
if n, ok := v.(json.Number); ok {
v = shared.DecodeJSONNumber(n)
} else if f, ok := v.(float64); ok && f == float64(int64(f)) {
v = int64(f)
}

Check notice on line 22 in compare.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 20:22 are not covered by tests.

Comment on lines +18 to +23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 | Confidence: High

The variable collection logic now handles json.Number and converts float64 to int64 when possible. This changes the type of collected variables from float64 to int64 for integer values, as seen in TestComparer_Equal_vars_scalar where 123.0 becomes int64(123). This could break existing tests that rely on float64 equality comparisons.

c.Vars.Set(s, v)

return true
}
}

return false
}

func (c Comparer) filterDeltas(deltas []diff.Delta, ignoreAdded bool) []diff.Delta {
result := make([]diff.Delta, 0, len(deltas))

for _, delta := range deltas {
switch v := delta.(type) {
case *diff.Modified:
if c.IgnoreDiff == "" && c.Vars == nil {
break
}

if s, ok := v.OldValue.(string); ok {
if s == c.IgnoreDiff { // discarding ignored diff
continue
}

if c.varCollected(s, v.NewValue) {
continue
}
}
case *diff.Object:
v.Deltas = c.filterDeltas(v.Deltas, ignoreAdded)
if len(v.Deltas) == 0 {
continue
}

delta = v
case *diff.Array:
v.Deltas = c.filterDeltas(v.Deltas, ignoreAdded)
if len(v.Deltas) == 0 {
continue
}

delta = v

case *diff.Added:
if ignoreAdded {
continue
}
}

result = append(result, delta)
}

return result
}

type df struct {
deltas []diff.Delta
}

func (df *df) Deltas() []diff.Delta {
return df.deltas
}

func (df *df) Modified() bool {
return len(df.deltas) > 0
}

func (c Comparer) filterExpected(expected []byte) ([]byte, error) {
if c.Vars != nil {
for k, v := range c.Vars.GetAll() {
j, err := json.Marshal(v)
if err != nil {
return nil, fmt.Errorf("failed to marshal var %s: %w", k, err) // Not wrapping to support go1.12.
}

Check notice on line 97 in compare.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 95:97 are not covered by tests.

expected = bytes.ReplaceAll(expected, []byte(`"`+k+`"`), j)
}
}

return expected, nil
}

func (c Comparer) compare(expDecoded, actDecoded interface{}) (diff.Diff, error) {
switch v := expDecoded.(type) {
case []interface{}:
if actArray, ok := actDecoded.([]interface{}); ok {
return diff.New().CompareArrays(v, actArray), nil
}

return nil, errors.New("types mismatch, array expected")

case map[string]interface{}:
if actObject, ok := actDecoded.(map[string]interface{}); ok {
return diff.New().CompareObjects(v, actObject), nil
}

return nil, errors.New("types mismatch, object expected")

default:
if !reflect.DeepEqual(expDecoded, actDecoded) { // scalar value comparison
return nil, fmt.Errorf("values %v and %v are not equal", expDecoded, actDecoded)
}
}

return nil, nil
}

func unmarshal(data []byte, decoded interface{}) error {
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()

return dec.Decode(decoded)
}

func (c Comparer) fail(expected, actual []byte, ignoreAdded bool) error {
var expDecoded, actDecoded interface{}

expected, err := c.filterExpected(expected)
if err != nil {
return err
}

Check notice on line 144 in compare.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 142:144 are not covered by tests.

err = unmarshal(expected, &expDecoded)
if err != nil {
return fmt.Errorf("failed to unmarshal expected:\n%wv", err)
}
Comment on lines +146 to +149
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 | Confidence: High

Error formatting uses incorrect verb %wv which will output literal 'v' after wrapped error. This creates malformed error messages like "failed to unmarshal expected: v". Should use %w without trailing 'v'.

Suggested change
err = unmarshal(expected, &expDecoded)
if err != nil {
return fmt.Errorf("failed to unmarshal expected:\n%wv", err)
}
return fmt.Errorf("failed to unmarshal expected:\n%w", err)


err = unmarshal(actual, &actDecoded)
if err != nil {
return fmt.Errorf("failed to unmarshal actual:\n%wv", err)
}

if s, ok := expDecoded.(string); ok && c.Vars != nil && c.Vars.IsVar(s) {
if c.varCollected(s, actDecoded) {
return nil
}

if v, found := c.Vars.Get(s); found {
expDecoded = v
}

Check notice on line 163 in compare.go

View workflow job for this annotation

GitHub Actions / test (stable)

2 statement(s) are not covered by tests.
}

diffValue, err := c.compare(expDecoded, actDecoded)
if err != nil {
return err
}

if diffValue == nil {
return nil
}

if !diffValue.Modified() {
return nil
}

diffValue = &df{deltas: c.filterDeltas(diffValue.Deltas(), ignoreAdded)}
if !diffValue.Modified() {
return nil
}

diffText, err := diff.NewASCIIFormatter(expDecoded, c.FormatterConfig).Format(diffValue)
if err != nil {
return fmt.Errorf("failed to format diff:\n%wv", err)
}

Check notice on line 187 in compare.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 185:187 are not covered by tests.

diffText = c.reduceDiff(diffText)

return errors.New("not equal:\n" + diffText)
}

func (c Comparer) reduceDiff(diffText string) string {
if c.KeepFullDiff {
return diffText
}

Check notice on line 197 in compare.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 195:197 are not covered by tests.

if c.FullDiffMaxLines == 0 {
c.FullDiffMaxLines = 50
}

if c.DiffSurroundingLines == 0 {
c.DiffSurroundingLines = 5
}

diffRows := strings.Split(diffText, "\n")
if len(diffRows) <= c.FullDiffMaxLines {
return diffText
}

var result []string

prev := 0

for i, r := range diffRows {
if len(r) == 0 {
continue
}

if r[0] == '-' || r[0] == '+' {
start := i - c.DiffSurroundingLines
if start < prev {
start = prev
} else if start > prev {
result = append(result, "...")
}

end := i + c.DiffSurroundingLines
if end >= len(diffRows) {
end = len(diffRows) - 1
}

Check notice on line 232 in compare.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 230:232 are not covered by tests.

prev = end

for k := start; k < end; k++ {
result = append(result, diffRows[k])
}
Comment on lines +236 to +238
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 | Confidence: Medium

The reduceDiff logic uses exclusive end bound (k < end) while the old code used inclusive (k <= end). This might truncate the last line of diff context. The behavior should be verified against the original implementation.

}
}

if prev < len(diffRows)-1 {
result = append(result, "...")
}

return strings.Join(result, "\n")
}
Loading
Loading