Skip to content

Commit fdcb7e3

Browse files
authored
test: unit tests for backend pure logic (cache, catalog, bedrock) (#832)
* test: add unit tests for backend pure logic (cache, catalog, bedrock) Closes #816 * test: address review feedback — tighten types and assert base_url - Add base_url assertions to bedrock happy-path tests - Tighten bare list annotations to list[int]/list[str]
1 parent 2dcda8b commit fdcb7e3

3 files changed

Lines changed: 263 additions & 0 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Unit tests for the intrinsics catalog — metadata lookup, validation, and enumeration."""
2+
3+
import pytest
4+
5+
from mellea.backends.adapters.catalog import (
6+
AdapterType,
7+
fetch_intrinsic_metadata,
8+
known_intrinsic_names,
9+
)
10+
11+
# --- known_intrinsic_names ---
12+
13+
14+
def test_known_intrinsic_names_returns_non_empty_list():
15+
names = known_intrinsic_names()
16+
assert isinstance(names, list)
17+
assert len(names) > 0
18+
19+
20+
def test_known_intrinsic_names_contains_expected_entries():
21+
names = known_intrinsic_names()
22+
for expected in ("answerability", "citations", "uncertainty"):
23+
assert expected in names
24+
25+
26+
# --- fetch_intrinsic_metadata ---
27+
28+
29+
def test_fetch_returns_correct_entry():
30+
entry = fetch_intrinsic_metadata("answerability")
31+
assert entry.name == "answerability"
32+
assert isinstance(entry.repo_id, str)
33+
assert len(entry.repo_id) > 0
34+
35+
36+
def test_fetch_unknown_name_raises_value_error():
37+
with pytest.raises(ValueError, match="Unknown intrinsic name 'bogus'"):
38+
fetch_intrinsic_metadata("bogus")
39+
40+
41+
def test_fetch_returns_defensive_copy():
42+
entry_a = fetch_intrinsic_metadata("answerability")
43+
entry_b = fetch_intrinsic_metadata("answerability")
44+
assert entry_a == entry_b
45+
assert entry_a is not entry_b
46+
47+
48+
# --- adapter types ---
49+
50+
51+
def test_default_adapter_types():
52+
entry = fetch_intrinsic_metadata("answerability")
53+
assert AdapterType.LORA in entry.adapter_types
54+
assert AdapterType.ALORA in entry.adapter_types
55+
56+
57+
def test_lora_only_entry():
58+
entry = fetch_intrinsic_metadata("query_clarification")
59+
assert entry.adapter_types == (AdapterType.LORA,)

test/backends/test_bedrock_unit.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""Unit tests for bedrock helpers — URI construction, ModelIdentifier matching, env var validation.
2+
3+
These tests cover the pure-logic helpers and error paths in bedrock.py without
4+
requiring AWS credentials or network access. Happy-path tests mock only the
5+
network boundary (list_mantle_models) and verify the rest of the pipeline.
6+
"""
7+
8+
from dataclasses import dataclass
9+
from unittest.mock import patch
10+
11+
import pytest
12+
13+
from mellea.backends.bedrock import (
14+
_make_mantle_uri,
15+
_make_region_for_uri,
16+
create_bedrock_mantle_backend,
17+
)
18+
from mellea.backends.model_ids import ModelIdentifier
19+
from mellea.backends.openai import OpenAIBackend
20+
21+
22+
@dataclass
23+
class _FakeModel:
24+
"""Minimal stand-in for the model objects returned by list_mantle_models."""
25+
26+
id: str
27+
28+
29+
_FAKE_MODELS = [_FakeModel("granite-3.3-8b"), _FakeModel("llama-4-scout")]
30+
31+
# --- _make_region_for_uri ---
32+
33+
34+
def test_region_default_when_none():
35+
assert _make_region_for_uri(None) == "us-east-1"
36+
37+
38+
# --- _make_mantle_uri ---
39+
40+
41+
def test_mantle_uri_default_region():
42+
uri = _make_mantle_uri()
43+
assert uri == "https://bedrock-mantle.us-east-1.api.aws/v1"
44+
45+
46+
def test_mantle_uri_custom_region():
47+
uri = _make_mantle_uri("ap-northeast-1")
48+
assert uri == "https://bedrock-mantle.ap-northeast-1.api.aws/v1"
49+
50+
51+
# --- create_bedrock_mantle_backend error paths ---
52+
53+
54+
def test_model_identifier_without_bedrock_name_raises():
55+
mid = ModelIdentifier(hf_model_name="some/model")
56+
with pytest.raises(Exception, match="do not have a known bedrock model identifier"):
57+
create_bedrock_mantle_backend(mid)
58+
59+
60+
def test_missing_env_var_raises(monkeypatch):
61+
monkeypatch.delenv("AWS_BEARER_TOKEN_BEDROCK", raising=False)
62+
mid = ModelIdentifier(bedrock_name="some.model-id")
63+
with pytest.raises(AssertionError, match="AWS_BEARER_TOKEN_BEDROCK"):
64+
create_bedrock_mantle_backend(mid)
65+
66+
67+
# --- create_bedrock_mantle_backend happy paths (mock network boundary) ---
68+
69+
70+
@patch("mellea.backends.bedrock.list_mantle_models", return_value=_FAKE_MODELS)
71+
def test_create_backend_with_model_identifier(mock_list, monkeypatch):
72+
monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "fake-token")
73+
mid = ModelIdentifier(bedrock_name="granite-3.3-8b")
74+
backend = create_bedrock_mantle_backend(mid, region="eu-west-1")
75+
76+
assert isinstance(backend, OpenAIBackend)
77+
assert backend.model_id == "granite-3.3-8b"
78+
assert backend._base_url == "https://bedrock-mantle.eu-west-1.api.aws/v1"
79+
mock_list.assert_called_once_with("eu-west-1")
80+
81+
82+
@patch("mellea.backends.bedrock.list_mantle_models", return_value=_FAKE_MODELS)
83+
def test_create_backend_with_string_model_id(mock_list, monkeypatch):
84+
monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "fake-token")
85+
backend = create_bedrock_mantle_backend("llama-4-scout")
86+
87+
assert isinstance(backend, OpenAIBackend)
88+
assert backend.model_id == "llama-4-scout"
89+
assert backend._base_url == "https://bedrock-mantle.us-east-1.api.aws/v1"
90+
91+
92+
@patch("mellea.backends.bedrock.list_mantle_models", return_value=_FAKE_MODELS)
93+
def test_model_not_in_region_raises(mock_list, monkeypatch):
94+
monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "fake-token")
95+
with pytest.raises(Exception, match="not supported in region"):
96+
create_bedrock_mantle_backend("nonexistent-model")

test/backends/test_cache.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"""Unit tests for SimpleLRUCache — LRU eviction, callbacks, and capacity edge cases."""
2+
3+
from mellea.backends.cache import SimpleLRUCache
4+
5+
# --- basic put / get ---
6+
7+
8+
def test_put_and_get():
9+
cache = SimpleLRUCache(capacity=3)
10+
cache.put("a", 1)
11+
assert cache.get("a") == 1
12+
13+
14+
def test_get_missing_key_returns_none():
15+
cache = SimpleLRUCache(capacity=3)
16+
assert cache.get("missing") is None
17+
18+
19+
def test_current_size_tracks_entries():
20+
cache = SimpleLRUCache(capacity=5)
21+
assert cache.current_size() == 0
22+
cache.put("a", 1)
23+
cache.put("b", 2)
24+
assert cache.current_size() == 2
25+
26+
27+
# --- update existing key ---
28+
29+
30+
def test_put_existing_key_updates_value():
31+
cache = SimpleLRUCache(capacity=2)
32+
cache.put("a", "old")
33+
cache.put("a", "new")
34+
assert cache.get("a") == "new"
35+
assert cache.current_size() == 1
36+
37+
38+
def test_put_existing_key_does_not_evict():
39+
evicted: list[int] = []
40+
cache = SimpleLRUCache(capacity=1, on_evict=evicted.append)
41+
cache.put("a", 1)
42+
cache.put("a", 2)
43+
assert evicted == []
44+
45+
46+
# --- eviction ---
47+
48+
49+
def test_evicts_least_recently_used():
50+
cache = SimpleLRUCache(capacity=2)
51+
cache.put("a", 1)
52+
cache.put("b", 2)
53+
cache.put("c", 3) # should evict "a"
54+
assert cache.get("a") is None
55+
assert cache.get("b") == 2
56+
assert cache.get("c") == 3
57+
58+
59+
def test_on_evict_callback_receives_evicted_value():
60+
evicted: list[str] = []
61+
cache = SimpleLRUCache(capacity=2, on_evict=evicted.append)
62+
cache.put("a", "val_a")
63+
cache.put("b", "val_b")
64+
cache.put("c", "val_c") # evicts "a"
65+
assert evicted == ["val_a"]
66+
67+
68+
def test_multiple_evictions_fire_callback_each_time():
69+
evicted: list[int] = []
70+
cache = SimpleLRUCache(capacity=1, on_evict=evicted.append)
71+
cache.put("a", 1)
72+
cache.put("b", 2) # evicts 1
73+
cache.put("c", 3) # evicts 2
74+
assert evicted == [1, 2]
75+
76+
77+
# --- LRU ordering: get promotes to most-recent ---
78+
79+
80+
def test_get_promotes_key_preventing_eviction():
81+
cache = SimpleLRUCache(capacity=2)
82+
cache.put("a", 1)
83+
cache.put("b", 2)
84+
cache.get("a") # promote "a" — now "b" is LRU
85+
cache.put("c", 3) # should evict "b", not "a"
86+
assert cache.get("a") == 1
87+
assert cache.get("b") is None
88+
89+
90+
# --- capacity edge cases ---
91+
92+
93+
def test_zero_capacity_put_is_noop():
94+
evicted: list[int] = []
95+
cache = SimpleLRUCache(capacity=0, on_evict=evicted.append)
96+
cache.put("a", 1)
97+
assert cache.current_size() == 0
98+
assert cache.get("a") is None
99+
assert evicted == []
100+
101+
102+
def test_capacity_one():
103+
cache = SimpleLRUCache(capacity=1)
104+
cache.put("a", 1)
105+
cache.put("b", 2)
106+
assert cache.get("a") is None
107+
assert cache.get("b") == 2
108+
assert cache.current_size() == 1

0 commit comments

Comments
 (0)