pytest-neon 0.2.1__py3-none-any.whl → 0.3.0__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.
- pytest_neon/__init__.py +1 -1
- pytest_neon/plugin.py +21 -13
- {pytest_neon-0.2.1.dist-info → pytest_neon-0.3.0.dist-info}/METADATA +23 -1
- pytest_neon-0.3.0.dist-info/RECORD +8 -0
- pytest_neon-0.2.1.dist-info/RECORD +0 -8
- {pytest_neon-0.2.1.dist-info → pytest_neon-0.3.0.dist-info}/WHEEL +0 -0
- {pytest_neon-0.2.1.dist-info → pytest_neon-0.3.0.dist-info}/entry_points.txt +0 -0
- {pytest_neon-0.2.1.dist-info → pytest_neon-0.3.0.dist-info}/licenses/LICENSE +0 -0
pytest_neon/__init__.py
CHANGED
pytest_neon/plugin.py
CHANGED
|
@@ -7,14 +7,12 @@ import time
|
|
|
7
7
|
from collections.abc import Generator
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from datetime import datetime, timedelta, timezone
|
|
10
|
-
from typing import
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
import pytest
|
|
13
13
|
import requests
|
|
14
14
|
from neon_api import NeonAPI
|
|
15
|
-
|
|
16
|
-
if TYPE_CHECKING:
|
|
17
|
-
pass
|
|
15
|
+
from neon_api.schema import EndpointState
|
|
18
16
|
|
|
19
17
|
# Default branch expiry in seconds (10 minutes)
|
|
20
18
|
DEFAULT_BRANCH_EXPIRY_SECONDS = 600
|
|
@@ -168,9 +166,10 @@ def _create_neon_branch(
|
|
|
168
166
|
raise RuntimeError(f"No endpoint created for branch {branch.id}")
|
|
169
167
|
|
|
170
168
|
# Wait for endpoint to be ready (it starts in "init" state)
|
|
171
|
-
# Endpoints typically become active in 1-2 seconds
|
|
169
|
+
# Endpoints typically become active in 1-2 seconds, but we allow up to 60s
|
|
170
|
+
# to handle occasional Neon API slowness or high load scenarios
|
|
172
171
|
max_wait_seconds = 60
|
|
173
|
-
poll_interval = 0.5
|
|
172
|
+
poll_interval = 0.5 # Poll every 500ms for responsive feedback
|
|
174
173
|
waited = 0.0
|
|
175
174
|
|
|
176
175
|
while True:
|
|
@@ -180,7 +179,7 @@ def _create_neon_branch(
|
|
|
180
179
|
endpoint = endpoint_response.endpoint
|
|
181
180
|
state = endpoint.current_state
|
|
182
181
|
|
|
183
|
-
if state ==
|
|
182
|
+
if state == EndpointState.active:
|
|
184
183
|
break
|
|
185
184
|
|
|
186
185
|
if waited >= max_wait_seconds:
|
|
@@ -254,7 +253,7 @@ def _reset_branch_to_parent(branch: NeonBranch, api_key: str) -> None:
|
|
|
254
253
|
"Content-Type": "application/json",
|
|
255
254
|
}
|
|
256
255
|
response = requests.post(
|
|
257
|
-
url, headers=headers, json={"source_branch_id": branch.parent_id}
|
|
256
|
+
url, headers=headers, json={"source_branch_id": branch.parent_id}, timeout=30
|
|
258
257
|
)
|
|
259
258
|
response.raise_for_status()
|
|
260
259
|
|
|
@@ -304,6 +303,15 @@ def neon_branch(
|
|
|
304
303
|
config = request.config
|
|
305
304
|
api_key = _get_config_value(config, "neon_api_key", "NEON_API_KEY")
|
|
306
305
|
|
|
306
|
+
# Validate that branch has a parent for reset functionality
|
|
307
|
+
if not _neon_branch_for_reset.parent_id:
|
|
308
|
+
pytest.fail(
|
|
309
|
+
f"\n\nBranch {_neon_branch_for_reset.branch_id} has no parent. "
|
|
310
|
+
f"The neon_branch fixture requires a parent branch for reset.\n\n"
|
|
311
|
+
f"Use neon_branch_shared if you don't need reset, or specify "
|
|
312
|
+
f"a parent branch with --neon-parent-branch or NEON_PARENT_BRANCH_ID."
|
|
313
|
+
)
|
|
314
|
+
|
|
307
315
|
yield _neon_branch_for_reset
|
|
308
316
|
|
|
309
317
|
# Reset branch to parent state after each test
|
|
@@ -311,11 +319,11 @@ def neon_branch(
|
|
|
311
319
|
try:
|
|
312
320
|
_reset_branch_to_parent(branch=_neon_branch_for_reset, api_key=api_key)
|
|
313
321
|
except Exception as e:
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
f"
|
|
318
|
-
|
|
322
|
+
pytest.fail(
|
|
323
|
+
f"\n\nFailed to reset branch {_neon_branch_for_reset.branch_id} "
|
|
324
|
+
f"after test. Subsequent tests in this module may see dirty "
|
|
325
|
+
f"database state.\n\nError: {e}\n\n"
|
|
326
|
+
f"To keep the branch for debugging, use --neon-keep-branches"
|
|
319
327
|
)
|
|
320
328
|
|
|
321
329
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-neon
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Pytest plugin for Neon database branch isolation in tests
|
|
5
5
|
Project-URL: Homepage, https://github.com/zain/pytest-neon
|
|
6
6
|
Project-URL: Repository, https://github.com/zain/pytest-neon
|
|
@@ -276,6 +276,28 @@ jobs:
|
|
|
276
276
|
|
|
277
277
|
Branches use copy-on-write storage, so you only pay for data that differs from the parent branch.
|
|
278
278
|
|
|
279
|
+
### What Reset Does
|
|
280
|
+
|
|
281
|
+
The `neon_branch` fixture uses Neon's branch restore API to reset database state after each test:
|
|
282
|
+
|
|
283
|
+
- **Data changes are reverted**: All INSERT, UPDATE, DELETE operations are undone
|
|
284
|
+
- **Schema changes are reverted**: CREATE TABLE, ALTER TABLE, DROP TABLE, etc. are undone
|
|
285
|
+
- **Sequences are reset**: Auto-increment counters return to parent state
|
|
286
|
+
- **Complete rollback**: The branch is restored to the exact state of the parent at the time the child branch was created
|
|
287
|
+
|
|
288
|
+
This is similar to database transactions but at the branch level.
|
|
289
|
+
|
|
290
|
+
## Limitations
|
|
291
|
+
|
|
292
|
+
### Parallel Test Execution
|
|
293
|
+
|
|
294
|
+
This plugin sets the `DATABASE_URL` environment variable, which is process-global. This means it is **not compatible with pytest-xdist** or other parallel test runners that run tests in the same process.
|
|
295
|
+
|
|
296
|
+
If you need parallel execution, you can:
|
|
297
|
+
- Use `neon_branch.connection_string` directly instead of relying on `DATABASE_URL`
|
|
298
|
+
- Run with `pytest-xdist --dist=loadfile` to keep modules in separate processes
|
|
299
|
+
- Run tests serially (default pytest behavior)
|
|
300
|
+
|
|
279
301
|
## Troubleshooting
|
|
280
302
|
|
|
281
303
|
### "psycopg not installed" or "psycopg2 not installed"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pytest_neon/__init__.py,sha256=RftEKwHM37DDwxA_oVN00DMXTh_3tlpaz6LbbN_IOYA,398
|
|
2
|
+
pytest_neon/plugin.py,sha256=A4eTJIV6XKQv4AIBizhg5qRDrvB_Hfx4ysTerjROHUg,17938
|
|
3
|
+
pytest_neon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
pytest_neon-0.3.0.dist-info/METADATA,sha256=JmC3wuJSupLxVqeDlgzpUyYYpawRArzGj96N2CtcTE0,10555
|
|
5
|
+
pytest_neon-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
+
pytest_neon-0.3.0.dist-info/entry_points.txt,sha256=5U88Idj_G8-PSDb9VF3OwYFbGLHnGOo_GxgYvi0dtXw,37
|
|
7
|
+
pytest_neon-0.3.0.dist-info/licenses/LICENSE,sha256=aKKp_Ex4WBHTByY4BhXJ181dzB_qYhi2pCUmZ7Spn_0,1067
|
|
8
|
+
pytest_neon-0.3.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
pytest_neon/__init__.py,sha256=tFnPqmxYhQPW1IlP53MTxeCumOOvS-OCuuMUn0hW7Zg,398
|
|
2
|
-
pytest_neon/plugin.py,sha256=-7NHdQ-nqfR-ZQXFmLBu_vV445LTLJ44UXnkQPtCgOQ,17217
|
|
3
|
-
pytest_neon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
pytest_neon-0.2.1.dist-info/METADATA,sha256=sNq5SF3HGzgCzh9Y1o9cGLSbPTcF8BV1kegvEdMeVt0,9496
|
|
5
|
-
pytest_neon-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
-
pytest_neon-0.2.1.dist-info/entry_points.txt,sha256=5U88Idj_G8-PSDb9VF3OwYFbGLHnGOo_GxgYvi0dtXw,37
|
|
7
|
-
pytest_neon-0.2.1.dist-info/licenses/LICENSE,sha256=aKKp_Ex4WBHTByY4BhXJ181dzB_qYhi2pCUmZ7Spn_0,1067
|
|
8
|
-
pytest_neon-0.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|