From 608482e9aa62991c0b1751bc10a72bb68f1c0392 Mon Sep 17 00:00:00 2001 From: Klaus Laube Date: Thu, 26 Sep 2019 16:21:45 +0200 Subject: [PATCH] test: Poc of the Integration Tests This PR proofs the concept of adding an additional layer to our pyramid test, which is responsible for ensuring the connection between Tomaco and other services (like an Auth server or Postgres). connects to #74 --- .coveragerc | 3 +- .editorconfig | 8 +- Makefile | 3 + Pipfile | 2 + Pipfile.lock | 118 ++++++++++---------- README.md | 16 +++ bin/docker-entrypoint.sh | 5 + docker-compose.yaml | 4 +- pytest.ini | 4 +- tomaco/auth/tests/integration/__init__.py | 0 tomaco/auth/tests/integration/test_login.py | 18 +++ tomaco/conftest.py | 15 ++- tomaco/settings.py | 5 +- 13 files changed, 128 insertions(+), 73 deletions(-) create mode 100755 bin/docker-entrypoint.sh create mode 100644 tomaco/auth/tests/integration/__init__.py create mode 100644 tomaco/auth/tests/integration/test_login.py diff --git a/.coveragerc b/.coveragerc index 6a83082..aed15a8 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,4 +1,5 @@ [run] omit = - *tests/* + *conftest.py + *tests* *.html diff --git a/.editorconfig b/.editorconfig index 5b0f668..a0a5379 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,12 +14,8 @@ charset = utf-8 [*.py] max_line_length = 119 -# Use 2 spaces for the HTML files -[*{.css,.js,.html,.tpl}] -indent_size = 2 - -# The JSON files contain newlines inconsistently -[*.json] +# Use 2 spaces for most of the assets +[*{.css,.js,.json,.html,.tpl,.yaml}] indent_size = 2 # Makefiles always use tabs for indentation diff --git a/Makefile b/Makefile index 0a1d17e..2d4315d 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ help: @echo "migrate-down .............................. Run the database migration (downgrade)" @echo "migrate-up................................. Run the database migration (upgrade)" @echo "test ...................................... Run frontend and backend tests" + @echo "test-it ................................... Run integration tests" @echo "test-javascript ........................... Run the frontend tests" @echo "test-python ............................... Run the backend tests" @echo @@ -95,3 +96,5 @@ test-javascript: test-python: pipenv run test +test-it: + pipenv run test_integration diff --git a/Pipfile b/Pipfile index 5ae7f54..b385155 100644 --- a/Pipfile +++ b/Pipfile @@ -17,6 +17,7 @@ pytest-flask = "*" pytest-cov = "==2.7.1" python-coveralls = "==2.9.2" pytest-mock = "*" +requests = "*" [packages] gunicorn = "*" @@ -42,3 +43,4 @@ run = "flask run --host=0.0.0.0 --port=8080" security = "bandit -r ." test = "pytest ." test_coverage = "pytest --cov=tomaco --cov-report term-missing tomaco" +test_integration = "pytest . -m 'integration'" diff --git a/Pipfile.lock b/Pipfile.lock index 3b1b905..2c18da4 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "02aa5e9ff2f90059867b662ba88d2b4861e13e8b641bbe354289b75feb6318a3" + "sha256": "df5145e0c05dd51fbe85a26abaa57233f0976422d90ba18c4f45f99046b7d6bf" }, "pipfile-spec": 6, "requires": { @@ -18,16 +18,16 @@ "default": { "alembic": { "hashes": [ - "sha256:cdb7d98bd5cbf65acd38d70b1c05573c432e6473a82f955cdea541b5c153b0cc" + "sha256:9f907d7e8b286a1cfb22db9084f9ce4fde7ad7956bb496dc7c952e10ac90e36a" ], - "version": "==1.0.11" + "version": "==1.2.1" }, "certifi": { "hashes": [ - "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", - "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" + "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", + "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" ], - "version": "==2019.6.16" + "version": "==2019.9.11" }, "chardet": { "hashes": [ @@ -61,11 +61,11 @@ }, "flask-sqlalchemy": { "hashes": [ - "sha256:0c9609b0d72871c540a7945ea559c8fdf5455192d2db67219509aed680a3d45a", - "sha256:8631bbea987bc3eb0f72b1f691d47bd37ceb795e73b59ab48586d76d75a7c605" + "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", + "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" ], "index": "pypi", - "version": "==2.4.0" + "version": "==2.4.1" }, "gunicorn": { "hashes": [ @@ -184,9 +184,9 @@ }, "sqlalchemy": { "hashes": [ - "sha256:0459bf0ea6478f3e904de074d65769a11d74cdc34438ab3159250c96d089aef0" + "sha256:2f8ff566a4d3a92246d367f2e9cd6ed3edeef670dcd6dda6dfdc9efed88bcd80" ], - "version": "==1.3.7" + "version": "==1.3.8" }, "sqlalchemy-utils": { "hashes": [ @@ -197,17 +197,17 @@ }, "urllib3": { "hashes": [ - "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", - "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" + "sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398", + "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86" ], - "version": "==1.25.3" + "version": "==1.25.6" }, "werkzeug": { "hashes": [ - "sha256:87ae4e5b5366da2347eb3116c0e6c681a0e939a33b2805e2c0cbd282664932c4", - "sha256:a13b74dd3c45f758d4ebdb224be8f1ab8ef58b3c0ffc1783a8c7d9f4f50227e6" + "sha256:7280924747b5733b246fe23972186c6b348f9ae29724135a6dfc1e53cea433e7", + "sha256:e5f4a1f98b52b18a93da705a7458e55afb26f32bff83ff5d19189f92462d65c4" ], - "version": "==0.15.5" + "version": "==0.16.0" } }, "develop": { @@ -272,10 +272,10 @@ }, "certifi": { "hashes": [ - "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", - "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" + "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", + "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" ], - "version": "==2019.6.16" + "version": "==2019.9.11" }, "cfgv": { "hashes": [ @@ -402,10 +402,10 @@ }, "identify": { "hashes": [ - "sha256:9aba2d08a82aa8e6f58810d4887ed3cf103a1befeb1eaf632d9c6fd2d6642542", - "sha256:b50ffad180b3a93b33a58b42597ef22493240d406ba07cc5058daf70f44b8d7c" + "sha256:4f1fe9a59df4e80fcb0213086fcf502bc1765a01ea4fe8be48da3b65afd2a017", + "sha256:d8919589bd2a5f99c66302fec0ef9027b12ae150b0b0213999ad3f695fc7296e" ], - "version": "==1.4.6" + "version": "==1.4.7" }, "idna": { "hashes": [ @@ -416,11 +416,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:23d3d873e008a513952355379d93cbcab874c58f4f034ff657c7a87422fa64e8", - "sha256:80d2de76188eabfbfcf27e6a37342c2827801e59c4cc14b0371c56fed43820e3" + "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", + "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af" ], "markers": "python_version < '3.8'", - "version": "==0.19" + "version": "==0.23" }, "ipdb": { "hashes": [ @@ -431,10 +431,10 @@ }, "ipython": { "hashes": [ - "sha256:1d3a1692921e932751bc1a1f7bb96dc38671eeefdc66ed33ee4cbc57e92a410e", - "sha256:537cd0176ff6abd06ef3e23f2d0c4c2c8a4d9277b7451544c6cbf56d1c79a83d" + "sha256:c4ab005921641e40a68e405e286e7a1fcc464497e14d81b6914b4fd95e5dee9b", + "sha256:dd76831f065f17bddd7eaa5c781f5ea32de5ef217592cf019e34043b56895aa1" ], - "version": "==7.7.0" + "version": "==7.8.0" }, "ipython-genutils": { "hashes": [ @@ -519,10 +519,10 @@ }, "packaging": { "hashes": [ - "sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9", - "sha256:c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe" + "sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", + "sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108" ], - "version": "==19.1" + "version": "==19.2" }, "parso": { "hashes": [ @@ -533,10 +533,10 @@ }, "pbr": { "hashes": [ - "sha256:56e52299170b9492513c64be44736d27a512fa7e606f21942160b68ce510b4bc", - "sha256:9b321c204a88d8ab5082699469f52cc94c5da45c51f114113d01b3d993c24cdf" + "sha256:2c8e420cd4ed4cec4e7999ee47409e876af575d4c35a45840d59e8b5f3155ab8", + "sha256:b32c8ccaac7b1a20c0ce00ce317642e6cf231cf038f9875e0280e28af5bf7ac9" ], - "version": "==5.4.2" + "version": "==5.4.3" }, "pexpect": { "hashes": [ @@ -555,18 +555,18 @@ }, "pluggy": { "hashes": [ - "sha256:0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc", - "sha256:b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c" + "sha256:0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6", + "sha256:fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34" ], - "version": "==0.12.0" + "version": "==0.13.0" }, "pre-commit": { "hashes": [ - "sha256:21ce389ea3a480170804208baff8ceaac815ecf6b9bd6c6797de5584ad69cff8", - "sha256:3b0e901f442b966444833f1924e9bf9a7c10c79741b21520f68bc87639220f5e" + "sha256:1d3c0587bda7c4e537a46c27f2c84aa006acc18facf9970bf947df596ce91f3f", + "sha256:fa78ff96e8e9ac94c748388597693f18b041a181c94a4f039ad20f45287ba44a" ], "index": "pypi", - "version": "==1.18.2" + "version": "==1.18.3" }, "prompt-toolkit": { "hashes": [ @@ -620,11 +620,11 @@ }, "pytest": { "hashes": [ - "sha256:95b1f6db806e5b1b5b443efeb58984c24945508f93a866c1719e1a507a957d7c", - "sha256:c3d5020755f70c82eceda3feaf556af9a341334414a8eca521a18f463bcead88" + "sha256:813b99704b22c7d377bbd756ebe56c35252bb710937b46f207100e843440b3c2", + "sha256:cc6620b96bc667a0c8d4fa592a8c9c94178a1bd6cc799dbb057dfd9286d31a31" ], "index": "pypi", - "version": "==5.1.1" + "version": "==5.1.3" }, "pytest-cov": { "hashes": [ @@ -700,10 +700,10 @@ }, "stevedore": { "hashes": [ - "sha256:7be098ff53d87f23d798a7ce7ae5c31f094f3deb92ba18059b1aeb1ca9fec0a0", - "sha256:7d1ce610a87d26f53c087da61f06f9b7f7e552efad2a7f6d2322632b5f932ea2" + "sha256:01d9f4beecf0fbd070ddb18e5efb10567801ba7ef3ddab0074f54e3cd4e91730", + "sha256:e0739f9739a681c7a1fda76a102b65295e96a144ccdb552f2ae03c5f0abe8a14" ], - "version": "==1.30.1" + "version": "==1.31.0" }, "toml": { "hashes": [ @@ -714,24 +714,24 @@ }, "traitlets": { "hashes": [ - "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", - "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9" + "sha256:262089114405f22f4833be96b31e143ab906d7764a22c04c71fee0bbda4787ba", + "sha256:6ad5b30dacd5e2424c46cc94a0aeab990d98ae17d181acea2cc4272ac3409fca" ], - "version": "==4.3.2" + "version": "==4.3.3.dev0" }, "urllib3": { "hashes": [ - "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", - "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" + "sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398", + "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86" ], - "version": "==1.25.3" + "version": "==1.25.6" }, "virtualenv": { "hashes": [ - "sha256:94a6898293d07f84a98add34c4df900f8ec64a570292279f6d91c781d37fd305", - "sha256:f6fc312c031f2d2344f885de114f1cb029dfcffd26aa6e57d2ee2296935c4e7d" + "sha256:680af46846662bb38c5504b78bad9ed9e4f3ba2d54f54ba42494fdf94337fe30", + "sha256:f78d81b62d3147396ac33fc9d77579ddc42cc2a98dd9ea38886f616b33bc7fb2" ], - "version": "==16.7.4" + "version": "==16.7.5" }, "wcwidth": { "hashes": [ @@ -742,10 +742,10 @@ }, "werkzeug": { "hashes": [ - "sha256:87ae4e5b5366da2347eb3116c0e6c681a0e939a33b2805e2c0cbd282664932c4", - "sha256:a13b74dd3c45f758d4ebdb224be8f1ab8ef58b3c0ffc1783a8c7d9f4f50227e6" + "sha256:7280924747b5733b246fe23972186c6b348f9ae29724135a6dfc1e53cea433e7", + "sha256:e5f4a1f98b52b18a93da705a7458e55afb26f32bff83ff5d19189f92462d65c4" ], - "version": "==0.15.5" + "version": "==0.16.0" }, "zipp": { "hashes": [ diff --git a/README.md b/README.md index c011fc4..ba4830c 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,22 @@ Or using docker: $ make test-docker ``` +### Integration tests + +In order to execute the IT tests, first you need to start the Docker containers: + +``` +$ docker-compose up +``` + +After that, you are now able to run the integration tests: + +``` +$ make test-it +``` + +### Linting + Linting is a good way of keeping the code quality high. You can have everything you want (Python, Javascript and security checks) in a single task: ``` diff --git a/bin/docker-entrypoint.sh b/bin/docker-entrypoint.sh new file mode 100755 index 0000000..2b93e44 --- /dev/null +++ b/bin/docker-entrypoint.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +./bin/wait-for-it.sh -t 60 postgres:5432 +flask db upgrade --directory tomaco/migrations +npm run run & flask run --host=0.0.0.0 --port=8080 diff --git a/docker-compose.yaml b/docker-compose.yaml index 32d0652..96fd93d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -11,12 +11,9 @@ services: - "./data/postgres:/var/lib/postgresql/data" ports: - 5432:5432 - tomaco: build: . container_name: tomaco - command: > - sh -c "npm run run & flask run --host=0.0.0.0 --port=8080" volumes: - ./:/app ports: @@ -28,6 +25,7 @@ services: GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID} GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET} DATABASE_URL: "postgresql://root@postgres/tomaco_dev" + entrypoint: bin/docker-entrypoint.sh stdin_open: true tty: true depends_on: diff --git a/pytest.ini b/pytest.ini index d52d0cc..0b42e6e 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,6 @@ [pytest] -addopts = -s +addopts = -s -m 'not integration' filterwarnings = ignore::DeprecationWarning +markers = + integration: marks tests as integration tests diff --git a/tomaco/auth/tests/integration/__init__.py b/tomaco/auth/tests/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tomaco/auth/tests/integration/test_login.py b/tomaco/auth/tests/integration/test_login.py new file mode 100644 index 0000000..fab4e42 --- /dev/null +++ b/tomaco/auth/tests/integration/test_login.py @@ -0,0 +1,18 @@ +import pytest + +from ...models import User + + +@pytest.mark.integration +class TestLogin: + def teardown_method(self, method): + User.query.delete() + User.query.session.commit() + + def test_should_access_login_page_when_not_logged_in(self, it_client): + result = it_client.get("http://0.0.0.0:8080/login") + assert result.status_code == 200 + + def test_should_create_user_after_login(self, it_client): + it_client.get("http://0.0.0.0:8080/login/start") + assert User.query.filter_by(username="gandalf").count() == 1 diff --git a/tomaco/conftest.py b/tomaco/conftest.py index c614cc2..52b9d21 100644 --- a/tomaco/conftest.py +++ b/tomaco/conftest.py @@ -1,3 +1,4 @@ +import requests import pytest from tomaco import create_app @@ -10,8 +11,13 @@ @pytest.fixture -def app(): - return create_app("tomaco.settings.Testing") +def app(request): + settings = ( + "tomaco.settings.IntegrationTests" + if request.config.option.markexpr == "integration" + else "tomaco.settings.Testing" + ) + return create_app(settings) @pytest.fixture @@ -45,3 +51,8 @@ def auth_client(client, user): @pytest.fixture def db_session_commit_mock(mocker, db): return mocker.patch.object(db.session, "commit") + + +@pytest.fixture +def it_client(app): + return requests diff --git a/tomaco/settings.py b/tomaco/settings.py index 6ce0c36..96b3ffb 100644 --- a/tomaco/settings.py +++ b/tomaco/settings.py @@ -41,9 +41,12 @@ class Development(Config): SQLALCHEMY_DATABASE_URI = os.environ.get( "DATABASE_URL", "postgresql://root@localhost/tomaco_dev" ) - TEMPLATE_AUTO_RELOAD = True class Testing(Config): TESTING = True SQLALCHEMY_DATABASE_URI = "sqlite://" + + +class IntegrationTests(Testing): + SQLALCHEMY_DATABASE_URI = "postgresql://root@0.0.0.0:5432/tomaco_dev"