pytest-dblift 0.1.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.
@@ -0,0 +1,3 @@
1
+ """pytest-dblift: pytest plugin for DBLift migrations."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,71 @@
1
+ """URL resolution and DBLiftClient construction for pytest-dblift fixtures."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ import pytest
9
+
10
+ from api import DBLiftClient
11
+
12
+
13
+ def _worker_id(config: pytest.Config) -> str:
14
+ """Return xdist worker id ('gw0', ...) or 'master' when not under xdist."""
15
+ workerinput = getattr(config, "workerinput", None)
16
+ if workerinput:
17
+ return workerinput.get("workerid", "master")
18
+ return "master"
19
+
20
+
21
+ def default_sqlite_file_url(
22
+ tmp_path_factory: pytest.TempPathFactory, config: pytest.Config | None = None
23
+ ) -> str:
24
+ """Session-scoped temp SQLite file URL. Under xdist, suffix the filename with the worker id."""
25
+ base = tmp_path_factory.mktemp("dblift_pytest", numbered=True)
26
+ wid = _worker_id(config) if config is not None else "master"
27
+ if wid != "master":
28
+ db_path = base / f"test_{wid}.db"
29
+ else:
30
+ db_path = base / "test.db"
31
+ return f"sqlite:///{db_path}"
32
+
33
+
34
+ def resolve_dblift_config(
35
+ pytestconfig: pytest.Config,
36
+ *,
37
+ tmp_path_factory: pytest.TempPathFactory,
38
+ ) -> dict[str, Any]:
39
+ """Build config dict from CLI options + defaults.
40
+
41
+ Returns dict with 'url' and 'migrations_dir' (absolute path str).
42
+ A relative migrations dir is resolved against pytest rootdir.
43
+ """
44
+ url = pytestconfig.getoption("--dblift-url")
45
+ if not url:
46
+ url = default_sqlite_file_url(tmp_path_factory, pytestconfig)
47
+
48
+ raw_mig = pytestconfig.getoption("--dblift-migrations-dir") or "migrations"
49
+ rootdir = getattr(pytestconfig, "rootdir", None) or Path.cwd()
50
+ rootdir = Path(rootdir)
51
+ mig_path = Path(raw_mig)
52
+ if not mig_path.is_absolute():
53
+ mig_path = (rootdir / mig_path).resolve()
54
+
55
+ return {
56
+ "url": url,
57
+ "migrations_dir": str(mig_path),
58
+ }
59
+
60
+
61
+ def create_dblift_client(
62
+ engine: Any,
63
+ *,
64
+ migrations_dir: str | Path | list[str | Path] | None,
65
+ schema: str | None = None,
66
+ ) -> DBLiftClient:
67
+ return DBLiftClient.from_sqlalchemy(
68
+ engine,
69
+ migrations_dir=migrations_dir,
70
+ schema=schema,
71
+ )
@@ -0,0 +1,84 @@
1
+ """pytest-dblift fixtures.
2
+
3
+ Session scope for config/engine/client. Function scope for migrate/clean/validate/undo.
4
+ No autouse: tests opt in by requesting a fixture.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Callable, Iterator
10
+
11
+ import pytest
12
+ from sqlalchemy import create_engine
13
+
14
+ from api import DBLiftClient
15
+
16
+ from ._client import create_dblift_client, resolve_dblift_config
17
+
18
+
19
+ @pytest.fixture(scope="session")
20
+ def dblift_config(
21
+ pytestconfig: pytest.Config, tmp_path_factory: pytest.TempPathFactory
22
+ ) -> dict[str, Any]:
23
+ """Session config from CLI or temp SQLite. Overridable in consumer conftest.py."""
24
+ return resolve_dblift_config(pytestconfig, tmp_path_factory=tmp_path_factory)
25
+
26
+
27
+ @pytest.fixture(scope="session")
28
+ def dblift_engine(dblift_config: dict[str, Any]) -> Iterator[Any]:
29
+ engine = create_engine(dblift_config["url"])
30
+ yield engine
31
+ engine.dispose()
32
+
33
+
34
+ @pytest.fixture(scope="session")
35
+ def dblift_client(dblift_engine: Any, dblift_config: dict[str, Any]) -> Iterator[DBLiftClient]:
36
+ client = create_dblift_client(
37
+ dblift_engine,
38
+ migrations_dir=dblift_config.get("migrations_dir"),
39
+ schema=dblift_config.get("schema"),
40
+ )
41
+ yield client
42
+ client.close()
43
+
44
+
45
+ @pytest.fixture
46
+ def dblift_migrated_db(dblift_client: DBLiftClient) -> Iterator[DBLiftClient]:
47
+ result = dblift_client.migrate()
48
+ assert getattr(result, "success", False), (
49
+ f"migrate failed: {getattr(result, 'error_message', result)}"
50
+ )
51
+ yield dblift_client
52
+
53
+
54
+ @pytest.fixture
55
+ def dblift_empty_db(dblift_client: DBLiftClient) -> Iterator[DBLiftClient]:
56
+ result = dblift_client.clean(clean_enabled=True)
57
+ assert getattr(result, "success", False), (
58
+ f"clean failed: {getattr(result, 'error_message', result)}"
59
+ )
60
+ yield dblift_client
61
+
62
+
63
+ @pytest.fixture
64
+ def dblift_validate(dblift_client: DBLiftClient) -> Callable[..., Any]:
65
+ def _run_validate(**kwargs: Any) -> Any:
66
+ result = dblift_client.validate(**kwargs)
67
+ assert getattr(result, "success", False), (
68
+ f"validate failed: {getattr(result, 'error_message', result)}"
69
+ )
70
+ return result
71
+
72
+ return _run_validate
73
+
74
+
75
+ @pytest.fixture
76
+ def dblift_undo(dblift_client: DBLiftClient) -> Callable[..., Any]:
77
+ def _run_undo(**kwargs: Any) -> Any:
78
+ result = dblift_client.undo(**kwargs)
79
+ assert getattr(result, "success", False), (
80
+ f"undo failed: {getattr(result, 'error_message', result)}"
81
+ )
82
+ return result
83
+
84
+ return _run_undo
@@ -0,0 +1,31 @@
1
+ """pytest11 entry: CLI options and fixture loading."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+
7
+ pytest_plugins = ["pytest_dblift.fixtures"]
8
+
9
+
10
+ def pytest_addoption(parser: pytest.Parser) -> None:
11
+ group = parser.getgroup("dblift", "dblift pytest integration")
12
+ group.addoption(
13
+ "--dblift-url",
14
+ action="store",
15
+ default=None,
16
+ help="Database URL for dblift (e.g. sqlite:////tmp/test.db or postgresql+psycopg://...). "
17
+ "Used when no dblift_config fixture override is provided.",
18
+ )
19
+ group.addoption(
20
+ "--dblift-migrations-dir",
21
+ action="store",
22
+ default="migrations",
23
+ help="Path to the migrations directory. Defaults to migrations.",
24
+ )
25
+
26
+
27
+ def pytest_configure(config: pytest.Config) -> None:
28
+ config.addinivalue_line(
29
+ "markers",
30
+ "dblift: marks tests as using dblift fixtures (provided by pytest-dblift)",
31
+ )
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytest-dblift
3
+ Version: 0.1.0
4
+ Summary: pytest plugin for DBLift migrations
5
+ Author: DBLift
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/dblift/dblift
8
+ Classifier: Framework :: Pytest
9
+ Classifier: License :: OSI Approved :: Apache Software License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Python: >=3.11
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: dblift>=3.9
16
+ Requires-Dist: pytest>=7.3
17
+ Requires-Dist: sqlalchemy>=2.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest-xdist>=3.3; extra == "dev"
20
+
21
+ # pytest-dblift
22
+
23
+ pytest plugin for [DBLift](https://github.com/dblift/dblift). It applies your migrations in tests and exposes a `DBLiftClient`.
24
+
25
+ This is a **separate PyPI package**, not `dblift[pytest]`.
26
+
27
+ ## Install
28
+
29
+ SQLite (default, stdlib driver):
30
+
31
+ ```bash
32
+ pip install pytest-dblift
33
+ ```
34
+
35
+ Any other engine: install the matching dblift extra so the native driver is present. The plugin does not install drivers and does not open a second connection — it calls `create_engine(url)` then `DBLiftClient.from_sqlalchemy`.
36
+
37
+ ```bash
38
+ pip install pytest-dblift "dblift[postgresql]"
39
+ pytest --dblift-url "postgresql+psycopg://user:pass@localhost/app_test"
40
+ ```
41
+
42
+ ## Quickstart
43
+
44
+ ```python
45
+ def test_schema(dblift_migrated_db, dblift_client):
46
+ info = dblift_client.info()
47
+ assert info.pending_migrations == []
48
+ ```
49
+
50
+ `dblift_migrated_db` applies pending migrations (function scope). `dblift_client` is a session-scoped `DBLiftClient`. No fixture is autouse: request what you need.
51
+
52
+ ## Fixtures
53
+
54
+ | Fixture | Scope | Role |
55
+ | --- | --- | --- |
56
+ | `dblift_config` | session | Dict with `url`, `migrations_dir`, optional `schema`. Override in `conftest.py`. |
57
+ | `dblift_engine` | session | SQLAlchemy engine from that URL. Override to inject your app engine. |
58
+ | `dblift_client` | session | `DBLiftClient.from_sqlalchemy(...)`. |
59
+ | `dblift_migrated_db` | function | `client.migrate()`, then yield the client. |
60
+ | `dblift_empty_db` | function | `client.clean(clean_enabled=True)`, then yield the client. |
61
+ | `dblift_validate` | function | Callable: `dblift_validate(**kwargs)` runs `client.validate` and asserts success. |
62
+ | `dblift_undo` | function | Callable: `dblift_undo(**kwargs)` runs `client.undo` and asserts success. Does not migrate. |
63
+
64
+ Undo uses companion `U*` scripts (same as dblift). There is no `undo()` function inside a `V*.py` file.
65
+
66
+ ```python
67
+ def test_rollback(dblift_migrated_db, dblift_undo):
68
+ result = dblift_undo(target_version="0")
69
+ assert result.success
70
+ ```
71
+
72
+ Override config in `tests/conftest.py`:
73
+
74
+ ```python
75
+ import pytest
76
+
77
+ @pytest.fixture(scope="session")
78
+ def dblift_config():
79
+ return {
80
+ "url": "postgresql+psycopg://user:pass@localhost/app_test",
81
+ "migrations_dir": "migrations",
82
+ }
83
+ ```
84
+
85
+ ## CLI options
86
+
87
+ | Option | What it does |
88
+ | --- | --- |
89
+ | `--dblift-url` | Database URL when `dblift_config` is not overridden. Default: a temp SQLite **file**. |
90
+ | `--dblift-migrations-dir` | One migrations directory (not a comma-separated list). Default: `migrations`. |
91
+
92
+ pytest-xdist: only the default SQLite file is per-worker (`test_gw0.db`, …). A URL you pass with `--dblift-url` is used as-is.
@@ -0,0 +1,9 @@
1
+ pytest_dblift/__init__.py,sha256=ug4A_sjForoM-WH-Yz0oG1mr2ZsKpJTWlJRaNHFAXMg,81
2
+ pytest_dblift/_client.py,sha256=788Z9jNmKVUnfw9v5O0XlD35tlapKROtNhUHx7Z0Z0w,2111
3
+ pytest_dblift/fixtures.py,sha256=YwtHGpDtbRRfNomwovuPly4UIRchgc6Dr5Aco_06GTI,2535
4
+ pytest_dblift/plugin.py,sha256=jSqJpEyiciPRWvocyxLxfywmdfwsxdQXQRvQpClUoAA,920
5
+ pytest_dblift-0.1.0.dist-info/METADATA,sha256=PoLjl35UfMUtr4H2NDIwWBc_ziT9zbkZSzPDBMYZgOM,3288
6
+ pytest_dblift-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
7
+ pytest_dblift-0.1.0.dist-info/entry_points.txt,sha256=YxMThYqLZg4KkQg2d6bA3sPHZPkACTBgaQMwMK870ms,41
8
+ pytest_dblift-0.1.0.dist-info/top_level.txt,sha256=jJf1n0xrr0N-iEXJYOFdEtgenXQrivW5FZps5PMEvZY,14
9
+ pytest_dblift-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [pytest11]
2
+ dblift = pytest_dblift.plugin
@@ -0,0 +1 @@
1
+ pytest_dblift