diff --git a/airflow/api_connexion/schemas/pool_schema.py b/airflow/api_connexion/schemas/pool_schema.py index fd42d71e89578..59b58b4adcc36 100644 --- a/airflow/api_connexion/schemas/pool_schema.py +++ b/airflow/api_connexion/schemas/pool_schema.py @@ -70,9 +70,18 @@ def get_deferred_slots(obj: Pool) -> int: return obj.deferred_slots() @staticmethod - def get_open_slots(obj: Pool) -> float: - """Return the open slots of the pool.""" - return obj.open_slots() + def get_open_slots(obj: Pool) -> int: + """ + Return the open slots of the pool. + + Unlimited pools (``slots == -1``) use ``float('inf')`` internally; the REST API + schema requires an integer, so we serialize that as ``-1`` (same convention as + ``slots`` for unlimited). + """ + open_slots = obj.open_slots() + if isinstance(open_slots, float) and open_slots == float("inf"): + return -1 + return int(open_slots) class PoolCollection(NamedTuple): diff --git a/tests/api_connexion/schemas/test_pool_schemas.py b/tests/api_connexion/schemas/test_pool_schemas.py index 110103073aaa5..b9a7c6fdecbcc 100644 --- a/tests/api_connexion/schemas/test_pool_schemas.py +++ b/tests/api_connexion/schemas/test_pool_schemas.py @@ -33,22 +33,34 @@ def setup_method(self) -> None: def teardown_method(self) -> None: clear_db_pools() + @pytest.mark.parametrize( + ("pool_name", "slots", "expected_open_slots"), + [ + pytest.param("test_pool", 2, 2, id="finite_slots"), + pytest.param("unlimited_pool", -1, -1, id="unlimited_slots_serialized_as_minus_one"), + ], + ) @provide_session - def test_serialize(self, session): - pool_model = Pool(pool="test_pool", slots=2, include_deferred=False) + def test_serialize(self, pool_name, slots, expected_open_slots, session): + """Pools must serialize open_slots as an integer. + + Unlimited pools (``slots == -1``) must not leak ``inf`` into the JSON + response since the OpenAPI schema declares ``open_slots`` as an integer. + """ + pool_model = Pool(pool=pool_name, slots=slots, include_deferred=False) session.add(pool_model) session.commit() pool_instance = session.query(Pool).filter(Pool.pool == pool_model.pool).first() serialized_pool = pool_schema.dump(pool_instance) assert serialized_pool == { - "name": "test_pool", - "slots": 2, + "name": pool_name, + "slots": slots, "occupied_slots": 0, "running_slots": 0, "queued_slots": 0, "scheduled_slots": 0, "deferred_slots": 0, - "open_slots": 2, + "open_slots": expected_open_slots, "description": None, "include_deferred": False, }