<commit_before>module('data-remote', {

	teardown: function() {
		$('a').remove();
		$('input').remove();
		$('form').remove();
	},

	setup: function() {

		$(document.body).append($('<a />', {
			href: 'http://example.com/address',
			'data-remote': 'true',
			text: 'my address'
		}));

		$(document.body).append($('<input />', {
			href: 'http://example.com/address',
			'data-remote': 'true',
			name: 'submit',
			type: 'submit',
			value: 'Click me'
		}));

		$(document.body).append($('<form />', {
			action: 'http://example.com/address',
			'data-remote': 'true',
			method: 'post'
		}));

		$('form').append($('<input />', {
			id: 'user_name',
			type: 'text',
			size: '30',
			'name': 'user_name',
			'value': 'john'
		}));

	}
});

test('clicking on a link with data-remote attribute', function() {
	expect(3);

	var ajaxArgs;

	jack(function() {
		jack.expect('$.ajax').once().mock(function(args) {
			ajaxArgs = args;
		});
		$('a[data-remote]').trigger('click');
	});

	equals(ajaxArgs.url, 'http://example.com/address', 'ajax arguments should have passed url');
	equals(ajaxArgs.dataType, 'script', 'ajax arguments should have script as the data type');
	equals(ajaxArgs.type, 'GET', 'ajax arguments should have GET as request type');
});

test('clicking on Submit input tag with data-remote attribute', function() {
	expect(3);

	var ajaxArgs;

	jack(function() {
		jack.expect('$.ajax').once().mock(function(args) {
			ajaxArgs = args;
		});
		$('input[data-remote]').trigger('click');
	});

	equals(ajaxArgs.url, 'http://example.com/address', 'ajax arguments should have passed url');
	equals(ajaxArgs.dataType, 'script', 'ajax arguments should have script as the data type');
	equals(ajaxArgs.type, 'GET', 'ajax arguments should have GET as request type');
});

test('Sbumitting form with data-remote attribute', function() {
	expect(4);

	var ajaxArgs;

	jack(function() {
		jack.expect('$.ajax').once().mock(function(args) {
			ajaxArgs = args;
		});
		$('form[data-remote]').trigger('submit');
	});

	equals(ajaxArgs.url, 'http://example.com/address', 'ajax arguments should have passed url');
	equals(ajaxArgs.dataType, 'script', 'ajax arguments should have script as the data type');
	equals(ajaxArgs.type, 'POST', 'ajax arguments should have GET as request type');
	console.log(ajaxArgs.data);
	//FIXME
	//equals(ajaxArgs.data, [ { "name": "user_name", "value": "john" } ], 'ajax arguments should have data that was submitted');
});

<commit_msg>fixing the number of assertions<commit_after>module('data-remote', {

	teardown: function() {
		$('a').remove();
		$('input').remove();
		$('form').remove();
	},

	setup: function() {

		$(document.body).append($('<a />', {
			href: 'http://example.com/address',
			'data-remote': 'true',
			text: 'my address'
		}));

		$(document.body).append($('<input />', {
			href: 'http://example.com/address',
			'data-remote': 'true',
			name: 'submit',
			type: 'submit',
			value: 'Click me'
		}));

		$(document.body).append($('<form />', {
			action: 'http://example.com/address',
			'data-remote': 'true',
			method: 'post'
		}));

		$('form').append($('<input />', {
			id: 'user_name',
			type: 'text',
			size: '30',
			'name': 'user_name',
			'value': 'john'
		}));

	}
});

test('clicking on a link with data-remote attribute', function() {
	expect(3);

	var ajaxArgs;

	jack(function() {
		jack.expect('$.ajax').once().mock(function(args) {
			ajaxArgs = args;
		});
		$('a[data-remote]').trigger('click');
	});

	equals(ajaxArgs.url, 'http://example.com/address', 'ajax arguments should have passed url');
	equals(ajaxArgs.dataType, 'script', 'ajax arguments should have script as the data type');
	equals(ajaxArgs.type, 'GET', 'ajax arguments should have GET as request type');
});

test('clicking on Submit input tag with data-remote attribute', function() {
	expect(3);

	var ajaxArgs;

	jack(function() {
		jack.expect('$.ajax').once().mock(function(args) {
			ajaxArgs = args;
		});
		$('input[data-remote]').trigger('click');
	});

	equals(ajaxArgs.url, 'http://example.com/address', 'ajax arguments should have passed url');
	equals(ajaxArgs.dataType, 'script', 'ajax arguments should have script as the data type');
	equals(ajaxArgs.type, 'GET', 'ajax arguments should have GET as request type');
});

test('Sbumitting form with data-remote attribute', function() {
	expect(3);

	var ajaxArgs;

	jack(function() {
		jack.expect('$.ajax').once().mock(function(args) {
			ajaxArgs = args;
		});
		$('form[data-remote]').trigger('submit');
	});

	equals(ajaxArgs.url, 'http://example.com/address', 'ajax arguments should have passed url');
	equals(ajaxArgs.dataType, 'script', 'ajax arguments should have script as the data type');
	equals(ajaxArgs.type, 'POST', 'ajax arguments should have GET as request type');
	console.log(ajaxArgs.data);
	//FIXME
	//equals(ajaxArgs.data, [ { "name": "user_name", "value": "john" } ], 'ajax arguments should have data that was submitted');
});

<|endoftext|>


<commit_before>/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
var expect = require('chai').expect;
var fs        = require('fs');

describe('Test package.json and io-package.json', function() {
    it('Test package files', function (done) {
        console.log();

        var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
        var ioPackage = JSON.parse(fileContentIOPackage);

        var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
        var npmPackage = JSON.parse(fileContentNPMPackage);

        expect(ioPackage).to.be.an('object');
        expect(npmPackage).to.be.an('object');

        expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
        expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;

        expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);

        if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
            console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
            console.log();
        }

        expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
        expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;

        if (ioPackage.common.name.indexOf('template') !== 0) {
            if (Array.isArray(ioPackage.common.authors)) {
                expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
                if (ioPackage.common.authors.length === 1) {
                    expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
                }
            }
            else {
                expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
            }
        }
        else {
            console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
            console.log();
        }
        expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
        if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
            console.log('WARNING: titleLang is not existing in io-package.json. Please add');
            console.log();
        }
        if (
            ioPackage.common.title.indexOf('iobroker') !== -1 ||
            ioPackage.common.title.indexOf('ioBroker') !== -1 ||
            ioPackage.common.title.indexOf('adapter') !== -1 ||
            ioPackage.common.title.indexOf('Adapter') !== -1
        ) {
            console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.');
            console.log();
        }

        if (ioPackage.common.name.indexOf('vis-') !== 0) {
            if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
                console.log('WARNING: Admin3 support is missing! Please add it');
                console.log();
            }
            if (ioPackage.common.materialize) {
                expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
            }
        }

        var licenseFileExists = fs.existsSync(__dirname + '/../LICENSE');
        var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
        if (fileContentReadme.indexOf('## Changelog') === -1) {
            console.log('Warning: The README.md should have a section ## Changelog');
            console.log();
        }
        expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true;
        if (!licenseFileExists) {
            console.log('Warning: The License should also exist as LICENSE file');
            console.log();
        }
        if (fileContentReadme.indexOf('## License') === -1) {
            console.log('Warning: The README.md should also have a section ## License to be shown in Admin3');
            console.log();
        }
        done();
    });
});
<commit_msg>update basic package-file testing<commit_after>/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
'use strict';

const expect = require('chai').expect;
const fs     = require('fs');

describe('Test package.json and io-package.json', () => {
    it('Test package files', done => {
        console.log();

        const fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
        const ioPackage = JSON.parse(fileContentIOPackage);

        const fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
        const npmPackage = JSON.parse(fileContentNPMPackage);

        expect(ioPackage).to.be.an('object');
        expect(npmPackage).to.be.an('object');

        expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
        expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;

        expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);

        if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
            console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
            console.log();
        }

        expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
        expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;

        expect(ioPackage.common.license, 'ERROR: License missing in io-package in common.license').to.exist;

        if (ioPackage.common.name.indexOf('template') !== 0) {
            if (Array.isArray(ioPackage.common.authors)) {
                expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
                if (ioPackage.common.authors.length === 1) {
                    expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
                }
            }
            else {
                expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
            }
        }
        else {
            console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
            console.log();
        }
        expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
        if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
            console.log('WARNING: titleLang is not existing in io-package.json. Please add');
            console.log();
        }
        if (
            ioPackage.common.title.indexOf('iobroker') !== -1 ||
            ioPackage.common.title.indexOf('ioBroker') !== -1 ||
            ioPackage.common.title.indexOf('adapter') !== -1 ||
            ioPackage.common.title.indexOf('Adapter') !== -1
        ) {
            console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.');
            console.log();
        }

        if (!ioPackage.common.controller && !ioPackage.common.onlyWWW && !ioPackage.common.noConfig) {
            if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
                console.log('WARNING: Admin3 support is missing! Please add it');
                console.log();
            }
            if (ioPackage.common.materialize) {
                expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
            }
        }

        const licenseFileExists = fs.existsSync(__dirname + '/../LICENSE');
        const fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
        if (fileContentReadme.indexOf('## Changelog') === -1) {
            console.log('Warning: The README.md should have a section ## Changelog');
            console.log();
        }
        expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true;
        if (!licenseFileExists) {
            console.log('Warning: The License should also exist as LICENSE file');
            console.log();
        }
        if (fileContentReadme.indexOf('## License') === -1) {
            console.log('Warning: The README.md should also have a section ## License to be shown in Admin3');
            console.log();
        }
        done();
    });
});
<|endoftext|>


<commit_before>'use strict';

const assert = require('assert');
const del = require('del');
const fs = require('fs');
const Promise = require('bluebird');

const prepareContext = require('../lib/prepareContext');
const renderHerman = require('../lib/renderHerman');

const rmdir = Promise.promisify(fs.rmdir);
const mkdir = Promise.promisify(fs.mkdir);
const access = Promise.promisify(fs.access);

describe('renderHerman', function() {
  beforeEach(function(done) {
    this.dest = `${__dirname}/dest`;
    rmdir(this.dest)
      .then(
        () => {
          return mkdir(this.dest);
        },
        error => {}
      )
      .then(done, done);
  });

  it('does the herman things', function(done) {
    prepareContext({
      data: [],
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        fs.access(`${this.dest}/index.html`, err => {
          assert.equal(err, undefined);

          del(`${this.dest}/*`).then(() => {
            done();
          });
        });
      });
  });

  it('copies an internal shortcutIcon', function(done) {
    const shortcutIcon = `${__dirname}/assets/img/favicon.ico`;
    const expectedShortcutIcon = `${__dirname}/dest/assets/img/favicon.ico`;
    prepareContext({
      data: [],
      shortcutIcon,
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        return access(shortcutIcon);
      })
      .then(() => {
        // We only get here if the file is accessible.
        assert.ok(true);
        done();
      })
      .catch(done);
  });

  it('ignores an external shortcutIcon', function(done) {
    prepareContext({
      data: [],
      shortcutIcon: 'http://example.com/img/favicon.ico',
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        const shortcutIcon = `${__dirname}/assets/img/favicon.ico`;
        // Is this the right thing to test access? I suspect not.
        return access(shortcutIcon);
      })
      .then(() => {
        // We only get here if the file is accessible.
        assert.ok(true);
        done();
      })
      .catch(done);
  });

  it('handles customCSS', function(done) {
    prepareContext({
      data: [],
      customCSS: {
        path: `${__dirname}/dest/assets/css/main.css`,
      },
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });

  it('handles customCSS and customCSSFiles', function(done) {
    prepareContext({
      data: [],
      customCSS: {
        path: `${__dirname}/dest/assets/css/main.css`,
      },
      customCSSFiles: [`${__dirname}/dest/assets/css/main.css`],
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });

  it('resolves local fonts', function(done) {
    prepareContext({
      herman: {
        fontpath: '/path',
      },
      dir: '.',
      data: [],
      localFonts: [`${__dirname}/dest/assets/fonts/sample.ttf`],
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });

  it('renders extraDocs', function(done) {
    prepareContext({
      data: [],
      extraDocs: [
        {
          filename: '',
          name: '',
          text: '',
        },
      ],
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });

  it('renders a page for each group', function(done) {
    prepareContext({
      data: [
        {
          description: 'This is some text',
          context: {
            type: 'variable',
          },
          group: ['group1'],
          access: 'public',
        },
      ],
      groups: {
        group1: 'Group 1',
      },
      display: {
        access: ['private', 'public'],
      },
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });

  it('renders subprojects', function(done) {
    const item = {
      description: 'This is some text',
      context: {
        type: 'variable',
      },
      group: ['group1'],
      access: 'public',
    };
    prepareContext({
      data: [],
      subprojects: {
        subproj: {
          data: [item],
          groups: {
            group1: 'Group 1',
          },
          display: {
            access: ['private', 'public'],
          },
          // @@@ I think this denotes a bug; for top-level projects, we don't
          // have to set byGroup explicitly, but on this nested project, we
          // must:
          byGroup: {
            group1: item,
          },
        },
      },
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });
});
<commit_msg>Fix some simple linting issues<commit_after>'use strict';

const assert = require('assert');
const del = require('del');
const fs = require('fs');
const Promise = require('bluebird');

const prepareContext = require('../lib/prepareContext');
const renderHerman = require('../lib/renderHerman');

const rmdir = Promise.promisify(fs.rmdir);
const mkdir = Promise.promisify(fs.mkdir);
const access = Promise.promisify(fs.access);

describe('renderHerman', function() {
  beforeEach(function(done) {
    this.dest = `${__dirname}/dest`;
    rmdir(this.dest)
      .then(
        () => mkdir(this.dest),
        // Skip in case of error
        () => {}
      )
      .then(done, done);
  });

  it('does the herman things', function(done) {
    prepareContext({
      data: [],
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        fs.access(`${this.dest}/index.html`, err => {
          assert.equal(err, undefined);

          del(`${this.dest}/*`).then(() => {
            done();
          });
        });
      });
  });

  it('copies an internal shortcutIcon', function(done) {
    const shortcutIcon = `${__dirname}/assets/img/favicon.ico`;
    const expectedShortcutIcon = `${__dirname}/dest/assets/img/favicon.ico`;
    prepareContext({
      data: [],
      shortcutIcon,
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => access(expectedShortcutIcon))
      .then(() => {
        // We only get here if the file is accessible.
        assert.ok(true);
        done();
      })
      .catch(done);
  });

  it('ignores an external shortcutIcon', function(done) {
    prepareContext({
      data: [],
      shortcutIcon: 'http://example.com/img/favicon.ico',
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        const shortcutIcon = `${__dirname}/assets/img/favicon.ico`;
        // Is this the right thing to test access? I suspect not.
        return access(shortcutIcon);
      })
      .then(() => {
        // We only get here if the file is accessible.
        assert.ok(true);
        done();
      })
      .catch(done);
  });

  it('handles customCSS', function(done) {
    prepareContext({
      data: [],
      customCSS: {
        path: `${__dirname}/dest/assets/css/main.css`,
      },
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });

  it('handles customCSS and customCSSFiles', function(done) {
    prepareContext({
      data: [],
      customCSS: {
        path: `${__dirname}/dest/assets/css/main.css`,
      },
      customCSSFiles: [`${__dirname}/dest/assets/css/main.css`],
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });

  it('resolves local fonts', function(done) {
    prepareContext({
      herman: {
        fontpath: '/path',
      },
      dir: '.',
      data: [],
      localFonts: [`${__dirname}/dest/assets/fonts/sample.ttf`],
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });

  it('renders extraDocs', function(done) {
    prepareContext({
      data: [],
      extraDocs: [
        {
          filename: '',
          name: '',
          text: '',
        },
      ],
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });

  it('renders a page for each group', function(done) {
    prepareContext({
      data: [
        {
          description: 'This is some text',
          context: {
            type: 'variable',
          },
          group: ['group1'],
          access: 'public',
        },
      ],
      groups: {
        group1: 'Group 1',
      },
      display: {
        access: ['private', 'public'],
      },
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });

  it('renders subprojects', function(done) {
    const item = {
      description: 'This is some text',
      context: {
        type: 'variable',
      },
      group: ['group1'],
      access: 'public',
    };
    prepareContext({
      data: [],
      subprojects: {
        subproj: {
          data: [item],
          groups: {
            group1: 'Group 1',
          },
          display: {
            access: ['private', 'public'],
          },
          // @@@ I think this denotes a bug; for top-level projects, we don't
          // have to set byGroup explicitly, but on this nested project, we
          // must:
          byGroup: {
            group1: item,
          },
        },
      },
    })
      .then(ctx => renderHerman(this.dest, ctx))
      .then(() => {
        // TODO
        // assert something?
        done();
      });
  });
});
<|endoftext|>


<commit_before>// test article api:

var
    _ = require('lodash'),
    async=require('async'),
    should = require('should');

var remote = require('./_test');

var log = console.log;

describe('#articles', function() {

    var category = null;

    before(remote.setup);

    before(function(done) {
        remote.post(remote.admin, '/api/categories', {
            name: 'Article Category'
        }, function(r) {
            r.name.should.equal('Article Category');
            r.id.should.be.ok.and.have.lengthOf(50);
            category = r;
            done();
        });
    });

    describe('#api', function() {

        it('should get empty articles', function(done) {
            remote.get(remote.guest, '/api/articles', null, function(r) {
                r.articles.length.should.equal(0);
                done();
            });
        });

        it('create and update article by editor', function(done) {
            // create article:
            remote.post(remote.editor, '/api/articles', {
                category_id: category.id,
                name: 'Test Article   ',
                description: '   blablabla\nhaha...  \n   ',
                tags: ' aaa,\n BBB,  \t ccc,CcC',
                content: '  Long content'
            }, function(r2) {
                r2.category_id.should.equal(category.id);
                r2.name.should.equal('Test Article');
                r2.description.should.equal('blablabla\nhaha...');
                r2.tags.should.equal('aaa,BBB,ccc');

                    // update article:
                    //remote.post(remote.editor, '/api/articles/' + r2.id, {
                    //    name: 'Name Changed'
                    //}, function(r3) {
                    //    assert.equal(r3.display_order, 1, 'display order should be 1 for second category.');
                    done();
                    //});
            });
        });

        it('create article with wrong parameter by editor', function(done) {
            var create_missing_params = function(pname) {
                var r = {
                    name: 'Test',
                    description: 'blablabla...',
                    category_id: category.id,
                    tags: 'tag1,tag2,tag3',
                    content: 'a long content...'
                };
                delete r[pname];
                return r;
            };
            var tests = _.map(['name', 'category_id', 'content'], function(param) {
                return function(callback) {
                    remote.post(remote.editor, '/api/articles', create_missing_params(param), function(r) {
                        r.error.should.equal('parameter:invalid');
                        r.data.should.equal(param);
                        r.message.should.be.ok;
                        callback(null);
                    });
                };
            });
            async.series(tests, function(err, results) {
                done();
            });
        });
    });
});
<commit_msg>add tests for article api<commit_after>// test article api:

var fs = require('fs');

var
    _ = require('lodash'),
    async=require('async'),
    should = require('should');

var remote = require('./_test');

var log = console.log;

describe('#articles', function() {

    var category = null;

    before(remote.setup);

    before(function(done) {
        remote.post(remote.admin, '/api/categories', {
            name: 'Article Category'
        }, function(r) {
            r.name.should.equal('Article Category');
            r.id.should.be.ok.and.have.lengthOf(50);
            category = r;
            done();
        });
    });

    describe('#api', function() {

        it('should get empty articles', function(done) {
            remote.get(remote.guest, '/api/articles', null, function(r) {
                r.articles.length.should.equal(0);
                done();
            });
        });

        it('create and update article by editor', function(done) {
            // create article:
            remote.post(remote.editor, '/api/articles', {
                category_id: category.id,
                name: 'Test Article   ',
                description: '   blablabla\nhaha...  \n   ',
                tags: ' aaa,\n BBB,  \t ccc,CcC',
                content: '  Long content... '
            }, function(r2) {
                r2.category_id.should.equal(category.id);
                r2.name.should.equal('Test Article');
                r2.description.should.equal('blablabla\nhaha...');
                r2.tags.should.equal('aaa,BBB,ccc');
                r2.content.should.equal('Long content...');
                r2.cover_id.should.equal('');

                    // update article:
                    //remote.post(remote.editor, '/api/articles/' + r2.id, {
                    //    name: 'Name Changed'
                    //}, function(r3) {
                    //    assert.equal(r3.display_order, 1, 'display order should be 1 for second category.');
                    done();
                    //});
            });
        });

        it('create article with cover by editor', function(done) {
            // create article:
            remote.post(remote.editor, '/api/articles', {
                category_id: category.id,
                name: ' Test Article With Cover  ',
                description: '   blablabla\nhaha...  \n   ',
                tags: ' cover,\n CoveR',
                content: '  Article comes with cover...   ',
                file: remote.createReadStream('./test/res-image.jpg')
            }, function(r) {
                r.category_id.should.equal(category.id);
                r.name.should.equal('Test Article With Cover');
                r.description.should.equal('blablabla\nhaha...');
                r.tags.should.equal('cover');
                r.content.should.equal('Article comes with cover...');
                r.cover_id.should.be.ok;
                // check cover:
                remote.get(remote.guest, '/api/attachments/' + r.cover_id, null, function(r2) {
                    r2.id.should.equal(r.cover_id);
                    r2.name.should.equal(r.name);
                    r2.size.should.equal(346158);
                    // download image:
                    remote.download('/files/attachments/' + r2.id, function(content_type, content_length, content) {
                        content_type.should.equal('image/jpeg');
                        content_length.should.equal(346158);
                        done();
                    });
                });
            });
        });

        it('create article with wrong parameter by editor', function(done) {
            var create_missing_params = function(pname) {
                var r = {
                    name: 'Test',
                    description: 'blablabla...',
                    category_id: category.id,
                    tags: 'tag1,tag2,tag3',
                    content: 'a long content...'
                };
                delete r[pname];
                return r;
            };
            var tests = _.map(['name', 'category_id', 'content'], function(param) {
                return function(callback) {
                    remote.post(remote.editor, '/api/articles', create_missing_params(param), function(r) {
                        r.error.should.equal('parameter:invalid');
                        r.data.should.equal(param);
                        r.message.should.be.ok;
                        callback(null);
                    });
                };
            });
            async.series(tests, function(err, results) {
                done();
            });
        });
    });
});
<|endoftext|>


<commit_before>'use strict';

describe('charm panel', function() {
  var Y, models, views, juju, ENTER,
      searchResult = '{"results": [{"data_url": "this is my URL", ' +
      '"name": "membase", "series": "precise", "summary": ' +
      '"Membase Server", "relevance": 8.728194117350437, ' +
      '"owner": "charmers", "store_url": "cs:precise/membase-6"}]}';

  before(function(done) {
    Y = YUI(GlobalConfig).use(
        'juju-models',
        'juju-views',
        'juju-gui',
        'juju-env',
        'juju-tests-utils',
        'node-event-simulate',
        'node',
        'event-key',
        'juju-charm-store',

        function(Y) {
          models = Y.namespace('juju.models');
          views = Y.namespace('juju.views');
          juju = Y.namespace('juju');
          ENTER = Y.Node.DOM_EVENTS.key.eventDef.KEY_MAP.enter;
          done();
        });

  });

  beforeEach(function() {
    // The charms panel needs these elements
    var docBody = Y.one(document.body);
    Y.Node.create('
' +
        '

' +
        '

' +
        '<input type="text" id="charm-search-field" />' +
        '
').appendTo(docBody);
  });

  afterEach(function() {
    Y.namespace('juju.views').CharmPanel.killInstance();
    Y.one('#charm-search-test').remove(true);
  });

  it('must be able to show and hide the panel', function() {
    var panel = Y.namespace('juju.views').CharmPanel
          .getInstance({testing: true, app: {}}),
        container = panel.node;
    container.getStyle('display').should.equal('none');
    panel.show();
    container.getStyle('display').should.equal('block');
    panel.hide();
    container.getStyle('display').should.equal('none');
    panel.toggle();
    container.getStyle('display').should.equal('block');
    panel.toggle();
    container.getStyle('display').should.equal('none');
  });

  it('must be able to search', function() {
    var searchTriggered = false,
        panel = Y.namespace('juju.views').CharmPanel.getInstance({
          charm_store: new juju.CharmStore({datasource: {
            sendRequest: function(params) {
              searchTriggered = true;
              // Stubbing the server callback value
              params.callback.success({
                response: {
                  results: [{
                    responseText: searchResult
                  }]
                }
              });
            }
          }}),
          testing: true,
          app: {}
        }),
        node = panel.node;
    panel.show(true);
    var field = Y.one('#charm-search-field');
    field.set('value', 'aaa');
    field.simulate('keydown', { keyCode: ENTER });

    searchTriggered.should.equal(true);
    node.one('.charm-entry .btn.deploy').getData('url').should.equal(
        'cs:precise/membase-6');
  });

  it('must be able to trigger charm details', function() {
    var db = new models.Database(),
        panel = Y.namespace('juju.views').CharmPanel.getInstance({
          charm_store: new juju.CharmStore({datasource: {
            sendRequest: function(params) {
              // Mocking the server callback value
              params.callback.success({
                response: {
                  results: [{
                    responseText: searchResult
                  }]
                }
              });
            }
          }}),
          app: {db: db},
          testing: true
        }),
        node = panel.node;
    db.charms.add({id: 'cs:precise/membase-6'});

    panel.show();
    var field = Y.one('#charm-search-field');
    field.set('value', 'aaa');
    field.simulate('keydown', { keyCode: ENTER });
    node.one('a.charm-detail').simulate('click');
    node.one('.charm-description h3').get('text').trim()
      .should.equal('membase');
  });

  it('must be able to deploy from the description panel by going to the ' +
     'configuration panel', function() {
        var db = new models.Database(),
            panel = Y.namespace('juju.views').CharmPanel.getInstance({
              charm_store: new juju.CharmStore({datasource: {
                sendRequest: function(params) {
                  // Mocking the server callback value
                  params.callback.success({
                    response: {
                      results: [{
                        responseText: searchResult
                      }]
                    }
                  });
                }
              }}),
              app: {db: db},
              testing: true
            }),
            node = panel.node,
            charm = db.charms.add({id: 'cs:precise/membase-6'});
        charm.loaded = true;
        panel.show();
        var field = Y.one('#charm-search-field');
        field.set('value', 'aaa');
        field.simulate('keydown', { keyCode: ENTER });
        node.one('a.charm-detail').simulate('click');
        node.one('.btn-primary').simulate('click');
        node.one('.control-label').get('text').trim()
         .should.equal('Service name');
      });
});

describe('charm description', function() {
  var Y, models, views, juju, conn, env, container, db, app, charm,
      charm_store_data, charm_store, charms;

  before(function(done) {
    Y = YUI(GlobalConfig).use(
        'juju-models',
        'juju-views',
        'juju-gui',
        'juju-env',
        'juju-tests-utils',
        'node-event-simulate',
        'node',
        'datasource-local',
        'json-stringify',
        'juju-charm-store',

        function(Y) {
          models = Y.namespace('juju.models');
          views = Y.namespace('juju.views');
          juju = Y.namespace('juju');
          done();
        });

  });

  beforeEach(function() {
    conn = new (Y.namespace('juju-tests.utils')).SocketStub(),
    env = new (Y.namespace('juju')).Environment({conn: conn});
    env.connect();
    conn.open();
    container = Y.Node.create('
');
    Y.one('#main').append(container);
    db = new models.Database();
    charm = db.charms.add({ id: 'cs:precise/mysql-7' });
    charms = new models.CharmList(),
    charm_store_data = {responseText: '{}'};
    charm_store = new juju.CharmStore(
        {datasource: new Y.DataSource.Local({source: [charm_store_data]})});
    app = { db: db, env: env, charm_store: charm_store };
  });

  afterEach(function() {
    container.remove(true);
    db.destroy();
    env.destroy();
  });

  it('can render no charm', function() {
    var view = new views.CharmDescriptionView(
        { container: container, app: app }).render();
    container.one('div.alert').get('text').trim().should.equal(
        'Waiting on charm data...');
  });

  it('can render incomplete charm', function() {
    var view = new views.CharmDescriptionView(
        { container: container, app: app, model: charm,
          charmStore: charm_store }).render(),
        html = container.one('.charm-description'),
        description_div = html.one('.charm-section div');
    html.one('h3').get('text').trim().should.equal('mysql');
    description_div.getStyle('height').should.not.equal('0px');
    html.all('div.charm-section').size().should.equal(1);
  });

  it('can render fuller charm', function() {
    charm.setAttrs(
        { summary: 'A DB',
          provides: {munin: {'interface': 'munin-node'}},
          last_change:
              { created: 1349797266.032,
                committer: 'fred',
                message: 'fixed EVERYTHING'}});
    charm_store_data.responseText = Y.JSON.stringify(
        { matches: 1,
          results: [
            { store_url: 'cs:precise/superthing-7',
              summary: 'A super thing.'}]});
    var view = new views.CharmDescriptionView(
        { container: container, app: app, model: charm, charms: charms,
          charmStore: charm_store }).render(),
        html = container.one('.charm-description'),
        sections = html.all('.charm-section'),
        description_div = sections.item(0).one('div'),
        interface_div = sections.item(1).one('div'),
        last_change_div = sections.item(2).one('div'),
        related_div = sections.item(3).one('div');
    description_div.get('text').should.contain('A DB');
    interface_div.getStyle('height').should.equal('0px');
    interface_div.get('text').should.contain('munin');
    last_change_div.getStyle('height').should.equal('0px');
    last_change_div.get('text').should.contain('fixed EVERYTHING');
    last_change_div.get('text').should.contain('2012-10-09');
    related_div.one('a').getAttribute('href').should.equal(
        'cs:precise/superthing-7');
    related_div.one('a').get('text').trim().should.equal('superthing');
  });

  it('can toggle visibility of subsections', function() {
    charm.setAttrs(
        { summary: 'A DB',
          provides: {munin: {'interface': 'munin-node'}},
          last_change:
              { created: 1349797266.032,
                committer: 'fred',
                message: 'fixed EVERYTHING'}});
    var view = new views.CharmDescriptionView(
        { container: container, app: app, model: charm,
          charmStore: charm_store }).render(),
        html = container.one('.charm-description'),
        // We use the last change div.
        section_container = html.one('div.charm-section:last-child');
        //section_container = html.one('div.charm-section:nth-of-type(3)');
    section_container.one('div').getStyle('height').should.equal('0px');
    assert(section_container.one('h4 i').hasClass('icon-chevron-up'));
    section_container.one('h4').simulate('click');
    assert(section_container.one('h4 i').hasClass('icon-chevron-down'));
    section_container.one('div').getStyle('height').should.not.equal('0px');
    section_container.one('h4').simulate('click');
    assert(section_container.one('h4 i').hasClass('icon-chevron-up'));
    // The transition is still running, so we can't check display.
  });

  it('can respond to the "back" link', function(done) {
    var view = new views.CharmDescriptionView(
        { container: container, app: app, model: charm,
          charmStore: charm_store }).render();
    view.on('changePanel', function(ev) {
      ev.name.should.equal('charms');
      done();
    });
    container.one('.charm-nav-back').simulate('click');
  });

});
<commit_msg>Removed commented line.<commit_after>'use strict';

describe('charm panel', function() {
  var Y, models, views, juju, ENTER,
      searchResult = '{"results": [{"data_url": "this is my URL", ' +
      '"name": "membase", "series": "precise", "summary": ' +
      '"Membase Server", "relevance": 8.728194117350437, ' +
      '"owner": "charmers", "store_url": "cs:precise/membase-6"}]}';

  before(function(done) {
    Y = YUI(GlobalConfig).use(
        'juju-models',
        'juju-views',
        'juju-gui',
        'juju-env',
        'juju-tests-utils',
        'node-event-simulate',
        'node',
        'event-key',
        'juju-charm-store',

        function(Y) {
          models = Y.namespace('juju.models');
          views = Y.namespace('juju.views');
          juju = Y.namespace('juju');
          ENTER = Y.Node.DOM_EVENTS.key.eventDef.KEY_MAP.enter;
          done();
        });

  });

  beforeEach(function() {
    // The charms panel needs these elements
    var docBody = Y.one(document.body);
    Y.Node.create('
' +
        '

' +
        '

' +
        '<input type="text" id="charm-search-field" />' +
        '
').appendTo(docBody);
  });

  afterEach(function() {
    Y.namespace('juju.views').CharmPanel.killInstance();
    Y.one('#charm-search-test').remove(true);
  });

  it('must be able to show and hide the panel', function() {
    var panel = Y.namespace('juju.views').CharmPanel
          .getInstance({testing: true, app: {}}),
        container = panel.node;
    container.getStyle('display').should.equal('none');
    panel.show();
    container.getStyle('display').should.equal('block');
    panel.hide();
    container.getStyle('display').should.equal('none');
    panel.toggle();
    container.getStyle('display').should.equal('block');
    panel.toggle();
    container.getStyle('display').should.equal('none');
  });

  it('must be able to search', function() {
    var searchTriggered = false,
        panel = Y.namespace('juju.views').CharmPanel.getInstance({
          charm_store: new juju.CharmStore({datasource: {
            sendRequest: function(params) {
              searchTriggered = true;
              // Stubbing the server callback value
              params.callback.success({
                response: {
                  results: [{
                    responseText: searchResult
                  }]
                }
              });
            }
          }}),
          testing: true,
          app: {}
        }),
        node = panel.node;
    panel.show(true);
    var field = Y.one('#charm-search-field');
    field.set('value', 'aaa');
    field.simulate('keydown', { keyCode: ENTER });

    searchTriggered.should.equal(true);
    node.one('.charm-entry .btn.deploy').getData('url').should.equal(
        'cs:precise/membase-6');
  });

  it('must be able to trigger charm details', function() {
    var db = new models.Database(),
        panel = Y.namespace('juju.views').CharmPanel.getInstance({
          charm_store: new juju.CharmStore({datasource: {
            sendRequest: function(params) {
              // Mocking the server callback value
              params.callback.success({
                response: {
                  results: [{
                    responseText: searchResult
                  }]
                }
              });
            }
          }}),
          app: {db: db},
          testing: true
        }),
        node = panel.node;
    db.charms.add({id: 'cs:precise/membase-6'});

    panel.show();
    var field = Y.one('#charm-search-field');
    field.set('value', 'aaa');
    field.simulate('keydown', { keyCode: ENTER });
    node.one('a.charm-detail').simulate('click');
    node.one('.charm-description h3').get('text').trim()
      .should.equal('membase');
  });

  it('must be able to deploy from the description panel by going to the ' +
     'configuration panel', function() {
        var db = new models.Database(),
            panel = Y.namespace('juju.views').CharmPanel.getInstance({
              charm_store: new juju.CharmStore({datasource: {
                sendRequest: function(params) {
                  // Mocking the server callback value
                  params.callback.success({
                    response: {
                      results: [{
                        responseText: searchResult
                      }]
                    }
                  });
                }
              }}),
              app: {db: db},
              testing: true
            }),
            node = panel.node,
            charm = db.charms.add({id: 'cs:precise/membase-6'});
        charm.loaded = true;
        panel.show();
        var field = Y.one('#charm-search-field');
        field.set('value', 'aaa');
        field.simulate('keydown', { keyCode: ENTER });
        node.one('a.charm-detail').simulate('click');
        node.one('.btn-primary').simulate('click');
        node.one('.control-label').get('text').trim()
         .should.equal('Service name');
      });
});

describe('charm description', function() {
  var Y, models, views, juju, conn, env, container, db, app, charm,
      charm_store_data, charm_store, charms;

  before(function(done) {
    Y = YUI(GlobalConfig).use(
        'juju-models',
        'juju-views',
        'juju-gui',
        'juju-env',
        'juju-tests-utils',
        'node-event-simulate',
        'node',
        'datasource-local',
        'json-stringify',
        'juju-charm-store',

        function(Y) {
          models = Y.namespace('juju.models');
          views = Y.namespace('juju.views');
          juju = Y.namespace('juju');
          done();
        });

  });

  beforeEach(function() {
    conn = new (Y.namespace('juju-tests.utils')).SocketStub(),
    env = new (Y.namespace('juju')).Environment({conn: conn});
    env.connect();
    conn.open();
    container = Y.Node.create('
');
    Y.one('#main').append(container);
    db = new models.Database();
    charm = db.charms.add({ id: 'cs:precise/mysql-7' });
    charms = new models.CharmList(),
    charm_store_data = {responseText: '{}'};
    charm_store = new juju.CharmStore(
        {datasource: new Y.DataSource.Local({source: [charm_store_data]})});
    app = { db: db, env: env, charm_store: charm_store };
  });

  afterEach(function() {
    container.remove(true);
    db.destroy();
    env.destroy();
  });

  it('can render no charm', function() {
    var view = new views.CharmDescriptionView(
        { container: container, app: app }).render();
    container.one('div.alert').get('text').trim().should.equal(
        'Waiting on charm data...');
  });

  it('can render incomplete charm', function() {
    var view = new views.CharmDescriptionView(
        { container: container, app: app, model: charm,
          charmStore: charm_store }).render(),
        html = container.one('.charm-description'),
        description_div = html.one('.charm-section div');
    html.one('h3').get('text').trim().should.equal('mysql');
    description_div.getStyle('height').should.not.equal('0px');
    html.all('div.charm-section').size().should.equal(1);
  });

  it('can render fuller charm', function() {
    charm.setAttrs(
        { summary: 'A DB',
          provides: {munin: {'interface': 'munin-node'}},
          last_change:
              { created: 1349797266.032,
                committer: 'fred',
                message: 'fixed EVERYTHING'}});
    charm_store_data.responseText = Y.JSON.stringify(
        { matches: 1,
          results: [
            { store_url: 'cs:precise/superthing-7',
              summary: 'A super thing.'}]});
    var view = new views.CharmDescriptionView(
        { container: container, app: app, model: charm, charms: charms,
          charmStore: charm_store }).render(),
        html = container.one('.charm-description'),
        sections = html.all('.charm-section'),
        description_div = sections.item(0).one('div'),
        interface_div = sections.item(1).one('div'),
        last_change_div = sections.item(2).one('div'),
        related_div = sections.item(3).one('div');
    description_div.get('text').should.contain('A DB');
    interface_div.getStyle('height').should.equal('0px');
    interface_div.get('text').should.contain('munin');
    last_change_div.getStyle('height').should.equal('0px');
    last_change_div.get('text').should.contain('fixed EVERYTHING');
    last_change_div.get('text').should.contain('2012-10-09');
    related_div.one('a').getAttribute('href').should.equal(
        'cs:precise/superthing-7');
    related_div.one('a').get('text').trim().should.equal('superthing');
  });

  it('can toggle visibility of subsections', function() {
    charm.setAttrs(
        { summary: 'A DB',
          provides: {munin: {'interface': 'munin-node'}},
          last_change:
              { created: 1349797266.032,
                committer: 'fred',
                message: 'fixed EVERYTHING'}});
    var view = new views.CharmDescriptionView(
        { container: container, app: app, model: charm,
          charmStore: charm_store }).render(),
        html = container.one('.charm-description'),
        // We use the last change div.
        section_container = html.one('div.charm-section:last-child');
    section_container.one('div').getStyle('height').should.equal('0px');
    assert(section_container.one('h4 i').hasClass('icon-chevron-up'));
    section_container.one('h4').simulate('click');
    assert(section_container.one('h4 i').hasClass('icon-chevron-down'));
    section_container.one('div').getStyle('height').should.not.equal('0px');
    section_container.one('h4').simulate('click');
    assert(section_container.one('h4 i').hasClass('icon-chevron-up'));
    // The transition is still running, so we can't check display.
  });

  it('can respond to the "back" link', function(done) {
    var view = new views.CharmDescriptionView(
        { container: container, app: app, model: charm,
          charmStore: charm_store }).render();
    view.on('changePanel', function(ev) {
      ev.name.should.equal('charms');
      done();
    });
    container.one('.charm-nav-back').simulate('click');
  });

});
<|endoftext|>


<commit_before>var computeUtil = require('../lib/ComputeUtil');
var should   = require('should');

describe('Util Function', function () {

});
<commit_msg>stringSplit() function unit function.<commit_after>var ComputeUtil = require('../lib/ComputeUtil');
var should   = require('should');

var computeUtil = new ComputeUtil();

describe('Util Function', function () {

    describe('#stringSplit()', function () {
        it('should stringSplit two numbers', function () {
            var string = 'R123';
            computeUtil.stringSplit(string).should.be.eql({
                '0': 'R', '1': '1', '2': '2', '3': '3'
            });
        });

        it('show be a number', function () {
            var string = 'R123';
            computeUtil.stringSplit(string).should.be.a.Array;
        });
    });
});
<|endoftext|>


<commit_before>'use strict'

var chai = require('chai')
var expect = chai.expect
var merge = require('merge')

var defaultProtocolVersion = require('../../package.json').version

function testEchoMessages (clientSpecs, serverSpecs, onSuccess, onFailure) {
  var nbTestMessages = 10
  var currentTestMessage = 0

  var clientSocket = clientSpecs.socket
  var serverSocket = serverSpecs.socket
  var listeningInfo = serverSpecs.listeningInfo

  var serverTestStream
  var clientTestStream

  // when a new stream is generated
  serverSocket.on('connection', function (echoStream, connectionInfo) {
    console.log('echo stream available')
    serverTestStream = echoStream
    expect(connectionInfo).to.not.be.undefined
    // then pipe the read stream to the write stream (echo behavior)
    echoStream.pipe(echoStream)
    // write stream end
    echoStream.on('finish', function () {
      console.log('echo write stream ended')
    })
    // read stream end
    echoStream.on('end', function () {
      console.log('echo read stream ended')
    })
    // try to close server socket
    serverSocket.close(
      function () {
        setTimeout(function () { // to cope with TCP closing behavior
          onSuccess(
            clientTestStream,
            serverTestStream
          )
        }, 500)
      },
      function (error) {
        onFailure(error)
      })
  })
  serverSocket.on('error', onFailure)

  function sendTestMessage (stream) {
    var testMessage = 'test message ' + currentTestMessage
    console.log('sending message ' + testMessage)
    stream.write(testMessage)
  }

  // bind echo socket
  serverSocket.listenP(listeningInfo)
    .then(function (connectionInfo) {
      if (listeningInfo) {
        var protocolVersion = {
          version: defaultProtocolVersion
        }
        expect(connectionInfo).to.deep.equal(merge(protocolVersion, listeningInfo))
      }
      return clientSocket.connectP(connectionInfo)
    })
    .then(function (clientStream) {
      console.log('client stream available')
      clientTestStream = clientStream
      // verify incoming test messages
      clientStream.on('data', function (chunk) {
        var message = chunk.toString()
        console.log('receiving message ' + message)
        expect(message).to.equal('test message ' + currentTestMessage++)
        if (currentTestMessage !== nbTestMessages) {
          sendTestMessage(clientStream)
        } else {
          clientStream.end()
        }
      })
      clientStream.on('error', onFailure)
      // read stream end
      clientStream.on('end', function () {
        console.log('client read stream ended')
      })
      // write stream end
      clientStream.on('finish', function () {
        console.log('client write stream ended')
      })
      // send test messages
      sendTestMessage(clientStream)
    })
    .catch(function (error) {
      onFailure(error)
    })
}

function testDestroyStream (clientSpecs, serverSpecs, streamToDestroy, onSuccess, onFailure) {
  var clientSocket = clientSpecs.socket
  var serverSocket = serverSpecs.socket
  var listeningInfo = serverSpecs.listeningInfo

  var serverStream, clientStream
  var clientStreamClosed = false
  var echoStreamClosed = false

  // when a new stream is generated
  serverSocket.on('connection', function (echoStream) {
    console.log('echo stream available')
    serverStream = echoStream
    echoStream.on('close', function () {
      console.log('echoStreamClosed')
      echoStreamClosed = true
      if (echoStreamClosed && clientStreamClosed) {
        onSuccess()
      }
    })
    if (!serverStream || !clientStream) {
      // serverStream or clientStream are missing -- don't do anything
      return
    }
    if (streamToDestroy === 'client') {
      clientStream.destroy()
    } else {
      serverStream.destroy()
    }
  })
  serverSocket.on('error', onFailure)

  // bind echo socket
  serverSocket.listenP(listeningInfo)
    .then(function (connectionInfo) {
      if (listeningInfo) {
        var protocolVersion = {
          version: defaultProtocolVersion
        }
        expect(connectionInfo).to.deep.equal(merge(protocolVersion, listeningInfo))
      }
      return clientSocket.connectP(connectionInfo)
    })
    .then(function (sourceStream) {
      console.log('client stream available')
      clientStream = sourceStream
      sourceStream.on('data', function () {
        var errorMsg = 'not expecting data arrival'
        console.error(errorMsg)
        onFailure(errorMsg)
      })
      sourceStream.on('error', onFailure)
      sourceStream.on('close', function () {
        console.log('clientStreamClosed')
        clientStreamClosed = true
        if (echoStreamClosed && clientStreamClosed) {
          onSuccess()
        }
      })
      if (!serverStream || !clientStream) {
        // serverStream or clientStream are missing -- don't do anything
        return
      }
      if (streamToDestroy === 'client') {
        clientStream.destroy()
      } else {
        serverStream.destroy()
      }
    })
    .catch(function (error) {
      onFailure(error)
    })
}

module.exports.testEchoMessages = testEchoMessages
module.exports.testDestroyStream = testDestroyStream
<commit_msg>updated test scripts to improve flexibility<commit_after>'use strict'

var chai = require('chai')
var expect = chai.expect
var merge = require('merge')

var defaultProtocolVersion = require('../../package.json').version

function testEchoMessages (clientSpecs, serverSpecs, onSuccess, onFailure) {
  var nbTestMessages = 10
  var currentTestMessage = 0

  var clientSocket = clientSpecs.socket
  var serverSocket = serverSpecs.socket
  var listeningInfo = serverSpecs.listeningInfo

  var serverTestStream
  var clientTestStream

  // when a new stream is generated
  serverSocket.on('connection', function (echoStream, connectionInfo) {
    console.log('echo stream available')
    serverTestStream = echoStream
    expect(connectionInfo).to.not.be.undefined
    // then pipe the read stream to the write stream (echo behavior)
    echoStream.pipe(echoStream)
    // write stream end
    echoStream.on('finish', function () {
      console.log('echo write stream ended')
    })
    // read stream end
    echoStream.on('end', function () {
      console.log('echo read stream ended')
    })
    // try to close server socket
    serverSocket.close(
      function () {
        setTimeout(function () { // to cope with TCP closing behavior
          onSuccess(
            clientTestStream,
            serverTestStream
          )
        }, 500)
      },
      function (error) {
        onFailure(error)
      })
  })
  serverSocket.on('error', onFailure)

  function sendTestMessage (stream) {
    var testMessage = 'test message ' + currentTestMessage
    console.log('sending message ' + testMessage)
    stream.write(testMessage)
  }

  // bind echo socket
  serverSocket.listenP(listeningInfo)
    .then(function (connectionInfo) {
      if (listeningInfo) {
        var protocolVersion = {
          version: defaultProtocolVersion
        }
        expect(connectionInfo).to.deep.equal(merge(protocolVersion, listeningInfo))
      }
      return clientSocket.connectP(connectionInfo)
    })
    .then(function (clientStream) {
      console.log('client stream available')
      clientTestStream = clientStream
      // verify incoming test messages
      clientStream.on('data', function (chunk) {
        var message = chunk.toString()
        console.log('receiving message ' + message)
        expect(message).to.equal('test message ' + currentTestMessage++)
        if (currentTestMessage !== nbTestMessages) {
          sendTestMessage(clientStream)
        } else {
          clientStream.end()
        }
      })
      clientStream.on('error', onFailure)
      // read stream end
      clientStream.on('end', function () {
        console.log('client read stream ended')
      })
      // write stream end
      clientStream.on('finish', function () {
        console.log('client write stream ended')
        console.log(clientStream.writable)
      })
      // send test messages
      sendTestMessage(clientStream)
    })
    .catch(function (error) {
      onFailure(error)
    })
}

function testDestroyStream (clientSpecs, serverSpecs, streamToDestroy, onSuccess, onFailure) {
  var clientSocket = clientSpecs.socket
  var serverSocket = serverSpecs.socket
  var listeningInfo = serverSpecs.listeningInfo

  var serverStream, clientStream
  var clientStreamClosed = false
  var echoStreamClosed = false

  // when a new stream is generated
  serverSocket.on('connection', function (echoStream) {
    console.log('echo stream available')
    serverStream = echoStream
    echoStream.on('close', function () {
      console.log('echoStreamClosed')
      echoStreamClosed = true
      if (echoStreamClosed && clientStreamClosed) {
        onSuccess()
      }
    })
    if (!serverStream || !clientStream) {
      // serverStream or clientStream are missing -- don't do anything
      return
    }
    if (streamToDestroy === 'client') {
      clientStream.destroy()
    } else {
      serverStream.destroy()
    }
  })
  serverSocket.on('error', onFailure)

  // bind echo socket
  serverSocket.listenP(listeningInfo)
    .then(function (connectionInfo) {
      if (listeningInfo) {
        var protocolVersion = {
          version: defaultProtocolVersion
        }
        expect(connectionInfo).to.deep.equal(merge(protocolVersion, listeningInfo))
      }
      return clientSocket.connectP(connectionInfo)
    })
    .then(function (sourceStream) {
      console.log('client stream available')
      clientStream = sourceStream
      sourceStream.on('data', function () {
        var errorMsg = 'not expecting data arrival'
        console.error(errorMsg)
        onFailure(errorMsg)
      })
      sourceStream.on('error', onFailure)
      sourceStream.on('close', function () {
        console.log('clientStreamClosed')
        clientStreamClosed = true
        if (echoStreamClosed && clientStreamClosed) {
          onSuccess()
        }
      })
      if (!serverStream || !clientStream) {
        // serverStream or clientStream are missing -- don't do anything
        return
      }
      if (streamToDestroy === 'client') {
        clientStream.destroy()
      } else {
        serverStream.destroy()
      }
    })
    .catch(function (error) {
      onFailure(error)
    })
}

module.exports.testEchoMessages = testEchoMessages
module.exports.testDestroyStream = testDestroyStream
<|endoftext|>


<commit_before>'use strict'

var dgram = require('dgram')
var transports = require('stun-js').transports
var TurnClient = require('../src/turn_client')

var chai = require('chai')
var chaiAsPromised = require('chai-as-promised')
var expect = chai.expect
chai.use(chaiAsPromised)
chai.should()

var turnAddr = process.env.TURN_ADDR
var turnPort = process.env.TURN_PORT
var turnUser = process.env.TURN_USER
var turnPwd = process.env.TURN_PASS
var socketPort = 33333

describe('#TURN operations', function () {
  this.timeout(15000)

  it('should execute TURN allocate operation over UDP socket using promises', function (done) {
    var retransmissionTimer
    // send a TURN allocate request and verify the reply
    var sendAllocateRequest = function (client, socket) {
      client.allocateP()
        .then(function (result) {
          // end retransmissionTimer
          clearTimeout(retransmissionTimer)
          // test results
          expect(result).not.to.be.undefined
          expect(result).to.have.property('mappedAddress')
          expect(result.mappedAddress).to.have.property('address')
          expect(result.mappedAddress).to.have.property('port')
          // expect(result.mappedAddress.address).to.equal(testGW)
          expect(result).to.have.property('relayedAddress')
          expect(result.relayedAddress).to.have.property('address')
          expect(result.relayedAddress).to.have.property('port')
          // expect(result.relayedAddress.address).to.equal(turnAddr)
          return client.closeP()
        })
        .then(function () {
          expect(socket.listeners('message').length).to.equal(1)
          expect(socket.listeners('error').length).to.equal(1)
          // close socket
          socket.close(function () {
            done()
          })
        })
        .catch(function (error) {
          done(error)
        })
    }
    // create socket
    var socket = dgram.createSocket('udp4')
    socket.on('message', function (message, rinfo) { //
      done(new Error('message callback should not be fired'))
    })
    socket.on('error', function (error) {
      done(error)
    })
    socket.on('listening', function () {
      // create turn client and pass socket over
      var transport = new transports.UDP(socket)
      var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, transport)
      // retransmission timer -- we're using UDP ...
      retransmissionTimer = setTimeout(function () {
        console.log('resending ALLOCATE request')
        sendAllocateRequest(client, socket)
      }, 5000)
      // allocate request
      sendAllocateRequest(client, socket)
    })
    socket.bind(socketPort)
  })

  it('should execute TURN allocate operation over TCP socket using callbacks', function (done) {
    var transport = new transports.TCP()
    var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, transport)
    var onError = function (error) {
      done(error)
    }
    var onReady = function (result) {
      expect(result).not.to.be.undefined
      expect(result).to.have.property('mappedAddress')
      expect(result.mappedAddress).to.have.property('address')
      expect(result.mappedAddress).to.have.property('port')
      // expect(result.mappedAddress.address).to.equal(testGW)
      expect(result).to.have.property('relayedAddress')
      expect(result.relayedAddress).to.have.property('address')
      expect(result.relayedAddress).to.have.property('port')
      // expect(result.relayedAddress.address).to.equal(turnAddr)
      client.close(
        function () {
          done()
        },
        onError
      )
    }
    client.allocate(onReady, onError)
  })

  it('should execute TURN allocate operation over unspecified UDP socket using promises', function (done) {
    var retransmissionTimer
    // send a TURN allocate request and verify the reply
    var sendAllocateRequest = function (client) {
      client.allocateP()
        .then(function (result) {
          // end retransmissionTimer
          clearTimeout(retransmissionTimer)
          // test results
          expect(result).not.to.be.undefined
          expect(result).to.have.property('mappedAddress')
          expect(result.mappedAddress).to.have.property('address')
          expect(result.mappedAddress).to.have.property('port')
          // expect(result.mappedAddress.address).to.equal(testGW)
          expect(result).to.have.property('relayedAddress')
          expect(result.relayedAddress).to.have.property('address')
          expect(result.relayedAddress).to.have.property('port')
          // expect(result.relayedAddress.address).to.equal(turnAddr)
          return client.closeP()
        })
        .then(function () {
          done()
        })
        .catch(function (error) {
          done(error)
        })
    }
    // create turn client
    var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd)
    // retransmission timer -- we're using UDP ...
    retransmissionTimer = setTimeout(function () {
      console.log('resending ALLOCATE request')
      sendAllocateRequest(client)
    }, 5000)
    // allocate request
    sendAllocateRequest(client)
  })

  it('should execute TURN allocate followed by refresh over UDP socket using promises', function (done) {
    var lifetime = 3600
    var retransmissionTimer
    // send a TURN allocate request and verify the reply
    var sendAllocateAndRefreshRequest = function (client) {
      client.allocateP()
        .then(function (result) {
          return client.refreshP(lifetime)
        })
        .then(function (duration) {
          // end retransmissionTimer
          clearTimeout(retransmissionTimer)
          // test results
          expect(duration).to.equal(lifetime)
          // close turn client
          return client.closeP()
        })
        .then(function () {
          expect(socket.listeners('message').length).to.equal(1)
          expect(socket.listeners('error').length).to.equal(1)
          done()
        })
        .catch(function (error) {
          done(error)
        })
    }
    // create socket
    var socket = dgram.createSocket('udp4')
    socket.on('message', function (message, rinfo) { //
      done(new Error('message callback should not be fired'))
    })
    socket.on('error', function (error) {
      done(error)
    })
    socket.on('listening', function () {
      // create stun client and pass socket over
      var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd)
      // retransmission timer -- we're using UDP ...
      retransmissionTimer = setTimeout(function () {
        console.log('resending ALLOCATE and REFRESH request')
        sendAllocateAndRefreshRequest(client)
      }, 5000)
      sendAllocateAndRefreshRequest(client)
    })
    socket.bind(socketPort)
  })

  it('should execute TURN allocate followed by create permission over TCP socket using promises', function () {
    var transport = new transports.TCP()
    var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, transport)
    var turnAddress = '1.2.3.4'
    return client.allocateP()
      .then(function (result) {
        return client.createPermissionP(turnAddress)
      })
      .then(function () {
        return client.closeP()
      })
  })

  it('should receive messages that are sent via relay server over TCP sockets', function (done) {
    var testData = 'hello there'
    var testRuns = 10
    var messagesReceived = 0

    // alice's client
    var clientAlice = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, new transports.TCP())
    // bob's client
    var clientBob = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, new transports.TCP())
    var srflxAddressAlice, srflxAddressBob, relayAddressAlice, relayAddressBob

    var sendTestMessageFromAliceToBob = function () {
      var bytes = new Buffer(testData)
      clientAlice.sendToRelay(
        bytes,
        relayAddressBob.address,
        relayAddressBob.port,
        function () {
          console.log('message sent to ' + relayAddressBob.address + ':' + relayAddressBob.port)
        }, // on success
        function (error) {
          done(error)
        }
      )
    }

    // subscribe to incoming messages
    clientBob.on('relayed-message', function (bytes, peerAddress) {
      var message = bytes.toString()
      expect(message).to.equal(testData)
      console.log('receiving test message ' + message)
      messagesReceived++
      if (messagesReceived === testRuns) {
        clientBob.closeP()
          .then(function () {
            return clientAlice.closeP()
          })
          .then(function () {
            done()
          })
          .catch(function (error) {
            done(error)
          })
      } else {
        sendTestMessageFromAliceToBob()
      }
    })

    // allocate relaying session for alice
    clientAlice.allocateP()
      .then(function (allocateAddress) {
        srflxAddressAlice = allocateAddress.mappedAddress
        relayAddressAlice = allocateAddress.relayedAddress
        console.log("alice's srflx address = " + srflxAddressAlice.address + ':' + srflxAddressAlice.port)
        console.log("alice's relay address = " + relayAddressAlice.address + ':' + relayAddressAlice.port)
        // allocate relaying session for bob
        return clientBob.allocateP()
      })
      .then(function (allocateAddress) {
        srflxAddressBob = allocateAddress.mappedAddress
        relayAddressBob = allocateAddress.relayedAddress
        console.log("bob's address = " + srflxAddressBob.address + ':' + srflxAddressBob.port)
        console.log("bob's relay address = " + relayAddressBob.address + ':' + relayAddressBob.port)
        // create permission for alice to send messages to bob
        return clientBob.createPermissionP(relayAddressAlice.address)
      })
      .then(function () {
        // create permission for bob to send messages to alice
        return clientAlice.createPermissionP(relayAddressBob.address)
      })
      .then(function () {
        // send test message
        sendTestMessageFromAliceToBob()
      })
      .catch(function (error) {
        done(error)
      })
  })

  it('should execute TURN channel binding and receive messages sent via these channels over TCP sockets using promises', function (done) {
    var testData = 'hello there'
    var testRuns = 10
    var messagesReceived = 0

    // alice's client
    var clientAlice = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, new transports.TCP())
    // bob's client
    var clientBob = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, new transports.TCP())
    var srflxAddressAlice, srflxAddressBob, relayAddressAlice, relayAddressBob
    var channelId

    var sendTestMessageFromAliceToBob = function () {
      var bytes = new Buffer(testData)
      clientAlice.sendToChannel(
        bytes,
        channelId,
        function () {
          console.log('message sent to channel ' + channelId)
        },
        function (error) {
          done(error)
        }
      )
    }

    // subscribe to incoming messages
    clientBob.on('relayed-message', function (bytes, peerAddress) {
      var message = bytes.toString()
      expect(message).to.equal(testData)
      console.log('receiving test message ' + message)
      messagesReceived++
      if (messagesReceived === testRuns) {
        clientBob.closeP()
          .then(function () {
            return clientAlice.closeP()
          })
          .then(function () {
            done()
          })
          .catch(function (error) {
            done(error)
          })
      } else {
        sendTestMessageFromAliceToBob()
      }
    })

    // allocate relaying session for alice
    clientAlice.allocateP()
      .then(function (allocateAddress) {
        srflxAddressAlice = allocateAddress.mappedAddress
        relayAddressAlice = allocateAddress.relayedAddress
        console.log("alice's srflx address = " + srflxAddressAlice.address + ':' + srflxAddressAlice.port)
        console.log("alice's relay address = " + relayAddressAlice.address + ':' + relayAddressAlice.port)
        // allocate relaying session for bob
        return clientBob.allocateP()
      })
      .then(function (allocateAddress) {
        srflxAddressBob = allocateAddress.mappedAddress
        relayAddressBob = allocateAddress.relayedAddress
        console.log("bob's address = " + srflxAddressBob.address + ':' + srflxAddressBob.port)
        console.log("bob's relay address = " + relayAddressBob.address + ':' + relayAddressBob.port)
        // create permission for alice to send messages to bob
        return clientBob.createPermissionP(relayAddressAlice.address)
      })
      .then(function () {
        // create channel from alice to bob
        return clientAlice.bindChannelP(relayAddressBob.address, relayAddressBob.port)
      })
      .then(function (channel) {
        expect(channel).not.to.be.undefined
        channelId = channel
        //  create permission for bob to send messages to alice
        return clientAlice.createPermissionP(relayAddressBob.address)
      })
      .then(function () {
        // create channel from bob to alice
        return clientBob.bindChannelP(relayAddressAlice.address, relayAddressAlice.port)
      })
      .then(function (anotherChannel) {
        // send test message
        sendTestMessageFromAliceToBob()
      })
      .catch(function (error) {
        done(error)
      })
  })
})
<commit_msg>added test to mimicy behavior of a CreatePermission being refreshed<commit_after>'use strict'

var dgram = require('dgram')
var transports = require('stun-js').transports
var TurnClient = require('../src/turn_client')

var chai = require('chai')
var chaiAsPromised = require('chai-as-promised')
var expect = chai.expect
chai.use(chaiAsPromised)
chai.should()

var turnAddr = process.env.TURN_ADDR
var turnPort = process.env.TURN_PORT
var turnUser = process.env.TURN_USER
var turnPwd = process.env.TURN_PASS
var socketPort = 33333

describe('#TURN operations', function () {
  this.timeout(15000)

  it('should execute TURN allocate operation over UDP socket using promises', function (done) {
    var retransmissionTimer
    // send a TURN allocate request and verify the reply
    var sendAllocateRequest = function (client, socket) {
      client.allocateP()
        .then(function (result) {
          // end retransmissionTimer
          clearTimeout(retransmissionTimer)
          // test results
          expect(result).not.to.be.undefined
          expect(result).to.have.property('mappedAddress')
          expect(result.mappedAddress).to.have.property('address')
          expect(result.mappedAddress).to.have.property('port')
          // expect(result.mappedAddress.address).to.equal(testGW)
          expect(result).to.have.property('relayedAddress')
          expect(result.relayedAddress).to.have.property('address')
          expect(result.relayedAddress).to.have.property('port')
          // expect(result.relayedAddress.address).to.equal(turnAddr)
          return client.closeP()
        })
        .then(function () {
          expect(socket.listeners('message').length).to.equal(1)
          expect(socket.listeners('error').length).to.equal(1)
          // close socket
          socket.close(function () {
            done()
          })
        })
        .catch(function (error) {
          done(error)
        })
    }
    // create socket
    var socket = dgram.createSocket('udp4')
    socket.on('message', function (message, rinfo) { //
      done(new Error('message callback should not be fired'))
    })
    socket.on('error', function (error) {
      done(error)
    })
    socket.on('listening', function () {
      // create turn client and pass socket over
      var transport = new transports.UDP(socket)
      var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, transport)
      // retransmission timer -- we're using UDP ...
      retransmissionTimer = setTimeout(function () {
        console.log('resending ALLOCATE request')
        sendAllocateRequest(client, socket)
      }, 5000)
      // allocate request
      sendAllocateRequest(client, socket)
    })
    socket.bind(socketPort)
  })

  it('should execute TURN allocate operation over TCP socket using callbacks', function (done) {
    var transport = new transports.TCP()
    var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, transport)
    var onError = function (error) {
      done(error)
    }
    var onReady = function (result) {
      expect(result).not.to.be.undefined
      expect(result).to.have.property('mappedAddress')
      expect(result.mappedAddress).to.have.property('address')
      expect(result.mappedAddress).to.have.property('port')
      // expect(result.mappedAddress.address).to.equal(testGW)
      expect(result).to.have.property('relayedAddress')
      expect(result.relayedAddress).to.have.property('address')
      expect(result.relayedAddress).to.have.property('port')
      // expect(result.relayedAddress.address).to.equal(turnAddr)
      client.close(
        function () {
          done()
        },
        onError
      )
    }
    client.allocate(onReady, onError)
  })

  it('should execute TURN allocate operation over unspecified UDP socket using promises', function (done) {
    var retransmissionTimer
    // send a TURN allocate request and verify the reply
    var sendAllocateRequest = function (client) {
      client.allocateP()
        .then(function (result) {
          // end retransmissionTimer
          clearTimeout(retransmissionTimer)
          // test results
          expect(result).not.to.be.undefined
          expect(result).to.have.property('mappedAddress')
          expect(result.mappedAddress).to.have.property('address')
          expect(result.mappedAddress).to.have.property('port')
          // expect(result.mappedAddress.address).to.equal(testGW)
          expect(result).to.have.property('relayedAddress')
          expect(result.relayedAddress).to.have.property('address')
          expect(result.relayedAddress).to.have.property('port')
          // expect(result.relayedAddress.address).to.equal(turnAddr)
          return client.closeP()
        })
        .then(function () {
          done()
        })
        .catch(function (error) {
          done(error)
        })
    }
    // create turn client
    var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd)
    // retransmission timer -- we're using UDP ...
    retransmissionTimer = setTimeout(function () {
      console.log('resending ALLOCATE request')
      sendAllocateRequest(client)
    }, 5000)
    // allocate request
    sendAllocateRequest(client)
  })

  it('should execute TURN allocate followed by refresh over UDP socket using promises', function (done) {
    var lifetime = 3600
    var retransmissionTimer
    // send a TURN allocate request and verify the reply
    var sendAllocateAndRefreshRequest = function (client) {
      client.allocateP()
        .then(function (result) {
          return client.refreshP(lifetime)
        })
        .then(function (duration) {
          // end retransmissionTimer
          clearTimeout(retransmissionTimer)
          // test results
          expect(duration).to.equal(lifetime)
          // close turn client
          return client.closeP()
        })
        .then(function () {
          expect(socket.listeners('message').length).to.equal(1)
          expect(socket.listeners('error').length).to.equal(1)
          done()
        })
        .catch(function (error) {
          done(error)
        })
    }
    // create socket
    var socket = dgram.createSocket('udp4')
    socket.on('message', function (message, rinfo) { //
      done(new Error('message callback should not be fired'))
    })
    socket.on('error', function (error) {
      done(error)
    })
    socket.on('listening', function () {
      // create stun client and pass socket over
      var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd)
      // retransmission timer -- we're using UDP ...
      retransmissionTimer = setTimeout(function () {
        console.log('resending ALLOCATE and REFRESH request')
        sendAllocateAndRefreshRequest(client)
      }, 5000)
      sendAllocateAndRefreshRequest(client)
    })
    socket.bind(socketPort)
  })

  it('should execute TURN allocate followed by create permission over TCP socket using promises', function () {
    var transport = new transports.TCP()
    var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, transport)
    var turnAddress = '1.2.3.4'
    return client.allocateP()
      .then(function (result) {
        return client.createPermissionP(turnAddress)
      })
      .then(function () {
        return client.closeP()
      })
  })

  it('should execute TURN allocate followed by two consecutive create permissions (testing permission refresh) over TCP socket using promises', function () {
    var transport = new transports.TCP()
    var client = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, transport)
    var turnAddress = '1.2.3.4'
    return client.allocateP()
      .then(function (result) {
        return client.createPermissionP(turnAddress)
      })
      .then(function () {
        return client.createPermissionP(turnAddress)
      })
      .then(function () {
        return client.closeP()
      })
  })

  it('should receive messages that are sent via relay server over TCP sockets', function (done) {
    var testData = 'hello there'
    var testRuns = 10
    var messagesReceived = 0

    // alice's client
    var clientAlice = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, new transports.TCP())
    // bob's client
    var clientBob = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, new transports.TCP())
    var srflxAddressAlice, srflxAddressBob, relayAddressAlice, relayAddressBob

    var sendTestMessageFromAliceToBob = function () {
      var bytes = new Buffer(testData)
      clientAlice.sendToRelay(
        bytes,
        relayAddressBob.address,
        relayAddressBob.port,
        function () {
          console.log('message sent to ' + relayAddressBob.address + ':' + relayAddressBob.port)
        }, // on success
        function (error) {
          done(error)
        }
      )
    }

    // subscribe to incoming messages
    clientBob.on('relayed-message', function (bytes, peerAddress) {
      var message = bytes.toString()
      expect(message).to.equal(testData)
      console.log('receiving test message ' + message)
      messagesReceived++
      if (messagesReceived === testRuns) {
        clientBob.closeP()
          .then(function () {
            return clientAlice.closeP()
          })
          .then(function () {
            done()
          })
          .catch(function (error) {
            done(error)
          })
      } else {
        sendTestMessageFromAliceToBob()
      }
    })

    // allocate relaying session for alice
    clientAlice.allocateP()
      .then(function (allocateAddress) {
        srflxAddressAlice = allocateAddress.mappedAddress
        relayAddressAlice = allocateAddress.relayedAddress
        console.log("alice's srflx address = " + srflxAddressAlice.address + ':' + srflxAddressAlice.port)
        console.log("alice's relay address = " + relayAddressAlice.address + ':' + relayAddressAlice.port)
        // allocate relaying session for bob
        return clientBob.allocateP()
      })
      .then(function (allocateAddress) {
        srflxAddressBob = allocateAddress.mappedAddress
        relayAddressBob = allocateAddress.relayedAddress
        console.log("bob's address = " + srflxAddressBob.address + ':' + srflxAddressBob.port)
        console.log("bob's relay address = " + relayAddressBob.address + ':' + relayAddressBob.port)
        // create permission for alice to send messages to bob
        return clientBob.createPermissionP(relayAddressAlice.address)
      })
      .then(function () {
        // create permission for bob to send messages to alice
        return clientAlice.createPermissionP(relayAddressBob.address)
      })
      .then(function () {
        // send test message
        sendTestMessageFromAliceToBob()
      })
      .catch(function (error) {
        done(error)
      })
  })

  it('should execute TURN channel binding and receive messages sent via these channels over TCP sockets using promises', function (done) {
    var testData = 'hello there'
    var testRuns = 10
    var messagesReceived = 0

    // alice's client
    var clientAlice = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, new transports.TCP())
    // bob's client
    var clientBob = new TurnClient(turnAddr, turnPort, turnUser, turnPwd, new transports.TCP())
    var srflxAddressAlice, srflxAddressBob, relayAddressAlice, relayAddressBob
    var channelId

    var sendTestMessageFromAliceToBob = function () {
      var bytes = new Buffer(testData)
      clientAlice.sendToChannel(
        bytes,
        channelId,
        function () {
          console.log('message sent to channel ' + channelId)
        },
        function (error) {
          done(error)
        }
      )
    }

    // subscribe to incoming messages
    clientBob.on('relayed-message', function (bytes, peerAddress) {
      var message = bytes.toString()
      expect(message).to.equal(testData)
      console.log('receiving test message ' + message)
      messagesReceived++
      if (messagesReceived === testRuns) {
        clientBob.closeP()
          .then(function () {
            return clientAlice.closeP()
          })
          .then(function () {
            done()
          })
          .catch(function (error) {
            done(error)
          })
      } else {
        sendTestMessageFromAliceToBob()
      }
    })

    // allocate relaying session for alice
    clientAlice.allocateP()
      .then(function (allocateAddress) {
        srflxAddressAlice = allocateAddress.mappedAddress
        relayAddressAlice = allocateAddress.relayedAddress
        console.log("alice's srflx address = " + srflxAddressAlice.address + ':' + srflxAddressAlice.port)
        console.log("alice's relay address = " + relayAddressAlice.address + ':' + relayAddressAlice.port)
        // allocate relaying session for bob
        return clientBob.allocateP()
      })
      .then(function (allocateAddress) {
        srflxAddressBob = allocateAddress.mappedAddress
        relayAddressBob = allocateAddress.relayedAddress
        console.log("bob's address = " + srflxAddressBob.address + ':' + srflxAddressBob.port)
        console.log("bob's relay address = " + relayAddressBob.address + ':' + relayAddressBob.port)
        // create permission for alice to send messages to bob
        return clientBob.createPermissionP(relayAddressAlice.address)
      })
      .then(function () {
        // create channel from alice to bob
        return clientAlice.bindChannelP(relayAddressBob.address, relayAddressBob.port)
      })
      .then(function (channel) {
        expect(channel).not.to.be.undefined
        channelId = channel
        //  create permission for bob to send messages to alice
        return clientAlice.createPermissionP(relayAddressBob.address)
      })
      .then(function () {
        // create channel from bob to alice
        return clientBob.bindChannelP(relayAddressAlice.address, relayAddressAlice.port)
      })
      .then(function (anotherChannel) {
        // send test message
        sendTestMessageFromAliceToBob()
      })
      .catch(function (error) {
        done(error)
      })
  })
})
<|endoftext|>