diff --git a/.gitignore b/.gitignore index f6001f105..14a603d0b 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,3 @@ coverage oclif.manifest.json package-lock.json -bin/openwhisk-standalone*.jar diff --git a/bin/openwhisk-standalone-config/get-runtimes.js b/bin/openwhisk-standalone-config/get-runtimes.js deleted file mode 100644 index aac1f2b19..000000000 --- a/bin/openwhisk-standalone-config/get-runtimes.js +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -const fetch = require('node-fetch') - -const DOCKER_ORG = 'adobeapiplatform' -const DOCKER_REPOS = { // repo-name:kind - 'adobe-action-nodejs-v10': 'nodejs:10', - 'adobe-action-nodejs-v12': 'nodejs:12', - 'adobe-action-nodejs-v14': 'nodejs:14', - 'adobe-action-nodejs-v16': 'nodejs:16', - 'adobe-action-nodejs-v18': 'nodejs:18', - 'adobe-action-nodejs-v20': 'nodejs:20' -} - -const DEFAULT_KIND = 'nodejs:18' - -/** @private */ -async function main () { - const nodejs = [] - - for (const [repoName, kind] of Object.entries(DOCKER_REPOS)) { - const data = await fetch(`https://registry.hub.docker.com/v2/repositories/${DOCKER_ORG}/${repoName}/tags`) - const json = await data.json() - const defaultKind = (kind === DEFAULT_KIND) ? true : undefined - - nodejs.push({ - kind, - default: defaultKind, - image: { - prefix: DOCKER_ORG, - name: repoName, - tag: json.results[0].name - } - }) - } - - const output = { - runtimes: { - nodejs - } - } - console.log(JSON.stringify(output, null, 2)) -} - -main() - .catch(console.error) diff --git a/bin/openwhisk-standalone-config/runtimes.json b/bin/openwhisk-standalone-config/runtimes.json deleted file mode 100644 index 2583a934a..000000000 --- a/bin/openwhisk-standalone-config/runtimes.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "runtimes": { - "nodejs": [ - { - "kind": "nodejs:10", - "image": { - "prefix": "adobeapiplatform", - "name": "adobe-action-nodejs-v10", - "tag": "3.0.39" - } - }, - { - "kind": "nodejs:12", - "image": { - "prefix": "adobeapiplatform", - "name": "adobe-action-nodejs-v12", - "tag": "3.0.39" - } - }, - { - "kind": "nodejs:14", - "image": { - "prefix": "adobeapiplatform", - "name": "adobe-action-nodejs-v14", - "tag": "3.0.39" - } - }, - { - "kind": "nodejs:16", - "image": { - "prefix": "adobeapiplatform", - "name": "adobe-action-nodejs-v16", - "tag": "3.0.39" - } - }, - { - "kind": "nodejs:18", - "default": true, - "image": { - "prefix": "adobeapiplatform", - "name": "adobe-action-nodejs-v18", - "tag": "3.0.39" - } - }, - { - "kind": "nodejs:20", - "image": { - "prefix": "adobeapiplatform", - "name": "adobe-action-nodejs-v20", - "tag": "3.0.39" - } - } - ] - } -} diff --git a/package.json b/package.json index d6e4f82a2..d2d2dd6e5 100644 --- a/package.json +++ b/package.json @@ -27,9 +27,6 @@ "archiver": "^5.3.1", "chalk": "^4", "chokidar": "^3.5.2", - "debug": "^4.1.1", - "dedent-js": "^1.0.1", - "dotenv": "^16", "execa": "^5.0.0", "fs-extra": "^11.1.1", "get-port": "^5", diff --git a/src/commands/app/deploy.js b/src/commands/app/deploy.js index 59b903052..a8914d998 100644 --- a/src/commands/app/deploy.js +++ b/src/commands/app/deploy.js @@ -185,8 +185,7 @@ class Deploy extends BuildCommand { if (!script) { const hookResults = await this.config.runHook('deploy-actions', { appConfig: config, - filterEntities: filterActions || [], - isLocalDev: false + filterEntities: filterActions || [] }) if (hookResults?.failures?.length > 0) { // output should be "Error : : \n" for each failure diff --git a/src/commands/app/get-url.js b/src/commands/app/get-url.js index 98ad8c753..0e304dade 100644 --- a/src/commands/app/get-url.js +++ b/src/commands/app/get-url.js @@ -18,7 +18,6 @@ const BaseCommand = require('../../BaseCommand') const { wrapError } = require('../../lib/app-helper') const { getActionUrls } = require('@adobe/aio-lib-runtime').utils const yaml = require('js-yaml') -const { loadLocalDevConfig } = require('../../lib/run-local-runtime') class GetUrlCommand extends BaseCommand { async run () { @@ -46,16 +45,10 @@ class GetUrlCommand extends BaseCommand { } const actionUrls = {} - if (flags.local) { - Object.values(fullConfig.all).forEach(config => { - const localDevConfig = loadLocalDevConfig(config) - Object.assign(actionUrls, getActionUrls(localDevConfig, false, true)) - }) - } else { - Object.values(fullConfig.all).forEach(config => { - Object.assign(actionUrls, getActionUrls(config, true)) - }) - } + + Object.values(fullConfig.all).forEach(config => { + Object.assign(actionUrls, getActionUrls(config, true)) + }) urls.runtime = actionUrls const cdnUrls = {} if (options.cdn) { @@ -110,9 +103,6 @@ GetUrlCommand.flags = { yml: Flags.boolean({ description: 'Output yml', char: 'y' - }), - local: Flags.boolean({ - description: 'Display locally based action URLs' }) } diff --git a/src/commands/app/run.js b/src/commands/app/run.js index f48c65cb7..bc5c907ef 100644 --- a/src/commands/app/run.js +++ b/src/commands/app/run.js @@ -16,7 +16,6 @@ const fs = require('fs-extra') const https = require('https') const getPort = require('get-port') const open = require('open') -const os = require('node:os') const { Flags, ux } = require('@oclif/core') const coreConfig = require('@adobe/aio-lib-core-config') @@ -36,16 +35,6 @@ class Run extends BaseCommand { // cli input const { flags } = await this.parse(Run) - if (flags.local) { - const [firstCpu] = os.cpus() - // note: the earliest versions of M1 macs return 'Apple processor' under model. - if (firstCpu?.model?.startsWith('Apple')) { - this.error('The --local flag is not supported on Apple Silicon Macs.') - } else { - this.warn('The --local flag is deprecated and will be removed in the next major release.') - } - } - const spinner = ora() const runConfigs = await this.getAppExtConfigs(flags) @@ -87,7 +76,6 @@ class Run extends BaseCommand { shouldContentHash: false }, fetchLogs: true, - isLocal: flags.local, verbose: flags.verbose } @@ -107,7 +95,7 @@ class Run extends BaseCommand { } } - const verboseOutput = flags.verbose || flags.local || headlessApp + const verboseOutput = flags.verbose || headlessApp // we should evaluate this, a lot of output just disappears in the spinner text and // using verbose dumps ALL of parcel's output, so this become unreadable // we need a middle ground. -jm @@ -217,10 +205,6 @@ Run.args = {} Run.flags = { ...BaseCommand.flags, - local: Flags.boolean({ - description: '[deprecated] Run/debug actions locally (requires Docker running, not available on Apple Silicon Macs)', - exclusive: ['no-actions'] - }), serve: Flags.boolean({ description: '[default: true] Start frontend server (experimental)', default: true, diff --git a/src/lib/actions-watcher.js b/src/lib/actions-watcher.js index e8b8202b0..93b179bfc 100644 --- a/src/lib/actions-watcher.js +++ b/src/lib/actions-watcher.js @@ -24,7 +24,6 @@ const deployActions = require('./deploy-actions') /** * @typedef {object} WatcherOptions * @property {object} config the app config (see src/lib/config-loader.js) - * @property {boolean} isLocal whether the deployment is local or not * @property {Function} log the app logger * @property {object} [watcher] the watcher itself */ @@ -61,10 +60,9 @@ module.exports = async (watcherOptions) => { * @param {Array} filterActions add filters to deploy only specified OpenWhisk actions */ async function buildAndDeploy (watcherOptions, filterActions) { - const { config, isLocal, log, inprocHook } = watcherOptions + const { config, log, inprocHook } = watcherOptions await buildActions(config, filterActions) const deployConfig = { - isLocalDev: isLocal, filterEntities: { actions: filterActions } diff --git a/src/lib/app-helper.js b/src/lib/app-helper.js index ec58e01f5..dd5025233 100644 --- a/src/lib/app-helper.js +++ b/src/lib/app-helper.js @@ -16,7 +16,6 @@ const which = require('which') const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:lib-app-helper', { provider: 'debug' }) const { getToken, context } = require('@adobe/aio-lib-ims') const { CLI } = require('@adobe/aio-lib-ims/src/context') -const { createFetch } = require('@adobe/aio-lib-core-networking') const chalk = require('chalk') const aioConfig = require('@adobe/aio-lib-core-config') const { AIO_CONFIG_WORKSPACE_SERVICES, AIO_CONFIG_ORG_SERVICES } = require('./defaults') @@ -262,116 +261,6 @@ function writeConfig (file, config) { ) } -/** @private */ -async function isDockerRunning () { - // todo more checks - const args = ['info'] - try { - await execa('docker', args) - return true - } catch (error) { - aioLogger.debug('Error spawning docker info: ' + error) - } - return false -} - -/** @private */ -async function hasDockerCLI () { - // todo check min version - try { - const result = await execa('docker', ['-v']) - aioLogger.debug('docker version : ' + result.stdout) - return true - } catch (error) { - aioLogger.debug('Error spawning docker info: ' + error) - } - return false -} - -/** @private */ -async function hasJavaCLI () { - // todo check min version - try { - const result = await execa('java', ['-version']) - // stderr is where the version is printed out for - aioLogger.debug('java version : ' + result.stderr) - return true - } catch (error) { - aioLogger.debug('Error spawning java info: ' + error) - } - return false -} - -/** @private */ -async function downloadOWJar (url, outFile) { - aioLogger.debug(`downloadOWJar - url: ${url} outFile: ${outFile}`) - let response - try { - const fetch = createFetch() - response = await fetch(url) - } catch (e) { - aioLogger.debug(`connection error while downloading '${url}'`, e) - throw new Error(`connection error while downloading '${url}', are you online?`) - } - if (!response.ok) throw new Error(`unexpected response while downloading '${url}': ${response.statusText}`) - fs.ensureDirSync(path.dirname(outFile)) - const fstream = fs.createWriteStream(outFile) - - return new Promise((resolve, reject) => { - response.body.pipe(fstream) - response.body.on('error', (err) => { - reject(err) - }) - fstream.on('finish', () => { - resolve() - }) - }) -} - -/** @private */ -async function waitForOpenWhiskReadiness (host, endTime, period, timeout, lastStatus, waitFunc) { - if (Date.now() > endTime) { - throw new Error(`local openwhisk stack startup timed out after ${timeout}ms due to ${lastStatus}`) - } - - let ok, status - - try { - const fetch = createFetch() - const response = await fetch(host + '/api/v1') - ok = response.ok - status = response.statusText - } catch (e) { - ok = false - status = e.toString() - } - - if (!ok) { - await waitFunc(period) - return waitForOpenWhiskReadiness(host, endTime, period, timeout, status, waitFunc) - } -} - -/** @private */ -function waitFor (t) { - return new Promise(resolve => setTimeout(resolve, t)) -} - -/** @private */ -async function runOpenWhiskJar (jarFile, runtimeConfigFile, apihost, waitInitTime, waitPeriodTime, timeout, /* istanbul ignore next */ execaOptions = {}) { - aioLogger.debug(`runOpenWhiskJar - jarFile: ${jarFile} runtimeConfigFile ${runtimeConfigFile} apihost: ${apihost} waitInitTime: ${waitInitTime} waitPeriodTime: ${waitPeriodTime} timeout: ${timeout}`) - const jvmConfig = aioConfig.get('ow.jvm.args') - const jvmArgs = jvmConfig ? jvmConfig.split(' ') : [] - const proc = execa('java', ['-jar', '-Dwhisk.concurrency-limit.max=10', ...jvmArgs, jarFile, '-m', runtimeConfigFile, '--no-ui', '--disable-color-logging'], execaOptions) - - const endTime = Date.now() + timeout - await waitFor(waitInitTime) - await waitForOpenWhiskReadiness(apihost, endTime, waitPeriodTime, timeout, null, waitFor) - - // must wrap in an object as execa return value is awaitable - return { proc } -} - /** * *Converts a service array to an input string that can be consumed by generator-aio-app @@ -599,14 +488,8 @@ module.exports = { removeProtocolFromURL, urlJoin, checkFile, - hasDockerCLI, - hasJavaCLI, - isDockerRunning, writeConfig, - downloadOWJar, - runOpenWhiskJar, servicesToGeneratorInput, - waitForOpenWhiskReadiness, warnIfOverwriteServicesInProductionWorkspace, setOrgServicesConfig, setWorkspaceServicesConfig, diff --git a/src/lib/owlocal.js b/src/lib/owlocal.js deleted file mode 100644 index e5cd202bd..000000000 --- a/src/lib/owlocal.js +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -const path = require('path') -const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:owlocal', { provider: 'debug' }) -const execa = require('execa') -const url = require('url') - -const OW_LOCAL_DOCKER_PORT = 3233 - -/** @private */ -function isWindowsOrMac () { - return ( - process.platform === 'win32' || - process.platform === 'darwin' - ) -} - -/** @private */ -function owJarPath (owJarUrl) { - const { pathname } = new url.URL(owJarUrl) - const idx = pathname.indexOf('/openwhisk/') - let jarPath - - if (idx === -1) { - jarPath = path.join('openwhisk', 'openwhisk-standalone.jar') // default path - aioLogger.warn(`Could not parse openwhisk jar path from ${owJarUrl}, using default ${jarPath}`) - } else { - jarPath = pathname - .substring(idx + 1) // skip initial forward slash - .split(path.posix.sep) // split on forward slashes - .join(path.sep) // join on os path separator (for Windows) - aioLogger.debug(`Parsed openwhisk jar path from ${owJarUrl}, using ${jarPath}`) - } - - return jarPath -} - -/** @private */ -function getDockerNetworkAddress () { - try { - // Docker for Windows and macOS do not allow routing to the containers via - // IP address, only port forwarding is allowed - if (!isWindowsOrMac()) { - const args = ['network', 'inspect', 'bridge'] - const result = execa.sync('docker', args) - const json = JSON.parse(result.stdout) - return `http://${json[0].IPAM.Config[0].Gateway}:${OW_LOCAL_DOCKER_PORT}` - } - } catch (error) { - aioLogger.debug(`getDockerNetworkAddress ${error}`) - } - - return `http://localhost:${OW_LOCAL_DOCKER_PORT}` -} - -// gets these values if the keys are set in the environment, if not it will use the defaults set -const { - OW_JAR_URL = 'https://github.com/adobe/aio-cli-plugin-app/releases/download/6.2.0/openwhisk-standalone.jar', - OW_CONFIG_RUNTIMES_FILE = path.resolve(__dirname, '../../bin/openwhisk-standalone-config/runtimes.json'), - OW_LOCAL_APIHOST = getDockerNetworkAddress(), - OW_LOCAL_NAMESPACE = 'guest', - OW_LOCAL_AUTH = '23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP', - OW_LOCAL_LOG_FILE -} = process.env - -module.exports = { - getDockerNetworkAddress, - OW_LOCAL_DOCKER_PORT, - OW_JAR_URL, - OW_JAR_PATH: owJarPath(OW_JAR_URL), - OW_CONFIG_RUNTIMES_FILE, - OW_LOCAL_APIHOST, - OW_LOCAL_NAMESPACE, - OW_LOCAL_AUTH, - OW_LOCAL_LOG_FILE -} diff --git a/src/lib/run-dev.js b/src/lib/run-dev.js index 0d6771f0f..5205a89bb 100644 --- a/src/lib/run-dev.js +++ b/src/lib/run-dev.js @@ -13,13 +13,11 @@ governing permissions and limitations under the License. const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:runDev', { provider: 'debug' }) const rtLib = require('@adobe/aio-lib-runtime') const rtLibUtils = rtLib.utils -const vscode = require('./vscode') const { bundle } = require('@adobe/aio-lib-web') const bundleServe = require('./bundle-serve') const { defaultHttpServerPort: SERVER_DEFAULT_PORT } = require('./defaults') const serve = require('./serve') const Cleanup = require('./cleanup') -const { runLocalRuntime } = require('./run-local-runtime') const buildActions = require('./build-actions') const deployActions = require('./deploy-actions') @@ -47,7 +45,6 @@ async function runDev (config, dataDir, options = {}, log = () => {}, inprocHook // control variables const hasFrontend = config.app.hasFrontend const withBackend = config.app.hasBackend && !skipActions - const isLocal = options.isLocal // applies only for backend const portToUse = parseInt(process.env.PORT) || SERVER_DEFAULT_PORT const uiPort = await getPort({ port: portToUse }) @@ -56,12 +53,11 @@ async function runDev (config, dataDir, options = {}, log = () => {}, inprocHook } aioLogger.debug(`hasFrontend ${hasFrontend}`) aioLogger.debug(`withBackend ${withBackend}`) - aioLogger.debug(`isLocal ${isLocal}`) let frontEndUrl // state - let devConfig = config // config will be different if local or remote + const devConfig = config // config will be different if local or remote devConfig.envFile = '.env' const cleanup = new Cleanup() @@ -70,20 +66,14 @@ async function runDev (config, dataDir, options = {}, log = () => {}, inprocHook try { // Build Phase - actions if (withBackend) { - if (isLocal) { - const { config: localConfig, cleanup: localCleanup } = await runLocalRuntime(config, dataDir, log, options.verbose) - devConfig = localConfig - cleanup.add(() => localCleanup(), 'cleaning up runDevLocal') - } else { - // check credentials - rtLibUtils.checkOpenWhiskCredentials(devConfig) - log('using remote actions') - } + // check credentials + rtLibUtils.checkOpenWhiskCredentials(devConfig) + log('using remote actions') // build and deploy actions log('building actions..') await buildActions(devConfig, null, false /* force build */) - const { cleanup: watcherCleanup } = await actionsWatcher({ config: devConfig, isLocal, log, inprocHook }) + const { cleanup: watcherCleanup } = await actionsWatcher({ config: devConfig, log, inprocHook }) cleanup.add(() => watcherCleanup(), 'stopping action watcher...') } @@ -95,7 +85,7 @@ async function runDev (config, dataDir, options = {}, log = () => {}, inprocHook // note the condition: we still write backend urls EVEN if skipActions is set // the urls will always point to remotely deployed actions if skipActions is set log('injecting backend urls into frontend config') - urls = rtLibUtils.getActionUrls(devConfig, true, isLocal && !skipActions, true) + urls = rtLibUtils.getActionUrls(devConfig, true, !skipActions, true) } utils.writeConfig(devConfig.web.injectedConfig, urls) @@ -123,7 +113,6 @@ async function runDev (config, dataDir, options = {}, log = () => {}, inprocHook if (withBackend) { log('redeploying actions..') const deployConfig = { - isLocalDev: isLocal, filterEntities: { byBuiltActions: true } @@ -166,12 +155,6 @@ async function runDev (config, dataDir, options = {}, log = () => {}, inprocHook // also there was a latent issue with projects that defined an action src as a folder with an index.js file. // it looks explicitly for package.json and fails if it does not find it. // regarless, we don't need it, and when we actually remove --local we can be rid of this. - if (isLocal) { - log('setting up vscode debug configuration files...') - const vscodeConfig = vscode(devConfig) - await vscodeConfig.update({ frontEndUrl }) - cleanup.add(() => vscodeConfig.cleanup(), 'cleaning up vscode debug configuration files...') - } // automatically fetch logs if there are actions if (config.app.hasBackend && fetchLogs) { diff --git a/src/lib/run-local-runtime.js b/src/lib/run-local-runtime.js deleted file mode 100644 index 11b1153d1..000000000 --- a/src/lib/run-local-runtime.js +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -const path = require('path') -const fs = require('fs-extra') -const cloneDeep = require('lodash.clonedeep') -const utils = require('./app-helper') -const dedent = require('dedent-js') -const rtLib = require('@adobe/aio-lib-runtime') -const rtLibUtils = rtLib.utils -const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:run-dev-local', { provider: 'debug' }) - -const { - OW_CONFIG_RUNTIMES_FILE, - OW_JAR_URL, OW_JAR_PATH, - OW_LOCAL_APIHOST, - OW_LOCAL_NAMESPACE, - OW_LOCAL_AUTH, - OW_LOCAL_LOG_FILE -} = require('../lib/owlocal') - -const OW_WAIT_INIT_TIME = 2000 -const OW_WAIT_PERIOD_TIME = 500 -const OW_TIMEOUT = 60000 - -const LOCAL_RUNTIME = { - namespace: OW_LOCAL_NAMESPACE, - auth: OW_LOCAL_AUTH, - apihost: OW_LOCAL_APIHOST -} - -/** - * @typedef {object} RunDevLocalObject - * @property {string} config the modified dev config - * @property {Function} cleanup callback function to cleanup available resources - */ - -/** - * @typedef {object} RuntimeCredentials - * @property {string} namespace the runtime namespace - * @property {string} auth the runtime auth key - * @property {string} apihost the runtime apihost - */ - -/** - * Checks the system for pre-requisites to run local Openwhisk, then runs it. - * - * @param {object} config the app config - * @param {object} dataDir global config folder to store the ow jar - * @param {Function} [log] function to log application logs - * @param {boolean} [verbose=false] set to true to have verbose logging (openwhisk) - * @returns {RunDevLocalObject} the RunDevLocalObject - */ -async function runDevLocal (config, dataDir, log = () => undefined, verbose = false) { - const owJarFile = path.join(dataDir, OW_JAR_PATH) - const devConfig = loadLocalDevConfig(config) - - // take following steps only when we have a backend - log('checking if java is installed...') - if (!await utils.hasJavaCLI()) { - throw new Error('could not find java CLI, please make sure java is installed') - } - - log('checking if docker is installed...') - if (!await utils.hasDockerCLI()) { - throw new Error('could not find docker CLI, please make sure docker is installed') - } - - log('checking if docker is running...') - if (!await utils.isDockerRunning()) { - throw new Error('docker is not running, please make sure to start docker') - } - - if (!fs.existsSync(owJarFile)) { - log(`downloading OpenWhisk standalone jar from ${OW_JAR_URL} to ${owJarFile}, this might take a while... (to be done only once!)`) - await utils.downloadOWJar(OW_JAR_URL, owJarFile) - } - - log('starting local OpenWhisk stack...') - const owLocalLogFile = OW_LOCAL_LOG_FILE || path.join(config.app.dist, 'openwhisk-local.log.txt') - const owExecaOptions = { - stdio: [ - null, // stdin - verbose ? fs.openSync(owLocalLogFile, 'w') : null, // stdout - 'inherit' // stderr - ] - } - const res = await utils.runOpenWhiskJar(owJarFile, OW_CONFIG_RUNTIMES_FILE, OW_LOCAL_APIHOST, OW_WAIT_INIT_TIME, OW_WAIT_PERIOD_TIME, OW_TIMEOUT, owExecaOptions) - - log(`writing credentials to tmp wskdebug config '${devConfig.envFile}'`) - await writeLocalEnvFile(devConfig, LOCAL_RUNTIME) - - const cleanup = () => { - aioLogger.debug('stopping local OpenWhisk stack...') - res.proc.kill() - - aioLogger.debug('removing wskdebug tmp .env file...') - if (fs.existsSync(devConfig.envFile)) { - fs.unlinkSync(devConfig.envFile) - } - } - - return { - config: devConfig, - cleanup - } -} - -/** - * @param {object} config the app config - * @param {Function} [log] function to log application logs - * @returns {RunDevLocalObject} the RunDevLocalObject - */ -function loadLocalDevConfig (config, log) { - const devConfig = cloneDeep(config) - devConfig.envFile = path.join(config.app.dist, '.env.local') - log && log('setting local openwhisk credentials...') - devConfig.ow = { ...devConfig.ow, ...LOCAL_RUNTIME } - return devConfig -} - -/** - * Writes the local debugging .env file - * - * @param {object} appConfig the app config - * @param {RuntimeCredentials} runtimeCredentials the runtime credentials - */ -async function writeLocalEnvFile (appConfig, runtimeCredentials) { - const { root, envFile } = appConfig - const { dist } = appConfig.app - const { namespace, auth, apihost } = runtimeCredentials - - fs.ensureDirSync(dist) - const envFilePath = rtLibUtils._absApp(root, envFile) - - await fs.outputFile(envFilePath, dedent(` - # This file is auto-generated, do not edit. - # The items below are temporary credentials for local debugging - OW_NAMESPACE=${namespace} - OW_AUTH=${auth} - OW_APIHOST=${apihost} - `)) -} - -module.exports = { runLocalRuntime: runDevLocal, loadLocalDevConfig } diff --git a/src/lib/vscode.js b/src/lib/vscode.js deleted file mode 100644 index 1f3963269..000000000 --- a/src/lib/vscode.js +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:vscode', { provider: 'debug' }) -const rtLibUtils = require('@adobe/aio-lib-runtime').utils -const fs = require('fs-extra') -const path = require('path') -const yeoman = require('yeoman-environment') -const generators = require('@adobe/generator-aio-app') - -const LAUNCH_JSON_FILE = '.vscode/launch.json' -const LAUNCH_JSON_FILE_BACKUP = '.vscode/launch.json.save' - -/** @private */ -function files (config) { - return () => ({ - backupFile: rtLibUtils._absApp(config.root, LAUNCH_JSON_FILE_BACKUP), - mainFile: rtLibUtils._absApp(config.root, LAUNCH_JSON_FILE) - }) -} - -/** @private */ -function update (config) { - const _files = files(config) - return async (props) => { - const { backupFile, mainFile } = _files() - - fs.ensureDirSync(path.dirname(mainFile)) - if (fs.existsSync(mainFile)) { - if (!fs.existsSync(backupFile)) { - fs.moveSync(mainFile, backupFile) - } - } - - const env = yeoman.createEnv() - // by default yeoman runs the install, we control installation from the app plugin - env.options = { skipInstall: true } - const gen = env.instantiate(generators['add-vscode-config'], { - options: { - 'app-config': config, - 'env-file': config.envFile, - 'frontend-url': props.frontEndUrl, - 'skip-prompt': true - } - }) - await env.runGenerator(gen) - } -} - -/** @private */ -function cleanup (config) { - const _files = files(config) - return () => { - const { backupFile, mainFile } = _files() - - if (fs.existsSync(mainFile) && !fs.existsSync(backupFile)) { - aioLogger.debug(`removing ${mainFile}...`) - const vscodeDir = path.dirname(mainFile) - fs.unlinkSync(mainFile) - if (fs.readdirSync(vscodeDir).length === 0) { - fs.rmdirSync(vscodeDir) - } - } - - if (fs.existsSync(backupFile)) { - aioLogger.debug(`restoring previous ${mainFile}`) - fs.moveSync(backupFile, mainFile, { overwrite: true }) - } - } -} - -module.exports = (config) => ({ - files: files(config), - update: update(config), - cleanup: cleanup(config) -}) diff --git a/test/commands/app/build.test.js b/test/commands/app/build.test.js index ac1ec93ac..38680a6da 100644 --- a/test/commands/app/build.test.js +++ b/test/commands/app/build.test.js @@ -129,7 +129,7 @@ const sampleAppConfig = { dependencies: { dependency1: { location: 'fake.com/package' } } } }, - actions: { src: '/actions', dist: '/dist/actions', isLocal: true }, + actions: { src: '/actions', dist: '/dist/actions' }, root: path.resolve('test/__fixtures__/sample-app') } diff --git a/test/commands/app/deploy.test.js b/test/commands/app/deploy.test.js index 7e63499fe..e0fed79d5 100644 --- a/test/commands/app/deploy.test.js +++ b/test/commands/app/deploy.test.js @@ -874,8 +874,7 @@ describe('run', () => { expect(command.config.runHook).toHaveBeenCalledWith('deploy-actions', expect.objectContaining({ appConfig: expect.any(Object), - filterEntities: [], - isLocalDev: false + filterEntities: [] })) expect(command.config.runHook).toHaveBeenCalledTimes(2) expect(mockRuntimeLib.deployActions).toHaveBeenCalledTimes(1) diff --git a/test/commands/app/geturl.test.js b/test/commands/app/geturl.test.js index 2916b1742..8312562e8 100644 --- a/test/commands/app/geturl.test.js +++ b/test/commands/app/geturl.test.js @@ -15,7 +15,6 @@ const BaseCommand = require('../../../src/BaseCommand') const mockRuntimeLib = require('@adobe/aio-lib-runtime') const dataMocks = require('../../data-mocks/config-loader') -const { loadLocalDevConfig } = require('../../../src/lib/run-local-runtime') const createFullConfig = (aioConfig = {}, appFixtureName = 'legacy-app') => { const appConfig = dataMocks(appFixtureName, aioConfig) @@ -59,17 +58,12 @@ describe('run', () => { beforeEach(() => { mockRuntimeLib.utils.getActionUrls.mockReset() mockRuntimeLib.utils.getActionUrls.mockImplementation(jest.fn( - (config, isRemoteDev, isLocalDev) => { + (config, isRemoteDev) => { if (isRemoteDev) { return { action: 'https://fake_ns.adobeioruntime.net/api/v1/web/sample-app-1.0.0/action' } } - if (isLocalDev) { - return { - action: 'http://localhost:3233/api/v1/web/sample-app-1.0.0/action' - } - } // !isRemoteDev return { action: 'https://fake_ns.adobeio-static.net/api/v1/web/sample-app-1.0.0/action' @@ -274,24 +268,4 @@ describe('run', () => { await command.run() expect(command.error).toHaveBeenCalledWith(new Error('No action with name invalid found')) }) - - test('get local actions, --local', async () => { - const appConfig = createFullConfig(command.appConfig) - command.getFullConfig.mockResolvedValueOnce(appConfig) - - const res = { - runtime: { - action: 'http://localhost:3233/api/v1/web/sample-app-1.0.0/action' - } - } - command.argv = ['--local'] - const urls = await command.run() - expect(command.error).toHaveBeenCalledTimes(0) - Object.values(appConfig.all).forEach(config => { - const localConfig = loadLocalDevConfig(config) - expect(mockRuntimeLib.utils.getActionUrls).toHaveBeenCalledWith(localConfig, false, true) - }) - expect(urls).toEqual(res) - expect(command.log).toHaveBeenCalledWith(expect.stringContaining(urls.runtime.action)) - }) }) diff --git a/test/commands/app/run.test.js b/test/commands/app/run.test.js index ab0063329..5f3b939da 100644 --- a/test/commands/app/run.test.js +++ b/test/commands/app/run.test.js @@ -156,10 +156,6 @@ describe('run command definition', () => { }) test('flags', async () => { - expect(typeof TheCommand.flags.local).toBe('object') - expect(typeof TheCommand.flags.local.description).toBe('string') - expect(TheCommand.flags.local.exclusive).toEqual(['no-actions']) - expect(typeof TheCommand.flags.serve).toBe('object') expect(typeof TheCommand.flags.serve.description).toBe('string') expect(TheCommand.flags.serve.default).toEqual(true) @@ -240,7 +236,7 @@ describe('run', () => { mockFSExists([PRIVATE_KEY_PATH, PUB_CERT_PATH]) mockRunDev.mockImplementation((config, dataDir, options, logFunc) => { logFunc('boo') - expect(options.isLocal).toBe(undefined) + // expect(options.isLocal).toBe(undefined) }) command.argv = ['--verbose'] command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig)) @@ -254,7 +250,6 @@ describe('run', () => { mockFSExists([PRIVATE_KEY_PATH, PUB_CERT_PATH]) mockRunDev.mockImplementation((config, dataDir, options, logFunc) => { logFunc('boo') - expect(options.isLocal).toBe(undefined) }) command.argv = [] command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig)) @@ -286,8 +281,7 @@ describe('run', () => { expect(mockRunDev).toHaveBeenCalledWith(appConfig.application, expect.any(String), expect.objectContaining({ parcel: expect.objectContaining({ logLevel: 'warn' - }), - isLocal: undefined + }) }), expect.any(Function), expect.any(Function)) }) @@ -315,57 +309,7 @@ describe('run', () => { expect(mockRunDev).toHaveBeenCalledWith(appConfig.application, expect.any(String), expect.objectContaining({ parcel: expect.objectContaining({ logLevel: 'verbose' - }), - isLocal: undefined - }), expect.any(Function), expect.any(Function)) - }) - - test('app:run with --local NOT Apple Silicon', async () => { - mockFSExists([PRIVATE_KEY_PATH, PUB_CERT_PATH]) - mockRunDev.mockImplementation((config, dataDir, options, logFunc) => { - expect(options.isLocal).toBe(true) - }) - - command.argv = ['--local'] - command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig)) - - await command.run() - expect(command.error).toHaveBeenCalledTimes(0) - expect(mockRunDev).toHaveBeenCalledTimes(1) - }) - - test('app:run with --local with Apple Silicon', async () => { - mockFSExists([PRIVATE_KEY_PATH, PUB_CERT_PATH]) - mockRunDev.mockImplementation((config, dataDir, options, logFunc) => { - expect(options.isLocal).toBe(true) - }) - - os.cpus.mockImplementation(() => [{ model: 'Apple processor' }]) - command.argv = ['--local'] - command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig)) - - await command.run() - // this should be more like the following ... - // await expect(command.run().rejects.toThrow('The --local flag is not supported on Apple Silicon Macs.')) - expect(command.error).toHaveBeenCalledWith('The --local flag is not supported on Apple Silicon Macs.') - }) - - test('app:run with --local --verbose', async () => { - mockFSExists([PRIVATE_KEY_PATH, PUB_CERT_PATH]) - command.argv = ['--local', '--verbose'] - const appConfig = createAppConfig(command.appConfig) - command.getAppExtConfigs.mockResolvedValueOnce(appConfig) - // apple silicon would block this test - os.cpus.mockImplementation(() => [{ model: 'Intel Pentium MMX' }]) - - await command.run() - expect(command.error).toHaveBeenCalledTimes(0) - expect(mockRunDev).toHaveBeenCalledTimes(1) - expect(mockRunDev).toHaveBeenCalledWith(appConfig.application, expect.any(String), expect.objectContaining({ - parcel: expect.objectContaining({ - logLevel: 'verbose' - }), - isLocal: true + }) }), expect.any(Function), expect.any(Function)) }) diff --git a/test/commands/lib/app-helper.test.js b/test/commands/lib/app-helper.test.js index 8bd610bc9..1e6af12f9 100644 --- a/test/commands/lib/app-helper.test.js +++ b/test/commands/lib/app-helper.test.js @@ -14,10 +14,6 @@ governing permissions and limitations under the License. jest.unmock('@adobe/aio-lib-runtime') const mockFetch = jest.fn() -jest.mock('@adobe/aio-lib-core-networking', () => ({ - createFetch: jest.fn(() => mockFetch), - HttpExponentialBackoff: jest.fn() -})) jest.mock('@adobe/aio-lib-core-config') jest.mock('execa') @@ -51,78 +47,6 @@ beforeEach(() => { const getMockConfig = require('../../data-mocks/config-loader') -test('isDockerRunning', async () => { - let result - - expect(appHelper.isDockerRunning).toBeDefined() - expect(appHelper.isDockerRunning).toBeInstanceOf(Function) - - execa.mockImplementation(() => { - return { stdout: jest.fn() } - }) - - result = await appHelper.isDockerRunning() - expect(result).toBeTruthy() - - execa.mockImplementation((cmd, args) => { - if (cmd === 'docker' && args.includes('info')) { - throw new Error('fake error') - } - return { stdout: jest.fn() } - }) - - result = await appHelper.isDockerRunning() - expect(result).toBeFalsy() -}) - -test('hasDockerCLI', async () => { - let result - - expect(appHelper.hasDockerCLI).toBeDefined() - expect(appHelper.hasDockerCLI).toBeInstanceOf(Function) - - execa.mockImplementation(() => { - return { stdout: jest.fn() } - }) - - result = await appHelper.hasDockerCLI() - expect(result).toBeTruthy() - - execa.mockImplementation((cmd, args) => { - if (cmd === 'docker' && args.includes('-v')) { - throw new Error('fake error') - } - return { stdout: jest.fn() } - }) - - result = await appHelper.hasDockerCLI() - expect(result).toBeFalsy() -}) - -test('hasJavaCLI', async () => { - let result - - expect(appHelper.hasJavaCLI).toBeDefined() - expect(appHelper.hasJavaCLI).toBeInstanceOf(Function) - - execa.mockImplementation(() => { - return { stdout: jest.fn() } - }) - - result = await appHelper.hasJavaCLI() - expect(result).toBeTruthy() - - execa.mockImplementation((cmd) => { - if (cmd === 'java') { - throw new Error('fake error') - } - return { stdout: jest.fn() } - }) - - result = await appHelper.hasJavaCLI() - expect(result).toBeFalsy() -}) - test('isNpmInstalled', () => { expect(appHelper.isNpmInstalled).toBeDefined() expect(appHelper.isNpmInstalled).toBeInstanceOf(Function) @@ -424,145 +348,6 @@ test('checkFile', () => { expect(() => appHelper.checkFile('no/exists')).toThrow('no/exists is not a valid file') }) -test('downloadOWJar failed (server has response, but not ok)', async () => { - const response = { - ok: false, - statusText: 'some error' - } - mockFetch.mockResolvedValueOnce(response) - - const url = 'https://some.url' - const result = appHelper.downloadOWJar(url, 'foo/bar') - await expect(result).rejects.toEqual(new Error(`unexpected response while downloading '${url}': ${response.statusText}`)) -}) - -test('downloadOWJar failed (no server response, fetch exception)', async () => { - const err = new Error('some fetch error') - mockFetch.mockRejectedValueOnce(err) - - const url = 'https://some.url' - const result = appHelper.downloadOWJar(url, 'foo/bar') - await expect(result).rejects.toEqual(new Error(`connection error while downloading '${url}', are you online?`)) -}) - -test('downloadOWJar ok', async () => { - const response = { - ok: true, - statusText: 'success', - body: { - pipe: jest.fn(), - on: jest.fn() - } - } - - const url = 'https://some.url' - const fileToWrite = 'foo/bar' - - fs.createWriteStream.mockImplementation((outFile) => { - expect(outFile).toEqual(fileToWrite) - return { - on: jest.fn((eventName, fn) => { - if (eventName === 'finish') { // immediately call it - fn() - } - }) - } - }) - mockFetch.mockResolvedValueOnce(response) - - const result = appHelper.downloadOWJar(url, fileToWrite) - await expect(result).resolves.toBeUndefined() -}) - -test('downloadOWJar (server connected ok, streaming error)', async () => { - const streamError = new Error('stream error') - const response = { - ok: true, - statusText: 'success', - body: { - pipe: jest.fn(), - on: jest.fn((eventName, fn) => { - expect(eventName).toEqual('error') - fn(streamError) // immediately call it - }) - } - } - - const url = 'https://some.url' - const fileToWrite = 'foo/bar' - - mockFetch.mockResolvedValueOnce(response) - - const result = appHelper.downloadOWJar(url, fileToWrite) - await expect(result).rejects.toEqual(streamError) -}) - -test('runOpenWhiskJar ok', async () => { - mockFetch.mockResolvedValue({ ok: true }) - execa.mockReturnValue({ stdout: jest.fn() }) - - const result = appHelper.runOpenWhiskJar('jar', 'conf') - - await expect(result).resolves.toEqual({ - proc: expect.any(Object) - }) - expect(mockFetch).toHaveBeenCalledTimes(1) - expect(execa).toHaveBeenCalledWith('java', expect.arrayContaining(['jar', 'conf']), {}) -}) - -test('runOpenWhiskJar with AIO_OW_JVM_ARGS env var is passed to execa', async () => { - mockFetch.mockResolvedValue({ ok: true }) - execa.mockReturnValue({ stdout: jest.fn() }) - - aioConfig.get.mockReturnValueOnce('arg1 arg2') - - const result = appHelper.runOpenWhiskJar('jar', 'conf') - - await expect(result).resolves.toEqual({ - proc: expect.any(Object) - }) - expect(mockFetch).toHaveBeenCalledTimes(1) - expect(execa).toHaveBeenCalledWith('java', expect.arrayContaining(['arg1', 'arg2', 'jar', 'conf']), {}) -}) - -test('waitForOpenWhiskReadiness timeout', async () => { - const host = 'my-host' - const period = 5000 - const timeout = 5000 - const endTime = Date.now() - 1000 // ends now - const status = 'FAIL' - - const waitFunc = jest.fn((_period) => { - expect(_period).toEqual(period) - }) - const result = appHelper.waitForOpenWhiskReadiness(host, endTime, period, timeout, status, waitFunc) - - await expect(result).rejects.toEqual(new Error(`local openwhisk stack startup timed out after ${timeout}ms due to ${status}`)) - expect(mockFetch).toHaveBeenCalledTimes(0) - expect(waitFunc).toHaveBeenCalledTimes(0) -}) - -test('waitForOpenWhiskReadiness (fail, retry, then success)', async () => { - const host = 'my-host' - const period = 5000 - const timeout = 5000 - const endTime = Date.now() + 5000 - const status = null - - const waitFunc = jest.fn((_period) => { - expect(_period).toEqual(period) - }) - mockFetch - .mockRejectedValueOnce(new Error('some error')) // first fail (fetch exception) - .mockRejectedValueOnce({ ok: false }) // second fail (response not ok) - .mockResolvedValue({ ok: true }) // finally success - const result = appHelper.waitForOpenWhiskReadiness(host, endTime, period, timeout, status, waitFunc) - - await expect(result).resolves.not.toBeDefined() - expect(mockFetch).toHaveBeenCalledTimes(3) - expect(waitFunc).toHaveBeenCalledTimes(2) -}) - describe('warnIfOverwriteServicesInProductionWorkspace', () => { const logSpy = jest.spyOn(console, 'error') beforeEach(() => { diff --git a/test/commands/lib/ow-local.test.js b/test/commands/lib/ow-local.test.js deleted file mode 100644 index 55d6e94d3..000000000 --- a/test/commands/lib/ow-local.test.js +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -const path = require('path') - -jest.mock('execa') -const execa = require('execa') - -const aioLogger = require('@adobe/aio-lib-core-logging')() - -beforeEach(() => { - execa.sync.mockReset() - delete process.env.OW_LOCAL_NAMESPACE - delete process.env.OW_CONFIG_RUNTIMES_FILE - delete process.env.OW_LOCAL_AUTH - delete process.env.OW_JAR_URL - delete process.env.OW_LOCAL_APIHOST - aioLogger.debug.mockReset() -}) - -describe('owlocal', () => { - test('exports', () => { - let owLocal - jest.isolateModules(() => { - owLocal = require('../../../src/lib/owlocal') - }) - expect(typeof owLocal.getDockerNetworkAddress).toEqual('function') - expect(owLocal.OW_CONFIG_RUNTIMES_FILE).toEqual(expect.stringContaining(path.normalize('/bin/openwhisk-standalone-config/runtimes.json'))) - expect(owLocal.OW_JAR_URL).toMatch('https://github.com/adobe/aio-cli-plugin-app/releases/download/6.2.0/openwhisk-standalone.jar') - expect(owLocal.OW_JAR_PATH).toMatch(path.join('openwhisk', 'openwhisk-standalone.jar')) - expect(owLocal.OW_LOCAL_NAMESPACE).toMatch('guest') - expect(owLocal.OW_LOCAL_AUTH).toMatch('23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP') - }) - - test('exports can be overwritten by process env', () => { - process.env.OW_LOCAL_NAMESPACE = 'dude' - process.env.OW_CONFIG_RUNTIMES_FILE = 'file' - process.env.OW_LOCAL_AUTH = '123' - process.env.OW_JAR_URL = 'https://example.com/openwhisk/foo/bar.jar' - process.env.OW_LOCAL_APIHOST = 'fake.com' - let owLocal - jest.isolateModules(() => { - owLocal = require('../../../src/lib/owlocal') - }) - expect(owLocal.OW_CONFIG_RUNTIMES_FILE).toEqual('file') - expect(owLocal.OW_JAR_URL).toMatch('https://example.com/openwhisk/foo/bar.jar') - expect(owLocal.OW_JAR_PATH).toMatch(path.join('openwhisk', 'foo', 'bar.jar')) - expect(owLocal.OW_LOCAL_NAMESPACE).toMatch('dude') - expect(owLocal.OW_LOCAL_AUTH).toMatch('123') - expect(owLocal.OW_LOCAL_APIHOST).toMatch('fake.com') - }) - - test('use defaults for OW_JAR_PATH if path not found in OW_JAR_URL', () => { - process.env.OW_JAR_URL = 'https://example.com/some/path' - let owLocal - jest.isolateModules(() => { - owLocal = require('../../../src/lib/owlocal') - }) - expect(owLocal.OW_JAR_URL).toMatch('https://example.com/some/path') - expect(owLocal.OW_JAR_PATH).toMatch(path.join('openwhisk', 'openwhisk-standalone.jar')) - }) - - describe('getDockerNetworkAddress', () => { - test('is not windows or mac', () => { - Object.defineProperty(process, 'platform', { - value: 'abc' - }) - execa.sync.mockReturnValue( - { stdout: JSON.stringify([{ IPAM: { Config: [{ Gateway: 'example.com' }] } }]) } - ) - - let owLocal - jest.isolateModules(() => { - owLocal = require('../../../src/lib/owlocal') - }) - expect(owLocal.getDockerNetworkAddress()).toEqual('http://example.com:3233') - expect(execa.sync).toHaveBeenCalledWith('docker', ['network', 'inspect', 'bridge']) - expect(owLocal.OW_LOCAL_APIHOST).toEqual('http://example.com:3233') - }) - - test('is windows', () => { - Object.defineProperty(process, 'platform', { - value: 'win32' - }) - let owLocal - jest.isolateModules(() => { - owLocal = require('../../../src/lib/owlocal') - }) - expect(owLocal.getDockerNetworkAddress()).toBe('http://localhost:3233') - expect(execa.sync).not.toHaveBeenCalled() - expect(owLocal.OW_LOCAL_APIHOST).toEqual('http://localhost:3233') - }) - - test('is mac', () => { - Object.defineProperty(process, 'platform', { - value: 'darwin' - }) - let owLocal - jest.isolateModules(() => { - owLocal = require('../../../src/lib/owlocal') - }) - expect(owLocal.getDockerNetworkAddress()).toBe('http://localhost:3233') - expect(execa.sync).not.toHaveBeenCalled() - expect(owLocal.OW_LOCAL_APIHOST).toEqual('http://localhost:3233') - }) - - test('if execa fails', () => { - Object.defineProperty(process, 'platform', { - value: 'abc' - }) - const errorMessage = 'fake error' - execa.sync.mockImplementation(() => { throw new Error(errorMessage) }) - let owLocal - jest.isolateModules(() => { - owLocal = require('../../../src/lib/owlocal') - }) - expect(owLocal.getDockerNetworkAddress()).toBe('http://localhost:3233') // fall back to default - expect(execa.sync).toHaveBeenCalledWith('docker', ['network', 'inspect', 'bridge']) - expect(owLocal.OW_LOCAL_APIHOST).toEqual('http://localhost:3233') - expect(execa.sync).toThrow(errorMessage) - }) - }) -}) diff --git a/test/commands/lib/run-dev.test.js b/test/commands/lib/run-dev.test.js index eff407ba0..b81c5abac 100644 --- a/test/commands/lib/run-dev.test.js +++ b/test/commands/lib/run-dev.test.js @@ -11,13 +11,11 @@ governing permissions and limitations under the License. */ const runDev = require('../../../src/lib/run-dev') -const { runLocalRuntime } = require('../../../src/lib/run-local-runtime') const cloneDeep = require('lodash.clonedeep') const dataMocks = require('../../data-mocks/config-loader') const defaults = require('../../../src/lib/defaults') const getPort = require('get-port') -const VsCode = require('../../../src/lib/vscode') const { bundle } = require('@adobe/aio-lib-web') const bundleServe = require('../../../src/lib/bundle-serve') const serve = require('../../../src/lib/serve') @@ -29,11 +27,9 @@ const logPoller = require('../../../src/lib/log-poller') const appHelper = require('../../../src/lib/app-helper') const actionsWatcher = require('../../../src/lib/actions-watcher') -jest.mock('../../../src/lib/run-local-runtime') jest.mock('../../../src/lib/actions-watcher') jest.mock('../../../src/lib/app-helper') jest.mock('../../../src/lib/cleanup') -jest.mock('../../../src/lib/vscode') jest.mock('../../../src/lib/bundle-serve') jest.mock('../../../src/lib/serve') jest.mock('../../../src/lib/build-actions') @@ -53,7 +49,6 @@ const FRONTEND_URL = `https://localhost:${defaults.defaultHttpServerPort}` /// START OF TESTS ///////////////////// beforeEach(() => { - runLocalRuntime.mockReset() buildActions.mockReset() deployActions.mockReset() bundle.mockReset() @@ -99,13 +94,6 @@ beforeEach(() => { wait: jest.fn() } }) - - VsCode.mockImplementation(() => { - return { - update: jest.fn(), - cleanup: jest.fn() - } - }) }) test('no parameters (exception before processing)', async () => { @@ -115,11 +103,6 @@ test('no parameters (exception before processing)', async () => { test('port to use', async () => { const config = cloneDeep(createAppConfig().application) - runLocalRuntime.mockImplementation(() => ({ - config, - cleanup: jest.fn() - }) - ) // use default port getPort.mockImplementationOnce(params => { @@ -143,279 +126,212 @@ test('port to use', async () => { await runDev(config, DATA_DIR) }) -test('isLocal false', async () => { - const config = cloneDeep(createAppConfig().application) - runLocalRuntime.mockImplementation(() => ({ - config, - cleanup: jest.fn() - }) - ) - - const result = runDev(config, DATA_DIR, { isLocal: true }) - await expect(result).resolves.toEqual(FRONTEND_URL) - - expect(runLocalRuntime).toHaveBeenCalled() // only called when options.isLocal is true - expect(buildActions).toHaveBeenCalled() // only with backend, and !options.skipActions - expect(deployActions).toHaveBeenCalled() // only with backend - expect(bundle).toHaveBeenCalled() // only with frontend + !options.skipServe + !build-static hook - expect(serve).not.toHaveBeenCalled() // only with frontend + !options.skipServe + build-static hook - expect(bundleServe).toHaveBeenCalled() // only with frontend + !options.skipServe + !build-static hook + !serve-static hook - expect(mockRuntimeLib.utils.getActionUrls).toHaveBeenCalled() // only if it has frontend and backend (config.json write) - expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).not.toHaveBeenCalled() // only called when options.isLocal is false - expect(logPoller.run).not.toHaveBeenCalled() // only with backend and options.fetchLogs - expect(actionsWatcher).toHaveBeenCalled() // only with backend -}) - -test('isLocal true, fetchLogs true, cleanup coverage', async () => { +test('isLocal false, build-static hook set, serve-static hook set)', async () => { const config = cloneDeep(createAppConfig().application) - const mockCleanup = { - runLocalRuntime: jest.fn(), - actionsWatcher: jest.fn(), - bundleServe: jest.fn(), - vscode: jest.fn(), - logPoller: jest.fn() - } - - Cleanup.mockImplementation(() => { - const fns = [] - return { - add: jest.fn((fn) => { - fns.push(fn) - }), - run: jest.fn(() => { - fns.forEach(fn => fn()) - }), - wait: jest.fn(() => { - fns.forEach(fn => fn()) - }) - } - }) + // script contents are the hook names, for easy reference + config.hooks['build-static'] = 'build-static' + config.hooks['serve-static'] = 'serve-static' - VsCode.mockImplementation(() => { - return { - update: jest.fn(), - cleanup: mockCleanup.vscode + appHelper.runScript.mockImplementation(script => { + if (script === 'build-static' || script === 'serve-static') { + return {} } }) - runLocalRuntime.mockImplementation(() => ({ config, cleanup: mockCleanup.runLocalRuntime })) - actionsWatcher.mockImplementation(() => ({ cleanup: mockCleanup.actionsWatcher })) - bundleServe.mockImplementation(() => ({ url: FRONTEND_URL, cleanup: mockCleanup.bundleServe })) - logPoller.run.mockImplementation(() => ({ cleanup: mockCleanup.logPoller })) - - const result = runDev(config, DATA_DIR, { isLocal: true, fetchLogs: true }) - await expect(result).resolves.toEqual(FRONTEND_URL) + const result = runDev(config, DATA_DIR) + await expect(result).resolves.toEqual(undefined) // since serve-static is an app hook - expect(mockCleanup.runLocalRuntime).toHaveBeenCalled() - expect(mockCleanup.actionsWatcher).toHaveBeenCalled() - expect(mockCleanup.bundleServe).toHaveBeenCalled() - expect(mockCleanup.logPoller).toHaveBeenCalled() - expect(mockCleanup.vscode).toHaveBeenCalled() + expect(buildActions).toHaveBeenCalled() + expect(deployActions).toHaveBeenCalled() + expect(bundle).not.toHaveBeenCalled() + expect(serve).not.toHaveBeenCalled() + expect(bundleServe).not.toHaveBeenCalled() + expect(mockRuntimeLib.utils.getActionUrls).toHaveBeenCalled() + expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).toHaveBeenCalled() + expect(logPoller.run).not.toHaveBeenCalled() + expect(actionsWatcher).toHaveBeenCalled() }) -test('isLocal true, fetchLogs true', async () => { +test('isLocal false, build-static hook set, serve-static hook not set)', async () => { const config = cloneDeep(createAppConfig().application) - runLocalRuntime.mockImplementation(() => ({ - config, - cleanup: jest.fn() + // script contents are the hook names, for easy reference + config.hooks['build-static'] = 'build-static' + + appHelper.runScript.mockImplementation(script => { + if (script === 'build-static') { + return {} + } }) - ) - const result = runDev(config, DATA_DIR, { isLocal: true, fetchLogs: true }) - await expect(result).resolves.toEqual(FRONTEND_URL) + const result = runDev(config, DATA_DIR, { }) + await expect(result).resolves.toEqual(FRONTEND_URL) // we use our own serve + + expect(buildActions).toHaveBeenCalled() + expect(deployActions).toHaveBeenCalled() + expect(bundle).not.toHaveBeenCalled() + expect(serve).toHaveBeenCalled() + expect(bundleServe).not.toHaveBeenCalled() + expect(mockRuntimeLib.utils.getActionUrls).toHaveBeenCalled() + expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).toHaveBeenCalled() + expect(logPoller.run).not.toHaveBeenCalled() + expect(actionsWatcher).toHaveBeenCalled() }) -test('isLocal true, exception thrown while processing', async () => { +test('fetchLogs true)', async () => { const config = cloneDeep(createAppConfig().application) - runLocalRuntime.mockRejectedValue('error') - const result = runDev(config, DATA_DIR, { isLocal: true }) - await expect(result).rejects.toEqual('error') + const result = runDev(config, DATA_DIR, { fetchLogs: true }) + await expect(result).resolves.toEqual(FRONTEND_URL) // we use our own serve + + expect(buildActions).toHaveBeenCalled() + expect(deployActions).toHaveBeenCalled() + expect(mockRuntimeLib.utils.getActionUrls).toHaveBeenCalled() + expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).toHaveBeenCalled() + expect(logPoller.run).toHaveBeenCalled() + expect(actionsWatcher).toHaveBeenCalled() }) -test('isLocal true, frontend false, backend false', async () => { +test('fetchLogs false)', async () => { const config = cloneDeep(createAppConfig().application) - config.app.hasFrontend = false - config.app.hasBackend = false - runLocalRuntime.mockImplementation(() => ({ - config, - cleanup: jest.fn() - }) - ) - - const result = runDev(config, DATA_DIR, { isLocal: true }) - await expect(result).resolves.toEqual(undefined) // no frontend, no url + const result = runDev(config, DATA_DIR, { fetchLogs: false }) + await expect(result).resolves.toEqual(FRONTEND_URL) // we use our own serve - // nothing is called because there is nothing to do - expect(runLocalRuntime).not.toHaveBeenCalled() - expect(buildActions).not.toHaveBeenCalled() - expect(deployActions).not.toHaveBeenCalled() - expect(bundle).not.toHaveBeenCalled() - expect(serve).not.toHaveBeenCalled() - expect(bundleServe).not.toHaveBeenCalled() - expect(mockRuntimeLib.utils.getActionUrls).not.toHaveBeenCalled() - expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).not.toHaveBeenCalled() + expect(buildActions).toHaveBeenCalled() + expect(deployActions).toHaveBeenCalled() + expect(mockRuntimeLib.utils.getActionUrls).toHaveBeenCalled() + expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).toHaveBeenCalled() expect(logPoller.run).not.toHaveBeenCalled() - expect(actionsWatcher).not.toHaveBeenCalled() + expect(actionsWatcher).toHaveBeenCalled() }) -test('isLocal true, frontend true, backend false', async () => { +test('runDev does not always force builds', async () => { const config = cloneDeep(createAppConfig().application) - config.app.hasFrontend = true - config.app.hasBackend = false - runLocalRuntime.mockImplementation(() => ({ - config, - cleanup: jest.fn() - }) - ) - - const result = runDev(config, DATA_DIR, { isLocal: true }) - await expect(result).resolves.toEqual(FRONTEND_URL) + await runDev(config, DATA_DIR) - expect(runLocalRuntime).not.toHaveBeenCalled() - expect(buildActions).not.toHaveBeenCalled() - expect(deployActions).not.toHaveBeenCalled() - expect(bundle).toHaveBeenCalled() - expect(serve).not.toHaveBeenCalled() - expect(bundleServe).toHaveBeenCalled() - expect(mockRuntimeLib.utils.getActionUrls).not.toHaveBeenCalled() - expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).not.toHaveBeenCalled() - expect(logPoller.run).not.toHaveBeenCalled() - expect(actionsWatcher).not.toHaveBeenCalled() + expect(buildActions).toHaveBeenCalled() + expect(buildActions).toHaveBeenCalledWith(expect.any(Object) /* config */, + null /* filterActions */, + false /* forceBuild */) }) -test('isLocal true, frontend true, skipActions true', async () => { +test('calls cleanup on exception - hasBackend:false', async () => { const config = cloneDeep(createAppConfig().application) config.app.hasFrontend = true config.app.hasBackend = false - runLocalRuntime.mockImplementation(() => ({ - config, - cleanup: jest.fn() + Cleanup.mockImplementation(() => { + const fns = [] + return { + add: jest.fn((fn) => { + fns.push(fn) + }), + run: jest.fn(() => { + fns.forEach(fn => fn()) + }), + wait: jest.fn(() => { + throw new Error('Expect the unexpected') + }) + } }) - ) - - const result = runDev(config, DATA_DIR, { isLocal: true, skipActions: true }) - await expect(result).resolves.toEqual(FRONTEND_URL) - expect(runLocalRuntime).not.toHaveBeenCalled() - expect(buildActions).not.toHaveBeenCalled() - expect(deployActions).not.toHaveBeenCalled() - expect(bundle).toHaveBeenCalled() - expect(serve).not.toHaveBeenCalled() - expect(bundleServe).toHaveBeenCalled() - expect(mockRuntimeLib.utils.getActionUrls).not.toHaveBeenCalled() - expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).not.toHaveBeenCalled() - expect(logPoller.run).not.toHaveBeenCalled() - expect(actionsWatcher).not.toHaveBeenCalled() + const result = runDev(config, DATA_DIR) + await expect(result).rejects.toThrow('Expect the unexpected') }) -test('isLocal true, frontend true, backend true, skipServe true', async () => { +test('calls cleanup on exception - hasFrontend:false', async () => { const config = cloneDeep(createAppConfig().application) - config.app.hasFrontend = true + config.app.hasFrontend = false config.app.hasBackend = false - runLocalRuntime.mockImplementation(() => ({ - config, - cleanup: jest.fn() + Cleanup.mockImplementation(() => { + const fns = [] + return { + add: jest.fn((fn) => { + fns.push(fn) + }), + run: jest.fn(() => { + fns.forEach(fn => fn()) + }), + wait: jest.fn(() => { + throw new Error('Expect the unexpected') + }) + } }) - ) - - const result = runDev(config, DATA_DIR, { isLocal: true, skipServe: true }) - await expect(result).resolves.toEqual(undefined) // nothing was served, no url - - expect(runLocalRuntime).not.toHaveBeenCalled() - expect(buildActions).not.toHaveBeenCalled() - expect(deployActions).not.toHaveBeenCalled() - expect(bundle).not.toHaveBeenCalled() - expect(serve).not.toHaveBeenCalled() - expect(bundleServe).not.toHaveBeenCalled() - expect(mockRuntimeLib.utils.getActionUrls).not.toHaveBeenCalled() - expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).not.toHaveBeenCalled() - expect(logPoller.run).not.toHaveBeenCalled() - expect(actionsWatcher).not.toHaveBeenCalled() -}) - -test('isLocal false validation', async () => { - const config = cloneDeep(createAppConfig().application) - const result = runDev(config, DATA_DIR, { isLocal: false }) - await expect(result).resolves.toEqual(FRONTEND_URL) - - expect(runLocalRuntime).not.toHaveBeenCalled() // only called when options.isLocal is true - expect(buildActions).toHaveBeenCalled() // only with backend, and !options.skipActions - expect(deployActions).toHaveBeenCalled() // only with backend - expect(bundle).toHaveBeenCalled() // only with frontend and !options.skipServe and build-static hook *not* set - expect(serve).not.toHaveBeenCalled() // only with frontend and if we don't use the default bundler (build-static hook *is* set) - expect(bundleServe).toHaveBeenCalled() // only with frontend and if we use the default bundler (build-static hook *not* set) - expect(mockRuntimeLib.utils.getActionUrls).toHaveBeenCalled() // only if it has frontend and backend (config.json write) - expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).toHaveBeenCalled() // only called when options.isLocal is false - expect(logPoller.run).not.toHaveBeenCalled() // only with backend and options.fetchLogs - expect(actionsWatcher).toHaveBeenCalled() // only with backend + const result = runDev(config, DATA_DIR) + await expect(result).rejects.toThrow('Expect the unexpected') }) -test('isLocal false, build-static hook set, serve-static hook set)', async () => { +test('calls cleanup on exception - hasBackend && !skipActions', async () => { const config = cloneDeep(createAppConfig().application) - // script contents are the hook names, for easy reference - config.hooks['build-static'] = 'build-static' - config.hooks['serve-static'] = 'serve-static' + config.app.hasFrontend = false + config.app.hasBackend = true - appHelper.runScript.mockImplementation(script => { - if (script === 'build-static' || script === 'serve-static') { - return {} + Cleanup.mockImplementation(() => { + const fns = [] + return { + add: jest.fn((fn) => { + fns.push(fn) + }), + run: jest.fn(() => { + fns.forEach(fn => fn()) + }), + wait: jest.fn(() => { + throw new Error('Expect the unexpected') + }) } }) - const result = runDev(config, DATA_DIR, { isLocal: false }) - await expect(result).resolves.toEqual(undefined) // since serve-static is an app hook - - expect(runLocalRuntime).not.toHaveBeenCalled() - expect(buildActions).toHaveBeenCalled() - expect(deployActions).toHaveBeenCalled() - expect(bundle).not.toHaveBeenCalled() - expect(serve).not.toHaveBeenCalled() - expect(bundleServe).not.toHaveBeenCalled() - expect(mockRuntimeLib.utils.getActionUrls).toHaveBeenCalled() - expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).toHaveBeenCalled() - expect(logPoller.run).not.toHaveBeenCalled() - expect(actionsWatcher).toHaveBeenCalled() + const result = runDev(config, DATA_DIR, { skipActions: false }) + await expect(result).rejects.toThrow('Expect the unexpected') }) -test('isLocal false, build-static hook set, serve-static hook not set)', async () => { +test('calls cleanup on exception - hasFrontend && !skipActions && skipServe && fetchLogs: true', async () => { const config = cloneDeep(createAppConfig().application) - // script contents are the hook names, for easy reference - config.hooks['build-static'] = 'build-static' + config.app.hasFrontend = true + config.app.hasBackend = true - appHelper.runScript.mockImplementation(script => { - if (script === 'build-static') { - return {} + Cleanup.mockImplementation(() => { + const fns = [] + return { + add: jest.fn((fn) => { + fns.push(fn) + }), + run: jest.fn(() => { + fns.forEach(fn => fn()) + }), + wait: jest.fn(() => { + throw new Error('Expect the unexpected') + }) } }) - const result = runDev(config, DATA_DIR, { isLocal: false }) - await expect(result).resolves.toEqual(FRONTEND_URL) // we use our own serve - - expect(runLocalRuntime).not.toHaveBeenCalled() - expect(buildActions).toHaveBeenCalled() - expect(deployActions).toHaveBeenCalled() - expect(bundle).not.toHaveBeenCalled() - expect(serve).toHaveBeenCalled() - expect(bundleServe).not.toHaveBeenCalled() - expect(mockRuntimeLib.utils.getActionUrls).toHaveBeenCalled() - expect(mockRuntimeLib.utils.checkOpenWhiskCredentials).toHaveBeenCalled() - expect(logPoller.run).not.toHaveBeenCalled() - expect(actionsWatcher).toHaveBeenCalled() + const result = runDev(config, DATA_DIR, { skipActions: false, skipServe: true, fetchLogs: true }) + await expect(result).rejects.toThrow('Expect the unexpected') }) -test('runDev does not always force builds', async () => { +test('calls cleanup on exception)', async () => { const config = cloneDeep(createAppConfig().application) + config.app.hasFrontend = true + config.app.hasBackend = true - await runDev(config, DATA_DIR, { isLocal: false }) + Cleanup.mockImplementation(() => { + const fns = [] + return { + add: jest.fn((fn) => { + fns.push(fn) + }), + run: jest.fn(() => { + fns.forEach(fn => fn()) + }), + wait: jest.fn(() => { + throw new Error('Expect the unexpected') + }) + } + }) - expect(buildActions).toHaveBeenCalled() - expect(buildActions).toHaveBeenCalledWith(expect.any(Object) /* config */, - null /* filterActions */, - false /* forceBuild */) + const result = runDev(config, DATA_DIR, { skipActions: true }) + await expect(result).rejects.toThrow('Expect the unexpected') }) diff --git a/test/commands/lib/run-local-runtime.test.js b/test/commands/lib/run-local-runtime.test.js deleted file mode 100644 index b828e8015..000000000 --- a/test/commands/lib/run-local-runtime.test.js +++ /dev/null @@ -1,197 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -/* eslint jest/expect-expect: [ - "error", - { - "assertFunctionNames": [ - "expect", "testCleanupNoErrors", "testCleanupOnError", "expectUIServer", "failMissingRuntimeConfig" - ] - } -] -*/ - -const { - runLocalRuntime, - loadLocalDevConfig -} = require('../../../src/lib/run-local-runtime') -const utils = require('../../../src/lib/app-helper') -const mockLogger = require('@adobe/aio-lib-core-logging') -const path = require('path') -const fs = require('fs-extra') - -const { - OW_LOCAL_APIHOST, - OW_LOCAL_NAMESPACE, - OW_LOCAL_AUTH -} = require('../../../src/lib/owlocal') - -jest.mock('../../../src/lib/app-helper') -jest.mock('fs-extra') - -const LOCAL_CONFIG = { - root: '/my-app', - envFile: '.my.env', - app: { - dist: 'dist' - } -} - -const DATA_DIR = path.join('/', 'dataDir') - -// those must match the ones defined in dev.js -const OW_JAR_URL = 'https://github.com/adobe/aio-cli-plugin-app/releases/download/6.2.0/openwhisk-standalone.jar' -const OW_JAR_PATH = path.join(DATA_DIR, 'openwhisk', 'openwhisk-standalone.jar') - -beforeEach(() => { - mockLogger.mockReset() - - utils.downloadOWJar.mockReset() - utils.runOpenWhiskJar.mockReset() - fs.existsSync.mockReset() -}) - -test('should fail if java is not installed', async () => { - utils.hasJavaCLI.mockResolvedValueOnce(false) - - await expect(runLocalRuntime(LOCAL_CONFIG, DATA_DIR)).rejects.toEqual(expect.objectContaining({ message: 'could not find java CLI, please make sure java is installed' })) -}) - -test('should fail if docker CLI is not installed', async () => { - utils.hasJavaCLI.mockResolvedValueOnce(true) - utils.hasDockerCLI.mockResolvedValueOnce(false) - - await expect(runLocalRuntime(LOCAL_CONFIG, DATA_DIR)).rejects.toEqual(expect.objectContaining({ message: 'could not find docker CLI, please make sure docker is installed' })) -}) - -test('should fail if docker is not running', async () => { - utils.hasJavaCLI.mockResolvedValueOnce(true) - utils.hasDockerCLI.mockResolvedValueOnce(true) - utils.isDockerRunning.mockResolvedValueOnce(false) - - await expect(runLocalRuntime(LOCAL_CONFIG, DATA_DIR)).rejects.toEqual(expect.objectContaining({ message: 'docker is not running, please make sure to start docker' })) -}) - -test('should download openwhisk-standalone.jar if it does not exist', async () => { - utils.hasJavaCLI.mockResolvedValueOnce(true) - utils.hasDockerCLI.mockResolvedValueOnce(true) - utils.isDockerRunning.mockResolvedValueOnce(true) - - await runLocalRuntime(LOCAL_CONFIG, DATA_DIR) - - expect(utils.downloadOWJar).toHaveBeenCalledWith(OW_JAR_URL, OW_JAR_PATH) -}) - -test('should *not* download openwhisk-standalone.jar if it exists', async () => { - utils.hasJavaCLI.mockResolvedValueOnce(true) - utils.hasDockerCLI.mockResolvedValueOnce(true) - utils.isDockerRunning.mockResolvedValueOnce(true) - - fs.existsSync.mockImplementation(f => { - return true - }) - - await runLocalRuntime(LOCAL_CONFIG, DATA_DIR) - expect(utils.downloadOWJar).not.toHaveBeenCalled() -}) - -test('run openwhisk jar', async () => { - utils.hasJavaCLI.mockResolvedValueOnce(true) - utils.hasDockerCLI.mockResolvedValueOnce(true) - utils.isDockerRunning.mockResolvedValueOnce(true) - - await runLocalRuntime(LOCAL_CONFIG, DATA_DIR) - // test first argument of first call - expect(utils.runOpenWhiskJar.mock.calls[0][0]).toEqual(OW_JAR_PATH) -}) - -test('coverage (default parameters)', async () => { - utils.hasJavaCLI.mockResolvedValueOnce(true) - utils.hasDockerCLI.mockResolvedValueOnce(true) - utils.isDockerRunning.mockResolvedValueOnce(true) - - await runLocalRuntime(LOCAL_CONFIG, DATA_DIR, () => {}, true) - expect(utils.runOpenWhiskJar).toHaveBeenCalled() -}) - -test('cleanup', async () => { - utils.hasJavaCLI.mockResolvedValueOnce(true) - utils.hasDockerCLI.mockResolvedValueOnce(true) - utils.isDockerRunning.mockResolvedValueOnce(true) - - utils.runOpenWhiskJar.mockResolvedValueOnce({ - proc: { - kill: jest.fn() - } - }) - - fs.existsSync - .mockReturnValueOnce(true) // openwhisk jar - .mockReturnValueOnce(true) // dev config envFile - - const { cleanup, config } = await runLocalRuntime(LOCAL_CONFIG, DATA_DIR, () => {}, true) - expect(typeof cleanup).toEqual('function') - expect(typeof config).toEqual('object') - expect(cleanup).not.toThrow() -}) - -test('return value', async () => { - utils.hasJavaCLI.mockResolvedValueOnce(true) - utils.hasDockerCLI.mockResolvedValueOnce(true) - utils.isDockerRunning.mockResolvedValueOnce(true) - - utils.runOpenWhiskJar.mockResolvedValueOnce({ - proc: { - kill: jest.fn() - } - }) - - fs.existsSync - .mockReturnValueOnce(true) // openwhisk jar - .mockReturnValueOnce(false) // dev config envFile - - const { cleanup, config } = await runLocalRuntime(LOCAL_CONFIG, DATA_DIR, () => {}, true) - - expect(typeof cleanup).toEqual('function') - expect(cleanup).not.toThrow() - - expect(typeof config).toEqual('object') - expect(config).toEqual({ - ...LOCAL_CONFIG, - envFile: path.join(LOCAL_CONFIG.app.dist, '.env.local'), - ow: { - namespace: OW_LOCAL_NAMESPACE, - auth: OW_LOCAL_AUTH, - apihost: OW_LOCAL_APIHOST - } - }) -}) - -describe('loadLocalDevConfig', () => { - test('returns local config dev config', () => { - const localConfig = loadLocalDevConfig(LOCAL_CONFIG) - expect(localConfig).toEqual({ - ...LOCAL_CONFIG, - envFile: path.join(LOCAL_CONFIG.app.dist, '.env.local'), - ow: { - namespace: OW_LOCAL_NAMESPACE, - auth: OW_LOCAL_AUTH, - apihost: OW_LOCAL_APIHOST - } - }) - }) - test('calls logger when available (coverage)', () => { - jest.spyOn(console, 'log') - loadLocalDevConfig(LOCAL_CONFIG, console.log) - expect(console.log).toHaveBeenCalledWith(expect.any(String)) - }) -}) diff --git a/test/commands/lib/vscode.test.js b/test/commands/lib/vscode.test.js deleted file mode 100644 index 7e1832385..000000000 --- a/test/commands/lib/vscode.test.js +++ /dev/null @@ -1,249 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -const vscode = require('../../../src/lib/vscode') -const yeoman = require('yeoman-environment') -const fs = require('fs-extra') -const path = require('path') -const dataMocks = require('../../data-mocks/config-loader') - -jest.mock('fs-extra') -jest.mock('yeoman-environment') - -const mockYeomanInstantiate = jest.fn() -const mockYeomanRunGenerator = jest.fn() -yeoman.createEnv.mockReturnValue({ - instantiate: mockYeomanInstantiate, - runGenerator: mockYeomanRunGenerator -}) - -const createAppConfig = (aioConfig = {}, appFixtureName = 'legacy-app') => { - const appConfig = dataMocks(appFixtureName, aioConfig).all - appConfig.application = { ...appConfig.application, ...aioConfig } - return appConfig -} - -const createFileSystem = (initialFiles = {}) => { - const myFileSystem = { ...initialFiles } - - fs.existsSync.mockImplementation((filePath) => { - return (!!myFileSystem[filePath]) - }) - - fs.moveSync.mockImplementation((src, dest) => { - myFileSystem[dest] = myFileSystem[src] - delete myFileSystem[src] - }) - - const removeFile = (filePath) => { - delete myFileSystem[filePath] - } - - fs.unlinkSync.mockImplementation(removeFile) - fs.rmdirSync.mockImplementation(removeFile) - - fs.readdirSync.mockImplementation((filePath) => { - const item = myFileSystem[filePath] - if (!Array.isArray(item)) { - throw new Error(`Fake filesystem ${filePath} value does not contain an array.`) - } - return item - }) - - return myFileSystem -} - -beforeEach(() => { - mockYeomanInstantiate.mockClear() -}) - -test('exports', () => { - const vsCodeConfig = vscode({}) - - expect(typeof vscode).toEqual('function') - expect(typeof vsCodeConfig.update).toEqual('function') - expect(typeof vsCodeConfig.update).toEqual('function') - expect(typeof vsCodeConfig.cleanup).toEqual('function') -}) - -test('files()', () => { - const config = { root: '/my-root' } - const vsCodeConfig = vscode(config) - - const { backupFile, mainFile } = vsCodeConfig.files() - expect(backupFile).toEqual(path.join(config.root, '.vscode/launch.json.save')) - expect(mainFile).toEqual(path.join(config.root, '.vscode/launch.json')) -}) - -describe('update()', () => { - const props = { frontEndUrl: 'https://foo.bar' } - const config = { ...createAppConfig().application, envFile: 'my.env' } - const vsCodeConfig = vscode(config) - const { backupFile, mainFile } = vsCodeConfig.files() - - const mockYeomanOutput = (fileSystem) => { - mockYeomanRunGenerator.mockImplementation(() => { - fileSystem[mainFile] = 'generated-content' - }) - } - - test('launch.json does not exist, backup does not exist (no backup copy)', async () => { - const myFileSystem = createFileSystem() - mockYeomanOutput(myFileSystem) - - // launch.json does not exist - expect(mainFile in myFileSystem).toEqual(false) - // backup does not exist - expect(backupFile in myFileSystem).toEqual(false) - - await vsCodeConfig.update(props) - - // now launch.json exists - expect(mainFile in myFileSystem).toEqual(true) - expect(myFileSystem[mainFile].toString()).toEqual('generated-content') - // backup should not exist - expect(backupFile in myFileSystem).toEqual(false) - }) - - test('launch.json exists, backup does not exist (copy to backup)', async () => { - const myFileSystem = createFileSystem({ - [mainFile]: 'main-content' - }) - mockYeomanOutput(myFileSystem) - - // launch.json already exists - expect(mainFile in myFileSystem).toEqual(true) - // backup does not exist - expect(backupFile in myFileSystem).toEqual(false) - - await vsCodeConfig.update(props) - - expect(fs.existsSync).toHaveBeenCalled() - expect(fs.moveSync).toHaveBeenCalled() - - // still exists, but is generated - expect(mainFile in myFileSystem).toEqual(true) - expect(myFileSystem[mainFile].toString()).toEqual('generated-content') - // check backup is copied - expect(backupFile in myFileSystem).toEqual(true) - expect(myFileSystem[backupFile].toString()).toEqual('main-content') - }) - - test('launch.json exists, backup exists (do not overwrite backup)', async () => { - const myFileSystem = createFileSystem({ - [mainFile]: 'main-content', - [backupFile]: 'backup-content' - }) - mockYeomanOutput(myFileSystem) - - // launch.json already exists - expect(mainFile in myFileSystem).toEqual(true) - // backup already exists - expect(backupFile in myFileSystem).toEqual(true) - - await vsCodeConfig.update(props) - - // still exists, but is generated - expect(mainFile in myFileSystem).toEqual(true) - expect(myFileSystem[mainFile].toString()).toEqual('generated-content') - // check backup is *not* copied over - expect(backupFile in myFileSystem).toEqual(true) - expect(myFileSystem[backupFile].toString()).toEqual('backup-content') - }) -}) - -describe('cleanup()', () => { - const config = { ...createAppConfig().application, envFile: 'my.env' } - const vsCodeConfig = vscode(config) - const { backupFile, mainFile } = vsCodeConfig.files() - - test('launch.json does not exist, backup does not exist (do nothing)', () => { - const myFileSystem = createFileSystem() - - // launch.json does not exist - expect(mainFile in myFileSystem).toEqual(false) - // backup already exists - expect(backupFile in myFileSystem).toEqual(false) - - vsCodeConfig.cleanup() - - // nothing should exist still - expect(mainFile in myFileSystem).toEqual(false) - expect(backupFile in myFileSystem).toEqual(false) - }) - - test('launch.json exists, backup does not exist (remove launch.json, .vscode folder empty and is deleted)', () => { - const vscodeFolder = path.dirname(mainFile) - const myFileSystem = createFileSystem({ - [mainFile]: 'main-content', - [vscodeFolder]: [] // nothing in it - }) - - // launch.json exists - expect(mainFile in myFileSystem).toEqual(true) - // backup does not exist - expect(backupFile in myFileSystem).toEqual(false) - - vsCodeConfig.cleanup() - - // launch.json and the backup file should not exist - expect(mainFile in myFileSystem).toEqual(false) - expect(backupFile in myFileSystem).toEqual(false) - // the .vscode folder should be deleted as well (since there are no other contents) - expect(fs.existsSync(vscodeFolder)).toBe(false) - }) - - test('launch.json exists, backup does not exist (remove launch.json, .vscode folder not empty and is not deleted)', () => { - const vscodeFolder = path.dirname(mainFile) - const someOtherFileInVsCodeFolder = path.join(vscodeFolder, 'some-file') - - const myFileSystem = createFileSystem({ - [mainFile]: 'main-content', - [vscodeFolder]: [someOtherFileInVsCodeFolder], - [someOtherFileInVsCodeFolder]: 'some-content' - }) - - // launch.json exists - expect(mainFile in myFileSystem).toEqual(true) - // backup does not exist - expect(backupFile in myFileSystem).toEqual(false) - - vsCodeConfig.cleanup() - - // launch.json and the backup file should not exist - expect(mainFile in myFileSystem).toEqual(false) - expect(backupFile in myFileSystem).toEqual(false) - // if there are any other content in .vscode, it is not deleted - expect(fs.existsSync(vscodeFolder)).toBe(true) - expect(someOtherFileInVsCodeFolder in myFileSystem).toEqual(true) - }) - - test('launch.json exists, backup exists (restore backup)', () => { - const myFileSystem = createFileSystem({ - [mainFile]: 'main-content', - [backupFile]: 'backup-content' - }) - - // launch.json exists - expect(mainFile in myFileSystem).toEqual(true) - // backup exists - expect(backupFile in myFileSystem).toEqual(true) - - vsCodeConfig.cleanup() - - // launch.json restored from backup - expect(mainFile in myFileSystem).toEqual(true) - expect(myFileSystem[mainFile].toString()).toEqual('backup-content') - // backup should not exist - expect(backupFile in myFileSystem).toEqual(false) - }) -})