From 6d590cd4310b6d12fa52ff7c6fff435c09f295ac Mon Sep 17 00:00:00 2001 From: Denis Manente Date: Wed, 23 Nov 2022 17:53:42 +0100 Subject: [PATCH] add option ZeroAsNull --- cmd/yaml-diff/main.go | 4 + yamldiff/diff.go | 5 ++ yamldiff/diff_test.go | 176 ++++++++++++++++++++++++++++++++++++++++++ yamldiff/yaml.go | 7 ++ yamldiff/yaml_test.go | 90 +++++++++++++++++++++ 5 files changed, 282 insertions(+) diff --git a/cmd/yaml-diff/main.go b/cmd/yaml-diff/main.go index f534416..9aa8572 100644 --- a/cmd/yaml-diff/main.go +++ b/cmd/yaml-diff/main.go @@ -12,6 +12,7 @@ import ( func main() { ignoreEmptyFields := flag.Bool("ignore-empty-fields", false, "Ignore empty field") + ignoreZeroFields := flag.Bool("ignore-zero-fields", false, "Ignore zero field") flag.Parse() args := flag.Args() @@ -36,6 +37,9 @@ func main() { if *ignoreEmptyFields { opts = append(opts, yamldiff.EmptyAsNull()) } + if *ignoreZeroFields { + opts = append(opts, yamldiff.ZeroAsNull()) + } fmt.Printf("--- %s\n+++ %s\n\n", file1, file2) for _, diff := range yamldiff.Do(yamls1, yamls2, opts...) { diff --git a/yamldiff/diff.go b/yamldiff/diff.go index 977e8ed..f44a1e5 100644 --- a/yamldiff/diff.go +++ b/yamldiff/diff.go @@ -2,6 +2,7 @@ package yamldiff import ( "fmt" + "reflect" "github.com/goccy/go-yaml" ) @@ -312,6 +313,8 @@ func (r *runner) handlePrimitive(rawA rawType, rawB rawType, level int) *diff { case rawA == missingKey: if r.option.emptyAsNull && (rawB == nil || string(strB) == "{}" || string(strB) == "[]") { result.status = DiffStatusSame + } else if r.option.zeroAsNull && (reflect.ValueOf(rawB).IsValid() && reflect.ValueOf(rawB).IsZero()) { + result.status = DiffStatusSame } else { result.a = nil result.status = DiffStatus1Missing @@ -321,6 +324,8 @@ func (r *runner) handlePrimitive(rawA rawType, rawB rawType, level int) *diff { case rawB == missingKey: if r.option.emptyAsNull && rawA == nil { result.status = DiffStatusSame + } else if r.option.zeroAsNull && (reflect.ValueOf(rawA).IsValid() && reflect.ValueOf(rawA).IsZero()) { + result.status = DiffStatusSame } else { result.b = nil result.status = DiffStatus2Missing diff --git a/yamldiff/diff_test.go b/yamldiff/diff_test.go index b0356d2..f11092a 100644 --- a/yamldiff/diff_test.go +++ b/yamldiff/diff_test.go @@ -497,6 +497,80 @@ func Test_performDiff(t *testing.T) { }, }, }, + "#30": { + "zero string": { + a: rawTypeMap{ + yaml.MapItem{Key: "strA", Value: "foo"}, + yaml.MapItem{Key: "strB", Value: ""}, + yaml.MapItem{Key: "strC", Value: ""}, + }, + b: rawTypeMap{ + yaml.MapItem{Key: "strA", Value: "foo"}, + yaml.MapItem{Key: "strB", Value: ""}, + }, + want: &diff{ + children: &diffChildren{ + m: diffChildrenMap{ + "strA": &diff{ + status: DiffStatusSame, + a: "foo", + b: "foo", + treeLevel: 1, + }, + "strB": &diff{ + status: DiffStatusSame, + a: "", + b: "", + treeLevel: 1, + }, + "strC": &diff{ + status: DiffStatus2Missing, + a: "", + treeLevel: 1, + }, + }, + }, + status: DiffStatusDiff, + }, + }, + "zero int": { + a: rawTypeMap{ + yaml.MapItem{Key: "intA", Value: 5}, + yaml.MapItem{Key: "intB", Value: 0}, + yaml.MapItem{Key: "intC", Value: 0}, + }, + b: rawTypeMap{ + yaml.MapItem{Key: "intA", Value: 5}, + yaml.MapItem{Key: "intB", Value: 0}, + }, + want: &diff{ + children: &diffChildren{ + m: diffChildrenMap{ + "intA": &diff{ + status: DiffStatusSame, + a: 5, + b: 5, + treeLevel: 1, + }, + "intB": &diff{ + status: DiffStatusSame, + a: 0, + b: 0, + treeLevel: 1, + }, + "intC": &diff{ + status: DiffStatus2Missing, + a: 0, + diffCount: 1, + treeLevel: 1, + }, + }, + }, + diffCount: 1, + status: DiffStatusDiff, + }, + }, + }, } for n, tt := range tests { tt := tt @@ -612,3 +686,105 @@ func Test_performDiff_emptyAsNull(t *testing.T) { }) } } + +func Test_performDiff_zeroAsNull(t *testing.T) { + tests := map[string]map[string]struct { + a rawType + b rawType + want *diff + }{ + "#30": { + "zero string": { + a: rawTypeMap{ + yaml.MapItem{Key: "strA", Value: "foo"}, + yaml.MapItem{Key: "strB", Value: ""}, + yaml.MapItem{Key: "strC", Value: ""}, + }, + b: rawTypeMap{ + yaml.MapItem{Key: "strA", Value: "foo"}, + yaml.MapItem{Key: "strB", Value: ""}, + }, + want: &diff{ + children: &diffChildren{ + m: diffChildrenMap{ + "strA": &diff{ + status: DiffStatusSame, + a: "foo", + b: "foo", + treeLevel: 1, + }, + "strB": &diff{ + status: DiffStatusSame, + a: "", + b: "", + treeLevel: 1, + }, + "strC": &diff{ + status: DiffStatusSame, + a: "", + b: missingKey, + treeLevel: 1, + }, + }, + }, + status: DiffStatusSame, + }, + }, + "zero int": { + a: rawTypeMap{ + yaml.MapItem{Key: "intA", Value: 5}, + yaml.MapItem{Key: "intB", Value: 0}, + yaml.MapItem{Key: "intC", Value: 0}, + }, + b: rawTypeMap{ + yaml.MapItem{Key: "intA", Value: 5}, + yaml.MapItem{Key: "intB", Value: 0}, + }, + want: &diff{ + children: &diffChildren{ + m: diffChildrenMap{ + "intA": &diff{ + status: DiffStatusSame, + a: 5, + b: 5, + treeLevel: 1, + }, + "intB": &diff{ + status: DiffStatusSame, + a: 0, + b: 0, + treeLevel: 1, + }, + "intC": &diff{ + status: DiffStatusSame, + a: 0, + b: missingKey, + treeLevel: 1, + }, + }, + }, + status: DiffStatusSame, + }, + }, + }, + } + for n, tt := range tests { + tt := tt + t.Run(n, func(t *testing.T) { + // t.Parallel() + + for n, tc := range tt { + tc := tc + t.Run(n, func(t *testing.T) { + // t.Parallel() + + got := (&runner{option: doOptions{zeroAsNull: true}}).performDiff(tc.a, tc.b, 0) + + tc.want.a = tc.a + tc.want.b = tc.b + assert.Equal(t, tc.want, got) + }) + } + }) + } +} diff --git a/yamldiff/yaml.go b/yamldiff/yaml.go index e8e09b4..dfe80f2 100644 --- a/yamldiff/yaml.go +++ b/yamldiff/yaml.go @@ -69,6 +69,7 @@ func (y *YamlDiff) Dump() string { type doOptions struct { emptyAsNull bool + zeroAsNull bool } type DoOptionFunc func(o *doOptions) @@ -79,6 +80,12 @@ func EmptyAsNull() DoOptionFunc { } } +func ZeroAsNull() DoOptionFunc { + return func(o *doOptions) { + o.zeroAsNull = true + } +} + func Do(rawA RawYamlList, rawB RawYamlList, options ...DoOptionFunc) []*YamlDiff { opts := &doOptions{} for _, o := range options { diff --git a/yamldiff/yaml_test.go b/yamldiff/yaml_test.go index 6f60f4c..cb60622 100644 --- a/yamldiff/yaml_test.go +++ b/yamldiff/yaml_test.go @@ -50,6 +50,12 @@ this: is: the: same empty: +--- +someStr: foo +zeroStr: "" +someInt: 5 +zeroInt: 0 +differs: fromA ` yamlB := ` metadata: @@ -95,6 +101,10 @@ baz: this: is: the: same +--- +someStr: foo +someInt: 5 +differs: fromB ` yamlsA, err := Load(yamlA) @@ -119,6 +129,7 @@ this: confirm(resultOfNoOptions(), []DoOptionFunc{}) confirm(resultOfWithEmpty(), []DoOptionFunc{EmptyAsNull()}) + confirm(resultOfWithZero(), []DoOptionFunc{ZeroAsNull()}) } func resultOfNoOptions() string { @@ -170,6 +181,13 @@ func resultOfNoOptions() string { the: "same" - empty: + someStr: "foo" +- zeroStr: "" + someInt: 5 +- zeroInt: 0 +- differs: "fromA" ++ differs: "fromB" + + bar: + - "missing in a.yaml" @@ -228,6 +246,78 @@ func resultOfWithEmpty() string { the: "same" empty: + someStr: "foo" +- zeroStr: "" + someInt: 5 +- zeroInt: 0 +- differs: "fromA" ++ differs: "fromB" + ++ bar: ++ - "missing in a.yaml" + ++ baz: ++ - "missing in a.yaml" + +` +} + +func resultOfWithZero() string { + return ` + apiVersion: "v1" + kind: "Service" + metadata: + name: "my-service" + spec: + selector: + app: "MyApp" + ports: + - + protocol: "TCP" +- port: 80 ++ port: 8080 + targetPort: 9376 + + apiVersion: "apps/v1" + kind: "Deployment" + metadata: + name: "app-deployment" + labels: + app: "MyApp" + spec: +- replicas: 3 ++ replicas: 10 + selector: + matchLabels: + app: "MyApp" + template: + metadata: + labels: + app: "MyApp" + spec: + containers: + - + name: "app" +- image: "my-app:1.0.0" ++ image: "my-app:1.1.0" + ports: + - + containerPort: 9376 + +- foo: "missing-in-b" + + this: + is: + the: "same" +- empty: + + someStr: "foo" + zeroStr: "" + someInt: 5 + zeroInt: 0 +- differs: "fromA" ++ differs: "fromB" + + bar: + - "missing in a.yaml"