diff --git a/src/device-api/actions.ts b/src/device-api/actions.ts index a947d708..41a40277 100644 --- a/src/device-api/actions.ts +++ b/src/device-api/actions.ts @@ -1,4 +1,6 @@ +import * as eventTracker from '../event-tracker'; import log from '../lib/supervisor-console'; +import blink = require('../lib/blink'); /** * Run an array of healthchecks, outputting whether all passed or not @@ -22,3 +24,15 @@ export const runHealthchecks = async ( return true; }; + +/** + * Identify a device by blinking or some other method, if supported + * Used by: + * - POST /v1/blink + */ +const DEFAULT_IDENTIFY_DURATION = 15000; +export const identify = (ms: number = DEFAULT_IDENTIFY_DURATION) => { + eventTracker.track('Device blink'); + blink.pattern.start(); + setTimeout(blink.pattern.stop, ms); +}; diff --git a/src/device-api/index.ts b/src/device-api/index.ts index f12f3529..189531c0 100644 --- a/src/device-api/index.ts +++ b/src/device-api/index.ts @@ -4,10 +4,8 @@ import * as _ from 'lodash'; import * as middleware from './middleware'; import * as apiKeys from './api-keys'; import * as actions from './actions'; -import * as eventTracker from '../event-tracker'; import { reportCurrentState } from '../device-state'; import proxyvisor from '../proxyvisor'; -import blink = require('../lib/blink'); import log from '../lib/supervisor-console'; import type { Server } from 'http'; @@ -57,9 +55,7 @@ export class SupervisorAPI { this.api.use(middleware.auth); this.api.post('/v1/blink', (_req, res) => { - eventTracker.track('Device blink'); - blink.pattern.start(); - setTimeout(blink.pattern.stop, 15000); + actions.identify(); return res.sendStatus(200); }); diff --git a/test/integration/device-api/v1.spec.ts b/test/integration/device-api/v1.spec.ts index 7d6a42fc..a8a9be47 100644 --- a/test/integration/device-api/v1.spec.ts +++ b/test/integration/device-api/v1.spec.ts @@ -50,4 +50,17 @@ describe('device-api/v1', () => { await request(api).get('/v1/healthy').expect(500); }); }); + + describe('POST /v1/blink', () => { + // Actions are tested elsewhere so we can stub the dependency here + before(() => stub(actions, 'identify')); + after(() => (actions.identify as SinonStub).restore()); + + it('responds with 200', async () => { + await request(api) + .post('/v1/blink') + .set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) + .expect(200); + }); + }); }); diff --git a/test/legacy/41-device-api-v1.spec.ts b/test/legacy/41-device-api-v1.spec.ts index 10feb6f2..be11a6a3 100644 --- a/test/legacy/41-device-api-v1.spec.ts +++ b/test/legacy/41-device-api-v1.spec.ts @@ -1,13 +1,6 @@ import * as _ from 'lodash'; import { expect } from 'chai'; -import { - stub, - spy, - useFakeTimers, - SinonStub, - SinonSpy, - SinonFakeTimers, -} from 'sinon'; +import { stub, spy, SinonStub, SinonSpy } from 'sinon'; import * as supertest from 'supertest'; import * as path from 'path'; import { promises as fs } from 'fs'; @@ -28,7 +21,6 @@ import * as dbus from '~/lib/dbus'; import * as updateLock from '~/lib/update-lock'; import * as TargetState from '~/src/device-state/target-state'; import * as targetStateCache from '~/src/device-state/target-state-cache'; -import blink = require('~/lib/blink'); import constants = require('~/lib/constants'); import * as deviceAPIActions from '~/src/device-api/common'; import { UpdatesLockedError } from '~/lib/errors'; @@ -680,45 +672,6 @@ describe('SupervisorAPI [V1 Endpoints]', () => { }); }); - describe('POST /v1/blink', () => { - // Further blink function-specific testing located in 07-blink.spec.ts - it('responds with code 200 and empty body', async () => { - await request - .post('/v1/blink') - .set('Accept', 'application/json') - .set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) - .expect(sampleResponses.V1.POST['/blink'].statusCode) - .then((response) => { - expect(response.body).to.deep.equal( - sampleResponses.V1.POST['/blink'].body, - ); - expect(response.text).to.deep.equal( - sampleResponses.V1.POST['/blink'].text, - ); - }); - }); - - it('directs device to blink for 15000ms (hardcoded length)', async () => { - const blinkStartSpy: SinonSpy = spy(blink.pattern, 'start'); - const blinkStopSpy: SinonSpy = spy(blink.pattern, 'stop'); - const clock: SinonFakeTimers = useFakeTimers(); - - await request - .post('/v1/blink') - .set('Accept', 'application/json') - .set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) - .then(() => { - expect(blinkStartSpy.callCount).to.equal(1); - clock.tick(15000); - expect(blinkStopSpy.callCount).to.equal(1); - }); - - blinkStartSpy.restore(); - blinkStopSpy.restore(); - clock.restore(); - }); - }); - describe('POST /v1/regenerate-api-key', () => { it('returns a valid new API key', async () => { const refreshKeySpy: SinonSpy = spy(apiKeys, 'refreshKey'); diff --git a/test/unit/device-api/actions.spec.ts b/test/unit/device-api/actions.spec.ts index 20f1d8a4..93e681b8 100644 --- a/test/unit/device-api/actions.spec.ts +++ b/test/unit/device-api/actions.spec.ts @@ -1,6 +1,8 @@ import { expect } from 'chai'; +import { spy, useFakeTimers } from 'sinon'; import * as actions from '~/src/device-api/actions'; +import blink = require('~/lib/blink'); describe('device-api/actions', () => { describe('runs healthchecks', () => { @@ -22,4 +24,23 @@ describe('device-api/actions', () => { expect(await actions.runHealthchecks([taskError, taskError])).to.be.false; }); }); + + describe('identifies device', () => { + // This suite doesn't test that the blink submodule writes to the correct + // led file location on host. That test should be part of the blink module. + it('directs device to blink for set duration', async () => { + const blinkStartSpy = spy(blink.pattern, 'start'); + const blinkStopSpy = spy(blink.pattern, 'stop'); + const clock = useFakeTimers(); + + actions.identify(); + expect(blinkStartSpy.callCount).to.equal(1); + clock.tick(15000); + expect(blinkStopSpy.callCount).to.equal(1); + + blinkStartSpy.restore(); + blinkStopSpy.restore(); + clock.restore(); + }); + }); });