Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / tests.js @ 1e882dd7

History | View | Annotate | Download (22.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
    // what messages to display based on vm status
52
    synnefo.config.diagnostics_status_messages_map = {
53
        'BUILD': ['image-helper-task-start', 'image-info'],
54
        'ERROR': ['image-error']
55
    };
56
    synnefo.config.diagnostic_messages_tpls = {
57
      'image-helper-task-start': "Running 'MESSAGE'"
58
    };
59

    
60
    snf.user = {'token': 'TESTTOKEN'}
61

    
62
    module("VM Model")
63
    
64
    test("vm diagnostics", function() {
65
        synnefo.storage.images.add({
66
          id:1, 
67
          size: 100,
68
          metadata:{
69
            values: {
70
              size: 100
71
            }
72
          }
73
        });
74
        synnefo.storage.vms.add({id:1, imageRef:1});
75
        var vm = synnefo.storage.vms.at(0);
76
        var diagnostics = [{
77
          source:'image-helper-task-start', 
78
          message:'SSHCopyKey'
79
        }];
80
        synnefo.storage.vms.update([{id:1, 
81
                                     progress: 0,
82
                                     status: "BUILD"}], 
83
                                    {silent:false});
84
        equal(vm.get('status_message'), "init", 
85
              "Show initial progress message");
86
        synnefo.storage.vms.update([{id:1, 
87
                                     progress: 10,
88
                                     status: "BUILD"}], 
89
                                    {silent:false});
90
        equal(vm.get('status_message'), "10.00 MB, 100.00 MB, 10", 
91
              "Construct message based on image size");
92
        synnefo.storage.vms.update([{id:1, 
93
                                     progress: 99,
94
                                     status: "BUILD"}], 
95
                                    {silent:false});
96
        equal(vm.get('status_message'), "99.00 MB, 100.00 MB, 99", 
97
              "Calculate 99% of image size and display image");
98
        synnefo.storage.vms.update([{id:1, 
99
                                     progress: 99,
100
                                     status: "ERROR"}], 
101
                                    {silent:false});
102
        equal(vm.get('status_message'), null);
103
        synnefo.storage.vms.update([{id:1, 
104
                                     progress: 100,
105
                                     status: "BUILD"}], 
106
                                    {silent:false});
107
        equal(vm.get('status_message'), "final", 
108
              "Progress is 100, show finializing progress message");
109
        synnefo.storage.vms.update([{id:1, 
110
                                     progress: 100,
111
                                     diagnostics: _.clone(diagnostics),
112
                                     status: "BUILD"}], 
113
                                    {silent:false});
114

    
115
        synnefo.storage.vms.update([{id:1, 
116
                                     progress: 100,
117
                                     diagnostics: _.clone(diagnostics),
118
                                     status: "BUILD"}], 
119
                                    {silent:false});
120
        equal(vm.get('status_message'), "Running 'SSHCopyKey'", 
121
              "Diagnostics added, show first building diagnostic message");
122

    
123
        diagnostics.unshift({source:'unknown-source', message:'H&^^ACJJ'});
124
        synnefo.storage.vms.update([{id:1, 
125
                                     progress: 100,
126
                                     diagnostics: _.clone(diagnostics),
127
                                     status: "BUILD"}], 
128
                                    {silent:false});
129
        equal(vm.get('status_message'), "Running 'SSHCopyKey'", 
130
              "Unwanted diagnostics get filtered out, progress message remains");
131

    
132
        synnefo.storage.vms.update([{id:1, 
133
                                     progress: 100,
134
                                     diagnostics: _.clone(diagnostics),
135
                                     status: "ERROR"}], 
136
                                    {silent:false});
137
        equal(vm.get('status_message'), null, 
138
              "In error state since no error diagnostic is set we get null");
139

    
140
        diagnostics.unshift({source:'image-error', message:'Image error'});
141
        synnefo.storage.vms.update([{id:1, 
142
                                     progress: 100,
143
                                     diagnostics: _.clone(diagnostics),
144
                                     status: "BUILD"}], 
145
                                    {silent:false});
146
        equal(vm.get('status_message'), "Running 'SSHCopyKey'");
147

    
148
        diagnostics.unshift({source:'image-error', message:'Image error'});
149
        synnefo.storage.vms.update([{id:1, 
150
                                     progress: 100,
151
                                     diagnostics: _.clone(diagnostics),
152
                                     status: "ERROR"}], 
153
                                    {silent:false});
154
        equal(vm.get('status_message'), "Image error");
155

    
156
        synnefo.storage.vms.update([{id:1, 
157
                                     progress: 100,
158
                                     diagnostics: _.clone(diagnostics),
159
                                     status: "ACTIVE"}], 
160
                                    {silent:false});
161
        equal(vm.get('status_message'), null);
162

    
163
    });
164

    
165

    
166
    test("model change events", function(){
167
        expect(8);
168

    
169
        synnefo.storage.images.add({id:1,metadata:{values:{size:100}}});
170
        var v1 = new models.VM({'imageRef':1});
171
        v1.bind("change", function(){
172
            ok(1, "change event triggered")
173
            equal(v1.get("status"), "BUILD")
174
            equal(v1.get("state"), "BUILD_COPY")
175
        })
176
        v1.set({'status':'BUILD', 'progress':80, 'imageRef': 1});
177
        v1.unbind();
178

    
179
        v1.bind("change", function(){
180
            ok(1, "change event triggered")
181
            equal(v1.get("status"), "BUILD")
182
            equal(v1.get("state"), "DESTROY")
183
        })
184
        v1.set({'state':'DESTROY'});
185
        v1.unbind();
186

    
187
        v1.bind("change", function() {
188
            ok(1, "change event triggered")
189
            equal(v1.get("status"), "BUILD")
190
            equal(v1.get("state"), "BUILD_COPY")
191
        })
192
        v1.set({'status':'BUILD', 'progress':80, 'imageRef': 1});
193
        equal(v1.get("status"), "BUILD")
194
        equal(v1.get("state"), "DESTROY")
195
        v1.unbind();
196
    })
197

    
198
    test("model state transitions", function(){
199
        var vm = models.VM;
200
        
201
        var v1 = new vm();
202
        v1.set({status: 'BUILD_COPY'})
203
        equal(v1.get("state"), 'BUILD_COPY', "State is set");
204
        v1.set({status: 'DESTROY'})
205
        equal(v1.get("state"), 'DESTROY', "From buld to destroy");
206
        v1.set({status: 'BUILD'})
207
        equal(v1.get("state"), 'DESTROY', "Keep destroy state");
208

    
209
        v1 = new vm();
210
        v1.set({status: 'ACTIVE'})
211
        equal(v1.get("state"), 'ACTIVE', "State is set");
212

    
213
        v1.set({status: 'SHUTDOWN'})
214
        equal(v1.get("state"), 'SHUTDOWN', "From active to shutdown (should change)");
215

    
216
        v1.set({status: 'ACTIVE'})
217
        equal(v1.get("state"), 'SHUTDOWN', "From shutdown to active (should not change)");
218
        
219
        v1.set({status: 'STOPPED'})
220
        equal(v1.get("state"), 'STOPPED', "From shutdown to stopped (should change)");
221

    
222
        v1.set({status: 'ACTIVE'})
223
        equal(v1.get("state"), 'ACTIVE', "From stopped to active (should change)");
224
        v1.set({'status': 'STOPPED'})
225
        equal(v1.get('state'), 'STOPPED', "From shutdown to stopped should change");
226

    
227
        v1.set({'status': 'DESTROY'})
228
        equal(v1.get('state'), 'DESTROY', "From stopped to destory should set state to DESTROY");
229
        v1.set({'status': 'ACTIVE'})
230
        equal(v1.get('state'), 'DESTROY', "From destroy to active should keep state to DESTROY");
231
        v1.set({'status': 'REBOOT'})
232
        equal(v1.get('state'), 'DESTROY', "From destroy to active should keep state to DESTROY");
233
        v1.set({'status': 'DELETED'})
234
        equal(v1.get('state'), 'DELETED', "Destroy should be kept until DELETE or ERROR");
235

    
236
        v1 = new vm({status:'BUILD'});
237
        equal(v1.get('state'), 'BUILD', "new vm with build as initial status")
238
        equal(v1.get('status'), 'BUILD', "new vm with build as initial status")
239
        v1.set({status:'ACTIVE'})
240
        equal(v1.get('state'), 'ACTIVE', "active state has been set")
241
        equal(v1.get('status'), 'ACTIVE', "active status has been set")
242
    })
243

    
244

    
245
    test("building states", function(){
246
        synnefo.storage.images.add({id:1,metadata:{values:{size:100}}});
247
        var vm = models.VM;
248
        var v1 = new vm({'status':'BUILD','progress':0, 'imageRef':1});
249
        equal(v1.get('state'), 'BUILD_INIT', "progress 0 sets state to BUILD_INIT");
250
        equal(v1.get('status'), 'BUILD', "progress 0 sets status to BUILD");
251
        equal(v1.get('progress_message'), 'init', "message 'init'");
252
        v1.set({status:'BUILD', progress:50});
253
        equal(v1.get('state'), 'BUILD_COPY', "progress 50 sets state to BUILD_COPY");
254
        equal(v1.get('status'), 'BUILD', "progress 50 sets status to BUILD");
255
        equal(v1.get('progress_message'), '50.00 MB, 100.00 MB, 50', "message: 'final'");
256
        v1.set({status:'BUILD', progress:100});
257
        equal(v1.get('state'), 'BUILD_FINAL', "progress 100 sets state to BUILD_FINAL");
258
        equal(v1.get('status'), 'BUILD', "progress 100 sets status to BUILD");
259
        v1.set({status:'ACTIVE', progress:100});
260
        equal(v1.get('state'), 'ACTIVE', "ACTIVE set transition to ACTIVE");
261
        equal(v1.get('status'), 'ACTIVE', "ACTIVE set transition to ACTIVE");
262
        equal(v1.get('progress_message'), 'final', "message: 'final'");
263
    })
264

    
265
    test("active inactive states", function(){
266
    
267
        var vm = models.VM;
268
        var v1 = new vm();
269
        var v = {}
270
        var active = ['ACTIVE', 'BUILD', 'REBOOT'];
271
        for (v in active) {
272
            v = active[v];
273
            v1.set({status: v})
274
            equal(v1.is_active(), true, v + " status is active")
275
        }
276
        
277
        var v1 = new vm();
278
        var inactive = ['STOPPED', 'ERROR', 'UNKNOWN'];
279
        for (v in inactive) {
280
            v = inactive[v];
281
            v1.set({status: v})
282
            equal(v1.is_active(), false, v1.state() + " status is not active")
283
        }
284
    
285
    })
286

    
287
    test("transition event", function(){
288
        expect(9);
289

    
290
        var vm = new models.VM({status:'BUILD'});
291
        vm.bind("transition", function(data) {
292
            ok(true, "Transition triggered");
293
            equal(data.from, "BUILD")
294
            equal(data.to, "ACTIVE");
295
        })
296
        // trigger 1 time
297
        vm.set({status:'BUILD'});
298
        vm.set({status:'ACTIVE'});
299
        vm.unbind();
300
        
301
        // from build to active
302
        vm = new models.VM({status:'BUILD'});
303
        vm.bind("transition", function(data) {
304
            ok(true, "Transition triggered");
305
            equal(data.from, "BUILD")
306
            equal(data.to, "ACTIVE");
307
        })
308
        // trigger 1 time
309
        vm.set({status:'ACTIVE'});
310
        vm.unbind();
311

    
312
        // from active to shutdown
313
        vm = new models.VM({status:'SHUTDOWN'});
314
        vm.bind("transition", function(data) {
315
            ok(true, "Transition triggered");
316
            equal(data.from, "SHUTDOWN")
317
            equal(data.to, "STOPPED");
318
        })
319
        // trigger 1 time
320
        vm.set({status:'STOPPED'});
321
    })
322
    
323
    module("Collections");
324
        
325
    test("status model remove events", function(){
326
        vms.unbind();
327
        expect(1)
328

    
329
        vms.bind("change", function(){
330
            ok(-1, "change event should not get triggered");
331
        })
332

    
333
        vms.bind("remove", function(){
334
            ok(1, "remove event triggered")
335
        })
336

    
337
        var vm = new models.VM({id:1, status:"ACTIVE", name:"oldname"});
338
        vms.add(vm);
339
        
340
        // NO change/delete just DELETE event triggered
341
        vms.update([{id:1, status:"DELETED", name:"newname"}])
342
    });
343

    
344
    test("collection reset events", function() {
345
        expect(9);
346

    
347
        var testCollection = models.Collection.extend({
348
            url: '/testObject'
349
        });
350
        var collection = new testCollection();
351

    
352
        
353
        // reset on new entry after empty
354
        $.mockjax({
355
            url: '/testObject',
356
            responseTime: 50,
357
            responseText: [
358
                {id:1, attr1: 1, attr2: 2}
359
            ]
360
        }); 
361
        // THIS SHOULD NOT FIRE, since we force update method
362
        collection.bind("reset", function() {
363
            ok(1, "NOT EXPECTED: reset triggered on new entry while collection was empty");
364
        });
365
        collection.bind("add", function() {
366
            ok(1, "1: add triggered on new entry while collection was empty");
367
        });
368
        // THIS SHOULD NOT FIRE, model was added, not changed
369
        collection.bind("change", function() {
370
            ok(1, "NOT EXPECTED: change triggered on new entry while collection was empty");
371
        });
372
        collection.fetch({'async': false});
373
        equal(collection.length, 1, "2: collection contains 1 model");
374
        collection.unbind();
375
        $.mockjaxClear();
376
        
377
        // reset is called on change
378
        $.mockjax({
379
            url: '/testObject',
380
            responseTime: 50,
381
            responseText: [
382
                {id:1, attr1: 4, attr2: 2}
383
            ]
384
        });
385
        collection.bind("reset", function() {
386
            ok(1, "NOT EXPECTED: reset triggered on new entry while collection was empty");
387
        });
388
        collection.bind("add", function() {
389
            ok(1, "NOT EXPECTED: add triggered on new entry while collection was empty");
390
        });
391
        // THIS SHOULD NOT FIRE, model was added, not changed
392
        collection.bind("change", function() {
393
            ok(1, "3: change triggered on new entry while collection was empty");
394
        });
395
        collection.fetch({'async': false, refresh:true});
396
        equal(collection.length, 1, "4 collection contains 1 model");
397
        collection.unbind();
398
        $.mockjaxClear();
399

    
400
        // reset on second entry
401
        $.mockjax({
402
            url: '/testObject',
403
            responseTime: 50,
404
            responseText: [
405
                {id:1, attr1: 4, attr2: 2},
406
                {id:2, attr1: 1, attr2: 2}
407
            ]
408
        });
409
        collection.bind("reset", function() {
410
            ok(1, "NOT EXPECTED: reset triggered when new model arrived");
411
        })
412
        collection.bind("add", function() {
413
            ok(1, "5: add triggered when new model arrived");
414
        })
415
        collection.bind("change", function() {
416
            ok(1, "NOT EXPECTED: change triggered when new model arrived");
417
        }) 
418
        collection.fetch({async:false, refresh:true});
419
        equal(collection.length, 2, "6 new model added");
420
        collection.unbind();
421
        $.mockjaxClear();
422
        
423
        // reset does not remove
424
        $.mockjax({
425
            url: '/testObject',
426
            responseTime: 50,
427
            responseText: [
428
                {id:2, attr1: 1, attr2: 2}
429
            ]
430
        }); 
431
        collection.bind("reset", function() {
432
            ok(1, "NOT EXPECTED: reset triggered when model removed");
433
        })
434
        collection.bind("remove", function() {
435
            ok(1, "7: remove triggered when model removed");
436
        })
437
        collection.bind("change", function() {
438
            ok(1, "NOT EXPECTED: change triggered when model removed");
439
        })
440

    
441
        collection.fetch({async:false, refresh:true});
442
        equal(collection.length, 1, "8 one model removed");
443
        collection.unbind();
444
        $.mockjaxClear();
445

    
446
        // reset is not called on 304
447
        $.mockjax({
448
            url: '/testObject',
449
            responseTime: 50,
450
            status: 304,
451
            responseText: undefined
452
        }); 
453
        // NO event is triggered on 304
454
        collection.bind("reset", function() {
455
            ok(1, "WRONG: reset triggered on 304");
456
        });
457
        collection.bind("remove", function() {
458
            ok(1, "WRONG: remove triggered on 304");
459
        });
460
        collection.bind("change", function() {
461
            ok(1, "WRONG: remove triggered on 304");
462
        });
463
        collection.fetch({async:false, refresh:true});
464
        equal(collection.length, 1, "9 one model removed");
465
        collection.unbind();
466
        $.mockjaxClear();
467
    })
468

    
469

    
470
    module("network vm connections")
471
    test("network vm connections", function() {
472
        function _net(id, ip) {
473
            return {
474
                id: id,
475
                name: "net " + id,
476
                values:[{version:4, addr: ip}, {version:6, addr:ip}] 
477
            }
478
        }
479
        vms.add({id:1, name:"s1", linked_to_nets:[_net("p", "127")]});
480
        var vm = vms.at(0);
481
        
482
        nets.add({id:"p", nid:"p", name:"n1", linked_to:[1]});
483
        var n = nets.at(0);
484
    })
485

    
486
    module("images/flavors")
487
    test("Test DELETE state image retrieval", function() {
488
        snf.storage.images.reset();
489
        snf.storage.vms.reset();
490

    
491
        var images = snf.storage.images;
492
        var vms = snf.storage.vms;
493

    
494
        var img = images.add({name:"image 1", id:1}).last();
495
        var vm1 = vms.add({imageRef:1, name:"vm1"}).last();
496
        var vm2 = vms.add({imageRef:2, name:"vm2"}).last();
497
            
498
        vm1.get_image(function(i){
499
            equal(i, img);
500
        });
501
        
502
        // reset is not called on 304
503
        $.mockjax({
504
            url: '/api/v1.1/images/2',
505
            responseTime: 50,
506
            status: 200,
507
            responseText: {image:{name:"image 2", id:2}}
508
        }); 
509
        
510
        
511
        equal(images.length, 1, "1 image exists");
512
        vm2.get_image(function(i){
513
            equal(images.get(2).get("name"), "image 2", "image data parsed");
514
            equal(images.length, 2);
515
        });
516
    })
517

    
518
    test("Test DELETE state flavor retrieval", function() {
519
        snf.storage.flavors.reset();
520
        snf.storage.vms.reset();
521

    
522
        var flavors = snf.storage.flavors;
523
        var vms = snf.storage.vms;
524

    
525
        var flv = flavors.add({id:1, cpu:1, disk:1, ram:1024}).last();
526
        var vm1 = vms.add({flavorRef:1, name:"vm1"}).last();
527
        var vm2 = vms.add({flavorRef:2, name:"vm2"}).last();
528
            
529
        equal(flv, vm1.get_flavor());
530
        
531
        // reset is not called on 304
532
        $.mockjax({
533
            url: '/api/v1.1/flavors/2',
534
            responseTime: 50,
535
            status: 200,
536
            responseText: {flavor:{cpu:1, ram:2048, disk:100, id:2}}
537
        }); 
538
        
539
        
540
        equal(flavors.length, 1, "1 flavor exists");
541
        vm2.get_flavor();
542
        equal(flavors.get(2).get("ram"), 2048, "flavor data parsed");
543
        equal(flavors.length, 2);
544
    })
545

    
546
    test("actions list object", function(){
547
        var m = new models.Image();
548
        var l = new models.ParamsList(m, "actions");
549
        var count = 0;
550

    
551
        l.add("destroy");
552
        equal(l.has_action("destroy"), true);
553
        equal(l.contains("destroy"), true);
554

    
555
        l.add("destroy", 1, {});
556
        equal(l.has_action("destroy"), true);
557
        equal(l.contains("destroy", 1, {}), true);
558

    
559
        l.remove("destroy", 1, {});
560
        equal(l.contains("destroy", 1, {}), false);
561

    
562
        m.bind("change:actions", function() { count ++});
563
        l.add("destroy");
564
        
565
        equal(count, 0);
566
        l.add("destroy", 1, {});
567
        equal(count, 1);
568
    });
569

    
570
    module("update handlers")
571
    test("update handlers", function() {
572
        // this test is based on multiple timeouts
573
        // so the results might differ between different browsers
574
        // or browser load
575
        stop();
576

    
577
        var counter = 0;
578
        var cb = function() {
579
            counter++;
580
        }
581
        
582
        var opts = {
583
            callback:cb,
584
            interval: 10,
585
            fast: 5,
586
            increase: 5,
587
            max: 15,
588
            increase_after_calls: 3,
589
            initial_call: false
590
        }
591

    
592
        var h = new snf.api.updateHandler(opts);
593
        h.start();
594

    
595
        var add = $.browser.msie ? 8 : 0;
596

    
597
        window.setTimeout(function(){
598
            h.stop();
599
            start();
600
            // 4 calls, limit reached
601
            equal(counter, 4, "normal calls");
602
            equal(h.interval, opts.max, "limit reached");
603

    
604
            stop();
605
            h.start(false);
606
            h.faster();
607
            window.setTimeout(function(){
608
                // 11 calls, limit reached
609
                start();
610
                equal(counter, 11, "faster calls");
611
                equal(h.interval, opts.max, "limit reached");
612
                h.stop();
613
                stop();
614
                window.setTimeout(function(){
615
                    // no additional calls because we stopped it
616
                    start();
617
                    equal(counter, 11, "no additional calls")
618
                }, 50 + add)
619
            }, 50 + add)
620
        }, 43 + add)
621
    })
622
})