icsDataValidation 1.0.430__py3-none-any.whl → 1.0.438__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- icsDataValidation/connection_setups/sqlserver_connection_setup.py +4 -3
- icsDataValidation/input_parameters/testing_tool_params.py +0 -1
- icsDataValidation/services/database_services/snowflake_service.py +170 -67
- icsDataValidation/services/database_services/sqlserver_service.py +196 -88
- {icsdatavalidation-1.0.430.dist-info → icsdatavalidation-1.0.438.dist-info}/METADATA +1 -1
- {icsdatavalidation-1.0.430.dist-info → icsdatavalidation-1.0.438.dist-info}/RECORD +22 -8
- {icsdatavalidation-1.0.430.dist-info → icsdatavalidation-1.0.438.dist-info}/WHEEL +1 -1
- {icsdatavalidation-1.0.430.dist-info → icsdatavalidation-1.0.438.dist-info}/top_level.txt +1 -0
- tests/snowflake_service/test_create_checksums.py +146 -0
- tests/snowflake_service/test_create_pandas_df_from_group_by.py +485 -0
- tests/snowflake_service/test_create_pandas_df_from_sample.py +444 -0
- tests/snowflake_service/test_get_checksum_statement.py +243 -0
- tests/snowflake_service/test_get_column_clause.py +305 -0
- tests/snowflake_service/test_get_countnulls_statement.py +128 -0
- tests/snowflake_service/test_get_in_clause.py +66 -0
- tests/sqlserver_service/test_create_checksums.py +153 -0
- tests/sqlserver_service/test_create_pandas_df_from_group_by.py +427 -0
- tests/sqlserver_service/test_create_pandas_df_from_sample.py +286 -0
- tests/sqlserver_service/test_get_checksum_statement.py +160 -0
- tests/sqlserver_service/test_get_column_clause.py +182 -0
- tests/sqlserver_service/test_get_countnulls_statement.py +121 -0
- tests/sqlserver_service/test_get_in_clause.py +87 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from unittest.mock import MagicMock, Mock
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from icsDataValidation.core.database_objects import DatabaseObject
|
|
6
|
+
from icsDataValidation.services.database_services.sqlserver_service import SQLServerService
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def sqlserver_service():
|
|
11
|
+
"""Create a SQLServerService instance with mocked connection."""
|
|
12
|
+
connection_params = {
|
|
13
|
+
'Driver': 'ODBC Driver 18 for SQL Server',
|
|
14
|
+
'Server': 'localhost',
|
|
15
|
+
'Port': '1433',
|
|
16
|
+
'Database': 'testdb',
|
|
17
|
+
'User': 'sa',
|
|
18
|
+
'Password': 'password',
|
|
19
|
+
'Encrypt': True,
|
|
20
|
+
'TrustServerCertificate': True
|
|
21
|
+
}
|
|
22
|
+
service = SQLServerService(connection_params=connection_params)
|
|
23
|
+
service.sqlserver_connection = MagicMock()
|
|
24
|
+
return service
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def mock_database_object():
|
|
29
|
+
"""Create a mock DatabaseObject."""
|
|
30
|
+
obj = Mock(spec=DatabaseObject)
|
|
31
|
+
obj.database = "TestDB"
|
|
32
|
+
obj.schema = "dbo"
|
|
33
|
+
obj.name = "TestTable"
|
|
34
|
+
obj.type = "table"
|
|
35
|
+
return obj
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TestGetCountnullsStatementParametrized:
|
|
39
|
+
"""Parametrized tests for _get_countnulls_statement method."""
|
|
40
|
+
|
|
41
|
+
@pytest.mark.parametrize(
|
|
42
|
+
"columns,exclude_columns,where_clause,expected_contains,expected_not_in",
|
|
43
|
+
[
|
|
44
|
+
( # single column
|
|
45
|
+
["Amount"],
|
|
46
|
+
[],
|
|
47
|
+
"",
|
|
48
|
+
["SUM(CASE WHEN [AMOUNT] IS NULL THEN 1 ELSE 0 END)", "AS [COUNTNULLS_AMOUNT]", "FROM dbo.TestTable"],
|
|
49
|
+
[]
|
|
50
|
+
),
|
|
51
|
+
( # multiple columns
|
|
52
|
+
["Amount", "Name", "IsActive"],
|
|
53
|
+
[],
|
|
54
|
+
"",
|
|
55
|
+
[
|
|
56
|
+
"SUM(CASE WHEN [AMOUNT] IS NULL THEN 1 ELSE 0 END)", "AS [COUNTNULLS_AMOUNT]",
|
|
57
|
+
"SUM(CASE WHEN [NAME] IS NULL THEN 1 ELSE 0 END)", "AS [COUNTNULLS_NAME]",
|
|
58
|
+
"SUM(CASE WHEN [ISACTIVE] IS NULL THEN 1 ELSE 0 END)", "AS [COUNTNULLS_ISACTIVE]"
|
|
59
|
+
],
|
|
60
|
+
[]
|
|
61
|
+
),
|
|
62
|
+
( # with where clause
|
|
63
|
+
["Amount"],
|
|
64
|
+
[],
|
|
65
|
+
"WHERE Amount > 100",
|
|
66
|
+
["SUM(CASE WHEN [AMOUNT] IS NULL THEN 1 ELSE 0 END)", "WHERE Amount > 100"],
|
|
67
|
+
[]
|
|
68
|
+
),
|
|
69
|
+
( # with exclude columns
|
|
70
|
+
["Amount", "Price", "Quantity"],
|
|
71
|
+
["Price"],
|
|
72
|
+
"",
|
|
73
|
+
["AMOUNT", "QUANTITY"],
|
|
74
|
+
["PRICE"]
|
|
75
|
+
),
|
|
76
|
+
( # special characters in column names
|
|
77
|
+
["/ISDFPS/OBJNR", "MANDT"],
|
|
78
|
+
[],
|
|
79
|
+
"",
|
|
80
|
+
["[/ISDFPS/OBJNR]", "AS [COUNTNULLS_/ISDFPS/OBJNR]", "[MANDT]", "AS [COUNTNULLS_MANDT]"],
|
|
81
|
+
[]
|
|
82
|
+
),
|
|
83
|
+
( # empty columns
|
|
84
|
+
[],
|
|
85
|
+
[],
|
|
86
|
+
"",
|
|
87
|
+
["SELECT", "FROM dbo.TestTable"],
|
|
88
|
+
["COUNTNULLS"]
|
|
89
|
+
),
|
|
90
|
+
( # all columns excluded
|
|
91
|
+
["Amount", "Price"],
|
|
92
|
+
["Amount", "Price"],
|
|
93
|
+
"",
|
|
94
|
+
["SELECT", "FROM dbo.TestTable"],
|
|
95
|
+
["COUNTNULLS"]
|
|
96
|
+
),
|
|
97
|
+
( # case insensitive columns
|
|
98
|
+
["amount", "Name", "QUANTITY"],
|
|
99
|
+
[],
|
|
100
|
+
"",
|
|
101
|
+
["[AMOUNT]", "[NAME]", "[QUANTITY]"],
|
|
102
|
+
[]
|
|
103
|
+
),
|
|
104
|
+
],
|
|
105
|
+
)
|
|
106
|
+
def test_get_countnulls_statement(
|
|
107
|
+
self, sqlserver_service, mock_database_object,
|
|
108
|
+
columns, exclude_columns, where_clause, expected_contains, expected_not_in
|
|
109
|
+
):
|
|
110
|
+
"""Test countnulls statement with various configurations."""
|
|
111
|
+
result = sqlserver_service._get_countnulls_statement(
|
|
112
|
+
object=mock_database_object,
|
|
113
|
+
column_intersections=columns,
|
|
114
|
+
exclude_columns=exclude_columns,
|
|
115
|
+
where_clause=where_clause
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
for expected in expected_contains:
|
|
119
|
+
assert expected in result
|
|
120
|
+
for expected in expected_not_in:
|
|
121
|
+
assert expected not in result
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from icsDataValidation.services.database_services.sqlserver_service import SQLServerService
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestInClauseVariations:
|
|
7
|
+
"""Test various IN clause generation scenarios using parametrization."""
|
|
8
|
+
|
|
9
|
+
@pytest.mark.parametrize(
|
|
10
|
+
"key_filters,numeric_columns,numeric_scale,expected_contains",
|
|
11
|
+
[
|
|
12
|
+
( # basic single row
|
|
13
|
+
{"col1": ["value1"], "col2": ["value2"]},
|
|
14
|
+
[],
|
|
15
|
+
2,
|
|
16
|
+
[" AND (CONCAT(", "CONCAT([col1], '|' ,[col2], '|')", "('value1|value2|')"],
|
|
17
|
+
),
|
|
18
|
+
( # basic single row with one column
|
|
19
|
+
{"col1": ["value1"]},
|
|
20
|
+
[],
|
|
21
|
+
2,
|
|
22
|
+
[" AND (CONCAT(", "CONCAT([col1], '|')", "('value1|')"],
|
|
23
|
+
),
|
|
24
|
+
( # multiple rows
|
|
25
|
+
{"id": [1, 2, 3], "name": ["a", "b", "c"]},
|
|
26
|
+
[],
|
|
27
|
+
2,
|
|
28
|
+
["(CONCAT([id], '|' ,[name], '|')", "('1|a|','2|b|','3|c|')"],
|
|
29
|
+
),
|
|
30
|
+
( # numeric columns with rounding
|
|
31
|
+
{"price": [10.5, 20.3], "quantity": [5, 10]},
|
|
32
|
+
["price"],
|
|
33
|
+
2,
|
|
34
|
+
["ROUND([price], 2)", "numeric(38, 2)", "quantity", "('10.5|5|','20.3|10|')"],
|
|
35
|
+
),
|
|
36
|
+
( # quotes in column names (should be removed)
|
|
37
|
+
{"'col1'": ["value1"], "'col2'": ["value2"]},
|
|
38
|
+
[],
|
|
39
|
+
2,
|
|
40
|
+
["col1", "col2"],
|
|
41
|
+
),
|
|
42
|
+
( # empty filters
|
|
43
|
+
{},
|
|
44
|
+
[],
|
|
45
|
+
2,
|
|
46
|
+
[""],
|
|
47
|
+
),
|
|
48
|
+
( # numeric with specific scale
|
|
49
|
+
{"Price": [100.123, 200.456], "Count": [1, 2]},
|
|
50
|
+
["Price"],
|
|
51
|
+
3,
|
|
52
|
+
["ROUND([Price], 3)", "numeric(38, 3)", "Count"],
|
|
53
|
+
),
|
|
54
|
+
( # all numeric columns
|
|
55
|
+
{"col1": [1.1, 2.2], "col2": [3.3, 4.4]},
|
|
56
|
+
["col1", "col2"],
|
|
57
|
+
2,
|
|
58
|
+
["ROUND([col1], 2)", "ROUND([col2], 2)", "numeric(38, 2)"],
|
|
59
|
+
),
|
|
60
|
+
( # mixed data types
|
|
61
|
+
{"amount": [100, 200], "category": ["A", "B"], "quantity": [10, 20]},
|
|
62
|
+
["amount", "quantity"],
|
|
63
|
+
0,
|
|
64
|
+
["ROUND([amount], 0)", "ROUND([quantity], 0)", "category"],
|
|
65
|
+
),
|
|
66
|
+
( # values order check
|
|
67
|
+
{"id": [1, 2], "name": ["alice", "bob"]},
|
|
68
|
+
[],
|
|
69
|
+
2,
|
|
70
|
+
["('1|alice|','2|bob|')"],
|
|
71
|
+
),
|
|
72
|
+
( # special character column names
|
|
73
|
+
{"/ABC": ["value1", "value2"], "Normal": ["val1", "val2"]},
|
|
74
|
+
[],
|
|
75
|
+
2,
|
|
76
|
+
["[/ABC]", "[Normal]", "CONCAT([/ABC], '|' ,[Normal], '|')"],
|
|
77
|
+
),
|
|
78
|
+
],
|
|
79
|
+
)
|
|
80
|
+
def test_in_clause_contains(
|
|
81
|
+
self, key_filters, numeric_columns, numeric_scale, expected_contains
|
|
82
|
+
):
|
|
83
|
+
"""Test that result contains expected substrings."""
|
|
84
|
+
result = SQLServerService._get_in_clause(key_filters, numeric_columns, numeric_scale)
|
|
85
|
+
|
|
86
|
+
for expected in expected_contains:
|
|
87
|
+
assert expected in result
|