GUI -- Cleaned up websocket callback wrapping and beefed up unit tests.

Change-Id: I3457e7d8009d0c7ebb900d06ae01c59cf7370cba
diff --git a/web/gui/src/main/webapp/app/fw/remote/websocket.js b/web/gui/src/main/webapp/app/fw/remote/websocket.js
index 15e992d..abe6025 100644
--- a/web/gui/src/main/webapp/app/fw/remote/websocket.js
+++ b/web/gui/src/main/webapp/app/fw/remote/websocket.js
@@ -23,14 +23,21 @@
     var fs;
 
     function fnOpen(f) {
-        return fs.isF(f);
+        // wrap the onOpen function; we will handle any housekeeping here...
+        if (!fs.isF(f)) {
+            return null;
+        }
+
+        return function (openEvent) {
+            // NOTE: nothing worth passing to the caller?
+            f();
+        };
     }
 
     function fnMessage(f) {
         // wrap the onMessage function; we will attempt to decode the
         // message event payload as JSON and pass that in...
-        var fn = fs.isF(f);
-        if (!fn) {
+        if (!fs.isF(f)) {
             return null;
         }
 
@@ -44,12 +51,21 @@
                     e: e
                 };
             }
-            fn(ev);
-        }
+            f(ev);
+        };
     }
 
     function fnClose(f) {
-        return fs.isF(f);
+        // wrap the onClose function; we will handle any parameters to the
+        // close event here...
+        if (!fs.isF(f)) {
+            return null;
+        }
+
+        return function (closeEvent) {
+            // NOTE: only seen {reason == ""} so far, nevertheless...
+            f(closeEvent.reason);
+        };
     }
 
     angular.module('onosRemote')
@@ -60,7 +76,7 @@
             fs = _fs_;
 
             // creates a web socket for the given path, returning a "handle".
-            // opts contains the event handler callbacks.
+            // opts contains the event handler callbacks, etc.
             function createWebSocket(path, opts) {
                 var o = opts || {},
                     wsport = opts && opts.wsport,
@@ -86,13 +102,14 @@
                     ws.onclose = fnClose(o.onClose);
                 }
 
-                function send(msg) {
-                    if (msg) {
+                // messages are expected to be event objects..
+                function send(ev) {
+                    if (ev) {
                         if (ws) {
-                            ws.send(msg);
+                            ws.send(JSON.stringify(ev));
                         } else {
                             $log.warn('ws.send() no web socket open!',
-                                fullUrl, msg);
+                                fullUrl, ev);
                         }
                     }
                 }
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index 696677c..729f006 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -141,8 +141,8 @@
 
     }
 
-    function onWsClose(closeEvent) {
-        $log.log('web socket closed...', closeEvent);
+    function onWsClose(reason) {
+        $log.log('web socket closed; reason=', reason);
 
     }
 
diff --git a/web/gui/src/main/webapp/tests/app/fw/remote/websocket-spec.js b/web/gui/src/main/webapp/tests/app/fw/remote/websocket-spec.js
index 0d066c7..a823b72 100644
--- a/web/gui/src/main/webapp/tests/app/fw/remote/websocket-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/remote/websocket-spec.js
@@ -50,15 +50,58 @@
     });
 
     it('should use the appropriate URL', function () {
-        debugger;
         var ws = wss.createWebSocket('foo/path');
         expect(ws.meta.path).toEqual('ws://foo:80/onos/ui/ws/foo/path');
     });
 
+    it('should use the appropriate URL with modified port', function () {
+        var ws = wss.createWebSocket('foo/path', { wsport: 1243 });
+        expect(ws.meta.path).toEqual('ws://foo:1243/onos/ui/ws/foo/path');
+    });
+
+    var oCalled, mCalled, cCalled, json, reason;
+
+    function oo() {
+        oCalled++;
+    }
+    function om(j) {
+        mCalled++;
+        json = j;
+    }
+    function oc(r) {
+        cCalled++;
+        reason = r;
+    }
+
+    function resetCounters() {
+        oCalled = mCalled = cCalled = 0;
+        json = reason = null;
+    }
+
+    function validateCallbacks(ws, op, msg, cl) {
+        // we have to cheat a little, by digging into the websocket structure
+        var onO = fs.isF(ws.meta.ws.onopen),
+            onM = fs.isF(ws.meta.ws.onmessage),
+            onC = fs.isF(ws.meta.ws.onclose);
+
+        expect(!!onO).toEqual(op);
+        expect(!!onM).toEqual(msg);
+        expect(!!onC).toEqual(cl);
+
+        onO && onO({});
+        onM && onM({ data: '{ "item": "ivalue" }'});
+        onC && onC({ reason: 'rvalue' });
+
+        expect(oCalled).toEqual(op ? 1 : 0);
+        expect(mCalled).toEqual(msg ? 1 : 0);
+        expect(cCalled).toEqual(cl ? 1 : 0);
+
+        expect(json).toEqual(msg ? { item: 'ivalue' } : null);
+        expect(reason).toEqual(cl ? 'rvalue' : null);
+    }
+
     it('should install the appropriate callbacks', function () {
-        function oo() {}
-        function om() {}
-        function oc() {}
+        resetCounters();
 
         var ws = wss.createWebSocket('foo', {
             onOpen: oo,
@@ -66,27 +109,43 @@
             onClose: oc
         });
 
-        expect(ws.meta.ws.onopen).toBe(oo);
-        // TODO: om is wrapped - we can't test by reference
-        //expect(ws.meta.ws.onmessage).toBe(om);
-        expect(ws.meta.ws.onclose).toBe(oc);
+        validateCallbacks(ws, true, true, true);
     });
 
     it('should install partial callbacks', function () {
-        function oo() {}
-        function om() {}
+        resetCounters();
 
         var ws = wss.createWebSocket('foo', {
             onOpen: oo,
             onMessage: om
         });
 
-        expect(ws.meta.ws.onopen).toBe(oo);
-        // TODO: om is wrapped - we can't test by reference
-        //expect(ws.meta.ws.onmessage).toBe(om);
-        expect(ws.meta.ws.onclose).toBeNull();
+        validateCallbacks(ws, true, true, false);
     });
 
-    // TODO: more testing to be done.
+    it('should install no callbacks', function () {
+        resetCounters();
 
+        var ws = wss.createWebSocket('foo');
+
+        validateCallbacks(ws, false, false, false);
+    });
+
+    // can't really test send without faking out the WebSocket.
+/*
+    it('should stringify objects for sending', function () {
+        var ws = wss.createWebSocket('foo');
+        ws.send({ item: 'itemVal' });
+
+        // what to assert?
+    });
+*/
+
+    it('should remove websocket reference on close', function () {
+        var ws = wss.createWebSocket('foo');
+        expect(ws.meta.ws instanceof WebSocket).toBeTruthy();
+
+        ws.close();
+        expect(ws.meta.ws).toBeNull();
+    });
 });