rpcProvider.ts 7.34 KB
/// <reference path="../typings/index.d.ts"/>

import * as assert from 'assert';

import RpcProvider from '../src/RpcProvider';

suite('RPC provider', function() {

    let local: RpcProvider,
        remote: RpcProvider,
        transferLocalToRemote: Array<any>,
        transferRemoteToLocal: Array<any>,
        errorLocal: Error,
        errorRemote: Error;
    
    setup(function() {
        local = new RpcProvider(
            (message, transfer) => (transferLocalToRemote = transfer, remote.dispatch(message)),
            50
        );
        
        local.error.addHandler(err => errorLocal = err);

        remote = new RpcProvider(
            (message, transfer) => (transferRemoteToLocal = transfer, local.dispatch(message)),
            50
        );

        remote.error.addHandler(err => errorRemote = err);

        transferLocalToRemote = transferRemoteToLocal = undefined;
        errorRemote = errorLocal = undefined
    });

    suite('signals', function() {

        test('Signals are propagated', function() {
            let x = -1;

            remote.registerSignalHandler('action', (value: number) => x = value);

            local.signal('action', 5);

            assert(!errorLocal);
            assert(!errorRemote);
            assert.strictEqual(x, 5);
        });

        test('Unregistered signals raise an error', function() {
            local.signal('action', 10);

            assert(errorLocal);
            assert(errorRemote);
        });

        test('Multiple signals do not interfere', function() {
            let x = -1, y = -1;

            remote.registerSignalHandler('setx', (value: number) => x = value);
            remote.registerSignalHandler('sety', (value: number) => y = value);

            local.signal('setx', 5);
            local.signal('sety', 6);

            assert(!errorLocal);
            assert(!errorRemote);
            assert.strictEqual(x, 5);
            assert.strictEqual(y, 6);
        });

        test('Multiple handlers can be bound to one signal', function() {
            let x = -1;

            remote.registerSignalHandler('action', (value: number) => x = value);

            local.signal('action', 1);
            local.signal('action', 2);

            assert(!errorLocal);
            assert(!errorRemote);
            assert.strictEqual(x, 2);
        });

        test('Handlers can be deregistered', function() {
            let x = -1;

            const handler = (value: number) => x = value;

            remote.registerSignalHandler('action', handler);
            remote.deregisterSignalHandler('action', handler);

            local.signal('action', 5);

            assert(!errorLocal);
            assert(!errorRemote);
            assert.strictEqual(x, -1);
        });

        test('Transfer is honored', function() {
            let x = -1;
            const transfer = [1, 2, 3];

            remote.registerSignalHandler('action', (value: number) => x = value);

            local.signal('action', 2, transfer);

            assert(!errorLocal);
            assert(!errorRemote);
            assert.strictEqual(x, 2);
            assert.strictEqual(transferLocalToRemote, transfer);
            assert(!transferRemoteToLocal);
        });

    });

    suite('RPC', function() {

        test('RPC handlers can return values', function() {
            remote.registerRpcHandler('action', () => 10);

            return local
                .rpc('action')
                .then(result => (
                    assert.strictEqual(result, 10),
                    assert(!errorLocal),
                    assert(!errorRemote)
                ));
        });

        test('RPC handlers can return promises', function() {
            remote.registerRpcHandler('action', () => new Promise(r => setTimeout(() => r(10), 15)));

            return local
                .rpc('action')
                .then(result => (
                    assert.strictEqual(result, 10),
                    assert(!errorLocal),
                    assert(!errorRemote)
                ));
        })

        test('Promise rejection is transferred', function() {
            remote.registerRpcHandler('action', () => new Promise((resolve, reject) => setTimeout(() => reject(10), 15)));

            return local
                .rpc('action')
                .then(
                    () => Promise.reject('should have been rejected'),
                    result => (
                        assert.strictEqual(result, 10),
                        assert(!errorLocal),
                        assert(!errorRemote)
                    )
                );
        });

        test('Invalid RPC calls are rejected', function() {
            return local
                .rpc('action')
                .then(
                    () => Promise.reject('should have been rejected'),
                    () => undefined
                );
        });

        test('Invalid RPC calls throw on both ends', function() {
            return local
                .rpc('action')
                .then(
                    () => Promise.reject('should have been rejected'),
                    () => undefined
                )
                .then(() => (
                    assert(errorLocal),
                    assert(errorRemote)
                ));
        });

        test('RPC calls time out', function() {
            remote.registerRpcHandler('action', () => new Promise(r => setTimeout(() => r(10), 100)));

            return local
                .rpc('action')
                .then(
                    () => Promise.reject('should have been rejected'),
                    () => (assert(errorLocal), new Promise(r => setTimeout(r, 100)))
                )
                .then(() => assert(errorRemote));
        });

        test('Multiple RPC handlers do not interfere', function() {
            remote.registerRpcHandler('a1', (value: number) => new Promise(r => setTimeout(() => r(value), 30)));
            remote.registerRpcHandler('a2', (value: number) => 2 * value);

            return Promise
                .all([
                    local.rpc('a1', 10),
                    local.rpc('a2', 20)
                ])
                .then(([r1, r2]) => (
                    assert.strictEqual(r1, 10),
                    assert.strictEqual(r2, 40),
                    assert(!errorLocal),
                    assert(!errorRemote)
                ));
        });

        test('RPC handler can be deregistered', function() {
            const handler = () => 10;

            remote.registerRpcHandler('action', handler);
            remote.deregisterRpcHandler('action', handler);

            return local
                .rpc('action')
                .then(
                    () => Promise.reject('should have been rejected'),
                    () => (
                        assert(errorLocal),
                        assert(errorRemote)
                    )
                );
        });

        test('Transfer is honored', function() {
            const transfer = [1, 2, 3];

            remote.registerRpcHandler('action', () => 10);

            return local
                .rpc('action', undefined, transfer)
                .then(x => (
                    assert.strictEqual(transferLocalToRemote, transfer),
                    assert.strictEqual(x, 10),
                    assert(!errorLocal),
                    assert(!errorRemote)
                ));
        });

    });

});