"""Unit tests for VideoConfig parsing and LoRASpec validation. Pure-python, no model imports, no CUDA, no ffmpeg. Safe for Windows CI. """ import pytest from server.video import VideoConfig, LoRASpec def test_defaults_when_raw_is_empty(): cfg = VideoConfig.from_dict({}) assert cfg.enabled is False assert cfg.backend == "lightx2v" assert cfg.mode == "reflective" assert cfg.resolution == 480 assert cfg.fps == 16 assert cfg.library_base_clip_count == 4 assert cfg.reflective_prompt_reply_words == 18 assert cfg.loras == [] def test_defaults_when_raw_is_none(): cfg = VideoConfig.from_dict(None) # type: ignore[arg-type] assert cfg.enabled is False def test_library_section_override(): cfg = VideoConfig.from_dict( {"enabled": True, "mode": "library", "library": {"base_clip_count": 7, "base_clip_seconds": 3}} ) assert cfg.enabled is True assert cfg.mode == "library" assert cfg.library_base_clip_count == 7 assert cfg.library_base_clip_seconds == 3 def test_reflective_section_override(): cfg = VideoConfig.from_dict( { "reflective": { "clip_seconds": 9, "clip_prompt_template": "my template: {reply_hint}", "prompt_reply_words": 5, } } ) assert cfg.reflective_clip_seconds == 9 assert cfg.reflective_prompt_template == "my template: {reply_hint}" assert cfg.reflective_prompt_reply_words == 5 def test_lora_parse_minimal(): cfg = VideoConfig.from_dict({"loras": [{"path": "/tmp/a.safetensors"}]}) assert len(cfg.loras) == 1 lora = cfg.loras[0] assert lora.path == "/tmp/a.safetensors" assert lora.weight == 1.0 assert lora.target == "both" assert lora.name is None def test_lora_parse_full(): cfg = VideoConfig.from_dict( { "loras": [ { "path": "/tmp/a.safetensors", "weight": 0.7, "target": "both", "name": "style-a", }, { "path": "/tmp/b.safetensors", "weight": 0.4, "target": "both", "name": "style-b", }, ] } ) assert len(cfg.loras) == 2 assert cfg.loras[0].target == "both" assert cfg.loras[0].name == "style-a" assert cfg.loras[1].target == "both" assert cfg.loras[1].weight == 0.4 def test_lora_legacy_moe_target_coerced_to_both(): """Legacy MoE configs with target='high_noise'/'low_noise' get coerced.""" cfg = VideoConfig.from_dict( { "loras": [ {"path": "/tmp/hi.safetensors", "target": "high_noise"}, {"path": "/tmp/lo.safetensors", "target": "low_noise"}, {"path": "/tmp/x.safetensors", "target": "bogus"}, ] } ) assert all(l.target == "both" for l in cfg.loras) def test_lora_entries_without_path_are_dropped(): cfg = VideoConfig.from_dict( {"loras": [{"weight": 0.5}, {"path": "/tmp/ok.safetensors"}, None]} ) assert len(cfg.loras) == 1 assert cfg.loras[0].path == "/tmp/ok.safetensors" def test_models_section_override(): cfg = VideoConfig.from_dict( { "models": { "wan22_base_repo": "/local/weights/wan22", "wan22_dit_repo": "/local/weights/wan22-dit", "wan22_dit_quant_scheme": "gguf-Q4_K_M", "wan22_config_json": "/local/cfg/turbo.json", "wan22_model_cls": "wan2.2", "musetalk_path": "/local/weights/musetalk", } } ) assert cfg.wan22_base_repo == "/local/weights/wan22" assert cfg.wan22_dit_repo == "/local/weights/wan22-dit" assert cfg.wan22_dit_quant_scheme == "gguf-Q4_K_M" assert cfg.wan22_config_json == "/local/cfg/turbo.json" assert cfg.wan22_model_cls == "wan2.2" assert cfg.musetalk_model_path == "/local/weights/musetalk" def test_models_section_defaults_to_5b_turbo(): cfg = VideoConfig.from_dict({}) assert cfg.wan22_base_repo == "Wan-AI/Wan2.2-TI2V-5B" assert cfg.wan22_dit_repo == "hum-ma/Wan2.2-TI2V-5B-Turbo-GGUF" assert cfg.wan22_dit_quant_scheme == "gguf-Q8_0" assert cfg.wan22_t5_quantized is True assert cfg.wan22_model_cls == "wan2.2" assert cfg.wan22_config_json == "/app/configs/lightx2v/wan22_i2v_gguf_5b_turbo.json"