Skip to content

Commit 5ef1537

Browse files
luis-dkclaude
andcommitted
:feat(test-definitions): faceted add-test picker dialog
Add a faceted picker to the Add-Test dialog so users browse and search test types by taxonomy (algorithm, statistical technique) and column type before advancing to the existing parameter form. Persists the taxonomy fields on test types and surfaces them, with column-type metadata, to the dialog. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 99a56a2 commit 5ef1537

61 files changed

Lines changed: 998 additions & 96 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

testgen/common/models/test_definition.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from sqlalchemy import (
1010
Boolean,
1111
Column,
12+
Enum,
1213
ForeignKey,
1314
String,
1415
Text,
@@ -40,6 +41,87 @@ class Severity(StrEnum):
4041
WARNING = "Warning"
4142

4243

44+
class TestAlgorithm(StrEnum):
45+
"""SQL-derived algorithm family for a test type (faceted picker axis)."""
46+
47+
BOUNDARY_CHECK = "Boundary check"
48+
COUNTING = "Counting"
49+
PATTERN_REGEX = "Pattern / regex"
50+
SET_LOOKUP = "Set / lookup"
51+
STATISTICAL_DRIFT = "Statistical drift"
52+
AGGREGATE_RECONCILIATION = "Aggregate reconciliation"
53+
FRESHNESS_TIME = "Freshness / time"
54+
SCHEMA_METADATA = "Schema / metadata"
55+
CUSTOM_SQL = "Custom SQL"
56+
57+
58+
class StatisticalTechnique(StrEnum):
59+
"""Named statistical technique a test type uses to evaluate its measure."""
60+
61+
COHENS_D = "Cohen's D"
62+
COHENS_H = "Cohen's H"
63+
OUTLIER_DETECTION = "Outlier Detection"
64+
SD_SHIFT = "SD Shift"
65+
JENSEN_SHANNON_DIVERGENCE = "Jensen-Shannon Divergence"
66+
PREDICTIVE_MODEL = "Predictive Model"
67+
68+
69+
class TestCriteria(StrEnum):
70+
"""What a test type needs to be set up (faceted picker axis).
71+
72+
Derived, not stored: ``derive_test_criteria`` is the single source of truth, shared by the
73+
UI lookup and MCP so the value never drifts between surfaces.
74+
"""
75+
76+
DEFINED_RULE = "Defined Rule"
77+
DEFINED_THRESHOLD = "Defined Threshold"
78+
DEFINED_VALUE = "Defined Value"
79+
LIST_OF_VALUES = "List of Values"
80+
REFERENCE_DATASET = "Reference Dataset"
81+
CUSTOM_CRITERIA = "Custom Criteria"
82+
83+
84+
# Predefined validity/integrity rules — the user just enables them; the rule itself is fixed
85+
# (dedup/uniqueness and standard format validators). Not separable from structural attributes
86+
# alone: e.g. Valid_Month is structurally identical to the Defined-Value test Pattern_Match
87+
# (both column-scoped, Pattern / regex, with baseline_value + threshold_value params).
88+
_DEFINED_RULE_TESTS = frozenset({
89+
"Dupe_Rows", "Unique", "Email_Format", "Street_Addr_Pattern",
90+
"Valid_Characters", "Valid_Month", "Valid_US_Zip", "Valid_US_Zip3",
91+
})
92+
# The user asserts the expected value/pattern the column should hold (a constant, a regex
93+
# baseline, or simply that a value is present). Enumerated for the same reason as above.
94+
_DEFINED_VALUE_TESTS = frozenset({
95+
"Constant", "Pattern_Match", "Required",
96+
})
97+
98+
99+
def derive_test_criteria(
100+
test_type: str,
101+
test_scope: str | None,
102+
algorithm: str | None,
103+
) -> TestCriteria:
104+
"""Classify a test type by the kind of setup it requires.
105+
106+
Single source of truth for the Criteria facet — call from both the UI lookup and MCP rather
107+
than reproducing the rules. Scope and algorithm resolve the cleanly-typed buckets (referential
108+
is checked before Set / lookup so Combo_Match stays Reference Dataset). Defined Rule, Defined
109+
Value, and Defined Threshold can't be told apart from structural attributes, so the first two
110+
are enumerated and Defined Threshold is the fallthrough.
111+
"""
112+
if test_scope == "custom":
113+
return TestCriteria.CUSTOM_CRITERIA
114+
if test_scope == "referential":
115+
return TestCriteria.REFERENCE_DATASET
116+
if algorithm == TestAlgorithm.SET_LOOKUP:
117+
return TestCriteria.LIST_OF_VALUES
118+
if test_type in _DEFINED_RULE_TESTS:
119+
return TestCriteria.DEFINED_RULE
120+
if test_type in _DEFINED_VALUE_TESTS:
121+
return TestCriteria.DEFINED_VALUE
122+
return TestCriteria.DEFINED_THRESHOLD
123+
124+
43125
class InvalidTestDefinitionFields(ValueError):
44126
"""Aggregated field-level validation errors. ``errors``: ``dict[field_name, reason]``."""
45127

@@ -175,6 +257,15 @@ def process_bind_param(self, value: str | None, _dialect) -> str | None:
175257
return value or None
176258

177259

260+
def _enum_by_value(enum_cls: type[StrEnum]) -> Enum:
261+
"""Map a StrEnum to its VARCHAR column by member value (not name) so reads return enum members.
262+
263+
These columns store the display value (e.g. ``"Boundary check"``), which differs from the enum
264+
member name, so the default name-based mapping would not round-trip.
265+
"""
266+
return Enum(enum_cls, native_enum=False, values_callable=lambda cls: [member.value for member in cls])
267+
268+
178269
class TestType(ParamFieldsMixin, Entity):
179270
__tablename__ = "test_types"
180271

@@ -204,12 +295,19 @@ class TestType(ParamFieldsMixin, Entity):
204295
dq_dimension: str = Column(String)
205296
impact_dimension: str = Column(String)
206297
health_dimension: str = Column(String)
298+
algorithm: TestAlgorithm | None = Column(_enum_by_value(TestAlgorithm))
299+
statistical_technique: StatisticalTechnique | None = Column(_enum_by_value(StatisticalTechnique))
207300
threshold_description: str = Column(String)
208301
usage_notes: str = Column(String)
209302
active: str = Column(String)
210303

211304
# Unmapped columns: generation_template, result_visualization, result_visualization_params
212305

306+
@property
307+
def criteria(self) -> TestCriteria:
308+
"""Setup-kind facet, derived via the shared classifier (see ``derive_test_criteria``)."""
309+
return derive_test_criteria(self.test_type, self.test_scope, self.algorithm)
310+
213311
_summary_columns = (
214312
*[key for key in TestTypeSummary.__annotations__.keys() if key not in ("default_test_description", "default_impact_dimension")],
215313
test_description.label("default_test_description"),

testgen/template/dbsetup/030_initialize_new_schema_structure.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,8 @@ CREATE TABLE test_types (
576576
dq_dimension VARCHAR(50),
577577
impact_dimension VARCHAR(20),
578578
health_dimension VARCHAR(50),
579+
algorithm VARCHAR(64),
580+
statistical_technique VARCHAR(64),
579581
threshold_description VARCHAR(200),
580582
result_visualization VARCHAR(50) DEFAULT 'line_chart',
581583
result_visualization_params TEXT DEFAULT NULL,

testgen/template/dbsetup_test_types/test_types_Aggregate_Balance.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ test_types:
3737
usage_notes: |-
3838
This test compares sums or counts of a column rolled up to one or more category combinations across two different tables. Both tables must be accessible at the same time. It's ideal for confirming that two datasets exactly match -- that the sum of a measure or count of a value hasn't changed or shifted between categories. Use this test to compare a raw and processed version of the same dataset, or to confirm that an aggregated table exactly matches the detail table that it's built from. An error here means that one or more value combinations fail to match. New categories or combinations will cause failure.
3939
active: Y
40+
algorithm: Aggregate reconciliation
4041
cat_test_conditions: []
4142
target_data_lookups:
4243
- id: '1400'

testgen/template/dbsetup_test_types/test_types_Aggregate_Balance_Percent.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ test_types:
3737
usage_notes: |-
3838
This test compares sums or counts of a column rolled up to one or more category combinations across two different tables. Both tables must be accessible at the same time. Use it to confirm that two datasets closely match within the tolerance you set -- that the sum of a measure or count of a value remains sufficiently consistent between categories. You could use this test compare sales per product within one month to another, when you want to be alerted if the difference for any product falls outside of the range defined as 5% below to 10% above the prior month. An error here means that one or more value combinations fail to match within the set tolerances. New categories or combinations will cause failure.
3939
active: Y
40+
algorithm: Aggregate reconciliation
4041
cat_test_conditions: []
4142
target_data_lookups:
4243
- id: '1404'

testgen/template/dbsetup_test_types/test_types_Aggregate_Balance_Range.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ test_types:
3737
usage_notes: |-
3838
This test compares sums or counts of a column rolled up to one or more category combinations across two different tables. Both tables must be accessible at the same time. Use it to confirm that two datasets closely match within the tolerances you define as specific values above or below the aggregate measure for the same categories in the reference dataset -- that the sum of a measure or count of a value remains sufficiently consistent between categories. For instance, you can use this test to compare sales per product within one month to another, when you want to be alerted if the difference for any product falls outside of the range defined as 10000 dollars above or below the prior week. An error here means that one or more value combinations fail to match within the set tolerances. New categories or combinations will cause failure.
3939
active: Y
40+
algorithm: Aggregate reconciliation
4041
cat_test_conditions: []
4142
target_data_lookups:
4243
- id: '1405'

testgen/template/dbsetup_test_types/test_types_Aggregate_Minimum.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ test_types:
3737
usage_notes: |-
3838
This test compares sums or counts of a column rolled up to one or more category combinations, but requires a match or increase in the aggregate value, rather than an exact match, across two different tables. Both tables must be accessible at the same time. Use this to confirm that aggregate values have not dropped for any set of categories, even if some values may rise. This test is useful to compare an older and newer version of a cumulative dataset. An error here means that one or more values per category set fail to match or exceed the prior dataset. New categories or combinations are allowed (but can be restricted independently with a Combo_Match test). Both tables must be present to run this test.
3939
active: Y
40+
algorithm: Aggregate reconciliation
4041
cat_test_conditions: []
4142
target_data_lookups:
4243
- id: '1401'

testgen/template/dbsetup_test_types/test_types_Alpha_Trunc.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ test_types:
3636
usage_notes: |-
3737
Alpha Truncation tests that the longest text value in a column hasn't become shorter than the defined threshold, initially 95% of the longest value at baseline. This could indicate a problem in a cumulative dataset, where prior values should still exist unchanged. A failure here would suggest that some process changed data that you would still expect to be present and matching its value when the column was profiled. This test would not be appropriate for an incremental or windowed dataset.
3838
active: Y
39+
algorithm: Boundary check
3940
cat_test_conditions:
4041
- id: '7001'
4142
test_type: Alpha_Trunc

testgen/template/dbsetup_test_types/test_types_Avg_Shift.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ test_types:
3737
usage_notes: |-
3838
Average Shift tests that the average of a numeric column has not significantly changed since baseline, when profiling was done. A significant shift may indicate errors in processing, differences in source data, or valid changes that may nevertheless impact assumptions in downstream data products. The test uses Cohen's D, a statistical technique to identify significant shifts in a value. Cohen's D measures the difference between the two averages, reporting results on a standardized scale, which can be interpreted via a rule-of-thumb from small to huge. Depending on your data, some difference may be expected, so it's reasonable to adjust the threshold value that triggers test failure. This test works well for measures, or even for identifiers if you expect them to increment consistently. You may want to periodically adjust the expected threshold, or even the expected average value if you expect shifting over time. Consider this test along with Variability Increase. If variability rises too, process or measurement flaws could be at work. If variability remains consistent, the issue is more likely to be with the source data itself.
3939
active: Y
40+
algorithm: Statistical drift
41+
statistical_technique: Cohen's D
4042
cat_test_conditions:
4143
- id: '7002'
4244
test_type: Avg_Shift

testgen/template/dbsetup_test_types/test_types_CUSTOM.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ test_types:
3838
usage_notes: |-
3939
This business-rule test is highly flexible, covering any error state that can be expressed by a SQL query against one or more tables in the database. In operation, the user-defined query is embedded within a parent query returning the count of error rows identified. Any row returned by the query is interpreted as a single error condition in the test. Note that this query is run independently of other tests, and that performance will be slower, depending in large part on the efficiency of the query you write. Interpretation is based on the user-defined meaning of the test. Your query might be written to return errors in individual rows identified by joining tables. Or it might return an error based on a multi-column aggregate condition returning a single row if an error is found. This query is run separately when you click `Review Source Data` from Test Results, so be sure to include enough data in your results to follow-up. Interpretation is based on the user-defined meaning of the test.
4040
active: Y
41+
algorithm: Custom SQL
4142
cat_test_conditions: []
4243
target_data_lookups: []
4344
test_templates:

testgen/template/dbsetup_test_types/test_types_Combo_Match.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ test_types:
3737
usage_notes: |-
3838
This test verifies that values, or combinations of values, that are present in the main table are also found in a reference table. This is a useful test for referential integrity between fact and dimension tables. You can also use it to confirm the validity of a code or category, or of combinations of values that should only be found together within each record, such as product/size/color. An error here means that one or more category combinations in the main table are not found in the reference table. Both tables must be present to run this test.
3939
active: Y
40+
algorithm: Set / lookup
4041
cat_test_conditions: []
4142
target_data_lookups:
4243
- id: '1402'

0 commit comments

Comments
 (0)