Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e05e58a
Merge branch 'main' into 'enterprise'
aarthy-dk Oct 13, 2025
e9c196d
fix(db-data-type): handle null values
aarthy-dk Oct 15, 2025
10e0d86
fix(runs): empty state css
aarthy-dk Oct 15, 2025
556e6a2
misc(docker): upgrade base image
aarthy-dk Oct 15, 2025
4386690
Merge branch 'aarthy/fixes' into 'enterprise'
Oct 15, 2025
f5f0673
feat(profiling): add progress, approx row counts and error handling
aarthy-dk Oct 3, 2025
2e0bc64
fix: address pr feedback
aarthy-dk Oct 24, 2025
243353f
Merge branch 'aarthy/job-enhancements' into 'enterprise'
Oct 24, 2025
e91ff88
load score cards history in one go
luis-dk Oct 25, 2025
056024a
fix(scores): profiling rollup queries retrieval
luis-dk Oct 28, 2025
cbe83d0
Merge branch 'faster-scores-dashboard' into 'enterprise'
Oct 28, 2025
da60a0d
feat(sampling): Apply sampling to subqueries
rboni-dk Nov 3, 2025
f787961
Merge branch 'tg-962-subquery-sampling' into 'enterprise'
Nov 5, 2025
1d1f68f
feat(test-execution): add progress and error handling
aarthy-dk Oct 24, 2025
598f6d4
misc: self-review fixes
aarthy-dk Nov 11, 2025
bebfae5
fix(schedules): migrate test run schedules to use id
aarthy-dk Nov 11, 2025
9c20a6b
Merge branch 'aarthy/test-execution' into 'enterprise'
Nov 12, 2025
94526a0
fix(test-generation): handle pipes and newlines in LOV Match test
aarthy-dk Nov 7, 2025
1c1096e
fix(lookup): add templated limit counts to lookup queries
aarthy-dk Nov 10, 2025
73b31c0
Merge branch 'aarthy/lov-match' into 'enterprise'
Nov 12, 2025
2842a4c
feat(notifications): Adding basic email sending functionality
rboni-dk Nov 6, 2025
8b303aa
Merge branch 'tg-964-email-service' into 'enterprise'
Nov 12, 2025
5be70ce
feat(test-definitions): add sort dropdown
aarthy-dk Nov 12, 2025
c93d08b
fix(grid): initial selection not working
aarthy-dk Nov 12, 2025
950b9d2
Merge branch 'aarthy/test-sort' into 'enterprise'
Nov 20, 2025
d173535
fix(test): improve table freshness test to avoid overflow
aarthy-dk Nov 20, 2025
5c62432
Merge branch 'aarthy/table-freshness' into 'enterprise'
Nov 21, 2025
b2ce30d
feat(connections): connect to azure mssql using managed identities
luis-dk Nov 14, 2025
433177b
fix: address review comments
aarthy-dk Nov 21, 2025
964b174
Merge branch 'azure-passwordless-auth' into 'enterprise'
Nov 21, 2025
4c4fe4f
fix: update default max query chars
aarthy-dk Nov 24, 2025
8d90202
Merge branch 'aarthy/max-query' into 'enterprise'
Nov 24, 2025
458d77d
fix(ui): profiling dialog error
aarthy-dk Nov 25, 2025
0840123
Merge branch 'qa-fixes' into 'enterprise'
Nov 26, 2025
b1cb525
fix(test-results): error on refresh score
aarthy-dk Dec 2, 2025
a38786f
fix: error on download dialog
aarthy-dk Dec 2, 2025
57c8447
fix(profiling-runs): error on cancel and improve display
aarthy-dk Dec 2, 2025
6a17e3f
fix(test-runs): improve display for canceled runs
aarthy-dk Dec 2, 2025
808542f
fix(quality-dashboard): scorecard display and sorting
aarthy-dk Dec 2, 2025
9b54583
fix(test-execution): improve progress tooltip
aarthy-dk Dec 2, 2025
db4e585
Merge branch 'aarthy/qa' into 'enterprise'
Dec 2, 2025
006fc4f
release: 4.32.5 -> 4.38.3
aarthy-dk Dec 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions deploy/testgen-base.dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.12.7-alpine3.20
FROM python:3.12-alpine3.22

ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
Expand All @@ -14,7 +14,7 @@ RUN apk update && apk upgrade && apk add --no-cache \
cmake \
musl-dev \
gfortran \
linux-headers=6.6-r0 \
linux-headers=6.14.2-r0 \
# Tools needed for installing the MSSQL ODBC drivers \
curl \
gpg \
Expand All @@ -25,7 +25,7 @@ RUN apk update && apk upgrade && apk add --no-cache \
unixodbc=2.3.12-r0 \
unixodbc-dev=2.3.12-r0 \
# Pinned versions for security
xz=5.6.2-r1
xz=5.8.1-r0

RUN apk add --no-cache \
--repository https://dl-cdn.alpinelinux.org/alpine/v3.21/community \
Expand All @@ -48,7 +48,6 @@ RUN apk del \
cmake \
musl-dev \
gfortran \
curl \
gpg \
linux-headers \
openblas-dev \
Expand Down
2 changes: 1 addition & 1 deletion deploy/testgen.dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG TESTGEN_BASE_LABEL=v7
ARG TESTGEN_BASE_LABEL=v9

FROM datakitchen/dataops-testgen-base:${TESTGEN_BASE_LABEL} AS release-image

Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "dataops-testgen"
version = "4.32.5"
version = "4.38.3"
description = "DataKitchen's Data Quality DataOps TestGen"
authors = [
{ "name" = "DataKitchen, Inc.", "email" = "info@datakitchen.io" },
Expand Down Expand Up @@ -60,6 +60,8 @@ dependencies = [
"streamlit-pydantic==0.6.0",
"cron-converter==1.2.1",
"cron-descriptor==2.0.5",
"pybars3==0.9.7",
"azure-identity==1.25.1",

# Pinned to match the manually compiled libs or for security
"pyarrow==18.1.0",
Expand Down
69 changes: 39 additions & 30 deletions testgen/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
import subprocess
import sys
from dataclasses import dataclass, field
from datetime import UTC, datetime, timedelta

import click
from click.core import Context
from progress.spinner import MoonSpinner

from testgen import settings
from testgen.commands.run_execute_tests import run_execution_steps
from testgen.commands.run_generate_tests import run_test_gen_queries
from testgen.commands.run_get_entities import (
run_get_results,
Expand All @@ -29,8 +28,9 @@
)
from testgen.commands.run_launch_db_config import run_launch_db_config
from testgen.commands.run_observability_exporter import run_observability_exporter
from testgen.commands.run_profiling_bridge import run_profiling_queries
from testgen.commands.run_profiling import run_profiling
from testgen.commands.run_quick_start import run_quick_start, run_quick_start_increment
from testgen.commands.run_test_execution import run_test_execution
from testgen.commands.run_test_metadata_exporter import run_test_metadata_exporter
from testgen.commands.run_upgrade_db_config import get_schema_revision, is_db_revision_up_to_date, run_upgrade_db_config
from testgen.common import (
Expand All @@ -45,6 +45,7 @@
from testgen.common.models import with_database_session
from testgen.common.models.profiling_run import ProfilingRun
from testgen.common.models.test_run import TestRun
from testgen.common.models.test_suite import TestSuite
from testgen.scheduler import register_scheduler_job, run_scheduler
from testgen.utils import plugins

Expand Down Expand Up @@ -114,20 +115,16 @@ def cli(ctx: Context, verbose: bool):

@register_scheduler_job
@cli.command("run-profile", help="Generates a new profile of the table group.")
@pass_configuration
@click.option(
"-tg",
"--table-group-id",
required=True,
type=click.STRING,
help="The identifier for the table group used during a profile run. Use a table_group_id shown in list-table-groups.",
help="ID of the table group to profile. Use a table_group_id shown in list-table-groups.",
)
def run_profile(configuration: Configuration, table_group_id: str):
def run_profile(table_group_id: str):
click.echo(f"run-profile with table_group_id: {table_group_id}")
spinner = None
if not configuration.verbose:
spinner = MoonSpinner("Processing ... ")
message = run_profiling_queries(table_group_id, spinner=spinner)
message = run_profiling(table_group_id)
click.echo("\n" + message)


Expand Down Expand Up @@ -163,28 +160,40 @@ def run_test_generation(configuration: Configuration, table_group_id: str, test_

@register_scheduler_job
@cli.command("run-tests", help="Performs tests defined for a test suite.")
@click.option(
"-t",
"--test-suite-id",
required=False,
type=click.STRING,
help="ID of the test suite to run. Use a test_suite_id shown in list-test-suites.",
)
@click.option(
"-pk",
"--project-key",
help="The identifier for a TestGen project. Use a project_key shown in list-projects.",
help="DEPRECATED. Use --test-suite-id instead.",
required=False,
type=click.STRING,
default=settings.PROJECT_KEY,
)
@click.option(
"-ts",
"--test-suite-key",
help="The identifier for a test suite. Use a test_suite_key shown in list-test-suites.",
help="DEPRECATED. Use --test-suite-id instead.",
required=False,
default=settings.DEFAULT_TEST_SUITE_KEY,
)
@pass_configuration
def run_tests(configuration: Configuration, project_key: str, test_suite_key: str):
click.echo(f"run-tests for suite: {test_suite_key}")
spinner = None
if not configuration.verbose:
spinner = MoonSpinner("Processing ... ")
message = run_execution_steps(project_key, test_suite_key, spinner=spinner)
@with_database_session
def run_tests(test_suite_id: str | None = None, project_key: str | None = None, test_suite_key: str | None = None):
click.echo(f"run-tests for suite: {test_suite_id or test_suite_key}")
# For backward compatibility
if not test_suite_id:
test_suites = TestSuite.select_minimal_where(
TestSuite.project_code == project_key,
TestSuite.test_suite == test_suite_key,
)
if test_suites:
test_suite_id = test_suites[0].id
message = run_test_execution(test_suite_id)
click.echo("\n" + message)


Expand Down Expand Up @@ -370,27 +379,27 @@ def quick_start(

click.echo("loading initial data")
run_quick_start_increment(0)
minutes_offset = -30*24*60 # 1 month ago
table_group_id="0ea85e17-acbe-47fe-8394-9970725ad37d"
now_date = datetime.now(UTC)
time_delta = timedelta(days=-30) # 1 month ago
table_group_id = "0ea85e17-acbe-47fe-8394-9970725ad37d"
test_suite_id = "9df7489d-92b3-49f9-95ca-512160d7896f"

click.echo(f"run-profile with table_group_id: {table_group_id}")
spinner = None
if not configuration.verbose:
spinner = MoonSpinner("Processing ... ")
message = run_profiling_queries(table_group_id, spinner=spinner, minutes_offset=minutes_offset)
message = run_profiling(table_group_id, run_date=now_date + time_delta)
click.echo("\n" + message)

LOG.info(f"run-test-generation with table_group_id: {table_group_id} test_suite: {settings.DEFAULT_TEST_SUITE_KEY}")
message = run_test_gen_queries(table_group_id, settings.DEFAULT_TEST_SUITE_KEY)
click.echo("\n" + message)

run_execution_steps(settings.PROJECT_KEY, settings.DEFAULT_TEST_SUITE_KEY, minutes_offset=minutes_offset)
run_test_execution(test_suite_id, run_date=now_date + time_delta)

for iteration in range(1, 4):
click.echo(f"Running iteration: {iteration} / 3")
minutes_offset = -10*24*60 * (3-iteration)
total_iterations = 3
for iteration in range(1, total_iterations + 1):
click.echo(f"Running iteration: {iteration} / {total_iterations}")
run_date = now_date + timedelta(days=-10 * (total_iterations - iteration)) # 10 day increments
run_quick_start_increment(iteration)
run_execution_steps(settings.PROJECT_KEY, settings.DEFAULT_TEST_SUITE_KEY, minutes_offset=minutes_offset)
run_test_execution(test_suite_id, run_date=run_date)

click.echo("Quick start has successfully finished.")

Expand Down
51 changes: 51 additions & 0 deletions testgen/commands/queries/contingency_query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# UNUSED CODE - TO BE REVIVED LATER

import dataclasses
from uuid import UUID

from testgen.common import read_template_sql_file
from testgen.common.database.database_service import quote_csv_items, replace_params


@dataclasses.dataclass
class ContingencyTable:
schema_name: str
table_name: str
contingency_columns: str


class ContingencySQL:

contingency_max_values = 6

def _get_query(
self,
template_file_name: str,
sub_directory: str | None = "contingency",
params: dict | None = None,
) -> tuple[str | None, dict]:
query = read_template_sql_file(template_file_name, sub_directory)
query = replace_params(query, params or {})

return query, params

def get_contingency_columns(self, profiling_run_id: UUID) -> tuple[str, dict]:
# Runs on App database
return self._get_query(
"contingency_columns.sql",
params={
"PROFILE_RUN_ID": profiling_run_id,
"CONTINGENCY_MAX_VALUES": self.contingency_max_values,
},
)

def get_contingency_counts(self, contingency_table: ContingencyTable) -> tuple[str, dict]:
# Runs on Target database
return self._get_query(
"contingency_counts.sql",
params={
"DATA_SCHEMA": contingency_table.schema_name,
"DATA_TABLE": contingency_table.table_name,
"CONTINGENCY_COLUMNS": quote_csv_items(contingency_table.contingency_columns),
},
)
123 changes: 0 additions & 123 deletions testgen/commands/queries/execute_cat_tests_query.py

This file was deleted.

Loading