From 25a39c32932641971f51ea0cc9054eb528776fcd Mon Sep 17 00:00:00 2001 From: RabsRincon Date: Mon, 11 Dec 2017 00:41:51 +0100 Subject: [PATCH] Add properties tests Will come back to add more eventually. This is a base implementation --- lib/properties.js | 63 +++++------ test/example-config/default.test.properties | 19 ++++ .../overwrite.overridden-base.properties | 2 + .../overwrite.overridden-tip.properties | 2 + test/properties-test.js | 106 ++++++++++++++++++ 5 files changed, 159 insertions(+), 33 deletions(-) create mode 100644 test/example-config/default.test.properties create mode 100644 test/example-config/overwrite.overridden-base.properties create mode 100644 test/example-config/overwrite.overridden-tip.properties create mode 100644 test/properties-test.js diff --git a/lib/properties.js b/lib/properties.js index 1aaf43018..1bb0ded95 100644 --- a/lib/properties.js +++ b/lib/properties.js @@ -22,78 +22,75 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -var fs = require('fs'), +const fs = require('fs'), logger = require('./logger').logger, - _ = require('underscore-node'); + _ = require('underscore-node'), + path = require('path'); -var properties = {}; +let properties = {}; -var hierarchy = []; +let hierarchy = []; -var propDebug = false; +let propDebug = false; function findProps(base, elem) { - var name = base + '.' + elem; + const name = base + '.' + elem; return properties[name]; } function debug(string) { - if (propDebug) logger.info("prop: " + string); + if (propDebug) logger.info(`prop: ${string}`); } function get(base, property, defaultValue) { - var result = defaultValue; - var source = 'default'; - hierarchy.forEach(function (elem) { - var propertyMap = findProps(base, elem); + let result = defaultValue; + let source = 'default'; + hierarchy.forEach(elem => { + const propertyMap = findProps(base, elem); if (propertyMap && property in propertyMap) { - debug(base + '.' + property + ': overriding ' + source + ' value (' + result + ') with ' + propertyMap[property]); + debug(`${base}.${property}: overriding ${source} value (${result}) with ${propertyMap[property]}`); result = propertyMap[property]; source = elem; } }); - debug(base + '.' + property + ': returning ' + result + ' (from ' + source + ')'); + debug(`${base}.${property}: returning ${result} (from ${source})`); return result; } function toProperty(prop) { - if (prop == 'true' || prop == 'yes') return true; - if (prop == 'false' || prop == 'no') return false; - if (prop.match(/^[0-9]+$/)) return parseInt(prop); - if (prop.match(/^[0-9]*\.[0-9]+$/)) return parseFloat(prop); + if (prop === 'true' || prop === 'yes') return true; + if (prop === 'false' || prop === 'no') return false; + if (prop.match(/^-?[0-9]+$/)) return parseInt(prop); + if (prop.match(/^-?[0-9]*\.[0-9]+$/)) return parseFloat(prop); return prop; } function parseProperties(blob, name) { - var props = {}; - blob.split('\n').forEach(function (line, index) { + const props = {}; + blob.split('\n').forEach((line, index) => { line = line.replace(/#.*/, '').trim(); if (!line) return; - var split = line.match(/([^=]+)=(.*)/); + let split = line.match(/([^=]+)=(.*)/); if (!split) { - logger.error("Bad line: " + line + " in " + name + ":" + (index + 1)); + logger.error(`Bad line: ${line} in ${name}: ${index + 1}`); return; } props[split[1].trim()] = toProperty(split[2].trim()); - debug(split[1].trim() + " = " + split[2].trim()); + debug(`${split[1].trim()} = ${split[2].trim()}`); }); return props; } function initialize(directory, hier) { if (hier === null) throw new Error('Must supply a hierarchy array'); - hierarchy = _.map(hier, function (x) { - return x.toLowerCase(); - }); - logger.info("Reading properties from " + directory + " with hierarchy " + hierarchy); - var endsWith = /\.properties$/; - var propertyFiles = fs.readdirSync(directory).filter(function (filename) { - return filename.match(endsWith); - }); + hierarchy = _.map(hier, x => x.toLowerCase()); + logger.info(`Reading properties from ${directory} with hierarchy ${hierarchy}`); + const endsWith = /\.properties$/; + const propertyFiles = fs.readdirSync(directory).filter(filename => filename.match(endsWith)); properties = {}; - propertyFiles.forEach(function (file) { - var baseName = file.replace(endsWith, ''); - file = directory + '/' + file; + propertyFiles.forEach(file => { + const baseName = file.replace(endsWith, ''); + file = path.join(directory, file); debug('Reading config from ' + file); properties[baseName] = parseProperties(fs.readFileSync(file, 'utf-8'), file); }); diff --git a/test/example-config/default.test.properties b/test/example-config/default.test.properties new file mode 100644 index 000000000..ab3039c10 --- /dev/null +++ b/test/example-config/default.test.properties @@ -0,0 +1,19 @@ +# Should ignore comments... +#commentedProperty=This is not expected +exampleProperty=value +emptyProperty= +numericPropertyPositive=42 +numericPropertyZero=0 +numericPropertyNegative=-11 +floatPropertyPositive=3.14 +floatPropertyNegative=-9000.0 +commaAsDecimalProperty=3,14 +stringPropertyNumberLike=- 97 +truePropertyYes=yes +truePropertyTrue=true +stringPropertyYes=Yes +stringPropertyTrue=True +falsePropertyNo=no +falsePropertyFalse=false +stringPropertyNo=No +stringPropertyFalse=False diff --git a/test/example-config/overwrite.overridden-base.properties b/test/example-config/overwrite.overridden-base.properties new file mode 100644 index 000000000..ac4fa4016 --- /dev/null +++ b/test/example-config/overwrite.overridden-base.properties @@ -0,0 +1,2 @@ +overrodeProperty=NOT ACTUALLY USED +nonOverriddenProperty=.... . .-.. .-.. --- diff --git a/test/example-config/overwrite.overridden-tip.properties b/test/example-config/overwrite.overridden-tip.properties new file mode 100644 index 000000000..67390aaf1 --- /dev/null +++ b/test/example-config/overwrite.overridden-tip.properties @@ -0,0 +1,2 @@ +overrodeProperty=ACTUALLY USED +localProperty=11235813 \ No newline at end of file diff --git a/test/properties-test.js b/test/properties-test.js new file mode 100644 index 000000000..f5e82aa10 --- /dev/null +++ b/test/properties-test.js @@ -0,0 +1,106 @@ +// Copyright (c) 2012-2017, Rubén Rincón +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) , +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +const should = require('chai').should(); +const properties = require('../lib/properties'); + + +properties.initialize('test/example-config/', ['test', 'overridden-base', 'overridden-tip']); + +const defaultProps = properties.propsFor("default"); +const overridingProps = properties.propsFor("overwrite"); + +describe('Properties', () => { + it('Has working propsFor', () => { + should.equal(properties.get("default", "exampleProperty"), defaultProps("exampleProperty")); + }); + it('Does not find non existent properties when no default is set', () => { + should.equal(defaultProps("nonexistentProp"), undefined); + }); + it('Falls back to default if value not found and default is set', () => { + // Randomly generated number... + defaultProps("nonexistentProp", 4).should.be.equal(4); + should.equal(defaultProps("nonexistentProp", 4), 4); + }); + it('Handles empty properties as empty strings', () => { + should.equal(defaultProps("emptyProperty"), ""); + }); + it('Ignores commented out properties', () => { + should.equal(defaultProps("commentedProperty"), undefined); + }); + it('Understands positive integers', () => { + should.equal(defaultProps("numericPropertyPositive"), 42); + }); + it('Understands zero as integer', () => { + should.equal(defaultProps("numericPropertyZero"), 0); + }); + it('Understands negative integers', () => { + should.equal(defaultProps("numericPropertyNegative"), -11); + }); + it('Understands positive floats', () => { + should.equal(defaultProps("floatPropertyPositive"), 3.14); + }); + it('Understands negative floats', () => { + should.equal(defaultProps("floatPropertyNegative"), -9000.0); + }); + it('Does not understand comma decimal as float', () => { + should.equal(defaultProps("commaAsDecimalProperty"), "3,14"); + }); + it('Does not understand DASH-SPACE-NUMBER as a negative number', () => { + should.equal(defaultProps("stringPropertyNumberLike"), "- 97"); + }); + it('Understands yes as true boolean', () => { + should.equal(defaultProps("truePropertyYes"), true); + }); + it('Understands true as true boolean', () => { + should.equal(defaultProps("truePropertyTrue"), true); + }); + it('Does not understand Yes as boolean', () => { + should.equal(defaultProps("stringPropertyYes"), "Yes"); + }); + it('Does not understand True as boolean', () => { + should.equal(defaultProps("stringPropertyTrue"), "True"); + }); + it('Understands no as false boolean', () => { + should.equal(defaultProps("falsePropertyNo"), false); + }); + it('Understands false as false boolean', () => { + should.equal(defaultProps("falsePropertyFalse"), false); + }); + it('Does not understand No as boolean', () => { + should.equal(defaultProps("stringPropertyNo"), "No"); + }); + it('Does not understand False as boolean', () => { + should.equal(defaultProps("stringPropertyFalse"), "False"); + }); + it('Should find non overridden properties', () => { + should.equal(overridingProps("nonOverriddenProperty"), ".... . .-.. .-.. ---"); + }); + it('Should handle overridden properties', () => { + should.equal(overridingProps("overrodeProperty"), "ACTUALLY USED"); + }); + it('Should fall back from overridden', () => { + should.equal(overridingProps("localProperty"), 11235813); + }); +}); \ No newline at end of file