-
Notifications
You must be signed in to change notification settings - Fork 7
Improve code coverage with targeted tests #253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -175,3 +175,70 @@ | |||||||||||||||||||||||||||||
| assert key_str_one != key_str_two | ||||||||||||||||||||||||||||||
| assert key_str_one != key_str_three | ||||||||||||||||||||||||||||||
| assert key_str_two != key_str_three | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_fernet_with_source_material_and_salt(memory_store: MemoryStore): | ||||||||||||||||||||||||||||||
| """Test that FernetEncryptionWrapper works with source_material and salt.""" | ||||||||||||||||||||||||||||||
| wrapper = FernetEncryptionWrapper( | ||||||||||||||||||||||||||||||
| key_value=memory_store, | ||||||||||||||||||||||||||||||
| source_material="my-secret-key", | ||||||||||||||||||||||||||||||
| salt="my-unique-salt", | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| assert wrapper is not None | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_fernet_cannot_provide_fernet_with_source_material(memory_store: MemoryStore): | ||||||||||||||||||||||||||||||
| """Test that providing both fernet and source_material raises ValueError.""" | ||||||||||||||||||||||||||||||
| fernet = Fernet(key=Fernet.generate_key()) | ||||||||||||||||||||||||||||||
| with pytest.raises(ValueError, match="Cannot provide fernet together with source_material or salt"): | ||||||||||||||||||||||||||||||
| FernetEncryptionWrapper( | ||||||||||||||||||||||||||||||
|
Check failure on line 194 in key-value/key-value-aio/tests/stores/wrappers/test_encryption.py
|
||||||||||||||||||||||||||||||
| key_value=memory_store, | ||||||||||||||||||||||||||||||
| fernet=fernet, | ||||||||||||||||||||||||||||||
| source_material="test", | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_fernet_cannot_provide_fernet_with_salt(memory_store: MemoryStore): | ||||||||||||||||||||||||||||||
| """Test that providing both fernet and salt raises ValueError.""" | ||||||||||||||||||||||||||||||
| fernet = Fernet(key=Fernet.generate_key()) | ||||||||||||||||||||||||||||||
| with pytest.raises(ValueError, match="Cannot provide fernet together with source_material or salt"): | ||||||||||||||||||||||||||||||
| FernetEncryptionWrapper( | ||||||||||||||||||||||||||||||
|
Check failure on line 205 in key-value/key-value-aio/tests/stores/wrappers/test_encryption.py
|
||||||||||||||||||||||||||||||
| key_value=memory_store, | ||||||||||||||||||||||||||||||
| fernet=fernet, | ||||||||||||||||||||||||||||||
| salt="test", | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_fernet_must_provide_source_material(memory_store: MemoryStore): | ||||||||||||||||||||||||||||||
| """Test that not providing fernet or source_material raises ValueError.""" | ||||||||||||||||||||||||||||||
| with pytest.raises(ValueError, match="Must provide either fernet or source_material"): | ||||||||||||||||||||||||||||||
| FernetEncryptionWrapper(key_value=memory_store) | ||||||||||||||||||||||||||||||
|
Comment on lines
+212
to
+215
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add type: ignore comment for intentional overload violation. This test intentionally omits required parameters to verify error handling. Add a 🔎 Proposed fix def test_fernet_must_provide_source_material(memory_store: MemoryStore):
"""Test that not providing fernet or source_material raises ValueError."""
with pytest.raises(ValueError, match="Must provide either fernet or source_material"):
- FernetEncryptionWrapper(key_value=memory_store)
+ FernetEncryptionWrapper(key_value=memory_store) # type: ignore[call-overload]📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Check: static_analysis (key-value/key-value-aio)[failure] 215-215: 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_fernet_must_provide_salt_with_source_material(memory_store: MemoryStore): | ||||||||||||||||||||||||||||||
| """Test that providing source_material without salt raises ValueError.""" | ||||||||||||||||||||||||||||||
| with pytest.raises(ValueError, match="Must provide a salt"): | ||||||||||||||||||||||||||||||
| FernetEncryptionWrapper( | ||||||||||||||||||||||||||||||
|
Check failure on line 221 in key-value/key-value-aio/tests/stores/wrappers/test_encryption.py
|
||||||||||||||||||||||||||||||
| key_value=memory_store, | ||||||||||||||||||||||||||||||
| source_material="test-source", | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
Comment on lines
+218
to
+224
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add type: ignore comment for intentional overload violation. This test intentionally omits the required 🔎 Proposed fix def test_fernet_must_provide_salt_with_source_material(memory_store: MemoryStore):
"""Test that providing source_material without salt raises ValueError."""
with pytest.raises(ValueError, match="Must provide a salt"):
- FernetEncryptionWrapper(
+ FernetEncryptionWrapper( # type: ignore[call-overload]
key_value=memory_store,
source_material="test-source",
)📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Check: static_analysis (key-value/key-value-aio)[failure] 221-221: 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_fernet_empty_source_material(memory_store: MemoryStore): | ||||||||||||||||||||||||||||||
| """Test that empty source_material raises ValueError.""" | ||||||||||||||||||||||||||||||
| with pytest.raises(ValueError, match="Must provide either fernet or source_material"): | ||||||||||||||||||||||||||||||
| FernetEncryptionWrapper( | ||||||||||||||||||||||||||||||
| key_value=memory_store, | ||||||||||||||||||||||||||||||
| source_material=" ", | ||||||||||||||||||||||||||||||
| salt="test", | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_fernet_empty_salt(memory_store: MemoryStore): | ||||||||||||||||||||||||||||||
| """Test that empty salt raises ValueError.""" | ||||||||||||||||||||||||||||||
| with pytest.raises(ValueError, match="Must provide a salt"): | ||||||||||||||||||||||||||||||
| FernetEncryptionWrapper( | ||||||||||||||||||||||||||||||
| key_value=memory_store, | ||||||||||||||||||||||||||||||
| source_material="test-source", | ||||||||||||||||||||||||||||||
| salt=" ", | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from collections.abc import Mapping | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from collections.abc import Mapping, Sequence | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Any, SupportsFloat | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import pytest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -17,11 +17,43 @@ async def get(self, key: str, *, collection: str | None = None) -> dict[str, Any | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg = "Primary store unavailable" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ConnectionError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[dict[str, Any] | None]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg = "Primary store unavailable" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ConnectionError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def ttl(self, key: str, *, collection: str | None = None) -> tuple[dict[str, Any] | None, float | None]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg = "Primary store unavailable" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ConnectionError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def ttl_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg = "Primary store unavailable" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ConnectionError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def put(self, key: str, value: Mapping[str, Any], *, collection: str | None = None, ttl: SupportsFloat | None = None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg = "Primary store unavailable" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ConnectionError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def put_many( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, keys: Sequence[str], values: Sequence[Mapping[str, Any]], *, collection: str | None = None, ttl: SupportsFloat | None = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg = "Primary store unavailable" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ConnectionError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def delete(self, key: str, *, collection: str | None = None) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg = "Primary store unavailable" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ConnectionError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def delete_many(self, keys: Sequence[str], *, collection: str | None = None) -> int: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg = "Primary store unavailable" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ConnectionError(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class TestFallbackWrapper(BaseStoreTests): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -77,3 +109,81 @@ async def test_write_to_fallback_enabled(self): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Verify it was written to fallback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = await fallback_store.get(collection="test", key="test") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert result == {"test": "value"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def test_fallback_get_many(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| primary_store = FailingStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fallback_store = MemoryStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wrapper = FallbackWrapper(primary_key_value=primary_store, fallback_key_value=fallback_store) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Put data in fallback store | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fallback_store.put(collection="test", key="k1", value={"v": "1"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fallback_store.put(collection="test", key="k2", value={"v": "2"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Should fall back for get_many | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = await wrapper.get_many(collection="test", keys=["k1", "k2"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert result == [{"v": "1"}, {"v": "2"}] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def test_fallback_ttl(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| primary_store = FailingStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fallback_store = MemoryStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wrapper = FallbackWrapper(primary_key_value=primary_store, fallback_key_value=fallback_store) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Put data in fallback store with TTL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fallback_store.put(collection="test", key="test", value={"v": "1"}, ttl=100) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Should fall back for ttl | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value, ttl = await wrapper.ttl(collection="test", key="test") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert value == {"v": "1"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert ttl is not None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def test_fallback_ttl_many(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| primary_store = FailingStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fallback_store = MemoryStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wrapper = FallbackWrapper(primary_key_value=primary_store, fallback_key_value=fallback_store) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Put data in fallback store | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fallback_store.put(collection="test", key="k1", value={"v": "1"}, ttl=100) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fallback_store.put(collection="test", key="k2", value={"v": "2"}, ttl=200) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Should fall back for ttl_many | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| results = await wrapper.ttl_many(collection="test", keys=["k1", "k2"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert results[0][0] == {"v": "1"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert results[1][0] == {"v": "2"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+139
to
+151
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider verifying TTL values. Test correctly validates ttl_many fallback, but only checks values. Optionally assert that 🔎 Optional enhancement results = await wrapper.ttl_many(collection="test", keys=["k1", "k2"])
assert results[0][0] == {"v": "1"}
assert results[1][0] == {"v": "2"}
+ assert results[0][1] is not None
+ assert results[1][1] is not None📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def test_fallback_put_many_enabled(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| primary_store = FailingStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fallback_store = MemoryStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wrapper = FallbackWrapper(primary_key_value=primary_store, fallback_key_value=fallback_store, write_to_fallback=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Should fall back for put_many | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await wrapper.put_many(collection="test", keys=["k1", "k2"], values=[{"v": "1"}, {"v": "2"}]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Verify in fallback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert await fallback_store.get(collection="test", key="k1") == {"v": "1"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert await fallback_store.get(collection="test", key="k2") == {"v": "2"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+153
to
+163
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial LGTM! Test correctly validates put_many falls back to secondary store when write_to_fallback is enabled. Optionally add a corresponding test for 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def test_fallback_delete_enabled(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| primary_store = FailingStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fallback_store = MemoryStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wrapper = FallbackWrapper(primary_key_value=primary_store, fallback_key_value=fallback_store, write_to_fallback=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Put data in fallback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fallback_store.put(collection="test", key="test", value={"v": "1"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Should fall back for delete | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = await wrapper.delete(collection="test", key="test") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert result is True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert await fallback_store.get(collection="test", key="test") is None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def test_fallback_delete_many_enabled(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| primary_store = FailingStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fallback_store = MemoryStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wrapper = FallbackWrapper(primary_key_value=primary_store, fallback_key_value=fallback_store, write_to_fallback=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Put data in fallback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fallback_store.put(collection="test", key="k1", value={"v": "1"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fallback_store.put(collection="test", key="k2", value={"v": "2"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Should fall back for delete_many | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = await wrapper.delete_many(collection="test", keys=["k1", "k2"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert result == 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+165
to
+189
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial LGTM! Both delete tests correctly validate fallback behavior when write_to_fallback is enabled. The tests verify return values and actual deletion in the fallback store. Optionally add corresponding tests for 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,3 +28,80 @@ async def cache_store(self, memory_store: MemoryStore) -> MemoryStore: | |
| async def store(self, primary_store: DiskStore, cache_store: MemoryStore) -> PassthroughCacheWrapper: | ||
| primary_store._cache.clear() # pyright: ignore[reportPrivateUsage] | ||
| return PassthroughCacheWrapper(primary_key_value=primary_store, cache_key_value=cache_store) | ||
|
|
||
| async def test_ttl_caches_from_primary(self): | ||
| """Test that ttl retrieves from primary and caches the result.""" | ||
| primary_store = MemoryStore() | ||
| cache_store = MemoryStore() | ||
| wrapper = PassthroughCacheWrapper(primary_key_value=primary_store, cache_key_value=cache_store) | ||
|
|
||
| # Put data in primary with TTL | ||
| await primary_store.put(collection="test", key="test", value={"v": "1"}, ttl=100) | ||
|
|
||
| # Call ttl - should get from primary and cache it | ||
| value, ttl = await wrapper.ttl(collection="test", key="test") | ||
| assert value == {"v": "1"} | ||
| assert ttl is not None | ||
|
|
||
| # Verify it's now in cache | ||
| cached_value = await cache_store.get(collection="test", key="test") | ||
| assert cached_value == {"v": "1"} | ||
|
Comment on lines
+32
to
+48
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Optional: Validate TTL values more precisely. The test checks that 🤖 Prompt for AI Agents |
||
|
|
||
| async def test_ttl_returns_cached_value(self): | ||
| """Test that ttl returns cached value when available.""" | ||
| primary_store = MemoryStore() | ||
| cache_store = MemoryStore() | ||
| wrapper = PassthroughCacheWrapper(primary_key_value=primary_store, cache_key_value=cache_store) | ||
|
|
||
| # Put data only in cache | ||
| await cache_store.put(collection="test", key="test", value={"v": "cached"}, ttl=100) | ||
|
|
||
| # Call ttl - should return cached value | ||
| value, ttl = await wrapper.ttl(collection="test", key="test") | ||
| assert value == {"v": "cached"} | ||
| assert ttl is not None | ||
|
|
||
| async def test_ttl_returns_none_for_missing(self): | ||
| """Test that ttl returns (None, None) for missing entries.""" | ||
| primary_store = MemoryStore() | ||
| cache_store = MemoryStore() | ||
| wrapper = PassthroughCacheWrapper(primary_key_value=primary_store, cache_key_value=cache_store) | ||
|
|
||
| # Call ttl for non-existent key | ||
| value, ttl = await wrapper.ttl(collection="test", key="missing") | ||
| assert value is None | ||
| assert ttl is None | ||
|
|
||
| async def test_ttl_many_caches_from_primary(self): | ||
| """Test that ttl_many retrieves from primary and caches results.""" | ||
| primary_store = MemoryStore() | ||
| cache_store = MemoryStore() | ||
| wrapper = PassthroughCacheWrapper(primary_key_value=primary_store, cache_key_value=cache_store) | ||
|
|
||
| # Put data in primary with TTL | ||
| await primary_store.put(collection="test", key="k1", value={"v": "1"}, ttl=100) | ||
| await primary_store.put(collection="test", key="k2", value={"v": "2"}, ttl=200) | ||
|
|
||
| # Call ttl_many - should get from primary and cache | ||
| results = await wrapper.ttl_many(collection="test", keys=["k1", "k2"]) | ||
| assert results[0][0] == {"v": "1"} | ||
| assert results[1][0] == {"v": "2"} | ||
|
|
||
| # Verify in cache | ||
| assert await cache_store.get(collection="test", key="k1") == {"v": "1"} | ||
| assert await cache_store.get(collection="test", key="k2") == {"v": "2"} | ||
|
Comment on lines
+75
to
+92
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider testing partial cache hits. Current tests cover full primary fetch and full cache hit scenarios. A test where some keys are cached and others require primary fetch would strengthen coverage of 🤖 Prompt for AI Agents |
||
|
|
||
| async def test_ttl_many_returns_cached_values(self): | ||
| """Test that ttl_many returns cached values when available.""" | ||
| primary_store = MemoryStore() | ||
| cache_store = MemoryStore() | ||
| wrapper = PassthroughCacheWrapper(primary_key_value=primary_store, cache_key_value=cache_store) | ||
|
|
||
| # Put data in cache | ||
| await cache_store.put(collection="test", key="k1", value={"v": "cached1"}, ttl=100) | ||
| await cache_store.put(collection="test", key="k2", value={"v": "cached2"}, ttl=200) | ||
|
|
||
| # Call ttl_many - should return cached values | ||
| results = await wrapper.ttl_many(collection="test", keys=["k1", "k2"]) | ||
| assert results[0][0] == {"v": "cached1"} | ||
| assert results[1][0] == {"v": "cached2"} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add type: ignore comments for intentional overload violations.
These tests intentionally pass invalid argument combinations to verify error handling. The type checker correctly flags them as not matching any overload signature. Add
type: ignorecomments to suppress the warnings.🔎 Proposed fix
def test_fernet_cannot_provide_fernet_with_source_material(memory_store: MemoryStore): """Test that providing both fernet and source_material raises ValueError.""" fernet = Fernet(key=Fernet.generate_key()) with pytest.raises(ValueError, match="Cannot provide fernet together with source_material or salt"): - FernetEncryptionWrapper( + FernetEncryptionWrapper( # type: ignore[call-overload] key_value=memory_store, fernet=fernet, source_material="test", ) def test_fernet_cannot_provide_fernet_with_salt(memory_store: MemoryStore): """Test that providing both fernet and salt raises ValueError.""" fernet = Fernet(key=Fernet.generate_key()) with pytest.raises(ValueError, match="Cannot provide fernet together with source_material or salt"): - FernetEncryptionWrapper( + FernetEncryptionWrapper( # type: ignore[call-overload] key_value=memory_store, fernet=fernet, salt="test", )🧰 Tools
🪛 GitHub Actions: Run Tests
[error] 194-194: No overloads for "init" match the provided arguments. Argument types: (MemoryStore, Fernet, Literal['test'])
🪛 GitHub Check: static_analysis (key-value/key-value-aio)
[failure] 205-205:
No overloads for "init" match the provided arguments
Argument types: (MemoryStore, Fernet, Literal['test']) (reportCallIssue)
[failure] 194-194:
No overloads for "init" match the provided arguments
Argument types: (MemoryStore, Fernet, Literal['test']) (reportCallIssue)
🤖 Prompt for AI Agents