From 65c6df4eae789cdd84962e5db04c99c38a7d0d75 Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Tue, 3 Dec 2024 23:44:51 +0530 Subject: [PATCH 01/12] fix: normalise json before check diff Signed-off-by: Ayush Sharma --- jsondiff.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/jsondiff.go b/jsondiff.go index 63ab356..cb98992 100644 --- a/jsondiff.go +++ b/jsondiff.go @@ -1,6 +1,7 @@ package colorisediff import ( + "bytes" "encoding/json" "fmt" "reflect" @@ -118,6 +119,20 @@ func checkKeyInMaps(expectedJSONMap, actualJSONMap []byte, targetKey string) (st // actualJSON: The second JSON object in byte form. // Returns a string representing the differences and an error if any. func calculateJSONDiffs(expectedJSON, actualJSON []byte) (string, error) { + expectedJSON, err := normalizeJSON(expectedJSON) + + if err != nil { + fmt.Println("Error normalizing expected JSON") + return "", err + } + + actualJSON, err = normalizeJSON(actualJSON) + + if err != nil { + fmt.Println("Error normalizing actual JSON") + return "", err + } + // Parse both JSON objects. expectedResult := gjson.ParseBytes(expectedJSON) actualResult := gjson.ParseBytes(actualJSON) @@ -914,3 +929,11 @@ func diffArrayRange(s1, s2 string) ([]int, []int, bool) { // Return the indices of differences for both strings and whether differences were found. return indices1, indices2, diffFound } + +func normalizeJSON(input []byte) ([]byte, error) { + var buffer bytes.Buffer + if err := json.Compact(&buffer, input); err != nil { + return nil, err + } + return buffer.Bytes(), nil +} From c15c95215ef2226b8bce0bedb2c51c3194f089cd Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Wed, 4 Dec 2024 00:55:35 +0530 Subject: [PATCH 02/12] fix: noise handling Signed-off-by: Ayush Sharma --- jsondiff.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/jsondiff.go b/jsondiff.go index cb98992..df898f9 100644 --- a/jsondiff.go +++ b/jsondiff.go @@ -406,6 +406,12 @@ func separateAndColorize(diffStr string, noise map[string][]string) (string, str actualTrimmedLine := nextLine[3:] // Trim the '+ ' prefix from the next line. actualKeyValue := strings.SplitN(actualTrimmedLine, ":", 2) actualKey = strings.TrimSpace(actualKeyValue[0]) + isNoised := checkNoise(actualKey, noise) + + if isNoised { + continue + } + value := strings.TrimSpace(actualKeyValue[1]) var jsonObj map[string]interface{} switch { @@ -422,6 +428,12 @@ func separateAndColorize(diffStr string, noise map[string][]string) (string, str expectTrimmedLine := line[3:] // Trim the '- ' prefix from the current line. expectkeyValue := strings.SplitN(expectTrimmedLine, ":", 2) expectKey = strings.TrimSpace(expectkeyValue[0]) + isNoised := checkNoise(expectKey, noise) + + if isNoised { + continue + } + value := strings.TrimSpace(expectkeyValue[1]) var jsonObj map[string]interface{} switch { @@ -937,3 +949,12 @@ func normalizeJSON(input []byte) ([]byte, error) { } return buffer.Bytes(), nil } + +func checkNoise(key string, noise map[string][]string) bool { + for e := range noise { + if strings.Contains(key, e) { + return true + } + } + return false +} From 5d7e70a23e6130ff48be1a5aed134bda7b4317bc Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Wed, 4 Dec 2024 01:16:15 +0530 Subject: [PATCH 03/12] fix: add tests for noise Signed-off-by: Ayush Sharma --- jsonDiff_test.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/jsonDiff_test.go b/jsonDiff_test.go index 1f06be3..81e65dd 100644 --- a/jsonDiff_test.go +++ b/jsonDiff_test.go @@ -25,6 +25,8 @@ func TestSprintJSONDiff(t *testing.T) { json2 string expectedStringA []string expectedStringB []string + isNoised bool + noise map[string][]string }{ { expectedStringA: []string{ @@ -669,11 +671,35 @@ func TestSprintJSONDiff(t *testing.T) { json2: "{\"zoo\":{\"animals\":[{\"species\":\"mammal\",\"name\":\"Elephant\",\"age\":10},{\"type\":\"bird\",\"name\":\"Parrot\",\"age\":2}]}}", name: "random key change in deeply nested mixed structures", }, + { + expectedStringA: []string{ + "29a03b5d51ae5ae3b35affbc646f08b8d77d4c34a001945f125dda0b9d581a7b", + }, + expectedStringB: []string{ + "aa041336dda91711129ab5d24f1e19d636e819452252ab20da1bf072b21c75f4", + }, + json1: "{\"key1\": [\"a\", \"b\", \"c\"], \"key2\": \"value1\"}", + json2: "{\"key1\": [\"a\", \"b\", \"d\"], \"keyX\": \"value1\"}", + name: "random key transformation in a map with nested array key changes labeled as noise", + isNoised: true, + noise: map[string][]string{ + "key1": {}, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - resp, err := CompareJSON([]byte(tt.json1), []byte(tt.json2), map[string][]string{}, false) + + var resp Diff + var err error + + if tt.isNoised { + resp, err = CompareJSON([]byte(tt.json1), []byte(tt.json2), tt.noise, false) + } else { + resp, err = CompareJSON([]byte(tt.json1), []byte(tt.json2), map[string][]string{}, false) + } + if err != nil { fmt.Println(err.Error()) fmt.Println(resp) From 07375b5c0343fa2c1ccd7345dcfde60cf0d6da94 Mon Sep 17 00:00:00 2001 From: Sarthak160 Date: Wed, 4 Dec 2024 18:56:56 +0530 Subject: [PATCH 04/12] chore: add debug statements Signed-off-by: Sarthak160 --- jsondiff.go | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/jsondiff.go b/jsondiff.go index df898f9..e68ab36 100644 --- a/jsondiff.go +++ b/jsondiff.go @@ -391,27 +391,35 @@ func separateAndColorize(diffStr string, noise map[string][]string) (string, str var isExpectMap, isActualMap bool expect, actual := "", "" - // Adding Open Brackets - expect += "{\n" - actual += "{\n" + // Stack to track the JSON path + jsonPath := []string{} + + fmt.Println("DIFF STR ::", diffStr,"len ::", len(lines)) // Iterate over the lines, processing each line and the next line together. for i := 0; i < len(lines)-1; i++ { var expectKey, actualKey string line := lines[i] nextLine := lines[i+1] - + fmt.Println("CURRENT LINE ::", line) // Process lines that start with a '-' indicating expected differences. if len(line) > 0 && line[0] == '-' && i != len(lines)-1 { if len(nextLine) > 3 && len(strings.SplitN(nextLine[3:], ":", 2)) == 2 { actualTrimmedLine := nextLine[3:] // Trim the '+ ' prefix from the next line. actualKeyValue := strings.SplitN(actualTrimmedLine, ":", 2) actualKey = strings.TrimSpace(actualKeyValue[0]) - isNoised := checkNoise(actualKey, noise) + // Update the JSON path stack + jsonPath = append(jsonPath, actualKey) + fmt.Println("CURRENT ACTUAL JSON PATH ::", jsonPath) + // Check for noise based on the current JSON path + isNoised := checkNoise(strings.Join(jsonPath, "."), noise) if isNoised { + // Pop the key off the path stack and continue + jsonPath = jsonPath[:len(jsonPath)-1] continue } + // Process the value value := strings.TrimSpace(actualKeyValue[1]) var jsonObj map[string]interface{} switch { @@ -423,17 +431,29 @@ func separateAndColorize(diffStr string, noise map[string][]string) (string, str actualValue = value } + // Pop the key off the path stack + jsonPath = jsonPath[:len(jsonPath)-1] } + if len(strings.SplitN(line[3:], ":", 2)) == 2 { expectTrimmedLine := line[3:] // Trim the '- ' prefix from the current line. expectkeyValue := strings.SplitN(expectTrimmedLine, ":", 2) expectKey = strings.TrimSpace(expectkeyValue[0]) - isNoised := checkNoise(expectKey, noise) + // Update the JSON path stack + jsonPath = append(jsonPath, expectKey) + + fmt.Println("CURRENT EXPECTED JSON PATH ::", jsonPath) + + // Check for noise based on the current JSON path + isNoised := checkNoise(strings.Join(jsonPath, "."), noise) if isNoised { + // Pop the key off the path stack and continue + jsonPath = jsonPath[:len(jsonPath)-1] continue } + // Process the value value := strings.TrimSpace(expectkeyValue[1]) var jsonObj map[string]interface{} switch { @@ -444,7 +464,11 @@ func separateAndColorize(diffStr string, noise map[string][]string) (string, str default: expectValue = value } + + // Pop the key off the path stack + jsonPath = jsonPath[:len(jsonPath)-1] } + // Define color functions for red and green. red := color.New(color.FgRed).SprintFunc() green := color.New(color.FgGreen).SprintFunc() @@ -951,10 +975,11 @@ func normalizeJSON(input []byte) ([]byte, error) { } func checkNoise(key string, noise map[string][]string) bool { + fmt.Println("KEYYYY: ", key, "NOISE: ", noise) for e := range noise { if strings.Contains(key, e) { return true } } - return false + return false // Return false if no noise path matched } From 631130a9c64b18b661c07ef689633d3fe9857be8 Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Thu, 5 Dec 2024 18:30:42 +0530 Subject: [PATCH 05/12] fix: noise for multiple levels Signed-off-by: Ayush Sharma --- jsondiff.go | 85 +++++++++++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 49 deletions(-) diff --git a/jsondiff.go b/jsondiff.go index e68ab36..82fd8e8 100644 --- a/jsondiff.go +++ b/jsondiff.go @@ -214,7 +214,7 @@ func writeKeyValuePair(builder *strings.Builder, key string, value interface{}, // indent: The indentation string to use for formatting. // red, green: Functions to apply red and green colors respectively for differences. // Returns two strings: the colorized differences for the expected and actual slices. -func compareAndColorizeSlices(a, b []interface{}, indent string, red, green func(a ...interface{}) string) (string, string) { +func compareAndColorizeSlices(a, b []interface{}, indent string, red, green func(a ...interface{}) string, jsonPath string, noise map[string][]string) (string, string) { var expectedOutput strings.Builder // Builder for the expected output string. var actualOutput strings.Builder // Builder for the actual output string. maxLength := len(a) // Determine the maximum length between the two slices. @@ -257,7 +257,7 @@ func compareAndColorizeSlices(a, b []interface{}, indent string, red, green func case map[string]interface{}: if v2, ok := bValue.(map[string]interface{}); ok { // Recursively compare and colorize maps. - expectedText, actualText := compareAndColorizeMaps(v1, v2, indent+" ", red, green) + expectedText, actualText := compareAndColorizeMaps(v1, v2, indent+" ", red, green, jsonPath, noise) expectedOutput.WriteString(fmt.Sprintf("%s[%d]: %s\n", indent, i, expectedText)) actualOutput.WriteString(fmt.Sprintf("%s[%d]: %s\n", indent, i, actualText)) continue @@ -266,7 +266,7 @@ func compareAndColorizeSlices(a, b []interface{}, indent string, red, green func case []interface{}: if v2, ok := bValue.([]interface{}); ok { // Recursively compare and colorize slices. - expectedText, actualText := compareAndColorizeSlices(v1, v2, indent+" ", red, green) + expectedText, actualText := compareAndColorizeSlices(v1, v2, indent+" ", red, green, jsonPath, noise) expectedOutput.WriteString(fmt.Sprintf("%s[%d]: [\n%s%s]\n", indent, i, expectedText, indent)) actualOutput.WriteString(fmt.Sprintf("%s[%d]: [\n%s%s]\n", indent, i, actualText, indent)) continue @@ -307,14 +307,22 @@ func serialize(value interface{}) string { // expect: The builder for the expected output. // actual: The builder for the actual output. // red, green: Functions to apply red and green colors respectively for differences. -func compare(key string, val1, val2 interface{}, indent string, expect, actual *strings.Builder, red, green func(a ...interface{}) string) { +func compare(key string, val1, val2 interface{}, indent string, expect, actual *strings.Builder, red, green func(a ...interface{}) string, jsonPath string, noise map[string][]string) { + jsonPath = jsonPath + "." + key + + isNoised := checkNoise(jsonPath, noise) + + if isNoised { + return + } + switch v1 := val1.(type) { // Case for map[string]interface{} type case map[string]interface{}: // Check if the second value is also a map[string]interface{} if v2, ok := val2.(map[string]interface{}); ok { // Recursively compare and colorize maps - expectedText, actualText := compareAndColorizeMaps(v1, v2, indent+" ", red, green) + expectedText, actualText := compareAndColorizeMaps(v1, v2, indent+" ", red, green, jsonPath, noise) expect.WriteString(fmt.Sprintf("%s\"%s\": %s\n", indent, key, expectedText)) actual.WriteString(fmt.Sprintf("%s\"%s\": %s\n", indent, key, actualText)) return @@ -328,7 +336,7 @@ func compare(key string, val1, val2 interface{}, indent string, expect, actual * // Check if the second value is also a []interface{} if v2, ok := val2.([]interface{}); ok { // Recursively compare and colorize slices - expectedText, actualText := compareAndColorizeSlices(v1, v2, indent+" ", red, green) + expectedText, actualText := compareAndColorizeSlices(v1, v2, indent+" ", red, green, jsonPath, noise) expect.WriteString(fmt.Sprintf("%s\"%s\": [\n%s\n%s]\n", indent, key, expectedText, indent)) actual.WriteString(fmt.Sprintf("%s\"%s\": [\n%s\n%s]\n", indent, key, actualText, indent)) return @@ -391,34 +399,21 @@ func separateAndColorize(diffStr string, noise map[string][]string) (string, str var isExpectMap, isActualMap bool expect, actual := "", "" - // Stack to track the JSON path - jsonPath := []string{} + expect += "{\n" + actual += "{\n" - fmt.Println("DIFF STR ::", diffStr,"len ::", len(lines)) // Iterate over the lines, processing each line and the next line together. for i := 0; i < len(lines)-1; i++ { var expectKey, actualKey string line := lines[i] nextLine := lines[i+1] - fmt.Println("CURRENT LINE ::", line) + // Process lines that start with a '-' indicating expected differences. if len(line) > 0 && line[0] == '-' && i != len(lines)-1 { if len(nextLine) > 3 && len(strings.SplitN(nextLine[3:], ":", 2)) == 2 { actualTrimmedLine := nextLine[3:] // Trim the '+ ' prefix from the next line. actualKeyValue := strings.SplitN(actualTrimmedLine, ":", 2) actualKey = strings.TrimSpace(actualKeyValue[0]) - - // Update the JSON path stack - jsonPath = append(jsonPath, actualKey) - fmt.Println("CURRENT ACTUAL JSON PATH ::", jsonPath) - // Check for noise based on the current JSON path - isNoised := checkNoise(strings.Join(jsonPath, "."), noise) - if isNoised { - // Pop the key off the path stack and continue - jsonPath = jsonPath[:len(jsonPath)-1] - continue - } - // Process the value value := strings.TrimSpace(actualKeyValue[1]) var jsonObj map[string]interface{} @@ -430,29 +425,12 @@ func separateAndColorize(diffStr string, noise map[string][]string) (string, str default: actualValue = value } - - // Pop the key off the path stack - jsonPath = jsonPath[:len(jsonPath)-1] } if len(strings.SplitN(line[3:], ":", 2)) == 2 { expectTrimmedLine := line[3:] // Trim the '- ' prefix from the current line. expectkeyValue := strings.SplitN(expectTrimmedLine, ":", 2) expectKey = strings.TrimSpace(expectkeyValue[0]) - - // Update the JSON path stack - jsonPath = append(jsonPath, expectKey) - - fmt.Println("CURRENT EXPECTED JSON PATH ::", jsonPath) - - // Check for noise based on the current JSON path - isNoised := checkNoise(strings.Join(jsonPath, "."), noise) - if isNoised { - // Pop the key off the path stack and continue - jsonPath = jsonPath[:len(jsonPath)-1] - continue - } - // Process the value value := strings.TrimSpace(expectkeyValue[1]) var jsonObj map[string]interface{} @@ -464,9 +442,6 @@ func separateAndColorize(diffStr string, noise map[string][]string) (string, str default: expectValue = value } - - // Pop the key off the path stack - jsonPath = jsonPath[:len(jsonPath)-1] } // Define color functions for red and green. @@ -474,13 +449,15 @@ func separateAndColorize(diffStr string, noise map[string][]string) (string, str green := color.New(color.FgGreen).SprintFunc() var expectedText, actualText string + intialJsonPath := "" + if expectValue != nil && actualValue != nil { var expectBuilder, actualBuilder strings.Builder if expectKey != actualKey { actualBuilder.WriteString(fmt.Sprintf("%s: %s\n", green(serialize(actualKey[:len(actualKey)-1])), actualValue)) expectBuilder.WriteString(fmt.Sprintf("%s: %s\n", red(serialize(expectKey[:len(expectKey)-1])), expectValue)) } else { - compare(expectKey[:len(expectKey)-1], expectValue, actualValue, " ", &expectBuilder, &actualBuilder, red, green) + compare(expectKey[:len(expectKey)-1], expectValue, actualValue, " ", &expectBuilder, &actualBuilder, red, green, intialJsonPath, noise) } expectedText = expectBuilder.String() actualText = actualBuilder.String() @@ -488,9 +465,13 @@ func separateAndColorize(diffStr string, noise map[string][]string) (string, str if actualKey != expectKey { continue } - expectedText, actualText = compareAndColorizeSlices(expectsArray, actualsArray, " ", red, green) + isNoised := checkNoise(actualKey, noise) + if isNoised { + continue + } + expectedText, actualText = compareAndColorizeSlices(expectsArray, actualsArray, " ", red, green, intialJsonPath, noise) } else if isExpectMap && isActualMap { - expectedText, actualText = compareAndColorizeMaps(expectMap, actualMap, " ", red, green) + expectedText, actualText = compareAndColorizeMaps(expectMap, actualMap, " ", red, green, intialJsonPath, noise) // Removing extra { and } from the expected and actual text. expectedText = expectedText[2 : len(expectedText)-2] actualText = actualText[2 : len(actualText)-2] @@ -774,7 +755,7 @@ func truncateToMatchWithEllipsis(expectedText, actualText string) (string, strin // indent: The indentation string to use for formatting. // red, green: Functions to apply red and green colors respectively. // Returns two strings: the colorized differences for the expected and actual maps. -func compareAndColorizeMaps(a, b map[string]interface{}, indent string, red, green func(a ...interface{}) string) (string, string) { +func compareAndColorizeMaps(a, b map[string]interface{}, indent string, red, green func(a ...interface{}) string, jsonPath string, noise map[string][]string) (string, string) { var expectedOutput, actualOutput strings.Builder // Builders for the resulting strings. expectedOutput.WriteString("{\n") // Start the expected output with an opening brace and newline. actualOutput.WriteString("{\n") // Start the actual output with an opening brace and newline. @@ -788,13 +769,19 @@ func compareAndColorizeMaps(a, b map[string]interface{}, indent string, red, gre } // Compare the values for the current key in both maps. - compare(key, aValue, bValue, indent+" ", &expectedOutput, &actualOutput, red, green) + compare(key, aValue, bValue, indent+" ", &expectedOutput, &actualOutput, red, green, jsonPath, noise) } // Iterate over each key-value pair in the second map. for key, bValue := range b { if _, aHasKey := a[key]; !aHasKey { // If the key does not exist in the first map. - writeKeyValuePair(&actualOutput, green(key), bValue, indent+" ", green) // Write the key-value pair with green color. + jsonPath = jsonPath + "." + key + + isNoised := checkNoise(jsonPath, noise) + + if !isNoised { + writeKeyValuePair(&actualOutput, green(key), bValue, indent+" ", green) // Write the key-value pair with green color. + } } } @@ -975,7 +962,7 @@ func normalizeJSON(input []byte) ([]byte, error) { } func checkNoise(key string, noise map[string][]string) bool { - fmt.Println("KEYYYY: ", key, "NOISE: ", noise) + key = strings.TrimPrefix(key, ".") for e := range noise { if strings.Contains(key, e) { return true From 49c3fe6ba32e8affb3ddf6a348813244e8152c75 Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Fri, 6 Dec 2024 13:03:46 +0530 Subject: [PATCH 06/12] fix: noise for slices Signed-off-by: Ayush Sharma --- jsondiff.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jsondiff.go b/jsondiff.go index 82fd8e8..bdcbf17 100644 --- a/jsondiff.go +++ b/jsondiff.go @@ -257,7 +257,8 @@ func compareAndColorizeSlices(a, b []interface{}, indent string, red, green func case map[string]interface{}: if v2, ok := bValue.(map[string]interface{}); ok { // Recursively compare and colorize maps. - expectedText, actualText := compareAndColorizeMaps(v1, v2, indent+" ", red, green, jsonPath, noise) + prefixedValue := jsonPath + "[" + fmt.Sprint(i) + "]" + expectedText, actualText := compareAndColorizeMaps(v1, v2, indent+" ", red, green, prefixedValue, noise) expectedOutput.WriteString(fmt.Sprintf("%s[%d]: %s\n", indent, i, expectedText)) actualOutput.WriteString(fmt.Sprintf("%s[%d]: %s\n", indent, i, actualText)) continue From 3e1fb520b3eb343f2c7cd058125e711e1989a7ae Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Sat, 7 Dec 2024 21:19:34 +0530 Subject: [PATCH 07/12] fix: check for response types Signed-off-by: Ayush Sharma --- jsondiff.go | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/jsondiff.go b/jsondiff.go index bdcbf17..55ffc8d 100644 --- a/jsondiff.go +++ b/jsondiff.go @@ -28,6 +28,36 @@ type Diff struct { func CompareJSON(expectedJSON []byte, actualJSON []byte, noise map[string][]string, disableColor bool) (Diff, error) { color.NoColor = disableColor + + var expectedType interface{} + var actualType interface{} + + if err := json.Unmarshal(expectedJSON, &expectedType); err != nil { + fmt.Println("Error unmarshalling expected JSON") + return Diff{}, err + } + + if err := json.Unmarshal(actualJSON, &actualType); err != nil { + fmt.Println("Error unmarshalling actual JSON") + return Diff{}, err + } + + // Check if types of expected and actual JSON are the same. + + if reflect.TypeOf(expectedType) != reflect.TypeOf(actualType) { + expectedJSONString := `Type of expected body: ` + reflect.TypeOf(expectedType).Kind().String() + actualJSONString := `Type of actual body: ` + reflect.TypeOf(actualType).Kind().String() + offset := []int{4} + + highlightExpected := color.FgHiRed + highlightActual := color.FgHiGreen + + return Diff{ + Expected: breakSliceWithColor(expectedJSONString, &highlightExpected, offset), + Actual: breakSliceWithColor(actualJSONString, &highlightActual, offset), + }, nil + } + // Calculate the differences between the two JSON objects. diffString, err := calculateJSONDiffs(expectedJSON, actualJSON) if err != nil || diffString == "" { @@ -36,7 +66,7 @@ func CompareJSON(expectedJSON []byte, actualJSON []byte, noise map[string][]stri // Extract the modified keys from the diff string. modifiedKeys := extractKey(diffString) - t := reflect.TypeOf(expectedJSON) + t := reflect.TypeOf(expectedType) if t.Kind() == reflect.Map { // Check if the modified keys exist in the provided maps and add additional context if they do. From af0c0a21686746a2ddbb3430da98423a057ce786 Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Mon, 9 Dec 2024 14:53:33 +0530 Subject: [PATCH 08/12] fix: remove color from truncate . add breakLines for compare Signed-off-by: Ayush Sharma --- jsondiff.go | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/jsondiff.go b/jsondiff.go index 55ffc8d..4353d3b 100644 --- a/jsondiff.go +++ b/jsondiff.go @@ -108,8 +108,8 @@ func Compare(expectedJSON, actualJSON string) Diff { // Return the colorized differences in a Diff struct. return Diff{ - Expected: colorizedExpected, - Actual: colorizedActual, + Expected: breakLines(colorizedExpected), + Actual: breakLines(colorizedActual), } } @@ -225,17 +225,24 @@ func extractKey(diffString string) string { // colorFunc: The function to apply color to the value, if provided. func writeKeyValuePair(builder *strings.Builder, key string, value interface{}, indent string, applyColor func(a ...interface{}) string) { // Serialize the value to a pretty-printed JSON string. - serializedValue, _ := json.MarshalIndent(value, "", " ") - formattedValue := string(serializedValue) + switch reflect.TypeOf(value).Kind() { + case reflect.Map: + formattedValue := applyColor("{ ... }") - // Check if a color function is provided and the value is not empty. - if applyColor != nil && value != "" { - formattedValue = applyColor(formattedValue) - } + builder.WriteString(fmt.Sprintf("%s\"%s\": %s,\n", indent, key, formattedValue)) + default: - // Write the key-value pair to the builder with or without colorization. - builder.WriteString(fmt.Sprintf("%s\"%s\": %s,\n", indent, key, formattedValue)) + serializedValue, _ := json.MarshalIndent(value, "", " ") + formattedValue := string(serializedValue) + // Check if a color function is provided and the value is not empty. + if applyColor != nil && value != "" { + formattedValue = applyColor(formattedValue) + } + + // Write the key-value pair to the builder with or without colorization. + builder.WriteString(fmt.Sprintf("%s\"%s\": %s,\n", indent, key, formattedValue)) + } } // compareAndColorizeSlices compares two slices and returns the differences as colorized strings. @@ -751,7 +758,7 @@ func truncateToMatchWithEllipsis(expectedText, actualText string) (string, strin ellipsis := builder.String() // Function to truncate the lines and add ellipses in the middle. - truncate := func(lines []string, matchLineCount int, color string) string { + truncate := func(lines []string, matchLineCount int, _ string) string { // If the number of lines is less than or equal to the match line count, return the lines as a single string. if len(lines) <= matchLineCount { return strings.Join(lines, "\n") @@ -767,7 +774,7 @@ func truncateToMatchWithEllipsis(expectedText, actualText string) (string, strin bottomHalfLineCount := matchLineCount - 3 - topHalfLineCount // Truncate the lines by keeping the top and bottom halves and adding ellipses in the middle. - truncated := append(lines[:topHalfLineCount], ellipsis+color) + truncated := append(lines[:topHalfLineCount], ellipsis) truncated = append(truncated, lines[len(lines)-bottomHalfLineCount:]...) return strings.Join(truncated, "\n") + reset } From be8297883d34c3449e88e34bb9ae570d42fedb05 Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Mon, 9 Dec 2024 16:46:53 +0530 Subject: [PATCH 09/12] fix: noise and truncate slices Signed-off-by: Ayush Sharma --- jsondiff.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jsondiff.go b/jsondiff.go index 4353d3b..cfba290 100644 --- a/jsondiff.go +++ b/jsondiff.go @@ -229,6 +229,10 @@ func writeKeyValuePair(builder *strings.Builder, key string, value interface{}, case reflect.Map: formattedValue := applyColor("{ ... }") + builder.WriteString(fmt.Sprintf("%s\"%s\": %s,\n", indent, key, formattedValue)) + case reflect.Slice: + formattedValue := applyColor("[ ... ]") + builder.WriteString(fmt.Sprintf("%s\"%s\": %s,\n", indent, key, formattedValue)) default: @@ -354,6 +358,13 @@ func compare(key string, val1, val2 interface{}, indent string, expect, actual * return } + // check if the values are of same type or not + if reflect.TypeOf(val1) != reflect.TypeOf(val2) { + writeKeyValuePair(expect, key, val1, indent, red) + writeKeyValuePair(actual, key, val2, indent, green) + return + } + switch v1 := val1.(type) { // Case for map[string]interface{} type case map[string]interface{}: @@ -1001,6 +1012,7 @@ func normalizeJSON(input []byte) ([]byte, error) { func checkNoise(key string, noise map[string][]string) bool { key = strings.TrimPrefix(key, ".") + key = strings.ToLower(key) for e := range noise { if strings.Contains(key, e) { return true From de225e8d81a284cb9528abb7bd8c72c17bdbce7b Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Mon, 9 Dec 2024 19:11:02 +0530 Subject: [PATCH 10/12] fix: noise for slices Signed-off-by: Ayush Sharma --- jsondiff.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jsondiff.go b/jsondiff.go index cfba290..d527246 100644 --- a/jsondiff.go +++ b/jsondiff.go @@ -308,7 +308,8 @@ func compareAndColorizeSlices(a, b []interface{}, indent string, red, green func case []interface{}: if v2, ok := bValue.([]interface{}); ok { // Recursively compare and colorize slices. - expectedText, actualText := compareAndColorizeSlices(v1, v2, indent+" ", red, green, jsonPath, noise) + prefixedValue := jsonPath + "[" + fmt.Sprint(i) + "]" + expectedText, actualText := compareAndColorizeSlices(v1, v2, indent+" ", red, green, prefixedValue, noise) expectedOutput.WriteString(fmt.Sprintf("%s[%d]: [\n%s%s]\n", indent, i, expectedText, indent)) actualOutput.WriteString(fmt.Sprintf("%s[%d]: [\n%s%s]\n", indent, i, actualText, indent)) continue @@ -316,7 +317,9 @@ func compareAndColorizeSlices(a, b []interface{}, indent string, red, green func default: // If values are not deeply equal, write the values with colors. - if reflect.DeepEqual(aValue, bValue) { + prefixedValue := jsonPath + "[" + fmt.Sprint(i) + "]" + isNoised := checkNoise(prefixedValue, noise) + if reflect.DeepEqual(aValue, bValue) || isNoised { expectedOutput.WriteString(fmt.Sprintf("%s[%d]: %v\n", indent, i, aValue)) actualOutput.WriteString(fmt.Sprintf("%s[%d]: %v\n", indent, i, bValue)) continue From 03e15dff0fc869be427c0e6687de7b80a1e1e1b4 Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Mon, 9 Dec 2024 19:23:56 +0530 Subject: [PATCH 11/12] fix: update unit tests Signed-off-by: Ayush Sharma --- jsonDiff_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/jsonDiff_test.go b/jsonDiff_test.go index 81e65dd..cb84dbf 100644 --- a/jsonDiff_test.go +++ b/jsonDiff_test.go @@ -614,11 +614,10 @@ func TestSprintJSONDiff(t *testing.T) { }, { expectedStringA: []string{ - "d6ce134d7dd3367ee6201869c1ee642f065b54f200a8023d5ccd6df6417828d5", - "cc1d6ab9fd7c8cf07fbf85a1710eab59ebc861f4da193cafbdcfe7784ebf22d2", + "0e3cca6969dcc5294cf90792b2cb5253adc51b37ab5b8a17f25dfeef51b798ab", }, expectedStringB: []string{ - "094a72abe8018edc3aa6dabd237f5f405c4d061eb1bba76f14b6a862b818d9d4", + "2e2609aeccc418eaf137d9dd04af5de8805e8c031bfd1f7b6d1327dd1db41d1b", }, json1: "{\"animal\":{\"name\":\"Cat\",\"attributes\":{\"color\":\"black\",\"age\":5}}}", json2: "{\"animal\":{\"name\":\"Cat\",\"characteristics\":{\"color\":\"black\",\"age\":5}}}", From dc21a6893383d641bb68c6eae73fa8cd54666de2 Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Mon, 9 Dec 2024 19:32:14 +0530 Subject: [PATCH 12/12] fix: update tests Signed-off-by: Ayush Sharma --- jsonDiff_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jsonDiff_test.go b/jsonDiff_test.go index cb84dbf..80635a6 100644 --- a/jsonDiff_test.go +++ b/jsonDiff_test.go @@ -148,6 +148,7 @@ func TestSprintJSONDiff(t *testing.T) { "ef0a5b31ffc0a36df02dcc08898cad0b92857cd1405cad0feefc18d888bf57d0", "e0236118ff8532288842ad67be5bca9f81b15191ee2efc2eee077406fabf8bbd", "c828a0590dbd6e6eefbee21c5855b19a8bff98930f0219816fe6f24c3705c5cb", + "17f1dc518cda544ae5ff4b2479e94d5ef811e542b387608dfe7b44e42937e452", }, expectedStringB: []string{ "d79b35acf01b0f5138699ff1cc49ea89373b8ebf7e96118b839586a28c28bbee", @@ -156,6 +157,7 @@ func TestSprintJSONDiff(t *testing.T) { "001ff4d6bf9821bb067c73812ba5900574dd161d813f10623ba2515fdbed0f88", "19018c74ffe402eb59202aadc1cab4f5c8171c96ba50f4621ab9d72f3b18914e", "0bd78116662eba5d4fa8bbc64f81afbd879fb2e73cd6d85105e1f9bf3a658ae0", + "d11e6a5e5047d70f5e5633650bc4b3fd7a588d126fb785e5ac42b92fbf3e44f6", }, json1: "{\"books\":[{\"title\":\"Book A\",\"author\":{\"name\":\"Author 1\"}},{\"title\":\"Book B\",\"author\":{\"name\":\"Author 2\"}}]}", json2: "{\"books\":[{\"title\":\"Book B\",\"author\":{\"name\":\"Author 2\"}},{\"title\":\"Book A\",\"author\":{\"name\":\"Author 1\"}}]}", @@ -615,9 +617,11 @@ func TestSprintJSONDiff(t *testing.T) { { expectedStringA: []string{ "0e3cca6969dcc5294cf90792b2cb5253adc51b37ab5b8a17f25dfeef51b798ab", + "59fb6a700fb710db7d05e59ca4425b21de4fc754ab8ea1a4ff1ac05d1e70d478", }, expectedStringB: []string{ "2e2609aeccc418eaf137d9dd04af5de8805e8c031bfd1f7b6d1327dd1db41d1b", + "2e2609aeccc418eaf137d9dd04af5de8805e8c031bfd1f7b6d1327dd1db41d1b", }, json1: "{\"animal\":{\"name\":\"Cat\",\"attributes\":{\"color\":\"black\",\"age\":5}}}", json2: "{\"animal\":{\"name\":\"Cat\",\"characteristics\":{\"color\":\"black\",\"age\":5}}}",