diff --git a/documentation/Configs.md b/documentation/Configs.md
index 0223ccf5b..c4a4a12fe 100644
--- a/documentation/Configs.md
+++ b/documentation/Configs.md
@@ -84,3 +84,18 @@ for the reset API and how to use it.
**Purpose**: This config fetches default settings from a remote URL. The default URL is set to
`https://raw.githubusercontent.com/GPII/universal/master/testData/defaultSettings/defaultSettings.win32.json5`
in this config. See [Reset Computer](ResetComputer.md) for what are default settings.
+
+### Load Solutions from the Repository in Production Contexts
+
+**Config file**: [`%flowManager/configs/gpii.config.local.flowManager.loadSolutionsFromRepository.json5`](../gpii/node_modules/flowManager/configs/gpii.config.local.flowManager.loadSolutionsFromRepository.json5)
+
+**Purpose**: This add-on config is for use in production where the cloud based
+flow manager supports a valid `/revision` end point, and where the local
+flow manager loads solutions registries from both its local hard drive and from
+the source code repository. See [SolutionsRegistryDataSource](SolutionsRegistryDataSource.md#local-flow-manager-solutions-registry-data-source)
+for more details.
+
+In addition, for development testing, the default is to assume there is no valid
+revision, and no request for the revision is made (see [GpiiRevisionRequester.js](../gpii/node_modules/flowManager/src/GpiiRevisionRequester.js)).
+This is done by setting the requester's `cloudURL` option to `null`. This
+effectively stops the process of loading solutions from the repository.
diff --git a/documentation/FlowManager.md b/documentation/FlowManager.md
index 6b65e7bef..cdcbe4dea 100644
--- a/documentation/FlowManager.md
+++ b/documentation/FlowManager.md
@@ -115,11 +115,19 @@ payload structure to start a matchMaking process.
cloud based components of the GPII.
* **route:** `/revision`
* **method:** `GET`
-* **return:** A JSON document containing the revision:
+* **return:** On success, an http status code of 200 and a payload containing the revision, e.g.:
-```json
+```json5
+{"sha256": "2602bdf868aec49993d8780feec42d4e9f995e21"}
+```
+
+Otherwise, returns status code 404 and an error payload:
+
+```json5
{
- "sha256": "2602bdf868aec49993d8780feec42d4e9f995e21"
+ "isError": true,
+ "message": "Error retrieving full git revision: %reason",
+ "statusCode": 404
}
```
diff --git a/documentation/SolutionsRegistryDataSource.md b/documentation/SolutionsRegistryDataSource.md
new file mode 100644
index 000000000..179976134
--- /dev/null
+++ b/documentation/SolutionsRegistryDataSource.md
@@ -0,0 +1,134 @@
+# Solutions Registry Data Source
+
+The solutions registry data source provides a RESTful means of fetching
+solutions registries via the [FlowManager](FlowManager.md) in order to determine
+which solutions are available and appropriate for a user's preferences. A
+solution is an application, such as the NVDA screen reader, or an operating
+system feature set using a control panel, such as the Windows high contrast
+theme. Each solution entry in the registry declares a set of preferences that
+it supports and describes how to configure the solution, launch it, reset it,
+and stop it.
+
+The solutions are listed in `JSON` files called "solutions registries". The
+structure of a solutions registry is documented in
+[SolutionsRegistryFormat](SolutionsRegistryFormat.md). Since solutions are
+frequently specific to an operating system, or "platform", there are separate
+solution registries for each platform. Examples of platforms are Windows,
+GNOME-Linux, MacOS, and Android.
+
+The SolutionsRegistryDataSource is a component that loads and caches
+solutions registries when the GPII starts up, and then serves solutions to the
+rest of the system upon request. The flow manager coordinates retrieval of
+solutions, user preferences, device information, and so on, passing these to the
+[MatchMaker Framework](MatchMakerFramework.md) and [LifecycleManager](LifecycleManager.md).
+
+There are two initialization workflows with respect to solutions registries
+depending on whether the flow manager is running in the cloud or locally on the
+client device. The Cloud Based Flow Manager uses a Solutions Registry Data
+Source implemented to run in that context, whereas the Local Flow Manager uses a
+Solutions Registry Data Source appropriate for running on client machines.
+These two scenarios are described in the following two sections.
+
+## Cloud Based Flow Manager Solutions Registry Data Source
+
+With respect to the Cloud Based Flow Manager, the solutions registries are
+included in the distribution of `gpii-universal` along with the other components
+of the GPII -- the flow manager, lifecycle manager and so on. The Cloud Based
+FlowManager is a central service for GPII clients, and these clients run on a
+variety of platforms. As such, the platform that the Cloud Based FlowManager is
+executing on is irrelevant in terms of providing solutions for the client. When
+a request for a solutionsvregistry is made, the flow manager needs to have
+access to all platform solutions in order to respond with the solutions relevant
+to a particular client.
+
+Here, the SolutionsRegistryDataSource component loads the solutions
+registries from the local file system at system startup. In this context,
+"local" refers to the file system associated with machine that the Cloud Based
+Flow Manager is running on. The SolutionsRegistryDataSource is a
+subcomponent of the flow manager, and the sequence of operations and
+events that occur during its instantiation are:
+
+
+loadSolutions.loadFromLocalDisk
,
+
+ - The solutions registry files are loaded from the local file system,
+
+
+loadSolutions.solutionsLoaded
+
+ - Fires a
solutionsRegistryReady
event.
+
+
+
+
+The `solutionsRegistryReady` informs the flow manager that its
+SolutionsRegistryDataSource is ready to provide solutions upon request.
+
+## Local Flow Manager Solutions Registry Data Source
+
+As in the case of the Cloud Based FlowManager, the solutions registries are
+included in the distribution of GPII for the client. However, they are not
+updated as frequently as those in the cloud. In particular, the solutions
+registry may be stale for the platform that the client is running on. However,
+the registry associated with the latest version of the cloud based GPII is
+available in `gpii-universal`'s source code repository (github), and can be
+downloaded from there. In this regard, the Cloud Based FlowManager provides a
+`/revision` end-point that responds with the full `SHA256` of the revision of
+the source associated with the latest solutions registries.
+
+The SolutionsRegistryDataSource associated with the Local FlowManager uses the
+following sequence of events and operations at startup:
+
+
+loadSolutions.loadFromLocalDisk
,
+
+ - The solutions registry files are loaded from the local file system,
+
+
+loadSolutions.getRevision
,
+
+ - Make an http request of the Cloud Based FlowManager for the revision of
+ the source code of the GPII used by the cloud,
+
+
+
+loadSolutions.loadFromRepository
,
+
+ - Make an http request of the source code respository, passing:
+
+ - the platform ID associated with the OS that the client is running on,
+ e.g. "darwin",
+
+ - the revision fetched at the previous step,
+
+
+ - The solutions registry corresponding to the platform and revision provided
+ is downloaded from the source code repository and overlays the one fetched
+ from the local file system at the first step.
+
+
+
+loadSolutions.solutionsLoaded
+
+ - Fires a
solutionsRegistryReady
event
+
+
+
+
+The above sequence is embeded within the Local FlowManager's `flowManagerReady`
+startup interlock such that all initialization is completed before the GPII
+client responds to user interactions.
+
+Note that both the `getRevision` and/or the `loadFromRepository` steps could
+fail. In that case, the latest solutions registry for the client platform will
+not be downloaded and cached within the client. When solutions for the client
+platform are requested, the SolutionsRegistryDataSource uses a fallback where
+the solutions loaded from the local file system during the first
+`loadFromLocalDisk` step are provided.
+
+Further note that the `getRevision` step feeds its result into the
+`loadFromRepository` step. The revision is necessary to fetch the
+correct solutions registry from the repository. If a developer wants to avoid
+that, they can set the `cloudURL` of the `GpiiRevisionRequester` to `null`,
+effectively stopping the entire sequence. No solutions registry will be
+downloaded from the repository in that case.
diff --git a/gpii/node_modules/flowManager/configs/gpii.config.local.flowManager.loadSolutionsFromRepository.json5 b/gpii/node_modules/flowManager/configs/gpii.config.local.flowManager.loadSolutionsFromRepository.json5
new file mode 100644
index 000000000..680c905c8
--- /dev/null
+++ b/gpii/node_modules/flowManager/configs/gpii.config.local.flowManager.loadSolutionsFromRepository.json5
@@ -0,0 +1,21 @@
+/**
+ * This configuration is for the production version of the system where the
+ * local flowManager provides its solutions registry dataSource with the url to
+ * the cloud based flowManager's /revision endpoint.
+ */
+{
+ "type": "gpii.config.local.flowManager.loadSolutionsFromRepository",
+ "options": {
+ "distributeOptions": {
+ "flowManager.defaultRevisionCloudURL": {
+ "record": "http://localhost:8084",
+ "target": "{that gpii.flowManager.local solutionsRegistryDataSource revisionRequester}.options.cloudURL"
+ },
+ "flowManager.gpiiRevisionCloudURL": {
+ "record": "@expand:kettle.resolvers.env(GPII_CLOUD_URL)",
+ "target": "{that gpii.flowManager.local solutionsRegistryDataSource revisionRequester}.options.cloudURL",
+ "priority": "after:flowManager.defaultRevisionCloudURL"
+ }
+ }
+ }
+}
diff --git a/gpii/node_modules/flowManager/configs/gpii.flowManager.config.base.json5 b/gpii/node_modules/flowManager/configs/gpii.flowManager.config.base.json5
index 30f65ca07..8e0d1b254 100644
--- a/gpii/node_modules/flowManager/configs/gpii.flowManager.config.base.json5
+++ b/gpii/node_modules/flowManager/configs/gpii.flowManager.config.base.json5
@@ -7,7 +7,6 @@
"distributeOptions": {
"flowManager.solutions": {
"record": {
- "type": "gpii.flowManager.solutionsRegistry.dataSource",
"options": {
"gradeNames": "gpii.flowManager.solutionsRegistry.dataSource.moduleTerms",
"path": "%gpii-universal/testData/solutions/"
diff --git a/gpii/node_modules/flowManager/configs/gpii.flowManager.config.local.base.json5 b/gpii/node_modules/flowManager/configs/gpii.flowManager.config.local.base.json5
index b7814ffb0..0e812a221 100644
--- a/gpii/node_modules/flowManager/configs/gpii.flowManager.config.local.base.json5
+++ b/gpii/node_modules/flowManager/configs/gpii.flowManager.config.local.base.json5
@@ -5,6 +5,14 @@
"flowManager.local": {
"record": ["gpii.flowManager.local"],
"target": "{that flowManager}.options.gradeNames"
+ },
+ "flowManager.local.solutionsRegistryRepositoryPrefix": {
+ "record": "https://raw.githubusercontent.com/GPII/universal",
+ "target": "{that flowManager solutionsRegistryDataSource repositorySolutionsLoader}.options.urlPrefix"
+ },
+ "flowManager.local.solutionsRegistryRepositorySuffix": {
+ "record": "testData/solutions",
+ "target": "{that flowManager solutionsRegistryDataSource repositorySolutionsLoader}.options.urlSuffix"
}
}
},
diff --git a/gpii/node_modules/flowManager/src/FlowManager.js b/gpii/node_modules/flowManager/src/FlowManager.js
index 61fc345d3..1c9f58d93 100644
--- a/gpii/node_modules/flowManager/src/FlowManager.js
+++ b/gpii/node_modules/flowManager/src/FlowManager.js
@@ -42,9 +42,8 @@ require("gpii-user-errors");
fluid.defaults("gpii.flowManager", {
gradeNames: ["kettle.app"],
components: {
- // TODO: Make this use the solutions registry data source by default?
solutionsRegistryDataSource: {
- type: "kettle.dataSource",
+ type: "gpii.flowManager.solutionsRegistry.dataSource.cloudBased",
options: {
termMap: {
"os": "%os",
@@ -154,9 +153,23 @@ fluid.defaults("gpii.flowManager.local", {
"lifecycleManager.pspChannel.sessionBinder": {
record: "gpii.pspChannel.sessionBinder",
target: "{that lifecycleManager gpii.lifecycleManager.userSession}.options.gradeNames"
+ },
+ "solutionsRegistryDataSource.platformReporter": {
+ record: {
+ platformReporter: "{gpii.flowManager.local}.deviceReporter.platformReporter"
+ },
+ target: "{that solutionsRegistryDataSource}.options.components"
}
},
components: {
+ solutionsRegistryDataSource: {
+ type: "gpii.flowManager.solutionsRegistry.dataSource.local",
+ options: {
+ events: {
+ solutionsRegistryReady: "{gpii.flowManager.local}.events.solutionsRegistryReady"
+ }
+ }
+ },
lifecycleManager: {
type: "gpii.lifecycleManager",
options: {
@@ -222,10 +235,12 @@ fluid.defaults("gpii.flowManager.local", {
preferencesSavedSuccess: null,
preferencesSavedError: null,
noUserLoggedIn: null,
+ solutionsRegistryReady: null,
getDefaultSettingsData: null,
defaultSettingsDataLoaded: null,
flowManagerReady: {
events: {
+ "solutionsRegistryReady": "solutionsRegistryReady",
"defaultSettingsDataLoaded": "defaultSettingsDataLoaded",
"kettleReady": "{kettle.server}.events.onListen"
}
@@ -242,7 +257,6 @@ fluid.defaults("gpii.flowManager.local", {
funcName: "gpii.flowManager.local.mountWebSocketsSettingsHandler",
args: ["{webSocketsSettingsHandlerComponent}"]
},
-
// Fire "getDefaultSettingsData" event to trigger promise transform chain that does:
// 1. Read default settings from the reset to default file;
// 2. Calculate defaultLifecycleInstructions and defaultSnapshot based on the default settings;
diff --git a/gpii/node_modules/flowManager/src/GpiiRevisionRequester.js b/gpii/node_modules/flowManager/src/GpiiRevisionRequester.js
new file mode 100644
index 000000000..846be9ff1
--- /dev/null
+++ b/gpii/node_modules/flowManager/src/GpiiRevisionRequester.js
@@ -0,0 +1,79 @@
+/*!
+GPII Full SHA Revision DataSource
+
+Copyright 2020 OCAD University
+
+Licensed under the New BSD license. You may not use this file except in
+compliance with this License.
+
+You may obtain a copy of the License at
+https://github.com/GPII/universal/blob/master/LICENSE.txt
+*/
+
+"use strict";
+
+var fluid = require("infusion"),
+ gpii = fluid.registerNamespace("gpii");
+
+require("kettle");
+
+fluid.registerNamespace("gpii.flowmanager.revisionRequester");
+
+fluid.defaults("gpii.flowmanager.revisionRequester", {
+ gradeNames: ["fluid.component"],
+
+ // Prepend CBFM host/port distributed down from, e.g., gpii.flowManager.config.untrusted
+ // For development tests where there is no valid /revision endpoint in the
+ // cloud, this is left as null.
+ cloudURL: null,
+ urlTemplate: "%cloudURL/revision",
+
+ // Compute url from CBFM base URL and template
+ revisionGetUrl: {
+ expander: {
+ funcName: "fluid.stringTemplate",
+ args: ["{that}.options.urlTemplate", {
+ cloudURL: "{that}.options.cloudURL"
+ }]
+ }
+ },
+ components: {
+ gpiiRevisionDataSource: {
+ type: "kettle.dataSource.URL",
+ options: {
+ url: "{revisionRequester}.options.revisionGetUrl"
+ }
+ }
+ },
+ invokers: {
+ getRevision: {
+ funcName: "gpii.flowmanager.revisionRequester.getRevision",
+ args: ["{that}"]
+ }
+ }
+});
+
+/**
+ * Retrieve the respository revision's full SHA256 from the cloud.
+ * @param {Component} that - An instance of gpii.flowManager.revisionRequester.
+ * @return {Promise} A promise whose resolved value is eiher the revision or, if
+ * there is an error, an object with an "isError: true" property. The revision
+ * has the form { "sha256": "86a83d2f93a6f8f954a4fef618ca6aea1399c980" }.
+ */
+gpii.flowmanager.revisionRequester.getRevision = function (that) {
+ var togo = fluid.promise();
+ if (that.options.cloudURL !== null) {
+ var revisionPromise = that.gpiiRevisionDataSource.get();
+ revisionPromise.then(function (/*revision*/) {
+ fluid.promise.follow(revisionPromise, togo);
+ }, function (err) {
+ togo.resolve(err);
+ });
+ } else {
+ // If the url to the CBFM is null, assume this is running in a
+ // development testing enviroment or, generally, a scenario where
+ // requests for the revision are to be suppressed.
+ togo.resolve(null);
+ }
+ return togo;
+};
diff --git a/gpii/node_modules/flowManager/src/MatchMaking.js b/gpii/node_modules/flowManager/src/MatchMaking.js
index f805484db..0d051f5d5 100644
--- a/gpii/node_modules/flowManager/src/MatchMaking.js
+++ b/gpii/node_modules/flowManager/src/MatchMaking.js
@@ -96,17 +96,15 @@
gpii.flowManager.getSolutions = function (solutionsRegistryDataSource, deviceContext, onSuccessEvent, onErrorEvent) {
var promiseTogo = fluid.promise();
- var os = fluid.get(deviceContext, "OS.id");
- var promise = solutionsRegistryDataSource.get({});
- promise.then(function (solutions) {
- var solutionsRegistryEntries = gpii.matchMakerFramework.filterSolutions(solutions[os], deviceContext);
+ var promise = solutionsRegistryDataSource.get({os: fluid.get(deviceContext, "OS.id")});
+ promise.then(function (entries) {
+ var solutionsRegistryEntries = gpii.matchMakerFramework.filterSolutions(entries, deviceContext);
fluid.log("Fetched filtered solutions registry entries: ", gpii.renderMegapayload({solutionsRegistryEntries: solutionsRegistryEntries}));
promiseTogo.resolve({
- solutionsRegistryEntries: solutionsRegistryEntries,
- solutions: solutions
+ solutionsRegistryEntries: solutionsRegistryEntries
});
if (onSuccessEvent) {
- onSuccessEvent.fire(solutionsRegistryEntries, solutions);
+ onSuccessEvent.fire(solutionsRegistryEntries);
}
}, function (error) {
promiseTogo.reject(error);
@@ -114,7 +112,6 @@
onErrorEvent.fire(error);
}
});
-
return promiseTogo;
};
@@ -187,8 +184,7 @@
gpiiKey: "{that}.gpiiKey",
preferences: "{arguments}.preferences.0",
deviceContext: "{arguments}.deviceContext.0",
- solutionsRegistryEntries: "{arguments}.solutions.0",
- fullSolutionsRegistry: "{arguments}.solutions.1"
+ solutionsRegistryEntries: "{arguments}.solutions.0"
}]
}
},
diff --git a/gpii/node_modules/flowManager/src/RepositorySolutionsLoader.js b/gpii/node_modules/flowManager/src/RepositorySolutionsLoader.js
new file mode 100644
index 000000000..f6527a6c9
--- /dev/null
+++ b/gpii/node_modules/flowManager/src/RepositorySolutionsLoader.js
@@ -0,0 +1,108 @@
+/*!
+GPII Full SHA Revision DataSource
+
+Copyright 2020 OCAD University
+
+Licensed under the New BSD license. You may not use this file except in
+compliance with this License.
+
+You may obtain a copy of the License at
+https://github.com/GPII/universal/blob/master/LICENSE.txt
+*/
+
+"use strict";
+
+var fluid = require("infusion"),
+ gpii = fluid.registerNamespace("gpii");
+
+require("kettle");
+
+fluid.registerNamespace("gpii.flowManager.repositorySolutionsLoader");
+
+fluid.defaults("gpii.flowManager.repositorySolutionsLoader", {
+ gradeNames: ["kettle.dataSource.URL"],
+ urlTemplate: "%prefix/%revision/%suffix/%fileName",
+ url: {
+ expander: {
+ funcName: "fluid.stringTemplate",
+ args: ["{that}.options.urlTemplate", {
+ prefix: "{that}.options.urlPrefix",
+ suffix: "{that}.options.urlSuffix"
+ }]
+ }
+ },
+ termMap: {
+ revision: "%directRevision",
+ fileName: "%directFileName"
+ },
+ // The URL prefix and suffix are distributed from a config file such as
+ // e.g., gpii.flowManager.config.local.base.
+ urlPrefix: "",
+ urlSuffix: "",
+ protocol: "https:", // default
+ members: {
+ // Contents of the solutions registry file downloaded from the source
+ // code repository
+ solutionsRegistry: null
+ },
+ components: {
+ encoding: {
+ type: "kettle.dataSource.encoding.JSON5"
+ }
+ },
+ invokers: {
+ getSolutions: {
+ funcName: "gpii.flowManager.repositorySolutionsLoader.getSolutions",
+ args: ["{that}", "{arguments}.0", "{arguments}.1"]
+ // gpii revision, platform id
+ }
+ }
+});
+
+/**
+ * Retrieve the solutions registry JSON file from the source code repository.
+ * @param {Component} that - An instance of gpii.flowManager.repositorySolutionsLoader:
+ * @param {Component} that.solutionsRegistry - The contents of the solutions
+ * registry fetched from the source code repository, set herein (will be
+ * `null` on failure).
+ * @param {String} gpiiRevision - the SHA256 revision of the repository to fetch.
+ * @param {String} platformId - the platform matching the solutions registry
+ * that this is retrieving, e.g. "win32". It is used
+ * to construct the name of the solutions registry
+ * file.
+ * @return {Promise} A promise whose resolved value is eiher the solutions
+ * registry, or, if there is an error, an object with an "isError: true"
+ * property.
+ */
+gpii.flowManager.repositorySolutionsLoader.getSolutions = function (that, gpiiRevision, platformId) {
+ var togo = fluid.promise();
+ if (gpiiRevision && platformId) {
+ var fileName = platformId + ".json5";
+ var solutionsPromise = that.get({
+ directRevision: gpiiRevision,
+ directFileName: fileName
+ });
+ solutionsPromise.then(function (solutions) {
+ var taggedRegistry = {};
+ taggedRegistry[platformId] = solutions;
+ that.solutionsRegistry = fluid.freezeRecursive(taggedRegistry);
+ togo.resolve(that.solutionsRegistry);
+ }, function (err) {
+ fluid.log("Error retrieving solutions from repository: ", err, ", ", that.options.url);
+ togo.reject(err);
+ });
+ } else {
+ var msg = "Error retrieving solutions from repository: missing ";
+ if (!gpiiRevision && !platformId) {
+ msg += "revision and platform ID";
+ }
+ else if (!gpiiRevision) {
+ msg += "revision";
+ }
+ else {
+ msg += "platform ID";
+ }
+ togo.reject({isError: true, message: msg, statusCode: 404});
+ }
+ return togo;
+};
diff --git a/gpii/node_modules/flowManager/src/SolutionsRegistryDataSource.js b/gpii/node_modules/flowManager/src/SolutionsRegistryDataSource.js
index 1f81a4c72..4b6aba9a6 100644
--- a/gpii/node_modules/flowManager/src/SolutionsRegistryDataSource.js
+++ b/gpii/node_modules/flowManager/src/SolutionsRegistryDataSource.js
@@ -2,6 +2,7 @@
* GPII Solutions Registry Datasource
*
* Copyright 2016 RtF-I
+ * Copyright 2020 OCAD University
*
* Licensed under the New BSD license. You may not use this file except in
* compliance with this License.
@@ -20,13 +21,20 @@ var fluid = require("infusion"),
gpii = fluid.registerNamespace("gpii"),
fs = require("fs");
-
require("kettle");
+require("./GpiiRevisionRequester.js");
+require("./RepositorySolutionsLoader.js");
fluid.registerNamespace("gpii.flowManager.solutionsRegistry");
+// The base solutions registry data source, containing information and functions
+// shared by derived grades.
fluid.defaults("gpii.flowManager.solutionsRegistry.dataSource", {
gradeNames: ["kettle.dataSource"],
+ termMap: {
+ "os": "%os",
+ "version": "%version"
+ },
components: {
encoding: {
type: "kettle.dataSource.encoding.none"
@@ -35,21 +43,35 @@ fluid.defaults("gpii.flowManager.solutionsRegistry.dataSource", {
members: {
fullSolutionsRegistry: null
},
- readOnlyGrade: "gpii.flowManager.solutionsRegistry.dataSource",
invokers: {
- getImpl: {
- funcName: "gpii.flowManager.solutionsRegistry.dataSource.handle",
- args: ["{that}", "{arguments}.1", "{arguments}.2"]
- // options, directModel
+ loadFromLocalDisk: {
+ funcName: "gpii.flowManager.solutionsRegistry.dataSource.loadFromLocalDisk",
+ args: ["{that}"]
}
},
+ events: {
+ loadSolutions: null,
+ solutionsRegistryReady: null
+ },
listeners: {
- onCreate: "gpii.flowManager.solutionsRegistry.dataSource.loadSolutionsRegistry"
+ "onCreate.loadSolutions": {
+ listener: "fluid.promise.fireTransformEvent",
+ args: ["{that}.events.loadSolutions"]
+ }
}
});
-// TODO: Add an invoker to reload once we are "more live".
-gpii.flowManager.solutionsRegistry.dataSource.loadSolutionsRegistry = function (that) {
+/**
+ * Load the solutions registry from the local file system -- the file system
+ * that this solutions registry data source is running on.
+ *
+ * @param {Object} that - The gpii.flowManager.solutionsRegistry.dataSource.
+ * @param {String} that.options.path - The path to the solutions registry
+ * directory.
+ * @param {Object} that.fullSolutionsRegistry - This will be set to the
+ * collection of solution registries given by path.
+ */
+gpii.flowManager.solutionsRegistry.dataSource.loadFromLocalDisk = function (that) {
if (!that.options.path) {
fluid.fail("The solutionsRegistry datasource ", that, " needs a \"path\" option pointing to the solution entries folder");
}
@@ -57,33 +79,189 @@ gpii.flowManager.solutionsRegistry.dataSource.loadSolutionsRegistry = function (
if (!fs.existsSync(url)) {
fluid.fail("The path provided to the solutionsRegistry datasource (", url, ") has not been found on the file system");
}
-
that.fullSolutionsRegistry = fluid.freezeRecursive(require(url));
};
+// Solutions registry data source used by the Cloud Based Flow Manager
+fluid.defaults("gpii.flowManager.solutionsRegistry.dataSource.cloudBased", {
+ gradeNames: ["gpii.flowManager.solutionsRegistry.dataSource"],
+ readOnlyGrade: "gpii.flowManager.solutionsRegistry.dataSource.cloudBased",
+ invokers: {
+ getImpl: {
+ funcName: "gpii.flowManager.solutionsRegistry.dataSource.cloudBased.handle",
+ args: ["{that}", "{arguments}.1", "{arguments}.2"]
+ // options, directModel
+ }
+ },
+ listeners: {
+ "loadSolutions.loadFromLocalDisk": {
+ listener: "{that}.loadFromLocalDisk",
+ priority: "first"
+ },
+ "loadSolutions.solutionsLoaded": {
+ listener: "{that}.events.solutionsRegistryReady",
+ priority: "after:loadFromLocalDisk"
+ }
+ }
+});
+
/**
- * Handler for get requests of solutions registry. It will return either a full solution registry,
- * or if an 'os' is provided in the requestOptions, only the entries for that os will be returned
+ * Handler for get requests to the solutions registry used by the Cloud Based
+ * Flow Manager. It will return an object containing the solutions registry for
+ * the given platform.
*
- * @param {Object} that - The gpii.flowManager.solutionsRegistry.dataSource.
- * @param {Object} requestOptions - Currently the only request option supported is "os". If provided,
- * the returned solutions registry will be filtered by OS version.
- * @return {Promise} A promise that will be resolved with results (see above) or rejected on error.
+ * @param {Object} that - The gpii.flowManager.solutionsRegistry.dataSource.cloudBased
+ instance that is handling the GET request.
+ * @param {Object} requestOptions - Currently the only request option supported
+ * is "os". The returned solutions registry will be the solutions for
+ * the OS version provided.
+ * @return {Promise} A promise that will be resolved with results (see above) or
+ * rejected on error.
+ */
+gpii.flowManager.solutionsRegistry.dataSource.cloudBased.handle = function (that, requestOptions) {
+ var promise = fluid.promise();
+ if (requestOptions.os && requestOptions.os in that.fullSolutionsRegistry) {
+ promise.resolve(that.fullSolutionsRegistry[requestOptions.os]);
+ } else {
+ promise.reject({
+ isError: true,
+ message: "The requested OS (" + requestOptions.os + ") was not present in the solutions registry"
+ });
+ }
+ return promise;
+};
+
+// Solutions registry datasource used by the Local Flow Manager. On creation,
+// it first loads all of the solution registries from the local file system for
+// all of the platforms (win32, linux, darwin, android, etc). Then, the
+// solutions for the current platform are downloaded from the source code
+// repository. The current platform is the platform on which the Local Flow
+// Manager is running. The solutions fetched from the source code repository
+// overlay the set retrieved from the local file system at the first step. If
+// the download from the source code repository fails, then the solutions loaded
+// from the local file system are used.
+fluid.defaults("gpii.flowManager.solutionsRegistry.dataSource.local", {
+ gradeNames: ["gpii.flowManager.solutionsRegistry.dataSource"],
+ components: {
+ revisionRequester: {
+ type: "gpii.flowmanager.revisionRequester"
+ },
+ repositorySolutionsLoader: {
+ type: "gpii.flowManager.repositorySolutionsLoader"
+ }
+// The platformReporter is distributed down from the local flowManager
+// platformReporter: {
+// type: "gpii.platformReporter.native"
+// }
+ },
+ readOnlyGrade: "gpii.flowManager.solutionsRegistry.dataSource.local",
+ members: {
+ repositorySolutionsRegistry: null
+ },
+ invokers: {
+ getImpl: {
+ funcName: "gpii.flowManager.solutionsRegistry.dataSource.local.handle",
+ args: ["{that}", "{arguments}.1"]
+ // options
+ }
+ },
+ listeners: {
+ "loadSolutions.loadFromLocalDisk": {
+ listener: "{that}.loadFromLocalDisk",
+ priority: "first"
+ },
+ "loadSolutions.getRevision": {
+ listener: "{revisionRequester}.getRevision",
+ priority: "after:loadFromLocalDisk"
+ },
+ "loadSolutions.loadFromRepository": {
+ listener: "gpii.flowManager.solutionsRegistry.dataSource.local.loadFromRepository",
+ args: ["{that}", "{arguments}.0", "{that}.platformReporter"],
+ // revision
+ priority: "after:getRevision"
+ },
+ "loadSolutions.solutionsLoaded": {
+ listener: "{that}.events.solutionsRegistryReady",
+ priority: "after:loadFromRepository"
+ }
+ }
+});
+
+/**
+ * Retrieve the solutions registry JSON file from the source code repository.
+ * If successful, sets the repositorySolutionsRegistry member to the fetched
+ * solutions registry.
+ * @param {Component} that - An instance of gpii.flowManager.solutionsRegistry.dataSource
+ * @param {Object} that.repositorySolutionsRegistry - Set to point to the
+ * solutions registry retrieved from the respository, or null if
+ * none were retrieved.
+ * @param {Object} revision - the SHA256 revision of the repository in the form:
+ * { sha256: ... }. If the sha256 is missing, the
+ * result of the load is null.
+ * @param {Component} platformReporter - used to get the name of the platform,
+ * e.g., "win32".
+ * @return {Promise} A promise whose resolved value is either the solutions
+ * registry from the source code repository, or null, if there was an error.
+ */
+gpii.flowManager.solutionsRegistry.dataSource.local.loadFromRepository = function (that, revision, platformReporter) {
+ var togo = fluid.promise();
+ if (revision) {
+ var gpiiRevision = revision.sha256;
+ var platformId = platformReporter.reportPlatform().id;
+ var repoLoadPromise = that.repositorySolutionsLoader.getSolutions(gpiiRevision, platformId);
+
+ // Either the solutions registry has been successfully retrieved from the
+ // repository, or it hasn't. Set that.repositorySolutionsRegistry to
+ // to either the result, or to null.
+ repoLoadPromise.then(
+ function (repositorySolutions) {
+ that.repositorySolutionsRegistry = repositorySolutions;
+ togo.resolve(repositorySolutions);
+ },
+ function () {
+ that.repositorySolutionsRegistry = null;
+ togo.resolve(null);
+ }
+ );
+ } else {
+ that.repositorySolutionsRegistry = null;
+ togo.resolve(null);
+ }
+ return togo;
+};
+
+/**
+ * Handler for get requests when running with the gpii-app client and the Local
+ * Flow Manager. If a solutions registry was retrieved from the source code
+ * repository, it is used to mask the equivalent platform in the full set of
+ * solutions registries. This handler will return the solutions registry for
+ * the given platform.
+ * @param {Object} that - The gpii.flowManager.solutionsRegistry.dataSource.local
+ * instance.
+ * @param {Object} requestOptions - Currently the only request option supported
+ * is "os". The returned solutions registry will be the solutions for the
+ * given OS version.
+ * @return {Promise} A promise that will be resolved with results (see above) or
+ * rejected on error.
*/
-gpii.flowManager.solutionsRegistry.dataSource.handle = function (that, requestOptions) {
+gpii.flowManager.solutionsRegistry.dataSource.local.handle = function (that, requestOptions) {
var promise = fluid.promise();
- if (requestOptions.os) { // if "os" is defined, return only solution registry entries for that OS
- if (requestOptions.os in that.fullSolutionsRegistry) {
+ if (requestOptions && requestOptions.os) {
+ if (that.repositorySolutionsRegistry && (requestOptions.os in that.repositorySolutionsRegistry)) {
+ promise.resolve(that.repositorySolutionsRegistry[requestOptions.os]);
+ } else if (requestOptions.os in that.fullSolutionsRegistry) {
promise.resolve(that.fullSolutionsRegistry[requestOptions.os]);
} else {
promise.reject({
isError: true,
- message: "The requested OS (" + requestOptions.os + ") was not present in the solutions registry",
- statusCode: 404
+ message: "The requested OS (" + requestOptions.os + ") was not present in the solutions registry"
});
}
- } else { // if no "os" is requested, return the full solutions registry
- promise.resolve(that.fullSolutionsRegistry);
+ } else {
+ promise.reject({
+ isError: true,
+ message: "Missing OS (undefined) for accessing the solutions registry"
+ });
}
return promise;
};
diff --git a/gpii/node_modules/flowManager/test/CaptureTests.js b/gpii/node_modules/flowManager/test/CaptureTests.js
index 981fafa96..16fe93fc2 100644
--- a/gpii/node_modules/flowManager/test/CaptureTests.js
+++ b/gpii/node_modules/flowManager/test/CaptureTests.js
@@ -41,9 +41,10 @@ gpii.tests.flowManager.capture.checkForFakeMags = function (payload) {
}, payload.fakemag2);
};
+gpii.tests.flowManager.capture.platformId = "darwin";
gpii.tests.flowManager.capture.platformReporter = function () {
return {
- id: "darwin"
+ id: gpii.tests.flowManager.capture.platformId
};
};
@@ -53,9 +54,17 @@ gpii.tests.flowManager.capture.checkForInstalledMags = function (payload) {
jqUnit.assertEquals("Check for Fake Mag 2", "Fake Magnifier 2 - fully featured", payload.fakemag2.name);
};
+gpii.tests.flowManager.capture.handleSolutionsReadyEvent = function (that) {
+ that.isReady = true;
+};
+
+gpii.tests.flowManager.capture.solutionsRegistryIsReady = function (msg, solutionsRegistryDataSource) {
+ jqUnit.assertTrue(msg, solutionsRegistryDataSource.isReady);
+};
+
/*
* A simple resolver config for testing resolver substituion inside of solutions for
- * capture. This only needs to support a single varialbe in test `PWD` that will be
+ * capture. This only needs to support a single variable in test `PWD` that will be
* used to lookup json settings files for the fake magnifier configurations.
*/
fluid.defaults("gpii.tests.flowManager.capture.standardResolverConfig", {
@@ -81,8 +90,18 @@ fluid.defaults("gpii.tests.flowManager.capture.tests", {
name: "Simple system capture",
tests: [{
name: "Check for existing FakeMag Settings",
- expect: 2,
+ expect: 3,
sequence: [{
+ // Cannot listen for solutionsRegistryReady event directly
+ // since it is long gone by the time this executes. Check
+ // that the handleSolutionsReadyEvent listener heard the
+ // event.
+ funcName: "gpii.tests.flowManager.capture.solutionsRegistryIsReady",
+ args: [
+ "Solutions registry ready",
+ "{config}.server.flowManager.solutionsRegistryDataSource"
+ ]
+ }, {
task: "{config}.server.flowManager.capture.getSystemSettingsCapture",
args: [],
resolve: "gpii.tests.flowManager.capture.checkForFakeMags",
diff --git a/gpii/node_modules/flowManager/test/GpiiRevisionRequesterTests.js b/gpii/node_modules/flowManager/test/GpiiRevisionRequesterTests.js
new file mode 100644
index 000000000..8706617dd
--- /dev/null
+++ b/gpii/node_modules/flowManager/test/GpiiRevisionRequesterTests.js
@@ -0,0 +1,219 @@
+/*!
+GPII revision request tests
+
+Copyright 2020 OCAD University
+
+Licensed under the New BSD license. You may not use this file except in
+compliance with this License.
+
+You may obtain a copy of the License at
+https://github.com/GPII/universal/blob/master/LICENSE.txt
+*/
+
+"use strict";
+
+var fluid = fluid || require("infusion"),
+ gpii = fluid.registerNamespace("gpii"),
+ kettle = require("kettle"),
+ nock = require("nock");
+
+fluid.require("%gpii-universal/gpii/node_modules/testing/src/NockUtils.js");
+fluid.require("%flowManager/src/GpiiRevisionRequester.js");
+
+kettle.loadTestingSupport();
+
+fluid.registerNamespace("gpii.tests.revisionRequester");
+
+gpii.tests.revisionRequester.hostname = "http://gpii.net";
+gpii.tests.revisionRequester.path = "/revision";
+
+// Set up mock cloud request/response
+gpii.tests.revisionRequester.setUpNock = function (config) {
+ var cloudMock = nock(config.hostname);
+ cloudMock.log(console.log);
+
+ // mock GET request
+ cloudMock.get(config.path)
+ .reply(config.status, config.response);
+};
+
+// Revision requester customized for testing
+fluid.defaults("gpii.tests.revisionRequester", {
+ gradeNames: ["gpii.flowmanager.revisionRequester"],
+ cloudURL: gpii.tests.revisionRequester.hostname
+});
+
+// Base testEnvironment
+fluid.defaults("gpii.tests.revisionRequesterTests", {
+ gradeNames: ["fluid.test.testEnvironment"],
+ testCaseHolderGrade: null, // supplied by individual tests
+ distributeOptions: {
+ testCaseHolderGrade: {
+ source: "{that}.options.testCaseHolderGrade",
+ target: "{that > testCaseHolder}.type"
+ }
+ },
+ components: {
+ revisionRequester: {
+ type: "gpii.tests.revisionRequester"
+ },
+ testCaseHolder: {
+ type: "fluid.test.testCaseHolder"
+ }
+ }
+});
+
+// 1. Successful retrieval
+gpii.tests.revisionRequester.success = {
+ nockConfig: {
+ hostname: gpii.tests.revisionRequester.hostname,
+ path: gpii.tests.revisionRequester.path,
+ type: "get",
+ status: 200,
+ response: {"sha256": "2602bdf868aec49993d8780feec42d4e9f995e21"}
+ }
+};
+
+fluid.defaults("gpii.tests.revisionRequester.testCaseHolder.success", {
+ gradeNames: "fluid.test.testCaseHolder",
+ modules: [{
+ name: "Revision requester module tests - successful retrieval",
+ expect: 1,
+ tests: [{
+ name: "Response: valid revision",
+ sequence: [{
+ task: "{revisionRequester}.getRevision",
+ resolve: "jqUnit.assertDeepEq",
+ resolveArgs: [
+ "The response is the expected revision",
+ gpii.tests.revisionRequester.success.nockConfig.response,
+ "{arguments}.0"
+ ]
+ }]
+ }]
+ }]
+});
+
+fluid.defaults("gpii.tests.revisionRequesterTests.success", {
+ gradeNames: ["gpii.tests.revisionRequesterTests", "gpii.test.testWithNock"],
+ testCaseHolderGrade: "gpii.tests.revisionRequester.testCaseHolder.success",
+ invokers: {
+ setUpNock: {
+ funcName: "gpii.tests.revisionRequester.setUpNock",
+ args: gpii.tests.revisionRequester.success.nockConfig
+ }
+ }
+});
+
+// 2. Retrieval of malformed or missing revision
+gpii.tests.revisionRequester.missingRevision = {
+ nockConfig: {
+ hostname: gpii.tests.revisionRequester.hostname,
+ path: gpii.tests.revisionRequester.path,
+ type: "get",
+ status: 404,
+ response: {
+ isError: true,
+ statusCode: 404,
+ message: "Error retrieving full git revision: Missing revision value"
+ }
+ },
+ expected: {
+ isError: true,
+ statusCode: 404,
+ // FIXME: Find out where in nock "while executing HTTP GET on url http://gpii.net/revision"
+ // is coming from, and get rid of it.
+ message: "Error retrieving full git revision: Missing revision value while executing HTTP GET on url http://gpii.net/revision"
+ }
+};
+
+fluid.defaults("gpii.tests.revisionRequester.testCaseHolder.missingRevision", {
+ gradeNames: "fluid.test.testCaseHolder",
+ modules: [{
+ name: "Revision requester module tests - missing revision",
+ expect: 1,
+ tests: [{
+ name: "Response: missing revision",
+ sequence: [{
+ task: "{revisionRequester}.getRevision",
+ resolve: "jqUnit.assertDeepEq",
+ resolveArgs: [
+ "The response is expected as missing",
+ gpii.tests.revisionRequester.missingRevision.expected,
+ "{arguments}.0"
+ ]
+ }]
+ }]
+ }]
+});
+
+fluid.defaults("gpii.tests.revisionRequesterTests.missingRevision", {
+ gradeNames: ["gpii.tests.revisionRequesterTests", "gpii.test.testWithNock"],
+ testCaseHolderGrade: "gpii.tests.revisionRequester.testCaseHolder.missingRevision",
+ invokers: {
+ setUpNock: {
+ funcName: "gpii.tests.revisionRequester.setUpNock",
+ args: gpii.tests.revisionRequester.missingRevision.nockConfig
+ }
+ }
+});
+
+// 3. Null cloudURL for the revision request
+gpii.tests.revisionRequester.nullCloudUrl = {
+ nockConfig: {
+ // The hostname should be null here, but nock can't handle it. Taken
+ // care of in the setup of the GpiiRevisionRquester used in the test:
+ // see the "components" block of
+ // "gpii.tests.revisionRequesterTests.nullCloudUrl" below.
+ hostname: gpii.tests.revisionRequester.hostname,
+ path: null,
+ type: "get"
+ },
+ expected: null
+};
+
+fluid.defaults("gpii.tests.revisionRequester.testCaseHolder.nullCloudUrl", {
+ gradeNames: "fluid.test.testCaseHolder",
+ modules: [{
+ name: "Revision requester module tests - null cloudURL",
+ expect: 1,
+ tests: [{
+ name: "No revision request with a null result",
+ sequence: [{
+ task: "{revisionRequester}.getRevision",
+ resolve: "jqUnit.assertNull",
+ resolveArgs: [
+ "A null result is expected",
+ gpii.tests.revisionRequester.nullCloudUrl.expected,
+ "{arguments}.0"
+ ]
+ }]
+ }]
+ }]
+});
+
+fluid.defaults("gpii.tests.revisionRequesterTests.nullCloudUrl", {
+ gradeNames: ["gpii.tests.revisionRequesterTests", "gpii.test.testWithNock"],
+ testCaseHolderGrade: "gpii.tests.revisionRequester.testCaseHolder.nullCloudUrl",
+ invokers: {
+ setUpNock: {
+ funcName: "gpii.tests.revisionRequester.setUpNock",
+ args: gpii.tests.revisionRequester.nullCloudUrl.nockConfig
+ }
+ },
+ components: {
+ revisionRequester: {
+ type: "gpii.tests.revisionRequester",
+ options: {
+ cloudURL: null
+ }
+ }
+ }
+});
+
+// Run all tests
+fluid.test.runTests([
+ "gpii.tests.revisionRequesterTests.success",
+ "gpii.tests.revisionRequesterTests.missingRevision",
+ "gpii.tests.revisionRequesterTests.nullCloudUrl"
+]);
diff --git a/gpii/node_modules/flowManager/test/RepositorySolutionsLoaderTests.js b/gpii/node_modules/flowManager/test/RepositorySolutionsLoaderTests.js
new file mode 100644
index 000000000..e557bb0fe
--- /dev/null
+++ b/gpii/node_modules/flowManager/test/RepositorySolutionsLoaderTests.js
@@ -0,0 +1,305 @@
+/*!
+GPII download and save solutions registry from source code repositoyr
+
+Copyright 2020 OCAD University
+
+Licensed under the New BSD license. You may not use this file except in
+compliance with this License.
+
+You may obtain a copy of the License at
+https://github.com/GPII/universal/blob/master/LICENSE.txt
+*/
+
+"use strict";
+
+var fluid = fluid || require("infusion"),
+ gpii = fluid.registerNamespace("gpii"),
+ kettle = require("kettle"),
+ nock = require("nock");
+
+fluid.require("%gpii-universal/gpii/node_modules/testing/src/NockUtils.js");
+fluid.require("%flowManager/src/RepositorySolutionsLoader.js");
+
+kettle.loadTestingSupport();
+
+fluid.registerNamespace("gpii.tests.repositorySolutionsLoader");
+
+gpii.tests.repositorySolutionsLoader = {
+ hostname: "http://gpii.net",
+ prefix: "prefix",
+ revision: "32759c38f9e1a5a38072f8c5e60b02a5d20969d7",
+ suffix: "suffix",
+ platformId: "darwin",
+ fileName: "darwin.json5",
+ solutionsRegistryFolderPath: "%flowManager/test/data/",
+ taggedSolutions: {} // filled in below
+};
+gpii.tests.repositorySolutionsLoader.path = "/" +
+ gpii.tests.repositorySolutionsLoader.prefix + "/" +
+ gpii.tests.repositorySolutionsLoader.revision + "/" +
+ gpii.tests.repositorySolutionsLoader.suffix + "/" +
+ gpii.tests.repositorySolutionsLoader.fileName;
+
+gpii.tests.repositorySolutionsLoader.solutions = fluid.require(
+ gpii.tests.repositorySolutionsLoader.solutionsRegistryFolderPath +
+ gpii.tests.repositorySolutionsLoader.fileName
+);
+
+// The solutions registry files contain a set of solutions as an object. The
+// RepositorySolutionsLoader tags the set with a platform ID and returns the
+// tagged set as its payload.
+fluid.set(
+ gpii.tests.repositorySolutionsLoader.taggedSolutions,
+ gpii.tests.repositorySolutionsLoader.platformId,
+ gpii.tests.repositorySolutionsLoader.solutions
+);
+
+// Set up mock cloud request/response
+gpii.tests.repositorySolutionsLoader.setUpNock = function (config) {
+ var cloudMock = nock(gpii.tests.repositorySolutionsLoader.hostname);
+ cloudMock.log(console.log);
+
+ // mock GET request
+ cloudMock.get(gpii.tests.repositorySolutionsLoader.path)
+ .reply(config.status, config.response);
+};
+
+// Repository loader customized for testing
+fluid.defaults("gpii.tests.repositorySolutionsLoader", {
+ gradeNames: ["gpii.flowManager.repositorySolutionsLoader"],
+ urlPrefix: gpii.tests.repositorySolutionsLoader.hostname + "/" + gpii.tests.repositorySolutionsLoader.prefix,
+ urlSuffix: gpii.tests.repositorySolutionsLoader.suffix,
+ protocol: "http:"
+});
+
+// Base testEnvironment
+fluid.defaults("gpii.tests.repositorySolutionsLoaderTests", {
+ gradeNames: ["fluid.test.testEnvironment"],
+ testCaseHolderGrade: null, // supplied by individual tests
+ distributeOptions: {
+ testCaseHolderGrade: {
+ source: "{that}.options.testCaseHolderGrade",
+ target: "{that > testCaseHolder}.type"
+ }
+ },
+ components: {
+ repositorySolutionsLoader: {
+ type: "gpii.tests.repositorySolutionsLoader"
+ },
+ testCaseHolder: {
+ type: "fluid.test.testCaseHolder"
+ }
+ }
+});
+
+// 1. Successful retrieval
+gpii.tests.repositorySolutionsLoader.success = {
+ nockConfig: {
+ url: gpii.tests.repositorySolutionsLoader.hostname + gpii.tests.repositorySolutionsLoader.path,
+ type: "get",
+ status: 200,
+ response: gpii.tests.repositorySolutionsLoader.solutions
+ }
+};
+
+fluid.defaults("gpii.tests.repositorySolutionsLoader.testCaseHolder.success", {
+ gradeNames: "fluid.test.testCaseHolder",
+ modules: [{
+ name: "Repository solutions loader module tests - successful retrieval",
+ expect: 1,
+ tests: [{
+ name: "Response: solutions loaded",
+ sequence: [{
+ task: "{repositorySolutionsLoader}.getSolutions",
+ args: [
+ gpii.tests.repositorySolutionsLoader.revision,
+ gpii.tests.repositorySolutionsLoader.platformId
+ ],
+ resolve: "jqUnit.assertDeepEq",
+ resolveArgs: [
+ "Check response for tagged solutions",
+ gpii.tests.repositorySolutionsLoader.taggedSolutions,
+ "{arguments}.0"
+ ]
+ }]
+ }]
+ }]
+});
+
+fluid.defaults("gpii.tests.repositorySolutionsLoaderTests.success", {
+ gradeNames: ["gpii.tests.repositorySolutionsLoaderTests", "gpii.test.testWithNock"],
+ testCaseHolderGrade: "gpii.tests.repositorySolutionsLoader.testCaseHolder.success",
+ invokers: {
+ setUpNock: {
+ funcName: "gpii.tests.repositorySolutionsLoader.setUpNock",
+ args: gpii.tests.repositorySolutionsLoader.success.nockConfig
+ }
+ }
+});
+
+// 2. Failure due to missing revision
+gpii.tests.repositorySolutionsLoader.missingRevision = {
+ nockConfig: {
+ url: gpii.tests.repositorySolutionsLoader.hostname + gpii.tests.repositorySolutionsLoader.path,
+ type: "get",
+ status: 404,
+ response: {
+ isError: true,
+ statusCode: 404,
+ message: "Error retrieving solutions from repository: missing revision"
+ }
+ },
+ expected: {
+ isError: true,
+ statusCode: 404,
+ message: "Error retrieving solutions from repository: missing revision"
+ }
+};
+
+fluid.defaults("gpii.tests.repositorySolutionsLoader.testCaseHolder.missingRevision", {
+ gradeNames: "fluid.test.testCaseHolder",
+ modules: [{
+ name: "Revision requester module tests - missing revision",
+ expect: 1,
+ tests: [{
+ name: "Response: missing revision",
+ sequence: [{
+ task: "{repositorySolutionsLoader}.getSolutions",
+ args: [
+ null,
+ gpii.tests.repositorySolutionsLoader.platformId
+ ],
+ reject: "jqUnit.assertDeepEq",
+ rejectArgs: [
+ "Check missing revision",
+ gpii.tests.repositorySolutionsLoader.missingRevision.expected,
+ "{arguments}.0"
+ ]
+ }]
+ }]
+ }]
+});
+
+fluid.defaults("gpii.tests.repositorySolutionsLoaderTests.missingRevision", {
+ gradeNames: ["gpii.tests.repositorySolutionsLoaderTests", "gpii.test.testWithNock"],
+ testCaseHolderGrade: "gpii.tests.repositorySolutionsLoader.testCaseHolder.missingRevision",
+ invokers: {
+ setUpNock: {
+ funcName: "gpii.tests.repositorySolutionsLoader.setUpNock",
+ args: gpii.tests.repositorySolutionsLoader.missingRevision.nockConfig
+ }
+ }
+});
+
+// 3. Failure due to missing platform ID
+gpii.tests.repositorySolutionsLoader.missingPlatformId = {
+ nockConfig: {
+ url: gpii.tests.repositorySolutionsLoader.hostname + gpii.tests.repositorySolutionsLoader.path,
+ type: "get",
+ status: 404,
+ response: {
+ isError: true,
+ statusCode: 404,
+ message: "Error retrieving solutions from repository: missing platform ID"
+ }
+ },
+ expected: {
+ isError: true,
+ statusCode: 404,
+ message: "Error retrieving solutions from repository: missing platform ID"
+ }
+};
+
+fluid.defaults("gpii.tests.repositorySolutionsLoader.testCaseHolder.missingPlatformId", {
+ gradeNames: "fluid.test.testCaseHolder",
+ modules: [{
+ name: "Revision requester module tests - missing platform ID",
+ expect: 1,
+ tests: [{
+ name: "Response: missing platform ID",
+ sequence: [{
+ task: "{repositorySolutionsLoader}.getSolutions",
+ args: [
+ gpii.tests.repositorySolutionsLoader.revision,
+ null
+ ],
+ reject: "jqUnit.assertDeepEq",
+ rejectArgs: [
+ "Check missing platform ID",
+ gpii.tests.repositorySolutionsLoader.missingPlatformId.expected,
+ "{arguments}.0"
+ ]
+ }]
+ }]
+ }]
+});
+
+fluid.defaults("gpii.tests.repositorySolutionsLoaderTests.missingPlatformId", {
+ gradeNames: ["gpii.tests.repositorySolutionsLoaderTests", "gpii.test.testWithNock"],
+ testCaseHolderGrade: "gpii.tests.repositorySolutionsLoader.testCaseHolder.missingPlatformId",
+ invokers: {
+ setUpNock: {
+ funcName: "gpii.tests.repositorySolutionsLoader.setUpNock",
+ args: gpii.tests.repositorySolutionsLoader.missingPlatformId.nockConfig
+ }
+ }
+});
+
+// 4. Failure due to missing revision and platform ID
+gpii.tests.repositorySolutionsLoader.missingRevisionAndPlatformId = {
+ nockConfig: {
+ url: gpii.tests.repositorySolutionsLoader.hostname + gpii.tests.repositorySolutionsLoader.path,
+ type: "get",
+ status: 404,
+ response: {
+ isError: true,
+ statusCode: 404,
+ message: "Error retrieving solutions from repository: missing revision and platform ID"
+ }
+ },
+ expected: {
+ isError: true,
+ statusCode: 404,
+ message: "Error retrieving solutions from repository: missing revision and platform ID"
+ }
+};
+
+fluid.defaults("gpii.tests.repositorySolutionsLoader.testCaseHolder.missingRevisionAndPlatformId", {
+ gradeNames: "fluid.test.testCaseHolder",
+ modules: [{
+ name: "Revision requester module tests - missing revision and platform ID",
+ expect: 1,
+ tests: [{
+ name: "Response: missing revision and platform ID",
+ sequence: [{
+ task: "{repositorySolutionsLoader}.getSolutions",
+ args: [null, null],
+ reject: "jqUnit.assertDeepEq",
+ rejectArgs: [
+ "Check missing both revision and platform ID",
+ gpii.tests.repositorySolutionsLoader.missingRevisionAndPlatformId.expected,
+ "{arguments}.0"
+ ]
+ }]
+ }]
+ }]
+});
+
+fluid.defaults("gpii.tests.repositorySolutionsLoaderTests.missingRevisionAndPlatformId", {
+ gradeNames: ["gpii.tests.repositorySolutionsLoaderTests", "gpii.test.testWithNock"],
+ testCaseHolderGrade: "gpii.tests.repositorySolutionsLoader.testCaseHolder.missingRevisionAndPlatformId",
+ invokers: {
+ setUpNock: {
+ funcName: "gpii.tests.repositorySolutionsLoader.setUpNock",
+ args: gpii.tests.repositorySolutionsLoader.missingRevisionAndPlatformId.nockConfig
+ }
+ }
+});
+
+// Run all tests
+fluid.test.runTests([
+ "gpii.tests.repositorySolutionsLoaderTests.success",
+ "gpii.tests.repositorySolutionsLoaderTests.missingRevision",
+ "gpii.tests.repositorySolutionsLoaderTests.missingPlatformId",
+ "gpii.tests.repositorySolutionsLoaderTests.missingRevisionAndPlatformId"
+]);
diff --git a/gpii/node_modules/flowManager/test/SolutionsRegistryDataSourceTests.js b/gpii/node_modules/flowManager/test/SolutionsRegistryDataSourceTests.js
new file mode 100644
index 000000000..d449b2838
--- /dev/null
+++ b/gpii/node_modules/flowManager/test/SolutionsRegistryDataSourceTests.js
@@ -0,0 +1,328 @@
+/*!
+GPII download and save solutions registry from source code repositoyr
+
+Copyright 2020 OCAD University
+
+Licensed under the New BSD license. You may not use this file except in
+compliance with this License.
+
+You may obtain a copy of the License at
+https://github.com/GPII/universal/blob/master/LICENSE.txt
+*/
+
+"use strict";
+
+var fluid = fluid || require("infusion"),
+ kettle = require("kettle"),
+ jqUnit = fluid.registerNamespace("jqUnit"),
+ gpii = fluid.registerNamespace("gpii");
+
+kettle.loadTestingSupport();
+
+fluid.require("%flowManager/src/SolutionsRegistryDataSource.js");
+fluid.registerNamespace("gpii.tests.solutionsRegistry");
+
+gpii.tests.solutionsRegistry = {
+ path: "%gpii-universal/testData/solutions",
+ platformIds: ["win32", "linux", "darwin", "android", "web"],
+ platformId: "darwin",
+ repositoryDarwinSolutions: require("./data/darwin.json5"),
+ requestOptions: { os: "darwin" },
+ requestOptionsMissingOs: {foo: "bar"},
+ win32PlatformId: "win32"
+};
+
+// Check that the local solutions for the given platformId matches the result.
+gpii.tests.solutionsRegistry.checkLocalRegistry = function (msg, platformId, result) {
+ var solutions = fluid.require(gpii.tests.solutionsRegistry.path + "/" + platformId + ".json5");
+ jqUnit.assertDeepEq(msg, solutions, result);
+};
+
+// equalityCheck is either jqUnit.assertDeepEq() or jqUnit.assertDeepNeq().
+gpii.tests.solutionsRegistry.checkNamedRegistry = function (msg, equalityCheck, expectedSolutions, result) {
+ jqUnit.assertValue(msg, result);
+ equalityCheck(msg, expectedSolutions, result);
+};
+
+// Check for failures.
+gpii.tests.solutionsRegistry.checkRejection = function (msg, result) {
+ jqUnit.assertTrue(msg, result.isError);
+};
+
+gpii.tests.solutionsRegistry.handleReadyEvent = function (that) {
+ that.isReady = true;
+};
+
+// ======== Testing the solutions registry data source used by the CBFM ========
+
+fluid.defaults("gpii.tests.solutionsRegistry.cloud.testCaseHolder", {
+ gradeNames: "fluid.test.testCaseHolder",
+ components: {
+ solutionsRegistryDataSource: {
+ type: "gpii.flowManager.solutionsRegistry.dataSource.cloudBased",
+ options: {
+ path: gpii.tests.solutionsRegistry.path,
+ members: {
+ isReady: false
+ },
+ listeners: {
+ "solutionsRegistryReady": {
+ listener: "gpii.tests.solutionsRegistry.handleReadyEvent",
+ args: ["{that}"]
+ }
+ }
+ }
+ }
+ },
+ modules: [{
+ name: "Solutions registry used by CBFM - file-based solutions",
+ expect: 2,
+ tests: [{
+ name: "Solutions registry used with CBFM",
+ sequence: [
+ { funcName: "fluid.log", args: ["Solutions Registry for CBFM, getting named registry -- START"] },
+ {
+ // Cannot listen for solutionsRegistryReady event directly
+ // since it is long gone by the time this executes. Check
+ // that the handleIsReady listener heard the event.
+ funcName: "jqUnit.assertTrue",
+ args: [
+ "Cloud based solutionsRegistryDataSource is ready",
+ "{solutionsRegistryDataSource}.isReady"
+ ]
+ }, {
+ task: "{solutionsRegistryDataSource}.get",
+ args: [gpii.tests.solutionsRegistry.requestOptions],
+ resolve: "gpii.tests.solutionsRegistry.checkLocalRegistry",
+ resolveArgs: [
+ "Expecting one solutions registry",
+ gpii.tests.solutionsRegistry.requestOptions.os,
+ "{arguments}.0"
+ ]
+ },
+ { funcName: "fluid.log", args: ["Solutions Registry for CBFM, getting named registry -- END"] },
+ { funcName: "fluid.log", args: ["Solutions Registry for CBFM, failure to provide platform -- START"] },
+ {
+ task: "{solutionsRegistryDataSource}.get",
+ args: [gpii.tests.solutionsRegistry.requestOptionsMissingOs],
+ reject: "gpii.tests.solutionsRegistry.checkRejection",
+ rejectArgs: [
+ "Check missing OS parameter",
+ "{arguments}.0"
+ ]
+ },
+ { funcName: "fluid.log", args: ["Solutions Registry for CBFM, failure to provide platform -- END"] }
+ ]
+ }]
+ }]
+});
+
+// Test environment for CBFM solutions registry data source
+fluid.defaults("gpii.tests.solutionsRegistry.dataSource.cloud.env", {
+ gradeNames: ["fluid.test.testEnvironment"],
+ components: {
+ tester: {
+ type: "gpii.tests.solutionsRegistry.cloud.testCaseHolder"
+ }
+ }
+});
+kettle.test.bootstrap("gpii.tests.solutionsRegistry.dataSource.cloud.env");
+
+// ======== Testing the solutions registry data source used by the LFM ========
+
+// Test component for the LFM solutions registry data source mocking a load
+// from the source code repository.
+fluid.defaults("gpii.tests.solutionsRegistry.dataSource.local.mockLoadFromRepository", {
+ gradeNames: ["gpii.flowManager.solutionsRegistry.dataSource.local"],
+ components: {
+ revisionRequester: {
+ type: "gpii.flowmanager.revisionRequester",
+ options: {
+ cloudURL: "http://gpii.net"
+ }
+ }
+ },
+ listeners: {
+ "loadSolutions.loadFromRepository": {
+ listener: "gpii.tests.solutionsRegistry.dataSource.local.mockRepoLoad",
+ args: [
+ "{that}",
+ gpii.tests.solutionsRegistry.platformId,
+ gpii.tests.solutionsRegistry.repositoryDarwinSolutions
+ ],
+ priority: "after:getRevision"
+ }
+ }
+});
+
+gpii.tests.solutionsRegistry.dataSource.local.mockRepoLoad = function (that, platformId, solutions) {
+ var promise = fluid.promise();
+ var solutionsRegistry = {};
+ solutionsRegistry[platformId] = solutions;
+ that.repositorySolutionsRegistry = fluid.freezeRecursive(solutionsRegistry);
+ promise.resolve(that.repositorySolutionsRegistry);
+ return promise;
+};
+
+fluid.defaults("gpii.tests.solutionsRegistry.local.mockRepository.testCaseHolder", {
+ gradeNames: "fluid.test.testCaseHolder",
+ components: {
+ solutionsRegistryMockRepoLoad: {
+ type: "gpii.tests.solutionsRegistry.dataSource.local.mockLoadFromRepository",
+ options: {
+ path: gpii.tests.solutionsRegistry.path
+ }
+ }
+ },
+ modules: [{
+ name: "Solutions registry used by LFM - repository + file system based solutions",
+ expect: 3,
+ tests: [{
+ name: "Solutions registry LFM - solutions loaded from repository and looking for named registry",
+ sequence: [
+ { funcName: "fluid.log", args: ["Solutions Registry Mock Repo Load, getting named repository registry -- START"] },
+ {
+ event: "{solutionsRegistryMockRepoLoad}.events.solutionsRegistryReady",
+ listener: "jqUnit.assert",
+ args: ["LFM solutionsRegistryMockRepoLoad ready"]
+ }, {
+ task: "{solutionsRegistryMockRepoLoad}.get",
+ args: [gpii.tests.solutionsRegistry.requestOptions],
+ resolve: "gpii.tests.solutionsRegistry.checkNamedRegistry",
+ resolveArgs: [
+ "Expecting named solutions registry from repository",
+ jqUnit.assertDeepEq,
+ gpii.tests.solutionsRegistry.repositoryDarwinSolutions,
+ "{arguments}.0"
+ ]
+ },
+ { funcName: "fluid.log", args: ["Solutions Registry Mock Repo Load, getting named repository registry -- END"] },
+ { funcName: "fluid.log", args: ["Solutions Registry Mock Repo Load, getting named local registry -- START"] },
+ {
+ task: "{solutionsRegistryMockRepoLoad}.get",
+ args: [{os: gpii.tests.solutionsRegistry.win32PlatformId}],
+ resolve: "gpii.tests.solutionsRegistry.checkLocalRegistry",
+ resolveArgs: [
+ "Expecting named solutions registries, but from the local file system",
+ gpii.tests.solutionsRegistry.win32PlatformId,
+ "{arguments}.0"
+ ]
+ },
+ { funcName: "fluid.log", args: ["Solutions Registry Mock Repo Load, getting named local registry -- END"] },
+ { funcName: "fluid.log", args: ["Solutions Registry Mock Repo Load, failure to provide platform -- START"] },
+ {
+ task: "{solutionsRegistryMockRepoLoad}.get",
+ args: [gpii.tests.solutionsRegistry.requestOptionsMissingOs],
+ reject: "gpii.tests.solutionsRegistry.checkRejection",
+ rejectArgs: [
+ "Check missing OS parameter",
+ "{arguments}.0"
+ ]
+ },
+ { funcName: "fluid.log", args: ["Solutions Registry Mock Repo Load, failure to provide platform -- END"] }
+ ]
+ }]
+ }]
+});
+
+// Test environment for LFM solutions registry data source where solutions are
+// fetched from the repository
+fluid.defaults("gpii.tests.solutionsRegistry.dataSource.local.mockRepository.env", {
+ gradeNames: ["fluid.test.testEnvironment"],
+ components: {
+ tester: {
+ type: "gpii.tests.solutionsRegistry.local.mockRepository.testCaseHolder"
+ }
+ }
+});
+kettle.test.bootstrap("gpii.tests.solutionsRegistry.dataSource.local.mockRepository.env");
+
+// Test component for the LFM solutions registry data source where it fails to
+// load from the source code repository.
+fluid.defaults("gpii.tests.solutionsRegistry.dataSource.local.failLoadFromRepository", {
+ gradeNames: ["gpii.flowManager.solutionsRegistry.dataSource.local"],
+ components: {
+ revisionRequester: {
+ type: "gpii.flowmanager.revisionRequester",
+ options: {
+ cloudURL: "http://gpii.net"
+ }
+ }
+ },
+ listeners: {
+ "loadSolutions.loadFromRepository": {
+ listener: "gpii.tests.solutionsRegistry.dataSource.local.nullRepoLoad",
+ args: ["{that}"],
+ priority: "after:getRevision"
+ }
+ }
+});
+
+gpii.tests.solutionsRegistry.dataSource.local.nullRepoLoad = function (that) {
+ var promise = fluid.promise();
+ that.repositorySolutionsRegistry = null;
+ promise.resolve(that.repositorySolutionsRegistry);
+ return promise;
+};
+
+fluid.defaults("gpii.tests.solutionsRegistry.local.noRepository.testCaseHolder", {
+ gradeNames: "fluid.test.testCaseHolder",
+ components: {
+ solutionsRegistryNullRepoLoad: {
+ type: "gpii.tests.solutionsRegistry.dataSource.local.failLoadFromRepository",
+ options: {
+ path: gpii.tests.solutionsRegistry.path
+ }
+ }
+ },
+ modules: [{
+ name: "Solutions registry used by LFM - only file system based solutions available",
+ expect: 2,
+ tests: [{
+ name: "Solutions registry used with LFM - no solutions loaded from repository",
+ sequence: [
+ { funcName: "fluid.log", args: ["Solutions Registry No Repo Load, getting named 'repository' registry -- START"] },
+ {
+ event: "{solutionsRegistryNullRepoLoad}.events.solutionsRegistryReady",
+ listener: "jqUnit.assert",
+ args: ["LFM solutionsRegistryNullRepoLoad ready"]
+ }, {
+ task: "{solutionsRegistryNullRepoLoad}.get",
+ args: [gpii.tests.solutionsRegistry.requestOptions],
+ resolve: "gpii.tests.solutionsRegistry.checkNamedRegistry",
+ resolveArgs: [
+ "Expecting local solutions registry",
+ jqUnit.assertDeepNeq,
+ gpii.tests.solutionsRegistry.repositoryDarwinSolutions,
+ "{arguments}.0"
+ ]
+ },
+ { funcName: "fluid.log", args: ["Solutions Registry No Repo Load, getting named 'repository' registry -- END"] },
+ { funcName: "fluid.log", args: ["Solutions Registry No Repo Load, getting named local registry -- START"] },
+ {
+ task: "{solutionsRegistryNullRepoLoad}.get",
+ args: [{os: gpii.tests.solutionsRegistry.win32PlatformId}],
+ resolve: "gpii.tests.solutionsRegistry.checkLocalRegistry",
+ resolveArgs: [
+ "Expecting local named solutions registry",
+ gpii.tests.solutionsRegistry.win32PlatformId,
+ "{arguments}.0"
+ ]
+ },
+ { funcName: "fluid.log", args: ["Solutions Registry No Repo Load, named local registry -- END"] }
+ ]
+ }]
+ }]
+});
+
+// Test environment for LFM solutions registry data source where solutions are
+// NOT fetched from the repository
+fluid.defaults("gpii.tests.solutionsRegistry.dataSource.local.noRepository.env", {
+ gradeNames: ["fluid.test.testEnvironment"],
+ components: {
+ tester: {
+ type: "gpii.tests.solutionsRegistry.local.noRepository.testCaseHolder"
+ }
+ }
+});
+kettle.test.bootstrap("gpii.tests.solutionsRegistry.dataSource.local.noRepository.env");
diff --git a/gpii/node_modules/flowManager/test/configs/gpii.flowManager.tests.capture.fakeData.config.json5 b/gpii/node_modules/flowManager/test/configs/gpii.flowManager.tests.capture.fakeData.config.json5
index d3a12630f..95f502105 100644
--- a/gpii/node_modules/flowManager/test/configs/gpii.flowManager.tests.capture.fakeData.config.json5
+++ b/gpii/node_modules/flowManager/test/configs/gpii.flowManager.tests.capture.fakeData.config.json5
@@ -1,6 +1,7 @@
//
// This configuration is used for testing the snappingshotting Capture API. Capturing setting will only
-// ever happen on a users local machine, so there is only an untrusted test configuration.
+// ever happen on a users local machine, so there is only an untrusted test configuration. Also, it
+// will use the LFM's version of the solutions registry data source (gpii.flowManager.solutionsRegistry.dataSource.local)
//
{
"type": "gpii.flowManager.tests.capture.fakeData.config",
@@ -11,6 +12,15 @@
"target": "{that flowManager solutionsRegistryDataSource}.options.path",
"priority": "after:flowManager.solutions"
},
+ "capture.noteSolutionsRegistryReady": {
+ "record":{
+ "solutionsRegistryReady": {
+ "listener": "gpii.tests.flowManager.capture.handleSolutionsReadyEvent",
+ "args": ["{that}"]
+ }
+ },
+ "target": "{that flowManager solutionsRegistryDataSource}.options.listeners"
+ },
"capture.deviceReporter": {
"record": "%gpii-universal/gpii/node_modules/flowManager/test/data/capture_deviceReporter.json",
"target": "{that deviceReporter installedSolutionsDataSource}.options.path",
diff --git a/gpii/node_modules/flowManager/test/data/darwin.json5 b/gpii/node_modules/flowManager/test/data/darwin.json5
new file mode 100644
index 000000000..7bd475de8
--- /dev/null
+++ b/gpii/node_modules/flowManager/test/data/darwin.json5
@@ -0,0 +1,119 @@
+{
+ "fakemag1": {
+ "name": "Fake Magnifier 1",
+ "contexts": {
+ "OS": [
+ {
+ "id": "darwin"
+ }
+ ]
+ },
+ "capabilities": [
+ "http://registry\\.gpii\\.net/common/magnification/enabled"
+ ],
+ "settingsHandlers": {
+ "configuration": {
+ "type": "gpii.settingsHandlers.JSONSettingsHandler",
+ "liveness": "live",
+ "options": {
+ "filename": "/tmp/fakemag1.settings.json"
+ },
+ "capabilitiesTransformations": {
+ "magnification": "http://registry\\.gpii\\.net/common/magnification"
+ }
+ }
+ },
+ "configure": [
+ "settings.configuration"
+ ],
+ "restore": [
+ "settings.configuration"
+ ],
+ "start": [],
+ "stop": [],
+ "isInstalled": [
+ {
+ "type": "gpii.deviceReporter.alwaysInstalled"
+ }
+ ]
+ },
+ "fakemag2": {
+ "name": "Fake Magnifier 2 - fully featured",
+ "contexts": {
+ "OS": [
+ {
+ "id": "darwin"
+ }
+ ]
+ },
+ "capabilities": [
+ "http://registry\\.gpii\\.net/common/magnification/enabled"
+ ],
+ "settingsHandlers": {
+ "configuration": {
+ "type": "gpii.settingsHandlers.JSONSettingsHandler",
+ "liveness": "live",
+ "options": {
+ "filename": "/tmp/fakemag2.settings.json"
+ },
+ "capabilitiesTransformations": {
+ "magnification": "http://registry\\.gpii\\.net/common/magnification",
+ "invert": "http://registry\\.gpii\\.net/common/invertColours"
+ }
+ }
+ },
+ "configure": [
+ "settings.configuration"
+ ],
+ "restore": [
+ "settings.configuration"
+ ],
+ "start": [],
+ "stop": [],
+ "isInstalled": [
+ {
+ "type": "gpii.deviceReporter.alwaysInstalled"
+ }
+ ]
+ },
+ "fakescreenreader1": {
+ "name": "fake screenreader",
+ "contexts": {
+ "OS": [
+ {
+ "id": "darwin"
+ }
+ ]
+ },
+ "capabilities": [
+ "http://registry\\.gpii\\.net/common/screenReaderTTS/enabled"
+ ],
+ "settingsHandlers": {
+ "configuration": {
+ "type": "gpii.settingsHandlers.JSONSettingsHandler",
+ "liveness": "live",
+ "options": {
+ "filename": "/tmp/fakescreenreader1.json"
+ },
+ "capabilitiesTransformations": {
+ "pitch": "http://registry\\.gpii\\.net/common/pitch",
+ "volumeTTS": "http://registry\\.gpii\\.net/common/volumeTTS",
+ "rate": "http://registry\\.gpii\\.net/common/speechRate"
+ }
+ }
+ },
+ "configure": [
+ "settings.configuration"
+ ],
+ "restore": [
+ "settings.configuration"
+ ],
+ "start": [],
+ "stop": [],
+ "isInstalled": [
+ {
+ "type": "gpii.deviceReporter.alwaysInstalled"
+ }
+ ]
+ }
+}
diff --git a/gpii/node_modules/matchMakerFramework/src/MatchMakerUtilities.js b/gpii/node_modules/matchMakerFramework/src/MatchMakerUtilities.js
index 2f56767ef..4623a628f 100644
--- a/gpii/node_modules/matchMakerFramework/src/MatchMakerUtilities.js
+++ b/gpii/node_modules/matchMakerFramework/src/MatchMakerUtilities.js
@@ -50,10 +50,6 @@ var fluid = fluid || require("infusion"),
specialPreferences: gpii.matchMakerFramework.utils.findSpecialPreferences(initialPayload.preferences)
}, fluid.copy(initialPayload));
gpii.matchMakerFramework.utils.addCapabilitiesInformation(matchMakerInput);
- // remove full solutions registry from the payload, now that we've used it
- // to avoid sending a too large payload to the matchmaker (see GPII-1880)
- delete matchMakerInput.fullSolutionsRegistry;
-
return matchMakerInput;
};
diff --git a/gpii/node_modules/testing/src/RunTestDefs.js b/gpii/node_modules/testing/src/RunTestDefs.js
index d17e5f693..eae59890b 100644
--- a/gpii/node_modules/testing/src/RunTestDefs.js
+++ b/gpii/node_modules/testing/src/RunTestDefs.js
@@ -88,7 +88,8 @@ gpii.test.testDefToEnvironment = function (testDef, environmentType, sequenceGra
};
gpii.test.testDefToServerEnvironment = function (testDef) {
- return gpii.test.testDefToEnvironment(testDef, "gpii.test.serverEnvironment", "gpii.test.standardServerSequenceGrade");
+ var serverEnvironmentGrade = testDef.testEnvironmentGrade ? testDef.testEnvironmentGrade : "gpii.test.serverEnvironment";
+ return gpii.test.testDefToEnvironment(testDef, serverEnvironmentGrade, "gpii.test.standardServerSequenceGrade");
};
gpii.test.testDefToCouchEnvironment = function (testDef) {
diff --git a/scripts/vagrantCloudBasedContainers.sh b/scripts/vagrantCloudBasedContainers.sh
index a13604b61..a47e4669f 100755
--- a/scripts/vagrantCloudBasedContainers.sh
+++ b/scripts/vagrantCloudBasedContainers.sh
@@ -27,6 +27,22 @@ fi
UNIVERSAL_IMAGE=vagrant-universal
+# The following SHA256 is guaranteed to be a revison on github, and recent. It
+# is not necessarily the latest revision that is used in production -- it
+# could be a later revision -- but it is sufficient for the tests. For local
+# development, @{upstream} is used in case the local changes have not been
+# pushed. CI has no upstream, and uses HEAD instead. The result is written to
+# the file "gpii-revision.json" at the root universal folder. See the
+# Dockerfile.
+GITFULLREV="$(git rev-parse @{upstream})"
+if [ $? != 0 ]
+then
+ echo "No upstream, using HEAD for revision.json"
+ GITFULLREV="$(git rev-parse HEAD)"
+else
+ echo "Using upstream for revision.json"
+fi
+
COUCHDB_IMAGE=couchdb:2.3.1
COUCHDB_PORT=5984
COUCHDB_HEALTHCHECK_DELAY=2
@@ -73,7 +89,7 @@ if [ "$NO_REBUILD" != "true" ] ; then
docker rmi -f $UNIVERSAL_IMAGE 2>/dev/null || true
# Build image
- docker build -t $UNIVERSAL_IMAGE .
+ docker build --build-arg gitFullRev="$GITFULLREV" -t $UNIVERSAL_IMAGE .
fi
# Start the CouchDB container
diff --git a/tests/JournalIntegrationTests.js b/tests/JournalIntegrationTests.js
index 2f3966f90..b2a89df03 100644
--- a/tests/JournalIntegrationTests.js
+++ b/tests/JournalIntegrationTests.js
@@ -17,6 +17,7 @@ var gpii = fluid.registerNamespace("gpii");
var jqUnit = require("node-jqunit");
var kettle = require("kettle");
+
kettle.loadTestingSupport();
fluid.require("%gpii-universal");
@@ -297,50 +298,48 @@ fluid.defaults("gpii.tests.journal.testCaseHolder", {
// Used on the first run where the settings handler crashes the whole system
gpii.tests.journal.solutionsRegistryOverlay = {
- win32: {
- "com.microsoft.windows.cursors": {
- settingsHandlers: {
- explode: {
- type: "gpii.tests.journal.explodingSettingsHandler",
- supportedSettings: {
- AppStarting: {}
- },
- capabilitiesTransformations: {
- AppStarting: {
- transform: {
- type: "fluid.transforms.value",
- inputPath: "http://registry\\.gpii\\.net/common/cursorSize",
- outputPath: "value"
- }
- }
- }
+ "com.microsoft.windows.cursors": {
+ settingsHandlers: {
+ explode: {
+ type: "gpii.tests.journal.explodingSettingsHandler",
+ supportedSettings: {
+ AppStarting: {}
},
- maybeThrow: {
- type: "gpii.tests.journal.throwingSettingsHandler",
- supportedSettings: {
- AppStarting: {}
- },
- capabilitiesTransformations: {
- AppStarting: {
- transform: {
- type: "fluid.transforms.value",
- inputPath: "http://registry\\.gpii\\.net/common/cursorSize",
- outputPath: "value"
- }
+ capabilitiesTransformations: {
+ AppStarting: {
+ transform: {
+ type: "fluid.transforms.value",
+ inputPath: "http://registry\\.gpii\\.net/common/cursorSize",
+ outputPath: "value"
}
}
+ }
+ },
+ maybeThrow: {
+ type: "gpii.tests.journal.throwingSettingsHandler",
+ supportedSettings: {
+ AppStarting: {}
},
- configure: {
- supportedSettings: {
- AppStarting: {}
+ capabilitiesTransformations: {
+ AppStarting: {
+ transform: {
+ type: "fluid.transforms.value",
+ inputPath: "http://registry\\.gpii\\.net/common/cursorSize",
+ outputPath: "value"
+ }
}
}
},
- restore: ["settings.maybeThrow", "settings.configure"],
- // It's necessary to execute settings.maybeThrow otherwise its entry does not appear in the snapshotted solutions
- // registry block entered into the journal, and hence it cannot be found on the next turn
- configure: ["settings.configure", "settings.maybeThrow", "settings.explode"]
- }
+ configure: {
+ supportedSettings: {
+ AppStarting: {}
+ }
+ }
+ },
+ restore: ["settings.maybeThrow", "settings.configure"],
+ // It's necessary to execute settings.maybeThrow otherwise its entry does not appear in the snapshotted solutions
+ // registry block entered into the journal, and hence it cannot be found on the next turn
+ configure: ["settings.configure", "settings.maybeThrow", "settings.explode"]
}
};
diff --git a/tests/all-tests.js b/tests/all-tests.js
index f2bf8c474..99d5dbdad 100644
--- a/tests/all-tests.js
+++ b/tests/all-tests.js
@@ -59,10 +59,12 @@ var testIncludes = [
"../gpii/node_modules/flatMatchMaker/test/FlatMatchMakerTests.js",
"../gpii/node_modules/flowManager/test/BrowserChannelTests.js",
"../gpii/node_modules/flowManager/test/CaptureTests.js",
+ "../gpii/node_modules/flowManager/test/SolutionsRegistryDataSourceTests.js",
"../gpii/node_modules/flowManager/test/DefaultSettingsLoaderTests.js",
"../gpii/node_modules/flowManager/test/PrefsServerDataSourceTests.js",
"../gpii/node_modules/flowManager/test/PSPChannelTests.js",
"../gpii/node_modules/flowManager/test/SettingsDataSourceTests.js",
+ "../gpii/node_modules/flowManager/test/RepositorySolutionsLoaderTests.js",
"../gpii/node_modules/gpii-db-operation/test/DbDataStoreTests.js",
"../gpii/node_modules/gpii-ini-file/test/iniFileTests.js",
"../gpii/node_modules/gpii-oauth2/gpii-oauth2-authz-server/test/authGrantFinderTests.js",
diff --git a/tests/configs/gpii.tests.untrusted.production.config.json5 b/tests/configs/gpii.tests.untrusted.production.config.json5
index 14e009b58..8229dc055 100644
--- a/tests/configs/gpii.tests.untrusted.production.config.json5
+++ b/tests/configs/gpii.tests.untrusted.production.config.json5
@@ -3,5 +3,8 @@
"options": {
"mainServerPort": "@expand:kettle.resolvers.env(GPII_FLOWMANAGER_LISTEN_PORT)"
},
- "mergeConfigs": "%gpii-universal/gpii/configs/shared/gpii.config.untrusted.development.json5"
+ "mergeConfigs": [
+ "%gpii-universal/gpii/node_modules/flowManager/configs/gpii.config.local.flowManager.loadSolutionsFromRepository",
+ "%gpii-universal/gpii/configs/shared/gpii.config.untrusted.development.json5"
+ ]
}
diff --git a/tests/production/CloudStatusProductionTests.js b/tests/production/CloudStatusProductionTests.js
index 3ab0ca0a1..7f970dd66 100644
--- a/tests/production/CloudStatusProductionTests.js
+++ b/tests/production/CloudStatusProductionTests.js
@@ -1,5 +1,5 @@
/**
-GPII Production Config tests
+GPII Production Config tests - Cloud Status
Requirements:
* an internet connection
@@ -10,7 +10,7 @@ Requirements:
---
Copyright 2015 Raising the Floor - International
-Copyright 2018, 2019 OCAD University
+Copyright 2018-2020 OCAD University
Licensed under the New BSD license. You may not use this file except in
compliance with this License.
@@ -36,38 +36,18 @@ gpii.loadTestingSupport();
fluid.registerNamespace("gpii.tests.productionConfigTesting");
-require("../shared/DevelopmentTestDefs.js");
require("./ProductionTestsUtils.js");
-// Flowmanager tests for:
-// /user/%gpiiKey/login and /user/%gpiiKey/logout (as defined in gpii.tests.development.testDefs),
+gpii.tests.productionConfigTesting.validGpiiRevision = require(
+ fluid.module.resolvePath(
+ "%gpii-universal/gpii-revision.json"
+ )
+);
+
+// Flowmanager tests for these http endpoints:
// /health,
// /ready,
-gpii.tests.productionConfigTesting.testDefs = fluid.transform(gpii.tests.development.testDefs, function (testDefIn) {
- var testDef = fluid.extend(true, {}, testDefIn, {
- name: "Flow Manager production tests -- status, login, and logout",
- config: gpii.tests.productionConfigTesting.config,
- expect: 6,
- components: {
- healthRequest: {
- type: "gpii.tests.productionConfigTesting.cloudStatusRequest",
- options: {
- path: "/health",
- expectedPayload: {"isHealthy": true}
- }
- },
- readyRequest: {
- type: "gpii.tests.productionConfigTesting.cloudStatusRequest",
- options: {
- path: "/ready",
- expectedPayload: {"isReady": true}
- }
- }
- },
- sequenceGrade: "gpii.tests.productionConfigTesting.cloudStatusSequence"
- });
- return testDef;
-});
+// /revision
fluid.defaults("gpii.tests.productionConfigTesting.cloudStatus", {
gradeNames: ["fluid.test.sequenceElement"],
@@ -83,6 +63,11 @@ fluid.defaults("gpii.tests.productionConfigTesting.cloudStatus", {
}, {
event: "{readyRequest}.events.onComplete",
listener: "gpii.tests.productionConfigTesting.testResponse"
+ }, {
+ func: "{revisionRequest}.sendToCBFM"
+ }, {
+ event: "{revisionRequest}.events.onComplete",
+ listener: "gpii.tests.productionConfigTesting.testResponse"
},
{ funcName: "fluid.log", args: ["Cloud status tests end"]}
]
@@ -94,12 +79,38 @@ fluid.defaults("gpii.tests.productionConfigTesting.cloudStatusSequence", {
cloudStatus: {
gradeNames: "gpii.tests.productionConfigTesting.cloudStatus",
priority: "after:startServer"
- },
- loginLogout: {
- gradeNames: "gpii.tests.development.loginLogout",
- priority: "after:cloudStatus"
}
}
});
+gpii.tests.productionConfigTesting.testDefs = [{
+ name: "Flow Manager production tests -- Cloud status",
+ config: gpii.tests.productionConfigTesting.config,
+ expect: 6,
+ components: {
+ healthRequest: {
+ type: "gpii.tests.productionConfigTesting.cloudStatusRequest",
+ options: {
+ path: "/health",
+ expectedPayload: {"isHealthy": true}
+ }
+ },
+ readyRequest: {
+ type: "gpii.tests.productionConfigTesting.cloudStatusRequest",
+ options: {
+ path: "/ready",
+ expectedPayload: {"isReady": true}
+ }
+ },
+ revisionRequest: {
+ type: "gpii.tests.productionConfigTesting.cloudStatusRequest",
+ options: {
+ path: "/revision",
+ expectedPayload: gpii.tests.productionConfigTesting.validGpiiRevision
+ }
+ }
+ },
+ sequenceGrade: "gpii.tests.productionConfigTesting.cloudStatusSequence"
+}];
+
gpii.test.runServerTestDefs(gpii.tests.productionConfigTesting.testDefs);
diff --git a/tests/production/LoginLogoutProductionTests.js b/tests/production/LoginLogoutProductionTests.js
new file mode 100644
index 000000000..568084cda
--- /dev/null
+++ b/tests/production/LoginLogoutProductionTests.js
@@ -0,0 +1,73 @@
+/**
+GPII Production Config tests - Login/Logout process
+
+Requirements:
+* an internet connection
+* a cloud based flow manager
+* a preferences server
+* a CouchDB server
+
+---
+
+Copyright 2015 Raising the Floor - International
+Copyright 2018-2020 OCAD University
+
+Licensed under the New BSD license. You may not use this file except in
+compliance with this License.
+
+The research leading to these results has received funding from the European Union's
+Seventh Framework Programme (FP7/2007-2013) under grant agreement no. 289016.
+
+You may obtain a copy of the License at
+https://github.com/GPII/universal/blob/master/LICENSE.txt
+
+WARNING: Do not run these tests directly. They are called from within the
+"vagrantCloudBasedContainers.sh" after it has initialized the environment.
+*/
+
+"use strict";
+
+var fluid = require("infusion"),
+ gpii = fluid.registerNamespace("gpii");
+
+fluid.require("%gpii-universal");
+
+gpii.loadTestingSupport();
+
+fluid.registerNamespace("gpii.tests.productionConfigTesting");
+
+require("../shared/DevelopmentTestDefs.js");
+require("./ProductionTestsUtils.js");
+
+/** Flowmanager tests for the key in and key out processes:
+ * /user/%gpiiKey/login and /user/%gpiiKey/logout (as defined in gpii.tests.development.testDefs),
+ *
+ * Note: When Local Flow Manager starts, it now fetches Solution Registry and default settings file from remote URLs.
+ * This test needs to ensure the keyin/keyout test sequence starts when LFM is ready and noUser has been keyed into
+ * the system. To accomplish it, a special testEnvironment component is created to define an aggregate event
+ * "onSystemReady"that will be fired when:
+ * 1. The test server has been constructed;
+ * 2. The local flow manager initial actions have completed and is ready to process login/logout requests.
+ * The keyin/keyout test sequence will wait until "onSystemReady" is fired.
+ */
+
+fluid.defaults("gpii.tests.productionConfigTesting.loginLogoutSequence", {
+ gradeNames: ["gpii.test.standardServerSequenceGrade"],
+ sequenceElements: {
+ loginLogout: {
+ gradeNames: "gpii.tests.development.loginLogout",
+ priority: "after:startServer"
+ }
+ }
+});
+
+gpii.tests.productionConfigTesting.keyInKeyOutTestDefs = fluid.transform(gpii.tests.development.testDefs, function (testDefIn) {
+ var testDef = fluid.extend(true, {}, testDefIn, {
+ config: gpii.tests.productionConfigTesting.config,
+ testEnvironmentGrade: "gpii.tests.productionConfigTesting.testEnvironment",
+ sequenceGrade: "gpii.tests.productionConfigTesting.loginLogoutSequence"
+ });
+ return testDef;
+});
+
+gpii.test.runServerTestDefs(gpii.tests.productionConfigTesting.keyInKeyOutTestDefs);
diff --git a/tests/production/ProductionTestsUtils.js b/tests/production/ProductionTestsUtils.js
index 3d4d73bef..0411dd8d2 100644
--- a/tests/production/ProductionTestsUtils.js
+++ b/tests/production/ProductionTestsUtils.js
@@ -88,6 +88,40 @@ fluid.defaults("gpii.tests.cloud.oauth2.accessTokensDeleteRequests", {
}
});
+// The customized testEnvironment component that adds and listens to the aggregate event "onSystemReady"
+fluid.defaults("gpii.tests.productionConfigTesting.testEnvironment", {
+ gradeNames: ["gpii.test.serverEnvironment"],
+ distributeOptions: {
+ "resetAtStartSuccess.escalate": {
+ record: {
+ resetAtStartSuccess: "{testEnvironment}.events.resetAtStartSuccess"
+ },
+ target: "{that gpii.flowManager.local}.options.events"
+ },
+ "productionConfigTesting.startServerSequence": {
+ record: [
+ { // This sequence point is required because of a QUnit bug - it defers the start of sequence by 13ms "to avoid any current callbacks" in its words
+ func: "{testEnvironment}.events.constructServer.fire"
+ },
+ {
+ event: "{testEnvironment}.events.onSystemReady",
+ listener: "fluid.identity"
+ }
+ ],
+ target: "{that gpii.test.startServerSequence}.options.sequence"
+ }
+ },
+ events: {
+ resetAtStartSuccess: null,
+ onSystemReady: {
+ events: {
+ resetAtStartSuccess: "resetAtStartSuccess",
+ onServerReady: "onServerReady"
+ }
+ }
+ }
+});
+
// Sequence elements for cleaning up extra access tokens
fluid.defaults("gpii.tests.productionConfigTesting.deleteAccessTokensSequence", {
gradeNames: ["fluid.test.sequenceElement"],
diff --git a/tests/production/SolutionsRegistryLoadSequenceTests.js b/tests/production/SolutionsRegistryLoadSequenceTests.js
new file mode 100644
index 000000000..11cde4167
--- /dev/null
+++ b/tests/production/SolutionsRegistryLoadSequenceTests.js
@@ -0,0 +1,144 @@
+/**
+GPII Production Config tests - Loading Solutions Registries
+
+Requirements:
+* an internet connection
+* a cloud based flow manager
+---
+
+Copyright 2020 OCAD University
+
+Licensed under the New BSD license. You may not use this file except in
+compliance with this License.
+
+The research leading to these results has received funding from the European Union's
+Seventh Framework Programme (FP7/2007-2013) under grant agreement no. 289016.
+
+You may obtain a copy of the License at
+https://github.com/GPII/universal/blob/master/LICENSE.txt
+
+WARNING: Do not run these tests directly. They are called from within the
+"vagrantCloudBasedContainers.sh" after it has initialized the environment.
+*/
+
+"use strict";
+
+var fluid = require("infusion"),
+ jqUnit = fluid.require("node-jqunit", require, "jqUnit"),
+ gpii = fluid.registerNamespace("gpii");
+
+fluid.require("%gpii-universal");
+
+gpii.loadTestingSupport();
+
+fluid.registerNamespace("gpii.tests.productionConfigTesting");
+
+require("./ProductionTestsUtils.js");
+
+gpii.tests.productionConfigTesting.validGpiiRevision = require(
+ fluid.module.resolvePath(
+ "%gpii-universal/gpii-revision.json"
+ )
+);
+
+gpii.tests.productionConfigTesting.localSolutionsRegistries =
+ fluid.module.resolvePath("%gpii-universal/testData/solutions");
+
+/** Local FlowManager tests of the solutions registry loading sequence:
+ *
+ * When the LFM starts, its SolutionsRegisryDataSource loads solutions
+ * registries from both the local file system, and from the source code
+ * repository. The tests need to start running *after* this loading process is
+ * complete, and are sensitive to the "onSystemReady" event, in that regard. See
+ * the component "gpii.tests.productionConfigTesting.testEnvironment" in the
+ * "ProductionTestUtils.js" file.
+ *
+ * "onSystemReady" is fired when:
+ * 1. The test server has been constructed;
+ * 2. The local flow manager initial actions have completed and is ready for
+ * requests
+ */
+fluid.defaults("gpii.tests.productionConfigTesting.loadingSolutionsTransform", {
+ gradeNames: ["gpii.test.standardServerSequenceGrade"],
+ sequenceElements: {
+ testLoadedSequence: {
+ gradeNames: "gpii.tests.productionConfigTesting.testLoadedSequence",
+ priority: "after:startServer"
+ }
+ }
+});
+
+fluid.defaults("gpii.tests.productionConfigTesting.testLoadedSequence", {
+ gradeNames: ["fluid.test.sequenceElement"],
+ sequence: [{
+ funcName: "gpii.tests.productionConfigTesting.checkSolutionsRegistries"
+ }]
+});
+
+gpii.tests.productionConfigTesting.loadingSolutionsTransform.testDefs = [{
+ name: "Flow Manager production tests -- solutions loading sequence",
+ config: gpii.tests.productionConfigTesting.config,
+ testEnvironmentGrade: "gpii.tests.productionConfigTesting.testEnvironment",
+ distributeOptions: {
+ // Override the default getRevision() to record the value it resolves after
+ // requesting the revision.
+ "store.revision": {
+ "record": {
+ "loadSolutions.getRevision": {
+ "listener": "gpii.tests.productionConfigTesting.loadingSolutionsTransform.getRevisionTest",
+ "args": ["{that}"]
+ }
+ },
+ "target": "{that gpii.flowManager.local solutionsRegistryDataSource}.options.listeners"
+ },
+ "store.registries": {
+ "record":{
+ "solutionsRegistryReady": {
+ "listener": "gpii.tests.productionConfigTesting.loadingSolutionsTransform.storeRegistries",
+ "args": ["{that}"]
+ }
+ },
+ "target": "{that flowManager solutionsRegistryDataSource}.options.listeners"
+ }
+ },
+ sequenceGrade: "gpii.tests.productionConfigTesting.loadingSolutionsTransform"
+}];
+
+// Call the default getRevision() and store the result is the SRDS
+gpii.tests.productionConfigTesting.loadingSolutionsTransform.getRevisionTest = function (solutionRegistryDataSource) {
+ var revisionPromise = solutionRegistryDataSource.revisionRequester.getRevision();
+ revisionPromise.then(function (revision) {
+ gpii.tests.productionConfigTesting.loadingSolutionsTransform.revision = revision;
+ });
+ return revisionPromise;
+};
+
+// After the SRDS has finished loading the registries, keep a copy of them for
+// testing.
+gpii.tests.productionConfigTesting.loadingSolutionsTransform.storeRegistries = function (solutionRegistryDataSource) {
+ gpii.tests.productionConfigTesting.loadingSolutionsTransform.fullSolutionsRegistry =
+ solutionRegistryDataSource.fullSolutionsRegistry;
+
+ gpii.tests.productionConfigTesting.loadingSolutionsTransform.repositorySolutionsRegistry =
+ solutionRegistryDataSource.repositorySolutionsRegistry;
+};
+
+// Check that the solutions were loaded from local file system and repository,
+// and that the revision used matches.
+gpii.tests.productionConfigTesting.checkSolutionsRegistries = function () {
+ jqUnit.assertDeepEq(
+ "Check revision",
+ gpii.tests.productionConfigTesting.validGpiiRevision,
+ gpii.tests.productionConfigTesting.loadingSolutionsTransform.revision
+ );
+ jqUnit.assertNotNull(
+ "Check loading of solutions registries from local file system",
+ gpii.tests.productionConfigTesting.loadingSolutionsTransform.fullSolutionsRegistry
+ );
+ jqUnit.assertNotNull(
+ "Check loading from soruce code respository",
+ gpii.tests.productionConfigTesting.loadingSolutionsTransform.repositorySolutionsRegistry
+ );
+};
+
+gpii.test.runServerTestDefs(gpii.tests.productionConfigTesting.loadingSolutionsTransform.testDefs);
diff --git a/tests/production/all-tests.js b/tests/production/all-tests.js
index a9fd06dd7..972866a72 100644
--- a/tests/production/all-tests.js
+++ b/tests/production/all-tests.js
@@ -26,6 +26,8 @@ fluid.require("%gpii-universal", require);
var testIncludes = [
"./CloudStatusProductionTests.js",
+ "./SolutionsRegistryLoadSequenceTests.js",
+ "./LoginLogoutProductionTests.js",
"./SettingsGetProductionTests.js",
"./SettingsPutProductionTests.js"
];