UNPKG

gpii-universal

Version:

Cross platform, core components of the GPII personalization infrastructure.

257 lines (215 loc) 12.9 kB
"use strict"; var fluid = require("infusion"); var gpii = fluid.registerNamespace("gpii"); var jqUnit = require("node-jqunit"); require("json5/lib/register"); // TODO: reenable once we're ready to fill in missing schema data in other platforms. //var fs = require("fs"); var path = require("path"); require("gpii-json-schema"); require("./lib/validation"); require("../../transformer/src/js/Transformer"); var solutionSchema = require("../src/schemas/solution-schema.json5"); jqUnit.module("Sanity checks for Solutions Registry files."); fluid.registerNamespace("gpii.tests.universal.solutionsRegistry.srFiles"); gpii.tests.universal.solutionsRegistry.srFiles.checkSolutionsFiles = function (that, globalValidator) { var resolvedGenericTermsPath = fluid.module.resolvePath(that.options.genericPreferenceTermsPath); var genericTerms = require(resolvedGenericTermsPath); var resolvedSrPath = fluid.module.resolvePath(that.options.solutionsRegistryPath); // TODO: reenable once we're ready to fill in missing schema data in other platforms. //var files = fs.readdirSync(resolvedSrPath); var files = ["win32.json5"]; fluid.each(files, function (filename) { if (filename.match(/^.+\.json5?$/i)) { var filePath = path.resolve(resolvedSrPath, filename); var singleFileSolutions = require(filePath); fluid.each(singleFileSolutions, function (solutionDef, solutionKey) { gpii.tests.universal.solutionsRegistry.srFiles.checkSingleSolution(globalValidator, filename, solutionKey, solutionDef, genericTerms); }); } }); }; gpii.tests.universal.solutionsRegistry.srFiles.checkSingleSolution = function (globalValidator, filename, solutionKey, solutionDef, genericTerms) { jqUnit.test("Sanity-checking solution '" + solutionKey + "' in file '" + filename + "'.", function () { var isSolutionValid = gpii.tests.universal.solutionsRegistry.validateSinglePayload( globalValidator, solutionDef, solutionSchema, "solution file '" + filename + "' -> solution '" + solutionKey + "'" ); jqUnit.assertTrue("Solution '" + solutionKey + "' in file '" + filename + "' should have a valid schema.", isSolutionValid); gpii.tests.universal.solutionsRegistry.srFiles.checkDefaults(globalValidator, filename, solutionKey, solutionDef); gpii.tests.universal.solutionsRegistry.srFiles.checkCapabilities(filename, solutionKey, solutionDef, genericTerms); gpii.tests.universal.solutionsRegistry.srFiles.checkTransforms(filename, solutionKey, solutionDef, genericTerms); gpii.tests.universal.solutionsRegistry.srFiles.checkWindowsSettingsHandlers(filename, solutionKey, solutionDef); // TODO: Ensure that all forward and inverse transforms are sane, i.e. that they can at least be used with an empty object. // TODO: Ensure that all references to generic preference settings in transforms actually refer to generic preference settings that exist. // Discuss with Antranig how best to accomplish these checks, i.e. how much in the way of resolvers, etc. do we // actually need to meaningfully sanity check transforms? }); }; gpii.tests.universal.solutionsRegistry.srFiles.checkWindowsSettingsHandlers = function (filename, solutionKey, solutionDef) { // These settings handlers require a deeper structure, i.e an object with properties. var settingsHandlersDemandingObjects = [ "gpii.windows.nativeSettingsHandler", "gpii.windows.spiSettingsHandler", "gpii.windows.systemSettingsHandler", "gpii.windows.wmiSettingsHandler" ]; var settingsHandlersDemandingDataTypes = [ "gpii.windows.registrySettingsHandler" ]; var objectivelyAwfulSettingsCount = 0; var brokenRegistrySettingsCount = 0; fluid.each(fluid.get(solutionDef, "settingsHandlers"), function (settingsHandlerDef, settingsHandlerKey) { var settingsHandlerType = fluid.get(settingsHandlerDef, "type"); if (settingsHandlersDemandingObjects.indexOf(settingsHandlerType) !== -1) { fluid.each(settingsHandlerDef.supportedSettings, function (settingDef, settingKey) { var hasProperties = fluid.get(settingDef, "schema.properties") !== undefined; if (!hasProperties) { objectivelyAwfulSettingsCount++; fluid.log(filename + " -> " + solutionKey + " -> " + settingsHandlerKey + " -> " + settingKey + " should have properties defined."); } var isObject = fluid.get(settingDef, "schema.type") === "object"; if (!isObject) { fluid.log("WARNING: " + filename + " -> " + solutionKey + " -> " + settingsHandlerKey + " -> " + settingKey + " does not have the correct 'object' type value."); } }); } if (settingsHandlersDemandingDataTypes.indexOf(settingsHandlerType) !== -1) { var dataTypeKeys = Object.keys(fluid.get(settingsHandlerDef, "options.dataTypes")); var supportedSettingsKeys = Object.keys(fluid.get(settingsHandlerDef, "supportedSettings")); var keyCounts = {}; fluid.each([dataTypeKeys, supportedSettingsKeys], function (keys) { fluid.each(keys, function (key) { var existingValue = fluid.get(keyCounts, key) || 0; fluid.set(keyCounts, key, existingValue + 1); }); }); fluid.each(keyCounts, function (count, key) { if (count !== 2) { fluid.log("WARNING: " + filename + " -> " + solutionKey + " -> " + settingsHandlerKey + " -> " + key + " should have both a data type and/or supported settings entry."); brokenRegistrySettingsCount++; } }); } }); jqUnit.assertEquals("There should be no settings that lack a proper object structure.", 0, objectivelyAwfulSettingsCount); jqUnit.assertEquals("There should be no mismatch between registry settings and data types.", 0, brokenRegistrySettingsCount); }; // Ensure that all "capabilities" refer to a generic preference setting. gpii.tests.universal.solutionsRegistry.srFiles.checkCapabilities = function (filename, solutionKey, solutionDef, genericTerms) { var missingTermsCount = 0; if (solutionDef.capabilities && solutionDef.capabilities.length) { fluid.each(solutionDef.capabilities, function (capabilityKey) { var unescapedKey = capabilityKey.replace(/\\/g, ""); var termDef = genericTerms[unescapedKey]; if (termDef === undefined) { missingTermsCount++; fluid.log("The capability '" + capabilityKey + "' specified in solution '" + solutionKey + "' does not exist."); } }); jqUnit.assertEquals("There should be no missing capabilities in solution '" + solutionKey + "'.", 0, missingTermsCount); } else { jqUnit.assert("Entry '" + solutionKey + "' has no capabilities, which is fine."); } }; // Ensure that all transforms involving common terms refer to common terms that actually exist. gpii.tests.universal.solutionsRegistry.srFiles.checkTransforms = function (filename, solutionKey, solutionDef, genericTerms) { fluid.each(solutionDef.settingsHandlers, function (settingsHandlerDef) { var allTransformTerms = []; if (settingsHandlerDef.capabilitiesTransformations) { var forwardTransformTerms = gpii.tests.universal.solutionsRegistry.srFiles.extractCommonTerms(settingsHandlerDef.capabilitiesTransformations); if (forwardTransformTerms.length) { allTransformTerms = allTransformTerms.concat(forwardTransformTerms); } } if (settingsHandlerDef.inverseCapabilitiesTransformations) { var inverseTransformTerms = gpii.tests.universal.solutionsRegistry.srFiles.extractCommonTerms(settingsHandlerDef.inverseCapabilitiesTransformations); if (inverseTransformTerms.length) { allTransformTerms = allTransformTerms.concat(inverseTransformTerms); } } var missingTermsCount = 0; fluid.each(allTransformTerms, function (transformTerm) { var unescapedTermKey = transformTerm.replace(/\\\./g, "."); var termExists = (fluid.get(genericTerms, [unescapedTermKey]) !== undefined); if (!termExists) { missingTermsCount++; } }); jqUnit.assertEquals("There should be no transforms that use missing common terms.", 0, missingTermsCount); }); }; gpii.tests.universal.solutionsRegistry.srFiles.extractCommonTerms = function (toInspect) { var foundTerms = []; if (typeof toInspect === "object") { fluid.each(toInspect, function (deepValue) { var subSegments = gpii.tests.universal.solutionsRegistry.srFiles.extractCommonTerms(deepValue); if (subSegments.length) { foundTerms = foundTerms.concat(subSegments); } }); } else if (typeof toInspect === "string" && toInspect.startsWith("http://registry.gpii.net/common/")) { foundTerms.push(toInspect); } return foundTerms; }; // If there are default values, test them against the schema. gpii.tests.universal.solutionsRegistry.srFiles.checkDefaults = function (globalValidator, filename, solutionKey, solutionDef) { var fileErrors = 0; fluid.each(solutionDef.settingsHandlers, function (settingsHandler) { fluid.each(settingsHandler.supportedSettings, function (supportedSetting, settingKey) { // Check the specified default against its schema. This only works for simple settings, and not for // deeply nested objects. var defaultValue = fluid.get(supportedSetting, "schema.default"); if (defaultValue !== undefined) { // Check the raw value var isValid = gpii.tests.universal.solutionsRegistry.validateSinglePayload( globalValidator, defaultValue, supportedSetting.schema, "Default value for " + filename + " -> " + solutionKey + " -> " + settingKey ); if (!isValid) { fileErrors++; } // Transform the default value to any "intra-application" variant we might expect to see and ensure that it's still valid. if (fluid.get(settingsHandler, ["capabilitiesTransformations", settingKey]) && settingsHandler.inverseCapabilitiesTransformations) { var settingWrapper = {}; fluid.set(settingWrapper, [settingKey], defaultValue); var invertedPayload = fluid.model.transformWithRules(settingWrapper, settingsHandler.inverseCapabilitiesTransformations); if (Object.keys(invertedPayload).length > 0) { var reinvertedPayload = fluid.model.transformWithRules(invertedPayload, settingsHandler.capabilitiesTransformations); var reinvertedDefaultValue = fluid.get(reinvertedPayload, [settingKey]); if (reinvertedDefaultValue !== defaultValue) { // Warn on "softly equal" things like "truthy" and "falsy" values. if (reinvertedDefaultValue != defaultValue) { // eslint-disable-line eqeqeq fluid.log("ERROR: The capabilities transforms for " + solutionKey + " -> " + settingKey + " may result in data corruption."); fileErrors++; } else { fluid.log("WARNING: The capabilities transforms for " + solutionKey + " -> " + settingKey + " are only safe for truthy and falsy values."); } } } } } }); }); jqUnit.assertEquals("There should be no validation errors in solutions registry file '" + filename + "'.", 0, fileErrors); }; fluid.defaults("gpii.tests.universal.solutionsRegistry.srFiles", { gradeNames: ["fluid.component"], solutionsRegistryPath: "%gpii-universal/testData/solutions", genericPreferenceTermsPath: "%gpii-universal/testData/ontologies/flat.json5", listeners: { "onCreate.checkAll": { funcName: "gpii.tests.universal.solutionsRegistry.srFiles.checkSolutionsFiles", args: ["{that}", "{gpii.schema.validator}"] } } }); gpii.tests.universal.solutionsRegistry.srFiles();