-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
I'd like to start making some improvements to our deployment script to support things like Heroku preboot, but that introduces enough complexity that I'd like to add proper unit tests for our deploy script first. This also adds a new `Snapshot` helper class that makes it easier to do Jest-like snapshot testing using pytest. This also removes `deploy.py` from our `.coveragerc` so we're now tracking coverage of it. The coverage for the whole project is going down right now because we don't have enough tests to cover the whole thing, and I need to move on to I18n for now so I don't have time to add full test coverage, so we'll just have to do with what we've got for now.
- Loading branch information
Showing
5 changed files
with
168 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,3 @@ omit = | |
.venv/* | ||
docker_django_management.py | ||
manage.py | ||
deploy.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
from pathlib import Path | ||
import contextlib | ||
from unittest.mock import MagicMock | ||
import json | ||
import pytest | ||
|
||
from project.util.testing_util import Snapshot | ||
import deploy | ||
|
||
MY_DIR = Path(__file__).parent.resolve() | ||
|
||
SNAPSHOT_DIR = MY_DIR / "test_deploy_snapshots" | ||
|
||
DEFAULT_SUBPROCESS_CMD_PREFIX_OUTPUTS = { | ||
"git remote get-url": b"https://git.heroku.com/boop.git", | ||
"heroku config": json.dumps({ | ||
'DATABASE_URL': 'postgres://boop' | ||
}).encode('utf-8'), | ||
"git rev-parse HEAD": b"e7408710b8d091377041cfbe4c185931a214f280", | ||
"git status -uno --porcelain": b"M somefile.py", | ||
"heroku auth:token": b"00112233-aabb-ccdd-eeff-001122334455", | ||
} | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
def fake_tempfile(monkeypatch): | ||
import tempfile | ||
|
||
@contextlib.contextmanager | ||
def fake_temporary_directory(): | ||
yield "/tmp/somedir" | ||
|
||
monkeypatch.setattr(tempfile, 'TemporaryDirectory', fake_temporary_directory) | ||
|
||
|
||
def create_check_output(cmd_prefix_outputs=DEFAULT_SUBPROCESS_CMD_PREFIX_OUTPUTS): | ||
def check_output(args, **kwargs): | ||
cmd = ' '.join(args) | ||
for cmd_prefix, output in cmd_prefix_outputs.items(): | ||
assert isinstance(output, bytes), f"Output for '{cmd_prefix}' must be bytes" | ||
if cmd.startswith(cmd_prefix): | ||
return output | ||
raise AssertionError(f"Unexpected check_output args: {args}") | ||
|
||
return check_output | ||
|
||
|
||
def successful_check_call_with_print(args, **kwargs): | ||
cmd = ' '.join(args) | ||
print(f'Running "{cmd}".') | ||
return 0 | ||
|
||
|
||
@pytest.fixture | ||
def subprocess(monkeypatch): | ||
import subprocess | ||
monkeypatch.setattr(subprocess, 'check_call', MagicMock()) | ||
monkeypatch.setattr(subprocess, 'check_output', MagicMock()) | ||
monkeypatch.setattr(subprocess, 'call', MagicMock()) | ||
yield subprocess | ||
|
||
|
||
@contextlib.contextmanager | ||
def expect_normal_exit(): | ||
with pytest.raises(SystemExit) as excinfo: | ||
yield | ||
assert excinfo.value.code == 0 | ||
|
||
|
||
@contextlib.contextmanager | ||
def expect_abnormal_exit(): | ||
with pytest.raises(SystemExit) as excinfo: | ||
yield | ||
assert excinfo.value.code != 0 | ||
|
||
|
||
def test_it_shows_help_when_asked_for_help(capsys): | ||
with expect_normal_exit(): | ||
deploy.main(["--help"]) | ||
assert "usage: " in capsys.readouterr().out | ||
|
||
|
||
def test_it_shows_help_when_given_no_args(capsys): | ||
with expect_abnormal_exit(): | ||
deploy.main([]) | ||
assert "usage: " in capsys.readouterr().out | ||
|
||
|
||
def test_selfcheck_works(capsys): | ||
deploy.main(['selfcheck']) | ||
assert "Deployment prerequisites satisfied" in capsys.readouterr().out | ||
|
||
|
||
def test_heroku_raises_err_with_no_remote(capsys): | ||
with pytest.raises(ValueError, match="Please specify a git remote"): | ||
deploy.main(['heroku']) | ||
|
||
|
||
def test_heroku_works(subprocess, capsys): | ||
subprocess.check_output.side_effect = create_check_output() | ||
subprocess.call.side_effect = successful_check_call_with_print | ||
subprocess.check_call.side_effect = successful_check_call_with_print | ||
|
||
deploy.main(['heroku', '-r', 'myapp']) | ||
|
||
snapshot = Snapshot(capsys.readouterr().out, SNAPSHOT_DIR / "heroku_works.txt") | ||
assert snapshot.expected == snapshot.actual |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Running "docker build -f Dockerfile.web -t registry.heroku.com/boop/web --build-arg GIT_REVISION=e7408710b8d091377041cfbe4c185931a214f280 --build-arg IS_GIT_REPO_PRISTINE=False .". | ||
Running "docker build -f Dockerfile.worker -t registry.heroku.com/boop/worker --build-arg DOCKERFILE_WEB=registry.heroku.com/boop/web /tmp/somedir". | ||
Pushing containers to Docker registry... | ||
Running "docker login --username=_ --password=00112233-aabb-ccdd-eeff-001122334455 registry.heroku.com". | ||
Running "docker push registry.heroku.com/boop/web". | ||
Running "docker push registry.heroku.com/boop/worker". | ||
Running "heroku maintenance:on -r myapp". | ||
Running migrations... | ||
Running "docker run --rm -it -e DATABASE_URL registry.heroku.com/boop/web python manage.py migrate". | ||
Running "docker run --rm -it -e DATABASE_URL registry.heroku.com/boop/web python manage.py initgroups". | ||
Initiating Heroku release phase... | ||
Running "heroku container:release web worker -r myapp". | ||
Running "heroku maintenance:off -r myapp". | ||
Deploy finished. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters