1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20const ttypes = require("./gen-nodejs/JsDeepConstructorTest_types");
21const thrift = require("thrift");
22const test = require("tape");
23const bufferEquals = require("buffer-equals");
24
25function serializeBinary(data) {
26  let buff;
27  const transport = new thrift.TBufferedTransport(null, function(msg) {
28    buff = msg;
29  });
30  const prot = new thrift.TBinaryProtocol(transport);
31  data.write(prot);
32  prot.flush();
33  return buff;
34}
35
36function deserializeBinary(serialized, type) {
37  const t = new thrift.TFramedTransport(serialized);
38  const p = new thrift.TBinaryProtocol(t);
39  const data = new type();
40  data.read(p);
41  return data;
42}
43
44function serializeJSON(data) {
45  let buff;
46  const transport = new thrift.TBufferedTransport(null, function(msg) {
47    buff = msg;
48  });
49  const protocol = new thrift.TJSONProtocol(transport);
50  protocol.writeMessageBegin("", 0, 0);
51  data.write(protocol);
52  protocol.writeMessageEnd();
53  protocol.flush();
54  return buff;
55}
56
57function deserializeJSON(serialized, type) {
58  const transport = new thrift.TFramedTransport(serialized);
59  const protocol = new thrift.TJSONProtocol(transport);
60  protocol.readMessageBegin();
61  const data = new type();
62  data.read(protocol);
63  protocol.readMessageEnd();
64  return data;
65}
66
67function createThriftObj() {
68  return new ttypes.Complex({
69    struct_field: new ttypes.Simple({ value: "a" }),
70
71    struct_list_field: [
72      new ttypes.Simple({ value: "b" }),
73      new ttypes.Simple({ value: "c" })
74    ],
75
76    struct_set_field: [
77      new ttypes.Simple({ value: "d" }),
78      new ttypes.Simple({ value: "e" })
79    ],
80
81    struct_map_field: {
82      A: new ttypes.Simple({ value: "f" }),
83      B: new ttypes.Simple({ value: "g" })
84    },
85
86    struct_nested_containers_field: [
87      [
88        {
89          C: [
90            new ttypes.Simple({ value: "h" }),
91            new ttypes.Simple({ value: "i" })
92          ]
93        }
94      ]
95    ],
96
97    struct_nested_containers_field2: {
98      D: [
99        {
100          DA: new ttypes.Simple({ value: "j" })
101        },
102        {
103          DB: new ttypes.Simple({ value: "k" })
104        }
105      ]
106    },
107
108    list_of_list_field: [
109      ["l00", "l01", "l02"],
110      ["l10", "l11", "l12"],
111      ["l20", "l21", "l22"]
112    ],
113
114    list_of_list_of_list_field: [
115      [
116        ["m000", "m001", "m002"],
117        ["m010", "m011", "m012"],
118        ["m020", "m021", "m022"]
119      ],
120      [
121        ["m100", "m101", "m102"],
122        ["m110", "m111", "m112"],
123        ["m120", "m121", "m122"]
124      ],
125      [
126        ["m200", "m201", "m202"],
127        ["m210", "m211", "m212"],
128        ["m220", "m221", "m222"]
129      ]
130    ]
131  });
132}
133
134function createJsObj() {
135  return {
136    struct_field: { value: "a" },
137
138    struct_list_field: [{ value: "b" }, { value: "c" }],
139
140    struct_set_field: [{ value: "d" }, { value: "e" }],
141
142    struct_map_field: {
143      A: { value: "f" },
144      B: { value: "g" }
145    },
146
147    struct_nested_containers_field: [
148      [
149        {
150          C: [{ value: "h" }, { value: "i" }]
151        }
152      ]
153    ],
154
155    struct_nested_containers_field2: {
156      D: [
157        {
158          DA: { value: "j" }
159        },
160        {
161          DB: { value: "k" }
162        }
163      ]
164    },
165
166    list_of_list_field: [
167      ["l00", "l01", "l02"],
168      ["l10", "l11", "l12"],
169      ["l20", "l21", "l22"]
170    ],
171
172    list_of_list_of_list_field: [
173      [
174        ["m000", "m001", "m002"],
175        ["m010", "m011", "m012"],
176        ["m020", "m021", "m022"]
177      ],
178      [
179        ["m100", "m101", "m102"],
180        ["m110", "m111", "m112"],
181        ["m120", "m121", "m122"]
182      ],
183      [
184        ["m200", "m201", "m202"],
185        ["m210", "m211", "m212"],
186        ["m220", "m221", "m222"]
187      ]
188    ]
189  };
190}
191
192function assertValues(obj, assert) {
193  assert.equals(obj.struct_field.value, "a");
194  assert.equals(obj.struct_list_field[0].value, "b");
195  assert.equals(obj.struct_list_field[1].value, "c");
196  assert.equals(obj.struct_set_field[0].value, "d");
197  assert.equals(obj.struct_set_field[1].value, "e");
198  assert.equals(obj.struct_map_field.A.value, "f");
199  assert.equals(obj.struct_map_field.B.value, "g");
200  assert.equals(obj.struct_nested_containers_field[0][0].C[0].value, "h");
201  assert.equals(obj.struct_nested_containers_field[0][0].C[1].value, "i");
202  assert.equals(obj.struct_nested_containers_field2.D[0].DA.value, "j");
203  assert.equals(obj.struct_nested_containers_field2.D[1].DB.value, "k");
204  assert.equals(obj.list_of_list_field[0][0], "l00");
205  assert.equals(obj.list_of_list_field[0][1], "l01");
206  assert.equals(obj.list_of_list_field[0][2], "l02");
207  assert.equals(obj.list_of_list_field[1][0], "l10");
208  assert.equals(obj.list_of_list_field[1][1], "l11");
209  assert.equals(obj.list_of_list_field[1][2], "l12");
210  assert.equals(obj.list_of_list_field[2][0], "l20");
211  assert.equals(obj.list_of_list_field[2][1], "l21");
212  assert.equals(obj.list_of_list_field[2][2], "l22");
213
214  assert.equals(obj.list_of_list_of_list_field[0][0][0], "m000");
215  assert.equals(obj.list_of_list_of_list_field[0][0][1], "m001");
216  assert.equals(obj.list_of_list_of_list_field[0][0][2], "m002");
217  assert.equals(obj.list_of_list_of_list_field[0][1][0], "m010");
218  assert.equals(obj.list_of_list_of_list_field[0][1][1], "m011");
219  assert.equals(obj.list_of_list_of_list_field[0][1][2], "m012");
220  assert.equals(obj.list_of_list_of_list_field[0][2][0], "m020");
221  assert.equals(obj.list_of_list_of_list_field[0][2][1], "m021");
222  assert.equals(obj.list_of_list_of_list_field[0][2][2], "m022");
223
224  assert.equals(obj.list_of_list_of_list_field[1][0][0], "m100");
225  assert.equals(obj.list_of_list_of_list_field[1][0][1], "m101");
226  assert.equals(obj.list_of_list_of_list_field[1][0][2], "m102");
227  assert.equals(obj.list_of_list_of_list_field[1][1][0], "m110");
228  assert.equals(obj.list_of_list_of_list_field[1][1][1], "m111");
229  assert.equals(obj.list_of_list_of_list_field[1][1][2], "m112");
230  assert.equals(obj.list_of_list_of_list_field[1][2][0], "m120");
231  assert.equals(obj.list_of_list_of_list_field[1][2][1], "m121");
232  assert.equals(obj.list_of_list_of_list_field[1][2][2], "m122");
233
234  assert.equals(obj.list_of_list_of_list_field[2][0][0], "m200");
235  assert.equals(obj.list_of_list_of_list_field[2][0][1], "m201");
236  assert.equals(obj.list_of_list_of_list_field[2][0][2], "m202");
237  assert.equals(obj.list_of_list_of_list_field[2][1][0], "m210");
238  assert.equals(obj.list_of_list_of_list_field[2][1][1], "m211");
239  assert.equals(obj.list_of_list_of_list_field[2][1][2], "m212");
240  assert.equals(obj.list_of_list_of_list_field[2][2][0], "m220");
241  assert.equals(obj.list_of_list_of_list_field[2][2][1], "m221");
242  assert.equals(obj.list_of_list_of_list_field[2][2][2], "m222");
243}
244
245function createTestCases(serialize, deserialize) {
246  const cases = {
247    "Serialize/deserialize should return equal object": function(assert) {
248      const tObj = createThriftObj();
249      const received = deserialize(serialize(tObj), ttypes.Complex);
250      assert.ok(tObj !== received, "not the same object");
251      assert.deepEqual(tObj, received);
252      assert.end();
253    },
254
255    "Nested structs and containers initialized from plain js objects should serialize same as if initialized from thrift objects": function(
256      assert
257    ) {
258      const tObj1 = createThriftObj();
259      const tObj2 = new ttypes.Complex(createJsObj());
260      assertValues(tObj2, assert);
261      const s1 = serialize(tObj1);
262      const s2 = serialize(tObj2);
263      assert.ok(bufferEquals(s1, s2));
264      assert.end();
265    },
266
267    "Modifications to args object should not affect constructed Thrift object": function(
268      assert
269    ) {
270      const args = createJsObj();
271      assertValues(args, assert);
272
273      const tObj = new ttypes.Complex(args);
274      assertValues(tObj, assert);
275
276      args.struct_field.value = "ZZZ";
277      args.struct_list_field[0].value = "ZZZ";
278      args.struct_list_field[1].value = "ZZZ";
279      args.struct_set_field[0].value = "ZZZ";
280      args.struct_set_field[1].value = "ZZZ";
281      args.struct_map_field.A.value = "ZZZ";
282      args.struct_map_field.B.value = "ZZZ";
283      args.struct_nested_containers_field[0][0].C[0] = "ZZZ";
284      args.struct_nested_containers_field[0][0].C[1] = "ZZZ";
285      args.struct_nested_containers_field2.D[0].DA = "ZZZ";
286      args.struct_nested_containers_field2.D[0].DB = "ZZZ";
287
288      assertValues(tObj, assert);
289      assert.end();
290    },
291
292    "nulls are ok": function(assert) {
293      const tObj = new ttypes.Complex({
294        struct_field: null,
295        struct_list_field: null,
296        struct_set_field: null,
297        struct_map_field: null,
298        struct_nested_containers_field: null,
299        struct_nested_containers_field2: null
300      });
301      const received = deserialize(serialize(tObj), ttypes.Complex);
302      assert.strictEqual(tObj.struct_field, null);
303      assert.ok(tObj !== received);
304      assert.deepEqual(tObj, received);
305      assert.end();
306    },
307
308    "Can make list with objects": function(assert) {
309      const tObj = new ttypes.ComplexList({
310        struct_list_field: [new ttypes.Complex({})]
311      });
312      const innerObj = tObj.struct_list_field[0];
313      assert.ok(innerObj instanceof ttypes.Complex);
314      assert.strictEqual(innerObj.struct_field, null);
315      assert.strictEqual(innerObj.struct_list_field, null);
316      assert.strictEqual(innerObj.struct_set_field, null);
317      assert.strictEqual(innerObj.struct_map_field, null);
318      assert.strictEqual(innerObj.struct_nested_containers_field, null);
319      assert.strictEqual(innerObj.struct_nested_containers_field2, null);
320      assert.end();
321    }
322  };
323  return cases;
324}
325
326function run(name, cases) {
327  Object.keys(cases).forEach(function(caseName) {
328    test(name + ": " + caseName, cases[caseName]);
329  });
330}
331
332run("binary", createTestCases(serializeBinary, deserializeBinary));
333run("json", createTestCases(serializeJSON, deserializeJSON));
334