@@ -869,6 +869,109 @@ async def test_edit_entity_with_observations_and_relations(
869869 assert new_rel .relation_type == "relates to"
870870
871871
872+ @pytest .mark .asyncio
873+ async def test_create_entity_from_markdown_race_condition_handling (
874+ entity_service : EntityService , file_service : FileService
875+ ):
876+ """Test that create_entity_from_markdown handles race condition with IntegrityError (lines 304-311)."""
877+ from unittest .mock import patch , AsyncMock
878+ from sqlalchemy .exc import IntegrityError
879+
880+ file_path = Path ("test/race-condition.md" )
881+
882+ # Create a mock EntityMarkdown object
883+ from basic_memory .markdown .schemas import EntityFrontmatter , EntityMarkdown as RealEntityMarkdown
884+ from datetime import datetime , timezone
885+
886+ frontmatter = EntityFrontmatter (metadata = {"title" : "Race Condition Test" , "type" : "test" })
887+ markdown = RealEntityMarkdown (
888+ frontmatter = frontmatter ,
889+ observations = [],
890+ relations = [],
891+ created = datetime .now (timezone .utc ),
892+ modified = datetime .now (timezone .utc )
893+ )
894+
895+ # Mock the repository.add to raise IntegrityError on first call, then succeed on second
896+ original_add = entity_service .repository .add
897+ original_update = entity_service .update_entity_and_observations
898+
899+ call_count = 0
900+
901+ async def mock_add (* args , ** kwargs ):
902+ nonlocal call_count
903+ call_count += 1
904+ if call_count == 1 :
905+ # Simulate race condition - another process created the entity
906+ raise IntegrityError ("UNIQUE constraint failed: entity.file_path" , None , None )
907+ else :
908+ return await original_add (* args , ** kwargs )
909+
910+ # Mock update method to return a dummy entity
911+ async def mock_update (* args , ** kwargs ):
912+ from basic_memory .models import Entity
913+ from datetime import datetime , timezone
914+
915+ return Entity (
916+ id = 1 ,
917+ title = "Race Condition Test" ,
918+ entity_type = "test" ,
919+ file_path = str (file_path ),
920+ permalink = "test/race-condition-test" ,
921+ content_type = "text/markdown" ,
922+ created_at = datetime .now (timezone .utc ),
923+ updated_at = datetime .now (timezone .utc ),
924+ )
925+
926+ with patch .object (entity_service .repository , 'add' , side_effect = mock_add ), \
927+ patch .object (entity_service , 'update_entity_and_observations' , side_effect = mock_update ) as mock_update_call :
928+
929+ # Call the method
930+ result = await entity_service .create_entity_from_markdown (file_path , markdown )
931+
932+ # Verify it handled the race condition gracefully
933+ assert result is not None
934+ assert result .title == "Race Condition Test"
935+ assert result .file_path == str (file_path )
936+
937+ # Verify that update_entity_and_observations was called as fallback
938+ mock_update_call .assert_called_once_with (file_path , markdown )
939+
940+
941+ @pytest .mark .asyncio
942+ async def test_create_entity_from_markdown_integrity_error_reraise (
943+ entity_service : EntityService , file_service : FileService
944+ ):
945+ """Test that create_entity_from_markdown re-raises IntegrityError for non-race-condition cases."""
946+ from unittest .mock import patch
947+ from sqlalchemy .exc import IntegrityError
948+
949+ file_path = Path ("test/integrity-error.md" )
950+
951+ # Create a mock EntityMarkdown object
952+ from basic_memory .markdown .schemas import EntityFrontmatter , EntityMarkdown as RealEntityMarkdown
953+ from datetime import datetime , timezone
954+
955+ frontmatter = EntityFrontmatter (metadata = {"title" : "Integrity Error Test" , "type" : "test" })
956+ markdown = RealEntityMarkdown (
957+ frontmatter = frontmatter ,
958+ observations = [],
959+ relations = [],
960+ created = datetime .now (timezone .utc ),
961+ modified = datetime .now (timezone .utc )
962+ )
963+
964+ # Mock the repository.add to raise a different IntegrityError (not file_path/permalink constraint)
965+ async def mock_add (* args , ** kwargs ):
966+ # Simulate a different constraint violation
967+ raise IntegrityError ("UNIQUE constraint failed: entity.some_other_field" , None , None )
968+
969+ with patch .object (entity_service .repository , 'add' , side_effect = mock_add ):
970+ # Should re-raise the IntegrityError since it's not a file_path/permalink constraint
971+ with pytest .raises (IntegrityError , match = "UNIQUE constraint failed: entity.some_other_field" ):
972+ await entity_service .create_entity_from_markdown (file_path , markdown )
973+
974+
872975# Edge case tests for find_replace operation
873976@pytest .mark .asyncio
874977async def test_edit_entity_find_replace_not_found (entity_service : EntityService ):
0 commit comments