Skip to content
Closed
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
7 changes: 5 additions & 2 deletions src/modelseed_api/jobs/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,11 +378,14 @@ def _apply_media(cobra_model, ms_media):
exc_rxn_id = f"EX_{cpd.id}_e0"
if exc_rxn_id in rxn_ids:
medium[exc_rxn_id] = cpd.maxFlux or 1000.0
cobra_model.medium = medium
if medium:
cobra_model.medium = medium
logger.info("Applied media: %d exchange reactions open", len(medium))
elif ms_media.mediacompounds:
logger.warning("Media had no matching exchange reactions — running with default bounds")
else:
logger.info("Applied empty media: all exchange reactions closed")
cobra_model.medium = {}
logger.info("Applied Empty media: all exchanges closed")


def _fix_gapfilling_metadata(ws_data: dict, media_workspace_ref: str | None) -> None:
Expand Down
86 changes: 86 additions & 0 deletions tests/unit/test_load_media_and_apply_media.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Unit tests for _load_media and _apply_media.

Regression tests for the bug where _load_media raised ValueError for the
Empty media (zero compounds), and _apply_media left default exchange bounds
instead of closing all exchanges for an empty MSMedia.
"""
from __future__ import annotations

import json
from unittest.mock import MagicMock, patch

import pytest

from modelseed_api.jobs.tasks import _apply_media, _load_media


def _mock_storage(raw_data):
"""Return a mock storage service that serves raw_data for any path."""
ws = MagicMock()
ws.get.return_value = [[None, raw_data]]
return ws


class TestLoadMediaEmpty:
def test_empty_json_returns_zero_compound_msmedia(self):
raw = json.dumps({"mediacompounds": []})
with patch("modelseed_api.jobs.tasks.get_storage_service", return_value=_mock_storage(raw)):
ms_media = _load_media("/chenry/public/modelsupport/media/Empty", "tok")
assert ms_media is not None
assert len(ms_media.mediacompounds) == 0

def test_empty_dict_returns_zero_compound_msmedia(self):
raw = {"mediacompounds": []}
with patch("modelseed_api.jobs.tasks.get_storage_service", return_value=_mock_storage(raw)):
ms_media = _load_media("/chenry/public/modelsupport/media/Empty", "tok")
assert ms_media is not None
assert len(ms_media.mediacompounds) == 0

def test_nonempty_media_still_loads_compounds(self):
raw = {"mediacompounds": [{"compound_ref": "~/compounds/cpd00001", "concentration": 0.001, "minFlux": -100, "maxFlux": 100}]}
with patch("modelseed_api.jobs.tasks.get_storage_service", return_value=_mock_storage(raw)):
ms_media = _load_media("/chenry/public/modelsupport/media/NMS", "tok")
assert len(ms_media.mediacompounds) == 1


class TestApplyMediaEmpty:
def _make_cobra_model(self, exchange_ids):
model = MagicMock()
reactions = [MagicMock(id=rid) for rid in exchange_ids]
model.reactions = reactions
model.medium = {"EX_cpd00001_e0": 100.0}
return model

def test_empty_media_closes_all_exchanges(self):
from modelseedpy.core.msmedia import MSMedia
ms_media = MSMedia("Empty", name="Empty")
cobra_model = self._make_cobra_model(["EX_cpd00001_e0", "EX_cpd00002_e0"])

_apply_media(cobra_model, ms_media)

cobra_model.__setattr__
assert cobra_model.medium == {}

def test_media_with_matching_compounds_opens_exchanges(self):
from modelseedpy.core.msmedia import MSMedia, MediaCompound
ms_media = MSMedia("NMS", name="NMS")
ms_media.mediacompounds.append(MediaCompound("cpd00001", 100.0, -100.0))

cobra_model = self._make_cobra_model(["EX_cpd00001_e0", "EX_cpd00002_e0"])
_apply_media(cobra_model, ms_media)

assert cobra_model.medium == {"EX_cpd00001_e0": 100.0}

def test_media_with_no_matching_reactions_leaves_default_bounds(self):
"""Compounds present but none match model reactions -- not an Empty case."""
from modelseedpy.core.msmedia import MSMedia, MediaCompound
ms_media = MSMedia("Weird", name="Weird")
ms_media.mediacompounds.append(MediaCompound("cpd99999", 100.0, -100.0))

original_medium = {"EX_cpd00001_e0": 100.0}
cobra_model = self._make_cobra_model(["EX_cpd00001_e0"])
cobra_model.medium = original_medium.copy()

_apply_media(cobra_model, ms_media)

assert cobra_model.medium == original_medium
Loading