velocity-python 0.0.132__py3-none-any.whl → 0.0.135__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of velocity-python might be problematic. Click here for more details.
- velocity/__init__.py +1 -1
- velocity/app/tests/__init__.py +1 -0
- velocity/app/tests/test_email_processing.py +112 -0
- velocity/app/tests/test_payment_profile_sorting.py +191 -0
- velocity/app/tests/test_spreadsheet_functions.py +124 -0
- velocity/aws/tests/__init__.py +1 -0
- velocity/aws/tests/test_lambda_handler_json_serialization.py +120 -0
- velocity/aws/tests/test_response.py +163 -0
- velocity/db/core/decorators.py +20 -3
- velocity/db/core/engine.py +33 -7
- velocity/db/exceptions.py +7 -0
- velocity/db/servers/base/initializer.py +2 -1
- velocity/db/servers/mysql/__init__.py +13 -4
- velocity/db/servers/postgres/__init__.py +14 -4
- velocity/db/servers/sqlite/__init__.py +13 -4
- velocity/db/servers/sqlserver/__init__.py +13 -4
- velocity/db/tests/__init__.py +1 -0
- velocity/db/tests/common_db_test.py +0 -0
- velocity/db/tests/postgres/__init__.py +1 -0
- velocity/db/tests/postgres/common.py +49 -0
- velocity/db/tests/postgres/test_column.py +29 -0
- velocity/db/tests/postgres/test_connections.py +25 -0
- velocity/db/tests/postgres/test_database.py +21 -0
- velocity/db/tests/postgres/test_engine.py +205 -0
- velocity/db/tests/postgres/test_general_usage.py +88 -0
- velocity/db/tests/postgres/test_imports.py +8 -0
- velocity/db/tests/postgres/test_result.py +19 -0
- velocity/db/tests/postgres/test_row.py +137 -0
- velocity/db/tests/postgres/test_row_comprehensive.py +707 -0
- velocity/db/tests/postgres/test_schema_locking.py +335 -0
- velocity/db/tests/postgres/test_schema_locking_unit.py +115 -0
- velocity/db/tests/postgres/test_sequence.py +34 -0
- velocity/db/tests/postgres/test_sql_comprehensive.py +471 -0
- velocity/db/tests/postgres/test_table.py +101 -0
- velocity/db/tests/postgres/test_table_comprehensive.py +644 -0
- velocity/db/tests/postgres/test_transaction.py +106 -0
- velocity/db/tests/sql/__init__.py +1 -0
- velocity/db/tests/sql/common.py +177 -0
- velocity/db/tests/sql/test_postgres_select_advanced.py +285 -0
- velocity/db/tests/sql/test_postgres_select_variances.py +517 -0
- velocity/db/tests/test_cursor_rowcount_fix.py +150 -0
- velocity/db/tests/test_db_utils.py +221 -0
- velocity/db/tests/test_postgres.py +212 -0
- velocity/db/tests/test_postgres_unchanged.py +81 -0
- velocity/db/tests/test_process_error_robustness.py +292 -0
- velocity/db/tests/test_result_caching.py +279 -0
- velocity/db/tests/test_result_sql_aware.py +117 -0
- velocity/db/tests/test_row_get_missing_column.py +72 -0
- velocity/db/tests/test_schema_locking_initializers.py +226 -0
- velocity/db/tests/test_schema_locking_simple.py +97 -0
- velocity/db/tests/test_sql_builder.py +165 -0
- velocity/db/tests/test_tablehelper.py +486 -0
- velocity/misc/tests/__init__.py +1 -0
- velocity/misc/tests/test_db.py +90 -0
- velocity/misc/tests/test_fix.py +78 -0
- velocity/misc/tests/test_format.py +64 -0
- velocity/misc/tests/test_iconv.py +203 -0
- velocity/misc/tests/test_merge.py +82 -0
- velocity/misc/tests/test_oconv.py +144 -0
- velocity/misc/tests/test_original_error.py +52 -0
- velocity/misc/tests/test_timer.py +74 -0
- {velocity_python-0.0.132.dist-info → velocity_python-0.0.135.dist-info}/METADATA +1 -1
- velocity_python-0.0.135.dist-info/RECORD +128 -0
- velocity_python-0.0.132.dist-info/RECORD +0 -76
- {velocity_python-0.0.132.dist-info → velocity_python-0.0.135.dist-info}/WHEEL +0 -0
- {velocity_python-0.0.132.dist-info → velocity_python-0.0.135.dist-info}/licenses/LICENSE +0 -0
- {velocity_python-0.0.132.dist-info → velocity_python-0.0.135.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Tests for database utility functions.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import unittest
|
|
7
|
+
from src.velocity.db.utils import (
|
|
8
|
+
safe_sort_key_none_last,
|
|
9
|
+
safe_sort_key_none_first,
|
|
10
|
+
safe_sort_key_with_default,
|
|
11
|
+
safe_sort_rows,
|
|
12
|
+
group_by_fields,
|
|
13
|
+
safe_sort_grouped_rows,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestDatabaseUtils(unittest.TestCase):
|
|
18
|
+
"""Test database utility functions."""
|
|
19
|
+
|
|
20
|
+
def setUp(self):
|
|
21
|
+
"""Set up test data."""
|
|
22
|
+
self.sample_data = [
|
|
23
|
+
{"id": 1, "name": "Alice", "date": "2024-03", "amount": 100},
|
|
24
|
+
{"id": 2, "name": "Bob", "date": None, "amount": 200},
|
|
25
|
+
{"id": 3, "name": "Charlie", "date": "2024-01", "amount": None},
|
|
26
|
+
{"id": 4, "name": "David", "date": "2024-02", "amount": 150},
|
|
27
|
+
{"id": 5, "name": "Eve", "date": None, "amount": 300},
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
def test_safe_sort_key_none_last(self):
|
|
31
|
+
"""Test sorting with None values at the end."""
|
|
32
|
+
sort_key = safe_sort_key_none_last("date")
|
|
33
|
+
sorted_data = sorted(self.sample_data, key=sort_key)
|
|
34
|
+
|
|
35
|
+
dates = [row["date"] for row in sorted_data]
|
|
36
|
+
expected = ["2024-01", "2024-02", "2024-03", None, None]
|
|
37
|
+
self.assertEqual(dates, expected)
|
|
38
|
+
|
|
39
|
+
def test_safe_sort_key_none_first(self):
|
|
40
|
+
"""Test sorting with None values at the beginning."""
|
|
41
|
+
sort_key = safe_sort_key_none_first("date")
|
|
42
|
+
sorted_data = sorted(self.sample_data, key=sort_key)
|
|
43
|
+
|
|
44
|
+
dates = [row["date"] for row in sorted_data]
|
|
45
|
+
expected = [None, None, "2024-01", "2024-02", "2024-03"]
|
|
46
|
+
self.assertEqual(dates, expected)
|
|
47
|
+
|
|
48
|
+
def test_safe_sort_key_with_default(self):
|
|
49
|
+
"""Test sorting with None values replaced by default."""
|
|
50
|
+
sort_key = safe_sort_key_with_default("date", "1900-01")
|
|
51
|
+
sorted_data = sorted(self.sample_data, key=sort_key)
|
|
52
|
+
|
|
53
|
+
dates = [row["date"] for row in sorted_data]
|
|
54
|
+
expected = [None, None, "2024-01", "2024-02", "2024-03"]
|
|
55
|
+
self.assertEqual(dates, expected)
|
|
56
|
+
|
|
57
|
+
def test_safe_sort_rows_none_last(self):
|
|
58
|
+
"""Test safe_sort_rows with none_handling='last'."""
|
|
59
|
+
sorted_data = safe_sort_rows(self.sample_data, "date", none_handling="last")
|
|
60
|
+
|
|
61
|
+
dates = [row["date"] for row in sorted_data]
|
|
62
|
+
expected = ["2024-01", "2024-02", "2024-03", None, None]
|
|
63
|
+
self.assertEqual(dates, expected)
|
|
64
|
+
|
|
65
|
+
def test_safe_sort_rows_none_first(self):
|
|
66
|
+
"""Test safe_sort_rows with none_handling='first'."""
|
|
67
|
+
sorted_data = safe_sort_rows(self.sample_data, "date", none_handling="first")
|
|
68
|
+
|
|
69
|
+
dates = [row["date"] for row in sorted_data]
|
|
70
|
+
expected = [None, None, "2024-01", "2024-02", "2024-03"]
|
|
71
|
+
self.assertEqual(dates, expected)
|
|
72
|
+
|
|
73
|
+
def test_safe_sort_rows_with_default(self):
|
|
74
|
+
"""Test safe_sort_rows with none_handling='default'."""
|
|
75
|
+
sorted_data = safe_sort_rows(
|
|
76
|
+
self.sample_data, "date", none_handling="default", default_value="1900-01"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
dates = [row["date"] for row in sorted_data]
|
|
80
|
+
expected = [None, None, "2024-01", "2024-02", "2024-03"]
|
|
81
|
+
self.assertEqual(dates, expected)
|
|
82
|
+
|
|
83
|
+
def test_safe_sort_rows_reverse(self):
|
|
84
|
+
"""Test safe_sort_rows with reverse=True."""
|
|
85
|
+
sorted_data = safe_sort_rows(self.sample_data, "date", reverse=True)
|
|
86
|
+
|
|
87
|
+
dates = [row["date"] for row in sorted_data]
|
|
88
|
+
expected = [None, None, "2024-03", "2024-02", "2024-01"]
|
|
89
|
+
self.assertEqual(dates, expected)
|
|
90
|
+
|
|
91
|
+
def test_safe_sort_rows_invalid_none_handling(self):
|
|
92
|
+
"""Test safe_sort_rows with invalid none_handling option."""
|
|
93
|
+
with self.assertRaises(ValueError) as context:
|
|
94
|
+
safe_sort_rows(self.sample_data, "date", none_handling="invalid")
|
|
95
|
+
|
|
96
|
+
self.assertIn("Invalid none_handling option", str(context.exception))
|
|
97
|
+
|
|
98
|
+
def test_group_by_fields_single_field(self):
|
|
99
|
+
"""Test grouping by a single field."""
|
|
100
|
+
# Add data with same names for grouping
|
|
101
|
+
test_data = [
|
|
102
|
+
{"name": "Alice", "type": "A", "value": 1},
|
|
103
|
+
{"name": "Bob", "type": "B", "value": 2},
|
|
104
|
+
{"name": "Alice", "type": "C", "value": 3},
|
|
105
|
+
{"name": "Bob", "type": "A", "value": 4},
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
groups = group_by_fields(test_data, "name")
|
|
109
|
+
|
|
110
|
+
self.assertEqual(len(groups), 2)
|
|
111
|
+
self.assertIn(("Alice",), groups)
|
|
112
|
+
self.assertIn(("Bob",), groups)
|
|
113
|
+
self.assertEqual(len(groups[("Alice",)]), 2)
|
|
114
|
+
self.assertEqual(len(groups[("Bob",)]), 2)
|
|
115
|
+
|
|
116
|
+
def test_group_by_fields_multiple_fields(self):
|
|
117
|
+
"""Test grouping by multiple fields."""
|
|
118
|
+
test_data = [
|
|
119
|
+
{"name": "Alice", "type": "A", "value": 1},
|
|
120
|
+
{"name": "Bob", "type": "B", "value": 2},
|
|
121
|
+
{"name": "Alice", "type": "A", "value": 3},
|
|
122
|
+
{"name": "Alice", "type": "B", "value": 4},
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
groups = group_by_fields(test_data, "name", "type")
|
|
126
|
+
|
|
127
|
+
self.assertEqual(len(groups), 3)
|
|
128
|
+
self.assertIn(("Alice", "A"), groups)
|
|
129
|
+
self.assertIn(("Bob", "B"), groups)
|
|
130
|
+
self.assertIn(("Alice", "B"), groups)
|
|
131
|
+
self.assertEqual(len(groups[("Alice", "A")]), 2)
|
|
132
|
+
self.assertEqual(len(groups[("Bob", "B")]), 1)
|
|
133
|
+
self.assertEqual(len(groups[("Alice", "B")]), 1)
|
|
134
|
+
|
|
135
|
+
def test_safe_sort_grouped_rows(self):
|
|
136
|
+
"""Test sorting rows within groups."""
|
|
137
|
+
# Create grouped data
|
|
138
|
+
test_data = [
|
|
139
|
+
{"group": "A", "date": "2024-03", "value": 1},
|
|
140
|
+
{"group": "A", "date": None, "value": 2},
|
|
141
|
+
{"group": "A", "date": "2024-01", "value": 3},
|
|
142
|
+
{"group": "B", "date": "2024-02", "value": 4},
|
|
143
|
+
{"group": "B", "date": None, "value": 5},
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
groups = group_by_fields(test_data, "group")
|
|
147
|
+
sorted_groups = safe_sort_grouped_rows(groups, "date")
|
|
148
|
+
|
|
149
|
+
# Check group A is sorted correctly
|
|
150
|
+
group_a_dates = [row["date"] for row in sorted_groups[("A",)]]
|
|
151
|
+
expected_a = ["2024-01", "2024-03", None]
|
|
152
|
+
self.assertEqual(group_a_dates, expected_a)
|
|
153
|
+
|
|
154
|
+
# Check group B is sorted correctly
|
|
155
|
+
group_b_dates = [row["date"] for row in sorted_groups[("B",)]]
|
|
156
|
+
expected_b = ["2024-02", None]
|
|
157
|
+
self.assertEqual(group_b_dates, expected_b)
|
|
158
|
+
|
|
159
|
+
def test_payment_profile_scenario(self):
|
|
160
|
+
"""Test the specific payment profile scenario that was failing."""
|
|
161
|
+
payment_profiles = [
|
|
162
|
+
{
|
|
163
|
+
"sys_id": 1,
|
|
164
|
+
"email_address": "test@example.com",
|
|
165
|
+
"card_number": "1234",
|
|
166
|
+
"expiration_date": "2024-12",
|
|
167
|
+
"status": "active",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"sys_id": 2,
|
|
171
|
+
"email_address": "test@example.com",
|
|
172
|
+
"card_number": "1234",
|
|
173
|
+
"expiration_date": "2024-06",
|
|
174
|
+
"status": "active",
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"sys_id": 3,
|
|
178
|
+
"email_address": "test@example.com",
|
|
179
|
+
"card_number": "1234",
|
|
180
|
+
"expiration_date": None,
|
|
181
|
+
"status": "active",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"sys_id": 4,
|
|
185
|
+
"email_address": "other@example.com",
|
|
186
|
+
"card_number": "5678",
|
|
187
|
+
"expiration_date": "2025-01",
|
|
188
|
+
"status": "active",
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"sys_id": 5,
|
|
192
|
+
"email_address": "other@example.com",
|
|
193
|
+
"card_number": "5678",
|
|
194
|
+
"expiration_date": None,
|
|
195
|
+
"status": "active",
|
|
196
|
+
},
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
# Group by email and card number
|
|
200
|
+
groups = group_by_fields(payment_profiles, "email_address", "card_number")
|
|
201
|
+
|
|
202
|
+
# Sort each group by expiration date
|
|
203
|
+
sorted_groups = safe_sort_grouped_rows(groups, "expiration_date")
|
|
204
|
+
|
|
205
|
+
# Verify we can safely enumerate through each group
|
|
206
|
+
for group_key, group in sorted_groups.items():
|
|
207
|
+
for idx, row in enumerate(group):
|
|
208
|
+
# This should not raise any errors
|
|
209
|
+
self.assertIsInstance(idx, int)
|
|
210
|
+
self.assertIn("sys_id", row)
|
|
211
|
+
self.assertIn("expiration_date", row)
|
|
212
|
+
|
|
213
|
+
# Check specific group sorting
|
|
214
|
+
test_group = sorted_groups[("test@example.com", "1234")]
|
|
215
|
+
exp_dates = [row["expiration_date"] for row in test_group]
|
|
216
|
+
expected = ["2024-06", "2024-12", None]
|
|
217
|
+
self.assertEqual(exp_dates, expected)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if __name__ == "__main__":
|
|
221
|
+
unittest.main()
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import decimal
|
|
3
|
+
from velocity.db.servers.postgres.sql import SQL
|
|
4
|
+
from velocity.db.servers.tablehelper import TableHelper
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestSQLModule(unittest.TestCase):
|
|
8
|
+
def test_quote_simple_identifier(self):
|
|
9
|
+
self.assertEqual(TableHelper.quote("test"), "test")
|
|
10
|
+
|
|
11
|
+
def test_quote_reserved_word(self):
|
|
12
|
+
self.assertEqual(TableHelper.quote("SELECT"), '"SELECT"')
|
|
13
|
+
|
|
14
|
+
def test_quote_with_special_characters(self):
|
|
15
|
+
self.assertEqual(TableHelper.quote("my/schema"), '"my/schema"')
|
|
16
|
+
|
|
17
|
+
def test_quote_dot_notation(self):
|
|
18
|
+
self.assertEqual(TableHelper.quote("my_table.my_column"), "my_table.my_column")
|
|
19
|
+
|
|
20
|
+
def test_quote_list_identifiers(self):
|
|
21
|
+
self.assertEqual(
|
|
22
|
+
TableHelper.quote(["test", "SELECT", "my_table"]),
|
|
23
|
+
["test", '"SELECT"', "my_table"],
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def test_make_where_simple_equality(self):
|
|
27
|
+
# Create a mock transaction and table helper
|
|
28
|
+
mock_tx = type("MockTx", (), {})()
|
|
29
|
+
helper = TableHelper(mock_tx, "test_table")
|
|
30
|
+
|
|
31
|
+
sql, vals = helper.make_where({"column1": "value1"})
|
|
32
|
+
self.assertIn("column1 = %s", sql)
|
|
33
|
+
self.assertEqual(vals, ("value1",))
|
|
34
|
+
|
|
35
|
+
def test_make_where_with_null(self):
|
|
36
|
+
mock_tx = type("MockTx", (), {})()
|
|
37
|
+
helper = TableHelper(mock_tx, "test_table")
|
|
38
|
+
|
|
39
|
+
sql, vals = helper.make_where({"column1": None})
|
|
40
|
+
self.assertIn("column1 is NULL", sql)
|
|
41
|
+
self.assertEqual(vals, ())
|
|
42
|
+
|
|
43
|
+
def test_make_where_with_not_null(self):
|
|
44
|
+
mock_tx = type("MockTx", (), {})()
|
|
45
|
+
helper = TableHelper(mock_tx, "test_table")
|
|
46
|
+
|
|
47
|
+
sql, vals = helper.make_where({"column1!": None})
|
|
48
|
+
self.assertIn("column1 is not NULL", sql)
|
|
49
|
+
self.assertEqual(vals, ())
|
|
50
|
+
|
|
51
|
+
def test_make_where_with_operators(self):
|
|
52
|
+
mock_tx = type("MockTx", (), {})()
|
|
53
|
+
helper = TableHelper(mock_tx, "test_table")
|
|
54
|
+
|
|
55
|
+
sql, vals = helper.make_where({"column1>": 10, "column2!": "value2"})
|
|
56
|
+
self.assertIn("column1 > %s", sql)
|
|
57
|
+
self.assertIn("column2 != %s", sql)
|
|
58
|
+
self.assertEqual(len(vals), 2)
|
|
59
|
+
|
|
60
|
+
def test_make_where_with_list(self):
|
|
61
|
+
mock_tx = type("MockTx", (), {})()
|
|
62
|
+
helper = TableHelper(mock_tx, "test_table")
|
|
63
|
+
|
|
64
|
+
sql, vals = helper.make_where({"column1": [1, 2, 3]})
|
|
65
|
+
self.assertIn("column1 in", sql.lower())
|
|
66
|
+
self.assertEqual(len(vals), 3)
|
|
67
|
+
|
|
68
|
+
def test_make_where_between(self):
|
|
69
|
+
mock_tx = type("MockTx", (), {})()
|
|
70
|
+
helper = TableHelper(mock_tx, "test_table")
|
|
71
|
+
|
|
72
|
+
sql, vals = helper.make_where({"column1><": [1, 10]})
|
|
73
|
+
self.assertIn("between", sql.lower())
|
|
74
|
+
self.assertEqual(len(vals), 2)
|
|
75
|
+
|
|
76
|
+
def test_sql_select_simple(self):
|
|
77
|
+
sql_query, params = SQL.select(columns="*", table="my_table")
|
|
78
|
+
self.assertEqual(sql_query, "SELECT * FROM my_table")
|
|
79
|
+
self.assertEqual(params, ())
|
|
80
|
+
|
|
81
|
+
def test_sql_select_with_where(self):
|
|
82
|
+
sql_query, params = SQL.select(columns="*", table="my_table", where={"id": 1})
|
|
83
|
+
self.assertEqual(sql_query, "SELECT * FROM my_table WHERE id = %s")
|
|
84
|
+
self.assertEqual(params, (1,))
|
|
85
|
+
|
|
86
|
+
def test_sql_select_with_order_by(self):
|
|
87
|
+
sql_query, params = SQL.select(columns="*", table="my_table", orderby="id DESC")
|
|
88
|
+
self.assertEqual(sql_query, "SELECT * FROM my_table ORDER BY id DESC")
|
|
89
|
+
self.assertEqual(params, ())
|
|
90
|
+
|
|
91
|
+
def test_sql_insert(self):
|
|
92
|
+
sql_query, params = SQL.insert(
|
|
93
|
+
table="my_table", data={"column1": "value1", "column2": 2}
|
|
94
|
+
)
|
|
95
|
+
self.assertEqual(
|
|
96
|
+
sql_query, "INSERT INTO my_table (column1,column2) VALUES (%s,%s)"
|
|
97
|
+
)
|
|
98
|
+
self.assertEqual(params, ("value1", 2))
|
|
99
|
+
|
|
100
|
+
def test_sql_update(self):
|
|
101
|
+
sql_query, params = SQL.update(
|
|
102
|
+
table="my_table", data={"column1": "new_value"}, pk={"id": 1}
|
|
103
|
+
)
|
|
104
|
+
self.assertEqual(sql_query, "UPDATE my_table SET column1 = %s WHERE id = %s")
|
|
105
|
+
self.assertEqual(params, ("new_value", 1))
|
|
106
|
+
|
|
107
|
+
def test_sql_delete(self):
|
|
108
|
+
sql_query, params = SQL.delete(table="my_table", where={"id": 1})
|
|
109
|
+
self.assertEqual(sql_query, "DELETE FROM my_table WHERE id = %s")
|
|
110
|
+
self.assertEqual(params, (1,))
|
|
111
|
+
|
|
112
|
+
def test_sql_create_table(self):
|
|
113
|
+
sql_query, params = SQL.create_table(
|
|
114
|
+
name="public.test_table", columns={"name": str, "age": int}, drop=True
|
|
115
|
+
)
|
|
116
|
+
self.assertIn("CREATE TABLE public.test_table", sql_query)
|
|
117
|
+
self.assertIn("DROP TABLE IF EXISTS public.test_table CASCADE;", sql_query)
|
|
118
|
+
self.assertEqual(params, ())
|
|
119
|
+
|
|
120
|
+
def test_sql_drop_table(self):
|
|
121
|
+
sql_query, params = SQL.drop_table("public.test_table")
|
|
122
|
+
self.assertEqual(sql_query, "drop table if exists public.test_table cascade;")
|
|
123
|
+
self.assertEqual(params, ())
|
|
124
|
+
|
|
125
|
+
def test_sql_create_index(self):
|
|
126
|
+
sql_query, params = SQL.create_index(
|
|
127
|
+
table="my_table", columns="column1", unique=True
|
|
128
|
+
)
|
|
129
|
+
self.assertIn("CREATE UNIQUE INDEX", sql_query)
|
|
130
|
+
self.assertIn("ON my_table (column1)", sql_query)
|
|
131
|
+
self.assertEqual(params, ())
|
|
132
|
+
|
|
133
|
+
def test_sql_drop_index(self):
|
|
134
|
+
sql_query, params = SQL.drop_index(table="my_table", columns="column1")
|
|
135
|
+
self.assertIn("DROP INDEX IF EXISTS", sql_query)
|
|
136
|
+
self.assertEqual(params, ())
|
|
137
|
+
|
|
138
|
+
def test_sql_foreign_key_creation(self):
|
|
139
|
+
sql_query, params = SQL.create_foreign_key(
|
|
140
|
+
table="child_table",
|
|
141
|
+
columns="parent_id",
|
|
142
|
+
key_to_table="parent_table",
|
|
143
|
+
key_to_columns="id",
|
|
144
|
+
)
|
|
145
|
+
self.assertIn("ALTER TABLE child_table ADD CONSTRAINT", sql_query)
|
|
146
|
+
self.assertIn(
|
|
147
|
+
"FOREIGN KEY (parent_id) REFERENCES parent_table (id);", sql_query
|
|
148
|
+
)
|
|
149
|
+
self.assertEqual(params, ())
|
|
150
|
+
|
|
151
|
+
def test_sql_merge_insert(self):
|
|
152
|
+
sql_query, params = SQL.merge(
|
|
153
|
+
table="my_table",
|
|
154
|
+
data={"column1": "value1"},
|
|
155
|
+
pk={"id": 1},
|
|
156
|
+
on_conflict_do_nothing=True,
|
|
157
|
+
on_conflict_update=False,
|
|
158
|
+
)
|
|
159
|
+
self.assertIn("INSERT INTO my_table", sql_query)
|
|
160
|
+
self.assertIn("ON CONFLICT (id) DO NOTHING", sql_query)
|
|
161
|
+
self.assertEqual(params, ("value1", 1))
|
|
162
|
+
|
|
163
|
+
def test_sql_merge_update(self):
|
|
164
|
+
sql_query, params = SQL.merge(
|
|
165
|
+
table="my_table",
|
|
166
|
+
data={"column1": "value1"},
|
|
167
|
+
pk={"id": 1},
|
|
168
|
+
on_conflict_do_nothing=False,
|
|
169
|
+
on_conflict_update=True,
|
|
170
|
+
)
|
|
171
|
+
self.assertIn("INSERT INTO my_table", sql_query)
|
|
172
|
+
self.assertIn("ON CONFLICT (id) DO UPDATE SET", sql_query)
|
|
173
|
+
self.assertEqual(params, ("value1", 1))
|
|
174
|
+
|
|
175
|
+
def test_get_type_mapping(self):
|
|
176
|
+
self.assertEqual(SQL.get_type("string"), "TEXT")
|
|
177
|
+
self.assertEqual(SQL.get_type(123), "BIGINT")
|
|
178
|
+
self.assertEqual(SQL.get_type(123.456), "NUMERIC(19, 6)")
|
|
179
|
+
self.assertEqual(SQL.get_type(True), "BOOLEAN")
|
|
180
|
+
self.assertEqual(SQL.get_type(None), "TEXT")
|
|
181
|
+
|
|
182
|
+
def test_py_type_mapping(self):
|
|
183
|
+
self.assertEqual(SQL.py_type("INTEGER"), int)
|
|
184
|
+
self.assertEqual(SQL.py_type("NUMERIC"), decimal.Decimal)
|
|
185
|
+
self.assertEqual(SQL.py_type("TEXT"), str)
|
|
186
|
+
self.assertEqual(SQL.py_type("BOOLEAN"), bool)
|
|
187
|
+
|
|
188
|
+
def test_sql_truncate(self):
|
|
189
|
+
sql_query, params = SQL.truncate("my_table")
|
|
190
|
+
self.assertEqual(sql_query, "truncate table my_table")
|
|
191
|
+
self.assertEqual(params, ())
|
|
192
|
+
|
|
193
|
+
def test_sql_create_view(self):
|
|
194
|
+
sql_query, params = SQL.create_view(
|
|
195
|
+
name="my_view", query="SELECT * FROM my_table", temp=True, silent=True
|
|
196
|
+
)
|
|
197
|
+
self.assertIn(
|
|
198
|
+
"CREATE OR REPLACE TEMPORARY VIEW my_view AS SELECT * FROM my_table",
|
|
199
|
+
sql_query,
|
|
200
|
+
)
|
|
201
|
+
self.assertEqual(params, ())
|
|
202
|
+
|
|
203
|
+
def test_sql_drop_view(self):
|
|
204
|
+
sql_query, params = SQL.drop_view(name="my_view", silent=True)
|
|
205
|
+
self.assertEqual(sql_query, "DROP VIEW IF EXISTS my_view")
|
|
206
|
+
self.assertEqual(params, ())
|
|
207
|
+
|
|
208
|
+
# Additional tests can be added here to cover more methods and edge cases
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
if __name__ == "__main__":
|
|
212
|
+
unittest.main()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Quick test to verify PostgreSQL implementation is functionally unchanged.
|
|
4
|
+
"""
|
|
5
|
+
import sys
|
|
6
|
+
import os
|
|
7
|
+
sys.path.insert(0, '/home/ubuntu/tenspace/velocity-python/src')
|
|
8
|
+
|
|
9
|
+
def test_postgres_unchanged():
|
|
10
|
+
"""Test that PostgreSQL implementation is functionally unchanged."""
|
|
11
|
+
print("Testing PostgreSQL implementation...")
|
|
12
|
+
|
|
13
|
+
# Test imports
|
|
14
|
+
try:
|
|
15
|
+
from velocity.db.servers.postgres import initialize
|
|
16
|
+
from velocity.db.servers.postgres.sql import SQL
|
|
17
|
+
from velocity.db.servers.postgres.types import TYPES
|
|
18
|
+
from velocity.db.servers.postgres.operators import OPERATORS
|
|
19
|
+
print("✓ All imports successful")
|
|
20
|
+
except ImportError as e:
|
|
21
|
+
print(f"✗ Import failed: {e}")
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
# Test SQL class attributes are the same
|
|
25
|
+
expected_server = "PostGreSQL"
|
|
26
|
+
if SQL.server != expected_server:
|
|
27
|
+
print(f"✗ SQL.server changed: expected '{expected_server}', got '{SQL.server}'")
|
|
28
|
+
return False
|
|
29
|
+
print("✓ SQL.server unchanged")
|
|
30
|
+
|
|
31
|
+
# Test error codes are preserved
|
|
32
|
+
expected_duplicate_codes = ["23505"]
|
|
33
|
+
if SQL.DuplicateKeyErrorCodes != expected_duplicate_codes:
|
|
34
|
+
print(f"✗ DuplicateKeyErrorCodes changed: expected {expected_duplicate_codes}, got {SQL.DuplicateKeyErrorCodes}")
|
|
35
|
+
return False
|
|
36
|
+
print("✓ Error codes unchanged")
|
|
37
|
+
|
|
38
|
+
# Test TYPES class methods exist
|
|
39
|
+
if not hasattr(TYPES, 'get_type'):
|
|
40
|
+
print("✗ TYPES.get_type method missing")
|
|
41
|
+
return False
|
|
42
|
+
if not hasattr(TYPES, 'get_conv'):
|
|
43
|
+
print("✗ TYPES.get_conv method missing")
|
|
44
|
+
return False
|
|
45
|
+
if not hasattr(TYPES, 'py_type'):
|
|
46
|
+
print("✗ TYPES.py_type method missing")
|
|
47
|
+
return False
|
|
48
|
+
print("✓ TYPES methods present")
|
|
49
|
+
|
|
50
|
+
# Test type mappings are correct
|
|
51
|
+
if TYPES.get_type(str) != "TEXT":
|
|
52
|
+
print(f"✗ TYPES.get_type(str) changed: expected 'TEXT', got '{TYPES.get_type(str)}'")
|
|
53
|
+
return False
|
|
54
|
+
if TYPES.get_type(int) != "BIGINT":
|
|
55
|
+
print(f"✗ TYPES.get_type(int) changed: expected 'BIGINT', got '{TYPES.get_type(int)}'")
|
|
56
|
+
return False
|
|
57
|
+
print("✓ Type mappings unchanged")
|
|
58
|
+
|
|
59
|
+
# Test operators are preserved
|
|
60
|
+
if OPERATORS.get("<>") != "<>":
|
|
61
|
+
print(f"✗ Operator '<>' mapping changed")
|
|
62
|
+
return False
|
|
63
|
+
if OPERATORS.get("%%") != "ILIKE":
|
|
64
|
+
print(f"✗ Operator '%%' mapping changed")
|
|
65
|
+
return False
|
|
66
|
+
print("✓ Operators unchanged")
|
|
67
|
+
|
|
68
|
+
# Test SQL methods exist (just check key ones)
|
|
69
|
+
sql_methods = ['select', 'insert', 'update', 'delete', 'merge', 'version', 'databases']
|
|
70
|
+
for method in sql_methods:
|
|
71
|
+
if not hasattr(SQL, method):
|
|
72
|
+
print(f"✗ SQL.{method} method missing")
|
|
73
|
+
return False
|
|
74
|
+
print("✓ SQL methods present")
|
|
75
|
+
|
|
76
|
+
print("\n🎉 PostgreSQL implementation is functionally unchanged!")
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
success = test_postgres_unchanged()
|
|
81
|
+
sys.exit(0 if success else 1)
|