-
Notifications
You must be signed in to change notification settings - Fork 206
Expand file tree
/
Copy pathtest_deps.py
More file actions
232 lines (182 loc) · 8.75 KB
/
test_deps.py
File metadata and controls
232 lines (182 loc) · 8.75 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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
"""Tests for dependency injection functions in deps.py."""
from datetime import datetime, timezone
from pathlib import Path
import pytest
import pytest_asyncio
from fastapi import HTTPException
from basic_memory.deps import get_project_config, get_project_id
from basic_memory.deps.projects import validate_project_id
from basic_memory.models.project import Project
from basic_memory.repository.project_repository import ProjectRepository
@pytest_asyncio.fixture
async def project_with_spaces(project_repository: ProjectRepository) -> Project:
"""Create a project with spaces in the name for testing permalink normalization."""
project_data = {
"name": "My Test Project",
"description": "A project with spaces in the name",
"path": "/my/test/project",
"is_active": True,
"is_default": False,
"created_at": datetime.now(timezone.utc),
"updated_at": datetime.now(timezone.utc),
}
return await project_repository.create(project_data)
@pytest_asyncio.fixture
async def project_with_special_chars(project_repository: ProjectRepository) -> Project:
"""Create a project with special characters for testing permalink normalization."""
project_data = {
"name": "Project: Test & Development!",
"description": "A project with special characters",
"path": "/project/test/dev",
"is_active": True,
"is_default": False,
"created_at": datetime.now(timezone.utc),
"updated_at": datetime.now(timezone.utc),
}
return await project_repository.create(project_data)
@pytest.mark.asyncio
async def test_get_project_config_with_spaces(
project_repository: ProjectRepository, project_with_spaces: Project
):
"""Test that get_project_config normalizes project names with spaces."""
# The project name has spaces: "My Test Project"
# The permalink should be: "my-test-project"
assert project_with_spaces.name == "My Test Project"
assert project_with_spaces.permalink == "my-test-project"
# Call get_project_config with the project name (not permalink)
# This simulates what happens when the project name comes from URL path
config = await get_project_config(
project="My Test Project", project_repository=project_repository
)
# Verify we got the correct project config
assert config.name == "My Test Project"
assert config.home == Path("/my/test/project")
@pytest.mark.asyncio
async def test_get_project_config_with_permalink(
project_repository: ProjectRepository, project_with_spaces: Project
):
"""Test that get_project_config works when already given a permalink."""
# Call with the permalink directly
config = await get_project_config(
project="my-test-project", project_repository=project_repository
)
# Verify we got the correct project config
assert config.name == "My Test Project"
assert config.home == Path("/my/test/project")
@pytest.mark.asyncio
async def test_get_project_config_with_special_chars(
project_repository: ProjectRepository, project_with_special_chars: Project
):
"""Test that get_project_config normalizes project names with special characters."""
# The project name has special chars: "Project: Test & Development!"
# The permalink should be: "project-test-development"
assert project_with_special_chars.name == "Project: Test & Development!"
assert project_with_special_chars.permalink == "project-test-development"
# Call get_project_config with the project name
config = await get_project_config(
project="Project: Test & Development!", project_repository=project_repository
)
# Verify we got the correct project config
assert config.name == "Project: Test & Development!"
assert config.home == Path("/project/test/dev")
@pytest.mark.asyncio
async def test_get_project_config_not_found(project_repository: ProjectRepository):
"""Test that get_project_config raises HTTPException when project not found."""
with pytest.raises(HTTPException) as exc_info:
await get_project_config(
project="Nonexistent Project", project_repository=project_repository
)
assert exc_info.value.status_code == 404
assert "Project 'Nonexistent Project' not found" in exc_info.value.detail
@pytest.mark.asyncio
async def test_get_project_id_with_spaces(
project_repository: ProjectRepository, project_with_spaces: Project
):
"""Test that get_project_id normalizes project names with spaces."""
# Call get_project_id with the project name (not permalink)
project_id = await get_project_id(
project_repository=project_repository, project="My Test Project"
)
# Verify we got the correct project ID
assert project_id == project_with_spaces.id
@pytest.mark.asyncio
async def test_get_project_id_with_permalink(
project_repository: ProjectRepository, project_with_spaces: Project
):
"""Test that get_project_id works when already given a permalink."""
# Call with the permalink directly
project_id = await get_project_id(
project_repository=project_repository, project="my-test-project"
)
# Verify we got the correct project ID
assert project_id == project_with_spaces.id
@pytest.mark.asyncio
async def test_get_project_id_with_special_chars(
project_repository: ProjectRepository, project_with_special_chars: Project
):
"""Test that get_project_id normalizes project names with special characters."""
# Call get_project_id with the project name
project_id = await get_project_id(
project_repository=project_repository, project="Project: Test & Development!"
)
# Verify we got the correct project ID
assert project_id == project_with_special_chars.id
@pytest.mark.asyncio
async def test_get_project_id_not_found(project_repository: ProjectRepository):
"""Test that get_project_id raises HTTPException when project not found."""
with pytest.raises(HTTPException) as exc_info:
await get_project_id(project_repository=project_repository, project="Nonexistent Project")
assert exc_info.value.status_code == 404
assert "Project 'Nonexistent Project' not found" in exc_info.value.detail
@pytest.mark.asyncio
async def test_get_project_id_fallback_to_name(
project_repository: ProjectRepository, test_project: Project
):
"""Test that get_project_id falls back to name lookup if permalink lookup fails.
This test verifies the fallback behavior in get_project_id where it tries
get_by_name if get_by_permalink returns None.
"""
# The test_project fixture has name "test-project" and permalink "test-project"
# Since both are the same, we can't easily test the fallback with existing fixtures
# So this test just verifies the normal path works with test_project
project_id = await get_project_id(project_repository=project_repository, project="test-project")
assert project_id == test_project.id
@pytest.mark.asyncio
async def test_get_project_config_case_sensitivity(
project_repository: ProjectRepository, project_with_spaces: Project
):
"""Test that get_project_config handles case variations correctly.
Permalink normalization should convert to lowercase, so different case
variations of the same name should resolve to the same project.
"""
# Create project with mixed case: "My Test Project" -> permalink "my-test-project"
# Try with different case variations
config1 = await get_project_config(
project="My Test Project", project_repository=project_repository
)
config2 = await get_project_config(
project="my test project", project_repository=project_repository
)
config3 = await get_project_config(
project="MY TEST PROJECT", project_repository=project_repository
)
# All should resolve to the same project
assert config1.name == config2.name == config3.name == "My Test Project"
assert config1.home == config2.home == config3.home == Path("/my/test/project")
# --- Tests for validate_project_id (v2 API) ---
@pytest.mark.asyncio
async def test_validate_project_id_success(
project_repository: ProjectRepository, test_project: Project
):
"""Test that validate_project_id returns project_id when project exists."""
project_id = await validate_project_id(
project_id=test_project.id, project_repository=project_repository
)
assert project_id == test_project.id
@pytest.mark.asyncio
async def test_validate_project_id_not_found(project_repository: ProjectRepository):
"""Test that validate_project_id raises HTTPException when project not found."""
with pytest.raises(HTTPException) as exc_info:
await validate_project_id(project_id=99999, project_repository=project_repository)
assert exc_info.value.status_code == 404
assert "Project with ID 99999 not found" in exc_info.value.detail