Files
Ant-Browser/backend/app_instance_open_url_test.go
T

266 lines
7.3 KiB
Go

package backend
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"reflect"
goruntime "runtime"
"strings"
"testing"
"time"
"ant-chrome/backend/internal/browser"
"ant-chrome/backend/internal/config"
)
func TestBrowserInstanceOpenURLUsesCDPCreateTarget(t *testing.T) {
t.Parallel()
server := newRecordedCDPServer(t)
defer server.Close()
app := newBrowserOpenURLTestApp(t)
app.browserMgr.Profiles = map[string]*BrowserProfile{
"profile-ready": {
ProfileId: "profile-ready",
ProfileName: "Ready Browser",
Running: true,
DebugReady: true,
DebugPort: server.Port(),
Pid: 12345,
},
}
app.browserMgr.BrowserProcesses = make(map[string]*exec.Cmd)
ok, err := app.BrowserInstanceOpenUrl("profile-ready", "https://open.example/")
if err != nil {
t.Fatalf("BrowserInstanceOpenUrl returned error: %v", err)
}
if !ok {
t.Fatal("expected BrowserInstanceOpenUrl to succeed")
}
want := []recordedCDPCommand{
{Scope: "browser", Method: "Target.createTarget", URL: "https://open.example/"},
}
if got := server.Commands(); !reflect.DeepEqual(got, want) {
t.Fatalf("unexpected CDP command sequence:\n got=%v\nwant=%v", got, want)
}
}
func TestBrowserInstanceOpenURLFallsBackToWindowWhenDebugPending(t *testing.T) {
app, exePath := newBrowserOpenURLTestAppWithCore(t)
cmd := longLivedCommand(2 * time.Second)
if err := cmd.Start(); err != nil {
t.Fatalf("启动长生命周期测试进程失败: %v", err)
}
defer func() {
if cmd.Process != nil {
_ = cmd.Process.Kill()
_, _ = cmd.Process.Wait()
}
}()
profile := &BrowserProfile{
ProfileId: "profile-pending",
ProfileName: "Pending Browser",
UserDataDir: "profile-pending",
Running: true,
DebugReady: false,
DebugPort: 0,
Pid: cmd.Process.Pid,
}
app.browserMgr.Profiles = map[string]*BrowserProfile{profile.ProfileId: profile}
app.browserMgr.BrowserProcesses = map[string]*exec.Cmd{profile.ProfileId: cmd}
expectedUserDataDir := app.browserMgr.ResolveUserDataDir(profile)
var gotPath string
var gotArgs []string
originalStart := startBrowserWindowProcess
startBrowserWindowProcess = func(chromeBinaryPath string, args []string) (*exec.Cmd, error) {
gotPath = chromeBinaryPath
gotArgs = append([]string{}, args...)
return nil, nil
}
defer func() {
startBrowserWindowProcess = originalStart
}()
ok, err := app.BrowserInstanceOpenUrl(profile.ProfileId, "https://pending.example/")
if err != nil {
t.Fatalf("BrowserInstanceOpenUrl returned error: %v", err)
}
if !ok {
t.Fatal("expected BrowserInstanceOpenUrl to succeed")
}
if gotPath != exePath {
t.Fatalf("unexpected browser path: got=%q want=%q", gotPath, exePath)
}
wantArgs := []string{
fmt.Sprintf("--user-data-dir=%s", expectedUserDataDir),
"https://pending.example/",
}
if !reflect.DeepEqual(gotArgs, wantArgs) {
t.Fatalf("unexpected browser args:\n got=%v\nwant=%v", gotArgs, wantArgs)
}
}
func TestBrowserInstanceOpenURLFallsBackToWindowWhenCDPOpenFails(t *testing.T) {
app, exePath := newBrowserOpenURLTestAppWithCore(t)
cmd := longLivedCommand(2 * time.Second)
if err := cmd.Start(); err != nil {
t.Fatalf("启动长生命周期测试进程失败: %v", err)
}
defer func() {
if cmd.Process != nil {
_ = cmd.Process.Kill()
_, _ = cmd.Process.Wait()
}
}()
profile := &BrowserProfile{
ProfileId: "profile-ready-fallback",
ProfileName: "Fallback Browser",
UserDataDir: "profile-ready-fallback",
Running: true,
DebugReady: true,
DebugPort: freeLoopbackPort(t),
Pid: cmd.Process.Pid,
}
app.browserMgr.Profiles = map[string]*BrowserProfile{profile.ProfileId: profile}
app.browserMgr.BrowserProcesses = map[string]*exec.Cmd{profile.ProfileId: cmd}
expectedUserDataDir := app.browserMgr.ResolveUserDataDir(profile)
var gotPath string
var gotArgs []string
originalStart := startBrowserWindowProcess
startBrowserWindowProcess = func(chromeBinaryPath string, args []string) (*exec.Cmd, error) {
gotPath = chromeBinaryPath
gotArgs = append([]string{}, args...)
return nil, nil
}
defer func() {
startBrowserWindowProcess = originalStart
}()
ok, err := app.BrowserInstanceOpenUrl(profile.ProfileId, "https://fallback.example/")
if err != nil {
t.Fatalf("BrowserInstanceOpenUrl returned error: %v", err)
}
if !ok {
t.Fatal("expected BrowserInstanceOpenUrl to succeed")
}
if gotPath != exePath {
t.Fatalf("unexpected browser path: got=%q want=%q", gotPath, exePath)
}
wantArgs := []string{
fmt.Sprintf("--user-data-dir=%s", expectedUserDataDir),
"https://fallback.example/",
}
if !reflect.DeepEqual(gotArgs, wantArgs) {
t.Fatalf("unexpected browser args:\n got=%v\nwant=%v", gotArgs, wantArgs)
}
}
func TestBrowserInstanceOpenURLMarksStaleProfileStopped(t *testing.T) {
t.Parallel()
app := newBrowserOpenURLTestApp(t)
profile := &BrowserProfile{
ProfileId: "profile-stale",
ProfileName: "Stale Browser",
Running: true,
DebugReady: false,
}
app.browserMgr.Profiles = map[string]*BrowserProfile{profile.ProfileId: profile}
app.browserMgr.BrowserProcesses = make(map[string]*exec.Cmd)
ok, err := app.BrowserInstanceOpenUrl(profile.ProfileId, "https://stale.example/")
if err == nil {
t.Fatal("expected BrowserInstanceOpenUrl to fail for stale runtime state")
}
if ok {
t.Fatal("expected BrowserInstanceOpenUrl to return false for stale runtime state")
}
if !strings.Contains(err.Error(), "运行状态已失效") {
t.Fatalf("unexpected error: %v", err)
}
if profile.Running {
t.Fatal("expected stale profile to be marked stopped")
}
if profile.DebugReady {
t.Fatal("expected stale profile debug state to be cleared")
}
if profile.DebugPort != 0 || profile.Pid != 0 {
t.Fatalf("expected runtime identifiers to be cleared, got debugPort=%d pid=%d", profile.DebugPort, profile.Pid)
}
}
func newBrowserOpenURLTestApp(t *testing.T) *App {
t.Helper()
cfg := config.DefaultConfig()
app := NewApp("")
app.config = cfg
app.browserMgr = browser.NewManager(cfg, t.TempDir())
app.browserMgr.Profiles = make(map[string]*BrowserProfile)
app.browserMgr.BrowserProcesses = make(map[string]*exec.Cmd)
return app
}
func newBrowserOpenURLTestAppWithCore(t *testing.T) (*App, string) {
t.Helper()
cfg := config.DefaultConfig()
exePath := createFakeBrowserExecutable(t)
cfg.Browser.Cores = []config.BrowserCore{
{
CoreId: "core-open-url-test",
CoreName: "Open URL Test Core",
CorePath: exePath,
IsDefault: true,
},
}
app := NewApp("")
app.config = cfg
app.browserMgr = browser.NewManager(cfg, t.TempDir())
app.browserMgr.Profiles = make(map[string]*BrowserProfile)
app.browserMgr.BrowserProcesses = make(map[string]*exec.Cmd)
return app, exePath
}
func createFakeBrowserExecutable(t *testing.T) string {
t.Helper()
candidates := browser.CoreExecutableCandidates()
if len(candidates) == 0 {
t.Fatal("no core executable candidates available")
}
baseDir := t.TempDir()
exePath := filepath.Join(baseDir, filepath.FromSlash(candidates[0]))
if err := os.MkdirAll(filepath.Dir(exePath), 0o755); err != nil {
t.Fatalf("创建测试内核目录失败: %v", err)
}
mode := os.FileMode(0o644)
content := []byte("test-browser")
if goruntime.GOOS != "windows" {
mode = 0o755
content = []byte("#!/bin/sh\nexit 0\n")
}
if err := os.WriteFile(exePath, content, mode); err != nil {
t.Fatalf("写入测试内核可执行文件失败: %v", err)
}
return exePath
}