Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
"""Regression test for #44037 — holographic provider leaked its SQLite
|
||||
connection to GC on shutdown instead of closing it.
|
||||
|
||||
The corruption-mechanism framing in #44037 (TLS bytes written into the DB via
|
||||
an fd-recycle race) was not reproducible from the code: dropping a sqlite
|
||||
connection flushes valid pages through SQLite's own VFS, never TLS framing, and
|
||||
the provider is at most a *releaser* of DB fds, not the TLS-flushing owner.
|
||||
|
||||
But the underlying resource-hygiene bug is real and is what this test pins:
|
||||
``HolographicMemoryProvider.shutdown()`` must call ``MemoryStore.close()`` so
|
||||
the ``check_same_thread=False`` connection's fd is released deterministically
|
||||
on shutdown, rather than at a non-deterministic GC time on an arbitrary thread.
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
|
||||
import pytest
|
||||
|
||||
from plugins.memory.holographic import HolographicMemoryProvider
|
||||
|
||||
|
||||
def _make_provider(tmp_path):
|
||||
db_path = str(tmp_path / "memory_store.db")
|
||||
provider = HolographicMemoryProvider(config={"db_path": db_path, "hrr_dim": 64})
|
||||
provider.initialize(session_id="test-session")
|
||||
return provider
|
||||
|
||||
|
||||
def test_shutdown_closes_store_connection(tmp_path):
|
||||
provider = _make_provider(tmp_path)
|
||||
store = provider._store
|
||||
assert store is not None
|
||||
conn = store._conn
|
||||
|
||||
# Connection is live before shutdown.
|
||||
conn.execute("SELECT 1").fetchone()
|
||||
|
||||
provider.shutdown()
|
||||
|
||||
# References are dropped...
|
||||
assert provider._store is None
|
||||
assert provider._retriever is None
|
||||
|
||||
# ...AND the underlying connection was actually closed (not left to GC).
|
||||
with pytest.raises(sqlite3.ProgrammingError):
|
||||
conn.execute("SELECT 1")
|
||||
|
||||
|
||||
def test_release_all_under_closes_connections_inside_directory_only(tmp_path):
|
||||
"""Regression test for #88347 — profile delete must break refcounted handles.
|
||||
|
||||
``close()`` alone can never free a database held by a live agent (refs >= 1),
|
||||
and on Windows an open SQLite handle makes rmtree of the profile directory
|
||||
fail with WinError 32. ``release_all_under`` force-closes exactly the shared
|
||||
connections under the doomed directory and leaves everything else alone.
|
||||
"""
|
||||
from plugins.memory.holographic.store import MemoryStore
|
||||
|
||||
profile_dir = tmp_path / "profiles" / "default-2"
|
||||
profile_dir.mkdir(parents=True)
|
||||
inside = MemoryStore(db_path=profile_dir / "memory_store.db", hrr_dim=64)
|
||||
outside = MemoryStore(db_path=tmp_path / "other" / "memory_store.db", hrr_dim=64)
|
||||
inside_conn = inside._conn
|
||||
outside_conn = outside._conn
|
||||
# Both live before the release; the inside one is held "forever" (refs=1,
|
||||
# like a live agent's provider — nobody calls close()).
|
||||
inside_conn.execute("SELECT 1").fetchone()
|
||||
outside_conn.execute("SELECT 1").fetchone()
|
||||
|
||||
try:
|
||||
released = MemoryStore.release_all_under(profile_dir)
|
||||
assert released == 1
|
||||
|
||||
# The doomed connection is really closed despite refs > 0 ...
|
||||
with pytest.raises(sqlite3.ProgrammingError):
|
||||
inside_conn.execute("SELECT 1")
|
||||
# ... and the registry entry is gone, so a second release finds
|
||||
# nothing (the CLI-process no-op case).
|
||||
assert str((profile_dir / "memory_store.db").resolve()) not in MemoryStore._shared
|
||||
assert MemoryStore.release_all_under(profile_dir) == 0
|
||||
# A new store on the same path reopens fresh instead of reusing
|
||||
# the dead connection.
|
||||
reopened = MemoryStore(db_path=profile_dir / "memory_store.db", hrr_dim=64)
|
||||
reopened._conn.execute("SELECT 1").fetchone()
|
||||
|
||||
# The sibling outside the directory is untouched.
|
||||
outside_conn.execute("SELECT 1").fetchone()
|
||||
finally:
|
||||
inside.close()
|
||||
reopened.close()
|
||||
outside.close()
|
||||
|
||||
|
||||
|
||||
|
||||
def test_stale_holder_close_does_not_evict_fresh_registry_entry(tmp_path):
|
||||
"""Follow-up to #88347 — a stale holder's late ``close()`` must be inert.
|
||||
|
||||
After ``release_all_under`` force-closes a profile's connection, a store
|
||||
re-created on the same path registers a FRESH shared entry under the same
|
||||
key. If the stale holder (whose entry was force-closed) then calls
|
||||
``close()``, it must not pop the fresh entry out of the registry — that
|
||||
would let a third store open a SECOND connection to the same database and
|
||||
silently reintroduce the multi-writer contention the registry prevents.
|
||||
"""
|
||||
from plugins.memory.holographic.store import MemoryStore
|
||||
|
||||
profile_dir = tmp_path / "profiles" / "default-2"
|
||||
profile_dir.mkdir(parents=True)
|
||||
db_path = profile_dir / "memory_store.db"
|
||||
|
||||
stale = MemoryStore(db_path=db_path, hrr_dim=64)
|
||||
assert MemoryStore.release_all_under(profile_dir) == 1
|
||||
|
||||
fresh = MemoryStore(db_path=db_path, hrr_dim=64)
|
||||
key = fresh._key
|
||||
fresh_entry = MemoryStore._shared[key]
|
||||
|
||||
# The stale holder's late close must leave the fresh entry registered...
|
||||
stale.close()
|
||||
assert MemoryStore._shared.get(key) is fresh_entry
|
||||
# ...and a third store must attach to the SAME shared connection.
|
||||
third = MemoryStore(db_path=db_path, hrr_dim=64)
|
||||
try:
|
||||
assert third._conn is fresh._conn
|
||||
finally:
|
||||
third.close()
|
||||
fresh.close()
|
||||
# Normal last-holder close still evicts its own entry.
|
||||
assert key not in MemoryStore._shared
|
||||
Reference in New Issue
Block a user