Files
Ant-Browser/backend/test/launchcode/server_runtime_session_test.go
T
ant-black 70132417ad publish: 1.2.0 snapshot (f3d7ec5)
channel: master

version: 1.2.0

source-ref: D:\code\open_source\ant-chrome master

source-commit: f3d7ec5

merged-public-base: 3d264eb
2026-05-05 18:08:10 +08:00

221 lines
6.7 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 launchcode_test
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"ant-chrome/backend/internal/browser"
"ant-chrome/backend/internal/launchcode"
)
type sessionStarter struct {
mgr *browser.Manager
waitReady bool
waitErr error
lastParams launchcode.LaunchRequestParams
}
func newSessionStarter(mgr *browser.Manager, waitReady bool) *sessionStarter {
return &sessionStarter{
mgr: mgr,
waitReady: waitReady,
}
}
func (m *sessionStarter) StartInstance(profileID string) (*browser.Profile, error) {
return m.StartInstanceWithParams(profileID, launchcode.LaunchRequestParams{})
}
func (m *sessionStarter) StartInstanceWithParams(profileID string, params launchcode.LaunchRequestParams) (*browser.Profile, error) {
m.lastParams = params
m.mgr.Mutex.Lock()
defer m.mgr.Mutex.Unlock()
profile, ok := m.mgr.Profiles[profileID]
if !ok || profile == nil {
return nil, fmt.Errorf("profile not found")
}
profile.Running = true
profile.DebugReady = false
profile.DebugPort = 9666
profile.Pid = 4321
profile.RuntimeWarning = "debug pending"
profile.LastError = ""
return profile, nil
}
func (m *sessionStarter) StatusInstance(profileID string) (*browser.Profile, error) {
m.mgr.Mutex.Lock()
defer m.mgr.Mutex.Unlock()
profile, ok := m.mgr.Profiles[profileID]
if !ok || profile == nil {
return nil, fmt.Errorf("profile not found")
}
return profile, nil
}
func (m *sessionStarter) WaitInstanceDebugReady(profileID string, debugPort int, timeout time.Duration) (*browser.Profile, bool, error) {
if m.waitErr != nil {
return nil, false, m.waitErr
}
m.mgr.Mutex.Lock()
defer m.mgr.Mutex.Unlock()
profile, ok := m.mgr.Profiles[profileID]
if !ok || profile == nil {
return nil, false, fmt.Errorf("profile not found")
}
if m.waitReady {
profile.Running = true
profile.DebugReady = true
profile.DebugPort = debugPort
profile.RuntimeWarning = ""
return profile, true, nil
}
return profile, false, nil
}
func TestRuntimeSessionWaitsUntilDebugReady(t *testing.T) {
svc := newInMemoryService()
profile := &browser.Profile{
ProfileId: "runtime-session-ready",
ProfileName: "Runtime Session Ready",
}
manager := newSelectorTestManager(profile)
starter := newSessionStarter(manager, true)
if _, err := svc.SetCode(profile.ProfileId, "runtime-session-ready"); err != nil {
t.Fatalf("SetCode 失败: %v", err)
}
handler := buildTestHandlerWithManager(svc, starter, manager)
req := httptest.NewRequest(http.MethodPost, "/api/runtime/session", bytes.NewBufferString(`{
"code":"runtime-session-ready",
"timeoutMs":5000,
"launchArgs":["--window-size=1400,900"],
"startUrls":["https://example.com"],
"skipDefaultStartUrls":true
}`))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("期望 200,实际 %dbody=%s", w.Code, w.Body.String())
}
if len(starter.lastParams.LaunchArgs) != 1 || starter.lastParams.LaunchArgs[0] != "--window-size=1400,900" {
t.Fatalf("launchArgs 透传错误: %+v", starter.lastParams)
}
if len(starter.lastParams.StartURLs) != 1 || starter.lastParams.StartURLs[0] != "https://example.com" {
t.Fatalf("startUrls 透传错误: %+v", starter.lastParams)
}
if !starter.lastParams.SkipDefaultStartURLs {
t.Fatalf("skipDefaultStartUrls 透传错误: %+v", starter.lastParams)
}
var resp struct {
OK bool `json:"ok"`
Ready bool `json:"ready"`
WaitTimedOut bool `json:"waitTimedOut"`
Retryable bool `json:"retryable"`
Active bool `json:"active"`
ProfileID string `json:"profileId"`
LaunchCode string `json:"launchCode"`
DebugReady bool `json:"debugReady"`
CDPURL string `json:"cdpUrl"`
DirectDebugURL string `json:"directDebugUrl"`
}
if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
t.Fatalf("解析响应失败: %v", err)
}
if !resp.OK || !resp.Ready || resp.WaitTimedOut || resp.Retryable {
t.Fatalf("ready 响应不正确: %+v", resp)
}
if !resp.Active || !resp.DebugReady || resp.ProfileID != profile.ProfileId {
t.Fatalf("会话状态不正确: %+v", resp)
}
if resp.LaunchCode != "RUNTIME-SESSION-READY" {
t.Fatalf("launchCode 不正确: %+v", resp)
}
if resp.CDPURL == "" || resp.DirectDebugURL == "" {
t.Fatalf("应返回可接管地址: %+v", resp)
}
}
func TestRuntimeSessionReturnsAcceptedWhileDebugIsPending(t *testing.T) {
svc := newInMemoryService()
profile := &browser.Profile{
ProfileId: "runtime-session-pending",
ProfileName: "Runtime Session Pending",
}
manager := newSelectorTestManager(profile)
starter := newSessionStarter(manager, false)
if _, err := svc.SetCode(profile.ProfileId, "runtime-session-pending"); err != nil {
t.Fatalf("SetCode 失败: %v", err)
}
handler := buildTestHandlerWithManager(svc, starter, manager)
req := httptest.NewRequest(http.MethodPost, "/api/runtime/session", bytes.NewBufferString(`{
"code":"runtime-session-pending",
"timeoutMs":1000
}`))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Code != http.StatusAccepted {
t.Fatalf("期望 202,实际 %dbody=%s", w.Code, w.Body.String())
}
var resp struct {
OK bool `json:"ok"`
Ready bool `json:"ready"`
WaitTimedOut bool `json:"waitTimedOut"`
Retryable bool `json:"retryable"`
Active bool `json:"active"`
DebugReady bool `json:"debugReady"`
RuntimeWarning string `json:"runtimeWarning"`
CDPURL string `json:"cdpUrl"`
DirectDebugURL string `json:"directDebugUrl"`
}
if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
t.Fatalf("解析响应失败: %v", err)
}
if !resp.OK || resp.Ready || !resp.WaitTimedOut || !resp.Retryable {
t.Fatalf("pending 响应不正确: %+v", resp)
}
if resp.Active || resp.DebugReady {
t.Fatalf("pending 会话不应标记为 active/debugReady: %+v", resp)
}
if resp.RuntimeWarning != "debug pending" {
t.Fatalf("runtimeWarning 不正确: %+v", resp)
}
if resp.CDPURL != "" || resp.DirectDebugURL != "" {
t.Fatalf("pending 会话不应返回可接管地址: %+v", resp)
}
}
func TestRuntimeSessionRejectsMatchModeAll(t *testing.T) {
manager := newSelectorTestManager()
handler := buildTestHandlerWithManager(newInMemoryService(), newSessionStarter(manager, true), manager)
req := httptest.NewRequest(http.MethodPost, "/api/runtime/session", bytes.NewBufferString(`{"keyword":"shop","matchMode":"all"}`))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("期望 400,实际 %dbody=%s", w.Code, w.Body.String())
}
}