Skip to content

Commit 6c731a7

Browse files
rboni-dkclaude
andcommitted
test: add unit tests for utils, custom_types, clean_sql, date_service
Adds 97 new unit tests covering pure-logic modules that had zero coverage: - testgen/utils: to_int, str_to_timestamp, is_uuid4, try_json, make_json_safe, chunk_queries, score, friendly_score, to_dataframe, log_and_swallow_exception - common/models/custom_types: YNString, NullIfEmptyString, ZeroIfEmptyInteger, UpdateTimestamp, EncryptedBytea/EncryptedJson roundtrip - common/clean_sql: CleanSQL comment/whitespace normalization, concat_columns - common/date_service: as_iso_timestamp, get_now_as_iso_timestamp Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 48ad053 commit 6c731a7

4 files changed

Lines changed: 525 additions & 0 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
from datetime import UTC, datetime
2+
from unittest.mock import patch
3+
4+
import pytest
5+
6+
from testgen.common.models.custom_types import (
7+
EncryptedBytea,
8+
EncryptedJson,
9+
NullIfEmptyString,
10+
UpdateTimestamp,
11+
YNString,
12+
ZeroIfEmptyInteger,
13+
)
14+
15+
pytestmark = pytest.mark.unit
16+
17+
18+
# --- NullIfEmptyString ---
19+
20+
@pytest.mark.parametrize(
21+
"value, expected",
22+
[
23+
("", None),
24+
("hello", "hello"),
25+
(None, None),
26+
],
27+
)
28+
def test_null_if_empty_string(value, expected):
29+
t = NullIfEmptyString()
30+
assert t.process_bind_param(value, None) == expected
31+
32+
33+
# --- YNString ---
34+
35+
@pytest.mark.parametrize(
36+
"value, expected",
37+
[
38+
(True, "Y"),
39+
(False, "N"),
40+
("Y", "Y"),
41+
("N", "N"),
42+
(None, None),
43+
],
44+
)
45+
def test_yn_string_bind(value, expected):
46+
t = YNString()
47+
assert t.process_bind_param(value, None) == expected
48+
49+
50+
@pytest.mark.parametrize(
51+
"value, expected",
52+
[
53+
("Y", True),
54+
("N", False),
55+
(None, None),
56+
],
57+
)
58+
def test_yn_string_result(value, expected):
59+
t = YNString()
60+
assert t.process_result_value(value, None) == expected
61+
62+
63+
# --- ZeroIfEmptyInteger ---
64+
65+
@pytest.mark.parametrize(
66+
"value, expected",
67+
[
68+
(5, 5),
69+
(0, 0),
70+
("", 0),
71+
(None, 0),
72+
],
73+
)
74+
def test_zero_if_empty_integer(value, expected):
75+
t = ZeroIfEmptyInteger()
76+
assert t.process_bind_param(value, None) == expected
77+
78+
79+
# --- UpdateTimestamp ---
80+
81+
def test_update_timestamp():
82+
t = UpdateTimestamp()
83+
before = datetime.now(UTC)
84+
result = t.process_bind_param("ignored", None)
85+
after = datetime.now(UTC)
86+
assert before <= result <= after
87+
88+
89+
# --- EncryptedBytea roundtrip ---
90+
91+
@patch("testgen.common.encrypt.settings")
92+
def test_encrypted_bytea_roundtrip(mock_settings):
93+
mock_settings.APP_ENCRYPTION_SALT = "testsalt12345678"
94+
mock_settings.APP_ENCRYPTION_SECRET = "testsecret123456"
95+
96+
t = EncryptedBytea()
97+
original = "sensitive data"
98+
99+
encrypted = t.process_bind_param(original, None)
100+
assert encrypted != original.encode()
101+
102+
decrypted = t.process_result_value(encrypted, None)
103+
assert decrypted == original
104+
105+
106+
@patch("testgen.common.encrypt.settings")
107+
def test_encrypted_bytea_none(mock_settings):
108+
t = EncryptedBytea()
109+
assert t.process_bind_param(None, None) is None
110+
assert t.process_result_value(None, None) is None
111+
112+
113+
# --- EncryptedJson roundtrip ---
114+
115+
@patch("testgen.common.encrypt.settings")
116+
def test_encrypted_json_roundtrip(mock_settings):
117+
mock_settings.APP_ENCRYPTION_SALT = "testsalt12345678"
118+
mock_settings.APP_ENCRYPTION_SECRET = "testsecret123456"
119+
120+
t = EncryptedJson()
121+
original = {"key": "value", "num": 42, "list": [1, 2, 3]}
122+
123+
encrypted = t.process_bind_param(original, None)
124+
decrypted = t.process_result_value(encrypted, None)
125+
assert decrypted == original
126+
127+
128+
@patch("testgen.common.encrypt.settings")
129+
def test_encrypted_json_none(mock_settings):
130+
t = EncryptedJson()
131+
assert t.process_bind_param(None, None) is None
132+
assert t.process_result_value(None, None) is None
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import pytest
2+
3+
from testgen.common.clean_sql import CleanSQL, concat_columns
4+
5+
pytestmark = pytest.mark.unit
6+
7+
8+
# --- CleanSQL ---
9+
10+
def test_clean_sql_block_comments():
11+
assert CleanSQL("SELECT /* comment */ 1") == "SELECT 1"
12+
13+
14+
def test_clean_sql_multiline_block_comments():
15+
sql = """SELECT /*
16+
multi-line
17+
comment
18+
*/ 1"""
19+
assert CleanSQL(sql) == "SELECT 1"
20+
21+
22+
def test_clean_sql_line_comments():
23+
sql = "SELECT 1 -- this is a comment\nFROM t"
24+
assert CleanSQL(sql) == "SELECT 1 FROM t"
25+
26+
27+
def test_clean_sql_tabs_and_extra_spaces():
28+
sql = "SELECT\t 1\t\tFROM t"
29+
assert CleanSQL(sql) == "SELECT 1 FROM t"
30+
31+
32+
def test_clean_sql_preserves_quoted_strings():
33+
sql = "SELECT ' spaces ' FROM t"
34+
result = CleanSQL(sql)
35+
assert "' spaces '" in result
36+
37+
38+
def test_clean_sql_preserves_double_quoted_strings():
39+
sql = 'SELECT " col name " FROM t'
40+
result = CleanSQL(sql)
41+
assert '" col name "' in result
42+
43+
44+
def test_clean_sql_combined():
45+
sql = """
46+
SELECT /* get all */
47+
col1, col2
48+
FROM table1 -- main table
49+
WHERE col1 = 'hello world'
50+
"""
51+
result = CleanSQL(sql)
52+
assert "/* get all */" not in result
53+
assert "-- main table" not in result
54+
assert "'hello world'" in result
55+
assert "col1, col2" in result
56+
57+
58+
# --- concat_columns ---
59+
60+
def test_concat_columns_multiple():
61+
result = concat_columns("col1, col2, col3", "NULL")
62+
assert result == "CONCAT(COALESCE(col1, 'NULL'), COALESCE(col2, 'NULL'), COALESCE(col3, 'NULL'))"
63+
64+
65+
def test_concat_columns_single():
66+
assert concat_columns("col1", "NULL") == "col1"
67+
68+
69+
def test_concat_columns_empty():
70+
assert concat_columns("", "NULL") == ""
71+
72+
73+
def test_concat_columns_none():
74+
assert concat_columns(None, "NULL") == ""
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from datetime import UTC, datetime
2+
from unittest.mock import patch
3+
4+
import pytest
5+
6+
from testgen.common.date_service import as_iso_timestamp, get_now_as_iso_timestamp
7+
8+
pytestmark = pytest.mark.unit
9+
10+
11+
@pytest.mark.parametrize(
12+
"value, expected",
13+
[
14+
(datetime(2024, 3, 15, 10, 30, 45), "2024-03-15T10:30:45Z"),
15+
(datetime(2024, 1, 1, 0, 0, 0), "2024-01-01T00:00:00Z"),
16+
(None, None),
17+
],
18+
)
19+
def test_as_iso_timestamp(value, expected):
20+
assert as_iso_timestamp(value) == expected
21+
22+
23+
def test_get_now_as_iso_timestamp():
24+
with patch("testgen.common.date_service.datetime") as mock_dt:
25+
mock_dt.now.return_value = datetime(2024, 6, 15, 12, 0, 0, tzinfo=UTC)
26+
mock_dt.strftime = datetime.strftime
27+
result = get_now_as_iso_timestamp()
28+
29+
assert result == "2024-06-15T12:00:00Z"

0 commit comments

Comments
 (0)