Skip to content
219 changes: 185 additions & 34 deletions src/scripts/sonar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,16 @@ const shellCommand = sandbox.stub(SonarScript.prototype, 'invokeShellCommand');
const subScript = sandbox.stub(SonarScript.prototype, 'invokeScript');

function getSonarScript(targetBranch: string, logger: {}): SonarScript {
let options = {};
if (targetBranch) {
options = {
targetBranch
};
}

return new SonarScript({
args: {},
options: {
targetBranch
},
options,
program: sinon.stub() as any,
command: sinon.stub() as any,
ddash: sinon.stub() as any,
Expand All @@ -52,6 +57,15 @@ function simulateCurrentGitLocalBranchIs(currentLocalBranch: string) {
mySpawn.setDefault(mySpawn.simple(0 /* exit code */, currentLocalBranch /* stdout */));
}

function simulateThereIsNoLocalGitRepository() {
shellCommand.withArgs('git', ['branch', '--show-current'], sinon.match.any).callThrough();
const mockSpawn = require('mock-spawn');
const mySpawn = mockSpawn();
require('child_process').spawn = mySpawn;
const gitOutputMessage = 'fatal: not a git repository (or any of the parent directories): .git';
mySpawn.setDefault(mySpawn.simple(128 /* exit code */, gitOutputMessage /* stdout */));
}

describe('sonar script', function () {
timeout(this, 30000);

Expand Down Expand Up @@ -94,55 +108,192 @@ error: Script "sonar" failed after 0 s with: ENOENT: no such file or directory,
nock.cleanAll();
});

it(` should successfully analyze code when project already exists in Sonar.`, async () => {
simulateSonarProjectAlreadyExists();
simulateCurrentGitLocalBranchIs('current-local-branch');
it(` should fail when there is no local git repository`, async () => {
simulateThereIsNoLocalGitRepository();

const loggerRecorder = new LoggerRecorder();
const sonarScript = getSonarScript('develop', loggerRecorder.logger);
const sonarScript = getSonarScript(null, loggerRecorder.logger);

await sonarScript.run();
await expect(sonarScript.run()).to.be.rejectedWith(
Error,
'Expected success codes were "0", but the process exited with "128".'
);

expect(loggerRecorder.recordedLogs)
.to.startsWith('info: Script "sonar" starting...\n')
.and.to.contain('info: Analyzing current branch "current-local-branch" source code...\n')
.and.to.endWith('info: Script "sonar" successful after 0 s\n');
.to.startWith(`info: Script "sonar" starting...\n`)
.and.to.contain('info: Executing: git branch,--show-current\n')
.and.to.endWith('error: Script "sonar" failed after 0 s with: Expected success codes were "0", but the process exited with "128".\n');

subScript.should.not.have.been.called;

shellCommand.should.have.been.calledTwice;
shellCommand.should.have.been.calledWith('git', ['branch', '--show-current']);
shellCommand.should.have.been.calledWithExactly(SONAR_SCANNER, ['-Dsonar.branch.name=current-local-branch', '-Dsonar.branch.target=develop']);
shellCommand.should.have.been.calledOnceWith('git', ['branch', '--show-current']);
});

it(` should initialize Sonar project with a warning and then successfully analyze code when project does not yet exist in Sonar.`, async () => {
simulateSonarProjectDoesNotYetExist();
simulateCurrentGitLocalBranchIs('current-local-branch');
describe(' when project already exists in Sonar', () => {
beforeEach(() => {
simulateSonarProjectAlreadyExists();
simulateCurrentGitLocalBranchIs('current-local-branch');
});

const loggerRecorder = new LoggerRecorder();
const sonarScript = getSonarScript('develop', loggerRecorder.logger);
it(` should succeed when simple code analysis succeeds.`, async () => {
const loggerRecorder = new LoggerRecorder();
const sonarScript = getSonarScript(null, loggerRecorder.logger);

await sonarScript.run();
shellCommand.withArgs(SONAR_SCANNER).returns(0);

expect(loggerRecorder.recordedLogs)
.to.startsWith('info: Script "sonar" starting...\n')
.and.to.contain('info: Analyzing current branch "current-local-branch" source code...\n')
.and.to.endWith('info: Script "sonar" successful after 0 s\n');
await sonarScript.run();

expect(loggerRecorder.recordedLogs)
.to.contain("warn: 'my-test-project-key' Sonar project does not yet exist on https://example.com/sonar/ ! Initializing it first...\n");
expect(loggerRecorder.recordedLogs)
.to.startsWith('info: Script "sonar" starting...\n')
.and.to.contain('info: Analyzing current branch "current-local-branch" source code...\n')
.and.to.endWith('info: Script "sonar" successful after 0 s\n');

subScript.should.not.have.been.called;

subScript.should.have.been.calledOnceWithExactly(SonarInitScript, {}, {});
shellCommand.should.have.been.calledTwice;
shellCommand.should.have.been.calledWith('git', ['branch', '--show-current']);
shellCommand.should.have.been.calledWithExactly(SONAR_SCANNER, ['-Dsonar.branch.name=current-local-branch']);
});

shellCommand.should.have.been.calledTwice;
shellCommand.should.have.been.calledWith('git', ['branch', '--show-current']);
shellCommand.should.have.been.calledWithExactly(SONAR_SCANNER, ['-Dsonar.branch.name=current-local-branch', '-Dsonar.branch.target=develop']);
it(` should succeed when code analysis against a target branch succeeds.`, async () => {
const loggerRecorder = new LoggerRecorder();
const sonarScript = getSonarScript('develop', loggerRecorder.logger);

shellCommand.withArgs(SONAR_SCANNER).returns(0);

await sonarScript.run();

expect(loggerRecorder.recordedLogs)
.to.startsWith('info: Script "sonar" starting...\n')
.and.to.contain('info: Analyzing current branch "current-local-branch" source code...\n')
.and.to.endWith('info: Script "sonar" successful after 0 s\n');

subScript.should.not.have.been.called;

shellCommand.should.have.been.calledTwice;
shellCommand.should.have.been.calledWith('git', ['branch', '--show-current']);
shellCommand.should.have.been.calledWithExactly(SONAR_SCANNER, ['-Dsonar.branch.name=current-local-branch', '-Dsonar.branch.target=develop']);
});

it(` should fail when simple code analysis fails.`, async () => {
const loggerRecorder = new LoggerRecorder();
const sonarScript = getSonarScript(null, loggerRecorder.logger);

shellCommand.withArgs(SONAR_SCANNER).rejects(new Error('An error occurred while analyzing source code.'))

await expect(sonarScript.run()).to.be.rejectedWith(
Error,
'An error occurred while analyzing source code.'
);

expect(loggerRecorder.recordedLogs)
.to.startsWith('info: Script "sonar" starting...\n')
.and.to.contain('info: Analyzing current branch "current-local-branch" source code...\n')
.and.to.endWith(`error: Script "sonar" failed after 0 s with: An error occurred while analyzing source code.\n`);

subScript.should.not.have.been.called;

shellCommand.should.have.been.calledTwice;
shellCommand.should.have.been.calledWith('git', ['branch', '--show-current']);
shellCommand.should.have.been.calledWithExactly(SONAR_SCANNER, ['-Dsonar.branch.name=current-local-branch']);
});
});

// TODO Geraud : add more test cases:
// - when git branch fails
// - when passing no target branch
// - when sonar project does not yet exists
describe(' when project does not yet exist in Sonar', () => {
beforeEach(() => {
simulateSonarProjectDoesNotYetExist();
simulateCurrentGitLocalBranchIs('current-local-branch');
});

it(` should initialize Sonar project with a warning and then successfully analyze code.`, async () => {
const loggerRecorder = new LoggerRecorder();
const sonarScript = getSonarScript(null, loggerRecorder.logger);

shellCommand.withArgs(SONAR_SCANNER).returns(0);

await sonarScript.run();

expect(loggerRecorder.recordedLogs)
.to.startsWith('info: Script "sonar" starting...\n')
.and.to.contain("warn: 'my-test-project-key' Sonar project does not yet exist on https://example.com/sonar/ ! Initializing it first...\n")
.and.to.contain('info: Analyzing current branch "current-local-branch" source code...\n')
.and.to.endWith('info: Script "sonar" successful after 0 s\n');

subScript.should.have.been.calledOnceWithExactly(SonarInitScript, {}, {});

shellCommand.should.have.been.calledTwice;
shellCommand.should.have.been.calledWith('git', ['branch', '--show-current']);
shellCommand.should.have.been.calledWithExactly(SONAR_SCANNER, ['-Dsonar.branch.name=current-local-branch']);
});

it(` should initialize Sonar project with a warning and then successfully analyze code against a target branch.`, async () => {
const loggerRecorder = new LoggerRecorder();
const sonarScript = getSonarScript('develop', loggerRecorder.logger);

shellCommand.withArgs(SONAR_SCANNER).returns(0);

await sonarScript.run();

expect(loggerRecorder.recordedLogs)
.to.startsWith('info: Script "sonar" starting...\n')
.and.to.contain("warn: 'my-test-project-key' Sonar project does not yet exist on https://example.com/sonar/ ! Initializing it first...\n")
.and.to.contain('info: Analyzing current branch "current-local-branch" source code...\n')
.and.to.endWith('info: Script "sonar" successful after 0 s\n');

subScript.should.have.been.calledOnceWithExactly(SonarInitScript, {}, {});

shellCommand.should.have.been.calledTwice;
shellCommand.should.have.been.calledWith('git', ['branch', '--show-current']);
shellCommand.should.have.been.calledWithExactly(SONAR_SCANNER, ['-Dsonar.branch.name=current-local-branch', '-Dsonar.branch.target=develop']);
});

it(` should fail when Sonar project initialization fails.`, async () => {
const loggerRecorder = new LoggerRecorder();
const sonarScript = getSonarScript(null, loggerRecorder.logger);

subScript.withArgs(SonarInitScript).rejects(new Error('An error occurred while calling sonar-init sub-script.'))

await expect(sonarScript.run()).to.be.rejectedWith(
Error,
'An error occurred while calling sonar-init sub-script.'
);

expect(loggerRecorder.recordedLogs)
.to.startsWith('info: Script "sonar" starting...\n')
.and.to.contain("warn: 'my-test-project-key' Sonar project does not yet exist on https://example.com/sonar/ ! Initializing it first...\n")
.and.to.endWith('error: Script "sonar" failed after 0 s with: An error occurred while calling sonar-init sub-script.\n')
.and.to.not.contain('info: Analyzing current branch "current-local-branch" source code...\n')

subScript.should.have.been.calledOnceWithExactly(SonarInitScript, {}, {});

shellCommand.should.have.been.calledOnceWith('git', ['branch', '--show-current']);
});

it(` should fail when code analysis fails after project initialization.`, async () => {
const loggerRecorder = new LoggerRecorder();
const sonarScript = getSonarScript(null, loggerRecorder.logger);

subScript.withArgs(SonarInitScript).returns(0);
shellCommand.withArgs(SONAR_SCANNER).rejects(new Error('An error occurred while analyzing source code.'));

await expect(sonarScript.run()).to.be.rejectedWith(
Error,
'An error occurred while analyzing source code.'
);

expect(loggerRecorder.recordedLogs)
.to.startsWith('info: Script "sonar" starting...\n')
.and.to.contain("warn: 'my-test-project-key' Sonar project does not yet exist on https://example.com/sonar/ ! Initializing it first...\n")
.and.to.contain('info: Analyzing current branch "current-local-branch" source code...\n')
.and.to.endWith('error: Script "sonar" failed after 0 s with: An error occurred while analyzing source code.\n')

subScript.should.have.been.calledOnceWithExactly(SonarInitScript, {}, {});

shellCommand.should.have.been.calledTwice;
shellCommand.should.have.been.calledWith('git', ['branch', '--show-current']);
shellCommand.should.have.been.calledWithExactly(SONAR_SCANNER, ['-Dsonar.branch.name=current-local-branch']);
});
});

});

Expand Down
14 changes: 7 additions & 7 deletions src/scripts/sonar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,6 @@ export class SonarScript extends SonarBaseScript<Options> {
protected async main() {
const {sonarHostUrl, sonarProjectKey} = this.getSonarProjectInformation();

if (! await this.sonarProjectAlreadyExists(sonarProjectKey, sonarHostUrl)) {
this.logger.warn(`'${sonarProjectKey}' Sonar project does not yet exist on ${sonarHostUrl} ! Initializing it first...`);
await this.invokeScript(SonarInitScript, {}, {});
}

// npx sonar-scanner -Dsonar.branch.name=`git branch --show-current` -Dsonar.branch.target=develop

// TODO Geraud : extract method to determine current git branch
let currentBranch = '';
await this.invokeShellCommand('git', ['branch', '--show-current'], {
Expand All @@ -45,6 +38,13 @@ export class SonarScript extends SonarBaseScript<Options> {
}
});

if (! await this.sonarProjectAlreadyExists(sonarProjectKey, sonarHostUrl)) {
this.logger.warn(`'${sonarProjectKey}' Sonar project does not yet exist on ${sonarHostUrl} ! Initializing it first...`);
await this.invokeScript(SonarInitScript, {}, {});
}

// npx sonar-scanner -Dsonar.branch.name=`git branch --show-current` -Dsonar.branch.target=develop

this.logger.info(`Analyzing current branch "${currentBranch}" source code...`);

const args = [`-Dsonar.branch.name=${currentBranch}`];
Expand Down