wip feat: add redis-based queue
CI / lint (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped

This commit is contained in:
perl_d committed 2026-09-11 17:28:51 +02:00
1 parent a177484e61
commit a2e29a455d
6 files changed
+621 -1

No files matched your search

+131
View File
@@ -0,0 +1,131 @@
import json
from uuid import UUID, uuid4
import pytest
from aare.queue.models import (
QueueInstructionBase,
QueueItemKind,
RasterInstruction,
RotationInstruction,
ScreeningInstruction,
UnknownInstruction,
dump_queue_item,
parse_queue_item,
)
KNOWN_INSTRUCTIONS = [
(RasterInstruction, QueueItemKind.RASTER),
(ScreeningInstruction, QueueItemKind.SCREENING),
(RotationInstruction, QueueItemKind.ROTATION),
]
# A plausible instruction from a producer newer than this reader: a kind we have
# never heard of, plus fields we know nothing about.
FUTURE_INSTRUCTION = {
"id": "11111111-2222-3333-4444-555555555555",
"kind": "xrf_map",
"sample_id": 9,
"grid_steps": [4, 4],
"dwell_ms": 50,
}
@pytest.mark.parametrize("cls,kind", KNOWN_INSTRUCTIONS, ids=lambda v: getattr(v, "value", None))
def test_known_instruction_defaults_its_own_kind(cls, kind):
assert cls(sample_id=1).kind == kind
def test_ids_are_generated_and_distinct():
ids = {RasterInstruction(sample_id=1).id for _ in range(5)}
assert len(ids) == 5
@pytest.mark.parametrize("cls,kind", KNOWN_INSTRUCTIONS, ids=lambda v: getattr(v, "value", None))
def test_known_instruction_round_trips_to_its_own_class(cls, kind):
original = cls(sample_id=3)
restored = parse_queue_item(dump_queue_item(original))
assert type(restored) is cls
assert restored.sample_id == 3
@pytest.mark.parametrize("cls,kind", KNOWN_INSTRUCTIONS, ids=lambda v: getattr(v, "value", None))
def test_round_trip_preserves_the_id(cls, kind):
"""Identity ops target the id, so a read must never regenerate it."""
original = cls(sample_id=3)
assert parse_queue_item(dump_queue_item(original)).id == original.id
def test_unrecognised_kind_becomes_an_unknown_instruction():
item = parse_queue_item(json.dumps(FUTURE_INSTRUCTION))
assert isinstance(item, UnknownInstruction)
assert item.kind == "xrf_map"
assert item.id == UUID(FUTURE_INSTRUCTION["id"])
def test_unknown_instruction_survives_a_read_write_cycle_verbatim():
"""Reordering rewrites every entry. A reader that cannot interpret an item
must not drop the fields it did not understand."""
item = parse_queue_item(json.dumps(FUTURE_INSTRUCTION))
assert json.loads(dump_queue_item(item)) == FUTURE_INSTRUCTION
def test_unknown_instruction_is_a_queue_instruction():
"""It shares the base, so consumers reach its common fields the same way
they would for a kind they understand."""
item = parse_queue_item(json.dumps(FUTURE_INSTRUCTION))
assert isinstance(item, QueueInstructionBase)
assert item.id == UUID(FUTURE_INSTRUCTION["id"])
assert item.sample_id == FUTURE_INSTRUCTION["sample_id"]
def test_unknown_fields_are_kept_as_extras():
"""Held as pydantic extras rather than a raw blob, which is what lets them
serialize back out without a custom serializer."""
item = parse_queue_item(json.dumps(FUTURE_INSTRUCTION))
assert item.model_extra == {
"grid_steps": FUTURE_INSTRUCTION["grid_steps"],
"dwell_ms": FUTURE_INSTRUCTION["dwell_ms"],
}
def test_unknown_instruction_does_not_regenerate_a_missing_id():
"""The base defaults id, but a stored entry always has one already --
minting a fresh one on read would silently break remove/reorder."""
with pytest.raises(ValueError):
parse_queue_item(json.dumps({"kind": "xrf_map", "sample_id": 9}))
@pytest.mark.parametrize(
"payload",
[
{"kind": "xrf_map", "sample_id": 9},
{"id": "not-a-uuid", "kind": "xrf_map", "sample_id": 9},
{"id": str(uuid4()), "sample_id": 9},
{"id": str(uuid4()), "kind": "xrf_map"},
],
ids=["no-id", "non-uuid-id", "no-kind", "no-sample-id"],
)
def test_entries_missing_a_base_field_are_rejected(payload):
"""Every instruction inherits id and sample_id and declares a kind, so an
entry missing any of them is corrupt rather than merely unrecognised -- it
must not parse as Unknown."""
with pytest.raises(ValueError):
parse_queue_item(json.dumps(payload))
def test_malformed_json_is_rejected():
with pytest.raises(ValueError):
parse_queue_item("NOT JSON AT ALL")
def test_non_object_json_is_rejected():
with pytest.raises(TypeError):
parse_queue_item("[1, 2, 3]")
def test_a_known_kind_with_a_bad_payload_is_not_smuggled_in_as_unknown():
"""Falling back on any ValidationError would turn a malformed raster item
into an opaque Unknown that silently round-trips broken data."""
with pytest.raises(ValueError):
parse_queue_item(json.dumps({"id": str(uuid4()), "kind": "raster"})) # no sample_id