dnssd
Version:
Bonjour/Avahi-like service discovery in pure JavaScript
384 lines (288 loc) • 10.8 kB
JavaScript
const chai = require('chai');
const expect = chai.expect;
const rewire = require('rewire');
const sinon = require('sinon');
const sinonChai = require('sinon-chai');
chai.use(sinonChai);
const dir = process['test-dir'] || '../../src';
const ServiceType = rewire(dir + '/ServiceType');
describe('ServiceType', function() {
describe('#constructor()', function() {
it('should call this._fromString() on string', sinon.test(function() {
this.stub(ServiceType.prototype, '_fromString');
this.stub(ServiceType.prototype, '_validate');
const type = new ServiceType('string');
expect(type._fromString).to.have.been.called;
}));
it('should call this._fromArray() on array', sinon.test(function() {
this.stub(ServiceType.prototype, '_fromArray');
this.stub(ServiceType.prototype, '_validate');
const type = new ServiceType([]);
expect(type._fromArray).to.have.been.called;
}));
it('should call this._fromObj() on object', sinon.test(function() {
this.stub(ServiceType.prototype, '_fromObj');
this.stub(ServiceType.prototype, '_validate');
const type = new ServiceType({});
expect(type._fromObj).to.have.been.called;
}));
it('should convert multiple args to array form', sinon.test(function() {
this.stub(ServiceType.prototype, '_fromArray');
this.stub(ServiceType.prototype, '_validate');
const type = new ServiceType('_http', '_tcp', 'sub1', 'sub2');
const expected = ['_http', '_tcp', 'sub1', 'sub2'];
expect(type._fromArray).to.have.been.calledWithMatch(expected);
}));
it('should throw an error for any other input type', sinon.test(function() {
expect(() => new ServiceType(99)).to.throw();
}));
});
describe('#_fromString()', function() {
it('should parse names without subtypes', function() {
const input = '_http._tcp';
const results = {};
ServiceType.prototype._fromString.call(results, input);
const expected = {
name: '_http',
protocol: '_tcp',
subtypes: [],
};
expect(results).to.eql(expected);
});
it('should parse names with subtypes', function() {
const input = '_http._tcp,sub1,sub2';
const results = {};
ServiceType.prototype._fromString.call(results, input);
const expected = {
name: '_http',
protocol: '_tcp',
subtypes: ['sub1', 'sub2'],
};
expect(results).to.eql(expected);
});
it('should trim off weird commas/whitespace', function() {
const input = ' _http._tcp ,sub1,sub2, ';
const results = {};
ServiceType.prototype._fromString.call(results, input);
const expected = {
name: '_http',
protocol: '_tcp',
subtypes: ['sub1', 'sub2'],
};
expect(results).to.eql(expected);
});
it('should handle service enumerator string', function() {
const input = '_services._dns-sd._udp';
const results = {};
ServiceType.prototype._fromString.call(results, input);
const expected = {
name: '_services._dns-sd',
protocol: '_udp',
subtypes: [],
};
expect(results).to.eql(expected);
});
});
describe('#_fromArray()', function() {
it('should handle nested array', function() {
const input = ['_http', '_tcp', ['sub1', 'sub2']];
const results = {_fromObj: sinon.stub()};
ServiceType.prototype._fromArray.call(results, input);
const expected = {
name: '_http',
protocol: '_tcp',
subtypes: ['sub1', 'sub2'],
};
expect(results._fromObj).to.have.been.calledWithMatch(expected);
});
it('should handle flat array too', function() {
const input = ['_http', '_tcp', 'sub1', 'sub2'];
const results = {_fromObj: sinon.stub()};
ServiceType.prototype._fromArray.call(results, input);
const expected = {
name: '_http',
protocol: '_tcp',
subtypes: ['sub1', 'sub2'],
};
expect(results._fromObj).to.have.been.calledWithMatch(expected);
});
});
describe('#_fromObj()', function() {
it('should cast subtypes to array', function() {
const results = {};
const input = {
name: '_http',
protocol: '_tcp',
subtypes: 'sub1',
};
ServiceType.prototype._fromObj.call(results, input);
const expected = {
name: '_http',
protocol: '_tcp',
subtypes: ['sub1'],
};
expect(results).to.eql(expected);
});
it('should use name, protocol, subs and ignore other properties', function() {
const results = {};
const input = {
name: '_http',
protocol: '_tcp',
subtypes: ['sub1'],
ignore: 'ok',
};
ServiceType.prototype._fromObj.call(results, input);
const expected = {
name: '_http',
protocol: '_tcp',
subtypes: ['sub1'],
};
expect(results).to.eql(expected);
});
});
describe('#_validate()', function() {
const stub = {
serviceName: sinon.stub(),
protocol : sinon.stub(),
label : sinon.stub(),
};
let revert;
before(function() {
revert = ServiceType.__set__('validate', stub);
});
after(function() {
revert();
});
beforeEach(function() {
stub.serviceName.reset();
stub.protocol.reset();
stub.label.reset();
});
it('should throw error if name is missing / is not a string', function() {
const input_1 = {name: 4};
const input_2 = {name: ''};
expect(ServiceType.prototype._validate.bind(input_1)).to.throw(Error);
expect(ServiceType.prototype._validate.bind(input_2)).to.throw(Error);
});
it('should throw error if protocol is missing / is not a string', function() {
const input_1 = {name: '_http', protocol: 4};
const input_2 = {name: '_http', protocol: ''};
expect(ServiceType.prototype._validate.bind(input_1)).to.throw(Error);
expect(ServiceType.prototype._validate.bind(input_2)).to.throw(Error);
});
it('should be forgiving about underscores in name/protocol', function() {
const context = {
name: 'http',
protocol: 'tcp',
subtypes: [],
};
ServiceType.prototype._validate.call(context);
expect(context.name).to.equal('_http');
expect(context.protocol).to.equal('_tcp');
});
it('should run validation on name, protocol, and subtypes', function() {
const context = {
name: '_http',
protocol: '_tcp',
subtypes: [],
};
ServiceType.prototype._validate.call(context);
expect(stub.serviceName).to.have.been.called;
expect(stub.protocol).to.have.been.called;
expect(stub.label).to.have.callCount(context.subtypes.length);
});
it('should *not* run validation on service enumerator types', function() {
const context = {
name: '_services._dns-sd',
protocol: '_udp',
subtypes: ['sub1', 'sub2'],
};
ServiceType.prototype._validate.call(context);
expect(context.subtypes).to.be.empty;
expect(context.isEnumerator).to.be.true;
expect(stub.serviceName).to.not.have.been.called;
});
});
describe('#toString()', function() {
it('should spit out a valid string without subtypes', function() {
const context = {
name: '_http',
protocol: '_tcp',
subtypes: [],
};
const result = ServiceType.prototype.toString.call(context);
expect(result).to.equal('_http._tcp');
});
it('should spit out a valid string with subtypes', function() {
const context = {
name: '_http',
protocol: '_tcp',
subtypes: ['sub1', 'sub2'],
};
const result = ServiceType.prototype.toString.call(context);
expect(result).to.equal('_http._tcp,sub1,sub2');
});
});
describe('::tcp()', function() {
it('should throw error if service is missing / is not a string', function() {
expect(ServiceType.tcp.bind(null)).to.throw(Error);
expect(ServiceType.tcp.bind(null, '')).to.throw(Error);
});
it('should return a correct tcp ServiceType', function() {
// single string
expect(ServiceType.tcp('_http'))
.to.be.an.instanceof(ServiceType)
.to.include({name: '_http', protocol: '_tcp'});
// name and subtype
expect(ServiceType.tcp('_http', 'sub1'))
.to.be.an.instanceof(ServiceType)
.to.include({name: '_http', protocol: '_tcp'})
.to.property('subtypes').eql(['sub1']);
// name and subtypes in array
expect(ServiceType.tcp('_http', ['sub1', 'sub2']))
.to.be.an.instanceof(ServiceType)
.to.include({name: '_http', protocol: '_tcp'})
.to.property('subtypes').eql(['sub1', 'sub2']);
// name and subtypes
expect(ServiceType.tcp('_http', 'sub1', 'sub2'))
.to.be.an.instanceof(ServiceType)
.to.include({name: '_http', protocol: '_tcp'})
.to.property('subtypes').eql(['sub1', 'sub2']);
});
});
describe('::udp()', function() {
it('should throw error if service is missing / is not a string', function() {
expect(ServiceType.udp.bind(null)).to.throw(Error);
expect(ServiceType.udp.bind(null, '')).to.throw(Error);
});
it('should return a correct udp ServiceType', function() {
// single string
expect(ServiceType.udp('_sleep-proxy'))
.to.be.an.instanceof(ServiceType)
.to.include({name: '_sleep-proxy', protocol: '_udp'});
// name and subtype
expect(ServiceType.udp('_sleep-proxy', 'sub1'))
.to.be.an.instanceof(ServiceType)
.to.include({name: '_sleep-proxy', protocol: '_udp'})
.to.property('subtypes').eql(['sub1']);
// name and subtypes in array
expect(ServiceType.udp('_sleep-proxy', ['sub1', 'sub2']))
.to.be.an.instanceof(ServiceType)
.to.include({name: '_sleep-proxy', protocol: '_udp'})
.to.property('subtypes').eql(['sub1', 'sub2']);
// name and subtypes
expect(ServiceType.udp('_sleep-proxy', 'sub1', 'sub2'))
.to.be.an.instanceof(ServiceType)
.to.include({name: '_sleep-proxy', protocol: '_udp'})
.to.property('subtypes').eql(['sub1', 'sub2']);
});
});
describe('::all()', function() {
it('should return a correct enumerator ServiceType', function() {
expect(ServiceType.all())
.to.be.an.instanceof(ServiceType)
.to.include({name: '_services._dns-sd', protocol: '_udp'})
.to.have.property('isEnumerator', true);
});
});
});