Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions backend/tests/unit/test_sync_v2.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""

Check warning on line 1 in backend/tests/unit/test_sync_v2.py

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

backend/tests/unit/test_sync_v2.py is 3486 lines; consider splitting files over 800 lines.
Tests for v2 async sync-local-files endpoints (#5941, #7281).

v2 saves raw files and returns 202 immediately, then runs the full pipeline
Expand Down Expand Up @@ -1273,7 +1273,7 @@
mocked dependencies. Verifies actual call sequences and outcomes."""

@staticmethod
def _load_sync_module():

Check warning on line 1276 in backend/tests/unit/test_sync_v2.py

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Long function

_load_sync_module is 220 lines; consider extracting focused helpers over 150 lines.
"""Load routers/sync.py with all heavy deps stubbed, return (module, stubs)."""
saved_modules = {}
stubs = {}
Expand Down Expand Up @@ -1514,6 +1514,34 @@
finally:
self._cleanup(stubs['saved_modules'])

def test_merged_reprocess_fence_isolated_to_its_conversation(self, fenced_worker_module):
"""One replaced merge target must not supersede its sibling conversations."""
_module, stubs = fenced_worker_module
pipeline = stubs['pipeline']

class ConversationPersistenceFenced(RuntimeError):
pass

pipeline.SyncConversationPersistenceFenced = ConversationPersistenceFenced
pipeline.logger = MagicMock()
pipeline._reprocess_conversation_after_update = MagicMock(
side_effect=[ConversationPersistenceFenced('conversation replaced'), None]
)
response = {'_merged': {'replaced-conversation': 'en', 'current-conversation': 'fr'}}

pipeline._reprocess_merged_conversations('uid', response)

assert response == {}
assert pipeline._reprocess_conversation_after_update.call_args_list == [
unittest.mock.call('uid', 'replaced-conversation', 'en'),
unittest.mock.call('uid', 'current-conversation', 'fr'),
]
pipeline.logger.info.assert_called_once_with(
'event=sync_conversation_reprocess outcome=fenced conversation_id=%s',
'replaced-conversation',
)
pipeline.logger.error.assert_not_called()

@pytest.mark.asyncio
async def test_durable_completion_offloads_epoch_and_terminal_metric(self, fenced_worker_module):
"""Redis-backed fencing and telemetry never run on the async coordinator loop."""
Expand Down Expand Up @@ -2714,7 +2742,7 @@
"""Execute v2 endpoints using FastAPI TestClient with mocked dependencies."""

@staticmethod
def _build_test_app():

Check warning on line 2745 in backend/tests/unit/test_sync_v2.py

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Long function

_build_test_app is 165 lines; consider extracting focused helpers over 150 lines.
"""Build a minimal FastAPI app with the sync router, all deps mocked."""
saved_modules = {}
mock_sync_jobs = MagicMock()
Expand Down
2 changes: 1 addition & 1 deletion backend/utils/sync/pipeline.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Sync local-files pipeline: decode → VAD → fair-use → STT → conversation merge.

Check warning on line 1 in backend/utils/sync/pipeline.py

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

backend/utils/sync/pipeline.py is 2389 lines; consider splitting files over 800 lines.

Extracted from routers/sync.py so the router stays thin and utils never imports routers.
"""
Expand Down Expand Up @@ -990,7 +990,7 @@
self._cond.notify_all()


def process_segment(

Check warning on line 993 in backend/utils/sync/pipeline.py

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Long function

process_segment is 285 lines; consider extracting focused helpers over 150 lines.
path: str,
uid: str,
response: dict,
Expand Down Expand Up @@ -1288,7 +1288,7 @@
try:
_reprocess_conversation_after_update(uid, conversation_id, language)
except SyncConversationPersistenceFenced:
raise
logger.info('event=sync_conversation_reprocess outcome=fenced conversation_id=%s', conversation_id)
Comment on lines 1290 to +1291

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Drop fenced conversations before audio finalization

When private_cloud_sync_enabled is true and a merged conversation is fenced during reprocessing, this catch now lets the pipeline continue while the fenced conversation ID remains in response['updated_memories']; the caller then runs _finalize_sync_audio_files, which derives conversation_ids from updated_memories and writes audio_files/precache/enqueues an artifact for that same lifecycle-fenced conversation. Before this change the re-raised fence stopped before those post-processing side effects, so please remove the fenced conversation from the response (or otherwise pass a skipped set) before continuing to siblings.

Useful? React with 👍 / 👎.

except Exception as e:
logger.error(f'sync: failed to reprocess merged conversation {conversation_id}: {e}')

Expand Down Expand Up @@ -1550,7 +1550,7 @@
return vad_errors, vad_ms


async def _run_full_pipeline_background_async( # pyright: ignore[reportGeneralTypeIssues] — legacy coordinator exceeds Pyright's analyzer complexity ceiling

Check warning on line 1553 in backend/utils/sync/pipeline.py

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Long function

_run_full_pipeline_background_async is 811 lines; consider extracting focused helpers over 150 lines.
job_id: str,
uid: str,
raw_paths: list,
Expand Down
Loading