diff --git a/README.md b/README.md index 593807e..adf4fc4 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,10 @@ The following parameters can be set in config files or in env variables: - REVIEW_TOPIC : Review topic, default value is 'submission.notification.score' - CREATE_SUBMISSION_TOPIC : create submission topic, default value is 'submission.notification.create' - UPDATE_SUBMISSION_TOPIC : update submission topic, default value is 'submission.notification.update' -- REVIEW_API_URL: review api url, default is 'https://api.topcoder-dev.com/v5/reviews' -- SUBMISSION_API_URL: submission api url, default is 'https://api.topcoder-dev.com/v5/submissions' -- REVIEW_TYPE_API_URL: review type api url, default is 'https://api.topcoder-dev.com/v5/reviewTypes' +- SUBMISSION_API_URL: submission api url, default is 'https://api.topcoder-dev.com/v5' - CHALLENGE_API_URL: challenge API URL, default is 'https://api.topcoder-dev.com/v4/challenges' - SCORECARD_API_URL: scorecard API URL, default is 'http://localhost:4000/scorecards' +- BUS_API_URL: bus API URL, default is 'https://api.topcoder-dev.com/v5/bus/events' - AUTH0_URL: Auth0 URL, used to get TC M2M token - AUTH0_AUDIENCE: Auth0 audience, used to get TC M2M token - TOKEN_CACHE_TIME: Auth0 token cache time, used to get TC M2M token @@ -38,7 +37,6 @@ Also note that there is a `/health` endpoint that checks for the health of the a Configuration for the tests is at `config/test.js`, only add such new configurations different from `config/default.js` - WAIT_TIME: wait time used in test, default is 2000 or 2 seconds -- REVIEW_SUMMATION_API_URL: review summation api url, used to clear resource during testing, default is 'https://api.topcoder-dev.com/v5/reviewSummations' ## Local Kafka setup diff --git a/Verification.md b/Verification.md index c24ed07..f670d58 100644 --- a/Verification.md +++ b/Verification.md @@ -172,51 +172,38 @@ debug: Successfully processed message ## Unit test Coverage - 129 passing (51s) - ---------------------------------|----------|----------|----------|----------|-------------------| -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | ---------------------------------|----------|----------|----------|----------|-------------------| -All files | 89.89 | 78.46 | 98.31 | 90.22 | | - config | 100 | 97.22 | 100 | 100 | | - default.js | 100 | 100 | 100 | 100 | | - test.js | 100 | 75 | 100 | 100 | 6 | - src/common | 84.62 | 53.85 | 95 | 86.52 | | - helper.js | 69.23 | 50 | 85.71 | 75 | 18,91,92,93,95,96 | - logger.js | 90.77 | 55 | 100 | 90.77 |31,55,60,84,98,118 | - src/services | 89.32 | 78.85 | 100 | 89.11 | | - ReviewProcessorService.js | 94.74 | 85 | 100 | 94.59 | 66,67 | - SubmissionProcessorService.js | 86.15 | 75 | 100 | 85.94 |... 20,126,130,140 | - test/unit | 91.89 | 75 | 100 | 91.86 | | - review.processor.test.js | 91.87 | 70 | 100 | 91.8 |... 54,169,183,194 | - submission.processor.test.js | 91.91 | 83.33 | 100 | 91.91 |... 64,178,191,202 | ---------------------------------|----------|----------|----------|----------|-------------------| - + 129 passing (1s) + +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +--------------------------------|----------|----------|----------|----------|------------------- +All files | 91.63 | 79.63 | 100 | 91.46 | + config | 100 | 96.67 | 100 | 100 | + default.js | 100 | 100 | 100 | 100 | + test.js | 100 | 50 | 100 | 100 | 6 + src/common | 92.13 | 57.69 | 100 | 91.95 | + helper.js | 95.83 | 66.67 | 100 | 95.45 | 18 + logger.js | 90.77 | 55 | 100 | 90.77 |31,55,60,84,98,118 + src/services | 91.07 | 80.77 | 100 | 90.91 | + ReviewProcessorService.js | 97.87 | 90 | 100 | 97.83 | 72 + SubmissionProcessorService.js | 86.15 | 75 | 100 | 85.94 |... 20,126,130,140 ## E2E test Coverage - 136 passing (6m) - ---------------------------------|----------|----------|----------|----------|-------------------| -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | ---------------------------------|----------|----------|----------|----------|-------------------| -All files | 94.82 | 79.33 | 96.2 | 95.11 | | - config | 100 | 97.22 | 100 | 100 | | - default.js | 100 | 100 | 100 | 100 | | - test.js | 100 | 75 | 100 | 100 | 6 | - src | 93.88 | 71.43 | 90 | 93.75 | | - app.js | 93.75 | 71.43 | 90 | 93.62 | 49,61,86 | - bootstrap.js | 100 | 100 | 100 | 100 | | - src/common | 84.62 | 61.54 | 95 | 86.52 | | - helper.js | 69.23 | 50 | 85.71 | 75 | 18,91,92,93,95,96 | - logger.js | 90.77 | 65 | 100 | 90.77 |31,55,60,84,98,118 | - src/services | 89.32 | 78.85 | 100 | 89.11 | | - ReviewProcessorService.js | 94.74 | 85 | 100 | 94.59 | 66,67 | - SubmissionProcessorService.js | 86.15 | 75 | 100 | 85.94 |... 20,126,130,140 | - test/e2e | 99.4 | 77.27 | 97.62 | 99.4 | | - review.processor.test.js | 99.27 | 75 | 100 | 99.26 | 32 | - submission.processor.test.js | 99.49 | 80 | 95.83 | 99.49 | 49 | ---------------------------------|----------|----------|----------|----------|-------------------| - - + 136 passing (5m) + +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +--------------------------------|----------|----------|----------|----------|------------------- +All files | 92.06 | 80.33 | 97.14 | 91.9 | + config | 100 | 96.67 | 100 | 100 | + default.js | 100 | 100 | 100 | 100 | + test.js | 100 | 50 | 100 | 100 | 6 + src | 93.88 | 71.43 | 90 | 93.75 | + app.js | 93.75 | 71.43 | 90 | 93.62 | 49,61,86 + bootstrap.js | 100 | 100 | 100 | 100 | + src/common | 92.13 | 65.38 | 100 | 91.95 | + helper.js | 95.83 | 66.67 | 100 | 95.45 | 18 + logger.js | 90.77 | 65 | 100 | 90.77 |31,55,60,84,98,118 + src/services | 91.07 | 80.77 | 100 | 90.91 | + ReviewProcessorService.js | 97.87 | 90 | 100 | 97.83 | 72 + SubmissionProcessorService.js | 86.15 | 75 | 100 | 85.94 |... 20,126,130,140 diff --git a/config/default.js b/config/default.js index 96a1c2b..e6ee37e 100644 --- a/config/default.js +++ b/config/default.js @@ -21,9 +21,8 @@ module.exports = { // Kafka topic related to update submission UPDATE_SUBMISSION_TOPIC: process.env.UPDATE_SUBMISSION_TOPIC || 'submission.notification.update', - REVIEW_API_URL: process.env.REVIEW_API_URL || 'https://api.topcoder-dev.com/v5/reviews', - SUBMISSION_API_URL: process.env.SUBMISSION_API_URL || 'https://api.topcoder-dev.com/v5/submissions', - REVIEW_TYPE_API_URL: process.env.REVIEW_TYPE_API_URL || 'https://api.topcoder-dev.com/v5/reviewTypes', + SUBMISSION_API_URL: process.env.SUBMISSION_API_URL || 'https://api.topcoder-dev.com/v5', + CHALLENGE_API_URL: process.env.CHALLENGE_API_URL || 'https://api.topcoder-dev.com/v4/challenges', SCORECARD_API_URL: process.env.SCORECARD_API_URL || 'http://localhost:4000/scorecards', BUS_API_URL: process.env.BUS_API_URL || 'https://api.topcoder-dev.com/v5/bus/events', diff --git a/config/test.js b/config/test.js index 071f2bc..4c10cc3 100644 --- a/config/test.js +++ b/config/test.js @@ -3,7 +3,5 @@ */ module.exports = { - WAIT_TIME: process.env.WAIT_TIME ? Number(process.env.WAIT_TIME) : 2000, - REVIEW_SUMMATION_API_URL: process.env.REVIEW_SUMMATION_API_URL || - 'https://api.topcoder-dev.com/v5/reviewSummations' + WAIT_TIME: process.env.WAIT_TIME ? Number(process.env.WAIT_TIME) : 2000 } diff --git a/mock-scorecard-api/index.js b/mock-scorecard-api/index.js index 64b5121..90631af 100755 --- a/mock-scorecard-api/index.js +++ b/mock-scorecard-api/index.js @@ -15,5 +15,5 @@ scoreSystems.forEach(element => { app.get('/scoreSystems', (req, res) => res.json(scoreSystems)) app.get('/scorecards/:id', (req, res) => res.json(scorecardDetails)) -app.listen(process.env.PORT || 4000); +app.listen(process.env.PORT || 4000) console.log(`Server listening on http://localhost:${process.env.PORT || 4000}`) diff --git a/package-lock.json b/package-lock.json index 5f27587..56afb42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -119,6 +119,157 @@ "to-fast-properties": "^2.0.0" } }, + "@hapi/address": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.2.tgz", + "integrity": "sha512-O4QDrx+JoGKZc6aN64L04vqa7e41tIiLU+OvKdcYaEMP97UttL0f9GIi9/0A4WAMx0uBd6SidDIhktZhgOcN8Q==" + }, + "@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==" + }, + "@hapi/hoek": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.2.4.tgz", + "integrity": "sha512-Ze5SDNt325yZvNO7s5C4fXDscjJ6dcqLFXJQ/M7dZRQCewuDj2iDUuBi6jLQt+APbW9RjjVEvLr35FXuOEqjow==" + }, + "@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "requires": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "@hapi/topo": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.4.tgz", + "integrity": "sha512-aVWQTOI9wBD6zawmOr6f+tdEIxQC8JXfQVLTjgGe8YEStAWGn/GNNVTobKJhbWKveQj2RyYF3oYbO9SC8/eOCA==", + "requires": { + "@hapi/hoek": "8.x.x" + } + }, + "@topcoder-platform/topcoder-submission-api-wrapper": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@topcoder-platform/topcoder-submission-api-wrapper/-/topcoder-submission-api-wrapper-1.1.0.tgz", + "integrity": "sha512-yFVG6JZLZmL9KkE+CkY66LWAxLIcHygUCohXMhUg2lwP6Mw6BdGue27RmTi0qzTR9Rs9owriODNdxpm8oPzP2g==", + "requires": { + "@hapi/joi": "^15.0.3", + "lodash": "^4.17.15", + "superagent": "^3.8.3", + "tc-core-library-js": "github:appirio-tech/tc-core-library-js" + }, + "dependencies": { + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "superagent": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" + } + }, + "tc-core-library-js": { + "version": "github:appirio-tech/tc-core-library-js#f45352974dafe5a10c86fc50bdd59ef399b50c65", + "from": "github:appirio-tech/tc-core-library-js", + "requires": { + "auth0-js": "^9.4.2", + "axios": "^0.19.0", + "bunyan": "^1.8.12", + "jsonwebtoken": "^8.3.0", + "jwks-rsa": "^1.3.0", + "le_node": "^1.3.1", + "lodash": "^4.17.10", + "millisecond": "^0.1.2", + "request": "^2.88.0" + } + } + } + }, "@types/bluebird": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.0.tgz", @@ -302,6 +453,12 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", @@ -595,6 +752,20 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -612,6 +783,12 @@ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "dev": true }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", @@ -816,6 +993,15 @@ "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", "dev": true }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1590,6 +1776,12 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, "get-parameter-names": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/get-parameter-names/-/get-parameter-names-0.3.0.tgz", @@ -1867,6 +2059,11 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + }, "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", @@ -2411,6 +2608,12 @@ } } }, + "mocha-prepare": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/mocha-prepare/-/mocha-prepare-0.1.0.tgz", + "integrity": "sha1-VRMidoEiLkNJSB7k5GJHLzHGu4I=", + "dev": true + }, "moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", @@ -2498,6 +2701,28 @@ "wrr-pool": "^1.0.3" } }, + "nock": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/nock/-/nock-11.3.5.tgz", + "integrity": "sha512-6WGeZcWc3RExkBcMSYSrUm/5YukDo52m/jhwniQyrnuiCnKRljBwwje9vTwJyEi4J6m2bq0Aj6C1vzuM6iuaeg==", + "dev": true, + "requires": { + "chai": "^4.1.2", + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.13", + "mkdirp": "^0.5.0", + "propagate": "^2.0.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -3692,6 +3917,12 @@ "pify": "^2.0.0" } }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -3802,6 +4033,12 @@ "react-is": "^16.8.1" } }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, "protocol-buffers-schema": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.3.2.tgz", @@ -4487,6 +4724,12 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", diff --git a/package.json b/package.json index 0018ed0..33717e8 100644 --- a/package.json +++ b/package.json @@ -7,18 +7,21 @@ "start": "node src/app.js", "lint": "standard", "lint:fix": "standard --fix", - "test": "nyc --reporter=html --reporter=text mocha test/unit/*.test.js --timeout 20000 --exit", - "e2e": "nyc --reporter=html --reporter=text mocha test/e2e/*.test.js --timeout 20000 --exit" + "test": "nyc --reporter=html --reporter=text mocha test/unit/*.test.js --require test/common/prepare.js --timeout 20000 --exit", + "e2e": "nyc --reporter=html --reporter=text mocha test/e2e/*.test.js --require test/common/prepare.js --timeout 20000 --exit" }, "author": "TCSCODER", "license": "none", "devDependencies": { "mocha": "^5.2.0", + "mocha-prepare": "^0.1.0", "nyc": "^13.3.0", "should": "^13.2.3", - "standard": "^12.0.1" + "standard": "^12.0.1", + "nock": "^11.3.5" }, "dependencies": { + "@topcoder-platform/topcoder-submission-api-wrapper": "^1.1.0", "bluebird": "^3.5.3", "config": "^3.0.1", "get-parameter-names": "^0.3.0", @@ -41,8 +44,8 @@ "nyc": { "exclude": [ "test/common/*.js", - "test/unit/test.js", - "test/e2e/test.js" + "test/unit/*.js", + "test/e2e/*.js" ] } } diff --git a/src/common/helper.js b/src/common/helper.js index f10aca4..b378b1c 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -45,52 +45,20 @@ async function getRequest (url, query, m2mToken) { } /** - * Uses superagent to proxy post request - * @param {String} url the url - * @param {Object} body the JSON object body - * @param {String} m2mToken the M2M token - * @returns {Object} the response - */ -async function postRequest (url, body, m2mToken) { - await request - .post(url) - .send(body) - .set('Authorization', `Bearer ${m2mToken}`) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json') -} - -/** - * Uses superagent to proxy put request - * @param {String} url the url - * @param {Object} body the JSON object body - * @param {String} m2mToken the M2M token - * @returns {Object} the response - */ -async function putRequest (url, body, m2mToken) { - await request - .put(url) - .send(body) - .set('Authorization', `Bearer ${m2mToken}`) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json') -} - -/** - * Fetch all resources. - * @param {String} url the url + * Fetch all resources using submission api wrapper client + * @param {Object} client the submission api wrapper client + * @param {String} methodName the wrapper method name * @param {Object} query the query object - * @param {String} m2mToken the M2M token * @returns {Object} the response */ -async function fetchAll (url, query, m2mToken) { - const res = await getRequest(url, query, m2mToken) +async function fetchAll (client, methodName, query) { + const res = await client[methodName](query) let result = res.body const totalPage = Number(res.header['x-total-pages']) if (totalPage > 1) { const requests = [] for (let i = 2; i <= totalPage; i++) { - requests.push(getRequest(url, _.assign({ page: i }, query), m2mToken)) + requests.push(client[methodName](_.assign({ page: i }, query))) } const extraRes = await Promise.all(requests) result = _.reduce(extraRes, (ret, e) => ret.concat(e.body), result) @@ -102,7 +70,5 @@ module.exports = { getKafkaOptions, getM2Mtoken, getRequest, - postRequest, - putRequest, fetchAll } diff --git a/src/services/ReviewProcessorService.js b/src/services/ReviewProcessorService.js index b062951..eb9b6e9 100644 --- a/src/services/ReviewProcessorService.js +++ b/src/services/ReviewProcessorService.js @@ -7,6 +7,8 @@ const joi = require('joi') const config = require('config') const logger = require('../common/logger') const helper = require('../common/helper') +const submissionApi = require('@topcoder-platform/topcoder-submission-api-wrapper') +const submissionApiM2MClient = submissionApi(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET', 'SUBMISSION_API_URL', 'AUTH0_PROXY_SERVER_URL'])) /** * Process create and update review message @@ -15,26 +17,27 @@ const helper = require('../common/helper') async function processReview (message) { const eventType = _.get(message, 'payload.eventType') if (eventType === 'CREATE' || eventType === 'UPDATE') { - const m2mToken = await helper.getM2Mtoken() const { payload } = message // get submission id via legacy id - const res = await helper.getRequest(config.SUBMISSION_API_URL, - { legacySubmissionId: payload.submissionId }, m2mToken) + logger.debug('Get submission') + const res = await submissionApiM2MClient.searchSubmissions({ legacySubmissionId: payload.submissionId }) const [submission] = res.body if (_.isUndefined(submission)) { throw new Error(`Incorrect submission id ${payload.submissionId}`) } + logger.debug(`Submission id: ${submission.id}`) // get review type UUID + logger.debug('Get review type') const typeName = config.REVIEW_TYPES[payload.reviewTypeId] - const list = await helper.fetchAll(config.REVIEW_TYPE_API_URL, - { name: typeName, isActive: true, perPage: 100 }, m2mToken) + const list = await helper.fetchAll(submissionApiM2MClient, 'searchReviewTypes', { name: typeName, isActive: true, perPage: 100 }) const reviewType = _.reduce(list, (result, e) => e.name.toLowerCase() === typeName.toLowerCase() ? e : result, undefined) if (_.isUndefined(reviewType)) { throw new Error(`Incorrect review type id ${payload.reviewTypeId}`) } + logger.debug(`Review type id: ${reviewType.id}`) // Use metadata to store legacy review id const body = { @@ -46,16 +49,18 @@ async function processReview (message) { } if (eventType === 'CREATE') { - await helper.postRequest(config.REVIEW_API_URL, body, m2mToken) + logger.debug('Create review') + await submissionApiM2MClient.createReview(body) } else { // retrieve review - let res = await helper.getRequest(config.REVIEW_API_URL, + logger.debug('Get review') + let res = await submissionApiM2MClient.searchReviews( { submissionId: submission.id, reviewerId: payload.reviewerId, scoreCardId: payload.scorecardId - }, - m2mToken) + } + ) const reviews = res.body || [] // find latest review let review @@ -71,8 +76,11 @@ async function processReview (message) { throw new Error(`Review doesn't exist under criteria ${_.pick(payload, ['submissionId', 'reviewerId', 'scorecardId'])}`) } + logger.debug(`Review id: ${review.id}`) + // update review - await helper.putRequest(`${config.REVIEW_API_URL}/${review.id}`, body, m2mToken) + logger.debug('Update review') + await submissionApiM2MClient.updateReview(review.id, body) } } else { throw new Error(`Invalid or not supported eventType: ${eventType}`) diff --git a/test/common/prepare.js b/test/common/prepare.js new file mode 100644 index 0000000..5beae8a --- /dev/null +++ b/test/common/prepare.js @@ -0,0 +1,44 @@ +/* + * Setting up Mock for all tests + */ + +const nock = require('nock') +const prepare = require('mocha-prepare') +const testData = require('../common/testData') + +prepare(function (done) { + nock(/\.com/) + .persist() + .post('/oauth/token') + .reply(200, { access_token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiQ29ubmVjdCBTdXBwb3J0IiwiYWRtaW5pc3RyYXRvciIsInRlc3RSb2xlIiwiYWFhIiwidG9ueV90ZXN0XzEiLCJDb25uZWN0IE1hbmFnZXIiLCJDb25uZWN0IEFkbWluIiwiY29waWxvdCIsIkNvbm5lY3QgQ29waWxvdCBNYW5hZ2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJUb255SiIsImV4cCI6MTU2NTY4MTkyMCwidXNlcklkIjoiODU0Nzg5OSIsImlhdCI6MTU1NTY4MTMyMCwiZW1haWwiOiJhamVmdHNAdG9wY29kZXIuY29tIiwianRpIjoiMTlhMDkzNzAtMjk4OC00N2I4LTkxODktMGRhODVjNjM0ZWQyIn0.V8nsQpbzQ_4iEd0dAbuYsfeydnhSAEQ95AKKwl8RONw' }) + .get('/v4/challenges/30049360') + .reply(200, testData.challenges['30049360']) + .get('/v4/challenges/30054674') + .reply(200, testData.challenges['30054674']) + .get('/v4/challenges/89898989') + .reply(404, testData.challenges['89898989']) + .get('/v5/submissions?legacySubmissionId=206743&') + .reply(200, [{ id: 'b91a0ca3-3988-4899-bab4-c789f22def39' }]) + .get('/v5/submissions?legacySubmissionId=111111111&') + .reply(200, []) + .get('/v5/reviewTypes?isActive=true&name=Review&perPage=100&') + .reply(200, [testData.reviewTypes[0]], { 'X-Total-Pages': 2 }) + .get('/v5/reviewTypes?isActive=true&name=Review&page=2&perPage=100&') + .reply(200, [testData.reviewTypes[1]], { 'X-Total-Pages': 2 }) + .get('/v5/reviewTypes?isActive=true&name=Post-Mortem&perPage=100&') + .reply(200, [], { 'X-Total-Pages': 1 }) + .get('/v5/reviews?reviewerId=151743&scoreCardId=300001610&submissionId=b91a0ca3-3988-4899-bab4-c789f22def39&') + .reply(200, testData.reviews, { 'X-Total-Pages': 1 }) + .get('/v5/reviews?reviewerId=151743&scoreCardId=300001611&submissionId=b91a0ca3-3988-4899-bab4-c789f22def39&') + .reply(200, [], { 'X-Total-Pages': 1 }) + .put('/v5/reviews/9c1c080a-b54f-46c4-b87b-6218038be765') + .reply(200) + .post('/v5/reviews') + .reply(200) + + done() +}, function (done) { +// called after all test completes (regardless of errors) + nock.cleanAll() + done() +}) diff --git a/test/common/testData.js b/test/common/testData.js index 7ec1c8b..c9a6b1b 100644 --- a/test/common/testData.js +++ b/test/common/testData.js @@ -122,8 +122,671 @@ const testTopics = { } } +const challenges = { + 30049360: { + 'id': '4da4e65e:16cb911536f:4771', + 'result': { + 'success': true, + 'status': 200, + 'metadata': null, + 'content': { + 'subTrack': 'CODE', + 'challengeTitle': 'Code Dev-Env Test', + 'challengeId': 30049360, + 'projectId': 7572, + 'forumId': 28457, + 'detailedRequirements': '

testing

\n', + 'reviewScorecardId': 30001610, + 'numberOfCheckpointsPrizes': 0, + 'postingDate': '2015-07-27T13:00:00.000Z', + 'registrationEndDate': '2019-12-02T14:00:00.000Z', + 'submissionEndDate': '2019-12-02T14:00:00.000Z', + 'reviewType': 'COMMUNITY', + 'forumLink': 'https://apps.topcoder.com/forums/?module=Category&categoryID=28457', + 'appealsEndDate': '2019-12-06T02:00:00.000Z', + 'currentStatus': 'Active', + 'challengeCommunity': 'develop', + 'directUrl': 'https://www.topcoder.com/direct/contest/detail.action?projectId=30049360', + 'prizes': [ + 350.0, + 150.0 + ], + 'terms': [ + { + 'termsOfUseId': 20704, + 'role': 'Primary Screener', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Reviewer Terms v1.0', + 'url': '' + }, + { + 'termsOfUseId': 20704, + 'role': 'Reviewer', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Reviewer Terms v1.0', + 'url': '' + }, + { + 'termsOfUseId': 21193, + 'role': 'Submitter', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Terms for TopCoder Competitions v2.1', + 'url': '' + }, + { + 'termsOfUseId': 20704, + 'role': 'Aggregator', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Reviewer Terms v1.0', + 'url': '' + }, + { + 'termsOfUseId': 20704, + 'role': 'Specification Reviewer', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Reviewer Terms v1.0', + 'url': '' + }, + { + 'termsOfUseId': 20704, + 'role': 'Final Reviewer', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Reviewer Terms v1.0', + 'url': '' + }, + { + 'termsOfUseId': 20794, + 'role': 'Manager', + 'agreeabilityType': 'Non-electronically-agreeable', + 'title': 'Approved OR Managers - TopCoder Technical Team', + 'url': 'http://www.topcoder.com' + }, + { + 'termsOfUseId': 20893, + 'role': 'Copilot', + 'agreeabilityType': 'Non-electronically-agreeable', + 'title': 'TopCoder Master Services Agreement', + 'url': 'http://www.topcoder.com/wiki/download/attachments/35129137/Member+Master+Agreement+v0020409.pdf' + } + ], + 'finalSubmissionGuidelines': '

testing

\n', + 'technologies': [ + '.NET' + ], + 'platforms': [ + 'Android' + ], + 'currentPhaseName': 'Registration', + 'currentPhaseRemainingTime': 5962969, + 'currentPhaseEndDate': '2019-12-02T14:00:00.000Z', + 'registrants': [ + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2015-10-27T17:20:32.000Z', + 'submissionDate': '2018-08-24T19:51:34.000Z', + 'handle': 'TonyJ' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2015-10-29T00:38:21.000Z', + 'handle': 'Ghostar' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2015-10-29T00:42:04.000Z', + 'submissionDate': '2015-10-29T04:21:18.000Z', + 'handle': 'hohosky' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2015-10-29T01:46:16.000Z', + 'handle': 'DhananjayKumar1' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2015-10-29T04:55:29.000Z', + 'submissionDate': '2015-10-29T05:06:51.000Z', + 'handle': 'dplass' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2015-10-29T05:09:02.000Z', + 'submissionDate': '2015-10-29T06:20:36.000Z', + 'handle': 'dmwright' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2015-07-30T23:37:34.000Z', + 'submissionDate': '2015-10-29T00:01:36.000Z', + 'handle': 'albertwang' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2017-10-07T14:03:34.000Z', + 'submissionDate': '2017-11-28T20:28:56.000Z', + 'handle': 'dan_developer' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2017-10-13T17:57:34.000Z', + 'handle': 'tjefts_block15' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2017-10-13T17:58:56.000Z', + 'handle': 'tjefts_block20' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2017-11-11T20:53:27.000Z', + 'submissionDate': '2018-03-15T06:39:40.033Z', + 'handle': 'amy_admin' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2018-03-11T09:02:55.000Z', + 'handle': 'kevinkid' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2018-03-23T08:17:30.000Z', + 'handle': 'sushil1000' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2018-05-24T21:19:45.000Z', + 'handle': 'bop_t26' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2018-08-20T23:39:04.000Z', + 'handle': 'hoho123456' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2018-10-22T14:48:10.000Z', + 'handle': 'TestAccount1' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2019-04-08T15:14:39.000Z', + 'handle': 'huanner' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2019-05-13T19:02:21.238Z', + 'handle': 'lazybaer' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2019-08-01T17:29:50.000Z', + 'handle': 'callmekatootie' + }, + { + 'reliability': null, + 'colorStyle': 'color: #555555', + 'rating': 1, + 'registrationDate': '2019-08-03T06:36:25.000Z', + 'handle': 'easteregg' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2019-08-03T09:31:02.000Z', + 'handle': 'FireIce' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2019-08-26T12:22:53.000Z', + 'handle': 'jcori' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2019-09-20T10:41:52.000Z', + 'handle': 'mess' + } + ], + 'phases': [ + { + 'duration': 137293200000, + 'actualStartTime': '2015-07-27T13:00:00.000Z', + 'scheduledStartTime': '2015-07-27T13:00:00.000Z', + 'phaseId': 733195, + 'scheduledEndTime': '2019-12-02T14:00:00.000Z', + 'fixedStartTime': '2015-07-27T13:00:00.000Z', + 'type': 'Registration', + 'status': 'Open' + }, + { + 'duration': 137292900000, + 'scheduledStartTime': '2015-07-27T13:05:00.000Z', + 'phaseId': 733196, + 'scheduledEndTime': '2019-12-02T14:00:00.000Z', + 'type': 'Submission', + 'status': 'Closed' + }, + { + 'duration': 172800000, + 'scheduledStartTime': '2019-12-02T14:00:00.000Z', + 'phaseId': 733197, + 'scheduledEndTime': '2019-12-04T14:00:00.000Z', + 'type': 'Review', + 'status': 'Scheduled' + }, + { + 'duration': 86400000, + 'scheduledStartTime': '2019-12-04T14:00:00.000Z', + 'phaseId': 733198, + 'scheduledEndTime': '2019-12-05T14:00:00.000Z', + 'type': 'Appeals', + 'status': 'Scheduled' + }, + { + 'duration': 43200000, + 'scheduledStartTime': '2019-12-05T14:00:00.000Z', + 'phaseId': 733199, + 'scheduledEndTime': '2019-12-06T02:00:00.000Z', + 'type': 'Appeals Response', + 'status': 'Scheduled' + } + ], + 'submissions': [ + { + 'submitter': 'albertwang', + 'submitterId': 10336829, + 'submissions': [ + { + 'submissionId': 509262, + 'submissionStatus': 'Active', + 'submissionTime': '2015-10-29T00:01:36.000Z' + } + ] + }, + { + 'submitter': 'hohosky', + 'submitterId': 16096823, + 'submissions': [ + { + 'submissionId': 509263, + 'submissionStatus': 'Active', + 'submissionTime': '2015-10-29T04:21:18.000Z' + } + ] + }, + { + 'submitter': 'dplass', + 'submitterId': 251184, + 'submissions': [ + { + 'submissionId': 509265, + 'submissionStatus': 'Active', + 'submissionTime': '2015-10-29T05:06:51.000Z' + } + ] + }, + { + 'submitter': 'dmwright', + 'submitterId': 114853, + 'submissions': [ + { + 'submissionId': 509267, + 'submissionStatus': 'Active', + 'submissionTime': '2015-10-29T06:20:36.000Z' + } + ] + }, + { + 'submitter': 'TonyJ', + 'submitterId': 8547899, + 'submissions': [ + { + 'submissionId': 205459, + 'submissionStatus': 'Active', + 'submissionTime': '2018-08-24T19:51:34.000Z' + } + ] + }, + { + 'submitter': 'amy_admin', + 'submitterId': 40153455, + 'submissions': [ + { + 'submissionId': 204922, + 'submissionStatus': 'Active', + 'submissionTime': '2018-03-15T06:39:40.033Z' + } + ] + }, + { + 'submitter': 'dan_developer', + 'submitterId': 40152905, + 'submissions': [ + { + 'submissionId': 509401, + 'submissionStatus': 'Active', + 'submissionTime': '2017-11-28T20:28:56.000Z' + } + ] + } + ], + 'checkpoints': [], + 'numberOfRegistrants': 23, + 'numberOfSubmissions': 7, + 'numberOfSubmitters': 7 + } + }, + 'version': 'v3' + }, + 30054674: { + 'id': '4da4e65e:16cb911536f:4774', + 'result': { + 'success': true, + 'status': 200, + 'metadata': null, + 'content': { + 'subTrack': 'CODE', + 'challengeTitle': 'Test for submission review app api 12345', + 'challengeId': 30054674, + 'projectId': 7817, + 'forumId': 32818, + 'detailedRequirements': '

[This contest specification will be made available to all competitors for an Assembly competition. It is intended to give the competitors (and reviewers) all the necessary information and detailed instructions to understand what is expected for the Assembly competition.

\n\n

This specification should not include any company specific information or proprietary information because it will be available to users who have not signed confidentiality assignments.

\n\n

This specification will be reviewed internally by TopCoder to verify the following points:

\n\n\n\n

The Architect will be responsible for this specification. However, there are numerous sections that will be completed by the Copilot or the Project Manager.]

\n\n

Project Overview

\n\n

[Provide an overview of the overall application the Assembly competition is implementing.]

\n\n

EXAMPLE:

\n\n

The Sales IM tool is an application that will create a web based instant messaging system.  The system    will be used installed and run on a company's web site. The tool will allow potential clients of the company to ask questions and chat with the company.

\n\n

Competition Task Overview

\n\n

[Provide an overview of the pieces of the application that the Assembly competition is implementing.]

\n\n

EXAMPLE:
\nHTML prototype has been converted to JSP pages with the correct flows. The majority of the application logic has been developed inside the components. The main tasks will involve:

\n\n\n\n

[You should break out 2.X points if there are any complicated instructions to the members.]

\n\n

Testing

\n\n

[Provide Testing Requirements. This section should include any testing requirements (unit tests) that will help review the application.]

\n\n

EXAMPLE:
\nUnit test are only needed for NASWorker.cs class.

\n\n

You need to provide manual demos to verify your implementation.

\n\n

Technology Overview

\n\n

[List the relevant technologies for the project. This is intended to help the interested competitor determine if they are able to compete on the project. Include any details on VM/etc. in this section.]

\n\n

EXAMPLE:
\nThe working environment requirement details for this application are outlined in the Application Requirements Specification. An overview of the environment requirements are listed below:

\n\n\n\n

Documentation Provided

\n\n

[The majority of the documentation deliverables should be consistent across all applications. However, document any additional information that will be provided for this project and remove any documents in this list that are not applicable.]

\n\n

Documentation and Applications that will be provided to registered members:

\nDocument NameDocument Description                                
\n[Deliverable Information][Deliverable Information]?
\nRequirements Documentation
\n
\nDatabase Schema
\n
\nHTML Prototype
\n
\nJSP Converted Prototype
\n
\nSample Data\n

Project Dependencies

\n\n

Custom / New Generic Components

\n\n

[List all custom and new generic components that are required for this application.  This is useful for competitors to know where to look for component information if the component is not in the catalog yet. This is also useful for managers and architects to easily check which new components are dependent on this Assembly.

\n\n

** Note that all new and custom components will need to be posted to the forums for this competition unless they are made available in the catalog prior to the start of the competition.]

\n\n
 
\n\n
Component NameVersion         Generic / Custom               Component URL
\n
\n
\n
\n
\n
\n
\n
\n
\n 
\n', + 'reviewScorecardId': 30001610, + 'numberOfCheckpointsPrizes': 0, + 'postingDate': '2019-02-05T14:15:18.544Z', + 'registrationEndDate': '2019-02-06T14:16:59.418Z', + 'submissionEndDate': '2019-02-06T14:17:07.247Z', + 'reviewType': 'COMMUNITY', + 'forumLink': 'https://apps.topcoder.com/forums/?module=Category&categoryID=32818', + 'appealsEndDate': '2019-02-06T15:09:43.244Z', + 'currentStatus': 'Completed', + 'challengeCommunity': 'develop', + 'directUrl': 'https://www.topcoder.com/direct/contest/detail.action?projectId=30054674', + 'prizes': [ + 1000.0, + 500.0 + ], + 'terms': [ + { + 'termsOfUseId': 20704, + 'role': 'Primary Screener', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Reviewer Terms v1.0', + 'url': '' + }, + { + 'termsOfUseId': 20704, + 'role': 'Reviewer', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Reviewer Terms v1.0', + 'url': '' + }, + { + 'termsOfUseId': 21303, + 'role': 'Submitter', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Terms for TopCoder Competitions v2.2', + 'url': '' + }, + { + 'termsOfUseId': 20704, + 'role': 'Aggregator', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Reviewer Terms v1.0', + 'url': '' + }, + { + 'termsOfUseId': 20704, + 'role': 'Specification Reviewer', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Reviewer Terms v1.0', + 'url': '' + }, + { + 'termsOfUseId': 20704, + 'role': 'Final Reviewer', + 'agreeabilityType': 'Electronically-agreeable', + 'title': 'Standard Reviewer Terms v1.0', + 'url': '' + }, + { + 'termsOfUseId': 20794, + 'role': 'Manager', + 'agreeabilityType': 'Non-electronically-agreeable', + 'title': 'Approved OR Managers - TopCoder Technical Team', + 'url': 'http://www.topcoder.com' + }, + { + 'termsOfUseId': 20893, + 'role': 'Copilot', + 'agreeabilityType': 'Non-electronically-agreeable', + 'title': 'TopCoder Master Services Agreement', + 'url': 'http://www.topcoder.com/wiki/download/attachments/35129137/Member+Master+Agreement+v0020409.pdf' + } + ], + 'winners': [ + { + 'submitter': 'denis', + 'submissionId': 206743, + 'rank': 1, + 'submissionTime': '2019-02-05T15:58:32.000Z', + 'points': 96.25 + }, + { + 'submitter': 'lars2520', + 'submissionId': 206744, + 'rank': 2, + 'submissionTime': '2019-02-05T16:00:20.000Z', + 'points': 92.5 + } + ], + 'finalSubmissionGuidelines': '

[This contest specification will be made available to all competitors for an Assembly competition. It is intended to give the competitors (and reviewers) all the necessary information and detailed instructions to understand what is expected for the Assembly competition.

\n\n

This specification should not include any company specific information or proprietary information because it will be available to users who have not signed confidentiality assignments.

\n\n

This specification will be reviewed internally by TopCoder to verify the following points:

\n\n\n\n

The Architect will be responsible for this specification. However, there are numerous sections that will be completed by the Copilot or the Project Manager.]

\n\n

Submission Deliverables

\n\n

A complete list of deliverables can be viewed in the TopCoder Assembly competition Tutorial at: http://apps.topcoder.com/wiki/display/tc/Assembly+Competition+Tutorials 

\n\n

[Document any specific deliverables associated with this competition]

\n\n

EXAMPLE:
\nBelow is an overview of the deliverables:

\n\n\n\n

Final Submission

\n\n

For each member, the final submission should be uploaded via the challenge detail page on topcoder.com.

\n\n

[Add any special instructions or restrictions on submission here.]

\n\n

Environment Setup

\n\n

[By preparing a ready-to-use development environment, you can greatly increase the chances for a high quality submission. Try to give competitors an environment that needs minimal configuration and setup so members can focus on coding a solution rather than preparing to code one. Absolutely critical to this setup is providing valid test data. Relying on competitors to make their test data is error prone and will lead to other issues in downstream development efforts.]

\n\n

Source code setup

\n\n

[The architect should document how the source code should be set up and packaged if different from the standard used in Assemblies.]

\n\n

Build Setup

\n\n

[Document all the ant tasks that are required.]

\n\n

The ant 'compile' and ant 'build' commands should be implemented to compile the source code and create the war file.

\n\n

[Add something about how you can run an ant task to clear test data]

\n\n

[Add something about how you can run an ant task to load the lookup / sample data]

\n\n

SVN Access

\n\n

[Each member might need to be assigned SVN access. Please list all SVN branches which members need to request access to.]

\n\n
Repository NameURL
\nOnline Reviewhttps://coder.topcoder.com/tcs/clients/name/branches
\nStudiohttps://coder.topcoder.com/client/branches/name
\nDatabaseshttps://coder.topcoder.com/client/trunk
\n\n

Development Environment

\n\n

[This section should document what environment software will be available to each member.

\n\n

For example:

\n\n\n\n

Document how members will be given access / permissions]

\n', + 'technologies': [ + 'Android' + ], + 'platforms': [ + 'Android' + ], + 'currentPhaseName': 'Stalled', + 'registrants': [ + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2019-02-05T15:47:19.080Z', + 'submissionDate': '2019-02-05T15:58:32.000Z', + 'handle': 'denis' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2019-02-05T15:53:34.534Z', + 'submissionDate': '2019-02-05T16:00:20.000Z', + 'handle': 'lars2520' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2019-03-19T08:38:04.399Z', + 'handle': 'easteregg' + }, + { + 'reliability': null, + 'colorStyle': 'color: #151516', + 'registrationDate': '2019-09-13T14:39:24.097Z', + 'handle': 'sachin-kumar' + } + ], + 'phases': [ + { + 'duration': 86400000, + 'actualStartTime': '2019-02-05T14:15:18.544Z', + 'scheduledStartTime': '2019-02-05T14:15:18.544Z', + 'phaseId': 764423, + 'scheduledEndTime': '2019-02-06T14:16:59.418Z', + 'fixedStartTime': '2019-02-05T14:00:00.000Z', + 'actualEndTime': '2019-02-06T14:16:59.418Z', + 'type': 'Registration', + 'status': 'Closed' + }, + { + 'duration': 86100000, + 'actualStartTime': '2019-02-05T14:20:39.454Z', + 'scheduledStartTime': '2019-02-05T14:20:39.454Z', + 'phaseId': 764424, + 'scheduledEndTime': '2019-02-06T14:17:07.247Z', + 'actualEndTime': '2019-02-06T14:17:07.247Z', + 'type': 'Submission', + 'status': 'Closed' + }, + { + 'duration': 172800000, + 'actualStartTime': '2019-02-06T14:17:14.057Z', + 'scheduledStartTime': '2019-02-06T14:17:14.057Z', + 'phaseId': 764425, + 'scheduledEndTime': '2019-02-06T14:42:50.396Z', + 'actualEndTime': '2019-02-06T14:42:50.396Z', + 'type': 'Review', + 'status': 'Closed' + }, + { + 'duration': 86400000, + 'actualStartTime': '2019-02-06T14:42:53.338Z', + 'scheduledStartTime': '2019-02-06T14:42:53.338Z', + 'phaseId': 764426, + 'scheduledEndTime': '2019-02-06T14:48:45.275Z', + 'actualEndTime': '2019-02-06T14:48:45.275Z', + 'type': 'Appeals', + 'status': 'Closed' + }, + { + 'duration': 43200000, + 'actualStartTime': '2019-02-06T14:48:48.059Z', + 'scheduledStartTime': '2019-02-06T14:48:48.059Z', + 'phaseId': 764427, + 'scheduledEndTime': '2019-02-06T15:09:43.244Z', + 'actualEndTime': '2019-02-06T15:09:43.244Z', + 'type': 'Appeals Response', + 'status': 'Closed' + } + ], + 'submissions': [ + { + 'submitter': 'denis', + 'submitterId': 251280, + 'submissions': [ + { + 'finalScore': 96.25, + 'submissionId': 206743, + 'submissionStatus': 'Active', + 'initialScore': 95, + 'placement': 1, + 'submissionTime': '2019-02-05T15:58:32.000Z' + } + ] + }, + { + 'submitter': 'lars2520', + 'submitterId': 287131, + 'submissions': [ + { + 'finalScore': 92.5, + 'submissionId': 206744, + 'submissionStatus': 'Completed Without Win', + 'initialScore': 90, + 'placement': 2, + 'submissionTime': '2019-02-05T16:00:20.000Z' + } + ] + } + ], + 'checkpoints': [], + 'numberOfRegistrants': 4, + 'numberOfSubmissions': 2, + 'numberOfSubmitters': 2 + } + }, + 'version': 'v3' + }, + 89898989: { + 'id': '4da4e65e:16cb911536f:4772', + 'result': { + 'success': true, + 'status': 404, + 'metadata': null, + 'content': 'The challenge does not exists with the id:89898989' + }, + 'version': 'v3' + } +} + +const reviewTypes = [ + { + name: 'Review', + id: 'ff5742d6-22bf-4734-b632-add6641078be', + isActive: true + }, + { + name: 'test-for-review', + id: 'af5742d6-22bf-4734-b632-add6641078be', + isActive: true + } +] + +const reviews = [ + { + 'legacyReviewId': 123456788, + 'score': 92.5, + 'updatedBy': 'callmekatootie', + 'reviewerId': 'c56a4180-65aa-42ec-a945-5fd21d3d26f8', + 'submissionId': 'a12a4180-65aa-42ec-a945-5fd21dec0505', + 'createdBy': 'callmekatootie', + 'created': '2019-09-24T00:37:38.537Z', + 'scoreCardId': 123456789, + 'typeId': 'c56a4180-65aa-42ec-a945-5fd21dec0503', + 'id': '9c1c080a-b54f-46c4-b87b-6218038be765', + 'updated': '2019-09-24T00:37:38.537Z', + 'status': 'queued' + }, + { + 'score': 100, + 'updatedBy': 'maE2maBSv9fRVHjSlC31LFZSq6VhhZqC@clients', + 'reviewerId': '7b3e99d2-cc1f-43d8-811d-350b48267589', + 'submissionId': 'b91a0ca3-3988-4899-bab4-c789f22def39', + 'createdBy': 'maE2maBSv9fRVHjSlC31LFZSq6VhhZqC@clients', + 'created': '2019-09-23T17:25:04.255Z', + 'scoreCardId': 30001850, + 'typeId': '68c5a381-c8ab-48af-92a7-7a869a4ee6c3', + 'id': '41f30ab2-726f-4f4c-a922-c46eff0d7256', + 'updated': '2019-09-23T17:25:04.255Z', + 'status': 'completed' + } +] + module.exports = { avScanTopic, reviewActionTopic, - testTopics + testTopics, + challenges, + reviewTypes, + reviews } diff --git a/test/e2e/review.processor.test.js b/test/e2e/review.processor.test.js index f0914ee..ea82583 100644 --- a/test/e2e/review.processor.test.js +++ b/test/e2e/review.processor.test.js @@ -6,32 +6,14 @@ process.env.NODE_ENV = 'test' global.Promise = require('bluebird') const _ = require('lodash') -const config = require('config') const should = require('should') const request = require('superagent') -const helper = require('../../src/common/helper') -const logger = require('../../src/common/logger') const testHelper = require('../common/testHelper') const { testTopics } = require('../common/testData') describe('Topcoder - Scorecard Review Processor E2E Test', () => { - let m2mToken - before(async () => { - // generate M2M token - m2mToken = await helper.getM2Mtoken() - - // clear reviews if any - const submissionId = '5035ded4-db41-4198-9fdf-096671114317' - let res = await helper.getRequest(config.REVIEW_API_URL, - { submissionId, reviewerId: 151743, scoreCardId: 300001610 }, - m2mToken) - const reviews = res.body || [] - for (let i = 0; i < reviews.length; i += 1) { - await testHelper.deleteRequest(`${config.REVIEW_API_URL}/${reviews[i].id}`, m2mToken) - } - // consume and commit existing messages if any await testHelper.consumeMessages() // init kafka producer @@ -48,24 +30,6 @@ describe('Topcoder - Scorecard Review Processor E2E Test', () => { testHelper.restoreLogger() await testHelper.stopProducer() - - const submissionId = '5035ded4-db41-4198-9fdf-096671114317' - let res = await helper.getRequest(config.REVIEW_API_URL, - { submissionId, reviewerId: 151743, scoreCardId: 300001610 }, - m2mToken) - const [review] = res.body - await testHelper.deleteRequest(`${config.REVIEW_API_URL}/${review.id}`, m2mToken) - - res = await helper.getRequest(`${config.REVIEW_SUMMATION_API_URL}`, { submissionId }, m2mToken) - const [reviewSummation] = res.body - if (reviewSummation) { - try { - await testHelper.deleteRequest(`${config.REVIEW_SUMMATION_API_URL}/${reviewSummation.id}`, m2mToken) - } catch (e) { - // deleting review summation is optional, so we may ignore error here - logger.error(`Ignored error of deleting review summation: ${e.message}`) - } - } }) beforeEach(() => { @@ -110,29 +74,25 @@ describe('Topcoder - Scorecard Review Processor E2E Test', () => { it('processor create review success', async () => { await testHelper.sendMessage(testTopics.create.testMessage) await testHelper.waitJob() - // wait for the data updated in remote server - await testHelper.sleep(config.WAIT_TIME) - const submissionId = '5035ded4-db41-4198-9fdf-096671114317' - let res = await helper.getRequest(config.REVIEW_API_URL, - { submissionId, reviewerId: 151743, scoreCardId: 300001610 }, - m2mToken) - const [review] = res.body - should.equal(review.score, 90) - should.equal(review.typeId, 'c56a4180-65aa-42ec-a945-5fd21dec0503') + + testHelper.assertDebugMessage('Get submission') + testHelper.assertDebugMessage('Submission id: b91a0ca3-3988-4899-bab4-c789f22def39') + testHelper.assertDebugMessage('Get review type') + testHelper.assertDebugMessage('Review type id: ff5742d6-22bf-4734-b632-add6641078be') + testHelper.assertDebugMessage('Create review') }) it('processor update review success', async () => { await testHelper.sendMessage(testTopics.update.testMessage) await testHelper.waitJob() - // wait for the data updated in remote server - await testHelper.sleep(config.WAIT_TIME) - const submissionId = '5035ded4-db41-4198-9fdf-096671114317' - let res = await helper.getRequest(config.REVIEW_API_URL, - { submissionId, reviewerId: 151743, scoreCardId: 300001610 }, - m2mToken) - const [review] = res.body - should.equal(review.score, 95) - should.equal(review.typeId, 'c56a4180-65aa-42ec-a945-5fd21dec0503') + + testHelper.assertDebugMessage('Get submission') + testHelper.assertDebugMessage('Submission id: b91a0ca3-3988-4899-bab4-c789f22def39') + testHelper.assertDebugMessage('Get review type') + testHelper.assertDebugMessage('Review type id: ff5742d6-22bf-4734-b632-add6641078be') + testHelper.assertDebugMessage('Get review') + testHelper.assertDebugMessage('Review id: 9c1c080a-b54f-46c4-b87b-6218038be765') + testHelper.assertDebugMessage('Update review') }) it('test invalid parameters, userId is forbidden for create review message.', async () => { diff --git a/test/unit/review.processor.test.js b/test/unit/review.processor.test.js index 44dd61a..eb26204 100644 --- a/test/unit/review.processor.test.js +++ b/test/unit/review.processor.test.js @@ -7,53 +7,17 @@ global.Promise = require('bluebird') const _ = require('lodash') const config = require('config') -const should = require('should') -const helper = require('../../src/common/helper') -const logger = require('../../src/common/logger') const ReviewProcessorService = require('../../src/services/ReviewProcessorService') const testHelper = require('../common/testHelper') const { testTopics } = require('../common/testData') describe('Topcoder - Scorecard Review Processor Unit Test', () => { - let m2mToken - before(async () => { - // generate M2M token - m2mToken = await helper.getM2Mtoken() - - // clear reviews if any - const submissionId = '5035ded4-db41-4198-9fdf-096671114317' - let res = await helper.getRequest(config.REVIEW_API_URL, - { submissionId, reviewerId: 151743, scoreCardId: 300001610 }, - m2mToken) - const reviews = res.body || [] - for (let i = 0; i < reviews.length; i += 1) { - await testHelper.deleteRequest(`${config.REVIEW_API_URL}/${reviews[i].id}`, m2mToken) - } - testHelper.interceptLogger() }) after(async () => { testHelper.restoreLogger() - - const submissionId = '5035ded4-db41-4198-9fdf-096671114317' - let res = await helper.getRequest(config.REVIEW_API_URL, - { submissionId, reviewerId: 151743, scoreCardId: 300001610 }, - m2mToken) - const [review] = res.body - await testHelper.deleteRequest(`${config.REVIEW_API_URL}/${review.id}`, m2mToken) - - res = await helper.getRequest(`${config.REVIEW_SUMMATION_API_URL}`, { submissionId }, m2mToken) - const [reviewSummation] = res.body - if (reviewSummation) { - try { - await testHelper.deleteRequest(`${config.REVIEW_SUMMATION_API_URL}/${reviewSummation.id}`, m2mToken) - } catch (e) { - // deleting review summation is optional, so we may ignore error here - logger.error(`Ignored error of deleting review summation: ${e.message}`) - } - } }) beforeEach(() => { @@ -62,28 +26,24 @@ describe('Topcoder - Scorecard Review Processor Unit Test', () => { it('processor create review success', async () => { await ReviewProcessorService.processReview(testTopics.create.testMessage) - // wait for the data updated in remote server - await testHelper.sleep(config.WAIT_TIME) - const submissionId = '5035ded4-db41-4198-9fdf-096671114317' - let res = await helper.getRequest(config.REVIEW_API_URL, - { submissionId, reviewerId: 151743, scoreCardId: 300001610 }, - m2mToken) - const [review] = res.body - should.equal(review.score, 90) - should.equal(review.typeId, 'c56a4180-65aa-42ec-a945-5fd21dec0503') + + testHelper.assertDebugMessage('Get submission') + testHelper.assertDebugMessage('Submission id: b91a0ca3-3988-4899-bab4-c789f22def39') + testHelper.assertDebugMessage('Get review type') + testHelper.assertDebugMessage('Review type id: ff5742d6-22bf-4734-b632-add6641078be') + testHelper.assertDebugMessage('Create review') }) it('processor update review success', async () => { await ReviewProcessorService.processReview(testTopics.update.testMessage) - // wait for the data updated in remote server - await testHelper.sleep(config.WAIT_TIME) - const submissionId = '5035ded4-db41-4198-9fdf-096671114317' - let res = await helper.getRequest(config.REVIEW_API_URL, - { submissionId, reviewerId: 151743, scoreCardId: 300001610 }, - m2mToken) - const [review] = res.body - should.equal(review.score, 95) - should.equal(review.typeId, 'c56a4180-65aa-42ec-a945-5fd21dec0503') + + testHelper.assertDebugMessage('Get submission') + testHelper.assertDebugMessage('Submission id: b91a0ca3-3988-4899-bab4-c789f22def39') + testHelper.assertDebugMessage('Get review type') + testHelper.assertDebugMessage('Review type id: ff5742d6-22bf-4734-b632-add6641078be') + testHelper.assertDebugMessage('Get review') + testHelper.assertDebugMessage('Review id: 9c1c080a-b54f-46c4-b87b-6218038be765') + testHelper.assertDebugMessage('Update review') }) it('test invalid parameters, userId is forbidden for create review message.', async () => { @@ -115,7 +75,7 @@ describe('Topcoder - Scorecard Review Processor Unit Test', () => { await ReviewProcessorService.processReview(message) throw new Error('should not throw error here') } catch (err) { - err.message.should.containEql('Invalid or not supported eventType: INVALID_TYPE') + testHelper.assertErrorMessage('Invalid or not supported eventType: INVALID_TYPE') } })