//////////////////////////////////////////////////////////////////////////// // // Copyright 2016 Realm Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////// 'use strict'; const Realm = require('realm'); const TestCase = require('./asserts'); const schemas = require('./schemas'); const RANDOM_DATA = new Uint8Array([ 0xd8, 0x21, 0xd6, 0xe8, 0x00, 0x57, 0xbc, 0xb2, 0x6a, 0x15, 0x77, 0x30, 0xac, 0x77, 0x96, 0xd9, 0x67, 0x1e, 0x40, 0xa7, 0x6d, 0x52, 0x83, 0xda, 0x07, 0x29, 0x9c, 0x70, 0x38, 0x48, 0x4e, 0xff, ]); const allTypesValues = { boolCol: true, intCol: 1, floatCol: 1.1, doubleCol: 1.11, stringCol: 'string', dateCol: new Date(1), dataCol: RANDOM_DATA, objectCol: {doubleCol: 2.2}, optBoolCol: true, optIntCol: 1, optFloatCol: 1.1, optDoubleCol: 1.11, optStringCol: 'string', optDateCol: new Date(1), optDataCol: RANDOM_DATA, boolArrayCol: [true], intArrayCol: [1], floatArrayCol: [1.1], doubleArrayCol: [1.11], stringArrayCol: ['string'], dateArrayCol: [new Date(1)], dataArrayCol: [RANDOM_DATA], objectArrayCol: [{doubleCol: 2.2}], optBoolArrayCol: [true], optIntArrayCol: [1], optFloatArrayCol: [1.1], optDoubleArrayCol: [1.11], optStringArrayCol: ['string'], optDateArrayCol: [new Date(1)], optDataArrayCol: [RANDOM_DATA], }; const nullPropertyValues = (() => { let values = {} for (let name in allTypesValues) { if (name.includes('opt')) { values[name] = name.includes('Array') ? [null] : null; } else { values[name] = allTypesValues[name]; } } return values; })(); module.exports = { testAllPropertyGetters: function() { const realm = new Realm({schema: [schemas.AllTypes, schemas.TestObject, schemas.LinkToAllTypes]}); let object, nullObject; realm.write(function() { object = realm.create('AllTypesObject', allTypesValues); nullObject = realm.create('AllTypesObject', nullPropertyValues); }); const objectSchema = realm.schema[0]; for (const name of Object.keys(objectSchema.properties)) { const type = objectSchema.properties[name].type; if (type === 'linkingObjects') { TestCase.assertEqual(object[name].length, 0); TestCase.assertEqual(nullObject[name].length, 0); continue; } TestCase.assertSimilar(type, object[name], allTypesValues[name]); TestCase.assertSimilar(type, nullObject[name], nullPropertyValues[name]); } TestCase.assertEqual(object.nonexistent, undefined); }, testAllTypesPropertySetters: function() { const realm = new Realm({schema: [schemas.AllTypes, schemas.TestObject, schemas.LinkToAllTypes]}); let obj; realm.write(function() { obj = realm.create('AllTypesObject', allTypesValues); }); TestCase.assertThrows(function() { obj.boolCol = false; }, 'can only set property values in a write transaction'); TestCase.assertEqual(obj.boolCol, true, 'bool value changed outside transaction'); realm.write(function() { TestCase.assertThrows(() => obj.boolCol = 'cat'); TestCase.assertThrows(() => obj.intCol = 'dog'); // Non-optional properties should complain about null for (const name of ['boolCol', 'intCol', 'floatCol', 'doubleCol', 'stringCol', 'dataCol', 'dateCol']) { TestCase.assertThrows(() => obj[name] = null, `Setting ${name} to null should throw`); TestCase.assertThrows(() => obj[name] = undefined, `Setting ${name} to undefined should throw`); } // Optional properties should allow it for (const name of ['optBoolCol', 'optIntCol', 'optFloatCol', 'optDoubleCol', 'optStringCol', 'optDataCol', 'optDateCol', 'objectCol']) { obj[name] = null; TestCase.assertEqual(obj[name], null); obj[name] = undefined; TestCase.assertEqual(obj[name], null); } function tryAssign(name, value) { var prop = schemas.AllTypes.properties[name]; var type = typeof prop == 'object' ? prop.type : prop; obj[name] = value; TestCase.assertSimilar(type, obj[name], value, undefined, 1); } tryAssign('boolCol', false); tryAssign('intCol', 10); tryAssign('floatCol', 2.2); tryAssign('doubleCol', 3.3); tryAssign('stringCol', 'new str'); tryAssign('dateCol', new Date(2)); tryAssign('dataCol', RANDOM_DATA); tryAssign('optBoolCol', null); tryAssign('optIntCol', null); tryAssign('optFloatCol', null); tryAssign('optDoubleCol', null); tryAssign('optStringCol', null); tryAssign('optDateCol', null); tryAssign('optDataCol', null); tryAssign('optBoolCol', false); tryAssign('optIntCol', 10); tryAssign('optFloatCol', 2.2); tryAssign('optDoubleCol', 3.3); tryAssign('optStringCol', 'new str'); tryAssign('optDateCol', new Date(2)); tryAssign('optDataCol', RANDOM_DATA); }); }, testLinkTypesPropertyGetters: function() { const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var obj = null; realm.write(function() { obj = realm.create('LinkTypesObject', { objectCol: {doubleCol: 1}, objectCol1: null, arrayCol: [{doubleCol: 3}], }); }); var objVal = obj.objectCol; TestCase.assertEqual(typeof objVal, 'object'); TestCase.assertNotEqual(objVal, null); TestCase.assertEqual(objVal.doubleCol, 1); TestCase.assertEqual(obj.objectCol1, null); var arrayVal = obj.arrayCol; TestCase.assertEqual(typeof arrayVal, 'object'); TestCase.assertNotEqual(arrayVal, null); TestCase.assertEqual(arrayVal.length, 1); TestCase.assertEqual(arrayVal[0].doubleCol, 3); }, testLinkTypesPropertySetters: function() { const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var objects = realm.objects('TestObject'); var obj; realm.write(function() { obj = realm.create('LinkTypesObject', { objectCol: {doubleCol: 1}, objectCol1: null, arrayCol: [{doubleCol: 3}], }); }); TestCase.assertEqual(objects.length, 2); TestCase.assertThrows(function() { obj.objectCol1 = obj.objectCol; }, 'can only set property values in a write transaction'); // set/reuse object property realm.write(function() { obj.objectCol1 = obj.objectCol; }); TestCase.assertEqual(obj.objectCol1.doubleCol, 1); //TestCase.assertEqual(obj.objectCol, obj.objectCol1); TestCase.assertEqual(objects.length, 2); realm.write(function() { obj.objectCol = null; obj.objectCol1 = null; }); TestCase.assertEqual(obj.objectCol, null); TestCase.assertEqual(obj.objectCol1, null); // set object as JSON realm.write(function() { obj.objectCol = {doubleCol: 1}; }); TestCase.assertEqual(obj.objectCol.doubleCol, 1); TestCase.assertEqual(objects.length, 3); // set array property realm.write(function() { obj.arrayCol = [ obj.arrayCol[0], obj.objectCol, realm.create('TestObject', {doubleCol: 2}), ]; }); TestCase.assertEqual(objects.length, 4); TestCase.assertEqual(obj.arrayCol.length, 3); TestCase.assertEqual(obj.arrayCol[0].doubleCol, 3); TestCase.assertEqual(obj.arrayCol[1].doubleCol, 1); TestCase.assertEqual(obj.arrayCol[2].doubleCol, 2); // set object from another realm var another = new Realm({path: 'another.realm', schema: realm.schema}); var anotherObj; another.write(function() { anotherObj = another.create('TestObject', {doubleCol: 3}); }); realm.write(function() { obj.objectCol = anotherObj; }); TestCase.assertEqual(obj.objectCol.doubleCol, 3); }, testEnumerablePropertyNames: function() { const realm = new Realm({schema: [schemas.AllTypes, schemas.TestObject, schemas.LinkToAllTypes]}); let object; realm.write(() => object = realm.create('AllTypesObject', allTypesValues)); const propNames = Object.keys(schemas.AllTypes.properties); TestCase.assertArraysEqual(Object.keys(object), propNames, 'Object.keys'); for (let key in object) { TestCase.assertEqual(key, propNames.shift()); } TestCase.assertEqual(propNames.length, 0); }, testDataProperties: function() { const realm = new Realm({schema: [schemas.DefaultValues, schemas.TestObject]}); var object; // Should be be able to set a data property with a typed array. realm.write(function() { object = realm.create('DefaultValuesObject', {dataCol: RANDOM_DATA}); }); // Data properties should return an instance of an ArrayBuffer. TestCase.assertTrue(object.dataCol instanceof ArrayBuffer); TestCase.assertArraysEqual(new Uint8Array(object.dataCol), RANDOM_DATA); // Should be able to also set a data property to an ArrayBuffer. realm.write(function() { object.dataCol = RANDOM_DATA.buffer; }); TestCase.assertArraysEqual(new Uint8Array(object.dataCol), RANDOM_DATA); if (Realm.Sync) { // The base64 decoder comes from realm-sync // Should be able to also set a data property to base64-encoded string. realm.write(function() { object.dataCol = require('buffer/').Buffer.from(RANDOM_DATA).toString('base64'); }); TestCase.assertArraysEqual(new Uint8Array(object.dataCol), RANDOM_DATA); } // Should be to set a data property to a DataView. realm.write(function() { object.dataCol = new DataView(RANDOM_DATA.buffer); }); TestCase.assertArraysEqual(new Uint8Array(object.dataCol), RANDOM_DATA); // Test that a variety of size and slices of data still work. [ [0, -1], [0, -2], [1, 0], [1, -1], [1, -2], [2, 0], [2, -1], [2, -2], ].forEach(function(range) { var array = RANDOM_DATA.subarray(range[0], range[1]); realm.write(function() { // Use a partial "view" of the underlying ArrayBuffer. object.dataCol = new Uint8Array(RANDOM_DATA.buffer, range[0], array.length); }); TestCase.assertArraysEqual(new Uint8Array(object.dataCol), array, range.join('...')); }); // Test other TypedArrays to make sure they all work for setting data properties. [ Int8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, ].forEach(function(TypedArray) { var array = new TypedArray(RANDOM_DATA.buffer); realm.write(function() { object.dataCol = array; }); TestCase.assertArraysEqual(new TypedArray(object.dataCol), array, TypedArray.name); }); realm.write(function() { TestCase.assertThrows(function() { object.dataCol = true; }); TestCase.assertThrows(function() { object.dataCol = 1; }); TestCase.assertThrows(function() { object.dataCol = 'some binary data'; }); TestCase.assertThrows(function() { object.dataCol = [1]; }); }); }, testObjectConstructor: function() { const realm = new Realm({schema: [schemas.TestObject]}); realm.write(function() { var obj = realm.create('TestObject', {doubleCol: 1}); TestCase.assertTrue(obj instanceof Realm.Object); }); TestCase.assertEqual(typeof Realm.Object, 'function'); TestCase.assertTrue(Realm.Object instanceof Function); }, testIsValid: function() { const realm = new Realm({schema: [schemas.TestObject]}); var obj; realm.write(function() { obj = realm.create('TestObject', {doubleCol: 1}); TestCase.assertEqual(obj.isValid(), true); realm.delete(obj); TestCase.assertEqual(obj.isValid(), false); }); TestCase.assertEqual(obj.isValid(), false); TestCase.assertThrows(function() { obj.doubleCol; }); }, testObjectSchema: function() { const realm = new Realm({schema: [schemas.TestObject]}); var obj; realm.write(function() { obj = realm.create('TestObject', {doubleCol: 1}); }); const schema = obj.objectSchema(); TestCase.assertEqual(schema.name, schemas.TestObject.name); TestCase.assertArraysEqual(Object.keys(schema.properties), Object.keys(schemas.TestObject.properties)); TestCase.assertEqual(schema.properties.doubleCol.type, 'double'); }, testIgnoredProperties: function() { var realm = new Realm({schema: [schemas.TestObject]}); var obj; realm.write(function() { obj = realm.create('TestObject', {doubleCol: 1, ignored: true}); }); TestCase.assertEqual(obj.doubleCol, 1); TestCase.assertEqual(obj.ignored, undefined); obj.ignored = true; TestCase.assertEqual(obj.ignored, true); }, testDates: function() { Realm.copyBundledRealmFiles(); // test file format upgrade var realm_v3 = new Realm({path: 'dates-v3.realm', schema: [schemas.DateObject]}); TestCase.assertEqual(realm_v3.objects('Date').length, 2); TestCase.assertEqual(realm_v3.objects('Date')[0].currentDate.getTime(), 1462500087955); TestCase.assertEqual(realm_v3.objects('Date')[0].nullDate.getTime(), 1462500087955); TestCase.assertEqual(realm_v3.objects('Date')[1].currentDate.getTime(), -10000); TestCase.assertEqual(realm_v3.objects('Date')[1].nullDate, null); // get new file format is not upgraded var realm_v5 = new Realm({path: 'dates-v5.realm', schema: [schemas.DateObject]}); TestCase.assertEqual(realm_v5.objects('Date').length, 2); TestCase.assertEqual(realm_v3.objects('Date')[0].currentDate.getTime(), 1462500087955); TestCase.assertEqual(realm_v3.objects('Date')[0].nullDate.getTime(), 1462500087955); TestCase.assertEqual(realm_v3.objects('Date')[1].currentDate.getTime(), -10000); TestCase.assertEqual(realm_v3.objects('Date')[1].nullDate, null); // test different dates var realm = new Realm({schema: [schemas.DateObject]}); const stringifiedDate = new Date(); realm.write(function() { realm.create('Date', { currentDate: new Date(10000) }); realm.create('Date', { currentDate: new Date(-10000) }); realm.create('Date', { currentDate: new Date(1000000000000) }); realm.create('Date', { currentDate: new Date(-1000000000000) }); realm.create('Date', { currentDate: stringifiedDate.toString() }); }); TestCase.assertEqual(realm.objects('Date')[0].currentDate.getTime(), 10000); TestCase.assertEqual(realm.objects('Date')[1].currentDate.getTime(), -10000); TestCase.assertEqual(realm.objects('Date')[2].currentDate.getTime(), 1000000000000); TestCase.assertEqual(realm.objects('Date')[3].currentDate.getTime(), -1000000000000); TestCase.assertEqual(realm.objects('Date')[4].currentDate.toString(), stringifiedDate.toString()); } };