[#184]: verbs joined to keywords

Signed-off-by: anastasia prasolova <anastasia@nspcc.ru>
This commit is contained in:
anastasia prasolova 2022-06-06 16:47:13 +03:00 committed by Anastasia Prasolova
parent d696a8ee68
commit e086d0d62b
8 changed files with 211 additions and 224 deletions

View file

@ -92,6 +92,30 @@ def decode_storage_group(data: dict):
return data return data
def decode_tombstone(data: dict):
'''
This function reencodes Tombstone header.
'''
try:
data = decode_simple_header(data)
data['header']['sessionToken'] = decode_session_token(
data['header']['sessionToken'])
except Exception as exc:
raise ValueError(f"failed to decode JSON output: {exc}") from exc
return data
def decode_session_token(data: dict):
'''
This function reencodes a fragment of header which contains
information about session token.
'''
data['body']['object']['address']['containerID'] = json_reencode(
data['body']['object']['address']['containerID']['value'])
data['body']['object']['address']['objectID'] = json_reencode(
data['body']['object']['address']['objectID']['value'])
return data
def json_reencode(data: str): def json_reencode(data: str):
''' '''
According to JSON protocol, binary data (Object/Container/Storage Group IDs, etc) According to JSON protocol, binary data (Object/Container/Storage Group IDs, etc)
@ -103,6 +127,10 @@ def json_reencode(data: str):
def encode_for_json(data: str): def encode_for_json(data: str):
'''
This function encodes binary data for sending them as protobuf
structures.
'''
return base64.b64encode(base58.b58decode(data)).decode('utf-8') return base64.b64encode(base58.b58decode(data)).decode('utf-8')
def decode_common_fields(data: dict): def decode_common_fields(data: dict):

View file

@ -1,20 +1,15 @@
#!/usr/bin/python3 #!/usr/bin/python3
import base64
import json import json
import os import os
import re
import random import random
import uuid
import base58
from neo3 import wallet from neo3 import wallet
from common import (NEOFS_NETMAP, WALLET_PASS, NEOFS_ENDPOINT, from common import NEOFS_NETMAP_DICT
NEOFS_NETMAP_DICT, ASSETS_DIR) import neofs_verbs
from cli_helpers import _cmd_run
import json_transformers
from robot.api.deco import keyword from robot.api.deco import keyword
from robot.api import logger from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
ROBOT_AUTO_KEYWORDS = False ROBOT_AUTO_KEYWORDS = False
@ -30,52 +25,35 @@ def get_scripthash(wif: str):
@keyword('Verify Head Tombstone') @keyword('Verify Head Tombstone')
def verify_head_tombstone(wallet: str, cid: str, oid_ts: str, oid: str, addr: str): def verify_head_tombstone(wallet_path: str, cid: str, oid_ts: str, oid: str):
# TODO: replace with HEAD from neofs_verbs.py header = neofs_verbs.head_object(wallet_path, cid, oid_ts)
object_cmd = ( header = header['header']
f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet} '
f'--config {WALLET_PASS} object head --cid {cid} --oid {oid_ts} --json'
)
output = _cmd_run(object_cmd)
full_headers = json.loads(output)
logger.info(f"Output: {full_headers}")
# Header verification BuiltIn().should_be_equal(header["containerID"], cid,
header_cid = full_headers["header"]["containerID"]["value"] msg="Tombstone Header CID is wrong")
if json_transformers.json_reencode(header_cid) == cid:
logger.info(f"Header CID is expected: {cid} ({header_cid} in the output)")
else:
raise Exception("Header CID is not expected.")
header_owner = full_headers["header"]["ownerID"]["value"] wlt_data = dict()
if json_transformers.json_reencode(header_owner) == addr: with open(wallet_path, 'r') as fout:
logger.info(f"Header ownerID is expected: {addr} ({header_owner} in the output)") wlt_data = json.loads(fout.read())
else: wlt = wallet.Wallet.from_json(wlt_data, password='')
raise Exception("Header ownerID is not expected.") addr = wlt.accounts[0].address
header_type = full_headers["header"]["objectType"] BuiltIn().should_be_equal(header["ownerID"], addr,
if header_type == "TOMBSTONE": msg="Tombstone Owner ID is wrong")
logger.info(f"Header Type is expected: {header_type}")
else:
raise Exception("Header Type is not expected.")
header_session_type = full_headers["header"]["sessionToken"]["body"]["object"]["verb"] BuiltIn().should_be_equal(header["objectType"], 'TOMBSTONE',
if header_session_type == "DELETE": msg="Header Type isn't Tombstone")
logger.info(f"Header Session Type is expected: {header_session_type}")
else:
raise Exception("Header Session Type is not expected.")
header_session_cid = full_headers["header"]["sessionToken"]["body"]["object"]["address"]["containerID"]["value"] BuiltIn().should_be_equal(header["sessionToken"]["body"]["object"]["verb"], 'DELETE',
if json_transformers.json_reencode(header_session_cid) == cid: msg="Header Session Type isn't DELETE")
logger.info(f"Header ownerID is expected: {addr} ({header_session_cid} in the output)")
else:
raise Exception("Header Session CID is not expected.")
header_session_oid = full_headers["header"]["sessionToken"]["body"]["object"]["address"]["objectID"]["value"] BuiltIn().should_be_equal(header["sessionToken"]["body"]["object"]["address"]["containerID"],
if json_transformers.json_reencode(header_session_oid) == oid: cid,
logger.info(f"Header Session OID (deleted object) is expected: {oid} ({header_session_oid} in the output)") msg="Header Session ID is wrong")
else:
raise Exception("Header Session OID (deleted object) is not expected.") BuiltIn().should_be_equal(header["sessionToken"]["body"]["object"]["address"]["objectID"],
oid,
msg="Header Session OID is wrong")
@keyword('Get control endpoint with wif') @keyword('Get control endpoint with wif')

View file

@ -31,7 +31,7 @@ def get_object(wallet: str, cid: str, oid: str, bearer_token: str="",
GET from NeoFS. GET from NeoFS.
Args: Args:
wif (str): WIF of the wallet on whose behalf GET is done wallet (str): wallet on whose behalf GET is done
cid (str): ID of Container where we get the Object from cid (str): ID of Container where we get the Object from
oid (str): Object ID oid (str): Object ID
bearer_token (optional, str): path to Bearer Token file, appends to `--bearer` key bearer_token (optional, str): path to Bearer Token file, appends to `--bearer` key
@ -59,19 +59,20 @@ def get_object(wallet: str, cid: str, oid: str, bearer_token: str="",
return file_path return file_path
# TODO: make `bearer_token` optional
@keyword('Get Range Hash') @keyword('Get Range Hash')
def get_range_hash(wallet: str, cid: str, oid: str, bearer_token: str, def get_range_hash(wallet: str, cid: str, oid: str, bearer_token: str, range_cut: str,
range_cut: str, options: str=""): options: str=""):
''' '''
GETRANGEHASH of given Object. GETRANGEHASH of given Object.
Args: Args:
wif (str): WIF of the wallet on whose behalf GETRANGEHASH is done wallet (str): wallet on whose behalf GETRANGEHASH is done
cid (str): ID of Container where we get the Object from cid (str): ID of Container where we get the Object from
oid (str): Object ID oid (str): Object ID
bearer_token (str): path to Bearer Token file, appends to `--bearer` key
range_cut (str): Range to take hash from in the form offset1:length1,..., range_cut (str): Range to take hash from in the form offset1:length1,...,
value to pass to the `--range` parameter value to pass to the `--range` parameter
bearer_token (optional, str): path to Bearer Token file, appends to `--bearer` key
options (optional, str): any options which `neofs-cli object hash` accepts options (optional, str): any options which `neofs-cli object hash` accepts
Returns: Returns:
None None
@ -82,7 +83,9 @@ def get_range_hash(wallet: str, cid: str, oid: str, bearer_token: str,
f'{"--bearer " + bearer_token if bearer_token else ""} ' f'{"--bearer " + bearer_token if bearer_token else ""} '
f'{options}' f'{options}'
) )
_cmd_run(cmd) output = _cmd_run(cmd)
# cutting off output about range offset and length
return output.split(':')[1].strip()
@keyword('Put object') @keyword('Put object')
@ -92,7 +95,7 @@ def put_object(wallet: str, path: str, cid: str, bearer: str="", user_headers: d
PUT of given file. PUT of given file.
Args: Args:
wif (str): WIF of the wallet on whose behalf PUT is done wallet (str): wallet on whose behalf PUT is done
path (str): path to file to be PUT path (str): path to file to be PUT
cid (str): ID of Container where we get the Object from cid (str): ID of Container where we get the Object from
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
@ -123,7 +126,7 @@ def delete_object(wallet: str, cid: str, oid: str, bearer: str="", options: str=
DELETE an Object. DELETE an Object.
Args: Args:
wif (str): WIF of the wallet on whose behalf DELETE is done wallet (str): wallet on whose behalf DELETE is done
cid (str): ID of Container where we get the Object from cid (str): ID of Container where we get the Object from
oid (str): ID of Object we are going to delete oid (str): ID of Object we are going to delete
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
@ -142,47 +145,52 @@ def delete_object(wallet: str, cid: str, oid: str, bearer: str="", options: str=
return tombstone.strip() return tombstone.strip()
# TODO: remove `file_path` parameter as it is a boilerplate
# TODO: make `bearer` an optional parameter
@keyword('Get Range') @keyword('Get Range')
def get_range(wallet: str, cid: str, oid: str, range_file: str, bearer: str, def get_range(wallet: str, cid: str, oid: str, file_path: str, bearer: str, range_cut: str,
range_cut: str, options:str=""): options:str=""):
''' '''
GETRANGE an Object. GETRANGE an Object.
Args: Args:
wif (str): WIF of the wallet on whose behalf GETRANGE is done wallet (str): wallet on whose behalf GETRANGE is done
cid (str): ID of Container where we get the Object from cid (str): ID of Container where we get the Object from
oid (str): ID of Object we are going to request oid (str): ID of Object we are going to request
range_file (str): file where payload range data will be written
bearer (str): path to Bearer Token file, appends to `--bearer` key
range_cut (str): range to take data from in the form offset:length range_cut (str): range to take data from in the form offset:length
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
options (optional, str): any options which `neofs-cli object range` accepts options (optional, str): any options which `neofs-cli object range` accepts
Returns: Returns:
None (void)
''' '''
range_file = f"{ASSETS_DIR}/{uuid.uuid4()}"
cmd = ( cmd = (
f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet} ' f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet} '
f'object range --cid {cid} --oid {oid} --range {range_cut} --config {WALLET_PASS} ' f'object range --cid {cid} --oid {oid} --range {range_cut} --config {WALLET_PASS} '
f'--file {ASSETS_DIR}/{range_file} {options} ' f'{options} --file {range_file} '
f'{"--bearer " + bearer if bearer else ""} ' f'{"--bearer " + bearer if bearer else ""} '
) )
_cmd_run(cmd) _cmd_run(cmd)
content = ''
with open(range_file, 'rb') as fout:
content = fout.read()
return range_file, content
@keyword('Search object') @keyword('Search object')
def search_object(wallet: str, cid: str, keys: str="", bearer: str="", filters: dict={}, def search_object(wallet: str, cid: str, keys: str="", bearer: str="", filters: dict={},
expected_objects_list=[], options:str=""): expected_objects_list=[]):
''' '''
GETRANGE an Object. SEARCH an Object.
Args: Args:
wif (str): WIF of the wallet on whose behalf SEARCH is done wallet (str): wallet on whose behalf SEARCH is done
cid (str): ID of Container where we get the Object from cid (str): ID of Container where we get the Object from
keys(optional, str): any keys for Object SEARCH which `neofs-cli object search` keys(optional, str): any keys for Object SEARCH which `neofs-cli object search`
accepts, e.g. `--oid` accepts, e.g. `--oid`
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
filters (optional, dict): key=value pairs to filter Objects filters (optional, dict): key=value pairs to filter Objects
expected_objects_list (optional, list): a list of ObjectIDs to compare found Objects with expected_objects_list (optional, list): a list of ObjectIDs to compare found Objects with
options (optional, str): any options which `neofs-cli object search` accepts
Returns: Returns:
(list): list of found ObjectIDs (list): list of found ObjectIDs
''' '''
@ -194,7 +202,7 @@ def search_object(wallet: str, cid: str, keys: str="", bearer: str="", filters:
cmd = ( cmd = (
f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet} ' f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet} '
f'object search {keys} --cid {cid} {filters_result} {options} --config {WALLET_PASS} ' f'object search {keys} --cid {cid} {filters_result} --config {WALLET_PASS} '
f'{"--bearer " + bearer if bearer else ""}' f'{"--bearer " + bearer if bearer else ""}'
) )
output = _cmd_run(cmd) output = _cmd_run(cmd)
@ -206,7 +214,7 @@ def search_object(wallet: str, cid: str, keys: str="", bearer: str="", filters:
logger.info(f"Found objects list '{found_objects}' ", logger.info(f"Found objects list '{found_objects}' ",
f"is equal for expected list '{expected_objects_list}'") f"is equal for expected list '{expected_objects_list}'")
else: else:
raise Exception(f"Found object list {found_objects} ", logger.warn(f"Found object list {found_objects} ",
f"is not equal to expected list '{expected_objects_list}'") f"is not equal to expected list '{expected_objects_list}'")
return found_objects return found_objects
@ -220,7 +228,7 @@ def head_object(wallet: str, cid: str, oid: str, bearer_token: str="",
HEAD an Object. HEAD an Object.
Args: Args:
wif (str): WIF of the wallet on whose behalf HEAD is done wallet (str): wallet on whose behalf HEAD is done
cid (str): ID of Container where we get the Object from cid (str): ID of Container where we get the Object from
oid (str): ObjectID to HEAD oid (str): ObjectID to HEAD
bearer_token (optional, str): path to Bearer Token file, appends to `--bearer` key bearer_token (optional, str): path to Bearer Token file, appends to `--bearer` key
@ -279,5 +287,9 @@ def head_object(wallet: str, cid: str, oid: str, bearer_token: str="",
logger.info("decoding storage group") logger.info("decoding storage group")
return json_transformers.decode_storage_group(decoded) return json_transformers.decode_storage_group(decoded)
if decoded['header']['objectType'] == 'TOMBSTONE':
logger.info("decoding tombstone")
return json_transformers.decode_tombstone(decoded)
logger.info("decoding simple header") logger.info("decoding simple header")
return json_transformers.decode_simple_header(decoded) return json_transformers.decode_simple_header(decoded)

View file

@ -44,11 +44,9 @@ def get_file_hash(filename: str):
Returns: Returns:
(str): the hash of the file (str): the hash of the file
""" """
blocksize = 65536 file_hash = hashlib.sha256()
file_hash = hashlib.md5()
with open(filename, "rb") as out: with open(filename, "rb") as out:
for block in iter(lambda: out.read(blocksize), b""): file_hash.update(out.read())
file_hash.update(block)
return file_hash.hexdigest() return file_hash.hexdigest()

View file

@ -0,0 +1,65 @@
*** Settings ***
Variables common.py
Library contract_keywords.py
Library neofs.py
Library neofs_verbs.py
Library utility_keywords.py
Library Collections
Library OperatingSystem
*** Variables ***
${CLEANUP_TIMEOUT} = 10s
*** Keywords ***
Run All Verbs Except Delete And Expect Success
[Arguments] ${WALLET} ${CID} ${COMPLEXITY}
${OBJ_SIZE} = Run Keyword If """${COMPLEXITY}""" == """Simple"""
... Set Variable ${SIMPLE_OBJ_SIZE}
... ELSE
... Set Variable ${COMPLEX_OBJ_SIZE}
${FILE} ${FILE_HASH} = Generate file ${OBJ_SIZE}
${OID} = Put object ${WALLET} ${FILE} ${CID}
${OBJ_PATH} = Get object ${WALLET} ${CID} ${OID}
${DOWNLOADED_FILE_HASH} =
... Get file hash ${OBJ_PATH}
Should Be Equal ${DOWNLOADED_FILE_HASH} ${FILE_HASH}
# TODO: get rid of ${EMPTY}
${RANGE_FILE}
... ${DATA_RANGE} =
... Get Range ${WALLET} ${CID} ${OID} ${EMPTY} ${EMPTY} 0:10
${FILE_CONTENT} = Get Binary File ${FILE}
Should Contain ${FILE_CONTENT} ${DATA_RANGE}
# TODO: get rid of ${EMPTY}
${RANGE_HASH} = Get Range Hash ${WALLET} ${CID} ${OID} ${EMPTY} 0:10
${GR_HASH} = Get File Hash ${RANGE_FILE}
Should Be Equal ${GR_HASH} ${RANGE_HASH}
${FOUND_OBJECTS} = Search object ${WALLET} ${CID} keys=${OID}
List Should Contain Value
... ${FOUND_OBJECTS}
... ${OID}
&{RESPONSE} = Head object ${WALLET} ${CID} ${OID}
[Return] ${OID}
Delete Object And Validate Tombstone
[Arguments] ${WALLET} ${CID} ${OID}
${TOMBSTONE_ID} = Delete object ${WALLET} ${CID} ${OID}
Verify Head tombstone ${WALLET} ${CID} ${TOMBSTONE_ID} ${OID}
Tick Epoch
# we assume that during this time objects must be deleted
Sleep ${CLEANUP_TIMEOUT}
${ERR} = Run Keyword And Expect Error *
... Get object ${WALLET} ${CID} ${OID}
Should Contain ${ERR} code = 2052 message = object already removed

View file

@ -18,45 +18,61 @@ ${ATTR_DUPLICATE} = FileType=jpg,FileType=png
*** Test Cases *** *** Test Cases ***
Duplicated Object Attributes Object Attrubutes
[Documentation] Testcase to check duplicated attributes.
[Tags] Object
[Timeout] 10 min [Timeout] 10 min
[Setup] Setup [Setup] Setup
Check Various Object Attributes Simple
Check Various Object Attributes Complex
[Teardown] Teardown object_attributes
*** Keywords ***
Check Various Object Attributes
[Arguments] ${COMPLEXITY}
${WALLET} ${_} ${_} = Prepare Wallet And Deposit ${WALLET} ${_} ${_} = Prepare Wallet And Deposit
${PUBLIC_CID} = Create Container ${WALLET} basic_acl=${PUBLIC_ACL_F} ${PUBLIC_CID} = Create Container ${WALLET} basic_acl=${PUBLIC_ACL_F}
${FILE_S} ${_} = Generate File ${SIMPLE_OBJ_SIZE} ${OBJ_SIZE} = Run Keyword If """${COMPLEXITY}""" == """Simple"""
... Set Variable ${SIMPLE_OBJ_SIZE}
... ELSE
... Set Variable ${COMPLEX_OBJ_SIZE}
${FILE} ${_} = Generate File ${OBJ_SIZE}
################################################### ###################################################
# Checking that object attributes cannot duplicate # Checking that object attributes cannot duplicate
################################################### ###################################################
Run Keyword And Expect Error * ${ERR} = Run Keyword And Expect Error *
... Put object ${WALLET} ${FILE_S} ${PUBLIC_CID} user_headers=${ATTR_FILENAME} ... Put object ${WALLET} ${FILE} ${PUBLIC_CID} user_headers=${ATTR_FILENAME}
# Robot doesn't allow to create a dictionary with the same keys, so using plain text option here Should Contain ${ERR} code = 1024 message = duplication of attributes detected
Run Keyword And Expect Error * # Robot doesn't allow to create a dictionary with the same keys,
... Put object ${WALLET} ${FILE_S} ${PUBLIC_CID} options=--attributes ${ATTR_DUPLICATE} # so using plain text option here
${ERR} = Run Keyword And Expect Error *
... Put object ${WALLET} ${FILE} ${PUBLIC_CID} options=--attributes ${ATTR_DUPLICATE}
Should Contain ${ERR} code = 1024 message = duplication of attributes detected
################################################## ##################################################
# Checking that object cannot have empty attibute # Checking that object cannot have empty attibute
################################################## ##################################################
Run Keyword And Expect Error * ${ERR} = Run Keyword And Expect Error *
... Put object ${WALLET} ${FILE_S} ${PUBLIC_CID} user_headers=${ATTR_NONE} ... Put object ${WALLET} ${FILE} ${PUBLIC_CID} user_headers=${ATTR_NONE}
Should Contain ${ERR} code = 1024 message = empty attribute value
##################################################### #####################################################
# Checking a successful step with a single attribute # Checking a successful step with a single attribute
##################################################### #####################################################
${OID} = Put object ${WALLET} ${FILE_S} ${PUBLIC_CID} user_headers=${ATTR_SINGLE} ${OID} = Put object ${WALLET} ${FILE} ${PUBLIC_CID} user_headers=${ATTR_SINGLE}
${HEADER} = Head object ${WALLET} ${PUBLIC_CID} ${OID} ${HEADER} = Head object ${WALLET} ${PUBLIC_CID} ${OID}
Dictionary Should Contain Sub Dictionary Dictionary Should Contain Sub Dictionary
... ${HEADER}[header][attributes] ... ${HEADER}[header][attributes]
... ${ATTR_SINGLE} ... ${ATTR_SINGLE}
... msg="No expected User Attribute in HEAD response" ... msg="No expected User Attribute in HEAD response"
${FOUND_OIDS} = Search Object ${WALLET} ${PUBLIC_CID} filters=${ATTR_SINGLE}
[Teardown] Teardown object_attributes Should Be Equal ${OID} ${FOUND_OIDS}[0]
... msg="Cannot SEARCH an object by User Attribute"

View file

@ -13,13 +13,9 @@ Library utility_keywords.py
Library Collections Library Collections
Resource setup_teardown.robot Resource setup_teardown.robot
Resource verbs.robot
Resource payment_operations.robot Resource payment_operations.robot
*** Variables ***
${CLEANUP_TIMEOUT} = 10s
&{FILE_USR_HEADER} = key1=1 key2=abc
${ALREADY_REMOVED_ERROR} = code = 2052 message = object already removed
*** Test cases *** *** Test cases ***
NeoFS Complex Object Operations NeoFS Complex Object Operations
@ -29,80 +25,26 @@ NeoFS Complex Object Operations
[Setup] Setup [Setup] Setup
${WALLET} ${ADDR} ${_} = Prepare Wallet And Deposit ${WALLET} ${_} ${_} = Prepare Wallet And Deposit
${CID} = Create container ${WALLET} ${CID} = Create container ${WALLET}
${FILE} ${FILE_HASH} = Generate file ${COMPLEX_OBJ_SIZE} ${OID} =
... Run All Verbs Except Delete And Expect Success
... ${WALLET} ${CID} Complex
${S_OID} = Put object ${WALLET} ${FILE} ${CID} ${COPIES} = Get Complex Object Copies ${WALLET} ${CID} ${OID}
${H_OID} = Put object ${WALLET} ${FILE} ${CID} user_headers=${FILE_USR_HEADER}
Should Not Be Equal ${H_OID} ${S_OID}
${COPIES} = Get Complex Object Copies ${WALLET} ${CID} ${S_OID}
Should Be Equal As Numbers 2 ${COPIES} Should Be Equal As Numbers 2 ${COPIES}
${COPIES} = Get Complex Object Copies ${WALLET} ${CID} ${H_OID}
Should Be Equal As Numbers 2 ${COPIES}
${GET_OBJ_S} = Get object ${WALLET} ${CID} ${S_OID}
${GET_OBJ_H} = Get object ${WALLET} ${CID} ${H_OID}
${FILE_HASH_S} = Get file hash ${GET_OBJ_S}
${FILE_HASH_H} = Get file hash ${GET_OBJ_H}
Should Be Equal ${FILE_HASH_S} ${FILE_HASH}
Should Be Equal ${FILE_HASH_H} ${FILE_HASH}
Get Range Hash ${WALLET} ${CID} ${S_OID} ${EMPTY} 0:10
Get Range Hash ${WALLET} ${CID} ${H_OID} ${EMPTY} 0:10
Get Range ${WALLET} ${CID} ${S_OID} s_get_range ${EMPTY} 0:10
Get Range ${WALLET} ${CID} ${H_OID} h_get_range ${EMPTY} 0:10
@{S_OBJ_ALL} = Create List ${S_OID} ${H_OID}
@{S_OBJ_H} = Create List ${H_OID}
Search object ${WALLET} ${CID} --root expected_objects_list=${S_OBJ_ALL}
Search object ${WALLET} ${CID} --root filters=${FILE_USR_HEADER} expected_objects_list=${S_OBJ_H}
&{S_RESPONSE} = Head object ${WALLET} ${CID} ${S_OID}
&{H_RESPONSE} = Head object ${WALLET} ${CID} ${H_OID}
Dictionary Should Contain Sub Dictionary
... ${H_RESPONSE}[header][attributes]
... ${FILE_USR_HEADER}
... msg="There are no User Headers in HEAD response"
${PAYLOAD_LENGTH} ${PAYLOAD_LENGTH}
... ${SPLIT_ID} ... ${SPLIT_ID}
... ${SPLIT_OBJECTS} = Restore Large Object By Last ... ${SPLIT_OBJECTS} =
... ${WALLET} ${CID} ${S_OID} ... Restore Large Object By Last ${WALLET} ${CID} ${OID}
${H_PAYLOAD_LENGTH} Compare With Link Object ${WALLET} ${CID} ${OID} ${SPLIT_ID} ${SPLIT_OBJECTS}
... ${H_SPLIT_ID} &{RESPONSE} = Head object ${WALLET} ${CID} ${OID}
... ${H_SPLIT_OBJECTS} = Restore Large Object By Last Should Be Equal As Numbers ${RESPONSE.header.payloadLength} ${PAYLOAD_LENGTH}
... ${WALLET} ${CID} ${H_OID}
Compare With Link Object ${WALLET} ${CID} ${S_OID} ${SPLIT_ID} ${SPLIT_OBJECTS} Delete Object And Validate Tombstone
Compare With Link Object ${WALLET} ${CID} ${H_OID} ${H_SPLIT_ID} ${H_SPLIT_OBJECTS} ... ${WALLET} ${CID} ${OID}
Should Be Equal As Numbers ${S_RESPONSE.header.payloadLength} ${PAYLOAD_LENGTH}
Should Be Equal As Numbers ${H_RESPONSE.header.payloadLength} ${H_PAYLOAD_LENGTH}
${TOMBSTONE_S} = Delete object ${WALLET} ${CID} ${S_OID}
${TOMBSTONE_H} = Delete object ${WALLET} ${CID} ${H_OID}
Verify Head tombstone ${WALLET} ${CID} ${TOMBSTONE_S} ${S_OID} ${ADDR}
Verify Head tombstone ${WALLET} ${CID} ${TOMBSTONE_H} ${H_OID} ${ADDR}
Tick Epoch
# we assume that during this time objects must be deleted
Sleep ${CLEANUP_TIMEOUT}
${ERR_MSG} = Run Keyword And Expect Error *
... Get object ${WALLET} ${CID} ${S_OID}
Should Contain ${ERR_MSG} ${ALREADY_REMOVED_ERROR}
${ERR_MSG} = Run Keyword And Expect Error *
... Get object ${WALLET} ${CID} ${H_OID}
Should Contain ${ERR_MSG} ${ALREADY_REMOVED_ERROR}
[Teardown] Teardown object_complex [Teardown] Teardown object_complex

View file

@ -2,21 +2,12 @@
Variables common.py Variables common.py
Library container.py Library container.py
Library contract_keywords.py
Library neofs.py
Library neofs_verbs.py
Library storage_policy.py Library storage_policy.py
Library utility_keywords.py Library utility_keywords.py
Library Collections
Resource payment_operations.robot Resource payment_operations.robot
Resource setup_teardown.robot Resource setup_teardown.robot
Resource verbs.robot
*** Variables ***
${CLEANUP_TIMEOUT} = 10s
&{FILE_USR_HEADER} = key1=1 key2=abc
*** Test cases *** *** Test cases ***
NeoFS Simple Object Operations NeoFS Simple Object Operations
@ -26,61 +17,18 @@ NeoFS Simple Object Operations
[Setup] Setup [Setup] Setup
${WALLET} ${ADDR} ${_} = Prepare Wallet And Deposit ${WALLET} ${_} ${_} = Prepare Wallet And Deposit
${CID} = Create container ${WALLET} ${CID} = Create container ${WALLET}
${FILE} ${FILE_HASH} = Generate file ${SIMPLE_OBJ_SIZE} ${OID} =
... Run All Verbs Except Delete And Expect Success
... ${WALLET} ${CID} Simple
${S_OID} = Put object ${WALLET} ${FILE} ${CID} ${COPIES} = Get Simple Object Copies ${WALLET} ${CID} ${OID}
${H_OID} = Put object ${WALLET} ${FILE} ${CID} user_headers=${FILE_USR_HEADER}
${COPIES} = Get Simple Object Copies ${WALLET} ${CID} ${S_OID}
Should Be Equal As Numbers 2 ${COPIES}
${COPIES} = Get Simple Object Copies ${WALLET} ${CID} ${H_OID}
Should Be Equal As Numbers 2 ${COPIES} Should Be Equal As Numbers 2 ${COPIES}
@{S_OBJ_ALL} = Create List ${S_OID} ${H_OID} Delete Object And Validate Tombstone
@{S_OBJ_H} = Create List ${H_OID} ... ${WALLET} ${CID} ${OID}
${GET_OBJ_S} = Get object ${WALLET} ${CID} ${S_OID}
${GET_OBJ_H} = Get object ${WALLET} ${CID} ${H_OID}
${FILE_HASH_S} = Get file hash ${GET_OBJ_S}
${FILE_HASH_H} = Get file hash ${GET_OBJ_H}
Should Be Equal ${FILE_HASH_S} ${FILE_HASH}
Should Be Equal ${FILE_HASH_H} ${FILE_HASH}
Get Range Hash ${WALLET} ${CID} ${S_OID} ${EMPTY} 0:10
Get Range Hash ${WALLET} ${CID} ${H_OID} ${EMPTY} 0:10
Get Range ${WALLET} ${CID} ${S_OID} s_get_range ${EMPTY} 0:10
Get Range ${WALLET} ${CID} ${H_OID} h_get_range ${EMPTY} 0:10
Search object ${WALLET} ${CID} expected_objects_list=${S_OBJ_ALL}
Search object ${WALLET} ${CID} filters=${FILE_USR_HEADER} expected_objects_list=${S_OBJ_H}
Head object ${WALLET} ${CID} ${S_OID}
&{RESPONSE} = Head object ${WALLET} ${CID} ${H_OID}
Dictionary Should Contain Sub Dictionary
... ${RESPONSE}[header][attributes]
... ${FILE_USR_HEADER}
... msg="There are no User Headers in HEAD response"
${TOMBSTONE_S} = Delete object ${WALLET} ${CID} ${S_OID}
${TOMBSTONE_H} = Delete object ${WALLET} ${CID} ${H_OID}
Verify Head tombstone ${WALLET} ${CID} ${TOMBSTONE_S} ${S_OID} ${ADDR}
Verify Head tombstone ${WALLET} ${CID} ${TOMBSTONE_H} ${H_OID} ${ADDR}
Tick Epoch
# we assume that during this time objects must be deleted
Sleep ${CLEANUP_TIMEOUT}
Run Keyword And Expect Error *
... Get object ${WALLET} ${CID} ${S_OID} ${EMPTY} ${GET_OBJ_S}
Run Keyword And Expect Error *
... Get object ${WALLET} ${CID} ${H_OID} ${EMPTY} ${GET_OBJ_H}
[Teardown] Teardown object_simple [Teardown] Teardown object_simple