diff --git a/packages/functions/src/__tests__/xor.test.ts b/packages/functions/src/__tests__/xor.test.ts index 6ea29e233..49d328f32 100644 --- a/packages/functions/src/__tests__/xor.test.ts +++ b/packages/functions/src/__tests__/xor.test.ts @@ -26,6 +26,24 @@ describe('Core Functions / Xor', () => { ]); }); + it('given multiple properties that do not match, should return an error message', async () => { + expect( + await runXor( + { + version: '1.0.0', + title: 'Swagger Petstore', + termsOfService: 'http://swagger.io/terms/', + }, + { properties: ['yada-yada', 'whatever', 'foo'] }, + ), + ).toEqual([ + { + message: '"yada-yada", "whatever" and "foo" must not be both defined or both undefined', + path: [], + }, + ]); + }); + it('given both properties, should return an error message', async () => { expect( await runXor( @@ -99,22 +117,12 @@ describe('Core Functions / Xor', () => { ]), ], ], - [ - { properties: ['foo', 'bar', 'baz'] }, - [ - new RulesetValidationError( - 'invalid-function-options', - '"xor" and its "properties" option support 2-item tuples, i.e. ["id", "name"]', - ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], - ), - ], - ], [ { properties: ['foo', {}] }, [ new RulesetValidationError( 'invalid-function-options', - '"xor" and its "properties" option support 2-item tuples, i.e. ["id", "name"]', + '"xor" and its "properties" option require at least 2-item tuples, i.e. ["id", "name"]', ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], ), ], @@ -124,7 +132,7 @@ describe('Core Functions / Xor', () => { [ new RulesetValidationError( 'invalid-function-options', - '"xor" and its "properties" option support 2-item tuples, i.e. ["id", "name"]', + '"xor" and its "properties" option require at least 2-item tuples, i.e. ["id", "name"]', ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], ), ], @@ -134,7 +142,7 @@ describe('Core Functions / Xor', () => { [ new RulesetValidationError( 'invalid-function-options', - '"xor" and its "properties" option support 2-item tuples, i.e. ["id", "name"]', + '"xor" and its "properties" option require at least 2-item tuples, i.e. ["id", "name"]', ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], ), ], diff --git a/packages/functions/src/optionSchemas.ts b/packages/functions/src/optionSchemas.ts index f9a93c15e..7a3a2741d 100644 --- a/packages/functions/src/optionSchemas.ts +++ b/packages/functions/src/optionSchemas.ts @@ -207,8 +207,7 @@ export const optionSchemas: Record = { type: 'string', }, minItems: 2, - maxItems: 2, - errorMessage: `"xor" and its "properties" option support 2-item tuples, i.e. ["id", "name"]`, + errorMessage: `"xor" and its "properties" option require at least 2-item tuples, i.e. ["id", "name"]`, description: 'The properties to check.', }, }, diff --git a/packages/functions/src/xor.ts b/packages/functions/src/xor.ts index e9a122251..5d5d68b78 100644 --- a/packages/functions/src/xor.ts +++ b/packages/functions/src/xor.ts @@ -16,16 +16,20 @@ export default createRulesetFunction, Options>( options: optionSchemas.xor, }, function xor(targetVal, { properties }) { - if (properties.length !== 2) return; - const results: IFunctionResult[] = []; - const intersection = Object.keys(targetVal).filter(value => -1 !== properties.indexOf(value)); + const intersection = Object.keys(targetVal).filter(key => properties.includes(key)); + if (intersection.length !== 1) { + const formattedProperties = properties.map(prop => printValue(prop)); + + const lastProperty = formattedProperties.pop(); + let message = formattedProperties.join(', ') + (lastProperty != undefined ? ` and ${lastProperty}` : ''); + + message += ' must not be both defined or both undefined'; + results.push({ - message: `${printValue(properties[0])} and ${printValue( - properties[1], - )} must not be both defined or both undefined`, + message, }); }