Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compare headers fix #10

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
223 changes: 167 additions & 56 deletions jsondiff.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,50 +25,71 @@ type Diff struct {
Actual string
}

// CompareJSON compares two JSON objects (expectedJSON and actualJSON) and returns a Diff structure.
// It takes into account specific keys that are noisy or irrelevant, specified in the 'noise' parameter.
// If 'disableColor' is true, the colorization of the output will be disabled.
// The function calculates the differences between the JSON objects, and if modified keys exist in the provided maps,
// it adds additional context to the diff. It then separates and optionally colorizes the diff output into expected and actual parts.
// Returns a Diff struct containing the expected and actual differences, or an error if any issue arises.
func CompareJSON(expectedJSON []byte, actualJSON []byte, noise map[string][]string, disableColor bool) (Diff, error) {
color.NoColor = disableColor
// Calculate the differences between the two JSON objects.
diffString, err := calculateJSONDiffs(expectedJSON, actualJSON)
if err != nil || diffString == "" {
return Diff{}, err
}
// Extract the modified keys from the diff string.
modifiedKeys := extractKey(diffString)

// Check if the modified keys exist in the provided maps and add additional context if they do.
contextInfo, exists, error := checkKeyInMaps(expectedJSON, actualJSON, modifiedKeys)

if error != nil {
return Diff{}, error
}

if exists {
diffString = contextInfo + "\n" + diffString
}
// Separate and colorize the diff string into expected and actual outputs.
expect, actual := separateAndColorize(diffString, noise)

return Diff{
Expected: expect,
Actual: actual,
}, nil
// Disable or enable colorization of output based on 'disableColor' parameter.
color.NoColor = disableColor

// Calculate the differences between the two JSON objects.
diffString, err := calculateJSONDiffs(expectedJSON, actualJSON)
if err != nil || diffString == "" {
return Diff{}, err
}

// Extract the modified keys from the generated diff string.
modifiedKeys := extractKey(diffString)

// Check if the modified keys exist in the provided maps and add additional context if they do.
contextInfo, exists, error := checkKeyInMaps(expectedJSON, actualJSON, modifiedKeys)
if error != nil {
return Diff{}, error
}

// If relevant keys exist, prepend the context information to the diff string.
if exists {
diffString = contextInfo + "\n" + diffString
}

// Separate and optionally colorize the diff string into expected and actual outputs.
expect, actual := separateAndColorize(diffString, noise)

// Return a Diff struct containing the expected and actual differences.
return Diff{
Expected: expect,
Actual: actual,
}, nil
}


// Compare takes expected and actual JSON strings and returns the colorized differences.
// expectedJSON: The JSON string containing the expected values.
// actualJSON: The JSON string containing the actual values.
// Returns a Diff struct containing the colorized differences for the expected and actual JSON responses.
func Compare(expectedJSON, actualJSON string) Diff {
func Compare(expectedJSON, actualJSON string, disableColor bool) Diff {
// Calculate the ranges for differences between the expected and actual JSON strings.
offsetExpected, offsetActual, _ := diffArrayRange(expectedJSON, actualJSON)

// Define colors for highlighting differences.
highlightExpected := color.FgHiRed
highlightActual := color.FgHiGreen
offsetExpected, offsetActual, _ := diffArrayRange(expectedJSON, actualJSON)

// Define variables for the colorized strings
var colorizedExpected, colorizedActual string

if disableColor {
// If color is disabled, return the JSON strings as they are
colorizedExpected = expectedJSON
colorizedActual = actualJSON
} else {
// Define colors for highlighting differences.
highlightExpected := color.FgHiRed
highlightActual := color.FgHiGreen

// Colorize the differences in the expected and actual JSON strings.
colorizedExpected := breakSliceWithColor(expectedJSON, &highlightExpected, offsetExpected)
colorizedActual := breakSliceWithColor(actualJSON, &highlightActual, offsetActual)
// Colorize the differences in the expected and actual JSON strings.
colorizedExpected = breakSliceWithColor(expectedJSON, &highlightExpected, offsetExpected)
colorizedActual = breakSliceWithColor(actualJSON, &highlightActual, offsetActual)
}

// Return the colorized differences in a Diff struct.
return Diff{
Expand Down Expand Up @@ -722,35 +743,125 @@ func compareAndColorizeMaps(a, b map[string]interface{}, indent string, red, gre
}

// CompareHeaders compares the headers of the expected and actual maps and returns the differences as colorized strings.
// expect: The map containing the expected header values.
// actual: The map containing the actual header values.
// Returns a ColorizedResponse containing the colorized differences for the expected and actual headers.
func CompareHeaders(expectedHeaders, actualHeaders map[string]string) Diff {
var expectAll, actualAll strings.Builder // Builders for the resulting strings.
// It compares the values of the headers with the same keys from both maps and highlights the differences.
// - expectedHeaders: A map containing the expected header values.
// - actualHeaders: A map containing the actual header values.
// - options: A variadic array of interfaces to pass additional parameters (e.g., disableColor as a bool).
// - If a bool is passed, it will determine whether to disable colorization (true means colorization is disabled).
// - Additional parameters could be added in the future without changing the function signature.
// Returns a Diff struct containing colorized (or plain) differences in the expected and actual headers.
func CompareHeaders(expectedHeaders, actualHeaders map[string]string, options ...interface{}) Diff {
// Default value for disabling colorization (false means colorization is enabled).
disableColor := false

// Parse options: currently, we only handle disabling colorization.
for _, option := range options {
switch v := option.(type) {
case bool:
disableColor = v // If a boolean is passed, treat it as a flag to disable colorization.
}
}

// Initialize string builders to store the final expected and actual header strings.
var expectAll, actualAll strings.Builder

// Iterate over each key-value pair in the expected headers map.
for key, expValue := range expectedHeaders {
actValue, exists := actualHeaders[key]

if !exists {
// Handle missing headers
if disableColor {
expectAll.WriteString(fmt.Sprintf("%s: %s\n", key, expValue))
actualAll.WriteString(fmt.Sprintf("%s: <missing>\n", key))
} else {
// Use direct ANSI color codes for missing headers
expectAll.WriteString(fmt.Sprintf("%s: \033[91m%s\033[0m\n", key, expValue)) // Red
actualAll.WriteString(fmt.Sprintf("%s: <missing>\n", key))
}
continue
}

if expValue != actValue {
if disableColor {
expectAll.WriteString(fmt.Sprintf("%s: %s\n", key, expValue))
actualAll.WriteString(fmt.Sprintf("%s: %s\n", key, actValue))
} else {
// Calculate diffs only if values are different
offsetsStr1, offsetsStr2, _ := diffArrayRange(expValue, actValue)

// Use direct color initialization
redColor := color.New(color.FgHiRed).SprintFunc()
greenColor := color.New(color.FgHiGreen).SprintFunc()

// Apply colors to the differences
expectDiff := applyColorToRanges(expValue, offsetsStr1, redColor)
actualDiff := applyColorToRanges(actValue, offsetsStr2, greenColor)

expectAll.WriteString(fmt.Sprintf("%s: %s\n", key, expectDiff))
actualAll.WriteString(fmt.Sprintf("%s: %s\n", key, actualDiff))
}
} else {
// If values are the same, no need for coloring
expectAll.WriteString(fmt.Sprintf("%s: %s\n", key, expValue))
actualAll.WriteString(fmt.Sprintf("%s: %s\n", key, actValue))
}
}

// Check for additional headers in actual that weren't in expected
for key, actValue := range actualHeaders {
if _, exists := expectedHeaders[key]; !exists {
if disableColor {
expectAll.WriteString(fmt.Sprintf("%s: <missing>\n", key))
actualAll.WriteString(fmt.Sprintf("%s: %s\n", key, actValue))
} else {
expectAll.WriteString(fmt.Sprintf("%s: <missing>\n", key))
actualAll.WriteString(fmt.Sprintf("%s: \033[92m%s\033[0m\n", key, actValue)) // Green
}
}
}

return Diff{
Expected: expectAll.String(),
Actual: actualAll.String(),
}
}

// Iterate over each key-value pair in the expected map.
for key, expValue := range expectedHeaders {
actValue := actualHeaders[key] // Get the corresponding value from the actual map.
// Helper function to apply color to specific ranges of text
func applyColorToRanges(text string, ranges []int, colorFunc func(...interface{}) string) string {
if len(ranges) == 0 {
return text
}

// Calculate the offsets of the differences between the expected and actual values.
offsetsStr1, offsetsStr2, _ := diffArrayRange(string(expValue), string(actValue))
var result strings.Builder
lastIdx := 0

// Define colors for highlighting differences.
cE, cA := color.FgHiRed, color.FgHiGreen
for i := 0; i < len(ranges); i += 2 {
start := ranges[i]
end := ranges[i+1]

// Colorize the differences in the expected and actual values.
expectDiff := key + ": " + breakSliceWithColor(string(expValue), &cE, offsetsStr1)
actualDiff := key + ": " + breakSliceWithColor(string(actValue), &cA, offsetsStr2)
// Add uncolored text before the range
if start > lastIdx {
result.WriteString(text[lastIdx:start])
}

// Add the colorized differences to the builders.
expectAll.WriteString(breakLines(expectDiff) + "\n")
actualAll.WriteString(breakLines(actualDiff) + "\n")
}
// Add colored text for the range
if end > start {
result.WriteString(colorFunc(text[start:end]))
}

lastIdx = end
}

// Return the resulting strings.
return Diff{Expected: expectAll.String(), Actual: actualAll.String()}
// Add any remaining uncolored text
if lastIdx < len(text) {
result.WriteString(text[lastIdx:])
}

return result.String()
}


// breakSliceWithColor breaks the input string into slices and applies color to specified offsets.
// s: The input string to be processed.
// c: The color attribute to apply to the specified offsets.
Expand Down