Managedblockchain member additions (#2983)
* Added some member and proposal functions * Added additional member and proposal functions * Fixed admin password return and added update_member along with tests * Added network removal and member removal proposal * Fixed failing test * Fixed Python 2.7 test
This commit is contained in:
parent
774a764b69
commit
9bc393801f
@ -16,6 +16,16 @@ class BadRequestException(ManagedBlockchainClientError):
|
||||
)
|
||||
|
||||
|
||||
class InvalidRequestException(ManagedBlockchainClientError):
|
||||
def __init__(self, pretty_called_method, operation_error):
|
||||
super(InvalidRequestException, self).__init__(
|
||||
"InvalidRequestException",
|
||||
"An error occurred (InvalidRequestException) when calling the {0} operation: {1}".format(
|
||||
pretty_called_method, operation_error
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class ResourceNotFoundException(ManagedBlockchainClientError):
|
||||
def __init__(self, pretty_called_method, operation_error):
|
||||
self.code = 404
|
||||
@ -25,3 +35,14 @@ class ResourceNotFoundException(ManagedBlockchainClientError):
|
||||
pretty_called_method, operation_error
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class ResourceLimitExceededException(ManagedBlockchainClientError):
|
||||
def __init__(self, pretty_called_method, operation_error):
|
||||
self.code = 429
|
||||
super(ResourceLimitExceededException, self).__init__(
|
||||
"ResourceLimitExceededException",
|
||||
"An error occurred (ResourceLimitExceededException) when calling the {0} operation: {1}".format(
|
||||
pretty_called_method, operation_error
|
||||
),
|
||||
)
|
||||
|
@ -1,14 +1,28 @@
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import unicode_literals, division
|
||||
|
||||
import datetime
|
||||
import re
|
||||
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
|
||||
from .exceptions import BadRequestException, ResourceNotFoundException
|
||||
from .exceptions import (
|
||||
BadRequestException,
|
||||
ResourceNotFoundException,
|
||||
InvalidRequestException,
|
||||
ResourceLimitExceededException,
|
||||
)
|
||||
|
||||
from .utils import get_network_id, get_member_id
|
||||
from .utils import (
|
||||
get_network_id,
|
||||
get_member_id,
|
||||
get_proposal_id,
|
||||
get_invitation_id,
|
||||
member_name_exist_in_network,
|
||||
number_of_members_in_network,
|
||||
admin_password_ok,
|
||||
)
|
||||
|
||||
FRAMEWORKS = [
|
||||
"HYPERLEDGER_FABRIC",
|
||||
@ -18,10 +32,20 @@ FRAMEWORKVERSIONS = [
|
||||
"1.2",
|
||||
]
|
||||
|
||||
EDITIONS = [
|
||||
"STARTER",
|
||||
"STANDARD",
|
||||
]
|
||||
EDITIONS = {
|
||||
"STARTER": {
|
||||
"MaxMembers": 5,
|
||||
"MaxNodesPerMember": 2,
|
||||
"AllowedNodeInstanceTypes": ["bc.t3.small", "bc.t3.medium"],
|
||||
},
|
||||
"STANDARD": {
|
||||
"MaxMembers": 14,
|
||||
"MaxNodesPerMember": 3,
|
||||
"AllowedNodeInstanceTypes": ["bc.t3", "bc.m5", "bc.c5"],
|
||||
},
|
||||
}
|
||||
|
||||
VOTEVALUES = ["YES", "NO"]
|
||||
|
||||
|
||||
class ManagedBlockchainNetwork(BaseModel):
|
||||
@ -48,6 +72,42 @@ class ManagedBlockchainNetwork(BaseModel):
|
||||
self.member_configuration = member_configuration
|
||||
self.region = region
|
||||
|
||||
@property
|
||||
def network_name(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def network_framework(self):
|
||||
return self.framework
|
||||
|
||||
@property
|
||||
def network_framework_version(self):
|
||||
return self.frameworkversion
|
||||
|
||||
@property
|
||||
def network_creationdate(self):
|
||||
return self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z")
|
||||
|
||||
@property
|
||||
def network_description(self):
|
||||
return self.description
|
||||
|
||||
@property
|
||||
def network_edition(self):
|
||||
return self.frameworkconfiguration["Fabric"]["Edition"]
|
||||
|
||||
@property
|
||||
def vote_pol_proposal_duration(self):
|
||||
return self.voting_policy["ApprovalThresholdPolicy"]["ProposalDurationInHours"]
|
||||
|
||||
@property
|
||||
def vote_pol_threshold_percentage(self):
|
||||
return self.voting_policy["ApprovalThresholdPolicy"]["ThresholdPercentage"]
|
||||
|
||||
@property
|
||||
def vote_pol_threshold_comparator(self):
|
||||
return self.voting_policy["ApprovalThresholdPolicy"]["ThresholdComparator"]
|
||||
|
||||
def to_dict(self):
|
||||
# Format for list_networks
|
||||
d = {
|
||||
@ -63,7 +123,7 @@ class ManagedBlockchainNetwork(BaseModel):
|
||||
return d
|
||||
|
||||
def get_format(self):
|
||||
# Format for get_networks
|
||||
# Format for get_network
|
||||
frameworkattributes = {
|
||||
"Fabric": {
|
||||
"OrderingServiceEndpoint": "orderer.{0}.managedblockchain.{1}.amazonaws.com:30001".format(
|
||||
@ -93,9 +153,272 @@ class ManagedBlockchainNetwork(BaseModel):
|
||||
return d
|
||||
|
||||
|
||||
class ManagedBlockchainProposal(BaseModel):
|
||||
def __init__(
|
||||
self,
|
||||
id,
|
||||
networkid,
|
||||
memberid,
|
||||
membername,
|
||||
numofmembers,
|
||||
actions,
|
||||
network_expirtation,
|
||||
network_threshold,
|
||||
network_threshold_comp,
|
||||
description=None,
|
||||
):
|
||||
# In general, passing all values instead of creating
|
||||
# an apparatus to look them up
|
||||
self.id = id
|
||||
self.networkid = networkid
|
||||
self.memberid = memberid
|
||||
self.membername = membername
|
||||
self.numofmembers = numofmembers
|
||||
self.actions = actions
|
||||
self.network_expirtation = network_expirtation
|
||||
self.network_threshold = network_threshold
|
||||
self.network_threshold_comp = network_threshold_comp
|
||||
self.description = description
|
||||
|
||||
self.creationdate = datetime.datetime.utcnow()
|
||||
self.expirtationdate = self.creationdate + datetime.timedelta(
|
||||
hours=network_expirtation
|
||||
)
|
||||
self.yes_vote_count = 0
|
||||
self.no_vote_count = 0
|
||||
self.outstanding_vote_count = self.numofmembers
|
||||
self.status = "IN_PROGRESS"
|
||||
self.votes = {}
|
||||
|
||||
@property
|
||||
def network_id(self):
|
||||
return self.networkid
|
||||
|
||||
@property
|
||||
def proposal_status(self):
|
||||
return self.status
|
||||
|
||||
@property
|
||||
def proposal_votes(self):
|
||||
return self.votes
|
||||
|
||||
def proposal_actions(self, action_type):
|
||||
default_return = []
|
||||
if action_type.lower() == "invitations":
|
||||
if "Invitations" in self.actions:
|
||||
return self.actions["Invitations"]
|
||||
elif action_type.lower() == "removals":
|
||||
if "Removals" in self.actions:
|
||||
return self.actions["Removals"]
|
||||
return default_return
|
||||
|
||||
def to_dict(self):
|
||||
# Format for list_proposals
|
||||
d = {
|
||||
"ProposalId": self.id,
|
||||
"ProposedByMemberId": self.memberid,
|
||||
"ProposedByMemberName": self.membername,
|
||||
"Status": self.status,
|
||||
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
|
||||
"ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
|
||||
}
|
||||
return d
|
||||
|
||||
def get_format(self):
|
||||
# Format for get_proposal
|
||||
d = {
|
||||
"ProposalId": self.id,
|
||||
"NetworkId": self.networkid,
|
||||
"Actions": self.actions,
|
||||
"ProposedByMemberId": self.memberid,
|
||||
"ProposedByMemberName": self.membername,
|
||||
"Status": self.status,
|
||||
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
|
||||
"ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
|
||||
"YesVoteCount": self.yes_vote_count,
|
||||
"NoVoteCount": self.no_vote_count,
|
||||
"OutstandingVoteCount": self.outstanding_vote_count,
|
||||
}
|
||||
if self.description is not None:
|
||||
d["Description"] = self.description
|
||||
return d
|
||||
|
||||
def set_vote(self, votermemberid, votermembername, vote):
|
||||
if datetime.datetime.utcnow() > self.expirtationdate:
|
||||
self.status = "EXPIRED"
|
||||
return False
|
||||
|
||||
if vote.upper() == "YES":
|
||||
self.yes_vote_count += 1
|
||||
else:
|
||||
self.no_vote_count += 1
|
||||
self.outstanding_vote_count -= 1
|
||||
|
||||
perct_yes = (self.yes_vote_count / self.numofmembers) * 100
|
||||
perct_no = (self.no_vote_count / self.numofmembers) * 100
|
||||
self.votes[votermemberid] = {
|
||||
"MemberId": votermemberid,
|
||||
"MemberName": votermembername,
|
||||
"Vote": vote.upper(),
|
||||
}
|
||||
|
||||
if self.network_threshold_comp == "GREATER_THAN_OR_EQUAL_TO":
|
||||
if perct_yes >= self.network_threshold:
|
||||
self.status = "APPROVED"
|
||||
elif perct_no >= self.network_threshold:
|
||||
self.status = "REJECTED"
|
||||
else:
|
||||
if perct_yes > self.network_threshold:
|
||||
self.status = "APPROVED"
|
||||
elif perct_no > self.network_threshold:
|
||||
self.status = "REJECTED"
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class ManagedBlockchainInvitation(BaseModel):
|
||||
def __init__(
|
||||
self,
|
||||
id,
|
||||
networkid,
|
||||
networkname,
|
||||
networkframework,
|
||||
networkframeworkversion,
|
||||
networkcreationdate,
|
||||
region,
|
||||
networkdescription=None,
|
||||
):
|
||||
self.id = id
|
||||
self.networkid = networkid
|
||||
self.networkname = networkname
|
||||
self.networkdescription = networkdescription
|
||||
self.networkframework = networkframework
|
||||
self.networkframeworkversion = networkframeworkversion
|
||||
self.networkstatus = "AVAILABLE"
|
||||
self.networkcreationdate = networkcreationdate
|
||||
self.status = "PENDING"
|
||||
self.region = region
|
||||
|
||||
self.creationdate = datetime.datetime.utcnow()
|
||||
self.expirtationdate = self.creationdate + datetime.timedelta(days=7)
|
||||
|
||||
@property
|
||||
def invitation_status(self):
|
||||
return self.status
|
||||
|
||||
@property
|
||||
def invitation_networkid(self):
|
||||
return self.networkid
|
||||
|
||||
def to_dict(self):
|
||||
d = {
|
||||
"InvitationId": self.id,
|
||||
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
|
||||
"ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
|
||||
"Status": self.status,
|
||||
"NetworkSummary": {
|
||||
"Id": self.networkid,
|
||||
"Name": self.networkname,
|
||||
"Framework": self.networkframework,
|
||||
"FrameworkVersion": self.networkframeworkversion,
|
||||
"Status": self.networkstatus,
|
||||
"CreationDate": self.networkcreationdate,
|
||||
},
|
||||
}
|
||||
if self.networkdescription is not None:
|
||||
d["NetworkSummary"]["Description"] = self.networkdescription
|
||||
return d
|
||||
|
||||
def accept_invitation(self):
|
||||
self.status = "ACCEPTED"
|
||||
|
||||
def reject_invitation(self):
|
||||
self.status = "REJECTED"
|
||||
|
||||
def set_network_status(self, network_status):
|
||||
self.networkstatus = network_status
|
||||
|
||||
|
||||
class ManagedBlockchainMember(BaseModel):
|
||||
def __init__(
|
||||
self, id, networkid, member_configuration, region,
|
||||
):
|
||||
self.creationdate = datetime.datetime.utcnow()
|
||||
self.id = id
|
||||
self.networkid = networkid
|
||||
self.member_configuration = member_configuration
|
||||
self.status = "AVAILABLE"
|
||||
self.region = region
|
||||
self.description = None
|
||||
|
||||
@property
|
||||
def network_id(self):
|
||||
return self.networkid
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.member_configuration["Name"]
|
||||
|
||||
@property
|
||||
def member_status(self):
|
||||
return self.status
|
||||
|
||||
def to_dict(self):
|
||||
# Format for list_members
|
||||
d = {
|
||||
"Id": self.id,
|
||||
"Name": self.member_configuration["Name"],
|
||||
"Status": self.status,
|
||||
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
|
||||
"IsOwned": True,
|
||||
}
|
||||
if "Description" in self.member_configuration:
|
||||
self.description = self.member_configuration["Description"]
|
||||
return d
|
||||
|
||||
def get_format(self):
|
||||
# Format for get_member
|
||||
frameworkattributes = {
|
||||
"Fabric": {
|
||||
"AdminUsername": self.member_configuration["FrameworkConfiguration"][
|
||||
"Fabric"
|
||||
]["AdminUsername"],
|
||||
"CaEndpoint": "ca.{0}.{1}.managedblockchain.{2}.amazonaws.com:30002".format(
|
||||
self.id.lower(), self.networkid.lower(), self.region
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
d = {
|
||||
"NetworkId": self.networkid,
|
||||
"Id": self.id,
|
||||
"Name": self.name,
|
||||
"FrameworkAttributes": frameworkattributes,
|
||||
"LogPublishingConfiguration": self.member_configuration[
|
||||
"LogPublishingConfiguration"
|
||||
],
|
||||
"Status": self.status,
|
||||
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
|
||||
}
|
||||
if "Description" in self.member_configuration:
|
||||
d["Description"] = self.description
|
||||
return d
|
||||
|
||||
def delete(self):
|
||||
self.status = "DELETED"
|
||||
|
||||
def update(self, logpublishingconfiguration):
|
||||
self.member_configuration[
|
||||
"LogPublishingConfiguration"
|
||||
] = logpublishingconfiguration
|
||||
|
||||
|
||||
class ManagedBlockchainBackend(BaseBackend):
|
||||
def __init__(self, region_name):
|
||||
self.networks = {}
|
||||
self.members = {}
|
||||
self.proposals = {}
|
||||
self.invitations = {}
|
||||
self.region_name = region_name
|
||||
|
||||
def reset(self):
|
||||
@ -113,14 +436,6 @@ class ManagedBlockchainBackend(BaseBackend):
|
||||
member_configuration,
|
||||
description=None,
|
||||
):
|
||||
self.name = name
|
||||
self.framework = framework
|
||||
self.frameworkversion = frameworkversion
|
||||
self.frameworkconfiguration = frameworkconfiguration
|
||||
self.voting_policy = voting_policy
|
||||
self.member_configuration = member_configuration
|
||||
self.description = description
|
||||
|
||||
# Check framework
|
||||
if framework not in FRAMEWORKS:
|
||||
raise BadRequestException("CreateNetwork", "Invalid request body")
|
||||
@ -141,19 +456,25 @@ class ManagedBlockchainBackend(BaseBackend):
|
||||
## Generate network ID
|
||||
network_id = get_network_id()
|
||||
|
||||
## Generate memberid ID - will need to actually create member
|
||||
## Generate memberid ID and initial member
|
||||
member_id = get_member_id()
|
||||
self.members[member_id] = ManagedBlockchainMember(
|
||||
id=member_id,
|
||||
networkid=network_id,
|
||||
member_configuration=member_configuration,
|
||||
region=self.region_name,
|
||||
)
|
||||
|
||||
self.networks[network_id] = ManagedBlockchainNetwork(
|
||||
id=network_id,
|
||||
name=name,
|
||||
framework=self.framework,
|
||||
frameworkversion=self.frameworkversion,
|
||||
frameworkconfiguration=self.frameworkconfiguration,
|
||||
voting_policy=self.voting_policy,
|
||||
member_configuration=self.member_configuration,
|
||||
framework=framework,
|
||||
frameworkversion=frameworkversion,
|
||||
frameworkconfiguration=frameworkconfiguration,
|
||||
voting_policy=voting_policy,
|
||||
member_configuration=member_configuration,
|
||||
region=self.region_name,
|
||||
description=self.description,
|
||||
description=description,
|
||||
)
|
||||
|
||||
# Return the network and member ID
|
||||
@ -166,10 +487,324 @@ class ManagedBlockchainBackend(BaseBackend):
|
||||
def get_network(self, network_id):
|
||||
if network_id not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"CreateNetwork", "Network {0} not found".format(network_id)
|
||||
"GetNetwork", "Network {0} not found.".format(network_id)
|
||||
)
|
||||
return self.networks.get(network_id)
|
||||
|
||||
def create_proposal(
|
||||
self, networkid, memberid, actions, description=None,
|
||||
):
|
||||
# Check if network exists
|
||||
if networkid not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"CreateProposal", "Network {0} not found.".format(networkid)
|
||||
)
|
||||
|
||||
# Check if member exists
|
||||
if memberid not in self.members:
|
||||
raise ResourceNotFoundException(
|
||||
"CreateProposal", "Member {0} not found.".format(memberid)
|
||||
)
|
||||
|
||||
# CLI docs say that Invitations and Removals cannot both be passed - but it does
|
||||
# not throw an error and can be performed
|
||||
if "Invitations" in actions:
|
||||
for propinvitation in actions["Invitations"]:
|
||||
if re.match("[0-9]{12}", propinvitation["Principal"]) is None:
|
||||
raise InvalidRequestException(
|
||||
"CreateProposal",
|
||||
"Account ID format specified in proposal is not valid.",
|
||||
)
|
||||
|
||||
if "Removals" in actions:
|
||||
for propmember in actions["Removals"]:
|
||||
if propmember["MemberId"] not in self.members:
|
||||
raise InvalidRequestException(
|
||||
"CreateProposal",
|
||||
"Member ID format specified in proposal is not valid.",
|
||||
)
|
||||
|
||||
## Generate proposal ID
|
||||
proposal_id = get_proposal_id()
|
||||
|
||||
self.proposals[proposal_id] = ManagedBlockchainProposal(
|
||||
id=proposal_id,
|
||||
networkid=networkid,
|
||||
memberid=memberid,
|
||||
membername=self.members.get(memberid).name,
|
||||
numofmembers=number_of_members_in_network(self.members, networkid),
|
||||
actions=actions,
|
||||
network_expirtation=self.networks.get(networkid).vote_pol_proposal_duration,
|
||||
network_threshold=self.networks.get(
|
||||
networkid
|
||||
).vote_pol_threshold_percentage,
|
||||
network_threshold_comp=self.networks.get(
|
||||
networkid
|
||||
).vote_pol_threshold_comparator,
|
||||
description=description,
|
||||
)
|
||||
|
||||
# Return the proposal ID
|
||||
d = {"ProposalId": proposal_id}
|
||||
return d
|
||||
|
||||
def list_proposals(self, networkid):
|
||||
# Check if network exists
|
||||
if networkid not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"ListProposals", "Network {0} not found.".format(networkid)
|
||||
)
|
||||
|
||||
proposalsfornetwork = []
|
||||
for proposal_id in self.proposals:
|
||||
if self.proposals.get(proposal_id).network_id == networkid:
|
||||
proposalsfornetwork.append(self.proposals[proposal_id])
|
||||
return proposalsfornetwork
|
||||
|
||||
def get_proposal(self, networkid, proposalid):
|
||||
# Check if network exists
|
||||
if networkid not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"GetProposal", "Network {0} not found.".format(networkid)
|
||||
)
|
||||
|
||||
if proposalid not in self.proposals:
|
||||
raise ResourceNotFoundException(
|
||||
"GetProposal", "Proposal {0} not found.".format(proposalid)
|
||||
)
|
||||
return self.proposals.get(proposalid)
|
||||
|
||||
def vote_on_proposal(self, networkid, proposalid, votermemberid, vote):
|
||||
# Check if network exists
|
||||
if networkid not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"VoteOnProposal", "Network {0} not found.".format(networkid)
|
||||
)
|
||||
|
||||
if proposalid not in self.proposals:
|
||||
raise ResourceNotFoundException(
|
||||
"VoteOnProposal", "Proposal {0} not found.".format(proposalid)
|
||||
)
|
||||
|
||||
if votermemberid not in self.members:
|
||||
raise ResourceNotFoundException(
|
||||
"VoteOnProposal", "Member {0} not found.".format(votermemberid)
|
||||
)
|
||||
|
||||
if vote.upper() not in VOTEVALUES:
|
||||
raise BadRequestException("VoteOnProposal", "Invalid request body")
|
||||
|
||||
# Check to see if this member already voted
|
||||
# TODO Verify exception
|
||||
if votermemberid in self.proposals.get(proposalid).proposal_votes:
|
||||
raise BadRequestException("VoteOnProposal", "Invalid request body")
|
||||
|
||||
# Will return false if vote was not cast (e.g., status wrong)
|
||||
if self.proposals.get(proposalid).set_vote(
|
||||
votermemberid, self.members.get(votermemberid).name, vote.upper()
|
||||
):
|
||||
if self.proposals.get(proposalid).proposal_status == "APPROVED":
|
||||
## Generate invitations
|
||||
for propinvitation in self.proposals.get(proposalid).proposal_actions(
|
||||
"Invitations"
|
||||
):
|
||||
invitation_id = get_invitation_id()
|
||||
self.invitations[invitation_id] = ManagedBlockchainInvitation(
|
||||
id=invitation_id,
|
||||
networkid=networkid,
|
||||
networkname=self.networks.get(networkid).network_name,
|
||||
networkframework=self.networks.get(networkid).network_framework,
|
||||
networkframeworkversion=self.networks.get(
|
||||
networkid
|
||||
).network_framework_version,
|
||||
networkcreationdate=self.networks.get(
|
||||
networkid
|
||||
).network_creationdate,
|
||||
region=self.region_name,
|
||||
networkdescription=self.networks.get(
|
||||
networkid
|
||||
).network_description,
|
||||
)
|
||||
|
||||
## Delete members
|
||||
for propmember in self.proposals.get(proposalid).proposal_actions(
|
||||
"Removals"
|
||||
):
|
||||
self.delete_member(networkid, propmember["MemberId"])
|
||||
|
||||
def list_proposal_votes(self, networkid, proposalid):
|
||||
# Check if network exists
|
||||
if networkid not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"ListProposalVotes", "Network {0} not found.".format(networkid)
|
||||
)
|
||||
|
||||
if proposalid not in self.proposals:
|
||||
raise ResourceNotFoundException(
|
||||
"ListProposalVotes", "Proposal {0} not found.".format(proposalid)
|
||||
)
|
||||
|
||||
# Output the vote summaries
|
||||
proposalvotesfornetwork = []
|
||||
for proposal_id in self.proposals:
|
||||
if self.proposals.get(proposal_id).network_id == networkid:
|
||||
for pvmemberid in self.proposals.get(proposal_id).proposal_votes:
|
||||
proposalvotesfornetwork.append(
|
||||
self.proposals.get(proposal_id).proposal_votes[pvmemberid]
|
||||
)
|
||||
return proposalvotesfornetwork
|
||||
|
||||
def list_invitations(self):
|
||||
return self.invitations.values()
|
||||
|
||||
def reject_invitation(self, invitationid):
|
||||
if invitationid not in self.invitations:
|
||||
raise ResourceNotFoundException(
|
||||
"RejectInvitation", "InvitationId {0} not found.".format(invitationid)
|
||||
)
|
||||
self.invitations.get(invitationid).reject_invitation()
|
||||
|
||||
def create_member(
|
||||
self, invitationid, networkid, member_configuration,
|
||||
):
|
||||
# Check if network exists
|
||||
if networkid not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"CreateMember", "Network {0} not found.".format(networkid)
|
||||
)
|
||||
|
||||
if invitationid not in self.invitations:
|
||||
raise InvalidRequestException(
|
||||
"CreateMember", "Invitation {0} not valid".format(invitationid)
|
||||
)
|
||||
|
||||
if self.invitations.get(invitationid).invitation_status != "PENDING":
|
||||
raise InvalidRequestException(
|
||||
"CreateMember", "Invitation {0} not valid".format(invitationid)
|
||||
)
|
||||
|
||||
if (
|
||||
member_name_exist_in_network(
|
||||
self.members, networkid, member_configuration["Name"]
|
||||
)
|
||||
is True
|
||||
):
|
||||
raise InvalidRequestException(
|
||||
"CreateMember",
|
||||
"Member name {0} already exists in network {1}.".format(
|
||||
member_configuration["Name"], networkid
|
||||
),
|
||||
)
|
||||
|
||||
networkedition = self.networks.get(networkid).network_edition
|
||||
if (
|
||||
number_of_members_in_network(self.members, networkid)
|
||||
>= EDITIONS[networkedition]["MaxMembers"]
|
||||
):
|
||||
raise ResourceLimitExceededException(
|
||||
"CreateMember",
|
||||
"You cannot create a member in network {0}.{1} is the maximum number of members allowed in a {2} Edition network.".format(
|
||||
networkid, EDITIONS[networkedition]["MaxMembers"], networkedition
|
||||
),
|
||||
)
|
||||
|
||||
memberadminpassword = member_configuration["FrameworkConfiguration"]["Fabric"][
|
||||
"AdminPassword"
|
||||
]
|
||||
if admin_password_ok(memberadminpassword) is False:
|
||||
raise BadRequestException("CreateMember", "Invalid request body")
|
||||
|
||||
member_id = get_member_id()
|
||||
self.members[member_id] = ManagedBlockchainMember(
|
||||
id=member_id,
|
||||
networkid=networkid,
|
||||
member_configuration=member_configuration,
|
||||
region=self.region_name,
|
||||
)
|
||||
|
||||
# Accept the invitaiton
|
||||
self.invitations.get(invitationid).accept_invitation()
|
||||
|
||||
# Return the member ID
|
||||
d = {"MemberId": member_id}
|
||||
return d
|
||||
|
||||
def list_members(self, networkid):
|
||||
# Check if network exists
|
||||
if networkid not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"ListMembers", "Network {0} not found.".format(networkid)
|
||||
)
|
||||
|
||||
membersfornetwork = []
|
||||
for member_id in self.members:
|
||||
if self.members.get(member_id).network_id == networkid:
|
||||
membersfornetwork.append(self.members[member_id])
|
||||
return membersfornetwork
|
||||
|
||||
def get_member(self, networkid, memberid):
|
||||
# Check if network exists
|
||||
if networkid not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"GetMember", "Network {0} not found.".format(networkid)
|
||||
)
|
||||
|
||||
if memberid not in self.members:
|
||||
raise ResourceNotFoundException(
|
||||
"GetMember", "Member {0} not found.".format(memberid)
|
||||
)
|
||||
|
||||
## Cannot get a member than has been delted (it does show up in the list)
|
||||
if self.members.get(memberid).member_status == "DELETED":
|
||||
raise ResourceNotFoundException(
|
||||
"GetMember", "Member {0} not found.".format(memberid)
|
||||
)
|
||||
|
||||
return self.members.get(memberid)
|
||||
|
||||
def delete_member(self, networkid, memberid):
|
||||
# Check if network exists
|
||||
if networkid not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"DeleteMember", "Network {0} not found.".format(networkid)
|
||||
)
|
||||
|
||||
if memberid not in self.members:
|
||||
raise ResourceNotFoundException(
|
||||
"DeleteMember", "Member {0} not found.".format(memberid)
|
||||
)
|
||||
|
||||
self.members.get(memberid).delete()
|
||||
|
||||
# Is this the last member in the network? (all set to DELETED)
|
||||
if number_of_members_in_network(
|
||||
self.members, networkid, member_status="DELETED"
|
||||
) == len(self.members):
|
||||
# Set network status to DELETED for all invitations
|
||||
for invitation_id in self.invitations:
|
||||
if (
|
||||
self.invitations.get(invitation_id).invitation_networkid
|
||||
== networkid
|
||||
):
|
||||
self.invitations.get(invitation_id).set_network_status("DELETED")
|
||||
|
||||
# Remove network
|
||||
del self.networks[networkid]
|
||||
|
||||
def update_member(self, networkid, memberid, logpublishingconfiguration):
|
||||
# Check if network exists
|
||||
if networkid not in self.networks:
|
||||
raise ResourceNotFoundException(
|
||||
"UpdateMember", "Network {0} not found.".format(networkid)
|
||||
)
|
||||
|
||||
if memberid not in self.members:
|
||||
raise ResourceNotFoundException(
|
||||
"UpdateMember", "Member {0} not found.".format(memberid)
|
||||
)
|
||||
|
||||
self.members.get(memberid).update(logpublishingconfiguration)
|
||||
|
||||
|
||||
managedblockchain_backends = {}
|
||||
for region in Session().get_available_regions("managedblockchain"):
|
||||
|
@ -8,6 +8,9 @@ from .models import managedblockchain_backends
|
||||
from .utils import (
|
||||
region_from_managedblckchain_url,
|
||||
networkid_from_managedblockchain_url,
|
||||
proposalid_from_managedblockchain_url,
|
||||
invitationid_from_managedblockchain_url,
|
||||
memberid_from_managedblockchain_url,
|
||||
)
|
||||
|
||||
|
||||
@ -66,7 +69,7 @@ class ManagedBlockchainResponse(BaseResponse):
|
||||
member_configuration,
|
||||
description,
|
||||
)
|
||||
return 201, headers, json.dumps(response)
|
||||
return 200, headers, json.dumps(response)
|
||||
|
||||
@classmethod
|
||||
def networkid_response(clazz, request, full_url, headers):
|
||||
@ -88,3 +91,236 @@ class ManagedBlockchainResponse(BaseResponse):
|
||||
response = json.dumps({"Network": mbcnetwork.get_format()})
|
||||
headers["content-type"] = "application/json"
|
||||
return 200, headers, response
|
||||
|
||||
@classmethod
|
||||
def proposal_response(clazz, request, full_url, headers):
|
||||
region_name = region_from_managedblckchain_url(full_url)
|
||||
response_instance = ManagedBlockchainResponse(
|
||||
managedblockchain_backends[region_name]
|
||||
)
|
||||
return response_instance._proposal_response(request, full_url, headers)
|
||||
|
||||
def _proposal_response(self, request, full_url, headers):
|
||||
method = request.method
|
||||
if hasattr(request, "body"):
|
||||
body = request.body
|
||||
else:
|
||||
body = request.data
|
||||
parsed_url = urlparse(full_url)
|
||||
querystring = parse_qs(parsed_url.query, keep_blank_values=True)
|
||||
network_id = networkid_from_managedblockchain_url(full_url)
|
||||
if method == "GET":
|
||||
return self._all_proposals_response(network_id, headers)
|
||||
elif method == "POST":
|
||||
json_body = json.loads(body.decode("utf-8"))
|
||||
return self._proposal_response_post(
|
||||
network_id, json_body, querystring, headers
|
||||
)
|
||||
|
||||
def _all_proposals_response(self, network_id, headers):
|
||||
proposals = self.backend.list_proposals(network_id)
|
||||
response = json.dumps(
|
||||
{"Proposals": [proposal.to_dict() for proposal in proposals]}
|
||||
)
|
||||
headers["content-type"] = "application/json"
|
||||
return 200, headers, response
|
||||
|
||||
def _proposal_response_post(self, network_id, json_body, querystring, headers):
|
||||
memberid = json_body["MemberId"]
|
||||
actions = json_body["Actions"]
|
||||
|
||||
# Optional
|
||||
description = json_body.get("Description", None)
|
||||
|
||||
response = self.backend.create_proposal(
|
||||
network_id, memberid, actions, description,
|
||||
)
|
||||
return 200, headers, json.dumps(response)
|
||||
|
||||
@classmethod
|
||||
def proposalid_response(clazz, request, full_url, headers):
|
||||
region_name = region_from_managedblckchain_url(full_url)
|
||||
response_instance = ManagedBlockchainResponse(
|
||||
managedblockchain_backends[region_name]
|
||||
)
|
||||
return response_instance._proposalid_response(request, full_url, headers)
|
||||
|
||||
def _proposalid_response(self, request, full_url, headers):
|
||||
method = request.method
|
||||
network_id = networkid_from_managedblockchain_url(full_url)
|
||||
if method == "GET":
|
||||
proposal_id = proposalid_from_managedblockchain_url(full_url)
|
||||
return self._proposalid_response_get(network_id, proposal_id, headers)
|
||||
|
||||
def _proposalid_response_get(self, network_id, proposal_id, headers):
|
||||
proposal = self.backend.get_proposal(network_id, proposal_id)
|
||||
response = json.dumps({"Proposal": proposal.get_format()})
|
||||
headers["content-type"] = "application/json"
|
||||
return 200, headers, response
|
||||
|
||||
@classmethod
|
||||
def proposal_votes_response(clazz, request, full_url, headers):
|
||||
region_name = region_from_managedblckchain_url(full_url)
|
||||
response_instance = ManagedBlockchainResponse(
|
||||
managedblockchain_backends[region_name]
|
||||
)
|
||||
return response_instance._proposal_votes_response(request, full_url, headers)
|
||||
|
||||
def _proposal_votes_response(self, request, full_url, headers):
|
||||
method = request.method
|
||||
if hasattr(request, "body"):
|
||||
body = request.body
|
||||
else:
|
||||
body = request.data
|
||||
parsed_url = urlparse(full_url)
|
||||
querystring = parse_qs(parsed_url.query, keep_blank_values=True)
|
||||
network_id = networkid_from_managedblockchain_url(full_url)
|
||||
proposal_id = proposalid_from_managedblockchain_url(full_url)
|
||||
if method == "GET":
|
||||
return self._all_proposal_votes_response(network_id, proposal_id, headers)
|
||||
elif method == "POST":
|
||||
json_body = json.loads(body.decode("utf-8"))
|
||||
return self._proposal_votes_response_post(
|
||||
network_id, proposal_id, json_body, querystring, headers
|
||||
)
|
||||
|
||||
def _all_proposal_votes_response(self, network_id, proposal_id, headers):
|
||||
proposalvotes = self.backend.list_proposal_votes(network_id, proposal_id)
|
||||
response = json.dumps({"ProposalVotes": proposalvotes})
|
||||
headers["content-type"] = "application/json"
|
||||
return 200, headers, response
|
||||
|
||||
def _proposal_votes_response_post(
|
||||
self, network_id, proposal_id, json_body, querystring, headers
|
||||
):
|
||||
votermemberid = json_body["VoterMemberId"]
|
||||
vote = json_body["Vote"]
|
||||
|
||||
self.backend.vote_on_proposal(
|
||||
network_id, proposal_id, votermemberid, vote,
|
||||
)
|
||||
return 200, headers, ""
|
||||
|
||||
@classmethod
|
||||
def invitation_response(clazz, request, full_url, headers):
|
||||
region_name = region_from_managedblckchain_url(full_url)
|
||||
response_instance = ManagedBlockchainResponse(
|
||||
managedblockchain_backends[region_name]
|
||||
)
|
||||
return response_instance._invitation_response(request, full_url, headers)
|
||||
|
||||
def _invitation_response(self, request, full_url, headers):
|
||||
method = request.method
|
||||
if method == "GET":
|
||||
return self._all_invitation_response(request, full_url, headers)
|
||||
|
||||
def _all_invitation_response(self, request, full_url, headers):
|
||||
invitations = self.backend.list_invitations()
|
||||
response = json.dumps(
|
||||
{"Invitations": [invitation.to_dict() for invitation in invitations]}
|
||||
)
|
||||
headers["content-type"] = "application/json"
|
||||
return 200, headers, response
|
||||
|
||||
@classmethod
|
||||
def invitationid_response(clazz, request, full_url, headers):
|
||||
region_name = region_from_managedblckchain_url(full_url)
|
||||
response_instance = ManagedBlockchainResponse(
|
||||
managedblockchain_backends[region_name]
|
||||
)
|
||||
return response_instance._invitationid_response(request, full_url, headers)
|
||||
|
||||
def _invitationid_response(self, request, full_url, headers):
|
||||
method = request.method
|
||||
if method == "DELETE":
|
||||
invitation_id = invitationid_from_managedblockchain_url(full_url)
|
||||
return self._invitationid_response_delete(invitation_id, headers)
|
||||
|
||||
def _invitationid_response_delete(self, invitation_id, headers):
|
||||
self.backend.reject_invitation(invitation_id)
|
||||
headers["content-type"] = "application/json"
|
||||
return 200, headers, ""
|
||||
|
||||
@classmethod
|
||||
def member_response(clazz, request, full_url, headers):
|
||||
region_name = region_from_managedblckchain_url(full_url)
|
||||
response_instance = ManagedBlockchainResponse(
|
||||
managedblockchain_backends[region_name]
|
||||
)
|
||||
return response_instance._member_response(request, full_url, headers)
|
||||
|
||||
def _member_response(self, request, full_url, headers):
|
||||
method = request.method
|
||||
if hasattr(request, "body"):
|
||||
body = request.body
|
||||
else:
|
||||
body = request.data
|
||||
parsed_url = urlparse(full_url)
|
||||
querystring = parse_qs(parsed_url.query, keep_blank_values=True)
|
||||
network_id = networkid_from_managedblockchain_url(full_url)
|
||||
if method == "GET":
|
||||
return self._all_members_response(network_id, headers)
|
||||
elif method == "POST":
|
||||
json_body = json.loads(body.decode("utf-8"))
|
||||
return self._member_response_post(
|
||||
network_id, json_body, querystring, headers
|
||||
)
|
||||
|
||||
def _all_members_response(self, network_id, headers):
|
||||
members = self.backend.list_members(network_id)
|
||||
response = json.dumps({"Members": [member.to_dict() for member in members]})
|
||||
headers["content-type"] = "application/json"
|
||||
return 200, headers, response
|
||||
|
||||
def _member_response_post(self, network_id, json_body, querystring, headers):
|
||||
invitationid = json_body["InvitationId"]
|
||||
member_configuration = json_body["MemberConfiguration"]
|
||||
|
||||
response = self.backend.create_member(
|
||||
invitationid, network_id, member_configuration,
|
||||
)
|
||||
return 200, headers, json.dumps(response)
|
||||
|
||||
@classmethod
|
||||
def memberid_response(clazz, request, full_url, headers):
|
||||
region_name = region_from_managedblckchain_url(full_url)
|
||||
response_instance = ManagedBlockchainResponse(
|
||||
managedblockchain_backends[region_name]
|
||||
)
|
||||
return response_instance._memberid_response(request, full_url, headers)
|
||||
|
||||
def _memberid_response(self, request, full_url, headers):
|
||||
method = request.method
|
||||
if hasattr(request, "body"):
|
||||
body = request.body
|
||||
else:
|
||||
body = request.data
|
||||
network_id = networkid_from_managedblockchain_url(full_url)
|
||||
member_id = memberid_from_managedblockchain_url(full_url)
|
||||
if method == "GET":
|
||||
return self._memberid_response_get(network_id, member_id, headers)
|
||||
elif method == "PATCH":
|
||||
json_body = json.loads(body.decode("utf-8"))
|
||||
return self._memberid_response_patch(
|
||||
network_id, member_id, json_body, headers
|
||||
)
|
||||
elif method == "DELETE":
|
||||
return self._memberid_response_delete(network_id, member_id, headers)
|
||||
|
||||
def _memberid_response_get(self, network_id, member_id, headers):
|
||||
member = self.backend.get_member(network_id, member_id)
|
||||
response = json.dumps({"Member": member.get_format()})
|
||||
headers["content-type"] = "application/json"
|
||||
return 200, headers, response
|
||||
|
||||
def _memberid_response_patch(self, network_id, member_id, json_body, headers):
|
||||
logpublishingconfiguration = json_body["LogPublishingConfiguration"]
|
||||
self.backend.update_member(
|
||||
network_id, member_id, logpublishingconfiguration,
|
||||
)
|
||||
return 200, headers, ""
|
||||
|
||||
def _memberid_response_delete(self, network_id, member_id, headers):
|
||||
self.backend.delete_member(network_id, member_id)
|
||||
headers["content-type"] = "application/json"
|
||||
return 200, headers, ""
|
||||
|
@ -6,4 +6,11 @@ url_bases = ["https?://managedblockchain.(.+).amazonaws.com"]
|
||||
url_paths = {
|
||||
"{0}/networks$": ManagedBlockchainResponse.network_response,
|
||||
"{0}/networks/(?P<networkid>[^/.]+)$": ManagedBlockchainResponse.networkid_response,
|
||||
"{0}/networks/(?P<networkid>[^/.]+)/proposals$": ManagedBlockchainResponse.proposal_response,
|
||||
"{0}/networks/(?P<networkid>[^/.]+)/proposals/(?P<proposalid>[^/.]+)$": ManagedBlockchainResponse.proposalid_response,
|
||||
"{0}/networks/(?P<networkid>[^/.]+)/proposals/(?P<proposalid>[^/.]+)/votes$": ManagedBlockchainResponse.proposal_votes_response,
|
||||
"{0}/invitations$": ManagedBlockchainResponse.invitation_response,
|
||||
"{0}/invitations/(?P<invitationid>[^/.]+)$": ManagedBlockchainResponse.invitationid_response,
|
||||
"{0}/networks/(?P<networkid>[^/.]+)/members$": ManagedBlockchainResponse.member_response,
|
||||
"{0}/networks/(?P<networkid>[^/.]+)/members/(?P<memberid>[^/.]+)$": ManagedBlockchainResponse.memberid_response,
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import random
|
||||
import re
|
||||
import string
|
||||
|
||||
from six.moves.urllib.parse import urlparse
|
||||
@ -6,15 +7,18 @@ from six.moves.urllib.parse import urlparse
|
||||
|
||||
def region_from_managedblckchain_url(url):
|
||||
domain = urlparse(url).netloc
|
||||
|
||||
region = "us-east-1"
|
||||
if "." in domain:
|
||||
return domain.split(".")[1]
|
||||
else:
|
||||
return "us-east-1"
|
||||
region = domain.split(".")[1]
|
||||
return region
|
||||
|
||||
|
||||
def networkid_from_managedblockchain_url(full_url):
|
||||
return full_url.split("/")[-1]
|
||||
id_search = re.search("\/n-[A-Z0-9]{26}", full_url, re.IGNORECASE)
|
||||
return_id = None
|
||||
if id_search:
|
||||
return_id = id_search.group(0).replace("/", "")
|
||||
return return_id
|
||||
|
||||
|
||||
def get_network_id():
|
||||
@ -23,7 +27,80 @@ def get_network_id():
|
||||
)
|
||||
|
||||
|
||||
def memberid_from_managedblockchain_url(full_url):
|
||||
id_search = re.search("\/m-[A-Z0-9]{26}", full_url, re.IGNORECASE)
|
||||
return_id = None
|
||||
if id_search:
|
||||
return_id = id_search.group(0).replace("/", "")
|
||||
return return_id
|
||||
|
||||
|
||||
def get_member_id():
|
||||
return "m-" + "".join(
|
||||
random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
|
||||
)
|
||||
|
||||
|
||||
def proposalid_from_managedblockchain_url(full_url):
|
||||
id_search = re.search("\/p-[A-Z0-9]{26}", full_url, re.IGNORECASE)
|
||||
return_id = None
|
||||
if id_search:
|
||||
return_id = id_search.group(0).replace("/", "")
|
||||
return return_id
|
||||
|
||||
|
||||
def get_proposal_id():
|
||||
return "p-" + "".join(
|
||||
random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
|
||||
)
|
||||
|
||||
|
||||
def invitationid_from_managedblockchain_url(full_url):
|
||||
id_search = re.search("\/in-[A-Z0-9]{26}", full_url, re.IGNORECASE)
|
||||
return_id = None
|
||||
if id_search:
|
||||
return_id = id_search.group(0).replace("/", "")
|
||||
return return_id
|
||||
|
||||
|
||||
def get_invitation_id():
|
||||
return "in-" + "".join(
|
||||
random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
|
||||
)
|
||||
|
||||
|
||||
def member_name_exist_in_network(members, networkid, membername):
|
||||
membernamexists = False
|
||||
for member_id in members:
|
||||
if members.get(member_id).network_id == networkid:
|
||||
if members.get(member_id).name == membername:
|
||||
membernamexists = True
|
||||
break
|
||||
return membernamexists
|
||||
|
||||
|
||||
def number_of_members_in_network(members, networkid, member_status=None):
|
||||
return len(
|
||||
[
|
||||
membid
|
||||
for membid in members
|
||||
if members.get(membid).network_id == networkid
|
||||
and (
|
||||
member_status is None
|
||||
or members.get(membid).member_status == member_status
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def admin_password_ok(password):
|
||||
if not re.search("[a-z]", password):
|
||||
return False
|
||||
elif not re.search("[A-Z]", password):
|
||||
return False
|
||||
elif not re.search("[0-9]", password):
|
||||
return False
|
||||
elif re.search("['\"@\\/]", password):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
1
tests/test_managedblockchain/__init__.py
Normal file
1
tests/test_managedblockchain/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
67
tests/test_managedblockchain/helpers.py
Normal file
67
tests/test_managedblockchain/helpers.py
Normal file
@ -0,0 +1,67 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
default_frameworkconfiguration = {"Fabric": {"Edition": "STARTER"}}
|
||||
|
||||
default_votingpolicy = {
|
||||
"ApprovalThresholdPolicy": {
|
||||
"ThresholdPercentage": 50,
|
||||
"ProposalDurationInHours": 24,
|
||||
"ThresholdComparator": "GREATER_THAN_OR_EQUAL_TO",
|
||||
}
|
||||
}
|
||||
|
||||
default_memberconfiguration = {
|
||||
"Name": "testmember1",
|
||||
"Description": "Test Member 1",
|
||||
"FrameworkConfiguration": {
|
||||
"Fabric": {"AdminUsername": "admin", "AdminPassword": "Admin12345"}
|
||||
},
|
||||
"LogPublishingConfiguration": {
|
||||
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": False}}}
|
||||
},
|
||||
}
|
||||
|
||||
default_policy_actions = {"Invitations": [{"Principal": "123456789012"}]}
|
||||
|
||||
multiple_policy_actions = {
|
||||
"Invitations": [{"Principal": "123456789012"}, {"Principal": "123456789013"}]
|
||||
}
|
||||
|
||||
|
||||
def member_id_exist_in_list(members, memberid):
|
||||
memberidxists = False
|
||||
for member in members:
|
||||
if member["Id"] == memberid:
|
||||
memberidxists = True
|
||||
break
|
||||
return memberidxists
|
||||
|
||||
|
||||
def create_member_configuration(
|
||||
name, adminuser, adminpass, cloudwatchenabled, description=None
|
||||
):
|
||||
d = {
|
||||
"Name": name,
|
||||
"FrameworkConfiguration": {
|
||||
"Fabric": {"AdminUsername": adminuser, "AdminPassword": adminpass}
|
||||
},
|
||||
"LogPublishingConfiguration": {
|
||||
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": cloudwatchenabled}}}
|
||||
},
|
||||
}
|
||||
|
||||
if description is not None:
|
||||
d["Description"] = description
|
||||
|
||||
return d
|
||||
|
||||
|
||||
def select_invitation_id_for_network(invitations, networkid, status=None):
|
||||
# Get invitations based on network and maybe status
|
||||
invitationsfornetwork = []
|
||||
for invitation in invitations:
|
||||
if invitation["NetworkSummary"]["Id"] == networkid:
|
||||
if status is None or invitation["Status"] == status:
|
||||
invitationsfornetwork.append(invitation["InvitationId"])
|
||||
return invitationsfornetwork
|
@ -0,0 +1,142 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import boto3
|
||||
import sure # noqa
|
||||
|
||||
from moto.managedblockchain.exceptions import BadRequestException
|
||||
from moto import mock_managedblockchain
|
||||
from . import helpers
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_2_invitations():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.multiple_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
response["Invitations"].should.have.length_of(2)
|
||||
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
|
||||
response["Invitations"][0]["Status"].should.equal("PENDING")
|
||||
response["Invitations"][1]["NetworkSummary"]["Id"].should.equal(network_id)
|
||||
response["Invitations"][1]["Status"].should.equal("PENDING")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_reject_invitation():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
|
||||
response["Invitations"][0]["Status"].should.equal("PENDING")
|
||||
invitation_id = response["Invitations"][0]["InvitationId"]
|
||||
|
||||
# Reject - thanks but no thanks
|
||||
response = conn.reject_invitation(InvitationId=invitation_id)
|
||||
|
||||
# Check the invitation status
|
||||
response = conn.list_invitations()
|
||||
response["Invitations"][0]["InvitationId"].should.equal(invitation_id)
|
||||
response["Invitations"][0]["Status"].should.equal("REJECTED")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_reject_invitation_badinvitation():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
response = conn.reject_invitation.when.called_with(
|
||||
InvitationId="in-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "InvitationId in-ABCDEFGHIJKLMNOP0123456789 not found.")
|
669
tests/test_managedblockchain/test_managedblockchain_members.py
Normal file
669
tests/test_managedblockchain/test_managedblockchain_members.py
Normal file
@ -0,0 +1,669 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import boto3
|
||||
import sure # noqa
|
||||
|
||||
from moto.managedblockchain.exceptions import BadRequestException
|
||||
from moto import mock_managedblockchain
|
||||
from . import helpers
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_another_member():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Description="Test Network 1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
|
||||
response["Invitations"][0]["Status"].should.equal("PENDING")
|
||||
invitation_id = response["Invitations"][0]["InvitationId"]
|
||||
|
||||
# Create the member
|
||||
response = conn.create_member(
|
||||
InvitationId=invitation_id,
|
||||
NetworkId=network_id,
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember2", "admin", "Admin12345", False
|
||||
),
|
||||
)
|
||||
member_id2 = response["MemberId"]
|
||||
|
||||
# Check the invitation status
|
||||
response = conn.list_invitations()
|
||||
response["Invitations"][0]["InvitationId"].should.equal(invitation_id)
|
||||
response["Invitations"][0]["Status"].should.equal("ACCEPTED")
|
||||
|
||||
# Find member in full list
|
||||
response = conn.list_members(NetworkId=network_id)
|
||||
members = response["Members"]
|
||||
members.should.have.length_of(2)
|
||||
helpers.member_id_exist_in_list(members, member_id2).should.equal(True)
|
||||
|
||||
# Get member 2 details
|
||||
response = conn.get_member(NetworkId=network_id, MemberId=member_id2)
|
||||
response["Member"]["Name"].should.equal("testmember2")
|
||||
|
||||
# Update member
|
||||
logconfignewenabled = not helpers.default_memberconfiguration[
|
||||
"LogPublishingConfiguration"
|
||||
]["Fabric"]["CaLogs"]["Cloudwatch"]["Enabled"]
|
||||
logconfignew = {
|
||||
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": logconfignewenabled}}}
|
||||
}
|
||||
conn.update_member(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id2,
|
||||
LogPublishingConfiguration=logconfignew,
|
||||
)
|
||||
|
||||
# Get member 2 details
|
||||
response = conn.get_member(NetworkId=network_id, MemberId=member_id2)
|
||||
response["Member"]["LogPublishingConfiguration"]["Fabric"]["CaLogs"]["Cloudwatch"][
|
||||
"Enabled"
|
||||
].should.equal(logconfignewenabled)
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_another_member_withopts():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
|
||||
response["Invitations"][0]["Status"].should.equal("PENDING")
|
||||
invitation_id = response["Invitations"][0]["InvitationId"]
|
||||
|
||||
# Create the member
|
||||
response = conn.create_member(
|
||||
InvitationId=invitation_id,
|
||||
NetworkId=network_id,
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember2", "admin", "Admin12345", False, "Test Member 2"
|
||||
),
|
||||
)
|
||||
member_id2 = response["MemberId"]
|
||||
|
||||
# Check the invitation status
|
||||
response = conn.list_invitations()
|
||||
response["Invitations"][0]["InvitationId"].should.equal(invitation_id)
|
||||
response["Invitations"][0]["Status"].should.equal("ACCEPTED")
|
||||
|
||||
# Find member in full list
|
||||
response = conn.list_members(NetworkId=network_id)
|
||||
members = response["Members"]
|
||||
members.should.have.length_of(2)
|
||||
helpers.member_id_exist_in_list(members, member_id2).should.equal(True)
|
||||
|
||||
# Get member 2 details
|
||||
response = conn.get_member(NetworkId=network_id, MemberId=member_id2)
|
||||
response["Member"]["Description"].should.equal("Test Member 2")
|
||||
|
||||
# Try to create member with already used invitation
|
||||
response = conn.create_member.when.called_with(
|
||||
InvitationId=invitation_id,
|
||||
NetworkId=network_id,
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember2", "admin", "Admin12345", False, "Test Member 2 Duplicate"
|
||||
),
|
||||
).should.throw(Exception, "Invitation {0} not valid".format(invitation_id))
|
||||
|
||||
# Delete member 2
|
||||
conn.delete_member(NetworkId=network_id, MemberId=member_id2)
|
||||
|
||||
# Member is still in the list
|
||||
response = conn.list_members(NetworkId=network_id)
|
||||
members = response["Members"]
|
||||
members.should.have.length_of(2)
|
||||
|
||||
# But cannot get
|
||||
response = conn.get_member.when.called_with(
|
||||
NetworkId=network_id, MemberId=member_id2,
|
||||
).should.throw(Exception, "Member {0} not found".format(member_id2))
|
||||
|
||||
# Delete member 1
|
||||
conn.delete_member(NetworkId=network_id, MemberId=member_id)
|
||||
|
||||
# Network should be gone
|
||||
response = conn.list_networks()
|
||||
mbcnetworks = response["Networks"]
|
||||
mbcnetworks.should.have.length_of(0)
|
||||
|
||||
# Verify the invitation network status is DELETED
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
response["Invitations"].should.have.length_of(1)
|
||||
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
|
||||
response["Invitations"][0]["NetworkSummary"]["Status"].should.equal("DELETED")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_and_delete_member():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Create proposal (create additional member)
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
invitation_id = response["Invitations"][0]["InvitationId"]
|
||||
|
||||
# Create the member
|
||||
response = conn.create_member(
|
||||
InvitationId=invitation_id,
|
||||
NetworkId=network_id,
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember2", "admin", "Admin12345", False, "Test Member 2"
|
||||
),
|
||||
)
|
||||
member_id2 = response["MemberId"]
|
||||
|
||||
both_policy_actions = {
|
||||
"Invitations": [{"Principal": "123456789012"}],
|
||||
"Removals": [{"MemberId": member_id2}],
|
||||
}
|
||||
|
||||
# Create proposal (invite and remove member)
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id, MemberId=member_id, Actions=both_policy_actions,
|
||||
)
|
||||
proposal_id2 = response["ProposalId"]
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id2)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id2,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Check the invitation status
|
||||
response = conn.list_invitations()
|
||||
invitations = helpers.select_invitation_id_for_network(
|
||||
response["Invitations"], network_id, "PENDING"
|
||||
)
|
||||
invitations.should.have.length_of(1)
|
||||
|
||||
# Member is still in the list
|
||||
response = conn.list_members(NetworkId=network_id)
|
||||
members = response["Members"]
|
||||
members.should.have.length_of(2)
|
||||
foundmember2 = False
|
||||
for member in members:
|
||||
if member["Id"] == member_id2 and member["Status"] == "DELETED":
|
||||
foundmember2 = True
|
||||
foundmember2.should.equal(True)
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_too_many_members():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Create 4 more members - create invitations for 5
|
||||
for counter in range(2, 7):
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
for counter in range(2, 6):
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
invitation_id = helpers.select_invitation_id_for_network(
|
||||
response["Invitations"], network_id, "PENDING"
|
||||
)[0]
|
||||
|
||||
# Create the member
|
||||
response = conn.create_member(
|
||||
InvitationId=invitation_id,
|
||||
NetworkId=network_id,
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember" + str(counter),
|
||||
"admin",
|
||||
"Admin12345",
|
||||
False,
|
||||
"Test Member " + str(counter),
|
||||
),
|
||||
)
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Find member in full list
|
||||
response = conn.list_members(NetworkId=network_id)
|
||||
members = response["Members"]
|
||||
members.should.have.length_of(counter)
|
||||
helpers.member_id_exist_in_list(members, member_id).should.equal(True)
|
||||
|
||||
# Get member details
|
||||
response = conn.get_member(NetworkId=network_id, MemberId=member_id)
|
||||
response["Member"]["Description"].should.equal("Test Member " + str(counter))
|
||||
|
||||
# Try to create the sixth
|
||||
response = conn.list_invitations()
|
||||
invitation_id = helpers.select_invitation_id_for_network(
|
||||
response["Invitations"], network_id, "PENDING"
|
||||
)[0]
|
||||
|
||||
# Try to create member with already used invitation
|
||||
response = conn.create_member.when.called_with(
|
||||
InvitationId=invitation_id,
|
||||
NetworkId=network_id,
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember6", "admin", "Admin12345", False, "Test Member 6"
|
||||
),
|
||||
).should.throw(
|
||||
Exception,
|
||||
"5 is the maximum number of members allowed in a STARTER Edition network",
|
||||
)
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_another_member_alreadyhave():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Description="Test Network 1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
invitation_id = response["Invitations"][0]["InvitationId"]
|
||||
|
||||
# Should fail trying to create with same name
|
||||
response = conn.create_member.when.called_with(
|
||||
NetworkId=network_id,
|
||||
InvitationId=invitation_id,
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember1", "admin", "Admin12345", False
|
||||
),
|
||||
).should.throw(
|
||||
Exception,
|
||||
"Member name {0} already exists in network {1}".format(
|
||||
"testmember1", network_id
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_another_member_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.create_member.when.called_with(
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
InvitationId="id-ABCDEFGHIJKLMNOP0123456789",
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember2", "admin", "Admin12345", False
|
||||
),
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_another_member_badinvitation():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
|
||||
response = conn.create_member.when.called_with(
|
||||
NetworkId=network_id,
|
||||
InvitationId="in-ABCDEFGHIJKLMNOP0123456789",
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember2", "admin", "Admin12345", False
|
||||
),
|
||||
).should.throw(Exception, "Invitation in-ABCDEFGHIJKLMNOP0123456789 not valid")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_another_member_adminpassword():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
invitation_id = response["Invitations"][0]["InvitationId"]
|
||||
|
||||
badadminpassmemberconf = helpers.create_member_configuration(
|
||||
"testmember2", "admin", "Admin12345", False
|
||||
)
|
||||
|
||||
# Too short
|
||||
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
|
||||
"AdminPassword"
|
||||
] = "badap"
|
||||
response = conn.create_member.when.called_with(
|
||||
NetworkId=network_id,
|
||||
InvitationId=invitation_id,
|
||||
MemberConfiguration=badadminpassmemberconf,
|
||||
).should.throw(
|
||||
Exception,
|
||||
"Invalid length for parameter MemberConfiguration.FrameworkConfiguration.Fabric.AdminPassword",
|
||||
)
|
||||
|
||||
# No uppercase or numbers
|
||||
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
|
||||
"AdminPassword"
|
||||
] = "badadminpwd"
|
||||
response = conn.create_member.when.called_with(
|
||||
NetworkId=network_id,
|
||||
InvitationId=invitation_id,
|
||||
MemberConfiguration=badadminpassmemberconf,
|
||||
).should.throw(Exception, "Invalid request body")
|
||||
|
||||
# No lowercase or numbers
|
||||
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
|
||||
"AdminPassword"
|
||||
] = "BADADMINPWD"
|
||||
response = conn.create_member.when.called_with(
|
||||
NetworkId=network_id,
|
||||
InvitationId=invitation_id,
|
||||
MemberConfiguration=badadminpassmemberconf,
|
||||
).should.throw(Exception, "Invalid request body")
|
||||
|
||||
# No numbers
|
||||
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
|
||||
"AdminPassword"
|
||||
] = "badAdminpwd"
|
||||
response = conn.create_member.when.called_with(
|
||||
NetworkId=network_id,
|
||||
InvitationId=invitation_id,
|
||||
MemberConfiguration=badadminpassmemberconf,
|
||||
).should.throw(Exception, "Invalid request body")
|
||||
|
||||
# Invalid character
|
||||
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
|
||||
"AdminPassword"
|
||||
] = "badAdmin@pwd1"
|
||||
response = conn.create_member.when.called_with(
|
||||
NetworkId=network_id,
|
||||
InvitationId=invitation_id,
|
||||
MemberConfiguration=badadminpassmemberconf,
|
||||
).should.throw(Exception, "Invalid request body")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_list_members_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.list_members.when.called_with(
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_get_member_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.get_member.when.called_with(
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_get_member_badmember():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
|
||||
response = conn.get_member.when.called_with(
|
||||
NetworkId=network_id, MemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_delete_member_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.delete_member.when.called_with(
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_delete_member_badmember():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
|
||||
response = conn.delete_member.when.called_with(
|
||||
NetworkId=network_id, MemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_update_member_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.update_member.when.called_with(
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
LogPublishingConfiguration=helpers.default_memberconfiguration[
|
||||
"LogPublishingConfiguration"
|
||||
],
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_update_member_badmember():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
|
||||
response = conn.update_member.when.called_with(
|
||||
NetworkId=network_id,
|
||||
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
LogPublishingConfiguration=helpers.default_memberconfiguration[
|
||||
"LogPublishingConfiguration"
|
||||
],
|
||||
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
|
@ -5,28 +5,7 @@ import sure # noqa
|
||||
|
||||
from moto.managedblockchain.exceptions import BadRequestException
|
||||
from moto import mock_managedblockchain
|
||||
|
||||
|
||||
default_frameworkconfiguration = {"Fabric": {"Edition": "STARTER"}}
|
||||
|
||||
default_votingpolicy = {
|
||||
"ApprovalThresholdPolicy": {
|
||||
"ThresholdPercentage": 50,
|
||||
"ProposalDurationInHours": 24,
|
||||
"ThresholdComparator": "GREATER_THAN_OR_EQUAL_TO",
|
||||
}
|
||||
}
|
||||
|
||||
default_memberconfiguration = {
|
||||
"Name": "testmember1",
|
||||
"Description": "Test Member 1",
|
||||
"FrameworkConfiguration": {
|
||||
"Fabric": {"AdminUsername": "admin", "AdminPassword": "Admin12345"}
|
||||
},
|
||||
"LogPublishingConfiguration": {
|
||||
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": False}}}
|
||||
},
|
||||
}
|
||||
from . import helpers
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
@ -37,12 +16,14 @@ def test_create_network():
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=default_frameworkconfiguration,
|
||||
VotingPolicy=default_votingpolicy,
|
||||
MemberConfiguration=default_memberconfiguration,
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
response["NetworkId"].should.match("n-[A-Z0-9]{26}")
|
||||
response["MemberId"].should.match("m-[A-Z0-9]{26}")
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
network_id.should.match("n-[A-Z0-9]{26}")
|
||||
member_id.should.match("m-[A-Z0-9]{26}")
|
||||
|
||||
# Find in full list
|
||||
response = conn.list_networks()
|
||||
@ -51,7 +32,6 @@ def test_create_network():
|
||||
mbcnetworks[0]["Name"].should.equal("testnetwork1")
|
||||
|
||||
# Get network details
|
||||
network_id = mbcnetworks[0]["Id"]
|
||||
response = conn.get_network(NetworkId=network_id)
|
||||
response["Network"]["Name"].should.equal("testnetwork1")
|
||||
|
||||
@ -65,12 +45,14 @@ def test_create_network_withopts():
|
||||
Description="Test Network 1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=default_frameworkconfiguration,
|
||||
VotingPolicy=default_votingpolicy,
|
||||
MemberConfiguration=default_memberconfiguration,
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
response["NetworkId"].should.match("n-[A-Z0-9]{26}")
|
||||
response["MemberId"].should.match("m-[A-Z0-9]{26}")
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
network_id.should.match("n-[A-Z0-9]{26}")
|
||||
member_id.should.match("m-[A-Z0-9]{26}")
|
||||
|
||||
# Find in full list
|
||||
response = conn.list_networks()
|
||||
@ -79,7 +61,6 @@ def test_create_network_withopts():
|
||||
mbcnetworks[0]["Description"].should.equal("Test Network 1")
|
||||
|
||||
# Get network details
|
||||
network_id = mbcnetworks[0]["Id"]
|
||||
response = conn.get_network(NetworkId=network_id)
|
||||
response["Network"]["Description"].should.equal("Test Network 1")
|
||||
|
||||
@ -93,9 +74,9 @@ def test_create_network_noframework():
|
||||
Description="Test Network 1",
|
||||
Framework="HYPERLEDGER_VINYL",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=default_frameworkconfiguration,
|
||||
VotingPolicy=default_votingpolicy,
|
||||
MemberConfiguration=default_memberconfiguration,
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
).should.throw(Exception, "Invalid request body")
|
||||
|
||||
|
||||
@ -108,9 +89,9 @@ def test_create_network_badframeworkver():
|
||||
Description="Test Network 1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.X",
|
||||
FrameworkConfiguration=default_frameworkconfiguration,
|
||||
VotingPolicy=default_votingpolicy,
|
||||
MemberConfiguration=default_memberconfiguration,
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
).should.throw(
|
||||
Exception, "Invalid version 1.X requested for framework HYPERLEDGER_FABRIC"
|
||||
)
|
||||
@ -128,8 +109,8 @@ def test_create_network_badedition():
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=frameworkconfiguration,
|
||||
VotingPolicy=default_votingpolicy,
|
||||
MemberConfiguration=default_memberconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
).should.throw(Exception, "Invalid request body")
|
||||
|
||||
|
||||
@ -138,5 +119,5 @@ def test_get_network_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.get_network.when.called_with(
|
||||
NetworkId="n-BADNETWORK",
|
||||
).should.throw(Exception, "Network n-BADNETWORK not found")
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
199
tests/test_managedblockchain/test_managedblockchain_proposals.py
Normal file
199
tests/test_managedblockchain/test_managedblockchain_proposals.py
Normal file
@ -0,0 +1,199 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import boto3
|
||||
import sure # noqa
|
||||
|
||||
from moto.managedblockchain.exceptions import BadRequestException
|
||||
from moto import mock_managedblockchain
|
||||
from . import helpers
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_proposal():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
network_id.should.match("n-[A-Z0-9]{26}")
|
||||
member_id.should.match("m-[A-Z0-9]{26}")
|
||||
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
proposal_id.should.match("p-[A-Z0-9]{26}")
|
||||
|
||||
# Find in full list
|
||||
response = conn.list_proposals(NetworkId=network_id)
|
||||
proposals = response["Proposals"]
|
||||
proposals.should.have.length_of(1)
|
||||
proposals[0]["ProposalId"].should.equal(proposal_id)
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_proposal_withopts():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
network_id.should.match("n-[A-Z0-9]{26}")
|
||||
member_id.should.match("m-[A-Z0-9]{26}")
|
||||
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
Description="Adding a new member",
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
proposal_id.should.match("p-[A-Z0-9]{26}")
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["Description"].should.equal("Adding a new member")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_proposal_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.create_proposal.when.called_with(
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
Actions=helpers.default_policy_actions,
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_proposal_badmember():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
|
||||
response = conn.create_proposal.when.called_with(
|
||||
NetworkId=network_id,
|
||||
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
Actions=helpers.default_policy_actions,
|
||||
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_proposal_badinvitationacctid():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Must be 12 digits
|
||||
actions = {"Invitations": [{"Principal": "1234567890"}]}
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
response = conn.create_proposal.when.called_with(
|
||||
NetworkId=network_id, MemberId=member_id, Actions=actions,
|
||||
).should.throw(Exception, "Account ID format specified in proposal is not valid")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_proposal_badremovalmemid():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Must be 12 digits
|
||||
actions = {"Removals": [{"MemberId": "m-ABCDEFGHIJKLMNOP0123456789"}]}
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
response = conn.create_proposal.when.called_with(
|
||||
NetworkId=network_id, MemberId=member_id, Actions=actions,
|
||||
).should.throw(Exception, "Member ID format specified in proposal is not valid")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_list_proposal_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.list_proposals.when.called_with(
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_get_proposal_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.get_proposal.when.called_with(
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_get_proposal_badproposal():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
|
||||
response = conn.get_proposal.when.called_with(
|
||||
NetworkId=network_id, ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Proposal p-ABCDEFGHIJKLMNOP0123456789 not found")
|
@ -0,0 +1,529 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
import boto3
|
||||
import sure # noqa
|
||||
from freezegun import freeze_time
|
||||
from nose import SkipTest
|
||||
|
||||
from moto.managedblockchain.exceptions import BadRequestException
|
||||
from moto import mock_managedblockchain, settings
|
||||
from . import helpers
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_vote_on_proposal_one_member_total_yes():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# List proposal votes
|
||||
response = conn.list_proposal_votes(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["ProposalVotes"][0]["MemberId"].should.equal(member_id)
|
||||
|
||||
# Get proposal details - should be APPROVED
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["Status"].should.equal("APPROVED")
|
||||
response["Proposal"]["YesVoteCount"].should.equal(1)
|
||||
response["Proposal"]["NoVoteCount"].should.equal(0)
|
||||
response["Proposal"]["OutstandingVoteCount"].should.equal(0)
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_vote_on_proposal_one_member_total_no():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
# Create proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
|
||||
|
||||
# Vote no
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="NO",
|
||||
)
|
||||
|
||||
# List proposal votes
|
||||
response = conn.list_proposal_votes(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["ProposalVotes"][0]["MemberId"].should.equal(member_id)
|
||||
|
||||
# Get proposal details - should be REJECTED
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["Status"].should.equal("REJECTED")
|
||||
response["Proposal"]["YesVoteCount"].should.equal(0)
|
||||
response["Proposal"]["NoVoteCount"].should.equal(1)
|
||||
response["Proposal"]["OutstandingVoteCount"].should.equal(0)
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_vote_on_proposal_yes_greater_than():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
votingpolicy = {
|
||||
"ApprovalThresholdPolicy": {
|
||||
"ThresholdPercentage": 50,
|
||||
"ProposalDurationInHours": 24,
|
||||
"ThresholdComparator": "GREATER_THAN",
|
||||
}
|
||||
}
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
invitation_id = response["Invitations"][0]["InvitationId"]
|
||||
|
||||
# Create the member
|
||||
response = conn.create_member(
|
||||
InvitationId=invitation_id,
|
||||
NetworkId=network_id,
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember2", "admin", "Admin12345", False, "Test Member 2"
|
||||
),
|
||||
)
|
||||
member_id2 = response["MemberId"]
|
||||
|
||||
# Create another proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Vote yes with member 1
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_vote_on_proposal_no_greater_than():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
votingpolicy = {
|
||||
"ApprovalThresholdPolicy": {
|
||||
"ThresholdPercentage": 50,
|
||||
"ProposalDurationInHours": 24,
|
||||
"ThresholdComparator": "GREATER_THAN",
|
||||
}
|
||||
}
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
invitation_id = response["Invitations"][0]["InvitationId"]
|
||||
|
||||
# Create the member
|
||||
response = conn.create_member(
|
||||
InvitationId=invitation_id,
|
||||
NetworkId=network_id,
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember2", "admin", "Admin12345", False, "Test Member 2"
|
||||
),
|
||||
)
|
||||
member_id2 = response["MemberId"]
|
||||
|
||||
# Create another proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Vote no with member 1
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="NO",
|
||||
)
|
||||
|
||||
# Vote no with member 2
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id2,
|
||||
Vote="NO",
|
||||
)
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("REJECTED")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_vote_on_proposal_expiredproposal():
|
||||
if os.environ.get("TEST_SERVER_MODE", "false").lower() == "true":
|
||||
raise SkipTest("Cant manipulate time in server mode")
|
||||
|
||||
votingpolicy = {
|
||||
"ApprovalThresholdPolicy": {
|
||||
"ThresholdPercentage": 50,
|
||||
"ProposalDurationInHours": 1,
|
||||
"ThresholdComparator": "GREATER_THAN_OR_EQUAL_TO",
|
||||
}
|
||||
}
|
||||
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
with freeze_time("2015-01-01 12:00:00"):
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
with freeze_time("2015-02-01 12:00:00"):
|
||||
# Vote yes - should set status to expired
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get proposal details - should be EXPIRED
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["Status"].should.equal("EXPIRED")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_vote_on_proposal_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.vote_on_proposal.when.called_with(
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
|
||||
VoterMemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
Vote="YES",
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_vote_on_proposal_badproposal():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
|
||||
response = conn.vote_on_proposal.when.called_with(
|
||||
NetworkId=network_id,
|
||||
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
|
||||
VoterMemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
Vote="YES",
|
||||
).should.throw(Exception, "Proposal p-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_vote_on_proposal_badmember():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
response = conn.vote_on_proposal.when.called_with(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId="m-ABCDEFGHIJKLMNOP0123456789",
|
||||
Vote="YES",
|
||||
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_vote_on_proposal_badvote():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
response = conn.vote_on_proposal.when.called_with(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="FOO",
|
||||
).should.throw(Exception, "Invalid request body")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_vote_on_proposal_alreadyvoted():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network - need a good network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Vote yes
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Get the invitation
|
||||
response = conn.list_invitations()
|
||||
invitation_id = response["Invitations"][0]["InvitationId"]
|
||||
|
||||
# Create the member
|
||||
response = conn.create_member(
|
||||
InvitationId=invitation_id,
|
||||
NetworkId=network_id,
|
||||
MemberConfiguration=helpers.create_member_configuration(
|
||||
"testmember2", "admin", "Admin12345", False, "Test Member 2"
|
||||
),
|
||||
)
|
||||
member_id2 = response["MemberId"]
|
||||
|
||||
# Create another proposal
|
||||
response = conn.create_proposal(
|
||||
NetworkId=network_id,
|
||||
MemberId=member_id,
|
||||
Actions=helpers.default_policy_actions,
|
||||
)
|
||||
|
||||
proposal_id = response["ProposalId"]
|
||||
|
||||
# Get proposal details
|
||||
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
|
||||
response["Proposal"]["NetworkId"].should.equal(network_id)
|
||||
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
|
||||
|
||||
# Vote yes with member 1
|
||||
response = conn.vote_on_proposal(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
)
|
||||
|
||||
# Vote yes with member 1 again
|
||||
response = conn.vote_on_proposal.when.called_with(
|
||||
NetworkId=network_id,
|
||||
ProposalId=proposal_id,
|
||||
VoterMemberId=member_id,
|
||||
Vote="YES",
|
||||
).should.throw(Exception, "Invalid request body")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_list_proposal_votes_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.list_proposal_votes.when.called_with(
|
||||
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
|
||||
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_list_proposal_votes_badproposal():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
# Create network
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=helpers.default_frameworkconfiguration,
|
||||
VotingPolicy=helpers.default_votingpolicy,
|
||||
MemberConfiguration=helpers.default_memberconfiguration,
|
||||
)
|
||||
network_id = response["NetworkId"]
|
||||
member_id = response["MemberId"]
|
||||
|
||||
response = conn.list_proposal_votes.when.called_with(
|
||||
NetworkId=network_id, ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
|
||||
).should.throw(Exception, "Proposal p-ABCDEFGHIJKLMNOP0123456789 not found")
|
Loading…
Reference in New Issue
Block a user