Making long running operations asynchronous
This commit is contained in:
1 parent
373a2c6acf
commit
d108cb7149
7 files changed
+533
-76
No files matched your search
@@ -0,0 +1,156 @@
|
||||
import asyncio
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from app.crud import role as role_crud
|
||||
from app.models.attachment_point import AttachmentPoint
|
||||
from app.models.attachment_point_name import AttachmentPointName
|
||||
from app.models.augmented_part import AugmentedPart
|
||||
from app.models.part_type import PartType
|
||||
from tests.test_scope_import import _scope_row, _workbook
|
||||
|
||||
_JOB_URL_RE = re.compile(r'hx-get="([^"]+/jobs/[^"]+)"')
|
||||
|
||||
|
||||
async def _web_login(client, email, password="secret123"):
|
||||
resp = await client.post("/login", data={"email": email, "password": password})
|
||||
assert resp.status_code == 303
|
||||
|
||||
|
||||
async def _poll_until_done(client, url, max_tries=50):
|
||||
"""Follow the same htmx polling loop a browser would, until no further
|
||||
``hx-get`` job-status link is present in the response."""
|
||||
body = ""
|
||||
for _ in range(max_tries):
|
||||
resp = await client.get(url)
|
||||
assert resp.status_code == 200
|
||||
body = resp.text
|
||||
match = _JOB_URL_RE.search(body)
|
||||
if not match:
|
||||
return body
|
||||
url = match.group(1)
|
||||
await asyncio.sleep(0.02)
|
||||
raise AssertionError(f"job did not finish after {max_tries} polls: {body!r}")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scope_import_runs_in_background(
|
||||
client, seeded, make_user, db_sessionmaker, monkeypatch
|
||||
):
|
||||
monkeypatch.setattr("app.web.admin.AsyncSessionLocal", db_sessionmaker)
|
||||
async with db_sessionmaker() as db:
|
||||
perms = seeded["perms"]
|
||||
editor = await role_crud.create(
|
||||
db,
|
||||
name="scope-import-editor",
|
||||
permission_ids=[perms["augmented_parts:write"].id],
|
||||
)
|
||||
editor_id = editor.id
|
||||
await make_user("importer@example.com", role_ids=[editor_id])
|
||||
await _web_login(client, "importer@example.com")
|
||||
|
||||
data = _workbook([_scope_row(product_number="BG-SCOPE-1")])
|
||||
resp = await client.post(
|
||||
"/admin/scope-import",
|
||||
files={"file": ("scopes.xlsx", data, "application/octet-stream")},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert "scope-import-status" in resp.text
|
||||
match = _JOB_URL_RE.search(resp.text)
|
||||
assert match, resp.text
|
||||
|
||||
final_body = await _poll_until_done(client, match.group(1))
|
||||
assert "1</strong> created" in final_body
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_augmented_parts_sync_runs_in_background(
|
||||
client, seeded, make_user, db_sessionmaker, monkeypatch
|
||||
):
|
||||
monkeypatch.setattr("app.web.admin.AsyncSessionLocal", db_sessionmaker)
|
||||
async with db_sessionmaker() as db:
|
||||
perms = seeded["perms"]
|
||||
editor = await role_crud.create(
|
||||
db, name="sync-editor", permission_ids=[perms["augmented_parts:write"].id]
|
||||
)
|
||||
editor_id = editor.id
|
||||
await make_user("syncer@example.com", role_ids=[editor_id])
|
||||
await _web_login(client, "syncer@example.com")
|
||||
|
||||
resp = await client.post("/admin/augmented-parts/sync")
|
||||
assert resp.status_code == 200
|
||||
assert 'id="augmented-part-list"' in resp.text
|
||||
match = _JOB_URL_RE.search(resp.text)
|
||||
assert match, resp.text
|
||||
|
||||
final_body = await _poll_until_done(client, match.group(1))
|
||||
assert "Sync complete" in final_body
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fit_recompute_for_part_runs_in_background(
|
||||
client, seeded, make_user, db_sessionmaker, monkeypatch
|
||||
):
|
||||
monkeypatch.setattr("app.web.admin.AsyncSessionLocal", db_sessionmaker)
|
||||
async with db_sessionmaker() as db:
|
||||
apn = AttachmentPointName(name="Objective")
|
||||
part_t = PartType(name="Scope")
|
||||
db.add_all([apn, part_t])
|
||||
await db.flush()
|
||||
part = AugmentedPart(
|
||||
description="Test scope",
|
||||
colour_id=1,
|
||||
manufacturer_id=1,
|
||||
part_type_id=part_t.id,
|
||||
)
|
||||
db.add(part)
|
||||
await db.flush()
|
||||
db.add(
|
||||
AttachmentPoint(augmented_part_id=part.id, attachment_point_name_id=apn.id)
|
||||
)
|
||||
await db.commit()
|
||||
part_id = part.id
|
||||
|
||||
perms = seeded["perms"]
|
||||
editor = await role_crud.create(
|
||||
db,
|
||||
name="fit-recompute-editor",
|
||||
permission_ids=[perms["fits:write"].id, perms["augmented_parts:read"].id],
|
||||
)
|
||||
editor_id = editor.id
|
||||
await make_user("recomputer@example.com", role_ids=[editor_id])
|
||||
await _web_login(client, "recomputer@example.com")
|
||||
|
||||
resp = await client.post(f"/admin/augmented-parts/{part_id}/fits/recompute")
|
||||
assert resp.status_code == 200
|
||||
assert f'id="ap-{part_id}-fits"' in resp.text
|
||||
match = _JOB_URL_RE.search(resp.text)
|
||||
assert match, resp.text
|
||||
|
||||
final_body = await _poll_until_done(client, match.group(1))
|
||||
assert "created" in final_body and "removed" in final_body
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fit_recompute_all_runs_in_background(
|
||||
client, seeded, make_user, db_sessionmaker, monkeypatch
|
||||
):
|
||||
monkeypatch.setattr("app.web.admin.AsyncSessionLocal", db_sessionmaker)
|
||||
async with db_sessionmaker() as db:
|
||||
perms = seeded["perms"]
|
||||
editor = await role_crud.create(
|
||||
db, name="fit-recompute-all-editor", permission_ids=[perms["fits:write"].id]
|
||||
)
|
||||
editor_id = editor.id
|
||||
await make_user("recomputerall@example.com", role_ids=[editor_id])
|
||||
await _web_login(client, "recomputerall@example.com")
|
||||
|
||||
resp = await client.post("/admin/augmented-parts/fits/recompute")
|
||||
assert resp.status_code == 200
|
||||
assert "fits-recompute-status" in resp.text
|
||||
match = _JOB_URL_RE.search(resp.text)
|
||||
assert match, resp.text
|
||||
|
||||
final_body = await _poll_until_done(client, match.group(1))
|
||||
assert "Recompute complete" in final_body
|
||||
Reference in new issue
Block a user