diff --git a/packages/did-core-test-server/suites/did-core-properties/did-metadata-structure.js b/packages/did-core-test-server/suites/did-core-properties/did-metadata-structure.js index d26ea746..095e0cf8 100644 --- a/packages/did-core-test-server/suites/did-core-properties/did-metadata-structure.js +++ b/packages/did-core-test-server/suites/did-core-properties/did-metadata-structure.js @@ -1,35 +1,3 @@ -const didMetadataStructureTest_Map_Recursive = (didDocumentMetadata) => { - it('7.3 Metadata Structure - ' + - 'The structure used to communicate this metadata MUST be a map of properties.', () => { - expect(didDocumentMetadata).toBeInfraMap(); - }); - - it('7.3 Metadata Structure - ' + - 'Each property name MUST be a string.', () => { - Object.keys(didDocumentMetadata).forEach((property_name) => { - expect(property_name).toBeString(); - }); - }); - - it('7.3 Metadata Structure - ' + - 'Each property value MUST be a string, map, list, ordered set, boolean, or null.', () => { - Object.keys(didDocumentMetadata).forEach((property_name) => { - expect(didDocumentMetadata[property_name]).toBeDidDocumentPropertyValueType(); - }); - }); - - it('7.3 Metadata Structure - ' + - 'The values within any complex data structures such as maps and lists ' + - 'MUST be one of these data types as well.', () => { - Object.keys(didDocumentMetadata).forEach((property_name) => { - if (typeof didDocumentMetadata[property_name] == "object") { - didMetadataStructureTest_Map_Recursive(didDocumentMetadata[property_name]); - } - }); - }); - -}; - const didMetadataStructureTests = (suiteConfig) => { suiteConfig.dids.forEach((didExample) => { describe(didExample, () => { @@ -37,7 +5,34 @@ const didMetadataStructureTests = (suiteConfig) => { describe(contentType, () => { const didDocumentMetadata = suiteConfig[didExample][contentType].didDocumentMetadata; if (didDocumentMetadata) { - didMetadataStructureTest_Map_Recursive(didDocumentMetadata); + it('7.3 Metadata Structure - ' + + 'The structure used to communicate this metadata MUST be a map of properties.', () => { + expect(didDocumentMetadata).toBeInfraMap(); + }); + + it('7.3 Metadata Structure - ' + + 'Each property name MUST be a string.', () => { + Object.keys(didDocumentMetadata).forEach((property_name) => { + expect(property_name).toBeString(); + }); + }); + + it('7.3 Metadata Structure - ' + + 'Each property value MUST be a string, map, list, ordered set, boolean, or null.', () => { + Object.keys(didDocumentMetadata).forEach((property_name) => { + expect(didDocumentMetadata[property_name]).toBeDidDocumentPropertyValueType(); + }); + }); + + it('7.3 Metadata Structure - ' + + 'The values within any complex data structures such as maps and lists ' + + 'MUST be one of these data types as well.', () => { + Object.keys(didDocumentMetadata).forEach((property_name) => { + if (typeof didDocumentMetadata[property_name] == "object") { + expect(didDocumentMetadata[property_name]).toBeDidDocumentMap(); + } + }); + }); it('7.3 Metadata Structure - ' + 'The entire metadata structure MUST be serializable according to the JSON ' + diff --git a/packages/jest-did-matcher/README.md b/packages/jest-did-matcher/README.md index e61d03ba..7c7df48b 100644 --- a/packages/jest-did-matcher/README.md +++ b/packages/jest-did-matcher/README.md @@ -20,6 +20,7 @@ You can also prepare by running `npm run prepare` at the top level of `did-test- - toBeValidDid / isValidDid - toBeValidDidUrl / isValidDidUrl - toBeDidCoreDatetime / isDidCoreDatetime + - toBeDidDocumentMap / isDidDocumentMap - toBeDidDocumentPropertyValueType / isDidDocumentPropertyValueType - Various encoding formats - toBeBase58String / isBase58String diff --git a/packages/jest-did-matcher/src/matchers/toBeDidDocumentMap/index.js b/packages/jest-did-matcher/src/matchers/toBeDidDocumentMap/index.js new file mode 100644 index 00000000..95153ac1 --- /dev/null +++ b/packages/jest-did-matcher/src/matchers/toBeDidDocumentMap/index.js @@ -0,0 +1,31 @@ +import { matcherHint, printExpected, printReceived } from 'jest-matcher-utils'; +import { predicate } from './predicate'; + +const passMessage = received => () => + matcherHint('.not.toBeDidDocumentMap', 'received', '') + + '\n\n' + + 'Expected value to not be a DID document map:\n' + + ` ${printReceived(received)}`; + +const failMessage = received => () => + matcherHint('.toBeDidDocumentMap', 'received', '') + + '\n\n' + + 'Expected value to be of a DID document map:\n' + + ` ${printExpected('A DID document map')}\n` + + 'Received:\n' + + ` ${printReceived(received)}`; + +export default { + toBeDidDocumentMap: expected => { + const pass = predicate(expected); + if (pass) { + return { pass: true, message: passMessage(expected) }; + } + + return { pass: false, message: failMessage(expected) }; + }, + + isDidDocumentMap: obj => { + return predicate(obj); + } +}; diff --git a/packages/jest-did-matcher/src/matchers/toBeDidDocumentMap/index.test.js b/packages/jest-did-matcher/src/matchers/toBeDidDocumentMap/index.test.js new file mode 100644 index 00000000..5b7e85f9 --- /dev/null +++ b/packages/jest-did-matcher/src/matchers/toBeDidDocumentMap/index.test.js @@ -0,0 +1,83 @@ +import each from 'jest-each'; + +import matcher from '.'; + +expect.extend(matcher); + + +describe('.toBeDidDocumentMap', () => { + each([ + [ + { + "a": "1" + } + ], + [ + { + "b": [ "1", "2", "3" ] + }, + ], + [ + { + "didDocumentMetadata": { + "canonicalId":"x", + "equivalentId":[ + "x", + "y" + ], + "method":{ + "updateCommitment":"z", + "recoveryCommitment":"t", + "published":true + } + } + }, + ], + [new Map(Object.entries({ a: "1", b: "2", c: {"x": "1", "y": "2"}}))], + [new Set(["1", "2", "3", ["4", "5", "6"], {"x":"1", "y": "2"}])] +]).test('passes when item is a type allowed in a DID Document map: %s', given => { + expect(given).toBeDidDocumentMap(); + }); +}); + +describe('.not.toBeDidDocumentMap', () => { + each([ + [{ "v" : 0 }], + [{ "v" : undefined }], + [{ "v" : NaN }], + [{"didDocumentMetadata": { + "canonicalId":NaN, + "equivalentId":[ + "x", + "y" + ], + "method":{ + "updateCommitment":"z", + "recoveryCommitment":"t", + "published":true + } + }}], + [{"didDocumentMetadata": { + "canonicalId":"i", + "equivalentId":[ + NaN, + "y" + ], + "method":{ + "updateCommitment":"z", + "recoveryCommitment":"t", + "published":true + } + }}], + [new Map(Object.entries({ a: NaN, b: "2", c: {"x": "1", "y": "2"}}))], + [new Map(Object.entries({ a: "1", b: "2", c: {"x": NaN, "y": "2"}}))], + [new Map(Object.entries({ a: "1", b: undefined, c: {"x": "1", "y": "2"}}))], + [new Map(Object.entries({ a: "1", b: "2", c: {"x": "1", "y": undefined}}))], + [new Set(["1", "2", "3", ["4", NaN, "6"], {"x":"1", "y": "2"}])], + [new Set(["1", "2", "3", ["4", "5", undefined], {"x":"1", "y": "2"}])], + [new Set(["1", "2", "3", ["4", "5", "6"], {"x": undefined, "y": "2"}])], + [new Set(["1", "2", "3", ["4", "5", "6"], {"x": "1", "y": undefined }])] + ]).test('passes when item is not of type DID Document map: %s', given => { + expect(given).not.toBeDidDocumentMap(); + }); +}); diff --git a/packages/jest-did-matcher/src/matchers/toBeDidDocumentMap/predicate.js b/packages/jest-did-matcher/src/matchers/toBeDidDocumentMap/predicate.js new file mode 100644 index 00000000..1cae09df --- /dev/null +++ b/packages/jest-did-matcher/src/matchers/toBeDidDocumentMap/predicate.js @@ -0,0 +1,33 @@ +// (DID document) property value MUST be a string, map, list, ordered set, boolean, or null. + +export function predicate(expected) { + if (expected === null) { + return true; + } + + if (Array.isArray(expected)) { + return expected.map(element => predicate(element)).reduce( (p, c) => (p && c), true); + } + + if (expected instanceof Map || expected instanceof Set) { + let r = true; + expected.forEach(v => { r = r && predicate(v); }); + return r; + } + + if (typeof expected == "object") { + return Object.keys(expected).map(key => predicate(expected[key])).reduce( (p, c) => (p && c), true); + } + + switch (typeof expected) { + case 'boolean': + case 'string': + return true; + } + + if (expected instanceof String) { + return true; + } + + return false; +} diff --git a/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/index.js b/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/index.js index b2ebdf20..8bb9cfe3 100644 --- a/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/index.js +++ b/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/index.js @@ -11,7 +11,7 @@ const failMessage = received => () => matcherHint('.toBeDidDocumentPropertyValueType', 'received', '') + '\n\n' + 'Expected value to be of type allowed in DID Document:\n' + - ` ${printExpected('type of type allowed in DID Document')}` + + ` ${printExpected('value type allowed in DID Document')}\n` + 'Received:\n' + ` ${printReceived(received)}`; diff --git a/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/index.test.js b/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/index.test.js index 1d46a2b8..67fed62d 100644 --- a/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/index.test.js +++ b/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/index.test.js @@ -7,11 +7,15 @@ expect.extend(matcher); describe('.toBeDidDocumentPropertyValueType', () => { each([ ["String"], - [new String], + [new String()], [{}], + [{ a: "1", b: "2", c: {"x": "1", "y": "2"}}], + [new Map()], + [new Map(Object.entries({ a: "1", b: "2", c: {"x": "1", "y": "2"}}))], [[]], - [new Array], - [new Set], + [new Array(1)], + [new Set()], + [new Set(["1", "2", "3", ["4", "5", "6"], {"x":"1", "y": "2"}])], [true], [false], [null] diff --git a/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/predicate.js b/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/predicate.js index c0ba8b19..c3e9454d 100644 --- a/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/predicate.js +++ b/packages/jest-did-matcher/src/matchers/toBeDidDocumentPropertyValueType/predicate.js @@ -8,7 +8,7 @@ export default expected => { return true; } - if (expected instanceof Set) { + if (expected instanceof Map || expected instanceof Set) { return true; }