fix tests

This commit is contained in:
phernandez
2024-12-24 20:29:21 -06:00
parent d713097333
commit 90ca41fead
16 changed files with 137 additions and 138 deletions
+1 -9
View File
@@ -127,15 +127,7 @@ async def delete_relations(
data: DeleteRelationsRequest, knowledge_service: KnowledgeServiceDep
) -> CreateEntityResponse:
"""Delete relations between entities."""
to_delete = [
{
"from_id": relation.from_id,
"to_id": relation.to_id,
"relation_type": relation.relation_type,
}
for relation in data.relations
]
updated_entities = await knowledge_service.delete_relations(to_delete)
updated_entities = await knowledge_service.delete_relations(data.relations)
return CreateEntityResponse(
entities=[EntityResponse.model_validate(entity) for entity in updated_entities]
)
@@ -79,8 +79,12 @@ class EntityRepository(Repository[Entity]):
def get_load_options(self) -> List[LoaderOption]:
return [
selectinload(Entity.observations),
# Load from_relations and both entities for each relation
selectinload(Entity.from_relations).selectinload(Relation.from_entity),
selectinload(Entity.from_relations).selectinload(Relation.to_entity),
# Load to_relations and both entities for each relation
selectinload(Entity.to_relations).selectinload(Relation.from_entity),
selectinload(Entity.to_relations).selectinload(Relation.to_entity),
]
async def find_by_path_ids(self, path_ids: List[str]) -> Sequence[Entity]:
@@ -1,10 +1,10 @@
"""Repository for managing Relation objects."""
from typing import Sequence, List
from sqlalchemy import and_
from typing import Sequence, List, Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import async_sessionmaker
from sqlalchemy.orm import selectinload
from sqlalchemy.orm import selectinload, aliased
from sqlalchemy.orm.interfaces import LoaderOption
from basic_memory.models import Relation, Entity
@@ -17,6 +17,25 @@ class RelationRepository(Repository[Relation]):
def __init__(self, session_maker: async_sessionmaker):
super().__init__(session_maker, Relation)
async def find_relation(self, from_path_id: str, to_path_id: str, relation_type: str) -> Optional[Relation]:
"""Find a relation by its from and to path IDs."""
from_entity = aliased(Entity)
to_entity = aliased(Entity)
query = (
select(Relation)
.join(from_entity, Relation.from_id == from_entity.id)
.join(to_entity, Relation.to_id == to_entity.id)
.where(
and_(
from_entity.path_id == from_path_id,
to_entity.path_id == to_path_id,
Relation.relation_type == relation_type
)
)
)
return await self.find_one(query)
async def find_by_entity(self, from_entity_id: int) -> Sequence[Relation]:
"""Find all relations from a specific entity."""
query = select(Relation).filter(Relation.from_id == from_entity_id)
+5
View File
@@ -8,3 +8,8 @@ class EntityNotFoundError(Exception):
"""Raised when an entity cannot be found"""
pass
class EntityCreationError(Exception):
"""Raised when an entity cannot be created"""
pass
@@ -7,6 +7,7 @@ from loguru import logger
from basic_memory.models import Entity as EntityModel
from basic_memory.schemas import Entity as EntitySchema
from .files import FileOperations
from ..exceptions import EntityCreationError, EntityNotFoundError
class EntityOperations(FileOperations):
@@ -49,7 +50,7 @@ class EntityOperations(FileOperations):
for entity in entities:
created_entity = await self.create_entity(entity)
created.append(created_entity)
return created
async def delete_entity(self, path_id: str) -> bool:
@@ -59,8 +60,6 @@ class EntityOperations(FileOperations):
try:
# Get entity first for file deletion
entity = await self.entity_service.get_by_path_id(path_id)
if not entity:
return True # Already deleted
# Delete file first (it's source of truth)
path = self.get_entity_path(entity)
@@ -68,7 +67,11 @@ class EntityOperations(FileOperations):
# Delete from DB (this will cascade to observations/relations)
return await self.entity_service.delete_entity(path_id)
except EntityNotFoundError:
logger.info(f"Entity not found: {path_id}")
return True # Already deleted
except Exception as e:
logger.error(f"Failed to delete entity: {e}")
raise
@@ -36,10 +36,11 @@ class ObservationOperations(RelationOperations):
updated_entity = await self.entity_service.get_by_path_id(path_id)
# Write updated file and checksum
checksum = await self.write_entity_file(entity)
_, checksum = await self.write_entity_file(entity)
await self.entity_service.update_entity(path_id, {"checksum": checksum})
return updated_entity
# query to fetch all relations
return await self.entity_service.get_by_path_id(path_id)
except Exception as e:
logger.error(f"Failed to add observations: {e}")
@@ -59,7 +60,7 @@ class ObservationOperations(RelationOperations):
await self.observation_service.delete_observations(entity.id, observations)
# Write updated file
checksum = await self.write_entity_file(entity)
_, checksum = await self.write_entity_file(entity)
await self.entity_service.update_entity(path_id, {"checksum": checksum})
# Get final entity with all updates
@@ -66,20 +66,26 @@ class RelationOperations(EntityOperations):
# select again to eagerly load all relations
return await self.entity_service.open_nodes([e.path_id for e in updated_entities])
async def delete_relations(self, to_delete: List[Dict[str, Any]]) -> Sequence[EntityModel]:
async def delete_relations(self, to_delete: List[RelationSchema]) -> Sequence[EntityModel]:
"""Delete relations and return all updated entities."""
logger.debug(f"Deleting {len(to_delete)} relations")
updated_entities = []
entities_to_update = set()
relations = []
try:
# Delete relations from DB
for relation in to_delete:
entities_to_update.add(relation["from_id"])
entities_to_update.add(relation["to_id"])
entities_to_update.add(relation.from_id)
entities_to_update.add(relation.to_id)
relation = await self.relation_service.find_relation(relation.from_id, relation.to_id, relation.relation_type)
if relation:
relations.append(relation)
deleted = await self.relation_service.delete_relations(to_delete)
if not deleted:
# pass Relation models to delete
num_deleted = await self.relation_service.delete_relations(relations)
if num_deleted == 0:
logger.warning("No relations were deleted")
# Get fresh copies of all updated entities
@@ -91,7 +97,7 @@ class RelationOperations(EntityOperations):
raise EntityNotFoundError(f"Entity not found: {path_id}")
# Write updated file
checksum = await self.write_entity_file(entity)
_, checksum = await self.write_entity_file(entity)
updated = await self.entity_service.update_entity(
path_id, {"checksum": checksum}
)
+7 -11
View File
@@ -23,6 +23,9 @@ class RelationService(BaseService[RelationRepository]):
logger.debug(f"Creating relation: {relation}")
return await self.repository.add(relation)
async def find_relation(self, from_path_id: str, to_path_id: str, relation_type: str) -> Relation:
return await self.repository.find_relation(from_path_id, to_path_id, relation_type)
async def delete_relation(
self, from_entity: Entity, to_entity: Entity, relation_type: str
) -> bool:
@@ -37,20 +40,13 @@ class RelationService(BaseService[RelationRepository]):
)
return result
async def delete_relations(self, relations: List[Dict[str, Any]]) -> bool:
async def delete_relations(self, relations: List[Relation]) -> int:
"""Delete relations matching specified criteria."""
logger.debug(f"Deleting {len(relations)} relations")
deleted = False
for relation in relations:
filters = {"from_id": relation["from_id"], "to_id": relation["to_id"]}
if "relation_type" in relation:
filters["relation_type"] = relation["relation_type"]
result = await self.repository.delete_by_fields(**filters)
if result:
deleted = True
return deleted
ids = [relation.id for relation in relations]
return await self.repository.delete_by_ids(ids)
async def create_relations(self, relations: List[Relation]) -> Sequence[Relation]:
"""Create multiple relations between entities."""
+10 -10
View File
@@ -308,10 +308,10 @@ async def test_delete_relations(client, relation_repository):
async def test_delete_nonexistent_entity(client: AsyncClient):
"""Test deleting a nonexistent entity by path ID."""
response = await client.post(
"/knowledge/entities/delete", json={"entity_ids": ["test/NonExistent"]}
"/knowledge/entities/delete", json={"entity_ids": ["test/non_existent"]}
)
assert response.status_code == 200
assert response.json() == {"deleted": False}
assert response.json() == {"deleted": True}
@pytest.mark.asyncio
@@ -336,8 +336,8 @@ async def test_delete_nonexistent_relations(client: AsyncClient):
request_data = {
"relations": [
{
"from_id": "test/NonExistent1",
"to_id": "test/NonExistent2",
"from_id": "test/non_existent1",
"to_id": "test/non_existent2",
"relation_type": "nonexistent",
}
]
@@ -389,13 +389,13 @@ async def test_full_knowledge_flow(client: AsyncClient):
json={
"relations": [
{
"from_id": "test/MainEntity",
"to_id": "test/RelatedOne",
"from_id": "test/main_entity",
"to_id": "test/related_one",
"relation_type": "connects_to",
},
{
"from_id": "test/MainEntity",
"to_id": "test/RelatedTwo",
"from_id": "test/main_entity",
"to_id": "test/related_two",
"relation_type": "connects_to",
},
]
@@ -407,7 +407,7 @@ async def test_full_knowledge_flow(client: AsyncClient):
await client.post(
"/knowledge/observations",
json={
"entity_id": "test/MainEntity",
"entity_id": "test/main_entity",
"observations": [
"Connected to first related entity",
"Connected to second related entity",
@@ -428,7 +428,7 @@ async def test_full_knowledge_flow(client: AsyncClient):
# 6. Search should find all related entities
search = await client.post("/knowledge/search", json={"query": "Related"})
matches = search.json()["matches"]
assert len(matches) == 2 # Should find both related entities
assert len(matches) == 3 # Should find both related entities, and the main one with the observation
# 7. Delete main entity
response = await client.post(
@@ -87,7 +87,7 @@ async def test_delete_observations(session_maker: async_sessionmaker, repo):
"""Test deleting observations by entity_id."""
# Create test entity
async with db.scoped_session(session_maker) as session:
entity = Entity(name="test_entity", entity_type="test", description="Test entity")
entity = Entity(name="test_entity", entity_type="test", description="Test entity", path_id="test/test_entity")
session.add(entity)
await session.flush()
@@ -110,7 +110,7 @@ async def test_delete_observation_by_id(session_maker: async_sessionmaker, repo)
"""Test deleting a single observation by its ID."""
# Create test entity
async with db.scoped_session(session_maker) as session:
entity = Entity(name="test_entity", entity_type="test", description="Test entity")
entity = Entity(name="test_entity", entity_type="test", description="Test entity", path_id="test/test_entity")
session.add(entity)
await session.flush()
@@ -132,7 +132,7 @@ async def test_delete_observation_by_content(session_maker: async_sessionmaker,
"""Test deleting observations by content."""
# Create test entity
async with db.scoped_session(session_maker) as session:
entity = Entity(name="test_entity", entity_type="test", description="Test entity")
entity = Entity(name="test_entity", entity_type="test", description="Test entity", path_id="test/test_entity")
session.add(entity)
await session.flush()
@@ -15,6 +15,7 @@ async def source_entity(session_maker):
entity = Entity(
name="test_source",
entity_type="source",
path_id="source/test_source",
description="Source entity",
)
async with db.scoped_session(session_maker) as session:
@@ -29,6 +30,7 @@ async def target_entity(session_maker):
entity = Entity(
name="test_target",
entity_type="target",
path_id="target/test_target",
description="Target entity",
)
async with db.scoped_session(session_maker) as session:
@@ -56,6 +58,7 @@ async def related_entity(entity_repository):
entity_data = {
"name": "Related Entity",
"entity_type": "test",
"path_id": "test/related_entity",
"description": "A related test entity",
"references": "",
}
@@ -151,6 +154,14 @@ async def test_find_by_entities(
assert relations[0].id == sample_relation.id
assert relations[0].relation_type == sample_relation.relation_type
@pytest.mark.asyncio
async def test_find_relation(relation_repository: RelationRepository, sample_relation: Relation):
"""Test finding relations by type"""
relation = await relation_repository.find_relation(from_path_id=sample_relation.from_entity.path_id,
to_path_id=sample_relation.to_entity.path_id,
relation_type=sample_relation.relation_type)
assert relation.id == sample_relation.id
@pytest.mark.asyncio
async def test_find_by_type(relation_repository: RelationRepository, sample_relation: Relation):
+4 -5
View File
@@ -100,20 +100,19 @@ def test_entity_out_from_attributes():
"""Test EntityOut creation from database model attributes."""
# Simulate database model attributes
db_data = {
"id": "123",
"path_id": "test/test",
"name": "test",
"entity_type": "test",
"description": "test description",
"observations": [{"id": 1, "content": "test obs", "context": None}],
"relations": [
{"id": 1, "from_id": 123, "to_id": 456, "relation_type": "test", "context": None}
{"id": 1, "from_id": "test/test", "to_id": "test/test", "relation_type": "test", "context": None}
],
}
entity = EntityResponse.model_validate(db_data)
assert entity.id == 123
assert entity.path_id == "test/test"
assert entity.description == "test description"
assert len(entity.observations) == 1
assert entity.observations[0].id == 1
assert len(entity.relations) == 1
@@ -155,7 +154,7 @@ def test_search_nodes_input():
def test_open_nodes_input():
"""Test OpenNodesInput validation."""
open_input = OpenNodesRequest.model_validate({"entity_ids": [1, 2]})
open_input = OpenNodesRequest.model_validate({"entity_ids": ["test", "test2"]})
assert len(open_input.entity_ids) == 2
# Empty names list should fail
+5 -4
View File
@@ -15,9 +15,10 @@ async def file_sync_service(document_repository) -> FileSyncService:
@pytest_asyncio.fixture
async def docs_dir(test_project_path) -> Path:
async def docs_dir(test_config) -> Path:
"""Get documents directory."""
return test_project_path / "documents"
test_config.documents_dir.mkdir(parents=True)
return test_config.documents_dir
@pytest_asyncio.fixture
@@ -26,8 +27,8 @@ async def sample_files(docs_dir) -> dict[str, str]:
# Create test structure
design_dir = docs_dir / "design"
notes_dir = docs_dir / "notes"
design_dir.mkdir(exist_ok=True)
notes_dir.mkdir(exist_ok=True)
design_dir.mkdir(parents=True, exist_ok=True)
notes_dir.mkdir(parents=True, exist_ok=True)
# Map of relative paths to content
files = {
+20 -24
View File
@@ -4,11 +4,12 @@ from pathlib import Path
from typing import List
import pytest
from sqlalchemy.exc import IntegrityError
from basic_memory.models import Entity as EntityModel
from basic_memory.schemas import Entity as EntitySchema, Relation as RelationSchema
from basic_memory.services import EntityService
from basic_memory.services.exceptions import EntityNotFoundError, FileOperationError
from basic_memory.services.exceptions import EntityNotFoundError, FileOperationError, EntityCreationError
from basic_memory.services.knowledge import KnowledgeService
@@ -17,7 +18,7 @@ async def test_get_entity_path(knowledge_service: KnowledgeService):
"""Should generate correct filesystem path for entity."""
entity = EntityModel(id=1, name="test-entity", entity_type="concept", description="Test entity")
path = knowledge_service.get_entity_path(entity)
assert path == Path(knowledge_service.base_path / "knowledge/concept/test-entity.md")
assert path == Path(knowledge_service.base_path / "concept/test-entity.md")
@pytest.mark.asyncio
@@ -71,8 +72,8 @@ async def test_create_relations(knowledge_service: KnowledgeService, entity_serv
# Create relation
relations = [
RelationSchema(
from_id=entity1.id,
to_id=entity2.id,
from_id=entity1.path_id,
to_id=entity2.path_id,
relation_type="test_relation",
context="Test context",
)
@@ -82,9 +83,9 @@ async def test_create_relations(knowledge_service: KnowledgeService, entity_serv
assert len(updated_entities) == 2
# Verify files were updated
for entity_id in [entity1.id, entity2.id]:
entity = await entity_service.get_entity(entity_id)
file_path = knowledge_service.get_entity_path(entity)
for entity in [entity1, entity2]:
found = await entity_service.get_by_path_id(entity.path_id)
file_path = knowledge_service.get_entity_path(found)
content, _ = await knowledge_service.file_service.read_file(file_path)
assert "test_relation" in content
@@ -100,7 +101,7 @@ async def test_add_observations(knowledge_service: KnowledgeService):
# Add observations
observations = ["Test observation 1", "Test observation 2"]
updated_entity = await knowledge_service.add_observations(
entity.id, observations, "Test context"
entity.path_id, observations, "Test context"
)
# Verify observations in DB
@@ -128,7 +129,7 @@ async def test_delete_entity(knowledge_service: KnowledgeService):
assert await knowledge_service.file_service.exists(file_path)
# Delete entity
success = await knowledge_service.delete_entity(entity.id)
success = await knowledge_service.delete_entity(entity.path_id)
assert success
# Verify file was deleted
@@ -136,7 +137,7 @@ async def test_delete_entity(knowledge_service: KnowledgeService):
# Verify entity was deleted from DB
with pytest.raises(EntityNotFoundError):
await knowledge_service.entity_service.get_entity(entity.id)
await knowledge_service.entity_service.get_by_path_id(entity.path_id)
@pytest.mark.asyncio
@@ -151,7 +152,7 @@ async def test_delete_multiple_entities(knowledge_service: KnowledgeService):
entities.append(entity)
# Delete entities
success = await knowledge_service.delete_entities([e.id for e in entities])
success = await knowledge_service.delete_entities([e.path_id for e in entities])
assert success
# Verify files were deleted
@@ -159,7 +160,7 @@ async def test_delete_multiple_entities(knowledge_service: KnowledgeService):
file_path = knowledge_service.get_entity_path(entity)
assert not await knowledge_service.file_service.exists(file_path)
with pytest.raises(EntityNotFoundError):
await knowledge_service.entity_service.get_entity(entity.id)
await knowledge_service.entity_service.get_by_path_id(entity.path_id)
@pytest.mark.asyncio
@@ -187,14 +188,14 @@ async def test_entity_not_found_error(knowledge_service: KnowledgeService):
@pytest.mark.asyncio
async def test_cleanup_on_creation_failure(knowledge_service: KnowledgeService, monkeypatch):
"""Should clean up DB entity if file write fails."""
entity_ids: List[int] = []
entity_ids: List[str] = []
# Capture created entity ID
original_create = knowledge_service.entity_service.create_entity
async def mock_create_entity(*args, **kwargs):
entity = await original_create(*args, **kwargs)
entity_ids.append(entity.id)
entity_ids.append(entity.path_id)
return entity
# Force file write to fail
@@ -213,7 +214,7 @@ async def test_cleanup_on_creation_failure(knowledge_service: KnowledgeService,
# Verify entity was cleaned up
assert len(entity_ids) == 1
with pytest.raises(EntityNotFoundError):
await knowledge_service.entity_service.get_entity(entity_ids[0])
await knowledge_service.entity_service.get_by_path_id(entity_ids[0])
@pytest.mark.asyncio
@@ -225,13 +226,8 @@ async def test_skip_failed_batch_operations(knowledge_service: KnowledgeService)
EntitySchema(name="test-2", entity_type="test", description="Test entity 2"),
]
created = await knowledge_service.create_entities(entities)
assert len(created) == 2 # Middle one should fail but not stop processing
# Verify first and last were created
names = [e.name for e in created]
assert "test-1" in names
assert "test-2" in names
with pytest.raises(IntegrityError):
await knowledge_service.create_entities(entities)
@pytest.mark.asyncio
@@ -248,8 +244,8 @@ async def test_update_relations_in_files(knowledge_service: KnowledgeService):
# Create relation
relations = [
RelationSchema(
from_id=entity1.id,
to_id=entity2.id,
from_id=entity1.path_id,
to_id=entity2.path_id,
relation_type="connects_to",
context="Test connection",
)
+1 -1
View File
@@ -27,7 +27,7 @@ async def observation_service(observation_repository: ObservationRepository) ->
async def test_entity(session_maker: async_sessionmaker[AsyncSession]) -> Entity:
"""Create a test entity."""
async with session_maker() as session:
entity = Entity(entity_type="test", name="test", description="Test entity")
entity = Entity(entity_type="test", name="test", description="Test entity", path_id="test/test")
session.add(entity)
await session.commit()
return entity
+20 -54
View File
@@ -4,9 +4,8 @@ import pytest
import pytest_asyncio
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from basic_memory.models import Entity as EntityModel
from basic_memory.models import Entity, Relation
from basic_memory.repository.relation_repository import RelationRepository
from basic_memory.schemas import Entity, Relation
from basic_memory.services.relation_service import RelationService
@@ -27,17 +26,19 @@ async def relation_service(relation_repository: RelationRepository) -> RelationS
@pytest_asyncio.fixture
async def test_entities(
session_maker: async_sessionmaker[AsyncSession],
) -> tuple[EntityModel, EntityModel]:
) -> tuple[Entity, Entity]:
"""Create two test entities."""
async with session_maker() as session:
entity1 = EntityModel(
entity1 = Entity(
name="test_entity_1",
entity_type="test",
path_id="test/test_entity_1",
description="Test entity 1",
)
entity2 = EntityModel(
entity2 = Entity(
name="test_entity_2",
entity_type="test",
path_id="test/test_entity_2",
description="Test entity 2",
)
session.add_all([entity1, entity2])
@@ -47,7 +48,7 @@ async def test_entities(
@pytest.mark.asyncio
async def test_create_relation(
relation_service: RelationService, test_entities: tuple[EntityModel, EntityModel]
relation_service: RelationService, test_entities: tuple[Entity, Entity]
):
"""Test creating a basic relation between two entities."""
entity1, entity2 = test_entities
@@ -63,7 +64,7 @@ async def test_create_relation(
@pytest.mark.asyncio
async def test_create_relations(
relation_service: RelationService, test_entities: tuple[EntityModel, EntityModel]
relation_service: RelationService, test_entities: tuple[Entity, Entity]
):
"""Test creating a basic relation between two entities."""
entity1, entity2 = test_entities
@@ -88,7 +89,7 @@ async def test_create_relations(
@pytest.mark.asyncio
async def test_create_relation_with_context(
relation_service: RelationService, test_entities: tuple[EntityModel, EntityModel]
relation_service: RelationService, test_entities: tuple[Entity, Entity]
):
"""Test creating a relation with context information."""
entity1, entity2 = test_entities
@@ -104,7 +105,7 @@ async def test_create_relation_with_context(
@pytest.mark.asyncio
async def test_delete_relation(
relation_service: RelationService, test_entities: tuple[EntityModel, EntityModel]
relation_service: RelationService, test_entities: tuple[Entity, Entity]
):
"""Test deleting a relation between entities."""
entity1, entity2 = test_entities
@@ -113,73 +114,38 @@ async def test_delete_relation(
relation_data = Relation(from_id=entity1.id, to_id=entity2.id, relation_type="test_relation")
await relation_service.create_relation(relation_data)
# Create Entity schema instances for delete_relation call
from_entity = Entity(
id=entity1.id,
name=entity1.name,
entity_type=entity1.entity_type,
description=entity1.description,
observations=[],
)
to_entity = Entity(
id=entity2.id,
name=entity2.name,
entity_type=entity2.entity_type,
description=entity2.description,
observations=[],
)
# Delete the relation
result = await relation_service.delete_relation(from_entity, to_entity, "test_relation")
result = await relation_service.delete_relation(entity1, entity2, "test_relation")
assert result is True
@pytest.mark.asyncio
async def test_delete_nonexistent_relation(
relation_service: RelationService, test_entities: tuple[EntityModel, EntityModel]
relation_service: RelationService, test_entities: tuple[Entity, Entity]
):
"""Test trying to delete a relation that doesn't exist."""
entity1, entity2 = test_entities
from_entity = Entity(
id=entity1.id,
name=entity1.name,
entity_type=entity1.entity_type,
description=entity1.description,
observations=[],
)
to_entity = Entity(
id=entity2.id,
name=entity2.name,
entity_type=entity2.entity_type,
description=entity2.description,
observations=[],
)
result = await relation_service.delete_relation(from_entity, to_entity, "nonexistent_relation")
result = await relation_service.delete_relation(entity1, entity2, "nonexistent_relation")
assert result is False
@pytest.mark.asyncio
async def test_delete_relations_by_criteria(
relation_service: RelationService, test_entities: tuple[EntityModel, EntityModel]
relation_service: RelationService, test_entities: tuple[Entity, Entity]
):
"""Test deleting relations by criteria."""
entity1, entity2 = test_entities
# Create test relations
await relation_service.create_relation(
Relation(from_id=entity1.id, to_id=entity2.id, relation_type="relation1")
)
await relation_service.create_relation(
Relation(from_id=entity1.id, to_id=entity2.id, relation_type="relation2")
)
relation1 = Relation(from_id=entity1.id, to_id=entity2.id, relation_type="relation1")
await relation_service.create_relation(relation1)
relation2 = Relation(from_id=entity1.id, to_id=entity2.id, relation_type="relation2")
await relation_service.create_relation(relation2)
# Delete relations matching criteria
result = await relation_service.delete_relations(
[{"from_id": entity1.id, "to_id": entity2.id, "relation_type": "relation1"}]
)
result = await relation_service.delete_relations([relation1, relation2])
assert result is True
assert result == 2