import { DiffTestCase, registerDiffTestCases, } from '../support/register-diff-test-cases'; describe('diff-schemas type object', () => { describe('additionalProperties', () => { const testCases: DiffTestCase[] = [ { description: 'object additionalProperties constrained to accept nothing', examples: [{}, { a: 1 }, { a: 1, b: 'a' }, 'foo'], input: { a: { additionalProperties: true, type: 'object', }, b: { additionalProperties: false, type: 'object', }, }, output: { added: false, removed: { minProperties: 1, type: 'object', }, }, }, { description: 'additionalProperties constrained to one type gets relaxed with support for another type', examples: [ {}, { a: 1 }, { a: 'a' }, { a: 1, b: 1 }, { a: 'a', b: 'b' }, { a: 1, b: 'b' }, { a: 1, b: true }, 'foo', ], input: { a: { additionalProperties: { type: 'number' }, type: 'object', }, b: { additionalProperties: { type: ['number', 'string'] }, type: 'object', }, }, output: { added: { additionalProperties: { type: ['number', 'string'], }, not: { additionalProperties: { type: 'number', }, type: 'object', }, type: 'object', }, removed: false, }, }, ]; registerDiffTestCases(testCases); }); describe('properties', () => { const testCases: DiffTestCase[] = [ { description: 'object with no constraints gets a property constrained to some types', examples: [ {}, { name: 1 }, { name: null }, { other: 1 }, { name: 1, other: 1 }, { name: null, other: 1 }, 'foo', ], input: { a: { type: 'object', }, b: { properties: { name: { type: [ 'object', 'string', 'array', 'integer', 'number', 'boolean', ], }, }, type: 'object', }, }, output: { added: false, removed: { properties: { name: { type: 'null' }, }, required: ['name'], type: 'object', }, }, }, { description: 'object property constrained to a type is changed to be constrained to a different type', examples: [ {}, { other: 1 }, { name: [] }, { name: 'a' }, { name: 1 }, { name: [], other: 1 }, { name: 'a', other: 1 }, { name: true, other: 1 }, 'foo', ], input: { a: { properties: { name: { type: 'array' }, }, type: 'object', }, b: { properties: { name: { type: 'string' }, }, type: 'object', }, }, output: { added: { properties: { name: { type: 'string' }, }, required: ['name'], type: 'object', }, removed: { properties: { name: { type: 'array' }, }, required: ['name'], type: 'object', }, }, }, { description: 'objects get a hard constraint on a property', examples: [ {}, { first: 1 }, { last: 1 }, { first: 1, middle: 1 }, { last: 1, middle: 1 }, { first: 1, last: 1 }, { first: 1, last: 1, middle: 1 }, 'foo', ], input: { a: { type: 'object', }, b: { properties: { first: true, last: false, }, type: 'object', }, }, output: { added: false, removed: { properties: { last: true, }, required: ['last'], type: 'object', }, }, }, ]; registerDiffTestCases(testCases); }); describe('required', () => { const testCases: DiffTestCase[] = [ { description: 'object with required property constraint is relaxed to support all objects', examples: [{}, { first: 1 }, { last: 1 }, { first: 1, last: 1 }, 'foo'], input: { a: { required: ['first'], type: 'object', }, b: { type: 'object' }, }, output: { added: { properties: { first: false, }, type: 'object', }, removed: false, }, }, { description: '', examples: [ {}, { first: 1 }, { last: 1 }, { other: 1 }, { first: 1, other: 1 }, { last: 1, other: 1 }, { first: 1, last: 1 }, { first: 1, last: 1, other: 1 }, 'foo', ], input: { a: { required: ['first', 'last'], type: 'object', }, b: { required: ['last'], type: 'object', }, }, output: { added: { properties: { first: false }, required: ['last'], type: 'object', }, removed: false, }, }, ]; registerDiffTestCases(testCases); }); describe('minProperties', () => { const testCases: DiffTestCase[] = [ { description: 'objects gets a minProperties constraint', examples: [ {}, { a: 1 }, { a: 1, b: 1 }, { a: 1, b: 1, c: 1 }, { a: 1, b: 1, c: 1, d: 1 }, 'foo', ], input: { a: { type: 'object', }, b: { minProperties: 3, type: 'object', }, }, output: { added: false, removed: { maxProperties: 2, type: 'object', }, }, }, { description: 'objects with minProperties constraint gets minProperties made more restrictive', examples: [ {}, { a: 1 }, { a: 1, b: 1 }, { a: 1, b: 1, c: 1 }, { a: 1, b: 1, c: 1, d: 1 }, { a: 1, b: 1, c: 1, d: 1, e: 1 }, 'foo', ], input: { a: { minProperties: 2, type: 'object', }, b: { minProperties: 5, type: 'object', }, }, output: { added: false, removed: { maxProperties: 4, minProperties: 2, type: 'object', }, }, }, { description: 'objects get constrained to have at least one property', examples: [{}, { a: 1 }, { a: 1, b: 1 }, 'foo'], input: { a: { type: 'object', }, b: { minProperties: 1, type: 'object', }, }, output: { added: false, removed: { additionalProperties: false, type: 'object', }, }, }, ]; registerDiffTestCases(testCases); }); describe('maxProperties', () => { const testCases: DiffTestCase[] = [ { description: 'objects gets a maxProperties constraint', examples: [{}, { a: 1 }, { a: 1, b: 1 }, { a: 1, b: 1, c: 1 }, 'foo'], input: { a: { type: 'object', }, b: { maxProperties: 1, type: 'object', }, }, output: { added: false, removed: { minProperties: 2, type: 'object', }, }, }, { description: 'objects with a maxProperties constraint gets maxProperties made more restrictive', examples: [ {}, { a: 1 }, { a: 1, b: 1 }, { a: 1, b: 1, c: 1 }, { a: 1, b: 1, c: 1, d: 1 }, { a: 1, b: 1, c: 1, d: 1, e: 1 }, 'foo', ], input: { a: { maxProperties: 4, type: 'object', }, b: { maxProperties: 1, type: 'object', }, }, output: { added: false, removed: { maxProperties: 4, minProperties: 2, type: 'object', }, }, }, { description: 'object with maxProperties=0 constraint gets constrained to accept nothing', examples: [{}, { a: 1 }, 'foo'], input: { a: { maxProperties: 0, type: 'object', }, b: false, }, output: { added: false, removed: { additionalProperties: false, type: 'object', }, }, }, ]; registerDiffTestCases(testCases); }); describe('additionalProperties + maxProperties', () => { const testCases: DiffTestCase[] = [ { description: 'hard additionalProperties constraint replaced with equivalent maxProperties=0 constraint', examples: [{}, { a: 1 }, 'foo'], input: { a: { additionalProperties: false, type: 'object', }, b: { maxProperties: 0, type: 'object', }, }, output: { added: false, removed: false, }, }, ]; registerDiffTestCases(testCases); }); describe('additionalProperties + minProperties', () => { const testCases: DiffTestCase[] = [ { description: 'objects with contradicting additionalProperties and minProperties is considered false', examples: [{}, { a: 1 }, 'foo'], input: { a: { additionalProperties: false, minProperties: 1, type: 'object', }, b: false, }, output: { added: false, removed: false, }, }, ]; registerDiffTestCases(testCases); }); describe('additionalProperties + properties', () => { const testCases: DiffTestCase[] = [ { description: 'object with redundant relaxed property constraint gets additionalProperties constraint', examples: [{}, { name: 1 }, { other: 1 }, { name: 1, other: 1 }, 'foo'], input: { a: { properties: { name: true, }, type: 'object', }, b: { additionalProperties: false, type: 'object', }, }, output: { added: false, removed: { minProperties: 1, type: 'object', }, }, }, { description: 'redundant property constraint matching additionalProperties constraint is removed', examples: [ {}, { other: 1 }, { other: 'a' }, { name: 1 }, { name: 'a' }, { name: 'a', other: 1 }, { name: 1, other: 'a' }, { name: 'a', other: 'b' }, 'foo', ], input: { a: { additionalProperties: { type: 'string' }, properties: { name: { type: 'string' }, }, type: 'object', }, b: { additionalProperties: { type: 'string' }, type: 'object', }, }, output: { added: false, removed: false, }, }, { description: 'object with additionalProperties constraint gets a more restrictive property constraint', examples: [ {}, { a: 1 }, { a: 'a' }, { a: true }, { a: 'a', b: 1 }, { a: 'a', b: 1, c: true }, { name: 1 }, { name: 'a' }, { name: true }, { name: 1, other: 1 }, { name: 1, other: 'a' }, { name: 1, other: true }, { name: 'a', other: 1 }, { name: 'a', other: 'a' }, { name: 'a', other: true }, { name: true, other: 1 }, 'foo', ], input: { a: { additionalProperties: { type: ['string', 'number'] }, type: 'object', }, b: { additionalProperties: { type: ['string', 'number'] }, properties: { name: { type: 'string', }, }, type: 'object', }, }, output: { added: false, removed: { additionalProperties: { type: ['number', 'string'] }, properties: { name: { type: 'number' }, }, required: ['name'], type: 'object', }, }, }, { description: 'object with property constraint gets a fully restrictive additionalProperties constraint', examples: [ {}, { name: 'a' }, { name: 1 }, { other: true }, { name: 'a', other: true }, { name: 1, other: true }, 'foo', ], input: { a: { properties: { name: { type: 'string' }, }, type: 'object', }, b: { additionalProperties: false, properties: { name: { type: 'string' }, }, type: 'object', }, }, output: { added: false, removed: { not: { additionalProperties: false, properties: { name: { type: 'string' }, }, type: 'object', }, properties: { name: { type: 'string' }, }, type: 'object', }, }, }, { description: 'numbers and property-constrained object gets numbers constrained ' + 'and additionalProperties constraint', examples: [ {}, 1, { name: 1 }, { name: 'a' }, { other: 1 }, { name: 1, other: 'a' }, { name: 'a', other: 'a' }, { name: 'a', other: 1 }, { name: 1, other: 1 }, 'foo', ], input: { a: { properties: { name: { type: 'string' }, }, type: ['object', 'number'], }, b: { additionalProperties: false, properties: { name: { type: 'string' }, }, type: 'object', }, }, output: { added: false, removed: { not: { additionalProperties: false, properties: { name: { type: 'string' }, }, type: 'object', }, properties: { name: { type: 'string' }, }, type: ['number', 'object'], }, }, }, { description: 'objects with 2 property constraints get additionalProperties that accepts nothing', examples: [ {}, { first: 'a' }, { first: 1 }, { last: 'a' }, { last: 1 }, { other: 'a' }, { first: 'a', last: 'a' }, { first: 1, last: 'a' }, { first: 'a', last: 1 }, { first: 1, last: 1 }, { first: 'a', last: 'a', other: 1 }, { first: 1, last: 'a', other: 1 }, { first: 'a', last: 1, other: 1 }, { first: 1, last: 1, other: 1 }, 'foo', ], input: { a: { properties: { first: { type: 'string' }, last: { type: 'string' }, }, type: 'object', }, b: { additionalProperties: false, properties: { first: { type: 'string' }, last: { type: 'string' }, }, type: 'object', }, }, output: { added: false, removed: { not: { additionalProperties: false, properties: { first: { type: 'string' }, last: { type: 'string' }, }, type: 'object', }, properties: { first: { type: 'string' }, last: { type: 'string' }, }, type: 'object', }, }, }, { description: 'objects with 2 property constraints has additionalProperties constrained', examples: [ {}, { first: 'a' }, { first: 1 }, { last: 'a' }, { last: 1 }, { a: 1 }, { a: [] }, { first: 'a', a: 1 }, { first: 'a', a: true }, { first: 'a', a: true, b: 1 }, { last: 'a', a: true, b: 1 }, { last: 'a', a: 1 }, { last: 'a', a: true }, { first: 'a', last: 'a', a: true, b: 1 }, 'foo', ], input: { a: { additionalProperties: { type: ['boolean', 'number'], }, properties: { first: { type: 'string' }, last: { type: 'string' }, }, type: 'object', }, b: { additionalProperties: { type: 'number', }, properties: { first: { type: 'string' }, last: { type: 'string' }, }, type: 'object', }, }, output: { added: false, removed: { additionalProperties: { type: ['boolean', 'number'], }, not: { additionalProperties: { type: 'number', }, properties: { first: { type: 'string' }, last: { type: 'string' }, }, type: 'object', }, properties: { first: { type: 'string' }, last: { type: 'string' }, }, type: 'object', }, }, }, ]; registerDiffTestCases(testCases); }); describe('maxProperties + required', () => { const testCases: DiffTestCase[] = [ { description: 'object with required and maxProperties contradiction', examples: [ {}, { first: 1 }, { last: 1 }, { first: 1, last: 1 }, { foo: 1 }, 'foo', ], input: { a: { maxProperties: 1, required: ['first', 'last'], type: 'object', }, b: false, }, output: { added: false, removed: false, }, }, ]; registerDiffTestCases(testCases); }); describe('maxProperties + minProperties', () => { const testCases: DiffTestCase[] = [ { description: 'object with minProperties and maxProperties contradiction', examples: [{}, { a: 1 }, { a: 1, b: 1 }, 'foo'], input: { a: { maxProperties: 0, minProperties: 1, type: 'object', }, b: { type: 'object', }, }, output: { added: { type: 'object' }, removed: false, }, }, ]; registerDiffTestCases(testCases); }); describe('properties + required', () => { const testCases: DiffTestCase[] = [ { description: 'comparing object contradictions at different layers', examples: [ {}, { name: 1 }, { foo: 1 }, { name: 1, foo: 1 }, { address: 1 }, { address: { street: 1 } }, 'foo', ], input: { a: { properties: { name: false, }, required: ['name'], type: 'object', }, b: { properties: { address: { properties: { street: false, }, required: ['street'], type: 'object', }, }, required: ['address'], type: 'object', }, }, output: { added: false, removed: false, }, }, { description: 'adding a required property with a changed schema', examples: [ {}, { name: 1 }, { first: 1 }, { first: 'a' }, { first: [] }, { foo: 1 }, { name: 1, first: 1 }, { name: 1, first: 'a' }, { name: 1, first: [] }, { name: 1, foo: 1 }, 'foo', ], input: { a: { properties: { first: { type: 'string', }, }, required: ['name'], type: 'object', }, b: { properties: { first: { type: 'array', }, }, required: ['name', 'first'], type: 'object', }, }, output: { added: { properties: { first: { type: 'array' }, }, required: ['name', 'first'], type: 'object', }, removed: { properties: { first: { type: 'string' }, }, required: ['name'], type: 'object', }, }, }, ]; registerDiffTestCases(testCases); }); describe('additionalProperties + properties + required', () => { const testCases: DiffTestCase[] = [ { description: 'object containing a contradiction has all constraints relaxed', examples: [{}, { foo: 1 }, { name: 1 }, { foo: 1, name: 1 }, 'foo'], input: { a: { additionalProperties: false, properties: { name: false, }, required: ['name'], type: 'object', }, b: { type: 'object', }, }, output: { added: { type: 'object' }, removed: false, }, }, ]; registerDiffTestCases(testCases); }); });