import pytest
import uuid
from unittest.mock import Mock, patch
from steps.api.utils.parser import (
find, to_float, to_int, parse_string, parse_nested_value,
get_true_keys, try_read_from_variable, try_save_value_to_variable, check_expected_values_in_response,
RANDOM_UUID_KEY
)
[docs]
def test_find():
# Test with a simple dictionary
test_dict = {
"components": {
"schemas": {
"User": {
"properties": {
"id": {"type": "integer"}
}
}
}
}
}
# Test finding a nested path
result = find("components/schemas/User", test_dict)
assert result == test_dict["components"]["schemas"]["User"]
# Test finding a path with multiple levels
result = find("components/schemas/User/properties/id", test_dict)
assert result == {"type": "integer"}
# Test with a path that doesn't exist
with pytest.raises(KeyError):
find("components/schemas/NonExistent", test_dict)
[docs]
def test_to_float():
# Test with valid float string
assert to_float("3.14") == 3.14
# Test with valid integer string
assert to_float("42") == 42.0
# Test with invalid string
assert to_float("not_a_float") is None
# Test with empty string
assert to_float("") is None
[docs]
def test_to_int():
# Test with valid integer string
assert to_int("42") == 42
# Test with float string (should fail)
assert to_int("3.14") is None
# Test with invalid string
assert to_int("not_an_int") is None
# Test with empty string
assert to_int("") is None
[docs]
def test_parse_string():
# Test with None value
assert parse_string("None") is None
# Test with boolean values
assert parse_string("true") is True
assert parse_string("false") is False
# Test with JSON object
json_obj = parse_string('{"key": "value"}')
assert isinstance(json_obj, dict)
assert json_obj["key"] == "value"
# Test with JSON array
json_arr = parse_string('[1, 2, 3]')
assert isinstance(json_arr, list)
assert json_arr == [1, 2, 3]
# Test with integer
assert parse_string("42") == 42
# Test with float
assert parse_string("3.14") == 3.14
# Test with string that can't be parsed as anything else
assert parse_string("just a string") == "just a string"
@patch("steps.api.utils.parser.try_read_from_variable")
[docs]
def test_parse_nested_value_string(mock_try_read):
# Mock the try_read_from_variable function
mock_try_read.return_value = "parsed_value"
# Test with a string
context = Mock()
result = parse_nested_value("test_string", context)
assert result == "parsed_value"
mock_try_read.assert_called_once_with("test_string", context)
[docs]
def test_parse_nested_value_dict():
# Test with a dictionary containing strings
context = Mock()
# Set up nested dictionary
test_dict = {
"key1": "value1",
"key2": {
"nested_key": "nested_value"
},
"key3": [
"list_item_1",
{"list_nested_key": "list_nested_value"}
]
}
# Mock the try_read_from_variable function
with patch("steps.api.utils.parser.try_read_from_variable",
side_effect=lambda value, ctx: f"parsed_{value}" if isinstance(value, str) else value):
result = parse_nested_value(test_dict, context)
# Check that all strings were parsed
assert result["key1"] == "parsed_value1"
assert result["key2"]["nested_key"] == "parsed_nested_value"
assert result["key3"][0] == "parsed_list_item_1"
assert result["key3"][1]["list_nested_key"] == "parsed_list_nested_value"
# Check that it's a copy, not the original
assert result is not test_dict
[docs]
def test_get_true_keys_dict():
# Test with a dictionary
test_dict = {
"key1": {
"key2": [
{"key3": "value"}
]
}
}
# Test getting keys for the first level
keys = get_true_keys(["key1"], test_dict)
assert keys == ["key1"]
# Test getting keys for nested levels
keys = get_true_keys(["key1", "key2", "0", "key3"], test_dict)
assert keys == ["key1", "key2", 0, "key3"]
# Test with invalid keys
with pytest.raises(KeyError):
get_true_keys(["nonexistent"], test_dict)
[docs]
def test_get_true_keys_list():
# Test with a list
test_list = [
{"key1": "value1"},
{"key2": "value2"}
]
# Test getting keys for the first level
keys = get_true_keys(["0"], test_list)
assert keys == [0]
# Test getting keys for nested levels
keys = get_true_keys(["0", "key1"], test_list)
assert keys == [0, "key1"]
# Test with invalid keys
with pytest.raises(IndexError):
get_true_keys(["2"], test_list) # Index out of range
[docs]
def test_get_true_keys_unsupported_type():
# Test with an unsupported type (not dict or list)
with pytest.raises(NotImplementedError):
get_true_keys(["key"], "not_a_dict_or_list")
[docs]
def test_try_read_from_variable_random_uuid():
# Test reading a random UUID
with patch("uuid.uuid4", return_value=uuid.UUID("12345678-1234-5678-1234-567812345678")):
result = try_read_from_variable(RANDOM_UUID_KEY, None)
assert result == "12345678-1234-5678-1234-567812345678"
[docs]
def test_try_read_from_variable_temporary():
# Test reading a temporary variable
context = Mock()
context.cloud.temp = {"test_key": "test_value"}
# Use the VARIABLE_METHOD_PATTERN to match "temporary variable "test_key""
result = try_read_from_variable('temporary variable "test_key"', context)
assert result == "test_value"
[docs]
def test_try_read_from_variable_user():
# Test reading a user variable
context = Mock()
context.cloud.users = [
{"first_name": "John", "last_name": "Doe"},
{"first_name": "Jane", "last_name": "Smith"}
]
# Use the USER_VARIABLE_METHOD_PATTERN to match 'variable "first_name" of user 0'
result = try_read_from_variable('variable "first_name" of user 0', context)
assert result == "John"
# Test with another user index
result = try_read_from_variable('variable "last_name" of user 1', context)
assert result == "Smith"
[docs]
def test_try_read_from_variable_admin():
# Test reading an admin variable
context = Mock()
context.cloud.admin = {"email": "admin@example.com", "role": "admin"}
# Use the ADMIN_VARIABLE_METHOD_PATTERN to match 'variable "email" of admin'
result = try_read_from_variable('variable "email" of admin', context)
assert result == "admin@example.com"
[docs]
def test_try_read_from_variable_environment():
# Test reading an environment variable
context = Mock()
context.cloud.environnement = {"TEST_VAR": "test_value"}
# Use the ENV_VARIABLE_METHOD_PATTERN to match 'environnement variable "TEST_VAR"'
result = try_read_from_variable('environnement variable "TEST_VAR"', context)
assert result == "test_value"
[docs]
def test_try_read_from_variable_type_conversion():
# Test string type conversion
result = try_read_from_variable('string "test"', None)
assert result == "test"
# Test int type conversion
result = try_read_from_variable('int 42', None)
assert result == 42
# Test float type conversion
result = try_read_from_variable('float 3.14', None)
assert result == 3.14
[docs]
def test_try_read_from_variable_fallback():
# Test fallback to returning the original string when no pattern matches
result = try_read_from_variable("unmatched string", None)
assert result == "unmatched string"
[docs]
def test_try_save_value_to_variable_temporary():
# Test saving to a temporary variable
context = Mock()
context.cloud.temp = {}
try_save_value_to_variable(context, "test_value", 'temporary variable "test_key"')
assert context.cloud.temp["test_key"] == "test_value"
[docs]
def test_try_save_value_to_variable_user():
# Test saving to a user variable
context = Mock()
context.cloud.users = [{}]
try_save_value_to_variable(context, "John", 'variable "first_name" of user 0')
assert context.cloud.users[0]["first_name"] == "John"
[docs]
def test_try_save_value_to_variable_nonexistent_pattern():
# Test with a pattern that doesn't match any variable type
context = Mock()
with pytest.raises(RuntimeWarning):
try_save_value_to_variable(context, "value", "invalid pattern")
[docs]
def test_check_expected_values_in_response_simple_key():
# Test a simple key match
context = Mock()
context.request = Mock()
context.request.response.json.return_value = {"status": "success", "message": "Operation completed"}
# Test with matching value
result, expected, response = check_expected_values_in_response(context, "status", "success")
assert result is True
assert expected == "success"
assert response == "success"
# Test with non-matching value
result, expected, response = check_expected_values_in_response(context, "status", "failure")
assert result is False
assert expected == "failure"
assert response == "success"
# Test with non-existent key
with pytest.raises(AssertionError):
check_expected_values_in_response(context, "nonexistent", "value")
[docs]
def test_check_expected_values_in_response_nested_key():
# Test with a nested key using bracket notation
context = Mock()
context.request = Mock()
context.request.response.json.return_value = {
"data": {
"user": {
"name": "John",
"id": 123
}
}
}
# Test with matching value
result, expected, response = check_expected_values_in_response(context, "data[\"user\"][\"name\"]", "John")
assert result is True
assert expected == "John"
assert response == "John"
# Test with non-matching value
result, expected, response = check_expected_values_in_response(context, "data[\"user\"][\"id\"]", "456")
assert result is False
assert expected == 456
assert response == 123
[docs]
def test_check_expected_values_in_response_eval_methods():
# Test with eval methods (any, all, none)
context = Mock()
context.request = Mock()
context.request.response.json.return_value = {
"items": [
{"id": 1, "status": "active"},
{"id": 2, "status": "inactive"},
{"id": 3, "status": "active"}
]
}
# Test with 'any' method matching
result, expected, response = check_expected_values_in_response(
context, "any([item[\"status\"] for item in response[\"items\"]])", "active")
assert result is True
assert expected == "active"
assert response == ["active", "inactive", "active"]
# Test with 'all' method not matching
result, expected, response = check_expected_values_in_response(
context, "all([item[\"status\"] for item in response[\"items\"]])", "active")
assert result is False
assert expected == "active"
assert response == ["active", "inactive", "active"]
# Test with 'none' method not matching
result, expected, response = check_expected_values_in_response(
context, "none([item[\"status\"] for item in response[\"items\"]])", "active")
assert result is False
assert expected == "active"
assert response == ["active", "inactive", "active"]
# Test with 'none' method matching
result, expected, response = check_expected_values_in_response(
context, "none([item[\"status\"] for item in response[\"items\"]])", "pending")
assert result is True
assert expected == "pending"
assert response == ["active", "inactive", "active"]
[docs]
def test_check_expected_values_in_response_with_variable():
# Test with a value from a variable
context = Mock()
context.request = Mock()
context.request.response.json.return_value = {"user_id": 123}
# Mock try_read_from_variable to return a specific value
with patch("steps.api.utils.parser.try_read_from_variable", return_value=123):
result, expected, response = check_expected_values_in_response(
context, "user_id", 'variable "id" of user 0')
assert result is True
assert expected == 123
assert response == 123