File size: 3,329 Bytes
eda316b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
"""Exercise the MCP server tools as plain Python callables.



FastMCP tools are registered on the server instance, but the underlying

functions are ordinary Python functions decorated with ``@mcp.tool()``.

We import the module and invoke those functions directly to verify the

end-to-end wiring (schemas validated, dispatch correct, JSON-serializable).

"""

import humeo_core.server as srv
from humeo_core.schemas import LayoutKind


def test_list_layouts_lists_all_three():
    result = srv.list_layouts()
    kinds = {layout["kind"] for layout in result["layouts"]}
    assert kinds == {k.value for k in LayoutKind}


def test_plan_layout_tool_returns_filtergraph():
    for k in LayoutKind:
        out = srv.plan_layout(layout=k.value)
        assert out["out_label"] == "vout"
        assert "[vout]" in out["filtergraph"]


def test_build_render_cmd_dry_run():
    req = {
        "source_path": "/tmp/src.mp4",
        "clip": {
            "clip_id": "1",
            "topic": "t",
            "start_time_sec": 0.0,
            "end_time_sec": 30.0,
        },
        "layout": {"clip_id": "1", "layout": LayoutKind.SIT_CENTER.value},
        "output_path": "/tmp/out.mp4",
    }
    out = srv.build_render_cmd(request=req)
    assert out["success"] is True
    assert out["output_path"] == "/tmp/out.mp4"
    assert any("-filter_complex" == part for part in out["ffmpeg_cmd"])


def test_select_clips_tool_happy_path():
    words = [
        {"word": f"w{i}", "start_time": float(i), "end_time": float(i) + 0.5}
        for i in range(120)
    ]
    plan = srv.select_clips(
        source_path="/tmp/x.mp4",
        transcript_words=words,
        duration_sec=120.0,
        target_count=2,
        min_sec=30.0,
        max_sec=60.0,
    )
    assert plan["source_path"] == "/tmp/x.mp4"
    assert 1 <= len(plan["clips"]) <= 2


def test_classify_scenes_tool_no_keyframes():
    scenes = [{"scene_id": "s0", "start_time": 0.0, "end_time": 5.0}]
    out = srv.classify_scenes(scenes=scenes)
    assert out["classifications"][0]["scene_id"] == "s0"
    assert out["classifications"][0]["layout"] in {k.value for k in LayoutKind}


def test_detect_scene_regions_returns_jobs_and_prompt():
    scenes = [
        {"scene_id": "s0", "start_time": 0.0, "end_time": 5.0, "keyframe_path": "/tmp/k0.jpg"},
        {"scene_id": "s1", "start_time": 5.0, "end_time": 10.0, "keyframe_path": "/tmp/k1.jpg"},
    ]
    out = srv.detect_scene_regions(scenes=scenes)
    assert "STRICT JSON" in out["prompt"]
    assert len(out["jobs"]) == 2
    assert out["jobs"][0]["scene_id"] == "s0"
    assert out["jobs"][0]["keyframe_path"] == "/tmp/k0.jpg"


def test_classify_scenes_with_vision_derives_instructions():
    regions = [
        {
            "scene_id": "s0",
            "chart_bbox": {"x1": 0.0, "y1": 0.0, "x2": 0.66, "y2": 1.0},
            "person_bbox": {"x1": 0.72, "y1": 0.1, "x2": 0.99, "y2": 0.95},
            "ocr_text": "CPI YoY",
        }
    ]
    out = srv.classify_scenes_with_vision(regions=regions)
    assert out["classifications"][0]["layout"] == LayoutKind.SPLIT_CHART_PERSON.value
    instr = out["layout_instructions"][0]
    assert instr["chart_x_norm"] == 0.0
    assert 0.8 < instr["person_x_norm"] < 0.9