Files
OpenViking/tests/session/test_session_commit_race.py
T
Qin Haojie 7abd6ab249 refactor(client): remove Python embedded mode (#3712)
* refactor(client): remove Python embedded mode

Consolidate Python consumers on the HTTP SDK while keeping shared server and storage capabilities unchanged.

* refactor(client): remove obsolete embedded leftovers
2026-08-10 18:00:00 +08:00

78 lines
3.0 KiB
Python

# Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd.
# SPDX-License-Identifier: AGPL-3.0
"""Tests for session commit race condition fix (#580)."""
import asyncio
from openviking.message import TextPart
class TestCommitRace:
"""Test concurrent commit safety."""
async def test_concurrent_commit_no_duplicate(self, client):
"""Two concurrent commits on the same session: only one should archive."""
session = client(session_id="race_test_dedup")
await session.ensure_exists()
session.add_message("user", [TextPart("Hello")])
session.add_message("assistant", [TextPart("Hi there")])
results = await asyncio.gather(
session.commit_async(),
session.commit_async(),
)
archived_count = sum(1 for r in results if r.get("archived") is True)
assert archived_count == 1, f"Expected exactly 1 archived commit, got {archived_count}"
# Messages should be cleared after commit
assert len(session.messages) == 0
# Compression index should have incremented exactly once
assert session._compression.compression_index == 1
async def test_message_added_during_commit_not_lost(
self,
client,
monkeypatch,
):
"""Messages added while commit is running should not be lost."""
session = client(session_id="race_test_msg_safety")
await session.ensure_exists()
session.add_message("user", [TextPart("Original message")])
# Use an Event for deterministic synchronization instead of sleeps
phase1_done = asyncio.Event()
session_type = type(session)
original_generate = session_type._generate_archive_summary_async
async def slow_generate(self, messages, latest_archive_overview=""):
# Signal that Phase 1 is complete (lock released, messages cleared)
phase1_done.set()
# Yield control so add_message can run before archive completes
await asyncio.sleep(0)
return await original_generate(
self,
messages,
latest_archive_overview=latest_archive_overview,
)
monkeypatch.setattr(session_type, "_generate_archive_summary_async", slow_generate)
async def commit_and_add():
"""Start commit, then add a message after Phase 1 completes."""
commit_task = asyncio.create_task(session.commit_async())
# Wait until Phase 1 is done (lock released, messages cleared)
await phase1_done.wait()
# Add message while commit is in Phase 2 (after lock released)
session.add_message("user", [TextPart("New message during commit")])
return await commit_task
result = await commit_and_add()
assert result.get("archived") is True
# The new message should still be in the session
assert len(session.messages) == 1
assert session.messages[0].content == "New message during commit"