pytest-neon 2.3.2__tar.gz → 3.0.1__tar.gz
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-2.3.2 → pytest_neon-3.0.1}/.gitignore +3 -0
- pytest_neon-3.0.1/CLAUDE.md +167 -0
- pytest_neon-3.0.1/PKG-INFO +348 -0
- pytest_neon-3.0.1/README.md +302 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/pyproject.toml +1 -1
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/src/pytest_neon/__init__.py +3 -3
- pytest_neon-3.0.1/src/pytest_neon/plugin.py +1216 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/tests/conftest.py +3 -36
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/tests/test_branch_lifecycle.py +60 -67
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/tests/test_branch_name_prefix.py +63 -104
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/tests/test_integration.py +37 -67
- pytest_neon-3.0.1/tests/test_migrations.py +195 -0
- pytest_neon-3.0.1/tests/test_reset_behavior.py +255 -0
- pytest_neon-3.0.1/tests/test_xdist_worker_support.py +161 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/uv.lock +1 -1
- pytest_neon-2.3.2/CLAUDE.md +0 -120
- pytest_neon-2.3.2/PKG-INFO +0 -650
- pytest_neon-2.3.2/README.md +0 -604
- pytest_neon-2.3.2/src/pytest_neon/plugin.py +0 -2104
- pytest_neon-2.3.2/tests/test_dirty_isolated_fixtures.py +0 -400
- pytest_neon-2.3.2/tests/test_migrations.py +0 -156
- pytest_neon-2.3.2/tests/test_readwrite_readonly_fixtures.py +0 -385
- pytest_neon-2.3.2/tests/test_reset_behavior.py +0 -581
- pytest_neon-2.3.2/tests/test_xdist_worker_support.py +0 -238
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/.config/wt.toml +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/.env.example +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/.github/workflows/release.yml +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/.github/workflows/tests.yml +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/.neon +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/LICENSE +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/src/pytest_neon/py.typed +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/tests/test_cli_options.py +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/tests/test_default_branch_safety.py +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/tests/test_env_var.py +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/tests/test_fixture_errors.py +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/tests/test_service_classes.py +0 -0
- {pytest_neon-2.3.2 → pytest_neon-3.0.1}/tests/test_skip_behavior.py +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Claude Code Instructions for pytest-neon
|
|
2
|
+
|
|
3
|
+
## Understanding the Plugin
|
|
4
|
+
|
|
5
|
+
Read `README.md` for complete documentation on how to use this plugin, including fixtures, configuration options, and migration support.
|
|
6
|
+
|
|
7
|
+
## Project Overview
|
|
8
|
+
|
|
9
|
+
This is a pytest plugin that provides Neon database branches for integration testing. All tests share a single branch per session.
|
|
10
|
+
|
|
11
|
+
## Key Architecture
|
|
12
|
+
|
|
13
|
+
- **Entry point**: `src/pytest_neon/plugin.py` - Contains all fixtures and pytest hooks
|
|
14
|
+
- **Test branch fixture**: `_neon_test_branch` - Session-scoped, single branch for all tests
|
|
15
|
+
- **User migration hook**: `neon_apply_migrations` - Session-scoped no-op, users override to run migrations
|
|
16
|
+
- **Main fixture**: `neon_branch` - Session-scoped, shared branch for all tests
|
|
17
|
+
- **Convenience fixtures**: `neon_connection`, `neon_connection_psycopg`, `neon_engine` - Optional, require extras
|
|
18
|
+
|
|
19
|
+
## Branch Hierarchy
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
Parent Branch (configured or project default)
|
|
23
|
+
└── Test Branch (session-scoped, 10-min expiry)
|
|
24
|
+
↑ migrations run here ONCE, all tests share this
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Dependencies
|
|
28
|
+
|
|
29
|
+
- Core: `pytest`, `neon-api`, `requests`, `filelock`
|
|
30
|
+
- Optional extras: `psycopg2`, `psycopg`, `sqlalchemy` - for convenience fixtures
|
|
31
|
+
|
|
32
|
+
## Important Patterns
|
|
33
|
+
|
|
34
|
+
### Modular Architecture
|
|
35
|
+
|
|
36
|
+
The plugin uses a service-oriented architecture for testability:
|
|
37
|
+
|
|
38
|
+
- **NeonConfig**: Dataclass for configuration extraction from pytest config
|
|
39
|
+
- **NeonBranchManager**: Manages Neon API operations (branch create/delete)
|
|
40
|
+
- **XdistCoordinator**: Handles worker synchronization with file locks and JSON caching
|
|
41
|
+
- **EnvironmentManager**: Manages DATABASE_URL environment variable lifecycle
|
|
42
|
+
|
|
43
|
+
### Fixture Scopes
|
|
44
|
+
- `_neon_config`: `scope="session"` - Configuration extracted from pytest config
|
|
45
|
+
- `_neon_branch_manager`: `scope="session"` - Branch lifecycle manager
|
|
46
|
+
- `_neon_xdist_coordinator`: `scope="session"` - Worker synchronization
|
|
47
|
+
- `_neon_test_branch`: `scope="session"` - Internal, creates branch, yields (branch, is_creator)
|
|
48
|
+
- `neon_apply_migrations`: `scope="session"` - User overrides to run migrations
|
|
49
|
+
- `neon_branch`: `scope="session"` - User-facing, shared branch for all tests
|
|
50
|
+
- Connection fixtures: `scope="function"` - Fresh connection per test
|
|
51
|
+
|
|
52
|
+
### Environment Variable Handling
|
|
53
|
+
The `EnvironmentManager` class handles `DATABASE_URL` lifecycle:
|
|
54
|
+
- Sets environment variable when fixture starts
|
|
55
|
+
- Saves original value for restoration
|
|
56
|
+
- Restores original value (or removes) when fixture ends
|
|
57
|
+
|
|
58
|
+
### xdist Worker Synchronization
|
|
59
|
+
The `XdistCoordinator` handles sharing resources across workers:
|
|
60
|
+
- Uses file locks (`filelock`) for coordination
|
|
61
|
+
- Stores shared resource data in JSON files
|
|
62
|
+
- `coordinate_resource()` ensures only one worker creates shared resources
|
|
63
|
+
- `wait_for_signal()` / `send_signal()` for migration synchronization
|
|
64
|
+
- All workers share ONE branch (no per-worker branches)
|
|
65
|
+
|
|
66
|
+
### Error Messages
|
|
67
|
+
Convenience fixtures use `pytest.fail()` with detailed, formatted error messages when dependencies are missing. Keep this pattern - users need clear guidance on how to fix import errors.
|
|
68
|
+
|
|
69
|
+
## Test Isolation
|
|
70
|
+
|
|
71
|
+
Since all tests share the same branch, users should implement their own isolation:
|
|
72
|
+
1. **Transaction rollback** - Recommended for most cases
|
|
73
|
+
2. **Table truncation** - For cases where transactions aren't suitable
|
|
74
|
+
3. **Unique identifiers** - Use UUIDs to avoid conflicts
|
|
75
|
+
|
|
76
|
+
## Documentation
|
|
77
|
+
|
|
78
|
+
Important help text should be documented in BOTH:
|
|
79
|
+
1. **README.md** - Full user-facing documentation
|
|
80
|
+
2. **Module/fixture docstrings** - So `help(pytest_neon)` shows useful info
|
|
81
|
+
|
|
82
|
+
The module docstring in `plugin.py` should include key usage notes. Keep docstrings and README in sync.
|
|
83
|
+
|
|
84
|
+
## Commit Messages
|
|
85
|
+
- Do NOT add Claude attribution or Co-Authored-By lines
|
|
86
|
+
- Keep commits clean and descriptive
|
|
87
|
+
|
|
88
|
+
## Testing
|
|
89
|
+
|
|
90
|
+
Run tests with:
|
|
91
|
+
```bash
|
|
92
|
+
uv run pytest tests/ -v
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Tests in `tests/` use `pytester` for testing pytest plugins. The plugin itself can be tested without a real Neon connection by mocking `NeonAPI`.
|
|
96
|
+
|
|
97
|
+
## Publishing
|
|
98
|
+
|
|
99
|
+
**Always use the GitHub Actions release workflow** - do not manually bump versions:
|
|
100
|
+
1. Go to Actions → Release → Run workflow
|
|
101
|
+
2. Choose patch/minor/major
|
|
102
|
+
3. Workflow bumps version, commits, tags, and publishes to PyPI
|
|
103
|
+
|
|
104
|
+
Package name on PyPI: `pytest-neon`
|
|
105
|
+
|
|
106
|
+
<!-- bv-agent-instructions-v1 -->
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Beads Workflow Integration
|
|
111
|
+
|
|
112
|
+
This project uses [beads_viewer](https://github.com/Dicklesworthstone/beads_viewer) for issue tracking. Issues are stored in `.beads/` and tracked in git.
|
|
113
|
+
|
|
114
|
+
### Essential Commands
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# View issues (launches TUI - avoid in automated sessions)
|
|
118
|
+
bv
|
|
119
|
+
|
|
120
|
+
# CLI commands for agents (use these instead)
|
|
121
|
+
bd ready # Show issues ready to work (no blockers)
|
|
122
|
+
bd list --status=open # All open issues
|
|
123
|
+
bd show <id> # Full issue details with dependencies
|
|
124
|
+
bd create --title="..." --type=task --priority=2
|
|
125
|
+
bd update <id> --status=in_progress
|
|
126
|
+
bd close <id> --reason="Completed"
|
|
127
|
+
bd close <id1> <id2> # Close multiple issues at once
|
|
128
|
+
bd sync # Commit and push changes
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Workflow Pattern
|
|
132
|
+
|
|
133
|
+
1. **Start**: Run `bd ready` to find actionable work
|
|
134
|
+
2. **Claim**: Use `bd update <id> --status=in_progress`
|
|
135
|
+
3. **Work**: Implement the task
|
|
136
|
+
4. **Complete**: Use `bd close <id>`
|
|
137
|
+
5. **Sync**: Always run `bd sync` at session end
|
|
138
|
+
|
|
139
|
+
### Key Concepts
|
|
140
|
+
|
|
141
|
+
- **Dependencies**: Issues can block other issues. `bd ready` shows only unblocked work.
|
|
142
|
+
- **Priority**: P0=critical, P1=high, P2=medium, P3=low, P4=backlog (use numbers, not words)
|
|
143
|
+
- **Types**: task, bug, feature, epic, question, docs
|
|
144
|
+
- **Blocking**: `bd dep add <issue> <depends-on>` to add dependencies
|
|
145
|
+
|
|
146
|
+
### Session Protocol
|
|
147
|
+
|
|
148
|
+
**Before ending any session, run this checklist:**
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
git status # Check what changed
|
|
152
|
+
git add <files> # Stage code changes
|
|
153
|
+
bd sync # Commit beads changes
|
|
154
|
+
git commit -m "..." # Commit code
|
|
155
|
+
bd sync # Commit any new beads changes
|
|
156
|
+
git push # Push to remote
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Best Practices
|
|
160
|
+
|
|
161
|
+
- Check `bd ready` at session start to find available work
|
|
162
|
+
- Update status as you work (in_progress → closed)
|
|
163
|
+
- Create new issues with `bd create` when you discover tasks
|
|
164
|
+
- Use descriptive titles and set appropriate priority/type
|
|
165
|
+
- Always `bd sync` before ending session
|
|
166
|
+
|
|
167
|
+
<!-- end-bv-agent-instructions -->
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytest-neon
|
|
3
|
+
Version: 3.0.1
|
|
4
|
+
Summary: Pytest plugin for Neon database branch isolation in tests
|
|
5
|
+
Project-URL: Homepage, https://github.com/ZainRizvi/pytest-neon
|
|
6
|
+
Project-URL: Repository, https://github.com/ZainRizvi/pytest-neon
|
|
7
|
+
Project-URL: Issues, https://github.com/ZainRizvi/pytest-neon/issues
|
|
8
|
+
Author: Zain Rizvi
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: branching,database,neon,postgres,pytest,testing
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Framework :: Pytest
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Database
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Requires-Dist: filelock>=3.0
|
|
28
|
+
Requires-Dist: neon-api>=0.1.0
|
|
29
|
+
Requires-Dist: pytest>=7.0
|
|
30
|
+
Requires-Dist: requests>=2.20
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == 'dev'
|
|
34
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-mock>=3.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
38
|
+
Requires-Dist: sqlalchemy>=2.0; extra == 'dev'
|
|
39
|
+
Provides-Extra: psycopg
|
|
40
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'psycopg'
|
|
41
|
+
Provides-Extra: psycopg2
|
|
42
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == 'psycopg2'
|
|
43
|
+
Provides-Extra: sqlalchemy
|
|
44
|
+
Requires-Dist: sqlalchemy>=2.0; extra == 'sqlalchemy'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# pytest-neon
|
|
48
|
+
|
|
49
|
+
[](https://github.com/ZainRizvi/pytest-neon/actions/workflows/tests.yml)
|
|
50
|
+
|
|
51
|
+
A pytest plugin that provides Neon database branches for integration testing.
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- **Automatic branch management**: Creates a test branch at session start, deletes at end
|
|
56
|
+
- **Branch expiry**: Auto-cleanup via 10-minute expiry (crash-safe)
|
|
57
|
+
- **Migration support**: Run migrations once, all tests share the migrated schema
|
|
58
|
+
- **pytest-xdist support**: All workers share a single branch
|
|
59
|
+
- **Minimal API calls**: Single branch creation reduces rate limiting issues
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install pytest-neon
|
|
65
|
+
|
|
66
|
+
# With optional database drivers
|
|
67
|
+
pip install pytest-neon[psycopg] # psycopg v3 support
|
|
68
|
+
pip install pytest-neon[psycopg2] # psycopg2 support
|
|
69
|
+
pip install pytest-neon[sqlalchemy] # SQLAlchemy engine support
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Quick Start
|
|
73
|
+
|
|
74
|
+
1. Set environment variables:
|
|
75
|
+
```bash
|
|
76
|
+
export NEON_API_KEY="your-api-key"
|
|
77
|
+
export NEON_PROJECT_ID="your-project-id"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
2. Use the `neon_branch` fixture in your tests:
|
|
81
|
+
```python
|
|
82
|
+
def test_query_users(neon_branch):
|
|
83
|
+
import psycopg
|
|
84
|
+
with psycopg.connect(neon_branch.connection_string) as conn:
|
|
85
|
+
result = conn.execute("SELECT * FROM users").fetchall()
|
|
86
|
+
assert len(result) >= 0
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The `DATABASE_URL` environment variable is automatically set when the fixture is active.
|
|
90
|
+
|
|
91
|
+
## Fixtures
|
|
92
|
+
|
|
93
|
+
### `neon_branch` (session-scoped)
|
|
94
|
+
|
|
95
|
+
The main fixture providing a shared Neon branch for all tests.
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
def test_example(neon_branch):
|
|
99
|
+
# neon_branch.branch_id - Neon branch ID
|
|
100
|
+
# neon_branch.project_id - Neon project ID
|
|
101
|
+
# neon_branch.connection_string - PostgreSQL connection string
|
|
102
|
+
# neon_branch.host - Database host
|
|
103
|
+
pass
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Important**: All tests share the same branch. Data written by one test is visible to subsequent tests. See [Test Isolation](#test-isolation) for patterns to handle this.
|
|
107
|
+
|
|
108
|
+
### `neon_apply_migrations` (session-scoped)
|
|
109
|
+
|
|
110
|
+
Override this fixture to run migrations before tests:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
# conftest.py
|
|
114
|
+
@pytest.fixture(scope="session")
|
|
115
|
+
def neon_apply_migrations(_neon_test_branch):
|
|
116
|
+
"""Run database migrations."""
|
|
117
|
+
import subprocess
|
|
118
|
+
subprocess.run(["alembic", "upgrade", "head"], check=True)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Or with Django:
|
|
122
|
+
```python
|
|
123
|
+
@pytest.fixture(scope="session")
|
|
124
|
+
def neon_apply_migrations(_neon_test_branch):
|
|
125
|
+
from django.core.management import call_command
|
|
126
|
+
call_command("migrate", "--noinput")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Or with raw SQL:
|
|
130
|
+
```python
|
|
131
|
+
@pytest.fixture(scope="session")
|
|
132
|
+
def neon_apply_migrations(_neon_test_branch):
|
|
133
|
+
import psycopg
|
|
134
|
+
branch, is_creator = _neon_test_branch
|
|
135
|
+
with psycopg.connect(branch.connection_string) as conn:
|
|
136
|
+
with open("schema.sql") as f:
|
|
137
|
+
conn.execute(f.read())
|
|
138
|
+
conn.commit()
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Connection Fixtures (Optional)
|
|
142
|
+
|
|
143
|
+
These require extra dependencies:
|
|
144
|
+
|
|
145
|
+
**`neon_connection`** - psycopg2 connection (requires `pytest-neon[psycopg2]`)
|
|
146
|
+
```python
|
|
147
|
+
def test_insert(neon_connection):
|
|
148
|
+
cur = neon_connection.cursor()
|
|
149
|
+
cur.execute("INSERT INTO users (name) VALUES (%s)", ("test",))
|
|
150
|
+
neon_connection.commit()
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**`neon_connection_psycopg`** - psycopg v3 connection (requires `pytest-neon[psycopg]`)
|
|
154
|
+
```python
|
|
155
|
+
def test_insert(neon_connection_psycopg):
|
|
156
|
+
with neon_connection_psycopg.cursor() as cur:
|
|
157
|
+
cur.execute("INSERT INTO users (name) VALUES ('test')")
|
|
158
|
+
neon_connection_psycopg.commit()
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**`neon_engine`** - SQLAlchemy engine (requires `pytest-neon[sqlalchemy]`)
|
|
162
|
+
```python
|
|
163
|
+
def test_query(neon_engine):
|
|
164
|
+
from sqlalchemy import text
|
|
165
|
+
with neon_engine.connect() as conn:
|
|
166
|
+
result = conn.execute(text("SELECT 1"))
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Test Isolation
|
|
170
|
+
|
|
171
|
+
Since all tests share a single branch, you may need to handle test isolation yourself. Here are recommended patterns:
|
|
172
|
+
|
|
173
|
+
### Transaction Rollback (Recommended)
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
@pytest.fixture
|
|
177
|
+
def db_transaction(neon_branch):
|
|
178
|
+
"""Provide a database transaction that rolls back after each test."""
|
|
179
|
+
import psycopg
|
|
180
|
+
conn = psycopg.connect(neon_branch.connection_string)
|
|
181
|
+
conn.execute("BEGIN")
|
|
182
|
+
yield conn
|
|
183
|
+
conn.execute("ROLLBACK")
|
|
184
|
+
conn.close()
|
|
185
|
+
|
|
186
|
+
def test_insert(db_transaction):
|
|
187
|
+
db_transaction.execute("INSERT INTO users (name) VALUES ('test')")
|
|
188
|
+
# Automatically rolled back - next test won't see this
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Table Truncation
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
@pytest.fixture(autouse=True)
|
|
195
|
+
def clean_tables(neon_branch):
|
|
196
|
+
"""Clean up test data after each test."""
|
|
197
|
+
yield
|
|
198
|
+
import psycopg
|
|
199
|
+
with psycopg.connect(neon_branch.connection_string) as conn:
|
|
200
|
+
conn.execute("TRUNCATE users, orders CASCADE")
|
|
201
|
+
conn.commit()
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Unique Identifiers
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
import uuid
|
|
208
|
+
|
|
209
|
+
def test_create_user(neon_branch):
|
|
210
|
+
unique_id = uuid.uuid4().hex[:8]
|
|
211
|
+
email = f"test_{unique_id}@example.com"
|
|
212
|
+
# Create user with unique email - no conflicts with other tests
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Configuration
|
|
216
|
+
|
|
217
|
+
### Environment Variables
|
|
218
|
+
|
|
219
|
+
| Variable | Description |
|
|
220
|
+
|----------|-------------|
|
|
221
|
+
| `NEON_API_KEY` | Neon API key (required) |
|
|
222
|
+
| `NEON_PROJECT_ID` | Neon project ID (required) |
|
|
223
|
+
| `NEON_PARENT_BRANCH_ID` | Parent branch to create test branches from |
|
|
224
|
+
| `NEON_DATABASE` | Database name (default: `neondb`) |
|
|
225
|
+
| `NEON_ROLE` | Database role (default: `neondb_owner`) |
|
|
226
|
+
|
|
227
|
+
### Command Line Options
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
pytest --neon-api-key=KEY --neon-project-id=ID
|
|
231
|
+
pytest --neon-parent-branch=BRANCH_ID
|
|
232
|
+
pytest --neon-database=mydb --neon-role=myrole
|
|
233
|
+
pytest --neon-keep-branches # Don't delete branches (for debugging)
|
|
234
|
+
pytest --neon-branch-expiry=600 # Branch expiry in seconds (default: 600)
|
|
235
|
+
pytest --neon-env-var=CUSTOM_URL # Use custom env var instead of DATABASE_URL
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### pytest.ini / pyproject.toml
|
|
239
|
+
|
|
240
|
+
```ini
|
|
241
|
+
[pytest]
|
|
242
|
+
neon_api_key = your-api-key
|
|
243
|
+
neon_project_id = your-project-id
|
|
244
|
+
neon_parent_branch = br-parent-123
|
|
245
|
+
neon_database = mydb
|
|
246
|
+
neon_role = myrole
|
|
247
|
+
neon_keep_branches = false
|
|
248
|
+
neon_branch_expiry = 600
|
|
249
|
+
neon_env_var = DATABASE_URL
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Architecture
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
Parent Branch (configured or project default)
|
|
256
|
+
└── Test Branch (session-scoped, 10-min expiry)
|
|
257
|
+
↑ migrations run here ONCE, all tests share this
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The plugin creates exactly **one branch per test session**:
|
|
261
|
+
1. First test triggers branch creation with auto-expiry
|
|
262
|
+
2. Migrations run once (if `neon_apply_migrations` is overridden)
|
|
263
|
+
3. All tests share the same branch
|
|
264
|
+
4. Branch deleted at session end (plus auto-expiry as safety net)
|
|
265
|
+
|
|
266
|
+
### pytest-xdist Support
|
|
267
|
+
|
|
268
|
+
When running with pytest-xdist, all workers share the same branch:
|
|
269
|
+
- First worker creates the branch and runs migrations
|
|
270
|
+
- Other workers wait for migrations to complete
|
|
271
|
+
- All workers see the same database state
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
pytest -n 4 # 4 workers, all sharing one branch
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Branch Naming
|
|
278
|
+
|
|
279
|
+
Branches are automatically named to help identify their source:
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
pytest-[git-branch]-[random]-test
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Examples:**
|
|
286
|
+
- `pytest-main-a1b2-test` - Test branch from `main`
|
|
287
|
+
- `pytest-feature-auth-c3d4-test` - Test branch from `feature/auth`
|
|
288
|
+
- `pytest-a1b2-test` - When not in a git repo
|
|
289
|
+
|
|
290
|
+
The git branch name is sanitized (only `a-z`, `0-9`, `-`, `_` allowed) and truncated to 15 characters.
|
|
291
|
+
|
|
292
|
+
## Upgrading from v2.x
|
|
293
|
+
|
|
294
|
+
Version 3.0 simplifies the plugin significantly. If you're upgrading from v2.x:
|
|
295
|
+
|
|
296
|
+
### Removed Fixtures
|
|
297
|
+
|
|
298
|
+
These fixtures have been removed:
|
|
299
|
+
- `neon_branch_readonly` → use `neon_branch`
|
|
300
|
+
- `neon_branch_readwrite` → use `neon_branch`
|
|
301
|
+
- `neon_branch_isolated` → use `neon_branch` + transaction rollback
|
|
302
|
+
- `neon_branch_dirty` → use `neon_branch`
|
|
303
|
+
- `neon_branch_shared` → use `neon_branch`
|
|
304
|
+
|
|
305
|
+
### Migration Hook Change
|
|
306
|
+
|
|
307
|
+
The migration hook now uses `_neon_test_branch` instead of `_neon_migration_branch`:
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
# Before (v2.x)
|
|
311
|
+
@pytest.fixture(scope="session")
|
|
312
|
+
def neon_apply_migrations(_neon_migration_branch):
|
|
313
|
+
...
|
|
314
|
+
|
|
315
|
+
# After (v3.x)
|
|
316
|
+
@pytest.fixture(scope="session")
|
|
317
|
+
def neon_apply_migrations(_neon_test_branch):
|
|
318
|
+
...
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### No Per-Test Reset
|
|
322
|
+
|
|
323
|
+
The v2.x `neon_branch_isolated` fixture reset the branch after each test. In v3.x, there's no automatic reset. Use transaction rollback or cleanup fixtures for test isolation.
|
|
324
|
+
|
|
325
|
+
## Troubleshooting
|
|
326
|
+
|
|
327
|
+
### Rate Limiting
|
|
328
|
+
|
|
329
|
+
The plugin includes automatic retry with exponential backoff for Neon API rate limits. If you're hitting rate limits:
|
|
330
|
+
- The plugin creates only 1-2 API calls per session (create + delete)
|
|
331
|
+
- Consider increasing `--neon-branch-expiry` to reduce cleanup calls
|
|
332
|
+
|
|
333
|
+
### Stale Connections (SQLAlchemy)
|
|
334
|
+
|
|
335
|
+
If using SQLAlchemy with connection pooling, use `pool_pre_ping=True`:
|
|
336
|
+
```python
|
|
337
|
+
engine = create_engine(DATABASE_URL, pool_pre_ping=True)
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
This is a best practice for any cloud database where connections can be terminated externally.
|
|
341
|
+
|
|
342
|
+
### Branch Not Deleted
|
|
343
|
+
|
|
344
|
+
If a test run crashes, the branch auto-expires after 10 minutes (configurable). You can also use `--neon-keep-branches` to prevent deletion for debugging.
|
|
345
|
+
|
|
346
|
+
## License
|
|
347
|
+
|
|
348
|
+
MIT
|