"""workflows/setup.py — scan_instance, map_resource, validate_protocol_template (plan §5).""" import pytest from fakes import FakeItem, make_item # --- scan_instance ---------------------------------------------------------- async def test_scan_counts_containers_and_markers(setup, client): client.items[13] = make_item(id=13, title="Water bottle", body="plain", containers=[]) result = await setup.scan_instance() assert result.containers_seen == 2 # containers on item 12 assert result.markers_found == 1 # marker on item 12 async def test_scan_proposes_marker_matches(setup, client): result = await setup.scan_instance() proposal = next(p for p in result.proposals if p.resource_key == "ethanol_absolute") assert proposal.item_id == 12 assert proposal.reason is not None async def test_scan_proposes_exact_title_match_as_suggestion(setup, client): """Exact title matches are setup-time suggestions with scores (plan §5.4).""" client.items[14] = FakeItem(id=14, title="sodium_azide", body="") result = await setup.scan_instance() proposal = next((p for p in result.proposals if p.item_id == 14), None) assert proposal is not None assert proposal.resource_key == "sodium_azide" # --- map_resource ----------------------------------------------------------- async def test_map_resource_binds_key(setup, client, resolver_stub): entry = await setup.map_resource("ethanol_absolute", 12) assert entry.resource_key == "ethanol_absolute" assert entry.item_id == 12 assert resolver_stub.mappings["ethanol_absolute"] == 12 async def test_map_resource_is_idempotent_upsert(setup, resolver_stub): await setup.map_resource("ethanol_absolute", 12) await setup.map_resource("ethanol_absolute", 12) assert resolver_stub.mappings["ethanol_absolute"] == 12 entries = await resolver_stub.list_mappings() assert len([e for e in entries if e.resource_key == "ethanol_absolute"]) == 1 async def test_map_resource_rebinding_updates(setup, resolver_stub): await setup.map_resource("ethanol_absolute", 12) await setup.map_resource("ethanol_absolute", 13) assert resolver_stub.mappings["ethanol_absolute"] == 13 # --- validate_protocol_template --------------------------------------------- def _annotated_template_client(): from fakes import FakeElabftwClient, FakeStep, make_experiment, make_item, make_template annotated_step = FakeStep( id=41, body=( "Add ethanol\n" "" ), ordering=1, ) broken_step = FakeStep( id=42, body="Broken\n", ordering=2 ) templates = [ make_template( id=7, steps=[annotated_step, broken_step], ) ] return FakeElabftwClient( templates=templates, experiments=[make_experiment()], items=[make_item()] ) async def test_validate_flags_unmapped_resource_key(setup, client, resolver_stub): """Annotation ok, but the mapping store lacks the key → blocking error.""" client.templates[7] = _annotated_template_client().templates[7] resolver_stub.mappings.clear() report = await setup.validate_protocol_template("experiments_templates", 7) assert report.ok is False errors = [i for i in report.issues if i.severity == "error"] assert any(i.resource_key == "ethanol_absolute" for i in errors) async def test_validate_ok_when_annotation_and_mapping_are_fine(setup, client, resolver_stub): client.templates[7] = _annotated_template_client().templates[7] report = await setup.validate_protocol_template("experiments_templates", 7) assert report.ok is False # the broken json step must block # fix the broken step, then expect a clean report client.templates[7].steps[1].body = "Clean step without annotation" report = await setup.validate_protocol_template("experiments_templates", 7) assert report.ok is True assert report.issues == [] async def test_validate_reports_annotation_error_with_step_id(setup, client): client.templates[7] = _annotated_template_client().templates[7] report = await setup.validate_protocol_template("experiments_templates", 7) issues = [i for i in report.issues if i.step_id == 42] assert issues and issues[0].severity == "error" async def test_validate_entity_type_is_mirrored_in_report(setup, client): client.templates[7] = _annotated_template_client().templates[7] report = await setup.validate_protocol_template("experiments_templates", 7) assert report.entity_type == "experiments_templates" assert report.entity_id == 7 async def test_validate_experiment_steps_too(setup, client, resolver_stub): """validate works on live experiments, not only templates.""" resolver_stub.mappings.clear() report = await setup.validate_protocol_template("experiments", 123) assert report.entity_type == "experiments" assert report.ok is False async def test_validate_unknown_entity_fails_cleanly(setup): with pytest.raises(Exception) as excinfo: await setup.validate_protocol_template("experiments", 9999) assert not isinstance(excinfo.value, NotImplementedError)