[ { "name": "one of each: an addition, a removal, a change and an untouched record", "args": [ [{ "id": 1, "name": "Ada", "team": "core" }, { "id": 2, "name": "Grace", "team": "ops" }, { "id": 3, "name": "Linus", "team": "core" }], [{ "id": 1, "name": "Ada", "team": "core" }, { "id": 3, "name": "Linus", "team": "kernel" }, { "id": 4, "name": "Barbara", "team": "ops" }], "id" ], "expect": { "added": [{ "id": 4, "name": "Barbara", "team": "ops" }], "removed": [{ "id": 2, "name": "Grace", "team": "ops" }], "changed": [{ "key": "3", "before": { "id": 3, "name": "Linus", "team": "core" }, "after": { "id": 3, "name": "Linus", "team": "kernel" }, "fields": ["team"] }], "unchanged": 1 } }, { "name": "identical lists: nothing added, removed or changed", "args": [[{ "k": "a", "v": 1 }, { "k": "b", "v": 2 }], [{ "k": "a", "v": 1 }, { "k": "b", "v": 2 }], "k"], "expect": { "added": [], "removed": [], "changed": [], "unchanged": 2 } }, { "name": "reordering is not a change", "args": [[{ "k": "a", "v": 1 }, { "k": "b", "v": 2 }], [{ "k": "b", "v": 2 }, { "k": "a", "v": 1 }], "k"], "expect": { "added": [], "removed": [], "changed": [], "unchanged": 2 } }, { "name": "from nothing: everything is added, in after's order", "args": [[], [{ "k": "b" }, { "k": "a" }], "k"], "expect": { "added": [{ "k": "b" }, { "k": "a" }], "removed": [], "changed": [], "unchanged": 0 } }, { "name": "to nothing: everything is removed, in before's order", "args": [[{ "k": "b" }, { "k": "a" }], [], "k"], "expect": { "added": [], "removed": [{ "k": "b" }, { "k": "a" }], "changed": [], "unchanged": 0 } }, { "name": "changed fields are sorted by code point, including a field added and a field removed", "args": [[{ "id": "x", "b": 1, "a": 1, "old": true }], [{ "id": "x", "new": 5, "b": 2, "a": 1 }], "id"], "expect": { "added": [], "removed": [], "changed": [{ "key": "x", "before": { "id": "x", "b": 1, "a": 1, "old": true }, "after": { "id": "x", "new": 5, "b": 2, "a": 1 }, "fields": ["b", "new", "old"] }], "unchanged": 0 } }, { "name": "changed records follow after's order", "args": [[{ "k": "a", "v": 1 }, { "k": "b", "v": 1 }], [{ "k": "b", "v": 2 }, { "k": "a", "v": 2 }], "k"], "expect": { "added": [], "removed": [], "changed": [ { "key": "b", "before": { "k": "b", "v": 1 }, "after": { "k": "b", "v": 2 }, "fields": ["v"] }, { "key": "a", "before": { "k": "a", "v": 1 }, "after": { "k": "a", "v": 2 }, "fields": ["v"] } ], "unchanged": 0 } }, { "name": "1 and 1.0 are the same number, so a float round trip is not a change", "args": [[{ "k": "a", "qty": 1 }], [{ "k": "a", "qty": 1.0 }], "k"], "expect": { "added": [], "removed": [], "changed": [], "unchanged": 1 } }, { "name": "a number that became a string did change", "args": [[{ "k": "a", "qty": 1 }], [{ "k": "a", "qty": "1" }], "k"], "expect": { "added": [], "removed": [], "changed": [{ "key": "a", "before": { "k": "a", "qty": 1 }, "after": { "k": "a", "qty": "1" }, "fields": ["qty"] }], "unchanged": 0 } }, { "name": "true is not 1", "args": [[{ "k": "a", "on": 1 }], [{ "k": "a", "on": true }], "k"], "expect": { "added": [], "removed": [], "changed": [{ "key": "a", "before": { "k": "a", "on": 1 }, "after": { "k": "a", "on": true }, "fields": ["on"] }], "unchanged": 0 } }, { "name": "a null field and a missing field are the same, so dropping nulls is not a change", "args": [[{ "k": "a", "note": null, "meta": { "x": null, "y": 1 } }], [{ "k": "a", "meta": { "y": 1 } }], "k"], "expect": { "added": [], "removed": [], "changed": [], "unchanged": 1 } }, { "name": "nested lists and maps are compared deeply; list order matters", "args": [[{ "k": "a", "tags": ["x", "y"], "addr": { "city": "Leeds" } }], [{ "k": "a", "tags": ["y", "x"], "addr": { "city": "Leeds" } }], "k"], "expect": { "added": [], "removed": [], "changed": [{ "key": "a", "before": { "k": "a", "tags": ["x", "y"], "addr": { "city": "Leeds" } }, "after": { "k": "a", "tags": ["y", "x"], "addr": { "city": "Leeds" } }, "fields": ["tags"] }], "unchanged": 0 } }, { "name": "a key of 1 and a key of \"1\" match; the key field itself is reported as changed type", "args": [[{ "id": 1, "v": "a" }], [{ "id": "1", "v": "a" }], "id"], "expect": { "added": [], "removed": [], "changed": [{ "key": "1", "before": { "id": 1, "v": "a" }, "after": { "id": "1", "v": "a" }, "fields": ["id"] }], "unchanged": 0 } }, { "name": "a duplicate key in before is an error, not a guess", "args": [[{ "k": "a" }, { "k": "a" }], [], "k"], "expectError": "duplicate key \"a\" in before" }, { "name": "a duplicate key in after is an error", "args": [[], [{ "k": 7 }, { "k": 7 }], "k"], "expectError": "duplicate key \"7\" in after" }, { "name": "a record with no value at the key is an error", "args": [[{ "k": "a" }], [{ "v": 1 }], "k"], "expectError": "record 0 in after has no value at \"k\"" }, { "name": "a fractional key is an error", "args": [[{ "k": 1.5 }], [], "k"], "expectError": "cannot diff by the fractional number" }, { "name": "an empty key name is an error", "args": [[], [], ""], "expectError": "needs a non-empty key name" } ]