-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_context_manager.py
More file actions
200 lines (157 loc) · 7 KB
/
test_context_manager.py
File metadata and controls
200 lines (157 loc) · 7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
"""Test the context-manager functionality."""
import sqlite3
from pathlib import Path
import pytest
from pytest_mock import MockerFixture
from sqliter.sqliter import SqliterDB
from tests.conftest import ExampleModel
class TestContextManager:
"""Test the context-manager functionality."""
def test_transaction_commit_success(
self, db_mock: SqliterDB, mocker: MockerFixture
) -> None:
"""Test that the transaction commits successfully with no exceptions."""
# Mock the connection's commit method to track the commit
mock_commit = mocker.patch.object(db_mock, "conn", create=True)
mock_commit.commit = mocker.MagicMock()
# Run the context manager without errors
with db_mock:
"""Dummy transaction."""
# Ensure commit was called
mock_commit.commit.assert_called_once()
def test_transaction_keeps_connection_open(
self, db_mock: SqliterDB, mocker: MockerFixture
) -> None:
"""Test the connection stays open after the transaction completes."""
# Mock the connection object itself
mock_conn = mocker.patch.object(db_mock, "conn", autospec=True)
# Run the context manager
with db_mock:
"""Dummy transaction."""
# Ensure the connection is not closed
mock_conn.close.assert_not_called()
def test_transaction_rollback_on_exception(
self, db_mock: SqliterDB, mocker: MockerFixture
) -> None:
"""Test that the transaction rolls back when an exception occurs."""
# Mock the connection object and ensure it's set as db_mock.conn
mock_conn = mocker.Mock()
mocker.patch.object(db_mock, "conn", mock_conn)
# Simulate an exception within the context manager
message = "Simulated error"
with pytest.raises(ValueError, match=message), db_mock:
raise ValueError(message)
# Ensure rollback was called on the mocked connection
mock_conn.rollback.assert_called_once()
mock_conn.commit.assert_not_called()
def test_in_transaction_flag(self, db_mock: SqliterDB) -> None:
"""Test that _in_transaction is set/unset inside a transaction."""
assert not db_mock._in_transaction # Initially, it should be False
with db_mock:
assert db_mock._in_transaction # Should be True inside the context
assert (
not db_mock._in_transaction
) # Should be False again after exiting the context
def test_rollback_resets_in_transaction_flag(
self, db_mock: SqliterDB, mocker: MockerFixture
) -> None:
"""Test that _in_transaction is reset after a rollback on exception."""
def test_transaction() -> None:
assert db_mock._in_transaction # Should be True during transaction
err_msg = "Simulated error"
raise ValueError(err_msg)
mock_conn = mocker.Mock()
mocker.patch.object(db_mock, "conn", mock_conn)
with pytest.raises(ValueError, match="Simulated error"), db_mock:
test_transaction()
assert (
not db_mock._in_transaction
) # Should be reset to False after exception
def test_maybe_commit_skips_in_transaction(
self, db_mock: SqliterDB, mocker: MockerFixture
) -> None:
"""Test that maybe_commit does not commit when inside a transaction."""
mock_conn = mocker.Mock()
mocker.patch.object(db_mock, "conn", mock_conn)
with db_mock:
db_mock._maybe_commit()
mock_conn.commit.assert_not_called()
mock_conn.commit.assert_called_once()
mock_conn.commit.reset_mock()
db_mock._maybe_commit()
mock_conn.commit.assert_called_once()
def test_commit_called_once_in_transaction(
self, mocker: MockerFixture, tmp_path: Path
) -> None:
"""Ensure data is committed at the end of a transaction."""
# Create a temporary database file
db_file = tmp_path / "test.db"
# Initialize the database with the file-based database
db_mock = SqliterDB(db_filename=str(db_file), auto_commit=True)
try:
db_mock.create_table(ExampleModel)
# Use the context manager to simulate a transaction
with db_mock:
db_mock.insert(
ExampleModel(
slug="test",
name="Test",
content="Test content",
)
)
# After the transaction, open a new connection to query the database
new_conn = sqlite3.connect(str(db_file))
try:
result = new_conn.execute(
"SELECT * FROM test_table WHERE slug = 'test'"
).fetchone()
finally:
new_conn.close()
# Assert that the data was committed
assert result is not None, "Data was not committed."
assert result[3] == "test", (
f"Expected slug to be 'test', but got {result[3]}"
)
finally:
db_mock.close()
def test_context_manager_keeps_memory_database_available(self) -> None:
"""In-memory DB data survives after leaving the transaction context."""
db = SqliterDB(memory=True)
with db:
db.create_table(ExampleModel)
inserted = db.insert(
ExampleModel(slug="persist", name="Persist", content="context")
)
fetched = db.get(ExampleModel, inserted.pk)
assert fetched is not None
assert fetched.slug == "persist"
assert db.conn is not None
db.close()
def test_set_in_transaction_updates_state(self, db_mock: SqliterDB) -> None:
"""set_in_transaction should preserve nested depth and reset state."""
db_mock.set_in_transaction(value=True)
assert db_mock._transaction_depth == 1
assert db_mock._in_transaction is True
assert db_mock.in_transaction is True
db_mock._transaction_depth = 2
db_mock.set_in_transaction(value=True)
assert db_mock._transaction_depth == 2
assert db_mock._in_transaction is True
assert db_mock.in_transaction is True
db_mock._rollback_requested = True
db_mock.set_in_transaction(value=False)
assert db_mock._transaction_depth == 0
assert db_mock._in_transaction is False
assert db_mock.in_transaction is False
assert db_mock._rollback_requested is False
def test_close_resets_transaction_scope(self, db_mock: SqliterDB) -> None:
"""Close should clear stale transaction bookkeeping."""
db_mock.set_in_transaction(value=True)
db_mock._transaction_depth = 2
db_mock._rollback_requested = True
db_mock.close()
assert db_mock.conn is None
assert db_mock._transaction_depth == 0
assert db_mock._in_transaction is False
assert db_mock.in_transaction is False
assert db_mock._rollback_requested is False