|
7 | 7 | import pytest |
8 | 8 | from httpx import AsyncClient |
9 | 9 |
|
| 10 | +from basic_memory.ignore_utils import get_bmignore_path |
10 | 11 | from basic_memory.models import Entity as EntityModel, Project |
11 | 12 | from basic_memory.repository.entity_repository import EntityRepository |
12 | 13 | from basic_memory.repository.project_repository import ProjectRepository |
@@ -1120,6 +1121,62 @@ async def test_sync_file_path_through_file_returns_404( |
1120 | 1121 | assert "File not found on disk" in response.json()["detail"] |
1121 | 1122 |
|
1122 | 1123 |
|
| 1124 | +@pytest.mark.asyncio |
| 1125 | +async def test_sync_file_rejects_hidden_file( |
| 1126 | + client: AsyncClient, v2_project_url, test_project: Project |
| 1127 | +): |
| 1128 | + """sync-file refuses hidden files, matching the default '.*' ignore pattern.""" |
| 1129 | + hidden_path = Path(test_project.path) / ".secrets.md" |
| 1130 | + hidden_path.write_text("# Hidden\n\nShould never be indexed.\n", encoding="utf-8") |
| 1131 | + |
| 1132 | + response = await client.post( |
| 1133 | + f"{v2_project_url}/knowledge/sync-file", |
| 1134 | + json={"file_path": ".secrets.md"}, |
| 1135 | + ) |
| 1136 | + assert response.status_code == 400 |
| 1137 | + assert "ignore rules" in response.json()["detail"] |
| 1138 | + |
| 1139 | + |
| 1140 | +@pytest.mark.asyncio |
| 1141 | +async def test_sync_file_rejects_gitignored_file( |
| 1142 | + client: AsyncClient, v2_project_url, test_project: Project |
| 1143 | +): |
| 1144 | + """sync-file honors the project .gitignore, matching scan/watch filtering.""" |
| 1145 | + project_path = Path(test_project.path) |
| 1146 | + (project_path / ".gitignore").write_text("private/\n", encoding="utf-8") |
| 1147 | + note_path = project_path / "private" / "secret.md" |
| 1148 | + note_path.parent.mkdir(parents=True, exist_ok=True) |
| 1149 | + note_path.write_text("# Secret\n\nGitignored content.\n", encoding="utf-8") |
| 1150 | + |
| 1151 | + response = await client.post( |
| 1152 | + f"{v2_project_url}/knowledge/sync-file", |
| 1153 | + json={"file_path": "private/secret.md"}, |
| 1154 | + ) |
| 1155 | + assert response.status_code == 400 |
| 1156 | + assert "ignore rules" in response.json()["detail"] |
| 1157 | + |
| 1158 | + |
| 1159 | +@pytest.mark.asyncio |
| 1160 | +async def test_sync_file_rejects_bmignored_file( |
| 1161 | + client: AsyncClient, v2_project_url, test_project: Project |
| 1162 | +): |
| 1163 | + """sync-file honors user .bmignore patterns, matching scan/watch filtering.""" |
| 1164 | + bmignore_path = get_bmignore_path() |
| 1165 | + bmignore_path.parent.mkdir(parents=True, exist_ok=True) |
| 1166 | + bmignore_path.write_text("drafts-wip\n", encoding="utf-8") |
| 1167 | + |
| 1168 | + note_path = Path(test_project.path) / "drafts-wip" / "scratch.md" |
| 1169 | + note_path.parent.mkdir(parents=True, exist_ok=True) |
| 1170 | + note_path.write_text("# Scratch\n\nBmignored content.\n", encoding="utf-8") |
| 1171 | + |
| 1172 | + response = await client.post( |
| 1173 | + f"{v2_project_url}/knowledge/sync-file", |
| 1174 | + json={"file_path": "drafts-wip/scratch.md"}, |
| 1175 | + ) |
| 1176 | + assert response.status_code == 400 |
| 1177 | + assert "ignore rules" in response.json()["detail"] |
| 1178 | + |
| 1179 | + |
1123 | 1180 | @pytest.mark.asyncio |
1124 | 1181 | async def test_sync_file_rejects_non_markdown( |
1125 | 1182 | client: AsyncClient, v2_project_url, test_project: Project |
|
0 commit comments