Skip to content
This repository was archived by the owner on Feb 18, 2026. It is now read-only.

Commit 6a9711d

Browse files
committed
Add tests for TagAccessLoggerTagstoreProxy
1 parent fabdf4d commit 6a9711d

1 file changed

Lines changed: 236 additions & 0 deletions

File tree

tests/test_tag_access_logger.py

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
"""Tests for TagAccessLoggerTagstoreProxy."""
2+
3+
from unittest.mock import AsyncMock, MagicMock, patch
4+
5+
import pytest
6+
from graphsenselib.tagstore.db.queries import TagPublic
7+
8+
from gsrest.dependencies import TagAccessLoggerTagstoreProxy
9+
10+
11+
def create_mock_tag(
12+
identifier: str = "test_address",
13+
creator: str = "test_creator",
14+
network: str = "btc",
15+
) -> TagPublic:
16+
"""Create a mock TagPublic object for testing."""
17+
return TagPublic(
18+
identifier=identifier,
19+
label="Test Label",
20+
source="test_source",
21+
creator=creator,
22+
confidence="high",
23+
confidence_level=100,
24+
tag_subject="address",
25+
tag_type="direct",
26+
actor=None,
27+
primary_concept=None,
28+
additional_concepts=[],
29+
is_cluster_definer=False,
30+
network=network,
31+
lastmod=1234567890,
32+
group="public",
33+
inherited_from=None,
34+
tagpack_title="Test Tagpack",
35+
tagpack_uri=None,
36+
)
37+
38+
39+
class TestShouldLogResult:
40+
"""Tests for _should_log_result method."""
41+
42+
def setup_method(self):
43+
self.mock_tagstore_db = MagicMock()
44+
self.mock_redis = AsyncMock()
45+
self.proxy = TagAccessLoggerTagstoreProxy(
46+
self.mock_tagstore_db, self.mock_redis, "test_prefix"
47+
)
48+
49+
def test_returns_false_for_none(self):
50+
"""Should return (False, False) for None result."""
51+
result = self.proxy._should_log_result(None)
52+
assert result == (False, False)
53+
54+
def test_returns_false_for_empty_list(self):
55+
"""Should return (False, False) for empty list."""
56+
result = self.proxy._should_log_result([])
57+
assert result == (False, False)
58+
59+
def test_returns_false_for_empty_string(self):
60+
"""Should return (False, False) for empty string."""
61+
result = self.proxy._should_log_result("")
62+
assert result == (False, False)
63+
64+
def test_returns_true_for_single_tag(self):
65+
"""Should return (True, False) for a single TagPublic object."""
66+
tag = create_mock_tag()
67+
result = self.proxy._should_log_result(tag)
68+
assert result == (True, False)
69+
70+
def test_returns_true_true_for_list_of_tags(self):
71+
"""Should return (True, True) for a list of TagPublic objects."""
72+
tags = [create_mock_tag(identifier="addr1"), create_mock_tag(identifier="addr2")]
73+
result = self.proxy._should_log_result(tags)
74+
assert result == (True, True)
75+
76+
def test_returns_false_for_string(self):
77+
"""Should return (False, False) for string result."""
78+
result = self.proxy._should_log_result("some string")
79+
assert result == (False, False)
80+
81+
def test_returns_false_for_dict(self):
82+
"""Should return (False, False) for dict result."""
83+
result = self.proxy._should_log_result({"key": "value"})
84+
assert result == (False, False)
85+
86+
def test_returns_false_for_list_of_non_tags(self):
87+
"""Should return (False, False) for list of non-TagPublic objects."""
88+
result = self.proxy._should_log_result([1, 2, 3])
89+
assert result == (False, False)
90+
91+
def test_returns_false_for_integer(self):
92+
"""Should return (False, False) for integer result."""
93+
result = self.proxy._should_log_result(42)
94+
assert result == (False, False)
95+
96+
97+
class TestLogTagAccess:
98+
"""Tests for _log_tag_access method."""
99+
100+
def setup_method(self):
101+
self.mock_tagstore_db = MagicMock()
102+
self.mock_redis = AsyncMock()
103+
self.proxy = TagAccessLoggerTagstoreProxy(
104+
self.mock_tagstore_db, self.mock_redis, "tag_access"
105+
)
106+
107+
@pytest.mark.asyncio
108+
async def test_increments_redis_key_with_correct_format(self):
109+
"""Should call redis.incr with correctly formatted key."""
110+
tag = create_mock_tag(
111+
identifier="1ABC123", creator="iknaio", network="btc"
112+
)
113+
114+
with patch("gsrest.dependencies.time") as mock_time:
115+
mock_time.localtime.return_value = None
116+
mock_time.strftime.return_value = "2025-01-27"
117+
118+
await self.proxy._log_tag_access("get_tags", tag)
119+
120+
expected_key = "tag_access|2025-01-27|iknaio|btc|1ABC123"
121+
self.mock_redis.incr.assert_called_once_with(expected_key)
122+
123+
@pytest.mark.asyncio
124+
async def test_uses_configured_prefix(self):
125+
"""Should use the configured prefix in the Redis key."""
126+
proxy = TagAccessLoggerTagstoreProxy(
127+
self.mock_tagstore_db, self.mock_redis, "custom_prefix"
128+
)
129+
tag = create_mock_tag(identifier="addr1", creator="creator1", network="eth")
130+
131+
with patch("gsrest.dependencies.time") as mock_time:
132+
mock_time.localtime.return_value = None
133+
mock_time.strftime.return_value = "2025-12-31"
134+
135+
await proxy._log_tag_access("some_method", tag)
136+
137+
expected_key = "custom_prefix|2025-12-31|creator1|eth|addr1"
138+
self.mock_redis.incr.assert_called_once_with(expected_key)
139+
140+
141+
class TestProxyMethodCalls:
142+
"""Tests for __getattr__ proxy behavior."""
143+
144+
def setup_method(self):
145+
self.mock_tagstore_db = MagicMock()
146+
self.mock_redis = AsyncMock()
147+
self.proxy = TagAccessLoggerTagstoreProxy(
148+
self.mock_tagstore_db, self.mock_redis, "test_prefix"
149+
)
150+
151+
@pytest.mark.asyncio
152+
async def test_proxies_method_call_to_underlying_db(self):
153+
"""Should proxy method calls to the underlying tagstore_db."""
154+
mock_method = AsyncMock(return_value=None)
155+
self.mock_tagstore_db.get_tags = mock_method
156+
157+
await self.proxy.get_tags("btc", "some_address")
158+
159+
mock_method.assert_called_once_with("btc", "some_address")
160+
161+
@pytest.mark.asyncio
162+
async def test_returns_result_from_underlying_method(self):
163+
"""Should return the result from the underlying method."""
164+
expected_result = {"data": "test"}
165+
self.mock_tagstore_db.some_method = AsyncMock(return_value=expected_result)
166+
167+
result = await self.proxy.some_method()
168+
169+
assert result == expected_result
170+
171+
@pytest.mark.asyncio
172+
async def test_logs_single_tag_result(self):
173+
"""Should log access when method returns a single TagPublic."""
174+
tag = create_mock_tag(identifier="addr1", creator="creator1", network="btc")
175+
self.mock_tagstore_db.get_tag = AsyncMock(return_value=tag)
176+
177+
with patch("gsrest.dependencies.time") as mock_time:
178+
mock_time.localtime.return_value = None
179+
mock_time.strftime.return_value = "2025-01-27"
180+
181+
result = await self.proxy.get_tag("btc", "addr1")
182+
183+
assert result == tag
184+
self.mock_redis.incr.assert_called_once()
185+
call_args = self.mock_redis.incr.call_args[0][0]
186+
assert "test_prefix|2025-01-27|creator1|btc|addr1" == call_args
187+
188+
@pytest.mark.asyncio
189+
async def test_logs_each_tag_in_list_result(self):
190+
"""Should log access for each tag when method returns a list."""
191+
tags = [
192+
create_mock_tag(identifier="addr1", creator="creator1", network="btc"),
193+
create_mock_tag(identifier="addr2", creator="creator2", network="btc"),
194+
]
195+
self.mock_tagstore_db.get_tags = AsyncMock(return_value=tags)
196+
197+
with patch("gsrest.dependencies.time") as mock_time:
198+
mock_time.localtime.return_value = None
199+
mock_time.strftime.return_value = "2025-01-27"
200+
201+
result = await self.proxy.get_tags("btc", ["addr1", "addr2"])
202+
203+
assert result == tags
204+
assert self.mock_redis.incr.call_count == 2
205+
206+
@pytest.mark.asyncio
207+
async def test_does_not_log_non_tag_results(self):
208+
"""Should not log when method returns non-TagPublic results."""
209+
self.mock_tagstore_db.get_count = AsyncMock(return_value=42)
210+
211+
result = await self.proxy.get_count()
212+
213+
assert result == 42
214+
self.mock_redis.incr.assert_not_called()
215+
216+
@pytest.mark.asyncio
217+
async def test_does_not_log_when_redis_client_is_none(self):
218+
"""Should not attempt to log when redis_client is None."""
219+
proxy = TagAccessLoggerTagstoreProxy(
220+
self.mock_tagstore_db, None, "test_prefix"
221+
)
222+
tag = create_mock_tag()
223+
self.mock_tagstore_db.get_tag = AsyncMock(return_value=tag)
224+
225+
result = await proxy.get_tag("btc", "addr1")
226+
227+
assert result == tag
228+
# No exception should be raised
229+
230+
def test_proxies_non_callable_attributes(self):
231+
"""Should proxy non-callable attributes directly."""
232+
self.mock_tagstore_db.some_attribute = "test_value"
233+
234+
result = self.proxy.some_attribute
235+
236+
assert result == "test_value"

0 commit comments

Comments
 (0)