Skip to content

Commit 392d80f

Browse files
committed
Refactor things into different files. Test coverage
1 parent 5ef62ec commit 392d80f

13 files changed

+638
-266
lines changed

commandHelpers.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const { execSync } = require('child_process')
2+
3+
function herokuCreate () {
4+
const stdout = execSync('heroku create')
5+
const appName = stdout
6+
.toString()
7+
.split('|')[0]
8+
.split('.')[0]
9+
.split('://')[1] // TODO: SAFER
10+
11+
return appName
12+
}
13+
14+
module.exports = {
15+
herokuCreate
16+
}

commandHelpers.spec.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* eslint-env jest */
2+
3+
it('herokuCreate', () => {
4+
jest.resetModules()
5+
jest.resetAllMocks()
6+
const execSync = jest.fn(() => {
7+
return 'https://tomato-sauce-54.herokuapps.com'
8+
})
9+
10+
jest.doMock('child_process', () => ({
11+
execSync
12+
}))
13+
const { herokuCreate } = require('./commandHelpers')
14+
const appName = herokuCreate()
15+
// expect(execSync).toHaveBeenCalledWith('heroku create')
16+
expect(appName).toBe('tomato-sauce-54')
17+
})

commands.spec.js

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/* eslint-env jest */
2+
3+
it('help', () => {
4+
const mockLog = jest.spyOn(console, 'log').mockImplementation(() => {})
5+
const { help } = require('./commands')
6+
help()
7+
expect(mockLog).toHaveBeenCalled()
8+
})
9+
10+
it('newDeploy', () => {
11+
jest.resetModules() // this is important - it clears the cache
12+
jest.doMock('os', () => ({
13+
homedir: () => '$HOME'
14+
}))
15+
16+
const execSync = jest.fn((cmd, opt) => {
17+
if (cmd.match(/heroku config:get MONGODB_URI/)) {
18+
return 'mongodb://user:[email protected]/db'
19+
}
20+
if (cmd.match(/heroku container:release/)) {
21+
return 'been released'
22+
}
23+
if (cmd.match(/heroku create/)) {
24+
return 'https://tomato-sauce-54.herokuapps.com'
25+
}
26+
return 'stdout'
27+
})
28+
jest.doMock('child_process', () => ({
29+
execSync
30+
}))
31+
32+
const writeFileSync = jest.fn()
33+
jest.doMock('fs', () => ({
34+
readFileSync: (path) => {
35+
if (path === '.meteor/release') {
36+
return '[email protected]\r\n'
37+
}
38+
},
39+
writeFileSync
40+
}))
41+
42+
const tarXMock = jest.fn()
43+
jest.doMock('tar', () => ({
44+
x: tarXMock
45+
}))
46+
47+
const clearBuildFolder = jest.fn()
48+
jest.doMock('./helpers', () => ({
49+
clearBuildFolder,
50+
meteorBuildDir: '$HOME/.meteor-hero/builds',
51+
explain: e => console.log(e),
52+
logger: {
53+
info: () => {},
54+
error: (e) => console.log(e),
55+
success: () => {}
56+
},
57+
objToEnvStr: () => 'E=t'
58+
}))
59+
60+
const spy = jest.spyOn(process, 'cwd')
61+
spy.mockReturnValue('cwd')
62+
63+
const dockerFile = require('./dockerfile')
64+
65+
const herokuCreate = jest.fn(() => '$APPNAME')
66+
jest.doMock('./commandHelpers', () => ({
67+
herokuCreate
68+
}))
69+
const { newDeploy } = require('./commands')
70+
newDeploy({})
71+
72+
expect(clearBuildFolder).toHaveBeenCalledWith('$HOME/.meteor-hero/builds')
73+
// buildMeteorApp
74+
expect(execSync).toHaveBeenCalledWith('meteor build $HOME/.meteor-hero/builds --server-only --architecture=os.linux.x86_64')
75+
76+
// unzipMeteorBundle
77+
expect(tarXMock)
78+
.toHaveBeenCalledWith({
79+
file: '$HOME/.meteor-hero/builds/cwd.tar.gz',
80+
cwd: '$HOME/.meteor-hero/builds',
81+
sync: true
82+
})
83+
84+
// writeDockerFile
85+
expect(writeFileSync).toHaveBeenCalledWith('$HOME/.meteor-hero/builds/bundle/Dockerfile', dockerFile)
86+
87+
// herokuCreate
88+
expect(herokuCreate).toHaveBeenCalled()
89+
90+
// writeAppName
91+
expect(writeFileSync).toHaveBeenCalledWith('.heroku_app_name', '$APPNAME')
92+
93+
// herokuLogin
94+
expect(execSync).toHaveBeenCalledWith('heroku container:login')
95+
// getFinalEnvVars (also looks for MONGO_URL)
96+
expect(execSync).toHaveBeenCalledWith('heroku addons:create mongolab --app $APPNAME')
97+
98+
const execOpts = { cwd: '$HOME/.meteor-hero/builds/bundle' }
99+
expect(execSync).toHaveBeenCalledWith(`heroku config:set --app $APPNAME E=t`, execOpts)
100+
expect(execSync).toHaveBeenCalledWith('heroku container:push web -a $APPNAME', execOpts)
101+
expect(execSync).toHaveBeenCalledWith('heroku container:release web -a $APPNAME', execOpts)
102+
})
103+
104+
it('update', () => {
105+
jest.resetModules() // this is important - it clears the cache
106+
jest.doMock('os', () => ({
107+
homedir: () => '$HOME'
108+
}))
109+
110+
const execSync = jest.fn((cmd, opt) => {
111+
if (cmd.match(/heroku config:get MONGODB_URI/)) {
112+
return 'mongodb://user:[email protected]/db'
113+
}
114+
if (cmd.match(/heroku container:release/)) {
115+
return 'been released'
116+
}
117+
if (cmd.match(/heroku create/)) {
118+
return 'https://tomato-sauce-54.herokuapps.com'
119+
}
120+
return 'stdout'
121+
})
122+
jest.doMock('child_process', () => ({
123+
execSync
124+
}))
125+
126+
const writeFileSync = jest.fn()
127+
jest.doMock('fs', () => ({
128+
readFileSync: (path) => {
129+
if (path === '.meteor/release') {
130+
return '[email protected]\r\n'
131+
}
132+
if (path === '.heroku_app_name') {
133+
return 'UPDATE_APP_NAME'
134+
}
135+
},
136+
writeFileSync
137+
}))
138+
139+
const tarXMock = jest.fn()
140+
jest.doMock('tar', () => ({
141+
x: tarXMock
142+
}))
143+
144+
const clearBuildFolder = jest.fn()
145+
jest.doMock('./helpers', () => ({
146+
clearBuildFolder,
147+
meteorBuildDir: '$HOME/.meteor-hero/builds',
148+
explain: e => console.log(e),
149+
logger: {
150+
info: () => {},
151+
error: (e) => console.log(e),
152+
success: () => {}
153+
},
154+
objToEnvStr: () => 'nv=smthn'
155+
}))
156+
157+
const spy = jest.spyOn(process, 'cwd')
158+
spy.mockReturnValue('cwd')
159+
160+
const dockerFile = require('./dockerfile')
161+
162+
const herokuCreate = jest.fn(() => '$APPNAME')
163+
jest.doMock('./commandHelpers', () => ({
164+
herokuCreate
165+
}))
166+
const { newDeploy } = require('./commands')
167+
newDeploy({ 'u': true, 'e': ['nv=smthn'] })
168+
169+
expect(clearBuildFolder).toHaveBeenCalledWith('$HOME/.meteor-hero/builds')
170+
// buildMeteorApp
171+
expect(execSync).toHaveBeenCalledWith('meteor build $HOME/.meteor-hero/builds --server-only --architecture=os.linux.x86_64')
172+
173+
// unzipMeteorBundle
174+
expect(tarXMock)
175+
.toHaveBeenCalledWith({
176+
file: '$HOME/.meteor-hero/builds/cwd.tar.gz',
177+
cwd: '$HOME/.meteor-hero/builds',
178+
sync: true
179+
})
180+
181+
// writeDockerFile
182+
expect(writeFileSync).toHaveBeenCalledWith('$HOME/.meteor-hero/builds/bundle/Dockerfile', dockerFile)
183+
184+
// herokuLogin
185+
expect(execSync).toHaveBeenCalledWith('heroku container:login')
186+
187+
const execOpts = { cwd: '$HOME/.meteor-hero/builds/bundle' }
188+
expect(execSync).toHaveBeenCalledWith('heroku config:set --app UPDATE_APP_NAME nv=smthn', execOpts)
189+
expect(execSync).toHaveBeenCalledWith('heroku container:push web -a UPDATE_APP_NAME', execOpts)
190+
expect(execSync).toHaveBeenCalledWith('heroku container:release web -a UPDATE_APP_NAME', execOpts)
191+
})

commands/help.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
const { cyan, bold, underline, grey } = require('chalk')
2+
const { meteorBuildDir } = require('../helpers')
3+
4+
function help () {
5+
console.log(`
6+
${bold(`meteor-hero`)} [options] <command>
7+
8+
${grey(`Description:`)}
9+
This program is designed to be run inside of a MeteorJS project and will do the following:
10+
1) Build the meteor application to BUILD_DIR ${grey(
11+
`(Default: ${meteorBuildDir})`
12+
)}
13+
2) Unzip the contents of the built meteor application
14+
3) Write a Dockerfile in the BUILD_DIR
15+
4) Create a new heroku instance with a MongoDB addon and set the appropriate env variables
16+
5) Release the heroku container and print the URL where it is accessible
17+
18+
${bold(
19+
`Note:`
20+
)} if run outside of a meteor application, may crash due to ${bold(
21+
`meteor build`
22+
)} failing
23+
24+
${grey(`Options:`)}
25+
-h Displays help message
26+
-b ${bold.underline(
27+
`DIR`
28+
)} Overwrite BUILD_DIR (Default: ${meteorBuildDir})
29+
-e ${underline(
30+
`${bold(`VAR`)}=value`
31+
)} Environment variables to set on the deployed heroku instance.
32+
-E ${bold.underline(
33+
`FILE`
34+
)} Env file to be read for environment variables to be set.
35+
36+
${grey(`Commands:`)}
37+
[] By default deploys a MeteorJS application to heroku.
38+
-u Update instead of creating a new url, update the previous deploy. The file .heroku_app_name must exist and contain the previous app name.
39+
40+
${grey(`Examples:`)}
41+
42+
${grey(`–`)} Deploy with environment variables
43+
44+
${cyan(
45+
`$ meteor-hero -e MONGO_URL="mongodb://user:[email protected]" -e ROOT_URL="example.net"`
46+
)}
47+
48+
${grey(`–`)} Deploy using env file
49+
50+
${cyan(`$ meteor-hero -E prod.env`)}
51+
52+
`)
53+
// .slice(1, -1)
54+
}
55+
56+
module.exports = help

commands/index.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const help = require('./help')
2+
const newDeploy = require('./newDeploy')
3+
4+
module.exports = {
5+
help,
6+
newDeploy
7+
}

0 commit comments

Comments
 (0)