- browsercfg 接进组合根:配置读 $DATA_DIR/config/plugins/browser.json(零 env)→ 探测(本机
Chromium → 已在跑的 CDP)→ 不可用就不注册(原因码入日志)
- 自研最小出站 CDP 客户端(不 Enabled Runtime、隔离世界求值、不发 --enable-automation)+
导航 + 截图 0600 落盘 + 指纹自检;工具 browser(open/screenshot/info,owner-only)
- 3 个变异自检(启动恢复 --enable-automation/无视可用性/空闲回收失效)
- 真机对照(两站各 1 次,**如实结论**):两侧都 pass ⇒ 「headless 被拦 / headful 通过」**未被证明**;
但测到两处硬差异:无头 UA 带 HeadlessChrome、screen 800 vs 2632;两模式 webdriver 均 false
(把 L6 的推断变成实测)。跑完 0 残留 Chrome、无对外监听端口
⚠️ 未做:MCP 自动接入(口径委派未落地)/自动注入上下文/面板页/搜索降级/docker 配方/Windows 用
pipe(Go ExtraFiles 不支持);SSRF 落了第三份同策实现待收口;浏览器会话未挂进程关闭链
89 lines
3.1 KiB
Go
89 lines
3.1 KiB
Go
package browser
|
||
|
||
import (
|
||
"bytes"
|
||
"image"
|
||
"image/color"
|
||
"image/png"
|
||
"os"
|
||
"path/filepath"
|
||
"runtime"
|
||
"testing"
|
||
)
|
||
|
||
// pngBytes 造一张 w×h 的合法 PNG(用标准编码器,保证 imageSize 真能解出来)。
|
||
func pngBytes(t *testing.T, w, h int) []byte {
|
||
t.Helper()
|
||
img := image.NewRGBA(image.Rect(0, 0, w, h))
|
||
img.Set(0, 0, color.RGBA{R: 1, G: 2, B: 3, A: 255})
|
||
var buf bytes.Buffer
|
||
if err := png.Encode(&buf, img); err != nil {
|
||
t.Fatalf("编码 PNG: %v", err)
|
||
}
|
||
return buf.Bytes()
|
||
}
|
||
|
||
// TestSaveShot_Writes0600AndDimensions 钉住截图落盘的**三件事**:0600 权限、
|
||
// 名字不含站点信息、宽高来自字节本身(而不是配置里的视口)。
|
||
func TestSaveShot_Writes0600AndDimensions(t *testing.T) {
|
||
dir := filepath.Join(t.TempDir(), "shots")
|
||
data := pngBytes(t, 7, 5)
|
||
shot, err := saveShot(dir, data, shotExtPNG)
|
||
if err != nil {
|
||
t.Fatalf("saveShot: %v", err)
|
||
}
|
||
if shot.Bytes != len(data) {
|
||
t.Errorf("Bytes = %d, want %d", shot.Bytes, len(data))
|
||
}
|
||
if !shot.DimensionsKnown || shot.Width != 7 || shot.Height != 5 {
|
||
t.Errorf("尺寸 = (%d,%d) known=%v, want (7,5) true", shot.Width, shot.Height, shot.DimensionsKnown)
|
||
}
|
||
if shot.Format != shotExtPNG || shot.Degraded {
|
||
t.Errorf("format=%q degraded=%v, want png/false", shot.Format, shot.Degraded)
|
||
}
|
||
if len(shot.SHA256) != 64 {
|
||
t.Errorf("SHA256 长度 = %d, want 64", len(shot.SHA256))
|
||
}
|
||
if filepath.Ext(shot.Path) != ".png" {
|
||
t.Errorf("扩展名 = %q, want .png", filepath.Ext(shot.Path))
|
||
}
|
||
if fi, err := os.Stat(shot.Path); err != nil {
|
||
t.Fatalf("stat %s: %v", shot.Path, err)
|
||
} else if runtime.GOOS != "windows" && fi.Mode().Perm() != shotFileMode {
|
||
// Windows 不做 POSIX 权限(Go 只映射只读位),因此这一条只在类 Unix 平台上断言;
|
||
// Windows 上 0600 是**尽力而为**(对齐 browsercfg.EnsureProfileDir 的既有口径)。
|
||
t.Errorf("权限 = %v, want %v", fi.Mode().Perm(), shotFileMode)
|
||
}
|
||
// 目录权限:非 Windows 上必须是 0700(Windows 不做 POSIX 权限,跳过)。
|
||
if runtime.GOOS != "windows" {
|
||
if fi, err := os.Stat(dir); err != nil {
|
||
t.Fatalf("stat %s: %v", dir, err)
|
||
} else if fi.Mode().Perm() != shotDirMode {
|
||
t.Errorf("目录权限 = %v, want %v", fi.Mode().Perm(), shotDirMode)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestSaveShot_DegradedJPEGFlagged 钉住「降级要说出来」:JPEG 落盘必须置 degraded
|
||
// 且扩展名是 .jpeg(模型/面板据此知道这张图不是无损的那张)。
|
||
func TestSaveShot_DegradedJPEGFlagged(t *testing.T) {
|
||
dir := t.TempDir()
|
||
shot, err := saveShot(dir, []byte{0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x01}, shotExtJPEG)
|
||
if err != nil {
|
||
t.Fatalf("saveShot: %v", err)
|
||
}
|
||
if !shot.Degraded || shot.Format != shotExtJPEG {
|
||
t.Errorf("degraded=%v format=%q, want true/jpeg", shot.Degraded, shot.Format)
|
||
}
|
||
if shot.DimensionsKnown {
|
||
t.Error("非图像字节不该被判成「尺寸已知」")
|
||
}
|
||
}
|
||
|
||
// TestSaveShot_RejectsEmpty 钉住空字节不落盘(不写一个 0 字节的「截图」骗人)。
|
||
func TestSaveShot_RejectsEmpty(t *testing.T) {
|
||
if _, err := saveShot(t.TempDir(), nil, shotExtPNG); err == nil {
|
||
t.Fatal("空字节应当报错")
|
||
}
|
||
}
|