Skip to content

Commit 9356e32

Browse files
rboni-dkclaude
andcommitted
test: fill coverage gaps in existing unit tests
Adds 11 tests covering untested logic branches in existing test files: - _get_table_criteria: table_set-only, no-filters, all-three-combined - replace_templated_functions: no-template passthrough - test run notifications: warning-only trigger branch (elif path) - Job.get_triggering_times: every-5-min, hourly, timezone-aware cron - profiling notifications: URL encoding of likelihood, is_new flag passthrough Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6c731a7 commit 9356e32

9 files changed

Lines changed: 110 additions & 10 deletions

tests/unit/commands/queries/test_refresh_data_chars_query.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,51 @@ def test_include_empty_include_mask(mask):
5959
assert r"""AND (
6060
(c.table_name LIKE 'important%' ) OR (c.table_name LIKE '%useful[_]%' )
6161
)""" in query
62+
63+
64+
def test_table_set_only():
65+
connection = Connection(sql_flavor="postgresql")
66+
table_group = TableGroup(
67+
table_group_schema="test_schema",
68+
profiling_table_set="users, orders, products",
69+
profiling_include_mask="",
70+
profiling_exclude_mask="",
71+
)
72+
sql_generator = RefreshDataCharsSQL(connection, table_group)
73+
criteria = sql_generator._get_table_criteria()
74+
75+
assert "IN ('users','orders','products')" in criteria
76+
assert "LIKE" not in criteria
77+
78+
79+
@pytest.mark.parametrize("include", ("", None))
80+
@pytest.mark.parametrize("exclude", ("", None))
81+
def test_no_filters(include, exclude):
82+
connection = Connection(sql_flavor="postgresql")
83+
table_group = TableGroup(
84+
table_group_schema="test_schema",
85+
profiling_table_set="",
86+
profiling_include_mask=include,
87+
profiling_exclude_mask=exclude,
88+
)
89+
sql_generator = RefreshDataCharsSQL(connection, table_group)
90+
criteria = sql_generator._get_table_criteria()
91+
92+
assert criteria == ""
93+
94+
95+
def test_table_set_with_include_exclude():
96+
connection = Connection(sql_flavor="postgresql")
97+
table_group = TableGroup(
98+
table_group_schema="test_schema",
99+
profiling_table_set="users, orders",
100+
profiling_include_mask="important%",
101+
profiling_exclude_mask="temp%",
102+
)
103+
sql_generator = RefreshDataCharsSQL(connection, table_group)
104+
criteria = sql_generator._get_table_criteria()
105+
106+
assert "IN ('users','orders')" in criteria
107+
assert "LIKE 'important%'" in criteria
108+
assert "AND NOT" in criteria
109+
assert "LIKE 'temp%'" in criteria

tests/unit/common/models/test_custom_types.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def test_update_timestamp():
9191
@patch("testgen.common.encrypt.settings")
9292
def test_encrypted_bytea_roundtrip(mock_settings):
9393
mock_settings.APP_ENCRYPTION_SALT = "testsalt12345678"
94-
mock_settings.APP_ENCRYPTION_SECRET = "testsecret123456"
94+
mock_settings.APP_ENCRYPTION_SECRET = "testsecret123456" # noqa: S105
9595

9696
t = EncryptedBytea()
9797
original = "sensitive data"
@@ -115,7 +115,7 @@ def test_encrypted_bytea_none(mock_settings):
115115
@patch("testgen.common.encrypt.settings")
116116
def test_encrypted_json_roundtrip(mock_settings):
117117
mock_settings.APP_ENCRYPTION_SALT = "testsalt12345678"
118-
mock_settings.APP_ENCRYPTION_SECRET = "testsecret123456"
118+
mock_settings.APP_ENCRYPTION_SECRET = "testsecret123456" # noqa: S105
119119

120120
t = EncryptedJson()
121121
original = {"key": "value", "num": 42, "list": [1, 2, 3]}

tests/unit/common/notifications/test_monitor_run_notifications.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ def test_send_monitor_notifications_url_construction(
301301

302302
context = send_mock.call_args[0][1]
303303
assert context["view_in_testgen_url"] == (
304-
"http://tg-base-url/monitors?project_code=proj-abc&table_group_id=tg-123"
304+
"http://tg-base-url/monitors?project_code=proj-abc&table_group_id=tg-123&source=email"
305305
)
306306

307307
send_mock.reset_mock()
@@ -317,6 +317,6 @@ def test_send_monitor_notifications_url_construction(
317317

318318
context = send_mock.call_args[0][1]
319319
assert context["view_in_testgen_url"] == (
320-
"http://tg-base-url/monitors?project_code=proj-abc&table_group_id=tg-123&table_name_filter=users"
320+
"http://tg-base-url/monitors?project_code=proj-abc&table_group_id=tg-123&table_name_filter=users&source=email"
321321
)
322322
assert context["summary"]["table_name"] == "users"

tests/unit/common/notifications/test_profiling_run_notifications.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from itertools import count
22
from unittest.mock import ANY, Mock, call, patch
3+
from urllib.parse import quote
34

45
import pytest
56

@@ -132,8 +133,8 @@ def test_send_profiling_run_notification(
132133
{
133134
"profiling_run": {
134135
"id": "pr-id",
135-
"issues_url": "http://tg-base-url/profiling-runs:hygiene?run_id=pr-id",
136-
"results_url": "http://tg-base-url/profiling-runs:results?run_id=pr-id",
136+
"issues_url": "http://tg-base-url/profiling-runs:hygiene?run_id=pr-id&source=email",
137+
"results_url": "http://tg-base-url/profiling-runs:results?run_id=pr-id&source=email",
137138
"start_time": None,
138139
"end_time": None,
139140
"status": profiling_run_status,
@@ -164,3 +165,22 @@ def test_send_profiling_run_notification(
164165
assert all(s.get("label") is not None for s in summary)
165166
assert all(s.get("priority") in priorities for s in summary)
166167
assert all(s.get("url") is not None for s in summary)
168+
169+
# Verify priority-to-likelihood URL mapping and URL encoding
170+
expected_likelihoods = {
171+
"Definite": "Definite",
172+
"Likely": "Likely",
173+
"Possible": "Possible",
174+
"High": "Potential PII",
175+
"Moderate": "Potential PII",
176+
}
177+
for s in summary:
178+
expected_likelihood = expected_likelihoods[s["priority"]]
179+
assert f"likelihood={quote(expected_likelihood)}" in s["url"]
180+
181+
# Verify is_new flags are passed through
182+
all_issues = [issue for s in summary for issue in s["issues"]]
183+
if not has_prev_run:
184+
assert all(issue["is_new"] is True for issue in all_issues)
185+
elif new_issue_count == 0:
186+
assert all(issue["is_new"] is False for issue in all_issues)

tests/unit/common/notifications/test_score_drop_notifications.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def test_send_score_drop_notifications(
171171
{
172172
"project_name": "Test Project",
173173
"definition": score_definition,
174-
"scorecard_url": "http://tg-base-url/quality-dashboard:score-details?definition_id=sd-1",
174+
"scorecard_url": "http://tg-base-url/quality-dashboard:score-details?definition_id=sd-1&source=email",
175175
"diff": [
176176
{**expected_total_diff, "notify": total_triggers},
177177
{**expected_cde_diff, "notify": cde_triggers},

tests/unit/common/notifications/test_test_run_notifications.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ def select_summary_mock():
9999
),
100100
[
101101
("Complete", 0, 0, 0, {}, 0, 0, 0, ["always"]),
102+
("Complete", 0, 5, 0, {}, 0, 5, 0, ["always", "on_warnings"]),
102103
("Complete", 1, 1, 1, {}, 1, 1, 1, ["always", "on_failures", "on_warnings"]),
103104
("Complete", 50, 50, 50, {"failed": 2, "warning": 3}, 10, 5, 5, [
104105
"always", "on_failures", "on_warnings", "on_changes",
@@ -173,7 +174,7 @@ def test_send_test_run_notification(
173174

174175
expected_context = {
175176
"test_run": summary,
176-
"test_run_url": "http://tg-base-url/test-runs:results?run_id=tr-id",
177+
"test_run_url": "http://tg-base-url/test-runs:results?run_id=tr-id&source=email",
177178
"test_run_id": "tr-id",
178179
"test_result_summary": ANY,
179180
}

tests/unit/common/test_read_file.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,8 @@ def test_replace_templated_missing_arg(query):
3535
match="Templated function call missing required arguments: <%DATEDIFF_YEAR;'{COL_NAME}'::DATE%>",
3636
):
3737
replace_templated_functions(query, "postgresql")
38+
39+
40+
def test_replace_templated_functions_no_templates():
41+
plain_query = "SELECT col1, col2 FROM my_table WHERE id = 1"
42+
assert replace_templated_functions(plain_query, "postgresql") == plain_query

tests/unit/scheduler/test_scheduler_base.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,34 @@ def now_func():
5555
yield now_func
5656

5757

58+
def test_get_triggering_times_every_5_min():
59+
job = Job(cron_expr="*/5 * * * *", cron_tz="UTC", delayed_policy=DelayedPolicy.ALL)
60+
base = datetime(2025, 4, 15, 9, 0, 0, tzinfo=UTC)
61+
times = list(islice(job.get_triggering_times(base), 5))
62+
minutes = [t.minute for t in times]
63+
# cron_converter yields starting at base time, then increments
64+
assert minutes == [0, 5, 10, 15, 20]
65+
assert all(t.hour == 9 for t in times)
66+
67+
68+
def test_get_triggering_times_hourly():
69+
job = Job(cron_expr="0 * * * *", cron_tz="UTC", delayed_policy=DelayedPolicy.ALL)
70+
base = datetime(2025, 4, 15, 9, 30, 0, tzinfo=UTC)
71+
times = list(islice(job.get_triggering_times(base), 3))
72+
hours = [t.hour for t in times]
73+
assert hours == [10, 11, 12]
74+
assert all(t.minute == 0 for t in times)
75+
76+
77+
def test_get_triggering_times_timezone():
78+
job = Job(cron_expr="0 9 * * *", cron_tz="America/New_York", delayed_policy=DelayedPolicy.ALL)
79+
base = datetime(2025, 4, 15, 12, 0, 0, tzinfo=UTC) # 8 AM ET (EDT)
80+
times = list(islice(job.get_triggering_times(base), 2))
81+
# 9 AM ET = 13:00 UTC (during EDT)
82+
assert times[0].astimezone(UTC).hour == 13
83+
assert times[1].astimezone(UTC).hour == 13
84+
85+
5886
def test_getting_jobs_wont_crash(scheduler_instance, base_time):
5987
scheduler_instance.get_jobs.side_effect = Exception
6088
scheduler_instance.start(base_time)

tests/unit/test_utils.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22
from datetime import UTC, datetime
33
from decimal import Decimal
44
from enum import Enum
5-
from unittest.mock import patch
65
from uuid import UUID
76

8-
import pandas as pd
97
import pytest
108

119
from testgen.utils import (

0 commit comments

Comments
 (0)