@@ -1565,3 +1565,79 @@ async def mock_compute_checksum(path):
15651565 failure_info = sync_service ._file_failures ["checksum_fail.md" ]
15661566 assert failure_info .count == 1
15671567 assert failure_info .last_checksum == "" # Empty when checksum fails
1568+
1569+
1570+ @pytest .mark .asyncio
1571+ async def test_sync_fatal_error_terminates_sync_immediately (
1572+ sync_service : SyncService , project_config : ProjectConfig , entity_service : EntityService
1573+ ):
1574+ """Test that SyncFatalError terminates sync immediately without circuit breaker retry.
1575+
1576+ This tests the fix for issue #188 where project deletion during sync should
1577+ terminate immediately rather than retrying each file 3 times.
1578+ """
1579+ from unittest .mock import patch
1580+ from basic_memory .services .exceptions import SyncFatalError
1581+
1582+ project_dir = project_config .home
1583+
1584+ # Create multiple test files
1585+ await create_test_file (
1586+ project_dir / "file1.md" ,
1587+ dedent (
1588+ """
1589+ ---
1590+ type: knowledge
1591+ ---
1592+ # File 1
1593+ Content 1
1594+ """
1595+ ),
1596+ )
1597+ await create_test_file (
1598+ project_dir / "file2.md" ,
1599+ dedent (
1600+ """
1601+ ---
1602+ type: knowledge
1603+ ---
1604+ # File 2
1605+ Content 2
1606+ """
1607+ ),
1608+ )
1609+ await create_test_file (
1610+ project_dir / "file3.md" ,
1611+ dedent (
1612+ """
1613+ ---
1614+ type: knowledge
1615+ ---
1616+ # File 3
1617+ Content 3
1618+ """
1619+ ),
1620+ )
1621+
1622+ # Mock entity_service.create_entity_from_markdown to raise SyncFatalError on first file
1623+ # This simulates project being deleted during sync
1624+ async def mock_create_entity_from_markdown (* args , ** kwargs ):
1625+ raise SyncFatalError (
1626+ "Cannot sync file 'file1.md': project_id=99999 does not exist in database. "
1627+ "The project may have been deleted. This sync will be terminated."
1628+ )
1629+
1630+ with patch .object (
1631+ entity_service , "create_entity_from_markdown" , side_effect = mock_create_entity_from_markdown
1632+ ):
1633+ # Sync should raise SyncFatalError and terminate immediately
1634+ with pytest .raises (SyncFatalError , match = "project_id=99999 does not exist" ):
1635+ await sync_service .sync (project_dir )
1636+
1637+ # Verify that circuit breaker did NOT record this as a file-level failure
1638+ # (SyncFatalError should bypass circuit breaker and re-raise immediately)
1639+ assert "file1.md" not in sync_service ._file_failures
1640+
1641+ # Verify that no other files were attempted (sync terminated on first error)
1642+ # If circuit breaker was used, we'd see file1 in failures
1643+ # If sync continued, we'd see attempts for file2 and file3
0 commit comments