|
1 | 1 | """Tests for V2 knowledge graph API routes (ID-based endpoints).""" |
2 | 2 |
|
| 3 | +from datetime import datetime, timezone |
3 | 4 | import uuid |
4 | 5 |
|
5 | 6 | import pytest |
6 | 7 | from httpx import AsyncClient |
7 | 8 |
|
8 | | -from basic_memory.models import Project |
| 9 | +from basic_memory.models import Entity as EntityModel, Project |
| 10 | +from basic_memory.repository.entity_repository import EntityRepository |
| 11 | +from basic_memory.repository.project_repository import ProjectRepository |
9 | 12 | from basic_memory.schemas import DeleteEntitiesResponse |
10 | 13 | from basic_memory.schemas.response import DirectoryMoveResult, DirectoryDeleteResult |
11 | 14 | from basic_memory.schemas.v2 import EntityResponseV2, EntityResolveResponse |
@@ -40,10 +43,55 @@ async def test_resolve_identifier_by_permalink( |
40 | 43 | assert response.status_code == 200 |
41 | 44 | resolved = EntityResolveResponse.model_validate(response.json()) |
42 | 45 | assert resolved.entity_id == entity_id |
| 46 | + assert resolved.project_external_id == test_project.external_id |
43 | 47 | assert resolved.permalink == created_entity.permalink |
44 | 48 | assert resolved.resolution_method == "permalink" |
45 | 49 |
|
46 | 50 |
|
| 51 | +@pytest.mark.asyncio |
| 52 | +async def test_resolve_identifier_returns_target_project_external_id_for_cross_project_link( |
| 53 | + client: AsyncClient, |
| 54 | + session_maker, |
| 55 | + tmp_path, |
| 56 | + v2_project_url, |
| 57 | +): |
| 58 | + """Cross-project resolves should expose the owning project external ID.""" |
| 59 | + project_repository = ProjectRepository(session_maker) |
| 60 | + other_project = await project_repository.create( |
| 61 | + { |
| 62 | + "name": "other-project", |
| 63 | + "description": "Secondary project", |
| 64 | + "path": str(tmp_path / "other-project"), |
| 65 | + "is_active": True, |
| 66 | + "is_default": False, |
| 67 | + } |
| 68 | + ) |
| 69 | + now = datetime.now(timezone.utc) |
| 70 | + other_entity_repository = EntityRepository(session_maker, project_id=other_project.id) |
| 71 | + target = await other_entity_repository.add( |
| 72 | + EntityModel( |
| 73 | + title="Cross Project Note", |
| 74 | + note_type="note", |
| 75 | + content_type="text/markdown", |
| 76 | + file_path="docs/Cross Project Note.md", |
| 77 | + permalink=f"{other_project.permalink}/docs/cross-project-note", |
| 78 | + created_at=now, |
| 79 | + updated_at=now, |
| 80 | + project_id=other_project.id, |
| 81 | + ) |
| 82 | + ) |
| 83 | + |
| 84 | + response = await client.post( |
| 85 | + f"{v2_project_url}/knowledge/resolve", |
| 86 | + json={"identifier": "other-project::Cross Project Note", "strict": True}, |
| 87 | + ) |
| 88 | + |
| 89 | + assert response.status_code == 200 |
| 90 | + resolved = EntityResolveResponse.model_validate(response.json()) |
| 91 | + assert resolved.entity_id == target.id |
| 92 | + assert resolved.project_external_id == other_project.external_id |
| 93 | + |
| 94 | + |
47 | 95 | @pytest.mark.asyncio |
48 | 96 | async def test_resolve_identifier_not_found(client: AsyncClient, v2_project_url): |
49 | 97 | """Test resolving a non-existent identifier returns 404.""" |
|
0 commit comments