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

Order independent flag for arrays #8

Open
dyst5422 opened this issue Dec 10, 2019 · 7 comments
Open

Order independent flag for arrays #8

dyst5422 opened this issue Dec 10, 2019 · 7 comments

Comments

@dyst5422
Copy link

Have a flag to indicate that the order of items in arrays do not matter.

@davidpdrsn
Copy link
Owner

This is something I agree would be really great to have. I have thought about it before but there is one issue I don't know how to resolve:

You probably don't want something that makes all array comparisons order independent. That would most likely lead to subtle false positives in your tests. I think it would be nicer to specifically mark which arrays should be compared order independently. However since we currently do all the comparisons directly on serde_json::Value types, we have no way of adding order independence annotations.

The only solution I see to that is creating our own JSON type with two variants for arrays: One where order matters, and one where it doesn't. If we did that we would probably also want our own json! macro. That is quite a bit of setup 😕

Do you have any other ideas?

@davidpdrsn davidpdrsn changed the title Order independent flag Order independent flag for arrays Jun 10, 2021
@marlon-sousa
Copy link

Hello,

This would be extremely helpful, even without all the setup needed to build the whole things. If it is a comparison mode, then someone opting in is expected to know what they are doing by reading the documentation.

I have exactly this kind of issue here, where my tests sudenly stopped working because the system has decided to return items on a first level array in a different order.

I suggest the following: with a new comparison mode, at first all arrays would be compared independently of the order (opt in for everything). If someone needs to take care of some of the arrays (e.e if these arrays need to be compared taking sorting in consideration) then a second call to assert_json_eq with the default mode (sorting matters) can be called passing only the sub arrays from both sides to be compared.

What I think could be a good addition is passing a json path (or a serde_json pointer) together with the two values to restrict comparison, so that sub objects can be selected in place.

Still, with an clear opt-in by means of a new comparison mode, I think that the implementation for all in adds enough value forusers.

@davidpdrsn
Copy link
Owner

I don't have time to work in this at the moment but PRs are much appreciated!

@marlon-sousa
Copy link

Hello,

Please see if you agree on the proposal.

If so, I might try to come with a solution.

Proposal

Create an assert_json_contains!(container: Value, contained: Value) macro.

Description

This macro will verify if container contains contained.

A container is considered to contain a contained if the following set of rules can be applied recursively:

  • all keys on contained are found in container.
  • All values of keys in contained are contained by the equivalent values of the container keys.

A value is considered to be contained if:

type rule
null container value is null
numeric container value is equals contained value, considering numeric mode
string container value is equalscontained value, case included
object
  • all keys on contained are found in container.
  • All values of keys in contained are contained by the equivalent values of the container keys.
array all items on contained array are found (using these same rules) on container array as many times as they are present on contained array

Examples

null

  • a = null
  • b = null
  • c = []
  • a contains b == true
  • b contains a == true
  • a contains c, b contains c, c contains a, c contains b == false

numeric

  • a = 1
  • b = 1
  • c = 2
  • a contains b == true
  • b contains a == true
  • a contains c, b contains c, c contains a, c contains b == false

string

  • a = "n"
  • b = "n"
  • c = "o"
  • d = "O"
  • a contains b == true
  • b contains a == true
  • a contains c, b contains c, c contains a, c contains b == false
  • c contains d, d contains c == false

Object

  • a = {"b": "c", "d": 1}

  • b = {"b": "c"}

  • c = {"b": "f"}

  • a contains b because all keys in b are found in a and all values from keys in b are contained by the equivalent values on a.

  • b does not contain a because a has an extra key that b does not have.

  • Neither a nor b contains c, because although all keys in c are present in a and b the value of keys in c are not contained by the equivalent values of these keys in a and b.

  • c does not contain b because values on c keys are not contained by values on the equivalent keys on b.

  • c does not contain a because a has keys that c does not have and because the keys matching keys on a do not have their values contained by the correspondend values on a.

arrays

  • a = [1, 2, 3]

  • b = [2, 3, 1]

  • c = [1, 2, 3, 5]

  • d = [1, 1, 2, 3]

  • e = [1, 2, 3, 1]

  • a contains b because all values on a are found in b. By found, understand that for each item the appropriate set of rules has been aplied.

  • b contains a because all values on a are found in b. By found, understand that for each item the appropriate set of rules has been aplied.

  • neither a nor b contain c, because c has at least one value not present neither in a nor b.

  • c contains both a and b, because all values on a and b are found in c.

  • a, b and c do not contain d because d has an item (the repeated 1) that none of them contain twice.

  • d contains a and b, because both a and b have their values found on d.

  • e contains d and d contains e because all values found on one are present in the other.

With all of that, a strict equality but not considering orders in lists might be obtained if obj1 contains obj2 and obj2 contains obj1.

If we do that we do not break the current API and give a good way for people trying to match service responses which are not expected to return a determined order on itens list to proceed.

What do you think?

@davidpdrsn
Copy link
Owner

Yes I think that makes sense and would be fairly straight forward to implement, its essentially how it works today except that arrays care about ordering.

The hard part, like mentioned above, is mixing array dependent ordering with independent. What if for parts of your JSON you care about ordering and other parts you don't? I've found that to be fairly common in practice.

@marlon-sousa
Copy link

Yup this is why creating another method would solve the problem.

From a philosophical point of view, strict equality should take order in consideration, because JSON specification enforses order on array, which it does not on fields in objects.

However, if you think about it, [1, 2, 3] contains [2, 3, 1] if 1 2 and 3 are contained.

By offering a way of "pure" contains, we cover yet another scenarios.

What if you have mixed requirements?

Then you will need to perform a contains operation to make sure that objects match and then a comparison of the strict ordering sub objects by equality or diuff.

I will try nto implement it and submit a pr.

@marlon-sousa
Copy link

See pull request #27

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

No branches or pull requests

3 participants