1 | import CONFIGURATION_1 from './data/configuration_1.json';
|
2 | import CONFIGURATION_1_OPERATIONS_1 from './data/configuration_1_operations_1.json';
|
3 | import CONFIGURATION_1_OPERATIONS_2 from './data/configuration_1_operations_2.json';
|
4 | import craftai from '../src';
|
5 | import INVALID_CONFIGURATION_1 from './data/invalid_configuration_1.json';
|
6 | import INVALID_CONFIGURATION_1_OPERATIONS_1 from './data/invalid_configuration_1_operations_1.json';
|
7 |
|
8 | describe('BULK:', function() {
|
9 | let client;
|
10 |
|
11 | before(function() {
|
12 | client = craftai(CRAFT_CFG);
|
13 | expect(client).to.be.ok;
|
14 | });
|
15 |
|
16 | function testAgentIntegrity(agent, agentId, configuration) {
|
17 | expect(agent).to.be.ok;
|
18 | expect(agent.id).to.be.equal(agentId);
|
19 | expect(agent.status).to.not.be.equal(400);
|
20 | return client.getAgent(agent.id)
|
21 | .then((retrieveAgent) => {
|
22 | expect(retrieveAgent.configuration).to.be.deep.equal(configuration);
|
23 | });
|
24 | }
|
25 |
|
26 | const TS0 = CONFIGURATION_1_OPERATIONS_1[CONFIGURATION_1_OPERATIONS_1.length - 1].timestamp;
|
27 |
|
28 |
|
29 | it('createAgentBulk: should succeed when using valid configurations and generated ids', function() {
|
30 | return client
|
31 | .createAgentBulk([
|
32 | { configuration: CONFIGURATION_1 },
|
33 | { configuration: CONFIGURATION_1 }
|
34 | ])
|
35 | .then((agentsList) => {
|
36 | agentsList.map((agent) => {
|
37 | testAgentIntegrity(agent, agent.id, CONFIGURATION_1);
|
38 | return client.deleteAgent(agent.id);
|
39 | });
|
40 | });
|
41 | });
|
42 |
|
43 | it('createAgentBulk: should succeed when using valid configurations and a specified id', function() {
|
44 | return client.deleteAgentBulk([{ id: 'press_f' }])
|
45 | .then(() => {
|
46 | return client
|
47 | .createAgentBulk([
|
48 | { id: 'press_f', configuration: CONFIGURATION_1 },
|
49 | { configuration: CONFIGURATION_1 }
|
50 | ])
|
51 | .then((agentsList) => {
|
52 | agentsList.map((agent) => {
|
53 | testAgentIntegrity(agent, agent.id, CONFIGURATION_1);
|
54 | return client.deleteAgent(agent.id);
|
55 | });
|
56 | });
|
57 | });
|
58 | });
|
59 |
|
60 | it('createAgentBulk: should succeed when using valid configurations and specified ids', function() {
|
61 | return client
|
62 | .deleteAgentBulk([{ id: 'press_f' }, { id: 't0' }, { id: 'pay_respects' }])
|
63 | .then(() => {
|
64 | return client
|
65 | .createAgentBulk([
|
66 | { id: 'press_f', configuration: CONFIGURATION_1 },
|
67 | { id: 't0', configuration: CONFIGURATION_1 },
|
68 | { id: 'pay_respects', configuration: CONFIGURATION_1 }
|
69 | ])
|
70 | .then((agentsList) => {
|
71 | agentsList.map((agent) => {
|
72 | testAgentIntegrity(agent, agent.id, CONFIGURATION_1);
|
73 | return client.deleteAgent(agent.id);
|
74 | });
|
75 | });
|
76 | });
|
77 | });
|
78 |
|
79 | it('createAgentBulk: should handle invalid configuration', function() {
|
80 | return client
|
81 | .deleteAgentBulk([
|
82 | { id: 'le_monde_est_sourd' },
|
83 | { id: 'partis_pour_rester' }
|
84 | ])
|
85 | .then(() => {
|
86 | return client
|
87 | .createAgentBulk([
|
88 | { id: 'le_monde_est_sourd', configuration: CONFIGURATION_1 },
|
89 | { id: 'partis_pour_rester', configuration: INVALID_CONFIGURATION_1 }
|
90 | ])
|
91 | .then((agentsList) => {
|
92 | const agent0 = agentsList[0];
|
93 | testAgentIntegrity(agent0, 'le_monde_est_sourd', CONFIGURATION_1);
|
94 | const agent1 = agentsList[1];
|
95 | expect(agent1.status).to.be.equal(400);
|
96 | expect(agent1.error).to.be.equal('ContextError');
|
97 | agentsList.map(({ id }) => client.deleteAgent(id));
|
98 | });
|
99 | });
|
100 | });
|
101 |
|
102 | it('createAgentBulk: should handle undefined configuration', function() {
|
103 | return client
|
104 | .deleteAgentBulk([
|
105 | { id: 'leila_et_les_chasseurs' },
|
106 | { id: 'la_robe_et_lechelle' }
|
107 | ])
|
108 | .then(() => {
|
109 | return client
|
110 | .createAgentBulk([
|
111 | { id: 'leila_et_les_chasseurs', configuration: CONFIGURATION_1 },
|
112 | { id: 'la_robe_et_lechelle', configuration: undefined }
|
113 | ])
|
114 | .then((agentsList) => {
|
115 | const agent0 = agentsList[0];
|
116 | testAgentIntegrity(
|
117 | agent0,
|
118 | 'leila_et_les_chasseurs',
|
119 | CONFIGURATION_1
|
120 | );
|
121 | const agent1 = agentsList[1];
|
122 | expect(agent1.status).to.be.equal(400);
|
123 | expect(agent1.error).to.be.equal('ContextError');
|
124 | agentsList.map(({ id }) => client.deleteAgent(id));
|
125 | });
|
126 | });
|
127 | });
|
128 |
|
129 | it('createAgentBulk: should handle invalid id', function() {
|
130 | return client
|
131 | .createAgentBulk([
|
132 | { configuration: CONFIGURATION_1 },
|
133 | { id: 'francis?cabrel', configuration: CONFIGURATION_1 }
|
134 | ])
|
135 | .then((agentsList) => {
|
136 | const agent0 = agentsList[0];
|
137 | testAgentIntegrity(agent0, agent0.id, CONFIGURATION_1);
|
138 |
|
139 | const agent1 = agentsList[1];
|
140 | expect(agent1.id).to.be.equal('francis?cabrel');
|
141 | expect(agent1.status).to.be.equal(400);
|
142 | expect(agent1.error).to.be.equal('AgentError');
|
143 |
|
144 | client.deleteAgent(agent0.id);
|
145 | });
|
146 | });
|
147 |
|
148 | it('createAgentBulk: should 200 then 400 when using the same id twice', function() {
|
149 | const agentId = 'francis_cabrel';
|
150 | return client.deleteAgentBulk([{ id: agentId }])
|
151 | .then(() => {
|
152 | return client
|
153 | .createAgentBulk([{ id: agentId, configuration: CONFIGURATION_1 }])
|
154 | .then((agentsList0) => {
|
155 | agentsList0.map((agent) =>
|
156 | testAgentIntegrity(agent, agentId, CONFIGURATION_1)
|
157 | );
|
158 | })
|
159 | .then(() => {
|
160 | client
|
161 | .createAgentBulk([{ id: agentId, configuration: CONFIGURATION_1 }])
|
162 | .then((agentsList1) => {
|
163 | agentsList1.map((agent) => {
|
164 | expect(agent).to.be.ok;
|
165 | expect(agent.id).to.be.equal(agentId);
|
166 | expect(agent.status).to.be.equal(400);
|
167 | });
|
168 | });
|
169 | })
|
170 | .then(() => client.deleteAgent(agentId));
|
171 | });
|
172 | });
|
173 |
|
174 | it('createAgentBulk: should return array of 200 and 400 if has mixed results', function() {
|
175 | return client
|
176 | .deleteAgentBulk([{ id: 'encore_et_encore' }, { id: 'petite_marie' }])
|
177 | .then(() =>
|
178 | client
|
179 | .createAgentBulk([
|
180 | { id: 'encore_et_encore', configuration: CONFIGURATION_1 }
|
181 | ])
|
182 | .then((agentsList0) => {
|
183 | agentsList0.map((agent) =>
|
184 | testAgentIntegrity(agent, agent.id, CONFIGURATION_1)
|
185 | );
|
186 | client
|
187 | .createAgentBulk([
|
188 | { id: 'encore_et_encore', configuration: CONFIGURATION_1 },
|
189 | { id: 'petite_marie', configuration: CONFIGURATION_1 }
|
190 | ])
|
191 | .then((agentsList1) => {
|
192 | const agent0 = agentsList1[0];
|
193 | const agent1 = agentsList1[1];
|
194 | expect(agent0.id).to.be.equal('encore_et_encore');
|
195 | expect(agent1.id).to.be.equal('petite_marie');
|
196 | expect(agent0.status).to.be.equal(400);
|
197 | expect(agent0.error).to.be.equal('ContextError');
|
198 | agentsList1.map(({ id }) => client.deleteAgent(id));
|
199 | });
|
200 | })
|
201 | );
|
202 | });
|
203 |
|
204 |
|
205 | it('deleteAgentBulk: should succeed when using valid ids.', function() {
|
206 | const agentIds = [
|
207 | { id: 'wild_horses' },
|
208 | { id: 'way_to_rome' },
|
209 | { id: 'postcards' }
|
210 | ];
|
211 | return client.deleteAgentBulk(agentIds)
|
212 | .then(() =>
|
213 | client
|
214 | .createAgentBulk(
|
215 | agentIds.map(({ id }) => ({ id, configuration: CONFIGURATION_1 }))
|
216 | )
|
217 | .then((agentsList0) => {
|
218 | agentsList0.map((agent, idx) => {
|
219 | testAgentIntegrity(agent, agentIds[idx].id, CONFIGURATION_1);
|
220 | });
|
221 | return client.deleteAgentBulk(agentIds)
|
222 | .then((agentsList1) => {
|
223 | agentsList1.map((agent, idx) => {
|
224 | expect(agent.id).to.be.equal(agentIds[idx].id);
|
225 | expect(agent.configuration).to.be.deep.equal(CONFIGURATION_1);
|
226 | });
|
227 | return client.deleteAgentBulk(agentIds)
|
228 | .then((agentsList2) => {
|
229 | agentsList2.map((agent, idx) => {
|
230 | expect(agent.id).to.be.equal(agentIds[idx].id);
|
231 | expect(agent.configuration).to.be.equal(undefined);
|
232 | });
|
233 | });
|
234 | });
|
235 | })
|
236 | );
|
237 | });
|
238 |
|
239 | it('deleteAgentBulk: should handle undefined id', function() {
|
240 | const agentIds = [{ id: '7$ shopping' }, {}, { id: undefined }];
|
241 | return client.deleteAgentBulk(agentIds)
|
242 | .then((del_res) => {
|
243 | expect(del_res[0].id).to.be.equal(agentIds[0].id);
|
244 | expect(del_res[0].status).to.be.equal(400);
|
245 | expect(del_res[0].error).to.be.equal('AgentError');
|
246 | expect(del_res[1].status).to.be.equal(400);
|
247 | expect(del_res[1].error).to.be.equal('ContextError');
|
248 | expect(del_res[2].status).to.be.equal(400);
|
249 | expect(del_res[2].error).to.be.equal('ContextError');
|
250 | });
|
251 | });
|
252 |
|
253 |
|
254 | it('addAgentContextOperationsBulk: should work with 10 agents with small number of operations', function() {
|
255 | const agentIds = Array.apply(null, Array(10))
|
256 | .map((x, i) => ({
|
257 | id: `agent${i}`
|
258 | }));
|
259 | return client
|
260 | .deleteAgentBulk(agentIds)
|
261 | .then(() =>
|
262 | client
|
263 | .createAgentBulk(
|
264 | agentIds.map(({ id }) => ({ id, configuration: CONFIGURATION_1 }))
|
265 | )
|
266 | .then(() =>
|
267 | client
|
268 | .addAgentContextOperationsBulk(
|
269 | agentIds.map(({ id }) => ({
|
270 | id,
|
271 | operations: CONFIGURATION_1_OPERATIONS_1
|
272 | }))
|
273 | )
|
274 | .then((result) => {
|
275 | agentIds.map((agent, idx) => {
|
276 | expect(result[idx].id).to.be.equal(agent.id);
|
277 | expect(result[idx].status).to.be.equal(201);
|
278 | });
|
279 | client.deleteAgentBulk(agentIds);
|
280 | })
|
281 | )
|
282 | )
|
283 | .catch((err) => {
|
284 | if (err.response) {
|
285 | throw new Error(err.response.body.message);
|
286 | }
|
287 | throw err;
|
288 | });
|
289 | });
|
290 |
|
291 | it('addAgentContextOperationsBulk: should work with 10 agents with large number of operations', function() {
|
292 | const agentIds = Array.apply(null, Array(10))
|
293 | .map((x, i) => ({
|
294 | id: `agent${i}`
|
295 | }));
|
296 | return client
|
297 | .deleteAgentBulk(agentIds)
|
298 | .then(() =>
|
299 | client
|
300 | .createAgentBulk(
|
301 | agentIds.map(({ id }) => ({ id, configuration: CONFIGURATION_1 }))
|
302 | )
|
303 | .then(() =>
|
304 | client
|
305 | .addAgentContextOperationsBulk(
|
306 | agentIds.map(({ id }) => ({
|
307 | id,
|
308 | operations: CONFIGURATION_1_OPERATIONS_2
|
309 | }))
|
310 | )
|
311 | .then((result) => {
|
312 | agentIds.map((agent, idx) => {
|
313 | expect(result[idx].id).to.be.equal(agent.id);
|
314 | expect(result[idx].status).to.be.equal(201);
|
315 | });
|
316 | client.deleteAgentBulk(agentIds);
|
317 | })
|
318 | )
|
319 | )
|
320 | .catch((err) => {
|
321 | if (err.response) {
|
322 | throw new Error(err.response.body.message);
|
323 | }
|
324 | throw err;
|
325 | });
|
326 | });
|
327 |
|
328 | it('addAgentContextOperationsBulk: should succeed with agents with different number of operations', function() {
|
329 | const agentIds = [{ id: 'agent0' }, { id: 'agent1' }, { id: 'agent2' }];
|
330 | return client.deleteAgentBulk(agentIds)
|
331 | .then(() =>
|
332 | client
|
333 | .createAgentBulk(
|
334 | agentIds.map(({ id }) => ({ id, configuration: CONFIGURATION_1 }))
|
335 | )
|
336 | .then(() =>
|
337 | client
|
338 | .addAgentContextOperationsBulk([
|
339 | { id: 'agent0', operations: CONFIGURATION_1_OPERATIONS_1 },
|
340 | { id: 'agent1', operations: CONFIGURATION_1_OPERATIONS_2 },
|
341 | { id: 'agent2', operations: CONFIGURATION_1_OPERATIONS_1 }
|
342 | ])
|
343 | .then((result) => {
|
344 | expect(result[0].id).to.be.equal('agent1');
|
345 | expect(result[1].id).to.be.equal('agent0');
|
346 | expect(result[2].id).to.be.equal('agent2');
|
347 | result.map(({ status }) => expect(status).to.be.equal(201));
|
348 | client.deleteAgentBulk(agentIds);
|
349 | })
|
350 | )
|
351 | );
|
352 | });
|
353 |
|
354 | it('addAgentContextOperationsBulk: should handle invalid agents', function() {
|
355 | const agentIds = [{ id: 'john_doe' }];
|
356 | const agentWrongIds = [
|
357 | ...agentIds,
|
358 | { id: 'john_doe_not_found' },
|
359 | { id: 'john?doe' }
|
360 | ];
|
361 | return client.deleteAgentBulk(agentWrongIds)
|
362 | .then(() =>
|
363 | client
|
364 | .createAgentBulk(
|
365 | agentIds.map(({ id }) => ({ id, configuration: CONFIGURATION_1 }))
|
366 | )
|
367 | .then((res) => {
|
368 | client
|
369 | .addAgentContextOperationsBulk(
|
370 | agentWrongIds.map(({ id }) => ({
|
371 | id,
|
372 | operations: CONFIGURATION_1_OPERATIONS_1
|
373 | }))
|
374 | )
|
375 | .then((result) => {
|
376 | expect(result[0].id).to.be.equal(agentWrongIds[0].id);
|
377 | expect(result[0].status).to.be.equal(201);
|
378 | expect(result[1].id).to.be.equal(agentWrongIds[1].id);
|
379 | expect(result[1].status).to.be.equal(404);
|
380 | expect(result[1].error).to.be.equal('NotFound');
|
381 | expect(result[2].id).to.be.equal(agentWrongIds[2].id);
|
382 | expect(result[2].status).to.be.equal(500);
|
383 | expect(result[2].error).to.be.equal('UnexpectedError');
|
384 |
|
385 | client.deleteAgentBulk(agentIds);
|
386 | });
|
387 | })
|
388 | );
|
389 | });
|
390 |
|
391 | it('addAgentContextOperationsBulk: should handle invalid context', function() {
|
392 | const agentIds = [{ id: 'John_Lemon' }, { id: 'Insane_Bane' }];
|
393 | return client.deleteAgentBulk(agentIds)
|
394 | .then(() =>
|
395 | client.createAgentBulk(
|
396 | agentIds.map(({ id }) => ({ id, configuration: CONFIGURATION_1 }))
|
397 | )
|
398 | .then((res) => {
|
399 | client.addAgentContextOperationsBulk(
|
400 | agentIds.map(({ id }) => ({
|
401 | id,
|
402 | operations: INVALID_CONFIGURATION_1_OPERATIONS_1
|
403 | }))
|
404 | )
|
405 | .then((results) => {
|
406 | results.map((agent_res, idx) => {
|
407 | expect(agent_res.id).to.be.equal(agentIds[idx].id);
|
408 | expect(agent_res.status).to.be.equal(400);
|
409 | expect(agent_res.error).to.be.equal('InvalidPropertyValue');
|
410 | });
|
411 | });
|
412 | client.deleteAgentBulk(agentIds);
|
413 | })
|
414 | );
|
415 | });
|
416 |
|
417 |
|
418 | it('getAgentDecisionTreeBulk: should work with two valid agents', function() {
|
419 | const agentIds = [{ id: 'charlotte_cardin' }, { id: 'ben_harper' }];
|
420 | return client.deleteAgentBulk(agentIds)
|
421 | .then(() =>
|
422 | client
|
423 | .createAgentBulk(
|
424 | agentIds.map(({ id }) => ({ id, configuration: CONFIGURATION_1 }))
|
425 | )
|
426 | .then(() =>
|
427 | client
|
428 | .addAgentContextOperationsBulk(
|
429 | agentIds.map(({ id }) => ({
|
430 | id,
|
431 | operations: CONFIGURATION_1_OPERATIONS_1
|
432 | }))
|
433 | )
|
434 | .then(() =>
|
435 | client
|
436 | .getAgentDecisionTreeBulk(
|
437 | agentIds.map(({ id }) => ({ id, timestamp: TS0 }))
|
438 | )
|
439 | .then((agentTrees) => {
|
440 | agentTrees.map((agent, idx) => {
|
441 | expect(agent.id).to.be.equal(agentIds[idx].id);
|
442 | expect(agent.timestamp).to.be.equal(TS0);
|
443 | expect(agent).to.be.ok;
|
444 | const { _version, configuration, trees } = agent.tree;
|
445 | expect(trees).to.be.ok;
|
446 | expect(_version).to.be.ok;
|
447 | expect(configuration).to.be.deep.equal(CONFIGURATION_1);
|
448 | });
|
449 | return client.deleteAgentBulk(agentIds);
|
450 | })
|
451 | )
|
452 | )
|
453 | );
|
454 | });
|
455 |
|
456 | it('getAgentDecisionTreeBulk: should handle unvalid agents ids', function() {
|
457 | const agentIds = [{ id: 'twenty_one' }, { id: 'pilots' }];
|
458 | const agentWrongIds = [...agentIds, { id: 'w3!rD_[D' }];
|
459 | return client.deleteAgentBulk(agentIds)
|
460 | .then(() =>
|
461 | client
|
462 | .createAgentBulk(
|
463 | agentIds.map(({ id }) => ({ id, configuration: CONFIGURATION_1 }))
|
464 | )
|
465 | .then(() =>
|
466 | client
|
467 | .addAgentContextOperationsBulk(
|
468 | agentIds.map(({ id }) => ({
|
469 | id,
|
470 | operations: CONFIGURATION_1_OPERATIONS_1
|
471 | }))
|
472 | )
|
473 | .then(() =>
|
474 | client
|
475 | .getAgentDecisionTreeBulk(
|
476 | agentWrongIds.map(({ id }) => ({ id, timestamp: TS0 }))
|
477 | )
|
478 | .then((agentTrees) => {
|
479 | agentTrees.map((agent, idx) => {
|
480 | expect(agent.id).to.be.equal(agentWrongIds[idx].id);
|
481 | expect(agent.timestamp).to.be.equal(TS0);
|
482 | });
|
483 | agentIds.map((agent, idx) => {
|
484 | expect(agentTrees[idx]).to.be.ok;
|
485 | const { _version, configuration, trees } = agentTrees[idx].tree;
|
486 | expect(trees).to.be.ok;
|
487 | expect(_version).to.be.ok;
|
488 | expect(configuration).to.be.deep.equal(CONFIGURATION_1);
|
489 | });
|
490 | expect(agentTrees[2].status).to.be.equal(400);
|
491 | expect(agentTrees[2].error).to.be.equal('AgentError');
|
492 |
|
493 | return client.deleteAgentBulk(agentIds);
|
494 | })
|
495 | )
|
496 | )
|
497 | );
|
498 | });
|
499 |
|
500 | it('getAgentDecisionTreeBulk: should handle several timestamps', function() {
|
501 | const agentId = 'tom_walker';
|
502 | const timestamps = [TS0, TS0 + 1000, TS0 + 2000];
|
503 | return client.deleteAgent(agentId)
|
504 | .then(() =>
|
505 | client
|
506 | .createAgentBulk([{
|
507 | id: agentId, configuration: CONFIGURATION_1
|
508 | }])
|
509 | .then(() =>
|
510 | client
|
511 | .addAgentContextOperationsBulk([{
|
512 | id: agentId,
|
513 | operations: CONFIGURATION_1_OPERATIONS_1
|
514 | }])
|
515 | .then(() =>
|
516 | client
|
517 | .getAgentDecisionTreeBulk(
|
518 | timestamps.map((timestamp) => ({ id: agentId, timestamp }))
|
519 | )
|
520 | .then((agentTrees) => {
|
521 | agentTrees.map((agent, idx) => {
|
522 | expect(agent.id).to.be.equal(agentId);
|
523 | expect(agent.timestamp).to.be.equal(timestamps[idx]);
|
524 | expect(agent).to.be.ok;
|
525 | const { _version, configuration, trees } = agent.tree;
|
526 | expect(trees).to.be.ok;
|
527 | expect(_version).to.be.ok;
|
528 | expect(configuration).to.be.deep.equal(CONFIGURATION_1);
|
529 | });
|
530 |
|
531 | return client.deleteAgent(agentId);
|
532 | })
|
533 | )
|
534 | )
|
535 | );
|
536 | });
|
537 |
|
538 |
|
539 | it('getAgentDecisionTreeBulk: should handle invalid timestamps', function() {
|
540 | const agentId = 'tom_walker';
|
541 | const timestamps = [TS0, 'INVALID_TIMESTAMP'];
|
542 | return client.deleteAgent(agentId)
|
543 | .then(() =>
|
544 | client
|
545 | .createAgentBulk([{
|
546 | id: agentId, configuration: CONFIGURATION_1
|
547 | }])
|
548 | .then(() =>
|
549 | client
|
550 | .addAgentContextOperationsBulk([{
|
551 | id: agentId,
|
552 | operations: CONFIGURATION_1_OPERATIONS_1
|
553 | }])
|
554 | .then(() =>
|
555 | client
|
556 | .getAgentDecisionTreeBulk(
|
557 | timestamps.map((timestamp) => ({ id: agentId, timestamp }))
|
558 | )
|
559 | .then((agentTrees) => {
|
560 | agentTrees.map((agent, idx) => {
|
561 | expect(agent.id).to.be.equal(agentId);
|
562 | expect(agent.timestamp).to.be.equal(timestamps[idx]);
|
563 | });
|
564 | const { _version, configuration, trees } = agentTrees[0].tree;
|
565 | expect(trees).to.be.ok;
|
566 | expect(_version).to.be.ok;
|
567 | expect(configuration).to.be.deep.equal(CONFIGURATION_1);
|
568 | expect(agentTrees[1].status).to.be.equal(400);
|
569 | expect(agentTrees[1].error).to.be.equal('ContextError');
|
570 |
|
571 | return client.deleteAgent(agentId);
|
572 | })
|
573 | )
|
574 | )
|
575 | );
|
576 | });
|
577 | });
|