Skip to content

Commit

Permalink
* Add unit testing (#37)
Browse files Browse the repository at this point in the history
* Check for non-existent obsoletedBy values
* Increment version number
  • Loading branch information
radford-for-smpte committed Jul 24, 2020
1 parent b33a8d5 commit e1795fb
Show file tree
Hide file tree
Showing 9 changed files with 1,241 additions and 18 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ jobs:
with:
node-version: '10'
- run: npm install
- run: npm test
- run: npm run validate
937 changes: 936 additions & 1 deletion package-lock.json

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"name": "isdcf-registries",
"version": "1.0.0-beta.2",
"version": "1.0.0-beta.3",
"description": "ISDCF registries",
"scripts": {
"test": "mocha",
"validate": "node src/main/scripts/validate.js"
},
"main": "src/main/scripts/validate.js",
"repository": {
"type": "git",
"url": "https://github.com/isdcf/registries.git"
Expand All @@ -15,5 +17,9 @@
"ajv": "^6.10.2",
"cldr-core": "^36.0.0",
"cldr-localenames-modern": "^36.0.0"
},
"dependencies": {
"chai": "^4.2.0",
"mocha": "^8.0.1"
}
}
6 changes: 6 additions & 0 deletions src/main/scripts/facilities.validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,11 @@ module.exports = (registry, name) => {
throw name + " registry key " + registry[i-1].code + " is " +
((registry[i-1].code === registry[i].code) ? "duplicated" : "not sorted");
}

/* ensure all obsoletedBy codes are found */
(registry[i].obsoletedBy||[]).forEach(obs => {
if (!registry.find(r => r.code === obs))
throw `${name}: ${registry[i].description} is obsoletedBy '${obs}' which is an invalid code`
})
}
}
6 changes: 6 additions & 0 deletions src/main/scripts/studios.validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,11 @@ module.exports = (registry, name) => {
throw name + " registry key " + registry[i-1].code + " is " +
((registry[i-1].code === registry[i].code) ? "duplicated" : "not sorted");
}

/* ensure all obsoletedBy codes are found */
(registry[i].obsoletedBy||[]).forEach(obs => {
if (!registry.find(r => r.code === obs))
throw `${name}: ${registry[i].description} is obsoletedBy '${obs}' which is an invalid code`
})
}
}
72 changes: 56 additions & 16 deletions src/main/scripts/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

const path = require('path')
const fs = require('fs');
const { basename, join } = require('path')
const { readFile, access, readdir } = require('fs').promises;
const assert = require('assert')
const ajv = require('ajv');

const DATA_PATH = "src/main/data/";
Expand All @@ -31,19 +33,57 @@ const DATA_VALIDATE_PATH = "src/main/scripts/%s.validate.js"; // additional chec

var validator_factory = new ajv();

for (const dataFile of fs.readdirSync(DATA_PATH).filter(f => /.json$/.test(f))) {
console.log(`Checking ${dataFile}`)
const name = path.basename(dataFile, ".json")
const registry = JSON.parse(fs.readFileSync(path.join(DATA_PATH, dataFile)))
const schemaFile = DATA_SCHEMA_PATH.replace("%s", name)
const validateFile = DATA_VALIDATE_PATH.replace("%s", name)
const validator = validator_factory.compile(JSON.parse(fs.readFileSync(schemaFile)))

if (!validator(registry))
throw `${name} registry fails validation`

/* perform additional checks if applicable */
if (fs.statSync(validateFile)) {
require("./" + path.basename(validateFile))(registry, name)
}
async function loadValidators() {
/* create a mapping of schema/data name to validator */
return await (await readdir(DATA_PATH)).reduce(async (aProm, dataFile) => {
const a = await aProm
const name = basename(dataFile, ".json")
const schemaFile = DATA_SCHEMA_PATH.replace("%s", name)
const validateFile = DATA_VALIDATE_PATH.replace("%s", name)
const schema = JSON.parse(await readFile(schemaFile))
const schemaVersion = basename(schema.$id)
const schemaValidate = validator_factory.compile(schema)
const data = JSON.parse(await readFile(join(DATA_PATH, dataFile)))

let additionalChecks = () => {}

/* perform additional checks if applicable */
try {
await access(validateFile, fs.constants.F_OK)
additionalChecks = require("./" + basename(validateFile))
}
catch (e) {
if (e.code !== "ENOENT")
throw e
}

const validate = (registry = data) => {
/* first check schema */
if (!schemaValidate(registry))
throw `${name} registry fails schema validation`

/* then invoke any additional checks not covered by JSON schema: */
additionalChecks(registry, name)
}

return { ...a, [name]: { schemaVersion, validate, data }}
}, {})

}

async function validateAll() {
const validators = await loadValidators()
Object.keys(validators).map(name => {
console.log(`Checking ${name}`)
validators[name].validate()
})
}

module.exports = {
loadValidators,
validateAll,
}

// invoke validateAll() if we're run as a script:
if (require.main === module)
validateAll().catch(console.error)
94 changes: 94 additions & 0 deletions test/facilities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const { loadValidators } = require("..")
const chai = require('chai')
const should = chai.should();
const assert = chai.assert

describe("facilities schema", () => {
let validate
before(async () => { ({ facilities: { validate } } = await loadValidators() ) })

it("schema exists", () => {
validate.should.be.a('function')
})

it("correct validation", () => {
assert.doesNotThrow(() => validate([
{ code: "AAA", description: "A A A" },
{ code: "BBB", description: "B B B", obsolete: true },
{ code: "CCC", description: "C C C", obsolete: true, comments: [ "foo", "bar" ] },
{ code: "DDD", description: "D D D", obsolete: true, comments: [ "foo", "bar" ], obsoletedBy: [ 'AAA', 'BBB' ] }
]))
})

it("bad order", () => {
assert.throw(() => validate([
{ code: "BBB", description: "B B B", obsolete: true },
{ code: "AAA", description: "A A A" },
]), /sorted/)
})

it("incorrect obsoletedBy", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsolete: true, obsoletedBy: [ true ] }
]), /fails schema/)
})

it("obsoletedBy unknown code", () => {
assert.throw(() => validate([
{ code: "AAA", description: "A A A" },
{ code: "DDD", description: "C C C", obsolete: true, obsoletedBy: [ "XYZ" ] }
]), /invalid code/)
})

it("empty obsoletedBy", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsolete: true, obsoletedBy: [ ] }
]), /fails schema/)
})

it("incorrect obsolete", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsolete: "foo", obsoletedBy: [ "AAA" ] }
]), /fails schema/)
})

it("no obsoletedBy without obsolete", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsolete: false, obsoletedBy: [ "AAA" ] }
]), /fails schema/)
})

it("no obsoletedBy without obsoletedBy (implicit value)", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsoletedBy: [ "AAA" ] }
]), /fails schema/)
})

it("invalid case", () => {
assert.throw(() => validate([
{ code: "aaa", description: "A A A" },
{ code: "BBB", description: "B B B" }
]), /fails schema/)
})

it("additional", () => {
assert.throw(() => validate([
{ code: "aaa", description: "A A A" },
{ code: "BBB", description: "B B B", foo: "bar" }
]), /fails schema/)
})

it("additional", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsolete: true, comments: [ "foo", "bar" ], obsoletedBy: [ 'AAA', 'DDD' ], foo: "bar" }
]), /fails schema/)
})

it("code too long", () => {
assert.throw(() => validate([
{ code: "AAA", description: "A A A" },
{ code: "BBBBB", description: "B B B" }
]), /fails schema/)
})

})
41 changes: 41 additions & 0 deletions test/langauges.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const { loadValidators } = require("..")
const chai = require('chai')
const should = chai.should();
const assert = chai.assert

describe("languages schema", () => {
before(async () => { ({ languages: { validate } } = await loadValidators() ) })

it("valid", () => {
assert.doesNotThrow(() => validate([
{
"dcncTag": "SQ",
"rfc5646Tag": "sq",
"use": [
"audio",
"text"
],
"comments": [
"DCNC notes: Albanian"
]
},
{
"dcncTag": "AR",
"rfc5646Tag": "ar",
"use": [
"audio",
"text"
],
"comments": [
"DCNC notes: Arabic"
]
}]))
})

it("invalid", () => {
assert.throw(() => validate([
"foo"
]), /fails schema/)
})

})
94 changes: 94 additions & 0 deletions test/studios.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const { loadValidators } = require("..")
const chai = require('chai')
const should = chai.should();
const assert = chai.assert

describe("studios schema", () => {
let validate
before(async () => { ({ studios: { validate } } = await loadValidators() ) })

it("schema exists", () => {
validate.should.be.a('function')
})

it("correct validation", () => {
assert.doesNotThrow(() => validate([
{ code: "AAA", description: "A A A" },
{ code: "BBB", description: "B B B", obsolete: true },
{ code: "CCC", description: "C C C", obsolete: true, comments: [ "foo", "bar" ] },
{ code: "DDD", description: "D D D", obsolete: true, comments: [ "foo", "bar" ], obsoletedBy: [ 'AAA', 'BBB' ] }
]))
})

it("bad order", () => {
assert.throw(() => validate([
{ code: "BBB", description: "B B B", obsolete: true },
{ code: "AAA", description: "A A A" },
]), /sorted/)
})

it("incorrect obsoletedBy", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsolete: true, obsoletedBy: [ true ] }
]), /fails schema/)
})

it("obsoletedBy unknown code", () => {
assert.throw(() => validate([
{ code: "AAA", description: "A A A" },
{ code: "DDD", description: "C C C", obsolete: true, obsoletedBy: [ "XYZ" ] }
]), /invalid code/)
})

it("empty obsoletedBy", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsolete: true, obsoletedBy: [ ] }
]), /fails schema/)
})

it("incorrect obsolete", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsolete: "foo", obsoletedBy: [ "AAA" ] }
]), /fails schema/)
})

it("no obsoletedBy without obsolete", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsolete: false, obsoletedBy: [ "AAA" ] }
]), /fails schema/)
})

it("no obsoletedBy without obsoletedBy (implicit value)", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsoletedBy: [ "AAA" ] }
]), /fails schema/)
})

it("invalid case", () => {
assert.throw(() => validate([
{ code: "aaa", description: "A A A" },
{ code: "BBB", description: "B B B" }
]), /fails schema/)
})

it("additional", () => {
assert.throw(() => validate([
{ code: "aaa", description: "A A A" },
{ code: "BBB", description: "B B B", foo: "bar" }
]), /fails schema/)
})

it("additional", () => {
assert.throw(() => validate([
{ code: "DDD", description: "C C C", obsolete: true, comments: [ "foo", "bar" ], obsoletedBy: [ 'AAA', 'DDD' ], foo: "bar" }
]), /fails schema/)
})

it("code too long", () => {
assert.throw(() => validate([
{ code: "AAA", description: "A A A" },
{ code: "BBBBB", description: "B B B" }
]), /fails schema/)
})

})

0 comments on commit e1795fb

Please sign in to comment.