From bbf48bc4bfd4a6efc27b3df2ca6baf246332da75 Mon Sep 17 00:00:00 2001 From: Benjamin Capodanno Date: Tue, 7 Apr 2026 15:00:49 -0700 Subject: [PATCH] fix: filter clinical control options to current mapped variants only The /score-sets/{urn}/clinical-controls/options endpoint was returning options derived from all mapped variants, including stale (non-current) ones. The sibling /clinical-controls endpoint already applied MappedVariant.current.is_(True); this endpoint simply missed it. Adds a regression test that marks all mapped variants as non-current and asserts the options endpoint returns 404, directly proving the filter is enforced. --- src/mavedb/routers/score_sets.py | 1 + tests/routers/test_score_set.py | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/mavedb/routers/score_sets.py b/src/mavedb/routers/score_sets.py index 47532cd3..9e3c645d 100644 --- a/src/mavedb/routers/score_sets.py +++ b/src/mavedb/routers/score_sets.py @@ -2351,6 +2351,7 @@ async def get_clinical_controls_options_for_score_set( select(ClinicalControl.db_name, ClinicalControl.db_version) .join(MappedVariant, ClinicalControl.mapped_variants) .join(Variant) + .where(MappedVariant.current.is_(True)) .where(Variant.score_set_id == item.id) ) diff --git a/tests/routers/test_score_set.py b/tests/routers/test_score_set.py index c1476a65..555a040c 100644 --- a/tests/routers/test_score_set.py +++ b/tests/routers/test_score_set.py @@ -22,6 +22,7 @@ from mavedb.models.enums.processing_state import ProcessingState from mavedb.models.enums.target_category import TargetCategory from mavedb.models.experiment import Experiment as ExperimentDbModel +from mavedb.models.mapped_variant import MappedVariant as MappedVariantDbModel from mavedb.models.score_set import ScoreSet as ScoreSetDbModel from mavedb.models.variant import Variant as VariantDbModel from mavedb.view_models.orcid import OrcidUser @@ -875,7 +876,9 @@ def test_show_score_sets_anonymous_can_fetch_public_score_sets( assert response_data[0]["urn"] == published_score_set["urn"] -def test_show_score_sets_anonymous_cannot_fetch_private_score_sets(session, client, setup_router_db, anonymous_app_overrides): +def test_show_score_sets_anonymous_cannot_fetch_private_score_sets( + session, client, setup_router_db, anonymous_app_overrides +): experiment = create_experiment(client) score_set = create_seq_score_set(client, experiment["urn"]) # Score set is private (not published); change ownership so it belongs to another user @@ -927,7 +930,9 @@ def test_show_score_sets_mixed_public_and_private_returns_404( ): experiment = create_experiment(client) public_score_set = create_seq_score_set(client, experiment["urn"]) - public_score_set = mock_worker_variant_insertion(client, session, data_provider, public_score_set, data_files / "scores.csv") + public_score_set = mock_worker_variant_insertion( + client, session, data_provider, public_score_set, data_files / "scores.csv" + ) private_score_set = create_seq_score_set(client, experiment["urn"]) with patch.object(arq.ArqRedis, "enqueue_job", return_value=None): published_score_set = publish_score_set(client, public_score_set["urn"]) @@ -3522,6 +3527,28 @@ def test_can_fetch_current_clinical_control_options_for_score_set( ) +def test_clinical_control_options_exclude_non_current(client, setup_router_db, session, data_provider, data_files): + experiment = create_experiment(client) + score_set = create_seq_score_set_with_mapped_variants( + client, session, data_provider, experiment["urn"], data_files / "scores.csv" + ) + link_clinical_controls_to_mapped_variants(session, score_set) + + # Mark all mapped variants as non-current to simulate stale mapping data. + mapped_variants = session.scalars( + select(MappedVariantDbModel) + .join(VariantDbModel) + .join(ScoreSetDbModel) + .where(ScoreSetDbModel.urn == score_set["urn"]) + ).all() + for mv in mapped_variants: + mv.current = False + session.commit() + + response = client.get(f"/api/v1/score-sets/{score_set['urn']}/clinical-controls/options") + assert response.status_code == 404 + + ######################################################################################################################## # Fetching annotated variants for a score set ########################################################################################################################