]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
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 | ||
20 | // This is the Node.js test driver for the standard Apache Thrift | |
21 | // test service. The driver invokes every function defined in the | |
22 | // Thrift Test service with a representative range of parameters. | |
23 | // | |
24 | // The ThriftTestDriver function requires a client object | |
25 | // connected to a server hosting the Thrift Test service and | |
26 | // supports an optional callback function which is called with | |
27 | // a status message when the test is complete. | |
28 | ||
29 | const test = require("tape"); | |
30 | ||
31 | const helpers = require("./helpers"); | |
32 | const ttypes = require(`./${helpers.genPath}/ThriftTest_types`); | |
33 | const TException = require("thrift").Thrift.TException; | |
34 | const Int64 = require("node-int64"); | |
35 | const testCases = require("./test-cases"); | |
36 | ||
37 | exports.ThriftTestDriver = function(client, callback) { | |
38 | test( | |
39 | "NodeJS Style Callback Client Tests", | |
40 | { skip: helpers.ecmaMode === "es6" }, | |
41 | function(assert) { | |
42 | const checkRecursively = makeRecursiveCheck(assert); | |
43 | ||
44 | function makeAsserter(assertionFn) { | |
45 | return function(c) { | |
46 | const fnName = c[0]; | |
47 | const expected = c[1]; | |
48 | client[fnName](expected, function(err, actual) { | |
49 | assert.error(err, fnName + ": no callback error"); | |
50 | assertionFn(actual, expected, fnName); | |
51 | }); | |
52 | }; | |
53 | } | |
54 | ||
55 | testCases.simple.forEach( | |
56 | makeAsserter(function(a, e, m) { | |
57 | if (a instanceof Int64) { | |
58 | const e64 = e instanceof Int64 ? e : new Int64(e); | |
59 | assert.deepEqual(a.buffer, e64.buffer, m); | |
60 | } else { | |
61 | assert.equal(a, e, m); | |
62 | } | |
63 | }) | |
64 | ); | |
65 | testCases.deep.forEach(makeAsserter(assert.deepEqual)); | |
66 | testCases.deepUnordered.forEach( | |
67 | makeAsserter(makeUnorderedDeepEqual(assert)) | |
68 | ); | |
69 | ||
70 | const arr = []; | |
71 | for (let i = 0; i < 256; ++i) { | |
72 | arr[i] = 255 - i; | |
73 | } | |
74 | let buf = new Buffer(arr); | |
75 | client.testBinary(buf, function(err, response) { | |
76 | assert.error(err, "testBinary: no callback error"); | |
77 | assert.equal(response.length, 256, "testBinary"); | |
78 | assert.deepEqual(response, buf, "testBinary(Buffer)"); | |
79 | }); | |
80 | buf = new Buffer(arr); | |
81 | client.testBinary(buf.toString("binary"), function(err, response) { | |
82 | assert.error(err, "testBinary: no callback error"); | |
83 | assert.equal(response.length, 256, "testBinary"); | |
84 | assert.deepEqual(response, buf, "testBinary(string)"); | |
85 | }); | |
86 | ||
87 | client.testMapMap(42, function(err, response) { | |
88 | const expected = { | |
89 | "4": { "1": 1, "2": 2, "3": 3, "4": 4 }, | |
90 | "-4": { "-4": -4, "-3": -3, "-2": -2, "-1": -1 } | |
91 | }; | |
92 | assert.error(err, "testMapMap: no callback error"); | |
93 | assert.deepEqual(expected, response, "testMapMap"); | |
94 | }); | |
95 | ||
96 | client.testStruct(testCases.out, function(err, response) { | |
97 | assert.error(err, "testStruct: no callback error"); | |
98 | checkRecursively(testCases.out, response, "testStruct"); | |
99 | }); | |
100 | ||
101 | client.testNest(testCases.out2, function(err, response) { | |
102 | assert.error(err, "testNest: no callback error"); | |
103 | checkRecursively(testCases.out2, response, "testNest"); | |
104 | }); | |
105 | ||
106 | client.testInsanity(testCases.crazy, function(err, response) { | |
107 | assert.error(err, "testInsanity: no callback error"); | |
108 | checkRecursively(testCases.insanity, response, "testInsanity"); | |
109 | }); | |
110 | ||
111 | client.testInsanity(testCases.crazy2, function(err, response) { | |
112 | assert.error(err, "testInsanity2: no callback error"); | |
113 | checkRecursively(testCases.insanity, response, "testInsanity2"); | |
114 | }); | |
115 | ||
116 | client.testException("TException", function(err, response) { | |
117 | assert.ok( | |
118 | err instanceof TException, | |
119 | "testException: correct error type" | |
120 | ); | |
121 | assert.ok(!response, "testException: no response"); | |
122 | }); | |
123 | ||
124 | client.testException("Xception", function(err, response) { | |
125 | assert.ok( | |
126 | err instanceof ttypes.Xception, | |
127 | "testException: correct error type" | |
128 | ); | |
129 | assert.ok(!response, "testException: no response"); | |
130 | assert.equal(err.errorCode, 1001, "testException: correct error code"); | |
131 | assert.equal( | |
132 | "Xception", | |
133 | err.message, | |
134 | "testException: correct error message" | |
135 | ); | |
136 | }); | |
137 | ||
138 | client.testException("no Exception", function(err, response) { | |
139 | assert.error(err, "testException: no callback error"); | |
140 | assert.ok(!response, "testException: no response"); | |
141 | }); | |
142 | ||
143 | client.testOneway(0, function(err, response) { | |
144 | assert.error(err, "testOneway: no callback error"); | |
145 | assert.strictEqual(response, undefined, "testOneway: void response"); | |
146 | }); | |
147 | ||
148 | checkOffByOne(function(done) { | |
149 | client.testI32(-1, function(err, response) { | |
150 | assert.error(err, "checkOffByOne: no callback error"); | |
151 | assert.equal(-1, response); | |
152 | assert.end(); | |
153 | done(); | |
154 | }); | |
155 | }, callback); | |
156 | } | |
157 | ); | |
158 | ||
159 | // ES6 does not support callback style | |
160 | if (helpers.ecmaMode === "es6") { | |
161 | checkOffByOne(done => done(), callback); | |
162 | } | |
163 | }; | |
164 | ||
165 | exports.ThriftTestDriverPromise = function(client, callback) { | |
166 | test("Promise Client Tests", function(assert) { | |
167 | const checkRecursively = makeRecursiveCheck(assert); | |
168 | ||
169 | function makeAsserter(assertionFn) { | |
170 | return function(c) { | |
171 | const fnName = c[0]; | |
172 | const expected = c[1]; | |
173 | client[fnName](expected) | |
174 | .then(function(actual) { | |
175 | assertionFn(actual, expected, fnName); | |
176 | }) | |
177 | .catch(() => assert.fail("fnName")); | |
178 | }; | |
179 | } | |
180 | ||
181 | testCases.simple.forEach( | |
182 | makeAsserter(function(a, e, m) { | |
183 | if (a instanceof Int64) { | |
184 | const e64 = e instanceof Int64 ? e : new Int64(e); | |
185 | assert.deepEqual(a.buffer, e64.buffer, m); | |
186 | } else { | |
187 | assert.equal(a, e, m); | |
188 | } | |
189 | }) | |
190 | ); | |
191 | testCases.deep.forEach(makeAsserter(assert.deepEqual)); | |
192 | testCases.deepUnordered.forEach( | |
193 | makeAsserter(makeUnorderedDeepEqual(assert)) | |
194 | ); | |
195 | ||
196 | client | |
197 | .testStruct(testCases.out) | |
198 | .then(function(response) { | |
199 | checkRecursively(testCases.out, response, "testStruct"); | |
200 | }) | |
201 | .catch(() => assert.fail("testStruct")); | |
202 | ||
203 | client | |
204 | .testNest(testCases.out2) | |
205 | .then(function(response) { | |
206 | checkRecursively(testCases.out2, response, "testNest"); | |
207 | }) | |
208 | .catch(() => assert.fail("testNest")); | |
209 | ||
210 | client | |
211 | .testInsanity(testCases.crazy) | |
212 | .then(function(response) { | |
213 | checkRecursively(testCases.insanity, response, "testInsanity"); | |
214 | }) | |
215 | .catch(() => assert.fail("testInsanity")); | |
216 | ||
217 | client | |
218 | .testInsanity(testCases.crazy2) | |
219 | .then(function(response) { | |
220 | checkRecursively(testCases.insanity, response, "testInsanity2"); | |
221 | }) | |
222 | .catch(() => assert.fail("testInsanity2")); | |
223 | ||
224 | client | |
225 | .testException("TException") | |
226 | .then(function() { | |
227 | assert.fail("testException: TException"); | |
228 | }) | |
229 | .catch(function(err) { | |
230 | assert.ok(err instanceof TException); | |
231 | }); | |
232 | ||
233 | client | |
234 | .testException("Xception") | |
235 | .then(function() { | |
236 | assert.fail("testException: Xception"); | |
237 | }) | |
238 | .catch(function(err) { | |
239 | assert.ok(err instanceof ttypes.Xception); | |
240 | assert.equal(err.errorCode, 1001); | |
241 | assert.equal("Xception", err.message); | |
242 | }); | |
243 | ||
244 | client | |
245 | .testException("no Exception") | |
246 | .then(function(response) { | |
247 | assert.equal(undefined, response); //void | |
248 | }) | |
249 | .catch(() => assert.fail("testException")); | |
250 | ||
251 | client | |
252 | .testOneway(0) | |
253 | .then(function(response) { | |
254 | assert.strictEqual(response, undefined, "testOneway: void response"); | |
255 | }) | |
256 | .catch(() => assert.fail("testOneway: should not reject")); | |
257 | ||
258 | checkOffByOne(function(done) { | |
259 | client | |
260 | .testI32(-1) | |
261 | .then(function(response) { | |
262 | assert.equal(-1, response); | |
263 | assert.end(); | |
264 | done(); | |
265 | }) | |
266 | .catch(() => assert.fail("checkOffByOne")); | |
267 | }, callback); | |
268 | }); | |
269 | }; | |
270 | ||
271 | // Helper Functions | |
272 | // ========================================================= | |
273 | ||
274 | function makeRecursiveCheck(assert) { | |
275 | return function(map1, map2, msg) { | |
276 | const equal = checkRecursively(map1, map2); | |
277 | ||
278 | assert.ok(equal, msg); | |
279 | ||
280 | // deepEqual doesn't work with fields using node-int64 | |
281 | function checkRecursively(map1, map2) { | |
282 | if (typeof map1 !== "function" && typeof map2 !== "function") { | |
283 | if (!map1 || typeof map1 !== "object") { | |
284 | //Handle int64 types (which use node-int64 in Node.js JavaScript) | |
285 | if ( | |
286 | typeof map1 === "number" && | |
287 | typeof map2 === "object" && | |
288 | map2.buffer && | |
289 | map2.buffer instanceof Buffer && | |
290 | map2.buffer.length === 8 | |
291 | ) { | |
292 | const n = new Int64(map2.buffer); | |
293 | return map1 === n.toNumber(); | |
294 | } else { | |
295 | return map1 == map2; | |
296 | } | |
297 | } else { | |
298 | return Object.keys(map1).every(function(key) { | |
299 | return checkRecursively(map1[key], map2[key]); | |
300 | }); | |
301 | } | |
302 | } | |
303 | } | |
304 | }; | |
305 | } | |
306 | ||
307 | function checkOffByOne(done, callback) { | |
308 | const retry_limit = 30; | |
309 | const retry_interval = 100; | |
310 | let test_complete = false; | |
311 | let retrys = 0; | |
312 | ||
313 | /** | |
314 | * redo a simple test after the oneway to make sure we aren't "off by one" -- | |
315 | * if the server treated oneway void like normal void, this next test will | |
316 | * fail since it will get the void confirmation rather than the correct | |
317 | * result. In this circumstance, the client will throw the exception: | |
318 | * | |
319 | * Because this is the last test against the server, when it completes | |
320 | * the entire suite is complete by definition (the tests run serially). | |
321 | */ | |
322 | done(function() { | |
323 | test_complete = true; | |
324 | }); | |
325 | ||
326 | //We wait up to retry_limit * retry_interval for the test suite to complete | |
327 | function TestForCompletion() { | |
328 | if (test_complete && callback) { | |
329 | callback("Server successfully tested!"); | |
330 | } else { | |
331 | if (++retrys < retry_limit) { | |
332 | setTimeout(TestForCompletion, retry_interval); | |
333 | } else if (callback) { | |
334 | callback( | |
335 | "Server test failed to complete after " + | |
336 | (retry_limit * retry_interval) / 1000 + | |
337 | " seconds" | |
338 | ); | |
339 | } | |
340 | } | |
341 | } | |
342 | ||
343 | setTimeout(TestForCompletion, retry_interval); | |
344 | } | |
345 | ||
346 | function makeUnorderedDeepEqual(assert) { | |
347 | return function(actual, expected, name) { | |
348 | assert.equal(actual.length, expected.length, name); | |
349 | for (const k in actual) { | |
350 | let found = false; | |
351 | for (const k2 in expected) { | |
352 | if (actual[k] === expected[k2]) { | |
353 | found = true; | |
354 | } | |
355 | } | |
356 | if (!found) { | |
357 | assert.fail("Unexpected value " + actual[k] + " with key " + k); | |
358 | } | |
359 | } | |
360 | }; | |
361 | } |