From b10f052a6523cc879202451de6ee9969ebb64fcb Mon Sep 17 00:00:00 2001 From: Qing Tomlinson Date: Wed, 27 Nov 2024 16:56:28 -0800 Subject: [PATCH] Add unit tests --- providers/upgrade/defUpgradeQueue.js | 4 +- providers/upgrade/process.js | 2 +- test/providers/upgrade/defUpgradeQueue.js | 86 +++++++++++++++++++ test/providers/upgrade/defVersionCheck.js | 80 +++++++++++++++++ .../upgrade/{process.js => processTest.js} | 67 +++++++++++---- 5 files changed, 218 insertions(+), 21 deletions(-) create mode 100644 test/providers/upgrade/defUpgradeQueue.js create mode 100644 test/providers/upgrade/defVersionCheck.js rename test/providers/upgrade/{process.js => processTest.js} (62%) diff --git a/providers/upgrade/defUpgradeQueue.js b/providers/upgrade/defUpgradeQueue.js index 798e9828..d793bbb4 100644 --- a/providers/upgrade/defUpgradeQueue.js +++ b/providers/upgrade/defUpgradeQueue.js @@ -34,8 +34,8 @@ class DefinitionQueueUpgrader extends DefinitionVersionChecker { return this._upgrade.initialize() } - setupProcessing(definitionService, logger) { - return setup(this._upgrade, definitionService, logger) + setupProcessing(definitionService, logger, once) { + return setup(this._upgrade, definitionService, logger, once) } } diff --git a/providers/upgrade/process.js b/providers/upgrade/process.js index 32912674..7f7dae73 100644 --- a/providers/upgrade/process.js +++ b/providers/upgrade/process.js @@ -59,7 +59,7 @@ class DefinitionUpgrader { let queueHandler let defUpgrader -function setup(_queue, _definitionService, _logger, _defVersionChecker = factory(), once = false) { +function setup(_queue, _definitionService, _logger, once = false, _defVersionChecker = factory()) { defUpgrader = new DefinitionUpgrader(_definitionService, _logger, _defVersionChecker) queueHandler = new QueueHandler(_queue, _logger, defUpgrader) return queueHandler.work(once) diff --git a/test/providers/upgrade/defUpgradeQueue.js b/test/providers/upgrade/defUpgradeQueue.js new file mode 100644 index 00000000..1e917197 --- /dev/null +++ b/test/providers/upgrade/defUpgradeQueue.js @@ -0,0 +1,86 @@ +// (c) Copyright 2024, SAP SE and ClearlyDefined contributors. Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +const chaiAsPromised = require('chai-as-promised') +const chai = require('chai') +chai.use(chaiAsPromised) +const { expect } = require('chai') +const sinon = require('sinon') +const DefinitionQueueUpgrader = require('../../../providers/upgrade/defUpgradeQueue') + +describe('DefinitionQueueUpgrader', () => { + const definition = { coordinates: 'test', _meta: { schemaVersion: '1.0.0' } } + let queue, upgrader + + beforeEach(async () => { + const logger = { debug: sinon.stub() } + queue = { + queue: sinon.stub().resolves(), + initialize: sinon.stub().resolves() + } + const queueFactory = sinon.stub().returns(queue) + upgrader = new DefinitionQueueUpgrader({ logger, queue: queueFactory }) + }) + + it('returns an instance of DefinitionQueueUpgrader', () => { + expect(upgrader).to.be.an.instanceOf(DefinitionQueueUpgrader) + }) + + it('sets and gets current schema version', () => { + upgrader.currentSchema = '1.0.0' + expect(upgrader.currentSchema).to.equal('1.0.0') + }) + + it('initializes', async () => { + await upgrader.initialize() + expect(queue.initialize.calledOnce).to.be.true + }) + + it('connects to queue after setupProcessing', async () => { + await upgrader.initialize() + const definitionService = { currentSchema: '1.0.0' } + const logger = { debug: sinon.stub() } + queue.dequeueMultiple = sinon.stub().resolves([]) + upgrader.setupProcessing(definitionService, logger, true) + expect(queue.dequeueMultiple.calledOnce).to.be.true + }) + + context('validate', () => { + it('should fail if current schema version is not set', async () => { + await expect(upgrader.validate(definition)).to.be.rejectedWith(Error) + }) + + it('fails if it is not initialized', async () => { + upgrader.currentSchema = '1.0.0' + const stale = { coordinates: 'test', _meta: { schemaVersion: '0.0.1' } } + await expect(upgrader.validate(stale)).to.be.rejectedWith(Error) + }) + }) + + context('validate after set up', () => { + beforeEach(async () => { + await upgrader.initialize() + upgrader.currentSchema = '1.0.0' + }) + + it('does not queue null definition', async () => { + const result = await upgrader.validate(null) + expect(result).to.be.not.ok + expect(queue.queue.called).to.be.false + }) + + it('does not queue an up-to-date definition', async () => { + const definition = { coordinates: 'test', _meta: { schemaVersion: '1.0.0' } } + const result = await upgrader.validate(definition) + expect(result).to.deep.equal(definition) + expect(queue.queue.called).to.be.false + }) + + it('queues and returns a stale definition', async () => { + const definition = { coordinates: 'test', _meta: { schemaVersion: '0.0.1' } } + const result = await upgrader.validate(definition) + expect(result).to.deep.equal(definition) + expect(queue.queue.calledOnce).to.be.true + }) + }) +}) diff --git a/test/providers/upgrade/defVersionCheck.js b/test/providers/upgrade/defVersionCheck.js new file mode 100644 index 00000000..bc261b79 --- /dev/null +++ b/test/providers/upgrade/defVersionCheck.js @@ -0,0 +1,80 @@ +// (c) Copyright 2024, SAP SE and ClearlyDefined contributors. Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +const { expect } = require('chai') +const sinon = require('sinon') +const { DefinitionVersionChecker, factory } = require('../../../providers/upgrade/defVersionCheck') + +describe('DefinitionVersionChecker', () => { + let logger, checker + beforeEach(() => { + logger = { debug: sinon.stub() } + checker = new DefinitionVersionChecker({ logger }) + }) + + it('returns an instance of DefinitionVersionChecker', () => { + expect(checker).to.be.an.instanceOf(DefinitionVersionChecker) + }) + + it('creates a new instance of DefinitionVersionChecker using factory', () => { + const checker = factory({ logger: logger }) + expect(checker).to.be.an.instanceOf(DefinitionVersionChecker) + }) + + it('sets and gets current schema version', () => { + checker.currentSchema = '1.0.0' + expect(checker.currentSchema).to.equal('1.0.0') + }) + + it('initializes and returns undefined', async () => { + const result = await checker.initialize() + expect(result).to.be.not.ok + }) + + it('returns after setupProcessing', async () => { + const result = checker.setupProcessing() + expect(result).to.be.not.ok + }) + + it('throws an error in validate if current schema version is not set', async () => { + const definition = { _meta: { schemaVersion: '1.0.0' } } + let errorMessage = '' + try { + await checker.validate(definition) + fail('should have failed') + } catch (error) { + errorMessage = error.message + } + expect(errorMessage).to.be.not.empty + }) + + context('validate after current schema version is set', () => { + beforeEach(() => { + checker.currentSchema = '1.0.0' + }) + + it('returns the definition if it is up-to-date', async () => { + const definition = { _meta: { schemaVersion: '1.0.0' } } + const result = await checker.validate(definition) + expect(result).to.deep.equal(definition) + }) + + it('returns undefined for a stale definition', async () => { + const definition = { _meta: { schemaVersion: '0.1.0' } } + const result = await checker.validate(definition) + expect(result).to.be.undefined + }) + + it('returns undefined for a definition without schema version', async () => { + const definition = {} + const result = await checker.validate(definition) + expect(result).to.be.undefined + }) + + it('handles null', async () => { + checker.currentSchema = '1.0.0' + const result = await checker.validate(null) + expect(result).to.be.not.ok + }) + }) +}) diff --git a/test/providers/upgrade/process.js b/test/providers/upgrade/processTest.js similarity index 62% rename from test/providers/upgrade/process.js rename to test/providers/upgrade/processTest.js index 13e48bf6..3f5739a3 100644 --- a/test/providers/upgrade/process.js +++ b/test/providers/upgrade/processTest.js @@ -6,13 +6,20 @@ const sinon = require('sinon') const { QueueHandler, DefinitionUpgrader } = require('../../../providers/upgrade/process') describe('Definition Upgrade Queue Processing', () => { + let logger + + beforeEach(() => { + logger = { + info: sinon.stub(), + error: sinon.stub(), + debug: sinon.stub() + } + }) + describe('QueueHandler', () => { - let logger, queue, messageHandler, handler + let queue, messageHandler, handler + beforeEach(() => { - logger = { - info: sinon.stub(), - error: sinon.stub() - } queue = { dequeueMultiple: sinon.stub(), delete: sinon.stub().resolves() @@ -23,11 +30,11 @@ describe('Definition Upgrade Queue Processing', () => { handler = new QueueHandler(queue, logger, messageHandler) }) - it('should return an instance of QueueHandler', () => { + it('returns an instance of QueueHandler', () => { expect(handler).to.be.an.instanceOf(QueueHandler) }) - it('should work on a queue', () => { + it('works on a queue', () => { queue.dequeueMultiple.resolves([]) handler.work(true) expect(queue.dequeueMultiple.calledOnce).to.be.true @@ -35,7 +42,7 @@ describe('Definition Upgrade Queue Processing', () => { expect(queue.delete.notCalled).to.be.true }) - it('should process one message', async () => { + it('processes one message', async () => { queue.dequeueMultiple.resolves([{ message: 'test' }]) await handler.work(true) expect(queue.dequeueMultiple.calledOnce).to.be.true @@ -43,7 +50,7 @@ describe('Definition Upgrade Queue Processing', () => { expect(queue.delete.calledOnce).to.be.true }) - it('should process multiple messages', async () => { + it('processes multiple messages', async () => { queue.dequeueMultiple.resolves([{ message: 'testA' }, { message: 'testB' }]) await handler.work(true) expect(queue.dequeueMultiple.calledOnce).to.be.true @@ -51,7 +58,17 @@ describe('Definition Upgrade Queue Processing', () => { expect(queue.delete.calledTwice).to.be.true }) - it('should log error and not delete the message', async () => { + it('handles if error is thrown', async () => { + queue.dequeueMultiple.resolves([{ message: 'testA' }]) + messageHandler.processMessage = sinon.stub().throws() + await handler.work(true) + expect(queue.dequeueMultiple.calledOnce).to.be.true + expect(messageHandler.processMessage.calledOnce).to.be.true + expect(queue.delete.called).to.be.false + expect(logger.error.calledOnce).to.be.true + }) + + it('handles both sucessful and unsucessful messages', async () => { queue.dequeueMultiple.resolves([{ message: 'testA' }, { message: 'testB' }]) messageHandler.processMessage = sinon.stub().onFirstCall().throws().onSecondCall().resolves() await handler.work(true) @@ -63,13 +80,10 @@ describe('Definition Upgrade Queue Processing', () => { }) describe('DefinitionUpgrader', () => { - const definition = { coordinates: 'pypi/pypi/-/test/revision' } - let logger, definitionService, versionChecker, upgrader + const definition = Object.freeze({ coordinates: 'pypi/pypi/-/test/revision' }) + let definitionService, versionChecker, upgrader + beforeEach(() => { - logger = { - info: sinon.stub(), - debug: sinon.stub() - } definitionService = { getStored: sinon.stub(), computeStoreAndCurate: sinon.stub().resolves() @@ -80,7 +94,7 @@ describe('Definition Upgrade Queue Processing', () => { upgrader = new DefinitionUpgrader(definitionService, logger, versionChecker) }) - it('should recompute a definition', async () => { + it('recomputes a definition, if a definition is not up-to-date', async () => { definitionService.getStored.resolves(definition) versionChecker.validate.resolves() @@ -90,7 +104,7 @@ describe('Definition Upgrade Queue Processing', () => { expect(definitionService.computeStoreAndCurate.calledOnce).to.be.true }) - it('should skip compute when a definition is up-to-date', async () => { + it('skips compute if a definition is up-to-date', async () => { definitionService.getStored.resolves(definition) versionChecker.validate.resolves(definition) @@ -99,5 +113,22 @@ describe('Definition Upgrade Queue Processing', () => { expect(versionChecker.validate.calledOnce).to.be.true expect(definitionService.computeStoreAndCurate.notCalled).to.be.true }) + + it('computes if a definition does not exist', async () => { + definitionService.getStored.resolves() + versionChecker.validate.resolves() + + await upgrader.processMessage({ data: { coordinates: 'pypi/pypi/-/test/revision' } }) + expect(definitionService.getStored.calledOnce).to.be.true + expect(versionChecker.validate.calledOnce).to.be.true + expect(definitionService.computeStoreAndCurate.calledOnce).to.be.true + }) + + it('skips if there is no coordinates', async () => { + await upgrader.processMessage({ data: {} }) + expect(definitionService.getStored.notCalled).to.be.true + expect(versionChecker.validate.notCalled).to.be.true + expect(definitionService.computeStoreAndCurate.notCalled).to.be.true + }) }) })