UNPKG

winsc

Version:

Windows Service Controller for NodeJs. Install/uninstall, start/stop and verify existence of a Windows service. Print out the current status of the service and list all services available on local machine.

506 lines (370 loc) 13.8 kB
const Rewire = require('rewire'); const Sinon = require('sinon'); const test = require('ava'); const WinSC = Rewire('../src/winsc.js'); const RealService = 'QWAVE'; const FakeService = 'imaginary-service'; const TestService = 'TestService'; const TestServicePath = '.\\sample\\SampleWindowsService\\SampleWindowsService\\bin\\Debug\\SampleWindowsService.exe' let baseExec = WinSC.__get__('exec'); // NOTE: these tests assumes you have the QWAVE // service on your windows test machine, // while this is a default service it is not guaranteed // to be present test.before(async (t) => { await WinSC.exists(TestService).then(async (exists) => { if (exists) { await WinSC.uninstall(TestService); } }); }); test.after(async (t) => { await WinSC.exists(TestService).then(async (exists) => { if (exists) { await WinSC.uninstall(TestService); } }); }); test.serial('all returns a list of all serivices registerd on local machine', async (t) => { await WinSC.all().then((serviceList) => { t.not(serviceList, null); t.truthy(Array.isArray(serviceList)); }); }); test.serial('all rejects if the call to sc.exe through exec throws an error', async (t) => { WinSC.__set__('exec', Sinon.stub().throws()); await WinSC.all().catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); }); test.serial('exists returns true if the service is registered in windows', async (t) => { await WinSC.exists(RealService).then((exists) => { t.truthy(exists); }); }); test.serial('exists returns false if the service is not registered in windows', async (t) => { await WinSC.exists(FakeService).then((exists) => { t.falsy(exists); }); }); test.serial('exists rejects if the service name provided is not valid', async (t) => { await WinSC.exists(null).catch((err) => { t.is(err.message, 'Service name is invalid'); }); }); test.serial('exists rejects if the call to sc.exe through exec throws an error', async (t) => { WinSC.__set__('exec', Sinon.stub().throws()); await WinSC.exists(RealService).catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); }); test.serial('status returns a string value for an existing service', async (t) => { await WinSC.status(RealService).then((status) => { t.not(status, null); t.regex(status, /^(RUNNING|STOPPED)$/); }); }); test.serial('status rejects if the service name does not exist', async (t) => { await WinSC.status(FakeService).catch((err) => { t.not(err, null); t.is(err, 'Service with name \'imaginary-service\' does not exists'); }); }); test.serial('status rejects if the call to sc.exe through exec throws an error', async (t) => { let testErr = new Error('Error'); WinSC.__set__('exec', Sinon.stub().throws()); await WinSC.status(RealService).catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); WinSC.__set__('exec', Sinon.stub().yields(testErr)); await WinSC.status(RealService).catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); }); test.serial('status rejects if an invalid service name is provided', async (t) => { await WinSC.status(null).catch((err) => { t.is(err.message, 'Service name is invalid'); }); }); test.serial('details returns an object with details on the service', async (t) => { await WinSC.details(RealService).then((details) => { t.not(details.name, null); t.is(typeof(details.name), 'string'); t.not(details.displayName, null); t.is(typeof(details.displayName), 'string'); t.not(details.startType, null); t.is(typeof(details.startType), 'string'); t.not(details.exePath, null); t.is(typeof(details.exePath), 'string'); t.not(details.dependencies, null); t.truthy(Array.isArray(details.dependencies)); }); }); test.serial('details rejects if the service name does not exist', async (t) => { await WinSC.details(FakeService).catch((err) => { t.not(err, null); t.is(err, 'Service with name \'imaginary-service\' does not exists'); }); }); test.serial('details rejects if an invalid service name is provided', async (t) => { await WinSC.details(null).catch((err) => { t.is(err.message, 'Service name is invalid'); }); }); test.serial('details rejects if the call to sc.exe through exec throws an error', async (t) => { let testErr = new Error('Error'); WinSC.__set__('exec', Sinon.stub().throws()); await WinSC.details(RealService).catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); WinSC.__set__('exec', Sinon.stub().yields(testErr)); await WinSC.details(RealService).catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); }); test.serial('startup returns true if the service exists and can be configurted', async (t) => { await WinSC.details(RealService).then(async (details) => { if (details.startType === 'Automatic') { await WinSC.startup(RealService, 'Manual'); } }); await WinSC.startup(RealService, 'Automatic').then((configured) => { t.truthy(configured); }).catch((err) => { console.error(err); t.fail(); }); await WinSC.startup(RealService, 'Disabled').then((configured) => { t.truthy(configured); }).catch((err) => { console.error(err); t.fail(); }); await WinSC.startup(RealService, 'Manual').then((configured) => { t.truthy(configured); }).catch((err) => { console.error(err); t.fail(); }); }); test.serial('startup returns true and sets the service to manual if no appropriate startup type name is passed', async (t) => { await WinSC.details(RealService).then(async (details) => { if (details.startType === 'Manual') { await WinSC.startup(RealService, 'Automatic'); } }); await WinSC.startup(RealService, null).then(async (configured) => { t.truthy(configured); await WinSC.details(RealService).then((details) => { t.is(details.startType, 'Manual'); }); }).catch((err) => { console.error(err); t.fail(); }); }); test.serial('startup rejects if the service name does not exist', async (t) => { await WinSC.startup(FakeService, 'Automatic').catch((err) => { t.not(err, null); t.is(err, 'Service with name \'imaginary-service\' does not exists'); }); }); test.serial('startup rejects if an invalid service name is provided', async (t) => { await WinSC.startup(null, 'Automatic').catch((err) => { t.is(err.message, 'Service name is invalid'); }); }); test.serial('startup rejects if the call to sc.exe through exec throws an error', async (t) => { let testErr = new Error('Error'); WinSC.__set__('exec', Sinon.stub().throws()); await WinSC.startup(FakeService, 'Automatic').catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); WinSC.__set__('exec', Sinon.stub().yields([testErr, 'huh'])); await WinSC.startup(TestService, 'Automatic').catch((err) => { t.is(err[1], 'huh'); }); WinSC.__set__('exec', baseExec); }); test.serial('start returns true if the service exists and can be started', async (t) => { await WinSC.status(RealService).then(async (status) => { if (status === 'RUNNING') { await WinSC.stop(RealService); } }); await WinSC.start(RealService).then((status) => { t.truthy(status); }).catch((err) => { console.error(err); t.fail(); }); }); test.serial('start rejects if the service name does not exist', async (t) => { await WinSC.start(FakeService).catch((err) => { t.not(err, null); t.is(err, 'Service with name \'imaginary-service\' does not exists'); }); }); test.serial('start rejects if an invalid service name is provided', async (t) => { await WinSC.start(null).catch((err) => { t.is(err.message, 'Service name is invalid'); }); }); test.serial('start rejects if the call to sc.exe through exec throws an error', async (t) => { let testErr = new Error('Error'); WinSC.__set__('exec', Sinon.stub().throws()); await WinSC.start(FakeService).catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); WinSC.__set__('exec', Sinon.stub().yields([testErr, 'huh'])); await WinSC.start(TestService).catch((err) => { t.is(err[1], 'huh'); }); WinSC.__set__('exec', baseExec); }); test.serial('stop returns true if the service exists and can be stopped', async (t) => { await WinSC.status(RealService).then(async (status) => { if (status === 'STOPPED') { await WinSC.start(RealService); } }); await WinSC.stop(RealService).then((status) => { t.truthy(status); }).catch((err) => { console.error(err); t.fail(); }); }); test.serial('stop rejects if the service name does not exist', async (t) => { await WinSC.stop(FakeService).catch((err) => { t.not(err, null); t.is(err, 'Service with name \'imaginary-service\' does not exists'); }); }); test.serial('stop rejects if an invalid service name is provided', async (t) => { await WinSC.stop(null).catch((err) => { t.is(err.message, 'Service name is invalid'); }); }); test.serial('stop rejects if the call to sc.exe through exec throws an error', async (t) => { let testErr = new Error('Error'); WinSC.__set__('exec', Sinon.stub().throws()); await WinSC.stop(FakeService).catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); WinSC.__set__('exec', Sinon.stub().yields([testErr, 'huh'])); await WinSC.stop(TestService).catch((err) => { t.is(err[1], 'huh'); }); WinSC.__set__('exec', baseExec); }); // NOTE: the install and uninstall tests assume that you have // built the provided sample service and have it in the // default location. test.serial('install installs the service and registers it with windows', async (t) => { await WinSC.exists(TestService).then((exists) => { t.falsy(exists); }); await WinSC.install(TestService, TestService, TestServicePath).then((installed) => { t.truthy(installed); }); await WinSC.exists(TestService).then((exists) => { t.truthy(exists); }); }); test.serial('install rejects if the service name is missing or invalid', async (t) => { await WinSC.exists(TestService).then(async (exists) => { if (exists) { await WinSC.uninstall(TestService); } }); await WinSC.install(null, TestService, TestServicePath).catch((err) => { t.is(err.message, 'Service name is invalid'); }); }); test.serial('install uses the service name for the service display name if none is provided', async (t) => { t.plan(4); await WinSC.exists(TestService).then((exists) => { t.falsy(exists); }); await WinSC.install(TestService, null, TestServicePath).then((installed) => { t.truthy(installed); }); await WinSC.exists(TestService).then((exists) => { t.truthy(exists); }); await WinSC.uninstall(TestService).then((removed) => { t.truthy(removed); }); }); test.serial('install rejects if the service path is missing or invalid', async (t) => { await WinSC.exists(TestService).then((exists) => { t.falsy(exists); }); await WinSC.install(TestService, TestService, null).catch((err) => { t.is(err.message, 'Executable file path is invalid'); }); }); test.serial('install rejects if the service already exists', async (t) => { await WinSC.install(RealService, RealService, TestServicePath).catch((err) => { t.is(err, 'Service with name \'QWAVE\' already exists'); }); }); test.serial('install rejects if the call to sc.exe through exec throws an error', async (t) => { let testErr = new Error('Error'); WinSC.__set__('exec', Sinon.stub().throws()); await WinSC.install(TestService, TestService, TestServicePath).catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); WinSC.__set__('exec', Sinon.stub().yields([testErr, 'huh'])); await WinSC.install(TestService, TestService, TestServicePath).catch((err) => { t.is(err[1], 'huh'); }); WinSC.__set__('exec', baseExec); }); test.serial('uninstall uninstalls the service and de-registers it with windows if it exists', async (t) => { await WinSC.exists(TestService).then(async (exists) => { if (!exists) { await WinSC.install(TestService, TestService, TestServicePath); } }); await WinSC.uninstall(TestService).then((removed) => { t.truthy(removed); }); await WinSC.exists(TestService).then((exists) => { t.falsy(exists); }); }); test.serial('uninstall rejects if the service name does not exist', async (t) => { await WinSC.uninstall(FakeService).catch((err) => { t.is(err, 'Service with name \'imaginary-service\' does not exists'); }); }) test.serial('uninstall rejects if the call to sc.exe through exec throws an error', async (t) => { let testErr = new Error('Error'); WinSC.__set__('exec', Sinon.stub().throws()); await WinSC.uninstall(TestService).catch((err) => { t.is(err.message, 'Error'); }); WinSC.__set__('exec', baseExec); WinSC.__set__('exec', Sinon.stub().yields([testErr, 'huh'])); await WinSC.uninstall(TestService).catch((err) => { t.is(err[1], 'huh'); }); WinSC.__set__('exec', baseExec); }); test.serial('uninstall rejects if the service name is invalid', async (t) => { await WinSC.uninstall(null).catch((err) => { t.is(err.message, 'Service name is invalid'); }); });