diff --git a/src/scripts/sonar.test.ts b/src/scripts/sonar.test.ts index 9439245..79a1bde 100644 --- a/src/scripts/sonar.test.ts +++ b/src/scripts/sonar.test.ts @@ -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, @@ -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); @@ -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']); + }); + }); }); diff --git a/src/scripts/sonar.ts b/src/scripts/sonar.ts index 04d6a2d..c9d975b 100644 --- a/src/scripts/sonar.ts +++ b/src/scripts/sonar.ts @@ -30,13 +30,6 @@ export class SonarScript extends SonarBaseScript { 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'], { @@ -45,6 +38,13 @@ export class SonarScript extends SonarBaseScript { } }); + 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}`];