diff --git a/.coveragerc b/.coveragerc index 41d7310d08fa780729e980ab180e34412016f015..b5709d31df033e2782b30f782a7dbaf8e579a739 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,5 +1,10 @@ [run] include = discuss_data/* -omit = *migrations*, *tests* +omit = venv/*, */tests/*, */migrations/*, */urls.py, */settings/* plugins = django_coverage_plugin +[report] +skip_empty = True +omit = */__init__.py, */admin.py, */apps.py +[html] +directory = htmlcov diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 760e4445f719c4621956004ffc14b5fea30b5e2c..1f86e94d47a6d4fa4e70adafa0732516850fa524 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -26,6 +26,7 @@ variables: DOCKER_TLS_CERTDIR: "" PIP_REQUIREMENTS_FILE: requirements/production.txt DS_DISABLE_DIND: "true" + SAST_EXCLUDED_PATHS: tests/* stages: diff --git a/README.rst b/README.rst index 6e02f18dcca126f6728126a744755481100c012d..7f1c114844357ee4048ea617fd15cda6fef14eb5 100644 --- a/README.rst +++ b/README.rst @@ -4,6 +4,21 @@ Discuss Data Open Platform for the Interactive Discussion of Research Data Quality (on the example of area studies on the post-Soviet region) +The software is provided as a Docker image and can be obtained from the project's own repository ``docker.gitlab.gwdg.de/discuss-data``. +To get the latest image, run: + +.. code:: bash + + docker pull docker.gitlab.gwdg.de/discuss-data/discuss-data:latest + +Discuss Data is looking forward to your contributions. +To provide feedback, report bugs or propose enhancements, feel free to use our `Issue tracker`_. +For contributions to the source code, see the `documentation on contributions`_. + + +.. _`Issue tracker`: https://gitlab.gwdg.de/discuss-data/discuss-data/-/issues +.. _`documentation on contributions`: https://discuss-data.pages.gwdg.de/discuss-data/docs/contribution.html + Documentation ------------- diff --git a/config/settings/local.py b/config/settings/local.py index 4d827444aa95a68b860d151fb47c8843d18e1413..740be47699748da44c9dc8e652361dac66ada3fb 100644 --- a/config/settings/local.py +++ b/config/settings/local.py @@ -60,7 +60,3 @@ INTERNAL_IPS += [ip[:-1] + "1" for ip in ips] # ------------------------------------------------------------------------------ # https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration INSTALLED_APPS += ["django_extensions"] # noqa F405 -INSTALLED_APPS += ["django_template_check"] # noqa F405 - -# Your stuff... -# ------------------------------------------------------------------------------ diff --git a/discuss_data/__init__.py b/discuss_data/__init__.py index 3f5000900055664ebd8f6410d9673679a7cecc7f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/discuss_data/__init__.py +++ b/discuss_data/__init__.py @@ -1,7 +0,0 @@ -__version__ = "0.5.0" -__version_info__ = tuple( - [ - int(num) if num.isdigit() else num - for num in __version__.replace("-", ".", 1).split(".") - ] -) diff --git a/discuss_data/conftest.py b/discuss_data/conftest.py index 0762deacd39871b9f7cc01a94cfa04ff08bba386..97ebd2943037fd2b692c1ad14e2d45ee925623a4 100644 --- a/discuss_data/conftest.py +++ b/discuss_data/conftest.py @@ -1,5 +1,10 @@ import pytest +# enable database for all tests +@pytest.fixture(autouse=True) +def enable_db_access_for_all_tests(db): + pass + # pytest finds added options only in root conftest.py if not configured otherwise def pytest_addoption(parser): @@ -14,3 +19,13 @@ def pytest_addoption(parser): # list marker if requested with 'pytest --markers' def pytest_configure(config): config.addinivalue_line("markers", "integration: mark a test as integration test") + + +def pytest_collection_modifyitems(config, items): + if config.getoption("--integration"): + # --integration given in cli: do not skip integration tests + return + skip_integration = pytest.mark.skip(reason="need --integration option to run") + for item in items: + if "integration" in item.keywords: + item.add_marker(skip_integration) diff --git a/discuss_data/ddcomments/tests.py b/discuss_data/ddcomments/tests.py deleted file mode 100644 index 7ce503c2dd97ba78597f6ff6e4393132753573f6..0000000000000000000000000000000000000000 --- a/discuss_data/ddcomments/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/discuss_data/ddpublications/tests.py b/discuss_data/ddpublications/tests.py deleted file mode 100644 index 7ce503c2dd97ba78597f6ff6e4393132753573f6..0000000000000000000000000000000000000000 --- a/discuss_data/ddpublications/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/discuss_data/dhrep/tests/unit/test_token.py b/discuss_data/dhrep/tests/unit/test_token.py index f8df9b61b7b9415d5a68f53eb544a189457c745a..c488647ddf9e9ac5fa0576f5a5eb465a5a2a6ce6 100644 --- a/discuss_data/dhrep/tests/unit/test_token.py +++ b/discuss_data/dhrep/tests/unit/test_token.py @@ -2,7 +2,7 @@ import pytest from django.core.exceptions import ImproperlyConfigured -from discuss_data.dhrep.token import Token, TokenHelper +from discuss_data.dhrep.token import Token, TokenHelper, InvalidTokenException class TestTokenHelper: @@ -44,6 +44,13 @@ class TestTokenHelper: assert t.expires_in == "86400" assert t.scope == "read,write" + @staticmethod + def test_create_invalid_token_object(): + token = "aksess_token=0d0#344e5-96c0-441d-bc2c-8230f0cea2a9&token_type=bearer&expires_in=86400&scope=read,write" + th: TokenHelper = TokenHelper() + with pytest.raises(InvalidTokenException): + th.create_token_object(token) + @staticmethod def test_invalid_token(): t = Token( @@ -51,3 +58,42 @@ class TestTokenHelper: ) th = TokenHelper() assert not th.check(t) + + @staticmethod + def test_create_token_object_from_dict(): + token = { + "access_token": "0d0#344e5-96c0-441d-bc2c-8230f0cea2a9", + "token_type": "bearer", + "expires_in": "86400", + "scope": "read,write", + } + th = TokenHelper() + t = th.create_token_object_from_dict(token) + assert t.access_token == "0d0#344e5-96c0-441d-bc2c-8230f0cea2a9" + assert t.token_type == "bearer" + assert t.expires_in == "86400" + assert t.scope == "read,write" + + @staticmethod + def test_create_invalid_key_token_object_from_dict(): + token = { + "aksess_token": "0d0#344e5-96c0-441d-bc2c-8230f0cea2a9", + "token_type": "bearer", + "expires_in": "86400", + "scope": "read,write", + } + th = TokenHelper() + with pytest.raises(InvalidTokenException): + th.create_token_object_from_dict(token) + + @staticmethod + def test_to_dict(): + token = Token( + "0d0#344e5-96c0-441d-bc2c-8230f0cea2a9", "bearer", "86400", "read,write" + ) + th = TokenHelper() + t = th.to_dict(token) + assert t["access_token"] == "0d0#344e5-96c0-441d-bc2c-8230f0cea2a9" + assert t["token_type"] == "bearer" + assert t["expires_in"] == "86400" + assert t["scope"] == "read,write" diff --git a/discuss_data/dhrep/tests/unit/test_urls.py b/discuss_data/dhrep/tests/unit/test_urls.py index 9f7fe0c441d743f728ffa1d501fc5ec76ce54f62..0b31c8c97dcd64156c51d40e9d13707ce2eef5cc 100644 --- a/discuss_data/dhrep/tests/unit/test_urls.py +++ b/discuss_data/dhrep/tests/unit/test_urls.py @@ -1,17 +1,17 @@ -import pytest - from django.urls import reverse, resolve class TestUrls: - def test_get_token(self): + @staticmethod + def test_get_token(): assert reverse("dhrep:get_token") == "/dhrep/token/" assert resolve("/dhrep/token/").view_name == "dhrep:get_token" - def test_show_token(self): + @staticmethod + def test_show_token(): token: str = "access_token=0d03344e5-96c0-441d-bc2c-8230f0cea2a9&token_type=bearer&expires_in=86400&scope=read,write" assert ( - reverse("dhrep:show_token", kwargs={"token": token},) + reverse("dhrep:show_token", kwargs={"token": token}) == "/dhrep/token/" + token ) assert resolve("/dhrep/token/" + token).view_name == "dhrep:show_token" diff --git a/discuss_data/dhrep/tests/unit/test_views.py b/discuss_data/dhrep/tests/unit/test_views.py index 8f4ba8c25c9dea21cf21e013870c84d066d4a079..da06ac7636e3929af98ee3c06ac2c0925f2f0cea 100644 --- a/discuss_data/dhrep/tests/unit/test_views.py +++ b/discuss_data/dhrep/tests/unit/test_views.py @@ -1,31 +1,46 @@ import pytest from django.core.exceptions import ValidationError, ImproperlyConfigured -from django.test import Client +from django.test import Client, TestCase + + from django.urls import reverse -from discuss_data.dhrep.views import get_token, show_token +from django.contrib.auth import get_user_model -class TestViews: - client = Client() +User = get_user_model() - @pytest.mark.django_db - def test_get_token(self): + +class TestViews(TestCase): + def setUp(self): + troi = User( + first_name="Deanna", + last_name="Troi", + username="dtroi", + name_prefix="Commander", + ) + troi.save() + + def test_get_token_no_login(self): response = self.client.get(reverse("dhrep:get_token")) - assert response.status_code == 200 - assert "get a token" in str(response.content) + assert response.status_code == 302 + assert response.url == "/shib/login/?next=" + reverse("dhrep:get_token") - @pytest.mark.django_db - def test_show_unautorized_token(self): + def test_show_token_no_login(self): token: str = "access_token=0d03344e5-96c0-441d-bc2c-8230f0cea2a9&token_type=bearer&expires_in=86400&scope=read,write" response = self.client.get(reverse("dhrep:show_token", kwargs={"token": token})) + assert response.status_code == 302 + + def test_get_token(self): + self.client.force_login(User.objects.get_or_create(username="dtroi")[0]) + response = self.client.get(reverse("dhrep:get_token")) assert response.status_code == 200 - assert "Unauthorized" in str(response.content) + assert "get a token" in str(response.content) - @pytest.mark.django_db - def test_show_gone_token(self): - token: str = "access_token=6c192873-768d-4c43-81e2-5e388969020c&token_type=bearer&expires_in=86400&scope=read,write" + def test_show_invalid_token(self): + self.client.force_login(User.objects.get_or_create(username="dtroi")[0]) + token: str = "access_token=0d03344e5-96c0-441d-bc2c-8230f0cea2a9&token_type=bearer&expires_in=86400&scope=read,write" response = self.client.get(reverse("dhrep:show_token", kwargs={"token": token})) assert response.status_code == 200 - assert "Gone" in str(response.content) + assert "get a token" in str(response.content) diff --git a/discuss_data/dhrep/token.py b/discuss_data/dhrep/token.py index 02f4e432f4c27134ea86e609f45fb52a015cd7d7..11dafb5b2936cac9238411233a1a0c7ebed8c784 100644 --- a/discuss_data/dhrep/token.py +++ b/discuss_data/dhrep/token.py @@ -23,7 +23,7 @@ class Token: """ def __init__( - self, access_token: str, token_type: str, expires_in: str, scope: str + self, access_token: str, token_type: str, expires_in: str, scope: str ) -> None: self._access_token = access_token self._token_type = token_type @@ -166,13 +166,9 @@ class TokenHelper: except (TypeError, KeyError) as e: logger.error(e) raise InvalidTokenException(e) - except (ValueError) as e: - logger.error(e) - raise InvalidTokenException(token["access_token"]) else: return t - @staticmethod def to_dict(token: Token) -> Dict[str, str]: return { diff --git a/discuss_data/pages/tests.py b/discuss_data/pages/tests.py deleted file mode 100644 index 7ce503c2dd97ba78597f6ff6e4393132753573f6..0000000000000000000000000000000000000000 --- a/discuss_data/pages/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/pytest.ini b/pytest.ini index fa5db4ec4f6c6d39b9e7027bbda1541c4fc0e990..d3b30827362f2be7643f7efef7773f26e3ef52fd 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,5 @@ [pytest] addopts = --ds=config.settings.test +DJANGO_SETTINGS_MODULE = config.settings.test python_files = tests.py test_*.py norecursedirs = node_modules diff --git a/requirements/local.txt b/requirements/local.txt index 3f3fe35b68eab2c9066475ad34323210a8c82388..4c6d285c2f8484fd7ae0a95d10732eb1617f9d01 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -16,10 +16,10 @@ django-coverage-plugin==1.8.0 # https://github.com/nedbat/django_coverage_plugi # Testing # ------------------------------------------------------------------------------ -django-nose==1.4.6 # https://github.com/jazzband/django-nose -django-template-check==0.3.1 # https://github.com/joostrijneveld/django-template-check +coverage==5.1 # https://github.com/nedbat/coveragepy +django-webtest==1.9.7 # https://github.com/django-webtest/django-webtest mypy==0.770 # https://github.com/python/mypy -pytest==5.3.2 # https://github.com/pytest-dev/pytest +pytest==5.4.2 # https://github.com/pytest-dev/pytest pytest-django==3.9.0 # https://github.com/pytest-dev/pytest-django pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar rdflib==4.2.2 # https://github.com/RDFLib/rdflib @@ -30,7 +30,7 @@ selenium==3.141.0 # https://github.com/SeleniumHQ/selenium # Code quality # ------------------------------------------------------------------------------ flake8==3.7.9 # https://github.com/PyCQA/flake8 -coverage==4.5.4 # https://github.com/nedbat/coveragepy black==19.10b0 # https://github.com/psf/black pylint-django==2.0.15 # https://github.com/PyCQA/pylint-django pre-commit==2.4.0 # https://github.com/pre-commit/pre-commit +yamllint==1.23.0 # https://github.com/adrienverge/yamllint diff --git a/run_pytest b/run_pytest index 8c0d512b6ab707db34187cb62ba953503af03289..10e2669f0b05aa5083db8ac266209d6017c10194 100755 --- a/run_pytest +++ b/run_pytest @@ -1,7 +1,7 @@ #!/bin/sh echo "preparing tests..." -cp ./discuss_data/dhrep/fixtures/* /app/discuss_data/media/ +cp ./discuss_data/dhrep/fixtures/* /app/data/ echo "making migrations..." ./manage.py makemigrations @@ -10,7 +10,7 @@ echo "migrating..." ./manage.py migrate echo "running pytest..." -coverage run -m pytest +coverage run -m pytest discuss_data/ echo "creating html coverage report..." coverage html