Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/omnibase_infra/test_utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: 2025 OmniNode.ai Inc.
# SPDX-License-Identifier: MIT
"""Test utilities ``omnibase_infra`` ships for downstream tests (OMN-10559).

These helpers are deliberately *not* under ``tests/`` because consumer
repos (omnimarket, etc.) import them from their own conftests. They are
shipped in the wheel — acceptable for an internal platform package.

Add lightweight fakes / fixtures here. Heavyweight fixtures that depend
on infrastructure (Postgres, Kafka) belong in ``omnibase_infra.testing``
instead.
"""

from __future__ import annotations

from omnibase_infra.test_utils.fake_secret_store import FakeSecretStore

__all__: list[str] = ["FakeSecretStore"]
59 changes: 59 additions & 0 deletions src/omnibase_infra/test_utils/fake_secret_store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# SPDX-FileCopyrightText: 2025 OmniNode.ai Inc.
# SPDX-License-Identifier: MIT
"""Deterministic in-memory ``ProtocolSecretStore`` for tests (OMN-10559).

Used by the canonical conftest in omnimarket and other consumers to
exercise code that depends on a ``ProtocolSecretStore`` without standing
up a real backend.

Packaging note: this module ships in the omnibase_infra wheel (under
``omnibase_infra.test_utils``) because downstream test suites import it.
The package is internal-platform-only; shipping test helpers is
deliberate. Heavier fixtures requiring infrastructure (Postgres, Kafka)
belong in ``omnibase_infra.testing`` instead.
"""

from __future__ import annotations

from collections.abc import Mapping


class FakeSecretStore:
"""In-memory ``ProtocolSecretStore`` impl backed by a ``dict``.

Fully implements every protocol method (gets, sets, deletes, lists,
health, close). Constructor accepts an optional ``initial`` mapping
to preload values.
"""

def __init__(self, initial: Mapping[str, str] | None = None) -> None:
self._data: dict[str, str] = dict(initial) if initial else {}
self._closed: bool = False

async def get_secret(self, key: str) -> str | None:
return self._data.get(key)

async def set_secret(self, key: str, value: str) -> bool:
self._data[key] = value
return True

async def delete_secret(self, key: str) -> bool:
if key in self._data:
del self._data[key]
return True
return False

async def list_keys(self, prefix: str | None = None) -> list[str]:
if prefix is None:
return sorted(self._data.keys())
return sorted(k for k in self._data if k.startswith(prefix))

async def health_check(self) -> bool:
return not self._closed

async def close(self, timeout_seconds: float = 30.0) -> None:
del timeout_seconds
self._closed = True


__all__: list[str] = ["FakeSecretStore"]
26 changes: 26 additions & 0 deletions tests/integration/test_fake_secret_store_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# SPDX-FileCopyrightText: 2025 OmniNode.ai Inc.
# SPDX-License-Identifier: MIT
"""Integration coverage for the fake secret store test utility (OMN-10559)."""

from __future__ import annotations

import pytest

from omnibase_infra.test_utils.fake_secret_store import FakeSecretStore
from omnibase_spi.protocols.services.protocol_secret_store import ProtocolSecretStore


@pytest.mark.integration
async def test_fake_secret_store_protocol_lifecycle() -> None:
store: ProtocolSecretStore = FakeSecretStore(initial={"db/url": "postgres"})

assert await store.health_check() is True
assert await store.get_secret("db/url") == "postgres"
assert await store.set_secret("llm/key", "secret") is True
assert await store.get_secret("llm/key") == "secret"
assert await store.list_keys(prefix="llm/") == ["llm/key"]
assert await store.delete_secret("db/url") is True
assert await store.get_secret("db/url") is None

await store.close()
assert await store.health_check() is False
3 changes: 3 additions & 0 deletions tests/unit/test_utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2025 OmniNode.ai Inc.
# SPDX-License-Identifier: MIT
"""Unit tests for ``omnibase_infra.test_utils``."""
98 changes: 98 additions & 0 deletions tests/unit/test_utils/test_fake_secret_store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# SPDX-FileCopyrightText: 2025 OmniNode.ai Inc.
# SPDX-License-Identifier: MIT
"""Unit tests for ``FakeSecretStore`` (OMN-10559)."""

from __future__ import annotations

import pytest

from omnibase_infra.test_utils.fake_secret_store import FakeSecretStore
from omnibase_spi.protocols.services.protocol_secret_store import ProtocolSecretStore


@pytest.mark.unit
def test_satisfies_protocol_secret_store() -> None:
assert isinstance(FakeSecretStore(), ProtocolSecretStore)


@pytest.mark.unit
async def test_empty_constructor_yields_empty_store() -> None:
store = FakeSecretStore()
assert await store.list_keys() == []
assert await store.get_secret("anything") is None


@pytest.mark.unit
async def test_initial_data_preloaded() -> None:
store = FakeSecretStore(initial={"a": "1", "b": "2"})
assert await store.get_secret("a") == "1"
assert await store.get_secret("b") == "2"


@pytest.mark.unit
async def test_initial_data_is_copied_not_referenced() -> None:
source = {"a": "1"}
store = FakeSecretStore(initial=source)
source["a"] = "mutated"
assert await store.get_secret("a") == "1"


@pytest.mark.unit
async def test_set_then_get_round_trip() -> None:
store = FakeSecretStore()
assert await store.set_secret("key", "value") is True
assert await store.get_secret("key") == "value"


@pytest.mark.unit
async def test_set_overwrites_existing_value() -> None:
store = FakeSecretStore(initial={"key": "original"})
await store.set_secret("key", "updated")
assert await store.get_secret("key") == "updated"


@pytest.mark.unit
async def test_delete_existing_returns_true_and_removes() -> None:
store = FakeSecretStore(initial={"key": "value"})
assert await store.delete_secret("key") is True
assert await store.get_secret("key") is None


@pytest.mark.unit
async def test_delete_missing_returns_false() -> None:
store = FakeSecretStore()
assert await store.delete_secret("absent") is False


@pytest.mark.unit
async def test_list_keys_no_prefix_returns_all_sorted() -> None:
store = FakeSecretStore(initial={"b": "2", "a": "1", "c": "3"})
assert await store.list_keys() == ["a", "b", "c"]


@pytest.mark.unit
async def test_list_keys_filters_by_prefix() -> None:
store = FakeSecretStore(
initial={"llm/openai": "k1", "db/pg": "k2", "llm/anthropic": "k3"}
)
assert await store.list_keys(prefix="llm/") == ["llm/anthropic", "llm/openai"]


@pytest.mark.unit
async def test_health_check_true_when_open() -> None:
store = FakeSecretStore()
assert await store.health_check() is True


@pytest.mark.unit
async def test_health_check_false_after_close() -> None:
store = FakeSecretStore()
await store.close()
assert await store.health_check() is False


@pytest.mark.unit
async def test_close_accepts_default_timeout() -> None:
store = FakeSecretStore()
await store.close()
await store.close(timeout_seconds=5.0)
Loading