From a820aada42e21a880d88debc81604a7cd6a56695 Mon Sep 17 00:00:00 2001 From: Jovan Zivanov Date: Thu, 26 Dec 2019 14:23:53 +0100 Subject: [PATCH 1/5] add codecommit create, get and delete repository --- IMPLEMENTATION_COVERAGE.md | 6 +- moto/__init__.py | 1 + moto/codecommit/__init__.py | 4 + moto/codecommit/exceptions.py | 32 +++++ moto/codecommit/models.py | 63 +++++++++ moto/codecommit/responses.py | 46 ++++++ moto/codecommit/urls.py | 6 + tests/test_codecommit/test_codecommit.py | 170 +++++++++++++++++++++++ 8 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 moto/codecommit/__init__.py create mode 100644 moto/codecommit/exceptions.py create mode 100644 moto/codecommit/models.py create mode 100644 moto/codecommit/responses.py create mode 100644 moto/codecommit/urls.py create mode 100644 tests/test_codecommit/test_codecommit.py diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 5e6ef1c9e..245cf4705 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -1240,14 +1240,14 @@ - [ ] create_commit - [ ] create_pull_request - [ ] create_pull_request_approval_rule -- [ ] create_repository +- [X] create_repository - [ ] create_unreferenced_merge_commit - [ ] delete_approval_rule_template - [ ] delete_branch - [ ] delete_comment_content - [ ] delete_file - [ ] delete_pull_request_approval_rule -- [ ] delete_repository +- [X] delete_repository - [ ] describe_merge_conflicts - [ ] describe_pull_request_events - [ ] disassociate_approval_rule_template_from_repository @@ -1268,7 +1268,7 @@ - [ ] get_pull_request - [ ] get_pull_request_approval_states - [ ] get_pull_request_override_state -- [ ] get_repository +- [X] get_repository - [ ] get_repository_triggers - [ ] list_approval_rule_templates - [ ] list_associated_approval_rule_templates_for_repository diff --git a/moto/__init__.py b/moto/__init__.py index a9f1bb8ba..db79c59f6 100644 --- a/moto/__init__.py +++ b/moto/__init__.py @@ -9,6 +9,7 @@ from .batch import mock_batch # noqa from .cloudformation import mock_cloudformation # noqa from .cloudformation import mock_cloudformation_deprecated # noqa from .cloudwatch import mock_cloudwatch, mock_cloudwatch_deprecated # noqa +from .codecommit import mock_codecommit from .codepipeline import mock_codepipeline # noqa from .cognitoidentity import mock_cognitoidentity # noqa from .cognitoidentity import mock_cognitoidentity_deprecated # noqa diff --git a/moto/codecommit/__init__.py b/moto/codecommit/__init__.py new file mode 100644 index 000000000..c1da043ee --- /dev/null +++ b/moto/codecommit/__init__.py @@ -0,0 +1,4 @@ +from .models import codecommit_backends +from ..core.models import base_decorator + +mock_codecommit = base_decorator(codecommit_backends) \ No newline at end of file diff --git a/moto/codecommit/exceptions.py b/moto/codecommit/exceptions.py new file mode 100644 index 000000000..2cb3f7fae --- /dev/null +++ b/moto/codecommit/exceptions.py @@ -0,0 +1,32 @@ +from moto.core.exceptions import JsonRESTError + + +class RepositoryNameExistsException(JsonRESTError): + code = 400 + + def __init__(self, repository_name): + super(RepositoryNameExistsException, self).__init__( + "RepositoryNameExistsException", "Repository named {0} already exists".format(repository_name) + ) + + +class RepositoryDoesNotExistException(JsonRESTError): + code = 400 + + def __init__(self, repository_name): + super(RepositoryDoesNotExistException, self).__init__( + "RepositoryDoesNotExistException", "{0} does not exist".format(repository_name) + ) + + +class InvalidRepositoryNameException(JsonRESTError): + code = 400 + + def __init__(self): + super(InvalidRepositoryNameException, self).__init__( + "InvalidRepositoryNameException", "The repository name is not valid. Repository names can be any valid " + "combination of letters, numbers, " + "periods, underscores, and dashes between 1 and 100 characters in " + "length. Names are case sensitive. " + "For more information, see Limits in the AWS CodeCommit User Guide. " + ) diff --git a/moto/codecommit/models.py b/moto/codecommit/models.py new file mode 100644 index 000000000..e691507b6 --- /dev/null +++ b/moto/codecommit/models.py @@ -0,0 +1,63 @@ +from boto3 import Session +from moto.core import BaseBackend, BaseModel +from moto.core.utils import iso_8601_datetime_with_milliseconds +from datetime import datetime +from moto.iam.models import ACCOUNT_ID +from .exceptions import RepositoryDoesNotExistException, RepositoryNameExistsException +import uuid + + +class CodeCommit(BaseModel): + def __init__(self, region, repository_description, repository_name): + current_date = iso_8601_datetime_with_milliseconds(datetime.utcnow()) + self.repository_metadata = dict() + self.repository_metadata["repositoryName"] = repository_name + self.repository_metadata["cloneUrlSsh"] = "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( + region, repository_name + ) + self.repository_metadata["cloneUrlHttp"] = "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( + region, repository_name + ) + self.repository_metadata["creationDate"] = current_date + self.repository_metadata["lastModifiedDate"] = current_date + self.repository_metadata["repositoryDescription"] = repository_description + self.repository_metadata["repositoryId"] = str(uuid.uuid4()) + self.repository_metadata["Arn"] = "arn:aws:codecommit:{0}:{1}:{2}".format( + region, ACCOUNT_ID, repository_name + ) + self.repository_metadata["accountId"] = ACCOUNT_ID + + +class CodeCommitBackend(BaseBackend): + def __init__(self): + self.repositories = {} + + def create_repository(self, region, repository_name, repository_description): + repository = self.repositories.get(repository_name) + if repository: + raise RepositoryNameExistsException(repository_name) + + self.repositories[repository_name] = CodeCommit(region, repository_description, repository_name) + + return self.repositories[repository_name].repository_metadata + + def get_repository(self, repository_name): + repository = self.repositories.get(repository_name) + if not repository: + raise RepositoryDoesNotExistException(repository_name) + + return repository.repository_metadata + + def delete_repository(self, repository_name): + repository = self.repositories.get(repository_name) + + if repository: + self.repositories.pop(repository_name) + return repository.repository_metadata.get("repositoryId") + + return None + + +codecommit_backends = {} +for region in Session().get_available_regions("codecommit"): + codecommit_backends[region] = CodeCommitBackend() diff --git a/moto/codecommit/responses.py b/moto/codecommit/responses.py new file mode 100644 index 000000000..1ee177f37 --- /dev/null +++ b/moto/codecommit/responses.py @@ -0,0 +1,46 @@ +import json +import re + +from moto.core.responses import BaseResponse +from .models import codecommit_backends +from .exceptions import InvalidRepositoryNameException + + +class CodeCommitResponse(BaseResponse): + @property + def codecommit_backend(self): + return codecommit_backends[self.region] + + def create_repository(self): + if not self._is_repository_name_valid(self._get_param("repositoryName")): + raise InvalidRepositoryNameException() + + repository_metadata = self.codecommit_backend.create_repository( + self.region, self._get_param("repositoryName"), self._get_param("repositoryDescription") + ) + + return json.dumps({"repositoryMetadata": repository_metadata}) + + def get_repository(self): + if not self._is_repository_name_valid(self._get_param("repositoryName")): + raise InvalidRepositoryNameException() + + repository_metadata = self.codecommit_backend.get_repository(self._get_param("repositoryName")) + + return json.dumps({"repositoryMetadata": repository_metadata}) + + def delete_repository(self): + if not self._is_repository_name_valid(self._get_param("repositoryName")): + raise InvalidRepositoryNameException() + + repository_id = self.codecommit_backend.delete_repository(self._get_param("repositoryName")) + + if repository_id: + return json.dumps({"repositoryId": repository_id}) + + return json.dumps({}) + + def _is_repository_name_valid(self, repository_name): + name_regex = re.compile(r"[\w\.-]+") + result = name_regex.fullmatch(repository_name) + return result diff --git a/moto/codecommit/urls.py b/moto/codecommit/urls.py new file mode 100644 index 000000000..1e3cdb1b4 --- /dev/null +++ b/moto/codecommit/urls.py @@ -0,0 +1,6 @@ +from __future__ import unicode_literals +from .responses import CodeCommitResponse + +url_bases = ["https?://codecommit.(.+).amazonaws.com"] + +url_paths = {"{0}/$": CodeCommitResponse.dispatch} diff --git a/tests/test_codecommit/test_codecommit.py b/tests/test_codecommit/test_codecommit.py new file mode 100644 index 000000000..c62c0127f --- /dev/null +++ b/tests/test_codecommit/test_codecommit.py @@ -0,0 +1,170 @@ +import boto3 + +import sure # noqa +from moto import mock_codecommit +from moto.iam.models import ACCOUNT_ID +from botocore.exceptions import ClientError +from nose.tools import assert_raises + + +@mock_codecommit +def test_create_repository(): + client = boto3.client("codecommit", region_name="eu-central-1") + response = client.create_repository( + repositoryName='repository_one', + repositoryDescription='description repo one' + ) + + response.should_not.be.none + response["repositoryMetadata"].should_not.be.none + response["repositoryMetadata"]["creationDate"].should_not.be.none + response["repositoryMetadata"]["lastModifiedDate"].should_not.be.none + response["repositoryMetadata"]["repositoryId"].should_not.be.empty + response["repositoryMetadata"]["repositoryName"].should.equal("repository_one") + response["repositoryMetadata"]["repositoryDescription"].should.equal('description repo one') + response["repositoryMetadata"]["cloneUrlSsh"].should.equal("ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}". + format("eu-central-1", 'repository_one')) + response["repositoryMetadata"]["cloneUrlHttp"].should.equal("https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}". + format("eu-central-1", 'repository_one')) + response["repositoryMetadata"]["Arn"].should.equal("arn:aws:codecommit:{0}:{1}:{2}".format( + "eu-central-1", ACCOUNT_ID, 'repository_one' + )) + response["repositoryMetadata"]["accountId"].should.equal(ACCOUNT_ID) + + response = client.create_repository( + repositoryName='repository_two' + ) + + response.should_not.be.none + response.get("repositoryMetadata").should_not.be.none + response.get("repositoryMetadata").get("repositoryName").should.equal("repository_two") + response.get("repositoryMetadata").get("repositoryDescription").should.be.none + + with assert_raises(ClientError) as e: + client.create_repository( + repositoryName='repository_two' + ) + ex = e.exception + ex.operation_name.should.equal("CreateRepository") + ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + ex.response["Error"]["Code"].should.contain("RepositoryNameExistsException") + ex.response["Error"]["Message"].should.equal("Repository named {0} already exists".format("repository_two")) + + +@mock_codecommit +def test_get_repository(): + client = boto3.client("codecommit", region_name="eu-central-1") + + repository_name = 'repository_one' + + client.create_repository( + repositoryName=repository_name, + repositoryDescription='description repo one' + ) + + response = client.get_repository( + repositoryName=repository_name + ) + + response.should_not.be.none + response.get("repositoryMetadata").should_not.be.none + response.get("repositoryMetadata").get("creationDate").should_not.be.none + response.get("repositoryMetadata").get("lastModifiedDate").should_not.be.none + response.get("repositoryMetadata").get("repositoryId").should_not.be.empty + response.get("repositoryMetadata").get("repositoryName").should.equal(repository_name) + response.get("repositoryMetadata").get("repositoryDescription").should.equal('description repo one') + response.get("repositoryMetadata").get("cloneUrlSsh") \ + .should.equal("ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format("eu-central-1", 'repository_one')) + response.get("repositoryMetadata").get("cloneUrlHttp") \ + .should.equal("https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format("eu-central-1", 'repository_one')) + response.get("repositoryMetadata").get("Arn") \ + .should.equal("arn:aws:codecommit:{0}:{1}:{2}".format("eu-central-1", ACCOUNT_ID, 'repository_one' + )) + response.get("repositoryMetadata").get("accountId").should.equal(ACCOUNT_ID) + + client = boto3.client("codecommit", region_name="us-east-1") + + with assert_raises(ClientError) as e: + client.get_repository( + repositoryName=repository_name + ) + ex = e.exception + ex.operation_name.should.equal("GetRepository") + ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + ex.response["Error"]["Code"].should.contain("RepositoryDoesNotExistException") + ex.response["Error"]["Message"].should.equal("{0} does not exist".format(repository_name)) + + +@mock_codecommit +def test_invalid_repository_name(): + client = boto3.client("codecommit", region_name="eu-central-1") + + with assert_raises(ClientError) as e: + client.create_repository( + repositoryName='repository_one-@#@' + ) + ex = e.exception + ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException") + ex.response["Error"]["Message"].should.equal("The repository name is not valid. Repository names can be any valid " + "combination of letters, numbers, " + "periods, underscores, and dashes between 1 and 100 characters in " + "length. Names are case sensitive. " + "For more information, see Limits in the AWS CodeCommit User Guide. ") + with assert_raises(ClientError) as e: + client.create_repository( + repositoryName='!_repository_one' + ) + + with assert_raises(ClientError) as e: + client.create_repository( + repositoryName='_rep@ository_one' + ) + + with assert_raises(ClientError) as e: + client.get_repository( + repositoryName='_rep@ository_one' + ) + + +@mock_codecommit +def test_delete_repository(): + client = boto3.client("codecommit", region_name="us-east-1") + + response = client.create_repository( + repositoryName='repository_one' + ) + + repository_id_create = response.get("repositoryMetadata").get("repositoryId") + + response = client.delete_repository( + repositoryName='repository_one' + ) + + response.get('repositoryId').should_not.be.none + repository_id_create.should.equal(response.get("repositoryId")) + + response = client.delete_repository( + repositoryName='unknown_repository' + ) + + response.get('repositoryId').should.be.none + + +@mock_codecommit +def test_delete_repository_invalid_repository_name(): + client = boto3.client("codecommit", region_name="us-east-1") + + with assert_raises(ClientError) as e: + client.delete_repository( + repositoryName='_rep@ository_one' + ) + ex = e.exception + ex.operation_name.should.equal("DeleteRepository") + ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException") + ex.response["Error"]["Message"].should.equal("The repository name is not valid. Repository names can be any valid " + "combination of letters, numbers, " + "periods, underscores, and dashes between 1 and 100 characters in " + "length. Names are case sensitive. " + "For more information, see Limits in the AWS CodeCommit User Guide. ") \ No newline at end of file From e20deb4acdacc0b590ce7f67507a932483904f99 Mon Sep 17 00:00:00 2001 From: Jovan Zivanov Date: Thu, 26 Dec 2019 15:02:24 +0100 Subject: [PATCH 2/5] fix linter exceptions --- moto/__init__.py | 2 +- moto/codecommit/__init__.py | 2 +- moto/codecommit/exceptions.py | 17 +-- moto/codecommit/models.py | 12 +- moto/codecommit/responses.py | 12 +- tests/test_codecommit/test_codecommit.py | 152 ++++++++++++----------- 6 files changed, 109 insertions(+), 88 deletions(-) diff --git a/moto/__init__.py b/moto/__init__.py index db79c59f6..835e37e2e 100644 --- a/moto/__init__.py +++ b/moto/__init__.py @@ -9,7 +9,7 @@ from .batch import mock_batch # noqa from .cloudformation import mock_cloudformation # noqa from .cloudformation import mock_cloudformation_deprecated # noqa from .cloudwatch import mock_cloudwatch, mock_cloudwatch_deprecated # noqa -from .codecommit import mock_codecommit +from .codecommit import mock_codecommit # noqa from .codepipeline import mock_codepipeline # noqa from .cognitoidentity import mock_cognitoidentity # noqa from .cognitoidentity import mock_cognitoidentity_deprecated # noqa diff --git a/moto/codecommit/__init__.py b/moto/codecommit/__init__.py index c1da043ee..6c5a8f5ad 100644 --- a/moto/codecommit/__init__.py +++ b/moto/codecommit/__init__.py @@ -1,4 +1,4 @@ from .models import codecommit_backends from ..core.models import base_decorator -mock_codecommit = base_decorator(codecommit_backends) \ No newline at end of file +mock_codecommit = base_decorator(codecommit_backends) diff --git a/moto/codecommit/exceptions.py b/moto/codecommit/exceptions.py index 2cb3f7fae..136af50f1 100644 --- a/moto/codecommit/exceptions.py +++ b/moto/codecommit/exceptions.py @@ -6,7 +6,8 @@ class RepositoryNameExistsException(JsonRESTError): def __init__(self, repository_name): super(RepositoryNameExistsException, self).__init__( - "RepositoryNameExistsException", "Repository named {0} already exists".format(repository_name) + "RepositoryNameExistsException", + "Repository named {0} already exists".format(repository_name), ) @@ -15,7 +16,8 @@ class RepositoryDoesNotExistException(JsonRESTError): def __init__(self, repository_name): super(RepositoryDoesNotExistException, self).__init__( - "RepositoryDoesNotExistException", "{0} does not exist".format(repository_name) + "RepositoryDoesNotExistException", + "{0} does not exist".format(repository_name), ) @@ -24,9 +26,10 @@ class InvalidRepositoryNameException(JsonRESTError): def __init__(self): super(InvalidRepositoryNameException, self).__init__( - "InvalidRepositoryNameException", "The repository name is not valid. Repository names can be any valid " - "combination of letters, numbers, " - "periods, underscores, and dashes between 1 and 100 characters in " - "length. Names are case sensitive. " - "For more information, see Limits in the AWS CodeCommit User Guide. " + "InvalidRepositoryNameException", + "The repository name is not valid. Repository names can be any valid " + "combination of letters, numbers, " + "periods, underscores, and dashes between 1 and 100 characters in " + "length. Names are case sensitive. " + "For more information, see Limits in the AWS CodeCommit User Guide. ", ) diff --git a/moto/codecommit/models.py b/moto/codecommit/models.py index e691507b6..6a4e82ad2 100644 --- a/moto/codecommit/models.py +++ b/moto/codecommit/models.py @@ -12,10 +12,14 @@ class CodeCommit(BaseModel): current_date = iso_8601_datetime_with_milliseconds(datetime.utcnow()) self.repository_metadata = dict() self.repository_metadata["repositoryName"] = repository_name - self.repository_metadata["cloneUrlSsh"] = "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( + self.repository_metadata[ + "cloneUrlSsh" + ] = "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( region, repository_name ) - self.repository_metadata["cloneUrlHttp"] = "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( + self.repository_metadata[ + "cloneUrlHttp" + ] = "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( region, repository_name ) self.repository_metadata["creationDate"] = current_date @@ -37,7 +41,9 @@ class CodeCommitBackend(BaseBackend): if repository: raise RepositoryNameExistsException(repository_name) - self.repositories[repository_name] = CodeCommit(region, repository_description, repository_name) + self.repositories[repository_name] = CodeCommit( + region, repository_description, repository_name + ) return self.repositories[repository_name].repository_metadata diff --git a/moto/codecommit/responses.py b/moto/codecommit/responses.py index 1ee177f37..3e2cf2539 100644 --- a/moto/codecommit/responses.py +++ b/moto/codecommit/responses.py @@ -16,7 +16,9 @@ class CodeCommitResponse(BaseResponse): raise InvalidRepositoryNameException() repository_metadata = self.codecommit_backend.create_repository( - self.region, self._get_param("repositoryName"), self._get_param("repositoryDescription") + self.region, + self._get_param("repositoryName"), + self._get_param("repositoryDescription"), ) return json.dumps({"repositoryMetadata": repository_metadata}) @@ -25,7 +27,9 @@ class CodeCommitResponse(BaseResponse): if not self._is_repository_name_valid(self._get_param("repositoryName")): raise InvalidRepositoryNameException() - repository_metadata = self.codecommit_backend.get_repository(self._get_param("repositoryName")) + repository_metadata = self.codecommit_backend.get_repository( + self._get_param("repositoryName") + ) return json.dumps({"repositoryMetadata": repository_metadata}) @@ -33,7 +37,9 @@ class CodeCommitResponse(BaseResponse): if not self._is_repository_name_valid(self._get_param("repositoryName")): raise InvalidRepositoryNameException() - repository_id = self.codecommit_backend.delete_repository(self._get_param("repositoryName")) + repository_id = self.codecommit_backend.delete_repository( + self._get_param("repositoryName") + ) if repository_id: return json.dumps({"repositoryId": repository_id}) diff --git a/tests/test_codecommit/test_codecommit.py b/tests/test_codecommit/test_codecommit.py index c62c0127f..8be10033a 100644 --- a/tests/test_codecommit/test_codecommit.py +++ b/tests/test_codecommit/test_codecommit.py @@ -11,8 +11,7 @@ from nose.tools import assert_raises def test_create_repository(): client = boto3.client("codecommit", region_name="eu-central-1") response = client.create_repository( - repositoryName='repository_one', - repositoryDescription='description repo one' + repositoryName="repository_one", repositoryDescription="description repo one" ) response.should_not.be.none @@ -21,78 +20,97 @@ def test_create_repository(): response["repositoryMetadata"]["lastModifiedDate"].should_not.be.none response["repositoryMetadata"]["repositoryId"].should_not.be.empty response["repositoryMetadata"]["repositoryName"].should.equal("repository_one") - response["repositoryMetadata"]["repositoryDescription"].should.equal('description repo one') - response["repositoryMetadata"]["cloneUrlSsh"].should.equal("ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}". - format("eu-central-1", 'repository_one')) - response["repositoryMetadata"]["cloneUrlHttp"].should.equal("https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}". - format("eu-central-1", 'repository_one')) - response["repositoryMetadata"]["Arn"].should.equal("arn:aws:codecommit:{0}:{1}:{2}".format( - "eu-central-1", ACCOUNT_ID, 'repository_one' - )) + response["repositoryMetadata"]["repositoryDescription"].should.equal( + "description repo one" + ) + response["repositoryMetadata"]["cloneUrlSsh"].should.equal( + "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( + "eu-central-1", "repository_one" + ) + ) + response["repositoryMetadata"]["cloneUrlHttp"].should.equal( + "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( + "eu-central-1", "repository_one" + ) + ) + response["repositoryMetadata"]["Arn"].should.equal( + "arn:aws:codecommit:{0}:{1}:{2}".format( + "eu-central-1", ACCOUNT_ID, "repository_one" + ) + ) response["repositoryMetadata"]["accountId"].should.equal(ACCOUNT_ID) - response = client.create_repository( - repositoryName='repository_two' - ) + response = client.create_repository(repositoryName="repository_two") response.should_not.be.none response.get("repositoryMetadata").should_not.be.none - response.get("repositoryMetadata").get("repositoryName").should.equal("repository_two") + response.get("repositoryMetadata").get("repositoryName").should.equal( + "repository_two" + ) response.get("repositoryMetadata").get("repositoryDescription").should.be.none with assert_raises(ClientError) as e: - client.create_repository( - repositoryName='repository_two' - ) + client.create_repository(repositoryName="repository_two") ex = e.exception ex.operation_name.should.equal("CreateRepository") ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) ex.response["Error"]["Code"].should.contain("RepositoryNameExistsException") - ex.response["Error"]["Message"].should.equal("Repository named {0} already exists".format("repository_two")) + ex.response["Error"]["Message"].should.equal( + "Repository named {0} already exists".format("repository_two") + ) @mock_codecommit def test_get_repository(): client = boto3.client("codecommit", region_name="eu-central-1") - repository_name = 'repository_one' + repository_name = "repository_one" client.create_repository( - repositoryName=repository_name, - repositoryDescription='description repo one' + repositoryName=repository_name, repositoryDescription="description repo one" ) - response = client.get_repository( - repositoryName=repository_name - ) + response = client.get_repository(repositoryName=repository_name) response.should_not.be.none response.get("repositoryMetadata").should_not.be.none response.get("repositoryMetadata").get("creationDate").should_not.be.none response.get("repositoryMetadata").get("lastModifiedDate").should_not.be.none response.get("repositoryMetadata").get("repositoryId").should_not.be.empty - response.get("repositoryMetadata").get("repositoryName").should.equal(repository_name) - response.get("repositoryMetadata").get("repositoryDescription").should.equal('description repo one') - response.get("repositoryMetadata").get("cloneUrlSsh") \ - .should.equal("ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format("eu-central-1", 'repository_one')) - response.get("repositoryMetadata").get("cloneUrlHttp") \ - .should.equal("https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format("eu-central-1", 'repository_one')) - response.get("repositoryMetadata").get("Arn") \ - .should.equal("arn:aws:codecommit:{0}:{1}:{2}".format("eu-central-1", ACCOUNT_ID, 'repository_one' - )) + response.get("repositoryMetadata").get("repositoryName").should.equal( + repository_name + ) + response.get("repositoryMetadata").get("repositoryDescription").should.equal( + "description repo one" + ) + response.get("repositoryMetadata").get("cloneUrlSsh").should.equal( + "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( + "eu-central-1", "repository_one" + ) + ) + response.get("repositoryMetadata").get("cloneUrlHttp").should.equal( + "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( + "eu-central-1", "repository_one" + ) + ) + response.get("repositoryMetadata").get("Arn").should.equal( + "arn:aws:codecommit:{0}:{1}:{2}".format( + "eu-central-1", ACCOUNT_ID, "repository_one" + ) + ) response.get("repositoryMetadata").get("accountId").should.equal(ACCOUNT_ID) client = boto3.client("codecommit", region_name="us-east-1") with assert_raises(ClientError) as e: - client.get_repository( - repositoryName=repository_name - ) + client.get_repository(repositoryName=repository_name) ex = e.exception ex.operation_name.should.equal("GetRepository") ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) ex.response["Error"]["Code"].should.contain("RepositoryDoesNotExistException") - ex.response["Error"]["Message"].should.equal("{0} does not exist".format(repository_name)) + ex.response["Error"]["Message"].should.equal( + "{0} does not exist".format(repository_name) + ) @mock_codecommit @@ -100,55 +118,43 @@ def test_invalid_repository_name(): client = boto3.client("codecommit", region_name="eu-central-1") with assert_raises(ClientError) as e: - client.create_repository( - repositoryName='repository_one-@#@' - ) + client.create_repository(repositoryName="repository_one-@#@") ex = e.exception ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException") - ex.response["Error"]["Message"].should.equal("The repository name is not valid. Repository names can be any valid " - "combination of letters, numbers, " - "periods, underscores, and dashes between 1 and 100 characters in " - "length. Names are case sensitive. " - "For more information, see Limits in the AWS CodeCommit User Guide. ") + ex.response["Error"]["Message"].should.equal( + "The repository name is not valid. Repository names can be any valid " + "combination of letters, numbers, " + "periods, underscores, and dashes between 1 and 100 characters in " + "length. Names are case sensitive. " + "For more information, see Limits in the AWS CodeCommit User Guide. " + ) with assert_raises(ClientError) as e: - client.create_repository( - repositoryName='!_repository_one' - ) + client.create_repository(repositoryName="!_repository_one") with assert_raises(ClientError) as e: - client.create_repository( - repositoryName='_rep@ository_one' - ) + client.create_repository(repositoryName="_rep@ository_one") with assert_raises(ClientError) as e: - client.get_repository( - repositoryName='_rep@ository_one' - ) + client.get_repository(repositoryName="_rep@ository_one") @mock_codecommit def test_delete_repository(): client = boto3.client("codecommit", region_name="us-east-1") - response = client.create_repository( - repositoryName='repository_one' - ) + response = client.create_repository(repositoryName="repository_one") repository_id_create = response.get("repositoryMetadata").get("repositoryId") - response = client.delete_repository( - repositoryName='repository_one' - ) + response = client.delete_repository(repositoryName="repository_one") - response.get('repositoryId').should_not.be.none + response.get("repositoryId").should_not.be.none repository_id_create.should.equal(response.get("repositoryId")) - response = client.delete_repository( - repositoryName='unknown_repository' - ) + response = client.delete_repository(repositoryName="unknown_repository") - response.get('repositoryId').should.be.none + response.get("repositoryId").should.be.none @mock_codecommit @@ -156,15 +162,15 @@ def test_delete_repository_invalid_repository_name(): client = boto3.client("codecommit", region_name="us-east-1") with assert_raises(ClientError) as e: - client.delete_repository( - repositoryName='_rep@ository_one' - ) + client.delete_repository(repositoryName="_rep@ository_one") ex = e.exception ex.operation_name.should.equal("DeleteRepository") ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException") - ex.response["Error"]["Message"].should.equal("The repository name is not valid. Repository names can be any valid " - "combination of letters, numbers, " - "periods, underscores, and dashes between 1 and 100 characters in " - "length. Names are case sensitive. " - "For more information, see Limits in the AWS CodeCommit User Guide. ") \ No newline at end of file + ex.response["Error"]["Message"].should.equal( + "The repository name is not valid. Repository names can be any valid " + "combination of letters, numbers, " + "periods, underscores, and dashes between 1 and 100 characters in " + "length. Names are case sensitive. " + "For more information, see Limits in the AWS CodeCommit User Guide. " + ) From cb1eb79b894f25c47c19da841599583da5beca8e Mon Sep 17 00:00:00 2001 From: Jovan Zivanov Date: Thu, 26 Dec 2019 16:06:53 +0100 Subject: [PATCH 3/5] add tests for codecommit --- tests/test_codecommit/test_codecommit.py | 68 ++++++++++++++++++++---- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/tests/test_codecommit/test_codecommit.py b/tests/test_codecommit/test_codecommit.py index 8be10033a..6e916f20a 100644 --- a/tests/test_codecommit/test_codecommit.py +++ b/tests/test_codecommit/test_codecommit.py @@ -40,6 +40,11 @@ def test_create_repository(): ) response["repositoryMetadata"]["accountId"].should.equal(ACCOUNT_ID) + +@mock_codecommit +def test_create_repository_without_description(): + client = boto3.client("codecommit", region_name="eu-central-1") + response = client.create_repository(repositoryName="repository_two") response.should_not.be.none @@ -48,9 +53,39 @@ def test_create_repository(): "repository_two" ) response.get("repositoryMetadata").get("repositoryDescription").should.be.none + response["repositoryMetadata"].should_not.be.none + response["repositoryMetadata"]["creationDate"].should_not.be.none + response["repositoryMetadata"]["lastModifiedDate"].should_not.be.none + response["repositoryMetadata"]["repositoryId"].should_not.be.empty + response["repositoryMetadata"]["cloneUrlSsh"].should.equal( + "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( + "eu-central-1", "repository_two" + ) + ) + response["repositoryMetadata"]["cloneUrlHttp"].should.equal( + "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format( + "eu-central-1", "repository_two" + ) + ) + response["repositoryMetadata"]["Arn"].should.equal( + "arn:aws:codecommit:{0}:{1}:{2}".format( + "eu-central-1", ACCOUNT_ID, "repository_two" + ) + ) + response["repositoryMetadata"]["accountId"].should.equal(ACCOUNT_ID) + + +@mock_codecommit +def test_create_repository_repository_name_exists(): + client = boto3.client("codecommit", region_name="eu-central-1") + + client.create_repository(repositoryName="repository_two") with assert_raises(ClientError) as e: - client.create_repository(repositoryName="repository_two") + client.create_repository( + repositoryName="repository_two", + repositoryDescription="description repo two", + ) ex = e.exception ex.operation_name.should.equal("CreateRepository") ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) @@ -60,6 +95,25 @@ def test_create_repository(): ) +@mock_codecommit +def test_create_repository_invalid_repository_name(): + client = boto3.client("codecommit", region_name="eu-central-1") + + with assert_raises(ClientError) as e: + client.create_repository(repositoryName="in_123_valid_@#$_characters") + ex = e.exception + ex.operation_name.should.equal("CreateRepository") + ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException") + ex.response["Error"]["Message"].should.equal( + "The repository name is not valid. Repository names can be any valid " + "combination of letters, numbers, " + "periods, underscores, and dashes between 1 and 100 characters in " + "length. Names are case sensitive. " + "For more information, see Limits in the AWS CodeCommit User Guide. " + ) + + @mock_codecommit def test_get_repository(): client = boto3.client("codecommit", region_name="eu-central-1") @@ -114,11 +168,11 @@ def test_get_repository(): @mock_codecommit -def test_invalid_repository_name(): +def test_get_repository_invalid_repository_name(): client = boto3.client("codecommit", region_name="eu-central-1") with assert_raises(ClientError) as e: - client.create_repository(repositoryName="repository_one-@#@") + client.get_repository(repositoryName="repository_one-@#@") ex = e.exception ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException") @@ -129,14 +183,6 @@ def test_invalid_repository_name(): "length. Names are case sensitive. " "For more information, see Limits in the AWS CodeCommit User Guide. " ) - with assert_raises(ClientError) as e: - client.create_repository(repositoryName="!_repository_one") - - with assert_raises(ClientError) as e: - client.create_repository(repositoryName="_rep@ository_one") - - with assert_raises(ClientError) as e: - client.get_repository(repositoryName="_rep@ository_one") @mock_codecommit From cba1b2e18026d1f45ba5e6536d2313c5be5c4201 Mon Sep 17 00:00:00 2001 From: Jovan Zivanov Date: Thu, 26 Dec 2019 16:30:06 +0100 Subject: [PATCH 4/5] [codecommit] fix repository name check --- moto/codecommit/responses.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/moto/codecommit/responses.py b/moto/codecommit/responses.py index 3e2cf2539..3c6fdc5ea 100644 --- a/moto/codecommit/responses.py +++ b/moto/codecommit/responses.py @@ -6,13 +6,23 @@ from .models import codecommit_backends from .exceptions import InvalidRepositoryNameException +def _is_repository_name_valid(repository_name): + name_regex = re.compile(r"[\w\.-]+") + result = name_regex.split(repository_name) + if len(result) > 0: + for match in result: + if len(match) > 0: + return False + return True + + class CodeCommitResponse(BaseResponse): @property def codecommit_backend(self): return codecommit_backends[self.region] def create_repository(self): - if not self._is_repository_name_valid(self._get_param("repositoryName")): + if not _is_repository_name_valid(self._get_param("repositoryName")): raise InvalidRepositoryNameException() repository_metadata = self.codecommit_backend.create_repository( @@ -24,7 +34,7 @@ class CodeCommitResponse(BaseResponse): return json.dumps({"repositoryMetadata": repository_metadata}) def get_repository(self): - if not self._is_repository_name_valid(self._get_param("repositoryName")): + if not _is_repository_name_valid(self._get_param("repositoryName")): raise InvalidRepositoryNameException() repository_metadata = self.codecommit_backend.get_repository( @@ -34,7 +44,7 @@ class CodeCommitResponse(BaseResponse): return json.dumps({"repositoryMetadata": repository_metadata}) def delete_repository(self): - if not self._is_repository_name_valid(self._get_param("repositoryName")): + if not _is_repository_name_valid(self._get_param("repositoryName")): raise InvalidRepositoryNameException() repository_id = self.codecommit_backend.delete_repository( @@ -45,8 +55,3 @@ class CodeCommitResponse(BaseResponse): return json.dumps({"repositoryId": repository_id}) return json.dumps({}) - - def _is_repository_name_valid(self, repository_name): - name_regex = re.compile(r"[\w\.-]+") - result = name_regex.fullmatch(repository_name) - return result From 45922fd4efc4497b2fadac85469acd352b7f0096 Mon Sep 17 00:00:00 2001 From: Jovan Zivanov Date: Thu, 26 Dec 2019 17:21:37 +0100 Subject: [PATCH 5/5] [codecommit] add support for server mode --- moto/backends.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/moto/backends.py b/moto/backends.py index 9295bc758..bfc2b398e 100644 --- a/moto/backends.py +++ b/moto/backends.py @@ -8,6 +8,7 @@ from moto.awslambda import lambda_backends from moto.batch import batch_backends from moto.cloudformation import cloudformation_backends from moto.cloudwatch import cloudwatch_backends +from moto.codecommit import codecommit_backends from moto.codepipeline import codepipeline_backends from moto.cognitoidentity import cognitoidentity_backends from moto.cognitoidp import cognitoidp_backends @@ -61,6 +62,7 @@ BACKENDS = { "batch": batch_backends, "cloudformation": cloudformation_backends, "cloudwatch": cloudwatch_backends, + "codecommit": codecommit_backends, "codepipeline": codepipeline_backends, "cognito-identity": cognitoidentity_backends, "cognito-idp": cognitoidp_backends,