Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions web/backend/tests/api/routes/test_create_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,24 +232,31 @@ def test_concurrent_fact_saves_with_the_same_revision_have_one_winner(
original_claim = create_routes._claim_fact_draft_update

def synchronized_claim(*args, **kwargs):
barrier.wait(timeout=5)
barrier.wait(timeout=30)
return original_claim(*args, **kwargs)

monkeypatch.setattr(create_routes, "_claim_fact_draft_update", synchronized_claim)

def save(source_text: str) -> tuple[str, int | dict]:
sources = ["Concurrent revision A.", "Concurrent revision B."]
# Read ORM state on the owning thread: commit expired the shared dataset,
# so reading dataset.id in a worker would use the parent session concurrently.
commands = [
FactDecompSaveCommand.model_validate(
_fact_command(
{
**_fact_payload(dataset, source_text),
"item_id": initial.id,
"expected_item_revision": initial.item_revision,
}
)
)
for source_text in sources
]

def save(body: FactDecompSaveCommand) -> tuple[str, int | dict]:
with Session(engine) as session:
current_author = session.get(User, author_id)
assert current_author is not None
body = FactDecompSaveCommand.model_validate(
_fact_command(
{
**_fact_payload(dataset, source_text),
"item_id": initial.id,
"expected_item_revision": initial.item_revision,
}
)
)
try:
response = create_routes._create_or_update_fact_decomp_item(
session,
Expand All @@ -263,9 +270,8 @@ def save(source_text: str) -> tuple[str, int | dict]:
return "conflict", exc.detail
return "saved", response.id

sources = ["Concurrent revision A.", "Concurrent revision B."]
with ThreadPoolExecutor(max_workers=2) as executor:
outcomes = list(executor.map(save, sources))
outcomes = list(executor.map(save, commands))

assert [outcome[0] for outcome in outcomes].count("saved") == 1
conflicts = [outcome[1] for outcome in outcomes if outcome[0] == "conflict"]
Expand Down
6 changes: 6 additions & 0 deletions web/backend/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from app.core.config import settings
from app.core.db import engine, init_db
from app.main import app
from app.services.rate_limit import rate_limiter
from tests.utils.user import authentication_token_from_email
from tests.utils.utils import get_superuser_token_headers

Expand All @@ -51,6 +52,11 @@ def _database_template() -> Generator[Path, None, None]:
shutil.rmtree(_TEST_DB_DIRECTORY)


@pytest.fixture(autouse=True)
def _clear_rate_limiter() -> None:
rate_limiter.clear()


@pytest.fixture(autouse=True)
def db(_database_template: Path) -> Generator[Session, None, None]:
"""Give every test a fresh database and direct session."""
Expand Down
Loading