Validate solcoverjs with json schema (#403)

truffle-plugin
cgewecke 5 years ago
parent 556d2d2e58
commit ee72e8d66d
  1. 5
      lib/app.js
  2. 3
      lib/ui.js
  3. 72
      lib/validator.js
  4. 1
      package.json
  5. 17
      test/units/truffle/errors.js
  6. 137
      test/units/validator.js
  7. 5
      yarn.lock

@ -6,6 +6,7 @@ const istanbul = require('istanbul');
const util = require('util');
const assert = require('assert');
const ConfigValidator = require('./validator');
const Instrumenter = require('./instrumenter');
const Coverage = require('./coverage');
const DataCollector = require('./collector');
@ -20,8 +21,12 @@ class App {
constructor(config) {
this.coverage = new Coverage();
this.instrumenter = new Instrumenter();
this.validator = new ConfigValidator()
this.config = config || {};
// Validate
this.validator.validate(this.config);
// Options
this.testsErrored = false;
this.instrumentToFile = (config.instrumentToFile === false) ? false : true;

@ -91,6 +91,9 @@ class AppUI extends UI {
const c = this.chalk;
const kinds = {
'config-fail':`${c.red('A config option (.solcover.js) is incorrectly formatted: ')}` +
`${c.red(args[0])}.`,
'instr-fail': `${c.red('Could not instrument:')} ${args[0]}. ` +
`${c.red('(Please verify solc can compile this file without errors.) ')}`,

@ -0,0 +1,72 @@
const Validator = require('jsonschema').Validator;
const AppUI = require('./ui').AppUI;
const util = require('util')
function isFunction(input){
}
Validator.prototype.customFormats.isFunction = function(input) {
return typeof input === "function"
};
const configSchema = {
id: "/solcoverjs",
type: "object",
properties: {
client: {type: "object"},
cwd: {type: "string"},
host: {type: "string"},
originalContractsDir: {type: "string"},
port: {type: "number"},
providerOptions: {type: "object"},
silent: {type: "boolean"},
// Hooks:
onServerReady: {type: "function", format: "isFunction"},
onTestComplete: {type: "function", format: "isFunction"},
onIstanbulComplete: {type: "function", format: "isFunction"},
// Arrays
skipFiles: {
type: "array",
items: {type: "string"}
},
istanbulReporter: {
type: "array",
items: {type: "string"}
},
},
};
class ConfigValidator {
constructor(){
this.validator = new Validator();
this.validator.addSchema(configSchema);
this.ui = new AppUI();
}
validate(config){
let result = this.validator.validate(config, configSchema);
if (result.errors.length){
let msg;
const option = `"${result.errors[0].property.replace('instance.', '')}"`;
(result.errors[0].argument === 'isFunction')
? msg = `${option} is not a function`
: msg = `${option} ${result.errors[0].message}`;
throw new Error(this.ui.generate('config-fail', [msg]));
}
return true;
}
}
module.exports = ConfigValidator;

@ -28,6 +28,7 @@
"global-modules": "^2.0.0",
"globby": "^10.0.1",
"istanbul": "^0.4.5",
"jsonschema": "^1.2.4",
"node-dir": "^0.1.17",
"node-emoji": "^1.10.0",
"pify": "^4.0.1",

@ -47,7 +47,7 @@ describe('Truffle Plugin: error cases', function() {
verify.coverageNotGenerated(truffleConfig);
});
it('project .solcover.js has syntax error', async function(){
it('.solcover.js has syntax error', async function(){
verify.cleanInitialState();
mock.installFullProject('bad-solcoverjs');
@ -64,7 +64,22 @@ describe('Truffle Plugin: error cases', function() {
verify.coverageNotGenerated(truffleConfig);
})
it('.solcover.js has incorrectly formatted option', async function(){
verify.cleanInitialState();
solcoverConfig.port = "Antwerpen";
mock.install('Simple', 'simple.js', solcoverConfig);
try {
await plugin(truffleConfig);
assert.fail()
} catch (err) {
assert(
err.message.includes('config option'),
`Should error on incorrect config options: ${err.message}`
);
}
});
it('lib module load failure', async function(){
verify.cleanInitialState();

@ -0,0 +1,137 @@
const assert = require('assert');
const util = require('util');
const ConfigValidator = require('./../../lib/validator');
describe('config validation', () => {
let validator;
let solcoverjs;
before(() => validator = new ConfigValidator());
beforeEach(() => solcoverjs = {});
it('validates an empty config', function() {
assert(validator.validate(solcoverjs), '{} should be valid');
})
it('validates config with unknown options', function(){
solcoverjs.unknown_option = 'hello';
assert(validator.validate(solcoverjs), '.cwd string should be valid')
})
it('validates the "string" options', function(){
const options = [
"cwd",
"host",
"originalContractsDir",
]
options.forEach(name => {
// Pass
solcoverjs = {};
solcoverjs[name] = "a_string";
assert(validator.validate(solcoverjs), `${name} string should be valid`)
// Fail
solcoverjs[name] = 0;
try {
validator.validate(solcoverjs);
assert.fail()
} catch (err){
assert(err.message.includes(`"${name}" is not of a type(s) string`), err.message);
}
});
});
it('validates the "object" options', function(){
const options = [
"client",
"providerOptions",
]
options.forEach(name => {
// Pass
solcoverjs = {};
solcoverjs[name] = {a_property: 'a'};
assert(validator.validate(solcoverjs), `${name} object should be valid`)
// Fail
solcoverjs[name] = 0;
try {
validator.validate(solcoverjs);
assert.fail()
} catch (err){
assert(err.message.includes(`"${name}" is not of a type(s) object`), err.message);
}
});
});
it('validates the "number" options', function(){
const options = [
"port",
]
options.forEach(name => {
// Pass
solcoverjs = {};
solcoverjs[name] = 0;
assert(validator.validate(solcoverjs), `${name} number should be valid`)
// Fail
solcoverjs[name] = "a_string";
try {
validator.validate(solcoverjs);
assert.fail()
} catch (err){
assert(err.message.includes(`"${name}" is not of a type(s) number`), err.message);
}
});
});
it('validates string array options', function(){
const options = [
"skipFiles",
"istanbulReporter",
]
options.forEach(name => {
// Pass
solcoverjs = {};
solcoverjs[name] = ['a_string'];
assert(validator.validate(solcoverjs), `${name} string array should be valid`)
// Fail
solcoverjs[name] = "a_string";
try {
validator.validate(solcoverjs);
assert.fail()
} catch (err){
assert(err.message.includes(`"${name}" is not of a type(s) array`), err.message);
}
});
});
it('validates function options', function(){
const options = [
"onServerReady",
"onTestComplete",
"onIstanbulComplete",
]
options.forEach(name => {
// Pass
solcoverjs = {};
solcoverjs[name] = async (a,b) => {};
assert(validator.validate(solcoverjs), `${name} string array should be valid`)
// Fail
solcoverjs[name] = "a_string";
try {
validator.validate(solcoverjs);
assert.fail()
} catch (err){
assert(err.message.includes(`"${name}" is not a function`), err.message);
}
});
});
});

@ -4138,6 +4138,11 @@ jsonparse@^1.2.0:
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=
jsonschema@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.4.tgz#a46bac5d3506a254465bc548876e267c6d0d6464"
integrity sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"

Loading…
Cancel
Save