- install node.js
npm install underscore
npm install -g jasmine-node
jasmine-node structure.spec.js
Last active
August 29, 2015 14:04
-
-
Save barahilia/58e6d5c969d14fb1d36d to your computer and use it in GitHub Desktop.
JSON pattern matching
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Allows match JSON objects by pattern. Intended for APIs comparison and verification. | |
E.g.: | |
compare( | |
{ a: 1, b: ['x', 'y'] }, // pattern | |
{ a: 42, b: ['first', 'second', 'any', 'last'] } // object for check | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The module intends to compare structure of 2 JSON | |
// objects. Should be used for API verification | |
// The function throws on invalid pattern | |
// First step - return true/false | |
// Second step - explain how the structures differ | |
var _ = require("underscore"); | |
var validateArray = function (pattern) { | |
if(pattern.length === 0) { | |
throw "Invalid pattern - empty array" | |
} | |
_.each(_.tail(pattern), function (elem, index) { | |
if(! compare(pattern[0], elem)) { | |
throw "Inconsistency in pattern array at indexes 0 and " + (index+1); | |
} | |
}); | |
}; | |
var compareArray = function (elemPattern, obj) { | |
if(_.isArray(obj)) { | |
if(obj.length === 0) { | |
return false; | |
} | |
return _.every(obj, function (elem) { | |
return compare(elemPattern, elem); | |
}); | |
} | |
else { | |
return false; | |
} | |
}; | |
var compareObject = function (pattern, obj) { | |
if(_.isObject(obj)) { | |
return _.every(_.keys(pattern), function (key) { | |
if(obj.hasOwnProperty(key)) { | |
return compare(pattern[key], obj[key]); | |
} | |
else { | |
return false; | |
} | |
}); | |
} | |
else { | |
return false; | |
} | |
}; | |
var compare = function (pattern, obj) { | |
if(_.isNumber(pattern)) { | |
return _.isNumber(obj); | |
} | |
else if(_.isString(pattern)) { | |
return _.isString(obj); | |
} | |
else if(_.isArray(pattern)) { | |
validateArray(pattern); | |
return compareArray(pattern[0], obj); | |
} | |
else if(_.isObject(pattern)) { | |
return compareObject(pattern, obj); | |
} | |
throw "Unknown type of pattern: " + (typeof pattern); | |
}; | |
exports.compare = compare; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var comp = require("./structure").compare; | |
describe("Comparator", function () { | |
var prepare = function (a, b) { | |
return function () { | |
comp(a, b); | |
}; | |
}; | |
it("should find equal two numbers", function () { | |
expect(comp(1, 2)).toBeTruthy(); | |
}); | |
it("should differ number and string", function () { | |
expect(comp(1, "aa")).toBeFalsy(); | |
}); | |
it("should differ string and number", function () { | |
expect(comp("aa", 1)).toBeFalsy(); | |
}); | |
it("should find equal two strings", function () { | |
expect(comp("aa", "bb")).toBeTruthy(); | |
}); | |
it("should differ number and object", function () { | |
expect(comp(1, { })).toBeFalsy(); | |
}); | |
it("should differ object and number", function () { | |
expect(comp({ }, 1)).toBeFalsy(); | |
}); | |
it("should find equal two objects", function () { | |
expect(comp({ }, { })).toBeTruthy(); | |
}); | |
it("should differ objects with different keys", function () { | |
expect(comp({ a: 1 }, { b: 1 })).toBeFalsy(); | |
}); | |
it("should find equal objects with same keys", function () { | |
expect(comp({ a: 1 }, { a: 2 })).toBeTruthy(); | |
}); | |
it("should find equal object whose keys are a superset of pattern's keys", function () { | |
expect(comp({ a: 1 }, { a: 2, b: 1 })).toBeTruthy(); | |
expect(comp({ a: 1, b: 2 }, { a: 2, c: 1, b: 1 })).toBeTruthy(); | |
expect(comp({ }, { a: 2, b: 1 })).toBeTruthy(); | |
}); | |
it("should find equal two arrays of numbers", function () { | |
expect(comp([ 1 ], [ 2 ])).toBeTruthy(); | |
expect(comp([ 1, 3 ], [ 2 ])).toBeTruthy(); | |
expect(comp([ 1, 3 ], [ 2, 4, 6, 8, 10 ])).toBeTruthy(); | |
expect(comp([ 1, 3 ], [ 1, 2, 3 ])).toBeTruthy(); | |
}); | |
it("should throw on empty pattern array", function () { | |
expect(prepare([ ], [ ])).toThrow(); | |
expect(prepare([ ], [ 2 ])).toThrow(); | |
expect(prepare([ ], [ 2, 4, 6, 8, 10 ])).toThrow(); | |
}); | |
it("should find an empty array to differ", function () { | |
expect(comp([ 1 ], [ ])).toBeFalsy(); | |
expect(comp([ 1, 2 ], [ ])).toBeFalsy(); | |
}); | |
it("should differ between arrays of numbers and strings", function () { | |
expect(comp([ "aa" ], [ 1, 2 ])).toBeFalsy(); | |
expect(comp([ 1, 2 ], [ "a", "b" ])).toBeFalsy(); | |
}); | |
it("should found any array element not matching pattern", function () { | |
expect(comp([ "aa" ], [ "a", 1 ])).toBeFalsy(); | |
expect(comp([ "aa" ], [ 1, "a" ])).toBeFalsy(); | |
expect(comp([ 1, 2 ], [ 1, 2, "a" ])).toBeFalsy(); | |
expect(comp([ 1, 2 ], [ 1, "a", 2 ])).toBeFalsy(); | |
expect(comp([ 1, 2 ], [ "a", 1, 2 ])).toBeFalsy(); | |
}); | |
it("should throw on inconsistent pattern array", function () { | |
expect(prepare([ 1, "a" ], [ ])).toThrow(); | |
expect(prepare([ 1, "a" ], [ 2 ])).toThrow(); | |
expect(prepare([ "a", 1 ], [ ])).toThrow(); | |
expect(prepare([ "a", 1 ], [ 2 ])).toThrow(); | |
expect(prepare([ "a", { } ], [ ])).toThrow(); | |
expect(prepare([ { }, "a" ], [ 2 ])).toThrow(); | |
expect(prepare([ 1, 2, 3, "a" ], [ ])).toThrow(); | |
expect(prepare([ 1, 2, 3, "a", 4 ], [ ])).toThrow(); | |
}); | |
it("should find equal objects with values of same structure", function () { | |
expect(comp({ a: 1 }, { a: 2})).toBeTruthy(); | |
expect(comp({ a: 'a', b: 2 }, { a: 'b', b: 3})).toBeTruthy(); | |
expect( | |
comp( | |
{ a: 'a', b: 'a', c: [1, 2] }, | |
{ a: 'b', b: 'b', c: [3, 4]} | |
) | |
).toBeTruthy(); | |
}); | |
it("should differ objects with values of different structure", function () { | |
expect(comp({ a: 1 }, { a: 'a'})).toBeFalsy(); | |
expect(comp({ a: 'a', b: 'a' }, { a: 'b', b: 3})).toBeFalsy(); | |
expect( | |
comp( | |
{ a: 'a', b: 'a', c: [1, 2] }, | |
{ a: 'b', b: 'b', c: ['a', 'b']} | |
) | |
).toBeFalsy(); | |
}); | |
it("should compare complex objects", function () { | |
expect( | |
prepare( | |
{ a: 'a', b: 'a', c: [1, 2, { a: 1} ] }, | |
{ a: 'b', b: 'b', c: ['a', 'b']} | |
) | |
).toThrow(); | |
expect( | |
comp( | |
{ a: 'a', b: 'a', c: [1, 2] }, | |
{ a: 'b', b: { d: 'a' }, c: [1, 4]} | |
) | |
).toBeFalsy(); | |
expect( | |
comp( | |
{ a: 'a', b: { d: 'b' }, c: [1, 2] }, | |
{ a: 'b', b: { d: 'a' }, c: [1, 4]} | |
) | |
).toBeTruthy(); | |
expect( | |
comp( | |
{ a: 'a', b: { d: 'b', e: ['a'] }, c: [1, 2] }, | |
{ a: 'b', b: { d: 'a', e: ['a', 'b', 'c'] }, c: [1, 4]} | |
) | |
).toBeTruthy(); | |
expect( | |
comp( | |
{ a: 'a', b: { d: 'b', e: ['a'] }, c: [1, 2] }, | |
{ a: 'b', b: { d: 'a', e: [] }, c: [ 3 ]} | |
) | |
).toBeFalsy(); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment