pytest-neon 2.3.1__py3-none-any.whl → 2.3.2__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 +44 -27
- {pytest_neon-2.3.1.dist-info → pytest_neon-2.3.2.dist-info}/METADATA +1 -1
- pytest_neon-2.3.2.dist-info/RECORD +8 -0
- pytest_neon-2.3.1.dist-info/RECORD +0 -8
- {pytest_neon-2.3.1.dist-info → pytest_neon-2.3.2.dist-info}/WHEEL +0 -0
- {pytest_neon-2.3.1.dist-info → pytest_neon-2.3.2.dist-info}/entry_points.txt +0 -0
- {pytest_neon-2.3.1.dist-info → pytest_neon-2.3.2.dist-info}/licenses/LICENSE +0 -0
pytest_neon/__init__.py
CHANGED
pytest_neon/plugin.py
CHANGED
|
@@ -306,6 +306,37 @@ def _extract_password_from_connection_string(connection_string: str) -> str:
|
|
|
306
306
|
raise ValueError(f"No password found in connection string: {connection_string}")
|
|
307
307
|
|
|
308
308
|
|
|
309
|
+
def _reveal_role_password(
|
|
310
|
+
api_key: str, project_id: str, branch_id: str, role_name: str
|
|
311
|
+
) -> str:
|
|
312
|
+
"""
|
|
313
|
+
Get the password for a role WITHOUT resetting it.
|
|
314
|
+
|
|
315
|
+
Uses Neon's reveal_password API endpoint (GET request).
|
|
316
|
+
|
|
317
|
+
Note: The neon-api library has a bug where it uses POST instead of GET,
|
|
318
|
+
so we make the request directly.
|
|
319
|
+
"""
|
|
320
|
+
url = (
|
|
321
|
+
f"https://console.neon.tech/api/v2/projects/{project_id}"
|
|
322
|
+
f"/branches/{branch_id}/roles/{role_name}/reveal_password"
|
|
323
|
+
)
|
|
324
|
+
headers = {
|
|
325
|
+
"Authorization": f"Bearer {api_key}",
|
|
326
|
+
"Accept": "application/json",
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
response = requests.get(url, headers=headers, timeout=30)
|
|
330
|
+
try:
|
|
331
|
+
response.raise_for_status()
|
|
332
|
+
except requests.exceptions.HTTPError:
|
|
333
|
+
# Wrap in NeonAPIError for consistent error handling
|
|
334
|
+
raise NeonAPIError(response.text) from None
|
|
335
|
+
|
|
336
|
+
data = response.json()
|
|
337
|
+
return data["password"]
|
|
338
|
+
|
|
339
|
+
|
|
309
340
|
def _get_schema_fingerprint(connection_string: str) -> tuple[tuple[Any, ...], ...]:
|
|
310
341
|
"""
|
|
311
342
|
Get a fingerprint of the database schema for change detection.
|
|
@@ -508,7 +539,7 @@ class NeonBranchManager:
|
|
|
508
539
|
)
|
|
509
540
|
|
|
510
541
|
# Get password
|
|
511
|
-
connection_string = self.
|
|
542
|
+
connection_string = self._get_password_and_build_connection_string(
|
|
512
543
|
branch.id, host
|
|
513
544
|
)
|
|
514
545
|
|
|
@@ -632,19 +663,19 @@ class NeonBranchManager:
|
|
|
632
663
|
time.sleep(poll_interval)
|
|
633
664
|
waited += poll_interval
|
|
634
665
|
|
|
635
|
-
def
|
|
666
|
+
def _get_password_and_build_connection_string(
|
|
636
667
|
self, branch_id: str, host: str
|
|
637
668
|
) -> str:
|
|
638
|
-
"""
|
|
639
|
-
|
|
640
|
-
lambda:
|
|
669
|
+
"""Get role password (without resetting) and build connection string."""
|
|
670
|
+
password = _retry_on_rate_limit(
|
|
671
|
+
lambda: _reveal_role_password(
|
|
672
|
+
api_key=self.config.api_key,
|
|
641
673
|
project_id=self.config.project_id,
|
|
642
674
|
branch_id=branch_id,
|
|
643
675
|
role_name=self.config.role_name,
|
|
644
676
|
),
|
|
645
|
-
operation_name="
|
|
677
|
+
operation_name="role_password_reveal",
|
|
646
678
|
)
|
|
647
|
-
password = password_response.role.password
|
|
648
679
|
|
|
649
680
|
return (
|
|
650
681
|
f"postgresql://{self.config.role_name}:{password}@{host}/"
|
|
@@ -1047,31 +1078,17 @@ def _create_neon_branch(
|
|
|
1047
1078
|
|
|
1048
1079
|
host = endpoint.host
|
|
1049
1080
|
|
|
1050
|
-
#
|
|
1051
|
-
#
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
raise RuntimeError(
|
|
1056
|
-
f"SAFETY CHECK FAILED: Attempted to reset password on default branch "
|
|
1057
|
-
f"{branch.id}. This should never happen - the plugin creates new "
|
|
1058
|
-
f"branches and should never operate on the default branch. "
|
|
1059
|
-
f"Please report this bug at https://github.com/ZainRizvi/pytest-neon/issues"
|
|
1060
|
-
)
|
|
1061
|
-
|
|
1062
|
-
# Reset password to get the password value
|
|
1063
|
-
# (newly created branches don't expose password)
|
|
1064
|
-
# Wrap in retry logic to handle rate limits
|
|
1065
|
-
# See: https://api-docs.neon.tech/reference/api-rate-limiting
|
|
1066
|
-
password_response = _retry_on_rate_limit(
|
|
1067
|
-
lambda: neon.role_password_reset(
|
|
1081
|
+
# Get password using reveal (not reset) to avoid invalidating existing connections
|
|
1082
|
+
# See: https://api-docs.neon.tech/reference/getprojectbranchrolepassword
|
|
1083
|
+
password = _retry_on_rate_limit(
|
|
1084
|
+
lambda: _reveal_role_password(
|
|
1085
|
+
api_key=api_key,
|
|
1068
1086
|
project_id=project_id,
|
|
1069
1087
|
branch_id=branch.id,
|
|
1070
1088
|
role_name=role_name,
|
|
1071
1089
|
),
|
|
1072
|
-
operation_name="
|
|
1090
|
+
operation_name="role_password_reveal",
|
|
1073
1091
|
)
|
|
1074
|
-
password = password_response.role.password
|
|
1075
1092
|
|
|
1076
1093
|
# Build connection string
|
|
1077
1094
|
connection_string = (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-neon
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.2
|
|
4
4
|
Summary: Pytest plugin for Neon database branch isolation in tests
|
|
5
5
|
Project-URL: Homepage, https://github.com/ZainRizvi/pytest-neon
|
|
6
6
|
Project-URL: Repository, https://github.com/ZainRizvi/pytest-neon
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pytest_neon/__init__.py,sha256=fFWOT5wYJg_WkV_FjkC8rv2YhvuyJAEHIGdsaG4c7iY,398
|
|
2
|
+
pytest_neon/plugin.py,sha256=Kokmw9qvfDiuvvqJMSx3mTJ7tKHQ4GouwvE9wvHbEZ4,76270
|
|
3
|
+
pytest_neon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
pytest_neon-2.3.2.dist-info/METADATA,sha256=aY4AnACisvVnH2L1Raf5VzK4RNNMWwuU9ctmCZKI0ic,23149
|
|
5
|
+
pytest_neon-2.3.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
+
pytest_neon-2.3.2.dist-info/entry_points.txt,sha256=5U88Idj_G8-PSDb9VF3OwYFbGLHnGOo_GxgYvi0dtXw,37
|
|
7
|
+
pytest_neon-2.3.2.dist-info/licenses/LICENSE,sha256=aKKp_Ex4WBHTByY4BhXJ181dzB_qYhi2pCUmZ7Spn_0,1067
|
|
8
|
+
pytest_neon-2.3.2.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
pytest_neon/__init__.py,sha256=jUIgvcpeZvbOiwyAZEkOnI5KkA1Pb4BYefeUq7xkrqM,398
|
|
2
|
-
pytest_neon/plugin.py,sha256=Lim03yyXviIpxQbHGZK8KUvG0AlwXZg7TtVDUGwHKDo,76166
|
|
3
|
-
pytest_neon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
pytest_neon-2.3.1.dist-info/METADATA,sha256=tNmTJvn0Rll969BcHvrPaacw9V1pARmFwzFIiSzxYAg,23149
|
|
5
|
-
pytest_neon-2.3.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
-
pytest_neon-2.3.1.dist-info/entry_points.txt,sha256=5U88Idj_G8-PSDb9VF3OwYFbGLHnGOo_GxgYvi0dtXw,37
|
|
7
|
-
pytest_neon-2.3.1.dist-info/licenses/LICENSE,sha256=aKKp_Ex4WBHTByY4BhXJ181dzB_qYhi2pCUmZ7Spn_0,1067
|
|
8
|
-
pytest_neon-2.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|