http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/spec/cache/ComplexObject.spec.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/cache/ComplexObject.spec.js 
b/modules/platforms/nodejs/spec/cache/ComplexObject.spec.js
new file mode 100644
index 0000000..2119ab5
--- /dev/null
+++ b/modules/platforms/nodejs/spec/cache/ComplexObject.spec.js
@@ -0,0 +1,464 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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';
+
+require('jasmine-expect');
+
+const Util = require('util');
+const TestingHelper = require('../TestingHelper');
+const IgniteClient = require('apache-ignite-client');
+const ObjectType = IgniteClient.ObjectType;
+const MapObjectType = IgniteClient.MapObjectType;
+const ComplexObjectType = IgniteClient.ComplexObjectType;
+const BinaryObject = IgniteClient.BinaryObject;
+
+const CACHE_NAME = '__test_cache';
+
+class Class1 {
+    constructor() {
+        this.field_1_1 = null;
+        this.field_1_2 = new Class2();
+        this.field_1_3 = null;
+    }
+}
+
+class SubClass1 extends Class1 {
+    constructor() {
+        super();
+        this.field_1_4 = null;
+        this.field_1_5 = new Class3();
+        this.field_1_6 = null;
+        this.field_1_7 = null;
+        this.field_1_8 = null;
+    }
+}
+
+class Class2 {
+    constructor() {
+        this.field_2_1 = null;
+        this.field_2_2 = null;
+    }
+}
+
+class Class3 {
+    constructor() {
+        this.field_3_1 = null;
+        this.field_3_2 = null;
+    }
+}
+
+describe('complex object test suite >', () => {
+    let igniteClient = null;
+
+    beforeAll((done) => {
+        Promise.resolve().
+            then(async () => {
+                await TestingHelper.init();
+                igniteClient = TestingHelper.igniteClient;
+                await testSuiteCleanup(done);
+                await igniteClient.getOrCreateCache(CACHE_NAME);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    }, TestingHelper.TIMEOUT);
+
+    afterAll((done) => {
+        Promise.resolve().
+            then(async () => {
+                await testSuiteCleanup(done);
+                await TestingHelper.cleanUp();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    }, TestingHelper.TIMEOUT);
+
+    it('put get complex objects', (done) => {
+        Promise.resolve().
+            then(async () => {
+                const value1 = new Class1();
+                value1.field_1_1 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.BYTE);
+                value1.field_1_2.field_2_1 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.SHORT);
+                value1.field_1_2.field_2_2 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.INTEGER);
+                value1.field_1_3 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.LONG);
+
+                const valueType1 = new ComplexObjectType(new Class1()).
+                    setFieldType('field_1_1', ObjectType.PRIMITIVE_TYPE.BYTE).
+                    setFieldType('field_1_2', new ComplexObjectType(new 
Class2(), 'Class2ShortInteger').
+                        setFieldType('field_2_1', 
ObjectType.PRIMITIVE_TYPE.SHORT).
+                        setFieldType('field_2_2', 
ObjectType.PRIMITIVE_TYPE.INTEGER)).
+                    setFieldType('field_1_3', ObjectType.PRIMITIVE_TYPE.LONG);
+
+                const value2 = new SubClass1();
+                value2.field_1_1 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.FLOAT);
+                value2.field_1_2.field_2_1 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.DOUBLE);
+                value2.field_1_2.field_2_2 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.CHAR);
+                value2.field_1_3 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.BOOLEAN);
+                value2.field_1_4 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.STRING);
+                value2.field_1_5.field_3_1 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.DATE);
+                value2.field_1_5.field_3_2 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.UUID);
+                value2.field_1_6 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.DECIMAL);
+                value2.field_1_7 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.TIMESTAMP);
+                value2.field_1_8 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.TIME);
+
+                const valueType2 = new ComplexObjectType(new SubClass1()).
+                    setFieldType('field_1_1', ObjectType.PRIMITIVE_TYPE.FLOAT).
+                    setFieldType('field_1_2', new ComplexObjectType(new 
Class2(), 'Class2DoubleChar').
+                        setFieldType('field_2_1', 
ObjectType.PRIMITIVE_TYPE.DOUBLE).
+                        setFieldType('field_2_2', 
ObjectType.PRIMITIVE_TYPE.CHAR)).
+                    setFieldType('field_1_3', 
ObjectType.PRIMITIVE_TYPE.BOOLEAN).
+                    setFieldType('field_1_4', 
ObjectType.PRIMITIVE_TYPE.STRING).
+                    setFieldType('field_1_5', new ComplexObjectType(new 
Class3()).
+                        setFieldType('field_3_1', 
ObjectType.PRIMITIVE_TYPE.DATE).
+                        setFieldType('field_3_2', 
ObjectType.PRIMITIVE_TYPE.UUID)).
+                    setFieldType('field_1_6', 
ObjectType.PRIMITIVE_TYPE.DECIMAL).
+                    setFieldType('field_1_7', 
ObjectType.PRIMITIVE_TYPE.TIMESTAMP).
+                    setFieldType('field_1_8', ObjectType.PRIMITIVE_TYPE.TIME);
+
+                await putGetComplexObjectsWithDifferentTypes(
+                    value1, value2, valueType1, valueType2, Class1, SubClass1);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('put get unnamed complex objects', (done) => {
+        Promise.resolve().
+            then(async () => {
+                const value1 = {};
+                value1.field_1_1 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.BYTE);
+                value1.field_1_2 = {};
+                value1.field_1_2.field_2_1 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.SHORT);
+                value1.field_1_2.field_2_2 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.INTEGER);
+                value1.field_1_3 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.LONG);
+
+                const valueType1 = new ComplexObjectType(value1, 
'ObjectWithByteObjLong').
+                    setFieldType('field_1_1', ObjectType.PRIMITIVE_TYPE.BYTE).
+                    setFieldType('field_1_2', new 
ComplexObjectType(value1.field_1_2, 'ObjectWithShortInteger').
+                        setFieldType('field_2_1', 
ObjectType.PRIMITIVE_TYPE.SHORT).
+                        setFieldType('field_2_2', 
ObjectType.PRIMITIVE_TYPE.INTEGER)).
+                    setFieldType('field_1_3', ObjectType.PRIMITIVE_TYPE.LONG);
+
+                const value2 = {};
+                value2.field_1_1 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.FLOAT);
+                value2.field_1_2 = {};
+                value2.field_1_2.field_2_1 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.DOUBLE);
+                value2.field_1_2.field_2_2 = {};
+                value2.field_1_2.field_2_2.field_3_1 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.CHAR);
+                value2.field_1_2.field_2_2.field_3_2 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.BOOLEAN);
+                value2.field_1_3 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.STRING);
+                value2.field_1_4 = 
getPrimitiveValue(ObjectType.PRIMITIVE_TYPE.DATE);
+
+                const valueType2 = new ComplexObjectType(value2, 
'ObjectWithFloatObjStringDate').
+                    setFieldType('field_1_1', ObjectType.PRIMITIVE_TYPE.FLOAT).
+                    setFieldType('field_1_2', new 
ComplexObjectType(value2.field_1_2, 'ObjectWithDoubleObj').
+                        setFieldType('field_2_1', 
ObjectType.PRIMITIVE_TYPE.DOUBLE).
+                        setFieldType('field_2_2', new 
ComplexObjectType(value2.field_1_2.field_2_2, 'ObjectWithCharBoolean').
+                            setFieldType('field_3_1', 
ObjectType.PRIMITIVE_TYPE.CHAR).
+                            setFieldType('field_3_2', 
ObjectType.PRIMITIVE_TYPE.BOOLEAN))).
+                    setFieldType('field_1_3', 
ObjectType.PRIMITIVE_TYPE.STRING).
+                    setFieldType('field_1_4', ObjectType.PRIMITIVE_TYPE.DATE);
+
+                await putGetComplexObjects(value1, value2,
+                    valueType1, valueType2, value2);
+
+                await putGetComplexObjects({}, {},
+                    new ComplexObjectType({}), new ComplexObjectType({}), {});
+
+                let binaryKey = await BinaryObject.fromObject(value1, 
valueType1);
+                let binaryValue = await BinaryObject.fromObject(value2, 
valueType2);
+                await putGetComplexObjects(binaryKey, binaryValue,
+                    null, null, value2);
+
+                binaryKey = await BinaryObject.fromObject({});
+                binaryValue = await BinaryObject.fromObject({});
+                await putGetComplexObjects(binaryKey, binaryValue,
+                    null, null, {});
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('put get complex objects with arrays', (done) => {
+        Promise.resolve().
+            then(async () => {
+                const value1 = new Class1();
+                value1.field_1_1 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.BYTE_ARRAY);
+                value1.field_1_2.field_2_1 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.SHORT_ARRAY);
+                value1.field_1_2.field_2_2 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.INTEGER_ARRAY);
+                value1.field_1_3 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.LONG_ARRAY);
+
+                const valueType1 = new ComplexObjectType(new Class1(), 
'Class1WithArrays').
+                    setFieldType('field_1_1', 
ObjectType.PRIMITIVE_TYPE.BYTE_ARRAY).
+                    setFieldType('field_1_2', new ComplexObjectType(new 
Class2(), 'Class2WithShortIntegerArrays').
+                        setFieldType('field_2_1', 
ObjectType.PRIMITIVE_TYPE.SHORT_ARRAY).
+                        setFieldType('field_2_2', 
ObjectType.PRIMITIVE_TYPE.INTEGER_ARRAY)).
+                    setFieldType('field_1_3', 
ObjectType.PRIMITIVE_TYPE.LONG_ARRAY);
+
+                const value2 = new SubClass1();
+                value2.field_1_1 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.FLOAT_ARRAY);
+                value2.field_1_2.field_2_1 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.DOUBLE_ARRAY);
+                value2.field_1_2.field_2_2 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.CHAR_ARRAY);
+                value2.field_1_3 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.BOOLEAN_ARRAY);
+                value2.field_1_4 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.STRING_ARRAY);
+                value2.field_1_5.field_3_1 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.DATE_ARRAY);
+                value2.field_1_5.field_3_2 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.UUID_ARRAY);
+                value2.field_1_6 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.DECIMAL_ARRAY);
+                value2.field_1_7 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.TIMESTAMP_ARRAY);
+                value2.field_1_8 = 
getArrayValues(ObjectType.PRIMITIVE_TYPE.TIME_ARRAY);
+
+                const valueType2 = new ComplexObjectType(new SubClass1(), 
'SubClass1WithArrays').
+                    setFieldType('field_1_1', 
ObjectType.PRIMITIVE_TYPE.FLOAT_ARRAY).
+                    setFieldType('field_1_2', new ComplexObjectType(new 
Class2(), 'Class2WithDoubleCharArrays').
+                        setFieldType('field_2_1', 
ObjectType.PRIMITIVE_TYPE.DOUBLE_ARRAY).
+                        setFieldType('field_2_2', 
ObjectType.PRIMITIVE_TYPE.CHAR_ARRAY)).
+                    setFieldType('field_1_3', 
ObjectType.PRIMITIVE_TYPE.BOOLEAN_ARRAY).
+                    setFieldType('field_1_4', 
ObjectType.PRIMITIVE_TYPE.STRING_ARRAY).
+                    setFieldType('field_1_5', new ComplexObjectType(new 
Class3(), 'Class3WithArrays').
+                        setFieldType('field_3_1', 
ObjectType.PRIMITIVE_TYPE.DATE_ARRAY).
+                        setFieldType('field_3_2', 
ObjectType.PRIMITIVE_TYPE.UUID_ARRAY)).
+                    setFieldType('field_1_6', 
ObjectType.PRIMITIVE_TYPE.DECIMAL_ARRAY).
+                    setFieldType('field_1_7', 
ObjectType.PRIMITIVE_TYPE.TIMESTAMP_ARRAY).
+                    setFieldType('field_1_8', 
ObjectType.PRIMITIVE_TYPE.TIME_ARRAY);
+
+                await putGetComplexObjectsWithDifferentTypes(
+                    value1, value2, valueType1, valueType2, Class1, SubClass1, 
true);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('put get complex objects with maps', (done) => {
+        Promise.resolve().
+            then(async () => {
+                const value1 = new Class1();
+                value1.field_1_1 = getMapValue(ObjectType.PRIMITIVE_TYPE.BYTE);
+                value1.field_1_2.field_2_1 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.SHORT);
+                value1.field_1_2.field_2_2 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.INTEGER);
+                value1.field_1_3 = getMapValue(ObjectType.PRIMITIVE_TYPE.LONG);
+
+                const valueType1 = new ComplexObjectType(new Class1(), 
'Class1WithMaps').
+                    setFieldType('field_1_1', new MapObjectType(
+                        MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.BYTE, ObjectType.PRIMITIVE_TYPE.BYTE)).
+                    setFieldType('field_1_2', new ComplexObjectType(new 
Class2(), 'Class2WithShortIntegerMaps').
+                        setFieldType('field_2_1', new MapObjectType(
+                            MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.SHORT, ObjectType.PRIMITIVE_TYPE.SHORT)).
+                        setFieldType('field_2_2', new MapObjectType(
+                            MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.INTEGER, ObjectType.PRIMITIVE_TYPE.INTEGER))).
+                    setFieldType('field_1_3', new MapObjectType(
+                        MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.LONG, ObjectType.PRIMITIVE_TYPE.LONG));
+
+                const value2 = new SubClass1();
+                value2.field_1_1 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.FLOAT);
+                value2.field_1_2.field_2_1 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.DOUBLE);
+                value2.field_1_2.field_2_2 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.CHAR);
+                value2.field_1_3 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.BOOLEAN);
+                value2.field_1_4 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.STRING);
+                value2.field_1_5.field_3_1 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.DATE);
+                value2.field_1_5.field_3_2 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.UUID);
+                value2.field_1_6 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.DECIMAL);
+                value2.field_1_7 = 
getMapValue(ObjectType.PRIMITIVE_TYPE.TIMESTAMP);
+                value2.field_1_8 = getMapValue(ObjectType.PRIMITIVE_TYPE.TIME);
+
+                const valueType2 = new ComplexObjectType(new SubClass1(), 
'SubClass1WithMaps').
+                    setFieldType('field_1_1', new MapObjectType(
+                        MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.STRING, ObjectType.PRIMITIVE_TYPE.FLOAT)).
+                    setFieldType('field_1_2', new ComplexObjectType(new 
Class2(), 'Class2WithDoubleCharMaps').
+                        setFieldType('field_2_1', new MapObjectType(
+                            MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.STRING, ObjectType.PRIMITIVE_TYPE.DOUBLE)).
+                        setFieldType('field_2_2', new MapObjectType(
+                            MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.CHAR, ObjectType.PRIMITIVE_TYPE.CHAR))).
+                    setFieldType('field_1_3', new MapObjectType(
+                        MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.BOOLEAN, ObjectType.PRIMITIVE_TYPE.BOOLEAN)).
+                    setFieldType('field_1_4', new MapObjectType(
+                        MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.STRING, ObjectType.PRIMITIVE_TYPE.STRING)).
+                    setFieldType('field_1_5', new ComplexObjectType(new 
Class3(), 'Class3WithMaps').
+                        setFieldType('field_3_1', new MapObjectType(
+                            MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.STRING, ObjectType.PRIMITIVE_TYPE.DATE)).
+                        setFieldType('field_3_2', new MapObjectType(
+                            MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.STRING, ObjectType.PRIMITIVE_TYPE.UUID))).
+                    setFieldType('field_1_6', new MapObjectType(
+                        MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.STRING, ObjectType.PRIMITIVE_TYPE.DECIMAL)).
+                    setFieldType('field_1_7', new MapObjectType(
+                        MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.STRING, ObjectType.PRIMITIVE_TYPE.TIMESTAMP)).
+                    setFieldType('field_1_8', new MapObjectType(
+                        MapObjectType.MAP_SUBTYPE.HASH_MAP, 
ObjectType.PRIMITIVE_TYPE.STRING, ObjectType.PRIMITIVE_TYPE.TIME));
+
+                await putGetComplexObjectsWithDifferentTypes(
+                    value1, value2, valueType1, valueType2, Class1, SubClass1, 
true);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('put get binary objects from objects', (done) => {
+        Promise.resolve().
+            then(async () => {
+                const valueType = new ComplexObjectType(new Class1(), 
'Class1WithStringObjStringArray').
+                    setFieldType('field_1_1', 
ObjectType.PRIMITIVE_TYPE.STRING).
+                    setFieldType('field_1_2', new ComplexObjectType(new 
Class2(), 'Class2WithShortBoolean').
+                        setFieldType('field_2_1', 
ObjectType.PRIMITIVE_TYPE.SHORT).
+                        setFieldType('field_2_2', 
ObjectType.PRIMITIVE_TYPE.BOOLEAN)).
+                    setFieldType('field_1_3', 
ObjectType.PRIMITIVE_TYPE.STRING_ARRAY);
+                await putGetBinaryObjects(valueType);
+                const defaultValueType = new ComplexObjectType(new Class1(), 
'Class1Default').
+                    setFieldType('field_1_2', new ComplexObjectType(new 
Class2(), 'Class2Default'));
+                await putGetBinaryObjects(defaultValueType);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    async function testSuiteCleanup(done) {
+        await TestingHelper.destroyCache(CACHE_NAME, done);
+    }
+
+    async function putGetComplexObjects(key, value, keyType, valueType, 
valuePattern) {
+        const cache = 
igniteClient.getCache(CACHE_NAME).setKeyType(keyType).setValueType(valueType);
+        try {
+            await cache.put(key, value);
+            const result = await cache.get(key);
+            expect(await TestingHelper.compare(valuePattern, 
result)).toBe(true,
+                `values are not equal: put 
value=${TestingHelper.printValue(valuePattern)}, get 
value=${TestingHelper.printValue(result)}`);
+        }
+        finally {
+            await cache.removeAll();
+        }
+    }
+
+    async function binaryObjectEquals(binaryObj, valuePattern, valueType) {
+        expect(await TestingHelper.compare(valuePattern, binaryObj)).toBe(true,
+            `binary values are not equal: put 
value=${TestingHelper.printValue(valuePattern)}, get 
value=${TestingHelper.printValue(binaryObj)}`);
+
+        let value1, value2;
+        for (let key of Object.keys(valuePattern)) {
+            value1 = valuePattern[key];
+            value2 = await binaryObj.getField(key, valueType ? 
valueType._getFieldType(key) : null);
+            expect(await TestingHelper.compare(value1, value2)).toBe(true,
+                `values for key ${key} are not equal: put 
value=${TestingHelper.printValue(value1)
+                }, get value=${TestingHelper.printValue(value2)}`);
+        }
+
+        if (valueType) {
+            const toObject = await binaryObj.toObject(valueType);
+            expect(await TestingHelper.compare(valuePattern, 
toObject)).toBe(true,
+                `values are not equal: put 
value=${TestingHelper.printValue(valuePattern)}, get 
value=${TestingHelper.printValue(toObject)}`);
+        }
+    }
+
+    async function putGetComplexObjectsWithDifferentTypes(
+        key, value, keyType, valueType, keyClass, valueClass, isNullable = 
false) {
+        await putGetComplexObjects(key, value, keyType, valueType, value);
+
+        if (isNullable) {
+            await putGetComplexObjects(new keyClass(), new valueClass(), 
keyType, valueType, new valueClass());
+        }
+
+        let binaryKey = await BinaryObject.fromObject(key, keyType);
+        let binaryValue = await BinaryObject.fromObject(value, valueType);
+        await putGetComplexObjects(binaryKey, binaryValue, null, null, value);
+
+        if (isNullable) {
+            binaryKey = await BinaryObject.fromObject(new keyClass(), keyType);
+            binaryValue = await BinaryObject.fromObject(new valueClass(), 
valueType);
+            await putGetComplexObjects(binaryKey, binaryValue, null, null, new 
valueClass());
+        }
+    }
+
+    async function putGetBinaryObjects(valueType) {
+        const value1 = new Class1();
+        value1.field_1_1 = 'abc';
+        value1.field_1_2.field_2_1 = 1234;
+        value1.field_1_2.field_2_2 = true;
+        value1.field_1_3 = ['a', 'bb', 'ccc'];
+
+        const value2 = new Class1();
+        value2.field_1_1 = 'def';
+        value2.field_1_2.field_2_1 = 5432;
+        value2.field_1_2.field_2_2 = false;
+        value2.field_1_3 = ['a', 'bb', 'ccc', 'dddd'];
+
+        const value3 = new Class1();
+        value3.field_1_1 = 'defdef';
+        value3.field_1_2.field_2_1 = 543;
+        value3.field_1_2.field_2_2 = false;
+        value3.field_1_3 = ['a', 'bb', 'ccc', 'dddd', 'eeeee'];
+
+        const field_1_2_Type = valueType ? 
valueType._getFieldType('field_1_2') : null;
+
+        const binaryValue1 = await BinaryObject.fromObject(value1, valueType);
+        const binaryValue2 = await BinaryObject.fromObject(value2, valueType);
+        const binaryValue3 = await BinaryObject.fromObject(value3, valueType);
+
+        const cache = igniteClient.getCache(CACHE_NAME);
+        try {
+            await cache.put(binaryValue1, binaryValue2);
+            let result = await cache.get(binaryValue1);
+            await binaryObjectEquals(result, value2, valueType);
+
+            binaryValue1.setField('field_1_1', 'abcde');
+            result = await cache.get(binaryValue1);
+            expect(result === null).toBe(true);
+
+            binaryValue2.setField('field_1_1', value3.field_1_1);
+            binaryValue2.setField('field_1_2', value3.field_1_2, 
field_1_2_Type);
+            binaryValue2.setField('field_1_3', value3.field_1_3);
+            await cache.put(binaryValue1, binaryValue2);
+            result = await cache.get(binaryValue1);
+            await binaryObjectEquals(result, value3, valueType);
+
+            binaryValue1.setField('field_1_1', 'abc');
+            binaryValue1.setField('field_1_3', await 
binaryValue1.getField('field_1_3'));
+            result = await cache.get(binaryValue1);
+            await binaryObjectEquals(result, value2, valueType);
+
+            result = await cache.get(binaryValue1);
+            await binaryObjectEquals(result, value2, valueType);
+
+            binaryValue3.setField('field_1_1', await 
result.getField('field_1_1'));
+            binaryValue3.setField('field_1_2', await 
result.getField('field_1_2', field_1_2_Type), field_1_2_Type);
+            binaryValue3.setField('field_1_3', await 
result.getField('field_1_3'));
+            await cache.put(binaryValue1, binaryValue3);
+            result = await cache.get(binaryValue1);
+            await binaryObjectEquals(result, value2, valueType);
+        }
+        finally {
+            await cache.removeAll();
+        }
+    }
+
+    function getPrimitiveValue(typeCode) {
+        return TestingHelper.primitiveValues[typeCode].values[0];
+    }
+
+    function getArrayValues(typeCode) {
+        return 
TestingHelper.primitiveValues[TestingHelper.arrayValues[typeCode].elemType].values;
+    }
+
+    function getMapValue(typeCode) {
+        const map = new Map();
+        const values = TestingHelper.primitiveValues[typeCode].values;
+        const length = values.length;
+        values.forEach((value, index) => {
+            if (!TestingHelper.primitiveValues[typeCode].isMapKey) {
+                value = Util.format("%s", value);
+            }
+            map.set(value, values[length - index - 1]);
+        });
+        return map;
+    }
+});

http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/spec/config.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/config.js 
b/modules/platforms/nodejs/spec/config.js
new file mode 100644
index 0000000..4e89875
--- /dev/null
+++ b/modules/platforms/nodejs/spec/config.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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';
+
+exports.endpoints = process.env.APACHE_IGNITE_CLIENT_ENDPOINTS ?
+                    process.env.APACHE_IGNITE_CLIENT_ENDPOINTS.split(',') : [];
+exports.debug = process.env.APACHE_IGNITE_CLIENT_DEBUG === 'true' || 
+                process.env.APACHE_IGNITE_CLIENT_DEBUG === '1';
+
+
+//exports.endpoints = ['127.0.0.1:10800'];
+//exports.debug = false;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/spec/examples/AuthExample.spec.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/examples/AuthExample.spec.js 
b/modules/platforms/nodejs/spec/examples/AuthExample.spec.js
new file mode 100644
index 0000000..3fb9205
--- /dev/null
+++ b/modules/platforms/nodejs/spec/examples/AuthExample.spec.js
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 TestingHelper = require('../TestingHelper');
+
+describe('execute auth example >', () => {
+    it('AuthTlsExample', (done) => {
+        TestingHelper.executeExample('examples/AuthTlsExample.js').
+            then(done).
+            catch(error => done.fail(error));
+    });
+});

http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/spec/examples/Examples.spec.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/examples/Examples.spec.js 
b/modules/platforms/nodejs/spec/examples/Examples.spec.js
new file mode 100644
index 0000000..c8dce3c
--- /dev/null
+++ b/modules/platforms/nodejs/spec/examples/Examples.spec.js
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 TestingHelper = require('../TestingHelper');
+
+describe('execute examples >', () => {
+    it('CachePutGetExample', (done) => {
+        TestingHelper.executeExample('examples/CachePutGetExample.js').
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('SqlExample', (done) => {
+        TestingHelper.executeExample('examples/SqlExample.js').
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('SqlQueryEntriesExample', (done) => {
+        TestingHelper.executeExample('examples/SqlQueryEntriesExample.js').
+            then(done).
+            catch(error => done.fail(error));
+    });
+});

http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/spec/query/ScanQuery.spec.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/query/ScanQuery.spec.js 
b/modules/platforms/nodejs/spec/query/ScanQuery.spec.js
new file mode 100644
index 0000000..ab28973
--- /dev/null
+++ b/modules/platforms/nodejs/spec/query/ScanQuery.spec.js
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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';
+
+require('jasmine-expect');
+
+const config = require('../config');
+const TestingHelper = require('../TestingHelper');
+const IgniteClient = require('apache-ignite-client');
+const Errors = IgniteClient.Errors;
+const ScanQuery = IgniteClient.ScanQuery;
+const ObjectType = IgniteClient.ObjectType;
+
+const CACHE_NAME = '__test_cache';
+const ELEMENTS_NUMBER = 10;
+
+describe('scan query test suite >', () => {
+    let igniteClient = null;
+
+    beforeAll((done) => {
+        Promise.resolve().
+            then(async () => {
+                await TestingHelper.init();
+                igniteClient = TestingHelper.igniteClient;
+                await testSuiteCleanup(done);
+                await igniteClient.getOrCreateCache(CACHE_NAME);
+                await generateData(done);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    }, TestingHelper.TIMEOUT);
+
+    afterAll((done) => {
+        Promise.resolve().
+            then(async () => {
+                await testSuiteCleanup(done);
+                await TestingHelper.cleanUp();
+            }).
+            then(done).
+            catch(error => done());
+    }, TestingHelper.TIMEOUT);
+
+    it('get all', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(new ScanQuery());
+                const set = new Set();
+                for (let cacheEntry of await cursor.getAll()) {
+                    expect(generateValue(cacheEntry.getKey()) === 
cacheEntry.getValue()).toBe(true);
+                    set.add(cacheEntry.getKey());
+                    expect(cacheEntry.getKey() >= 0 && cacheEntry.getKey() < 
ELEMENTS_NUMBER).toBe(true);
+                }
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get all with page size', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(new 
ScanQuery().setPageSize(1));
+                const set = new Set();
+                for (let cacheEntry of await cursor.getAll()) {
+                    expect(generateValue(cacheEntry.getKey()) === 
cacheEntry.getValue()).toBe(true);
+                    set.add(cacheEntry.getKey());
+                    expect(cacheEntry.getKey() >= 0 && cacheEntry.getKey() < 
ELEMENTS_NUMBER).toBe(true);
+                }
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get value', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(new ScanQuery());
+                const set = new Set();
+                do {
+                    let cacheEntry = await cursor.getValue();
+                    expect(generateValue(cacheEntry.getKey()) === 
cacheEntry.getValue()).toBe(true);
+                    set.add(cacheEntry.getKey());
+                    expect(cacheEntry.getKey() >= 0 && cacheEntry.getKey() < 
ELEMENTS_NUMBER).toBe(true);
+                } while (cursor.hasMore());
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get value with page size', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(new 
ScanQuery().setPageSize(2));
+                const set = new Set();
+                do {
+                    let cacheEntry = await cursor.getValue();
+                    expect(generateValue(cacheEntry.getKey()) === 
cacheEntry.getValue()).toBe(true);
+                    set.add(cacheEntry.getKey());
+                    expect(cacheEntry.getKey() >= 0 && cacheEntry.getKey() < 
ELEMENTS_NUMBER).toBe(true);
+                } while (cursor.hasMore());
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('close cursor', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(new 
ScanQuery().setPageSize(1));
+                await cursor.getValue();
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('close cursor after get all', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(new ScanQuery());
+                await cursor.getAll();
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('scan query settings', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(new ScanQuery().
+                    setPartitionNumber(0).
+                    setPageSize(2).
+                    setLocal(true));
+                await cursor.getAll();
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+
+    it('scan empty cache', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                await cache.removeAll();
+                let cursor = await cache.query(new ScanQuery());
+                const cacheEntries = await cursor.getAll();
+                expect(cacheEntries.length).toBe(0);
+                await cursor.close();
+
+                cursor = await cache.query(new ScanQuery());
+                expect(await cursor.getValue()).toBe(null);
+                expect(cursor.hasMore()).toBe(false);
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    async function testSuiteCleanup(done) {
+        await TestingHelper.destroyCache(CACHE_NAME, done);
+    }
+
+    async function generateData(done) {
+        try {
+            let cache = igniteClient.getCache(CACHE_NAME).
+                setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER);
+            for (let i = 0; i < ELEMENTS_NUMBER; i++) {
+                await cache.put(i, generateValue(i));
+            }
+        }
+        catch (err) {
+            done.fail('unexpected error: ' + err);
+        }
+    }
+
+    function generateValue(key) {
+        return 'value' + key;
+    }
+});

http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/spec/query/SqlFieldsQuery.spec.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/query/SqlFieldsQuery.spec.js 
b/modules/platforms/nodejs/spec/query/SqlFieldsQuery.spec.js
new file mode 100644
index 0000000..c81838a
--- /dev/null
+++ b/modules/platforms/nodejs/spec/query/SqlFieldsQuery.spec.js
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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';
+
+require('jasmine-expect');
+
+const config = require('../config');
+const TestingHelper = require('../TestingHelper');
+const IgniteClient = require('apache-ignite-client');
+const Errors = IgniteClient.Errors;
+const SqlFieldsQuery = IgniteClient.SqlFieldsQuery;
+const ObjectType = IgniteClient.ObjectType;
+const CacheConfiguration = IgniteClient.CacheConfiguration;
+
+const CACHE_NAME = '__test_cache';
+const TABLE_NAME = '__test_SqlFieldsQuery_table';
+const ELEMENTS_NUMBER = 10;
+
+describe('sql fields query test suite >', () => {
+    let igniteClient = null;
+
+    beforeAll((done) => {
+        Promise.resolve().
+            then(async () => {
+                await TestingHelper.init();
+                igniteClient = TestingHelper.igniteClient;
+                await testSuiteCleanup(done);
+                await igniteClient.getOrCreateCache(CACHE_NAME, new 
CacheConfiguration().setSqlSchema('PUBLIC'));
+                await generateData(done);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    }, TestingHelper.TIMEOUT);
+
+    afterAll((done) => {
+        Promise.resolve().
+            then(async () => {
+                await dropTables(done);
+                await testSuiteCleanup(done);
+                await TestingHelper.cleanUp();
+            }).
+            then(done).
+            catch(error => done());
+    }, TestingHelper.TIMEOUT);
+
+    it('get all', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(
+                    new SqlFieldsQuery(`SELECT * FROM ${TABLE_NAME}`));
+                const set = new Set();
+                for (let fields of await cursor.getAll()) {
+                    expect(fields.length).toBe(2);
+                    expect(generateValue(fields[0]) === fields[1]).toBe(true);
+                    set.add(fields[0]);
+                    expect(fields[0] >= 0 && fields[0] < 
ELEMENTS_NUMBER).toBe(true);
+                }
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get all with page size', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(
+                    new SqlFieldsQuery(`SELECT * FROM 
${TABLE_NAME}`).setPageSize(1));
+                const set = new Set();
+                for (let fields of await cursor.getAll()) {
+                    expect(fields.length).toBe(2);
+                    expect(generateValue(fields[0]) === fields[1]).toBe(true);
+                    set.add(fields[0]);
+                    expect(fields[0] >= 0 && fields[0] < 
ELEMENTS_NUMBER).toBe(true);
+                }
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get value', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(
+                    new SqlFieldsQuery(`SELECT * FROM ${TABLE_NAME}`));
+                const set = new Set();
+                do {
+                    let fields = await cursor.getValue();
+                    expect(fields.length).toBe(2);
+                    expect(generateValue(fields[0]) === fields[1]).toBe(true);
+                    set.add(fields[0]);
+                    expect(fields[0] >= 0 && fields[0] < 
ELEMENTS_NUMBER).toBe(true);
+                } while (cursor.hasMore());
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get value with page size', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(
+                    new SqlFieldsQuery(`SELECT * FROM 
${TABLE_NAME}`).setPageSize(2));
+                const set = new Set();
+                do {
+                    let fields = await cursor.getValue();
+                    expect(fields.length).toBe(2);
+                    expect(generateValue(fields[0]) === fields[1]).toBe(true);
+                    set.add(fields[0]);
+                    expect(fields[0] >= 0 && fields[0] < 
ELEMENTS_NUMBER).toBe(true);
+                } while (cursor.hasMore());
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('close cursor', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(
+                    new SqlFieldsQuery(`SELECT * FROM 
${TABLE_NAME}`).setPageSize(1));
+                await cursor.getValue();
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('close cursor after get all', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(new SqlFieldsQuery(`SELECT * 
FROM ${TABLE_NAME}`));
+                await cursor.getAll();
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('sql fields query settings', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                const cursor = await cache.query(new SqlFieldsQuery(`SELECT * 
FROM ${TABLE_NAME}`).
+                    setPageSize(2).
+                    setLocal(false).
+                    setSql(`INSERT INTO ${TABLE_NAME} (field1, field2) VALUES 
(?, ?)`).
+                    setArgTypes(ObjectType.PRIMITIVE_TYPE.INTEGER, 
ObjectType.PRIMITIVE_TYPE.STRING).
+                    setArgs(50, 'test').
+                    setDistributedJoins(true).
+                    setReplicatedOnly(false).
+                    setTimeout(10000).
+                    setSchema('PUBLIC').
+                    setMaxRows(20).
+                    setStatementType(SqlFieldsQuery.STATEMENT_TYPE.ANY).
+                    setEnforceJoinOrder(true).
+                    setCollocated(false).
+                    setLazy(true).
+                    setIncludeFieldNames(true));
+                await cursor.getAll();
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get empty results', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = igniteClient.getCache(CACHE_NAME);
+                await cache.removeAll();
+                let cursor = await cache.query(
+                    new SqlFieldsQuery(`SELECT field1 FROM ${TABLE_NAME} WHERE 
field1 > 100`));
+                const cacheEntries = await cursor.getAll();
+                expect(cacheEntries.length).toBe(0);
+                await cursor.close();
+
+                cursor = await cache.query(
+                    new SqlFieldsQuery(`SELECT field1 FROM ${TABLE_NAME} WHERE 
field1 > 100`));
+                expect(await cursor.getValue()).toBe(null);
+                expect(cursor.hasMore()).toBe(false);
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    async function dropTables(done) {
+        try {
+            let cache = igniteClient.getCache(CACHE_NAME);
+            (await cache.query(new SqlFieldsQuery(`DROP TABLE 
${TABLE_NAME}`))).getAll();
+        }
+        catch (err) {
+            done.fail('unexpected error: ' + err);
+        }
+    }
+
+    async function testSuiteCleanup(done) {
+        await TestingHelper.destroyCache(CACHE_NAME, done);
+    }
+
+    async function generateData(done) {
+        try {
+            let cache = igniteClient.getCache(CACHE_NAME);
+            (await cache.query(new SqlFieldsQuery(
+                `CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (field1 INT, field2 
VARCHAR, PRIMARY KEY (field1))`))).getAll();
+
+            const insertQuery = new SqlFieldsQuery(`INSERT INTO ${TABLE_NAME} 
(field1, field2) VALUES (?, ?)`).
+                setArgTypes(ObjectType.PRIMITIVE_TYPE.INTEGER);
+
+            for (let i = 0; i < ELEMENTS_NUMBER; i++) {
+                (await cache.query(insertQuery.setArgs(i, 
generateValue(i)))).getAll();
+            }
+        }
+        catch (err) {
+            done.fail('unexpected error: ' + err);
+        }
+    }
+
+    function generateValue(key) {
+        return 'value' + key;
+    }
+});

http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/spec/query/SqlQuery.spec.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/query/SqlQuery.spec.js 
b/modules/platforms/nodejs/spec/query/SqlQuery.spec.js
new file mode 100644
index 0000000..2bbd0e5
--- /dev/null
+++ b/modules/platforms/nodejs/spec/query/SqlQuery.spec.js
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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';
+
+require('jasmine-expect');
+
+const config = require('../config');
+const TestingHelper = require('../TestingHelper');
+const IgniteClient = require('apache-ignite-client');
+const Errors = IgniteClient.Errors;
+const SqlQuery = IgniteClient.SqlQuery;
+const SqlFieldsQuery = IgniteClient.SqlFieldsQuery;
+const ObjectType = IgniteClient.ObjectType;
+const CacheConfiguration = IgniteClient.CacheConfiguration;
+const QueryEntity = IgniteClient.QueryEntity;
+const QueryField = IgniteClient.QueryField;
+const ComplexObjectType = IgniteClient.ComplexObjectType;
+
+const CACHE_NAME = '__test_cache';
+const TABLE_NAME = '__test_SqlQuery';
+const ELEMENTS_NUMBER = 10;
+
+describe('sql query test suite >', () => {
+    let igniteClient = null;
+
+    beforeAll((done) => {
+        Promise.resolve().
+            then(async () => {
+                await TestingHelper.init();
+                igniteClient = TestingHelper.igniteClient;
+                await testSuiteCleanup(done);
+                await igniteClient.getOrCreateCache(
+                    CACHE_NAME, 
+                    new CacheConfiguration().
+                        setQueryEntities(
+                            new QueryEntity().
+                                setKeyTypeName('java.lang.Integer').
+                                setValueTypeName(TABLE_NAME).
+                                setFields([
+                                    new QueryField('field1', 
'java.lang.Integer'),
+                                    new QueryField('field2', 
'java.lang.String')
+                                ])));
+                await generateData(done);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    }, TestingHelper.TIMEOUT);
+
+    afterAll((done) => {
+        Promise.resolve().
+            then(async () => {
+                await testSuiteCleanup(done);
+                await TestingHelper.cleanUp();
+            }).
+            then(done).
+            catch(error => done());
+    }, TestingHelper.TIMEOUT);
+
+    it('get all', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = getCache();
+                const cursor = await cache.query(
+                    new SqlQuery(TABLE_NAME, `SELECT * FROM ${TABLE_NAME}`));
+                const set = new Set();
+                for (let cacheEntry of await cursor.getAll()) {
+                    expect(generateValue(cacheEntry.getKey()) === 
cacheEntry.getValue().field2).toBe(true);
+                    set.add(cacheEntry.getKey());
+                    expect(cacheEntry.getKey() >= 0 && cacheEntry.getKey() < 
ELEMENTS_NUMBER).toBe(true);
+                }
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get all with page size', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = getCache();
+                const cursor = await cache.query(
+                    new SqlQuery(TABLE_NAME, `SELECT * FROM 
${TABLE_NAME}`).setPageSize(1));
+                const set = new Set();
+                for (let cacheEntry of await cursor.getAll()) {
+                    expect(generateValue(cacheEntry.getKey()) === 
cacheEntry.getValue().field2).toBe(true);
+                    set.add(cacheEntry.getKey());
+                    expect(cacheEntry.getKey() >= 0 && cacheEntry.getKey() < 
ELEMENTS_NUMBER).toBe(true);
+                }
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get value', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = getCache();
+                const cursor = await cache.query(
+                    new SqlQuery(TABLE_NAME, `SELECT * FROM ${TABLE_NAME}`));
+                const set = new Set();
+                do {
+                    let cacheEntry = await cursor.getValue();
+                    expect(generateValue(cacheEntry.getKey()) === 
cacheEntry.getValue().field2).toBe(true);
+                    set.add(cacheEntry.getKey());
+                    expect(cacheEntry.getKey() >= 0 && cacheEntry.getKey() < 
ELEMENTS_NUMBER).toBe(true);
+                } while (cursor.hasMore());
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get value with page size', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = getCache();
+                const cursor = await cache.query(
+                    new SqlQuery(TABLE_NAME, `SELECT * FROM 
${TABLE_NAME}`).setPageSize(2));
+                const set = new Set();
+                do {
+                    let cacheEntry = await cursor.getValue();
+                    expect(generateValue(cacheEntry.getKey()) === 
cacheEntry.getValue().field2).toBe(true);
+                    set.add(cacheEntry.getKey());
+                    expect(cacheEntry.getKey() >= 0 && cacheEntry.getKey() < 
ELEMENTS_NUMBER).toBe(true);
+                } while (cursor.hasMore());
+                expect(set.size).toBe(ELEMENTS_NUMBER);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('close cursor', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = getCache();
+                const cursor = await cache.query(
+                    new SqlQuery(TABLE_NAME, `SELECT * FROM 
${TABLE_NAME}`).setPageSize(1));
+                await cursor.getValue();
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('close cursor after get all', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = getCache();
+                const cursor = await cache.query(
+                    new SqlQuery(TABLE_NAME, `SELECT * FROM ${TABLE_NAME}`));
+                await cursor.getAll();
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('sql query settings', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = getCache();
+                const cursor = await cache.query(new SqlQuery(TABLE_NAME, 
`SELECT * FROM ${TABLE_NAME}`).
+                    setType(TABLE_NAME).
+                    setPageSize(2).
+                    setLocal(false).
+                    setSql('field1 > ? and field1 <= ?').
+                    setArgTypes(ObjectType.PRIMITIVE_TYPE.INTEGER, 
ObjectType.PRIMITIVE_TYPE.INTEGER).
+                    setArgs(3, 7).
+                    setDistributedJoins(true).
+                    setReplicatedOnly(false).
+                    setTimeout(10000));
+                await cursor.getAll();
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('get values from empty cache', (done) => {
+        Promise.resolve().
+            then(async () => {
+                let cache = getCache();
+                await cache.removeAll();
+                let cursor = await cache.query(new SqlQuery(TABLE_NAME, 
`SELECT * FROM ${TABLE_NAME}`).setPageSize(1));
+                const cacheEntries = await cursor.getAll();
+                expect(cacheEntries.length).toBe(0);
+                await cursor.close();
+
+                cursor = await cache.query(new SqlQuery(TABLE_NAME, `SELECT * 
FROM ${TABLE_NAME}`).setPageSize(1));
+                expect(await cursor.getValue()).toBe(null);
+                expect(cursor.hasMore()).toBe(false);
+                await cursor.close();
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    async function testSuiteCleanup(done) {
+        await TestingHelper.destroyCache(CACHE_NAME, done);
+    }
+
+    function getCache() {
+        return igniteClient.getCache(CACHE_NAME).
+            setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER).
+            setValueType(new ComplexObjectType({
+                    'field1' : 1,
+                    'field2' : 'a'
+                }, TABLE_NAME).
+                setFieldType('field1', ObjectType.PRIMITIVE_TYPE.INTEGER));
+    }
+
+    async function generateData(done) {
+        try {
+            let cache = igniteClient.getCache(CACHE_NAME);
+
+            const insertQuery = new SqlFieldsQuery(`INSERT INTO ${TABLE_NAME} 
(_key, field1, field2) VALUES (?, ?, ?)`).
+                setArgTypes(ObjectType.PRIMITIVE_TYPE.INTEGER, 
ObjectType.PRIMITIVE_TYPE.INTEGER);
+
+            for (let i = 0; i < ELEMENTS_NUMBER; i++) {
+                (await cache.query(insertQuery.setArgs(i, i, 
generateValue(i)))).getAll();
+            }
+        }
+        catch (err) {
+            done.fail('unexpected error: ' + err);
+        }
+    }
+
+    function generateValue(key) {
+        return 'value' + key;
+    }
+});

http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/spec/support/jasmine.json
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/support/jasmine.json 
b/modules/platforms/nodejs/spec/support/jasmine.json
new file mode 100644
index 0000000..8ba68ba
--- /dev/null
+++ b/modules/platforms/nodejs/spec/support/jasmine.json
@@ -0,0 +1,11 @@
+{
+  "spec_dir": "spec",
+  "spec_files": [
+    "cache/**/*[sS]pec.js",
+    "query/**/*[sS]pec.js"
+  ],
+  "helpers": [
+    "helpers/**/*.js"
+  ],
+  "random": false
+}

Reply via email to