From 220e4fab27b7f419b3e89e2f2d28ae789aac09d7 Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期四, 08 十月 2026 16:29:55 +0800
Subject: [PATCH] 5种场景模式家长端ok

---
 xiaozhi-esp32-server/main/xiaozhi-server/tests/test_assessment.py |   75 ++++++++++++++++++++++++++++++++++---
 1 files changed, 69 insertions(+), 6 deletions(-)

diff --git a/xiaozhi-esp32-server/main/xiaozhi-server/tests/test_assessment.py b/xiaozhi-esp32-server/main/xiaozhi-server/tests/test_assessment.py
index 51bed6e..c1aea83 100644
--- a/xiaozhi-esp32-server/main/xiaozhi-server/tests/test_assessment.py
+++ b/xiaozhi-esp32-server/main/xiaozhi-server/tests/test_assessment.py
@@ -14,15 +14,15 @@
 
 
 CLIPS = [
-    "/english_learning/shadowing/lessons/unit01/01.mp3",
-    "/english_learning/shadowing/lessons/unit01/02.mp3",
+    "/english_learning/shadowing/lessons/l1/001/en/01.mp3",
+    "/english_learning/shadowing/lessons/l1/001/en/02.mp3",
 ]
 
 
 def test_resolve_expected_text_from_transcripts_not_path():
     task = {
         "transcripts": {"01.mp3": "Hello, how are you today?", "01": "Hello, how are you today?"},
-        "sentences": ["/english_learning/shadowing/lessons/unit01/01.mp3"],
+        "sentences": ["/english_learning/shadowing/lessons/l1/001/en/01.mp3"],
     }
     expected = resolve_expected_text(task, CLIPS, 0)
     assert expected == "Hello, how are you today?"
@@ -32,7 +32,7 @@
 
 def test_resolve_expected_text_rejects_path_as_original():
     task = {
-        "transcripts": {"01.mp3": "/english_learning/shadowing/lessons/unit01/01.mp3"},
+        "transcripts": {"01.mp3": "/english_learning/shadowing/lessons/l1/001/en/01.mp3"},
         "sentences": [CLIPS[0]],
     }
     assert resolve_expected_text(task, CLIPS, 0) is None
@@ -294,7 +294,7 @@
         )
     )
     assert switched == [("chat", "conversation", False, False)]
-    assert spoken == ["澶嶈缁撴潫銆傛垜浠潵鑱婂ぉ鍚с��"]
+    assert spoken == ["娉涘惉缁撴潫銆傛垜浠潵鑱婂ぉ鍚с��"]
 
 
 def test_late_asr_after_listen_window_still_scores_previous_sentence(tmp_path, monkeypatch):
@@ -320,8 +320,9 @@
 
     captured = {}
 
-    def fake_save(session_id, step_index, wav_bytes, conn=None):
+    def fake_save(session_id, step_index, wav_bytes, conn=None, text=None):
         captured["wav"] = wav_bytes
+        captured["text"] = text
         captured["id"] = f"{session_id}_{step_index}.wav"
         return captured["id"]
 
@@ -334,10 +335,33 @@
     turn = conn.task_session.find_turn(0)
     assert turn["metrics"][0]["method"] == "text_similarity_v0"
     assert turn["audio_id"] == captured["id"]
+    assert captured["text"] == "Hello, how are you today?"
     wav = pcm_snapshot_to_wav(conn.last_asr_audio_snapshot)
     assert captured["wav"][:4] == b"RIFF"
     assert wav[8:12] == b"WAVE"
     assert captured["wav"] == wav
+
+
+def test_later_asr_updates_sidecar_without_new_wav(tmp_path, monkeypatch):
+    from pathlib import Path
+    from core.app_runtime.runtime import (
+        apply_deferred_assessment,
+        apply_shadowing_user_text,
+        save_shadowing_listen_audio,
+    )
+
+    conn = _shadowing_conn(tmp_path, monkeypatch)
+    conn.shadowing_listen_pcm = [b"\x00\x00" * 80]
+    save_shadowing_listen_audio(conn, 0)
+    turn = conn.task_session.find_turn(0)
+    wav_path = tmp_path / "data" / "recordings" / Path(turn["audio_id"])
+    txt_path = wav_path.with_suffix(".txt")
+    assert txt_path.read_text(encoding="utf-8").strip() == ""
+    apply_shadowing_user_text(conn, "Hello, how are you today?")
+    apply_deferred_assessment(conn, 0, persist_audio=False)
+    recordings = list((tmp_path / "data" / "recordings").rglob("*.wav"))
+    assert len(recordings) == 1
+    assert txt_path.read_text(encoding="utf-8").strip() == "Hello, how are you today?"
 
 
 def test_shadowing_playlist_end_announces_then_chat(tmp_path, monkeypatch):
@@ -373,6 +397,42 @@
     assert played == []
     assert switched == [("chat", "conversation", False)]
     assert spoken == ["璺熻缁撴潫銆傛垜浠潵鑱婂ぉ鍚с��"]
+
+
+def test_repeater_completed_returns_chat_after_reconnect(monkeypatch):
+    """WS 鎺夌嚎鍚庤澶囧彲鑳藉湪鏂扮殑 CHAT 杩炴帴涓婅ˉ鍙� audio.completed銆�"""
+    from core.app_runtime.runtime import handle_skill_event
+
+    switched = []
+    spoken = []
+
+    async def fake_start(conn, app_id, task_id, params, source="voice", **kwargs):
+        switched.append((app_id, task_id, kwargs.get("speak_switch")))
+        conn.interaction_mode = "CHAT"
+        return None
+
+    monkeypatch.setattr("core.app_runtime.runtime.start_app_task", fake_start)
+    monkeypatch.setattr("core.app_runtime.runtime._speak", lambda conn, text: spoken.append(text))
+    conn = SimpleNamespace(
+        interaction_mode="CHAT",
+        active_app_id="chat",
+        current_task_id="conversation",
+        task_session=None,
+        repeater_speed_queue=[],
+        repeater_playlist=[],
+        shadowing_awaiting_user=False,
+        prompt="",
+        config={},
+        device_id="mac",
+        tts=None,
+    )
+    asyncio.run(
+        handle_skill_event(
+            conn, "[skill_event]audio.completed|english_learning|repeater|101290"
+        )
+    )
+    assert switched == [("chat", "conversation", False)]
+    assert spoken == ["娉涘惉缁撴潫銆傛垜浠潵鑱婂ぉ鍚с��"]
 
 
 def test_playlist_completed_returns_chat_after_reconnect(monkeypatch):
@@ -438,6 +498,9 @@
     wav_path = tmp_path / "data" / "recordings" / Path(turn["audio_id"])
     assert wav_path.is_file()
     assert wav_path.stat().st_size > 44
+    txt_path = wav_path.with_suffix(".txt")
+    assert txt_path.is_file()
+    assert txt_path.read_text(encoding="utf-8").strip() == ""
 
 
 def test_fifteen_listen_windows_write_fifteen_wavs(tmp_path, monkeypatch):

--
Gitblit v1.9.1