Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
"""Gateway-owned revisions for concurrent profile UI metadata updates.
|
||||
|
||||
Clients use ``profiles.list`` to read a shared key and its revision, merge a
|
||||
local mutation, then pass that revision back to ``profiles.configure``. The
|
||||
gateway must let exactly one concurrent writer advance the key and make every
|
||||
stale writer retry instead of silently replacing somebody else's state.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
import pytest
|
||||
|
||||
import tui_gateway.server as srv
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def home(tmp_path, monkeypatch):
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
return hermes_home
|
||||
|
||||
|
||||
def _configure(ui_meta, expected=None):
|
||||
params = {"name": "default", "ui_meta": ui_meta}
|
||||
if expected is not None:
|
||||
params["ui_meta_expected_revisions"] = expected
|
||||
return srv._methods["profiles.configure"]("configure", params)["result"]["applied"]
|
||||
|
||||
|
||||
def _default_profile():
|
||||
rows = srv._methods["profiles.list"](
|
||||
"list", {"include_sessions": False}
|
||||
)["result"]["profiles"]
|
||||
return next(row for row in rows if row["name"] == "default")
|
||||
|
||||
|
||||
def test_ui_meta_revision_advances_and_stale_compare_and_swap_is_rejected(home):
|
||||
first = _configure({"shared-room": {"messages": ["one"]}})
|
||||
assert first["ui_meta"] is True
|
||||
assert first["ui_meta_revisions"] == {"shared-room": 1}
|
||||
|
||||
second = _configure(
|
||||
{"shared-room": {"messages": ["one", "two"]}},
|
||||
{"shared-room": 1},
|
||||
)
|
||||
assert second["ui_meta"] is True
|
||||
assert second["ui_meta_revisions"] == {"shared-room": 2}
|
||||
|
||||
stale = _configure(
|
||||
{"shared-room": {"messages": ["stale replacement"]}},
|
||||
{"shared-room": 1},
|
||||
)
|
||||
assert stale["ui_meta"] is False
|
||||
assert stale["ui_meta_conflicts"] == {
|
||||
"shared-room": {"expected": 1, "actual": 2}
|
||||
}
|
||||
|
||||
row = _default_profile()
|
||||
assert row["ui_meta"]["shared-room"] == {"messages": ["one", "two"]}
|
||||
assert row["ui_meta_revisions"]["shared-room"] == 2
|
||||
|
||||
|
||||
def test_ui_meta_revision_survives_key_deletion(home):
|
||||
_configure({"shared-room": {"messages": ["one"]}})
|
||||
deleted = _configure({"shared-room": None}, {"shared-room": 1})
|
||||
|
||||
assert deleted["ui_meta"] is True
|
||||
assert deleted["ui_meta_revisions"] == {"shared-room": 2}
|
||||
row = _default_profile()
|
||||
assert "shared-room" not in row.get("ui_meta", {})
|
||||
assert row["ui_meta_revisions"]["shared-room"] == 2
|
||||
|
||||
stale_recreate = _configure(
|
||||
{"shared-room": {"messages": ["resurrected"]}},
|
||||
{"shared-room": 1},
|
||||
)
|
||||
assert stale_recreate["ui_meta"] is False
|
||||
assert stale_recreate["ui_meta_conflicts"]["shared-room"]["actual"] == 2
|
||||
|
||||
|
||||
def test_two_concurrent_writers_cannot_both_replace_the_same_revision(home):
|
||||
def write(label):
|
||||
return _configure(
|
||||
{"shared-room": {"messages": [label]}},
|
||||
{"shared-room": 0},
|
||||
)
|
||||
|
||||
with ThreadPoolExecutor(max_workers=2) as pool:
|
||||
results = list(pool.map(write, ("alpha", "beta")))
|
||||
|
||||
assert sum(result["ui_meta"] is True for result in results) == 1
|
||||
assert sum(result["ui_meta"] is False for result in results) == 1
|
||||
loser = next(result for result in results if result["ui_meta"] is False)
|
||||
assert loser["ui_meta_conflicts"]["shared-room"]["actual"] == 1
|
||||
assert _default_profile()["ui_meta_revisions"]["shared-room"] == 1
|
||||
Reference in New Issue
Block a user