Skip to content

Commit 8a58bab

Browse files
committed
Fix ci and add tests
1 parent 8a93691 commit 8a58bab

4 files changed

Lines changed: 260 additions & 0 deletions

File tree

tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Tests package

tests/test_client.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
"""
2+
Tests for the Semcache client.
3+
"""
4+
5+
from unittest.mock import Mock, patch
6+
7+
import pytest
8+
import requests
9+
10+
from semcache import (
11+
Semcache,
12+
SemcacheConnectionError,
13+
SemcacheTimeoutError,
14+
SemcacheAPIError,
15+
)
16+
17+
18+
class TestSemcache:
19+
"""Test cases for the Semcache client."""
20+
21+
def test_initialization_default(self):
22+
"""Test client initialization with default values."""
23+
client = Semcache()
24+
assert client.base_url == "http://localhost:8080"
25+
assert client.timeout == 30
26+
27+
def test_initialization_custom(self):
28+
"""Test client initialization with custom values."""
29+
client = Semcache(base_url="http://example.com:9090", timeout=60)
30+
assert client.base_url == "http://example.com:9090"
31+
assert client.timeout == 60
32+
33+
def test_base_url_trailing_slash(self):
34+
"""Test that trailing slashes are removed from base URL."""
35+
client = Semcache(base_url="http://localhost:8080/")
36+
assert client.base_url == "http://localhost:8080"
37+
38+
@patch("semcache.client.requests.Session")
39+
def test_put_success(self, mock_session_class):
40+
"""Test successful put operation."""
41+
# Setup mock
42+
mock_session = Mock()
43+
mock_session_class.return_value = mock_session
44+
mock_response = Mock()
45+
mock_response.status_code = 200
46+
mock_response.raise_for_status.return_value = None
47+
mock_session.put.return_value = mock_response
48+
49+
# Test
50+
client = Semcache()
51+
client.put("test key", "test data")
52+
53+
# Verify
54+
mock_session.put.assert_called_once_with(
55+
"http://localhost:8080/semcache/v1/put",
56+
json={"key": "test key", "data": "test data"},
57+
timeout=30,
58+
)
59+
60+
@patch("semcache.client.requests.Session")
61+
def test_get_success(self, mock_session_class):
62+
"""Test successful get operation."""
63+
# Setup mock
64+
mock_session = Mock()
65+
mock_session_class.return_value = mock_session
66+
mock_response = Mock()
67+
mock_response.status_code = 200
68+
mock_response.raise_for_status.return_value = None
69+
mock_response.text = "test data"
70+
mock_session.post.return_value = mock_response
71+
72+
# Test
73+
client = Semcache()
74+
result = client.get("test key")
75+
76+
# Verify
77+
assert result == "test data"
78+
mock_session.post.assert_called_once_with(
79+
"http://localhost:8080/semcache/v1/get",
80+
json={"key": "test key"},
81+
timeout=30,
82+
)
83+
84+
@patch("semcache.client.requests.Session")
85+
def test_get_not_found(self, mock_session_class):
86+
"""Test get operation when item is not found (404)."""
87+
# Setup mock
88+
mock_session = Mock()
89+
mock_session_class.return_value = mock_session
90+
mock_response = Mock()
91+
mock_response.status_code = 404
92+
mock_session.post.return_value = mock_response
93+
94+
# Test
95+
client = Semcache()
96+
result = client.get("test key")
97+
98+
# Verify
99+
assert result is None
100+
101+
@patch("semcache.client.requests.Session")
102+
def test_connection_error(self, mock_session_class):
103+
"""Test connection error handling."""
104+
# Setup mock
105+
mock_session = Mock()
106+
mock_session_class.return_value = mock_session
107+
mock_session.put.side_effect = requests.exceptions.ConnectionError()
108+
109+
# Test
110+
client = Semcache()
111+
with pytest.raises(SemcacheConnectionError) as exc_info:
112+
client.put("test", "test")
113+
114+
assert "Failed to connect to Semcache server" in str(exc_info.value)
115+
116+
@patch("semcache.client.requests.Session")
117+
def test_timeout_error(self, mock_session_class):
118+
"""Test timeout error handling."""
119+
# Setup mock
120+
mock_session = Mock()
121+
mock_session_class.return_value = mock_session
122+
mock_session.post.side_effect = requests.exceptions.Timeout()
123+
124+
# Test
125+
client = Semcache()
126+
with pytest.raises(SemcacheTimeoutError) as exc_info:
127+
client.get("test")
128+
129+
assert "Request timed out after 30 seconds" in str(exc_info.value)
130+
131+
@patch("semcache.client.requests.Session")
132+
def test_http_error(self, mock_session_class):
133+
"""Test HTTP error handling."""
134+
# Setup mock
135+
mock_session = Mock()
136+
mock_session_class.return_value = mock_session
137+
mock_response = Mock()
138+
mock_response.status_code = 400
139+
mock_response.text = "Bad Request"
140+
mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError()
141+
mock_session.put.return_value = mock_response
142+
143+
# Test
144+
client = Semcache()
145+
with pytest.raises(SemcacheAPIError) as exc_info:
146+
client.put("test", "test")
147+
148+
assert "HTTP 400: Bad Request" in str(exc_info.value)
149+
150+
def test_context_manager(self):
151+
"""Test context manager functionality."""
152+
with patch("semcache.client.requests.Session") as mock_session_class:
153+
mock_session = Mock()
154+
mock_session_class.return_value = mock_session
155+
156+
with Semcache() as client:
157+
assert isinstance(client, Semcache)
158+
159+
mock_session.close.assert_called_once()
160+
161+
def test_close_method(self):
162+
"""Test close method."""
163+
with patch("semcache.client.requests.Session") as mock_session_class:
164+
mock_session = Mock()
165+
mock_session_class.return_value = mock_session
166+
167+
client = Semcache()
168+
client.close()
169+
170+
mock_session.close.assert_called_once()

tests/test_integration.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""
2+
Integration tests for Semcache client.
3+
4+
These tests require a running Semcache server at http://localhost:8080.
5+
Run with: pytest tests/test_integration.py -v -s
6+
"""
7+
8+
import time
9+
10+
import pytest
11+
12+
from semcache import Semcache, SemcacheConnectionError
13+
14+
15+
class TestSemcacheIntegration:
16+
@pytest.fixture
17+
def client(self):
18+
client = Semcache(base_url="http://localhost:8080")
19+
yield client
20+
client.close()
21+
22+
@pytest.fixture(autouse=True)
23+
def check_server_available(self, client):
24+
"""Skip tests if Semcache server is not available."""
25+
try:
26+
# Try a simple operation to check if server is running
27+
# This should return None or a string, but not raise ConnectionError
28+
_ = client.get("test")
29+
except SemcacheConnectionError:
30+
pytest.skip("Semcache server not available at http://localhost:8080")
31+
32+
def test_put_and_get(self, client):
33+
client.put("What is Python?", "Python is a programming language")
34+
35+
time.sleep(0.1)
36+
37+
result = client.get("What is Python?")
38+
assert result == "Python is a programming language"
39+
40+
def test_semantic_similarity(self, client):
41+
client.put("What is the capital of France?", "Paris is the capital of France")
42+
43+
time.sleep(0.1)
44+
45+
# Try similar keys
46+
similar_keys = [
47+
"What's the capital city of France?",
48+
"Tell me the capital of France",
49+
"France's capital is?",
50+
]
51+
52+
for key in similar_keys:
53+
result = client.get(key)
54+
assert result == "Paris is the capital of France", f"Failed for key: {key}"
55+
56+
def test_get_nonexistent(self, client):
57+
result = client.get("This key definitely doesn't exist in the cache")
58+
assert result is None
59+
60+
def test_overwrite_value(self, client):
61+
client.put("test key", "initial value")
62+
time.sleep(0.1)
63+
64+
client.put("test key", "updated value")
65+
time.sleep(0.1)
66+
67+
# Verify new value is returned
68+
result = client.get("test key")
69+
assert result == "updated value"
70+
71+
def test_unicode_content(self, client):
72+
unicode_key = "What is café?"
73+
unicode_data = "Café is coffee in French ☕"
74+
75+
client.put(unicode_key, unicode_data)
76+
time.sleep(0.1)
77+
78+
result = client.get(unicode_key)
79+
assert result == unicode_data
80+
81+
def test_large_content(self, client):
82+
large_data = "x" * 10000 # 10KB of text
83+
84+
client.put("large content test", large_data)
85+
time.sleep(0.1)
86+
87+
result = client.get("large content test")
88+
assert result == large_data
89+
assert len(result) == 10000

0 commit comments

Comments
 (0)