Skip to content

Commit 6427186

Browse files
authored
Create a loadEnvs api function (#289)
- test without test double in mocha - proxyquire isn't great but it's better - a little bit of refactoring
1 parent 47d44d1 commit 6427186

File tree

10 files changed

+118
-60
lines changed

10 files changed

+118
-60
lines changed

.eslintrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
},
66
"globals": {
77
"assert": false,
8-
"td": false
8+
"proxyquire": false
99
},
1010
"extends": [
1111
"standard",

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"eslint-plugin-standard": "^3.0.1",
7979
"mocha": "^3.4.2",
8080
"np": "^2.12.0",
81+
"proxyquire": "^1.8.0",
8182
"testdouble": "^2.1.2"
8283
},
8384
"babel": {

src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ export { default as configSet } from './config-set'
1313
export { default as configRemove } from './config-remove'
1414
export { default as configList } from './config-list'
1515
export { default as configSync } from './config-sync'
16+
export { envs as loadEnvs } from './util/load'
1617
export { version } from '../package.json'

src/util/aws/lambda.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,11 @@ export async function isFunctionDeployed (FunctionName, Qualifier) {
181181

182182
try {
183183
await getFunction({ FunctionName, Qualifier })
184-
return true
185184
} catch (err) {
186185
if (err.code === 'ResourceNotFoundException') { return false }
187186
throw err
188187
}
188+
return true
189189
}
190190

191191
export async function doesAliasExist ({ FunctionName, Alias }) {
@@ -194,11 +194,11 @@ export async function doesAliasExist ({ FunctionName, Alias }) {
194194

195195
try {
196196
await lambda.getAlias({ FunctionName, Name: Alias }).promise()
197-
return true
198197
} catch (err) {
199198
if (err.code === 'ResourceNotFoundException') { return false }
200199
throw err
201200
}
201+
return true
202202
}
203203

204204
export async function getEnvironment (env, { FunctionName }) {
@@ -215,8 +215,10 @@ export async function getEnvironment (env, { FunctionName }) {
215215
.get('Variables')
216216
return envVars
217217
} catch (e) {
218-
if (e.code !== 'ResourceNotFoundException') { throw e }
219-
throw new AWSEnvironmentVariableNotFound(FunctionName)
218+
if (e.code === 'ResourceNotFoundException') {
219+
throw new AWSEnvironmentVariableNotFound(FunctionName)
220+
}
221+
throw e
220222
}
221223
}
222224

@@ -230,7 +232,7 @@ export async function getAliasVersion ({ functionName, aliasName }) {
230232
}
231233

232234
return lambda.getAlias(params).promise()
233-
.get('FunctionVersion')
235+
.get('FunctionVersion')
234236
}
235237

236238
export async function setPermission ({ name, region, env, apiId, accountId }) {

src/util/build-functions.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ export default async function (PATTERN, NODE_ENV) {
55
const { shep } = await pkg()
66
const buildCommand = (shep && shep.buildCommand) || 'webpack --bail'
77
return exec(buildCommand, { env: { ...process.env, PATTERN, NODE_ENV } })
8-
.catch({ code: 'ENOENT' }, (e) => {
9-
console.warn('No locally installed webpack found. Verify that webpack is installed')
10-
throw e
11-
})
8+
.catch({ code: 'ENOENT' }, (e) => {
9+
console.warn('No locally installed webpack found. Verify that webpack is installed')
10+
throw e
11+
})
1212
}

src/util/load.js

+22-18
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,33 @@
1+
import isEqual from 'lodash.isequal'
12
import path from 'path'
3+
import Promise from 'bluebird'
24
import { readdir, readJSON } from './modules/fs'
35
import minimatch from 'minimatch'
46
import { listAliases, isFunctionDeployed } from './aws/lambda'
5-
import Promise from 'bluebird'
67

78
export async function envs () {
8-
const fullFuncNames = await Promise.map(this.funcs(), this.lambdaConfig)
9-
.map(({ FunctionName }) => FunctionName)
9+
const functionLambdaConfigs = await Promise.map(funcs(), lambdaConfig).map(({ FunctionName }) => FunctionName)
1010

11-
const deployedFunctions = await Promise.filter(fullFuncNames, (x) => isFunctionDeployed(x))
12-
const allAliases = await Promise.map(deployedFunctions, (name) => listAliases(name).map(({ Name }) => Name))
11+
const deployedFunctions = await Promise.filter(functionLambdaConfigs, (name) => isFunctionDeployed(name))
12+
if (deployedFunctions.length <= 0) {
13+
throw new Error('No deployed functions')
14+
}
1315

14-
const aliases = allAliases.reduce((acc, aliasSet) => {
15-
const missingAliases = aliasSet.filter((alias) => acc.indexOf(alias) === -1)
16-
.concat(acc.filter((alias) => aliasSet.indexOf(alias) === -1))
16+
const allFunctionAliases = await Promise.map(deployedFunctions, async name => {
17+
const aliases = await listAliases(name).map(({ Name }) => Name)
18+
return aliases.sort()
19+
})
1720

18-
if (missingAliases.length !== 0) {
19-
throw new EnvironmentMismatch()
20-
}
21-
22-
return acc
23-
}, allAliases.pop())
21+
const aliases = allFunctionAliases.pop().sort()
22+
if (aliases.length === 0) {
23+
throw new AliasesNotFound()
24+
}
2425

25-
if (!aliases || aliases.length === 0) { throw new AliasesNotFound() }
26+
allFunctionAliases.forEach(functionAliases => {
27+
if (!isEqual(aliases, functionAliases)) {
28+
throw EnvironmentMismatch()
29+
}
30+
})
2631

2732
return aliases
2833
}
@@ -44,16 +49,15 @@ export async function funcs (pattern = '*') {
4449
const funcs = await readdir('functions').filter(minimatch.filter(pattern))
4550
if (funcs.length === 0) {
4651
throw new FunctionNotFound(pattern)
47-
} else {
48-
return funcs
4952
}
53+
return funcs
5054
}
5155

5256
export async function lambdaConfig (name) {
5357
const functionConfig = await readJSON(`functions/${name}/lambda.json`)
5458
const projectConfig = await readJSON(`lambda.json`)
5559

56-
return Object.assign(projectConfig, functionConfig)
60+
return Object.assign({}, projectConfig, functionConfig)
5761
}
5862

5963
export async function pkg () {

src/util/modules/fs.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import fs from 'fs-extra-promise'
22

3+
export const exists = fs.existsAsync
34
export const mkdirp = fs.mkdirpAsync
5+
export const readdir = fs.readdirAsync
6+
export const readdirSync = fs.readdirSync
47
export const readFile = fs.readFileAsync
5-
export const writeFile = fs.writeFileAsync
6-
export const writeJSON = fs.writeJSONAsync
78
export const readJSON = fs.readJSONAsync
89
export const readJSONSync = fs.readJSONSync
9-
export const readdirSync = fs.readdirSync
10-
export const readdir = fs.readdirAsync
11-
export const exists = fs.existsAsync
10+
export const writeFile = fs.writeFileAsync
11+
export const writeJSON = fs.writeJSONAsync

test/build-test.js

+37-22
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,54 @@
11
describe('shep.build', () => {
2-
const exec = td.replace('../src/util/modules/exec')
3-
td.when(exec(), { ignoreExtraArgs: true }).thenResolve()
4-
52
it('Executes custom command', async () => {
63
const buildCommand = 'custom-build --cool-flag -x 6'
7-
const load = td.replace('../src/util/load')
8-
td.when(load.pkg()).thenResolve({ shep: { buildCommand } })
9-
td.when(exec(), { ignoreExtraArgs: true }).thenResolve()
10-
const shep = require('../src')
4+
const exec = async (command) => {
5+
assert.equal(command, buildCommand)
6+
}
7+
exec['@global'] = true
8+
const shep = proxyquire('../src/', {
9+
'./load': {
10+
'@global': true,
11+
pkg: async () => { return { shep: { buildCommand } } }
12+
},
13+
'./modules/exec': exec
14+
})
1115

12-
td.when(exec(buildCommand), { ignoreExtraArgs: true }).thenResolve()
1316
await shep.build({ quiet: true })
1417
})
1518

1619
it('Logs to console when no webpack found', async () => {
17-
let error = new Error()
20+
const error = new Error()
1821
error.code = 'ENOENT'
1922

20-
const load = td.replace('../src/util/load')
21-
td.when(load.pkg()).thenResolve({ shep: {} })
23+
const exec = async (command) => {
24+
throw error
25+
}
26+
exec['@global'] = true
2227

23-
td.when(exec('webpack --bail'), { ignoreExtraArgs: true }).thenReject(error)
24-
td.replace(console, 'warn')
28+
const shep = proxyquire('../src/', {
29+
'./load': {
30+
'@global': true,
31+
pkg: async () => { return { shep: { } } }
32+
},
33+
'./modules/exec': exec
34+
})
2535

26-
const shep = require('../src')
27-
error = await assert.isRejected(shep.build({ quiet: true }))
28-
assert.equal(error.code, 'ENOENT')
29-
td.verify(console.warn(), { ignoreExtraArgs: true })
36+
assert.deepEqual(await assert.isRejected(shep.build({ quiet: true })), error)
3037
})
3138

32-
describe('Executed webpack', async () => {
33-
const load = td.replace('../src/util/load')
34-
const shep = require('../src')
35-
td.when(load.pkg()).thenResolve({})
36-
td.when(exec('webpack --bail'), { ignoreExtraArgs: true }).thenResolve()
39+
it('Executed webpack', async () => {
40+
const exec = async (command) => {
41+
assert.equal(command, 'webpack --bail')
42+
}
43+
exec['@global'] = true
44+
45+
const shep = proxyquire('../src/', {
46+
'./load': {
47+
'@global': true,
48+
pkg: async () => { return { shep: { } } }
49+
},
50+
'./modules/exec': exec
51+
})
3752
await shep.build({ quiet: true })
3853
})
3954
})

test/initializers.js

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
import td from 'testdouble'
2-
import Promise from 'bluebird'
31
import chai from 'chai'
42
import chaiAsPromised from 'chai-as-promised'
3+
import proxyquire from 'proxyquire'
54

65
chai.use(chaiAsPromised)
7-
8-
td.config({ promiseConstructor: Promise, ignoreWarnings: true })
9-
global.td = td
6+
global.proxyquire = proxyquire.noPreserveCache()

test/listenv-test.js

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
describe('shep.listEnv', () => {
2+
it('returns a list of environments', async () => {
3+
const exec = async (command) => {
4+
assert.equal(command, 'webpack --bail')
5+
}
6+
exec['@global'] = true
7+
8+
const shep = proxyquire('../src/', {
9+
'./load': {
10+
'@global': true,
11+
async pkg () { return { shep: {} } }
12+
},
13+
'./modules/exec': exec,
14+
'./modules/fs': {
15+
'@global': true,
16+
async readdir () { return ['function1', 'function2'] },
17+
async readJSON () { return {} }
18+
},
19+
'./aws/lambda': {
20+
'@global': true,
21+
async isFunctionDeployed () { return true },
22+
async listAliases () {
23+
return [
24+
{ Name: 'beta' },
25+
{ Name: 'production' },
26+
{ Name: 'staging' }
27+
]
28+
}
29+
}
30+
})
31+
32+
assert.deepEqual(await shep.loadEnvs(), [
33+
'beta',
34+
'production',
35+
'staging'
36+
])
37+
})
38+
})

0 commit comments

Comments
 (0)