Skip to content

Commit

Permalink
fix: set correct exit codes on failure (#185)
Browse files Browse the repository at this point in the history
  • Loading branch information
AnanyaJha authored and sfsholden committed May 4, 2021
1 parent 902ea7c commit 51f7f67
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 14 deletions.
2 changes: 1 addition & 1 deletion packages/apex-node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export { LogService, ApexLogGetOptions, LogRecord, LogResult } from './logs';
export { JUnitReporter, TapReporter, HumanReporter } from './reporters';
export {
ApexTestProgressValue,
ApexTestRunResultStatus,
ApexTestResultData,
ApexTestResultOutcome,
ApexTestRunResultStatus,
AsyncTestArrayConfiguration,
AsyncTestConfiguration,
CodeCoverageResult,
Expand Down
16 changes: 12 additions & 4 deletions packages/plugin-apex/src/commands/force/apex/test/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
HumanReporter,
TapReporter,
TestService,
TestResult
TestResult,
ApexTestRunResultStatus
} from '@salesforce/apex-node';
import { flags, SfdxCommand } from '@salesforce/command';
import { Messages, Org } from '@salesforce/core';
Expand All @@ -19,7 +20,12 @@ import {
CliJsonFormat,
buildOutputDirConfig
} from '../../../../reporters';
import { buildDescription, logLevels, resultFormat } from '../../../../utils';
import {
buildDescription,
logLevels,
resultFormat,
FAILURE_EXIT_CODE
} from '../../../../utils';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-apex', 'report');
Expand Down Expand Up @@ -109,6 +115,9 @@ export default class Report extends SfdxCommand {
}

try {
if (result.summary.outcome === ApexTestRunResultStatus.Failed) {
process.exitCode = FAILURE_EXIT_CODE;
}
switch (this.flags.resultformat) {
case 'tap':
this.logTap(result);
Expand All @@ -119,7 +128,7 @@ export default class Report extends SfdxCommand {
case 'json':
// when --json flag is specified, we should log CLI json format
if (!this.flags.json) {
this.ux.logJson(jsonOutput);
this.ux.logJson({ status: process.exitCode, result: jsonOutput });
}
break;
default:
Expand All @@ -130,7 +139,6 @@ export default class Report extends SfdxCommand {
const msg = messages.getMessage('testResultProcessErr', [e]);
this.ux.error(msg);
}

return jsonOutput as AnyJson;
}

Expand Down
18 changes: 15 additions & 3 deletions packages/plugin-apex/src/commands/force/apex/test/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
JUnitReporter,
HumanReporter,
TestResult,
TestLevel
TestLevel,
ApexTestRunResultStatus
} from '@salesforce/apex-node';
import { flags, SfdxCommand } from '@salesforce/command';
import { Messages, Org, SfdxError } from '@salesforce/core';
Expand All @@ -21,7 +22,12 @@ import {
CliJsonFormat,
JsonReporter
} from '../../../../reporters';
import { buildDescription, logLevels, resultFormat } from '../../../../utils';
import {
buildDescription,
logLevels,
resultFormat,
FAILURE_EXIT_CODE
} from '../../../../utils';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-apex', 'run');
Expand Down Expand Up @@ -193,6 +199,9 @@ export default class Run extends SfdxCommand {
}

try {
if (result.summary.outcome === ApexTestRunResultStatus.Failed) {
process.exitCode = FAILURE_EXIT_CODE;
}
switch (this.flags.resultformat) {
case 'human':
this.logHuman(
Expand All @@ -210,7 +219,10 @@ export default class Run extends SfdxCommand {
case 'json':
// when --json flag is specified, we should log CLI json format
if (!this.flags.json) {
this.ux.logJson(this.formatResultInJson(result));
this.ux.logJson({
status: process.exitCode,
result: this.formatResultInJson(result)
});
}
break;
default:
Expand Down
2 changes: 2 additions & 0 deletions packages/plugin-apex/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import * as chalk from 'chalk';

export const FAILURE_EXIT_CODE = 100;

export const colorSuccess = chalk.bold.green;
export const colorError = chalk.bold.red;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {
cliWithCoverage,
runWithCoverage,
jsonWithCoverage,
jsonResult
jsonResult,
runWithFailures
} from './testData';

Messages.importMessagesDirectory(__dirname);
Expand Down Expand Up @@ -285,6 +286,31 @@ describe('force:apex:test:report', () => {
}
);

test
.withOrg({ username: TEST_USERNAME }, true)
.loadConfig({
root: __dirname
})
.stub(process, 'cwd', () => projectPath)
.stub(TestService.prototype, 'reportAsyncResults', () => testRunSimple)
.stdout()
.command([
'force:apex:test:report',
'-i',
'01pxx00000NWwb3',
'--resultformat',
'json'
])
.it(
'should return a CLI json result with json result flag are specified',
ctx => {
const result = ctx.stdout;
expect(result).to.not.be.empty;
const resultJSON = JSON.parse(result);
expect(resultJSON).to.deep.equal(cliJsonResult);
}
);

test
.withOrg({ username: TEST_USERNAME }, true)
.loadConfig({
Expand All @@ -309,6 +335,48 @@ describe('force:apex:test:report', () => {
expect(resultJSON).to.deep.equal(cliWithCoverage);
});

test
.withOrg({ username: TEST_USERNAME }, true)
.loadConfig({
root: __dirname
})
.stub(process, 'cwd', () => projectPath)
.stub(TestService.prototype, 'reportAsyncResults', () => runWithFailures)
.stdout()
.command([
'force:apex:test:report',
'-i',
'01pxx00000NWwb3',
'--json',
'--resultformat',
'junit',
'-c'
])
.it('should set exit code as 100 for run with failures', () => {
expect(process.exitCode).to.eql(100);
});

test
.withOrg({ username: TEST_USERNAME }, true)
.loadConfig({
root: __dirname
})
.stub(process, 'cwd', () => projectPath)
.stub(TestService.prototype, 'reportAsyncResults', () => testRunSimple)
.stdout()
.command([
'force:apex:test:report',
'-i',
'01pxx00000NWwb3',
'--json',
'--resultformat',
'junit',
'-c'
])
.it('should set exit code as 0 for passing run', () => {
expect(process.exitCode).to.eql(0);
});

test
.withOrg({ username: TEST_USERNAME }, true)
.loadConfig({
Expand Down
52 changes: 47 additions & 5 deletions packages/plugin-apex/test/commands/force/apex/test/run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import {
runWithCoverage,
cliJsonResult,
cliWithCoverage,
jsonResult,
jsonWithCoverage,
jsonSyncResult,
rawSyncResult
rawSyncResult,
runWithFailures
} from './testData';

Messages.importMessagesDirectory(__dirname);
Expand Down Expand Up @@ -126,7 +126,7 @@ describe('force:apex:test:run', () => {
expect(ctx.stdout).to.contain('{\n "tests": []\n}\n');
expect(ctx.stderr).to.contain(
messages.getMessage('testResultProcessErr', [
"TypeError: Cannot read property 'testRunId' of undefined"
"TypeError: Cannot read property 'outcome' of undefined"
])
);
});
Expand Down Expand Up @@ -177,7 +177,7 @@ describe('force:apex:test:run', () => {
expect(ctx.stdout).to.contain('{\n "tests": []\n}\n');
expect(ctx.stderr).to.contain(
messages.getMessage('testResultProcessErr', [
"TypeError: Cannot read property 'testStartTime' of undefined"
"TypeError: Cannot read property 'outcome' of undefined"
])
);
});
Expand Down Expand Up @@ -279,7 +279,7 @@ describe('force:apex:test:run', () => {
const result = ctx.stdout;
expect(result).to.not.be.empty;
const resultJSON = JSON.parse(result);
expect(resultJSON).to.deep.equal(jsonResult);
expect(resultJSON).to.deep.equal(cliJsonResult);
}
);

Expand Down Expand Up @@ -1166,4 +1166,46 @@ describe('force:apex:test:run', () => {
expect(ctx.stderr).to.include(messages.getMessage('warningMessage'));
}
);

test
.withOrg({ username: TEST_USERNAME }, true)
.loadConfig({
root: __dirname
})
.stub(process, 'cwd', () => projectPath)
.stub(TestService.prototype, 'runTestAsynchronous', () => runWithFailures)
.stdout()
.command([
'force:apex:test:run',
'--tests',
'MyApexClass.testInsertTrigger',
'--outputdir',
'my/path/to/dir',
'-r',
'human'
])
.it('should set exit code as 100 for run with failures', () => {
expect(process.exitCode).to.eql(100);
});

test
.withOrg({ username: TEST_USERNAME }, true)
.loadConfig({
root: __dirname
})
.stub(process, 'cwd', () => projectPath)
.stub(TestService.prototype, 'runTestAsynchronous', () => testRunSimple)
.stdout()
.command([
'force:apex:test:run',
'--tests',
'MyApexClass.testInsertTrigger',
'--outputdir',
'my/path/to/dir',
'-r',
'human'
])
.it('should set exit code as 0 for passing run', () => {
expect(process.exitCode).to.eql(0);
});
});

0 comments on commit 51f7f67

Please sign in to comment.