fast-feature-testing 0.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.
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: fast-feature-testing
3
+ Version: 0.0.1
4
+ Summary: Reusable pytest contract for fast-feature FlagRepository implementations.
5
+ Author: byunjuneseok
6
+ Author-email: byunjuneseok <byunjuneseok@gmail.com>
7
+ License-Expression: Apache-2.0
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Framework :: Pytest
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: Apache Software License
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Typing :: Typed
17
+ Requires-Dist: fast-feature-core==0.0.1
18
+ Requires-Dist: pytest>=8.0
19
+ Requires-Python: >=3.10
@@ -0,0 +1,34 @@
1
+ [project]
2
+ name = "fast-feature-testing"
3
+ version = "0.0.1"
4
+ description = "Reusable pytest contract for fast-feature FlagRepository implementations."
5
+ license = "Apache-2.0"
6
+ requires-python = ">=3.10"
7
+ authors = [
8
+ { name = "byunjuneseok", email = "byunjuneseok@gmail.com" },
9
+ ]
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Framework :: Pytest",
13
+ "Intended Audience :: Developers",
14
+ "License :: OSI Approved :: Apache Software License",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Typing :: Typed",
20
+ ]
21
+ dependencies = [
22
+ "fast-feature-core==0.0.1",
23
+ "pytest>=8.0",
24
+ ]
25
+
26
+ [build-system]
27
+ requires = ["uv_build>=0.11.17,<0.12.0"]
28
+ build-backend = "uv_build"
29
+
30
+ [tool.uv.build-backend]
31
+ module-name = "fast_feature.testing"
32
+
33
+ [tool.uv.sources]
34
+ fast-feature-core = { workspace = true }
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from .contract import FlagRepositoryContract
4
+
5
+ __all__ = ["FlagRepositoryContract"]
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ import pytest
4
+
5
+ from fast_feature.core import (
6
+ Flag,
7
+ FlagAlreadyExistsError,
8
+ FlagNotFoundError,
9
+ FlagRepository,
10
+ )
11
+
12
+
13
+ class FlagRepositoryContract:
14
+ """Reusable pytest contract every ``FlagRepository`` must satisfy.
15
+
16
+ Subclass it and provide a ``repository`` fixture that returns a fresh,
17
+ empty repository::
18
+
19
+ class TestInMemory(FlagRepositoryContract):
20
+ @pytest.fixture
21
+ def repository(self) -> FlagRepository:
22
+ return InMemoryFlagRepository()
23
+ """
24
+
25
+ @staticmethod
26
+ def _flag(key: str = "flag", *, default: str = "off") -> Flag:
27
+ return Flag(key=key, variants={"on": True, "off": False}, default_variant=default)
28
+
29
+ async def test_create_then_get(self, repository: FlagRepository) -> None:
30
+ await repository.create(self._flag("a"))
31
+ loaded = await repository.get("a")
32
+ assert loaded is not None
33
+ assert loaded.key == "a"
34
+ assert loaded.variants == {"on": True, "off": False}
35
+
36
+ async def test_get_missing_returns_none(self, repository: FlagRepository) -> None:
37
+ assert await repository.get("nope") is None
38
+
39
+ async def test_create_duplicate_raises(self, repository: FlagRepository) -> None:
40
+ await repository.create(self._flag("a"))
41
+ with pytest.raises(FlagAlreadyExistsError):
42
+ await repository.create(self._flag("a"))
43
+
44
+ async def test_update_replaces(self, repository: FlagRepository) -> None:
45
+ await repository.create(self._flag("a", default="off"))
46
+ await repository.update(self._flag("a", default="on"))
47
+ loaded = await repository.get("a")
48
+ assert loaded is not None
49
+ assert loaded.default_variant == "on"
50
+
51
+ async def test_update_missing_raises(self, repository: FlagRepository) -> None:
52
+ with pytest.raises(FlagNotFoundError):
53
+ await repository.update(self._flag("ghost"))
54
+
55
+ async def test_delete_removes(self, repository: FlagRepository) -> None:
56
+ await repository.create(self._flag("a"))
57
+ await repository.delete("a")
58
+ assert await repository.get("a") is None
59
+
60
+ async def test_delete_missing_raises(self, repository: FlagRepository) -> None:
61
+ with pytest.raises(FlagNotFoundError):
62
+ await repository.delete("ghost")
63
+
64
+ async def test_list_all_sorted_by_key(self, repository: FlagRepository) -> None:
65
+ for key in ("c", "a", "b"):
66
+ await repository.create(self._flag(key))
67
+ assert [flag.key for flag in await repository.list_all()] == ["a", "b", "c"]
68
+
69
+ async def test_exists(self, repository: FlagRepository) -> None:
70
+ await repository.create(self._flag("a"))
71
+ assert await repository.exists("a") is True
72
+ assert await repository.exists("nope") is False
73
+
74
+ async def test_stored_flag_is_isolated_from_input(self, repository: FlagRepository) -> None:
75
+ flag = self._flag("a")
76
+ await repository.create(flag)
77
+ flag.variants["on"] = "mutated"
78
+ loaded = await repository.get("a")
79
+ assert loaded is not None
80
+ assert loaded.variants["on"] is True