PropertyTesting : migrating from js diffing library to elixir diffing library and need help writing proptest

Trying to write a property based test to prove ‘js_lib.patch(json_a, elixir_lib.diff(json_a, json_b)) == json_b’

  • a , b are maps and I need to check if the diff of (a,b) is equal to b.

I think something like this should probably be close to what you are looking for:

defmodule MyDiffingToolTest
  use ExUnit, async: true
  use ExUnitProperties
  import StreamData

  property "the Elixir tool works the same as the JS tool" do
    check all map_a <- map_of(term(), term()),
              map_b <- map_of(term(), term()) do
      assert JsLib.diff(map_a, map_b) == ElixirLib.diff(map_a, map_b)
    end
  end
end

And then inside JsLib you’d have some Elixir code that invokes the JS tool from within Elixir.
You might need to change term() into something like one_of([integer(), boolean(), string(), nil]) to restrict the types to only have things that can be JSON-encoded.

Writing a generator that can generate deeply nested JSON objects/arrays is also possible, but takes a little more work. (C.f. StreamData.tree/2)

An example of generating JSON using StreamData.
(This might be cleaned up slightly, but could provide a nice starting point.)

Livebook

Setup

Mix.install([
  :stream_data,
  :jason
])
:ok

StreamData JSON generator

import StreamData

# JSON 'supports' integers by secretly treating them 
# as if they were 64-bit floating point numbers.
#
# This means we can (only) safely store integers that are smaller than 2**53.
# Larger integers might (implementation-dependent) result in incorrect rounding.
valid_integer = integer(-(2 ** 53)..(2 ** 53 - 1))

primitive = one_of([valid_integer, float(), string(:printable), boolean(), nil])

json_fragment_gen =
  tree(primitive, fn child_gen ->
    StreamData.one_of([
      # A JSON array
      list_of(child_gen),
      # A JSON object. Only supports string keys.
      map_of(string(:printable), child_gen)
    ])
  end)

# Only valid top-level JSON are objects and arrays
json_gen = json_fragment_gen |> filter(fn val -> is_map(val) || is_list(val) end, 100)

# If you want to directly turn this into a JSON string:
json_string_gen =
  StreamData.map(json_gen, &Jason.encode!(&1, escape: :javascript_safe, pretty: true))

Enum.take(json_string_gen, 10)
["{\n  \"\": null\n}", "[]", "{\n  \"񚏞𭘸\": null\n}", "[]", "{}", "[\n  {},\n  {}\n]", "[]",
 "[\n  [\n    -1022274371879688,\n    {\n      \"𙦿\": -2745706130430791,\n      \"򢗟󰤳\": true\n    }\n  ],\n  {\n    \"\": [\n      7941802212603036\n    ],\n    \"𮿽\": {\n      \"\": false,\n      \"򨋘\": -0.88671875\n    }\n  }\n]",
 "{\n  \"񲼐򋊂󏪀\": [\n    null,\n    true\n  ],\n  \"򠤝򦋏\": {\n    \"\": null,\n    \"򕬱񳰎\": \"򾅶𮝤󂸘򼷭򔦊\"\n  },\n  \"󦼣񎌄򑉿򍑁󷞒\": {\n    \"񧡨\": false\n  },\n  \"􂨮󻉣󘱆\": []\n}",
 "{\n  \"䭦\": null\n}"]

Hi, thank you this is helpful. I am using this library for the diffing : GitHub - olafura/json_diff_ex: Diff for JSON in Elixir. I want to check if the diff(a,b) is equal to be, I might have miswrote. I believe what you wrote is checking both JS library’s diffing and the elixir diffing of a , b. First week using elixir so thank you for being patient with me.

Unless I am the one who misunderstood