THRIFT-3122 Javascript struct constructor should properly initialize struct and container members from plain js arguments
Patch: Igor Tkach
This closes #519
diff --git a/lib/nodejs/test/deep-constructor.test.js b/lib/nodejs/test/deep-constructor.test.js
new file mode 100644
index 0000000..41e7dd0
--- /dev/null
+++ b/lib/nodejs/test/deep-constructor.test.js
@@ -0,0 +1,233 @@
+var ttypes = require('./gen-nodejs/JsDeepConstructorTest_types');
+var thrift = require('thrift');
+var test = require('tape');
+var bufferEquals = require('buffer-equals');
+
+function serializeBinary(data) {
+ var buff;
+ var transport = new thrift.TBufferedTransport(null, function(msg){
+ buff = msg;
+ });
+ var prot = new thrift.TBinaryProtocol(transport);
+ data.write(prot);
+ prot.flush();
+ return buff;
+
+}
+
+
+function deserializeBinary(serialized, type) {
+ var t = new thrift.TFramedTransport(serialized);
+ var p = new thrift.TBinaryProtocol(t);
+ var data = new type();
+ data.read(p);
+ return data;
+}
+
+
+function serializeJSON(data) {
+ var buff;
+ var transport = new thrift.TBufferedTransport(null, function(msg){
+ buff = msg;
+ });
+ var protocol = new thrift.TJSONProtocol(transport);
+ protocol.writeMessageBegin("", 0, 0);
+ data.write(protocol);
+ protocol.writeMessageEnd();
+ protocol.flush();
+ return buff;
+}
+
+
+function deserializeJSON(serialized, type) {
+ var transport = new thrift.TFramedTransport(serialized);
+ var protocol = new thrift.TJSONProtocol(transport);
+ protocol.readMessageBegin();
+ var data = new type();
+ data.read(protocol);
+ protocol.readMessageEnd();
+ return data;
+}
+
+
+function createThriftObj() {
+
+ return new ttypes.Complex({
+
+ struct_field: new ttypes.Simple({value: 'a'}),
+
+ struct_list_field: [
+ new ttypes.Simple({value: 'b'}),
+ new ttypes.Simple({value: 'c'}),
+ ],
+
+ struct_set_field: [
+ new ttypes.Simple({value: 'd'}),
+ new ttypes.Simple({value: 'e'}),
+ ],
+
+ struct_map_field: {
+ A: new ttypes.Simple({value: 'f'}),
+ B: new ttypes.Simple({value: 'g'})
+ },
+
+ struct_nested_containers_field: [
+ [
+ {
+ C: [
+ new ttypes.Simple({value: 'h'}),
+ new ttypes.Simple({value: 'i'})
+ ]
+ }
+ ]
+ ],
+
+ struct_nested_containers_field2: {
+ D: [
+ {
+ DA: new ttypes.Simple({value: 'j'})
+ },
+ {
+ DB: new ttypes.Simple({value: 'k'})
+ }
+ ]
+ }
+ }
+ );
+}
+
+
+function createJsObj() {
+
+ return {
+
+ struct_field: {value: 'a'},
+
+ struct_list_field: [
+ {value: 'b'},
+ {value: 'c'},
+ ],
+
+ struct_set_field: [
+ {value: 'd'},
+ {value: 'e'},
+ ],
+
+ struct_map_field: {
+ A: {value: 'f'},
+ B: {value: 'g'}
+ },
+
+ struct_nested_containers_field: [
+ [
+ {
+ C: [
+ {value: 'h'},
+ {value: 'i'}
+ ]
+ }
+ ]
+ ],
+
+ struct_nested_containers_field2: {
+ D: [
+ {
+ DA: {value: 'j'}
+ },
+ {
+ DB: {value: 'k'}
+ }
+ ]
+ }
+ };
+}
+
+
+function assertValues(obj, assert) {
+ assert.equals(obj.struct_field.value, 'a');
+ assert.equals(obj.struct_list_field[0].value, 'b');
+ assert.equals(obj.struct_list_field[1].value, 'c');
+ assert.equals(obj.struct_set_field[0].value, 'd');
+ assert.equals(obj.struct_set_field[1].value, 'e');
+ assert.equals(obj.struct_map_field.A.value, 'f');
+ assert.equals(obj.struct_map_field.B.value, 'g');
+ assert.equals(obj.struct_nested_containers_field[0][0].C[0].value, 'h');
+ assert.equals(obj.struct_nested_containers_field[0][0].C[1].value, 'i');
+ assert.equals(obj.struct_nested_containers_field2.D[0].DA.value, 'j');
+ assert.equals(obj.struct_nested_containers_field2.D[1].DB.value, 'k');
+}
+
+function createTestCases(serialize, deserialize) {
+
+ var cases = {
+
+ "Serialize/deserialize should return equal object": function(assert){
+ var tObj = createThriftObj();
+ var received = deserialize(serialize(tObj), ttypes.Complex);
+ assert.ok(tObj !== received, 'not the same object');
+ assert.deepEqual(tObj, received);
+ assert.end();
+ },
+
+ "Nested structs and containers initialized from plain js objects should serialize same as if initialized from thrift objects": function(assert) {
+ var tObj1 = createThriftObj();
+ var tObj2 = new ttypes.Complex(createJsObj());
+ assertValues(tObj2, assert);
+ var s1 = serialize(tObj1);
+ var s2 = serialize(tObj2);
+ assert.ok(bufferEquals(s1, s2));
+ assert.end();
+ },
+
+ "Modifications to args object should not affect constructed Thrift object": function (assert) {
+
+ var args = createJsObj();
+ assertValues(args, assert);
+
+ var tObj = new ttypes.Complex(args);
+ assertValues(tObj, assert);
+
+ args.struct_field.value = 'ZZZ';
+ args.struct_list_field[0].value = 'ZZZ';
+ args.struct_list_field[1].value = 'ZZZ';
+ args.struct_set_field[0].value = 'ZZZ';
+ args.struct_set_field[1].value = 'ZZZ';
+ args.struct_map_field.A.value = 'ZZZ';
+ args.struct_map_field.B.value = 'ZZZ';
+ args.struct_nested_containers_field[0][0].C[0] = 'ZZZ';
+ args.struct_nested_containers_field[0][0].C[1] = 'ZZZ';
+ args.struct_nested_containers_field2.D[0].DA = 'ZZZ';
+ args.struct_nested_containers_field2.D[0].DB = 'ZZZ';
+
+ assertValues(tObj, assert);
+ assert.end();
+ },
+
+ "nulls are ok": function(assert) {
+ var tObj = new ttypes.Complex({
+ struct_field: null,
+ struct_list_field: null,
+ struct_set_field: null,
+ struct_map_field: null,
+ struct_nested_containers_field: null,
+ struct_nested_containers_field2: null
+ });
+ var received = deserialize(serialize(tObj), ttypes.Complex);
+ assert.ok(tObj !== received);
+ assert.deepEqual(tObj, received);
+ assert.end();
+ }
+
+ };
+ return cases;
+}
+
+
+function run(name, cases){
+ Object.keys(cases).forEach(function(caseName) {
+ test(name + ': ' + caseName, cases[caseName]);
+ });
+}
+
+run('binary', createTestCases(serializeBinary, deserializeBinary));
+run('json', createTestCases(serializeJSON, deserializeJSON));