diff --git a/package.json b/package.json index b6ee41b0..fd2e3b5c 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "homepage": "https://github.com/VCVRack/community#readme", "devDependencies": { + "ajv": "^5.5.2", "jasmine": "^2.8.0", "node-virustotal": "^2.4.2", "request": "^2.83.0" diff --git a/spec/README.md b/spec/README.md new file mode 100644 index 00000000..04880fc4 --- /dev/null +++ b/spec/README.md @@ -0,0 +1,27 @@ +# Jasmine Tests + +[Jasmine Intro](https://jasmine.github.io/2.8/introduction.html) + +To run all tests: + +``` +npm test +``` + +To run only the zip tests for example: + +``` +./node_modules/.bin/jasmine --filter=zip +``` + +# Virus Total + +**Rate Limit: 4 per minute** + +`VT_API_KEY` env var is required and already set in travisci settings + +[Public API](https://www.virustotal.com/en/documentation/public-api/v2/) + +[Node Module](https://github.com/natewatson999/node-virustotal) + + diff --git a/spec/basic.tests.spec.js b/spec/basic.tests.spec.js new file mode 100644 index 00000000..702464f8 --- /dev/null +++ b/spec/basic.tests.spec.js @@ -0,0 +1,59 @@ +const CUR_VER_PREFIX = '0.5'; +const fs = require("fs"); +const Ajv = require('ajv'); //https://github.com/epoberezkin/ajv +const ajv = new Ajv({}); +const validate = ajv.compile(require('./manifest.json')); + +describe("json", function() { + + it("manifest files should be valid against the schema", function(done) { + fs.readdir('plugins', function(err, files) { + if (err){ + fail("unable to read plugins dir"); + } + + for (let index in files) { + const fileName = files[index]; + const filePath = `plugins/${fileName}`; + if(!filePath.toLowerCase().endsWith('.json')){ + fail("manifests should have .json extension"); + } + + fs.readFile(filePath, 'utf8', (err, fileContent) => { + if (err){ + fail("unable to read manifest file"); + } + + let manifestObj; + try { + + const obj = JSON.parse(fileContent); + const valid = validate(obj); + + if (!valid) { + validate.errors.map(e=>e.message+=` in ${filePath}`) + fail(validate.errors); + } + + if(!(/^[a-zA-Z0-9_\-]*$/).test(obj.slug)){ + fail(`slug does not match regex in ${filePath}`); + } + + if(fileName.replace('.json','') !== obj.slug){ + fail(`slug '${obj.slug}' does not match fileName: ${fileName}`); + } + + if(obj.version && !obj.version.startsWith(CUR_VER_PREFIX)){ + fail(`version '${obj.version}' must start with '${CUR_VER_PREFIX}'`); + } + + done(); + } catch(err){ + fail(`Invalid JSON: ${filePath}\n${err}`); + } + }); + } + }); + }); + +}); diff --git a/spec/basics.spec.js b/spec/basics.spec.js deleted file mode 100644 index 8ba5ef2b..00000000 --- a/spec/basics.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -//Jasmine Test - https://jasmine.github.io/2.8/introduction.html -const fs = require("fs"); - -describe("json", function() { - - it("manifest files should be valid parsable json", function(done) { - fs.readdir('plugins', function(err, files) { - if (err){ - fail("unable to read plugins dir"); - } - - for (let index in files) { - const filePath = `plugins/${files[index]}`; - if(!filePath.toLowerCase().endsWith('.json')){ - fail("manifests should have .json extension"); - } - - fs.readFile(filePath, 'utf8', (err, fileContent) => { - if (err){ - fail("unable to read manifest file"); - } - - let manifestObj; - try { - manifestObj = JSON.parse(fileContent); - done(); - } catch(err){ - fail(`Invalid JSON: ${filePath}\n${err}`); - } - }); - } - }); - }); - -}); diff --git a/spec/manifest.json b/spec/manifest.json new file mode 100644 index 00000000..c80165bf --- /dev/null +++ b/spec/manifest.json @@ -0,0 +1,100 @@ +{ + "id": "manifest.json", + "type": "object", + "required": [ + "slug", + "version" + ], + "description": "A plugin manifest file for VCV Rack.", + "properties": { + "slug": { + "type": "string", + "description": "" + }, + "name": { + "type": "string", + "description": "" + }, + "author": { + "type": "string", + "description": "" + }, + "license": { + "type": "string", + "description": "" + }, + "version": { + "type": "string", + "description": "" + }, + "homepage": { + "type": "string", + "description": "" + }, + "donation": { + "type": "string", + "description": "" + }, + "manual": { + "type": "string", + "description": "" + }, + "source": { + "type": "string", + "description": "" + }, + "disabled": { + "type": "boolean", + "description": "used when a plugin needs to be disabled" + }, + "productId": { + "type": "number", + "description": "only used by vcv rack website" + }, + "downloads": { + "type": "object", + "properties": { + "win": { + "type": "object", + "properties": { + "download": { + "type": "string", + "description": "" + }, + "sha256": { + "type": "string", + "description": "" + } + } + }, + "lin": { + "type": "object", + "properties": { + "download": { + "type": "string", + "description": "" + }, + "sha256": { + "type": "string", + "description": "" + } + } + }, + "mac": { + "type": "object", + "properties": { + "download": { + "type": "string", + "description": "" + }, + "sha256": { + "type": "string", + "description": "" + } + } + } + } + } + }, + "additionalProperties": false +} \ No newline at end of file