Files
cdp_hub/internal/platform/browser/cdp_test.go
T

170 lines
5.7 KiB
Go

package browser
import (
"context"
"errors"
"net"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"chub/internal/domain"
)
func TestCDPInspectorValidatesEndpointFromProfileFile(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/json/version" {
t.Fatalf("path = %q", r.URL.Path)
}
_, _ = w.Write([]byte(`{"Browser":"Chrome/136.0.0.0"}`))
}))
defer server.Close()
parsed, err := url.Parse(server.URL)
if err != nil {
t.Fatal(err)
}
_, portText, err := net.SplitHostPort(parsed.Host)
if err != nil {
t.Fatal(err)
}
profile := t.TempDir()
if err := os.WriteFile(filepath.Join(profile, "DevToolsActivePort"), []byte(portText+"\n/devtools/browser/test\n"), 0o600); err != nil {
t.Fatal(err)
}
endpoint, err := NewCDPInspector().InspectRemoteDebugEndpoint(context.Background(), domain.BrowserChrome, profile)
if err != nil {
t.Fatalf("InspectRemoteDebugEndpoint() error = %v", err)
}
port, _ := strconv.Atoi(portText)
if endpoint.Port != port || endpoint.Browser != "Chrome/136.0.0.0" {
t.Fatalf("endpoint = %#v", endpoint)
}
direct, err := NewCDPInspector().InspectRemoteDebugPort(context.Background(), domain.BrowserChrome, port)
if err != nil || direct.Port != port {
t.Fatalf("InspectRemoteDebugPort() = %#v, %v", direct, err)
}
}
func TestCDPInspectorRejectsMismatchedAndMalformedEndpoints(t *testing.T) {
profile := t.TempDir()
if err := os.WriteFile(filepath.Join(profile, "DevToolsActivePort"), []byte("not-a-port\n/devtools/browser/test\n"), 0o600); err != nil {
t.Fatal(err)
}
if _, err := NewCDPInspector().InspectRemoteDebugEndpoint(context.Background(), domain.BrowserChrome, profile); !errors.Is(err, ErrRemoteDebugEndpointUnavailable) {
t.Fatalf("malformed endpoint error = %v", err)
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{"Browser":"Edg/136.0.0.0"}`))
}))
defer server.Close()
parsed, err := url.Parse(server.URL)
if err != nil {
t.Fatal(err)
}
_, portText, err := net.SplitHostPort(parsed.Host)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(profile, "DevToolsActivePort"), []byte(portText+"\n/devtools/browser/test\n"), 0o600); err != nil {
t.Fatal(err)
}
if _, err := NewCDPInspector().InspectRemoteDebugEndpoint(context.Background(), domain.BrowserChrome, profile); !errors.Is(err, ErrRemoteDebugEndpointUnavailable) {
t.Fatalf("mismatched endpoint error = %v", err)
}
}
func TestCDPInspectorFindsNextAvailablePort(t *testing.T) {
listener, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
start := listener.Addr().(*net.TCPAddr).Port
port, err := NewCDPInspector().FindAvailableRemoteDebugPort(context.Background(), start)
if err != nil {
t.Fatal(err)
}
if port != start+1 {
t.Fatalf("port = %d, want %d", port, start+1)
}
}
func TestCDPInspectorListsVerifiedTargetsWithoutControlEndpoints(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/json/version":
_, _ = w.Write([]byte(`{"Browser":"Chrome/136.0.0.0"}`))
case "/json/list":
_, _ = w.Write([]byte(`[
{"id":"page-1","type":"page","title":"运营后台","url":"https://user:secret@example.com/orders?token=hidden#section","webSocketDebuggerUrl":"ws://127.0.0.1/devtools/page/page-1"},
{"id":"worker-1","type":"service_worker","title":"","url":"chrome-extension://example/worker.js?key=hidden"}
]`))
default:
t.Fatalf("path = %q", r.URL.Path)
}
}))
defer server.Close()
parsed, err := url.Parse(server.URL)
if err != nil {
t.Fatal(err)
}
_, portText, err := net.SplitHostPort(parsed.Host)
if err != nil {
t.Fatal(err)
}
port, _ := strconv.Atoi(portText)
targets, err := NewCDPInspector().ListRemoteDebugTargets(context.Background(), domain.BrowserChrome, port)
if err != nil || len(targets) != 2 {
t.Fatalf("ListRemoteDebugTargets() = %#v, %v", targets, err)
}
if targets[0].ID != "page-1" || targets[0].Type != "page" || targets[0].Title != "运营后台" || targets[0].URL != "https://example.com/orders" {
t.Fatalf("first target = %#v", targets[0])
}
if targets[1].URL != "chrome-extension://example/worker.js" {
t.Fatalf("second target URL = %q", targets[1].URL)
}
}
func TestCDPInspectorRejectsMalformedOrOversizedTargetList(t *testing.T) {
for _, test := range []struct {
name string
status int
response string
}{
{name: "malformed JSON", status: http.StatusOK, response: `not-json`},
{name: "too many targets", status: http.StatusOK, response: `[` + strings.Repeat(`{"id":"page","type":"page"},`, remoteDebugTargetsMaxCount) + `{"id":"last","type":"page"}]`},
{name: "non-success status", status: http.StatusServiceUnavailable, response: `temporarily unavailable`},
} {
t.Run(test.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/json/version" {
_, _ = w.Write([]byte(`{"Browser":"Chrome/136.0.0.0"}`))
return
}
w.WriteHeader(test.status)
_, _ = w.Write([]byte(test.response))
}))
defer server.Close()
parsed, err := url.Parse(server.URL)
if err != nil {
t.Fatal(err)
}
_, portText, err := net.SplitHostPort(parsed.Host)
if err != nil {
t.Fatal(err)
}
port, _ := strconv.Atoi(portText)
if _, err := NewCDPInspector().ListRemoteDebugTargets(context.Background(), domain.BrowserChrome, port); !errors.Is(err, ErrRemoteDebugEndpointUnavailable) {
t.Fatalf("ListRemoteDebugTargets() error = %v", err)
}
})
}
}