NeoFS Yeouido testcases (#16)

* Update Header Verification with new CLI full headers output #14
* Add verification for the Link headers for complex objects #13
* Add Tombstone verification #6
* Support NeoFS Yeouido #7
This commit is contained in:
anatoly-bogatyrev 2020-12-24 01:38:16 +03:00 committed by GitHub
parent bb3c2bd208
commit 708bf2a012
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 342 additions and 183 deletions

View file

@ -12,6 +12,7 @@ import random
import base64 import base64
import base58 import base58
import docker import docker
import json
if os.getenv('ROBOT_PROFILE') == 'selectel_smoke': if os.getenv('ROBOT_PROFILE') == 'selectel_smoke':
from selectelcdn_smoke_vars import (NEOGO_CLI_PREFIX, NEO_MAINNET_ENDPOINT, from selectelcdn_smoke_vars import (NEOGO_CLI_PREFIX, NEO_MAINNET_ENDPOINT,
@ -612,151 +613,252 @@ def search_object(private_key: str, cid: str, keys: str, bearer: str, filters: s
raise Exception("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) raise Exception("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output))
@keyword('Verify Split Chain')
def verify_split_chain(private_key: str, cid: str, oid: str):
header_virtual_parsed = dict()
header_last_parsed = dict()
marker_last_obj = 0
marker_link_obj = 0
final_verif_data = dict()
# Get Latest object
logger.info("Collect Split objects information and verify chain of the objects.")
nodes = _get_storage_nodes(private_key)
for node in nodes:
header_virtual = head_object(private_key, cid, oid, '', '', '--raw --ttl 1', node, True)
parsed_header_virtual = parse_object_virtual_raw_header(header_virtual)
if 'Last object' in parsed_header_virtual.keys():
header_last = head_object(private_key, cid, parsed_header_virtual['Last object'], '', '', '--raw')
header_last_parsed = parse_object_system_header(header_last)
marker_last_obj = 1
# Recursive chain validation up to the first object
final_verif_data = _verify_child_link(private_key, cid, oid, header_last_parsed, final_verif_data)
break
if marker_last_obj == 0:
raise Exception("Latest object has not been found.")
# Get Linking object
logger.info("Compare Split objects result information with Linking object.")
for node in nodes:
header_virtual = head_object(private_key, cid, oid, '', '', '--raw --ttl 1', node, True)
parsed_header_virtual = parse_object_virtual_raw_header(header_virtual)
if 'Linking object' in parsed_header_virtual.keys():
header_link = head_object(private_key, cid, parsed_header_virtual['Linking object'], '', '', '--raw')
header_link_parsed = parse_object_system_header(header_link)
marker_link_obj = 1
reversed_list = final_verif_data['ID List'][::-1]
if header_link_parsed['Split ChildID'] == reversed_list:
logger.info("Split objects list from Linked Object is equal to expected %s" % ', '.join(header_link_parsed['Split ChildID']))
else:
raise Exception("Split objects list from Linking Object (%s) is not equal to expected (%s)" % ', '.join(header_link_parsed['Split ChildID']), ', '.join(reversed_list) )
if int(header_link_parsed['PayloadLength']) == 0:
logger.info("Linking object Payload is equal to expected - zero size.")
else:
raise Exception("Linking object Payload is not equal to expected. Should be zero.")
if header_link_parsed['Type'] == 'regular':
logger.info("Linking Object Type is 'regular' as expected.")
else:
raise Exception("Object Type is not 'regular'.")
if header_link_parsed['Split ID'] == final_verif_data['Split ID']:
logger.info("Linking Object Split ID is equal to expected %s." % final_verif_data['Split ID'] )
else:
raise Exception("Split ID from Linking Object (%s) is not equal to expected (%s)" % header_link_parsed['Split ID'], ffinal_verif_data['Split ID'] )
break
if marker_link_obj == 0:
raise Exception("Linked object has not been found.")
logger.info("Compare Split objects result information with Virtual object.")
header_virtual = head_object(private_key, cid, oid, '', '', '')
header_virtual_parsed = parse_object_system_header(header_virtual)
if int(header_virtual_parsed['PayloadLength']) == int(final_verif_data['PayloadLength']):
logger.info("Split objects PayloadLength are equal to Virtual Object Payload %s" % header_virtual_parsed['PayloadLength'])
else:
raise Exception("Split objects PayloadLength from Virtual Object (%s) is not equal to expected (%s)" % header_virtual_parsed['PayloadLength'], final_verif_data['PayloadLength'] )
if header_link_parsed['Type'] == 'regular':
logger.info("Virtual Object Type is 'regular' as expected.")
else:
raise Exception("Object Type is not 'regular'.")
return 1
def _verify_child_link(private_key: str, cid: str, oid: str, header_last_parsed: dict, final_verif_data: dict):
if 'PayloadLength' in final_verif_data.keys():
final_verif_data['PayloadLength'] = int(final_verif_data['PayloadLength']) + int(header_last_parsed['PayloadLength'])
else:
final_verif_data['PayloadLength'] = int(header_last_parsed['PayloadLength'])
if header_last_parsed['Type'] != 'regular':
raise Exception("Object Type is not 'regular'.")
if 'Split ID' in final_verif_data.keys():
if final_verif_data['Split ID'] != header_last_parsed['Split ID']:
raise Exception("Object Split ID (%s) is not expected (%s)." % header_last_parsed['Split ID'], final_verif_data['Split ID'])
else:
final_verif_data['Split ID'] = header_last_parsed['Split ID']
if 'ID List' in final_verif_data.keys():
final_verif_data['ID List'].append(header_last_parsed['ID'])
else:
final_verif_data['ID List'] = []
final_verif_data['ID List'].append(header_last_parsed['ID'])
if 'Split PreviousID' in header_last_parsed.keys():
header_virtual = head_object(private_key, cid, header_last_parsed['Split PreviousID'], '', '', '--raw')
parsed_header_virtual = parse_object_system_header(header_virtual)
final_verif_data = _verify_child_link(private_key, cid, oid, parsed_header_virtual, final_verif_data)
else:
logger.info("Chain of the objects has been parsed from the last object ot the first.")
return final_verif_data
'''
@keyword('Verify Head Tombstone') @keyword('Verify Head Tombstone')
def verify_head_tombstone(private_key: str, cid: str, oid: str): def verify_head_tombstone(private_key: str, cid: str, oid_ts: str, oid: str, addr: str):
ObjectCmd = f'neofs-cli --rpc-endpoint {NEOFS_ENDPOINT} --key {private_key} object head --cid {cid} --oid {oid} --full-headers' ObjectCmd = f'neofs-cli --rpc-endpoint {NEOFS_ENDPOINT} --key {private_key} object head --cid {cid} --oid {oid_ts} --json'
logger.info("Cmd: %s" % ObjectCmd) logger.info("Cmd: %s" % ObjectCmd)
try: try:
complProc = subprocess.run(ObjectCmd, check=True, universal_newlines=True, complProc = subprocess.run(ObjectCmd, check=True, universal_newlines=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15, shell=True) stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15, shell=True)
logger.info("Output: %s" % complProc.stdout)
if re.search(r'Type=Tombstone\s+Value=MARKED', complProc.stdout): full_headers = json.loads(complProc.stdout)
logger.info("Tombstone header 'Type=Tombstone Value=MARKED' was parsed from command output") logger.info("Output: %s" % full_headers)
# Header verification
header_cid = full_headers["header"]["containerID"]["value"]
if (_json_cli_decode(header_cid) == cid):
logger.info("Header CID is expected: %s (%s in the output)" % (cid, header_cid))
else: else:
raise Exception("Tombstone header 'Type=Tombstone Value=MARKED' was not found in the command output: \t%s" % (complProc.stdout)) raise Exception("Header CID is not expected.")
header_owner = full_headers["header"]["ownerID"]["value"]
if (_json_cli_decode(header_owner) == addr):
logger.info("Header ownerID is expected: %s (%s in the output)" % (addr, header_owner))
else:
raise Exception("Header ownerID is not expected.")
header_type = full_headers["header"]["objectType"]
if (header_type == "TOMBSTONE"):
logger.info("Header Type is expected: %s" % header_type)
else:
raise Exception("Header Type is not expected.")
header_session_type = full_headers["header"]["sessionToken"]["body"]["object"]["verb"]
if (header_session_type == "DELETE"):
logger.info("Header Session Type is expected: %s" % header_session_type)
else:
raise Exception("Header Session Type is not expected.")
header_session_cid = full_headers["header"]["sessionToken"]["body"]["object"]["address"]["containerID"]["value"]
if (_json_cli_decode(header_session_cid) == cid):
logger.info("Header ownerID is expected: %s (%s in the output)" % (addr, header_session_cid))
else:
raise Exception("Header Session CID is not expected.")
header_session_oid = full_headers["header"]["sessionToken"]["body"]["object"]["address"]["objectID"]["value"]
if (_json_cli_decode(header_session_oid) == oid):
logger.info("Header Session OID (deleted object) is expected: %s (%s in the output)" % (oid, header_session_oid))
else:
raise Exception("Header Session OID (deleted object) is not expected.")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise Exception("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) raise Exception("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output))
@keyword('Verify linked objects')
def verify_linked_objects(private_key: bytes, cid: str, oid: str, payload_size: float):
payload_size = int(float(payload_size))
# Get linked objects from first
postfix = f'object head --cid {cid} --oid {oid} --full-headers'
output = _exec_cli_cmd(private_key, postfix)
child_obj_list = []
for m in re.finditer(r'Type=Child ID=([\w-]+)', output):
child_obj_list.append(m.group(1))
if not re.search(r'PayloadLength=0', output):
raise Exception("Payload is not equal to zero in the parent object %s." % obj)
if not child_obj_list:
raise Exception("Child objects was not found.")
else:
logger.info("Child objects: %s" % child_obj_list)
# HEAD and validate each child object:
payload = 0
parent_id = "00000000-0000-0000-0000-000000000000"
first_obj = None
child_obj_list_headers = {}
for obj in child_obj_list:
postfix = f'object head --cid {cid} --oid {obj} --full-headers'
output = _exec_cli_cmd(private_key, postfix)
child_obj_list_headers[obj] = output
if re.search(r'Type=Previous ID=00000000-0000-0000-0000-000000000000', output):
first_obj = obj
logger.info("First child object %s has been found" % first_obj)
if not first_obj:
raise Exception("Can not find first object with zero Parent ID.")
else:
_check_linked_object(first_obj, child_obj_list_headers, payload_size, payload, parent_id)
return child_obj_list_headers.keys()
def _check_linked_object(obj:str, child_obj_list_headers:dict, payload_size:int, payload:int, parent_id:str):
output = child_obj_list_headers[obj]
logger.info("Verify headers of the child object %s" % obj)
if not re.search(r'Type=Previous ID=%s' % parent_id, output):
raise Exception("Incorrect previos ID %s in the child object %s." % parent_id, obj)
else:
logger.info("Previous ID is equal for expected: %s" % parent_id)
m = re.search(r'PayloadLength=(\d+)', output)
if m.start() != m.end():
payload += int(m.group(1))
else:
raise Exception("Can not get payload for the object %s." % obj)
if payload > payload_size:
raise Exception("Payload exceeds expected total payload %s." % payload_size)
elif payload == payload_size:
if not re.search(r'Type=Next ID=00000000-0000-0000-0000-000000000000', output):
raise Exception("Incorrect previos ID in the last child object %s." % obj)
else:
logger.info("Next ID is correct for the final child object: %s" % obj)
else:
m = re.search(r'Type=Next ID=([\w-]+)', output)
if m:
# next object should be in the expected list
logger.info(m.group(1))
if m.group(1) not in child_obj_list_headers.keys():
raise Exception(f'Next object {m.group(1)} is not in the expected list: {child_obj_list_headers.keys()}.')
else:
logger.info(f'Next object {m.group(1)} is in the expected list: {child_obj_list_headers.keys()}.')
_check_linked_object(m.group(1), child_obj_list_headers, payload_size, payload, obj)
else:
raise Exception("Can not get Next object ID for the object %s." % obj)
'''
def _json_cli_decode(data: str):
return base58.b58encode(base64.b64decode(data)).decode("utf-8")
@keyword('Head object') @keyword('Head object')
def head_object(private_key: str, cid: str, oid: str, bearer: str, user_headers:str=""): def head_object(private_key: str, cid: str, oid: str, bearer_token: str="", user_headers:str="", keys:str="", endpoint: str="", ignore_failure: bool = False):
options = "" options = ""
bearer_token = "" if bearer_token:
if bearer: bearer_token = f"--bearer {bearer_token}"
bearer_token = f"--bearer {bearer}"
ObjectCmd = f'neofs-cli --rpc-endpoint {NEOFS_ENDPOINT} --key {private_key} object head --cid {cid} --oid {oid} {bearer_token} {options}' if endpoint == "":
endpoint = NEOFS_ENDPOINT
ObjectCmd = f'neofs-cli --rpc-endpoint {endpoint} --key {private_key} object head --cid {cid} --oid {oid} {bearer_token} {keys}'
logger.info("Cmd: %s" % ObjectCmd) logger.info("Cmd: %s" % ObjectCmd)
try: try:
complProc = subprocess.run(ObjectCmd, check=True, universal_newlines=True, complProc = subprocess.run(ObjectCmd, check=True, universal_newlines=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15, shell=True) stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15, shell=True)
logger.info("Output: %s" % complProc.stdout) logger.info("Output: %s" % complProc.stdout)
for key in user_headers.split(","): if user_headers:
# user_header = f'Key={key} Val={user_headers_dict[key]}' for key in user_headers.split(","):
if re.search(r'(%s)' % key, complProc.stdout): if re.search(r'(%s)' % key, complProc.stdout):
logger.info("User header %s was parsed from command output" % key) logger.info("User header %s was parsed from command output" % key)
else: else:
raise Exception("User header %s was not found in the command output: \t%s" % (key, complProc.stdout)) raise Exception("User header %s was not found in the command output: \t%s" % (key, complProc.stdout))
return complProc.stdout return complProc.stdout
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise Exception("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) if ignore_failure:
logger.info("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output))
return e.output
else:
raise Exception("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output))
@keyword('Parse Object Virtual Raw Header')
def parse_object_virtual_raw_header(header: str):
# Header - Optional attributes
result_header = dict()
m = re.search(r'Split ID:\s+([\w-]+)', header)
if m != None:
if m.start() != m.end(): # e.g., if match found something
result_header['Split ID'] = m.group(1)
m = re.search(r'Linking object:\s+(\w+)', header)
if m != None:
if m.start() != m.end(): # e.g., if match found something
result_header['Linking object'] = m.group(1)
m = re.search(r'Last object:\s+(\w+)', header)
if m != None:
if m.start() != m.end(): # e.g., if match found something
result_header['Last object'] = m.group(1)
logger.info("Result: %s" % result_header)
return result_header
@keyword('Parse Object System Header') @keyword('Parse Object System Header')
def parse_object_system_header(header: str): def parse_object_system_header(header: str):
result_header = dict() result_header = dict()
# Header - Constant attributes
#SystemHeader
logger.info("Input: %s" % header)
# ID # ID
m = re.search(r'ID: (\w+)', header) m = re.search(r'^ID: (\w+)', header)
if m.start() != m.end(): # e.g., if match found something if m.start() != m.end(): # e.g., if match found something
result_header['ID'] = m.group(1) result_header['ID'] = m.group(1)
else: else:
@ -775,20 +877,7 @@ def parse_object_system_header(header: str):
result_header['OwnerID'] = m.group(1) result_header['OwnerID'] = m.group(1)
else: else:
raise Exception("no OwnerID was parsed from object header: \t%s" % output) raise Exception("no OwnerID was parsed from object header: \t%s" % output)
# PayloadLength
m = re.search(r'Size: (\d+)', header)
if m.start() != m.end(): # e.g., if match found something
result_header['PayloadLength'] = m.group(1)
else:
raise Exception("no PayloadLength was parsed from object header: \t%s" % output)
# CreatedAtUnixTime
m = re.search(r'Timestamp=(\d+)', header)
if m.start() != m.end(): # e.g., if match found something
result_header['CreatedAtUnixTime'] = m.group(1)
else:
raise Exception("no CreatedAtUnixTime was parsed from object header: \t%s" % output)
# CreatedAtEpoch # CreatedAtEpoch
m = re.search(r'CreatedAt: (\d+)', header) m = re.search(r'CreatedAt: (\d+)', header)
if m.start() != m.end(): # e.g., if match found something if m.start() != m.end(): # e.g., if match found something
@ -796,9 +885,61 @@ def parse_object_system_header(header: str):
else: else:
raise Exception("no CreatedAtEpoch was parsed from object header: \t%s" % output) raise Exception("no CreatedAtEpoch was parsed from object header: \t%s" % output)
# PayloadLength
m = re.search(r'Size: (\d+)', header)
if m.start() != m.end(): # e.g., if match found something
result_header['PayloadLength'] = m.group(1)
else:
raise Exception("no PayloadLength was parsed from object header: \t%s" % output)
# HomoHash
m = re.search(r'HomoHash:\s+(\w+)', header)
if m.start() != m.end(): # e.g., if match found something
result_header['HomoHash'] = m.group(1)
else:
raise Exception("no HomoHash was parsed from object header: \t%s" % output)
# Checksum
m = re.search(r'Checksum:\s+(\w+)', header)
if m.start() != m.end(): # e.g., if match found something
result_header['Checksum'] = m.group(1)
else:
raise Exception("no Checksum was parsed from object header: \t%s" % output)
# Type
m = re.search(r'Type:\s+(\w+)', header)
if m.start() != m.end(): # e.g., if match found something
result_header['Type'] = m.group(1)
else:
raise Exception("no Type was parsed from object header: \t%s" % output)
# Header - Optional attributes
m = re.search(r'Split ID:\s+([\w-]+)', header)
if m != None:
if m.start() != m.end(): # e.g., if match found something
result_header['Split ID'] = m.group(1)
m = re.search(r'Split PreviousID:\s+(\w+)', header)
if m != None:
if m.start() != m.end(): # e.g., if match found something
result_header['Split PreviousID'] = m.group(1)
m = re.search(r'Split ParentID:\s+(\w+)', header)
if m != None:
if m.start() != m.end(): # e.g., if match found something
result_header['Split ParentID'] = m.group(1)
# Split ChildID list
found_objects = re.findall(r'Split ChildID:\s+(\w+)', header)
if found_objects:
result_header['Split ChildID'] = found_objects
logger.info("Result: %s" % result_header) logger.info("Result: %s" % result_header)
return result_header return result_header
@keyword('Delete object') @keyword('Delete object')
def delete_object(private_key: str, cid: str, oid: str, bearer: str): def delete_object(private_key: str, cid: str, oid: str, bearer: str):
@ -812,6 +953,10 @@ def delete_object(private_key: str, cid: str, oid: str, bearer: str):
complProc = subprocess.run(ObjectCmd, check=True, universal_newlines=True, complProc = subprocess.run(ObjectCmd, check=True, universal_newlines=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30, shell=True) stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30, shell=True)
logger.info("Output: %s" % complProc.stdout) logger.info("Output: %s" % complProc.stdout)
tombstone = _parse_oid(complProc.stdout)
return tombstone
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise Exception("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) raise Exception("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output))
@ -1029,7 +1174,7 @@ def _search_object(node:str, private_key: str, cid:str, oid: str):
if re.search(r'local node is outside of object placement', e.output): if re.search(r'local node is outside of object placement', e.output):
logger.info("Server is not presented in container.") logger.info("Server is not presented in container.")
elif ( re.search(r'timed out after 30 seconds', e.output) or re.search(r'no route to host', e.output) ): elif ( re.search(r'timed out after 30 seconds', e.output) or re.search(r'no route to host', e.output) or re.search(r'i/o timeout', e.output)):
logger.warn("Node is unavailable") logger.warn("Node is unavailable")
else: else:

View file

@ -3,10 +3,11 @@ import os
NEOFS_ENDPOINT = "s01.neofs.devenv:8080" NEOFS_ENDPOINT = "s01.neofs.devenv:8080"
NEOGO_CLI_PREFIX = "docker exec -it main_chain neo-go" NEOGO_CLI_PREFIX = "docker exec -it main_chain neo-go"
NEO_MAINNET_ENDPOINT = "main_chain.neofs.devenv:30333" NEO_MAINNET_ENDPOINT = "http://main_chain.neofs.devenv:30333"
NEOFS_NEO_API_ENDPOINT = 'http://main_chain.neofs.devenv:30333' NEOFS_NEO_API_ENDPOINT = 'http://morph_chain.neofs.devenv:30333'
HTTP_GATE = 'http://http.neofs.devenv' HTTP_GATE = 'http://http.neofs.devenv'
S3_GATE = 'https://s3.neofs.devenv:8080' S3_GATE = 'https://s3.neofs.devenv:8080'
NEOFS_CONTRACT = "5f490fbd8010fd716754073ee960067d28549b7d" NEOFS_NETMAP = ['s01.neofs.devenv:8080', 's02.neofs.devenv:8080','s03.neofs.devenv:8080','s04.neofs.devenv:8080']
NEOFS_NETMAP = ['s01.neofs.devenv:8080', 's02.neofs.devenv:8080','s03.neofs.devenv:8080','s04.neofs.devenv:8080']
GAS_HASH = '0xb5df804bbadefea726afb5d3f4e8a6f6d32d2a20'

View file

@ -15,14 +15,14 @@ import robot.errors
from robot.libraries.BuiltIn import BuiltIn from robot.libraries.BuiltIn import BuiltIn
ROBOT_AUTO_KEYWORDS = False ROBOT_AUTO_KEYWORDS = False
NEOFS_CONTRACT = "5f490fbd8010fd716754073ee960067d28549b7d" NEOFS_CONTRACT = "ce96811ca25577c058484dab10dd8db2defc5eed"
if os.getenv('ROBOT_PROFILE') == 'selectel_smoke': if os.getenv('ROBOT_PROFILE') == 'selectel_smoke':
from selectelcdn_smoke_vars import (NEOGO_CLI_PREFIX, NEO_MAINNET_ENDPOINT, from selectelcdn_smoke_vars import (NEOGO_CLI_PREFIX, NEO_MAINNET_ENDPOINT,
NEOFS_NEO_API_ENDPOINT, NEOFS_ENDPOINT) NEOFS_NEO_API_ENDPOINT, NEOFS_ENDPOINT, GAS_HASH)
else: else:
from neofs_int_vars import (NEOGO_CLI_PREFIX, NEO_MAINNET_ENDPOINT, from neofs_int_vars import (NEOGO_CLI_PREFIX, NEO_MAINNET_ENDPOINT,
NEOFS_NEO_API_ENDPOINT, NEOFS_ENDPOINT) NEOFS_NEO_API_ENDPOINT, NEOFS_ENDPOINT, GAS_HASH)
@keyword('Init wallet') @keyword('Init wallet')
@ -98,8 +98,8 @@ def dump_privkey(wallet: str, address: str):
@keyword('Transfer Mainnet Gas') @keyword('Transfer Mainnet Gas')
def transfer_mainnet_gas(wallet: str, address: str, address_to: str, amount: int, wallet_pass:str=''): def transfer_mainnet_gas(wallet: str, address: str, address_to: str, amount: int, wallet_pass:str=''):
cmd = ( f"{NEOGO_CLI_PREFIX} wallet nep5 transfer -w {wallet} -r {NEOFS_NEO_API_ENDPOINT} --from {address} " cmd = ( f"{NEOGO_CLI_PREFIX} wallet nep17 transfer -w {wallet} -r {NEO_MAINNET_ENDPOINT} --from {address} "
f"--to {address_to} --token gas --amount {amount}" ) f"--to {address_to} --token GAS --amount {amount}" )
logger.info(f"Executing command: {cmd}") logger.info(f"Executing command: {cmd}")
out = _run_sh_with_passwd(wallet_pass, cmd) out = _run_sh_with_passwd(wallet_pass, cmd)
@ -112,7 +112,7 @@ def transfer_mainnet_gas(wallet: str, address: str, address_to: str, amount: int
@keyword('Withdraw Mainnet Gas') @keyword('Withdraw Mainnet Gas')
def withdraw_mainnet_gas(wallet: str, address: str, scripthash: str, amount: int): def withdraw_mainnet_gas(wallet: str, address: str, scripthash: str, amount: int):
cmd = ( f"{NEOGO_CLI_PREFIX} contract invokefunction -w {wallet} -a {address} -r {NEOFS_NEO_API_ENDPOINT} " cmd = ( f"{NEOGO_CLI_PREFIX} contract invokefunction -w {wallet} -a {address} -r {NEO_MAINNET_ENDPOINT} "
f"{NEOFS_CONTRACT} withdraw {scripthash} int:{amount} -- {scripthash}" ) f"{NEOFS_CONTRACT} withdraw {scripthash} int:{amount} -- {scripthash}" )
logger.info(f"Executing command: {cmd}") logger.info(f"Executing command: {cmd}")
@ -129,16 +129,18 @@ def withdraw_mainnet_gas(wallet: str, address: str, scripthash: str, amount: int
@keyword('Mainnet Balance') @keyword('Mainnet Balance')
def mainnet_balance(address: str): def mainnet_balance(address: str):
request = 'curl -X POST '+NEO_MAINNET_ENDPOINT+' --cacert ca/nspcc-ca.pem -H \'Content-Type: application/json\' -d \'{ "jsonrpc": "2.0", "id": 5, "method": "getnep5balances", "params": [\"'+address+'\"] }\''
logger.info(f"Executing request: {request}")
complProc = subprocess.run(request, check=True, universal_newlines=True, headers = {'Content-type': 'application/json'}
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15, shell=True) data = { "jsonrpc": "2.0", "id": 5, "method": "getnep17balances", "params": [ address ] }
response = requests.post(NEO_MAINNET_ENDPOINT, json=data, headers=headers, verify=False)
out = complProc.stdout if not response.ok:
logger.info(out) raise Exception(f"""Failed:
request: {data},
response: {response.text},
status code: {response.status_code} {response.reason}""")
m = re.search(r'"668e0c1f9d7b70a99dd9e06eadd4c784d641afbc","amount":"([\d\.]+)"', out) m = re.search(rf'"{GAS_HASH}","amount":"([\d\.]+)"', response.text)
if not m.start() != m.end(): if not m.start() != m.end():
raise Exception("Can not get mainnet gas balance.") raise Exception("Can not get mainnet gas balance.")
@ -150,16 +152,16 @@ def mainnet_balance(address: str):
@keyword('Expexted Mainnet Balance') @keyword('Expexted Mainnet Balance')
def expected_mainnet_balance(address: str, expected: float): def expected_mainnet_balance(address: str, expected: float):
amount = mainnet_balance(address) amount = mainnet_balance(address)
gas_expected = int(expected * 10**8)
if float(amount) != float(expected): if int(amount) != int(gas_expected):
raise Exception(f"Expected amount ({expected}) of GAS has not been found. Found {amount}.") raise Exception(f"Expected amount ({gas_expected}) of GAS has not been found. Found {amount}.")
return True return True
@keyword('NeoFS Deposit') @keyword('NeoFS Deposit')
def neofs_deposit(wallet: str, address: str, scripthash: str, amount: int, wallet_pass:str=''): def neofs_deposit(wallet: str, address: str, scripthash: str, amount: int, wallet_pass:str=''):
cmd = ( f"{NEOGO_CLI_PREFIX} contract invokefunction -w {wallet} -a {address} " cmd = ( f"{NEOGO_CLI_PREFIX} contract invokefunction -w {wallet} -a {address} "
f"-r {NEOFS_NEO_API_ENDPOINT} {NEOFS_CONTRACT} " f"-r {NEO_MAINNET_ENDPOINT} {NEOFS_CONTRACT} "
f"deposit {scripthash} int:{amount} bytes: -- {scripthash}") f"deposit {scripthash} int:{amount} bytes: -- {scripthash}")
logger.info(f"Executing command: {cmd}") logger.info(f"Executing command: {cmd}")
@ -185,20 +187,21 @@ def transaction_accepted_in_block(tx_id):
logger.info("Transaction id: %s" % tx_id) logger.info("Transaction id: %s" % tx_id)
TX_request = 'curl -X POST '+NEO_MAINNET_ENDPOINT+' --cacert ca/nspcc-ca.pem -H \'Content-Type: application/json\' -d \'{ "jsonrpc": "2.0", "id": 5, "method": "gettransactionheight", "params": [\"'+ tx_id +'\"] }\'' headers = {'Content-type': 'application/json'}
data = { "jsonrpc": "2.0", "id": 5, "method": "gettransactionheight", "params": [ tx_id ] }
response = requests.post(NEO_MAINNET_ENDPOINT, json=data, headers=headers, verify=False)
logger.info(f"Executing command: {TX_request}") if not response.ok:
raise Exception(f"""Failed:
request: {data},
response: {response.text},
status code: {response.status_code} {response.reason}""")
complProc = subprocess.run(TX_request, check=True, universal_newlines=True, if (response.text == 0):
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15, shell=True)
logger.info(complProc.stdout)
response = json.loads(complProc.stdout)
if (response['result'] == 0):
raise Exception( "Transaction is not found in the blocks." ) raise Exception( "Transaction is not found in the blocks." )
logger.info("Transaction has been found in the block %s." % response['result'] ) logger.info("Transaction has been found in the block %s." % response.text )
return response['result'] return response.text
@keyword('Get Transaction') @keyword('Get Transaction')
def get_transaction(tx_id: str): def get_transaction(tx_id: str):
@ -208,10 +211,18 @@ def get_transaction(tx_id: str):
:param tx_id: transaction id :param tx_id: transaction id
""" """
TX_request = 'curl -X POST '+NEO_MAINNET_ENDPOINT+' --cacert ca/nspcc-ca.pem -H \'Content-Type: application/json\' -d \'{ "jsonrpc": "2.0", "id": 5, "method": "getapplicationlog", "params": [\"'+tx_id+'\"] }\'' headers = {'Content-type': 'application/json'}
complProc = subprocess.run(TX_request, check=True, universal_newlines=True, data = { "jsonrpc": "2.0", "id": 5, "method": "getapplicationlog", "params": [ tx_id ] }
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15, shell=True) response = requests.post(NEO_MAINNET_ENDPOINT, json=data, headers=headers, verify=False)
logger.info(complProc.stdout)
if not response.ok:
raise Exception(f"""Failed:
request: {data},
response: {response.text},
status code: {response.status_code} {response.reason}""")
else:
logger.info(response.text)
@keyword('Get Balance') @keyword('Get Balance')
def get_balance(privkey: str): def get_balance(privkey: str):

View file

@ -8,5 +8,6 @@ NEO_MAINNET_ENDPOINT = "http://92.53.71.51:20332"
NEOFS_NEO_API_ENDPOINT = "http://92.53.71.51:20332" NEOFS_NEO_API_ENDPOINT = "http://92.53.71.51:20332"
HTTP_GATE = 'http://92.53.71.51:38080' HTTP_GATE = 'http://92.53.71.51:38080'
S3_GATE = 'https://92.53.71.51:28080' S3_GATE = 'https://92.53.71.51:28080'
NEOFS_CONTRACT = "5f490fbd8010fd716754073ee960067d28549b7d" NEOFS_NETMAP = ['92.53.71.51:18080', '92.53.71.52:18080','92.53.71.53:18080','92.53.71.54:18080', '92.53.71.55:18080']
NEOFS_NETMAP = ['92.53.71.51:18080', '92.53.71.52:18080','92.53.71.53:18080','92.53.71.54:18080', '92.53.71.55:18080']
GAS_HASH = '668e0c1f9d7b70a99dd9e06eadd4c784d641afbc'

View file

@ -344,7 +344,9 @@ Check eACL Deny and Allow All Bearer Filter UserHeader Equal
Run Keyword And Expect Error * Run Keyword And Expect Error *
... Head object ${USER_KEY} ${CID} ${S_OID_USER_2} bearer_allow_all_user ... Head object ${USER_KEY} ${CID} ${S_OID_USER_2} bearer_allow_all_user
Delete object ${USER_KEY} ${CID} ${S_OID_USER} bearer_allow_all_user # Delete can not be filtered by UserHeader.
Run Keyword And Expect Error *
... Delete object ${USER_KEY} ${CID} ${S_OID_USER} bearer_allow_all_user
Run Keyword And Expect Error * Run Keyword And Expect Error *
... Delete object ${USER_KEY} ${CID} ${S_OID_USER_2} bearer_allow_all_user ... Delete object ${USER_KEY} ${CID} ${S_OID_USER_2} bearer_allow_all_user

View file

@ -39,28 +39,22 @@ NeoFS Complex Object Operations
Container Existing ${PRIV_KEY} ${CID} Container Existing ${PRIV_KEY} ${CID}
Wait Until Keyword Succeeds 2 min 30 sec Wait Until Keyword Succeeds 2 min 30 sec
... Expected Balance ${PRIV_KEY} 50 -0.0007 ... Expected Balance ${PRIV_KEY} 50 -7e-08
${SIZE} = Set Variable 20e+6 ${SIZE} = Set Variable 20e+6
${FILE} = Generate file of bytes ${SIZE} ${FILE} = Generate file of bytes ${SIZE}
${FILE_HASH} = Get file hash ${FILE} ${FILE_HASH} = Get file hash ${FILE}
${S_OID} = Put object to NeoFS ${PRIV_KEY} ${FILE} ${CID} ${EMPTY} ${EMPTY} ${S_OID} = Put object to NeoFS ${PRIV_KEY} ${FILE} ${CID} ${EMPTY} ${EMPTY}
${H_OID} = Put object to NeoFS ${PRIV_KEY} ${FILE} ${CID} ${EMPTY} ${FILE_USR_HEADER} ${H_OID} = Put object to NeoFS ${PRIV_KEY} ${FILE} ${CID} ${EMPTY} ${FILE_USR_HEADER}
${H_OID_OTH} = Put object to NeoFS ${PRIV_KEY} ${FILE} ${CID} ${EMPTY} ${FILE_USR_HEADER_OTH} ${H_OID_OTH} = Put object to NeoFS ${PRIV_KEY} ${FILE} ${CID} ${EMPTY} ${FILE_USR_HEADER_OTH}
Should Be True '${S_OID}'!='${H_OID}' and '${H_OID}'!='${H_OID_OTH}'
Validate storage policy for object ${PRIV_KEY} 2 ${CID} ${S_OID} Validate storage policy for object ${PRIV_KEY} 2 ${CID} ${S_OID}
Validate storage policy for object ${PRIV_KEY} 2 ${CID} ${H_OID} Validate storage policy for object ${PRIV_KEY} 2 ${CID} ${H_OID}
Validate storage policy for object ${PRIV_KEY} 2 ${CID} ${H_OID_OTH} Validate storage policy for object ${PRIV_KEY} 2 ${CID} ${H_OID_OTH}
# @{Link_obj_S} = Verify linked objects ${PRIV_KEY} ${CID} ${S_OID} ${SIZE}
# @{Link_obj_H} = Verify linked objects ${PRIV_KEY} ${CID} ${H_OID} ${SIZE}
# @{Full_obj_list} = Create List @{Link_obj_S} @{Link_obj_H} ${S_OID} ${H_OID}
# Search object ${PRIV_KEY} ${CID} ${EMPTY} ${EMPTY} @{Full_obj_list}
@{S_OBJ_ALL} = Create List ${S_OID} ${H_OID} ${H_OID_OTH} @{S_OBJ_ALL} = Create List ${S_OID} ${H_OID} ${H_OID_OTH}
@{S_OBJ_H} = Create List ${H_OID} @{S_OBJ_H} = Create List ${H_OID}
@{S_OBJ_H_OTH} = Create List ${H_OID_OTH} @{S_OBJ_H_OTH} = Create List ${H_OID_OTH}
@ -86,12 +80,16 @@ NeoFS Complex Object Operations
Head object ${PRIV_KEY} ${CID} ${S_OID} ${EMPTY} Head object ${PRIV_KEY} ${CID} ${S_OID} ${EMPTY}
Head object ${PRIV_KEY} ${CID} ${H_OID} ${EMPTY} ${FILE_USR_HEADER} Head object ${PRIV_KEY} ${CID} ${H_OID} ${EMPTY} ${FILE_USR_HEADER}
Delete object ${PRIV_KEY} ${CID} ${S_OID} ${EMPTY} Verify Split Chain ${PRIV_KEY} ${CID} ${S_OID}
Delete object ${PRIV_KEY} ${CID} ${H_OID} ${EMPTY} Verify Split Chain ${PRIV_KEY} ${CID} ${H_OID}
${TOMBSTONE_S} = Delete object ${PRIV_KEY} ${CID} ${S_OID} ${EMPTY}
${TOMBSTONE_H} = Delete object ${PRIV_KEY} ${CID} ${H_OID} ${EMPTY}
Verify Head tombstone ${PRIV_KEY} ${CID} ${TOMBSTONE_S} ${S_OID} ${ADDR}
Verify Head tombstone ${PRIV_KEY} ${CID} ${TOMBSTONE_H} ${H_OID} ${ADDR}
#Verify Head tombstone ${PRIV_KEY} ${CID} ${S_OID}
Sleep 2min Sleep 2min
Run Keyword And Expect Error * Run Keyword And Expect Error *

View file

@ -38,7 +38,7 @@ NeoFS Simple Object Operations
Container Existing ${PRIV_KEY} ${CID} Container Existing ${PRIV_KEY} ${CID}
Wait Until Keyword Succeeds 2 min 30 sec Wait Until Keyword Succeeds 2 min 30 sec
... Expected Balance ${PRIV_KEY} 50 -0.0007 ... Expected Balance ${PRIV_KEY} 50 -7e-08
${FILE} = Generate file of bytes 1024 ${FILE} = Generate file of bytes 1024
${FILE_HASH} = Get file hash ${FILE} ${FILE_HASH} = Get file hash ${FILE}
@ -75,9 +75,11 @@ NeoFS Simple Object Operations
Head object ${PRIV_KEY} ${CID} ${S_OID} ${EMPTY} Head object ${PRIV_KEY} ${CID} ${S_OID} ${EMPTY}
Head object ${PRIV_KEY} ${CID} ${H_OID} ${EMPTY} ${FILE_USR_HEADER} Head object ${PRIV_KEY} ${CID} ${H_OID} ${EMPTY} ${FILE_USR_HEADER}
Delete object ${PRIV_KEY} ${CID} ${S_OID} ${EMPTY} ${TOMBSTONE_S} = Delete object ${PRIV_KEY} ${CID} ${S_OID} ${EMPTY}
Delete object ${PRIV_KEY} ${CID} ${H_OID} ${EMPTY} ${TOMBSTONE_H} = Delete object ${PRIV_KEY} ${CID} ${H_OID} ${EMPTY}
#Verify Head tombstone ${PRIV_KEY} ${CID} ${S_OID}
Verify Head tombstone ${PRIV_KEY} ${CID} ${TOMBSTONE_S} ${S_OID} ${ADDR}
Verify Head tombstone ${PRIV_KEY} ${CID} ${TOMBSTONE_H} ${H_OID} ${ADDR}
Sleep 2min Sleep 2min

View file

@ -30,7 +30,7 @@ NeoFS Deposit and Withdraw
Sleep 1 min Sleep 1 min
Expexted Mainnet Balance ${ADDR} 4.86192020 Expexted Mainnet Balance ${ADDR} 4.84454920
${NEOFS_BALANCE} = Get Balance ${PRIV_KEY} ${NEOFS_BALANCE} = Get Balance ${PRIV_KEY}
${TX} = Withdraw Mainnet Gas ${WALLET} ${ADDR} ${SCRIPT_HASH} 50 ${TX} = Withdraw Mainnet Gas ${WALLET} ${ADDR} ${SCRIPT_HASH} 50
@ -40,6 +40,5 @@ NeoFS Deposit and Withdraw
Sleep 1 min Sleep 1 min
Get Balance ${PRIV_KEY} Get Balance ${PRIV_KEY}
Expected Balance ${PRIV_KEY} ${NEOFS_BALANCE} -50 Expected Balance ${PRIV_KEY} ${NEOFS_BALANCE} -50
Expexted Mainnet Balance ${ADDR} 54.82554860 Expexted Mainnet Balance ${ADDR} 54.80800160