diff --git a/README.md b/README.md index a6d96d0..63d0a2e 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![readthedocs](https://github.com/Curts0/PyTabular/actions/workflows/readthedocs.yml/badge.svg)](https://github.com/Curts0/PyTabular/actions/workflows/readthedocs.yml) [![pages-build-deployment](https://github.com/Curts0/PyTabular/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/Curts0/PyTabular/actions/workflows/pages/pages-build-deployment) [![flake8](https://github.com/Curts0/PyTabular/actions/workflows/flake8.yml/badge.svg?branch=master)](https://github.com/Curts0/PyTabular/actions/workflows/flake8.yml) +[![docstr-coverage](https://github.com/Curts0/PyTabular/actions/workflows/docstr-coverage.yml/badge.svg)](https://github.com/Curts0/PyTabular/actions/workflows/docstr-coverage.yml) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit) ### What is it? diff --git a/pyproject.toml b/pyproject.toml index ebd0d17..08c5f78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "python_tabular" -version = "0.3.1" +version = "0.3.2" authors = [ { name="Curtis Stallings", email="curtisrstallings@gmail.com" }, ] @@ -40,4 +40,13 @@ include-package-data = true [tool.pytest.ini_options] filterwarnings = ["ignore::DeprecationWarning"] -addopts = "-vv" \ No newline at end of file +addopts = "-vv" + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover" +] +[tool.coverage.run] +include = [ + "pytabular/*" +] \ No newline at end of file diff --git a/pytabular/basic_checks.py b/pytabular/basic_checks.py index 900fa8b..bde7909 100644 --- a/pytabular/basic_checks.py +++ b/pytabular/basic_checks.py @@ -86,6 +86,10 @@ def BPA_Violations_To_DF(model: pytabular.Tabular, te2: str, bpa: str) -> pd.Dat Returns: pd.DataFrame: Super simple right now. Just splits into two columns.. The object in violation and the rule. """ + logger.warning(f"{sys._getframe(0).f_code.co_name} will be deprecated...") + logger.warning( + "Honestly it was dumb I ever made this. You can use model.Analyze_BPA()" + ) results = model.Analyze_BPA(te2, bpa) data = [ rule.replace(" violates rule ", "^").replace('"', "").split("^") @@ -116,6 +120,11 @@ def Last_X_Interval( Returns: pd.DataFrame: Pandas DataFrame of results. """ + # pragma: no cover + logger.warning(f"{sys._getframe(0).f_code.co_name} will be deprecated...") + logger.warning( + "Honestly it was dumb I ever made this. Just write the darn thing in DAX" + ) if isinstance(PyMeasure, str): try: Measure = [ diff --git a/pytabular/column.py b/pytabular/column.py index 1bfa456..6be807d 100644 --- a/pytabular/column.py +++ b/pytabular/column.py @@ -54,7 +54,9 @@ def get_sample_values(self, top_n: int = 3) -> pd.DataFrame: ORDER BY {column_to_sample} """ return self.Table.Model.Query(dax_query) - except Exception: + except Exception: # pragma: no cover + # Excluding exception from coverage.py for now... + # This is really tech debt anyways and should be replaced... dax_query = f""" EVALUATE TOPN( diff --git a/pytabular/logic_utils.py b/pytabular/logic_utils.py index 656a739..b858370 100644 --- a/pytabular/logic_utils.py +++ b/pytabular/logic_utils.py @@ -52,43 +52,6 @@ def pandas_datatype_to_tabular_datatype(df: pd.DataFrame) -> Dict: } -def pd_dataframe_to_dax_expression( - df: pd.DataFrame = pd.DataFrame(data={"col1": [1.0, 2.0], "col2": [3, 4]}) -) -> str: - """ - This will take a pandas dataframe and convert to a dax expression - For example this DF: - col1 col2 - 0 1 3 - 1 2 4 - - | - | - V - - Will convert to this expression string: - DEFINE - TABLE tablename = { ( 1, 3 ), ( 2, 4 ) } - - EVALUATE - SELECTCOLUMNS( - tablename, - "col1", tablename[Value1], - "col2", tablename[Value2] - ) - """ - - def dax_tableconstructor_rows_expression_generator( - list_of_strings: list[str], - ) -> str: - """ - Converts list[str] to dax table rows for example ['one','two'] -> '('one','two')' - """ - return - - return True - - def pd_dataframe_to_m_expression(df: pd.DataFrame) -> str: """This will take a pandas dataframe and convert to an m expression For example this DF: @@ -174,21 +137,12 @@ def remove_suffix(input_string, suffix): Returns: str: input_str with suffix removed """ - if suffix and input_string.endswith(suffix): - return input_string[: -len(suffix)] - return input_string - - -def sql_wrap_count_around_query(original_query: str) -> str: - """Simple string formating to get the total row count of a sql query. - - Args: - original_query (str): Regular sql query to get count of. - - Returns: - str: f"SELECT COUNT(1) FROM ({original_query}) temp_table" - """ - return f"SELECT COUNT(1) FROM ({original_query}) temp_table" + output = ( + input_string[: -len(suffix)] + if suffix and input_string.endswith(suffix) + else input_string + ) + return output def get_sub_list(lst: list, n: int) -> list: diff --git a/pytabular/pytabular.py b/pytabular/pytabular.py index 5d5d6a6..9b85bbd 100644 --- a/pytabular/pytabular.py +++ b/pytabular/pytabular.py @@ -13,7 +13,6 @@ MPartitionSource, ) -from Microsoft.AnalysisServices import UpdateOptions from typing import List, Union from collections import namedtuple import pandas as pd @@ -71,7 +70,7 @@ def __init__(self, CONNECTION_STR: str): for database in self.Server.Databases.GetEnumerator() if database.Name == self.Catalog or self.Catalog is None ][0] - except Exception: + except Exception: # pragma: no cover err_msg = f"Unable to find Database... {self.Catalog}" logger.error(err_msg) raise Exception(err_msg) @@ -153,15 +152,17 @@ def Is_Process(self) -> bool: _jobs_df = self.Query("select * from $SYSTEM.DISCOVER_JOBS") return len(_jobs_df[_jobs_df["JOB_DESCRIPTION"] == "Process"]) > 0 - def Disconnect(self) -> bool: - """Disconnects from Model - - Returns: - bool: True if successful - """ + def Disconnect(self) -> None: + """Disconnects from Model""" logger.info(f"Disconnecting from - {self.Server.Name}") + atexit.unregister(self.Disconnect) return self.Server.Disconnect() + def Reconnect(self) -> None: + """Reconnects to Model""" + logger.info(f"Reconnecting to {self.Server.Name}") + return self.Server.Reconnect() + def Refresh(self, *args, **kwargs) -> pd.DataFrame: """PyRefresh Class to handle refreshes of model. @@ -178,22 +179,12 @@ def Refresh(self, *args, **kwargs) -> pd.DataFrame: """ return self.PyRefresh(self, *args, **kwargs).Run() - def Update(self, UpdateOptions: UpdateOptions = UpdateOptions.ExpandFull) -> None: - """[Update Model](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.majorobject.update?view=analysisservices-dotnet#microsoft-analysisservices-majorobject-update(microsoft-analysisservices-updateoptions)) - - Args: - UpdateOptions (UpdateOptions, optional): See above MS Doc link. Defaults to UpdateOptions.ExpandFull. - - Returns: - None: Placeholder to eventually change. - """ - logger.debug("Running Update Request") - return self.Database.Update(UpdateOptions) - def SaveChanges(self): """Called after refreshes or any model changes. Currently will return a named tuple of all changes detected. However a ton of room for improvement here. """ + if self.Server.Connected is False: + self.Reconnect() def property_changes(Property_Changes): """ @@ -248,7 +239,7 @@ def property_changes(Property_Changes): Xmla_Results, ) - def Backup_Table(self, table_str: str) -> bool: + def Backup_Table(self, table_str: str) -> bool: # pragma: no cover """Will be removed. This is experimental with no written pytest for it. Backs up table in memory, brings with it measures, columns, hierarchies, relationships, roles, etc. It will add suffix '_backup' to all objects. @@ -350,7 +341,7 @@ def clone_role_permissions(): self.SaveChanges() return True - def Revert_Table(self, table_str: str) -> bool: + def Revert_Table(self, table_str: str) -> bool: # pragma: no cover """Will be removed. This is experimental with no written pytest for it. This is used in conjunction with Backup_Table(). It will take the 'TableName_backup' and replace with the original. Example scenario -> @@ -469,7 +460,8 @@ def Query( if Effective_User is None: return self.Adomd.Query(Query_Str) - try: + try: # pragma: no cover + # This needs a public model with effective users to properly test conn = self.Effective_Users[Effective_User] logger.debug(f"Effective user found querying as... {Effective_User}") except Exception: diff --git a/pytabular/refresh.py b/pytabular/refresh.py index 49a68c2..eb650f5 100644 --- a/pytabular/refresh.py +++ b/pytabular/refresh.py @@ -14,7 +14,6 @@ from table import PyTable, PyTables from partition import PyPartition from abc import ABC -import atexit logger = logging.getLogger("PyTabular") @@ -263,7 +262,6 @@ def _post_checks(self): if self.trace is not None: self.trace.Stop() self.trace.Drop() - atexit.unregister(self.trace.Drop) for check in self._checks: check.Post_Check() self._checks.remove_refresh_check(check) diff --git a/pytabular/relationship.py b/pytabular/relationship.py index 0eb26c0..718ebf6 100644 --- a/pytabular/relationship.py +++ b/pytabular/relationship.py @@ -3,9 +3,8 @@ Once connected to your model, interacting with relationship(s) will be done through these classes. """ import logging -from object import PyObject -from pytabular.object import PyObjects -from pytabular.table import PyTable, PyTables +from object import PyObject, PyObjects +from table import PyTable, PyTables from Microsoft.AnalysisServices.Tabular import ( CrossFilteringBehavior, diff --git a/pytabular/tabular_tracing.py b/pytabular/tabular_tracing.py index fd29817..9bc4a46 100644 --- a/pytabular/tabular_tracing.py +++ b/pytabular/tabular_tracing.py @@ -80,7 +80,7 @@ def add_column(trace_event, trace_event_column): """Adds the column to trace event.""" try: trace_event.Columns.Add(trace_event_column) - except Exception: + except Exception: # pragma: no cover logger.warning(f"{trace_event} - {trace_event_column} Skipped") pass @@ -98,14 +98,6 @@ def add_column(trace_event, trace_event_column): self.Trace.OnEvent += self.Handler return True - def Arguments( - Trace_Events: List[TraceEvent], - Trace_Event_Columns: List[TraceColumn], - Handler: Callable, - ): - """Raises NotImplementedError. Arguments must be created in order for Trace to work.""" - raise NotImplementedError - def Add(self) -> int: """Runs on initialization. Adds built Trace to the Server. @@ -123,6 +115,9 @@ def Update(self) -> None: Unless unsuccessful then it will return the error from Server. """ logger.info(f"Updating {self.Name} in {self.Tabular_Class.Server.Name}") + if self.Tabular_Class.Server.Connected is False: + self.Tabular_Class.Reconnect() + return self.Trace.Update() def Start(self) -> None: @@ -153,6 +148,7 @@ def Drop(self) -> None: then it will return the error from Server. """ logger.info(f"Dropping {self.Name} in {self.Tabular_Class.Server.Name}") + atexit.unregister(self.Drop) return self.Trace.Drop() def _Query_DMV_For_Event_Categories(self): @@ -189,7 +185,7 @@ def _Query_DMV_For_Event_Categories(self): return Event_Categories -def _refresh_handler(source, args): +def _refresh_handler(source, args): # pragma: no cover """Default function called when `Refresh_Trace` is used. It will log various steps of the refresh process. """ @@ -289,7 +285,7 @@ def __init__( super().__init__(Tabular_Class, Trace_Events, Trace_Event_Columns, Handler) -def _query_monitor_handler(source, args): +def _query_monitor_handler(source, args): # pragma: no cover """ Default function used with the `Query_Monitor` trace. """ diff --git a/test/adventureworks/AdventureWorks Sales.pbix b/test/adventureworks/AdventureWorks Sales.pbix index 6f6bce3..cf8791e 100644 Binary files a/test/adventureworks/AdventureWorks Sales.pbix and b/test/adventureworks/AdventureWorks Sales.pbix differ diff --git a/test/config.py b/test/config.py index 7708040..2021882 100644 --- a/test/config.py +++ b/test/config.py @@ -18,25 +18,29 @@ def get_test_path(): adventureworks_path = f'"{get_test_path()}\\adventureworks\\AdventureWorks Sales.pbix"' -p.logger.info(f"Opening {adventureworks_path}") -subprocess.run(["powershell", f"Start-Process {adventureworks_path}"]) -# Got to be a better way to wait and ensure the PBIX file is open? -p.logger.info("sleep(30)... Need a better way to wait until PBIX is loaded...") -sleep(30) +def find_local_pbi(): + attempt = p.find_local_pbi_instances() + if len(attempt) > 0: + return attempt[0] + else: + p.logger.info(f"Opening {adventureworks_path}") + subprocess.run(["powershell", f"Start-Process {adventureworks_path}"]) + # Got to be a better way to wait and ensure the PBIX file is open? + p.logger.info("sleep(30)... Need a better way to wait until PBIX is loaded...") + sleep(30) + return p.find_local_pbi_instances()[0] + + +LOCAL_FILE = find_local_pbi() -LOCAL_FILE = p.find_local_pbi_instances()[0] p.logger.info(f"Connecting to... {LOCAL_FILE[0]} - {LOCAL_FILE[1]}") local_pbix = p.Tabular(LOCAL_FILE[1]) -# subprocess.Popen(["powershell","Start-Process \"AdventureWorks Sales.pbix\""]) p.logger.info("Generating test data...") testingtablename = "PyTestTable" testingtabledf = pd.DataFrame(data={"col1": [1, 2, 3], "col2": ["four", "five", "six"]}) testing_parameters = [ - pytest.param(local_pbix, id="LOCAL"), + pytest.param(local_pbix, id=local_pbix.Name), ] - - -# os.path.basename(os.getcwd()) diff --git a/test/conftest.py b/test/conftest.py index 7b7ecf1..50e428b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -4,7 +4,10 @@ testingtabledf, ) import pytabular as p -import subprocess + + +class testing_storage: + query_trace = None def pytest_report_header(config): @@ -34,5 +37,5 @@ def pytest_sessionfinish(session, exitstatus): p.logger.info("Executing pytest cleanup...") remove_testing_table(local_pbix) p.logger.info("Finding and closing PBIX file...") - subprocess.run(["powershell", "Stop-Process -Name PBIDesktop"]) + # subprocess.run(["powershell", "Stop-Process -Name PBIDesktop"]) return True diff --git a/test/test_10logic_utils.py b/test/test_10logic_utils.py new file mode 100644 index 0000000..cd97ed1 --- /dev/null +++ b/test/test_10logic_utils.py @@ -0,0 +1,73 @@ +"""pytest for the table.py file. Covers the PyTable and PyTables classes. +""" +from test.config import testing_parameters +import pytest +from pytabular import logic_utils +import pandas as pd +import os + +sublists = [pytest.param(range(0, 50), x, id=f"Sublist {str(x)}") for x in range(1, 5)] + + +@pytest.mark.parametrize("lst, n", sublists) +def test_sub_list(lst, n): + """Tests `get_sub_list()` from `logic_utils.py`.""" + result = logic_utils.get_sub_list(lst, n) + assert isinstance(result, list) + + +suffix_list = [ + "helloworld_backup", + "helloworld_testing", + "helloworld_suffix", + "nosuffix", +] + + +@pytest.mark.parametrize( + "file_name, suffix", + [ + pytest.param( + suffix, suffix[suffix.find("_") + 1 :], id=suffix[suffix.find("_") + 1 :] + ) + for suffix in suffix_list + ], +) +def test_remove_suffix(file_name, suffix): + result = logic_utils.remove_suffix(file_name, suffix) + assert suffix not in result + + +dfs = [ + pytest.param(pd.DataFrame({"column_1": [0, 1, 2, 3, 4, 5]}), id="DataFrame1"), + pytest.param(pd.DataFrame({"column_1": ["one", "two", "three"]}), id="DataFrame2"), +] + + +@pytest.mark.parametrize("df", dfs) +def test_dataframe_to_dict(df): + assert isinstance(logic_utils.dataframe_to_dict(df), list) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_dict_to_markdown_table(model): + dependencies = [measure.get_dependencies() for measure in model.Measures] + columns = ["Referenced Object Type", "Referenced Table", "Referenced Object"] + result = logic_utils.dict_to_markdown_table(dependencies, columns) + assert isinstance(result, str) + + +def test_remove_dir(): + dir = "testing_to_be_deleted" + os.makedirs(dir) + remove = f"{os.getcwd()}\\{dir}" + logic_utils.remove_folder_and_contents(remove) + assert dir not in os.listdir() + + +def test_remove_file(): + file_to_delete = "testing_to_be_deleted.txt" + with open(file_to_delete, "w") as f: + f.write("Delete this file...") + logic_utils.remove_file(f"{os.getcwd()}\\{file_to_delete}") + assert file_to_delete not in os.listdir() diff --git a/test/test_2object.py b/test/test_2object.py new file mode 100644 index 0000000..a35fe69 --- /dev/null +++ b/test/test_2object.py @@ -0,0 +1,99 @@ +"""pytest for the table.py file. Covers the PyTable and PyTables classes. +""" +from test.config import testing_parameters +import pytest +from pytabular import Tabular + + +@pytest.mark.parametrize("model", testing_parameters) +def test_rich_repr_model(model): + try: + model.__rich_repr__() + except Exception: + pytest.fail("__rich_repr__() failed") + + +@pytest.mark.parametrize("model", testing_parameters) +def test_rich_repr_table(model): + try: + model.Tables[0].__rich_repr__() + except Exception: + pytest.fail("__rich_repr__() failed") + + +@pytest.mark.parametrize("model", testing_parameters) +def test_rich_repr_tables(model): + try: + model.Tables.__rich_repr__() + except Exception: + pytest.fail("__rich_repr__() failed") + + +@pytest.mark.parametrize("model", testing_parameters) +def test_rich_repr_column(model): + try: + model.Columns[0].__rich_repr__() + except Exception: + pytest.fail("__rich_repr__() failed") + + +@pytest.mark.parametrize("model", testing_parameters) +def test_rich_repr_columns(model): + try: + model.Columns.__rich_repr__() + except Exception: + pytest.fail("__rich_repr__() failed") + + +@pytest.mark.parametrize("model", testing_parameters) +def test_rich_repr_partition(model): + try: + model.Partitions[0].__rich_repr__() + except Exception: + pytest.fail("__rich_repr__() failed") + + +@pytest.mark.parametrize("model", testing_parameters) +def test_rich_repr_partitions(model): + try: + model.Partitions.__rich_repr__() + except Exception: + pytest.fail("__rich_repr__() failed") + + +@pytest.mark.parametrize("model", testing_parameters) +def test_rich_repr_measure(model): + try: + model.Measures[0].__rich_repr__() + except Exception: + pytest.fail("__rich_repr__() failed") + + +@pytest.mark.parametrize("model", testing_parameters) +def test_rich_repr_measures(model): + try: + model.Measures.__rich_repr__() + except Exception: + pytest.fail("__rich_repr__() failed") + + +# TODO need to get this to have coverage for full getattr +@pytest.mark.parametrize("model", testing_parameters) +def test_get_attr(model): + assert isinstance(model.Tables[0].Model, Tabular) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_iadd_tables(model): + a = model.Tables.Find("Sales") + b = model.Tables.Find("Date") + a += b + assert len(a.Find("Date")) > 0 + + +@pytest.mark.parametrize("model", testing_parameters) +def test_iadd_table(model): + a = model.Tables.Find("Sales") + b = model.Tables.Find("Date")[0] + a += b + assert len(a.Find("Date")) > 0 diff --git a/test/test_2tabular.py b/test/test_3tabular.py similarity index 62% rename from test/test_2tabular.py rename to test/test_3tabular.py index 1f9697d..3ed6de2 100644 --- a/test/test_2tabular.py +++ b/test/test_3tabular.py @@ -1,7 +1,7 @@ import pytest import pandas as pd import pytabular as p -from test.config import testingtablename, testing_parameters, get_test_path, LOCAL_FILE +from test.config import testingtablename, testing_parameters, get_test_path @pytest.mark.parametrize("model", testing_parameters) @@ -74,48 +74,75 @@ def test_nonetype_decimal_bug(model): @pytest.mark.parametrize("model", testing_parameters) def test_Table_Last_Refresh_Times(model): """Really just testing the the function completes successfully and returns df""" - assert isinstance(model.Tables.Last_Refresh(), pd.DataFrame) is True + assert isinstance(model.Tables.Last_Refresh(), pd.DataFrame) @pytest.mark.parametrize("model", testing_parameters) def test_Return_Zero_Row_Tables(model): """Testing that `Return_Zero_Row_Tables`""" - assert isinstance(model.Tables.Find_Zero_Rows(), p.pytabular.PyTables) is True + assert isinstance(model.Tables.Find_Zero_Rows(), p.pytabular.PyTables) @pytest.mark.parametrize("model", testing_parameters) def test_get_dependencies(model): - if len(model.Measures) > 0: - dependencies = model.Measures[0].get_dependencies() - assert len(dependencies) > 0 - else: - assert True + dependencies = model.Measures[0].get_dependencies() + assert len(dependencies) > 0 @pytest.mark.parametrize("model", testing_parameters) -def test_get_sample_values(model): - if LOCAL_FILE[0] == "AdventureWorks Sales": - df = model.Columns["Country"].get_sample_values() - assert len(df) > 0 - else: - assert True +def test_disconnect(model): + """Tests `Disconnect()` from `Tabular` class.""" + model.Disconnect() + assert model.Server.Connected is False @pytest.mark.parametrize("model", testing_parameters) -def test_query_every_table(model): - assert len(model.Tables.Query_All()) > 0 +def test_reconnect(model): + """Tests `Reconnect()` from `Tabular` class.""" + model.Reconnect() + assert model.Server.Connected is True @pytest.mark.parametrize("model", testing_parameters) -def test_query_every_column(model): - assert len(model.Tables[0].Columns.Query_All()) > 0 +def test_reconnect_savechanges(model): + """This will test the `Reconnect()` gets called in `SaveChanges()`""" + model.Disconnect() + model.SaveChanges() + assert model.Server.Connected is True @pytest.mark.parametrize("model", testing_parameters) -def test_query_every_table_deprecate(model): - assert len(model.Query_Every_Table()) > 0 +def test_is_process(model): + """Checks that `Is_Process()` from `Tabular` class returns bool""" + assert isinstance(model.Is_Process(), bool) @pytest.mark.parametrize("model", testing_parameters) -def test_query_every_column_deprecate(model): - assert len(model.Query_Every_Column()) > 0 +def test_bad_table(model): + """Checks for unable to find table exception""" + with pytest.raises(Exception): + model.Refresh("badtablename") + + +@pytest.mark.parametrize("model", testing_parameters) +def test_refresh_dict(model): + """Checks for refreshing dictionary""" + table = model.Tables[testingtablename] + refresh = model.Refresh({table.Name: table.Partitions[0].Name}) + assert isinstance(refresh, pd.DataFrame) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_refresh_dict_pypartition(model): + """Checks for refreshing dictionary""" + table = model.Tables[testingtablename] + refresh = model.Refresh({table.Name: table.Partitions[0]}) + assert isinstance(refresh, pd.DataFrame) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_bad_partition(model): + """Checks for refreshing dictionary""" + table = model.Tables[testingtablename] + with pytest.raises(Exception): + model.Refresh({table.Name: table.Partitions[0].Name + "fail"}) diff --git a/test/test_4bpa.py b/test/test_4bpa.py deleted file mode 100644 index 8e33c62..0000000 --- a/test/test_4bpa.py +++ /dev/null @@ -1,10 +0,0 @@ -import pytest -import pytabular as p -from test.config import testing_parameters - - -@pytest.mark.parametrize("model", testing_parameters) -def test_bpa(model): - te2 = p.Tabular_Editor().EXE - bpa = p.BPA().Location - assert isinstance(model.Analyze_BPA(te2, bpa), list) diff --git a/test/test_5column.py b/test/test_5column.py new file mode 100644 index 0000000..b708149 --- /dev/null +++ b/test/test_5column.py @@ -0,0 +1,40 @@ +"""pytest for the column.py file. Covers the PyColumn and PyColumns classes. +""" +from test.config import testing_parameters, testingtablename +import pytest +import pandas as pd +from numpy import int64 + + +@pytest.mark.parametrize("model", testing_parameters) +def test_values(model): + """Tests for `Values()` of PyColumn class.""" + vals = model.Tables[testingtablename].Columns[1].Values() + assert isinstance(vals, pd.DataFrame) or isinstance(vals, int) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_distinct_count_no_blank(model): + """Tests No_Blank=True for `Distinct_Count()` of PyColumn class.""" + vals = model.Tables[testingtablename].Columns[1].Distinct_Count(No_Blank=True) + assert isinstance(vals, int64) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_distinct_count_blank(model): + """Tests No_Blank=False for `Distinct_Count()` of PyColumn class.""" + vals = model.Tables[testingtablename].Columns[1].Distinct_Count(No_Blank=False) + assert isinstance(vals, int64) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_get_sample_values(model): + """Tests for `get_sample_values()` of PyColumn class.""" + sample_vals = model.Tables[testingtablename].Columns[1].get_sample_values() + assert isinstance(sample_vals, pd.DataFrame) or isinstance(sample_vals, int) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_query_every_column(model): + """Tests `Query_All()` of PyColumns class.""" + assert isinstance(model.Tables[testingtablename].Columns.Query_All(), pd.DataFrame) diff --git a/test/test_6table.py b/test/test_6table.py new file mode 100644 index 0000000..4b49f06 --- /dev/null +++ b/test/test_6table.py @@ -0,0 +1,60 @@ +"""pytest for the table.py file. Covers the PyTable and PyTables classes. +""" +from test.config import testing_parameters, testingtablename +import pytest +import pandas as pd +from datetime import datetime + + +@pytest.mark.parametrize("model", testing_parameters) +def test_row_count(model): + """Tests for `Row_Count()` of PyTable class.""" + assert model.Tables[testingtablename].Row_Count() > 0 + + +@pytest.mark.parametrize("model", testing_parameters) +def test_refresh(model): + """Tests for `Refresh()` of PyTable class.""" + assert isinstance(model.Tables[testingtablename].Refresh(), pd.DataFrame) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_last_refresh(model): + """Tests for `Last_Refresh()` of PyTable class.""" + assert isinstance(model.Tables[testingtablename].Last_Refresh(), datetime) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_related(model): + """Tests for `Related()` of PyTable class.""" + assert isinstance(model.Tables[testingtablename].Related(), type(model.Tables)) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_refresh_pytables(model): + """Tests for `Refresh()` of PyTables class.""" + assert isinstance(model.Tables.Find(testingtablename).Refresh(), pd.DataFrame) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_query_all_pytables(model): + """Tests for `Query_All()` of PyTables class.""" + assert isinstance(model.Tables.Query_All(), pd.DataFrame) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_find_zero_rows_pytables(model): + """Tests for `Find_Zero_Rows()` of PyTables class.""" + assert isinstance(model.Tables.Find_Zero_Rows(), type(model.Tables)) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_last_refresh_pytables_true(model): + """Tests group_partition=True for `Last_Refresh()` of PyTables class.""" + assert isinstance(model.Tables.Last_Refresh(group_partition=True), pd.DataFrame) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_last_refresh_pytables_false(model): + """Tests group_partition=False for `Last_Refresh()` of PyTables class.""" + assert isinstance(model.Tables.Last_Refresh(group_partition=False), pd.DataFrame) diff --git a/test/test_7tabular_tracing.py b/test/test_7tabular_tracing.py new file mode 100644 index 0000000..a091b21 --- /dev/null +++ b/test/test_7tabular_tracing.py @@ -0,0 +1,45 @@ +"""pytest for the table.py file. Covers the PyTable and PyTables classes. +""" +from test.config import testing_parameters, testingtablename +import pytest +import pytabular as p +from test.conftest import testing_storage + + +@pytest.mark.parametrize("model", testing_parameters) +def test_disconnect_for_trace(model): + """Tests `Disconnect()` from `Tabular` class.""" + model.Disconnect() + assert model.Server.Connected is False + + +@pytest.mark.parametrize("model", testing_parameters) +def test_reconnect_update(model): + """This will test the `Reconnect()` gets called in `Update()` + of the `Base_Trace` class. + """ + model.Disconnect() + model.Tables[testingtablename].Refresh() + assert model.Server.Connected is True + + +@pytest.mark.parametrize("model", testing_parameters) +def test_query_monitor_start(model): + """This will test the `Query_Monitor` trace and `Start()` it.""" + query_trace = p.Query_Monitor(model) + query_trace.Start() + testing_storage.query_trace = query_trace + assert testing_storage.query_trace.Trace.IsStarted + + +@pytest.mark.parametrize("model", testing_parameters) +def test_query_monitor_stop(model): + """Tests `Stop()` of `Query_Monitor` trace.""" + testing_storage.query_trace.Stop() + assert testing_storage.query_trace.Trace.IsStarted is False + + +@pytest.mark.parametrize("model", testing_parameters) +def test_query_monitor_drop(model): + """Tests `Drop()` of `Query_Monitor` trace.""" + assert testing_storage.query_trace.Drop() is None diff --git a/test/test_8bpa.py b/test/test_8bpa.py new file mode 100644 index 0000000..6c77625 --- /dev/null +++ b/test/test_8bpa.py @@ -0,0 +1,21 @@ +import pytest +import pytabular as p +from test.config import testing_parameters +from os import getcwd + + +@pytest.mark.parametrize("model", testing_parameters) +def test_bpa(model): + te2 = p.Tabular_Editor().EXE + bpa = p.BPA().Location + assert isinstance(model.Analyze_BPA(te2, bpa), list) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_te2_custom_file_path(model): + assert isinstance(p.Tabular_Editor(getcwd()), p.Tabular_Editor) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_bpa_custom_file_path(model): + assert isinstance(p.BPA(getcwd()), p.BPA) diff --git a/test/test_3custom.py b/test/test_9custom.py similarity index 56% rename from test/test_3custom.py rename to test/test_9custom.py index 1f9ea74..b42453a 100644 --- a/test/test_3custom.py +++ b/test/test_9custom.py @@ -4,6 +4,8 @@ """ from test.config import testing_parameters, testingtablename import pytest +import pytabular as p +import pandas as pd @pytest.mark.parametrize("model", testing_parameters) @@ -22,7 +24,7 @@ def test_backingup_table(model): @pytest.mark.parametrize("model", testing_parameters) -def test_revert_table(model): +def test_revert_table2(model): model.Revert_Table(testingtablename) assert ( len( @@ -34,3 +36,23 @@ def test_revert_table(model): ) == 1 ) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_query_every_table_deprecate(model): + assert len(model.Query_Every_Table()) > 0 + + +@pytest.mark.parametrize("model", testing_parameters) +def test_query_every_column_deprecate(model): + assert len(model.Query_Every_Column()) > 0 + + +@pytest.mark.parametrize("model", testing_parameters) +def test_zero_row_tables_deprecate(model): + assert isinstance(p.Return_Zero_Row_Tables(model), list) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_last_refresh_times_deprecate(model): + assert isinstance(p.Table_Last_Refresh_Times(model), pd.DataFrame)