Files
story/internal/callmemory/recordings_test.go
T

160 lines
6.0 KiB
Go
Raw Normal View History

package callmemory
import (
"context"
"data-terminal-cloud/internal/volcsubtitle"
"github.com/google/uuid"
"net/url"
"strings"
"testing"
)
func TestRecordingIdentityAndSignedURL(t *testing.T) {
prefixes, err := recordingPrefixes("KP01-020026091601", "bot_20260927080903000")
if err != nil {
t.Fatal(err)
}
key := prefixes[0] + "1790467744348_000058_c014e733.mp4"
record, ok := parseRecording(key, prefixes[0], "USER", 1045859)
if !ok || record.Duration != 58 || record.ID == "" {
t.Fatal(record, ok)
}
for _, bad := range []string{strings.Replace(key, "KP01-020026091601", "other", 1), key + "/extra", strings.Replace(key, "000058", "invalid", 1)} {
if _, ok := parseRecording(bad, prefixes[0], "USER", 1); ok {
t.Fatal("accepted foreign/malformed object")
}
}
if _, err = recordingPrefixes("../device", "bot_20260927080903000"); err == nil {
t.Fatal("unsafe device")
}
if _, err = recordingPrefixes("device", "persistent-bot"); err == nil {
t.Fatal("unverified room")
}
store, err := NewTOSRecordings("memory927", "cn-beijing", "test-ak", "test-sk", "")
if err != nil {
t.Fatal(err)
}
for _, action := range []string{"PLAY", "DOWNLOAD"} {
if action == "PLAY" {
if _, e := store.Sign(context.Background(), key, action, 60); e == nil {
t.Fatal("default domain playback allowed")
}
if e := store.ConfigurePlayback("https://recordings.example.com"); e != nil {
t.Fatal(e)
}
}
signed, e := store.Sign(context.Background(), key, action, 60)
if e != nil {
t.Fatal(e)
}
u, e := url.Parse(signed)
if e != nil {
t.Fatal(e)
}
host := "memory927.tos-cn-beijing.volces.com"
if action == "PLAY" {
host = "recordings.example.com"
}
if u.Host != host || u.Scheme != "https" || u.Query().Get("response-content-type") != "audio/mp4" || u.Query().Get("X-Tos-Expires") != "60" {
t.Fatal("incorrect direct URL options")
}
disposition := "inline"
if action == "DOWNLOAD" {
disposition = "attachment"
}
if !strings.HasPrefix(u.Query().Get("response-content-disposition"), disposition) {
t.Fatal("wrong disposition")
}
}
}
func TestRecordingIdentityFromAuthenticatedSubtitle(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)
}
n := int64(1)
packet := volcsubtitle.Packet{Type: "subtitle", Data: []volcsubtitle.Segment{{UserID: "bot_20260927080903000", RoundID: &n, Sequence: &n, Definite: true, Text: "你好"}}}
if err := f.s.DirectSubtitle(ctx, sid, packet); err != nil {
t.Fatal(err)
}
if err := f.s.DirectSubtitle(ctx, sid, packet); err != nil {
t.Fatal("duplicate", err)
}
var stored string
if err := f.p.QueryRow(ctx, `SELECT budget_snapshot->>'rtc_agent_user_id' FROM call_session WHERE id=$1::uuid`, sid).Scan(&stored); err != nil || stored != packet.Data[0].UserID {
t.Fatal("identity not retained", err)
}
packet.Data[0].UserID = "bot_20260927090903000"
if err := f.s.DirectSubtitle(ctx, sid, packet); err == nil {
t.Fatal("room reassignment accepted")
}
}
type fakeRecordings struct {
items []CloudRecording
listed, signed int
seconds int64
}
func (f *fakeRecordings) PlaybackReady() bool { return true }
func (f *fakeRecordings) List(context.Context, string, string) ([]CloudRecording, error) {
f.listed++
return f.items, nil
}
func (f *fakeRecordings) Sign(_ context.Context, key, action string, seconds int64) (string, error) {
f.signed++
f.seconds = seconds
return "https://memory927.tos-cn-beijing.volces.com/fixture", nil
}
func TestCloudRecordingAuthorization(t *testing.T) {
f := setup(t)
m, _, _ := f.call(t)
f.exec(t, `UPDATE call_session SET budget_snapshot='{"device_name":"device","rtc_agent_user_id":"bot_20260927080903000"}' WHERE id=$1::uuid`, m.SessionID)
// The fixture copies active consent types; explicitly add raw audio if absent.
f.exec(t, `INSERT INTO consent_record(tenant_id,subject_id,consent_type,scope,policy_version,notice_digest,actor_user_id,evidence_ref) SELECT tenant_id,subject_id,'raw_audio_storage',scope,policy_version,notice_digest,actor_user_id,evidence_ref FROM consent_record c WHERE subject_id=$1::uuid AND NOT EXISTS(SELECT 1 FROM consent_record r WHERE r.subject_id=c.subject_id AND r.consent_type='raw_audio_storage') LIMIT 1`, f.subject)
store := &fakeRecordings{items: []CloudRecording{{ID: hash([]byte("fixture")), Key: "fixture", Speaker: "USER", Duration: 58, Bytes: 100}}}
f.s.ConfigureRecordings(store)
in := recordingInput{ElderID: f.elder, SessionID: m.SessionID, RecordingID: store.items[0].ID, Action: "PLAY"}
deny := func(code string) {
t.Helper()
_, e := f.s.MemoryRequest(context.Background(), f.principal, "calls/recording-access", jsonBytes(in))
if e == nil || !strings.Contains(e.Error(), code) {
t.Fatalf("wanted %s got %v", code, e)
}
}
deny("RECORDING_ACCESS_DENIED")
if store.signed != 0 || store.listed != 0 {
t.Fatal("storage accessed before authorization")
}
f.exec(t, `INSERT INTO recording_access_grant(tenant_id,subject_id,grantee_user_id,permission,purpose,granted_by,expires_at) VALUES($1::uuid,$2::uuid,'10000000-0000-0000-0000-000000000101','PLAY','test','10000000-0000-0000-0000-000000000101',now()+interval '90 seconds')`, f.tenant, f.subject)
f.request(t, "calls/recording-access", in)
if store.signed != 1 || store.seconds > 90 {
t.Fatal("grant TTL not enforced")
}
in.Action = "DOWNLOAD"
deny("RECORDING_ACCESS_DENIED")
in.Action = "PLAY"
in.RecordingID = hash([]byte("another-file"))
deny("RECORDING_NOT_FOUND")
in.RecordingID = store.items[0].ID
in.SessionID = uuid.NewString()
deny("CALL_NOT_FOUND")
in.SessionID = m.SessionID
other := setup(t)
otherCall, _, _ := other.call(t)
in.SessionID = otherCall.SessionID
deny("CALL_NOT_FOUND")
in.SessionID = m.SessionID
before := store.signed
f.exec(t, `UPDATE consent_record SET withdrawn_at=now() WHERE subject_id=$1::uuid AND consent_type='raw_audio_storage'`, f.subject)
deny("RECORDING_ACCESS_DENIED")
if store.signed != before {
t.Fatal("revoked consent exposed URL")
}
}