Skip to content

Commit

Permalink
Implement a down command
Browse files Browse the repository at this point in the history
  • Loading branch information
grampajoe committed Feb 16, 2015
1 parent a136e36 commit 0af225f
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 4 deletions.
12 changes: 11 additions & 1 deletion README.rst
Expand Up @@ -55,7 +55,7 @@ Then, you can get rid of it like:
.. code:: text
$ happy down
Destroying app... done
Destroying app butt-man-123... done
It's down. :(
.. _app.json manifest: https://devcenter.heroku.com/articles/app-json-schema
Expand All @@ -69,11 +69,21 @@ up

Brings up a Heroku app.

The app name is stored in a file called ``.happy`` in the working directory so
happy can find it later.

- ``--tarball-url``

(optional) URL of the tarball containing app.json. If this is not given,
happy tries to infer it from an ``app.json`` file in the current directory.

down
~~~~

Brings down a Heroku app.

The app name is read from a file called ``.happy`` in the working directory.

Running the tests
-----------------

Expand Down
10 changes: 10 additions & 0 deletions happy/__init__.py
Expand Up @@ -30,3 +30,13 @@ def wait(build_id):
if api.check_build_status(build_id):
break
sleep(3)


def delete(app_name):
"""Deletes a Heroku app.
:param app_name: Name of the Heroku app to delete.
"""
api = Heroku()

api.delete_app(app_name=app_name)
19 changes: 19 additions & 0 deletions happy/cli.py
Expand Up @@ -29,6 +29,12 @@ def _write_app_name(app_name):
f.write(str(app_name))


def _read_app_name():
"""Reads the app name from the .happy file."""
with click.open_file('.happy', 'r') as f:
return f.read().strip()


@click.group(name='happy')
def cli():
"""Quickly set up and tear down Heroku apps!"""
Expand Down Expand Up @@ -58,3 +64,16 @@ def up(tarball_url):

click.echo('done')
click.echo("It's up! :) https://%s.herokuapp.com" % app_name)


@cli.command(name='down')
def down():
"""Brings down a Heroku app."""
app_name = _read_app_name()

click.echo('Destroying app %s... ' % app_name, nl=False)

happy.delete(app_name=app_name)

click.echo('done')
click.echo("It's down. :(")
13 changes: 12 additions & 1 deletion happy/heroku.py
Expand Up @@ -65,7 +65,11 @@ def create_build(self, tarball_url):
return self.api_request('POST', '/app-setups', data=data)

def check_build_status(self, build_id):
"""Checks the status of an app-setups build."""
"""Checks the status of an app-setups build.
:param build_id: ID of the build to check.
:returns: ``True`` if succeeded, ``False`` if pending.
"""
data = self.api_request('GET', '/app-setups/%s' % build_id)

status = data.get('status')
Expand All @@ -76,3 +80,10 @@ def check_build_status(self, build_id):
return True
else:
raise BuildError(data.get('failure_message'))

def delete_app(self, app_name):
"""Deletes an app.
:param app_name: Name of the app to delete.
"""
self.api_request('DELETE', '/apps/%s' % app_name)
30 changes: 28 additions & 2 deletions tests/test_cli.py
Expand Up @@ -112,10 +112,36 @@ def test_up_prints_info(create, wait, runner):
with runner.isolated_filesystem():
result = runner.invoke(cli, ['up', '--tarball-url=example.com'])

expected = (
assert result.output == (
"Creating app... butt-man-123\n"
"Building... done\n"
"It's up! :) https://butt-man-123.herokuapp.com\n"
)

assert result.output == expected

@mock.patch('happy.delete')
def test_down(delete, runner):
"""`happy.down` should delete the app."""
with runner.isolated_filesystem():
with open('.happy', 'w') as f:
f.write('butt-man-123')

result = runner.invoke(cli, ['down'])

delete.assert_called_with(app_name='butt-man-123')
assert result.exit_code == 0


@mock.patch('happy.delete')
def test_down_prints_info(delete, runner):
"""`happy.down` should print status info."""
with runner.isolated_filesystem():
with open('.happy', 'w') as f:
f.write('butt-man-123')

result = runner.invoke(cli, ['down'])

assert result.output == (
"Destroying app butt-man-123... done\n"
"It's down. :(\n"
)
7 changes: 7 additions & 0 deletions tests/test_happy.py
Expand Up @@ -54,3 +54,10 @@ def test_wait(heroku):
mock.call('12345'),
mock.call('12345'),
])


def test_delete(heroku):
"""Should delete the app."""
happy.delete(app_name='butt-man-123')

heroku.delete_app.assert_called_with(app_name='butt-man-123')
13 changes: 13 additions & 0 deletions tests/test_heroku.py
Expand Up @@ -146,3 +146,16 @@ def test_heroku_check_build_status_failed(api_request):
heroku.check_build_status('123')

assert 'oops' in str(exc.value)


@mock.patch.object(Heroku, 'api_request')
def test_heroku_delete_app(api_request):
"""Heroku.delete_app should delete an app."""
heroku = Heroku()

heroku.delete_app(app_name='butt-man-123')

api_request.assert_called_with(
'DELETE',
'/apps/butt-man-123',
)

0 comments on commit 0af225f

Please sign in to comment.