74 lines
2.7 KiB
Python
74 lines
2.7 KiB
Python
"""Private group lookup/create race regression tests."""
|
|
from __future__ import annotations
|
|
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
from pathlib import Path
|
|
from threading import Barrier
|
|
|
|
from app.services.groups import GroupStore
|
|
|
|
|
|
def test_private_group_lookup_and_create_is_atomic(tmp_path: Path):
|
|
store = GroupStore(tmp_path)
|
|
barrier = Barrier(8)
|
|
|
|
def get_group():
|
|
barrier.wait(timeout=5)
|
|
return store.get_or_create_private_group(
|
|
org_id="org-1",
|
|
owner_user_id="user-1",
|
|
owner_name="Trainee",
|
|
)
|
|
|
|
with ThreadPoolExecutor(max_workers=8) as pool:
|
|
groups = list(pool.map(lambda _: get_group(), range(8)))
|
|
|
|
assert len({group["id"] for group in groups}) == 1
|
|
assert len(store.list_for_org("org-1")) == 1
|
|
assert groups[0]["owner_user_id"] == "user-1"
|
|
|
|
|
|
def test_private_groups_are_scoped_by_org_and_owner(tmp_path: Path):
|
|
store = GroupStore(tmp_path)
|
|
|
|
first = store.get_or_create_private_group(org_id="org-1", owner_user_id="user-1")
|
|
same_owner = store.get_or_create_private_group(org_id="org-1", owner_user_id="user-1")
|
|
other_org = store.get_or_create_private_group(org_id="org-2", owner_user_id="user-1")
|
|
other_owner = store.get_or_create_private_group(org_id="org-1", owner_user_id="user-2")
|
|
|
|
assert same_owner["id"] == first["id"]
|
|
assert other_org["id"] != first["id"]
|
|
assert other_owner["id"] != first["id"]
|
|
assert len(store.list_for_org("org-1")) == 2
|
|
assert len(store.list_for_org("org-2")) == 1
|
|
|
|
|
|
def test_locked_persona_appends_preserve_all_concurrent_variants(tmp_path: Path):
|
|
store = GroupStore(tmp_path)
|
|
group = store.get_or_create_private_group(org_id="org-1", owner_user_id="user-1")
|
|
barrier = Barrier(8)
|
|
|
|
def append_one(index: int):
|
|
barrier.wait(timeout=5)
|
|
with store.record_lock(group["id"]):
|
|
current = store.get(group["id"])
|
|
store.set_personas(
|
|
group["id"],
|
|
current.get("personas", []) + [{"id": f"variant-{index}", "name": str(index)}],
|
|
)
|
|
|
|
with ThreadPoolExecutor(max_workers=8) as pool:
|
|
list(pool.map(append_one, range(8)))
|
|
|
|
personas = store.get(group["id"])["personas"]
|
|
assert {persona["id"] for persona in personas} == {f"variant-{i}" for i in range(8)}
|
|
|
|
|
|
def test_record_lock_is_reentrant_for_nested_service_writes(tmp_path: Path):
|
|
store = GroupStore(tmp_path)
|
|
group = store.create(org_id="org-1", creator_id="user-1", title="Nested")
|
|
with store.record_lock(group["id"]):
|
|
with store.record_lock(group["id"]):
|
|
store.set_personas(group["id"], [{"id": "persona-1", "name": "Nested"}])
|
|
assert store.get(group["id"])["personas"][0]["id"] == "persona-1"
|