diff --git a/lib/poller.test.js b/lib/poller.test.js index 18f5579a..29916813 100644 --- a/lib/poller.test.js +++ b/lib/poller.test.js @@ -26,6 +26,7 @@ describe('Poller', () => { loggerMock = sinon.mock() loggerMock.info = sinon.stub() + loggerMock.debug = sinon.stub() loggerMock.error = sinon.stub() pollerFactory = (secretDescriptor = { @@ -53,10 +54,19 @@ describe('Poller', () => { }) describe('_createSecretManifest', () => { + let clock + beforeEach(() => { + clock = sinon.useFakeTimers({ + now: Date.now() + }) backendMock.getSecretManifestData = sinon.stub() }) + afterEach(() => { + clock.restore() + }) + it('creates secret manifest', async () => { const poller = pollerFactory({ backendType: 'fakeBackendType', @@ -90,7 +100,10 @@ describe('Poller', () => { kind: 'Secret', metadata: { name: 'fakeSecretName', - ownerReferences: [ownerReference] + ownerReferences: [ownerReference], + annotations: { + 'externalsecret.kubernetes-client.io/last-poll': `${Date.now()}` + } }, type: 'Opaque', data: { @@ -110,6 +123,7 @@ describe('Poller', () => { properties: ['fakePropertyName1', 'fakePropertyName2'] }) poller._upsertKubernetesSecret = sinon.stub() + poller._setNextPoll = sinon.stub() }) it('polls secrets', async () => { @@ -119,6 +133,7 @@ describe('Poller', () => { expect(loggerMock.info.calledWith('running poll')).to.equal(true) expect(poller._upsertKubernetesSecret.calledWith()).to.equal(true) + expect(poller._setNextPoll.calledWith()).to.equal(true) }) it('logs error if storing secret operation fails', async () => { @@ -130,6 +145,112 @@ describe('Poller', () => { }) }) + describe('_checkForSecret', () => { + let kubeNamespaceMock + let poller + let clock + + beforeEach(() => { + poller = pollerFactory({ + backendType: 'fakeBackendType', + name: 'fakeSecretName', + properties: ['fakePropertyName'] + }) + clock = sinon.useFakeTimers({ + now: Date.now() + }) + kubeNamespaceMock = sinon.mock() + kubeNamespaceMock.secrets = sinon.stub().returns(kubeNamespaceMock) + kubeClientMock.api = sinon.mock() + kubeClientMock.api.v1 = sinon.mock() + kubeClientMock.api.v1.namespaces = sinon.stub().returns(kubeNamespaceMock) + poller._setNextPoll = sinon.stub() + poller._poll = sinon.stub() + }) + + afterEach(() => { + clock.restore() + }) + + it('existing secret - no last poll', async () => { + kubeNamespaceMock.get = sinon.stub().resolves({ + body: { + apiVersion: 'v1', + kind: 'Secret', + metadata: { + name: 'fakeSecretName' + }, + type: 'Opaque', + data: { + fakePropertyName: 'ZmFrZVByb3BlcnR5VmFsdWU=' + } + } + }) + + await poller._checkForSecret() + + expect(kubeNamespaceMock.secrets.calledWith('fakeSecretName')).to.equal(true) + expect(poller._setNextPoll.calledWith(0)).to.equal(true) + }) + + describe('with last poll', () => { + beforeEach(() => { + kubeNamespaceMock.get = sinon.stub().resolves({ + body: { + apiVersion: 'v1', + kind: 'Secret', + metadata: { + name: 'fakeSecretName', + annotations: { + 'externalsecret.kubernetes-client.io/last-poll': `${Date.now()}` + } + }, + type: 'Opaque', + data: { + fakePropertyName: 'ZmFrZVByb3BlcnR5VmFsdWU=' + } + } + }) + }) + + it('time remaining until next poll', async () => { + const elapsedTime = poller._intervalMilliseconds / 2 + clock.tick(elapsedTime) + + await poller._checkForSecret() + + expect(kubeNamespaceMock.secrets.calledWith('fakeSecretName')).to.equal(true) + expect(poller._setNextPoll.calledWith(elapsedTime)).to.equal(true) + }) + + it('ready for next poll', async () => { + clock.tick(poller._intervalMilliseconds * 2) // greater than poller._intervalMilliseconds + + await poller._checkForSecret() + + expect(kubeNamespaceMock.secrets.calledWith('fakeSecretName')).to.equal(true) + expect(poller._setNextPoll.calledWith(0)).to.equal(true) + }) + }) + + it('not existing secret', async () => { + kubeNamespaceMock.get = sinon.stub().throws({ statusCode: 404 }) + + await poller._checkForSecret() + + expect(poller._poll.calledWith()).to.equal(true) + }) + + it('logs error if it fails', async () => { + const error = new Error('something boom') + kubeNamespaceMock.get = sinon.stub().throws(error) + + await poller._checkForSecret() + + expect(loggerMock.error.calledWith(error, 'Secret check went boom for %s in %s', 'fakeSecretName', 'fakeNamespace')).to.equal(true) + }) + }) + describe('_upsertKubernetesSecret', () => { let kubeNamespaceMock let poller @@ -223,49 +344,55 @@ describe('Poller', () => { }) describe('start', () => { - let clock let poller beforeEach(() => { poller = pollerFactory() - clock = sinon.useFakeTimers() poller._poll = sinon.stub() + poller._checkForSecret = sinon.stub() }) afterEach(() => { poller.stop() - clock.restore() }) - it('starts poller', async () => { - expect(poller._interval).to.equal(null) + it('starts poller on force poll', async () => { + expect(poller._timeoutId).to.equal(null) + poller.start({ forcePoll: true }) - clock.tick(poller._intervalMilliseconds) - expect(loggerMock.info.calledWith('starting poller')).to.equal(true) - expect(poller._interval).to.not.equal(null) + + expect(loggerMock.debug.calledWith('starting poller')).to.equal(true) expect(poller._poll.called).to.equal(true) }) + + it('checks for secret if not forced poll', async () => { + expect(poller._timeoutId).to.equal(null) + + poller.start({ forcePoll: false }) + + expect(loggerMock.debug.calledWith('starting poller')).to.equal(true) + expect(poller._poll.called).to.equal(false) + expect(poller._checkForSecret.called).to.equal(true) + }) }) describe('stop', () => { - let clock let poller beforeEach(() => { poller = pollerFactory() - clock = sinon.useFakeTimers() poller._poll = sinon.stub() }) - afterEach(() => { - clock.restore() - }) - it('stops poller', async () => { - poller.start({ forcePoll: true }) + poller._timeoutId = 'some id' + + expect(poller._timeoutId).to.not.equal(null) + poller.stop() - expect(loggerMock.info.calledWith('stopping poller')).to.equal(true) - expect(poller._interval).to.equal(null) + + expect(loggerMock.debug.calledWith('stopping poller')).to.equal(true) + expect(poller._timeoutId).to.equal(null) }) }) })