UNPKG

13.6 kBJavaScriptView Raw
1/*!
2Kettle Data Source Request Tests
3
4Copyright 2014 Lucendo Development Ltd.
5
6Licensed under the New BSD license. You may not use this file except in
7compliance with this License.
8
9You may obtain a copy of the License at
10https://github.com/fluid-project/kettle/blob/master/LICENSE.txt
11*/
12
13"use strict";
14
15var fluid = require("infusion"),
16 kettle = require("../kettle.js"),
17 jqUnit = fluid.require("node-jqunit", require, "jqUnit");
18
19kettle.loadTestingSupport();
20
21fluid.registerNamespace("kettle.tests.dataSource");
22
23
24// These tests set up two Kettle servers, with a dataSource to mediate between them. This is a very
25// common use case, where a server provides a modified or proxied view onto another one. This suite
26// tests the action of the kettle.dataSource.URL dataSource both reading and writing, as well as the
27// "special pathway" which applies limited filtering to any returned payload from a "set" response
28// (typically just JSON parsing). It also tests the action of some of the callback wrapping applied
29// in the dataSource implementation in order to recontextualise the new stack frame with an existing
30// Kettle request.
31
32// These tests could be improved further to test the action of the various failure pathways through
33// the net of promises/dataSources - as well as verifying the proper treatment of the direct payloads
34// themselves
35
36// These tests are written in a simplified style avoiding the use of "configs" or any of the dedicated
37// server-centred Kettle test boostrap functions - partially because we have two servers to fire up here
38// rather than one, and partially to illustrate how this style of testing looks
39
40kettle.tests.endpointReturns = {
41 "get": 42,
42 "post": {payload : "post return value"},
43 "put": {payload: "put return value"}
44};
45
46kettle.tests.endpoint = function (type, request) {
47 jqUnit.assertValue("Request is resolvable", request.events.onSuccess);
48 var value = kettle.tests.endpointReturns[type];
49 // test operation of "request promise" as well as requirement for callback wrapper
50 fluid.invokeLater(kettle.wrapCallback(function () {
51 fluid.log("ENDPOINT Resolving with value ", value);
52 request.handlerPromise.resolve(JSON.stringify(value) + "\n");
53 }));
54};
55
56fluid.defaults("kettle.tests.serverPair.getEndpoint", {
57 gradeNames: "kettle.request.http",
58 invokers: {
59 handleRequest: {
60 funcName: "kettle.tests.endpoint",
61 args: ["get", "{request}"]
62 }
63 }
64});
65
66fluid.defaults("kettle.tests.serverPair.postEndpoint", {
67 gradeNames: "kettle.request.http",
68 invokers: {
69 handleRequest: {
70 funcName: "kettle.tests.endpoint",
71 args: ["post", "{request}"]
72 }
73 }
74});
75
76fluid.defaults("kettle.tests.serverPair.putEndpoint", {
77 gradeNames: "kettle.request.http",
78 invokers: {
79 handleRequest: {
80 funcName: "kettle.tests.endpoint",
81 args: ["put", "{request}"]
82 }
83 }
84});
85
86kettle.tests.dataSource.errorPayload = {
87 isError: true,
88 value: 123,
89 message: "Error payload message"
90};
91
92kettle.tests.dataSource.errorEndpoint = function (request) {
93 request.handlerPromise.reject(kettle.tests.dataSource.errorPayload);
94};
95
96fluid.defaults("kettle.tests.serverPair.errorEndpoint", {
97 gradeNames: "kettle.request.http",
98 invokers: {
99 handleRequest: {
100 funcName: "kettle.tests.dataSource.errorEndpoint"
101 }
102 }
103});
104
105kettle.tests.dataSource.relay = function (type, dataSource, handlerPromise, writeMethod) {
106 var args = writeMethod ? [undefined, undefined, {writeMethod: writeMethod}] : [];
107 var response = dataSource[type].apply(null, args);
108 response.then(function (value) { // white-box testing for dataSource resolution
109 var request = kettle.getCurrentRequest();
110 jqUnit.assertValue("Callback to dataSource must be contextualised", request);
111 if (type === "set") {
112 jqUnit.assertEquals("dataSource set payload must have been parsed", "object", typeof(value));
113 }
114 handlerPromise.resolve(value);
115 }, function (error) {
116 handlerPromise.reject(error);
117 });
118};
119
120kettle.tests.dataSource.errorRelay = function (dataSource, request) {
121 var response = dataSource.get(null);
122 response.then(function () {
123 jqUnit.fail("Should not receive resolve from error endpoint");
124 }, function (error) {
125 request.handlerPromise.reject(error);
126 });
127};
128
129fluid.defaults("kettle.tests.serverPair.getRelay", {
130 gradeNames: "kettle.request.http",
131 invokers: {
132 handleRequest: {
133 funcName: "kettle.tests.dataSource.relay",
134 args: ["get", "{relayDataSource}", "{request}.handlerPromise"]
135 }
136 }
137});
138
139fluid.defaults("kettle.tests.serverPair.postRelay", {
140 gradeNames: "kettle.request.http",
141 invokers: {
142 handleRequest: {
143 funcName: "kettle.tests.dataSource.relay",
144 args: ["set", "{relayDataSource}", "{request}.handlerPromise"]
145 }
146 }
147});
148
149fluid.defaults("kettle.tests.serverPair.putRelay", {
150 gradeNames: "kettle.request.http",
151 invokers: {
152 handleRequest: {
153 funcName: "kettle.tests.dataSource.relay",
154 args: ["set", "{relayDataSource}", "{request}.handlerPromise", "PUT"]
155 }
156 }
157});
158
159fluid.defaults("kettle.tests.serverPair.errorRelay", {
160 gradeNames: "kettle.request.http",
161 invokers: {
162 handleRequest: {
163 funcName: "kettle.tests.dataSource.errorRelay",
164 args: ["{errorDataSource}", "{request}"]
165 }
166 }
167});
168
169fluid.defaults("kettle.tests.serverPair", {
170 gradeNames: ["fluid.component"],
171 components: {
172 sourceServer: {
173 type: "kettle.server",
174 options: {
175 port: 8085,
176 distributeOptions: {
177 source: "{that}.options.port", // ideally we will move this top level once we can support non-that here
178 target: "{serverPair relayServer dataSource}.options.termMap.sourcePort"
179 },
180 components: {
181 sourceApp: {
182 type: "kettle.app",
183 options: {
184 requestHandlers: {
185 getEndpoint: {
186 type: "kettle.tests.serverPair.getEndpoint",
187 route: "/endpoint",
188 method: "get"
189 },
190 postEndpoint: {
191 type: "kettle.tests.serverPair.postEndpoint",
192 route: "/endpoint",
193 method: "post"
194 },
195 putEndpoint: {
196 type: "kettle.tests.serverPair.putEndpoint",
197 route: "/endpoint",
198 method: "put"
199 },
200 errorEndpoint: {
201 type: "kettle.tests.serverPair.errorEndpoint",
202 route: "/errorEndpoint",
203 method: "get"
204 }
205 }
206 }
207 }
208 }
209 }
210 },
211 relayServer: {
212 type: "kettle.server",
213 options: {
214 port: 8086,
215 components: {
216 relayDataSource: {
217 type: "kettle.dataSource.URL",
218 options: {
219 url: "http://localhost:%sourcePort/endpoint",
220 writable: true,
221 writeMethod: "POST"
222 }
223 },
224 errorDataSource: {
225 type: "kettle.dataSource.URL",
226 options: {
227 url: "http://localhost:%sourcePort/errorEndpoint"
228 }
229 },
230 relayApp: {
231 type: "kettle.app",
232 options: {
233 requestHandlers: {
234 getRelay: {
235 type: "kettle.tests.serverPair.getRelay",
236 route: "/relay",
237 method: "get"
238 },
239 postRelay: {
240 type: "kettle.tests.serverPair.postRelay",
241 route: "/relay",
242 method: "post"
243 },
244 putRelay: {
245 type: "kettle.tests.serverPair.putRelay",
246 route: "/relay",
247 method: "put"
248 },
249 errorRelay: {
250 type: "kettle.tests.serverPair.errorRelay",
251 route: "/errorRelay",
252 method: "get"
253 }
254 }
255 }
256 }
257 }
258 }
259 }
260 }
261});
262
263kettle.tests.testServerPairResponse = function (expected, data) {
264 var parsed = JSON.parse(data);
265 jqUnit.assertDeepEq("Expected response from request", expected, parsed);
266};
267
268kettle.tests.getServerPairSequence = [
269 {
270 func: "{getRequest}.send",
271 args: [null, {
272 path: "/relay"
273 }]
274 }, {
275 event: "{getRequest}.events.onComplete",
276 listener: "kettle.tests.testServerPairResponse",
277 args: [42, "{arguments}.0"]
278 }
279];
280
281kettle.tests.postServerPairSequence = [
282 {
283 func: "{postRequest}.send",
284 args: [{setDirectModel: 10}, {setModel: 20}] // TODO: currently ignored
285 }, {
286 event: "{postRequest}.events.onComplete",
287 listener: "kettle.tests.testServerPairResponse",
288 args: [{payload: "post return value"}, "{arguments}.0"]
289 }
290];
291
292kettle.tests.putServerPairSequence = [
293 {
294 func: "{putRequest}.send",
295 args: [{setDirectModel: 10}, {setModel: 20}] // TODO: currently ignored
296 }, {
297 event: "{putRequest}.events.onComplete",
298 listener: "kettle.tests.testServerPairResponse",
299 args: [{payload: "put return value"}, "{arguments}.0"]
300 }
301];
302
303kettle.tests.errorServerPairSequence = [
304 {
305 func: "{errorRequest}.send",
306 args: [null, {
307 path: "/errorRelay"
308 }]
309 }, {
310 event: "{errorRequest}.events.onComplete",
311 listener: "kettle.test.assertJSONResponse",
312 args: {
313 message: "Received relayed failure from original failed dataSource",
314 statusCode: 500,
315 string: "{arguments}.0",
316 request: "{errorRequest}",
317 expected: kettle.upgradeError(kettle.tests.dataSource.errorPayload, " while executing HTTP GET on url http://localhost:8085/errorEndpoint")
318 }
319 }
320];
321
322fluid.defaults("kettle.tests.serverPairTester", {
323 gradeNames: ["fluid.test.testEnvironment", "kettle.tests.serverPair"],
324 components: {
325 getRequest: {
326 type: "kettle.test.request.http",
327 options: {
328 port: 8086,
329 // path: "/relay", // omit this to test KETTLE-28 by supplying dynamically
330 method: "GET"
331 }
332 },
333 postRequest: {
334 type: "kettle.test.request.http",
335 options: {
336 port: 8086,
337 path: "/relay",
338 method: "POST"
339 }
340 },
341 putRequest: {
342 type: "kettle.test.request.http",
343 options: {
344 port: 8086,
345 path: "/relay",
346 method: "PUT"
347 }
348 },
349 errorRequest: {
350 type: "kettle.test.request.http",
351 options: {
352 port: 8086,
353 path: "/errorRelay"
354 }
355 },
356 fixtures: {
357 type: "fluid.test.testCaseHolder",
358 options: {
359 modules: [{
360 name: "Cross server datasource access",
361 tests: [{
362 name: "Access GET request",
363 expect: 3,
364 sequence: kettle.tests.getServerPairSequence
365 }, {
366 name: "Access SET request via POST",
367 expect: 4, // one extra assertion tests the type of a set response payload
368 sequence: kettle.tests.postServerPairSequence
369 }, {
370 name: "Access SET request via PUT",
371 expect: 4, // one extra assertion tests the type of a set response payload
372 sequence: kettle.tests.putServerPairSequence
373 }, {
374 name: "Relay error state via GET",
375 expect: 2,
376 sequence: kettle.tests.errorServerPairSequence
377 }]
378 }]
379 }
380 }
381 }
382});
383
384kettle.test.bootstrap("kettle.tests.serverPairTester");