-
Notifications
You must be signed in to change notification settings - Fork 195
Expand file tree
/
Copy pathtest_tool_build_context.py
More file actions
144 lines (115 loc) · 4.97 KB
/
test_tool_build_context.py
File metadata and controls
144 lines (115 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""Tests for discussion context MCP tool."""
import pytest
from datetime import datetime
from mcp.server.fastmcp.exceptions import ToolError
from basic_memory.mcp.tools import build_context
from basic_memory.schemas.memory import (
GraphContext,
)
@pytest.mark.asyncio
async def test_get_basic_discussion_context(client, test_graph, test_project):
"""Test getting basic discussion context."""
context = await build_context.fn(project=test_project.name, url="memory://test/root")
assert isinstance(context, GraphContext)
assert len(context.results) == 1
assert context.results[0].primary_result.permalink == "test/root"
assert len(context.results[0].related_results) > 0
# Verify metadata
assert context.metadata.uri == "test/root"
assert context.metadata.depth == 1 # default depth
assert context.metadata.timeframe is not None
assert isinstance(context.metadata.generated_at, datetime)
assert context.metadata.primary_count == 1
if context.metadata.related_count:
assert context.metadata.related_count > 0
@pytest.mark.asyncio
async def test_get_discussion_context_pattern(client, test_graph, test_project):
"""Test getting context with pattern matching."""
context = await build_context.fn(project=test_project.name, url="memory://test/*", depth=1)
assert isinstance(context, GraphContext)
assert len(context.results) > 1 # Should match multiple test/* paths
assert all("test/" in item.primary_result.permalink for item in context.results) # pyright: ignore [reportOperatorIssue]
assert context.metadata.depth == 1
@pytest.mark.asyncio
async def test_get_discussion_context_timeframe(client, test_graph, test_project):
"""Test timeframe parameter filtering."""
# Get recent context
recent_context = await build_context.fn(
project=test_project.name,
url="memory://test/root",
timeframe="1d", # Last 24 hours
)
# Get older context
older_context = await build_context.fn(
project=test_project.name,
url="memory://test/root",
timeframe="30d", # Last 30 days
)
# Calculate total related items
total_recent_related = (
sum(len(item.related_results) for item in recent_context.results)
if recent_context.results
else 0
)
total_older_related = (
sum(len(item.related_results) for item in older_context.results)
if older_context.results
else 0
)
assert total_older_related >= total_recent_related
@pytest.mark.asyncio
async def test_get_discussion_context_not_found(client, test_project):
"""Test handling of non-existent URIs."""
context = await build_context.fn(project=test_project.name, url="memory://test/does-not-exist")
assert isinstance(context, GraphContext)
assert len(context.results) == 0
assert context.metadata.primary_count == 0
assert context.metadata.related_count == 0
# Test data for different timeframe formats
valid_timeframes = [
"7d", # Standard format
"yesterday", # Natural language
"0d", # Zero duration
]
invalid_timeframes = [
"invalid", # Nonsense string
# NOTE: "tomorrow" now returns 1 day ago due to timezone safety - no longer invalid
]
@pytest.mark.asyncio
async def test_build_context_timeframe_formats(client, test_graph, test_project):
"""Test that build_context accepts various timeframe formats."""
test_url = "memory://specs/test"
# Test each valid timeframe
for timeframe in valid_timeframes:
try:
result = await build_context.fn(
project=test_project.name,
url=test_url,
timeframe=timeframe,
page=1,
page_size=10,
max_related=10,
)
assert result is not None
except Exception as e:
pytest.fail(f"Failed with valid timeframe '{timeframe}': {str(e)}")
# Test invalid timeframes should raise ValidationError
for timeframe in invalid_timeframes:
with pytest.raises(ToolError):
await build_context.fn(project=test_project.name, url=test_url, timeframe=timeframe)
@pytest.mark.asyncio
async def test_build_context_string_depth_parameter(client, test_graph, test_project):
"""Test that build_context handles string depth parameter correctly."""
test_url = "memory://test/root"
# Test valid string depth parameter - should either raise ToolError or convert to int
try:
result = await build_context.fn(url=test_url, depth="2", project=test_project.name)
# If it succeeds, verify the depth was converted to an integer
assert isinstance(result.metadata.depth, int)
assert result.metadata.depth == 2
except ToolError:
# This is also acceptable behavior - type validation should catch it
pass
# Test invalid string depth parameter - should raise ToolError
with pytest.raises(ToolError):
await build_context.fn(test_url, depth="invalid", project=test_project.name)