From e533b1a3ff7eafd7841aaa430e18d63e70291e9e Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Thu, 31 Mar 2022 12:26:17 +0000 Subject: [PATCH] QuickSight - list_users,list_groups,create_group_membership,create_data_set,create_ingestion (#4989) --- IMPLEMENTATION_COVERAGE.md | 16 +- docs/docs/services/quicksight.rst | 26 ++- moto/quicksight/models.py | 126 ++++++++++- moto/quicksight/responses.py | 80 +++++++ moto/quicksight/urls.py | 4 + .../test_quicksight_datasets.py | 73 ++++++ .../test_quicksight/test_quicksight_groups.py | 39 ++++ .../test_quicksight/test_quicksight_users.py | 213 ++++++++++++++++++ 8 files changed, 556 insertions(+), 21 deletions(-) create mode 100644 tests/test_quicksight/test_quicksight_datasets.py diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 46c68324e..7e805fb97 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -4042,20 +4042,20 @@ ## quicksight
-6% implemented +11% implemented - [ ] cancel_ingestion - [ ] create_account_customization - [ ] create_analysis - [ ] create_dashboard -- [ ] create_data_set +- [X] create_data_set - [ ] create_data_source - [ ] create_folder - [ ] create_folder_membership - [X] create_group -- [ ] create_group_membership +- [X] create_group_membership - [ ] create_iam_policy_assignment -- [ ] create_ingestion +- [X] create_ingestion - [ ] create_namespace - [ ] create_template - [ ] create_template_alias @@ -4092,6 +4092,7 @@ - [ ] describe_folder_permissions - [ ] describe_folder_resolved_permissions - [X] describe_group +- [X] describe_group_membership - [ ] describe_iam_policy_assignment - [ ] describe_ingestion - [ ] describe_ip_restriction @@ -4114,8 +4115,8 @@ - [ ] list_data_sources - [ ] list_folder_members - [ ] list_folders -- [ ] list_group_memberships -- [ ] list_groups +- [X] list_group_memberships +- [X] list_groups - [ ] list_iam_policy_assignments - [ ] list_iam_policy_assignments_for_user - [ ] list_ingestions @@ -4128,12 +4129,13 @@ - [ ] list_theme_versions - [ ] list_themes - [ ] list_user_groups -- [ ] list_users +- [X] list_users - [X] register_user - [ ] restore_analysis - [ ] search_analyses - [ ] search_dashboards - [ ] search_folders +- [ ] search_groups - [ ] tag_resource - [ ] untag_resource - [ ] update_account_customization diff --git a/docs/docs/services/quicksight.rst b/docs/docs/services/quicksight.rst index 0732a9043..9c4885362 100644 --- a/docs/docs/services/quicksight.rst +++ b/docs/docs/services/quicksight.rst @@ -31,14 +31,14 @@ quicksight - [ ] create_account_customization - [ ] create_analysis - [ ] create_dashboard -- [ ] create_data_set +- [X] create_data_set - [ ] create_data_source - [ ] create_folder - [ ] create_folder_membership - [X] create_group -- [ ] create_group_membership +- [X] create_group_membership - [ ] create_iam_policy_assignment -- [ ] create_ingestion +- [X] create_ingestion - [ ] create_namespace - [ ] create_template - [ ] create_template_alias @@ -75,6 +75,7 @@ quicksight - [ ] describe_folder_permissions - [ ] describe_folder_resolved_permissions - [X] describe_group +- [X] describe_group_membership - [ ] describe_iam_policy_assignment - [ ] describe_ingestion - [ ] describe_ip_restriction @@ -97,8 +98,16 @@ quicksight - [ ] list_data_sources - [ ] list_folder_members - [ ] list_folders -- [ ] list_group_memberships -- [ ] list_groups +- [X] list_group_memberships + + The NextToken and MaxResults parameters are not yet implemented + + +- [X] list_groups + + The NextToken and MaxResults parameters are not yet implemented + + - [ ] list_iam_policy_assignments - [ ] list_iam_policy_assignments_for_user - [ ] list_ingestions @@ -111,7 +120,11 @@ quicksight - [ ] list_theme_versions - [ ] list_themes - [ ] list_user_groups -- [ ] list_users +- [X] list_users + + The NextToken and MaxResults parameters are not yet implemented + + - [X] register_user The following parameters are not yet implemented: @@ -122,6 +135,7 @@ quicksight - [ ] search_analyses - [ ] search_dashboards - [ ] search_folders +- [ ] search_groups - [ ] tag_resource - [ ] untag_resource - [ ] update_account_customization diff --git a/moto/quicksight/models.py b/moto/quicksight/models.py index ce75d8a85..d62ad630a 100644 --- a/moto/quicksight/models.py +++ b/moto/quicksight/models.py @@ -5,6 +5,50 @@ from moto.core.utils import BackendDict from .exceptions import ResourceNotFoundException +def _create_id(aws_account_id, namespace, _id): + return f"{aws_account_id}:{namespace}:{_id}" + + +class QuicksightDataSet(BaseModel): + def __init__(self, region, _id, name): + self.arn = f"arn:aws:quicksight:{region}:{ACCOUNT_ID}:data-set/{_id}" + self._id = _id + self.name = name + self.region = region + + def to_json(self): + return { + "Arn": self.arn, + "DataSetId": self._id, + "IngestionArn": f"arn:aws:quicksight:{self.region}:{ACCOUNT_ID}:ingestion/tbd", + } + + +class QuicksightIngestion(BaseModel): + def __init__(self, region, data_set_id, ingestion_id): + self.arn = f"arn:aws:quicksight:{region}:{ACCOUNT_ID}:data-set/{data_set_id}/ingestions/{ingestion_id}" + self.ingestion_id = ingestion_id + + def to_json(self): + return { + "Arn": self.arn, + "IngestionId": self.ingestion_id, + "IngestionStatus": "INITIALIZED", + } + + +class QuicksightMembership(BaseModel): + def __init__(self, region, group, user): + self.group = group + self.user = user + self.arn = ( + f"arn:aws:quicksight:{region}:{ACCOUNT_ID}:group/default/{group}/{user}" + ) + + def to_json(self): + return {"Arn": self.arn, "MemberName": self.user} + + class QuicksightGroup(BaseModel): def __init__(self, region, group_name, description, aws_account_id, namespace): self.arn = ( @@ -14,6 +58,23 @@ class QuicksightGroup(BaseModel): self.description = description self.aws_account_id = aws_account_id self.namespace = namespace + self.region = region + + self.members = dict() + + def add_member(self, user_name): + membership = QuicksightMembership(self.region, self.group_name, user_name) + self.members[user_name] = membership + return membership + + def delete_member(self, user_name): + self.members.pop(user_name, None) + + def get_member(self, user_name): + return self.members[user_name] + + def list_members(self): + return self.members.values() def to_json(self): return { @@ -59,6 +120,9 @@ class QuickSightBackend(BaseBackend): self.__dict__ = {} self.__init__(region_name) + def create_data_set(self, data_set_id, name): + return QuicksightDataSet(self.region_name, data_set_id, name=name) + def create_group(self, group_name, description, aws_account_id, namespace): group = QuicksightGroup( region=self.region_name, @@ -67,24 +131,69 @@ class QuickSightBackend(BaseBackend): aws_account_id=aws_account_id, namespace=namespace, ) - self.groups[f"{aws_account_id}:{namespace}:{group_name}"] = group + _id = _create_id(aws_account_id, namespace, group_name) + self.groups[_id] = group return group + def create_group_membership(self, aws_account_id, namespace, group_name, user_name): + group = self.describe_group(aws_account_id, namespace, group_name) + return group.add_member(user_name) + + def create_ingestion(self, data_set_id, ingestion_id): + return QuicksightIngestion(self.region_name, data_set_id, ingestion_id) + def delete_group(self, aws_account_id, namespace, group_name): - self.groups.pop(f"{aws_account_id}:{namespace}:{group_name}", None) + _id = _create_id(aws_account_id, namespace, group_name) + self.groups.pop(_id, None) def delete_user(self, aws_account_id, namespace, user_name): - self.users.pop(f"{aws_account_id}:{namespace}:{user_name}", None) + # Delete users from all groups + for group in self.groups.values(): + group.delete_member(user_name) + # Delete user itself + _id = _create_id(aws_account_id, namespace, user_name) + self.users.pop(_id, None) def describe_group(self, aws_account_id, namespace, group_name): - if f"{aws_account_id}:{namespace}:{group_name}" not in self.groups: + _id = _create_id(aws_account_id, namespace, group_name) + if _id not in self.groups: raise ResourceNotFoundException(f"Group {group_name} not found") - return self.groups[f"{aws_account_id}:{namespace}:{group_name}"] + return self.groups[_id] + + def describe_group_membership( + self, aws_account_id, namespace, group_name, user_name + ): + group = self.describe_group(aws_account_id, namespace, group_name) + return group.get_member(user_name) def describe_user(self, aws_account_id, namespace, user_name): - if f"{aws_account_id}:{namespace}:{user_name}" not in self.users: + _id = _create_id(aws_account_id, namespace, user_name) + if _id not in self.users: raise ResourceNotFoundException(f"User {user_name} not found") - return self.users[f"{aws_account_id}:{namespace}:{user_name}"] + return self.users[_id] + + def list_groups(self, aws_account_id, namespace): + """ + The NextToken and MaxResults parameters are not yet implemented + """ + id_for_ns = _create_id(aws_account_id, namespace, _id="") + return [ + group for _id, group in self.groups.items() if _id.startswith(id_for_ns) + ] + + def list_group_memberships(self, aws_account_id, namespace, group_name): + """ + The NextToken and MaxResults parameters are not yet implemented + """ + group = self.describe_group(aws_account_id, namespace, group_name) + return group.list_members() + + def list_users(self, aws_account_id, namespace): + """ + The NextToken and MaxResults parameters are not yet implemented + """ + id_for_ns = _create_id(aws_account_id, namespace, _id="") + return [user for _id, user in self.users.items() if _id.startswith(id_for_ns)] def register_user( self, identity_type, email, user_role, aws_account_id, namespace, user_name @@ -100,7 +209,8 @@ class QuickSightBackend(BaseBackend): user_role=user_role, username=user_name, ) - self.users[f"{aws_account_id}:{namespace}:{user_name}"] = user + _id = _create_id(aws_account_id, namespace, user_name) + self.users[_id] = user return user def update_group(self, aws_account_id, namespace, group_name, description): diff --git a/moto/quicksight/responses.py b/moto/quicksight/responses.py index 69a45f446..91cc0bd31 100644 --- a/moto/quicksight/responses.py +++ b/moto/quicksight/responses.py @@ -13,10 +13,17 @@ class QuickSightResponse(BaseResponse): """Return backend instance specific for this region.""" return quicksight_backends[self.region] + def dataset(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + if request.method == "POST": + return self.create_data_set() + def groups(self, request, full_url, headers): self.setup_class(request, full_url, headers) if request.method == "POST": return self.create_group() + if request.method == "GET": + return self.list_groups() def group(self, request, full_url, headers): self.setup_class(request, full_url, headers) @@ -27,10 +34,29 @@ class QuickSightResponse(BaseResponse): if request.method == "PUT": return self.update_group() + def group_member(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + if request.method == "PUT": + return self.create_group_membership() + if request.method == "GET": + return self.describe_group_membership() + + def group_members(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + if request.method == "GET": + return self.list_group_memberships() + + def ingestion(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + if request.method == "PUT": + return self.create_ingestion() + def users(self, request, full_url, headers): self.setup_class(request, full_url, headers) if request.method == "POST": return self.register_user() + if request.method == "GET": + return self.list_users() def user(self, request, full_url, headers): self.setup_class(request, full_url, headers) @@ -39,6 +65,13 @@ class QuickSightResponse(BaseResponse): if request.method == "DELETE": return self.delete_user() + def create_data_set(self): + params = json.loads(self.body) + data_set_id = params.get("DataSetId") + name = params.get("Name") + data_set = self.quicksight_backend.create_data_set(data_set_id, name) + return 200, {}, json.dumps(data_set.to_json()) + def create_group(self): params = json.loads(self.body) group_name = params.get("GroupName") @@ -53,6 +86,53 @@ class QuickSightResponse(BaseResponse): ) return 200, {}, json.dumps(dict(Group=group.to_json())) + def create_group_membership(self): + aws_account_id = self.path.split("/")[-7] + namespace = self.path.split("/")[-5] + group_name = self.path.split("/")[-3] + user_name = self.path.split("/")[-1] + member = self.quicksight_backend.create_group_membership( + aws_account_id, namespace, group_name, user_name + ) + return 200, {}, json.dumps({"GroupMember": member.to_json()}) + + def create_ingestion(self): + data_set_id = self.path.split("/")[-3] + ingestion_id = self.path.split("/")[-1] + ingestion = self.quicksight_backend.create_ingestion(data_set_id, ingestion_id) + return 200, {}, json.dumps(ingestion.to_json()) + + def describe_group_membership(self): + aws_account_id = self.path.split("/")[-7] + namespace = self.path.split("/")[-5] + group_name = self.path.split("/")[-3] + user_name = self.path.split("/")[-1] + member = self.quicksight_backend.describe_group_membership( + aws_account_id, namespace, group_name, user_name + ) + return 200, {}, json.dumps({"GroupMember": member.to_json()}) + + def list_groups(self): + aws_account_id = self.path.split("/")[-4] + namespace = self.path.split("/")[-2] + groups = self.quicksight_backend.list_groups(aws_account_id, namespace) + return 200, {}, json.dumps(dict(GroupList=[g.to_json() for g in groups])) + + def list_group_memberships(self): + aws_account_id = self.path.split("/")[-6] + namespace = self.path.split("/")[-4] + group_name = self.path.split("/")[-2] + members = self.quicksight_backend.list_group_memberships( + aws_account_id, namespace, group_name + ) + return 200, {}, json.dumps({"GroupMemberList": [m.to_json() for m in members]}) + + def list_users(self): + aws_account_id = self.path.split("/")[-4] + namespace = self.path.split("/")[-2] + users = self.quicksight_backend.list_users(aws_account_id, namespace) + return 200, {}, json.dumps(dict(UserList=[u.to_json() for u in users])) + def register_user(self): params = json.loads(self.body) identity_type = params.get("IdentityType") diff --git a/moto/quicksight/urls.py b/moto/quicksight/urls.py index 872ed93cf..5c1e8445b 100644 --- a/moto/quicksight/urls.py +++ b/moto/quicksight/urls.py @@ -10,8 +10,12 @@ response = QuickSightResponse() url_paths = { + r"{0}/accounts/(?P[\d]+)/data-sets$": response.dataset, + r"{0}/accounts/(?P[\d]+)/data-sets/(?P[^/.]+)/ingestions/(?P[^/.]+)$": response.ingestion, r"{0}/accounts/(?P[\d]+)/namespaces/(?P[^/.]+)/groups$": response.groups, r"{0}/accounts/(?P[\d]+)/namespaces/(?P[^/.]+)/groups/(?P[^/.]+)$": response.group, + r"{0}/accounts/(?P[\d]+)/namespaces/(?P[^/.]+)/groups/(?P[^/.]+)/members$": response.group_members, + r"{0}/accounts/(?P[\d]+)/namespaces/(?P[^/.]+)/groups/(?P[^/.]+)/members/(?P[^/.]+)$": response.group_member, r"{0}/accounts/(?P[\d]+)/namespaces/(?P[^/.]+)/users$": response.users, r"{0}/accounts/(?P[\d]+)/namespaces/(?P[^/.]+)/users/(?P[^/.]+)$": response.user, } diff --git a/tests/test_quicksight/test_quicksight_datasets.py b/tests/test_quicksight/test_quicksight_datasets.py new file mode 100644 index 000000000..659f303c8 --- /dev/null +++ b/tests/test_quicksight/test_quicksight_datasets.py @@ -0,0 +1,73 @@ +import boto3 +import sure # noqa # pylint: disable=unused-import + +from moto import mock_quicksight +from moto.core import ACCOUNT_ID + +# See our Development Tips on writing tests for hints on how to write good tests: +# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html + + +@mock_quicksight +def test_create_data_set(): + client = boto3.client("quicksight", region_name="eu-west-1") + + resp = client.create_data_set( + AwsAccountId=ACCOUNT_ID, + DataSetId="myset", + Name="My Data Set", + ImportMode="SPICE", + PhysicalTableMap={ + "table1": { + "RelationalTable": { + "DataSourceArn": "d:s:arn", + "Catalog": "cat", + "Name": "dog", + "InputColumns": [{"Name": "c1", "Type": "string"}], + } + } + }, + ) + + resp.should.have.key("Arn").equals( + f"arn:aws:quicksight:eu-west-1:{ACCOUNT_ID}:data-set/myset" + ) + resp.should.have.key("DataSetId").equals("myset") + resp.should.have.key("IngestionArn").equals( + f"arn:aws:quicksight:eu-west-1:{ACCOUNT_ID}:ingestion/tbd" + ) + + +@mock_quicksight +def test_create_ingestion(): + client = boto3.client("quicksight", region_name="eu-west-1") + + client.create_data_set( + AwsAccountId=ACCOUNT_ID, + DataSetId="myset", + Name="My Data Set", + ImportMode="SPICE", + PhysicalTableMap={ + "table1": { + "RelationalTable": { + "DataSourceArn": "d:s:arn", + "Catalog": "cat", + "Name": "dog", + "InputColumns": [{"Name": "c1", "Type": "string"}], + } + } + }, + ) + + resp = client.create_ingestion( + AwsAccountId=ACCOUNT_ID, + DataSetId="n_a", + IngestionId="n_a2", + IngestionType="FULL_REFRESH", + ) + + resp.should.have.key("Arn").equals( + f"arn:aws:quicksight:eu-west-1:{ACCOUNT_ID}:data-set/n_a/ingestions/n_a2" + ) + resp.should.have.key("IngestionId").equals("n_a2") + resp.should.have.key("IngestionStatus").equals("INITIALIZED") diff --git a/tests/test_quicksight/test_quicksight_groups.py b/tests/test_quicksight/test_quicksight_groups.py index 89bcbeb4c..354d19947 100644 --- a/tests/test_quicksight/test_quicksight_groups.py +++ b/tests/test_quicksight/test_quicksight_groups.py @@ -106,3 +106,42 @@ def test_delete_group(): ) err = exc.value.response["Error"] err["Code"].should.equal("ResourceNotFoundException") + + +@mock_quicksight +def test_list_groups__initial(): + client = boto3.client("quicksight", region_name="us-east-2") + resp = client.list_groups(AwsAccountId=ACCOUNT_ID, Namespace="default") + + resp.should.have.key("GroupList").equals([]) + resp.should.have.key("Status").equals(200) + + +@mock_quicksight +def test_list_groups(): + client = boto3.client("quicksight", region_name="us-east-1") + for i in range(4): + client.create_group( + AwsAccountId=ACCOUNT_ID, Namespace="default", GroupName=f"group{i}" + ) + + resp = client.list_groups(AwsAccountId=ACCOUNT_ID, Namespace="default") + + resp.should.have.key("GroupList").length_of(4) + resp.should.have.key("Status").equals(200) + + resp["GroupList"].should.contain( + { + "Arn": f"arn:aws:quicksight:us-east-1:{ACCOUNT_ID}:group/default/group0", + "GroupName": "group0", + "PrincipalId": ACCOUNT_ID, + } + ) + + resp["GroupList"].should.contain( + { + "Arn": f"arn:aws:quicksight:us-east-1:{ACCOUNT_ID}:group/default/group3", + "GroupName": "group3", + "PrincipalId": ACCOUNT_ID, + } + ) diff --git a/tests/test_quicksight/test_quicksight_users.py b/tests/test_quicksight/test_quicksight_users.py index 238861f53..6e5e7715d 100644 --- a/tests/test_quicksight/test_quicksight_users.py +++ b/tests/test_quicksight/test_quicksight_users.py @@ -86,3 +86,216 @@ def test_delete_user__quicksight(): ) err = exc.value.response["Error"] err["Code"].should.equal("ResourceNotFoundException") + + +@mock_quicksight +def test_list_users__initial(): + client = boto3.client("quicksight", region_name="us-east-2") + resp = client.list_users(AwsAccountId=ACCOUNT_ID, Namespace="default") + + resp.should.have.key("UserList").equals([]) + resp.should.have.key("Status").equals(200) + + +@mock_quicksight +def test_list_users(): + client = boto3.client("quicksight", region_name="us-east-2") + for i in range(4): + client.register_user( + AwsAccountId=ACCOUNT_ID, + Namespace="default", + Email=f"fakeemail{i}@example.com", + IdentityType="QUICKSIGHT", + UserName=f"fake{i}", + UserRole="READER", + ) + + resp = client.list_users(AwsAccountId=ACCOUNT_ID, Namespace="default") + + resp.should.have.key("UserList").length_of(4) + resp.should.have.key("Status").equals(200) + + resp["UserList"].should.contain( + { + "Arn": f"arn:aws:quicksight:us-east-2:{ACCOUNT_ID}:user/default/fake0", + "UserName": "fake0", + "Email": "fakeemail0@example.com", + "Role": "READER", + "IdentityType": "QUICKSIGHT", + "Active": False, + } + ) + + resp["UserList"].should.contain( + { + "Arn": f"arn:aws:quicksight:us-east-2:{ACCOUNT_ID}:user/default/fake3", + "UserName": "fake3", + "Email": "fakeemail3@example.com", + "Role": "READER", + "IdentityType": "QUICKSIGHT", + "Active": False, + } + ) + + +@mock_quicksight +def test_create_group_membership(): + client = boto3.client("quicksight", region_name="us-east-2") + client.register_user( + AwsAccountId=ACCOUNT_ID, + Namespace="default", + Email=f"fakeemail@example.com", + IdentityType="QUICKSIGHT", + UserName="user1", + UserRole="READER", + ) + client.create_group( + AwsAccountId=ACCOUNT_ID, Namespace="default", GroupName="group1" + ) + + resp = client.create_group_membership( + MemberName="user1", + GroupName="group1", + AwsAccountId=ACCOUNT_ID, + Namespace="default", + ) + + resp.should.have.key("GroupMember").equals( + { + "Arn": f"arn:aws:quicksight:us-east-2:{ACCOUNT_ID}:group/default/group1/user1", + "MemberName": "user1", + } + ) + resp.should.have.key("Status").equals(200) + + +@mock_quicksight +def test_describe_group_membership(): + client = boto3.client("quicksight", region_name="us-east-2") + client.register_user( + AwsAccountId=ACCOUNT_ID, + Namespace="default", + Email=f"fakeemail@example.com", + IdentityType="QUICKSIGHT", + UserName="user1", + UserRole="READER", + ) + client.create_group( + AwsAccountId=ACCOUNT_ID, Namespace="default", GroupName="group1" + ) + + client.create_group_membership( + MemberName="user1", + GroupName="group1", + AwsAccountId=ACCOUNT_ID, + Namespace="default", + ) + + resp = client.describe_group_membership( + MemberName="user1", + GroupName="group1", + AwsAccountId=ACCOUNT_ID, + Namespace="default", + ) + + resp.should.have.key("GroupMember").equals( + { + "Arn": f"arn:aws:quicksight:us-east-2:{ACCOUNT_ID}:group/default/group1/user1", + "MemberName": "user1", + } + ) + resp.should.have.key("Status").equals(200) + + +@mock_quicksight +def test_list_group_memberships(): + client = boto3.client("quicksight", region_name="us-east-2") + for i in range(3): + client.register_user( + AwsAccountId=ACCOUNT_ID, + Namespace="default", + Email=f"fakeemail@example.com", + IdentityType="QUICKSIGHT", + UserName=f"user{i}", + UserRole="READER", + ) + client.create_group( + AwsAccountId=ACCOUNT_ID, Namespace="default", GroupName="group1" + ) + client.create_group( + AwsAccountId=ACCOUNT_ID, Namespace="default", GroupName="group2" + ) + + client.create_group_membership( + MemberName="user0", + GroupName="group1", + AwsAccountId=ACCOUNT_ID, + Namespace="default", + ) + client.create_group_membership( + MemberName="user1", + GroupName="group1", + AwsAccountId=ACCOUNT_ID, + Namespace="default", + ) + client.create_group_membership( + MemberName="user2", + GroupName="group2", + AwsAccountId=ACCOUNT_ID, + Namespace="default", + ) + + resp = client.list_group_memberships( + GroupName="group1", AwsAccountId=ACCOUNT_ID, Namespace="default" + ) + + resp.should.have.key("GroupMemberList").length_of(2) + resp.should.have.key("Status").equals(200) + + resp["GroupMemberList"].should.contain( + { + "Arn": f"arn:aws:quicksight:us-east-2:{ACCOUNT_ID}:group/default/group1/user0", + "MemberName": "user0", + } + ) + resp["GroupMemberList"].should.contain( + { + "Arn": f"arn:aws:quicksight:us-east-2:{ACCOUNT_ID}:group/default/group1/user1", + "MemberName": "user1", + } + ) + + +@mock_quicksight +def test_list_group_memberships__after_deleting_user(): + client = boto3.client("quicksight", region_name="us-east-2") + client.create_group( + AwsAccountId=ACCOUNT_ID, Namespace="default", GroupName="group1" + ) + for i in range(3): + client.register_user( + AwsAccountId=ACCOUNT_ID, + Namespace="default", + Email=f"fakeemail@example.com", + IdentityType="QUICKSIGHT", + UserName=f"user{i}", + UserRole="READER", + ) + client.create_group_membership( + MemberName=f"user{i}", + GroupName="group1", + AwsAccountId=ACCOUNT_ID, + Namespace="default", + ) + + resp = client.list_group_memberships( + GroupName="group1", AwsAccountId=ACCOUNT_ID, Namespace="default" + ) + resp.should.have.key("GroupMemberList").length_of(3) + + client.delete_user(UserName="user1", AwsAccountId=ACCOUNT_ID, Namespace="default") + + resp = client.list_group_memberships( + GroupName="group1", AwsAccountId=ACCOUNT_ID, Namespace="default" + ) + resp.should.have.key("GroupMemberList").length_of(2)