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

Add robust unit tests #25

Merged
merged 5 commits into from
Dec 16, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,6 @@ public/
.topcoderrc

# package-lock.json vary while installing on different operating system
package-lock.json
package-lock.json
submission-*-artifacts
*-submissions
144 changes: 78 additions & 66 deletions bin/topcoder-cli.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,75 @@
#!/usr/bin/env node

const program = require('commander')
const { Command } = require('commander')
const submissionHandler = require('../src/commands/submit')
const payHandler = require('../src/commands/pay')
const configHandler = require('../src/commands/config')
const fetchSubmissionHandler = require('../src/commands/fetchSubmissions')
const fetchArtifactsHandler = require('../src/commands/fetchArtifacts')
const logger = require('../src/common/logger')

const docs = {
submit: `\nEither use CLI parameters or Create a file .topcoderrc in JSON ` +
`format with below details\n` +
`{\n` +
` "memberId": "<Topcoder memberId",\n` +
` "challengeIds": [\n` +
` "30095545" // at least one item here\n` +
` ],\n` +
` "username": "<Topcoder username>",\n` +
` "password": "<Topcoder password>",\n` +
` "m2m": {\n` +
` "client_id": "<Client ID for M2M authentication>",\n` +
` "client_secret": "<Client Secret for M2M authentication>"\n` +
` }\n` +
`}\n` +
`and execute command \`topcoder submit\` to submit the contents of ` +
`current working directory except .topcoderrc file to the challenge.\n` +
`You'd need either the m2m config or the username and password, but ` +
`not both.`,
'fetch-submissions': `\nUse CLI parameters or create a file .topcoderrc in JSON format with below details\n` +
`{\n` +
` "memberId": "<Topcoder memberId",\n` +
` "challengeId": "<Topcoder challengeId",\n` +
` "submissionId": "<Topcoder submissionId",\n` +
` "latest": true,\n` +
` "username": "<Topcoder username>",\n` +
` "password": "<Topcoder password>",\n` +
` "m2m": {\n` +
` "client_id": "<Client ID for M2M authentication>",\n` +
` "client_secret": "<Client Secret for M2M authentication>"\n` +
` }\n` +
`}\n` +
`and execute command \`topcoder fetch-submissions\` to fetch submissions ` +
`for a challenge and save them.\n` +
`You may specify the m2m config or the username and password config, ` +
`but not both.\n` +
`If the submissionId parameter is provided, you must not provide the ` +
`memberId or the latest parameters.\n` +
`The challengeId parameter is always required.`,
'fetch-artifacts': `\nUse CLI parameters or create a file .topcoderrc in JSON format ` +
`with below details\n` +
`{\n` +
` "submissionId": "<Topcoder submissionId>",\n` +
` "legacySubmissionId": "<Topcoder legacySubmissionId>",\n` +
` "username": "<Topcoder username>",\n` +
` "password": "<Topcoder password>",\n` +
` "m2m": {\n` +
` "client_id": "<Client ID for M2M authentication>",\n` +
` "client_secret": "<Client Secret for M2M authentication>"\n` +
` }\n` +
`}\n` +
`and execute command \`topcoder fetch-artifacts\` to fetch submissions for` +
` a challenge and save them.\n` +
`You may specify the m2m config or the username and password config, ` +
`but not both.\n` +
`If the submissionId parameter is provided, you must not provide the the ` +
`legacySubmissionId parameters, and vice-versa.`

}

const program = new Command()

// Overall help text which will be displayed after usage information
program.on('--help', () => {
console.log('\nTopcoder CLI to interact with Topcoder systems\n')
Expand All @@ -30,26 +92,7 @@ program
)
.option('--dev', 'Points to Topcoder development environment')
.on('--help', () => {
console.log(
`\nEither use CLI parameters or Create a file .topcoderrc in JSON ` +
`format with below details\n` +
`{\n` +
` "memberId": "<Topcoder memberId",\n` +
` "challengeIds": [\n` +
` "30095545" // at least one item here\n` +
` ],\n` +
` "username": "<Topcoder username>",\n` +
` "password": "<Topcoder password>",\n` +
` "m2m": {\n` +
` "client_id": "<Client ID for M2M authentication>",\n` +
` "client_secret": "<Client Secret for M2M authentication>"\n` +
` }\n` +
`}\n` +
`and execute command \`topcoder submit\` to submit the contents of ` +
`current working directory except .topcoderrc file to the challenge.\n` +
`You'd need either the m2m config or the username and password, but ` +
`not both.`
)
console.log(docs.submit)
})
.action(async args => {
try {
Expand Down Expand Up @@ -82,28 +125,7 @@ program
.option('-l, --latest', 'fetch only the latest submission of each member')
.option('--dev', 'Points to Topcoder development environment')
.on('--help', () => {
console.log(
`\nUse CLI parameters or create a file .topcoderrc in JSON format with below details\n` +
`{\n` +
` "memberId": "<Topcoder memberId",\n` +
` "challengeId": "<Topcoder challengeId",\n` +
` "submissionId": "<Topcoder submissionId",\n` +
` "latest": true,\n` +
` "username": "<Topcoder username>",\n` +
` "password": "<Topcoder password>",\n` +
` "m2m": {\n` +
` "client_id": "<Client ID for M2M authentication>",\n` +
` "client_secret": "<Client Secret for M2M authentication>"\n` +
` }\n` +
`}\n` +
`and execute command \`topcoder fetch-submissions\` to fetch submissions ` +
`for a challenge and save them.\n` +
`You may specify the m2m config or the username and password config, ` +
`but not both.\n` +
`If the submissionId parameter is provided, you must not provide the ` +
`memberId or the latest parameters.\n` +
`The challengeId parameter is always required.`
)
console.log(docs['fetch-submissions'])
})
.action(async args => {
try {
Expand Down Expand Up @@ -132,24 +154,7 @@ program
.option('--dev', 'Points to Topcoder development environment')
.on('--help', () => {
console.log(
`\nUse CLI parameters or create a file .topcoderrc in JSON format ` +
`with below details\n` +
`{\n` +
` "submissionId": "<Topcoder submissionId>",\n` +
` "legacySubmissionId": "<Topcoder legacySubmissionId>",\n` +
` "username": "<Topcoder username>",\n` +
` "password": "<Topcoder password>",\n` +
` "m2m": {\n` +
` "client_id": "<Client ID for M2M authentication>",\n` +
` "client_secret": "<Client Secret for M2M authentication>"\n` +
` }\n` +
`}\n` +
`and execute command \`topcoder fetch-artifacts\` to fetch submissions for` +
` a challenge and save them.\n` +
`You may specify the m2m config or the username and password config, ` +
`but not both.\n` +
`If the submissionId parameter is provided, you must not provide the the ` +
`legacySubmissionId parameters, and vice-versa.`
docs['fetch-artifacts']
)
})
.action(async args => {
Expand Down Expand Up @@ -186,7 +191,7 @@ program
if (args.dev) {
process.env.NODE_ENV = 'dev'
}
payHandler.handleCommand(args)
payHandler.handleCommand(program.args)
})

// error on unknown commands
Expand All @@ -199,9 +204,16 @@ program.on('command:*', function () {
process.exit(1)
})

program.parse(process.argv)
/* istanbul ignore next */
if (!module.parent) {
program.parse(process.argv)
// If the CLI is invoked without any command, display help
if (process.argv.length < 3) {
program.help()
}
}

// If the CLI is invoked without any command, display help
if (process.argv.length < 3) {
program.help()
module.exports = {
program,
docs
}
2 changes: 1 addition & 1 deletion config/prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
process.env.TC_MEMBERS_API || 'https://api.topcoder.com/v3/members',
SUBMISSION_API_URL:
process.env.TEST_SUBMISSION_API_URL ||
'https://api.topcoder.com/v5/submissions',
'https://api.topcoder.com/v5',
AUTH0_URL: process.env.AUTH0_URL || 'https://topcoder.auth0.com/oauth/token',
TC_AUTHN_URL:
process.env.TC_AUTHN_URL || 'https://topcoder.auth0.com/oauth/ro',
Expand Down
8 changes: 8 additions & 0 deletions docs/Development.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ The following parameters can be set in config files or in env variables:
| TC_CLIENT_V2CONNECTION | CLIENT_V2CONNECTION | TC-User-Database | TC client connection protocol |
| AUTH0_AUDIENCE | AUTH0_AUDIENCE | https://m2m.topcoder.com/ | AUTH0 Audience (For M2M) |

# Configuration for Test
Configuration for test is at `test/common/testConfig.js`.
The following parameters can be set in config files or in env variables:

| Property | Environment varible | Default value | Description |
| --- | --- | --- | --- |
| WAIT_TIME | WAIT_TIME | 500 | Waiting for the CLI tool to process subcommands. Increate the value if needed |

# Publish the package to npm
- Create a npm account on https://www.npmjs.com/signup if you don't have one.
- Use the account to sign in via cli: `npm login`
Expand Down
13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"lint": "standard",
"lint:fix": "standard --fix",
"format": "prettier-standard \"src/**/*.js\" \"bin/**/*.js\" \"test/**/*.js\" \"config/**/*.js\"",
"test": "mocha --require test/prepare.js -t 20000 test/unit.test.js --exit",
"test": "mocha --require test/prepare.js -t 20000 test/*.test.js --exit",
"test:cov": "nyc --reporter=html --reporter=text npm test"
},
"dependencies": {
Expand All @@ -23,7 +23,9 @@
"lodash": "^4.17.15",
"moment": "^2.24.0",
"prompts": "^2.2.1",
"stream-mock": "^2.0.5",
"superagent": "^5.0.5",
"uuid": "^3.3.3",
"winston": "^3.2.1"
},
"bin": {
Expand All @@ -41,17 +43,24 @@
"homepage": "https://topcoder-platform.github.io/topcoder-cli/",
"devDependencies": {
"chai": "^4.2.0",
"delay": "^4.3.0",
"mocha": "^6.1.4",
"mocha-prepare": "^0.1.0",
"mock-require": "^3.0.3",
"nock": "^10.0.6",
"nyc": "^14.1.1",
"prettier-standard": "^16.0.0",
"sinon": "^7.5.0",
"standard": "^13.1.0"
},
"standard": {
"env": {
"mocha": true
}
},
"nyc": {
"exclude": [
"test/**",
"src/common/logger.js"
]
}
}
5 changes: 3 additions & 2 deletions src/commands/pay.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
const prompts = require('prompts')
const logger = require('../common/logger')

/**
* Handles the "pay" command
* @param {Array} args Arguments
*/
function handleCommand (args) {
const options = args[args.length - 1].opts()
const options = args[0].opts()
const challengeDetails = [
{
type: 'text',
Expand Down Expand Up @@ -41,7 +42,7 @@ function handleCommand (args) {
const response = await prompts(challengeDetails)
// respose + options.copilot (copilot payment money) contains total
// information needed to send request to API.
console.log(response)
logger.info(response)
// => response => { username, age, about }
}
promptQuestions()
Expand Down
7 changes: 1 addition & 6 deletions src/common/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ const submissionApi = require('@topcoder-platform/topcoder-submission-api-wrappe
const logger = require('./logger')
const configService = require('../services/configService')

let submissionApiClient = null

const defaultAuthSchema = Joi.object({
username: Joi.string(),
password: Joi.string(),
Expand Down Expand Up @@ -87,9 +85,6 @@ async function readFromRCFile (filename, cliParams, schema, validCLIParams) {
}

function getAPIClient (userName, password, m2m) {
if (submissionApiClient) {
return submissionApiClient
}
const config = require('../config')()
let clientConfig
if (userName && password) {
Expand All @@ -113,7 +108,7 @@ function getAPIClient (userName, password, m2m) {
clientConfig.AUTH0_CLIENT_ID = m2m.client_id
clientConfig.AUTH0_CLIENT_SECRET = m2m.client_secret
}
submissionApiClient = submissionApi(clientConfig)
const submissionApiClient = submissionApi(clientConfig)
return submissionApiClient
}

Expand Down
22 changes: 9 additions & 13 deletions src/services/configService.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async function addToConfigFile (key, value) {

let config = ini.parse('')
try {
config = readFromConfigFile()
config = await readFromConfigFile()
} catch (error) {
// catching .tcconfig file not found error here.
logger.info('Topcoder config file not found, creating file in' + homedir)
Expand All @@ -66,19 +66,15 @@ async function addToConfigFile (key, value) {
* @param {String} keyToBeDeleted Property key (to be deleted)
*/
async function deleteFromConfigFile (keyToBeDeleted) {
const config = readFromConfigFile()
let isDeleted = false

isDeleted = _.unset(config, keyToBeDeleted)

if (isDeleted) {
await fs.writeFile(configPath, ini.stringify(config))
logger.info(
`${keyToBeDeleted} is removed from the config file successfully.`
)
} else {
throw new Error(`${keyToBeDeleted} is not found in the config fle.`)
const config = await readFromConfigFile()
if (_.isUndefined(_.get(config, keyToBeDeleted))) {
throw new Error(`${keyToBeDeleted} is not found in the config file.`)
}
_.unset(config, keyToBeDeleted)
await fs.writeFile(configPath, ini.stringify(config))
logger.info(
`${keyToBeDeleted} is removed from the config file successfully.`
)
}

module.exports = {
Expand Down
Loading