diff --git a/infrahub_sdk/ctl/repository.py b/infrahub_sdk/ctl/repository.py index 8854e53b..6f69f9a5 100644 --- a/infrahub_sdk/ctl/repository.py +++ b/infrahub_sdk/ctl/repository.py @@ -1,12 +1,14 @@ from pathlib import Path +from typing import Optional import typer import yaml from pydantic import ValidationError from rich.console import Console +from infrahub_sdk.ctl.client import initialize_client + from ..async_typer import AsyncTyper -from ..ctl.client import initialize_client from ..ctl.exceptions import FileNotValidError from ..ctl.utils import init_logging from ..graphql import Mutation @@ -65,7 +67,7 @@ async def add( name: str, location: str, description: str = "", - username: str = "", + username: Optional[str] = None, password: str = "", commit: str = "", read_only: bool = False, @@ -88,10 +90,9 @@ async def add( client = initialize_client() - if username: - credential = await client.create(kind="CorePasswordCredential", name=name, username=username, password=password) - await credential.save() - input_data["data"]["credential"] = {"id": credential.id} + credential = await client.create(kind="CorePasswordCredential", name=name, username=username, password=password) + await credential.save(allow_upsert=True) + input_data["data"]["credential"] = {"id": credential.id} query = Mutation( mutation="CoreReadOnlyRepositoryCreate" if read_only else "CoreRepositoryCreate", diff --git a/tests/unit/ctl/test_repository_app.py b/tests/unit/ctl/test_repository_app.py new file mode 100644 index 00000000..fd558dc2 --- /dev/null +++ b/tests/unit/ctl/test_repository_app.py @@ -0,0 +1,273 @@ +"""Integration tests for infrahubctl commands.""" + +from unittest import mock + +import pytest +from typer.testing import CliRunner + +from infrahub_sdk.client import InfrahubClient +from infrahub_sdk.ctl.cli_commands import app + +runner = CliRunner() + + +@pytest.fixture +def mock_client() -> mock.Mock: + """Fixture for a mocked InfrahubClient.""" + client = mock.create_autospec(InfrahubClient) + return client + + +# --------------------------------------------------------- +# infrahubctl repository command tests +# --------------------------------------------------------- +@mock.patch("infrahub_sdk.ctl.repository.initialize_client") +class TestInfrahubctlRepository: + """Groups the 'infrahubctl repository' test cases.""" + + def test_repo_no_username(self, mock_init_client, mock_client) -> None: + """Case allow no username to be passed in and set it as None rather than blank string that fails.""" + mock_cred = mock.AsyncMock() + mock_cred.id = "1234" + mock_client.create.return_value = mock_cred + + mock_init_client.return_value = mock_client + output = runner.invoke( + app, + [ + "repository", + "add", + "Gitlab", + "https://gitlab.com/FragmentedPacket/nautobot-plugin-ansible-filters.git", + "--password", + "mySup3rSecureP@ssw0rd", + ], + ) + assert output.exit_code == 0 + mock_client.create.assert_called_once() + mock_client.create.assert_called_with( + name="Gitlab", + kind="CorePasswordCredential", + password="mySup3rSecureP@ssw0rd", + username=None, + ) + mock_cred.save.assert_called_once() + mock_cred.save.assert_called_with(allow_upsert=True) + mock_client.execute_graphql.assert_called_once() + mock_client.execute_graphql.assert_called_with( + query=""" +mutation { + CoreRepositoryCreate( + data: { + name: { + value: "Gitlab" + } + location: { + value: "https://gitlab.com/FragmentedPacket/nautobot-plugin-ansible-filters.git" + } + description: { + value: "" + } + commit: { + value: "" + } + credential: { + id: "1234" + } + } + ){ + ok + } +} +""", + branch_name="main", + tracker="mutation-repository-create", + ) + + def test_repo_username(self, mock_init_client, mock_client) -> None: + """Case allow no username to be passed in and set it as None rather than blank string that fails.""" + mock_cred = mock.AsyncMock() + mock_cred.id = "1234" + mock_client.create.return_value = mock_cred + + mock_init_client.return_value = mock_client + output = runner.invoke( + app, + [ + "repository", + "add", + "Gitlab", + "https://gitlab.com/FragmentedPacket/nautobot-plugin-ansible-filters.git", + "--password", + "mySup3rSecureP@ssw0rd", + "--username", + "opsmill", + ], + ) + assert output.exit_code == 0 + mock_client.create.assert_called_once() + mock_client.create.assert_called_with( + name="Gitlab", + kind="CorePasswordCredential", + password="mySup3rSecureP@ssw0rd", + username="opsmill", + ) + mock_cred.save.assert_called_once() + mock_cred.save.assert_called_with(allow_upsert=True) + mock_client.execute_graphql.assert_called_once() + mock_client.execute_graphql.assert_called_with( + query=""" +mutation { + CoreRepositoryCreate( + data: { + name: { + value: "Gitlab" + } + location: { + value: "https://gitlab.com/FragmentedPacket/nautobot-plugin-ansible-filters.git" + } + description: { + value: "" + } + commit: { + value: "" + } + credential: { + id: "1234" + } + } + ){ + ok + } +} +""", + branch_name="main", + tracker="mutation-repository-create", + ) + + def test_repo_readonly_true(self, mock_init_client, mock_client) -> None: + """Case allow no username to be passed in and set it as None rather than blank string that fails.""" + mock_cred = mock.AsyncMock() + mock_cred.id = "1234" + mock_client.create.return_value = mock_cred + + mock_init_client.return_value = mock_client + output = runner.invoke( + app, + [ + "repository", + "add", + "Gitlab", + "https://gitlab.com/FragmentedPacket/nautobot-plugin-ansible-filters.git", + "--password", + "mySup3rSecureP@ssw0rd", + "--read-only", + ], + ) + assert output.exit_code == 0 + mock_client.create.assert_called_once() + mock_client.create.assert_called_with( + name="Gitlab", + kind="CorePasswordCredential", + password="mySup3rSecureP@ssw0rd", + username=None, + ) + mock_cred.save.assert_called_once() + mock_cred.save.assert_called_with(allow_upsert=True) + mock_client.execute_graphql.assert_called_once() + mock_client.execute_graphql.assert_called_with( + query=""" +mutation { + CoreReadOnlyRepositoryCreate( + data: { + name: { + value: "Gitlab" + } + location: { + value: "https://gitlab.com/FragmentedPacket/nautobot-plugin-ansible-filters.git" + } + description: { + value: "" + } + commit: { + value: "" + } + credential: { + id: "1234" + } + } + ){ + ok + } +} +""", + branch_name="main", + tracker="mutation-repository-create", + ) + + def test_repo_description_commit_branch(self, mock_init_client, mock_client) -> None: + """Case allow no username to be passed in and set it as None rather than blank string that fails.""" + mock_cred = mock.AsyncMock() + mock_cred.id = "1234" + mock_client.create.return_value = mock_cred + + mock_init_client.return_value = mock_client + output = runner.invoke( + app, + [ + "repository", + "add", + "Gitlab", + "https://gitlab.com/FragmentedPacket/nautobot-plugin-ansible-filters.git", + "--password", + "mySup3rSecureP@ssw0rd", + "--username", + "opsmill", + "--description", + "This is a test description", + "--commit", + "myHashCommit", + "--branch", + "develop", + ], + ) + assert output.exit_code == 0 + mock_client.create.assert_called_once() + mock_client.create.assert_called_with( + name="Gitlab", + kind="CorePasswordCredential", + password="mySup3rSecureP@ssw0rd", + username="opsmill", + ) + mock_cred.save.assert_called_once() + mock_cred.save.assert_called_with(allow_upsert=True) + mock_client.execute_graphql.assert_called_once() + mock_client.execute_graphql.assert_called_with( + query=""" +mutation { + CoreRepositoryCreate( + data: { + name: { + value: "Gitlab" + } + location: { + value: "https://gitlab.com/FragmentedPacket/nautobot-plugin-ansible-filters.git" + } + description: { + value: "This is a test description" + } + commit: { + value: "myHashCommit" + } + credential: { + id: "1234" + } + } + ){ + ok + } +} +""", + branch_name="develop", + tracker="mutation-repository-create", + )