Files
hermes-agent/tests/tools/test_video_generate_schema.py
teknium1 f287036ea3 test: purge low-value tests, lane py19 (370 removed)
Change-detectors, tautologies, source-reading tests, redundant duplicates,
mock-echo tests and dead/unrunnable tests. Per-test rationale in the lane
ledger (category + reason for every removal).
2026-09-23 03:15:26 -07:00

200 lines
8.0 KiB
Python

"""video_generate dynamic schema — capability-gated params (#95681 diet).
Mirrors tests/tools/test_image_generate_schema.py (#97057). Coverage is
guaranteed two ways:
1. every in-tree video_gen plugin's capabilities() must declare EVERY axis
the schema builder reads (a new axis added to the builder without fleet
declarations fails here);
2. every FAL video family must carry the per-family keys the fal provider's
active-model capabilities() resolution reads;
"""
import os
import sys
import unittest
from unittest.mock import patch
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
import tools.video_generation_tool as vt
from tools.video_generation_tool import (
_build_dynamic_video_schema,
)
# Every axis _build_dynamic_video_schema reads from capabilities().
CAPABILITY_AXES = (
"modalities",
"aspect_ratios",
"resolutions",
"max_duration",
"min_duration",
"supports_audio",
"supports_negative_prompt",
"supports_seed",
"supports_upscale",
"max_reference_images",
)
# Per-family keys the FAL provider's capabilities() resolution reads.
FAL_FAMILY_KEYS = ("durations", "aspect_ratios", "resolutions", "audio",
"negative", "seed")
class TestFleetCapabilityCoverage(unittest.TestCase):
def test_every_provider_declares_every_axis(self):
"""Instantiate each in-tree provider class and check the RETURNED
capabilities dict — source grep can't see inherited keys."""
checked = 0
# fal
from plugins.video_gen.fal import FALVideoGenProvider
caps = FALVideoGenProvider().capabilities()
for axis in CAPABILITY_AXES:
self.assertIn(axis, caps, f"fal missing {axis}")
checked += 1
# xai
from plugins.video_gen.xai import XAIVideoGenProvider
caps = XAIVideoGenProvider().capabilities()
for axis in CAPABILITY_AXES:
self.assertIn(axis, caps, f"xai missing {axis}")
checked += 1
# deepinfra
from plugins.video_gen.deepinfra import DeepInfraVideoGenProvider
caps = DeepInfraVideoGenProvider().capabilities()
for axis in CAPABILITY_AXES:
self.assertIn(axis, caps, f"deepinfra missing {axis}")
checked += 1
# openrouter
from plugins.video_gen.openrouter import OpenRouterVideoGenProvider
caps = OpenRouterVideoGenProvider().capabilities()
for axis in CAPABILITY_AXES:
self.assertIn(axis, caps, f"openrouter missing {axis}")
checked += 1
self.assertGreaterEqual(checked, 4)
def test_abc_default_fails_closed(self):
from agent.video_gen_provider import VideoGenProvider
class _P(VideoGenProvider):
name = "t"
display_name = "T"
def generate(self, prompt, **kw):
return {}
def list_models(self):
return []
caps = _P().capabilities()
self.assertEqual(caps.get("modalities"), ["text"])
for axis in ("supports_audio", "supports_negative_prompt",
"supports_seed", "supports_upscale"):
self.assertFalse(caps.get(axis), axis)
for axis in CAPABILITY_AXES:
self.assertIn(axis, caps, f"ABC default missing {axis}")
def test_every_fal_family_declares_per_family_keys(self):
from plugins.video_gen.fal import FAL_FAMILIES
for fam, meta in FAL_FAMILIES.items():
with self.subTest(family=fam):
for key in FAL_FAMILY_KEYS:
self.assertIn(key, meta, f"{fam} missing {key}")
self.assertTrue(
meta.get("text_endpoint") or meta.get("image_endpoint"),
f"{fam} declares no endpoint",
)
# Audio truth model: "audio" means a generate_audio TOGGLE
# exists; "audio_native" means audio is ALWAYS ON with no
# toggle. A family may have neither (silent model) but
# never both — that would be contradictory.
self.assertFalse(
meta.get("audio") and meta.get("audio_native"),
f"{fam}: audio toggle and always-on are mutually "
"exclusive",
)
def test_always_on_audio_surfaces_in_description_not_param(self):
"""Maintainer catch (H3 has audio!): families whose audio is
always-on (no API toggle) must TELL the model about the audio in
the description rather than advertise a dead `audio` param."""
schema = TestDynamicParamGating._schema_with(
TestDynamicParamGating(), {
"modalities": ["text", "image"],
"supports_audio": False, "audio_always_on": True,
"supports_negative_prompt": False, "supports_seed": False,
"supports_upscale": False, "max_reference_images": 0,
})
self.assertNotIn("audio", schema["parameters"]["properties"])
self.assertIn("always on", schema["description"])
class TestDynamicParamGating(unittest.TestCase):
def _schema_with(self, caps, model_meta=None):
class _Prov:
name = "fake"
display_name = "Fake"
def capabilities(self):
return caps
def list_models(self):
return [dict({"id": "m1"}, **(model_meta or {}))]
def default_model(self):
return "m1"
with patch.object(vt, "_resolve_active_provider",
return_value=_Prov()), \
patch.object(vt, "_read_configured_video_model",
return_value="m1"):
return _build_dynamic_video_schema()
def test_full_featured_backend_gets_all_params(self):
schema = self._schema_with({
"modalities": ["text", "image"],
"aspect_ratios": ["16:9"], "resolutions": ["720p"],
"min_duration": 2, "max_duration": 12,
"supports_audio": True, "supports_negative_prompt": True,
"supports_seed": True, "supports_upscale": True,
"max_reference_images": 7,
})
props = schema["parameters"]["properties"]
for p in ("image_url", "reference_image_urls", "negative_prompt",
"audio", "seed", "upscale"):
self.assertIn(p, props, p)
self.assertEqual(props["reference_image_urls"]["maxItems"], 7)
self.assertEqual(props["duration"]["minimum"], 2)
self.assertEqual(props["duration"]["maximum"], 12)
self.assertEqual(props["aspect_ratio"]["enum"], ["16:9"])
def test_minimal_backend_gets_bare_params(self):
schema = self._schema_with({
"modalities": ["text"],
"supports_audio": False, "supports_negative_prompt": False,
"supports_seed": False, "supports_upscale": False,
"max_reference_images": 0,
})
props = schema["parameters"]["properties"]
for p in ("image_url", "reference_image_urls", "negative_prompt",
"audio", "seed", "upscale"):
self.assertNotIn(p, props, p)
self.assertIn("text-to-video only", schema["description"])
def test_i2v_only_model_overrides_backend_union(self):
# gemini-omni-flash case: dual-modality backend, i2v-only model.
schema = self._schema_with(
{"modalities": ["text", "image"], "max_reference_images": 0,
"supports_audio": False, "supports_negative_prompt": False,
"supports_seed": False, "supports_upscale": False},
model_meta={"modalities": ["image"]},
)
self.assertIn("image_url", schema["parameters"]["properties"])
self.assertIn("image-to-video only", schema["description"])
def test_no_provider_serves_prompt_only(self):
with patch.object(vt, "_resolve_active_provider", return_value=None):
schema = _build_dynamic_video_schema()
self.assertEqual(sorted(schema["parameters"]["properties"]), ["prompt"])
if __name__ == "__main__":
unittest.main()