diff --git a/src/lib/dbus.ts b/src/lib/dbus.ts index aa1e887f..9fe1dee5 100644 --- a/src/lib/dbus.ts +++ b/src/lib/dbus.ts @@ -1,15 +1,27 @@ import { getBus, Error as DBusError } from 'dbus'; import { promisify } from 'util'; import { TypedError } from 'typed-error'; +import * as _ from 'lodash'; import log from './supervisor-console'; +import DBus = require('dbus'); export class DbusError extends TypedError {} -const bus = getBus('system'); -const getInterfaceAsync = promisify(bus.getInterface.bind(bus)); +let bus: DBus.DBusConnection; +let getInterfaceAsync: ( + serviceName: string, + objectPath: string, + ifaceName: string, +) => Promise>; + +export const initialized = _.once(async () => { + bus = getBus('system'); + getInterfaceAsync = promisify(bus.getInterface.bind(bus)); +}); async function getSystemdInterface() { + await initialized(); try { return await getInterfaceAsync( 'org.freedesktop.systemd1', @@ -21,7 +33,8 @@ async function getSystemdInterface() { } } -export async function getLoginManagerInterface() { +async function getLoginManagerInterface() { + await initialized(); try { return await getInterfaceAsync( 'org.freedesktop.login1', diff --git a/test/legacy/.mocharc.js b/test/legacy/.mocharc.js index 0d987580..6a4ee1c4 100644 --- a/test/legacy/.mocharc.js +++ b/test/legacy/.mocharc.js @@ -8,6 +8,7 @@ module.exports = { 'tsconfig-paths/register', 'test/lib/chai.ts', 'test/legacy/fixtures.ts', + 'test/lib/legacy-mocha-hooks.ts' ], spec: ['test/legacy/**/*.spec.ts'], timeout: '30000', diff --git a/test/legacy/fixtures.ts b/test/legacy/fixtures.ts index e1389438..09bbbed0 100644 --- a/test/legacy/fixtures.ts +++ b/test/legacy/fixtures.ts @@ -31,6 +31,5 @@ fs.writeFileSync( fs.readFileSync('./test/data/testconfig.json'), ); -import '~/test-lib/mocked-dbus'; import '~/test-lib/mocked-dockerode'; import '~/test-lib/mocked-iptables'; diff --git a/test/lib/legacy-mocha-hooks.ts b/test/lib/legacy-mocha-hooks.ts new file mode 100644 index 00000000..21fbda83 --- /dev/null +++ b/test/lib/legacy-mocha-hooks.ts @@ -0,0 +1,70 @@ +// TODO: Remove this file when all legacy tests have migrated to unit/integration. + +import { stub, SinonStub } from 'sinon'; +import * as dbus from 'dbus'; +import { Error as DBusError, DBusInterface } from 'dbus'; +import { initialized } from '~/src/lib/dbus'; + +let getBusStub: SinonStub; + +export const mochaHooks = { + async beforeAll() { + getBusStub = stub(dbus, 'getBus').returns({ + getInterface: ( + serviceName: string, + _objectPath: string, + _interfaceName: string, + interfaceCb: (err: null | DBusError, iface: DBusInterface) => void, + ) => { + if (/systemd/.test(serviceName)) { + interfaceCb(null, { + StartUnit: () => { + // noop + }, + RestartUnit: () => { + // noop + }, + StopUnit: () => { + // noop + }, + EnableUnitFiles: () => { + // noop + }, + DisableUnitFiles: () => { + // noop + }, + GetUnit: ( + _unitName: string, + getUnitCb: (err: null | Error, unitPath: string) => void, + ) => { + getUnitCb(null, 'this is the unit path'); + }, + Get: ( + _unitName: string, + _property: string, + getCb: (err: null | Error, value: unknown) => void, + ) => { + getCb(null, 'this is the value'); + }, + } as any); + } else { + interfaceCb(null, { + Reboot: () => { + // noop + }, + PowerOff: () => { + // noop + }, + } as any); + } + }, + } as dbus.DBusConnection); + + // Initialize dbus module before any tests are run so any further tests + // that interface with lib/dbus use the stubbed busses above. + await initialized(); + }, + afterAll() { + getBusStub.restore(); + }, +}; diff --git a/test/lib/mocked-dbus.ts b/test/lib/mocked-dbus.ts deleted file mode 100644 index ae9496f8..00000000 --- a/test/lib/mocked-dbus.ts +++ /dev/null @@ -1,67 +0,0 @@ -import * as dbus from 'dbus'; -import { Error as DBusError, DBusInterface } from 'dbus'; -import { stub } from 'sinon'; - -/** - * Because lib/dbus invokes dbus.getBus on module import, - * getBus needs to be stubbed at the root level due how JS - * `require` works. lib/dbus interfaces with the systemd and - * logind interfaces, which expose the unit methods below. - * - * There should be no need to un-stub dbus.getBus at any point - * during testing, since we never want to interact with the actual - * dbus system socket in the test environment. - * - * To test interaction with lib/dbus, import lib/dbus into the test suite - * and stub the necessary methods, as you would with any other module. - */ -stub(dbus, 'getBus').returns({ - getInterface: ( - serviceName: string, - _objectPath: string, - _interfaceName: string, - interfaceCb: (err: null | DBusError, iface: DBusInterface) => void, - ) => { - if (/systemd/.test(serviceName)) { - interfaceCb(null, { - StartUnit: () => { - // noop - }, - RestartUnit: () => { - // noop - }, - StopUnit: () => { - // noop - }, - EnableUnitFiles: () => { - // noop - }, - DisableUnitFiles: () => { - // noop - }, - GetUnit: ( - _unitName: string, - getUnitCb: (err: null | Error, unitPath: string) => void, - ) => { - getUnitCb(null, 'this is the unit path'); - }, - Get: ( - _unitName: string, - _property: string, - getCb: (err: null | Error, value: unknown) => void, - ) => { - getCb(null, 'this is the value'); - }, - } as any); - } else { - interfaceCb(null, { - Reboot: () => { - // noop - }, - PowerOff: () => { - // noop - }, - } as any); - } - }, -} as dbus.DBusConnection);