diff --git a/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json b/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json index 3858edeed6b..bdb8cada2a9 100644 --- a/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json +++ b/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json @@ -368,6 +368,7 @@ "properties": { "protocolVersion": { "type": "integer", + "minimum": 0, "$comment": "Maximum is the latest protocol version" }, "$schema": { diff --git a/packages/rs-dpp/src/tests/data_contract/validation/data_contract_validator_spec.rs b/packages/rs-dpp/src/tests/data_contract/validation/data_contract_validator_spec.rs index 4a75eda3254..f19c8c2fd3e 100644 --- a/packages/rs-dpp/src/tests/data_contract/validation/data_contract_validator_spec.rs +++ b/packages/rs-dpp/src/tests/data_contract/validation/data_contract_validator_spec.rs @@ -148,10 +148,12 @@ mod protocol { let result = data_contract_validator .validate(&raw_data_contract) - .expect_err("protocol error should be returned"); + .expect("validation result should be returned"); trace!("The validation result is: {:#?}", result); - assert!(matches!(result, ProtocolError::Error(..))) + let schema_error = get_schema_error(&result, 0); + assert_eq!("/protocolVersion", schema_error.instance_path().to_string()); + assert_eq!(Some("minimum"), schema_error.keyword(),); } } diff --git a/packages/wasm-dpp/src/data_contract/data_contract.rs b/packages/wasm-dpp/src/data_contract/data_contract.rs index 499555c02c6..cca7c13583f 100644 --- a/packages/wasm-dpp/src/data_contract/data_contract.rs +++ b/packages/wasm-dpp/src/data_contract/data_contract.rs @@ -35,11 +35,16 @@ impl std::convert::Into for DataContractWasm { #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct DataContractParameters { - #[serde(rename = "$schema")] - schema: String, - #[serde(rename = "$id")] - id: Vec, - owner_id: Vec, + #[serde( + rename = "$schema", + skip_serializing_if = "serde_json::Value::is_null", + default + )] + schema: serde_json::Value, + #[serde(rename = "$id", skip_serializing_if = "Option::is_none")] + id: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + owner_id: Option>, #[serde(skip_serializing_if = "serde_json::Value::is_null", default)] documents: serde_json::Value, #[serde( @@ -48,8 +53,13 @@ pub(crate) struct DataContractParameters { rename = "$defs" )] defs: serde_json::Value, - protocol_version: u32, - version: u32, + #[serde(skip_serializing_if = "serde_json::Value::is_null", default)] + protocol_version: serde_json::Value, + #[serde(skip_serializing_if = "serde_json::Value::is_null", default)] + version: serde_json::Value, + + #[serde(flatten)] + _extras: serde_json::Value, // Captures excess fields to trigger validation failure later. } #[wasm_bindgen(js_class=DataContract)] diff --git a/packages/wasm-dpp/src/data_contract/index.rs b/packages/wasm-dpp/src/data_contract/index.rs index 1dbdf9f6391..fcf3179f79a 100644 --- a/packages/wasm-dpp/src/data_contract/index.rs +++ b/packages/wasm-dpp/src/data_contract/index.rs @@ -1,6 +1,9 @@ use dpp::{data_contract::document_type::IndexProperty, util::json_schema::Index}; +use serde::{ser::SerializeMap, Serialize, Serializer}; use wasm_bindgen::prelude::*; +use crate::{errors::RustConversionError, with_js_error}; + #[wasm_bindgen(js_name=IndexProperty)] #[derive(Debug, Clone)] pub struct IndexPropertyWasm { @@ -38,6 +41,51 @@ impl From for IndexDefinitionWasm { } } +/// Wrapper structure to serialize `Index` the way js-dpp expects it. +#[derive(Debug)] +struct IndexSerializeJs<'a>(&'a Index); + +impl Serialize for IndexSerializeJs<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(3))?; + map.serialize_entry("name", &self.0.name)?; + if self.0.unique { + map.serialize_entry("unique", &true)?; + } + map.serialize_entry( + "properties", + &self + .0 + .properties + .iter() + .map(IndexPropertySerializeJs) + .collect::>(), + )?; + + map.end() + } +} + +/// Wrapper structure to serialize `InderProperty` the way js-dpp expects it. +#[derive(Debug)] +struct IndexPropertySerializeJs<'a>(&'a IndexProperty); + +impl Serialize for IndexPropertySerializeJs<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(1))?; + let order_str = if self.0.ascending { "asc" } else { "desc" }; + map.serialize_entry(&self.0.name, order_str)?; + + map.end() + } +} + #[wasm_bindgen(js_class=IndexDefinition)] impl IndexDefinitionWasm { #[wasm_bindgen(js_name=getName)] @@ -58,4 +106,11 @@ impl IndexDefinitionWasm { pub fn is_unique(&self) -> bool { self.inner.unique } + + #[wasm_bindgen(js_name=toObject)] + pub fn to_object(&self) -> Result { + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + let object = with_js_error!(IndexSerializeJs(&self.inner).serialize(&serializer))?; + Ok(object) + } } diff --git a/packages/wasm-dpp/src/errors/protocol_error.rs b/packages/wasm-dpp/src/errors/protocol_error.rs index 629aed99264..444a5020453 100644 --- a/packages/wasm-dpp/src/errors/protocol_error.rs +++ b/packages/wasm-dpp/src/errors/protocol_error.rs @@ -1,12 +1,15 @@ -use wasm_bindgen::JsValue; +use wasm_bindgen::{JsError, JsValue}; use super::consensus_error::from_consensus_error; -pub fn from_protocol_error(e: dpp::ProtocolError) -> JsValue { - match e { +pub fn from_protocol_error(protocol_error: dpp::ProtocolError) -> JsValue { + match protocol_error { dpp::ProtocolError::AbstractConsensusError(consensus_error) => { from_consensus_error(*consensus_error) } - _ => unimplemented!(), + dpp::ProtocolError::Error(anyhow_error) => { + format!("Non-protocol error: {}", anyhow_error).into() + } + _ => todo!("Implement protocol error conversions"), } } diff --git a/packages/wasm-dpp/src/validation/validation_result.rs b/packages/wasm-dpp/src/validation/validation_result.rs index c220f725f80..5125730c64f 100644 --- a/packages/wasm-dpp/src/validation/validation_result.rs +++ b/packages/wasm-dpp/src/validation/validation_result.rs @@ -4,6 +4,7 @@ use js_sys::JsString; use wasm_bindgen::prelude::*; #[wasm_bindgen(js_name=ValidationResult)] +#[derive(Debug)] pub struct ValidationResultWasm(ValidationResult); impl From> for ValidationResultWasm diff --git a/packages/wasm-dpp/test/integration/dataContract/validation/validateDataContractFactory.spec.js b/packages/wasm-dpp/test/integration/dataContract/validation/validateDataContractFactory.spec.js index 225114c7186..89b01f8d1dd 100644 --- a/packages/wasm-dpp/test/integration/dataContract/validation/validateDataContractFactory.spec.js +++ b/packages/wasm-dpp/test/integration/dataContract/validation/validateDataContractFactory.spec.js @@ -1,86 +1,65 @@ -const { getRE2Class } = require('@dashevo/wasm-re2'); const lodashCloneDeep = require('lodash/cloneDeep'); - -const $RefParser = require('@apidevtools/json-schema-ref-parser'); - -const createAjv = require('@dashevo/dpp/lib/ajv/createAjv'); - -const JsonSchemaValidator = require('@dashevo/dpp/lib/validation/JsonSchemaValidator'); - -const ValidationResult = require('@dashevo/dpp/lib/validation/ValidationResult'); - -const validateDataContractFactory = require('@dashevo/dpp/lib/dataContract/validation/validateDataContractFactory'); -const validateDataContractMaxDepthFactory = require('@dashevo/dpp/lib/dataContract/validation/validateDataContractMaxDepthFactory'); -const enrichDataContractWithBaseSchema = require('@dashevo/dpp/lib/dataContract/enrichDataContractWithBaseSchema'); -const validateDataContractPatternsFactory = require('@dashevo/dpp/lib/dataContract/validation/validateDataContractPatternsFactory'); - const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const { expectJsonSchemaError, expectValidationError } = require('../../../../lib/test/expect/expectError'); -const { expectJsonSchemaError, expectValidationError } = require('@dashevo/dpp/lib/test/expect/expectError'); - -const DuplicateIndexError = require('@dashevo/dpp/lib/errors/consensus/basic/dataContract/DuplicateIndexError'); -const UndefinedIndexPropertyError = require('@dashevo/dpp/lib/errors/consensus/basic/dataContract/UndefinedIndexPropertyError'); -const InvalidIndexPropertyTypeError = require('@dashevo/dpp/lib/errors/consensus/basic/dataContract/InvalidIndexPropertyTypeError'); -const SystemPropertyIndexAlreadyPresentError = require('@dashevo/dpp/lib/errors/consensus/basic/dataContract/SystemPropertyIndexAlreadyPresentError'); -const UniqueIndicesLimitReachedError = require('@dashevo/dpp/lib/errors/consensus/basic/dataContract/UniqueIndicesLimitReachedError'); -const InvalidIndexedPropertyConstraintError = require('@dashevo/dpp/lib/errors/consensus/basic/dataContract/InvalidIndexedPropertyConstraintError'); -const InvalidCompoundIndexError = require('@dashevo/dpp/lib/errors/consensus/basic/dataContract/InvalidCompoundIndexError'); -const IncompatibleRe2PatternError = require('@dashevo/dpp/lib/errors/consensus/basic/dataContract/IncompatibleRe2PatternError'); -const InvalidJsonSchemaRefError = require('@dashevo/dpp/lib/errors/consensus/basic/dataContract/InvalidJsonSchemaRefError'); -const JsonSchemaCompilationError = require('@dashevo/dpp/lib/errors/consensus/basic/JsonSchemaCompilationError'); -const SomeConsensusError = require('@dashevo/dpp/lib/test/mocks/SomeConsensusError'); -const getPropertyDefinitionByPath = require('@dashevo/dpp/lib/dataContract/getPropertyDefinitionByPath'); - -describe('validateDataContractFactory', function main() { - this.timeout(15000); +const { default: loadWasmDpp } = require('../../../../dist'); +describe('validateDataContractFactory', () => { let dataContract; - let rawDataContract; let validateDataContract; - let RE2; - let validateProtocolVersionMock; + + let DataContractValidator; + let ValidationResult; + let JsonSchemaCompilationError; + let IncompatibleRe2PatternError; + let DuplicateIndexError; + let UndefinedIndexPropertyError; + let UniqueIndicesLimitReachedError; + let SystemPropertyIndexAlreadyPresentError; + let InvalidIndexPropertyTypeError; + let InvalidCompoundIndexError; + let InvalidIndexedPropertyConstraintError; + let InvalidJsonSchemaRefError; before(async () => { - RE2 = await getRE2Class(); + ({ + DataContractValidator, + ValidationResult, + JsonSchemaCompilationError, + IncompatibleRe2PatternError, + DuplicateIndexError, + UndefinedIndexPropertyError, + UniqueIndicesLimitReachedError, + SystemPropertyIndexAlreadyPresentError, + InvalidIndexPropertyTypeError, + InvalidCompoundIndexError, + InvalidIndexedPropertyConstraintError, + InvalidJsonSchemaRefError, + } = await loadWasmDpp()); }); - beforeEach(async function beforeEach() { + beforeEach(async () => { dataContract = getDataContractFixture(); - rawDataContract = dataContract.toObject(); - - const ajv = createAjv(RE2); - const jsonSchemaValidator = new JsonSchemaValidator(ajv); - const validateDataContractMaxDepth = validateDataContractMaxDepthFactory($RefParser); + const dataContractValidator = new DataContractValidator(); - const validateDataContractPatterns = validateDataContractPatternsFactory(RE2); - - validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + validateDataContract = (contract) => dataContractValidator.validate(contract); + }); - validateDataContract = validateDataContractFactory( - jsonSchemaValidator, - validateDataContractMaxDepth, - enrichDataContractWithBaseSchema, - validateDataContractPatterns, - validateProtocolVersionMock, - getPropertyDefinitionByPath, - ); + it('should pass validation if created from a fixture', () => { + const rawDataContract = dataContract.toObject(); + const result = validateDataContract(rawDataContract); + expect(result.isValid()).to.be.true(); }); describe('protocolVersion', () => { it('should be present', async () => { - rawDataContract = { - documents: { - someDocument: { - type: 'object', - }, - }, - }; + const rawDataContract = dataContract.toObject(); delete rawDataContract.protocolVersion; - const result = await validateDataContract(rawDataContract); + const result = validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -90,11 +69,13 @@ describe('validateDataContractFactory', function main() { }); it('should be an integer', async () => { + const rawDataContract = dataContract.toObject(); + rawDataContract.protocolVersion = '1'; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -102,37 +83,28 @@ describe('validateDataContractFactory', function main() { expect(error.getKeyword()).to.equal('type'); }); - it('should be valid', async () => { + it('should be an unsigned integer', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.protocolVersion = -1; - const protocolVersionError = new SomeConsensusError('test'); - const protocolVersionResult = new ValidationResult([ - protocolVersionError, - ]); - - validateProtocolVersionMock.returns(protocolVersionResult); - const result = await validateDataContract(rawDataContract); - - expectValidationError(result, SomeConsensusError); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error).to.equal(protocolVersionError); - - expect(validateProtocolVersionMock).to.be.calledOnceWith( - rawDataContract.protocolVersion, - ); + expect(error.getInstancePath()).to.equal('/protocolVersion'); + expect(error.getKeyword()).to.equal('minimum'); }); }); describe('$schema', () => { it('should be present', async () => { + const rawDataContract = dataContract.toObject(); delete rawDataContract.$schema; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -142,24 +114,29 @@ describe('validateDataContractFactory', function main() { }); it('should be a string', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.$schema = 1; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result, 2); - const [error] = result.getErrors(); + const [typeError, constError] = result.getErrors(); - expect(error.getInstancePath()).to.equal('/$schema'); - expect(error.getKeyword()).to.equal('type'); + expect(typeError.getInstancePath()).to.equal('/$schema'); + expect(typeError.getKeyword()).to.equal('type'); + + expect(constError.getInstancePath()).to.equal('/$schema'); + expect(constError.getKeyword()).to.equal('const'); }); it('should be a particular url', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.$schema = 'wrong'; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -170,11 +147,12 @@ describe('validateDataContractFactory', function main() { describe('ownerId', () => { it('should be present', async () => { + const rawDataContract = dataContract.toObject(); delete rawDataContract.ownerId; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -184,26 +162,26 @@ describe('validateDataContractFactory', function main() { }); it('should be a byte array', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.ownerId = new Array(32).fill('string'); - const result = await validateDataContract(rawDataContract); - - expectJsonSchemaError(result, 2); - - const [error, byteArrayError] = result.getErrors(); - - expect(error.getInstancePath()).to.equal('/ownerId/0'); - expect(error.getKeyword()).to.equal('type'); - - expect(byteArrayError.getKeyword()).to.equal('byteArray'); + // We decided to keep protocol error because of failed parse rather than doing hacks + // too keep it until validator will refuse it + try { + await validateDataContract(rawDataContract); + expect.fail('Should throw an error'); + } catch (e) { + expect(e).to.have.string('invalid type: string'); + } }); it('should be no less than 32 bytes', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.ownerId = Buffer.alloc(31); const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -212,11 +190,12 @@ describe('validateDataContractFactory', function main() { }); it('should be no longer than 32 bytes', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.ownerId = Buffer.alloc(33); const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -227,11 +206,12 @@ describe('validateDataContractFactory', function main() { describe('$id', () => { it('should be present', async () => { + const rawDataContract = dataContract.toObject(); delete rawDataContract.$id; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -241,26 +221,26 @@ describe('validateDataContractFactory', function main() { }); it('should be a byte array', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.$id = new Array(32).fill('string'); - const result = await validateDataContract(rawDataContract); - - expectJsonSchemaError(result, 2); - - const [error, byteArrayError] = result.getErrors(); - - expect(error.getInstancePath()).to.equal('/$id/0'); - expect(error.getKeyword()).to.equal('type'); - - expect(byteArrayError.getKeyword()).to.equal('byteArray'); + // We decided to keep protocol error because of failed parse rather than doing hacks + // too keep it until validator will refuse it + try { + await validateDataContract(rawDataContract); + expect.fail('Should throw an error'); + } catch (e) { + expect(e).to.have.string('invalid type: string'); + } }); it('should be no less than 32 bytes', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.$id = Buffer.alloc(31); const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -269,11 +249,12 @@ describe('validateDataContractFactory', function main() { }); it('should be no longer than 32 bytes', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.$id = Buffer.alloc(33); const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -284,6 +265,7 @@ describe('validateDataContractFactory', function main() { describe('$defs', () => { it('may not be present', async () => { + const rawDataContract = dataContract.toObject(); delete rawDataContract.$defs; delete rawDataContract.documents.prettyDocument; @@ -294,11 +276,12 @@ describe('validateDataContractFactory', function main() { }); it('should be an object', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.$defs = 1; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -307,11 +290,12 @@ describe('validateDataContractFactory', function main() { }); it('should not be empty', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.$defs = {}; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -320,24 +304,39 @@ describe('validateDataContractFactory', function main() { }); it('should have no non-alphanumeric properties', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.$defs = { $subSchema: {}, }; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); - - const [patternError, propertyNamesError] = result.getErrors(); + await expectJsonSchemaError(result); - expect(patternError.getInstancePath()).to.equal('/$defs'); - expect(patternError.getKeyword()).to.equal('pattern'); + const [propertyNamesError] = result.getErrors(); expect(propertyNamesError.getInstancePath()).to.equal('/$defs'); expect(propertyNamesError.getKeyword()).to.equal('propertyNames'); }); + it('should have valid properties\' values', async () => { + const rawDataContract = dataContract.toObject(); + rawDataContract.$defs = { + yeet: '3a1u9a', + }; + + const result = await validateDataContract(rawDataContract); + + await expectJsonSchemaError(result, 2); + + const [valueTypeError] = result.getErrors(); + + expect(valueTypeError.getInstancePath()).to.equal('/$defs/yeet'); + expect(valueTypeError.getKeyword()).to.equal('type'); + }); + it('should have no more than 100 properties', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.$defs = {}; Array(101).fill({ type: 'string' }).forEach((item, i) => { @@ -346,7 +345,7 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -361,20 +360,20 @@ describe('validateDataContractFactory', function main() { await Promise.all( validNames.map(async (name) => { - const clonedDataContract = lodashCloneDeep(rawDataContract); + const rawDataContract = dataContract.toObject(); - clonedDataContract.$defs = {}; - clonedDataContract.$defs[name] = { + rawDataContract.$defs = {}; + rawDataContract.$defs[name] = { type: 'string', }; - clonedDataContract.$defs[name] = { + rawDataContract.$defs[name] = { type: 'string', }; - const result = await validateDataContract(clonedDataContract); + const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 0); + await expectJsonSchemaError(result, 0); }), ); }); @@ -384,21 +383,21 @@ describe('validateDataContractFactory', function main() { await Promise.all( invalidNames.map(async (name) => { - const clonedDataContract = lodashCloneDeep(rawDataContract); + const rawDataContract = dataContract.toObject(); - clonedDataContract.$defs = {}; - clonedDataContract.$defs[name] = { + rawDataContract.$defs = {}; + rawDataContract.$defs[name] = { type: 'string', }; - const result = await validateDataContract(clonedDataContract); + const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result); const [error] = result.getErrors(); expect(error.getInstancePath()).to.equal('/$defs'); - expect(error.getKeyword()).to.equal('pattern'); + expect(error.getKeyword()).to.equal('propertyNames'); }), ); }); @@ -406,11 +405,12 @@ describe('validateDataContractFactory', function main() { describe('documents', () => { it('should be present', async () => { + const rawDataContract = dataContract.toObject(); delete rawDataContract.documents; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -420,11 +420,12 @@ describe('validateDataContractFactory', function main() { }); it('should be an object', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents = 1; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -433,11 +434,12 @@ describe('validateDataContractFactory', function main() { }); it('should not be empty', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents = {}; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -446,6 +448,7 @@ describe('validateDataContractFactory', function main() { }); it('should have valid property names (document types)', async () => { + const rawDataContract = dataContract.toObject(); const validNames = ['validName', 'valid_name', 'valid-name', 'abc', 'a123123bc', 'ab123c', 'ValidName', 'validName', 'abcdefghigklmnopqrstuvwxyz01234567890abcdefghigklmnopqrstuvwxyz', 'abc_gbf_gdb', 'abc-gbf-gdb']; @@ -457,12 +460,13 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(clonedDataContract); - expectJsonSchemaError(result, 0); + await expectJsonSchemaError(result, 0); }), ); }); it('should return an invalid result if a property (document type) has invalid format', async () => { + const rawDataContract = dataContract.toObject(); const invalidNames = ['*(*&^', '$test', '.', '.a']; await Promise.all( @@ -473,17 +477,18 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(clonedDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result); const [error] = result.getErrors(); expect(error.getInstancePath()).to.equal('/documents'); - expect(error.getKeyword()).to.equal('pattern'); + expect(error.getKeyword()).to.equal('propertyNames'); }), ); }); it('should have no more than 100 properties', async () => { + const rawDataContract = dataContract.toObject(); const niceDocumentDefinition = rawDataContract.documents.niceDocument; rawDataContract.documents = {}; @@ -494,7 +499,7 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -504,46 +509,50 @@ describe('validateDataContractFactory', function main() { describe('Document schema', () => { it('should not be empty', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.niceDocument.properties = {}; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/niceDocument/properties'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/properties'); expect(error.getKeyword()).to.equal('minProperties'); }); it('should have type "object"', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.niceDocument.type = 'string'; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/niceDocument/type'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/type'); expect(error.getKeyword()).to.equal('const'); }); it('should have "properties"', async () => { + const rawDataContract = dataContract.toObject(); delete rawDataContract.documents.niceDocument.properties; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/niceDocument'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument'); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('properties'); }); it('should have nested "properties"', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.niceDocument.properties.object = { type: 'array', prefixItems: [ @@ -562,16 +571,17 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 3); + await expectJsonSchemaError(result, 8); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/niceDocument/properties/object/prefixItems/0/properties/something'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/properties/object/prefixItems/0/properties/something'); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('properties'); }); it('should have valid property names', async () => { + const rawDataContract = dataContract.toObject(); const validNames = ['validName', 'valid_name', 'valid-name', 'abc', 'a123bc', 'abc123', 'ValidName', 'validName', 'abcdefghigklmnopqrstuvwxyz01234567890abcdefghigklmnopqrstuvwxyz', 'abc_gbf_gdb', 'abc-gbf-gdb']; @@ -585,12 +595,13 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(clonedDataContract); - expectJsonSchemaError(result, 0); + await expectJsonSchemaError(result, 0); }), ); }); it('should have valid nested property names', async () => { + const rawDataContract = dataContract.toObject(); const validNames = ['validName', 'valid_name', 'valid-name', 'abc', 'a123bc', 'abc123', 'ValidName', 'validName', 'abcdefghigklmnopqrstuvwxyz01234567890abcdefghigklmnopqrstuvwxyz', 'abc_gbf_gdb', 'abc-gbf-gdb']; @@ -610,12 +621,13 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(clonedDataContract); - expectJsonSchemaError(result, 0); + await expectJsonSchemaError(result, 0); }), ); }); it('should return an invalid result if a property has invalid format', async () => { + const rawDataContract = dataContract.toObject(); const invalidNames = ['*(*&^', '$test', '.', '.a']; await Promise.all( @@ -626,19 +638,18 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(clonedDataContract); - expectJsonSchemaError(result, 3); + await expectJsonSchemaError(result, 2); - const errors = result.getErrors(); + const [error] = result.getErrors(); - expect(errors[0].instancePath).to.equal('/documents/niceDocument/properties'); - expect(errors[0].keyword).to.equal('pattern'); - expect(errors[1].instancePath).to.equal('/documents/niceDocument/properties'); - expect(errors[1].keyword).to.equal('propertyNames'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/properties'); + expect(error.getKeyword()).to.equal('propertyNames'); }), ); }); it('should return an invalid result if a nested property has invalid format', async () => { + const rawDataContract = dataContract.toObject(); const invalidNames = ['*(*&^', '$test', '.', '.a']; rawDataContract.documents.niceDocument.properties.something = { @@ -654,50 +665,49 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(clonedDataContract); - expectJsonSchemaError(result, 4); + await expectJsonSchemaError(result, 4); - const errors = result.getErrors(); + const [errors] = result.getErrors(); - expect(errors[0].instancePath).to.equal( - '/documents/niceDocument/properties/something/properties', - ); - expect(errors[0].keyword).to.equal('pattern'); - expect(errors[1].instancePath).to.equal( + expect(errors.getInstancePath()).to.equal( '/documents/niceDocument/properties/something/properties', ); - expect(errors[1].keyword).to.equal('propertyNames'); + expect(errors.getKeyword()).to.equal('propertyNames'); }), ); }); it('should have "additionalProperties" defined', async () => { + const rawDataContract = dataContract.toObject(); delete rawDataContract.documents.niceDocument.additionalProperties; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/niceDocument'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument'); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('additionalProperties'); }); it('should have "additionalProperties" defined to false', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.niceDocument.additionalProperties = true; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/niceDocument/additionalProperties'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/additionalProperties'); expect(error.getKeyword()).to.equal('const'); }); it('should have nested "additionalProperties" defined', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.niceDocument.properties.object = { type: 'array', prefixItems: [ @@ -715,30 +725,32 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/niceDocument/properties/object/prefixItems/0'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/properties/object/prefixItems/0'); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('additionalProperties'); }); it('should return invalid result if there are additional properties', async () => { - rawDataContract.additionalProperty = { }; + const rawDataContract = dataContract.toObject(); + rawDataContract.additionalProperty = {}; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal(''); + expect(error.getInstancePath()).to.equal(''); expect(error.getKeyword()).to.equal('additionalProperties'); }); it('should have no more than 100 properties', async () => { - const propertyDefinition = { }; + const rawDataContract = dataContract.toObject(); + const propertyDefinition = {}; rawDataContract.documents.niceDocument.properties = {}; @@ -748,15 +760,16 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 406); - const [error] = result.getErrors(); + const errors = result.getErrors(); - expect(error.instancePath).to.equal('/documents/niceDocument/properties'); - expect(error.getKeyword()).to.equal('maxProperties'); + expect(errors[405].getInstancePath()).to.equal('/documents/niceDocument/properties'); + expect(errors[405].getKeyword()).to.equal('maxProperties'); }); it('should have defined items for arrays', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.new = { properties: { something: { @@ -768,16 +781,17 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/new/properties/something'); + expect(error.getInstancePath()).to.equal('/documents/new/properties/something'); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('items'); }); it('should have sub schema in items for arrays', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.new = { properties: { something: { @@ -797,16 +811,17 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 3); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/new/properties/something/items'); + expect(error.getInstancePath()).to.equal('/documents/new/properties/something/items'); expect(error.getKeyword()).to.equal('type'); expect(error.getParams().type).to.equal('object'); }); it('should have items if prefixItems is used for arrays', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.new = { type: 'object', properties: { @@ -828,16 +843,17 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 4); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/new/properties/something'); + expect(error.getInstancePath()).to.equal('/documents/new/properties/something'); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('items'); }); it('should not have items disabled if prefixItems is used for arrays', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.new = { properties: { something: { @@ -858,44 +874,49 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/new/properties/something/items'); + expect(error.getInstancePath()).to.equal('/documents/new/properties/something/items'); expect(error.getKeyword()).to.equal('const'); expect(error.getParams().allowedValue).to.equal(false); }); - it('should return invalid result if "default" keyword is used', async () => { + // TODO: jsonschema-rs unevaluatedProperties + it.skip('should return invalid result if "default" keyword is used', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.properties.firstName.default = '1'; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/properties/firstName'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/properties/firstName'); expect(error.getKeyword()).to.equal('unevaluatedProperties'); }); - it.skip('should return invalid result if remote `$ref` is used', async () => { + it('should return invalid result if remote `$ref` is used', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument = { $ref: 'http://remote.com/schema#', }; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/$ref'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/$ref'); expect(error.getKeyword()).to.equal('pattern'); }); - it('should not have `propertyNames`', async () => { + // TODO: jsonschema-rs unevaluatedProperties + it.skip('should not have `propertyNames`', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument = { type: 'object', properties: { @@ -911,16 +932,17 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument'); expect(error.getKeyword()).to.equal('unevaluatedProperties'); expect(error.getParams().unevaluatedProperty).to.equal('propertyNames'); }); it('should have `maxItems` if `uniqueItems` is used', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument = { type: 'object', properties: { @@ -934,16 +956,17 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 4); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/properties/something'); expect(error.getKeyword()).to.equal('required'); - expect(error.getParams().missingProperty).to.equal('items'); + expect(error.getParams().missingProperty).to.equal('maxItems'); }); it('should have `maxItems` no bigger than 100000 if `uniqueItems` is used', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument = { type: 'object', properties: { @@ -967,15 +990,16 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something/maxItems'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/properties/something/maxItems'); expect(error.getKeyword()).to.equal('maximum'); }); it('should return invalid result if document JSON Schema is not valid', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument = { type: 'object', properties: { @@ -993,15 +1017,13 @@ describe('validateDataContractFactory', function main() { expectValidationError(result, JsonSchemaCompilationError); const [error] = result.getErrors(); - - expect(error.getCode()).to.equal(1004); - - expect(error.message).to.be.a('string').and.satisfy((msg) => ( - msg.startsWith('unknown format "lalala" ignored in schema') - )); + // TODO: should be a JsonSchemaCompilationerror instead of JsonSchemaError + expect(error.getCode()).to.equal(1005); + expect(error.getKeyword()).to.equal('format'); }); it('should have `maxLength` if `pattern` is used', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument = { type: 'object', properties: { @@ -1015,16 +1037,17 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/properties/something'); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('maxLength'); }); it('should have `maxLength` no bigger than 50000 if `pattern` is used', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument = { type: 'object', properties: { @@ -1039,15 +1062,16 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something/maxLength'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/properties/something/maxLength'); expect(error.getKeyword()).to.equal('maximum'); }); it('should have `maxLength` if `format` is used', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument = { type: 'object', properties: { @@ -1061,16 +1085,17 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/properties/something'); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('maxLength'); }); it('should have `maxLength` no bigger than 50000 if `format` is used', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument = { type: 'object', properties: { @@ -1085,15 +1110,16 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something/maxLength'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/properties/something/maxLength'); expect(error.getKeyword()).to.equal('maximum'); }); it('should not have incompatible patterns', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument = { type: 'object', properties: { @@ -1115,52 +1141,55 @@ describe('validateDataContractFactory', function main() { expect(error.getCode()).to.equal(1009); expect(error.getPattern()).to.equal('^((?!-|_)[a-zA-Z0-9-_]{0,62}[a-zA-Z0-9])$'); expect(error.getPath()).to.equal('/documents/indexedDocument/properties/something'); - expect(error.getPatternError()).to.be.instanceOf(Error); }); describe('byteArray', () => { it('should be a boolean', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.withByteArrays.properties.byteArrayField.byteArray = 1; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 4); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/withByteArrays/properties/byteArrayField/byteArray'); + expect(error.getInstancePath()).to.equal('/documents/withByteArrays/properties/byteArrayField/byteArray'); expect(error.getKeyword()).to.equal('type'); expect(error.getParams().type).to.equal('boolean'); }); it('should equal to true', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.withByteArrays.properties.byteArrayField.byteArray = false; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/withByteArrays/properties/byteArrayField/byteArray'); + expect(error.getInstancePath()).to.equal('/documents/withByteArrays/properties/byteArrayField/byteArray'); expect(error.getKeyword()).to.equal('const'); expect(error.getParams().allowedValue).to.equal(true); }); it('should be used with type `array`', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.withByteArrays.properties.byteArrayField.type = 'string'; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/withByteArrays/properties/byteArrayField/type'); + expect(error.getInstancePath()).to.equal('/documents/withByteArrays/properties/byteArrayField/type'); expect(error.getKeyword()).to.equal('const'); }); it('should not be used with `items`', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.withByteArrays.properties.byteArrayField.items = { type: 'string', }; @@ -1172,48 +1201,50 @@ describe('validateDataContractFactory', function main() { const [error] = result.getErrors(); expect(error.getCode()).to.equal(1004); - expect(error.message).to.equal("'byteArray' should not be used with 'items'"); }); }); describe('contentMediaType', () => { describe('application/x.dash.dpp.identifier', () => { it('should be used with byte array only', async () => { + const rawDataContract = dataContract.toObject(); delete rawDataContract.documents.withByteArrays.properties.identifierField.byteArray; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 4); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/withByteArrays/properties/identifierField'); + expect(error.getInstancePath()).to.equal('/documents/withByteArrays/properties/identifierField'); expect(error.getKeyword()).to.equal('required'); }); it('should be used with byte array not shorter than 32 bytes', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.withByteArrays.properties.identifierField.minItems = 31; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/withByteArrays/properties/identifierField/minItems'); + expect(error.getInstancePath()).to.equal('/documents/withByteArrays/properties/identifierField/minItems'); expect(error.getKeyword()).to.equal('const'); }); it('should be used with byte array not longer than 32 bytes', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.withByteArrays.properties.identifierField.maxItems = 31; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/withByteArrays/properties/identifierField/maxItems'); + expect(error.getInstancePath()).to.equal('/documents/withByteArrays/properties/identifierField/maxItems'); expect(error.getKeyword()).to.equal('const'); }); }); @@ -1223,32 +1254,35 @@ describe('validateDataContractFactory', function main() { describe('indices', () => { it('should be an array', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.indices = 'definitely not an array'; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/indices'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/indices'); expect(error.getKeyword()).to.equal('type'); }); it('should have at least one item', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.indices = []; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/indices'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/indices'); expect(error.getKeyword()).to.equal('minItems'); }); it('should return invalid result if there are duplicated indices', async () => { + const rawDataContract = dataContract.toObject(); const indexDefinition = { ...rawDataContract.documents.indexedDocument.indices[0], name: 'otherIndexName', @@ -1263,11 +1297,12 @@ describe('validateDataContractFactory', function main() { const [error] = result.getErrors(); expect(error.getCode()).to.equal(1008); - expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); expect(error.getDocumentType()).to.deep.equal('indexedDocument'); }); it('should return invalid result if there are duplicated index names', async () => { + const rawDataContract = dataContract.toObject(); const indexDefinition = { ...rawDataContract.documents.indexedDocument.indices[0], }; @@ -1287,66 +1322,71 @@ describe('validateDataContractFactory', function main() { describe('index', () => { it('should be an object', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.indices = ['something else']; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/indices/0'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/indices/0'); expect(error.getKeyword()).to.equal('type'); }); it('should have properties definition', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.indices = [{}]; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/indices/0'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/indices/0'); expect(error.getParams().missingProperty).to.equal('properties'); expect(error.getKeyword()).to.equal('required'); }); describe('properties definition', () => { it('should be an array', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.indices[0] .properties = 'something else'; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal( + expect(error.getInstancePath()).to.equal( '/documents/indexedDocument/indices/0/properties', ); expect(error.getKeyword()).to.equal('type'); }); it('should have at least one property defined', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.indices[0] .properties = []; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal( + expect(error.getInstancePath()).to.equal( '/documents/indexedDocument/indices/0/properties', ); expect(error.getKeyword()).to.equal('minItems'); }); it('should have no more than 10 property $defs', async () => { + const rawDataContract = dataContract.toObject(); for (let i = 0; i < 10; i++) { rawDataContract.documents.indexedDocument.indices[0] .properties.push({ [`field${i}`]: 'asc' }); @@ -1354,11 +1394,11 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal( + expect(error.getInstancePath()).to.equal( '/documents/indexedDocument/indices/0/properties', ); expect(error.getKeyword()).to.equal('maxItems'); @@ -1366,38 +1406,41 @@ describe('validateDataContractFactory', function main() { describe('property definition', () => { it('should be an object', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.indices[0] .properties[0] = 'something else'; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal( + expect(error.getInstancePath()).to.equal( '/documents/indexedDocument/indices/0/properties/0', ); expect(error.getKeyword()).to.equal('type'); }); it('should have at least one property', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.indices[0] .properties = []; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal( + expect(error.getInstancePath()).to.equal( '/documents/indexedDocument/indices/0/properties', ); expect(error.getKeyword()).to.equal('minItems'); }); it('should have no more than one property', async () => { + const rawDataContract = dataContract.toObject(); const property = rawDataContract.documents.indexedDocument.indices[0] .properties[0]; @@ -1405,27 +1448,28 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result, 2); - const [error] = result.getErrors(); + const error = result.getErrors()[1]; - expect(error.instancePath).to.equal( + expect(error.getInstancePath()).to.equal( '/documents/indexedDocument/indices/0/properties/0', ); expect(error.getKeyword()).to.equal('maxProperties'); }); it('should have property values only "asc" or "desc"', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.indices[0] .properties[0].$ownerId = 'wrong'; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal( + expect(error.getInstancePath()).to.equal( '/documents/indexedDocument/indices/0/properties/0/$ownerId', ); expect(error.getKeyword()).to.equal('enum'); @@ -1435,6 +1479,7 @@ describe('validateDataContractFactory', function main() { describe('property names', () => { it('should have valid property names (indices)', async () => { + const rawDataContract = dataContract.toObject(); const validNames = ['validName', 'valid_name', 'valid-name', 'abc', 'a123123bc', 'ab123c', 'ValidName', 'validName', 'abcdefghigklmnopqrstuvwxyz01234567890abcdefghigklmnopqrstuvwxyz', 'abc_gbf_gdb', 'abc-gbf-gdb']; @@ -1448,12 +1493,13 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(clonedDataContract); - expectJsonSchemaError(result, 0); + await expectJsonSchemaError(result, 0); }), ); }); it('should return an invalid result if a property (indices) has invalid format', async () => { + const rawDataContract = dataContract.toObject(); const invalidNames = ['a.', '.a']; rawDataContract.documents.indexedDocument = { @@ -1495,19 +1541,21 @@ describe('validateDataContractFactory', function main() { }); it('should have "unique" flag to be of a boolean type', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.indices[0].unique = 12; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/indices/0/unique'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/indices/0/unique'); expect(error.getKeyword()).to.equal('type'); }); it('should have no more than 10 indices', async () => { + const rawDataContract = dataContract.toObject(); for (let i = 0; i < 10; i++) { const propertyName = `field${i}`; @@ -1520,17 +1568,18 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result, 11); - const [error] = result.getErrors(); + const error = result.getErrors()[10]; - expect(error.instancePath).to.equal( + expect(error.getInstancePath()).to.equal( '/documents/indexedDocument/indices', ); expect(error.getKeyword()).to.equal('maxItems'); }); it('should have no more than 3 unique indices', async () => { + const rawDataContract = dataContract.toObject(); for (let i = 0; i < 4; i++) { const propertyName = `field${i}`; @@ -1557,6 +1606,7 @@ describe('validateDataContractFactory', function main() { }); it('should return invalid result if $id is specified as an indexed property', async () => { + const rawDataContract = dataContract.toObject(); const indexDefinition = { name: 'index_1', properties: [ @@ -1578,10 +1628,11 @@ describe('validateDataContractFactory', function main() { expect(error.getCode()).to.equal(1015); expect(error.getPropertyName()).to.equal('$id'); expect(error.getDocumentType()).to.deep.equal('indexedDocument'); - expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); }); it('should return invalid result if indices has undefined property', async () => { + const rawDataContract = dataContract.toObject(); const indexDefinition = rawDataContract.documents.indexedDocument.indices[0]; indexDefinition.properties.push({ @@ -1597,10 +1648,11 @@ describe('validateDataContractFactory', function main() { expect(error.getCode()).to.equal(1016); expect(error.getPropertyName()).to.equal('missingProperty'); expect(error.getDocumentType()).to.deep.equal('indexedDocument'); - expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); }); it('should return invalid result if index property is object', async () => { + const rawDataContract = dataContract.toObject(); const indexedDocumentDefinition = rawDataContract.documents.indexedDocument; indexedDocumentDefinition.properties.objectProperty = { @@ -1631,10 +1683,11 @@ describe('validateDataContractFactory', function main() { expect(error.getPropertyName()).to.equal('objectProperty'); expect(error.getPropertyType()).to.equal('object'); expect(error.getDocumentType()).to.deep.equal('indexedDocument'); - expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); }); it('should return invalid result if index property is an array', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedArray = { type: 'object', indices: [ @@ -1674,167 +1727,177 @@ describe('validateDataContractFactory', function main() { expect(error.getPropertyName()).to.equal('mentions'); expect(error.getPropertyType()).to.equal('array'); expect(error.getDocumentType()).to.deep.equal('indexedArray'); - expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); + }); + + it('should return invalid result if index property is array of objects', async () => { + const rawDataContract = dataContract.toObject(); + const indexedDocumentDefinition = rawDataContract.documents.indexedDocument; + + indexedDocumentDefinition.properties.arrayProperty = { + type: 'array', + items: { + type: 'object', + properties: { + something: { + type: 'string', + }, + }, + additionalProperties: false, + }, + }; + + indexedDocumentDefinition.required.push('arrayProperty'); + + const indexDefinition = indexedDocumentDefinition.indices[0]; + + indexDefinition.properties.push({ + arrayProperty: 'asc', + }); + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexPropertyTypeError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1013); + expect(error.getPropertyName()).to.equal('arrayProperty'); + expect(error.getPropertyType()).to.equal('array'); + expect(error.getDocumentType()).to.deep.equal('indexedDocument'); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); }); - // it('should return invalid result if index property is array of objects', async () => { - // const indexedDocumentDefinition = rawDataContract.documents.indexedDocument; - // - // indexedDocumentDefinition.properties.arrayProperty = { - // type: 'array', - // items: { - // type: 'object', - // properties: { - // something: { - // type: 'string', - // }, - // }, - // additionalProperties: false, - // }, - // }; - // - // indexedDocumentDefinition.required.push('arrayProperty'); - // - // const indexDefinition = indexedDocumentDefinition.indices[0]; - // - // indexDefinition.properties.push({ - // arrayProperty: 'asc', - // }); - // - // const result = await validateDataContract(rawDataContract); - // - // expectValidationError(result, InvalidIndexPropertyTypeError); - // - // const [error] = result.getErrors(); - // - // expect(error.getCode()).to.equal(1013); - // expect(error.getPropertyName()).to.equal('arrayProperty'); - // expect(error.getPropertyType()).to.equal('array'); - // expect(error.getDocumentType()).to.deep.equal('indexedDocument'); - // expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); - // }); - - // it('should return invalid result if index property is an array of different types', - // async () => { - // const indexedDocumentDefinition = rawDataContract.documents.indexedArray; - // - // const indexDefinition = indexedDocumentDefinition.indices[0]; - // - // rawDataContract.documents.indexedArray.properties.mentions.prefixItems = [ - // { - // type: 'string', - // }, - // { - // type: 'number', - // }, - // ]; - // - // rawDataContract.documents.indexedArray.properties.mentions.minItems = 2; - // - // const result = await validateDataContract(rawDataContract); - // expectValidationError(result, InvalidIndexPropertyTypeError); - // - // const error = result.getFirstError(); - // - // expect(error.getCode()).to.equal(1013); - // expect(error.getPropertyName()).to.equal('mentions'); - // expect(error.getPropertyType()).to.equal('array'); - // expect(error.getDocumentType()).to.deep.equal('indexedArray'); - // expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); - // }); - // - // it('should return invalid result if index property contained prefixItems array of arrays', - // async () => { - // const indexedDocumentDefinition = rawDataContract.documents.indexedArray; - // - // const indexDefinition = indexedDocumentDefinition.indices[0]; - // - // rawDataContract.documents.indexedArray.properties.mentions.prefixItems = [ - // { - // type: 'array', - // items: { - // type: 'string', - // }, - // }, - // ]; - // - // const result = await validateDataContract(rawDataContract); - // expectValidationError(result, InvalidIndexPropertyTypeError); - // - // const error = result.getFirstError(); - // - // expect(error.getCode()).to.equal(1013); - // expect(error.getPropertyName()).to.equal('mentions'); - // expect(error.getPropertyType()).to.equal('array'); - // expect(error.getDocumentType()).to.deep.equal('indexedArray'); - // expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); - // }); - - // it('should return invalid result if index property contained prefixItems array of objects', - // async () => { - // const indexedDocumentDefinition = rawDataContract.documents.indexedArray; - // - // const indexDefinition = indexedDocumentDefinition.indices[0]; - // - // rawDataContract.documents.indexedArray.properties.mentions.prefixItems = [ - // { - // type: 'object', - // properties: { - // something: { - // type: 'string', - // }, - // }, - // additionalProperties: false, - // }, - // ]; - // - // const result = await validateDataContract(rawDataContract); - // expectValidationError(result, InvalidIndexPropertyTypeError); - // - // const error = result.getFirstError(); - // - // expect(error.getCode()).to.equal(1013); - // expect(error.getPropertyName()).to.equal('mentions'); - // expect(error.getPropertyType()).to.equal('array'); - // expect(error.getDocumentType()).to.deep.equal('indexedArray'); - // expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); - // }); - // - // it('should return invalid result if index property is array of arrays', async () => { - // const indexedDocumentDefinition = rawDataContract.documents.indexedDocument; - // - // indexedDocumentDefinition.properties.arrayProperty = { - // type: 'array', - // items: { - // type: 'array', - // items: { - // type: 'string', - // }, - // }, - // }; - // - // indexedDocumentDefinition.required.push('arrayProperty'); - // - // const indexDefinition = indexedDocumentDefinition.indices[0]; - // - // indexDefinition.properties.push({ - // arrayProperty: 'asc', - // }); - // - // const result = await validateDataContract(rawDataContract); - // - // expectValidationError(result, InvalidIndexPropertyTypeError); - // - // const [error] = result.getErrors(); - // - // expect(error.getCode()).to.equal(1013); - // expect(error.getPropertyName()).to.equal('arrayProperty'); - // expect(error.getPropertyType()).to.equal('array'); - // expect(error.getDocumentType()).to.deep.equal('indexedDocument'); - // expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); - // }); + // TODO: support indexed arrays + it.skip('should return invalid result if index property is an array of different types', + async () => { + const rawDataContract = dataContract.toObject(); + + const indexedDocumentDefinition = rawDataContract.documents.indexedArray; + + const indexDefinition = indexedDocumentDefinition.indices[0]; + + rawDataContract.documents.indexedArray.properties.mentions.prefixItems = [ + { + type: 'string', + }, + { + type: 'number', + }, + ]; + + rawDataContract.documents.indexedArray.properties.mentions.minItems = 2; + + const result = await validateDataContract(rawDataContract); + expectValidationError(result, InvalidIndexPropertyTypeError); + + const error = result.getFirstError(); + + expect(error.getCode()).to.equal(1013); + expect(error.getPropertyName()).to.equal('mentions'); + expect(error.getPropertyType()).to.equal('array'); + expect(error.getDocumentType()).to.deep.equal('indexedArray'); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); + }); + + // TODO: support indexed arrays + it.skip('should return invalid result if index property contained prefixItems array of arrays', async () => { + const rawDataContract = dataContract.toObject(); + + const indexedDocumentDefinition = rawDataContract.documents.indexedArray; + + const indexDefinition = indexedDocumentDefinition.indices[0]; + + rawDataContract.documents.indexedArray.properties.mentions.prefixItems = [ + { + type: 'array', + items: { + type: 'string', + }, + }, + ]; + + const result = await validateDataContract(rawDataContract); + expectValidationError(result, InvalidIndexPropertyTypeError); + + const error = result.getFirstError(); + + expect(error.getCode()).to.equal(1013); + expect(error.getPropertyName()).to.equal('mentions'); + expect(error.getPropertyType()).to.equal('array'); + expect(error.getDocumentType()).to.deep.equal('indexedArray'); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); + }); + + // TODO: support indexed arrays + it.skip('should return invalid result if index property contained prefixItems array of objects', async () => { + const rawDataContract = dataContract.toObject(); + + const indexedDocumentDefinition = rawDataContract.documents.indexedArray; + + const indexDefinition = indexedDocumentDefinition.indices[0]; + + rawDataContract.documents.indexedArray.properties.mentions.prefixItems = [ + { + type: 'object', + properties: { + something: { + type: 'string', + }, + }, + additionalProperties: false, + }, + ]; + + const result = await validateDataContract(rawDataContract); + expectValidationError(result, InvalidIndexPropertyTypeError); + + const error = result.getFirstError(); + + expect(error.getCode()).to.equal(1013); + expect(error.getPropertyName()).to.equal('mentions'); + expect(error.getPropertyType()).to.equal('array'); + expect(error.getDocumentType()).to.deep.equal('indexedArray'); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); + }); + + it('should return invalid result if index property is array of arrays', async () => { + const rawDataContract = dataContract.toObject(); + const indexedDocumentDefinition = rawDataContract.documents.indexedDocument; + + indexedDocumentDefinition.properties.arrayProperty = { + type: 'array', + items: { + type: 'array', + items: { + type: 'string', + }, + }, + }; + + indexedDocumentDefinition.required.push('arrayProperty'); + + const indexDefinition = indexedDocumentDefinition.indices[0]; + + indexDefinition.properties.push({ + arrayProperty: 'asc', + }); + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexPropertyTypeError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1013); + expect(error.getPropertyName()).to.equal('arrayProperty'); + expect(error.getPropertyType()).to.equal('array'); + expect(error.getDocumentType()).to.deep.equal('indexedDocument'); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); + }); it('should return invalid result if index property is array with different item definitions', async () => { + const rawDataContract = dataContract.toObject(); const indexedDocumentDefinition = rawDataContract.documents.indexedDocument; indexedDocumentDefinition.properties.arrayProperty = { @@ -1869,10 +1932,11 @@ describe('validateDataContractFactory', function main() { expect(error.getPropertyName()).to.equal('arrayProperty'); expect(error.getPropertyType()).to.equal('array'); expect(error.getDocumentType()).to.deep.equal('indexedDocument'); - expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + expect(error.getIndexDefinition().toObject()).to.deep.equal(indexDefinition); }); it('should return invalid result if unique compound index contains both required and optional properties', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.optionalUniqueIndexedDocument.required.splice(-1); const result = await validateDataContract(rawDataContract); @@ -1882,7 +1946,7 @@ describe('validateDataContractFactory', function main() { const [error] = result.getErrors(); expect(error.getCode()).to.equal(1010); - expect(error.getIndexDefinition()).to.deep.equal( + expect(error.getIndexDefinition().toObject()).to.deep.equal( rawDataContract.documents.optionalUniqueIndexedDocument.indices[1], ); expect(error.getDocumentType()).to.equal('optionalUniqueIndexedDocument'); @@ -1892,34 +1956,37 @@ describe('validateDataContractFactory', function main() { describe('signatureSecurityLevelRequirement', () => { it('should be a number', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.signatureSecurityLevelRequirement = 'definitely not a number'; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result, 2); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/signatureSecurityLevelRequirement'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/signatureSecurityLevelRequirement'); expect(error.getKeyword()).to.equal('type'); }); it('should be one of the available values', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.signatureSecurityLevelRequirement = 199; const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/documents/indexedDocument/signatureSecurityLevelRequirement'); + expect(error.getInstancePath()).to.equal('/documents/indexedDocument/signatureSecurityLevelRequirement'); expect(error.getKeyword()).to.equal('enum'); }); }); describe('dependentSchemas', () => { it('should be an object', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.niceDocument = { type: 'object', properties: { @@ -1933,18 +2000,18 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); expect(error.getKeyword()).to.equal('type'); - expect(error.instancePath).to.equal('/documents/niceDocument/dependentSchemas'); - expect(error.message).to.equal('must be object'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/dependentSchemas'); }); }); describe('dependentRequired', () => { it('should be an object', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.niceDocument = { type: 'object', properties: { @@ -1958,16 +2025,16 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); expect(error.getKeyword()).to.equal('type'); - expect(error.instancePath).to.equal('/documents/niceDocument/dependentRequired'); - expect(error.message).to.equal('must be object'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/dependentRequired'); }); it('should have an array value', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.niceDocument = { type: 'object', properties: { @@ -1985,16 +2052,16 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); expect(error.getKeyword()).to.equal('type'); - expect(error.instancePath).to.equal('/documents/niceDocument/dependentRequired/zxy'); - expect(error.message).to.equal('must be array'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/dependentRequired/zxy'); }); it('should have an array of strings', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.niceDocument = { type: 'object', properties: { @@ -2010,16 +2077,16 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); expect(error.getKeyword()).to.equal('type'); - expect(error.instancePath).to.equal('/documents/niceDocument/dependentRequired/zxy/0'); - expect(error.message).to.equal('must be string'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/dependentRequired/zxy/0'); }); it('should have an array of unique strings', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.niceDocument = { type: 'object', properties: { @@ -2035,17 +2102,18 @@ describe('validateDataContractFactory', function main() { const result = await validateDataContract(rawDataContract); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); expect(error.getKeyword()).to.equal('uniqueItems'); - expect(error.instancePath).to.equal('/documents/niceDocument/dependentRequired/zxy'); - expect(error.message).to.equal('must NOT have duplicate items (items ## 2 and 1 are identical)'); + expect(error.getInstancePath()).to.equal('/documents/niceDocument/dependentRequired/zxy'); }); }); - it.skip('should return invalid result with circular $ref pointer', async () => { + it('should return invalid result with circular $ref pointer', async () => { + const rawDataContract = dataContract.toObject(); + rawDataContract.$defs = {}; rawDataContract.$defs.object = { $ref: '#/$defs/object' }; const result = await validateDataContract(rawDataContract); @@ -2055,11 +2123,10 @@ describe('validateDataContractFactory', function main() { const [error] = result.getErrors(); expect(error.getCode()).to.equal(1014); - - expect(error.message).to.startsWith('Invalid JSON Schema $ref: Circular $ref pointer found'); }); it('should return invalid result if indexed string property missing maxLength constraint', async () => { + const rawDataContract = dataContract.toObject(); delete rawDataContract.documents.indexedDocument.properties.firstName.maxLength; const result = await validateDataContract(rawDataContract); @@ -2071,10 +2138,11 @@ describe('validateDataContractFactory', function main() { expect(error.getCode()).to.equal(1012); expect(error.getPropertyName()).to.equal('firstName'); expect(error.getConstraintName()).to.equal('maxLength'); - expect(error.getReason()).to.equal('should be less or equal 63'); + expect(error.getReason()).to.equal('should be less or equal than 63'); }); it('should return invalid result if indexed string property have to big maxLength', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.indexedDocument.properties.firstName.maxLength = 2048; const result = await validateDataContract(rawDataContract); @@ -2086,73 +2154,80 @@ describe('validateDataContractFactory', function main() { expect(error.getCode()).to.equal(1012); expect(error.getPropertyName()).to.equal('firstName'); expect(error.getConstraintName()).to.equal('maxLength'); + expect(error.getReason()).to.equal('should be less or equal than 63'); + }); + + // TODO: support indexed arrays + it.skip('should return invalid result if indexed array property missing maxItems constraint', + async () => { + const rawDataContract = dataContract.toObject(); + delete rawDataContract.documents.indexedArray.properties.mentions.maxItems; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexedPropertyConstraintError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1012); + expect(error.getPropertyName()).to.equal('mentions'); + expect(error.getConstraintName()).to.equal('maxItems'); + expect(error.getReason()).to.equal('should be less or equal 63'); + }); + + // TODO support indexed arrays + it.skip('should return invalid result if indexed array property have to big maxItems', async () => { + const rawDataContract = dataContract.toObject(); + rawDataContract.documents.indexedArray.properties.mentions.maxItems = 2048; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexedPropertyConstraintError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1012); + expect(error.getPropertyName()).to.equal('mentions'); + expect(error.getConstraintName()).to.equal('maxItems'); + expect(error.getReason()).to.equal('should be less or equal 63'); + }); + + // TODO support indexed arrays + it.skip('should return invalid result if indexed array property have string item without maxItems constraint', async () => { + const rawDataContract = dataContract.toObject(); + delete rawDataContract.documents.indexedArray.properties.mentions.maxItems; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexedPropertyConstraintError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1012); + expect(error.getPropertyName()).to.equal('mentions'); + expect(error.getConstraintName()).to.equal('maxItems'); expect(error.getReason()).to.equal('should be less or equal 63'); }); - // it('should return invalid result if indexed array property missing maxItems constraint', - // async () => { - // delete rawDataContract.documents.indexedArray.properties.mentions.maxItems; - // - // const result = await validateDataContract(rawDataContract); - // - // expectValidationError(result, InvalidIndexedPropertyConstraintError); - // - // const [error] = result.getErrors(); - // - // expect(error.getCode()).to.equal(1012); - // expect(error.getPropertyName()).to.equal('mentions'); - // expect(error.getConstraintName()).to.equal('maxItems'); - // expect(error.getReason()).to.equal('should be less or equal 63'); - // }); - // - // it('should return invalid result if indexed array property have to big maxItems', async () => { - // rawDataContract.documents.indexedArray.properties.mentions.maxItems = 2048; - // - // const result = await validateDataContract(rawDataContract); - // - // expectValidationError(result, InvalidIndexedPropertyConstraintError); - // - // const [error] = result.getErrors(); - // - // expect(error.getCode()).to.equal(1012); - // expect(error.getPropertyName()).to.equal('mentions'); - // expect(error.getConstraintName()).to.equal('maxItems'); - // expect(error.getReason()).to.equal('should be less or equal 63'); - // }); - // - // it('should return invalid result if indexed array property - // have string item without maxItems constraint', async () => { - // delete rawDataContract.documents.indexedArray.properties.mentions.maxItems; - // - // const result = await validateDataContract(rawDataContract); - // - // expectValidationError(result, InvalidIndexedPropertyConstraintError); - // - // const [error] = result.getErrors(); - // - // expect(error.getCode()).to.equal(1012); - // expect(error.getPropertyName()).to.equal('mentions'); - // expect(error.getConstraintName()).to.equal('maxItems'); - // expect(error.getReason()).to.equal('should be less or equal 63'); - // }); - // - // it('should return invalid result if indexed array property have - // string item with maxItems bigger than 1024', async () => { - // rawDataContract.documents.indexedArray.properties.mentions.maxItems = 2048; - // - // const result = await validateDataContract(rawDataContract); - // - // expectValidationError(result, InvalidIndexedPropertyConstraintError); - // - // const [error] = result.getErrors(); - // - // expect(error.getCode()).to.equal(1012); - // expect(error.getPropertyName()).to.equal('mentions'); - // expect(error.getConstraintName()).to.equal('maxItems'); - // expect(error.getReason()).to.equal('should be less or equal 63'); - // }); + // TODO support indexed arrays + it.skip('should return invalid result if indexed array property have string item with maxItems bigger than 1024', async () => { + const rawDataContract = dataContract.toObject(); + rawDataContract.documents.indexedArray.properties.mentions.maxItems = 2048; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexedPropertyConstraintError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1012); + expect(error.getPropertyName()).to.equal('mentions'); + expect(error.getConstraintName()).to.equal('maxItems'); + expect(error.getReason()).to.equal('should be less or equal 63'); + }); it('should return invalid result if indexed byte array property missing maxItems constraint', async () => { + const rawDataContract = dataContract.toObject(); delete rawDataContract.documents.withByteArrays.properties.byteArrayField.maxItems; const result = await validateDataContract(rawDataContract); @@ -2168,6 +2243,7 @@ describe('validateDataContractFactory', function main() { }); it('should return invalid result if indexed byte array property have to big maxItems', async () => { + const rawDataContract = dataContract.toObject(); rawDataContract.documents.withByteArrays.properties.byteArrayField.maxItems = 8192; const result = await validateDataContract(rawDataContract); @@ -2183,6 +2259,7 @@ describe('validateDataContractFactory', function main() { }); it('should return valid result if Data Contract is valid', async () => { + const rawDataContract = dataContract.toObject(); const result = await validateDataContract(rawDataContract); expect(result).to.be.an.instanceOf(ValidationResult); diff --git a/packages/wasm-dpp/test/unit/dataContract/createDataContract.spec.js b/packages/wasm-dpp/test/unit/dataContract/createDataContract.spec.js index 3c119b37353..a9c1f42a3a1 100644 --- a/packages/wasm-dpp/test/unit/dataContract/createDataContract.spec.js +++ b/packages/wasm-dpp/test/unit/dataContract/createDataContract.spec.js @@ -16,7 +16,6 @@ describe('createDataContract', () => { rawDataContract = { $id: generateRandomIdentifier().toBuffer(), ownerId: generateRandomIdentifier().toBuffer(), - contractId: generateRandomIdentifier().toBuffer(), protocolVersion: 4, version: 20, $schema: 'http://test.com/schema',