Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 2b87e48

Browse files
Mock scorecard api and update tests to not depend on a running kafka instance
1 parent 19690a3 commit 2b87e48

File tree

8 files changed

+133
-218
lines changed

8 files changed

+133
-218
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Also note that there is a `/health` endpoint that checks for the health of the a
3737

3838
Configuration for the tests is at `config/test.js`, only add such new configurations different from `config/default.js`
3939
- WAIT_TIME: wait time used in test, default is 2000 or 2 seconds
40+
- SCORECARD_API_URL: the scorecard api url used in testing
4041

4142
## Local Kafka setup
4243

config/test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
*/
44

55
module.exports = {
6-
WAIT_TIME: process.env.WAIT_TIME ? Number(process.env.WAIT_TIME) : 2000
6+
WAIT_TIME: process.env.WAIT_TIME ? Number(process.env.WAIT_TIME) : 2000,
7+
SCORECARD_API_URL: 'https://api.topcoder-dev.com/scorecards'
78
}

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
"devDependencies": {
1616
"mocha": "^5.2.0",
1717
"mocha-prepare": "^0.1.0",
18+
"nock": "^11.3.5",
1819
"nyc": "^13.3.0",
1920
"should": "^13.2.3",
20-
"standard": "^12.0.1",
21-
"nock": "^11.3.5"
21+
"standard": "^12.0.1"
2222
},
2323
"dependencies": {
2424
"@topcoder-platform/topcoder-submission-api-wrapper": "^1.1.0",

src/app.js

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ const SubmissionProcessorService = require('./services/SubmissionProcessorServic
1313

1414
// Start kafka consumer
1515
logger.info('Starting kafka consumer')
16-
// create consumer
1716

18-
const consumer = new Kafka.GroupConsumer(helper.getKafkaOptions())
17+
// create consumer
18+
let consumer
19+
if (process.env.NODE_ENV !== 'test') {
20+
consumer = new Kafka.GroupConsumer(helper.getKafkaOptions())
21+
}
1922

2023
/*
2124
* Data handler linked with Kafka consumer
@@ -52,7 +55,11 @@ const dataHandler = (messageSet, topic, partition) => Promise.each(messageSet, a
5255
.then(() => { logger.debug('Successfully processed message') })
5356
.catch((err) => { logger.logFullError(err) })
5457
// commit offset regardless of errors
55-
.finally(() => consumer.commitOffset({ topic, partition, offset: m.offset }))
58+
.finally(() => {
59+
if (consumer) {
60+
consumer.commitOffset({ topic, partition, offset: m.offset })
61+
}
62+
})
5663
})
5764

5865
// check if there is kafka connection alive
@@ -68,23 +75,25 @@ function check () {
6875
return connected
6976
}
7077

71-
const topics = [config.REVIEW_TOPIC, config.CREATE_SUBMISSION_TOPIC, config.UPDATE_SUBMISSION_TOPIC]
78+
if (consumer) {
79+
const topics = [config.REVIEW_TOPIC, config.CREATE_SUBMISSION_TOPIC, config.UPDATE_SUBMISSION_TOPIC]
7280

73-
consumer
74-
.init([{
75-
subscriptions: topics,
76-
handler: dataHandler
77-
}])
78-
// consume configured topics
79-
.then(() => {
80-
logger.info('Initialized.......')
81-
healthcheck.init([check])
82-
logger.info('Adding topics successfully.......')
83-
logger.info(topics)
84-
logger.info('Kick Start.......')
85-
})
86-
.catch((err) => logger.error(err))
81+
consumer
82+
.init([{
83+
subscriptions: topics,
84+
handler: dataHandler
85+
}])
86+
// consume configured topics
87+
.then(() => {
88+
logger.info('Initialized.......')
89+
healthcheck.init([check])
90+
logger.info('Adding topics successfully.......')
91+
logger.info(topics)
92+
logger.info('Kick Start.......')
93+
})
94+
.catch((err) => logger.error(err))
95+
}
8796

8897
if (process.env.NODE_ENV === 'test') {
89-
module.exports = consumer
98+
module.exports = dataHandler
9099
}

src/services/SubmissionProcessorService.js

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,30 @@ function generateScoreSystemMessage (message, scoreSystem) {
8181
* @param {Object} message the kafka message to send.
8282
*/
8383
async function sendMessage (message) {
84-
// init producer if needed
85-
if (!producer) {
86-
producer = new Kafka.Producer(helper.getKafkaOptions())
87-
// init kafka producer
88-
try {
89-
await producer.init()
90-
} catch (e) {
91-
// if there is any error, reset producer to null so that it will be re-created next time
92-
producer = null
93-
throw e
84+
if (process.env.NODE_ENV === 'test') {
85+
logger.debug(`Send Kafka message to topic: ${message.topic}`)
86+
logger.debug(`Payload: ${JSON.stringify(message)}`)
87+
} else {
88+
// init producer if needed
89+
if (!producer) {
90+
producer = new Kafka.Producer(helper.getKafkaOptions())
91+
// init kafka producer
92+
try {
93+
await producer.init()
94+
} catch (e) {
95+
// if there is any error, reset producer to null so that it will be re-created next time
96+
producer = null
97+
throw e
98+
}
9499
}
100+
// send message
101+
await producer.send({
102+
topic: message.topic,
103+
message: {
104+
value: JSON.stringify(message)
105+
}
106+
})
95107
}
96-
// send message
97-
await producer.send({
98-
topic: message.topic,
99-
message: {
100-
value: JSON.stringify(message)
101-
}
102-
})
103108
}
104109

105110
/**

test/common/prepare.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ prepare(function (done) {
3535
.reply(200)
3636
.post('/v5/reviews')
3737
.reply(200)
38+
.get(/\/scorecards\/.+/)
39+
.reply(200, { 'id': 'b9956051-4749-4912-abd5-bf4d701af891', 'scorecardDetails': [{ 'id': 'b9956051-4749-4912-abd1-af4d701af891', 'name': 'AV Scan', 'phase': 'submission', 'isActive': true, 'topic': 'avscan.action.scan', 'weight': 25 }, { 'id': 'b9956051-4749-4912-abd1-af4d701af892', 'name': 'OR', 'phase': 'review', 'isActive': true, 'topic': 'or.action.review', 'weight': 25 }, { 'id': 'b9956051-4749-4912-abd1-af4d701af893', 'name': 'AV Scan', 'phase': 'registration', 'isActive': true, 'topic': 'avscan.action.scan', 'weight': 25 }, { 'id': 'b9956051-4749-4912-abd1-af4d701af894', 'name': 'OR', 'phase': 'registration', 'isActive': true, 'topic': 'or.action.review', 'weight': 25 }] })
3840

3941
done()
4042
}, function (done) {

test/e2e/review.processor.test.js

Lines changed: 14 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,74 +6,42 @@ process.env.NODE_ENV = 'test'
66
global.Promise = require('bluebird')
77

88
const _ = require('lodash')
9-
const should = require('should')
10-
const request = require('superagent')
119

1210
const testHelper = require('../common/testHelper')
1311
const { testTopics } = require('../common/testData')
12+
const dataHandler = require('../../src/app')
1413

1514
describe('Topcoder - Scorecard Review Processor E2E Test', () => {
1615
before(async () => {
17-
// consume and commit existing messages if any
18-
await testHelper.consumeMessages()
19-
// init kafka producer
20-
await testHelper.initProducer()
21-
2216
// intercept logger
2317
testHelper.interceptLogger()
24-
25-
// start the application (kafka listener)
26-
await testHelper.initApp()
2718
})
2819

2920
after(async () => {
3021
testHelper.restoreLogger()
31-
32-
await testHelper.stopProducer()
3322
})
3423

3524
beforeEach(() => {
3625
testHelper.clearInterceptedLogging()
3726
})
3827

39-
it('Should setup healthcheck with check on kafka connection', async () => {
40-
const healthcheckEndpoint = `http://localhost:${process.env.PORT || 3000}/health`
41-
let result = await request.get(healthcheckEndpoint)
42-
should.equal(result.status, 200)
43-
should.deepEqual(result.body, { checksRun: 1 })
44-
testHelper.assertDebugMessage('connected=true')
45-
})
46-
4728
it('Should handle invalid json message', async () => {
4829
const { testMessage } = testTopics.create
49-
await testHelper.getProducer().send({
50-
topic: testMessage.topic,
51-
message: {
52-
value: '[ invalid'
53-
}
54-
})
55-
await testHelper.waitJob()
30+
await dataHandler([{ message: { value: '[ invalid' }, offset: 0 }], testMessage.topic, 0)
5631
testHelper.assertErrorMessage('Invalid message JSON.')
5732
})
5833

5934
it('Should handle incorrect topic field message', async () => {
6035
const { testMessage } = testTopics.create
6136
let message = _.cloneDeep(testMessage)
6237
message.topic = 'invalid'
63-
await testHelper.getProducer().send({
64-
topic: testMessage.topic,
65-
message: {
66-
value: JSON.stringify(message)
67-
}
68-
})
69-
await testHelper.waitJob()
38+
await dataHandler([{ message: { value: JSON.stringify(message) }, offset: 0 }], testMessage.topic, 0)
7039
testHelper.assertErrorMessage(
7140
'The message topic invalid doesn\'t match the Kafka topic submission.notification.score.')
7241
})
7342

7443
it('processor create review success', async () => {
75-
await testHelper.sendMessage(testTopics.create.testMessage)
76-
await testHelper.waitJob()
44+
await dataHandler([{ message: { value: JSON.stringify(testTopics.create.testMessage) }, offset: 0 }], testTopics.create.testMessage.topic, 0)
7745

7846
testHelper.assertDebugMessage('Get submission')
7947
testHelper.assertDebugMessage('Submission id: b91a0ca3-3988-4899-bab4-c789f22def39')
@@ -83,8 +51,7 @@ describe('Topcoder - Scorecard Review Processor E2E Test', () => {
8351
})
8452

8553
it('processor update review success', async () => {
86-
await testHelper.sendMessage(testTopics.update.testMessage)
87-
await testHelper.waitJob()
54+
await dataHandler([{ message: { value: JSON.stringify(testTopics.update.testMessage) }, offset: 0 }], testTopics.update.testMessage.topic, 0)
8855

8956
testHelper.assertDebugMessage('Get submission')
9057
testHelper.assertDebugMessage('Submission id: b91a0ca3-3988-4899-bab4-c789f22def39')
@@ -98,24 +65,21 @@ describe('Topcoder - Scorecard Review Processor E2E Test', () => {
9865
it('test invalid parameters, userId is forbidden for create review message.', async () => {
9966
let message = _.cloneDeep(testTopics.create.testMessage)
10067
message.payload.userId = '12345'
101-
await testHelper.sendMessage(message)
102-
await testHelper.waitJob()
68+
await dataHandler([{ message: { value: JSON.stringify(message) }, offset: 0 }], message.topic, 0)
10369
testHelper.assertErrorMessage('"userId" is not allowed')
10470
})
10571

10672
it('test invalid parameters, fail to retrieve review with given criteria.', async () => {
10773
let message = _.cloneDeep(testTopics.update.testMessage)
10874
message.payload.scorecardId = 300001611
109-
await testHelper.sendMessage(message)
110-
await testHelper.waitJob()
75+
await dataHandler([{ message: { value: JSON.stringify(message) }, offset: 0 }], message.topic, 0)
11176
testHelper.assertErrorMessage('Review doesn\'t exist under criteria')
11277
})
11378

11479
it('test invalid eventType, fail to retrieve review with given criteria.', async () => {
11580
let message = _.cloneDeep(testTopics.create.testMessage)
11681
message.payload.eventType = 'INVALID_TYPE'
117-
await testHelper.sendMessage(message)
118-
await testHelper.waitJob()
82+
await dataHandler([{ message: { value: JSON.stringify(message) }, offset: 0 }], message.topic, 0)
11983
testHelper.assertErrorMessage('Invalid or not supported eventType: INVALID_TYPE')
12084
})
12185

@@ -125,16 +89,14 @@ describe('Topcoder - Scorecard Review Processor E2E Test', () => {
12589
it('test invalid parameters, field submissionId incorrect', async () => {
12690
let message = _.cloneDeep(testMessage)
12791
message.payload.submissionId = 111111111
128-
await testHelper.sendMessage(message)
129-
await testHelper.waitJob()
92+
await dataHandler([{ message: { value: JSON.stringify(message) }, offset: 0 }], message.topic, 0)
13093
testHelper.assertErrorMessage('Incorrect submission id 111111111')
13194
})
13295

13396
it('test invalid parameters, fail to get reviewType UUID.', async () => {
13497
let message = _.cloneDeep(testMessage)
13598
message.payload.reviewTypeId = 12
136-
await testHelper.sendMessage(message)
137-
await testHelper.waitJob()
99+
await dataHandler([{ message: { value: JSON.stringify(message) }, offset: 0 }], message.topic, 0)
138100
testHelper.assertErrorMessage('Incorrect review type id 12')
139101
})
140102

@@ -143,8 +105,7 @@ describe('Topcoder - Scorecard Review Processor E2E Test', () => {
143105
it(`test invalid parameters, required field ${requiredField} is missing`, async () => {
144106
let message = _.cloneDeep(testMessage)
145107
message = _.omit(message, requiredField)
146-
await testHelper.sendMessage(message)
147-
await testHelper.waitJob()
108+
await dataHandler([{ message: { value: JSON.stringify(message) }, offset: 0 }], message.topic, 0)
148109
testHelper.assertErrorMessage(`"${_.last(requiredField.split('.'))}" is required`)
149110
})
150111
}
@@ -155,8 +116,7 @@ describe('Topcoder - Scorecard Review Processor E2E Test', () => {
155116
it(`test invalid parameters, invalid string type field ${stringField}`, async () => {
156117
let message = _.cloneDeep(testMessage)
157118
_.set(message, stringField, 123)
158-
await testHelper.sendMessage(message)
159-
await testHelper.waitJob()
119+
await dataHandler([{ message: { value: JSON.stringify(message) }, offset: 0 }], message.topic, 0)
160120
testHelper.assertErrorMessage(`"${_.last(stringField.split('.'))}" must be a string`)
161121
})
162122
}
@@ -166,16 +126,14 @@ describe('Topcoder - Scorecard Review Processor E2E Test', () => {
166126
it(`test invalid parameters, invalid integer type field ${integerField}(wrong number)`, async () => {
167127
let message = _.cloneDeep(testMessage)
168128
_.set(message, integerField, 'string')
169-
await testHelper.sendMessage(message)
170-
await testHelper.waitJob()
129+
await dataHandler([{ message: { value: JSON.stringify(message) }, offset: 0 }], message.topic, 0)
171130
testHelper.assertErrorMessage(`"${_.last(integerField.split('.'))}" must be a number`)
172131
})
173132

174133
it(`test invalid parameters, invalid integer type field ${integerField}(wrong integer)`, async () => {
175134
let message = _.cloneDeep(testMessage)
176135
_.set(message, integerField, 1.1)
177-
await testHelper.sendMessage(message)
178-
await testHelper.waitJob()
136+
await dataHandler([{ message: { value: JSON.stringify(message) }, offset: 0 }], message.topic, 0)
179137
if (integerField === 'payload.reviewTypeId') {
180138
testHelper.assertErrorMessage(`"${_.last(integerField.split('.'))}" must be one of [1, 2, 5, 6, 7, 8, 9, 10, 11, 12]`)
181139
} else {

0 commit comments

Comments
 (0)