Skip to content

Commit

Permalink
Increase test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
jrobinAV committed Nov 25, 2024
1 parent 57ca943 commit 379f462
Show file tree
Hide file tree
Showing 6 changed files with 830 additions and 29 deletions.
18 changes: 11 additions & 7 deletions taipy/gui_core/_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -1015,10 +1015,10 @@ def get_data_node_history(self, id: str):

def __check_readable_editable(self, state: State, id: str, ent_type: str, var: t.Optional[str]):
if not (reason := is_readable(t.cast(ScenarioId, id))):
_GuiCoreContext.__assign_var(state, var, f"{ent_type} {id} is not readable: {_get_reason(reason)}.")
_GuiCoreContext.__assign_var(state, var, f"{ent_type} {id} is not readable: {_get_reason(reason)}")
return False
if not (reason := is_editable(t.cast(ScenarioId, id))):
_GuiCoreContext.__assign_var(state, var, f"{ent_type} {id} is not editable: {_get_reason(reason)}.")
_GuiCoreContext.__assign_var(state, var, f"{ent_type} {id} is not editable: {_get_reason(reason)}")
return False
return True

Expand All @@ -1028,7 +1028,7 @@ def update_data(self, state: State, id: str, payload: t.Dict[str, str]):
if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
return
data = t.cast(dict, args[0])
error_var = payload.get("error_id")
error_var = data.get("error_id")
entity_id = t.cast(str, data.get(_GuiCoreContext.__PROP_ENTITY_ID))
if not self.__check_readable_editable(state, entity_id, "Data node", error_var):
return
Expand Down Expand Up @@ -1130,7 +1130,9 @@ def tabular_data_edit(self, state: State, var_name: str, payload: dict): # noqa
"Error updating data node tabular value: type does not support at[] indexer.",
)
if new_data is not None:
datanode.write(new_data, comment=user_data.get(_GuiCoreContext.__PROP_ENTITY_COMMENT))
datanode.write(new_data,
editor_id=self.gui._get_client_id(),
comment=user_data.get(_GuiCoreContext.__PROP_ENTITY_COMMENT))
_GuiCoreContext.__assign_var(state, error_var, "")
except Exception as e:
_GuiCoreContext.__assign_var(state, error_var, f"Error updating data node tabular value. {e}")
Expand Down Expand Up @@ -1217,18 +1219,19 @@ def on_dag_select(self, state: State, id: str, payload: t.Dict[str, str]):

def on_file_action(self, state: State, id: str, payload: t.Dict[str, t.Any]):
args = t.cast(list, payload.get("args"))
if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
return
act_payload = t.cast(t.Dict[str, str], args[0])
dn_id = t.cast(DataNodeId, act_payload.get("id"))
error_id = act_payload.get("error_id", "")
if reason := is_readable(dn_id):
try:
dn = t.cast(_FileDataNodeMixin, core_get(dn_id))
if act_payload.get("action") == "export":
path = dn._get_downloadable_path()
if path:
if reason := dn.is_downloadable():
path = dn._get_downloadable_path()
self.gui._download(Path(path), dn_id)
else:
reason = dn.is_downloadable()
state.assign(
error_id,
"Data unavailable: "
Expand All @@ -1242,6 +1245,7 @@ def on_file_action(self, state: State, id: str, payload: t.Dict[str, t.Any]):
act_payload.get("path", ""),
t.cast(t.Callable[[str, t.Any], bool], checker) if callable(checker) else None,
editor_id=self.gui._get_client_id(),
comment=None
)
):
state.assign(error_id, f"Data unavailable: {reason.reasons}")
Expand Down
14 changes: 4 additions & 10 deletions tests/gui_core/test_context_is_editable.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,9 @@ def test_update_data(self):
MockState(assign=assign),
"",
{
"args": [
{"id": a_datanode.id},
],
"error_id": "error_var",
"args": [{
"id": a_datanode.id,
"error_id": "error_var"}],
},
)
assign.assert_called()
Expand All @@ -269,12 +268,7 @@ def test_update_data(self):
gui_core_context.update_data(
MockState(assign=assign),
"",
{
"args": [
{"id": a_datanode.id},
],
"error_id": "error_var",
},
{"args": [{"id": a_datanode.id, "error_id": "error_var"}]},
)
assign.assert_called_once()
assert assign.call_args.args[0] == "error_var"
Expand Down
14 changes: 2 additions & 12 deletions tests/gui_core/test_context_is_readable.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,7 @@ def test_update_data(self):
gui_core_context.update_data(
MockState(assign=assign),
"",
{
"args": [
{"id": a_datanode.id},
],
"error_id": "error_var",
},
{"args": [{"id": a_datanode.id, "error_id": "error_var"}]},
)
assign.assert_called()
assert assign.call_args_list[0].args[0] == "error_var"
Expand All @@ -411,12 +406,7 @@ def test_update_data(self):
gui_core_context.update_data(
MockState(assign=assign),
"",
{
"args": [
{"id": a_datanode.id},
],
"error_id": "error_var",
},
{"args": [{"id": a_datanode.id, "error_id": "error_var"}]},
)
assign.assert_called_once()
assert assign.call_args.args[0] == "error_var"
Expand Down
209 changes: 209 additions & 0 deletions tests/gui_core/test_context_on_file_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
# Copyright 2021-2024 Avaiga Private Limited
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

import typing as t
from pathlib import Path
from unittest.mock import Mock, patch

import pytest

from taipy import DataNode, Gui, Scope
from taipy.core.data import PickleDataNode
from taipy.core.data._data_manager_factory import _DataManagerFactory
from taipy.core.data._file_datanode_mixin import _FileDataNodeMixin
from taipy.core.reason import Reason, ReasonCollection
from taipy.gui_core._context import _GuiCoreContext

dn = PickleDataNode("dn_config_id",
scope = Scope.GLOBAL,
properties={"default_path": "pa/th"})

def core_get(entity_id):
if entity_id == dn.id:
return dn
return None


def not_downloadable ():
return ReasonCollection()._add_reason(dn.id, Reason("foo"))


def downloadable():
return ReasonCollection()


def not_readable(entity_id):
return ReasonCollection()._add_reason(entity_id, Reason("foo"))


def readable(entity_id):
return ReasonCollection()


def mock_checker(**kwargs):
return True


def check_fails(**kwargs):
raise Exception("Failed")


def upload_fails (a, b, editor_id, comment):
return ReasonCollection()._add_reason(dn.id, Reason("bar"))


def download_fails (a, b, editor_id, comment):
return ReasonCollection()._add_reason(dn.id, Reason("bar"))


class MockState:
def __init__(self, **kwargs) -> None:
self.assign = kwargs.get("assign")


class TestGuiCoreContext_on_file_action:

@pytest.fixture(scope="class", autouse=True)
def set_entities(self):
_DataManagerFactory._build_manager()._set(dn)

def test_does_not_fail_if_wrong_args(self):
gui_core_context = _GuiCoreContext(Mock(Gui))
gui_core_context.on_file_action(state=Mock(), id="", payload={})
gui_core_context.on_file_action(state=Mock(), id="", payload={"args": "wrong_args"})
gui_core_context.on_file_action(state=Mock(), id="", payload={"args": ["wrong_args"]})

def test_datanode_not_readable(self):
with patch("taipy.gui_core._context.is_readable", side_effect=not_readable):
with patch("taipy.gui_core._context.core_get", side_effect=core_get) as mock_core_get:
with patch.object(DataNode, "write") as mock_write:
mockGui = Mock(Gui)
mockGui._get_client_id = lambda: "a_client_id"
gui_core_context = _GuiCoreContext(mockGui)
assign = Mock()
gui_core_context.on_file_action(
state=MockState(assign=assign),
id="",
payload={"args": [{"id": dn.id, "error_id": "error_var"}]},
)
mock_core_get.assert_not_called()
mock_write.assert_not_called()
assign.assert_called_once_with("error_var", "foo.")

def test_upload_file_without_checker(self):
with patch("taipy.gui_core._context.is_readable", side_effect=readable):
with patch("taipy.gui_core._context.core_get", side_effect=core_get) as mock_core_get:
with patch.object(_FileDataNodeMixin, "_upload") as mock_upload:
mockGui = Mock(Gui)
mockGui._get_client_id = lambda: "a_client_id"
gui_core_context = _GuiCoreContext(mockGui)
assign = Mock()
gui_core_context.on_file_action(
state=MockState(assign=assign),
id="",
payload={"args": [{"id": dn.id, "error_id": "error_var", "path": "pa/th"}]},
)
mock_core_get.assert_called_once_with(dn.id)
mock_upload.assert_called_once_with(
"pa/th",
None,
editor_id="a_client_id",
comment=None)
assign.assert_not_called()

def test_upload_file_with_checker(self):
with patch("taipy.gui_core._context.is_readable", side_effect=readable):
with patch("taipy.gui_core._context.core_get", side_effect=core_get) as mock_core_get:
with patch.object(_FileDataNodeMixin, "_upload") as mock_upload:
mockGui = Mock(Gui)
mockGui._get_client_id = lambda: "a_client_id"
mockGui._get_user_function = lambda _ : _
gui_core_context = _GuiCoreContext(mockGui)
assign = Mock()
gui_core_context.on_file_action(
state=MockState(assign=assign),
id="",
payload={"args": [
{"id": dn.id, "error_id": "error_var", "path": "pa/th", "upload_check": mock_checker}]},
)
mock_core_get.assert_called_once_with(dn.id)
mock_upload.assert_called_once_with(
"pa/th",
t.cast(t.Callable[[str, t.Any], bool], mock_checker),
editor_id="a_client_id",
comment=None)
assign.assert_not_called()

def test_upload_file_with_failing_checker(self):
with patch("taipy.gui_core._context.is_readable", side_effect=readable):
with patch("taipy.gui_core._context.core_get", side_effect=core_get) as mock_core_get:
with patch.object(_FileDataNodeMixin, "_upload", side_effect=upload_fails) as mock_upload:
mockGui = Mock(Gui)
mockGui._get_client_id = lambda: "a_client_id"
mockGui._get_user_function = lambda _ : _
gui_core_context = _GuiCoreContext(mockGui)
assign = Mock()
gui_core_context.on_file_action(
state=MockState(assign=assign),
id="",
payload={"args": [
{"id": dn.id, "error_id": "error_var", "path": "pa/th", "upload_check": check_fails}]},
)
mock_core_get.assert_called_once_with(dn.id)
mock_upload.assert_called_once_with(
"pa/th",
t.cast(t.Callable[[str, t.Any], bool], check_fails),
editor_id="a_client_id",
comment=None)
assign.assert_called_once_with("error_var", "Data unavailable: bar.")

def test_download_file_not_downloadable(self):
with patch.object(_FileDataNodeMixin, "is_downloadable", side_effect=not_downloadable):
with patch("taipy.gui_core._context.core_get", side_effect=core_get) as mock_core_get:
with patch.object(_FileDataNodeMixin, "_get_downloadable_path") as mock_download:
mockGui = Mock(Gui)
mockGui._get_client_id = lambda: "a_client_id"
mockGui._get_user_function = lambda _ : _
gui_core_context = _GuiCoreContext(mockGui)
assign = Mock()
gui_core_context.on_file_action(
state=MockState(assign=assign),
id="",
payload={"args": [
{"id": dn.id,
"action": "export",
"error_id": "error_var"}]},
)
mock_core_get.assert_called_once_with(dn.id)
mock_download.assert_not_called()
assign.assert_called_once_with("error_var", "Data unavailable: foo.")

def test_download(self):
with patch.object(_FileDataNodeMixin, "is_downloadable", side_effect=downloadable):
with patch("taipy.gui_core._context.core_get", side_effect=core_get) as mock_core_get:
with patch.object(_FileDataNodeMixin, "_get_downloadable_path") as mock_download:
mockGui = Mock(Gui)
mockGui._get_client_id = lambda: "a_client_id"
mockGui._download.return_value = None
gui_core_context = _GuiCoreContext(mockGui)
assign = Mock()
gui_core_context.on_file_action(
state=MockState(assign=assign),
id="",
payload={"args": [
{"id": dn.id,
"action": "export",
"error_id": "error_var"}]},
)
mock_core_get.assert_called_once_with(dn.id)
mock_download.assert_called_once()
mockGui._download.assert_called_once_with(Path(dn._get_downloadable_path()), dn.id)
assign.assert_not_called()
Loading

0 comments on commit 379f462

Please sign in to comment.