Skip to content

Commit

Permalink
feat: allow variables to be used globally
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrieltron committed Sep 12, 2021
1 parent 204e73a commit 26511c2
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 5 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Allow variables to be used globally [#507](https://github.com/scanapi/scanapi/pull/507)

### Fixed
- Fix the `--browser` flag not working on macOS [#504](https://github.com/scanapi/scanapi/pull/504)

Expand Down
6 changes: 3 additions & 3 deletions scanapi/evaluators/spec_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ def update(self, spec_vars, extras=None, filter_responses=False):
values = {
key: evaluate(value, extras) for key, value in spec_vars.items()
}
self.registry.update(extras)
self.registry.update(values)

def get(self, key, default=None):
Expand All @@ -45,8 +44,9 @@ def __getitem__(self, key):
if key in self:
return self.registry[key]

if key in self.endpoint.parent.spec_vars:
return self.endpoint.parent.spec_vars[key]
all_vars = self.endpoint.get_all_vars()
if key in all_vars:
return all_vars[key]

raise KeyError(key)

Expand Down
29 changes: 29 additions & 0 deletions scanapi/tree/endpoint_node.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import copy
import logging
from itertools import chain
from typing import Any, Dict, Optional

from scanapi.evaluators import SpecEvaluator
from scanapi.exit_code import ExitCode
Expand Down Expand Up @@ -138,6 +140,33 @@ def is_root(self):
"""
return not self.parent

def propagate_spec_vars(
self,
new_spec_vars: Dict[str, Any],
extras: Optional[Dict[str, Any]] = None,
) -> None:
"""Update the endpoint node spec_vars and propagate those changes
to the parent nodes.
Args:
new_spec_vars [dic]: the new spec_vars.
extras [dict]: extra variables used to update the spec_vars.
"""
self.spec_vars.update(new_spec_vars, extras)
if not self.is_root:
self.parent.propagate_spec_vars(self.spec_vars.registry)

def get_all_vars(self) -> Dict[str, Any]:
"""Get all variables in spec_vars from the node and its parents.
Returns:
[dict]: dict from the variable's name to its value.
"""
variables = copy.deepcopy(self.spec_vars.registry)
if not self.is_root:
variables.update(self.parent.get_all_vars())
return variables

def run(self):
"""Run the requests of the node and all children nodes.
Expand Down
3 changes: 2 additions & 1 deletion scanapi/tree/request_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,11 @@ def run(self):
extras = dict(self.endpoint.spec_vars)
extras["response"] = response

self.endpoint.spec_vars.update(
self.endpoint.propagate_spec_vars(
self.spec.get(VARS_KEY, {}), extras=extras,
)

self.endpoint.spec_vars.update({"response": response})
tests_results = self._run_tests()
hide_sensitive_info(response)

Expand Down
20 changes: 20 additions & 0 deletions tests/unit/evaluators/test_spec_evaluator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pytest import fixture, mark, raises
from unittest import mock

from scanapi.evaluators.spec_evaluator import SpecEvaluator
from scanapi.tree import EndpointNode
Expand All @@ -11,6 +12,11 @@ def mock_string_evaluate(mocker):
)


@fixture
def mock_get_all_vars(mocker):
return mocker.patch("scanapi.tree.endpoint_node.EndpointNode.get_all_vars")


@fixture
def spec_evaluator():
parent = EndpointNode({"name": "bar", "requests": [{}]})
Expand All @@ -36,6 +42,20 @@ def test_should_return_foo(self, spec_evaluator):
value = spec_evaluator.get(key)
assert value == "foo"

@mark.context("when key exists in endpoint node")
@mark.it("should search endpoint node")
@mark.it("should return the value in endpoint node")
def test_should_return_list(self, spec_evaluator):
key = "requests"
expected_value = "value"
with mock.patch(
"scanapi.tree.endpoint_node.EndpointNode.get_all_vars",
side_effect=lambda: {key: expected_value},
) as mocked_get_all_vars:
value = spec_evaluator.get(key)
mocked_get_all_vars.assert_called_once()
assert value == expected_value


@mark.describe("spec evaluator")
@mark.describe("del")
Expand Down
46 changes: 46 additions & 0 deletions tests/unit/tree/endpoint_node/test_get_all_vars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from pytest import mark

from scanapi.tree import EndpointNode


@mark.describe("endpoint node")
@mark.describe("get_all_vars")
class TestGetAllVars:
@mark.it("should return spec_var values from the node and all parents")
def test_get_all_vars(self):
grandparent = EndpointNode({"name": "grandparent"})
grandparent_key = "grandparent_key"
grandparent_value = "grandparent_value"
grandparent.spec_vars.registry[grandparent_key] = grandparent_value
parent = EndpointNode({"name": "parent"}, grandparent)
parent_key = "parent_key"
parent_value = "parent_value"
parent.spec_vars.registry[parent_key] = parent_value
node = EndpointNode({"name": "node"}, parent)
node_key = "node_key"
node_value = "node_value"
node.spec_vars.registry[node_key] = node_value

spec_vars = node.get_all_vars()

assert spec_vars == {
grandparent_key: grandparent_value,
parent_key: parent_value,
node_key: node_value,
}

@mark.it("should alter the content of spec_vars")
def test_do_not_alter_spec_var(self):
parent = EndpointNode({"name": "parent"})
parent_key = "parent_key"
parent_value = "parent_value"
parent.spec_vars.registry[parent_key] = parent_value
node = EndpointNode({"name": "node"}, parent)
node_key = "node_key"
node_value = "node_value"
node.spec_vars.registry[node_key] = node_value

node.get_all_vars()

assert parent.spec_vars.registry == {parent_key: parent_value}
assert node.spec_vars.registry == {node_key: node_value}
29 changes: 29 additions & 0 deletions tests/unit/tree/endpoint_node/test_propagate_spec_vars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from pytest import mark

from scanapi.tree import EndpointNode


@mark.describe("endpoint node")
@mark.describe("propagate_spec_vars")
class TestPropagateSpecVars:
test_data = [
({"spec_var": "value"}, None, {"spec_var": "value"}),
(
{"spec_var": "${{ response }}"},
{"response": "value"},
{"spec_var": "value"},
),
]

@mark.it("should update spec_vars of node and all parent nodes")
@mark.parametrize("new_spec_vars, extra, expected_result", test_data)
def test_propagate_spec_vars(self, new_spec_vars, extra, expected_result):
grandparent = EndpointNode({"name": "grandparent"})
parent = EndpointNode({"name": "parent"}, grandparent)
node = EndpointNode({"name": "node"}, parent)

node.propagate_spec_vars(new_spec_vars, extra)

assert node.spec_vars.registry == expected_result
assert parent.spec_vars.registry == expected_result
assert grandparent.spec_vars.registry == expected_result
15 changes: 14 additions & 1 deletion tests/unit/tree/request_node/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,17 @@ def mock_run_tests(self, mocker):
def mock_time_sleep(self, mocker):
return mocker.patch("scanapi.tree.request_node.time.sleep")

@fixture
def mock_propagate_spec_vars(self, mocker):
return mocker.patch(
"scanapi.tree.endpoint_node.EndpointNode.propagate_spec_vars"
)

@mark.it("should call the request method")
def test_calls_request(self, mock_session, mock_time_sleep):
@mark.it("should update spec vars of endpoint node")
def test_calls_request(
self, mock_session, mock_time_sleep, mock_propagate_spec_vars
):
request = RequestNode(
{"path": "http://foo.com", "name": "request_name"},
endpoint=EndpointNode(
Expand All @@ -39,6 +48,10 @@ def test_calls_request(self, mock_session, mock_time_sleep):
allow_redirects=False,
)

mock_propagate_spec_vars.assert_called_once_with(
{}, extras={"response": mock_session().request()}
)

assert result == {
"response": mock_session().request(),
"tests_results": [],
Expand Down

0 comments on commit 26511c2

Please sign in to comment.