diff --git a/swh/deposit/cli/admin.py b/swh/deposit/cli/admin.py --- a/swh/deposit/cli/admin.py +++ b/swh/deposit/cli/admin.py @@ -5,10 +5,17 @@ # WARNING: do not import unnecessary things here to keep cli startup time under # control +from __future__ import annotations + +from typing import TYPE_CHECKING + import click from swh.deposit.cli import deposit +if TYPE_CHECKING: + from swh.deposit.models import DepositCollection + @deposit.group("admin") @click.option( @@ -25,7 +32,7 @@ help="development or production platform", ) @click.pass_context -def admin(ctx, config_file, platform): +def admin(ctx, config_file: str, platform: str): """Server administration tasks (manipulate user or collections)""" from swh.deposit.config import setup_django_for @@ -41,15 +48,14 @@ pass -def _create_collection(name): +def _create_collection(name: str) -> DepositCollection: """Create the collection with name if it does not exist. Args: - name (str): collection's name + name: collection name Returns: - collection (DepositCollection): the existing collection object - (created or not) + collection: the existing collection object """ # to avoid loading too early django namespaces @@ -57,11 +63,11 @@ try: collection = DepositCollection.objects.get(name=name) - click.echo("Collection %s exists, nothing to do." % name) + click.echo(f"Collection '{name}' exists, skipping.") except DepositCollection.DoesNotExist: - click.echo("Create new collection %s" % name) + click.echo(f"Create collection '{name}'.") collection = DepositCollection.objects.create(name=name) - click.echo("Collection %s created" % name) + click.echo(f"Collection '{name}' created.") return collection @@ -77,14 +83,14 @@ @click.pass_context def user_create( ctx, - username, - password, - firstname, - lastname, - email, - collection, - provider_url, - domain, + username: str, + password: str, + firstname: str, + lastname: str, + email: str, + collection: str, + provider_url: str, + domain: str, ): """Create a user with some needed information (password, collection) @@ -100,20 +106,23 @@ # If collection is not provided, fallback to username if not collection: collection = username - click.echo("collection: %s" % collection) # create the collection if it does not exist - collection = _create_collection(collection) + collection_ = _create_collection(collection) # user create/update try: - user = DepositClient.objects.get(username=username) - click.echo("User %s exists, updating information." % user) + user = DepositClient.objects.get(username=username) # type: ignore + click.echo(f"Update user '{username}'.") user.set_password(password) + action_done = "updated" except DepositClient.DoesNotExist: - click.echo("Create new user %s" % username) - user = DepositClient.objects.create_user(username=username, password=password) + click.echo(f"Create user '{username}'.") + user = DepositClient.objects.create_user( # type: ignore + username=username, password=password + ) + action_done = "created" - user.collections = [collection.id] + user.collections = [collection_.id] user.first_name = firstname user.last_name = lastname user.email = email @@ -122,7 +131,7 @@ user.domain = domain user.save() - click.echo("Information registered for user %s" % user) + click.echo(f"User '{username}' {action_done}.") @user.command("list") @@ -148,18 +157,18 @@ @user.command("exists") @click.argument("username", required=True) @click.pass_context -def user_exists(ctx, username): +def user_exists(ctx, username: str): """Check if user exists. """ # to avoid loading too early django namespaces from swh.deposit.models import DepositClient try: - DepositClient.objects.get(username=username) - click.echo("User %s exists." % username) + DepositClient.objects.get(username=username) # type: ignore + click.echo(f"User {username} exists.") ctx.exit(0) except DepositClient.DoesNotExist: - click.echo("User %s does not exist." % username) + click.echo(f"User {username} does not exist.") ctx.exit(1) diff --git a/swh/deposit/tests/cli/conftest.py b/swh/deposit/tests/cli/conftest.py new file mode 100644 --- /dev/null +++ b/swh/deposit/tests/cli/conftest.py @@ -0,0 +1,12 @@ +# Copyright (C) 2019-2020 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU General Public License version 3, or any later version +# See top-level LICENSE file for more information + +from click.testing import CliRunner +import pytest + + +@pytest.fixture +def cli_runner(): + return CliRunner() diff --git a/swh/deposit/tests/cli/test_admin.py b/swh/deposit/tests/cli/test_admin.py new file mode 100644 --- /dev/null +++ b/swh/deposit/tests/cli/test_admin.py @@ -0,0 +1,189 @@ +# Copyright (C) 2019-2020 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU General Public License version 3, or any later version +# See top-level LICENSE file for more information + +import pytest + +from swh.deposit.cli.admin import admin as cli +from swh.deposit.models import DepositClient, DepositCollection + + +@pytest.fixture(autouse=True) +def enable_db_access_for_all_tests(db): + pass + + +def test_cli_admin_user_list_nothing(cli_runner): + result = cli_runner.invoke(cli, ["user", "list",]) + + assert result.exit_code == 0, f"Unexpected output: {result.output}" + assert result.output == "Empty user list\n" + + +def test_cli_admin_user_list_with_users(cli_runner, deposit_user): + result = cli_runner.invoke(cli, ["user", "list",]) + + assert result.exit_code == 0, f"Unexpected output: {result.output}" + assert result.output == f"{deposit_user.username}\n" # only 1 user + + +def test_cli_admin_collection_list_nothing(cli_runner): + result = cli_runner.invoke(cli, ["collection", "list",]) + + assert result.exit_code == 0, f"Unexpected output: {result.output}" + assert result.output == "Empty collection list\n" + + +def test_cli_admin_collection_list_with_collections(cli_runner, deposit_collection): + from swh.deposit.tests.conftest import create_deposit_collection + + new_collection = create_deposit_collection("something") + + result = cli_runner.invoke(cli, ["collection", "list",]) + + assert result.exit_code == 0, f"Unexpected output: {result.output}" + collections = "\n".join([deposit_collection.name, new_collection.name]) + assert result.output == f"{collections}\n" + + +def test_cli_admin_user_exists_unknown(cli_runner): + result = cli_runner.invoke(cli, ["user", "exists", "unknown"]) + + assert result.exit_code == 1, f"Unexpected output: {result.output}" + assert result.output == "User unknown does not exist.\n" + + +def test_cli_admin_user_exists(cli_runner, deposit_user): + result = cli_runner.invoke(cli, ["user", "exists", deposit_user.username]) + + assert result.exit_code == 0, f"Unexpected output: {result.output}" + assert result.output == f"User {deposit_user.username} exists.\n" + + +def test_cli_admin_create_collection(cli_runner): + collection_name = "something" + + try: + DepositCollection.objects.get(name=collection_name) + except DepositCollection.DoesNotExist: + pass + + result = cli_runner.invoke( + cli, ["collection", "create", "--name", collection_name,] + ) + assert result.exit_code == 0, f"Unexpected output: {result.output}" + + collection = DepositCollection.objects.get(name=collection_name) + assert collection is not None + + assert ( + result.output + == f"""Create collection '{collection_name}'. +Collection '{collection_name}' created. +""" + ) + + result2 = cli_runner.invoke( + cli, ["collection", "create", "--name", collection_name,] + ) + assert result2.exit_code == 0, f"Unexpected output: {result.output}" + assert ( + result2.output + == f"""Collection '{collection_name}' exists, skipping. +""" + ) + + +def test_cli_admin_user_create(cli_runner): + user_name = "user" + collection_name = user_name + + try: + DepositClient.objects.get(username=user_name) + except DepositClient.DoesNotExist: + pass + + try: + DepositCollection.objects.get(name=collection_name) + except DepositCollection.DoesNotExist: + pass + + result = cli_runner.invoke( + cli, ["user", "create", "--username", user_name, "--password", "password",] + ) + assert result.exit_code == 0, f"Unexpected output: {result.output}" + user = DepositClient.objects.get(username=user_name) + assert user is not None + collection = DepositCollection.objects.get(name=collection_name) + assert collection is not None + + assert ( + result.output + == f"""Create collection '{user_name}'. +Collection '{collection_name}' created. +Create user '{user_name}'. +User '{user_name}' created. +""" + ) + + assert collection.name == collection_name + assert user.username == user_name + first_password = user.password + assert first_password is not None + assert user.collections == [collection.id] + assert user.is_active is True + assert user.domain == "" + assert user.provider_url == "" + assert user.email == "" + assert user.first_name == "" + assert user.last_name == "" + + # create a user that already exists + result2 = cli_runner.invoke( + cli, + [ + "user", + "create", + "--username", + "user", + "--password", + "another-password", # changing password + "--collection", + collection_name, # specifying the collection this time + "--firstname", + "User", + "--lastname", + "no one", + "--email", + "user@org.org", + "--provider-url", + "http://some-provider.org", + "--domain", + "domain", + ], + ) + + assert result2.exit_code == 0, f"Unexpected output: {result2.output}" + user = DepositClient.objects.get(username=user_name) + assert user is not None + + assert user.username == user_name + assert user.collections == [collection.id] + assert user.is_active is True + second_password = user.password + assert second_password is not None + assert second_password != first_password, "Password should have changed" + assert user.domain == "domain" + assert user.provider_url == "http://some-provider.org" + assert user.email == "user@org.org" + assert user.first_name == "User" + assert user.last_name == "no one" + + assert ( + result2.output + == f"""Collection '{collection_name}' exists, skipping. +Update user '{user_name}'. +User '{user_name}' updated. +""" + ) diff --git a/swh/deposit/tests/cli/test_client.py b/swh/deposit/tests/cli/test_client.py --- a/swh/deposit/tests/cli/test_client.py +++ b/swh/deposit/tests/cli/test_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2019-2020 The Software Heritage developers +# Copyright (C) 2020 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information @@ -10,7 +10,6 @@ import os from unittest.mock import MagicMock -from click.testing import CliRunner import pytest import yaml @@ -50,11 +49,6 @@ return tmp_path -@pytest.fixture -def cli_runner(): - return CliRunner() - - @pytest.fixture def client_mock_api_down(mocker, slug): """A mock client whose connection with api fails due to maintenance issue