Skip to content

Commit

Permalink
Add tests to cli.ts
Browse files Browse the repository at this point in the history
Add tests to config.ts
Add tests to config-validator.ts

Fix #9
  • Loading branch information
sarvaje committed May 1, 2017
1 parent c281939 commit 9d2f28a
Show file tree
Hide file tree
Showing 15 changed files with 407 additions and 4 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"ava": "ava",
"build": "npm run clean && npm-run-all build:*",
"build:assets": "cpx \"./{src,tests}/**/*.!(ts)\" dist",
"build:fixtures": "cpx \"./{src,tests}/**/fixtures/**\" dist",
"build:ts": "tsc --outDir dist",
"clean": "rimraf dist",
"lint": "eslint --ext md --ext ts --ignore-pattern dist/* .",
Expand Down Expand Up @@ -110,4 +111,4 @@
"text"
]
}
}
}
2 changes: 1 addition & 1 deletion src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const loadConfigFile = (filePath: string): IConfig => {
break;

default:
config = {};
config = null;
}

return config;
Expand Down
9 changes: 8 additions & 1 deletion src/lib/config/config-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ export const validateConfig = (config): boolean => {
return false;
}

const validConfig = validateRule(rule, ruleConfig, ruleId);
let validConfig = true;

try {
validConfig = validateRule(rule, ruleConfig, ruleId);
} catch (err) {
// if severity is invalid
validConfig = false;
}

if (!validConfig) {
logger.error(`Invalid configuration for "${ruleId}"`);
Expand Down
3 changes: 2 additions & 1 deletion src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export * from './types/problems';
export * from './types/rules';

export interface IConfig {
sonarConfig?;
collector;
rules;
}

/** A resource required by Sonar: Collector, Formatter, Plugin, Rule. */
Expand Down
158 changes: 158 additions & 0 deletions tests/lib/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import * as proxyquire from 'proxyquire';
import * as sinon from 'sinon';
import test from 'ava';

const engine = {
close: () => { },
executeOn: () => { }
};
const sonar = {
create: () => {
return engine;
}
};
const formatter = { format: () => { } };
const resourceLoader = {
getFormatters() {
return [formatter];
}
};
const logger = {
error() { },
log() { }
};
const validator = { validateConfig() { } };
const config = {
getFilenameForDirectory() { },
load() { }
};

proxyquire('../../src/lib/cli', {
'./config': config,
'./config/config-validator': validator,
'./sonar': sonar,
'./utils/logging': logger,
'./utils/resource-loader': resourceLoader
});

import { cli } from '../../src/lib/cli';
import { Severity } from '../../src/lib/types';

test.beforeEach((t) => {
sinon.spy(logger, 'log');
sinon.spy(logger, 'error');
sinon.spy(config, 'getFilenameForDirectory');
sinon.spy(config, 'load');

t.context.logger = logger;
t.context.config = config;
});

test.afterEach((t) => {
t.context.logger.log.restore();
t.context.logger.error.restore();
t.context.config.getFilenameForDirectory.restore();
t.context.config.load.restore();
});

test.serial('if version option is defined should print the current version and return the exit code 0', async (t) => {
const exitCode = await cli.execute('-v');

t.true(t.context.logger.log.calledOnce);
t.true(t.context.logger.log.args[0][0].startsWith('v'));
t.is(exitCode, 0);
});

test.serial('if help option is defined should print the help and return the exit code 0', async (t) => {
const exitCode = await cli.execute('--help');

t.true(t.context.logger.log.calledOnce);
t.true(t.context.logger.log.args[0][0].includes('Basic configuration'));
t.is(exitCode, 0);
});

test.serial('if config is not defined should get the config file from a directory', async (t) => {
// We just want to test that we are calling to the config.getFilenameForDirectory method we are returning false validating to avoid to stub more functions
sinon.stub(validator, 'validateConfig').returns(false);
t.context.validator = validator;

await cli.execute('http://localhost/');

t.true(t.context.config.getFilenameForDirectory.called);

t.context.validator.validateConfig.restore();
});

test.serial('if config is defined should use that file', async (t) => {
sinon.stub(validator, 'validateConfig').returns(false);
t.context.validator = validator;

await cli.execute('http://localhost/ --config configfile.cfg');

t.false(t.context.config.getFilenameForDirectory.called);
t.is(t.context.config.load.args[0][0], 'configfile.cfg');

t.context.validator.validateConfig.restore();
});

test.serial('if validator fail should return the exit code 1', async (t) => {
sinon.stub(validator, 'validateConfig').returns(false);
t.context.validator = validator;

const exitCode = await cli.execute('http://localhost/');

t.true(t.context.logger.error.calledOnce);
t.is(exitCode, 1);

t.context.validator.validateConfig.restore();
});

test.serial('if executeOn returns an error should return the exit code 1 and call to formatter.format', async (t) => {
sinon.stub(validator, 'validateConfig').returns(true);
sinon.stub(engine, 'executeOn').returns([{ severity: Severity.error }]);
sinon.spy(formatter, 'format');
t.context.validator = validator;
t.context.engine = engine;
t.context.formatter = formatter;

const exitCode = await cli.execute('http://localhost/');

t.true(t.context.formatter.format.called);
t.is(exitCode, 1);

t.context.validator.validateConfig.restore();
t.context.engine.executeOn.restore();
t.context.formatter.format.restore();
});

test.serial('if executeOn throws an exception should return the exit code 1', async (t) => {
sinon.stub(validator, 'validateConfig').returns(true);
sinon.stub(engine, 'executeOn').throws(new Error());
t.context.validator = validator;
t.context.engine = engine;

const exitCode = await cli.execute('http://localhost/');

t.is(exitCode, 1);

t.context.validator.validateConfig.restore();
t.context.engine.executeOn.restore();
});

test.serial('if executeOn works fine should return exitCode 0 and call to formatter.format', async (t) => {
sinon.stub(validator, 'validateConfig').returns(true);
sinon.stub(engine, 'executeOn').returns([{ severity: 0 }]);
sinon.spy(formatter, 'format');
t.context.validator = validator;
t.context.engine = engine;
t.context.formatter = formatter;

const exitCode = await cli.execute('http://localhost/');

t.true(t.context.formatter.format.called);
t.is(exitCode, 0);

t.context.validator.validateConfig.restore();
t.context.engine.executeOn.restore();
t.context.formatter.format.restore();
});
63 changes: 63 additions & 0 deletions tests/lib/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as path from 'path';
import test from 'ava';

import * as config from '../../src/lib/config';


test('if there is no configuration file should return null', (t) => {
const result = config.getFilenameForDirectory('./fixtures/getFileNameForDirectoryEmpty');

t.is(result, null);
});


test('if there is configuration file should return the path to the file', (t) => {
const result = config.getFilenameForDirectory(path.join(__dirname, './fixtures/getFileNameForDirectory'));

t.true(result.includes('.sonarrc'));
});

test('if load is called with a non valid file extension it should return an exception', (t) => {
const error = t.throws(() => {
config.load(path.join(__dirname, './fixtures/notvalid/notvalid.css'));
});

t.is(error.message, `Couldn't find any valid configuration`);
});

test(`if package.json doesn't have a sonar configuration should return an exception`, (t) => {
const error = t.throws(() => {
config.load(path.join(__dirname, './fixtures/notvalid/package.json'));
});

t.is(error.message, `Couldn't find any valid configuration`);
});

test(`if package.json is an invalid JSON should return an exception`, (t) => {
const error = t.throws(() => {
config.load(path.join(__dirname, './fixtures/exception/package.json'));
});

t.true(error.message.startsWith('Cannot read config file: '));
});

test(`if the config file doesn't have an extension should parse the JSON file`, (t) => {
const configuration = config.load(path.join(__dirname, './fixtures/sonarrc'));

t.is(configuration.collector.name, 'cdp');
t.is(configuration.rules['disallowed-headers'], 'warning');
});

test(`if the config file is a javascript file should return the configuration`, (t) => {
const configuration = config.load(path.join(__dirname, './fixtures/sonarrc.js'));

t.is(configuration.collector.name, 'cdp');
t.is(configuration.rules['disallowed-headers'], 'warning');
});

test(`if config file is valid should should return the configuration`, (t) => {
const configuration = config.load(path.join(__dirname, './fixtures/package.json'));

t.is(configuration.collector.name, 'cdp');
t.is(configuration.rules['disallowed-headers'], 'warning');
});
66 changes: 66 additions & 0 deletions tests/lib/config/config-validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as _ from 'lodash';
import test from 'ava';

import * as configValidator from '../../../src/lib/config/config-validator';

const validConfig = {
collector: {
name: 'cdp',
options: { waitFor: 100 }
},
formatter: 'json',
rules: {
'disallowed-headers': ['warning', {}],
'lang-attribute': 'warning',
'manifest-exists': 'warning',
'manifest-file-extension': 'warning',
'manifest-is-valid': 'warning',
'no-friendly-error-pages': 'warning',
'no-html-only-headers': 'warning',
'no-protocol-relative-urls': 'warning',
'x-content-type-options': 'warning'
}
};
const invalidConfig = { formatter: 'json' };

test('if config has an invalid schema should return false', (t) => {
const valid = configValidator.validateConfig(invalidConfig);

t.false(valid);
});

test(`if there is a rule in the config that doesn't exist should return false`, (t) => {
const config = _.cloneDeep(validConfig);

config.rules['no-rule'] = 'warning';

const valid = configValidator.validateConfig(config);

t.false(valid);
});

test(`if rule severity isn't valid should return false`, (t) => {
const config = _.cloneDeep(validConfig);

config.rules['disallowed-headers'] = ['no-valid-severity', {}];

const valid = configValidator.validateConfig(config);

t.false(valid);
});

test(`if rule schema isn't valid should return false`, (t) => {
const config = _.cloneDeep(validConfig);

config.rules['disallowed-headers'] = ['warning', { ignore: 'Server' }];

const valid = configValidator.validateConfig(config);

t.false(valid);
});

test('if config is valid should return true', (t) => {
const valid = configValidator.validateConfig(validConfig);

t.true(valid);
});
3 changes: 3 additions & 0 deletions tests/lib/fixtures/exception/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"private: true
}
20 changes: 20 additions & 0 deletions tests/lib/fixtures/getFilenameForDirectory/.sonarrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"collector": {
"name": "cdp",
"options": {
"waitFor": 100
}
},
"formatter": "json",
"rules": {
"disallowed-headers": "warning",
"lang-attribute": "warning",
"manifest-exists": "warning",
"manifest-file-extension": "warning",
"manifest-is-valid": "warning",
"no-friendly-error-pages": "warning",
"no-html-only-headers": "warning",
"no-protocol-relative-urls": "warning",
"x-content-type-options": "warning"
}
}
Empty file.
20 changes: 20 additions & 0 deletions tests/lib/fixtures/notvalid/notvalid.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"collector": {
"name": "cdp",
"options": {
"waitFor": 100
}
},
"formatter": "json",
"rules": {
"disallowed-headers": "warning",
"lang-attribute": "warning",
"manifest-exists": "warning",
"manifest-file-extension": "warning",
"manifest-is-valid": "warning",
"no-friendly-error-pages": "warning",
"no-html-only-headers": "warning",
"no-protocol-relative-urls": "warning",
"x-content-type-options": "warning"
}
}
3 changes: 3 additions & 0 deletions tests/lib/fixtures/notvalid/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"private": true
}
Loading

0 comments on commit 9d2f28a

Please sign in to comment.