"""Tests for the task-body consolidation pipeline (gate + full pass). Design: docs/superpowers/specs/2026-05-13-task-as-durable-record-design.md """ from unittest.mock import AsyncMock, patch # ── Gate logic ─────────────────────────────────────────────────────────────── async def test_maybe_consolidate_below_threshold_does_not_fire(): """log_added with only 2 logs since last pass → no consolidation.""" from fabledassistant.services import consolidation with patch.object( consolidation, "_logs_since_last_consolidation", new=AsyncMock(return_value=2), ), patch.object( consolidation, "consolidate_task", new=AsyncMock(), ) as mock_consolidate, patch.object( consolidation, "_auto_consolidate_enabled", new=AsyncMock(return_value=True), ): await consolidation.maybe_consolidate( user_id=1, task_id=42, reason="log_added", ) mock_consolidate.assert_not_awaited() async def test_maybe_consolidate_at_threshold_fires(): """log_added with N logs since last pass → consolidation scheduled.""" from fabledassistant.services import consolidation # asyncio.create_task is the actual scheduler; replace it so the test # doesn't need an event-loop background task to settle. with patch.object( consolidation, "_logs_since_last_consolidation", new=AsyncMock(return_value=3), ), patch.object( consolidation, "_auto_consolidate_enabled", new=AsyncMock(return_value=True), ), patch( "fabledassistant.services.consolidation.asyncio.create_task", ) as mock_create_task: await consolidation.maybe_consolidate( user_id=1, task_id=42, reason="log_added", ) mock_create_task.assert_called_once() async def test_maybe_consolidate_task_closed_always_fires(): """task_closed bypasses the log-count gate.""" from fabledassistant.services import consolidation with patch.object( consolidation, "_logs_since_last_consolidation", new=AsyncMock(return_value=0), ), patch.object( consolidation, "_auto_consolidate_enabled", new=AsyncMock(return_value=True), ), patch( "fabledassistant.services.consolidation.asyncio.create_task", ) as mock_create_task: await consolidation.maybe_consolidate( user_id=1, task_id=42, reason="task_closed", ) mock_create_task.assert_called_once() async def test_maybe_consolidate_setting_off_blocks_both_reasons(): """auto_consolidate_tasks=false → neither trigger fires.""" from fabledassistant.services import consolidation with patch.object( consolidation, "_logs_since_last_consolidation", new=AsyncMock(return_value=5), ), patch.object( consolidation, "_auto_consolidate_enabled", new=AsyncMock(return_value=False), ), patch( "fabledassistant.services.consolidation.asyncio.create_task", ) as mock_create_task: await consolidation.maybe_consolidate( user_id=1, task_id=42, reason="log_added", ) await consolidation.maybe_consolidate( user_id=1, task_id=42, reason="task_closed", ) mock_create_task.assert_not_called() async def test_maybe_consolidate_unknown_reason_is_noop(): """Unknown reasons get logged and skipped — not raised.""" from fabledassistant.services import consolidation with patch.object( consolidation, "_logs_since_last_consolidation", new=AsyncMock(return_value=99), ), patch.object( consolidation, "_auto_consolidate_enabled", new=AsyncMock(return_value=True), ), patch( "fabledassistant.services.consolidation.asyncio.create_task", ) as mock_create_task: await consolidation.maybe_consolidate( user_id=1, task_id=42, reason="some_other_reason", ) mock_create_task.assert_not_called()