Files
Cyrene_v2/internal/services/channelbridge/inbound_documents_honesty_test.go
T
AskaEth 3be70f8232
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(channels): 适配器入站内联附件字节(telegram/kook/qq_obv11/weixin_ilink)
- 新增 internal/channel/mediafetch:来源白名单(deny-by-default,支持 *.suffix)
  + 逐跳重定向校验 + 边读边限 + 超时;只回来源主机,绝不回完整 URL
- 四渠道各自 media.go 接通入站内联:只从平台载荷给出的引用取回字节
  (telegram file_id→getFile→file_path 形态校验;kook CDN;qq 文件段 url;
   weixin cdnURL + AES-ECB 解密)
- 常量全部复用既有来源:documents.ExtAllowed / DefaultMaxBytes / MaxPerMessage
- channelbridge 诚实提示文案改为对「渠道只给远程引用」与「代取失败」两种成因都成立
- 18 个测试函数(含四渠道「正文里的 URL ⇒ 0 次命中」红线反证、端到端进模型可见视图)
- 变异验证:去掉尺寸上限 ⇒ 四渠道红(含修正后不再是假绿的 weixin 超量用例)
2026-10-04 22:13:17 +08:00

124 lines
5.2 KiB
Go
Raw 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 channelbridge
// 本文件承载 IM 入站附件摄取的**诚实性与边界**用例(design/24 §4.4;切片 M1),
// 与 inbound_documents_test.go 是同一批夹具的搬家(夹具留在那边,同包可见):
//
// - 拒绝原因必须**分类正确**:超限说超限,绝不笼统说成「格式不支持」;
// - 渠道只给远程引用(目前适配器的现实形态)时不代取、不静默;
// - 图片类媒体保持现状(不走文档摄取);
// - 组合根未接线时照旧跑回合,但模型知道附件读不到。
import (
"context"
"strings"
"testing"
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/documents"
)
// TestInboundOversizeFileIsHonestAboutSize 钉住诚实口径的**分类**:体积超限必须
// 说「超过体积上限」,绝不能笼统说成「格式不支持」——把原因说错也是不诚实。
func TestInboundOversizeFileIsHonestAboutSize(t *testing.T) {
t.Parallel()
env := newIngestEnv(t, documents.StoreConfig{MaxBytes: 8}, true)
msg := fileMessage("这个大吗", "big.txt", []byte("这是明显超过八个字节的正文\n"), "text/plain")
env.bridge.process(context.Background(), msg)
if names := docDirNames(t, env.uploads); len(names) != 0 {
t.Fatalf("超限附件被落库了:%v", names)
}
visible := env.model.visibleText()
if !strings.Contains(visible, "体积上限") {
t.Fatalf("超限附件的提示没有点明体积上限:%q", visible)
}
if strings.Contains(visible, "格式不在可读白名单内") {
t.Fatalf("超限附件被说成了「格式不支持」(原因说错):%q", visible)
}
if log := env.logs.String(); !strings.Contains(log, "inbound attachment rejected") {
t.Fatalf("缺少带来源的拒绝日志:%q", log)
}
}
// TestInboundRemoteOnlyMediaIsHonestWithoutFetch 钉住「字节为空」这一形态:桥接侧
// **不代取**(受支持格式的代取由适配器在入站时按平台资产白名单完成,切片 IM-1),
// 字节空 ⇒ 一行诚实提示 + 一条带来源的日志,绝不静默。
func TestInboundRemoteOnlyMediaIsHonestWithoutFetch(t *testing.T) {
t.Parallel()
env := newIngestEnv(t, documents.StoreConfig{}, true)
msg := directMessage()
msg.Content = "这个文件里是什么"
msg.Media = []adapter.Media{{
Kind: adapter.MediaFile, URL: "https://cdn.example.com/secret-token=abc/report.pdf",
Name: "report.pdf", Size: 1024, MIMEType: "application/pdf",
}}
env.bridge.process(context.Background(), msg)
if names := docDirNames(t, env.uploads); len(names) != 0 {
t.Fatalf("远程引用的附件被落库了:%v", names)
}
visible := env.model.visibleText()
if !strings.Contains(visible, "report.pdf") || !strings.Contains(visible, "远程引用") {
t.Fatalf("模型可见视图里没有诚实提示行:%q", visible)
}
log := env.logs.String()
if !strings.Contains(log, "no inlined bytes") {
t.Fatalf("缺少「无内联字节」的带来源日志:%q", log)
}
// 远程引用可能带凭据,绝不进日志。
if strings.Contains(log, "secret-token=abc") {
t.Fatalf("日志里出现了远程引用原串(可能含凭据):%q", log)
}
}
// TestInboundImageMediaKeepsExistingPath 钉住边界:图片类媒体**不**走文档摄取
// (design/24 §4.2 末:图片走既有视觉链路),因此不落库、不挂文档部件、不产生提示行。
func TestInboundImageMediaKeepsExistingPath(t *testing.T) {
t.Parallel()
env := newIngestEnv(t, documents.StoreConfig{}, true)
msg := directMessage()
msg.Content = "看这张图"
msg.Media = []adapter.Media{{
Kind: adapter.MediaImage, Name: "photo.png", MIMEType: "image/png",
Data: []byte("\x89PNG\r\n\x1a\n"), Size: 8,
}}
env.bridge.process(context.Background(), msg)
if names := docDirNames(t, env.uploads); len(names) != 0 {
t.Fatalf("图片类媒体被文档摄取落库了:%v", names)
}
visible := env.model.visibleText()
if strings.Contains(visible, "[附件 ") || strings.Contains(visible, "无法读取") {
t.Fatalf("图片类媒体产生了文档清单/提示行:%q", visible)
}
if !strings.Contains(visible, "看这张图") {
t.Fatalf("用户文本丢了:%q", visible)
}
if log := env.logs.String(); strings.Contains(log, "attachment") {
t.Fatalf("图片类媒体产生了附件日志:%q", log)
}
}
// TestInboundFileMediaWithoutStoreIsHonest 钉住「未接线」路径:组合根没给落盘配置时,
// 带文件的消息照旧能跑(模型收到诚实提示),只是不落库 —— 绝不假装读到了。
func TestInboundFileMediaWithoutStoreIsHonest(t *testing.T) {
t.Parallel()
env := newIngestEnv(t, documents.StoreConfig{}, false)
msg := fileMessage("这个文件呢", "notes.txt", []byte("正文\n"), "text/plain")
env.bridge.process(context.Background(), msg)
if env.model.calls() == 0 {
t.Fatal("未接线时回合没有跑到模型")
}
if !strings.Contains(env.model.visibleText(), "未接线文档存储") {
t.Fatalf("未接线时没有诚实提示:%q", env.model.visibleText())
}
if log := env.logs.String(); !strings.Contains(log, "document store not wired") {
t.Fatalf("未接线时没有带来源的日志:%q", log)
}
}