Files
wehub-resource-sync 5296d0e97c
CI / Ban suppressions and legacy annotations (push) Has been cancelled
CI / pytest (push) Has been cancelled
CI / ruff-check (push) Has been cancelled
CI / ruff-format (push) Has been cancelled
CI / ty (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:44 +08:00

938 lines
30 KiB
Python

import asyncio
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
import free_claude_code.messaging.session.persistence as persistence_module
from free_claude_code.config.admin.persistence import PreparedAdminUpdate
from free_claude_code.config.settings import Settings
from free_claude_code.messaging.command_context import StopOutcome
from free_claude_code.messaging.platforms.ports import (
InboundMessageHandler,
MessagingPlatformComponents,
MessagingStartupNotice,
)
from free_claude_code.messaging.session import SessionStore
from free_claude_code.messaging.workflow import MessagingWorkflow
from free_claude_code.providers.runtime import ProviderRuntime
from free_claude_code.runtime.application import ApplicationRuntime
from free_claude_code.runtime.provider_manager import ProviderRuntimeManager
class TrackingRuntime(ProviderRuntime):
def __init__(self, settings: Settings) -> None:
super().__init__(settings)
self.cleanup_calls = 0
async def cleanup(self) -> None:
self.cleanup_calls += 1
await super().cleanup()
class TrackingFactory:
def __init__(self) -> None:
self.runtimes: list[TrackingRuntime] = []
self.fail = False
self.events: list[str] = []
def __call__(self, settings: Settings) -> ProviderRuntime:
self.events.append(f"construct:{settings.model}")
if self.fail:
raise RuntimeError("candidate failed")
runtime = TrackingRuntime(settings)
self.runtimes.append(runtime)
return runtime
class TrackingTranscriber:
def __init__(self, events: list[str]) -> None:
self.events = events
self.close_calls = 0
async def transcribe(self, file_path: Path) -> str:
assert isinstance(file_path, Path)
return "transcribed"
async def close(self) -> None:
self.close_calls += 1
self.events.append("transcriber.close")
class FailingTranscriber(TrackingTranscriber):
async def close(self) -> None:
await super().close()
raise RuntimeError("transcriber close failed")
class CancelledTranscriber(TrackingTranscriber):
async def close(self) -> None:
await super().close()
raise asyncio.CancelledError
class CancellingOnceTranscriber(TrackingTranscriber):
async def close(self) -> None:
await super().close()
if self.close_calls == 1:
raise asyncio.CancelledError
class TrackingMessagingRuntime:
name = "tracking"
def __init__(
self,
events: list[str],
*,
fail_quiesce_once: bool = False,
fail_close_once: bool = False,
) -> None:
self.events = events
self.fail_quiesce_once = fail_quiesce_once
self.fail_close_once = fail_close_once
async def start(self) -> None:
self.events.append("messaging.start")
async def quiesce(self) -> None:
self.events.append("messaging.quiesce")
if self.fail_quiesce_once:
self.fail_quiesce_once = False
raise RuntimeError("quiesce failed")
async def close(self) -> None:
self.events.append("messaging.close")
if self.fail_close_once:
self.fail_close_once = False
raise RuntimeError("close failed")
def on_message(self, handler: InboundMessageHandler) -> None:
assert callable(handler)
@property
def is_connected(self) -> bool:
return True
class PersistentlyFailingMessagingRuntime(TrackingMessagingRuntime):
def __init__(self, events: list[str]) -> None:
super().__init__(events)
self.fail_quiesce = True
async def quiesce(self) -> None:
self.events.append("messaging.quiesce")
if self.fail_quiesce:
raise RuntimeError("quiesce failed")
def _settings(model: str, *, port: int = 8082) -> Settings:
return Settings().model_copy(update={"model": model, "port": port})
def _prepared(
settings: Settings,
tmp_path,
*,
pending_fields: tuple[str, ...] = (),
) -> PreparedAdminUpdate:
return PreparedAdminUpdate(
target_values={"MODEL": settings.model},
settings=settings,
errors=(),
pending_fields=pending_fields,
path=tmp_path / ".env",
)
def _applied_response(pending_fields: tuple[str, ...] = ()) -> dict[str, object]:
return {
"applied": True,
"valid": True,
"errors": [],
"env_preview": "MODEL=updated\n",
"path": ".env",
"pending_fields": list(pending_fields),
}
@pytest.mark.asyncio
async def test_stop_all_maps_messaging_outcome_to_application_count() -> None:
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
runtime = ApplicationRuntime(manager, transcriber=None)
workflow = MagicMock()
workflow.stop_all_tasks = AsyncMock(
return_value=StopOutcome(
cancelled_count=3,
status_feedback_scopes=frozenset(),
fallback_required=False,
)
)
runtime._messaging_workflow = workflow
result = await runtime.stop_all()
assert result is not None
assert result.cancelled_count == 3
workflow.stop_all_tasks.assert_awaited_once()
await manager.close()
@pytest.mark.asyncio
async def test_provider_apply_constructs_before_commit_then_publishes(tmp_path) -> None:
factory = TrackingFactory()
manager = ProviderRuntimeManager(
_settings("nvidia_nim/old"),
runtime_factory=factory,
)
runtime = ApplicationRuntime(manager, transcriber=None)
prepared = _prepared(_settings("nvidia_nim/new"), tmp_path)
factory.events.clear()
def commit(_prepared_update: PreparedAdminUpdate) -> dict[str, object]:
factory.events.append("commit")
assert manager.current_generation_id == 1
return _applied_response()
with (
patch(
"free_claude_code.runtime.application.prepare_admin_update",
return_value=prepared,
),
patch(
"free_claude_code.runtime.application.commit_prepared_admin_update",
side_effect=commit,
),
):
result = await runtime.apply_admin_config({"MODEL": "nvidia_nim/new"})
assert factory.events == ["construct:nvidia_nim/new", "commit"]
assert manager.current_generation_id == 2
assert manager.current_settings().model == "nvidia_nim/new"
assert result["restart"] == {
"required": False,
"automatic": False,
"admin_url": None,
"fields": [],
}
await manager.close()
@pytest.mark.asyncio
async def test_candidate_failure_never_commits_and_preserves_current(tmp_path) -> None:
factory = TrackingFactory()
manager = ProviderRuntimeManager(
_settings("nvidia_nim/old"),
runtime_factory=factory,
)
runtime = ApplicationRuntime(manager, transcriber=None)
prepared = _prepared(_settings("nvidia_nim/new"), tmp_path)
factory.fail = True
with (
patch(
"free_claude_code.runtime.application.prepare_admin_update",
return_value=prepared,
),
patch(
"free_claude_code.runtime.application.commit_prepared_admin_update"
) as commit,
pytest.raises(RuntimeError, match="candidate failed"),
):
await runtime.apply_admin_config({"MODEL": "nvidia_nim/new"})
commit.assert_not_called()
assert manager.current_generation_id == 1
assert manager.current_settings().model == "nvidia_nim/old"
await manager.close()
@pytest.mark.asyncio
async def test_persistence_failure_closes_candidate_and_preserves_current(
tmp_path,
) -> None:
factory = TrackingFactory()
manager = ProviderRuntimeManager(
_settings("nvidia_nim/old"),
runtime_factory=factory,
)
runtime = ApplicationRuntime(manager, transcriber=None)
prepared = _prepared(_settings("nvidia_nim/new"), tmp_path)
with (
patch(
"free_claude_code.runtime.application.prepare_admin_update",
return_value=prepared,
),
patch(
"free_claude_code.runtime.application.commit_prepared_admin_update",
side_effect=OSError("disk full"),
),
pytest.raises(OSError, match="disk full"),
):
await runtime.apply_admin_config({"MODEL": "nvidia_nim/new"})
assert manager.current_generation_id == 1
assert factory.runtimes[0].cleanup_calls == 0
assert factory.runtimes[1].cleanup_calls == 1
await manager.close()
@pytest.mark.asyncio
async def test_restart_required_apply_commits_without_hot_publication(tmp_path) -> None:
factory = TrackingFactory()
manager = ProviderRuntimeManager(
_settings("nvidia_nim/old"),
runtime_factory=factory,
)
restart = AsyncMock()
runtime = ApplicationRuntime(
manager,
transcriber=None,
restart_callback=restart,
)
prepared = _prepared(
_settings("nvidia_nim/old", port=9090),
tmp_path,
pending_fields=("PORT",),
)
with (
patch(
"free_claude_code.runtime.application.prepare_admin_update",
return_value=prepared,
),
patch(
"free_claude_code.runtime.application.commit_prepared_admin_update",
return_value=_applied_response(("PORT",)),
) as commit,
):
result = await runtime.apply_admin_config({"PORT": "9090"})
commit.assert_called_once_with(prepared)
assert manager.current_generation_id == 1
assert len(factory.runtimes) == 1
assert result["restart"] == {
"required": True,
"automatic": True,
"admin_url": "http://127.0.0.1:9090/admin",
"fields": ["PORT"],
}
restart.assert_not_awaited()
await runtime.request_restart()
restart.assert_awaited_once()
await manager.close()
@pytest.mark.asyncio
async def test_close_drains_messaging_before_transcriber_and_is_idempotent() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
transcriber = TrackingTranscriber(events)
runtime = ApplicationRuntime(manager, transcriber=transcriber)
runtime._messaging_runtime = TrackingMessagingRuntime(events)
workflow = MagicMock()
workflow.close = AsyncMock(side_effect=lambda: events.append("workflow.close"))
runtime._messaging_workflow = workflow
runtime._cli_manager = MagicMock()
assert runtime.is_closed is False
assert await runtime.close() is True
assert await runtime.close() is True
assert events == [
"messaging.quiesce",
"workflow.close",
"messaging.close",
"transcriber.close",
]
assert transcriber.close_calls == 1
assert runtime._transcriber is None
assert runtime._messaging_runtime is None
assert runtime._messaging_workflow is None
assert runtime.is_closed is True
@pytest.mark.asyncio
async def test_close_retains_transcriber_ownership_when_close_fails() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
transcriber = FailingTranscriber(events)
runtime = ApplicationRuntime(manager, transcriber=transcriber)
runtime._messaging_runtime = TrackingMessagingRuntime(events)
assert await runtime.close() is False
assert events == [
"messaging.quiesce",
"messaging.close",
"transcriber.close",
]
assert transcriber.close_calls == 1
assert runtime._transcriber is transcriber
assert runtime._closed is False
await manager.close()
@pytest.mark.asyncio
async def test_close_retries_runtime_before_closing_later_resources() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
transcriber = TrackingTranscriber(events)
runtime = ApplicationRuntime(manager, transcriber=transcriber)
messaging = TrackingMessagingRuntime(events, fail_close_once=True)
runtime._messaging_runtime = messaging
assert await runtime.close() is False
assert events == ["messaging.quiesce", "messaging.close"]
assert runtime._messaging_runtime is messaging
assert runtime._transcriber is transcriber
assert runtime._closed is False
assert await runtime.close() is True
assert events == [
"messaging.quiesce",
"messaging.close",
"messaging.quiesce",
"messaging.close",
"transcriber.close",
]
assert runtime._messaging_runtime is None
assert runtime._transcriber is None
assert runtime._closed is True
@pytest.mark.asyncio
async def test_close_retries_workflow_close_before_closing_delivery() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
runtime = ApplicationRuntime(manager, transcriber=None)
messaging = TrackingMessagingRuntime(events)
workflow = MagicMock()
close_calls = 0
async def close_workflow() -> None:
nonlocal close_calls
close_calls += 1
if close_calls == 1:
raise RuntimeError("drain failed")
events.append("workflow.close")
workflow.close = AsyncMock(side_effect=close_workflow)
runtime._messaging_runtime = messaging
runtime._messaging_workflow = workflow
assert await runtime.close() is False
assert events == ["messaging.quiesce"]
assert runtime._messaging_runtime is messaging
assert runtime._messaging_workflow is workflow
assert runtime._closed is False
assert await runtime.close() is True
assert events == [
"messaging.quiesce",
"messaging.quiesce",
"workflow.close",
"messaging.close",
]
assert runtime._closed is True
@pytest.mark.asyncio
async def test_close_does_not_drain_workflow_until_ingress_is_quiescent() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
runtime = ApplicationRuntime(manager, transcriber=None)
messaging = TrackingMessagingRuntime(events, fail_quiesce_once=True)
workflow = MagicMock()
workflow.close = AsyncMock(side_effect=lambda: events.append("workflow.close"))
runtime._messaging_runtime = messaging
runtime._messaging_workflow = workflow
assert await runtime.close() is False
workflow.close.assert_not_awaited()
assert runtime._messaging_runtime is messaging
assert runtime._messaging_workflow is workflow
assert await runtime.close() is True
assert events == [
"messaging.quiesce",
"messaging.quiesce",
"workflow.close",
"messaging.close",
]
assert runtime._closed is True
@pytest.mark.asyncio
async def test_close_retries_failed_persistence_before_closing_delivery() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
runtime = ApplicationRuntime(manager, transcriber=None)
messaging = TrackingMessagingRuntime(events)
workflow = MagicMock()
close_calls = 0
async def close_workflow() -> None:
nonlocal close_calls
close_calls += 1
if close_calls == 1:
raise RuntimeError("flush failed")
events.append("workflow.close")
workflow.close = AsyncMock(side_effect=close_workflow)
runtime._messaging_runtime = messaging
runtime._messaging_workflow = workflow
await runtime.close()
assert runtime._messaging_workflow is workflow
assert runtime._messaging_runtime is messaging
assert "messaging.close" not in events
await runtime.close()
assert events == [
"messaging.quiesce",
"messaging.quiesce",
"workflow.close",
"messaging.close",
]
assert runtime._closed is True
@pytest.mark.asyncio
async def test_close_retries_real_workflow_persistence_without_losing_latest_state(
tmp_path: Path,
) -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
runtime = ApplicationRuntime(manager, transcriber=None)
messaging = TrackingMessagingRuntime(events)
cli_manager = MagicMock()
cli_manager.stop_all = AsyncMock()
outbound = MagicMock()
store_path = tmp_path / "sessions.json"
real_replace = persistence_module.os.replace
replace_calls = 0
def fail_first_replace(source: str, target: str) -> None:
nonlocal replace_calls
replace_calls += 1
if replace_calls == 1:
raise OSError("replace failed once")
real_replace(source, target)
with patch.object(persistence_module.threading, "Timer"):
store = SessionStore(storage_path=str(store_path))
store.record_message_id(
"telegram",
"chat_1",
"old",
"out",
"status",
)
store.flush_pending_save()
store.record_message_id(
"telegram",
"chat_1",
"latest",
"out",
"status",
)
workflow = MessagingWorkflow(outbound, cli_manager, store)
runtime._messaging_runtime = messaging
runtime._messaging_workflow = workflow
runtime._cli_manager = cli_manager
with patch.object(
persistence_module.os,
"replace",
side_effect=fail_first_replace,
):
assert await runtime.close() is False
assert runtime._messaging_runtime is messaging
assert runtime._messaging_workflow is workflow
assert runtime._cli_manager is cli_manager
assert runtime.is_closed is False
assert store.dirty is True
assert store.get_tracked_message_ids_for_chat("telegram", "chat_1") == [
"old",
"latest",
]
assert SessionStore(
storage_path=str(store_path)
).get_tracked_message_ids_for_chat("telegram", "chat_1") == ["old"]
assert await runtime.close() is True
assert runtime._messaging_runtime is None
assert runtime._messaging_workflow is None
assert runtime._cli_manager is None
assert runtime.is_closed is True
assert store.dirty is False
assert SessionStore(
storage_path=str(store_path)
).get_tracked_message_ids_for_chat("telegram", "chat_1") == [
"old",
"latest",
]
assert events == [
"messaging.quiesce",
"messaging.quiesce",
"messaging.close",
]
@pytest.mark.asyncio
async def test_cancelled_transcriber_close_retains_ownership() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
transcriber = CancelledTranscriber(events)
runtime = ApplicationRuntime(manager, transcriber=transcriber)
with pytest.raises(asyncio.CancelledError):
await runtime._cleanup_transcriber()
assert transcriber.close_calls == 1
assert runtime._transcriber is transcriber
await manager.close()
@pytest.mark.asyncio
async def test_cancelled_application_close_remains_retryable() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
transcriber = CancellingOnceTranscriber(events)
runtime = ApplicationRuntime(manager, transcriber=transcriber)
with pytest.raises(asyncio.CancelledError):
await runtime.close()
assert runtime._closed is False
assert runtime._transcriber is transcriber
await runtime.close()
assert transcriber.close_calls == 2
assert runtime._transcriber is None
assert runtime._closed is True
@pytest.mark.asyncio
async def test_startup_failure_closes_owned_transcriber() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
transcriber = TrackingTranscriber(events)
runtime = ApplicationRuntime(manager, transcriber=transcriber)
with (
patch.object(
manager,
"validate_configured_models",
AsyncMock(side_effect=RuntimeError("startup failed")),
),
pytest.raises(RuntimeError, match="startup failed"),
):
await runtime.start()
assert transcriber.close_calls == 1
@pytest.mark.asyncio
async def test_startup_cancellation_cleans_partial_messaging_and_reraises() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
transcriber = TrackingTranscriber(events)
runtime = ApplicationRuntime(manager, transcriber=transcriber)
messaging = TrackingMessagingRuntime(events)
entered = asyncio.Event()
async def start_messaging() -> None:
runtime._messaging_runtime = messaging
entered.set()
await asyncio.Event().wait()
with patch.object(
runtime,
"_start_messaging_if_configured",
side_effect=start_messaging,
):
start_task = asyncio.create_task(runtime.start())
await entered.wait()
start_task.cancel()
with pytest.raises(asyncio.CancelledError):
await start_task
assert events == [
"messaging.quiesce",
"messaging.close",
"transcriber.close",
]
assert runtime._closed is True
assert runtime._messaging_runtime is None
assert runtime._transcriber is None
@pytest.mark.asyncio
async def test_public_start_retries_transient_partial_messaging_cleanup() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
runtime = ApplicationRuntime(manager, transcriber=None)
messaging = TrackingMessagingRuntime(events, fail_quiesce_once=True)
workflow = MagicMock()
workflow.close = AsyncMock(side_effect=lambda: events.append("workflow.close"))
cli_manager = MagicMock()
components = MessagingPlatformComponents(
name="tracking",
runtime=messaging,
outbound=MagicMock(),
)
startup_failure = RuntimeError("partial messaging startup failed")
async def fail_after_publication(
published: MessagingPlatformComponents,
) -> None:
assert published is components
runtime._messaging_runtime = messaging
runtime._messaging_workflow = workflow
runtime._cli_manager = cli_manager
raise startup_failure
with (
patch.object(
runtime,
"_validate_configured_models_best_effort",
AsyncMock(),
),
patch.object(manager, "start_model_list_refresh"),
patch(
"free_claude_code.runtime.application.messaging_platform_factory.create_messaging_components",
return_value=components,
),
patch.object(
runtime,
"_start_messaging_workflow",
side_effect=fail_after_publication,
),
pytest.raises(RuntimeError, match="cleanup incomplete") as raised,
):
await runtime.start()
assert raised.value.__cause__ is startup_failure
assert events == [
"messaging.quiesce",
"messaging.quiesce",
"workflow.close",
"messaging.close",
]
workflow.close.assert_awaited_once()
assert runtime._messaging_runtime is None
assert runtime._messaging_workflow is None
assert runtime._cli_manager is None
assert runtime.is_closed is True
@pytest.mark.asyncio
async def test_public_start_retains_persistently_unclean_partial_messaging_graph() -> (
None
):
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
transcriber = TrackingTranscriber(events)
runtime = ApplicationRuntime(manager, transcriber=transcriber)
messaging = PersistentlyFailingMessagingRuntime(events)
workflow = MagicMock()
workflow.close = AsyncMock(side_effect=lambda: events.append("workflow.close"))
cli_manager = MagicMock()
components = MessagingPlatformComponents(
name="tracking",
runtime=messaging,
outbound=MagicMock(),
)
startup_failure = RuntimeError("partial messaging startup failed")
async def fail_after_publication(
published: MessagingPlatformComponents,
) -> None:
assert published is components
runtime._messaging_runtime = messaging
runtime._messaging_workflow = workflow
runtime._cli_manager = cli_manager
raise startup_failure
with (
patch.object(
runtime,
"_validate_configured_models_best_effort",
AsyncMock(),
),
patch.object(manager, "start_model_list_refresh"),
patch(
"free_claude_code.runtime.application.messaging_platform_factory.create_messaging_components",
return_value=components,
),
patch.object(
runtime,
"_start_messaging_workflow",
side_effect=fail_after_publication,
),
pytest.raises(RuntimeError, match="cleanup incomplete") as raised,
):
await runtime.start()
assert raised.value.__cause__ is startup_failure
assert events == ["messaging.quiesce", "messaging.quiesce"]
workflow.close.assert_not_awaited()
assert transcriber.close_calls == 0
assert runtime._messaging_runtime is messaging
assert runtime._messaging_workflow is workflow
assert runtime._cli_manager is cli_manager
assert runtime._transcriber is transcriber
assert runtime._provider_manager_closed is False
assert runtime.is_closed is False
messaging.fail_quiesce = False
assert await runtime.close() is True
assert events == [
"messaging.quiesce",
"messaging.quiesce",
"messaging.quiesce",
"workflow.close",
"messaging.close",
"transcriber.close",
]
assert runtime.is_closed is True
@pytest.mark.asyncio
async def test_messaging_start_failure_is_nonfatal_after_complete_cleanup() -> None:
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
runtime = ApplicationRuntime(manager, transcriber=None)
with (
patch(
"free_claude_code.runtime.application.messaging_platform_factory.create_messaging_components",
side_effect=RuntimeError("messaging unavailable"),
),
patch.object(runtime, "_cleanup_messaging", AsyncMock(return_value=True)),
):
await runtime._start_messaging_if_configured()
await manager.close()
@pytest.mark.asyncio
async def test_messaging_start_failure_fails_closed_when_cleanup_is_incomplete() -> (
None
):
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
runtime = ApplicationRuntime(manager, transcriber=None)
with (
patch(
"free_claude_code.runtime.application.messaging_platform_factory.create_messaging_components",
side_effect=RuntimeError("messaging unavailable"),
),
patch.object(runtime, "_cleanup_messaging", AsyncMock(return_value=False)),
pytest.raises(RuntimeError, match="cleanup incomplete") as exc_info,
):
await runtime._start_messaging_if_configured()
assert isinstance(exc_info.value.__cause__, RuntimeError)
await manager.close()
@pytest.mark.asyncio
async def test_composition_records_runtime_before_workspace_setup() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
runtime = ApplicationRuntime(manager, transcriber=None)
messaging = TrackingMessagingRuntime(events)
components = MessagingPlatformComponents(
name="tracking",
runtime=messaging,
outbound=MagicMock(),
)
with (
patch(
"free_claude_code.runtime.application.os.makedirs",
side_effect=OSError("workspace failed"),
),
pytest.raises(OSError, match="workspace failed"),
):
await runtime._start_messaging_workflow(components)
assert runtime._messaging_runtime is messaging
await runtime.close()
assert events == ["messaging.quiesce", "messaging.close"]
assert runtime._closed is True
@pytest.mark.asyncio
async def test_composition_publishes_startup_notice_after_runtime_and_repair() -> None:
events: list[str] = []
manager = ProviderRuntimeManager(_settings("nvidia_nim/model"))
runtime = ApplicationRuntime(manager, transcriber=None)
messaging = TrackingMessagingRuntime(events)
notice = MessagingStartupNotice(
chat_id="chat",
transport_label="test transport",
)
components = MessagingPlatformComponents(
name="tracking",
runtime=messaging,
outbound=MagicMock(),
startup_notice=notice,
)
workflow = MagicMock()
workflow.handle_message = AsyncMock()
workflow.restore.side_effect = lambda: events.append("workflow.restore")
workflow.repair_restored_statuses = AsyncMock(
side_effect=lambda: events.append("workflow.repair")
)
workflow.publish_startup_notice = AsyncMock(
side_effect=lambda published: events.append("workflow.notice")
)
workflow.close = AsyncMock()
cli_manager = MagicMock()
with (
patch(
"free_claude_code.runtime.application.cli_managed.ManagedClaudeSessionManager",
return_value=cli_manager,
) as manager_constructor,
patch("free_claude_code.runtime.application.messaging_session.SessionStore"),
patch(
"free_claude_code.runtime.application.messaging_workflow_module.MessagingWorkflow",
return_value=workflow,
),
):
await runtime._start_messaging_workflow(components)
assert events == [
"workflow.restore",
"messaging.start",
"workflow.repair",
"workflow.notice",
]
workflow.publish_startup_notice.assert_awaited_once_with(notice)
assert manager_constructor.call_args.kwargs["proxy_root_url"] == (
"http://127.0.0.1:8082"
)
assert "api_url" not in manager_constructor.call_args.kwargs
assert "plans_directory" not in manager_constructor.call_args.kwargs
assert await runtime.close() is True