package callmemory import ( "context" "data-terminal-cloud/internal/volcsubtitle" "github.com/google/uuid" "sync" "testing" ) func TestSpokenVolumePhrases(t *testing.T) { for _, text := range []string{"调大音量", "把音量调大", "声音放大一点"} { if spokenVolumeAction(text) != "up" { t.Errorf("not recognized: %s", text) } } for text, want := range map[string]string{ "大点儿声": "up", "大点声": "up", "请你声音大一点": "up", "能不能大点儿声?": "up", "帮我把音量调高一点": "up", "声音太小了": "up", "声音有点低": "up", "小点儿声": "down", "把声音调小一点好吗": "down", "声音太大了": "down", "音量低一点": "down", "不要大点儿声": "", "不用调大音量": "", "他说大点儿声": "", "如果我说大点声会怎样": "", "大点声,小点声": "", "大一点": "", "我年轻时声音太大了": "", "“大点儿声”": "", "声音大一点也没用": "", } { if got := spokenVolumeAction(text); got != want { t.Errorf("%q: got %q want %q", text, got, want) } } } func TestSpokenVolumeCallbackSafety(t *testing.T) { f, d := directFixture(t) ctx := context.Background() sid := uuid.NewString() if _, err := f.s.DirectRequest(ctx, d, "start", DirectInput{SessionID: sid, BootID: f.boot}); err != nil { t.Fatal(err) } f.exec(t, `INSERT INTO device_twin(device_id,tenant_id,desired_revision,desired_config) VALUES($1,$2,1,'{"volume":{"speaker":22},"identity":{"display_name":"保留名称"}}') ON CONFLICT(device_id) DO UPDATE SET desired_config=EXCLUDED.desired_config,desired_revision=1`, f.device, f.tenant) packet := func(round, seq int64, who, text string, final bool) volcsubtitle.Packet { return volcsubtitle.Packet{Type: "subtitle", Data: []volcsubtitle.Segment{{UserID: who, RoundID: &round, Sequence: &seq, Text: text, Definite: final}}} } send := func(p volcsubtitle.Packet) { t.Helper() if err := f.s.DirectSubtitle(ctx, sid, p); err != nil { t.Fatal(err) } } check := func(want, rev int) { t.Helper() var got, r int var name string if err := f.p.QueryRow(ctx, `SELECT (desired_config->'volume'->>'speaker')::int,desired_revision,desired_config->'identity'->>'display_name' FROM device_twin WHERE device_id=$1`, f.device).Scan(&got, &r, &name); err != nil { t.Fatal(err) } if got != want || r != rev || name != "保留名称" { t.Fatalf("got volume=%d revision=%d name=%s", got, r, name) } } send(packet(1, 1, "input", "大点声", false)) send(packet(2, 1, "output", "大点声", true)) send(packet(3, 1, "input", "不要大点声", true)) check(22, 1) // Concurrent delivery retries and final fragments in the same round must not // increment twice; the subtitle and action are in the same transaction. p := packet(4, 1, "input", "大点儿声", true) errs := make(chan error, 4) var wg sync.WaitGroup for i := 0; i < 4; i++ { wg.Add(1) go func() { defer wg.Done(); errs <- f.s.DirectSubtitle(ctx, sid, p) }() } wg.Wait() close(errs) for err := range errs { if err != nil { t.Fatal(err) } } send(packet(4, 2, "input", "声音大一点", true)) check(32, 2) send(packet(5, 1, "input", "小点儿声", true)) check(22, 3) f.exec(t, `UPDATE device_twin SET desired_config=jsonb_set(desired_config,'{volume,speaker}','95') WHERE device_id=$1`, f.device) send(packet(6, 1, "input", "大点声", true)) check(100, 4) send(packet(7, 1, "input", "大点声", true)) check(100, 4) f.exec(t, `UPDATE device_twin SET desired_config=jsonb_set(desired_config,'{volume,speaker}','5') WHERE device_id=$1`, f.device) send(packet(8, 1, "input", "小点声", true)) check(0, 5) f.exec(t, `UPDATE call_session SET lease_expires_at=now()-interval '1 second' WHERE id=$1`, sid) send(packet(9, 1, "input", "大点声", true)) check(0, 5) f.exec(t, `UPDATE call_session SET lease_expires_at=now()+interval '45 seconds' WHERE id=$1`, sid) if _, err := f.s.DirectRequest(ctx, d, "end", DirectInput{SessionID: sid, Reason: "hangup", SDKStopped: true}); err != nil { t.Fatal(err) } send(packet(10, 1, "input", "大点声", true)) check(0, 5) }