Update eslint and switch to standard

The openstack eslint package is just really not needed at this
point. Use standardjs and run --fix on it.

Switch away from using node 8 to run linters and tests.

Change-Id: I51f68d4ec30c8da5d1601ef00045e53f90d0554b
This commit is contained in:
Monty Taylor 2020-05-25 11:14:23 -05:00
parent 7f843d7ecb
commit 346f7eeda0
39 changed files with 1679 additions and 1694 deletions

View File

@ -1,6 +1,16 @@
extends: openstack/es2015
parser: babel-eslint
plugins:
- standard
rules:
space-before-function-paren:
- 2
- never
no-var: error
no-console: off
semi: [error, never]
quotes: [error, single]
lines-between-class-members: error
extends:
- eslint:recommended
env:
browser: true
es6: true
jasmine: true
node: true

View File

@ -1,27 +1,26 @@
/*eslint no-process-env: "off" */
import fs from 'fs';
import karma from 'karma/lib/config';
import path from 'path';
/* eslint no-process-env: "off" */
import fs from 'fs'
import karma from 'karma/lib/config'
import path from 'path'
function getDevstackConfig() {
const karmaConfig = karma.parseConfig(path.resolve('./karma.conf.js'));
function getDevstackConfig () {
const karmaConfig = karma.parseConfig(path.resolve('./karma.conf.js'))
return getCorsConfig('$KEYSTONE_CONF', karmaConfig) +
getCorsConfig('$GLANCE_API_CONF', karmaConfig) +
getCorsConfig('$NEUTRON_CONF', karmaConfig) +
getCorsConfig('$NOVA_CONF', karmaConfig);
getCorsConfig('$NOVA_CONF', karmaConfig)
}
function getCorsConfig(service, karmaConfig) {
function getCorsConfig (service, karmaConfig) {
return `[[post-config|${service}]]
[cors]
allowed_origin=http://localhost:${karmaConfig.port}
`;
`
}
fs.appendFile(process.env.BASE + '/new/devstack/local.conf', getDevstackConfig(), (err) => {
if (err) {
throw err;
throw err
}
});
})

View File

@ -1,9 +1,9 @@
import webpackConfig from './webpack.config.babel';
import path from 'path';
import webpackConfig from './webpack.config.babel'
import path from 'path'
export default (config) => {
// test mode based on basePath parameter (eg. test/unit, test/functional)
const testDir = config.basePath ? path.basename(config.basePath) : 'unit';
const testDir = config.basePath ? path.basename(config.basePath) : 'unit'
config.set({
@ -69,9 +69,9 @@ export default (config) => {
// Generate a coverage report in /cover/karma
coverageReporter: {
type: 'html', //produces a html document after code is run
dir: '../../cover/' + testDir + '/browser/' //path to created html doc
},
type: 'html', // produces a html document after code is run
dir: '../../cover/' + testDir + '/browser/' // path to created html doc
}
});
};
})
}

View File

@ -1,2 +1,2 @@
require('@babel/register');
module.exports = require('./karma.conf.babel').default;
require('@babel/register')
module.exports = require('./karma.conf.babel').default

View File

@ -41,11 +41,12 @@
"@babel/preset-env": "^7.0.0",
"@babel/register": "^7.0.0",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-plugin-istanbul": "^6.0.0",
"babel-plugin-transform-inline-environment-variables": "^6.8.0",
"eslint": "^3.0.0",
"eslint-config-openstack": "4.0.1",
"eslint": "^7.1.0",
"eslint-plugin-standard": "^4.0.1",
"fetch-mock": "^5.0.5",
"jasmine": "^3.5.0",
"js-yaml": "^3.14.0",

View File

@ -13,7 +13,7 @@
* the License for the specific language governing permissions and limitations
* under the License.
*/
import AbstractService from './util/abstractService';
import AbstractService from './util/abstractService'
/**
* A list of all supported versions. Please keep this array sorted by most recent.
@ -23,10 +23,9 @@ import AbstractService from './util/abstractService';
*/
const supportedGlanceVersions = [
'v2.3'
];
]
export default class Glance extends AbstractService {
/**
* This class provides direct, idempotent, low-level access to the Glance API of a specific
* cloud. The constructor requires that you provide a specific glance interface endpoint
@ -42,17 +41,17 @@ export default class Glance extends AbstractService {
* }
* @param {{}} endpointConfig The configuration element for a specific glance endpoint.
*/
constructor(endpointConfig) {
constructor (endpointConfig) {
// Sanity checks.
if (!endpointConfig || !endpointConfig.url) {
throw new Error('An endpoint configuration is required.');
throw new Error('An endpoint configuration is required.')
}
// Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact).
endpointConfig = Object.assign({}, endpointConfig);
endpointConfig = Object.assign({}, endpointConfig)
super(endpointConfig.url, supportedGlanceVersions);
this._config = endpointConfig;
super(endpointConfig.url, supportedGlanceVersions)
this._config = endpointConfig
}
/**
@ -62,11 +61,11 @@ export default class Glance extends AbstractService {
* into one.
* @returns {Promise.<T>} A promise which will resolve with the list of images.
*/
imageList(token = null) {
imageList (token = null) {
return this
._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}images`, headers))
.then((response) => response.json())
.then((body) => body.images);
.then((body) => body.images)
}
}

View File

@ -1,4 +1,4 @@
export {default as Keystone} from './keystone';
export {default as Glance} from './glance';
export {default as Neutron} from './neutron';
export {default as OpenStack} from './openstack';
export { default as Keystone } from './keystone'
export { default as Glance } from './glance'
export { default as Neutron } from './neutron'
export { default as OpenStack } from './openstack'

View File

@ -1,4 +1,4 @@
import AbstractService from './util/abstractService';
import AbstractService from './util/abstractService'
/**
* A list of all supported versions. Please keep this array sorted by most recent.
@ -8,10 +8,9 @@ import AbstractService from './util/abstractService';
*/
const supportedKeystoneVersions = [
'v3.1'
];
]
export default class Keystone extends AbstractService {
/**
* This class provides direct, idempotent, low-level access to the Keystone API of a specific
* cloud. The constructor requires that you provide a configuration object for a specific
@ -22,17 +21,17 @@ export default class Keystone extends AbstractService {
* @param {{}} cloudConfig The configuration object for a specific cloud.
* @see http://docs.openstack.org/developer/os-client-config/#site-specific-file-locations
*/
constructor(cloudConfig) {
constructor (cloudConfig) {
// Sanity checks.
if (!cloudConfig) {
throw new Error('A configuration is required.');
throw new Error('A configuration is required.')
}
// Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact).
cloudConfig = Object.assign({}, cloudConfig);
cloudConfig = Object.assign({}, cloudConfig)
super(cloudConfig.auth.auth_url, supportedKeystoneVersions);
this.cloudConfig = cloudConfig;
super(cloudConfig.auth.auth_url, supportedKeystoneVersions)
this.cloudConfig = cloudConfig
}
/**
@ -44,18 +43,18 @@ export default class Keystone extends AbstractService {
* @returns {String} The value found in the config, or null.
* @ignore
*/
_safeConfigGet(path) {
let segments = path.split('.');
let pointer = this.cloudConfig;
_safeConfigGet (path) {
const segments = path.split('.')
let pointer = this.cloudConfig
while (segments.length > 0) {
let prop = segments.shift();
if (pointer.hasOwnProperty(prop)) {
pointer = pointer[prop];
const prop = segments.shift()
if (Object.prototype.hasOwnProperty.call(pointer, prop)) {
pointer = pointer[prop]
} else {
return null;
return null
}
}
return pointer;
return pointer
}
/**
@ -64,9 +63,9 @@ export default class Keystone extends AbstractService {
* @returns {Promise.<Object[]>} A promise that will resolve with the list of raw versions.
* @protected
*/
_rawVersions() {
_rawVersions () {
return super._rawVersions()
.then((versions) => versions.values);
.then((versions) => versions.values)
}
/**
@ -92,7 +91,7 @@ export default class Keystone extends AbstractService {
* Not required if a project ID is given.
* @returns {Promise.<T>} A promise which will resolve with a valid token.
*/
tokenIssue({
tokenIssue ({
user_id: userId = this._safeConfigGet('auth.user_id'),
username = this._safeConfigGet('auth.username'),
password = this._safeConfigGet('auth.password'),
@ -103,32 +102,32 @@ export default class Keystone extends AbstractService {
project_domain_id: projectDomainId = this._safeConfigGet('auth.project_domain_id'),
project_domain_name: projectDomainName = this._safeConfigGet('auth.project_domain_name')
} = {}) {
let project;
let user = {password};
let project
const user = { password }
if (userId) {
user.id = userId;
user.id = userId
} else if (username) {
user.name = username;
user.name = username
if (userDomainId) {
user.domain = {id: userDomainId};
user.domain = { id: userDomainId }
} else if (userDomainName) {
user.domain = {name: userDomainName};
user.domain = { name: userDomainName }
} else {
user.domain = {id: 'default'};
user.domain = { id: 'default' }
}
}
if (projectId) {
project = {id: projectId};
project = { id: projectId }
} else if (projectName) {
project = {name: projectName};
project = { name: projectName }
if (projectDomainId) {
project.domain = {id: projectDomainId};
project.domain = { id: projectDomainId }
} else if (projectDomainName) {
project.domain = {name: projectDomainName};
project.domain = { name: projectDomainName }
} else {
project.domain = {id: 'default'};
project.domain = { id: 'default' }
}
}
@ -136,18 +135,18 @@ export default class Keystone extends AbstractService {
auth: {
identity: {
methods: ['password'],
password: {user}
password: { user }
},
scope: project ? {project} : 'unscoped'
scope: project ? { project } : 'unscoped'
}
};
}
return this
.serviceEndpoint()
.then((url) => this.http.httpPost(`${url}auth/tokens`, body))
.then((response) => {
return response.headers.get('X-Subject-Token');
});
return response.headers.get('X-Subject-Token')
})
}
/**
@ -157,16 +156,16 @@ export default class Keystone extends AbstractService {
* @param {String} adminToken An optional admin token.
* @returns {Promise.<T>} A promise which will resolve if the token has been successfully revoked.
*/
tokenRevoke(token, adminToken = null) {
tokenRevoke (token, adminToken = null) {
return Promise
.all([this.serviceEndpoint(), token, adminToken])
.then(([url, token, adminToken]) => {
return [url, {
'X-Subject-Token': token,
'X-Auth-Token': adminToken || token
}];
}]
})
.then(([url, headers]) => this.http.httpRequest('DELETE', `${url}auth/tokens`, headers));
.then(([url, headers]) => this.http.httpRequest('DELETE', `${url}auth/tokens`, headers))
}
/**
@ -175,17 +174,17 @@ export default class Keystone extends AbstractService {
* @param {String} token The authorization token.
* @returns {Promise.<T>} A promise which will resolve with information about the token.
*/
tokenInfo(token) {
tokenInfo (token) {
return Promise
.all([this.serviceEndpoint(), token])
.then(([url, token]) => {
return [url, {
'X-Subject-Token': token,
'X-Auth-Token': token
}];
}]
})
.then(([url, headers]) => this.http.httpRequest('GET', `${url}auth/tokens`, headers))
.then((response) => response.json());
.then((response) => response.json())
}
/**
@ -194,11 +193,11 @@ export default class Keystone extends AbstractService {
* @param {String} token The authorization token.
* @returns {Promise.<T>} A promise which will resolve with the service catalog.
*/
catalogList(token = null) {
catalogList (token = null) {
return this
._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}auth/catalog`, headers))
.then((response) => response.json())
.then((body) => body.catalog);
.then((body) => body.catalog)
}
}

View File

@ -13,7 +13,7 @@
* the License for the specific language governing permissions and limitations
* under the License.
*/
import AbstractService from './util/abstractService';
import AbstractService from './util/abstractService'
/**
* A list of all supported versions. Please keep this array sorted by most recent.
@ -23,10 +23,9 @@ import AbstractService from './util/abstractService';
*/
const supportedNeutronVersions = [
'v2.0'
];
]
export default class Neutron extends AbstractService {
/**
* This class provides direct, idempotent, low-level access to the Neutron API of a specific
* cloud. The constructor requires that you provide a specific neutron interface endpoint
@ -42,16 +41,16 @@ export default class Neutron extends AbstractService {
* }
* @param {{}} endpointConfig The configuration element for a specific glance endpoint.
*/
constructor(endpointConfig) {
constructor (endpointConfig) {
// Sanity checks.
if (!endpointConfig || !endpointConfig.url) {
throw new Error('An endpoint configuration is required.');
throw new Error('An endpoint configuration is required.')
}
// Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact).
endpointConfig = Object.assign({}, endpointConfig);
endpointConfig = Object.assign({}, endpointConfig)
super(endpointConfig.url, supportedNeutronVersions);
super(endpointConfig.url, supportedNeutronVersions)
}
/**
@ -60,11 +59,11 @@ export default class Neutron extends AbstractService {
* @param {String} token An authorization token, or a promise which will resolve into one.
* @returns {Promise.<T>} A promise which will resolve with the list of networks.
*/
networkList(token = null) {
networkList (token = null) {
return this
._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}/networks`, headers))
.then((response) => response.json())
.then((body) => body.networks);
.then((body) => body.networks)
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
import AbstractService from "./util/abstractService";
import AbstractService from './util/abstractService'
/**
* A list of all supported versions. Please keep this array sorted by most recent.
@ -24,10 +24,9 @@ import AbstractService from "./util/abstractService";
*/
const supportedNovaVersions = [
'v2.1'
];
]
export default class Nova extends AbstractService {
/**
* This class provides direct, idempotent, low-level access to the Nova API of a specific
* cloud. The constructor requires that you provide a specific nova interface endpoint
@ -43,17 +42,17 @@ export default class Nova extends AbstractService {
* }
* @param {{}} endpointConfig The configuration element for a specific nova endpoint.
*/
constructor(endpointConfig) {
constructor (endpointConfig) {
// Sanity checks.
if (!endpointConfig || !endpointConfig.url) {
throw new Error('An endpoint configuration is required.');
throw new Error('An endpoint configuration is required.')
}
// Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact).
endpointConfig = Object.assign({}, endpointConfig);
endpointConfig = Object.assign({}, endpointConfig)
super(endpointConfig.url, supportedNovaVersions);
this._config = endpointConfig;
super(endpointConfig.url, supportedNovaVersions)
this._config = endpointConfig
}
/**
@ -62,11 +61,11 @@ export default class Nova extends AbstractService {
* @param {String} token An authorization token, or a promise which will resolve into one.
* @returns {Promise.<T>} A promise which will resolve with the list of flavors.
*/
flavorList(token = null) {
flavorList (token = null) {
return this
._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}flavors`, headers))
.then((response) => response.json())
.then((body) => body.flavors);
.then((body) => body.flavors)
}
}

View File

@ -1,7 +1,7 @@
import Keystone from "./keystone";
import Neutron from "./neutron";
import Glance from "./glance";
import Nova from "./nova";
import Keystone from './keystone'
import Neutron from './neutron'
import Glance from './glance'
import Nova from './nova'
export default class OpenStack {
/**
@ -9,21 +9,21 @@ export default class OpenStack {
*
* @param {{}} cloudConfig The configuration object for a specific cloud.
*/
constructor(cloudConfig) {
constructor (cloudConfig) {
// Sanity checks.
if (!cloudConfig) {
throw new Error('A configuration is required.');
throw new Error('A configuration is required.')
}
// Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact).
cloudConfig = Object.assign({}, cloudConfig);
cloudConfig = Object.assign({}, cloudConfig)
this.cloudConfig = cloudConfig;
this.cloudConfig = cloudConfig
}
getConfig() {
getConfig () {
// Returns the config instance
return this.cloudConfig;
return this.cloudConfig
}
/**
@ -31,9 +31,9 @@ export default class OpenStack {
*
* @returns {Promise.<T>} A promise which will resolve with the list of networks.
*/
networkList() {
networkList () {
return this._neutron
.then((neutron) => neutron.networkList(this._token));
.then((neutron) => neutron.networkList(this._token))
}
/**
@ -41,9 +41,9 @@ export default class OpenStack {
*
* @returns {Promise.<T>} A promise which will resolve with the list of images.
*/
imageList() {
imageList () {
return this._glance
.then((glance) => glance.imageList(this._token));
.then((glance) => glance.imageList(this._token))
}
/**
@ -51,9 +51,9 @@ export default class OpenStack {
*
* @returns {Promise.<T>} A promise which will resolve with the list of flavors.
*/
flavorList() {
flavorList () {
return this._nova
.then((nova) => nova.flavorList(this._token));
.then((nova) => nova.flavorList(this._token))
}
/**
@ -62,12 +62,12 @@ export default class OpenStack {
* @returns {Promise.<Keystone>} A promise which will resolve with Keystone instance.
* @private
*/
get _keystone() {
get _keystone () {
if (!this._keystonePromise) {
this._keystonePromise = Promise.resolve(new Keystone(this.getConfig()));
this._keystonePromise = Promise.resolve(new Keystone(this.getConfig()))
}
return this._keystonePromise;
return this._keystonePromise
}
/**
@ -76,12 +76,12 @@ export default class OpenStack {
* @returns {Promise.<Neutron>} A promise which will resolve with Neutron instance.
* @private
*/
get _neutron() {
get _neutron () {
if (!this._neutronPromise) {
this._neutronPromise = this._getComponentConfigFor('neutron')
.then((componentConfig) => new Neutron(componentConfig));
.then((componentConfig) => new Neutron(componentConfig))
}
return this._neutronPromise;
return this._neutronPromise
}
/**
@ -90,12 +90,12 @@ export default class OpenStack {
* @returns {Promise.<Glance>} A promise which will resolve with Glance instance.
* @private
*/
get _glance() {
get _glance () {
if (!this._glancePromise) {
this._glancePromise = this._getComponentConfigFor('glance')
.then((componentConfig) => new Glance(componentConfig));
.then((componentConfig) => new Glance(componentConfig))
}
return this._glancePromise;
return this._glancePromise
}
/**
@ -104,12 +104,12 @@ export default class OpenStack {
* @returns {Promise.<Nova>} A promise which will resolve with Nova instance.
* @private
*/
get _nova() {
get _nova () {
if (!this._novaPromise) {
this._novaPromise = this._getComponentConfigFor('nova')
.then((componentConfig) => new Nova(componentConfig));
.then((componentConfig) => new Nova(componentConfig))
}
return this._novaPromise;
return this._novaPromise
}
/**
@ -118,11 +118,11 @@ export default class OpenStack {
* @returns {Promise.<T>} A promise which will resolve with the token.
* @private
*/
get _token() {
get _token () {
if (!this._tokenPromise) {
this._tokenPromise = this._keystone.then((k) => k.tokenIssue());
this._tokenPromise = this._keystone.then((k) => k.tokenIssue())
}
return this._tokenPromise;
return this._tokenPromise
}
/**
@ -132,14 +132,13 @@ export default class OpenStack {
* @returns {Promise.<{}>} A promise which will resolve with the component config.
* @private
*/
_getComponentConfigFor(name) {
const config = this.getConfig();
_getComponentConfigFor (name) {
const config = this.getConfig()
return this._token
.then((token) => this._keystone.then((keystone) => keystone.catalogList(token)))
.then((catalog) => catalog.find((entry) => entry.name === name))
.then((entry) => entry.endpoints.find((endpoint) => {
return endpoint.region === config.region_name && endpoint.interface === 'public';
}));
return endpoint.region === config.region_name && endpoint.interface === 'public'
}))
}
}

View File

@ -14,12 +14,11 @@
* under the License.
*/
import Http from './http';
import Version from './version';
import URL from 'url-parse';
import Http from './http'
import Version from './version'
import URL from 'url-parse'
export default class AbstractService {
/**
* This class provides an abstract implementation of our services, which includes logic common to
* all of our services.
@ -27,9 +26,9 @@ export default class AbstractService {
* @param {string} endpointUrl The endpoint URL.
* @param {Array} supportedVersions The list of all supported versions.
*/
constructor(endpointUrl, supportedVersions) {
this._endpointUrl = endpointUrl;
this._supportedVersions = supportedVersions;
constructor (endpointUrl, supportedVersions) {
this._endpointUrl = endpointUrl
this._supportedVersions = supportedVersions
}
/**
@ -37,11 +36,11 @@ export default class AbstractService {
*
* @returns {Http} Our HTTP service instance.
*/
get http() {
get http () {
if (!this._http) {
this._http = new Http();
this._http = new Http()
}
return this._http;
return this._http
}
/**
@ -49,8 +48,8 @@ export default class AbstractService {
*
* @returns {Array} The list of all supported versions, or empty array.
*/
get supportedVersions() {
return this._supportedVersions || [];
get supportedVersions () {
return this._supportedVersions || []
}
/**
@ -58,8 +57,8 @@ export default class AbstractService {
*
* @returns {string} The URL of our service.
*/
get endpointUrl() {
return this._endpointUrl;
get endpointUrl () {
return this._endpointUrl
}
/**
@ -67,14 +66,14 @@ export default class AbstractService {
*
* @returns {Promise.<Version[]>} A promise that will resolve with the list of API versions.
*/
versions() {
versions () {
return this._rawVersions().then((versions) => {
return versions.map((rawVersion) => {
const version = new Version(rawVersion.id);
version.links = rawVersion.links;
return version;
});
});
const version = new Version(rawVersion.id)
version.links = rawVersion.links
return version
})
})
}
/**
@ -83,26 +82,26 @@ export default class AbstractService {
* @returns {Promise.<Object[]>} A promise that will resolve with the list of raw versions.
* @protected
*/
_rawVersions() {
_rawVersions () {
return new Promise((resolve, reject) => {
this.http
.httpGet(this._endpointUrl)
.catch((response) => {
if (response.status === 401) {
let rootUrl = new URL(this._endpointUrl);
rootUrl.set('pathname', '/');
rootUrl.set('query', '');
rootUrl.set('hash', '');
const rootUrl = new URL(this._endpointUrl)
rootUrl.set('pathname', '/')
rootUrl.set('query', '')
rootUrl.set('hash', '')
return this.http.httpGet(rootUrl.href);
return this.http.httpGet(rootUrl.href)
} else {
throw response;
throw response
}
})
.then((response) => response.json())
.then((body) => resolve(body.versions))
.catch(reject);
});
.catch(reject)
})
}
/**
@ -110,17 +109,17 @@ export default class AbstractService {
*
* @returns {Promise.<Version>} A promise that will resolve with the specific API version.
*/
version() {
version () {
return this
.versions()
.then((versions) => {
for (let version of versions) {
for (const version of versions) {
if (this.supportedVersions.find(version.supports)) {
return version;
return version
}
}
throw new Error("No supported API version available.");
});
throw new Error('No supported API version available.')
})
}
/**
@ -128,22 +127,22 @@ export default class AbstractService {
*
* @returns {Promise.<T>|*} A promise which will resolve with the endpoint URL string.
*/
serviceEndpoint() {
serviceEndpoint () {
if (!this._endpointPromise) {
this._endpointPromise = this.version()
.then((version) => {
if (version.links) {
for (let i = 0; i < version.links.length; i++) {
let link = version.links[i];
const link = version.links[i]
if (link.rel === 'self' && link.href) {
return link.href;
return link.href
}
}
}
throw new Error("No service endpoint discovered.");
});
throw new Error('No service endpoint discovered.')
})
}
return this._endpointPromise;
return this._endpointPromise
}
/**
@ -155,18 +154,18 @@ export default class AbstractService {
* @returns {Promise} A promise which resolves with [url, token].
* @private
*/
_requestComponents(token = null) {
_requestComponents (token = null) {
// Make sure the token is a promise.
let headerPromise = Promise
const headerPromise = Promise
.resolve(token)
.then((token) => {
if (token) {
return {
'X-Auth-Token': token
};
}
}
return {};
});
return Promise.all([this.serviceEndpoint(), headerPromise]);
return {}
})
return Promise.all([this.serviceEndpoint(), headerPromise])
}
}

View File

@ -14,8 +14,8 @@
* under the License.
*/
import 'isomorphic-fetch';
import log from 'loglevel';
import 'isomorphic-fetch'
import log from 'loglevel'
/**
* This utility class provides an abstraction layer for HTTP calls via fetch(). Its purpose is
@ -33,7 +33,6 @@ import log from 'loglevel';
* - Other features.
*/
export default class Http {
/**
* The default headers which will be sent with every request. A copy of these headers will be
* added to the Request instance passed through the interceptor chain, and may be
@ -41,18 +40,18 @@ export default class Http {
*
* @returns {{string: string}} A mapping of 'headerName': 'headerValue'
*/
get defaultHeaders() {
return this._defaultHeaders;
get defaultHeaders () {
return this._defaultHeaders
}
/**
* Create a new HTTP handler.
*/
constructor() {
constructor () {
// Add default response interceptors.
this._defaultHeaders = {
'Content-Type': 'application/json'
};
}
}
/**
@ -64,40 +63,39 @@ export default class Http {
* @param {{}} body The body. It will be JSON-Encoded by the handler.
* @returns {Promise} A promise which will resolve with the processed request response.
*/
httpRequest(method, url, headers = {}, body) {
httpRequest (method, url, headers = {}, body) {
// Sanitize the headers...
headers = Object.assign({}, headers, this.defaultHeaders);
headers = Object.assign({}, headers, this.defaultHeaders)
// Build the request
const init = {method, headers};
const init = { method, headers }
// The Request() constructor will throw an error if the method is GET/HEAD, and there's a body.
if (['GET', 'HEAD'].indexOf(method) === -1 && body) {
init.body = JSON.stringify(body);
init.body = JSON.stringify(body)
}
const request = new Request(url, init);
const request = new Request(url, init)
// Build the wrapper promise.
return new Promise((resolve, reject) => {
log.debug('-->', `HTTP ${method}`, url, JSON.stringify(headers), JSON.stringify(body));
let promise = fetch(request.url, init);
log.debug('-->', `HTTP ${method}`, url, JSON.stringify(headers), JSON.stringify(body))
const promise = fetch(request.url, init)
// Fetch will treat all http responses (2xx, 3xx, 4xx, 5xx, etc) as successful responses.
// This will catch all 4xx and 5xx responses and return them to the catch() handler. Note
// that it's up to the downstream developer to determine whether what they received is an
// error or a failed response.
promise.then((response) => {
log.debug('<--', `HTTP ${response.status}`);
log.debug('<--', `HTTP ${response.status}`)
if (response.status >= 400) {
return reject(response);
return reject(response)
} else {
return response;
return response
}
});
})
promise.then((response) => resolve(response), (error) => reject(error));
});
promise.then((response) => resolve(response), (error) => reject(error))
})
}
/**
@ -106,8 +104,8 @@ export default class Http {
* @param {String} url The request URL.
* @returns {Promise} A promise which will resolve with the processed request response.
*/
httpGet(url) {
return this.httpRequest('GET', url, {}, null);
httpGet (url) {
return this.httpRequest('GET', url, {}, null)
}
/**
@ -117,8 +115,8 @@ export default class Http {
* @param {{}} body The body. It will be JSON-Encoded by the handler.
* @returns {Promise} A promise which will resolve with the processed request response.
*/
httpPut(url, body) {
return this.httpRequest('PUT', url, {}, body);
httpPut (url, body) {
return this.httpRequest('PUT', url, {}, body)
}
/**
@ -128,8 +126,8 @@ export default class Http {
* @param {{}} body The body. It will be JSON-Encoded by the handler.
* @returns {Promise} A promise which will resolve with the processed request response.
*/
httpPost(url, body) {
return this.httpRequest('POST', url, {}, body);
httpPost (url, body) {
return this.httpRequest('POST', url, {}, body)
}
/**
@ -138,7 +136,7 @@ export default class Http {
* @param {String} url The request URL.
* @returns {Promise} A promise which will resolve with the processed request response.
*/
httpDelete(url) {
return this.httpRequest('DELETE', url, {}, null);
httpDelete (url) {
return this.httpRequest('DELETE', url, {}, null)
}
}

View File

@ -19,14 +19,13 @@
* comparable instance.
*/
export default class Version {
/**
* The name of the service.
*
* @returns {String|*|null} The name of the service, or null.
*/
get service() {
return this._service || null;
get service () {
return this._service || null
}
/**
@ -34,8 +33,8 @@ export default class Version {
*
* @returns {Number} The major version number
*/
get major() {
return this._major || 0;
get major () {
return this._major || 0
}
/**
@ -43,8 +42,8 @@ export default class Version {
*
* @returns {Number} The minor version number
*/
get minor() {
return this._minor || 0;
get minor () {
return this._minor || 0
}
/**
@ -52,8 +51,8 @@ export default class Version {
*
* @returns {Number} The patch version number.
*/
get patch() {
return this._patch || 0;
get patch () {
return this._patch || 0
}
/**
@ -61,8 +60,8 @@ export default class Version {
*
* @returns {Object[]} The list of links.
*/
get links() {
return this._links || null;
get links () {
return this._links || null
}
/**
@ -70,9 +69,9 @@ export default class Version {
*
* @param {Object[]} links The links to be set
*/
set links(links) {
set links (links) {
if (Array.isArray(links)) {
this._links = links;
this._links = links
}
}
@ -82,37 +81,37 @@ export default class Version {
* @param {String} [service] The name of the service.
* @param {String} versionString The version string for this service.
*/
constructor(service, versionString) {
constructor (service, versionString) {
// Sanitize input
if (typeof service !== 'string') {
service = undefined;
service = undefined
}
if (typeof versionString !== 'string') {
versionString = undefined;
versionString = undefined
}
if (versionString === undefined) {
versionString = service;
versionString = service
} else {
this._service = service;
this._service = service
}
// Sanity check before running regex.
if (!versionString || !versionString.match) {
return;
return
}
const results = versionString.match(/^(([^ ]+) )?v?(([0-9]+)(\.([0-9]+)(.([0-9]+))?)?)$/);
const results = versionString.match(/^(([^ ]+) )?v?(([0-9]+)(\.([0-9]+)(.([0-9]+))?)?)$/)
if (results) {
this._service = results[2] || this._service; // regex takes precedence
this._major = parseInt(results[4], 10);
this._minor = parseInt(results[6], 10);
this._patch = parseInt(results[8], 10);
this._service = results[2] || this._service // regex takes precedence
this._major = parseInt(results[4], 10)
this._minor = parseInt(results[6], 10)
this._patch = parseInt(results[8], 10)
}
this._links = null;
this._links = null
this.equals = this.equals.bind(this);
this.supports = this.supports.bind(this);
this.equals = this.equals.bind(this)
this.supports = this.supports.bind(this)
}
/**
@ -121,20 +120,20 @@ export default class Version {
* @param {String|Version} version The version to compare to.
* @returns {boolean} True if they are exactly the same, otherwise false.
*/
equals(version) {
equals (version) {
if (!(version instanceof Version)) {
// is it a parseable string?
if (typeof version === 'string') {
version = new Version(version);
version = new Version(version)
} else {
return false;
return false
}
}
return version.major === this.major &&
version.minor === this.minor &&
version.patch === this.patch &&
version.service === this.service;
version.service === this.service
}
/**
@ -144,30 +143,30 @@ export default class Version {
* @param {String|Version} version the version to support.
* @returns {boolean} True if the version is compatible, otherwise false
*/
supports(version) {
supports (version) {
if (!(version instanceof Version)) {
if (typeof version === 'string') {
version = new Version(version);
version = new Version(version)
} else {
return false;
return false
}
}
const compatibleVersion = version.service === this.service &&
version.major === this.major &&
version.minor <= this.minor;
version.minor <= this.minor
if (compatibleVersion && version.minor === this.minor) {
return version.patch <= this.patch;
return version.patch <= this.patch
}
return compatibleVersion;
return compatibleVersion
}
toString() {
let version = `${this.major}.${this.minor}`;
toString () {
let version = `${this.major}.${this.minor}`
if (this.patch) {
version = `${version}.${this.patch}`;
version = `${version}.${this.patch}`
}
return version;
return version
}
}

View File

@ -14,71 +14,69 @@
* under the License.
*/
import config from "./helpers/cloudsConfig";
import Version from '../../src/util/version';
import Glance from "../../src/glance";
import Keystone from "../../src/keystone";
import log from 'loglevel';
import config from './helpers/cloudsConfig'
import Version from '../../src/util/version'
import Glance from '../../src/glance'
import Keystone from '../../src/keystone'
import log from 'loglevel'
log.setLevel("DEBUG");
log.setLevel('DEBUG')
describe("Glance", () => {
describe('Glance', () => {
// Create a keystone instance and extract the glance API endpoint.
let devstackConfig = config.clouds.devstack;
let keystone = new Keystone(devstackConfig);
let tokenPromise = keystone.tokenIssue();
const devstackConfig = config.clouds.devstack
const keystone = new Keystone(devstackConfig)
const tokenPromise = keystone.tokenIssue()
let configPromise = tokenPromise
const configPromise = tokenPromise
.then((token) => keystone.catalogList(token))
.then((catalog) => catalog.find((entry) => entry.name === 'glance'))
.then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public'));
.then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public'))
describe("versions()", () => {
it("should return a list of all versions available on this clouds' glance", (done) => {
describe('versions()', () => {
it('should return a list of all versions available on this clouds\' glance', (done) => {
configPromise
.then((config) => new Glance(config))
.then((glance) => glance.versions())
.then((versions) => {
// Quick sanity check.
expect(versions.length > 0).toBeTruthy();
done();
expect(versions.length > 0).toBeTruthy()
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("version()", () => {
describe('version()', () => {
/**
* This test acts as a canary, to inform the SDK developers that the Glance API
* has changed in a significant way.
*/
it("should return a supported version.", (done) => {
it('should return a supported version.', (done) => {
configPromise
.then((config) => new Glance(config))
.then((glance) => glance.version())
.then((apiVersion) => {
expect(apiVersion instanceof Version).not.toBeFalsy();
done();
expect(apiVersion instanceof Version).not.toBeFalsy()
done()
})
.catch((error) => done.fail(error));
});
});
describe("imageList()", () => {
.catch((error) => done.fail(error))
})
})
describe('imageList()', () => {
/**
* Assert that we can get a list of images.
*/
it("should return a supported version.", (done) => {
it('should return a supported version.', (done) => {
configPromise
.then((config) => new Glance(config))
.then((glance) => glance.imageList(tokenPromise))
.then((images) => {
expect(images.length > 0).toBeTruthy();
done();
expect(images.length > 0).toBeTruthy()
done()
})
.catch((error) => done.fail(error));
});
});
});
.catch((error) => done.fail(error))
})
})
})

View File

@ -1,8 +1,8 @@
/*eslint no-sync: "off"*/
import yaml from 'js-yaml';
import fs from 'fs';
import cloudsYamlPath from './cloudsYamlPath';
/* eslint no-sync: "off" */
import yaml from 'js-yaml'
import fs from 'fs'
import cloudsYamlPath from './cloudsYamlPath'
const clouds = yaml.safeLoad(fs.readFileSync(cloudsYamlPath, 'utf8'));
const clouds = yaml.safeLoad(fs.readFileSync(cloudsYamlPath, 'utf8'))
export default clouds;
export default clouds

View File

@ -1,23 +1,22 @@
/*eslint no-process-env: "off", no-sync: "off"*/
import fs from 'fs';
import path from 'path';
/* eslint no-process-env: "off", no-sync: "off" */
import fs from 'fs'
import path from 'path'
const resolvePaths = [
'./clouds.yaml',
process.env.HOME + '/.config/openstack/clouds.yaml',
'/etc/openstack/clouds.yaml'
];
]
function fileExists(path) {
function fileExists (path) {
try {
fs.statSync(path);
return true;
fs.statSync(path)
return true
} catch (err) {
return false;
return false
}
}
const cloudFiles = resolvePaths.filter(fileExists);
export default cloudFiles.length > 0 ? path.resolve(cloudFiles[0]) : 'clouds.yaml';
const cloudFiles = resolvePaths.filter(fileExists)
export default cloudFiles.length > 0 ? path.resolve(cloudFiles[0]) : 'clouds.yaml'

View File

@ -14,81 +14,81 @@
* under the License.
*/
import Version from '../../src/util/version';
import Keystone from "../../src/keystone";
import config from "./helpers/cloudsConfig";
import log from 'loglevel';
import Version from '../../src/util/version'
import Keystone from '../../src/keystone'
import config from './helpers/cloudsConfig'
import log from 'loglevel'
log.setLevel("DEBUG");
log.setLevel('DEBUG')
describe("Keystone", () => {
let devstackConfig = config.clouds.devstack;
let adminConfig = config.clouds['devstack-admin'];
let keystone = new Keystone(devstackConfig);
describe('Keystone', () => {
const devstackConfig = config.clouds.devstack
const adminConfig = config.clouds['devstack-admin']
const keystone = new Keystone(devstackConfig)
describe("versions()", () => {
it("should return a list of all versions available on this clouds' keystone", (done) => {
describe('versions()', () => {
it('should return a list of all versions available on this clouds\' keystone', (done) => {
keystone.versions()
.then((versions) => {
// Quick sanity check.
expect(versions.length > 0).toBeTruthy();
done();
expect(versions.length > 0).toBeTruthy()
done()
})
.catch((response) => response.json()
.then((body) => done.fail(JSON.stringify(body)))
);
});
});
)
})
})
describe("version()", () => {
describe('version()', () => {
/**
* This test acts as a canary, to inform the SDK developers that the Keystone API
* has changed in a significant way.
*/
it("should return a supported version.", (done) => {
it('should return a supported version.', (done) => {
keystone.version()
.then((apiVersion) => {
expect(apiVersion instanceof Version).not.toBeFalsy();
done();
expect(apiVersion instanceof Version).not.toBeFalsy()
done()
})
.catch((response) => response.json()
.then((body) => done.fail(JSON.stringify(body)))
);
});
});
)
})
})
describe("tokenIssue()", () => {
let keystone = null;
describe('tokenIssue()', () => {
let keystone = null
beforeEach(() => {
keystone = new Keystone(config.clouds.devstack);
});
keystone = new Keystone(config.clouds.devstack)
})
it("should 'just work' by using provided credentials from the config.", (done) => {
it('should "just work" by using provided credentials from the config.', (done) => {
keystone
.tokenIssue()
.then((token) => {
expect(token).not.toBeNull();
done();
expect(token).not.toBeNull()
done()
})
.catch((response) => response.json()
.then((body) => done.fail(JSON.stringify(body)))
);
});
)
})
it("should permit passing your own user, password, and project.", (done) => {
it('should permit passing your own user, password, and project.', (done) => {
keystone
.tokenIssue(adminConfig.auth)
.then((token) => {
expect(token).not.toBeNull();
done();
expect(token).not.toBeNull()
done()
})
.catch((response) => response.json()
.then((body) => done.fail(JSON.stringify(body)))
);
});
)
})
it("should throw an exception if invalid username and password are provided.", (done) => {
it('should throw an exception if invalid username and password are provided.', (done) => {
keystone
.tokenIssue({
username: 'foo',
@ -96,12 +96,12 @@ describe("Keystone", () => {
})
.then((token) => done.fail(token))
.catch((error) => {
expect(error).not.toBeNull();
done();
});
});
expect(error).not.toBeNull()
done()
})
})
it("should throw an exception if invalid project is provided.", (done) => {
it('should throw an exception if invalid project is provided.', (done) => {
keystone
.tokenIssue({
project_id: 'foo',
@ -109,12 +109,12 @@ describe("Keystone", () => {
})
.then((token) => done.fail(token))
.catch((error) => {
expect(error).not.toBeNull();
done();
});
});
expect(error).not.toBeNull()
done()
})
})
it("should throw an exception if invalid user domain is provided.", (done) => {
it('should throw an exception if invalid user domain is provided.', (done) => {
keystone
.tokenIssue({
user_domain_id: 'foo',
@ -122,127 +122,127 @@ describe("Keystone", () => {
})
.then((token) => done.fail(token))
.catch((error) => {
expect(error).not.toBeNull();
done();
});
});
});
expect(error).not.toBeNull()
done()
})
})
})
describe("tokenRevoke()", () => {
let keystone = null;
describe('tokenRevoke()', () => {
let keystone = null
beforeEach(() => {
keystone = new Keystone(config.clouds.devstack);
});
keystone = new Keystone(config.clouds.devstack)
})
it("should permit self-revocation.", (done) => {
it('should permit self-revocation.', (done) => {
keystone
.tokenIssue()
.then((token) => {
return keystone.tokenRevoke(token);
return keystone.tokenRevoke(token)
})
.then((response) => {
expect(response.status).toBe(204); // No content
done();
expect(response.status).toBe(204) // No content
done()
})
.catch((response) => response.json()
.then((body) => done.fail(JSON.stringify(body)))
);
});
)
})
it("should allow an admin to revoke another token.", (done) => {
let adminToken;
let adminKeystone = new Keystone(adminConfig);
it('should allow an admin to revoke another token.', (done) => {
let adminToken
const adminKeystone = new Keystone(adminConfig)
adminKeystone.tokenIssue() // Get an admin token.
.then((token) => {
adminToken = token;
return keystone.tokenIssue(); // Regular token.
adminToken = token
return keystone.tokenIssue() // Regular token.
})
.then((token) => keystone.tokenRevoke(token, adminToken))
.then((response) => {
expect(response.status).toBe(204); // No content
done();
expect(response.status).toBe(204) // No content
done()
})
.catch((response) => response.json()
.then((body) => done.fail(JSON.stringify(body)))
);
});
)
})
it("should throw an exception if invalid token is provided.", (done) => {
it('should throw an exception if invalid token is provided.', (done) => {
keystone
.tokenRevoke('not_a_valid_token')
.then((response) => done.fail(response))
.catch((error) => {
expect(error).not.toBeNull();
done();
});
});
});
expect(error).not.toBeNull()
done()
})
})
})
describe("tokenInfo()", () => {
let keystone = null;
describe('tokenInfo()', () => {
let keystone = null
beforeEach(() => {
keystone = new Keystone(config.clouds.devstack);
});
keystone = new Keystone(config.clouds.devstack)
})
it("should retrieve info about a token.", (done) => {
it('should retrieve info about a token.', (done) => {
keystone
.tokenIssue()
.then((token) => {
return keystone.tokenInfo(token);
return keystone.tokenInfo(token)
})
.then((info) => {
expect('token' in info).toBe(true);
done();
expect('token' in info).toBe(true)
done()
})
.catch((response) => response.json()
.then((body) => done.fail(JSON.stringify(body)))
);
});
)
})
it("should throw an exception if invalid token is provided.", (done) => {
it('should throw an exception if invalid token is provided.', (done) => {
keystone
.tokenRevoke('not_a_valid_token')
.then((response) => done.fail(response))
.catch((error) => {
expect(error).not.toBeNull();
done();
});
});
});
expect(error).not.toBeNull()
done()
})
})
})
describe("catalogList()", () => {
let keystone = null;
describe('catalogList()', () => {
let keystone = null
beforeEach(() => {
keystone = new Keystone(config.clouds.devstack);
});
keystone = new Keystone(config.clouds.devstack)
})
it("should list a catalog.", (done) => {
it('should list a catalog.', (done) => {
keystone
.tokenIssue()
.then((token) => {
return keystone.catalogList(token);
return keystone.catalogList(token)
})
.then((catalog) => {
expect(catalog.length).not.toBe(0);
done();
expect(catalog.length).not.toBe(0)
done()
})
.catch((response) => response.json()
.then((body) => done.fail(JSON.stringify(body)))
);
});
)
})
it("should error if not authenticated.", (done) => {
it('should error if not authenticated.', (done) => {
keystone
.catalogList()
.then((response) => done.fail(response))
.catch((error) => {
expect(error).not.toBeNull();
done();
});
});
});
});
expect(error).not.toBeNull()
done()
})
})
})
})

View File

@ -14,67 +14,66 @@
* under the License.
*/
import config from "./helpers/cloudsConfig";
import Version from '../../src/util/version';
import Neutron from "../../src/neutron";
import Keystone from "../../src/keystone";
import log from 'loglevel';
import config from './helpers/cloudsConfig'
import Version from '../../src/util/version'
import Neutron from '../../src/neutron'
import Keystone from '../../src/keystone'
import log from 'loglevel'
log.setLevel("DEBUG");
log.setLevel('DEBUG')
describe("neutron", () => {
describe('neutron', () => {
// Create a keystone instance and extract the neutron API endpoint.
let devstackConfig = config.clouds.devstack;
let keystone = new Keystone(devstackConfig);
let tokenPromise = keystone.tokenIssue();
const devstackConfig = config.clouds.devstack
const keystone = new Keystone(devstackConfig)
const tokenPromise = keystone.tokenIssue()
let configPromise = tokenPromise
const configPromise = tokenPromise
.then((token) => keystone.catalogList(token))
.then((catalog) => catalog.find((entry) => entry.name === 'neutron'))
.then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public'));
.then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public'))
describe("versions()", () => {
it("should return a list of all versions available on this clouds' neutron", (done) => {
describe('versions()', () => {
it('should return a list of all versions available on this clouds\' neutron', (done) => {
configPromise
.then((config) => new Neutron(config))
.then((neutron) => neutron.versions())
.then((versions) => {
// Quick sanity check.
expect(versions.length > 0).toBeTruthy();
done();
expect(versions.length > 0).toBeTruthy()
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("version()", () => {
describe('version()', () => {
/**
* This test acts as a canary, to inform the SDK developers that the Neutron API
* has changed in a significant way.
*/
it("should return a supported version.", (done) => {
it('should return a supported version.', (done) => {
configPromise
.then((config) => new Neutron(config))
.then((neutron) => neutron.version())
.then((apiVersion) => {
expect(apiVersion instanceof Version).not.toBeFalsy();
done();
expect(apiVersion instanceof Version).not.toBeFalsy()
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("networkList()", () => {
it("should return the networks as an array.", (done) => {
describe('networkList()', () => {
it('should return the networks as an array.', (done) => {
configPromise
.then((config) => new Neutron(config))
.then((neutron) => neutron.networkList(tokenPromise))
.then((networks) => {
expect(networks.length > 0).toBeTruthy();
done();
expect(networks.length > 0).toBeTruthy()
done()
})
.catch((error) => done.fail(error));
});
});
});
.catch((error) => done.fail(error))
})
})
})

View File

@ -14,56 +14,55 @@
* limitations under the License.
*/
import config from "./helpers/cloudsConfig";
import Version from '../../src/util/version';
import Nova from "../../src/nova";
import Keystone from "../../src/keystone";
import log from 'loglevel';
import config from './helpers/cloudsConfig'
import Version from '../../src/util/version'
import Nova from '../../src/nova'
import Keystone from '../../src/keystone'
import log from 'loglevel'
log.setLevel("DEBUG");
log.setLevel('DEBUG')
describe("Nova", () => {
describe('Nova', () => {
// Create a keystone instance and extract the nova API endpoint.
let devstackConfig = config.clouds.devstack;
let keystone = new Keystone(devstackConfig);
let tokenPromise = keystone.tokenIssue();
const devstackConfig = config.clouds.devstack
const keystone = new Keystone(devstackConfig)
const tokenPromise = keystone.tokenIssue()
let configPromise = tokenPromise
const configPromise = tokenPromise
.then((token) => keystone.catalogList(token))
.then((catalog) => catalog.find((entry) => entry.name === 'nova'))
.then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public'));
.then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public'))
describe("version()", () => {
describe('version()', () => {
/**
* This test acts as a canary, to inform the SDK developers that the Nova API
* has changed in a significant way.
*/
it("should return a supported version.", (done) => {
it('should return a supported version.', (done) => {
configPromise
.then((config) => new Nova(config))
.then((nova) => nova.version())
.then((apiVersion) => {
expect(apiVersion instanceof Version).not.toBeFalsy();
done();
expect(apiVersion instanceof Version).not.toBeFalsy()
done()
})
.catch((error) => done.fail(error));
});
});
describe("flavorList()", () => {
.catch((error) => done.fail(error))
})
})
describe('flavorList()', () => {
/**
* Assert that we can get a list of flavors.
*/
it("should return a list of flavors.", (done) => {
it('should return a list of flavors.', (done) => {
configPromise
.then((config) => new Nova(config))
.then((nova) => nova.flavorList(tokenPromise))
.then((flavors) => {
expect(flavors.length > 0).toBeTruthy();
done();
expect(flavors.length > 0).toBeTruthy()
done()
})
.catch((error) => done.fail(error));
});
});
});
.catch((error) => done.fail(error))
})
})
})

View File

@ -1,49 +1,48 @@
import config from "./helpers/cloudsConfig";
import OpenStack from "../../src/openstack";
import log from 'loglevel';
import config from './helpers/cloudsConfig'
import OpenStack from '../../src/openstack'
import log from 'loglevel'
log.setLevel("DEBUG");
log.setLevel('DEBUG')
describe("OpenStack", () => {
let devstackConfig = config.clouds.devstack;
describe('OpenStack', () => {
const devstackConfig = config.clouds.devstack
describe("networkList()", () => {
it("should return the networks as an array.", (done) => {
const openstack = new OpenStack(devstackConfig);
describe('networkList()', () => {
it('should return the networks as an array.', (done) => {
const openstack = new OpenStack(devstackConfig)
openstack.networkList()
.then((networks) => {
expect(networks.length > 0).toBeTruthy();
done();
expect(networks.length > 0).toBeTruthy()
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("imageList()", () => {
it("should return the images as an array.", (done) => {
const openstack = new OpenStack(devstackConfig);
describe('imageList()', () => {
it('should return the images as an array.', (done) => {
const openstack = new OpenStack(devstackConfig)
openstack.imageList()
.then((images) => {
expect(images.length > 0).toBeTruthy();
done();
expect(images.length > 0).toBeTruthy()
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("flavorList()", () => {
it("should return the flavors as an array.", (done) => {
const openstack = new OpenStack(devstackConfig);
describe('flavorList()', () => {
it('should return the flavors as an array.', (done) => {
const openstack = new OpenStack(devstackConfig)
openstack.flavorList()
.then((flavors) => {
expect(flavors.length > 0).toBeTruthy();
done();
expect(flavors.length > 0).toBeTruthy()
done()
})
.catch((error) => done.fail(error));
});
});
});
.catch((error) => done.fail(error))
})
})
})

View File

@ -1,5 +1,5 @@
import Jasmine from 'jasmine';
import Jasmine from 'jasmine'
const jasmine = new Jasmine();
jasmine.loadConfigFile('test/functional/jasmine.json');
jasmine.execute();
const jasmine = new Jasmine()
jasmine.loadConfigFile('test/functional/jasmine.json')
jasmine.execute()

View File

@ -14,76 +14,75 @@
* under the License.
*/
import Glance from "../../src/glance.js";
import * as mockData from "./helpers/data/glance";
import fetchMock from "fetch-mock";
import Glance from '../../src/glance.js'
import * as mockData from './helpers/data/glance'
import fetchMock from 'fetch-mock'
describe('Glance', () => {
afterEach(fetchMock.restore);
afterEach(fetchMock.restore)
it('should export a class', () => {
const glance = new Glance(mockData.config);
expect(glance).toBeDefined();
});
const glance = new Glance(mockData.config)
expect(glance).toBeDefined()
})
it('should throw an error for an empty config', () => {
expect(() => new Glance()).toThrow();
});
expect(() => new Glance()).toThrow()
})
describe("serviceEndpoint()", () => {
it("Should return a valid endpoint to the glance API.", (done) => {
const glance = new Glance(mockData.config);
describe('serviceEndpoint()', () => {
it('Should return a valid endpoint to the glance API.', (done) => {
const glance = new Glance(mockData.config)
fetchMock.mock(mockData.root());
fetchMock.mock(mockData.root())
glance.serviceEndpoint()
.then((endpoint) => {
expect(endpoint).toEqual('http://192.168.99.99:9292/v2/');
done();
expect(endpoint).toEqual('http://192.168.99.99:9292/v2/')
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("imageList()", () => {
let glance = null;
describe('imageList()', () => {
let glance = null
beforeEach(() => {
fetchMock.mock(mockData.root());
glance = new Glance(mockData.config);
});
fetchMock.mock(mockData.root())
glance = new Glance(mockData.config)
})
it("should return the images as an array.", (done) => {
const token = 'test_token';
it('should return the images as an array.', (done) => {
const token = 'test_token'
fetchMock.mock(mockData.imageList(token));
fetchMock.mock(mockData.imageList(token))
glance
.imageList(token)
.then((images) => {
expect(images.length).not.toBe(0);
done();
expect(images.length).not.toBe(0)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should not cache its results", (done) => {
const token = 'test_token';
it('Should not cache its results', (done) => {
const token = 'test_token'
let mockOptions = mockData.imageList(token);
fetchMock.mock(mockOptions);
const mockOptions = mockData.imageList(token)
fetchMock.mock(mockOptions)
glance
.imageList(token)
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
return glance.imageList(token);
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
return glance.imageList(token)
})
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2);
done();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2)
done()
})
.catch((error) => done.fail(error));
});
});
});
.catch((error) => done.fail(error))
})
})
})

View File

@ -25,100 +25,100 @@
* format, rather just the subsection pointing to a particular cloud.
*/
const glanceConfig = {
region_id: "RegionOne",
url: "http://192.168.99.99:9292/",
region: "RegionOne",
interface: "public",
id: "0b8b5f0f14904136ab5a4f83f27ec49a"
};
region_id: 'RegionOne',
url: 'http://192.168.99.99:9292/',
region: 'RegionOne',
interface: 'public',
id: '0b8b5f0f14904136ab5a4f83f27ec49a'
}
/**
* Build a new FetchMock configuration for the root endpoint.
*
* @returns {{}} A full FetchMock configuration for Glance's Root Resource.
*/
function rootResponse() {
function rootResponse () {
return {
method: 'GET',
matcher: 'http://192.168.99.99:9292/',
response: {
versions: [
{
status: "CURRENT",
id: "v2.5",
status: 'CURRENT',
id: 'v2.5',
links: [
{
href: "http://192.168.99.99:9292/v2/",
rel: "self"
href: 'http://192.168.99.99:9292/v2/',
rel: 'self'
}
]
},
{
status: "SUPPORTED",
id: "v2.3",
status: 'SUPPORTED',
id: 'v2.3',
links: [
{
href: "http://192.168.99.99:9292/v2/",
rel: "self"
href: 'http://192.168.99.99:9292/v2/',
rel: 'self'
}
]
},
{
status: "SUPPORTED",
id: "v2.2",
status: 'SUPPORTED',
id: 'v2.2',
links: [
{
href: "http://192.168.99.99:9292/v2/",
rel: "self"
href: 'http://192.168.99.99:9292/v2/',
rel: 'self'
}
]
},
{
status: "SUPPORTED",
id: "v2.1",
status: 'SUPPORTED',
id: 'v2.1',
links: [
{
href: "http://192.168.99.99:9292/v2/",
rel: "self"
href: 'http://192.168.99.99:9292/v2/',
rel: 'self'
}
]
},
{
status: "SUPPORTED",
id: "v2.0",
status: 'SUPPORTED',
id: 'v2.0',
links: [
{
href: "http://192.168.99.99:9292/v2/",
rel: "self"
href: 'http://192.168.99.99:9292/v2/',
rel: 'self'
}
]
},
{
status: "SUPPORTED",
id: "v1.1",
status: 'SUPPORTED',
id: 'v1.1',
links: [
{
href: "http://192.168.99.99:9292/v1/",
rel: "self"
href: 'http://192.168.99.99:9292/v1/',
rel: 'self'
}
]
},
{
status: "SUPPORTED",
id: "v1.0",
status: 'SUPPORTED',
id: 'v1.0',
links: [
{
href: "http://192.168.99.99:9292/v1/",
rel: "self"
href: 'http://192.168.99.99:9292/v1/',
rel: 'self'
}
]
}
]
}
};
}
}
function imageList(token) {
function imageList (token) {
return {
method: 'GET',
matcher: 'http://192.168.99.99:9292/v2/images',
@ -195,10 +195,10 @@ function imageList(token) {
schema: '/v2/schemas/images',
first: '/v2/images'
}
};
}
}
export {
glanceConfig as config,
rootResponse as root,
imageList
};
}

View File

@ -32,302 +32,302 @@ const cloudConfig = {
project_name: 'js-openstack-lib',
auth_url: 'http://192.168.99.99/'
}
};
}
const catalogListData = [
{
endpoints: [
{
region_id: "RegionOne",
url: "http://192.168.99.99/identity_v2_admin",
region: "RegionOne",
interface: "admin",
id: "940664e070864b638dfafc53cfcbe887"
region_id: 'RegionOne',
url: 'http://192.168.99.99/identity_v2_admin',
region: 'RegionOne',
interface: 'admin',
id: '940664e070864b638dfafc53cfcbe887'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99/identity",
region: "RegionOne",
interface: "internal",
id: "c3707565bccb407c888040fa9b7e77b0"
region_id: 'RegionOne',
url: 'http://192.168.99.99/identity',
region: 'RegionOne',
interface: 'internal',
id: 'c3707565bccb407c888040fa9b7e77b0'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99/identity",
region: "RegionOne",
interface: "public",
id: "fb28f261810449ea98b2df646b847a74"
region_id: 'RegionOne',
url: 'http://192.168.99.99/identity',
region: 'RegionOne',
interface: 'public',
id: 'fb28f261810449ea98b2df646b847a74'
}
],
type: "identity",
id: "0599684d07a145659fa858c1deb4e885",
name: "keystone"
type: 'identity',
id: '0599684d07a145659fa858c1deb4e885',
name: 'keystone'
},
{
endpoints: [
{
region_id: "RegionOne",
url: "http://192.168.99.99:8776/v3/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "internal",
id: "611a5108ef0b4f999ad439b0e1abd9da"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8776/v3/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'internal',
id: '611a5108ef0b4f999ad439b0e1abd9da'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:8776/v3/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "public",
id: "ae08047e33d848c8b1c77f99bc572e22"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8776/v3/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'public',
id: 'ae08047e33d848c8b1c77f99bc572e22'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:8776/v3/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "admin",
id: "e26c6757baa549469772e16e03c051b8"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8776/v3/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'admin',
id: 'e26c6757baa549469772e16e03c051b8'
}
],
type: "volumev3",
id: "1092f88a41c64fc7b0331fce96e7df6c",
name: "cinderv3"
type: 'volumev3',
id: '1092f88a41c64fc7b0331fce96e7df6c',
name: 'cinderv3'
},
{
endpoints: [
{
region_id: "RegionOne",
url: "http://192.168.99.99:8776/v1/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "public",
id: "14ad1642b0874816a7ff08eb0e24be87"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8776/v1/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'public',
id: '14ad1642b0874816a7ff08eb0e24be87'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:8776/v1/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "internal",
id: "8bb7b28802d44e9d80fbb358a3e133af"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8776/v1/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'internal',
id: '8bb7b28802d44e9d80fbb358a3e133af'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:8776/v1/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "admin",
id: "c271745ff29c4c9d829ab3187d41cab7"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8776/v1/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'admin',
id: 'c271745ff29c4c9d829ab3187d41cab7'
}
],
type: "volume",
id: "5067360b6f264558945b7d2c312dd126",
name: "cinder"
type: 'volume',
id: '5067360b6f264558945b7d2c312dd126',
name: 'cinder'
},
{
endpoints: [
{
region_id: "RegionOne",
url: "http://192.168.99.99:9292",
region: "RegionOne",
interface: "admin",
id: "0b8b5f0f14904136ab5a4f83f27ec49a"
region_id: 'RegionOne',
url: 'http://192.168.99.99:9292',
region: 'RegionOne',
interface: 'admin',
id: '0b8b5f0f14904136ab5a4f83f27ec49a'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:9292",
region: "RegionOne",
interface: "internal",
id: "97c90e43e1fe473b85ef47627006dcdd"
region_id: 'RegionOne',
url: 'http://192.168.99.99:9292',
region: 'RegionOne',
interface: 'internal',
id: '97c90e43e1fe473b85ef47627006dcdd'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:9292",
region: "RegionOne",
interface: "public",
id: "ee114418c77a45d2a3cc28240dc4281d"
region_id: 'RegionOne',
url: 'http://192.168.99.99:9292',
region: 'RegionOne',
interface: 'public',
id: 'ee114418c77a45d2a3cc28240dc4281d'
}
],
type: "image",
id: "6512ca68fbd543928768201198cd7e42",
name: "glance"
type: 'image',
id: '6512ca68fbd543928768201198cd7e42',
name: 'glance'
},
{
endpoints: [
{
region_id: "RegionOne",
url: "http://192.168.99.99:8774/v2.1",
region: "RegionOne",
interface: "internal",
id: "14129d81da0e44abae0c082c535b58cc"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8774/v2.1',
region: 'RegionOne',
interface: 'internal',
id: '14129d81da0e44abae0c082c535b58cc'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:8774/v2.1",
region: "RegionOne",
interface: "public",
id: "be681632633d4a62a781148c2fedd6aa"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8774/v2.1',
region: 'RegionOne',
interface: 'public',
id: 'be681632633d4a62a781148c2fedd6aa'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:8774/v2.1",
region: "RegionOne",
interface: "admin",
id: "f8979efb0903442a9068d57fce4eafb2"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8774/v2.1',
region: 'RegionOne',
interface: 'admin',
id: 'f8979efb0903442a9068d57fce4eafb2'
}
],
type: "compute",
id: "6d3dd68ae2224fd39503342220b5d2c2",
name: "nova"
type: 'compute',
id: '6d3dd68ae2224fd39503342220b5d2c2',
name: 'nova'
},
{
endpoints: [
{
region_id: "RegionOne",
url: "http://192.168.99.99:8774/v2/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "admin",
id: "308f5ed663a7417db3f078f7e3b66db8"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8774/v2/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'admin',
id: '308f5ed663a7417db3f078f7e3b66db8'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:8774/v2/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "public",
id: "9f08e41e8156498ba01b5cc83cc9e1da"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8774/v2/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'public',
id: '9f08e41e8156498ba01b5cc83cc9e1da'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:8774/v2/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "internal",
id: "b855d4c048f1468f9df5a9950ae811c6"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8774/v2/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'internal',
id: 'b855d4c048f1468f9df5a9950ae811c6'
}
],
type: "compute_legacy",
id: "8ca07a04d03145a094c404b5edf70c18",
name: "nova_legacy"
type: 'compute_legacy',
id: '8ca07a04d03145a094c404b5edf70c18',
name: 'nova_legacy'
},
{
endpoints: [
{
region_id: "RegionOne",
url: "http://192.168.99.99:8776/v2/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "internal",
id: "2b6e28e0aade41b5b80baa9012e54ca4"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8776/v2/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'internal',
id: '2b6e28e0aade41b5b80baa9012e54ca4'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:8776/v2/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "admin",
id: "79c96252a8ab4c7181ef4fe97237c314"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8776/v2/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'admin',
id: '79c96252a8ab4c7181ef4fe97237c314'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:8776/v2/8b2aa635109f4d0ab355e18a269d341f",
region: "RegionOne",
interface: "public",
id: "8d4cbc86845a4ecb90f19903636205a7"
region_id: 'RegionOne',
url: 'http://192.168.99.99:8776/v2/8b2aa635109f4d0ab355e18a269d341f',
region: 'RegionOne',
interface: 'public',
id: '8d4cbc86845a4ecb90f19903636205a7'
}
],
type: "volumev2",
id: "a7967e90d1044b1fa6d80b033f1da510",
name: "cinderv2"
type: 'volumev2',
id: 'a7967e90d1044b1fa6d80b033f1da510',
name: 'cinderv2'
},
{
endpoints: [
{
region_id: "RegionOne",
url: "http://192.168.99.99:9696/",
region: "RegionOne",
interface: "public",
id: "7033fa4ebed74e3fa51753162150a1f2"
region_id: 'RegionOne',
url: 'http://192.168.99.99:9696/',
region: 'RegionOne',
interface: 'public',
id: '7033fa4ebed74e3fa51753162150a1f2'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:9696/",
region: "RegionOne",
interface: "internal",
id: "7aa942d402a34d4c90454b9d84285855"
region_id: 'RegionOne',
url: 'http://192.168.99.99:9696/',
region: 'RegionOne',
interface: 'internal',
id: '7aa942d402a34d4c90454b9d84285855'
},
{
region_id: "RegionOne",
url: "http://192.168.99.99:9696/",
region: "RegionOne",
interface: "admin",
id: "bd8db1bafe41489bbbc45641e525ee7d"
region_id: 'RegionOne',
url: 'http://192.168.99.99:9696/',
region: 'RegionOne',
interface: 'admin',
id: 'bd8db1bafe41489bbbc45641e525ee7d'
},
{
region_id: "RegionTwo",
url: "http://192.168.99.100:9696/",
region: "RegionTwo",
interface: "public",
id: "7033fa4ebed74e3fa51753162150a1f2"
region_id: 'RegionTwo',
url: 'http://192.168.99.100:9696/',
region: 'RegionTwo',
interface: 'public',
id: '7033fa4ebed74e3fa51753162150a1f2'
},
{
region_id: "RegionTwo",
url: "http://192.168.99.100:9696/",
region: "RegionOne",
interface: "RegionTwo",
id: "7aa942d402a34d4c90454b9d84285855"
region_id: 'RegionTwo',
url: 'http://192.168.99.100:9696/',
region: 'RegionOne',
interface: 'RegionTwo',
id: '7aa942d402a34d4c90454b9d84285855'
},
{
region_id: "RegionTwo",
url: "http://192.168.99.100:9696/",
region: "RegionTwo",
interface: "admin",
id: "bd8db1bafe41489bbbc45641e525ee7d"
region_id: 'RegionTwo',
url: 'http://192.168.99.100:9696/',
region: 'RegionTwo',
interface: 'admin',
id: 'bd8db1bafe41489bbbc45641e525ee7d'
}
],
type: "network",
id: "f36b9e68ef114769b85024513ee61047",
name: "neutron"
type: 'network',
id: 'f36b9e68ef114769b85024513ee61047',
name: 'neutron'
}
];
]
const tokenInfoData = {
is_domain: false,
methods: [
"password"
'password'
],
roles: [
{
id: "cfa75a8719f544e2903e5899785b0cf0",
name: "anotherrole"
id: 'cfa75a8719f544e2903e5899785b0cf0',
name: 'anotherrole'
},
{
id: "5f8126fad6704a999a3651955c7d8219",
name: "Member"
id: '5f8126fad6704a999a3651955c7d8219',
name: 'Member'
}
],
is_admin_project: false,
project: {
domain: {
id: "default",
name: "Default"
id: 'default',
name: 'Default'
},
id: "8b2aa635109f4d0ab355e18a269d341f",
name: "demo"
id: '8b2aa635109f4d0ab355e18a269d341f',
name: 'demo'
},
catalog: catalogListData,
expires_at: "2016-08-19T18:04:11.157434Z",
expires_at: '2016-08-19T18:04:11.157434Z',
user: {
domain: {
id: "default",
name: "Default"
id: 'default',
name: 'Default'
},
id: "d56a64f45da0450a826ede637be64304",
name: "demo"
id: 'd56a64f45da0450a826ede637be64304',
name: 'demo'
},
audit_ids: [
"FtgqCjtuR2-V36loBJ8mxQ"
'FtgqCjtuR2-V36loBJ8mxQ'
],
issued_at: "2016-08-19T17:04:11.157456Z"
};
issued_at: '2016-08-19T17:04:11.157456Z'
}
/**
* Build a new FetchMock configuration for the root endpoint.
*
* @returns {{}} A full FetchMock configuration for Keystone's Root Resource.
*/
function rootResponse() {
function rootResponse () {
return {
method: 'GET',
matcher: 'http://192.168.99.99/',
@ -335,56 +335,56 @@ function rootResponse() {
versions: {
values: [
{
status: "stable",
updated: "2016-10-06T00:00:00Z",
"media-types": [
status: 'stable',
updated: '2016-10-06T00:00:00Z',
'media-types': [
{
base: "application/json",
type: "application/vnd.openstack.identity-v3+json"
base: 'application/json',
type: 'application/vnd.openstack.identity-v3+json'
}
],
id: "v3.7",
id: 'v3.7',
links: [
{
href: "http://docs.openstack.org/",
type: "text/html",
rel: "describedby"
href: 'http://docs.openstack.org/',
type: 'text/html',
rel: 'describedby'
},
{
href: "http://192.168.99.99/identity_v2_admin/v3/",
rel: "self"
href: 'http://192.168.99.99/identity_v2_admin/v3/',
rel: 'self'
}
]
},
{
status: "deprecated",
updated: "2016-08-04T00:00:00Z",
"media-types": [
status: 'deprecated',
updated: '2016-08-04T00:00:00Z',
'media-types': [
{
base: "application/json",
type: "application/vnd.openstack.identity-v2.0+json"
base: 'application/json',
type: 'application/vnd.openstack.identity-v2.0+json'
}
],
id: "v2.0",
id: 'v2.0',
links: [
{
href: "http://192.168.99.99/identity_v2_admin/v2.0/",
rel: "self"
href: 'http://192.168.99.99/identity_v2_admin/v2.0/',
rel: 'self'
},
{
href: "http://docs.openstack.org/",
type: "text/html",
rel: "describedby"
href: 'http://docs.openstack.org/',
type: 'text/html',
rel: 'describedby'
}
]
}
]
}
}
};
}
}
function tokenIssue() {
function tokenIssue () {
return {
method: 'POST',
matcher: 'http://192.168.99.99/identity_v2_admin/v3/auth/tokens',
@ -397,10 +397,10 @@ function tokenIssue() {
token: tokenInfoData
}
}
};
}
}
function tokenRevoke(token, adminToken = null) {
function tokenRevoke (token, adminToken = null) {
return {
method: 'DELETE',
matcher: 'http://192.168.99.99/identity_v2_admin/v3/auth/tokens',
@ -411,10 +411,10 @@ function tokenRevoke(token, adminToken = null) {
response: {
status: 204
}
};
}
}
function tokenInfo(token) {
function tokenInfo (token) {
return {
method: 'GET',
matcher: 'http://192.168.99.99/identity_v2_admin/v3/auth/tokens',
@ -427,10 +427,10 @@ function tokenInfo(token) {
token: tokenInfoData
}
}
};
}
}
function catalogList(token) {
function catalogList (token) {
return {
method: 'GET',
matcher: 'http://192.168.99.99/identity_v2_admin/v3/auth/catalog',
@ -440,7 +440,7 @@ function catalogList(token) {
response: {
catalog: catalogListData
}
};
}
}
export {
@ -449,5 +449,5 @@ export {
tokenIssue,
tokenRevoke,
tokenInfo,
catalogList,
};
catalogList
}

View File

@ -25,19 +25,19 @@
* format but a subsection of service endpoint return by keystone's catalog.
*/
const neutronConfig = {
region_id: "RegionOne",
url: "http://192.168.99.99:9696/",
region: "RegionOne",
interface: "public",
id: "0b8b5f0f14904136ab5a4f83f27ec49a"
};
region_id: 'RegionOne',
url: 'http://192.168.99.99:9696/',
region: 'RegionOne',
interface: 'public',
id: '0b8b5f0f14904136ab5a4f83f27ec49a'
}
/**
* Build a new FetchMock configuration for the root endpoint.
*
* @returns {{}} A full FetchMock configuration for Neutron's Root Resource.
*/
function rootResponse() {
function rootResponse () {
return {
method: 'GET',
matcher: 'http://192.168.99.99:9696/',
@ -55,10 +55,10 @@ function rootResponse() {
}
]
}
};
}
}
function networkList(token) {
function networkList (token) {
return {
method: 'GET',
matcher: 'http://192.168.99.99:9696/v2.0/networks',
@ -103,11 +103,11 @@ function networkList(token) {
}
]
}
};
}
}
export {
neutronConfig as config,
rootResponse as root,
networkList
};
}

View File

@ -24,40 +24,40 @@
* A catalog entry that matches what we expect from the Keystone Catalog for nova compute.
*/
const novaConfig = {
region_id: "RegionOne",
url: "http://192.168.99.99:8774/v2.1",
region: "RegionOne",
interface: "public",
id: "be681632633d4a62a781148c2fedd6aa"
};
region_id: 'RegionOne',
url: 'http://192.168.99.99:8774/v2.1',
region: 'RegionOne',
interface: 'public',
id: 'be681632633d4a62a781148c2fedd6aa'
}
/**
* Build a new FetchMock configuration for the root endpoint.
*
* @returns {{}} A full FetchMock configuration for Nova's Root Resource.
*/
function rootResponse() {
function rootResponse () {
return {
method: 'GET',
matcher: 'http://192.168.99.99:8774/',
response: {
versions: [{
status: "CURRENT",
updated: "2013-07-23T11:33:21Z",
links: [{href: "http://192.168.99.99:8774/v2.1/", rel: "self"}],
min_version: "2.1",
version: "2.38",
id: "v2.1"
status: 'CURRENT',
updated: '2013-07-23T11:33:21Z',
links: [{ href: 'http://192.168.99.99:8774/v2.1/', rel: 'self' }],
min_version: '2.1',
version: '2.38',
id: 'v2.1'
}, {
status: "SUPPORTED",
updated: "2011-01-21T11:33:21Z",
links: [{href: "http://192.168.99.99:8774/v2/", rel: "self"}],
min_version: "",
version: "",
id: "v2.0"
status: 'SUPPORTED',
updated: '2011-01-21T11:33:21Z',
links: [{ href: 'http://192.168.99.99:8774/v2/', rel: 'self' }],
min_version: '',
version: '',
id: 'v2.0'
}]
}
};
}
}
/**
@ -66,14 +66,14 @@ function rootResponse() {
* @param {String} version The version ID.
* @return {{}} A FetchMock configuration for this request's response.
*/
function versionedRootResponse(version = 'v2.1') {
function versionedRootResponse (version = 'v2.1') {
return {
method: 'GET',
matcher: `http://192.168.99.99:8774/${version}`,
response: {
status: 401
}
};
}
}
/**
@ -82,7 +82,7 @@ function versionedRootResponse(version = 'v2.1') {
* @param {String} token An auth token.
* @return {{}} A FetchMock configuration for this request's response.
*/
function flavorList(token) {
function flavorList (token) {
return {
method: 'GET',
matcher: 'http://192.168.99.99:8774/v2.1/flavors',
@ -91,92 +91,92 @@ function flavorList(token) {
},
response: {
flavors: [{
id: "1",
id: '1',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/1", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/1", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/1', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/1', rel: 'bookmark' }
],
name: "m1.tiny"
name: 'm1.tiny'
}, {
id: "2",
id: '2',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/2", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/2", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/2', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/2', rel: 'bookmark' }
],
name: "m1.small"
name: 'm1.small'
}, {
id: "3",
id: '3',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/3", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/3", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/3', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/3', rel: 'bookmark' }
],
name: "m1.medium"
name: 'm1.medium'
}, {
id: "4",
id: '4',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/4", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/4", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/4', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/4', rel: 'bookmark' }
],
name: "m1.large"
name: 'm1.large'
}, {
id: "42",
id: '42',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/42", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/42", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/42', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/42', rel: 'bookmark' }
],
name: "m1.nano"
name: 'm1.nano'
}, {
id: "5",
id: '5',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/5", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/5", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/5', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/5', rel: 'bookmark' }
],
name: "m1.xlarge"
name: 'm1.xlarge'
}, {
id: "84",
id: '84',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/84", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/84", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/84', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/84', rel: 'bookmark' }
],
name: "m1.micro"
name: 'm1.micro'
}, {
id: "c1",
id: 'c1',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/c1", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/c1", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/c1', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/c1', rel: 'bookmark' }
],
name: "cirros256"
name: 'cirros256'
}, {
id: "d1",
id: 'd1',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/d1", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/d1", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/d1', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/d1', rel: 'bookmark' }
],
name: "ds512M"
name: 'ds512M'
}, {
id: "d2",
id: 'd2',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/d2", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/d2", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/d2', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/d2', rel: 'bookmark' }
],
name: "ds1G"
name: 'ds1G'
}, {
id: "d3",
id: 'd3',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/d3", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/d3", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/d3', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/d3', rel: 'bookmark' }
],
name: "ds2G"
name: 'ds2G'
}, {
id: "d4",
id: 'd4',
links: [
{href: "http://192.168.99.99:8774/v2.1/flavors/d4", rel: "self"},
{href: "http://192.168.99.99:8774/flavors/d4", rel: "bookmark"}
{ href: 'http://192.168.99.99:8774/v2.1/flavors/d4', rel: 'self' },
{ href: 'http://192.168.99.99:8774/flavors/d4', rel: 'bookmark' }
],
name: "ds4G"
name: 'ds4G'
}]
}
};
}
}
export {
@ -184,4 +184,4 @@ export {
rootResponse as root,
versionedRootResponse as rootVersion,
flavorList
};
}

View File

@ -25,7 +25,7 @@
* @param {String} regionName A region name to use
* @returns {{}} a cloud config object.
*/
function cloudConfig(regionName = 'RegionOne') {
function cloudConfig (regionName = 'RegionOne') {
return {
region_name: regionName,
auth: {
@ -34,9 +34,9 @@ function cloudConfig(regionName = 'RegionOne') {
project_name: 'js-openstack-lib',
auth_url: 'http://192.168.99.99/'
}
};
}
}
export {
cloudConfig as config,
};
cloudConfig as config
}

View File

@ -22,8 +22,8 @@
/**
* URLs to match the test data below.
*/
const rootUrl = "http://example.com/";
const subUrl = `${rootUrl}v1`;
const rootUrl = 'http://example.com/'
const subUrl = `${rootUrl}v1`
/**
* A mock list of supported versions for the below requests.
@ -32,72 +32,72 @@ const subUrl = `${rootUrl}v1`;
*/
const versions = [
'v2.3'
];
]
/**
* Build a new FetchMock configuration for the versions (root) endpoint.
*
* @returns {{}} A full FetchMock configuration for a versions resource.
*/
function rootResponse() {
function rootResponse () {
return {
method: 'GET',
matcher: rootUrl,
response: {
versions: [
{
status: "CURRENT",
id: "v2.3",
status: 'CURRENT',
id: 'v2.3',
links: [
{
href: `${rootUrl}v2/`,
rel: "self"
rel: 'self'
}
]
},
{
status: "SUPPORTED",
id: "v2.2",
status: 'SUPPORTED',
id: 'v2.2',
links: [
{
href: `${rootUrl}v2/`,
rel: "self"
rel: 'self'
}
]
},
{
status: "SUPPORTED",
id: "v2.1",
status: 'SUPPORTED',
id: 'v2.1',
links: [
{
href: `${rootUrl}v2/`,
rel: "self"
rel: 'self'
}
]
},
{
status: "SUPPORTED",
id: "v1.1",
status: 'SUPPORTED',
id: 'v1.1',
links: [
{
href: `${rootUrl}v1/`,
rel: "self"
rel: 'self'
}
]
},
{
status: "SUPPORTED",
id: "v1.0",
status: 'SUPPORTED',
id: 'v1.0',
links: [
{
href: `${rootUrl}v1/`,
rel: "self"
rel: 'self'
}
]
}
]
}
};
}
}
/**
@ -106,14 +106,14 @@ function rootResponse() {
* @param {int} httpStatus The HTTP status for the response.
* @returns {{}} A full FetchMock configuration a failing request..
*/
function subResponse(httpStatus = 401) {
function subResponse (httpStatus = 401) {
return {
method: 'GET',
matcher: subUrl,
response: {
status: httpStatus
}
};
}
}
export {
@ -122,4 +122,4 @@ export {
versions,
rootResponse,
subResponse
};
}

View File

@ -1,332 +1,330 @@
import Keystone from '../../src/keystone.js';
import * as mockData from './helpers/data/keystone';
import fetchMock from 'fetch-mock';
import Keystone from '../../src/keystone.js'
import * as mockData from './helpers/data/keystone'
import fetchMock from 'fetch-mock'
describe('Keystone', () => {
afterEach(fetchMock.restore);
afterEach(fetchMock.restore)
it('should export a class', () => {
const keystone = new Keystone(mockData.config);
expect(keystone).toBeDefined();
});
const keystone = new Keystone(mockData.config)
expect(keystone).toBeDefined()
})
it('should throw an error for an empty config', () => {
expect(() => new Keystone()).toThrow();
});
expect(() => new Keystone()).toThrow()
})
describe("serviceEndpoint()", () => {
it("Should return a valid endpoint to the keystone API.", (done) => {
const keystone = new Keystone(mockData.config);
describe('serviceEndpoint()', () => {
it('Should return a valid endpoint to the keystone API.', (done) => {
const keystone = new Keystone(mockData.config)
fetchMock.mock(mockData.root());
fetchMock.mock(mockData.root())
keystone.serviceEndpoint()
.then((endpoint) => {
expect(endpoint).toEqual('http://192.168.99.99/identity_v2_admin/v3/');
done();
expect(endpoint).toEqual('http://192.168.99.99/identity_v2_admin/v3/')
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("tokenIssue()", () => {
describe('tokenIssue()', () => {
it('should "just work" by using provided credentials from the config.', (done) => {
const mockOptions = mockData.tokenIssue()
fetchMock.mock(mockData.root())
fetchMock.mock(mockOptions)
it("should 'just work' by using provided credentials from the config.", (done) => {
let mockOptions = mockData.tokenIssue();
fetchMock.mock(mockData.root());
fetchMock.mock(mockOptions);
const keystone = new Keystone(mockData.config);
const keystone = new Keystone(mockData.config)
keystone
.tokenIssue()
.then((token) => {
expect(token).toEqual('test_token'); // From mock data
done();
expect(token).toEqual('test_token') // From mock data
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should support authentication with a user ID", (done) => {
let mockOptions = mockData.tokenIssue();
fetchMock.mock(mockData.root());
fetchMock.mock(mockOptions);
it('should support authentication with a user ID', (done) => {
const mockOptions = mockData.tokenIssue()
fetchMock.mock(mockData.root())
fetchMock.mock(mockOptions)
const userId = 'userId';
const userId = 'userId'
const keystone = new Keystone(mockData.config);
const keystone = new Keystone(mockData.config)
keystone
.tokenIssue({
user_id: userId
})
.then(() => {
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
expect(requestBody.auth.identity.password.user.id).toEqual(userId);
done();
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body)
expect(requestBody.auth.identity.password.user.id).toEqual(userId)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should support authentication with a username and a user domain ID", (done) => {
let mockOptions = mockData.tokenIssue();
fetchMock.mock(mockData.root());
fetchMock.mock(mockOptions);
it('should support authentication with a username and a user domain ID', (done) => {
const mockOptions = mockData.tokenIssue()
fetchMock.mock(mockData.root())
fetchMock.mock(mockOptions)
const username = 'username';
const userDomainId = 'userDomainId';
const username = 'username'
const userDomainId = 'userDomainId'
const keystone = new Keystone(mockData.config);
const keystone = new Keystone(mockData.config)
keystone
.tokenIssue({
username: username,
user_domain_id: userDomainId
})
.then(() => {
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
expect(requestBody.auth.identity.password.user.name).toEqual(username);
expect(requestBody.auth.identity.password.user.domain.id).toEqual(userDomainId);
done();
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body)
expect(requestBody.auth.identity.password.user.name).toEqual(username)
expect(requestBody.auth.identity.password.user.domain.id).toEqual(userDomainId)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should support authentication with a username and a user domain name", (done) => {
let mockOptions = mockData.tokenIssue();
fetchMock.mock(mockData.root());
fetchMock.mock(mockOptions);
it('should support authentication with a username and a user domain name', (done) => {
const mockOptions = mockData.tokenIssue()
fetchMock.mock(mockData.root())
fetchMock.mock(mockOptions)
const username = 'username';
const userDomainName = 'userDomainName';
const username = 'username'
const userDomainName = 'userDomainName'
const keystone = new Keystone(mockData.config);
const keystone = new Keystone(mockData.config)
keystone
.tokenIssue({
username: username,
user_domain_name: userDomainName
})
.then(() => {
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
expect(requestBody.auth.identity.password.user.name).toEqual(username);
expect(requestBody.auth.identity.password.user.domain.name).toEqual(userDomainName);
done();
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body)
expect(requestBody.auth.identity.password.user.name).toEqual(username)
expect(requestBody.auth.identity.password.user.domain.name).toEqual(userDomainName)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should support authentication with a project ID", (done) => {
let mockOptions = mockData.tokenIssue();
fetchMock.mock(mockData.root());
fetchMock.mock(mockOptions);
it('should support authentication with a project ID', (done) => {
const mockOptions = mockData.tokenIssue()
fetchMock.mock(mockData.root())
fetchMock.mock(mockOptions)
const projectId = 'projectId';
const projectId = 'projectId'
const keystone = new Keystone(mockData.config);
const keystone = new Keystone(mockData.config)
keystone
.tokenIssue({
project_id: projectId,
project_id: projectId
})
.then(() => {
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
expect(requestBody.auth.scope.project.id).toEqual(projectId);
done();
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body)
expect(requestBody.auth.scope.project.id).toEqual(projectId)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should support authentication with a project name and a project domain ID", (done) => {
let mockOptions = mockData.tokenIssue();
fetchMock.mock(mockData.root());
fetchMock.mock(mockOptions);
it('should support authentication with a project name and a project domain ID', (done) => {
const mockOptions = mockData.tokenIssue()
fetchMock.mock(mockData.root())
fetchMock.mock(mockOptions)
const projectName = 'projectName';
const projectDomainId = 'projectDomainId';
const projectName = 'projectName'
const projectDomainId = 'projectDomainId'
const keystone = new Keystone(mockData.config);
const keystone = new Keystone(mockData.config)
keystone
.tokenIssue({
project_name: projectName,
project_domain_id: projectDomainId
})
.then(() => {
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
expect(requestBody.auth.scope.project.name).toEqual(projectName);
expect(requestBody.auth.scope.project.domain.id).toEqual(projectDomainId);
done();
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body)
expect(requestBody.auth.scope.project.name).toEqual(projectName)
expect(requestBody.auth.scope.project.domain.id).toEqual(projectDomainId)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should support authentication with a project name and a project domain name", (done) => {
let mockOptions = mockData.tokenIssue();
fetchMock.mock(mockData.root());
fetchMock.mock(mockOptions);
it('should support authentication with a project name and a project domain name', (done) => {
const mockOptions = mockData.tokenIssue()
fetchMock.mock(mockData.root())
fetchMock.mock(mockOptions)
const projectName = 'projectName';
const projectDomainName = 'projectDomainName';
const projectName = 'projectName'
const projectDomainName = 'projectDomainName'
const keystone = new Keystone(mockData.config);
const keystone = new Keystone(mockData.config)
keystone
.tokenIssue({
project_name: projectName,
project_domain_name: projectDomainName
})
.then(() => {
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
expect(requestBody.auth.scope.project.name).toEqual(projectName);
expect(requestBody.auth.scope.project.domain.name).toEqual(projectDomainName);
done();
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body)
expect(requestBody.auth.scope.project.name).toEqual(projectName)
expect(requestBody.auth.scope.project.domain.name).toEqual(projectDomainName)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should not cache its results", (done) => {
let mockOptions = mockData.tokenIssue();
fetchMock.mock(mockData.root());
fetchMock.mock(mockOptions);
it('Should not cache its results', (done) => {
const mockOptions = mockData.tokenIssue()
fetchMock.mock(mockData.root())
fetchMock.mock(mockOptions)
const keystone = new Keystone(mockData.config);
const keystone = new Keystone(mockData.config)
keystone
.tokenIssue()
.then((token) => {
expect(token).toEqual('test_token'); // From mock data
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
return keystone.tokenIssue();
expect(token).toEqual('test_token') // From mock data
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
return keystone.tokenIssue()
})
.then((token) => {
expect(token).toEqual('test_token'); // From mock data
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2);
done();
expect(token).toEqual('test_token') // From mock data
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("tokenRevoke()", () => {
let keystone = null;
describe('tokenRevoke()', () => {
let keystone = null
beforeEach(() => {
fetchMock.mock(mockData.root());
keystone = new Keystone(mockData.config);
});
fetchMock.mock(mockData.root())
keystone = new Keystone(mockData.config)
})
it("should return a 204 response on a valid revocation.", (done) => {
const token = 'test_token';
const adminToken = 'test_admin_token';
it('should return a 204 response on a valid revocation.', (done) => {
const token = 'test_token'
const adminToken = 'test_admin_token'
fetchMock.mock(mockData.tokenRevoke(token, adminToken));
fetchMock.mock(mockData.tokenRevoke(token, adminToken))
keystone
.tokenRevoke(token, adminToken)
.then((response) => {
expect(response.status).toEqual(204); // From mock data
done();
expect(response.status).toEqual(204) // From mock data
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should not cache its results", (done) => {
const token = 'test_token';
it('Should not cache its results', (done) => {
const token = 'test_token'
let mockOptions = mockData.tokenRevoke(token);
fetchMock.mock(mockOptions);
const mockOptions = mockData.tokenRevoke(token)
fetchMock.mock(mockOptions)
keystone
.tokenRevoke(token)
.then((response) => {
expect(response.status).toEqual(204);
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
expect(response.status).toEqual(204)
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
// Yes, I realize that this should actually return an error since the token is no
// longer valid, but we're testing for promise caching here, not valid http flow.
return keystone.tokenRevoke(token);
return keystone.tokenRevoke(token)
})
.then((response) => {
expect(response.status).toEqual(204);
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2);
done();
expect(response.status).toEqual(204)
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("tokenInfo()", () => {
let keystone = null;
describe('tokenInfo()', () => {
let keystone = null
beforeEach(() => {
fetchMock.mock(mockData.root());
keystone = new Keystone(mockData.config);
});
fetchMock.mock(mockData.root())
keystone = new Keystone(mockData.config)
})
const token = 'test_token';
const token = 'test_token'
it("should return information about a token", (done) => {
fetchMock.mock(mockData.tokenInfo(token));
it('should return information about a token', (done) => {
fetchMock.mock(mockData.tokenInfo(token))
keystone
.tokenInfo(token)
.then((info) => {
expect(info.token).toBeDefined();
done();
expect(info.token).toBeDefined()
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should not cache its results", (done) => {
let mockOptions = mockData.tokenInfo(token);
fetchMock.mock(mockOptions);
it('Should not cache its results', (done) => {
const mockOptions = mockData.tokenInfo(token)
fetchMock.mock(mockOptions)
keystone
.tokenInfo(token)
.then((info) => {
expect(info.token).toBeDefined();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
return keystone.tokenInfo(token);
expect(info.token).toBeDefined()
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
return keystone.tokenInfo(token)
})
.then((info) => {
expect(info.token).toBeDefined();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2);
done();
expect(info.token).toBeDefined()
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("catalogList()", () => {
let keystone = null;
describe('catalogList()', () => {
let keystone = null
beforeEach(() => {
fetchMock.mock(mockData.root());
keystone = new Keystone(mockData.config);
});
fetchMock.mock(mockData.root())
keystone = new Keystone(mockData.config)
})
it("should return the catalog as an array.", (done) => {
const token = 'test_token';
it('should return the catalog as an array.', (done) => {
const token = 'test_token'
fetchMock.mock(mockData.catalogList(token));
fetchMock.mock(mockData.catalogList(token))
keystone
.catalogList(token)
.then((catalog) => {
expect(catalog.length).not.toBe(0);
done();
expect(catalog.length).not.toBe(0)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should not cache its results", (done) => {
const token = 'test_token';
it('Should not cache its results', (done) => {
const token = 'test_token'
let mockOptions = mockData.catalogList(token);
fetchMock.mock(mockOptions);
const mockOptions = mockData.catalogList(token)
fetchMock.mock(mockOptions)
keystone
.catalogList(token)
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
return keystone.catalogList(token);
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
return keystone.catalogList(token)
})
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2);
done();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2)
done()
})
.catch((error) => done.fail(error));
});
});
});
.catch((error) => done.fail(error))
})
})
})

View File

@ -14,66 +14,65 @@
* under the License.
*/
import Neutron from '../../src/neutron.js';
import * as mockData from './helpers/data/neutron';
import fetchMock from 'fetch-mock';
import Neutron from '../../src/neutron.js'
import * as mockData from './helpers/data/neutron'
import fetchMock from 'fetch-mock'
describe('neutron', () => {
afterEach(fetchMock.restore);
afterEach(fetchMock.restore)
it('should export a class', () => {
const neutron = new Neutron(mockData.config);
expect(neutron).toBeDefined();
});
const neutron = new Neutron(mockData.config)
expect(neutron).toBeDefined()
})
it('should throw an error for an empty config', () => {
try {
const neutron = new Neutron();
neutron.versions();
const neutron = new Neutron()
neutron.versions()
} catch (e) {
expect(e.message).toEqual('An endpoint configuration is required.');
expect(e.message).toEqual('An endpoint configuration is required.')
}
});
})
describe("networkList()", () => {
let neutron = null;
describe('networkList()', () => {
let neutron = null
beforeEach(() => {
fetchMock.mock(mockData.root());
neutron = new Neutron(mockData.config);
});
fetchMock.mock(mockData.root())
neutron = new Neutron(mockData.config)
})
it("should return the networks as an array.", (done) => {
const token = 'test_token';
it('should return the networks as an array.', (done) => {
const token = 'test_token'
fetchMock.mock(mockData.networkList(token));
fetchMock.mock(mockData.networkList(token))
neutron
.networkList(token)
.then((networks) => {
expect(networks.length).toBe(2);
done();
expect(networks.length).toBe(2)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should not cache its results", (done) => {
const token = 'test_token';
it('Should not cache its results', (done) => {
const token = 'test_token'
let mockOptions = mockData.networkList(token);
fetchMock.mock(mockOptions);
const mockOptions = mockData.networkList(token)
fetchMock.mock(mockOptions)
neutron
.networkList(token)
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
return neutron.networkList(token);
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
return neutron.networkList(token)
})
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2);
done();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2)
done()
})
.catch((error) => done.fail(error));
});
});
});
.catch((error) => done.fail(error))
})
})
})

View File

@ -14,62 +14,61 @@
* limitations under the License.
*/
import Nova from "../../src/nova.js";
import * as mockData from "./helpers/data/nova";
import fetchMock from "fetch-mock";
import Nova from '../../src/nova.js'
import * as mockData from './helpers/data/nova'
import fetchMock from 'fetch-mock'
describe('Nova', () => {
afterEach(fetchMock.restore);
afterEach(fetchMock.restore)
it('should export a class', () => {
const nova = new Nova(mockData.config);
expect(nova).toBeDefined();
});
const nova = new Nova(mockData.config)
expect(nova).toBeDefined()
})
it('should throw an error for an empty config', () => {
expect(() => new Nova(null)).toThrow();
});
expect(() => new Nova(null)).toThrow()
})
describe("flavorList()", () => {
let nova = null;
describe('flavorList()', () => {
let nova = null
beforeEach(() => {
fetchMock.mock(mockData.rootVersion());
fetchMock.mock(mockData.root());
nova = new Nova(mockData.config);
});
fetchMock.mock(mockData.rootVersion())
fetchMock.mock(mockData.root())
nova = new Nova(mockData.config)
})
it("should return the flavors as an array.", (done) => {
const token = 'test_token';
it('should return the flavors as an array.', (done) => {
const token = 'test_token'
fetchMock.mock(mockData.flavorList(token));
fetchMock.mock(mockData.flavorList(token))
nova
.flavorList(token)
.then((images) => {
expect(images.length).not.toBe(0);
done();
expect(images.length).not.toBe(0)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should not cache its results", (done) => {
const token = 'test_token';
it('Should not cache its results', (done) => {
const token = 'test_token'
let mockOptions = mockData.flavorList(token);
fetchMock.mock(mockOptions);
const mockOptions = mockData.flavorList(token)
fetchMock.mock(mockOptions)
nova
.flavorList(token)
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
return nova.flavorList(token);
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
return nova.flavorList(token)
})
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2);
done();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2)
done()
})
.catch((error) => done.fail(error));
});
});
});
.catch((error) => done.fail(error))
})
})
})

View File

@ -1,290 +1,288 @@
import OpenStack from "../../src/openstack";
import * as openStackMockData from './helpers/data/openstack';
import * as neutronMockData from './helpers/data/neutron';
import * as keystoneMockData from './helpers/data/keystone';
import * as glanceMockData from './helpers/data/glance';
import * as novaMockData from './helpers/data/nova';
import fetchMock from 'fetch-mock';
import Neutron from "../../src/neutron";
import Keystone from "../../src/keystone";
import Glance from "../../src/glance";
import Nova from "../../src/nova";
import OpenStack from '../../src/openstack'
import * as openStackMockData from './helpers/data/openstack'
import * as neutronMockData from './helpers/data/neutron'
import * as keystoneMockData from './helpers/data/keystone'
import * as glanceMockData from './helpers/data/glance'
import * as novaMockData from './helpers/data/nova'
import fetchMock from 'fetch-mock'
import Neutron from '../../src/neutron'
import Keystone from '../../src/keystone'
import Glance from '../../src/glance'
import Nova from '../../src/nova'
describe("Simple test", () => {
describe('Simple test', () => {
afterEach(fetchMock.restore)
afterEach(fetchMock.restore);
it('should export a class', () => {
const t = new OpenStack(openStackMockData.config())
expect(t).toBeDefined()
})
it("should export a class", () => {
let t = new OpenStack(openStackMockData.config());
expect(t).toBeDefined();
});
it("should throw an error for an empty config", () => {
it('should throw an error for an empty config', () => {
try {
let t = new OpenStack();
t.getConfig();
const t = new OpenStack()
t.getConfig()
} catch (e) {
expect(e.message).toEqual('A configuration is required.');
expect(e.message).toEqual('A configuration is required.')
}
});
})
it("getConfig should returns the config", () => {
let openstack = new OpenStack(openStackMockData.config());
let config = openstack.getConfig();
expect(config.region_name).toEqual('RegionOne');
});
it('getConfig should returns the config', () => {
const openstack = new OpenStack(openStackMockData.config())
const config = openstack.getConfig()
expect(config.region_name).toEqual('RegionOne')
})
describe('networkList', () => {
it('should fetch networkList from neutron', (done) => {
const openstack = new OpenStack(openStackMockData.config());
const neutron = mockNeutron(openstack);
const networksData = neutronMockData.networkList('token').response.networks;
const openstack = new OpenStack(openStackMockData.config())
const neutron = mockNeutron(openstack)
const networksData = neutronMockData.networkList('token').response.networks
spyOn(neutron, 'networkList').and.returnValue(Promise.resolve(networksData));
spyOn(neutron, 'networkList').and.returnValue(Promise.resolve(networksData))
openstack.networkList()
.then((networks) => {
expect(networks.length).toBe(2);
done();
expect(networks.length).toBe(2)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe('imageList', () => {
it('should fetch imageList from glance', (done) => {
const openstack = new OpenStack(openStackMockData.config());
const glance = mockGlance(openstack);
const imagesData = glanceMockData.imageList('token').response.images;
const openstack = new OpenStack(openStackMockData.config())
const glance = mockGlance(openstack)
const imagesData = glanceMockData.imageList('token').response.images
spyOn(glance, 'imageList').and.returnValue(Promise.resolve(imagesData));
spyOn(glance, 'imageList').and.returnValue(Promise.resolve(imagesData))
openstack.imageList()
.then((images) => {
expect(images.length).toBe(3);
done();
expect(images.length).toBe(3)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe('flavorList', () => {
it('should fetch flavorList from nova', (done) => {
const openstack = new OpenStack(openStackMockData.config());
const nova = mockNova(openstack);
const flavorsData = novaMockData.flavorList('token').response.flavors;
const openstack = new OpenStack(openStackMockData.config())
const nova = mockNova(openstack)
const flavorsData = novaMockData.flavorList('token').response.flavors
spyOn(nova, 'flavorList').and.returnValue(Promise.resolve(flavorsData));
spyOn(nova, 'flavorList').and.returnValue(Promise.resolve(flavorsData))
openstack.flavorList()
.then((flavors) => {
expect(flavors.length).toBe(12);
done();
expect(flavors.length).toBe(12)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe('_neutron', () => {
it('creates Neutron instance with the correct endpoint', (done) => {
const token = 'test_token';
const openstack = new OpenStack(openStackMockData.config());
const keystone = mockKeystone(openstack);
const catalogData = keystoneMockData.catalogList(token).response.catalog;
const token = 'test_token'
const openstack = new OpenStack(openStackMockData.config())
const keystone = mockKeystone(openstack)
const catalogData = keystoneMockData.catalogList(token).response.catalog
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve(token));
spyOn(keystone, 'catalogList').and.returnValue(Promise.resolve(catalogData));
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve(token))
spyOn(keystone, 'catalogList').and.returnValue(Promise.resolve(catalogData))
openstack._neutron
.then((neutron) => {
expect(keystone.catalogList).toHaveBeenCalledWith(token);
expect(neutron).toEqual(jasmine.any(Neutron));
expect(neutron.endpointUrl).toEqual('http://192.168.99.99:9696/');
done();
expect(keystone.catalogList).toHaveBeenCalledWith(token)
expect(neutron).toEqual(jasmine.any(Neutron))
expect(neutron.endpointUrl).toEqual('http://192.168.99.99:9696/')
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it('creates Neutron instance for the correct endpoint', (done) => {
const token = 'test_token';
const openstack = new OpenStack(openStackMockData.config('RegionTwo'));
const keystone = mockKeystone(openstack);
const catalogData = keystoneMockData.catalogList(token).response.catalog;
const token = 'test_token'
const openstack = new OpenStack(openStackMockData.config('RegionTwo'))
const keystone = mockKeystone(openstack)
const catalogData = keystoneMockData.catalogList(token).response.catalog
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve(token));
spyOn(keystone, 'catalogList').and.returnValue(Promise.resolve(catalogData));
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve(token))
spyOn(keystone, 'catalogList').and.returnValue(Promise.resolve(catalogData))
openstack._neutron
.then((neutron) => {
expect(neutron).toEqual(jasmine.any(Neutron));
expect(neutron.endpointUrl).toEqual('http://192.168.99.100:9696/');
done();
expect(neutron).toEqual(jasmine.any(Neutron))
expect(neutron.endpointUrl).toEqual('http://192.168.99.100:9696/')
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it('should cache Neutron instance and Keystone token', (done) => {
const openstack = new OpenStack(openStackMockData.config());
const tokenIssueMock = keystoneMockData.tokenIssue();
const catalogListMock = keystoneMockData.catalogList('test_token');
const openstack = new OpenStack(openStackMockData.config())
const tokenIssueMock = keystoneMockData.tokenIssue()
const catalogListMock = keystoneMockData.catalogList('test_token')
fetchMock.mock(keystoneMockData.root());
fetchMock.mock(tokenIssueMock);
fetchMock.mock(catalogListMock);
fetchMock.mock(keystoneMockData.root())
fetchMock.mock(tokenIssueMock)
fetchMock.mock(catalogListMock)
openstack._neutron
.then((neutron) => {
expect(neutron).toEqual(jasmine.any(Neutron));
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1);
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1);
return openstack._neutron;
expect(neutron).toEqual(jasmine.any(Neutron))
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1)
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1)
return openstack._neutron
})
.then((neutron) => {
expect(neutron).toEqual(jasmine.any(Neutron));
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1);
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1);
done();
expect(neutron).toEqual(jasmine.any(Neutron))
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1)
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe('_glance', () => {
it('creates Glance instance with the correct endpoint', (done) => {
const token = 'test_token';
const openstack = new OpenStack(openStackMockData.config());
const keystone = mockKeystone(openstack);
const catalogData = keystoneMockData.catalogList(token).response.catalog;
const token = 'test_token'
const openstack = new OpenStack(openStackMockData.config())
const keystone = mockKeystone(openstack)
const catalogData = keystoneMockData.catalogList(token).response.catalog
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve(token));
spyOn(keystone, 'catalogList').and.returnValue(Promise.resolve(catalogData));
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve(token))
spyOn(keystone, 'catalogList').and.returnValue(Promise.resolve(catalogData))
openstack._glance
.then((glance) => {
expect(keystone.catalogList).toHaveBeenCalledWith(token);
expect(glance).toEqual(jasmine.any(Glance));
expect(glance.endpointUrl).toEqual('http://192.168.99.99:9292');
done();
expect(keystone.catalogList).toHaveBeenCalledWith(token)
expect(glance).toEqual(jasmine.any(Glance))
expect(glance.endpointUrl).toEqual('http://192.168.99.99:9292')
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it('should cache Glance instance and Keystone token', (done) => {
const openstack = new OpenStack(openStackMockData.config());
const tokenIssueMock = keystoneMockData.tokenIssue();
const catalogListMock = keystoneMockData.catalogList('test_token');
const openstack = new OpenStack(openStackMockData.config())
const tokenIssueMock = keystoneMockData.tokenIssue()
const catalogListMock = keystoneMockData.catalogList('test_token')
fetchMock.mock(keystoneMockData.root());
fetchMock.mock(tokenIssueMock);
fetchMock.mock(catalogListMock);
fetchMock.mock(keystoneMockData.root())
fetchMock.mock(tokenIssueMock)
fetchMock.mock(catalogListMock)
openstack._glance
.then((glance) => {
expect(glance).toEqual(jasmine.any(Glance));
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1);
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1);
return openstack._glance;
expect(glance).toEqual(jasmine.any(Glance))
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1)
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1)
return openstack._glance
})
.then((glance) => {
expect(glance).toEqual(jasmine.any(Glance));
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1);
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1);
done();
expect(glance).toEqual(jasmine.any(Glance))
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1)
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe('_nova', () => {
it('creates Nova instance with the correct endpoint', (done) => {
const token = 'test_token';
const openstack = new OpenStack(openStackMockData.config());
const keystone = mockKeystone(openstack);
const catalogData = keystoneMockData.catalogList(token).response.catalog;
const token = 'test_token'
const openstack = new OpenStack(openStackMockData.config())
const keystone = mockKeystone(openstack)
const catalogData = keystoneMockData.catalogList(token).response.catalog
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve(token));
spyOn(keystone, 'catalogList').and.returnValue(Promise.resolve(catalogData));
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve(token))
spyOn(keystone, 'catalogList').and.returnValue(Promise.resolve(catalogData))
openstack._nova
.then((nova) => {
expect(keystone.catalogList).toHaveBeenCalledWith(token);
expect(nova).toEqual(jasmine.any(Nova));
expect(nova.endpointUrl).toEqual('http://192.168.99.99:8774/v2.1');
done();
expect(keystone.catalogList).toHaveBeenCalledWith(token)
expect(nova).toEqual(jasmine.any(Nova))
expect(nova.endpointUrl).toEqual('http://192.168.99.99:8774/v2.1')
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it('should cache Nova instance and Keystone token', (done) => {
const openstack = new OpenStack(openStackMockData.config());
const tokenIssueMock = keystoneMockData.tokenIssue();
const catalogListMock = keystoneMockData.catalogList('test_token');
const openstack = new OpenStack(openStackMockData.config())
const tokenIssueMock = keystoneMockData.tokenIssue()
const catalogListMock = keystoneMockData.catalogList('test_token')
fetchMock.mock(keystoneMockData.root());
fetchMock.mock(tokenIssueMock);
fetchMock.mock(catalogListMock);
fetchMock.mock(keystoneMockData.root())
fetchMock.mock(tokenIssueMock)
fetchMock.mock(catalogListMock)
openstack._nova
.then((nova) => {
expect(nova).toEqual(jasmine.any(Nova));
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1);
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1);
return openstack._nova;
expect(nova).toEqual(jasmine.any(Nova))
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1)
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1)
return openstack._nova
})
.then((nova) => {
expect(nova).toEqual(jasmine.any(Nova));
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1);
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1);
done();
expect(nova).toEqual(jasmine.any(Nova))
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1)
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe('_token', () => {
it('should fetch the token and cache it', (done) => {
const openstack = new OpenStack(openStackMockData.config());
const keystone = mockKeystone(openstack);
const openstack = new OpenStack(openStackMockData.config())
const keystone = mockKeystone(openstack)
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve('test_token'));
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve('test_token'))
openstack._token
.then((token) => {
expect(token).toEqual('test_token');
expect(keystone.tokenIssue.calls.count()).toEqual(1);
return openstack._token;
expect(token).toEqual('test_token')
expect(keystone.tokenIssue.calls.count()).toEqual(1)
return openstack._token
})
.then((token) => {
expect(token).toEqual('test_token');
expect(keystone.tokenIssue.calls.count()).toEqual(1);
done();
expect(token).toEqual('test_token')
expect(keystone.tokenIssue.calls.count()).toEqual(1)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
function mockKeystone(openstack) {
const keystone = new Keystone(keystoneMockData.config);
openstack._keystonePromise = Promise.resolve(keystone);
return keystone;
function mockKeystone (openstack) {
const keystone = new Keystone(keystoneMockData.config)
openstack._keystonePromise = Promise.resolve(keystone)
return keystone
}
function mockNeutron(openstack) {
const neutron = new Neutron(neutronMockData.config);
openstack._neutronPromise = Promise.resolve(neutron);
return neutron;
function mockNeutron (openstack) {
const neutron = new Neutron(neutronMockData.config)
openstack._neutronPromise = Promise.resolve(neutron)
return neutron
}
function mockGlance(openstack) {
const glance = new Glance(glanceMockData.config);
openstack._glancePromise = Promise.resolve(glance);
return glance;
function mockGlance (openstack) {
const glance = new Glance(glanceMockData.config)
openstack._glancePromise = Promise.resolve(glance)
return glance
}
function mockNova(openstack) {
const nova = new Nova(novaMockData.config);
openstack._novaPromise = Promise.resolve(nova);
return nova;
function mockNova (openstack) {
const nova = new Nova(novaMockData.config)
openstack._novaPromise = Promise.resolve(nova)
return nova
}
});
})

View File

@ -1,5 +1,5 @@
import Jasmine from 'jasmine';
import Jasmine from 'jasmine'
const jasmine = new Jasmine();
jasmine.loadConfigFile('test/unit/jasmine.json');
jasmine.execute();
const jasmine = new Jasmine()
jasmine.loadConfigFile('test/unit/jasmine.json')
jasmine.execute()

View File

@ -14,235 +14,231 @@
* limitations under the License.
*/
import AbstractService from "../../../src/util/abstractService";
import * as mockData from "../helpers/data/versions";
import fetchMock from "fetch-mock"; // Might as well use service
import AbstractService from '../../../src/util/abstractService'
import * as mockData from '../helpers/data/versions'
import fetchMock from 'fetch-mock' // Might as well use service
describe('AbstractService', () => {
afterEach(fetchMock.restore);
afterEach(fetchMock.restore)
it('should provide a singleton HTTP instance', () => {
let service = new AbstractService(mockData.rootUrl, mockData.versions);
const service = new AbstractService(mockData.rootUrl, mockData.versions)
expect(service._http).toBeUndefined();
let http1 = service.http;
expect(service._http).toBeDefined();
expect(http1).not.toBeNull();
let http2 = service.http;
expect(http1).toBe(http2);
});
expect(service._http).toBeUndefined()
const http1 = service.http
expect(service._http).toBeDefined()
expect(http1).not.toBeNull()
const http2 = service.http
expect(http1).toBe(http2)
})
it('should return supported versions', () => {
let service = new AbstractService(mockData.rootUrl, mockData.versions);
expect(service.supportedVersions).toEqual(mockData.versions);
});
const service = new AbstractService(mockData.rootUrl, mockData.versions)
expect(service.supportedVersions).toEqual(mockData.versions)
})
it('should return an empty array if no versions are configured', () => {
let service = new AbstractService(mockData.rootUrl, null);
expect(service.supportedVersions).toEqual([]);
});
const service = new AbstractService(mockData.rootUrl, null)
expect(service.supportedVersions).toEqual([])
})
describe("versions()", () => {
describe('versions()', () => {
it('Should return a list of all versions available from this resource', (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions)
it("Should return a list of all versions available from this resource", (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions);
fetchMock.mock(mockData.rootResponse());
fetchMock.mock(mockData.rootResponse())
service.versions()
.then((versions) => {
// Quick sanity check.
expect(versions.length).toBe(5);
expect(versions[0].major).toEqual(2);
expect(versions[0].minor).toEqual(3);
expect(versions[0].patch).toEqual(0);
expect(versions[0].links).not.toBe(null);
expect(versions[0].links[0].href).toEqual('http://example.com/v2/');
done();
expect(versions.length).toBe(5)
expect(versions[0].major).toEqual(2)
expect(versions[0].minor).toEqual(3)
expect(versions[0].patch).toEqual(0)
expect(versions[0].links).not.toBe(null)
expect(versions[0].links[0].href).toEqual('http://example.com/v2/')
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
// This test catches the case when the service catalog already points
// at an API version.
it("Should retry at the root URL if a 401 is encountered", (done) => {
const service = new AbstractService(mockData.subUrl, mockData.versions);
it('Should retry at the root URL if a 401 is encountered', (done) => {
const service = new AbstractService(mockData.subUrl, mockData.versions)
fetchMock.mock(mockData.subResponse());
fetchMock.mock(mockData.rootResponse());
fetchMock.mock(mockData.subResponse())
fetchMock.mock(mockData.rootResponse())
service.versions()
.then((versions) => {
// Quick sanity check.
expect(versions.length).toBe(5);
done();
expect(versions.length).toBe(5)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should not retry at the root URL if a different status is encountered", (done) => {
const service = new AbstractService(mockData.subUrl, mockData.versions);
it('Should not retry at the root URL if a different status is encountered', (done) => {
const service = new AbstractService(mockData.subUrl, mockData.versions)
fetchMock.mock(mockData.subResponse(500));
fetchMock.mock(mockData.subResponse(500))
service.versions()
.then((response) => done.fail(response))
.catch((error) => {
expect(error).not.toBeNull();
done();
});
});
expect(error).not.toBeNull()
done()
})
})
it("Should NOT cache its results", (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions);
const mockOptions = mockData.rootResponse();
it('Should NOT cache its results', (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions)
const mockOptions = mockData.rootResponse()
fetchMock.mock(mockOptions);
fetchMock.mock(mockOptions)
service.versions()
.then(() => {
// Validate that the mock has only been invoked once
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
return service.versions();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
return service.versions()
})
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2);
done();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("version()", () => {
describe('version()', () => {
it('Should return a supported version of the service API.', (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions)
it("Should return a supported version of the service API.", (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions);
fetchMock.mock(mockData.rootResponse());
fetchMock.mock(mockData.rootResponse())
service.version()
.then((version) => {
expect(version.equals('v2.3')).toBe(true);
done();
expect(version.equals('v2.3')).toBe(true)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should return the latest compatible version of the service API.", (done) => {
it('Should return the latest compatible version of the service API.', (done) => {
const service = new AbstractService(mockData.rootUrl, [
'v2.0'
]);
])
fetchMock.mock(mockData.rootResponse());
fetchMock.mock(mockData.rootResponse())
service.version()
.then((version) => {
expect(version.equals('v2.3')).toBe(true);
done();
expect(version.equals('v2.3')).toBe(true)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should throw an exception if no supported version is found.", (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions);
it('Should throw an exception if no supported version is found.', (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions)
// Build an invalid mock object.
const mockOptions = mockData.rootResponse();
mockOptions.response.versions.shift();
const mockOptions = mockData.rootResponse()
mockOptions.response.versions.shift()
fetchMock.mock(mockOptions);
fetchMock.mock(mockOptions)
service.version()
.then((response) => done.fail(response))
.catch((error) => {
expect(error).not.toBeNull();
done();
});
});
expect(error).not.toBeNull()
done()
})
})
it("Should NOT cache its results", (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions);
const mockOptions = mockData.rootResponse();
fetchMock.mock(mockOptions);
it('Should NOT cache its results', (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions)
const mockOptions = mockData.rootResponse()
fetchMock.mock(mockOptions)
service.version()
.then(() => {
// Validate that the mock has only been invoked once
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
return service.version();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
return service.version()
})
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2);
done();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2)
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("serviceEndpoint()", () => {
describe('serviceEndpoint()', () => {
it('Should return a valid endpoint to the API.', (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions)
it("Should return a valid endpoint to the API.", (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions);
fetchMock.mock(mockData.rootResponse());
fetchMock.mock(mockData.rootResponse())
service.serviceEndpoint()
.then((endpoint) => {
expect(endpoint).toEqual('http://example.com/v2/');
done();
expect(endpoint).toEqual('http://example.com/v2/')
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("Should throw an exception if no endpoint is provided.", (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions);
it('Should throw an exception if no endpoint is provided.', (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions)
// Build an exception payload.
const mockOptions = mockData.rootResponse();
mockOptions.response.versions[0].links = [];
fetchMock.mock(mockOptions);
const mockOptions = mockData.rootResponse()
mockOptions.response.versions[0].links = []
fetchMock.mock(mockOptions)
service.serviceEndpoint()
.then((response) => done.fail(response))
.catch((error) => {
expect(error).not.toBeNull();
done();
});
});
expect(error).not.toBeNull()
done()
})
})
it("Should throw an exception if no links array exists.", (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions);
it('Should throw an exception if no links array exists.', (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions)
// Build an exception payload.
const mockOptions = mockData.rootResponse();
delete mockOptions.response.versions[0].links;
fetchMock.mock(mockOptions);
const mockOptions = mockData.rootResponse()
delete mockOptions.response.versions[0].links
fetchMock.mock(mockOptions)
service.serviceEndpoint()
.then((response) => done.fail(response))
.catch((error) => {
expect(error).not.toBeNull();
done();
});
});
expect(error).not.toBeNull()
done()
})
})
it("Should cache its results", (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions);
const mockOptions = mockData.rootResponse();
fetchMock.mock(mockOptions);
it('Should cache its results', (done) => {
const service = new AbstractService(mockData.rootUrl, mockData.versions)
const mockOptions = mockData.rootResponse()
fetchMock.mock(mockOptions)
service.serviceEndpoint()
.then(() => {
// Validate that the mock has only been invoked once
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
return service.serviceEndpoint();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
return service.serviceEndpoint()
})
.then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1);
done();
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
done()
})
.catch((error) => done.fail(error));
});
});
});
.catch((error) => done.fail(error))
})
})
})

View File

@ -14,122 +14,122 @@
* under the License.
*/
import Http from '../../../src/util/http.js';
import fetchMock from 'fetch-mock';
import Http from '../../../src/util/http.js'
import fetchMock from 'fetch-mock'
describe('Http', () => {
let http;
const testUrl = 'https://example.com/';
const testRequest = {lol: 'cat'};
const testResponse = {foo: 'bar'};
let http
const testUrl = 'https://example.com/'
const testRequest = { lol: 'cat' }
const testResponse = { foo: 'bar' }
beforeEach(() => {
http = new Http();
});
http = new Http()
})
afterEach(fetchMock.restore);
afterEach(fetchMock.restore)
it("should permit manually constructing requests", (done) => {
fetchMock.get(testUrl, testResponse);
it('should permit manually constructing requests', (done) => {
fetchMock.get(testUrl, testResponse)
http.httpRequest('GET', testUrl)
.then((response) => response.json())
.then((body) => {
expect(fetchMock.called(testUrl)).toBe(true);
expect(body).toEqual(testResponse);
done();
expect(fetchMock.called(testUrl)).toBe(true)
expect(body).toEqual(testResponse)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should make GET requests", (done) => {
fetchMock.get(testUrl, testResponse);
it('should make GET requests', (done) => {
fetchMock.get(testUrl, testResponse)
http.httpGet(testUrl)
.then((response) => response.json())
.then((body) => {
expect(fetchMock.called(testUrl)).toBe(true);
expect(body).toEqual(testResponse);
done();
expect(fetchMock.called(testUrl)).toBe(true)
expect(body).toEqual(testResponse)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should make PUT requests", (done) => {
fetchMock.put(testUrl, testResponse, testRequest);
it('should make PUT requests', (done) => {
fetchMock.put(testUrl, testResponse, testRequest)
http.httpPut(testUrl, testRequest)
.then((response) => response.json())
.then((body) => {
expect(fetchMock.called(testUrl)).toEqual(true);
expect(body).toEqual(testResponse);
done();
expect(fetchMock.called(testUrl)).toEqual(true)
expect(body).toEqual(testResponse)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should make POST requests", (done) => {
fetchMock.post(testUrl, testResponse, testRequest);
it('should make POST requests', (done) => {
fetchMock.post(testUrl, testResponse, testRequest)
http.httpPost(testUrl, testRequest)
.then((response) => response.json())
.then((body) => {
expect(fetchMock.called(testUrl)).toEqual(true);
expect(body).toEqual(testResponse);
done();
expect(fetchMock.called(testUrl)).toEqual(true)
expect(body).toEqual(testResponse)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should make DELETE requests", (done) => {
fetchMock.delete(testUrl, testRequest);
it('should make DELETE requests', (done) => {
fetchMock.delete(testUrl, testRequest)
http.httpDelete(testUrl, testRequest)
.then(() => {
expect(fetchMock.called(testUrl)).toEqual(true);
done();
expect(fetchMock.called(testUrl)).toEqual(true)
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should permit setting default headers", (done) => {
http.defaultHeaders['Custom-Header'] = 'Custom-Value';
fetchMock.get(testUrl, testResponse);
it('should permit setting default headers', (done) => {
http.defaultHeaders['Custom-Header'] = 'Custom-Value'
fetchMock.get(testUrl, testResponse)
http.httpGet(testUrl)
.then(() => {
let headers = fetchMock.lastOptions().headers;
expect(headers['Custom-Header']).toEqual('Custom-Value');
done();
const headers = fetchMock.lastOptions().headers
expect(headers['Custom-Header']).toEqual('Custom-Value')
done()
})
.catch((error) => done.fail(error));
});
.catch((error) => done.fail(error))
})
it("should pass exceptions back to the invoker", (done) => {
it('should pass exceptions back to the invoker', (done) => {
fetchMock.get(testUrl, () => {
throw new TypeError(); // Example- net::ERR_NAME_NOT_RESOLVED
});
throw new TypeError() // Example- net::ERR_NAME_NOT_RESOLVED
})
http.httpGet(testUrl)
.then((response) => done.fail(response))
.catch((error) => {
expect(error.stack).toBeDefined();
done();
});
});
expect(error.stack).toBeDefined()
done()
})
})
it("should pass failed requests to the catch block.", (done) => {
fetchMock.get(testUrl, {status: 500, body: testResponse});
it('should pass failed requests to the catch block.', (done) => {
fetchMock.get(testUrl, { status: 500, body: testResponse })
http.httpGet(testUrl)
.then((response) => done.fail(response))
.catch((response) => {
expect(response.status).toBe(500);
done();
});
});
expect(response.status).toBe(500)
done()
})
})
it("should not interfere with mocks that have matching headers.", (done) => {
it('should not interfere with mocks that have matching headers.', (done) => {
fetchMock.mock({
method: 'GET',
matcher: testUrl,
@ -137,11 +137,11 @@ describe('Http', () => {
'Content-Type': 'application/json'
},
response: testResponse
});
})
http
.httpRequest('GET', testUrl, {'Content-Type': 'application/json'})
.httpRequest('GET', testUrl, { 'Content-Type': 'application/json' })
.then(() => done())
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})

View File

@ -14,145 +14,143 @@
* under the License.
*/
import Version from '../../../src/util/version.js';
import Version from '../../../src/util/version.js'
describe('Version', () => {
it('should parse various header versions', () => {
const testVersion = function (args, results) {
const v = new Version(...args)
expect(v.service).toBe(results[0])
expect(v.major).toBe(results[1])
expect(v.minor).toBe(results[2])
expect(v.patch).toBe(results[3])
}
it("should parse various header versions", () => {
const testVersion = function(args, results) {
const v = new Version(...args);
expect(v.service).toBe(results[0]);
expect(v.major).toBe(results[1]);
expect(v.minor).toBe(results[2]);
expect(v.patch).toBe(results[3]);
};
testVersion(['identity 1.2'], ['identity', 1, 2, 0]);
testVersion(['identity 0.2'], ['identity', 0, 2, 0]);
testVersion(['compute 2222222.09'], ['compute', 2222222, 9, 0]);
testVersion(['compute 03.09'], ['compute', 3, 9, 0]);
testVersion(['compute 03.0'], ['compute', 3, 0, 0]);
testVersion(['compute 1'], ['compute', 1, 0, 0]);
testVersion(['compute 0'], ['compute', 0, 0, 0]);
testVersion(['v2.1.1'], [null, 2, 1, 1]);
testVersion(['v2.1'], [null, 2, 1, 0]);
testVersion(['v2'], [null, 2, 0, 0]);
testVersion(['v'], [null, 0, 0, 0]);
testVersion(['v0.2'], [null, 0, 2, 0]);
testVersion(['2.1.1'], [null, 2, 1, 1]);
testVersion(['2.1'], [null, 2, 1, 0]);
testVersion(['2'], [null, 2, 0, 0]);
testVersion([''], [null, 0, 0, 0]);
testVersion(['0.2'], [null, 0, 2, 0]);
testVersion(['compute', 'v2.1.1'], ['compute', 2, 1, 1]);
testVersion(['compute', 'v2.1'], ['compute', 2, 1, 0]);
testVersion(['compute', 'v2'], ['compute', 2, 0, 0]);
testVersion(['compute', 'v'], ['compute', 0, 0, 0]);
testVersion(['compute', 'v0.2'], ['compute', 0, 2, 0]);
testVersion(['compute', '2.1.1'], ['compute', 2, 1, 1]);
testVersion(['compute', '2.1'], ['compute', 2, 1, 0]);
testVersion(['compute', '2'], ['compute', 2, 0, 0]);
testVersion(['compute', ''], ['compute', 0, 0, 0]);
testVersion(['compute', '0.2'], ['compute', 0, 2, 0]);
testVersion(['identity 1.2'], ['identity', 1, 2, 0])
testVersion(['identity 0.2'], ['identity', 0, 2, 0])
testVersion(['compute 2222222.09'], ['compute', 2222222, 9, 0])
testVersion(['compute 03.09'], ['compute', 3, 9, 0])
testVersion(['compute 03.0'], ['compute', 3, 0, 0])
testVersion(['compute 1'], ['compute', 1, 0, 0])
testVersion(['compute 0'], ['compute', 0, 0, 0])
testVersion(['v2.1.1'], [null, 2, 1, 1])
testVersion(['v2.1'], [null, 2, 1, 0])
testVersion(['v2'], [null, 2, 0, 0])
testVersion(['v'], [null, 0, 0, 0])
testVersion(['v0.2'], [null, 0, 2, 0])
testVersion(['2.1.1'], [null, 2, 1, 1])
testVersion(['2.1'], [null, 2, 1, 0])
testVersion(['2'], [null, 2, 0, 0])
testVersion([''], [null, 0, 0, 0])
testVersion(['0.2'], [null, 0, 2, 0])
testVersion(['compute', 'v2.1.1'], ['compute', 2, 1, 1])
testVersion(['compute', 'v2.1'], ['compute', 2, 1, 0])
testVersion(['compute', 'v2'], ['compute', 2, 0, 0])
testVersion(['compute', 'v'], ['compute', 0, 0, 0])
testVersion(['compute', 'v0.2'], ['compute', 0, 2, 0])
testVersion(['compute', '2.1.1'], ['compute', 2, 1, 1])
testVersion(['compute', '2.1'], ['compute', 2, 1, 0])
testVersion(['compute', '2'], ['compute', 2, 0, 0])
testVersion(['compute', ''], ['compute', 0, 0, 0])
testVersion(['compute', '0.2'], ['compute', 0, 2, 0])
// Invalid inputs...
testVersion([null, null], [null, 0, 0, 0]);
testVersion([{}, {}], [null, 0, 0, 0]);
testVersion([null, {}], [null, 0, 0, 0]);
testVersion([{}, null], [null, 0, 0, 0]);
});
testVersion([null, null], [null, 0, 0, 0])
testVersion([{}, {}], [null, 0, 0, 0])
testVersion([null, {}], [null, 0, 0, 0])
testVersion([{}, null], [null, 0, 0, 0])
})
it("should test for correct equality", () => {
const v1 = new Version("compute", "1.0.0");
it('should test for correct equality', () => {
const v1 = new Version('compute', '1.0.0')
// String tests...
expect(v1.equals("compute 1.0.0")).toBe(true);
expect(v1.equals("compute 1.0.1")).toBe(false);
expect(v1.equals("identity 1.0.0")).toBe(false);
expect(v1.equals('compute 1.0.0')).toBe(true)
expect(v1.equals('compute 1.0.1')).toBe(false)
expect(v1.equals('identity 1.0.0')).toBe(false)
// Version tests
expect(v1.equals(new Version("compute 1.0.0"))).toBe(true);
expect(v1.equals(new Version("compute 1.0.1"))).toBe(false);
expect(v1.equals(new Version("identity 1.0.0"))).toBe(false);
expect(v1.equals(new Version("1.0.0"))).toBe(false);
expect(v1.equals(new Version('compute 1.0.0'))).toBe(true)
expect(v1.equals(new Version('compute 1.0.1'))).toBe(false)
expect(v1.equals(new Version('identity 1.0.0'))).toBe(false)
expect(v1.equals(new Version('1.0.0'))).toBe(false)
// Other tests...
expect(v1.equals({})).toBe(false);
expect(v1.equals({})).toBe(false)
const v2 = new Version("1.0.0");
const v2 = new Version('1.0.0')
// String tests...
expect(v2.equals("compute 1.0.0")).toBe(false);
expect(v2.equals("compute 1.0.1")).toBe(false);
expect(v2.equals("1.0.0")).toBe(true);
expect(v2.equals('compute 1.0.0')).toBe(false)
expect(v2.equals('compute 1.0.1')).toBe(false)
expect(v2.equals('1.0.0')).toBe(true)
// Version tests
expect(v2.equals(new Version("compute 1.0.0"))).toBe(false);
expect(v2.equals(new Version("compute 1.0.1"))).toBe(false);
expect(v2.equals(new Version("identity 1.0.0"))).toBe(false);
expect(v2.equals(new Version("1.0.0"))).toBe(true);
expect(v2.equals(new Version('compute 1.0.0'))).toBe(false)
expect(v2.equals(new Version('compute 1.0.1'))).toBe(false)
expect(v2.equals(new Version('identity 1.0.0'))).toBe(false)
expect(v2.equals(new Version('1.0.0'))).toBe(true)
// Other tests...
expect(v2.equals({})).toBe(false);
});
expect(v2.equals({})).toBe(false)
})
it("should test for correct compatibility", () => {
const v1 = new Version("compute", "1.3.2");
it('should test for correct compatibility', () => {
const v1 = new Version('compute', '1.3.2')
// String tests
expect(v1.supports("compute 1.0.0")).toBe(true);
expect(v1.supports("compute 1.0.1")).toBe(true);
expect(v1.supports("compute 1.3.0")).toBe(true);
expect(v1.supports("compute 1.3.3")).toBe(false);
expect(v1.supports("compute 1.4.0")).toBe(false);
expect(v1.supports("compute 2.3.0")).toBe(false);
expect(v1.supports('compute 1.0.0')).toBe(true)
expect(v1.supports('compute 1.0.1')).toBe(true)
expect(v1.supports('compute 1.3.0')).toBe(true)
expect(v1.supports('compute 1.3.3')).toBe(false)
expect(v1.supports('compute 1.4.0')).toBe(false)
expect(v1.supports('compute 2.3.0')).toBe(false)
// Version tests
expect(v1.supports(new Version("compute", "1.0.0"))).toBe(true);
expect(v1.supports(new Version("compute", "1.0.1"))).toBe(true);
expect(v1.supports(new Version("compute", "1.3.0"))).toBe(true);
expect(v1.supports(new Version("compute", "1.3.3"))).toBe(false);
expect(v1.supports(new Version("compute", "1.4.0"))).toBe(false);
expect(v1.supports(new Version("compute", "2.3.0"))).toBe(false);
expect(v1.supports(new Version('compute', '1.0.0'))).toBe(true)
expect(v1.supports(new Version('compute', '1.0.1'))).toBe(true)
expect(v1.supports(new Version('compute', '1.3.0'))).toBe(true)
expect(v1.supports(new Version('compute', '1.3.3'))).toBe(false)
expect(v1.supports(new Version('compute', '1.4.0'))).toBe(false)
expect(v1.supports(new Version('compute', '2.3.0'))).toBe(false)
const v2 = new Version("1.3.2");
const v2 = new Version('1.3.2')
// String tests
expect(v2.supports("1.0.0")).toBe(true);
expect(v2.supports("1.0.1")).toBe(true);
expect(v2.supports("1.3.0")).toBe(true);
expect(v2.supports("1.3.3")).toBe(false);
expect(v2.supports("1.4.0")).toBe(false);
expect(v2.supports("2.3.0")).toBe(false);
expect(v2.supports('1.0.0')).toBe(true)
expect(v2.supports('1.0.1')).toBe(true)
expect(v2.supports('1.3.0')).toBe(true)
expect(v2.supports('1.3.3')).toBe(false)
expect(v2.supports('1.4.0')).toBe(false)
expect(v2.supports('2.3.0')).toBe(false)
// Version tests
expect(v2.supports(new Version("1.0.0"))).toBe(true);
expect(v2.supports(new Version("1.0.1"))).toBe(true);
expect(v2.supports(new Version("1.3.0"))).toBe(true);
expect(v2.supports(new Version("1.3.3"))).toBe(false);
expect(v2.supports(new Version("1.4.0"))).toBe(false);
expect(v2.supports(new Version("2.3.0"))).toBe(false);
});
expect(v2.supports(new Version('1.0.0'))).toBe(true)
expect(v2.supports(new Version('1.0.1'))).toBe(true)
expect(v2.supports(new Version('1.3.0'))).toBe(true)
expect(v2.supports(new Version('1.3.3'))).toBe(false)
expect(v2.supports(new Version('1.4.0'))).toBe(false)
expect(v2.supports(new Version('2.3.0'))).toBe(false)
})
it("should store links", () => {
const v1 = new Version("compute", "1.3.2");
it('should store links', () => {
const v1 = new Version('compute', '1.3.2')
expect(v1.links).toBe(null);
expect(v1.links).toBe(null)
v1.links = 'wrong data';
expect(v1.links).toBe(null);
v1.links = 'wrong data'
expect(v1.links).toBe(null)
v1.links = [
{
href: `http://example.org/v2/`,
rel: "self"
href: 'http://example.org/v2/',
rel: 'self'
}
];
expect(v1.links).not.toBe(null);
expect(v1.links.length).toBe(1);
]
expect(v1.links).not.toBe(null)
expect(v1.links.length).toBe(1)
expect(v1.links[0]).toEqual({
href: "http://example.org/v2/",
rel: "self"
});
});
});
href: 'http://example.org/v2/',
rel: 'self'
})
})
})

View File

@ -1,6 +1,6 @@
import path from 'path';
import webpack from 'webpack';
import cloudsYamlPath from './test/functional/helpers/cloudsYamlPath';
import path from 'path'
import webpack from 'webpack'
import cloudsYamlPath from './test/functional/helpers/cloudsYamlPath'
export default {
entry: ['./src/index.js'],
@ -23,13 +23,13 @@ export default {
}
}
}
],
]
},
plugins: [
new webpack.NormalModuleReplacementPlugin(/helpers\/cloudsConfig/,
'json!yaml!' + cloudsYamlPath)
],
node: {
fs: "empty"
fs: 'empty'
}
};
}

View File

@ -2,13 +2,16 @@
- project:
templates:
- nodejs8-docs
- nodejs8-jobs
- nodejs8-publish-to-npm
check:
jobs:
- nodejs-run-lint
- nodejs-run-test-browser
- js-openstack-lib-unit-tests-nodejs12
gate:
jobs:
- nodejs-run-lint
- nodejs-run-test-browser
- js-openstack-lib-unit-tests-nodejs12
experimental:
jobs: