UNPKG

5.01 kBJavaScriptView Raw
1'use strict';
2
3var async = require('async');
4var extend = require('xtend');
5var uuid = require('node-uuid').v4;
6var distributeProbabilities = require('./lib/distribute-flow-probabilities');
7var debug = require('debug')('flowbench:flow');
8var template = require('./lib/template');
9
10var defaultOptions = {};
11
12module.exports = function Flow(parent, options, experiment) {
13 var parentOptions = extend({}, parent.options);
14
15 options = extend({
16 request: parentOptions.request
17 }, defaultOptions, options);
18
19 var tasks = [];
20 var flows = [];
21 var req = parent.options.req || {};
22 var res = parent.options.res || {};
23 var templateData = {
24 req: req,
25 res: res
26 };
27 var lastRequest;
28 var flowing = false;
29
30 var flow = function(cb) {
31 debug('executing flow');
32 async.series(tasks, cb);
33 };
34
35 flow.options = options;
36 flow.req = req;
37 flow.res = res;
38
39 flow.prepare = function() {
40 distributeProbabilities(flows);
41 flows.forEach(function(flow) {
42 flow.prepare();
43 });
44 };
45
46 flow.request = function(method, url, options) {
47 checkNotFlowing();
48
49 url = template.prepare(url);
50 options = template.prepare(options);
51 var data = extend({}, templateData, {
52 fixtures: options && options.fixtures && fixturize(options.fixtures)
53 });
54 var dataAsArray = [data.req, data.res, data.fixtures];
55
56 tasks.push(function(cb) {
57 options = extend({}, options, {
58 uri: template.render(url, data, dataAsArray),
59 method: method.toUpperCase()
60 });
61 options = template.render(options, data, dataAsArray);
62
63 debug('options.body: %j', options.body);
64 debug('options.json: %j', options.json);
65
66 if (typeof options.json == 'undefined' &&
67 typeof options.body == 'object')
68 {
69 options.json = true;
70 }
71
72
73 if (! options.id) {
74 options.id = uuid();
75 }
76
77 lastRequest = options.id;
78
79 debug('request options:', options);
80
81 var request = parentOptions.request(options, function(err, resp, body) {
82 if (resp) {
83 experiment.emit('response', resp);
84 resp.body = body;
85 res[options.id] = resp;
86 }
87 if (err) {
88 experiment.emit('request-error', request, err);
89 }
90 cb();
91 });
92 experiment.emit('request', request);
93
94 req[options.id] = extend({}, request, {
95 body: options.body ? options.body : request.body,
96 qs: options.qs ? options.qs : request.qs
97 });
98 });
99
100 return flow;
101 };
102
103 ['get', 'post', 'delete', 'head', 'put'].forEach(function(method) {
104 flow[method] = function(url, options) {
105 return flow.request(method.toUpperCase(), url, options);
106 };
107 });
108
109 flow.verify = function() {
110 checkNotFlowing();
111 var verifiers = Array.prototype.slice.call(arguments);
112 verifiers.forEach(function(verifier) {
113 tasks.push(function(cb) {
114 var valid = false;
115 var err;
116 try {
117 valid = verifier.call(null, req[lastRequest], res[lastRequest]);
118 } catch(_err) {
119 err = _err;
120 }
121
122 if (! err && valid instanceof Error) {
123 err = valid;
124 }
125 if (! err && (typeof valid == 'boolean') && ! valid) {
126 err = new Error('unknown verification error');
127 }
128
129 if (err) {
130 experiment.emit(
131 'verify-error',
132 err,
133 req[lastRequest],
134 res[lastRequest]);
135 }
136 cb();
137 });
138 });
139
140 return flow;
141 };
142
143 flow.wait = function(ms) {
144 checkNotFlowing();
145 tasks.push(function(cb) {
146 setTimeout(cb, ms);
147 });
148 return flow;
149 };
150
151 flow.flow = function(options) {
152 if (! flowing) {
153 flowing = true;
154 tasks.push(doFlows);
155 }
156 var flow = Flow(this, options, experiment);
157 flows.push(flow);
158 return flow;
159 };
160
161 flow.end = function({
162 return parent;
163 };
164
165 flow.type = 'flow';
166
167 function doFlows(cb) {
168 var random = Math.random();
169 var sum = 0;
170 var flow;
171 var idx = 0;
172 while(sum < random && idx < flows.length) {
173 flow = flows[idx];
174 if (flow) {
175 sum += flow.options.probability;
176 }
177 idx ++;
178 }
179 if (! flow) {
180 throw new Error('No flow to select');
181 }
182 flow(cb);
183 }
184
185 function checkNotFlowing() {
186 if (flowing) {
187 throw new Error('Adding more tasks after flows is not allowed');
188 }
189 }
190
191 return flow;
192}
193
194function isFlow(task) {
195 return task.type == 'flow';
196}
197
198function fixturize(fixtures) {
199 if (Array.isArray(fixtures)) {
200 Object.defineProperty(fixtures, 'random', {
201 enumerable: false,
202 value: pickRandom
203 });
204 } else if(typeof fixtures == 'object') {
205 Object.keys(fixtures).forEach(function(key) {
206 if (fixtures.hasOwnProperty(key)) {
207 fixtures[key] = fixturize(fixtures[key]);
208 }
209 });
210 }
211
212 return fixtures;
213}
214
215function pickRandom() {
216 /* jshint validthis:true */
217 var i = Math.floor(Math.random() * this.length);
218 return this[i];
219}
\No newline at end of file