Skip to content

Commit 4b54a4d

Browse files
Merge pull request #54 from DataKitchen/release/4.38.6
Release/4.38.6
2 parents bf67a4a + 94693f7 commit 4b54a4d

25 files changed

+346
-110
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
88

99
[project]
1010
name = "dataops-testgen"
11-
version = "4.38.3"
11+
version = "4.38.6"
1212
description = "DataKitchen's Data Quality DataOps TestGen"
1313
authors = [
1414
{ "name" = "DataKitchen, Inc.", "email" = "[email protected]" },

testgen/__main__.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ def quick_start(
385385
test_suite_id = "9df7489d-92b3-49f9-95ca-512160d7896f"
386386

387387
click.echo(f"run-profile with table_group_id: {table_group_id}")
388-
message = run_profiling(table_group_id, run_date=now_date + time_delta)
388+
message = run_profiling(table_group_id, run_date=now_date + time_delta)
389389
click.echo("\n" + message)
390390

391391
LOG.info(f"run-test-generation with table_group_id: {table_group_id} test_suite: {settings.DEFAULT_TEST_SUITE_KEY}")
@@ -640,8 +640,6 @@ def list_ui_plugins():
640640
def run_ui():
641641
from testgen.ui.scripts import patch_streamlit
642642

643-
status_code: int = -1
644-
645643
use_ssl = os.path.isfile(settings.SSL_CERT_FILE) and os.path.isfile(settings.SSL_KEY_FILE)
646644

647645
patch_streamlit.patch(force=True)
@@ -656,25 +654,29 @@ def cancel_all_running():
656654

657655
cancel_all_running()
658656

659-
try:
660-
app_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui/app.py")
661-
status_code = subprocess.check_call(
662-
[ # noqa: S607
663-
"streamlit",
664-
"run",
665-
app_file,
666-
"--browser.gatherUsageStats=false",
667-
"--client.showErrorDetails=none",
668-
"--client.toolbarMode=minimal",
669-
f"--server.sslCertFile={settings.SSL_CERT_FILE}" if use_ssl else "",
670-
f"--server.sslKeyFile={settings.SSL_KEY_FILE}" if use_ssl else "",
671-
"--",
672-
f"{'--debug' if settings.IS_DEBUG else ''}",
673-
],
674-
env={**os.environ, "TG_JOB_SOURCE": "UI"}
675-
)
676-
except Exception:
677-
LOG.exception(f"Testgen UI exited with status code {status_code}")
657+
app_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui/app.py")
658+
process= subprocess.Popen(
659+
[ # noqa: S607
660+
"streamlit",
661+
"run",
662+
app_file,
663+
"--browser.gatherUsageStats=false",
664+
"--client.showErrorDetails=none",
665+
"--client.toolbarMode=minimal",
666+
f"--server.sslCertFile={settings.SSL_CERT_FILE}" if use_ssl else "",
667+
f"--server.sslKeyFile={settings.SSL_KEY_FILE}" if use_ssl else "",
668+
"--",
669+
f"{'--debug' if settings.IS_DEBUG else ''}",
670+
],
671+
env={**os.environ, "TG_JOB_SOURCE": "UI"}
672+
)
673+
def term_ui(signum, _):
674+
LOG.info(f"Sending termination signal {signum} to Testgen UI")
675+
process.send_signal(signum)
676+
signal.signal(signal.SIGINT, term_ui)
677+
signal.signal(signal.SIGTERM, term_ui)
678+
status_code = process.wait()
679+
LOG.log(logging.ERROR if status_code != 0 else logging.INFO, f"Testgen UI exited with status code {status_code}")
678680

679681

680682
@cli.command("run-app", help="Runs TestGen's application modules")

testgen/commands/queries/execute_tests_query.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from testgen.common import read_template_sql_file
88
from testgen.common.clean_sql import concat_columns
9-
from testgen.common.database.database_service import get_flavor_service, replace_params
9+
from testgen.common.database.database_service import get_flavor_service, get_tg_schema, replace_params
1010
from testgen.common.models.connection import Connection
1111
from testgen.common.models.table_group import TableGroup
1212
from testgen.common.models.test_definition import TestRunType, TestScope
@@ -107,7 +107,7 @@ def _get_params(self, test_def: TestExecutionDef | None = None) -> dict:
107107
"TEST_SUITE_ID": self.test_run.test_suite_id,
108108
"TEST_RUN_ID": self.test_run.id,
109109
"RUN_DATE": self.run_date,
110-
"SQL_FLAVOR": self.flavor,
110+
"SQL_FLAVOR": self.flavor,
111111
"VARCHAR_TYPE": self.flavor_service.varchar_type,
112112
"QUOTE": quote,
113113
}
@@ -116,7 +116,9 @@ def _get_params(self, test_def: TestExecutionDef | None = None) -> dict:
116116
params.update({
117117
"TEST_TYPE": test_def.test_type,
118118
"TEST_DEFINITION_ID": test_def.id,
119+
"APP_SCHEMA_NAME": get_tg_schema(),
119120
"SCHEMA_NAME": test_def.schema_name,
121+
"TABLE_GROUPS_ID": self.table_group.id,
120122
"TABLE_NAME": test_def.table_name,
121123
"COLUMN_NAME": f"{quote}{test_def.column_name or ''}{quote}",
122124
"COLUMN_NAME_NO_QUOTES": test_def.column_name,
@@ -146,7 +148,7 @@ def _get_params(self, test_def: TestExecutionDef | None = None) -> dict:
146148
"MATCH_HAVING_CONDITION": f"HAVING {test_def.match_having_condition}" if test_def.match_having_condition else "",
147149
"CUSTOM_QUERY": test_def.custom_query,
148150
"COLUMN_TYPE": test_def.column_type,
149-
"INPUT_PARAMETERS": self._get_input_parameters(test_def),
151+
"INPUT_PARAMETERS": self._get_input_parameters(test_def),
150152
})
151153
return params
152154

@@ -169,11 +171,11 @@ def _get_query(
169171
query = query.replace(":", "\\:")
170172

171173
return query, None if no_bind else params
172-
174+
173175
def get_active_test_definitions(self) -> tuple[dict]:
174176
# Runs on App database
175177
return self._get_query("get_active_test_definitions.sql")
176-
178+
177179
def get_target_identifiers(self, schemas: Iterable[str]) -> tuple[str, dict]:
178180
# Runs on Target database
179181
filename = "get_target_identifiers.sql"
@@ -185,7 +187,7 @@ def get_target_identifiers(self, schemas: Iterable[str]) -> tuple[str, dict]:
185187
return self._get_query(filename, f"flavors/{self.connection.sql_flavor}/validate_tests", extra_params=params)
186188
except ModuleNotFoundError:
187189
return self._get_query(filename, "flavors/generic/validate_tests", extra_params=params)
188-
190+
189191
def get_test_errors(self, test_defs: list[TestExecutionDef]) -> list[list[UUID | str | datetime]]:
190192
return [
191193
[
@@ -205,15 +207,15 @@ def get_test_errors(self, test_defs: list[TestExecutionDef]) -> list[list[UUID |
205207
None, # No result_measure on errors
206208
] for td in test_defs if td.errors
207209
]
208-
210+
209211
def disable_invalid_test_definitions(self) -> tuple[str, dict]:
210212
# Runs on App database
211213
return self._get_query("disable_invalid_test_definitions.sql")
212-
214+
213215
def update_historic_thresholds(self) -> tuple[str, dict]:
214216
# Runs on App database
215217
return self._get_query("update_historic_thresholds.sql")
216-
218+
217219
def run_query_test(self, test_def: TestExecutionDef) -> tuple[str, dict]:
218220
# Runs on Target database
219221
folder = "generic" if test_def.template_name.endswith("_generic.sql") else self.flavor
@@ -225,7 +227,7 @@ def run_query_test(self, test_def: TestExecutionDef) -> tuple[str, dict]:
225227
extra_params={"DATA_SCHEMA": test_def.schema_name},
226228
test_def=test_def,
227229
)
228-
230+
229231
def aggregate_cat_tests(
230232
self,
231233
test_defs: list[TestExecutionDef],
@@ -265,7 +267,7 @@ def add_query(test_defs: list[TestExecutionDef]) -> str:
265267

266268
aggregate_queries.append((query, None))
267269
aggregate_test_defs.append(test_defs)
268-
270+
269271
if single:
270272
for td in test_defs:
271273
# Add separate query for each test
@@ -296,9 +298,9 @@ def add_query(test_defs: list[TestExecutionDef]) -> str:
296298
current_test_defs.append(td)
297299

298300
add_query(current_test_defs)
299-
301+
300302
return aggregate_queries, aggregate_test_defs
301-
303+
302304
def get_cat_test_results(
303305
self,
304306
aggregate_results: list[AggregateResult],
@@ -309,7 +311,7 @@ def get_cat_test_results(
309311
test_defs = aggregate_test_defs[result["query_index"]]
310312
result_measures = result["result_measures"].split("|")
311313
result_codes = result["result_codes"].split(",")
312-
314+
313315
for index, td in enumerate(test_defs):
314316
test_results.append([
315317
self.test_run.id,
@@ -329,7 +331,7 @@ def get_cat_test_results(
329331
])
330332

331333
return test_results
332-
334+
333335
def update_test_results(self) -> list[tuple[str, dict]]:
334336
# Runs on App database
335337
return [

testgen/commands/run_launch_db_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def _get_params_mapping() -> dict:
5353
"TABLE_GROUPS_NAME": settings.DEFAULT_TABLE_GROUPS_NAME,
5454
"TEST_SUITE": settings.DEFAULT_TEST_SUITE_KEY,
5555
"TEST_SUITE_DESCRIPTION": settings.DEFAULT_TEST_SUITE_DESCRIPTION,
56+
"MONITOR_TEST_SUITE": settings.DEFAULT_MONITOR_TEST_SUITE_KEY,
5657
"MAX_THREADS": settings.PROJECT_CONNECTION_MAX_THREADS,
5758
"MAX_QUERY_CHARS": settings.PROJECT_CONNECTION_MAX_QUERY_CHAR,
5859
"OBSERVABILITY_API_URL": settings.OBSERVABILITY_API_URL,

testgen/commands/run_test_execution.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def run_test_execution_in_background(test_suite_id: str | UUID):
5454
def run_test_execution(test_suite_id: str | UUID, username: str | None = None, run_date: datetime | None = None) -> str:
5555
if test_suite_id is None:
5656
raise ValueError("Test Suite ID was not specified")
57-
57+
5858
LOG.info(f"Starting test run for test suite {test_suite_id}")
5959
time_delta = (run_date - datetime.now(UTC)) if run_date else timedelta()
6060

@@ -112,9 +112,7 @@ def run_test_execution(test_suite_id: str | UUID, username: str | None = None, r
112112
"CAT": partial(_run_cat_tests, sql_generator),
113113
}
114114
# Run metadata tests last so that results for other tests are available to them
115-
# TODO: TURN ON WHEN ADDING METADATA TESTS
116-
# for run_type in ["QUERY", "CAT", "METADATA"]:
117-
for run_type in ["QUERY", "CAT"]:
115+
for run_type in ["QUERY", "CAT", "METADATA"]:
118116
if (run_test_defs := [td for td in valid_test_defs if td.run_type == run_type]):
119117
run_functions[run_type](run_test_defs)
120118
else:
@@ -198,7 +196,7 @@ def update_test_progress(progress: ThreadedProgress) -> None:
198196
LOG.info(f"Writing {run_type} test errors")
199197
for index, error in error_data.items():
200198
test_defs[index].errors.append(error)
201-
199+
202200
error_results = sql_generator.get_test_errors(test_defs)
203201
write_to_app_db(error_results, sql_generator.result_columns, sql_generator.test_results_table)
204202

@@ -219,7 +217,7 @@ def _run_cat_tests(sql_generator: TestExecutionSQL, test_defs: list[TestExecutio
219217
total_count = len(test_defs)
220218
LOG.info(f"Aggregating CAT tests: {total_count}")
221219
aggregate_queries, aggregate_test_defs = sql_generator.aggregate_cat_tests(test_defs)
222-
220+
223221
def update_aggegate_progress(progress: ThreadedProgress) -> None:
224222
processed_count = sum(len(aggregate_test_defs[index]) for index in progress["indexes"])
225223
test_run.set_progress(
@@ -251,7 +249,7 @@ def update_aggegate_progress(progress: ThreadedProgress) -> None:
251249
error_test_defs: list[TestExecutionDef] = []
252250
for index in aggregate_errors:
253251
error_test_defs.extend(aggregate_test_defs[index])
254-
252+
255253
single_queries, single_test_defs = sql_generator.aggregate_cat_tests(error_test_defs, single=True)
256254

257255
test_run.set_progress(
@@ -260,7 +258,7 @@ def update_aggegate_progress(progress: ThreadedProgress) -> None:
260258
error="Rerunning errored tests singly",
261259
)
262260
test_run.save()
263-
261+
264262
def update_single_progress(progress: ThreadedProgress) -> None:
265263
test_run.set_progress(
266264
"CAT",
@@ -293,7 +291,7 @@ def update_single_progress(progress: ThreadedProgress) -> None:
293291
td = single_test_defs[index][0]
294292
td.errors.append(error)
295293
error_test_defs.append(td)
296-
294+
297295
error_results = sql_generator.get_test_errors(error_test_defs)
298296
write_to_app_db(error_results, sql_generator.result_columns, sql_generator.test_results_table)
299297

testgen/common/models/test_run.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,7 @@ def init_progress(self) -> None:
275275
"validation": {"label": "Validating test definitions"},
276276
"QUERY": {"label": "Running query tests"},
277277
"CAT": {"label": "Running aggregated tests"},
278-
# TODO: TURN ON WHEN ADDING METADATA TESTS
279-
# "METADATA": {"label": "Running metadata tests"},
278+
"METADATA": {"label": "Running metadata tests"},
280279
}
281280
for key in self._progress:
282281
self._progress[key].update({"key": key, "status": "Pending"})

testgen/settings.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,14 @@
299299
defaults to: `default_suite_desc`
300300
"""
301301

302+
DEFAULT_MONITOR_TEST_SUITE_KEY: str = os.getenv("DEFAULT_MONITOR_TEST_SUITE_NAME", "default-monitor-suite-1")
303+
"""
304+
Key to be assgined to the auto generated monitoring test suite.
305+
306+
from env variable: `DEFAULT_MONITOR_TEST_SUITE_NAME`
307+
defaults to: `default-monitor-suite-1`
308+
"""
309+
302310
DEFAULT_PROFILING_TABLE_SET = os.getenv("DEFAULT_PROFILING_TABLE_SET", "")
303311
"""
304312
Comma separated list of specific table names to include when running

testgen/template/dbsetup/050_populate_new_schema_metadata.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ VALUES ('Monitor', 'Recency'),
2323
('Monitor', 'Daily_Record_Ct'),
2424
('Monitor', 'Monthly_Rec_Ct'),
2525
('Monitor', 'Weekly_Rec_Ct'),
26-
('Monitor', 'Table_Freshness');
26+
('Monitor', 'Table_Freshness'),
27+
('Monitor', 'Schema_Drift');
2728

2829

2930
TRUNCATE TABLE test_templates;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
test_types:
2+
id: '1512'
3+
test_type: Schema_Drift
4+
test_name_short: Schema Drift
5+
test_name_long: Table Schema Changed
6+
test_description: |-
7+
Checks whether table schema has changed
8+
except_message: |-
9+
Table schema has changed.
10+
measure_uom: Was Schema Change Detected
11+
measure_uom_description: null
12+
selection_criteria: |-
13+
TEMPLATE
14+
dq_score_prevalence_formula: null
15+
dq_score_risk_factor: null
16+
column_name_prompt: null
17+
column_name_help: null
18+
default_parm_columns: null
19+
default_parm_values: null
20+
default_parm_prompts: null
21+
default_parm_help: null
22+
default_severity: Warning
23+
run_type: METADATA
24+
test_scope: tablegroup
25+
dq_dimension: null
26+
health_dimension: null
27+
threshold_description: null
28+
result_visualization: binary_chart
29+
result_visualization_params: '{"legend":{"labels":{"0":"No Changes","1":"Changes"}}}'
30+
usage_notes: |-
31+
This test compares the current table column types with previous data, to check whether the table schema has changed. This test allows you to track any changes to the table structure.
32+
active: N
33+
cat_test_conditions: []
34+
target_data_lookups: []
35+
test_templates:
36+
- id: '2514'
37+
test_type: Schema_Drift
38+
sql_flavor: bigquery
39+
template_name: ex_schema_drift_generic.sql
40+
- id: '2414'
41+
test_type: Schema_Drift
42+
sql_flavor: databricks
43+
template_name: ex_schema_drift_generic.sql
44+
- id: '2214'
45+
test_type: Schema_Drift
46+
sql_flavor: mssql
47+
template_name: ex_schema_drift_generic.sql
48+
- id: '2314'
49+
test_type: Schema_Drift
50+
sql_flavor: postgresql
51+
template_name: ex_schema_drift_generic.sql
52+
- id: '2014'
53+
test_type: Schema_Drift
54+
sql_flavor: redshift
55+
template_name: ex_schema_drift_generic.sql
56+
- id: '2614'
57+
test_type: Schema_Drift
58+
sql_flavor: redshift_spectrum
59+
template_name: ex_schema_drift_generic.sql
60+
- id: '2114'
61+
test_type: Schema_Drift
62+
sql_flavor: snowflake
63+
template_name: ex_schema_drift_generic.sql

0 commit comments

Comments
 (0)