Skip to content

Refactor JSON comparison options handling#9

Merged
timkimadobe merged 7 commits intomainfrom
path-options-refactor2
Dec 13, 2023
Merged

Refactor JSON comparison options handling#9
timkimadobe merged 7 commits intomainfrom
path-options-refactor2

Conversation

@timkimadobe
Copy link
Copy Markdown
Contributor

@timkimadobe timkimadobe commented Nov 17, 2023

Description

This PR overhauls the comparison option system to create a powerful modular design that allows for easy customizability. Previously, the design relied on a brittle nested dictionary that only allowed for:

  1. Specifying one comparison option
    • Value type/exact match
  2. All or nothing mode flipping (that is, no granularity on the scope of comparison options - this node only, entire subtree)

The new design specifically addresses these two points:

  1. Comparison options are easily extensible in the current design, relying on the MultiPathConfig protocol that provides for the base configuration options
    • New comparison options allow for:
      1. Value type/exact matching
      2. Collection equal count
      3. Wildcard matching
    • Future options could be:
      1. Key and/or value case insensitive compare
      2. Custom comparator functions
      3. Given type or null
      4. Key not present
      5. Array only equal count
      6. Dictionary only equal count
      7. Dictionary only validate if present in Actual
      8. And more!
  2. Granular scope for each option:
    1. This node only
    2. Subtree (this node + all descendants)

It also improves maintainability:

  1. Separation of concerns is much cleaner
    • NodeConfig handles all the options setup and resolution, AnyCodableAsserts methods can just use the options as needed
  2. Future additions of functionality is much easier - NodeConfig is an extensible container as opposed to the previous path dictionary that only allowed for 1 option
  3. An entire group of helper methods specifically dedicated to the exact equals case (assertEquals(expected:actual:file:line:)) could be removed because the underlying comparison system can now entirely handle this case, because it is not as brittle as before

The tree is minimally constructed; that is, it only builds the tree out to the paths that are specified and relies on last set defaults for the given portion of the subtree for unspecified nodes.

  1. This requires that the starting node has all the desired defaults set (which is strictly handled by the public APIs)

Note

The methods related to path extraction have been transferred to NodeConfig, pretty much unchanged:
extractValidWildcardIndex
extractRegexCaptureGroups
getCapturedRegexGroups
getKeyPathComponents
extractArrayFormattedComponents

Standard usage

Remains unchanged! (see unchanged unit test coverage)

Advanced usage

The usage has been refined so that for a given option configuration, multiple paths can easily be constructed with minimal syntax

Paths

This is possible using overloaded constructors that allow for passing path values as:

  1. Array of paths
let paths = ["[0]", "[1]"]
assertExactMatch(expected: expected, actual: actual, pathOptions: ValueTypeMatch(paths: paths))
  1. Variadic
assertExactMatch(expected: expected, actual: actual, pathOptions: ValueTypeMatch(paths: "[*0]", "[*1]"))

Active or not toggle

Each comparison option has a toggle which controls if it is "active" or not for the given node being evaluated

assertExactMatch(expected: expected, actual: actual, pathOptions: CollectionEqualCount(paths: "[*0]", "[*1]", isActive: true))

Scope

Scope allows the user to control the granularity of how the option applies to the rest of the JSON structure. Two options are currently allowed:

  1. Current node only
  2. Subtree
// For single node only
assertExactMatch(expected: expected, actual: actual, pathOptions: CollectionEqualCount(paths: "[*0]", "[*1]", scope: .singleNode))

// For node and all descendants
assertExactMatch(expected: expected, actual: actual, pathOptions: CollectionEqualCount(paths: "[*0]", "[*1]", scope: .subtree))

Related Issue

Motivation and Context

The primary motivation for this change was to accommodate the new option for CollectionEqualCount, allowing users to specify if they want collections (dictionary or array) to strictly validate the expected number of elements instead of the default permissive extensible object/array.

How Has This Been Tested?

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I have signed the Adobe Open Source CLA.
  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

Copy link
Copy Markdown
Contributor

@cdhoffmann cdhoffmann left a comment

Choose a reason for hiding this comment

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

This is really good stuff. Great job Tim.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants