root / snf-cyclades-app / synnefo / ui / static / snf / js / tests.js @ 9ccb70fd
History | View | Annotate | Download (17.2 kB)
1 |
// Copyright 2011 GRNET S.A. All rights reserved.
|
---|---|
2 |
//
|
3 |
// Redistribution and use in source and binary forms, with or
|
4 |
// without modification, are permitted provided that the following
|
5 |
// conditions are met:
|
6 |
//
|
7 |
// 1. Redistributions of source code must retain the above
|
8 |
// copyright notice, this list of conditions and the following
|
9 |
// disclaimer.
|
10 |
//
|
11 |
// 2. Redistributions in binary form must reproduce the above
|
12 |
// copyright notice, this list of conditions and the following
|
13 |
// disclaimer in the documentation and/or other materials
|
14 |
// provided with the distribution.
|
15 |
//
|
16 |
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
17 |
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 |
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
19 |
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
20 |
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23 |
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
24 |
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
26 |
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27 |
// POSSIBILITY OF SUCH DAMAGE.
|
28 |
//
|
29 |
// The views and conclusions contained in the software and
|
30 |
// documentation are those of the authors and should not be
|
31 |
// interpreted as representing official policies, either expressed
|
32 |
// or implied, of GRNET S.A.
|
33 |
//
|
34 |
|
35 |
$(document).ready(function(){ |
36 |
|
37 |
// shortcuts
|
38 |
snf = synnefo; |
39 |
models = snf.models; |
40 |
util = snf.utils; |
41 |
views = snf.views; |
42 |
bb = Backbone; |
43 |
vms = snf.storage.vms; |
44 |
nets = snf.storage.networks; |
45 |
|
46 |
synnefo.config.api_urls = { |
47 |
'compute': '/api/v1.1', |
48 |
'glance':'/images/v1.1' |
49 |
}; |
50 |
|
51 |
module("VM Model")
|
52 |
|
53 |
test("model change events", function(){ |
54 |
expect(8);
|
55 |
|
56 |
synnefo.storage.images.add({id:1,metadata:{values:{size:100}}}); |
57 |
var v1 = new models.VM({'imageRef':1}); |
58 |
v1.bind("change", function(){ |
59 |
ok(1, "change event triggered") |
60 |
equals(v1.get("status"), "BUILD") |
61 |
equals(v1.get("state"), "BUILD_COPY") |
62 |
}) |
63 |
v1.set({'status':'BUILD', 'progress':80, 'imageRef': 1}); |
64 |
v1.unbind(); |
65 |
|
66 |
v1.bind("change", function(){ |
67 |
ok(1, "change event triggered") |
68 |
equals(v1.get("status"), "BUILD") |
69 |
equals(v1.get("state"), "DESTROY") |
70 |
}) |
71 |
v1.set({'state':'DESTROY'}); |
72 |
v1.unbind(); |
73 |
|
74 |
v1.bind("change", function() { |
75 |
ok(1, "change event triggered") |
76 |
equals(v1.get("status"), "BUILD") |
77 |
equals(v1.get("state"), "BUILD_COPY") |
78 |
}) |
79 |
v1.set({'status':'BUILD', 'progress':80, 'imageRef': 1}); |
80 |
equals(v1.get("status"), "BUILD") |
81 |
equals(v1.get("state"), "DESTROY") |
82 |
v1.unbind(); |
83 |
}) |
84 |
|
85 |
test("model state transitions", function(){ |
86 |
var vm = models.VM;
|
87 |
|
88 |
var v1 = new vm(); |
89 |
v1.set({status: 'BUILD_COPY'}) |
90 |
equals(v1.get("state"), 'BUILD_COPY', "State is set"); |
91 |
v1.set({status: 'DESTROY'}) |
92 |
equals(v1.get("state"), 'DESTROY', "From buld to destroy"); |
93 |
v1.set({status: 'BUILD'}) |
94 |
equals(v1.get("state"), 'DESTROY', "Keep destroy state"); |
95 |
|
96 |
v1 = new vm();
|
97 |
v1.set({status: 'ACTIVE'}) |
98 |
equals(v1.get("state"), 'ACTIVE', "State is set"); |
99 |
|
100 |
v1.set({status: 'SHUTDOWN'}) |
101 |
equals(v1.get("state"), 'SHUTDOWN', "From active to shutdown (should change)"); |
102 |
|
103 |
v1.set({status: 'ACTIVE'}) |
104 |
equals(v1.get("state"), 'SHUTDOWN', "From shutdown to active (should not change)"); |
105 |
|
106 |
v1.set({status: 'STOPPED'}) |
107 |
equals(v1.get("state"), 'STOPPED', "From shutdown to stopped (should change)"); |
108 |
|
109 |
v1.set({status: 'ACTIVE'}) |
110 |
equals(v1.get("state"), 'ACTIVE', "From stopped to active (should change)"); |
111 |
v1.set({'status': 'STOPPED'}) |
112 |
equals(v1.get('state'), 'STOPPED', "From shutdown to stopped should change"); |
113 |
|
114 |
v1.set({'status': 'DESTROY'}) |
115 |
equals(v1.get('state'), 'DESTROY', "From stopped to destory should set state to DESTROY"); |
116 |
v1.set({'status': 'ACTIVE'}) |
117 |
equals(v1.get('state'), 'DESTROY', "From destroy to active should keep state to DESTROY"); |
118 |
v1.set({'status': 'REBOOT'}) |
119 |
equals(v1.get('state'), 'DESTROY', "From destroy to active should keep state to DESTROY"); |
120 |
v1.set({'status': 'DELETED'}) |
121 |
equals(v1.get('state'), 'DELETED', "Destroy should be kept until DELETE or ERROR"); |
122 |
|
123 |
v1 = new vm({status:'BUILD'}); |
124 |
equals(v1.get('state'), 'BUILD', "new vm with build as initial status") |
125 |
equals(v1.get('status'), 'BUILD', "new vm with build as initial status") |
126 |
v1.set({status:'ACTIVE'}) |
127 |
equals(v1.get('state'), 'ACTIVE', "active state has been set") |
128 |
equals(v1.get('status'), 'ACTIVE', "active status has been set") |
129 |
}) |
130 |
|
131 |
|
132 |
test("building states", function(){ |
133 |
synnefo.storage.images.add({id:1,metadata:{values:{size:100}}}); |
134 |
var vm = models.VM;
|
135 |
var v1 = new vm({'status':'BUILD','progress':0, 'imageRef':1}); |
136 |
equals(v1.get('state'), 'BUILD_INIT', "progress 0 sets state to BUILD_INIT"); |
137 |
equals(v1.get('status'), 'BUILD', "progress 0 sets status to BUILD"); |
138 |
equals(v1.get('progress_message'), 'init', "message 'init'"); |
139 |
v1.set({status:'BUILD', progress:50}); |
140 |
equals(v1.get('state'), 'BUILD_COPY', "progress 50 sets state to BUILD_COPY"); |
141 |
equals(v1.get('status'), 'BUILD', "progress 50 sets status to BUILD"); |
142 |
equals(v1.get('progress_message'), '50.00 MB, 100.00 MB, 50', "message: 'final'"); |
143 |
v1.set({status:'BUILD', progress:100}); |
144 |
equals(v1.get('state'), 'BUILD_FINAL', "progress 100 sets state to BUILD_FINAL"); |
145 |
equals(v1.get('status'), 'BUILD', "progress 100 sets status to BUILD"); |
146 |
v1.set({status:'ACTIVE', progress:100}); |
147 |
equals(v1.get('state'), 'ACTIVE', "ACTIVE set transition to ACTIVE"); |
148 |
equals(v1.get('status'), 'ACTIVE', "ACTIVE set transition to ACTIVE"); |
149 |
equals(v1.get('progress_message'), 'final', "message: 'final'"); |
150 |
}) |
151 |
|
152 |
test("active inactive states", function(){ |
153 |
|
154 |
var vm = models.VM;
|
155 |
var v1 = new vm(); |
156 |
var v = {}
|
157 |
var active = ['ACTIVE', 'BUILD', 'REBOOT']; |
158 |
for (v in active) { |
159 |
v = active[v]; |
160 |
v1.set({status: v})
|
161 |
equals(v1.is_active(), true, v + " status is active") |
162 |
} |
163 |
|
164 |
var v1 = new vm(); |
165 |
var inactive = ['STOPPED', 'ERROR', 'UNKNOWN']; |
166 |
for (v in inactive) { |
167 |
v = inactive[v]; |
168 |
v1.set({status: v})
|
169 |
equals(v1.is_active(), false, v1.state() + " status is not active") |
170 |
} |
171 |
|
172 |
}) |
173 |
|
174 |
test("transition event", function(){ |
175 |
expect(9);
|
176 |
|
177 |
var vm = new models.VM({status:'BUILD'}); |
178 |
vm.bind("transition", function(data) { |
179 |
ok(true, "Transition triggered"); |
180 |
equals(data.from, "BUILD")
|
181 |
equals(data.to, "ACTIVE");
|
182 |
}) |
183 |
// trigger 1 time
|
184 |
vm.set({status:'BUILD'}); |
185 |
vm.set({status:'ACTIVE'}); |
186 |
vm.unbind(); |
187 |
|
188 |
// from build to active
|
189 |
vm = new models.VM({status:'BUILD'}); |
190 |
vm.bind("transition", function(data) { |
191 |
ok(true, "Transition triggered"); |
192 |
equals(data.from, "BUILD")
|
193 |
equals(data.to, "ACTIVE");
|
194 |
}) |
195 |
// trigger 1 time
|
196 |
vm.set({status:'ACTIVE'}); |
197 |
vm.unbind(); |
198 |
|
199 |
// from active to shutdown
|
200 |
vm = new models.VM({status:'SHUTDOWN'}); |
201 |
vm.bind("transition", function(data) { |
202 |
ok(true, "Transition triggered"); |
203 |
equals(data.from, "SHUTDOWN")
|
204 |
equals(data.to, "STOPPED");
|
205 |
}) |
206 |
// trigger 1 time
|
207 |
vm.set({status:'STOPPED'}); |
208 |
}) |
209 |
|
210 |
module("Collections");
|
211 |
|
212 |
test("status model remove events", function(){ |
213 |
vms.unbind(); |
214 |
expect(1)
|
215 |
|
216 |
vms.bind("change", function(){ |
217 |
ok(-1, "change event should not get triggered"); |
218 |
}) |
219 |
|
220 |
vms.bind("remove", function(){ |
221 |
ok(1, "remove event triggered") |
222 |
}) |
223 |
|
224 |
var vm = new models.VM({id:1, status:"ACTIVE", name:"oldname"}); |
225 |
vms.add(vm); |
226 |
|
227 |
// NO change/delete just DELETE event triggered
|
228 |
vms.update([{id:1, status:"DELETED", name:"newname"}]) |
229 |
}); |
230 |
|
231 |
test("collection reset events", function() { |
232 |
expect(9);
|
233 |
|
234 |
var testCollection = models.Collection.extend({
|
235 |
url: '/testObject' |
236 |
}); |
237 |
var collection = new testCollection(); |
238 |
|
239 |
|
240 |
// reset on new entry after empty
|
241 |
$.mockjax({
|
242 |
url: '/testObject', |
243 |
responseTime: 50, |
244 |
responseText: [
|
245 |
{id:1, attr1: 1, attr2: 2} |
246 |
] |
247 |
}); |
248 |
// THIS SHOULD NOT FIRE, since we force update method
|
249 |
collection.bind("reset", function() { |
250 |
ok(1, "NOT EXPECTED: reset triggered on new entry while collection was empty"); |
251 |
}); |
252 |
collection.bind("add", function() { |
253 |
ok(1, "1: add triggered on new entry while collection was empty"); |
254 |
}); |
255 |
// THIS SHOULD NOT FIRE, model was added, not changed
|
256 |
collection.bind("change", function() { |
257 |
ok(1, "NOT EXPECTED: change triggered on new entry while collection was empty"); |
258 |
}); |
259 |
collection.fetch({'async': false}); |
260 |
equals(collection.length, 1, "2: collection contains 1 model"); |
261 |
collection.unbind(); |
262 |
$.mockjaxClear();
|
263 |
|
264 |
// reset is called on change
|
265 |
$.mockjax({
|
266 |
url: '/testObject', |
267 |
responseTime: 50, |
268 |
responseText: [
|
269 |
{id:1, attr1: 4, attr2: 2} |
270 |
] |
271 |
}); |
272 |
collection.bind("reset", function() { |
273 |
ok(1, "NOT EXPECTED: reset triggered on new entry while collection was empty"); |
274 |
}); |
275 |
collection.bind("add", function() { |
276 |
ok(1, "NOT EXPECTED: add triggered on new entry while collection was empty"); |
277 |
}); |
278 |
// THIS SHOULD NOT FIRE, model was added, not changed
|
279 |
collection.bind("change", function() { |
280 |
ok(1, "3: change triggered on new entry while collection was empty"); |
281 |
}); |
282 |
collection.fetch({'async': false, refresh:true}); |
283 |
equals(collection.length, 1, "4 collection contains 1 model"); |
284 |
collection.unbind(); |
285 |
$.mockjaxClear();
|
286 |
|
287 |
// reset on second entry
|
288 |
$.mockjax({
|
289 |
url: '/testObject', |
290 |
responseTime: 50, |
291 |
responseText: [
|
292 |
{id:1, attr1: 4, attr2: 2}, |
293 |
{id:2, attr1: 1, attr2: 2} |
294 |
] |
295 |
}); |
296 |
collection.bind("reset", function() { |
297 |
ok(1, "NOT EXPECTED: reset triggered when new model arrived"); |
298 |
}) |
299 |
collection.bind("add", function() { |
300 |
ok(1, "5: add triggered when new model arrived"); |
301 |
}) |
302 |
collection.bind("change", function() { |
303 |
ok(1, "NOT EXPECTED: change triggered when new model arrived"); |
304 |
}) |
305 |
collection.fetch({async:false, refresh:true}); |
306 |
equals(collection.length, 2, "6 new model added"); |
307 |
collection.unbind(); |
308 |
$.mockjaxClear();
|
309 |
|
310 |
// reset does not remove
|
311 |
$.mockjax({
|
312 |
url: '/testObject', |
313 |
responseTime: 50, |
314 |
responseText: [
|
315 |
{id:2, attr1: 1, attr2: 2} |
316 |
] |
317 |
}); |
318 |
collection.bind("reset", function() { |
319 |
ok(1, "NOT EXPECTED: reset triggered when model removed"); |
320 |
}) |
321 |
collection.bind("remove", function() { |
322 |
ok(1, "7: remove triggered when model removed"); |
323 |
}) |
324 |
collection.bind("change", function() { |
325 |
ok(1, "NOT EXPECTED: change triggered when model removed"); |
326 |
}) |
327 |
|
328 |
collection.fetch({async:false, refresh:true}); |
329 |
equals(collection.length, 1, "8 one model removed"); |
330 |
collection.unbind(); |
331 |
$.mockjaxClear();
|
332 |
|
333 |
// reset is not called on 304
|
334 |
$.mockjax({
|
335 |
url: '/testObject', |
336 |
responseTime: 50, |
337 |
status: 304, |
338 |
responseText: undefined |
339 |
}); |
340 |
// NO event is triggered on 304
|
341 |
collection.bind("reset", function() { |
342 |
ok(1, "WRONG: reset triggered on 304"); |
343 |
}); |
344 |
collection.bind("remove", function() { |
345 |
ok(1, "WRONG: remove triggered on 304"); |
346 |
}); |
347 |
collection.bind("change", function() { |
348 |
ok(1, "WRONG: remove triggered on 304"); |
349 |
}); |
350 |
collection.fetch({async:false, refresh:true}); |
351 |
equals(collection.length, 1, "9 one model removed"); |
352 |
collection.unbind(); |
353 |
$.mockjaxClear();
|
354 |
}) |
355 |
|
356 |
|
357 |
module("network vm connections")
|
358 |
test("network vm connections", function() { |
359 |
|
360 |
function _net(id, ip) { |
361 |
return {
|
362 |
id: id,
|
363 |
name: "net " + id, |
364 |
values:[{version:4, addr: ip}, {version:6, addr:ip}] |
365 |
} |
366 |
} |
367 |
vms.add({id:1, name:"s1", linked_to_nets:[_net("p", "127")]}); |
368 |
var vm = vms.at(0); |
369 |
|
370 |
nets.add({id:"p", nid:"p", name:"n1", linked_to:[1]}); |
371 |
var n = nets.at(0); |
372 |
}) |
373 |
|
374 |
module("images/flavors")
|
375 |
test("Test DELETE state image retrieval", function() { |
376 |
snf.storage.images.reset(); |
377 |
snf.storage.vms.reset(); |
378 |
|
379 |
var images = snf.storage.images;
|
380 |
var vms = snf.storage.vms;
|
381 |
|
382 |
var img = images.add({name:"image 1", id:1}).last(); |
383 |
var vm1 = vms.add({imageRef:1, name:"vm1"}).last(); |
384 |
var vm2 = vms.add({imageRef:2, name:"vm2"}).last(); |
385 |
|
386 |
equals(img, vm1.get_image()); |
387 |
|
388 |
// reset is not called on 304
|
389 |
$.mockjax({
|
390 |
url: '/api/v1.1/images/2', |
391 |
responseTime: 50, |
392 |
status: 200, |
393 |
responseText: {image:{name:"image 2", id:2}} |
394 |
}); |
395 |
|
396 |
|
397 |
equals(images.length, 1, "1 image exists"); |
398 |
vm2.get_image(); |
399 |
equals(images.get(2).get("name"), "image 2", "image data parsed"); |
400 |
equals(images.length, 2);
|
401 |
}) |
402 |
|
403 |
test("Test DELETE state flavor retrieval", function() { |
404 |
snf.storage.flavors.reset(); |
405 |
snf.storage.vms.reset(); |
406 |
|
407 |
var flavors = snf.storage.flavors;
|
408 |
var vms = snf.storage.vms;
|
409 |
|
410 |
var flv = flavors.add({id:1, cpu:1, disk:1, ram:1024}).last(); |
411 |
var vm1 = vms.add({flavorRef:1, name:"vm1"}).last(); |
412 |
var vm2 = vms.add({flavorRef:2, name:"vm2"}).last(); |
413 |
|
414 |
equals(flv, vm1.get_flavor()); |
415 |
|
416 |
// reset is not called on 304
|
417 |
$.mockjax({
|
418 |
url: '/api/v1.1/flavors/2', |
419 |
responseTime: 50, |
420 |
status: 200, |
421 |
responseText: {flavor:{cpu:1, ram:2048, disk:100, id:2}} |
422 |
}); |
423 |
|
424 |
|
425 |
equals(flavors.length, 1, "1 flavor exists"); |
426 |
vm2.get_flavor(); |
427 |
equals(flavors.get(2).get("ram"), 2048, "flavor data parsed"); |
428 |
equals(flavors.length, 2);
|
429 |
}) |
430 |
|
431 |
test("actions list object", function(){ |
432 |
var m = new models.Image(); |
433 |
var l = new models.ParamsList(m, "actions"); |
434 |
var count = 0; |
435 |
|
436 |
l.add("destroy");
|
437 |
equals(l.has_action("destroy"), true); |
438 |
equals(l.contains("destroy"), true); |
439 |
|
440 |
l.add("destroy", 1, {}); |
441 |
equals(l.has_action("destroy"), true); |
442 |
equals(l.contains("destroy", 1, {}), true); |
443 |
|
444 |
l.remove("destroy", 1, {}); |
445 |
equals(l.contains("destroy", 1, {}), false); |
446 |
|
447 |
m.bind("change:actions", function() { count ++}); |
448 |
l.add("destroy");
|
449 |
|
450 |
equals(count, 0);
|
451 |
l.add("destroy", 1, {}); |
452 |
equals(count, 1);
|
453 |
}); |
454 |
|
455 |
module("update handlers")
|
456 |
test("update handlers", function() { |
457 |
// this test is based on multiple timeouts
|
458 |
// so the results might differ between different browsers
|
459 |
// or browser load
|
460 |
stop(); |
461 |
|
462 |
var counter = 0; |
463 |
var cb = function() { |
464 |
counter++; |
465 |
} |
466 |
|
467 |
var opts = {
|
468 |
callback:cb,
|
469 |
interval: 10, |
470 |
fast: 5, |
471 |
increase: 5, |
472 |
max: 15, |
473 |
increase_after_calls: 3, |
474 |
initial_call: false |
475 |
} |
476 |
|
477 |
var h = new snf.api.updateHandler(opts); |
478 |
h.start(); |
479 |
|
480 |
var add = $.browser.msie ? 8 : 0; |
481 |
|
482 |
window.setTimeout(function(){
|
483 |
h.stop(); |
484 |
start(); |
485 |
// 4 calls, limit reached
|
486 |
equals(counter, 4, "normal calls"); |
487 |
equals(h.interval, opts.max, "limit reached");
|
488 |
|
489 |
stop(); |
490 |
h.start(false);
|
491 |
h.faster(); |
492 |
window.setTimeout(function(){
|
493 |
// 11 calls, limit reached
|
494 |
start(); |
495 |
equals(counter, 11, "faster calls"); |
496 |
equals(h.interval, opts.max, "limit reached");
|
497 |
h.stop(); |
498 |
stop(); |
499 |
window.setTimeout(function(){
|
500 |
// no additional calls because we stopped it
|
501 |
start(); |
502 |
equals(counter, 11, "no additional calls") |
503 |
}, 50 + add)
|
504 |
}, 50 + add)
|
505 |
}, 43 + add)
|
506 |
}) |
507 |
}) |