Skip to content

Commit 745a709

Browse files
author
ci bot
committed
Merge branch 'monitor-test-suite-1' into 'enterprise'
feat(tests): generate a monitor suite for new table groups See merge request dkinternal/testgen/dataops-testgen!304
2 parents 1e4c453 + 97fb07a commit 745a709

16 files changed

Lines changed: 150 additions & 16 deletions

testgen/commands/run_execute_tests.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,12 @@ def run_execution_steps_in_background(project_code, test_suite):
125125
if settings.IS_DEBUG:
126126
LOG.info(msg + ". Running in debug mode (new thread instead of new process).")
127127
empty_cache()
128+
username = None
129+
if session.auth:
130+
username = session.auth.user_display
128131
background_thread = threading.Thread(
129132
target=run_execution_steps,
130-
args=(project_code, test_suite, session.auth.user_display),
133+
args=(project_code, test_suite, username),
131134
)
132135
background_thread.start()
133136
else:

testgen/commands/run_profiling_bridge.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import testgen.common.process_service as process_service
1111
from testgen import settings
1212
from testgen.commands.queries.profiling_query import CProfilingSQL
13+
from testgen.commands.run_execute_tests import run_execution_steps_in_background
14+
from testgen.commands.run_generate_tests import run_test_gen_queries
1315
from testgen.commands.run_refresh_score_cards_results import run_refresh_score_cards_results
1416
from testgen.common import (
1517
date_service,
@@ -25,6 +27,7 @@
2527
from testgen.common.mixpanel_service import MixpanelService
2628
from testgen.common.models import with_database_session
2729
from testgen.common.models.connection import Connection
30+
from testgen.common.models.test_suite import TestSuite
2831
from testgen.ui.session import session
2932

3033
LOG = logging.getLogger("testgen")
@@ -238,6 +241,9 @@ def run_profiling_queries(table_group_id: str, username: str | None = None, spin
238241
profiling_run_id = str(uuid.uuid4())
239242

240243
params = get_profiling_params(table_group_id)
244+
needs_monitor_tests_generated = (
245+
bool(params["monitor_test_suite_id"]) and not params["last_complete_profile_run_id"]
246+
)
241247

242248
LOG.info("CurrentStep: Initializing Query Generator")
243249
clsProfiling = CProfilingSQL(params["project_code"], connection.sql_flavor, minutes_offset=minutes_offset)
@@ -471,7 +477,24 @@ def run_profiling_queries(table_group_id: str, username: str | None = None, spin
471477
scoring_duration=(datetime.now(UTC) - end_time).total_seconds(),
472478
)
473479

480+
if needs_monitor_tests_generated:
481+
_generate_monitor_tests(params["project_code"], table_group_id, params["monitor_test_suite_id"])
482+
474483
return f"""
475484
Profiling completed {"with errors. Check log for details." if has_errors else "successfully."}
476485
Run ID: {profiling_run_id}
477486
"""
487+
488+
489+
@with_database_session
490+
def _generate_monitor_tests(project_code: str, table_group_id: str, test_suite_id: str) -> None:
491+
try:
492+
monitor_test_suite = TestSuite.get(test_suite_id)
493+
if not monitor_test_suite:
494+
LOG.info("Skipping test generation on missing monitor test suite")
495+
else:
496+
LOG.info("Generating monitor tests")
497+
run_test_gen_queries(table_group_id, monitor_test_suite.test_suite, "Monitor")
498+
run_execution_steps_in_background(project_code, monitor_test_suite.test_suite)
499+
except Exception:
500+
LOG.exception("Error generating monitor tests")

testgen/common/get_pipeline_parms.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class ProfilingParams(BaseParams):
2121
profile_sample_min_count: int
2222
profile_do_pair_rules: str
2323
profile_pair_rule_pct: int
24+
monitor_test_suite_id: str | None
25+
last_complete_profile_run_id: str | None
2426

2527

2628
class TestGenerationParams(BaseParams):

testgen/common/models/scheduler.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@
44
from uuid import UUID, uuid4
55

66
from cron_converter import Cron
7-
from sqlalchemy import Column, String, select
7+
from sqlalchemy import Column, String, func, select
88
from sqlalchemy.dialects import postgresql
99
from sqlalchemy.orm import InstrumentedAttribute
1010

1111
from testgen.common.models import Base, get_current_session
12+
from testgen.common.models.test_definition import TestDefinition
13+
from testgen.common.models.test_suite import TestSuite
14+
15+
RUN_TESTS_JOB_KEY = "run-tests"
16+
RUN_PROFILE_JOB_KEY = "run-profile"
1217

1318

1419
class JobSchedule(Base):
@@ -25,7 +30,23 @@ class JobSchedule(Base):
2530

2631
@classmethod
2732
def select_where(cls, *clauses, order_by: str | InstrumentedAttribute | None = None) -> Iterable[Self]:
28-
query = select(cls).where(*clauses).order_by(order_by)
33+
test_definitions_count = (
34+
select(cls.id)
35+
.join(TestSuite, TestSuite.test_suite == cls.kwargs["test_suite_key"].astext)
36+
.join(TestDefinition, TestDefinition.test_suite_id == TestSuite.id)
37+
.where(cls.key == RUN_TESTS_JOB_KEY)
38+
.group_by(cls.id, TestSuite.test_suite)
39+
.having(func.count(TestDefinition.id) > 0)
40+
.subquery()
41+
)
42+
test_runs_query = (
43+
select(cls)
44+
.join(test_definitions_count, test_definitions_count.c.id == cls.id)
45+
.where(*clauses)
46+
)
47+
non_test_runs_query = select(cls).where(cls.key != RUN_TESTS_JOB_KEY, *clauses)
48+
query = test_runs_query.union_all(non_test_runs_query).order_by(order_by)
49+
2950
return get_current_session().execute(query)
3051

3152
@classmethod

testgen/common/models/table_group.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from testgen.common.models import get_current_session
1212
from testgen.common.models.custom_types import NullIfEmptyString, YNString
1313
from testgen.common.models.entity import ENTITY_HASH_FUNCS, Entity, EntityMinimal
14+
from testgen.common.models.scheduler import RUN_TESTS_JOB_KEY, JobSchedule
1415
from testgen.common.models.scores import ScoreDefinition
1516
from testgen.common.models.test_suite import TestSuite
1617

@@ -52,6 +53,11 @@ class TableGroup(Entity):
5253
id: UUID = Column(postgresql.UUID(as_uuid=True), primary_key=True, default=uuid4)
5354
project_code: str = Column(String, ForeignKey("projects.project_code"))
5455
connection_id: int = Column(BigInteger, ForeignKey("connections.connection_id"))
56+
monitor_test_suite_id: UUID | None = Column(
57+
postgresql.UUID(as_uuid=True),
58+
ForeignKey("test_suites.id"),
59+
default=None,
60+
)
5561
table_groups_name: str = Column(String)
5662
table_group_schema: str = Column(String)
5763
profiling_table_set: str = Column(NullIfEmptyString)
@@ -260,7 +266,12 @@ def clear_cache(cls) -> bool:
260266
cls.select_minimal_where.clear()
261267
cls.select_summary.clear()
262268

263-
def save(self, add_scorecard_definition: bool = False) -> None:
269+
def save(
270+
self,
271+
add_scorecard_definition: bool = False,
272+
add_monitor_test_suite: bool = False,
273+
monitor_schedule_timezone: str = "UTC",
274+
) -> None:
264275
if self.id:
265276
values = {
266277
column.key: getattr(self, column.key, None)
@@ -273,7 +284,38 @@ def save(self, add_scorecard_definition: bool = False) -> None:
273284
db_session.commit()
274285
else:
275286
super().save()
287+
db_session = get_current_session()
288+
276289
if add_scorecard_definition:
277290
ScoreDefinition.from_table_group(self).save()
278291

292+
if add_monitor_test_suite:
293+
test_suite = TestSuite(
294+
project_code=self.project_code,
295+
test_suite=f"{self.table_groups_name} Monitor",
296+
connection_id=self.connection_id,
297+
table_groups_id=self.id,
298+
export_to_observability=False,
299+
dq_score_exclude=True,
300+
view_mode="Monitor",
301+
)
302+
test_suite.save()
303+
304+
schedule_job = JobSchedule(
305+
project_code=self.project_code,
306+
key=RUN_TESTS_JOB_KEY,
307+
cron_expr="0 * * * *",
308+
cron_tz=monitor_schedule_timezone,
309+
args=[],
310+
kwargs={"project_key": self.project_code, "test_suite_key": test_suite.test_suite},
311+
)
312+
db_session.add(schedule_job)
313+
314+
self.monitor_test_suite_id = test_suite.id
315+
db_session.execute(
316+
update(TableGroup)
317+
.where(TableGroup.id == self.id).values(monitor_test_suite_id=test_suite.id)
318+
)
319+
db_session.commit()
320+
279321
TableGroup.clear_cache()

testgen/common/models/test_suite.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class TestSuite(Entity):
6565
component_name: str = Column(NullIfEmptyString)
6666
last_complete_test_run_id: UUID = Column(postgresql.UUID(as_uuid=True))
6767
dq_score_exclude: bool = Column(Boolean, default=False)
68+
view_mode: str | None = Column(NullIfEmptyString, default=None)
6869

6970
_default_order_by = (asc(func.lower(test_suite)),)
7071
_minimal_columns = TestSuiteMinimal.__annotations__.keys()

testgen/scheduler/cli_scheduler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def get_jobs(self) -> Iterable[CliJob]:
4646
self.reload_timer.start()
4747

4848
jobs = {}
49-
for (job_model,) in JobSchedule.select_where():
49+
for job_model in JobSchedule.select_where():
5050
if job_model.key not in JOB_REGISTRY:
5151
LOG.error("Job '%s' scheduled but not registered", job_model.key)
5252
continue

testgen/template/dbsetup/030_initialize_new_schema_structure.sql

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,15 @@ CREATE TABLE connections (
8484
CREATE TABLE table_groups
8585
(
8686
id UUID DEFAULT gen_random_uuid()
87-
CONSTRAINT pk_tg_id
87+
CONSTRAINT pk_tg_id
8888
PRIMARY KEY,
8989
project_code VARCHAR(30)
90-
CONSTRAINT table_groups_projects_project_code_fk
91-
REFERENCES projects,
90+
CONSTRAINT table_groups_projects_project_code_fk
91+
REFERENCES projects,
9292
connection_id BIGINT
93-
CONSTRAINT table_groups_connections_connection_id_fk
94-
REFERENCES connections,
93+
CONSTRAINT table_groups_connections_connection_id_fk
94+
REFERENCES connections,
95+
monitor_test_suite_id UUID DEFAULT NULL,
9596
table_groups_name VARCHAR(100),
9697
table_group_schema VARCHAR(100),
9798
profiling_table_set VARCHAR(2000),
@@ -161,10 +162,14 @@ CREATE TABLE test_suites (
161162
component_name VARCHAR(100),
162163
last_complete_test_run_id UUID,
163164
dq_score_exclude BOOLEAN default FALSE,
165+
view_mode VARCHAR(20) DEFAULT NULL,
164166
CONSTRAINT test_suites_id_pk
165167
PRIMARY KEY (id)
166168
);
167169

170+
ALTER TABLE table_groups ADD CONSTRAINT table_groups_test_suites_monitor_test_suite_id_fk
171+
FOREIGN KEY (monitor_test_suite_id) REFERENCES test_suites ON DELETE SET NULL;
172+
168173
CREATE TABLE test_definitions (
169174
id UUID DEFAULT gen_random_uuid(),
170175
cat_test_id BIGINT GENERATED BY DEFAULT AS IDENTITY
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
SET SEARCH_PATH TO {SCHEMA_NAME};
2+
3+
ALTER TABLE test_suites
4+
ADD COLUMN view_mode VARCHAR(20) DEFAULT NULL;
5+
6+
ALTER TABLE table_groups
7+
ADD COLUMN monitor_test_suite_id UUID DEFAULT NULL;
8+
9+
ALTER TABLE table_groups ADD CONSTRAINT table_groups_test_suites_monitor_test_suite_id_fk
10+
FOREIGN KEY (monitor_test_suite_id) REFERENCES test_suites ON DELETE SET NULL;

testgen/template/parms/parms_profiling.sql

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
SELECT tg.project_code,
22
tg.id::VARCHAR(50) as table_groups_id,
33
tg.table_group_schema,
4+
tg.table_group_schema,
45
CASE
56
WHEN tg.profiling_table_set ILIKE '''%''' THEN tg.profiling_table_set
67
ELSE fn_format_csv_quotes(tg.profiling_table_set)
@@ -14,6 +15,14 @@ SELECT tg.project_code,
1415
tg.profile_sample_percent,
1516
tg.profile_sample_min_count,
1617
tg.profile_do_pair_rules,
17-
tg.profile_pair_rule_pct
18+
tg.profile_pair_rule_pct,
19+
CASE
20+
WHEN tg.monitor_test_suite_id IS NULL THEN NULL
21+
ELSE tg.monitor_test_suite_id::VARCHAR(50)
22+
END as monitor_test_suite_id,
23+
CASE
24+
WHEN tg.last_complete_profile_run_id is NULL THEN NULL
25+
ELSE tg.last_complete_profile_run_id::VARCHAR(50)
26+
END as last_complete_profile_run_id
1827
FROM table_groups tg
1928
WHERE tg.id = :TABLE_GROUP_ID;

0 commit comments

Comments
 (0)