1212import neo4j
1313import pytest
1414
15+ import api .attack_paths .database as db_module
16+
1517
1618class TestLazyInitialization :
1719 """Test that Neo4j driver is initialized lazily on first use."""
1820
1921 @pytest .fixture (autouse = True )
2022 def reset_module_state (self ):
2123 """Reset module-level singleton state before each test."""
22- import api .attack_paths .database as db_module
23-
2424 original_driver = db_module ._driver
2525
2626 db_module ._driver = None
@@ -31,8 +31,6 @@ def reset_module_state(self):
3131
3232 def test_driver_not_initialized_at_import (self ):
3333 """Driver should be None after module import (no eager connection)."""
34- import api .attack_paths .database as db_module
35-
3634 assert db_module ._driver is None
3735
3836 @patch ("api.attack_paths.database.settings" )
@@ -41,8 +39,6 @@ def test_init_driver_creates_connection_on_first_call(
4139 self , mock_driver_factory , mock_settings
4240 ):
4341 """init_driver() should create connection only when called."""
44- import api .attack_paths .database as db_module
45-
4642 mock_driver = MagicMock ()
4743 mock_driver_factory .return_value = mock_driver
4844 mock_settings .DATABASES = {
@@ -69,8 +65,6 @@ def test_init_driver_returns_cached_driver_on_subsequent_calls(
6965 self , mock_driver_factory , mock_settings
7066 ):
7167 """Subsequent calls should return cached driver without reconnecting."""
72- import api .attack_paths .database as db_module
73-
7468 mock_driver = MagicMock ()
7569 mock_driver_factory .return_value = mock_driver
7670 mock_settings .DATABASES = {
@@ -99,8 +93,6 @@ def test_get_driver_delegates_to_init_driver(
9993 self , mock_driver_factory , mock_settings
10094 ):
10195 """get_driver() should use init_driver() for lazy initialization."""
102- import api .attack_paths .database as db_module
103-
10496 mock_driver = MagicMock ()
10597 mock_driver_factory .return_value = mock_driver
10698 mock_settings .DATABASES = {
@@ -118,14 +110,50 @@ def test_get_driver_delegates_to_init_driver(
118110 mock_driver_factory .assert_called_once ()
119111
120112
113+ class TestConnectionAcquisitionTimeout :
114+ """Test that the connection acquisition timeout is configurable."""
115+
116+ @pytest .fixture (autouse = True )
117+ def reset_module_state (self ):
118+ original_driver = db_module ._driver
119+ original_timeout = db_module .CONN_ACQUISITION_TIMEOUT
120+
121+ db_module ._driver = None
122+
123+ yield
124+
125+ db_module ._driver = original_driver
126+ db_module .CONN_ACQUISITION_TIMEOUT = original_timeout
127+
128+ @patch ("api.attack_paths.database.settings" )
129+ @patch ("api.attack_paths.database.neo4j.GraphDatabase.driver" )
130+ def test_driver_receives_configured_timeout (
131+ self , mock_driver_factory , mock_settings
132+ ):
133+ """init_driver() should pass CONN_ACQUISITION_TIMEOUT to the neo4j driver."""
134+ mock_driver_factory .return_value = MagicMock ()
135+ mock_settings .DATABASES = {
136+ "neo4j" : {
137+ "HOST" : "localhost" ,
138+ "PORT" : 7687 ,
139+ "USER" : "neo4j" ,
140+ "PASSWORD" : "password" ,
141+ }
142+ }
143+ db_module .CONN_ACQUISITION_TIMEOUT = 42
144+
145+ db_module .init_driver ()
146+
147+ _ , kwargs = mock_driver_factory .call_args
148+ assert kwargs ["connection_acquisition_timeout" ] == 42
149+
150+
121151class TestAtexitRegistration :
122152 """Test that atexit cleanup handler is registered correctly."""
123153
124154 @pytest .fixture (autouse = True )
125155 def reset_module_state (self ):
126156 """Reset module-level singleton state before each test."""
127- import api .attack_paths .database as db_module
128-
129157 original_driver = db_module ._driver
130158
131159 db_module ._driver = None
@@ -141,8 +169,6 @@ def test_atexit_registered_on_first_init(
141169 self , mock_driver_factory , mock_atexit_register , mock_settings
142170 ):
143171 """atexit.register should be called on first initialization."""
144- import api .attack_paths .database as db_module
145-
146172 mock_driver_factory .return_value = MagicMock ()
147173 mock_settings .DATABASES = {
148174 "neo4j" : {
@@ -168,8 +194,6 @@ def test_atexit_registered_only_once(
168194 The double-checked locking on _driver ensures the atexit registration
169195 block only executes once (when _driver is first created).
170196 """
171- import api .attack_paths .database as db_module
172-
173197 mock_driver_factory .return_value = MagicMock ()
174198 mock_settings .DATABASES = {
175199 "neo4j" : {
@@ -194,8 +218,6 @@ class TestCloseDriver:
194218 @pytest .fixture (autouse = True )
195219 def reset_module_state (self ):
196220 """Reset module-level singleton state before each test."""
197- import api .attack_paths .database as db_module
198-
199221 original_driver = db_module ._driver
200222
201223 db_module ._driver = None
@@ -206,8 +228,6 @@ def reset_module_state(self):
206228
207229 def test_close_driver_closes_and_clears_driver (self ):
208230 """close_driver() should close the driver and set it to None."""
209- import api .attack_paths .database as db_module
210-
211231 mock_driver = MagicMock ()
212232 db_module ._driver = mock_driver
213233
@@ -218,8 +238,6 @@ def test_close_driver_closes_and_clears_driver(self):
218238
219239 def test_close_driver_handles_none_driver (self ):
220240 """close_driver() should handle case where driver is None."""
221- import api .attack_paths .database as db_module
222-
223241 db_module ._driver = None
224242
225243 # Should not raise
@@ -229,8 +247,6 @@ def test_close_driver_handles_none_driver(self):
229247
230248 def test_close_driver_clears_driver_even_on_close_error (self ):
231249 """Driver should be cleared even if close() raises an exception."""
232- import api .attack_paths .database as db_module
233-
234250 mock_driver = MagicMock ()
235251 mock_driver .close .side_effect = Exception ("Connection error" )
236252 db_module ._driver = mock_driver
@@ -246,8 +262,6 @@ class TestExecuteReadQuery:
246262 """Test read query execution helper."""
247263
248264 def test_execute_read_query_calls_read_session_and_returns_result (self ):
249- import api .attack_paths .database as db_module
250-
251265 tx = MagicMock ()
252266 expected_graph = MagicMock ()
253267 run_result = MagicMock ()
@@ -289,8 +303,6 @@ def execute_read_side_effect(fn):
289303 assert result is expected_graph
290304
291305 def test_execute_read_query_defaults_parameters_to_empty_dict (self ):
292- import api .attack_paths .database as db_module
293-
294306 tx = MagicMock ()
295307 run_result = MagicMock ()
296308 run_result .graph .return_value = MagicMock ()
@@ -325,8 +337,6 @@ class TestGetSessionReadOnly:
325337
326338 @pytest .fixture (autouse = True )
327339 def reset_module_state (self ):
328- import api .attack_paths .database as db_module
329-
330340 original_driver = db_module ._driver
331341 db_module ._driver = None
332342 yield
@@ -341,8 +351,6 @@ def reset_module_state(self):
341351 )
342352 def test_get_session_raises_write_query_not_allowed (self , neo4j_code ):
343353 """Read-mode Neo4j errors should raise `WriteQueryNotAllowedException`."""
344- import api .attack_paths .database as db_module
345-
346354 mock_session = MagicMock ()
347355 neo4j_error = neo4j .exceptions .Neo4jError ._hydrate_neo4j (
348356 code = neo4j_code ,
@@ -362,8 +370,6 @@ def test_get_session_raises_write_query_not_allowed(self, neo4j_code):
362370
363371 def test_get_session_raises_generic_exception_for_other_errors (self ):
364372 """Non-read-mode Neo4j errors should raise GraphDatabaseQueryException."""
365- import api .attack_paths .database as db_module
366-
367373 mock_session = MagicMock ()
368374 neo4j_error = neo4j .exceptions .Neo4jError ._hydrate_neo4j (
369375 code = "Neo.ClientError.Statement.SyntaxError" ,
@@ -388,8 +394,6 @@ class TestThreadSafety:
388394 @pytest .fixture (autouse = True )
389395 def reset_module_state (self ):
390396 """Reset module-level singleton state before each test."""
391- import api .attack_paths .database as db_module
392-
393397 original_driver = db_module ._driver
394398
395399 db_module ._driver = None
@@ -404,8 +408,6 @@ def test_concurrent_init_creates_single_driver(
404408 self , mock_driver_factory , mock_settings
405409 ):
406410 """Multiple threads calling init_driver() should create only one driver."""
407- import api .attack_paths .database as db_module
408-
409411 mock_driver = MagicMock ()
410412 mock_driver_factory .return_value = mock_driver
411413 mock_settings .DATABASES = {
@@ -448,8 +450,6 @@ class TestHasProviderData:
448450 """Test has_provider_data helper for checking provider nodes in Neo4j."""
449451
450452 def test_returns_true_when_nodes_exist (self ):
451- import api .attack_paths .database as db_module
452-
453453 mock_session = MagicMock ()
454454 mock_result = MagicMock ()
455455 mock_result .single .return_value = MagicMock () # non-None record
@@ -468,8 +468,6 @@ def test_returns_true_when_nodes_exist(self):
468468 mock_session .run .assert_called_once ()
469469
470470 def test_returns_false_when_no_nodes (self ):
471- import api .attack_paths .database as db_module
472-
473471 mock_session = MagicMock ()
474472 mock_result = MagicMock ()
475473 mock_result .single .return_value = None
@@ -486,8 +484,6 @@ def test_returns_false_when_no_nodes(self):
486484 assert db_module .has_provider_data ("db-tenant-abc" , "provider-123" ) is False
487485
488486 def test_returns_false_when_database_not_found (self ):
489- import api .attack_paths .database as db_module
490-
491487 session_ctx = MagicMock ()
492488 session_ctx .__enter__ .side_effect = db_module .GraphDatabaseQueryException (
493489 message = "Database does not exist" ,
@@ -503,8 +499,6 @@ def test_returns_false_when_database_not_found(self):
503499 )
504500
505501 def test_raises_on_other_errors (self ):
506- import api .attack_paths .database as db_module
507-
508502 session_ctx = MagicMock ()
509503 session_ctx .__enter__ .side_effect = db_module .GraphDatabaseQueryException (
510504 message = "Connection refused" ,
0 commit comments