Files
AskaEth 1420e8aaa6
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(gateway): D3 审查读侧/revert 端点 + 语音用户可见面(L45)
(a) 记忆入库前审查的两条 admin 端点(主人判「有必要就做」):
  GET /api/v1/admin/memories/reviews?limit= (留痕读侧,不含记忆正文)
  POST /api/v1/admin/memories/{id}/revert (回滚软合并,落审计 memory.review.revert)
  两条 writeAuth(裁定 DECISIONS #10:留痕属私有面,不给 viewer)
(b) 语音用户可见面:DTO voice(含服务端派生 url)+ GET /api/v1/media/{name}
  (200/206/304/416/HEAD、Accept-Ranges、非法名/穿越统一 404 media_not_found)
  + documents.Store.SaveAudio(内容寻址、$DATA_DIR 0600)+ history voice 列读写与引用计数差动

缺口(如实):message_persisted/握手回放仍不带 voice(文件面外,需给 history.Message 加字段);
AMR 需一行 VoiceTranscoder 注入(DECISIONS #11 已排)。4 个变异自检(含一条自认的等价变异)。
2026-10-06 14:12:27 +08:00

127 lines
4.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package voice
// 本文件锁定**回放端点**那一侧的格式事实(design/24 §10.11;契约 v0.67 的
// `GET /api/v1/media/{name}` 表):白名单**字面**成员、扩展名归一、名字形态的
// deny-by-default,以及唯一派生字段 `url` 的拼法。
//
// 白名单用**字面量**钉住(而不是从被测表反推):表改成员时本用例必须红 ——
// 与「拿被测常量当期望」的教训同一条纪律。
import (
"reflect"
"testing"
)
// TestMediaExtensionsAreTheDeclaredTen 钉住白名单成员与项数(10 项,逐字)。
func TestMediaExtensionsAreTheDeclaredTen(t *testing.T) {
t.Parallel()
want := []string{
".aac", ".amr", ".flac", ".m4a", ".mp3",
".oga", ".ogg", ".opus", ".wav", ".webm",
}
if got := MediaExtensions(); !reflect.DeepEqual(got, want) {
t.Fatalf("MediaExtensions() = %v, want %v", got, want)
}
}
// TestMediaExtAllowedNormalizesAndDeniesByDefault 覆盖归一与默认拒。
func TestMediaExtAllowedNormalizesAndDeniesByDefault(t *testing.T) {
t.Parallel()
allowed := []string{".ogg", "ogg", ".OGG", " .Opus ", ".wav", ".amr", ".webm"}
for _, ext := range allowed {
if !MediaExtAllowed(ext) {
t.Fatalf("MediaExtAllowed(%q) = false, want true", ext)
}
}
denied := []string{"", ".", ".txt", ".mp4", ".pdf", ".ogg2", "ogs", ".OGX"}
for _, ext := range denied {
if MediaExtAllowed(ext) {
t.Fatalf("MediaExtAllowed(%q) = true, want false(deny-by-default)", ext)
}
}
}
// TestMediaMIME 逐项钉住下发类型(契约 v0.67 §4.1 的映射表)。
func TestMediaMIME(t *testing.T) {
t.Parallel()
cases := map[string]string{
".wav": "audio/wav",
".mp3": "audio/mpeg",
".aac": "audio/aac",
".ogg": "audio/ogg",
".oga": "audio/ogg",
".opus": "audio/ogg",
".m4a": "audio/mp4",
".webm": "audio/webm",
".flac": "audio/flac",
".amr": "audio/amr",
}
for ext, want := range cases {
if got := MediaMIME(ext); got != want {
t.Fatalf("MediaMIME(%q) = %q, want %q", ext, got, want)
}
if got := MediaMIME("." + ext[1:]); got != want {
t.Fatalf("MediaMIME(无点 %q) = %q, want %q", ext, got, want)
}
}
// 未知扩展名绝不回一个「看起来像音频」的兜底类型。
if got := MediaMIME(".txt"); got != "" {
t.Fatalf("MediaMIME(.txt) = %q, want 空串", got)
}
}
// TestMediaPath 钉住唯一派生字段的拼法(服务端一处拼出,客户端绝不自己拼)。
func TestMediaPath(t *testing.T) {
t.Parallel()
name := "0123456789abcdef0123456789abcdef.ogg"
if got, want := MediaPath(name), "/api/v1/media/"+name; got != want {
t.Fatalf("MediaPath = %q, want %q", got, want)
}
if MediaPathPrefix != "/api/v1/media/" {
t.Fatalf("MediaPathPrefix = %q, want /api/v1/media/", MediaPathPrefix)
}
}
// TestStoredNameHexLen 钉住主名宽度(与 documents 的同名常量同值由网关侧交叉断言)。
func TestStoredNameHexLen(t *testing.T) {
t.Parallel()
if StoredNameHexLen != 32 {
t.Fatalf("StoredNameHexLen = %d, want 32", StoredNameHexLen)
}
}
// TestValidMediaName 覆盖名字形态的放行与拒绝(含路径穿越的一切常见形态)。
func TestValidMediaName(t *testing.T) {
t.Parallel()
valid := []string{
"0123456789abcdef0123456789abcdef.ogg",
"ffffffffffffffffffffffffffffffff.amr",
"0123456789abcdef0123456789abcdef.webm",
}
for _, name := range valid {
if !ValidMediaName(name) {
t.Fatalf("ValidMediaName(%q) = false, want true", name)
}
}
invalid := []string{
"",
"0123456789ABCDEF0123456789abcdef.ogg", // 大写十六进制
"0123456789abcdef0123456789abcde.ogg", // 主名不足 32 位
"0123456789abcdef0123456789abcdefg.ogg", // 主名超 32 位
"0123456789abcdef0123456789abcdef", // 没有扩展名
"0123456789abcdef0123456789abcdef.xml", // 扩展名不在白名单
"0123456789abcdef0123456789abcdef.exe", // 同上
"0123456789abcdef0123456789abcdef.o.ogg", // 多一个点
"../0123456789abcdef0123456789abcdef.ogg",
"..\\0123456789abcdef0123456789abcdef.ogg",
"/etc/passwd",
"sub/0123456789abcdef0123456789abcdef.ogg",
"voice.ogg",
}
for _, name := range invalid {
if ValidMediaName(name) {
t.Fatalf("ValidMediaName(%q) = true, want false", name)
}
}
}