v2.14.1 (#756)
Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Gauthier Guinet <43207538+Gguinet@users.noreply.github.com> Co-authored-by: Michel Thomazo <51709227+michelTho@users.noreply.github.com> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Stanislas <stanislas.lange@mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
3f8487f761
commit
702d0f412e
51 changed files with 2446 additions and 502 deletions
|
|
@ -7,7 +7,6 @@ import time
|
|||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from textual.app import Notification
|
||||
|
||||
from tests.conftest import build_test_vibe_app, build_test_vibe_config
|
||||
from tests.update_notifier.adapters.fake_update_cache_repository import (
|
||||
|
|
@ -50,21 +49,6 @@ def build_update_test_app(
|
|||
return _build
|
||||
|
||||
|
||||
async def _wait_for_notification(
|
||||
app: VibeApp, pilot, *, timeout: float = 1.0, interval: float = 0.05
|
||||
) -> Notification:
|
||||
loop = asyncio.get_running_loop()
|
||||
deadline = loop.time() + timeout
|
||||
|
||||
while loop.time() < deadline:
|
||||
notifications = list(app._notifications)
|
||||
if notifications:
|
||||
return notifications[-1]
|
||||
await pilot.pause(interval)
|
||||
|
||||
pytest.fail("Notification not displayed")
|
||||
|
||||
|
||||
async def _assert_no_notifications(
|
||||
app: VibeApp, pilot, *, timeout: float = 1.0, interval: float = 0.05
|
||||
) -> None:
|
||||
|
|
@ -85,25 +69,25 @@ def vibe_config_with_update_checks_enabled() -> VibeConfig:
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_displays_update_notification(
|
||||
async def test_ui_writes_pending_update_to_cache_without_notifying(
|
||||
build_update_test_app: Callable[..., VibeApp],
|
||||
) -> None:
|
||||
notifier = FakeUpdateGateway(update=Update(latest_version="0.2.0"))
|
||||
app = build_update_test_app(update_notifier=notifier)
|
||||
repository = FakeUpdateCacheRepository()
|
||||
app = build_update_test_app(
|
||||
update_notifier=notifier, update_cache_repository=repository
|
||||
)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
notification = await _wait_for_notification(app, pilot, timeout=0.3)
|
||||
await _assert_no_notifications(app, pilot, timeout=0.3)
|
||||
|
||||
assert notification.severity == "information"
|
||||
assert notification.title == "Update available"
|
||||
assert (
|
||||
notification.message
|
||||
== "0.1.0 => 0.2.0\nPlease update mistral-vibe with your package manager"
|
||||
)
|
||||
assert notifier.fetch_update_calls == 1
|
||||
assert repository.update_cache is not None
|
||||
assert repository.update_cache.latest_version == "0.2.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_does_not_display_update_notification_when_not_available(
|
||||
async def test_ui_does_not_notify_when_no_update_is_available(
|
||||
build_update_test_app: Callable[..., VibeApp],
|
||||
) -> None:
|
||||
notifier = FakeUpdateGateway(update=None)
|
||||
|
|
@ -115,7 +99,7 @@ async def test_ui_does_not_display_update_notification_when_not_available(
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_displays_warning_toast_when_check_fails(
|
||||
async def test_ui_does_not_notify_when_gateway_errors(
|
||||
build_update_test_app: Callable[..., VibeApp],
|
||||
) -> None:
|
||||
notifier = FakeUpdateGateway(
|
||||
|
|
@ -124,13 +108,7 @@ async def test_ui_displays_warning_toast_when_check_fails(
|
|||
app = build_update_test_app(update_notifier=notifier)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause(0.3)
|
||||
notifications = list(app._notifications)
|
||||
|
||||
assert notifications
|
||||
warning = notifications[-1]
|
||||
assert warning.severity == "warning"
|
||||
assert "forbidden" in warning.message.lower()
|
||||
await _assert_no_notifications(app, pilot, timeout=0.3)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -167,11 +145,11 @@ async def test_ui_does_not_show_toast_when_update_is_known_in_recent_cache_alrea
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_does_show_toast_when_cache_entry_is_too_old(
|
||||
async def test_ui_refetches_and_updates_cache_when_cache_entry_is_too_old(
|
||||
build_update_test_app: Callable[..., VibeApp],
|
||||
) -> None:
|
||||
timestamp_two_days_ago = int(time.time()) - 2 * 24 * 60 * 60
|
||||
notifier = FakeUpdateGateway(update=Update(latest_version="0.2.0"))
|
||||
notifier = FakeUpdateGateway(update=Update(latest_version="0.3.0"))
|
||||
update_cache = UpdateCache(
|
||||
latest_version="0.2.0", stored_at_timestamp=timestamp_two_days_ago
|
||||
)
|
||||
|
|
@ -181,18 +159,11 @@ async def test_ui_does_show_toast_when_cache_entry_is_too_old(
|
|||
)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause(0.3)
|
||||
notifications = list(app._notifications)
|
||||
await _assert_no_notifications(app, pilot, timeout=0.3)
|
||||
|
||||
assert notifications
|
||||
notification = notifications[-1]
|
||||
assert notification.severity == "information"
|
||||
assert notification.title == "Update available"
|
||||
assert (
|
||||
notification.message
|
||||
== "0.1.0 => 0.2.0\nPlease update mistral-vibe with your package manager"
|
||||
)
|
||||
assert notifier.fetch_update_calls == 1
|
||||
assert update_cache_repository.update_cache is not None
|
||||
assert update_cache_repository.update_cache.latest_version == "0.3.0"
|
||||
|
||||
|
||||
async def _wait_for_whats_new_message(
|
||||
|
|
@ -339,51 +310,3 @@ async def test_ui_does_not_display_whats_new_when_file_does_not_exist(
|
|||
|
||||
assert repository.update_cache is not None
|
||||
assert repository.update_cache.seen_whats_new_version == "1.0.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_displays_success_notification_when_auto_update_succeeds(
|
||||
build_update_test_app: Callable[..., VibeApp],
|
||||
) -> None:
|
||||
config = build_test_vibe_config(enable_update_checks=True, enable_auto_update=True)
|
||||
notifier = FakeUpdateGateway(update=Update(latest_version="0.2.0"))
|
||||
|
||||
with patch("vibe.cli.update_notifier.update.UPDATE_COMMANDS", ["true"]):
|
||||
app = build_update_test_app(update_notifier=notifier, config=config)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause(0.3)
|
||||
notifications = list(app._notifications)
|
||||
|
||||
assert notifications, "No notifications displayed"
|
||||
notification = notifications[-1]
|
||||
assert notification.severity == "information"
|
||||
assert notification.title == "Update successful"
|
||||
assert (
|
||||
notification.message
|
||||
== "0.1.0 => 0.2.0\nVibe was updated successfully. Please restart to use the new version."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_displays_update_notification_when_auto_update_fails(
|
||||
build_update_test_app: Callable[..., VibeApp],
|
||||
) -> None:
|
||||
config = build_test_vibe_config(enable_update_checks=True, enable_auto_update=True)
|
||||
notifier = FakeUpdateGateway(update=Update(latest_version="0.2.0"))
|
||||
|
||||
with patch("vibe.cli.update_notifier.update.UPDATE_COMMANDS", ["false"]):
|
||||
app = build_update_test_app(update_notifier=notifier, config=config)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause(0.3)
|
||||
notifications = list(app._notifications)
|
||||
|
||||
assert notifications
|
||||
notification = notifications[-1]
|
||||
assert notification.severity == "information"
|
||||
assert notification.title == "Update available"
|
||||
assert (
|
||||
notification.message
|
||||
== "0.1.0 => 0.2.0\nPlease update mistral-vibe with your package manager"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,12 @@ from vibe.cli.update_notifier import (
|
|||
UpdateGatewayCause,
|
||||
UpdateGatewayError,
|
||||
)
|
||||
from vibe.cli.update_notifier.update import UpdateError, get_update_if_available
|
||||
from vibe.cli.update_notifier.update import (
|
||||
UpdateError,
|
||||
get_pending_update_from_cache,
|
||||
get_update_if_available,
|
||||
mark_update_as_dismissed,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -300,3 +305,136 @@ async def test_updates_cache_timestamp_with_current_version_when_gateway_errors(
|
|||
assert update_cache_repository.update_cache is not None
|
||||
assert update_cache_repository.update_cache.latest_version == "1.0.0"
|
||||
assert update_cache_repository.update_cache.stored_at_timestamp == current_timestamp
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_pending_update_from_cache_returns_none_when_cache_is_empty() -> None:
|
||||
repository = FakeUpdateCacheRepository()
|
||||
|
||||
assert await get_pending_update_from_cache(repository, "1.0.0") is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_pending_update_from_cache_returns_none_when_cached_version_equals_current() -> (
|
||||
None
|
||||
):
|
||||
repository = FakeUpdateCacheRepository(
|
||||
update_cache=UpdateCache(latest_version="1.0.0", stored_at_timestamp=0)
|
||||
)
|
||||
|
||||
assert await get_pending_update_from_cache(repository, "1.0.0") is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_pending_update_from_cache_returns_none_when_cached_version_is_older() -> (
|
||||
None
|
||||
):
|
||||
repository = FakeUpdateCacheRepository(
|
||||
update_cache=UpdateCache(latest_version="0.9.0", stored_at_timestamp=0)
|
||||
)
|
||||
|
||||
assert await get_pending_update_from_cache(repository, "1.0.0") is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_pending_update_from_cache_returns_version_when_newer_exists() -> (
|
||||
None
|
||||
):
|
||||
repository = FakeUpdateCacheRepository(
|
||||
update_cache=UpdateCache(latest_version="1.2.3", stored_at_timestamp=0)
|
||||
)
|
||||
|
||||
assert await get_pending_update_from_cache(repository, "1.0.0") == "1.2.3"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_pending_update_from_cache_returns_none_when_current_is_invalid() -> (
|
||||
None
|
||||
):
|
||||
repository = FakeUpdateCacheRepository(
|
||||
update_cache=UpdateCache(latest_version="1.2.3", stored_at_timestamp=0)
|
||||
)
|
||||
|
||||
assert await get_pending_update_from_cache(repository, "not-a-version") is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_pending_update_from_cache_returns_none_when_dismissed() -> None:
|
||||
repository = FakeUpdateCacheRepository(
|
||||
update_cache=UpdateCache(
|
||||
latest_version="1.2.3", stored_at_timestamp=0, dismissed_version="1.2.3"
|
||||
)
|
||||
)
|
||||
|
||||
assert await get_pending_update_from_cache(repository, "1.0.0") is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_pending_update_from_cache_returns_version_when_dismissed_is_older() -> (
|
||||
None
|
||||
):
|
||||
repository = FakeUpdateCacheRepository(
|
||||
update_cache=UpdateCache(
|
||||
latest_version="1.3.0", stored_at_timestamp=0, dismissed_version="1.2.3"
|
||||
)
|
||||
)
|
||||
|
||||
assert await get_pending_update_from_cache(repository, "1.0.0") == "1.3.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mark_update_as_dismissed_persists_version_and_preserves_other_fields() -> (
|
||||
None
|
||||
):
|
||||
repository = FakeUpdateCacheRepository(
|
||||
update_cache=UpdateCache(
|
||||
latest_version="1.2.3",
|
||||
stored_at_timestamp=42,
|
||||
seen_whats_new_version="1.0.0",
|
||||
)
|
||||
)
|
||||
|
||||
await mark_update_as_dismissed(repository, "1.2.3")
|
||||
|
||||
assert repository.update_cache is not None
|
||||
assert repository.update_cache.latest_version == "1.2.3"
|
||||
assert repository.update_cache.stored_at_timestamp == 42
|
||||
assert repository.update_cache.seen_whats_new_version == "1.0.0"
|
||||
assert repository.update_cache.dismissed_version == "1.2.3"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mark_update_as_dismissed_is_a_noop_when_cache_is_empty() -> None:
|
||||
repository = FakeUpdateCacheRepository()
|
||||
|
||||
await mark_update_as_dismissed(repository, "1.2.3")
|
||||
|
||||
assert repository.update_cache is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_writing_cache_preserves_seen_whats_new_and_dismissed_version(
|
||||
current_timestamp: int,
|
||||
) -> None:
|
||||
update_notifier = FakeUpdateGateway(update=Update(latest_version="1.0.2"))
|
||||
timestamp_two_days_ago = current_timestamp - 48 * 60 * 60
|
||||
update_cache_repository = FakeUpdateCacheRepository(
|
||||
update_cache=UpdateCache(
|
||||
latest_version="1.0.1",
|
||||
stored_at_timestamp=timestamp_two_days_ago,
|
||||
seen_whats_new_version="1.0.0",
|
||||
dismissed_version="1.0.1",
|
||||
)
|
||||
)
|
||||
|
||||
await get_update_if_available(
|
||||
update_notifier,
|
||||
current_version="1.0.0",
|
||||
update_cache_repository=update_cache_repository,
|
||||
get_current_timestamp=lambda: current_timestamp,
|
||||
)
|
||||
|
||||
assert update_cache_repository.update_cache is not None
|
||||
assert update_cache_repository.update_cache.latest_version == "1.0.2"
|
||||
assert update_cache_repository.update_cache.seen_whats_new_version == "1.0.0"
|
||||
assert update_cache_repository.update_cache.dismissed_version == "1.0.1"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue