Files
2026-02-21 07:43:33 +01:00

416 lines
12 KiB
Python

"""Comprehensive tests for the State module.
Tests cover:
- State initialization (singleton pattern)
- MutableMapping interface
- Key-value CRUD operations
- Data persistence via commit/load
- Singleton behavior
- Autocommit functionality
- Flush behavior
"""
import os
import pytest
import tempfile
from collections.abc import MutableMapping
from aiomql.core.state import State
@pytest.fixture(autouse=True)
def reset_state_singleton():
"""Reset State singleton before each test."""
# Remove singleton instance to ensure clean state for each test
if hasattr(State, "_instance"):
delattr(State, "_instance")
if hasattr(State, "_data"):
delattr(State, "_data")
State._initialized = False
yield
# Cleanup after test
if hasattr(State, "_instance"):
delattr(State, "_instance")
if hasattr(State, "_data"):
delattr(State, "_data")
State._initialized = False
class TestStateInitialization:
"""Tests for State initialization."""
@pytest.fixture
def temp_db(self):
"""Creates a temporary database file."""
fd, path = tempfile.mkstemp(suffix=".db")
os.close(fd)
yield path
if os.path.exists(path):
os.remove(path)
def test_init_creates_state(self, temp_db):
"""Test State can be initialized."""
state = State(db_name=temp_db)
assert isinstance(state, State)
def test_init_with_initial_data(self, temp_db):
"""Test State can be initialized with data."""
initial_data = {"key1": "value1", "key2": "value2"}
state = State(db_name=temp_db, data=initial_data, flush=True)
assert state["key1"] == "value1"
assert state["key2"] == "value2"
def test_init_with_flush(self, temp_db):
"""Test State flush clears existing data."""
# Create initial state with data
state1 = State(db_name=temp_db, data={"existing": "data"}, flush=True)
state1.commit()
# Reset singleton
delattr(State, "_instance")
delattr(State, "_data")
State._initialized = False
# Create new state with flush
state2 = State(db_name=temp_db, flush=True)
assert "existing" not in state2
def test_init_default_autocommit(self, temp_db):
"""Test State has autocommit True by default."""
state = State(db_name=temp_db)
assert state.autocommit is True
def test_init_autocommit_true(self, temp_db):
"""Test State can be initialized with autocommit=True."""
state = State(db_name=temp_db, autocommit=True)
assert state.autocommit is True
class TestStateSingleton:
"""Tests for State singleton behavior."""
@pytest.fixture
def temp_db(self):
"""Creates a temporary database file."""
fd, path = tempfile.mkstemp(suffix=".db")
os.close(fd)
yield path
if os.path.exists(path):
os.remove(path)
def test_singleton_returns_same_instance(self, temp_db):
"""Test State returns same instance."""
state1 = State(db_name=temp_db)
state2 = State(db_name=temp_db)
assert state1 is state2
def test_singleton_shares_data(self, temp_db):
"""Test State instances share data."""
state1 = State(db_name=temp_db)
state1["key"] = "value"
state2 = State(db_name=temp_db)
assert state2["key"] == "value"
class TestStateMutableMappingInterface:
"""Tests for MutableMapping interface implementation."""
@pytest.fixture
def temp_db(self):
"""Creates a temporary database file."""
fd, path = tempfile.mkstemp(suffix=".db")
os.close(fd)
yield path
if os.path.exists(path):
os.remove(path)
@pytest.fixture
def state(self, temp_db):
"""Creates a State instance."""
return State(db_name=temp_db)
def test_is_mutable_mapping(self, state):
"""Test State implements MutableMapping."""
assert isinstance(state, MutableMapping)
def test_setitem_getitem(self, state):
"""Test setting and getting items."""
state["key"] = "value"
assert state["key"] == "value"
def test_delitem(self, state):
"""Test deleting items."""
state["key"] = "value"
del state["key"]
assert "key" not in state
def test_delitem_raises_keyerror(self, state):
"""Test deleting nonexistent key raises KeyError."""
with pytest.raises(KeyError):
del state["nonexistent"]
def test_len(self, state):
"""Test len returns correct count."""
assert len(state) == 0
state["key1"] = "value1"
state["key2"] = "value2"
assert len(state) == 2
def test_contains(self, state):
"""Test 'in' operator."""
state["key"] = "value"
assert "key" in state
assert "nonexistent" not in state
def test_iter(self, state):
"""Test iteration over keys."""
state["key1"] = "value1"
state["key2"] = "value2"
keys = list(state)
assert "key1" in keys
assert "key2" in keys
def test_getitem_raises_keyerror(self, state):
"""Test getting nonexistent key raises KeyError."""
with pytest.raises(KeyError):
_ = state["nonexistent"]
class TestStateOperations:
"""Tests for State CRUD operations."""
@pytest.fixture
def temp_db(self):
"""Creates a temporary database file."""
fd, path = tempfile.mkstemp(suffix=".db")
os.close(fd)
yield path
if os.path.exists(path):
os.remove(path)
@pytest.fixture
def state(self, temp_db):
"""Creates a State instance."""
return State(db_name=temp_db)
def test_get_existing_key(self, state):
"""Test get returns value for existing key."""
state["key"] = "value"
assert state.get("key") == "value"
def test_get_nonexistent_key_default(self, state):
"""Test get returns default for nonexistent key."""
assert state.get("nonexistent") is None
assert state.get("nonexistent", "default") == "default"
def test_setdefault_existing_key(self, state):
"""Test setdefault returns existing value."""
state["key"] = "existing"
result = state.setdefault("key", "default")
assert result == "existing"
def test_setdefault_nonexistent_key(self, state):
"""Test setdefault sets and returns default."""
result = state.setdefault("key", "default")
assert result == "default"
assert state["key"] == "default"
def test_update_with_dict(self, state):
"""Test update with dictionary."""
state.update({"key1": "value1", "key2": "value2"})
assert state["key1"] == "value1"
assert state["key2"] == "value2"
def test_update_with_kwargs(self, state):
"""Test update with keyword arguments."""
state.update({"key1": "value1"}, key2="value2")
assert state["key1"] == "value1"
assert state["key2"] == "value2"
def test_pop_existing_key(self, state):
"""Test pop returns and removes value."""
state["key"] = "value"
result = state.pop("key")
assert result == "value"
assert "key" not in state
def test_pop_nonexistent_key_default(self, state):
"""Test pop returns default for nonexistent key."""
result = state.pop("nonexistent", "default")
assert result == "default"
def test_pop_nonexistent_key_raises(self, state):
"""Test pop raises KeyError without default."""
with pytest.raises(KeyError):
state.pop("nonexistent")
def test_keys(self, state):
"""Test keys returns dict_keys."""
state["key1"] = "value1"
state["key2"] = "value2"
keys = state.keys()
assert "key1" in keys
assert "key2" in keys
def test_values(self, state):
"""Test values returns dict_values."""
state["key1"] = "value1"
state["key2"] = "value2"
values = state.values()
assert "value1" in values
assert "value2" in values
def test_items(self, state):
"""Test items returns dict_items."""
state["key1"] = "value1"
items = state.items()
assert ("key1", "value1") in items
class TestStateDataProperty:
"""Tests for State data property."""
@pytest.fixture
def temp_db(self):
"""Creates a temporary database file."""
fd, path = tempfile.mkstemp(suffix=".db")
os.close(fd)
yield path
if os.path.exists(path):
os.remove(path)
def test_data_returns_dict(self, temp_db):
"""Test data property returns dictionary."""
state = State(db_name=temp_db)
state["key1"] = "value1"
data = state.data
assert isinstance(data, dict)
def test_data_setter(self, temp_db):
"""Test data property can be set."""
state = State(db_name=temp_db)
state.data = {"key": "value"}
assert state["key"] == "value"
def test_data_setter_requires_dict(self, temp_db):
"""Test data setter raises on non-dict."""
state = State(db_name=temp_db)
with pytest.raises(AssertionError):
state.data = "not a dict"
class TestStatePersistence:
"""Tests for State persistence functionality."""
@pytest.fixture
def temp_db(self):
"""Creates a temporary database file."""
fd, path = tempfile.mkstemp(suffix=".db")
os.close(fd)
yield path
if os.path.exists(path):
os.remove(path)
def test_commit_persists_data(self, temp_db):
"""Test commit persists data to database."""
state1 = State(db_name=temp_db)
state1["key"] = "value"
state1.commit()
# Reset singleton
delattr(State, "_instance")
delattr(State, "_data")
State._initialized = False
# Create new state and verify data loaded
state2 = State(db_name=temp_db)
assert state2["key"] == "value"
def test_flush_clears_and_sets_data(self, temp_db):
"""Test flush clears and optionally sets new data."""
state = State(db_name=temp_db)
state["key1"] = "value1"
state.flush({"key2": "value2"})
assert "key1" not in state
assert state["key2"] == "value2"
def test_flush_with_no_data(self, temp_db):
"""Test flush with no data clears state."""
state = State(db_name=temp_db)
state["key"] = "value"
state.flush()
assert len(state) == 0
async def test_acommit(self, temp_db):
"""Test async commit."""
state1 = State(db_name=temp_db)
state1["key"] = "value"
await state1.acommit()
# Reset singleton
delattr(State, "_instance")
delattr(State, "_data")
State._initialized = False
state2 = State(db_name=temp_db)
assert state2["key"] == "value"
class TestStateRepr:
"""Tests for State repr."""
@pytest.fixture
def temp_db(self):
"""Creates a temporary database file."""
fd, path = tempfile.mkstemp(suffix=".db")
os.close(fd)
yield path
if os.path.exists(path):
os.remove(path)
def test_repr(self, temp_db):
"""Test repr returns data representation."""
state = State(db_name=temp_db)
state["key"] = "value"
assert "key" in repr(state)
assert "value" in repr(state)
class TestStateAutocommit:
"""Tests for State autocommit functionality."""
@pytest.fixture
def temp_db(self):
"""Creates a temporary database file."""
fd, path = tempfile.mkstemp(suffix=".db")
os.close(fd)
yield path
if os.path.exists(path):
os.remove(path)
def test_autocommit_on_setitem(self, temp_db):
"""Test autocommit commits on setitem."""
state = State(db_name=temp_db, autocommit=True)
state["key"] = "value"
# Reset singleton
delattr(State, "_instance")
delattr(State, "_data")
State._initialized = False
state2 = State(db_name=temp_db)
assert state2.get("key") == "value"
def test_autocommit_on_delitem(self, temp_db):
"""Test autocommit commits on delitem."""
state = State(db_name=temp_db, autocommit=True, data={"key": "value"}, flush=True)
del state["key"]
# Reset singleton
delattr(State, "_instance")
delattr(State, "_data")
State._initialized = False
state2 = State(db_name=temp_db)
assert "key" not in state2