@@ -14,6 +14,7 @@ | |||||
}, | }, | ||||
"homepage": "https://github.com/VCVRack/community#readme", | "homepage": "https://github.com/VCVRack/community#readme", | ||||
"devDependencies": { | "devDependencies": { | ||||
"ajv": "^5.5.2", | |||||
"jasmine": "^2.8.0", | "jasmine": "^2.8.0", | ||||
"node-virustotal": "^2.4.2", | "node-virustotal": "^2.4.2", | ||||
"request": "^2.83.0" | "request": "^2.83.0" | ||||
@@ -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) | |||||
@@ -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}`); | |||||
} | |||||
}); | |||||
} | |||||
}); | |||||
}); | |||||
}); |
@@ -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}`); | |||||
} | |||||
}); | |||||
} | |||||
}); | |||||
}); | |||||
}); |
@@ -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 | |||||
} |