mirror of
https://github.com/zvx-echo6/central.git
synced 2026-05-22 18:44:40 +02:00
chore: normalize line endings to LF
This commit is contained in:
parent
43088d7fbb
commit
374a8c067f
26 changed files with 5357 additions and 5346 deletions
|
|
@ -1,132 +1,132 @@
|
|||
"""Tests for configuration source abstraction."""
|
||||
|
||||
import base64
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import asyncpg
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from central.config_source import (
|
||||
ConfigSource,
|
||||
DbConfigSource,
|
||||
)
|
||||
from central.crypto import KEY_SIZE, clear_key_cache
|
||||
|
||||
# Test database DSN
|
||||
TEST_DB_DSN = os.environ.get(
|
||||
"CENTRAL_TEST_DB_DSN",
|
||||
"postgresql://central_test:testpass@localhost/central_test",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def master_key_path(tmp_path_factory: pytest.TempPathFactory) -> Path:
|
||||
"""Create a master key file for the test session."""
|
||||
key = os.urandom(KEY_SIZE)
|
||||
key_path = tmp_path_factory.mktemp("keys") / "master.key"
|
||||
key_path.write_text(base64.b64encode(key).decode())
|
||||
return key_path
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_master_key(master_key_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Configure master key path for all tests."""
|
||||
clear_key_cache()
|
||||
monkeypatch.setenv("CENTRAL_DB_DSN", TEST_DB_DSN)
|
||||
monkeypatch.setenv("CENTRAL_MASTER_KEY_PATH", str(master_key_path))
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def db_conn() -> asyncpg.Connection:
|
||||
"""Get a direct database connection for setup/teardown."""
|
||||
conn = await asyncpg.connect(TEST_DB_DSN)
|
||||
yield conn
|
||||
await conn.close()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def clean_config_schema(db_conn: asyncpg.Connection) -> None:
|
||||
"""Ensure config schema exists and is clean before each test."""
|
||||
await db_conn.execute("CREATE SCHEMA IF NOT EXISTS config")
|
||||
await db_conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS config.adapters (
|
||||
name TEXT PRIMARY KEY,
|
||||
enabled BOOLEAN NOT NULL DEFAULT true,
|
||||
cadence_s INTEGER NOT NULL,
|
||||
settings JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
paused_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""")
|
||||
await db_conn.execute("DELETE FROM config.adapters")
|
||||
|
||||
|
||||
class TestDbConfigSource:
|
||||
"""Tests for database-backed config source."""
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def db_source(self, clean_config_schema: None) -> DbConfigSource:
|
||||
"""Create a DbConfigSource for testing."""
|
||||
source = await DbConfigSource.create(TEST_DB_DSN)
|
||||
yield source
|
||||
await source.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_enabled_adapters_empty(self, db_source: DbConfigSource) -> None:
|
||||
"""list_enabled_adapters returns empty list when no adapters."""
|
||||
adapters = await db_source.list_enabled_adapters()
|
||||
assert adapters == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_enabled_adapters(
|
||||
self, db_source: DbConfigSource, db_conn: asyncpg.Connection
|
||||
) -> None:
|
||||
"""list_enabled_adapters returns only enabled, non-paused adapters."""
|
||||
# Insert test adapters
|
||||
await db_conn.execute("""
|
||||
INSERT INTO config.adapters (name, enabled, cadence_s, settings)
|
||||
VALUES
|
||||
('enabled_adapter', true, 60, '{"key": "value"}'::jsonb),
|
||||
('disabled_adapter', false, 60, '{}'::jsonb),
|
||||
('paused_adapter', true, 60, '{}'::jsonb)
|
||||
""")
|
||||
await db_conn.execute("""
|
||||
UPDATE config.adapters
|
||||
SET paused_at = now()
|
||||
WHERE name = 'paused_adapter'
|
||||
""")
|
||||
|
||||
adapters = await db_source.list_enabled_adapters()
|
||||
|
||||
assert len(adapters) == 1
|
||||
assert adapters[0].name == "enabled_adapter"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_adapter(
|
||||
self, db_source: DbConfigSource, db_conn: asyncpg.Connection
|
||||
) -> None:
|
||||
"""get_adapter returns correct adapter config."""
|
||||
await db_conn.execute("""
|
||||
INSERT INTO config.adapters (name, enabled, cadence_s, settings)
|
||||
VALUES ('test_adapter', true, 120, '{"states": ["ID"]}'::jsonb)
|
||||
""")
|
||||
|
||||
adapter = await db_source.get_adapter("test_adapter")
|
||||
|
||||
assert adapter is not None
|
||||
assert adapter.name == "test_adapter"
|
||||
assert adapter.cadence_s == 120
|
||||
assert adapter.settings == {"states": ["ID"]}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_nonexistent_adapter(self, db_source: DbConfigSource) -> None:
|
||||
"""get_adapter returns None for nonexistent adapter."""
|
||||
adapter = await db_source.get_adapter("does_not_exist")
|
||||
assert adapter is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_implements_protocol(self, db_source: DbConfigSource) -> None:
|
||||
"""DbConfigSource implements ConfigSource protocol."""
|
||||
assert isinstance(db_source, ConfigSource)
|
||||
"""Tests for configuration source abstraction."""
|
||||
|
||||
import base64
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import asyncpg
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from central.config_source import (
|
||||
ConfigSource,
|
||||
DbConfigSource,
|
||||
)
|
||||
from central.crypto import KEY_SIZE, clear_key_cache
|
||||
|
||||
# Test database DSN
|
||||
TEST_DB_DSN = os.environ.get(
|
||||
"CENTRAL_TEST_DB_DSN",
|
||||
"postgresql://central_test:testpass@localhost/central_test",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def master_key_path(tmp_path_factory: pytest.TempPathFactory) -> Path:
|
||||
"""Create a master key file for the test session."""
|
||||
key = os.urandom(KEY_SIZE)
|
||||
key_path = tmp_path_factory.mktemp("keys") / "master.key"
|
||||
key_path.write_text(base64.b64encode(key).decode())
|
||||
return key_path
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_master_key(master_key_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Configure master key path for all tests."""
|
||||
clear_key_cache()
|
||||
monkeypatch.setenv("CENTRAL_DB_DSN", TEST_DB_DSN)
|
||||
monkeypatch.setenv("CENTRAL_MASTER_KEY_PATH", str(master_key_path))
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def db_conn() -> asyncpg.Connection:
|
||||
"""Get a direct database connection for setup/teardown."""
|
||||
conn = await asyncpg.connect(TEST_DB_DSN)
|
||||
yield conn
|
||||
await conn.close()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def clean_config_schema(db_conn: asyncpg.Connection) -> None:
|
||||
"""Ensure config schema exists and is clean before each test."""
|
||||
await db_conn.execute("CREATE SCHEMA IF NOT EXISTS config")
|
||||
await db_conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS config.adapters (
|
||||
name TEXT PRIMARY KEY,
|
||||
enabled BOOLEAN NOT NULL DEFAULT true,
|
||||
cadence_s INTEGER NOT NULL,
|
||||
settings JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
paused_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""")
|
||||
await db_conn.execute("DELETE FROM config.adapters")
|
||||
|
||||
|
||||
class TestDbConfigSource:
|
||||
"""Tests for database-backed config source."""
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def db_source(self, clean_config_schema: None) -> DbConfigSource:
|
||||
"""Create a DbConfigSource for testing."""
|
||||
source = await DbConfigSource.create(TEST_DB_DSN)
|
||||
yield source
|
||||
await source.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_enabled_adapters_empty(self, db_source: DbConfigSource) -> None:
|
||||
"""list_enabled_adapters returns empty list when no adapters."""
|
||||
adapters = await db_source.list_enabled_adapters()
|
||||
assert adapters == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_enabled_adapters(
|
||||
self, db_source: DbConfigSource, db_conn: asyncpg.Connection
|
||||
) -> None:
|
||||
"""list_enabled_adapters returns only enabled, non-paused adapters."""
|
||||
# Insert test adapters
|
||||
await db_conn.execute("""
|
||||
INSERT INTO config.adapters (name, enabled, cadence_s, settings)
|
||||
VALUES
|
||||
('enabled_adapter', true, 60, '{"key": "value"}'::jsonb),
|
||||
('disabled_adapter', false, 60, '{}'::jsonb),
|
||||
('paused_adapter', true, 60, '{}'::jsonb)
|
||||
""")
|
||||
await db_conn.execute("""
|
||||
UPDATE config.adapters
|
||||
SET paused_at = now()
|
||||
WHERE name = 'paused_adapter'
|
||||
""")
|
||||
|
||||
adapters = await db_source.list_enabled_adapters()
|
||||
|
||||
assert len(adapters) == 1
|
||||
assert adapters[0].name == "enabled_adapter"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_adapter(
|
||||
self, db_source: DbConfigSource, db_conn: asyncpg.Connection
|
||||
) -> None:
|
||||
"""get_adapter returns correct adapter config."""
|
||||
await db_conn.execute("""
|
||||
INSERT INTO config.adapters (name, enabled, cadence_s, settings)
|
||||
VALUES ('test_adapter', true, 120, '{"states": ["ID"]}'::jsonb)
|
||||
""")
|
||||
|
||||
adapter = await db_source.get_adapter("test_adapter")
|
||||
|
||||
assert adapter is not None
|
||||
assert adapter.name == "test_adapter"
|
||||
assert adapter.cadence_s == 120
|
||||
assert adapter.settings == {"states": ["ID"]}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_nonexistent_adapter(self, db_source: DbConfigSource) -> None:
|
||||
"""get_adapter returns None for nonexistent adapter."""
|
||||
adapter = await db_source.get_adapter("does_not_exist")
|
||||
assert adapter is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_implements_protocol(self, db_source: DbConfigSource) -> None:
|
||||
"""DbConfigSource implements ConfigSource protocol."""
|
||||
assert isinstance(db_source, ConfigSource)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue