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: rules:
space-before-function-paren: no-var: error
- 2 no-console: off
- never 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" */ /* eslint no-process-env: "off" */
import fs from 'fs'; import fs from 'fs'
import karma from 'karma/lib/config'; import karma from 'karma/lib/config'
import path from 'path'; import path from 'path'
function getDevstackConfig() { function getDevstackConfig () {
const karmaConfig = karma.parseConfig(path.resolve('./karma.conf.js')); const karmaConfig = karma.parseConfig(path.resolve('./karma.conf.js'))
return getCorsConfig('$KEYSTONE_CONF', karmaConfig) + return getCorsConfig('$KEYSTONE_CONF', karmaConfig) +
getCorsConfig('$GLANCE_API_CONF', karmaConfig) + getCorsConfig('$GLANCE_API_CONF', karmaConfig) +
getCorsConfig('$NEUTRON_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}]] return `[[post-config|${service}]]
[cors] [cors]
allowed_origin=http://localhost:${karmaConfig.port} allowed_origin=http://localhost:${karmaConfig.port}
`; `
} }
fs.appendFile(process.env.BASE + '/new/devstack/local.conf', getDevstackConfig(), (err) => { fs.appendFile(process.env.BASE + '/new/devstack/local.conf', getDevstackConfig(), (err) => {
if (err) { if (err) {
throw err; throw err
} }
}); })

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@
* the License for the specific language governing permissions and limitations * the License for the specific language governing permissions and limitations
* under the License. * 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. * 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 = [ const supportedGlanceVersions = [
'v2.3' 'v2.3'
]; ]
export default class Glance extends AbstractService { export default class Glance extends AbstractService {
/** /**
* This class provides direct, idempotent, low-level access to the Glance API of a specific * 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 * 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. * @param {{}} endpointConfig The configuration element for a specific glance endpoint.
*/ */
constructor(endpointConfig) { constructor (endpointConfig) {
// Sanity checks. // Sanity checks.
if (!endpointConfig || !endpointConfig.url) { 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 // Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact). // at runtime (no modifying the config after the fact).
endpointConfig = Object.assign({}, endpointConfig); endpointConfig = Object.assign({}, endpointConfig)
super(endpointConfig.url, supportedGlanceVersions); super(endpointConfig.url, supportedGlanceVersions)
this._config = endpointConfig; this._config = endpointConfig
} }
/** /**
@ -62,11 +61,11 @@ export default class Glance extends AbstractService {
* into one. * into one.
* @returns {Promise.<T>} A promise which will resolve with the list of images. * @returns {Promise.<T>} A promise which will resolve with the list of images.
*/ */
imageList(token = null) { imageList (token = null) {
return this return this
._requestComponents(token) ._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}images`, headers)) .then(([url, headers]) => this.http.httpRequest('GET', `${url}images`, headers))
.then((response) => response.json()) .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 Keystone } from './keystone'
export {default as Glance} from './glance'; export { default as Glance } from './glance'
export {default as Neutron} from './neutron'; export { default as Neutron } from './neutron'
export {default as OpenStack} from './openstack'; 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. * 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 = [ const supportedKeystoneVersions = [
'v3.1' 'v3.1'
]; ]
export default class Keystone extends AbstractService { export default class Keystone extends AbstractService {
/** /**
* This class provides direct, idempotent, low-level access to the Keystone API of a specific * 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 * 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. * @param {{}} cloudConfig The configuration object for a specific cloud.
* @see http://docs.openstack.org/developer/os-client-config/#site-specific-file-locations * @see http://docs.openstack.org/developer/os-client-config/#site-specific-file-locations
*/ */
constructor(cloudConfig) { constructor (cloudConfig) {
// Sanity checks. // Sanity checks.
if (!cloudConfig) { 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 // Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact). // at runtime (no modifying the config after the fact).
cloudConfig = Object.assign({}, cloudConfig); cloudConfig = Object.assign({}, cloudConfig)
super(cloudConfig.auth.auth_url, supportedKeystoneVersions); super(cloudConfig.auth.auth_url, supportedKeystoneVersions)
this.cloudConfig = cloudConfig; this.cloudConfig = cloudConfig
} }
/** /**
@ -44,18 +43,18 @@ export default class Keystone extends AbstractService {
* @returns {String} The value found in the config, or null. * @returns {String} The value found in the config, or null.
* @ignore * @ignore
*/ */
_safeConfigGet(path) { _safeConfigGet (path) {
let segments = path.split('.'); const segments = path.split('.')
let pointer = this.cloudConfig; let pointer = this.cloudConfig
while (segments.length > 0) { while (segments.length > 0) {
let prop = segments.shift(); const prop = segments.shift()
if (pointer.hasOwnProperty(prop)) { if (Object.prototype.hasOwnProperty.call(pointer, prop)) {
pointer = pointer[prop]; pointer = pointer[prop]
} else { } 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. * @returns {Promise.<Object[]>} A promise that will resolve with the list of raw versions.
* @protected * @protected
*/ */
_rawVersions() { _rawVersions () {
return super._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. * Not required if a project ID is given.
* @returns {Promise.<T>} A promise which will resolve with a valid token. * @returns {Promise.<T>} A promise which will resolve with a valid token.
*/ */
tokenIssue({ tokenIssue ({
user_id: userId = this._safeConfigGet('auth.user_id'), user_id: userId = this._safeConfigGet('auth.user_id'),
username = this._safeConfigGet('auth.username'), username = this._safeConfigGet('auth.username'),
password = this._safeConfigGet('auth.password'), 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_id: projectDomainId = this._safeConfigGet('auth.project_domain_id'),
project_domain_name: projectDomainName = this._safeConfigGet('auth.project_domain_name') project_domain_name: projectDomainName = this._safeConfigGet('auth.project_domain_name')
} = {}) { } = {}) {
let project; let project
let user = {password}; const user = { password }
if (userId) { if (userId) {
user.id = userId; user.id = userId
} else if (username) { } else if (username) {
user.name = username; user.name = username
if (userDomainId) { if (userDomainId) {
user.domain = {id: userDomainId}; user.domain = { id: userDomainId }
} else if (userDomainName) { } else if (userDomainName) {
user.domain = {name: userDomainName}; user.domain = { name: userDomainName }
} else { } else {
user.domain = {id: 'default'}; user.domain = { id: 'default' }
} }
} }
if (projectId) { if (projectId) {
project = {id: projectId}; project = { id: projectId }
} else if (projectName) { } else if (projectName) {
project = {name: projectName}; project = { name: projectName }
if (projectDomainId) { if (projectDomainId) {
project.domain = {id: projectDomainId}; project.domain = { id: projectDomainId }
} else if (projectDomainName) { } else if (projectDomainName) {
project.domain = {name: projectDomainName}; project.domain = { name: projectDomainName }
} else { } else {
project.domain = {id: 'default'}; project.domain = { id: 'default' }
} }
} }
@ -136,18 +135,18 @@ export default class Keystone extends AbstractService {
auth: { auth: {
identity: { identity: {
methods: ['password'], methods: ['password'],
password: {user} password: { user }
}, },
scope: project ? {project} : 'unscoped' scope: project ? { project } : 'unscoped'
} }
}; }
return this return this
.serviceEndpoint() .serviceEndpoint()
.then((url) => this.http.httpPost(`${url}auth/tokens`, body)) .then((url) => this.http.httpPost(`${url}auth/tokens`, body))
.then((response) => { .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. * @param {String} adminToken An optional admin token.
* @returns {Promise.<T>} A promise which will resolve if the token has been successfully revoked. * @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 return Promise
.all([this.serviceEndpoint(), token, adminToken]) .all([this.serviceEndpoint(), token, adminToken])
.then(([url, token, adminToken]) => { .then(([url, token, adminToken]) => {
return [url, { return [url, {
'X-Subject-Token': token, 'X-Subject-Token': token,
'X-Auth-Token': adminToken || 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. * @param {String} token The authorization token.
* @returns {Promise.<T>} A promise which will resolve with information about the token. * @returns {Promise.<T>} A promise which will resolve with information about the token.
*/ */
tokenInfo(token) { tokenInfo (token) {
return Promise return Promise
.all([this.serviceEndpoint(), token]) .all([this.serviceEndpoint(), token])
.then(([url, token]) => { .then(([url, token]) => {
return [url, { return [url, {
'X-Subject-Token': token, 'X-Subject-Token': token,
'X-Auth-Token': token 'X-Auth-Token': token
}]; }]
}) })
.then(([url, headers]) => this.http.httpRequest('GET', `${url}auth/tokens`, headers)) .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. * @param {String} token The authorization token.
* @returns {Promise.<T>} A promise which will resolve with the service catalog. * @returns {Promise.<T>} A promise which will resolve with the service catalog.
*/ */
catalogList(token = null) { catalogList (token = null) {
return this return this
._requestComponents(token) ._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}auth/catalog`, headers)) .then(([url, headers]) => this.http.httpRequest('GET', `${url}auth/catalog`, headers))
.then((response) => response.json()) .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 * the License for the specific language governing permissions and limitations
* under the License. * 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. * 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 = [ const supportedNeutronVersions = [
'v2.0' 'v2.0'
]; ]
export default class Neutron extends AbstractService { export default class Neutron extends AbstractService {
/** /**
* This class provides direct, idempotent, low-level access to the Neutron API of a specific * 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 * 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. * @param {{}} endpointConfig The configuration element for a specific glance endpoint.
*/ */
constructor(endpointConfig) { constructor (endpointConfig) {
// Sanity checks. // Sanity checks.
if (!endpointConfig || !endpointConfig.url) { 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 // Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact). // 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. * @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. * @returns {Promise.<T>} A promise which will resolve with the list of networks.
*/ */
networkList(token = null) { networkList (token = null) {
return this return this
._requestComponents(token) ._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}/networks`, headers)) .then(([url, headers]) => this.http.httpRequest('GET', `${url}/networks`, headers))
.then((response) => response.json()) .then((response) => response.json())
.then((body) => body.networks); .then((body) => body.networks)
} }
} }

View File

@ -14,7 +14,7 @@
* limitations under the License. * 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. * 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 = [ const supportedNovaVersions = [
'v2.1' 'v2.1'
]; ]
export default class Nova extends AbstractService { export default class Nova extends AbstractService {
/** /**
* This class provides direct, idempotent, low-level access to the Nova API of a specific * 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 * 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. * @param {{}} endpointConfig The configuration element for a specific nova endpoint.
*/ */
constructor(endpointConfig) { constructor (endpointConfig) {
// Sanity checks. // Sanity checks.
if (!endpointConfig || !endpointConfig.url) { 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 // Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact). // at runtime (no modifying the config after the fact).
endpointConfig = Object.assign({}, endpointConfig); endpointConfig = Object.assign({}, endpointConfig)
super(endpointConfig.url, supportedNovaVersions); super(endpointConfig.url, supportedNovaVersions)
this._config = endpointConfig; 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. * @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. * @returns {Promise.<T>} A promise which will resolve with the list of flavors.
*/ */
flavorList(token = null) { flavorList (token = null) {
return this return this
._requestComponents(token) ._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}flavors`, headers)) .then(([url, headers]) => this.http.httpRequest('GET', `${url}flavors`, headers))
.then((response) => response.json()) .then((response) => response.json())
.then((body) => body.flavors); .then((body) => body.flavors)
} }
} }

View File

@ -1,7 +1,7 @@
import Keystone from "./keystone"; import Keystone from './keystone'
import Neutron from "./neutron"; import Neutron from './neutron'
import Glance from "./glance"; import Glance from './glance'
import Nova from "./nova"; import Nova from './nova'
export default class OpenStack { export default class OpenStack {
/** /**
@ -9,21 +9,21 @@ export default class OpenStack {
* *
* @param {{}} cloudConfig The configuration object for a specific cloud. * @param {{}} cloudConfig The configuration object for a specific cloud.
*/ */
constructor(cloudConfig) { constructor (cloudConfig) {
// Sanity checks. // Sanity checks.
if (!cloudConfig) { 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 // Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact). // 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 // 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. * @returns {Promise.<T>} A promise which will resolve with the list of networks.
*/ */
networkList() { networkList () {
return this._neutron 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. * @returns {Promise.<T>} A promise which will resolve with the list of images.
*/ */
imageList() { imageList () {
return this._glance 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. * @returns {Promise.<T>} A promise which will resolve with the list of flavors.
*/ */
flavorList() { flavorList () {
return this._nova 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. * @returns {Promise.<Keystone>} A promise which will resolve with Keystone instance.
* @private * @private
*/ */
get _keystone() { get _keystone () {
if (!this._keystonePromise) { 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. * @returns {Promise.<Neutron>} A promise which will resolve with Neutron instance.
* @private * @private
*/ */
get _neutron() { get _neutron () {
if (!this._neutronPromise) { if (!this._neutronPromise) {
this._neutronPromise = this._getComponentConfigFor('neutron') 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. * @returns {Promise.<Glance>} A promise which will resolve with Glance instance.
* @private * @private
*/ */
get _glance() { get _glance () {
if (!this._glancePromise) { if (!this._glancePromise) {
this._glancePromise = this._getComponentConfigFor('glance') 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. * @returns {Promise.<Nova>} A promise which will resolve with Nova instance.
* @private * @private
*/ */
get _nova() { get _nova () {
if (!this._novaPromise) { if (!this._novaPromise) {
this._novaPromise = this._getComponentConfigFor('nova') 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. * @returns {Promise.<T>} A promise which will resolve with the token.
* @private * @private
*/ */
get _token() { get _token () {
if (!this._tokenPromise) { 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. * @returns {Promise.<{}>} A promise which will resolve with the component config.
* @private * @private
*/ */
_getComponentConfigFor(name) { _getComponentConfigFor (name) {
const config = this.getConfig(); const config = this.getConfig()
return this._token return this._token
.then((token) => this._keystone.then((keystone) => keystone.catalogList(token))) .then((token) => this._keystone.then((keystone) => keystone.catalogList(token)))
.then((catalog) => catalog.find((entry) => entry.name === name)) .then((catalog) => catalog.find((entry) => entry.name === name))
.then((entry) => entry.endpoints.find((endpoint) => { .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. * under the License.
*/ */
import Http from './http'; import Http from './http'
import Version from './version'; import Version from './version'
import URL from 'url-parse'; import URL from 'url-parse'
export default class AbstractService { export default class AbstractService {
/** /**
* This class provides an abstract implementation of our services, which includes logic common to * This class provides an abstract implementation of our services, which includes logic common to
* all of our services. * all of our services.
@ -27,9 +26,9 @@ export default class AbstractService {
* @param {string} endpointUrl The endpoint URL. * @param {string} endpointUrl The endpoint URL.
* @param {Array} supportedVersions The list of all supported versions. * @param {Array} supportedVersions The list of all supported versions.
*/ */
constructor(endpointUrl, supportedVersions) { constructor (endpointUrl, supportedVersions) {
this._endpointUrl = endpointUrl; this._endpointUrl = endpointUrl
this._supportedVersions = supportedVersions; this._supportedVersions = supportedVersions
} }
/** /**
@ -37,11 +36,11 @@ export default class AbstractService {
* *
* @returns {Http} Our HTTP service instance. * @returns {Http} Our HTTP service instance.
*/ */
get http() { get http () {
if (!this._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. * @returns {Array} The list of all supported versions, or empty array.
*/ */
get supportedVersions() { get supportedVersions () {
return this._supportedVersions || []; return this._supportedVersions || []
} }
/** /**
@ -58,8 +57,8 @@ export default class AbstractService {
* *
* @returns {string} The URL of our service. * @returns {string} The URL of our service.
*/ */
get endpointUrl() { get endpointUrl () {
return this._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. * @returns {Promise.<Version[]>} A promise that will resolve with the list of API versions.
*/ */
versions() { versions () {
return this._rawVersions().then((versions) => { return this._rawVersions().then((versions) => {
return versions.map((rawVersion) => { return versions.map((rawVersion) => {
const version = new Version(rawVersion.id); const version = new Version(rawVersion.id)
version.links = rawVersion.links; version.links = rawVersion.links
return version; return version
}); })
}); })
} }
/** /**
@ -83,26 +82,26 @@ export default class AbstractService {
* @returns {Promise.<Object[]>} A promise that will resolve with the list of raw versions. * @returns {Promise.<Object[]>} A promise that will resolve with the list of raw versions.
* @protected * @protected
*/ */
_rawVersions() { _rawVersions () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http this.http
.httpGet(this._endpointUrl) .httpGet(this._endpointUrl)
.catch((response) => { .catch((response) => {
if (response.status === 401) { if (response.status === 401) {
let rootUrl = new URL(this._endpointUrl); const rootUrl = new URL(this._endpointUrl)
rootUrl.set('pathname', '/'); rootUrl.set('pathname', '/')
rootUrl.set('query', ''); rootUrl.set('query', '')
rootUrl.set('hash', ''); rootUrl.set('hash', '')
return this.http.httpGet(rootUrl.href); return this.http.httpGet(rootUrl.href)
} else { } else {
throw response; throw response
} }
}) })
.then((response) => response.json()) .then((response) => response.json())
.then((body) => resolve(body.versions)) .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. * @returns {Promise.<Version>} A promise that will resolve with the specific API version.
*/ */
version() { version () {
return this return this
.versions() .versions()
.then((versions) => { .then((versions) => {
for (let version of versions) { for (const version of versions) {
if (this.supportedVersions.find(version.supports)) { 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. * @returns {Promise.<T>|*} A promise which will resolve with the endpoint URL string.
*/ */
serviceEndpoint() { serviceEndpoint () {
if (!this._endpointPromise) { if (!this._endpointPromise) {
this._endpointPromise = this.version() this._endpointPromise = this.version()
.then((version) => { .then((version) => {
if (version.links) { if (version.links) {
for (let i = 0; i < version.links.length; i++) { 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) { 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]. * @returns {Promise} A promise which resolves with [url, token].
* @private * @private
*/ */
_requestComponents(token = null) { _requestComponents (token = null) {
// Make sure the token is a promise. // Make sure the token is a promise.
let headerPromise = Promise const headerPromise = Promise
.resolve(token) .resolve(token)
.then((token) => { .then((token) => {
if (token) { if (token) {
return { return {
'X-Auth-Token': token 'X-Auth-Token': token
}; }
} }
return {}; return {}
}); })
return Promise.all([this.serviceEndpoint(), headerPromise]); return Promise.all([this.serviceEndpoint(), headerPromise])
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,76 +14,75 @@
* under the License. * under the License.
*/ */
import Glance from "../../src/glance.js"; import Glance from '../../src/glance.js'
import * as mockData from "./helpers/data/glance"; import * as mockData from './helpers/data/glance'
import fetchMock from "fetch-mock"; import fetchMock from 'fetch-mock'
describe('Glance', () => { describe('Glance', () => {
afterEach(fetchMock.restore)
afterEach(fetchMock.restore);
it('should export a class', () => { it('should export a class', () => {
const glance = new Glance(mockData.config); const glance = new Glance(mockData.config)
expect(glance).toBeDefined(); expect(glance).toBeDefined()
}); })
it('should throw an error for an empty config', () => { it('should throw an error for an empty config', () => {
expect(() => new Glance()).toThrow(); expect(() => new Glance()).toThrow()
}); })
describe("serviceEndpoint()", () => { describe('serviceEndpoint()', () => {
it("Should return a valid endpoint to the glance API.", (done) => { it('Should return a valid endpoint to the glance API.', (done) => {
const glance = new Glance(mockData.config); const glance = new Glance(mockData.config)
fetchMock.mock(mockData.root()); fetchMock.mock(mockData.root())
glance.serviceEndpoint() glance.serviceEndpoint()
.then((endpoint) => { .then((endpoint) => {
expect(endpoint).toEqual('http://192.168.99.99:9292/v2/'); expect(endpoint).toEqual('http://192.168.99.99:9292/v2/')
done(); done()
}) })
.catch((error) => done.fail(error)); .catch((error) => done.fail(error))
}); })
}); })
describe("imageList()", () => { describe('imageList()', () => {
let glance = null; let glance = null
beforeEach(() => { beforeEach(() => {
fetchMock.mock(mockData.root()); fetchMock.mock(mockData.root())
glance = new Glance(mockData.config); glance = new Glance(mockData.config)
}); })
it("should return the images as an array.", (done) => { it('should return the images as an array.', (done) => {
const token = 'test_token'; const token = 'test_token'
fetchMock.mock(mockData.imageList(token)); fetchMock.mock(mockData.imageList(token))
glance glance
.imageList(token) .imageList(token)
.then((images) => { .then((images) => {
expect(images.length).not.toBe(0); expect(images.length).not.toBe(0)
done(); done()
}) })
.catch((error) => done.fail(error)); .catch((error) => done.fail(error))
}); })
it("Should not cache its results", (done) => { it('Should not cache its results', (done) => {
const token = 'test_token'; const token = 'test_token'
let mockOptions = mockData.imageList(token); const mockOptions = mockData.imageList(token)
fetchMock.mock(mockOptions); fetchMock.mock(mockOptions)
glance glance
.imageList(token) .imageList(token)
.then(() => { .then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1); expect(fetchMock.calls(mockOptions.matcher).length).toEqual(1)
return glance.imageList(token); return glance.imageList(token)
}) })
.then(() => { .then(() => {
expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2); expect(fetchMock.calls(mockOptions.matcher).length).toEqual(2)
done(); 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. * format, rather just the subsection pointing to a particular cloud.
*/ */
const glanceConfig = { const glanceConfig = {
region_id: "RegionOne", region_id: 'RegionOne',
url: "http://192.168.99.99:9292/", url: 'http://192.168.99.99:9292/',
region: "RegionOne", region: 'RegionOne',
interface: "public", interface: 'public',
id: "0b8b5f0f14904136ab5a4f83f27ec49a" id: '0b8b5f0f14904136ab5a4f83f27ec49a'
}; }
/** /**
* Build a new FetchMock configuration for the root endpoint. * Build a new FetchMock configuration for the root endpoint.
* *
* @returns {{}} A full FetchMock configuration for Glance's Root Resource. * @returns {{}} A full FetchMock configuration for Glance's Root Resource.
*/ */
function rootResponse() { function rootResponse () {
return { return {
method: 'GET', method: 'GET',
matcher: 'http://192.168.99.99:9292/', matcher: 'http://192.168.99.99:9292/',
response: { response: {
versions: [ versions: [
{ {
status: "CURRENT", status: 'CURRENT',
id: "v2.5", id: 'v2.5',
links: [ links: [
{ {
href: "http://192.168.99.99:9292/v2/", href: 'http://192.168.99.99:9292/v2/',
rel: "self" rel: 'self'
} }
] ]
}, },
{ {
status: "SUPPORTED", status: 'SUPPORTED',
id: "v2.3", id: 'v2.3',
links: [ links: [
{ {
href: "http://192.168.99.99:9292/v2/", href: 'http://192.168.99.99:9292/v2/',
rel: "self" rel: 'self'
} }
] ]
}, },
{ {
status: "SUPPORTED", status: 'SUPPORTED',
id: "v2.2", id: 'v2.2',
links: [ links: [
{ {
href: "http://192.168.99.99:9292/v2/", href: 'http://192.168.99.99:9292/v2/',
rel: "self" rel: 'self'
} }
] ]
}, },
{ {
status: "SUPPORTED", status: 'SUPPORTED',
id: "v2.1", id: 'v2.1',
links: [ links: [
{ {
href: "http://192.168.99.99:9292/v2/", href: 'http://192.168.99.99:9292/v2/',
rel: "self" rel: 'self'
} }
] ]
}, },
{ {
status: "SUPPORTED", status: 'SUPPORTED',
id: "v2.0", id: 'v2.0',
links: [ links: [
{ {
href: "http://192.168.99.99:9292/v2/", href: 'http://192.168.99.99:9292/v2/',
rel: "self" rel: 'self'
} }
] ]
}, },
{ {
status: "SUPPORTED", status: 'SUPPORTED',
id: "v1.1", id: 'v1.1',
links: [ links: [
{ {
href: "http://192.168.99.99:9292/v1/", href: 'http://192.168.99.99:9292/v1/',
rel: "self" rel: 'self'
} }
] ]
}, },
{ {
status: "SUPPORTED", status: 'SUPPORTED',
id: "v1.0", id: 'v1.0',
links: [ links: [
{ {
href: "http://192.168.99.99:9292/v1/", href: 'http://192.168.99.99:9292/v1/',
rel: "self" rel: 'self'
} }
] ]
} }
] ]
} }
}; }
} }
function imageList(token) { function imageList (token) {
return { return {
method: 'GET', method: 'GET',
matcher: 'http://192.168.99.99:9292/v2/images', matcher: 'http://192.168.99.99:9292/v2/images',
@ -195,10 +195,10 @@ function imageList(token) {
schema: '/v2/schemas/images', schema: '/v2/schemas/images',
first: '/v2/images' first: '/v2/images'
} }
}; }
} }
export { export {
glanceConfig as config, glanceConfig as config,
rootResponse as root, rootResponse as root,
imageList imageList
}; }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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