(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 个变异自检(含一条自认的等价变异)。
127 lines
4.2 KiB
Go
127 lines
4.2 KiB
Go
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)
|
||
}
|
||
}
|
||
}
|