Files
Ant-Browser/backend/automation_script_http_e2e_test.go
T

259 lines
8.9 KiB
Go

package backend
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
"ant-chrome/backend/internal/automation"
"ant-chrome/backend/internal/config"
)
type automationHTTPProfileCreateResponse struct {
OK bool `json:"ok"`
Created bool `json:"created"`
Launched bool `json:"launched"`
ProfileID string `json:"profileId"`
LaunchCode string `json:"launchCode"`
}
type automationHTTPScriptsResponse struct {
OK bool `json:"ok"`
Data struct {
Count int `json:"count"`
Items []struct {
ID string `json:"id"`
} `json:"items"`
} `json:"data"`
}
type automationHTTPRunResponse struct {
OK bool `json:"ok"`
Data struct {
Run struct {
Status string `json:"status"`
Summary string `json:"summary"`
Error string `json:"error"`
ResultText string `json:"resultText"`
} `json:"run"`
} `json:"data"`
}
type automationHTTPHookEnvelopeResponse struct {
OK bool `json:"ok"`
Status string `json:"status"`
Summary string `json:"summary"`
Result map[string]interface{} `json:"result"`
}
type automationHTTPLaunchLogsResponse struct {
OK bool `json:"ok"`
Items []json.RawMessage `json:"items"`
}
func TestAutomationScriptRunHTTPReturnsSavedMailProbeScript(t *testing.T) {
nodePath := lookupAutomationHTTPProbeNode(t)
chromePath := lookupAutomationHTTPProbeChrome(t)
repoRoot := automationHTTPRepoRoot(t)
tempRoot := t.TempDir()
cfg := config.DefaultConfig()
cfg.Logging.FileEnabled = false
cfg.LaunchServer.Port = automationHTTPFreePort(t)
cfg.Automation.Enabled = true
cfg.Automation.NodeSource = config.AutomationNodeSourceSystem
cfg.Automation.SystemNodePath = nodePath
cfg.Automation.HeadlessDefault = true
if err := cfg.Save(filepath.Join(tempRoot, "config.yaml")); err != nil {
t.Fatalf("save config failed: %v", err)
}
if err := prepareAutomationHTTPRuntime(tempRoot, repoRoot, cfg.Automation.RuntimeVersion); err != nil {
t.Fatalf("prepare runtime failed: %v", err)
}
fixtureServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = io.WriteString(w, automationHTTPMailFixtureHTML)
}))
defer fixtureServer.Close()
app := NewApp(tempRoot)
Start(app, nil)
defer Stop(app, nil)
if err := app.BrowserCoreSave(BrowserCoreInput{
CoreId: "system-chrome",
CoreName: "System Chrome",
CorePath: chromePath,
IsDefault: true,
}); err != nil {
t.Fatalf("save core failed: %v", err)
}
if err := app.BrowserCoreSetDefault("system-chrome"); err != nil {
t.Fatalf("set default core failed: %v", err)
}
baseURL, ok := app.GetLaunchServerInfo()["baseUrl"].(string)
if !ok || strings.TrimSpace(baseURL) == "" {
t.Fatalf("launch server baseUrl missing: %+v", app.GetLaunchServerInfo())
}
var createResp automationHTTPProfileCreateResponse
if err := automationHTTPRequestJSON(http.MethodPost, baseURL+"/api/profiles", map[string]any{
"profile": map[string]any{
"profileName": "mail-probe",
"launchArgs": []string{
"--headless=new",
"--disable-gpu",
"--no-first-run",
"--no-default-browser-check",
"--window-size=1440,1024",
},
},
"launchCode": "MAIL01",
}, &createResp); err != nil {
t.Fatalf("create profile via http failed: %v", err)
}
if !createResp.OK || !createResp.Created || createResp.LaunchCode != "MAIL01" {
t.Fatalf("unexpected create response: %+v", createResp)
}
savedScript, err := app.AutomationScriptSave(automation.ScriptRecord{
ID: "mail-probe-script",
Name: "测试邮件探针",
Type: "playwright-cdp",
Status: "ready",
EntryFile: "index.cjs",
ScriptText: automationHTTPMailProbeScriptText,
})
if err != nil {
t.Fatalf("save mail probe script failed: %v", err)
}
if savedScript == nil {
t.Fatalf("expected mail probe script to be saved")
}
savedScript.PublicAPI = automation.ScriptPublicAPIConfig{
Enabled: true,
Method: "POST",
Path: "mail/probe-message",
RequestMode: "params-only",
ResponseMode: "envelope",
TimeoutMs: 120000,
}
savedScript.SelectorText = fmt.Sprintf("{\n \"code\": %q\n}", createResp.LaunchCode)
if _, err := app.AutomationScriptSave(*savedScript); err != nil {
t.Fatalf("save mail probe public api config failed: %v", err)
}
var scriptsResp automationHTTPScriptsResponse
if err := automationHTTPRequestJSON(http.MethodGet, baseURL+"/api/automation/scripts", nil, &scriptsResp); err != nil {
t.Fatalf("list scripts via http failed: %v", err)
}
if !scriptsResp.OK || scriptsResp.Data.Count == 0 {
t.Fatalf("unexpected scripts response: %+v", scriptsResp)
}
if !automationHTTPHasScript(scriptsResp.Data.Items, "mail-probe-script") {
t.Fatalf("saved mail probe script missing: %+v", scriptsResp)
}
var runResp automationHTTPRunResponse
runErr := automationHTTPRequestJSON(http.MethodPost, baseURL+"/api/automation/scripts/run", map[string]any{
"scriptId": "mail-probe-script",
"selector": map[string]any{
"code": createResp.LaunchCode,
},
"params": map[string]any{
"inboxUrl": fixtureServer.URL,
"timeoutMs": 45000,
},
"timeoutMs": 120000,
}, &runResp)
if runErr != nil {
t.Fatalf("run script via http failed: %v", runErr)
}
parsed := make(map[string]any)
if text := strings.TrimSpace(runResp.Data.Run.ResultText); text != "" {
if err := json.Unmarshal([]byte(text), &parsed); err != nil {
t.Fatalf("parse run result failed: %v; result=%s", err, text)
}
if nested, ok := parsed["result"].(map[string]any); ok && len(nested) > 0 {
parsed = nested
}
}
if runResp.Data.Run.Status != "success" {
var logsResp automationHTTPLaunchLogsResponse
_ = automationHTTPRequestJSON(http.MethodGet, baseURL+"/api/launch/logs?limit=10", nil, &logsResp)
t.Fatalf("unexpected run response: status=%s summary=%s error=%s logs=%s",
runResp.Data.Run.Status,
runResp.Data.Run.Summary,
runResp.Data.Run.Error,
automationHTTPMarshal(t, logsResp),
)
}
if got := automationHTTPStringValue(parsed, "mailboxName"); got != "ChatGPT" {
t.Fatalf("unexpected mailboxName: %q parsed=%s", got, automationHTTPMarshal(t, parsed))
}
if got := automationHTTPStringValue(parsed, "senderEmail"); got != "noreply@tm.openai.com" {
t.Fatalf("unexpected senderEmail: %q parsed=%s", got, automationHTTPMarshal(t, parsed))
}
if got := automationHTTPStringValue(parsed, "recipientEmail"); got != "target@example.com" {
t.Fatalf("unexpected recipientEmail: %q parsed=%s", got, automationHTTPMarshal(t, parsed))
}
if got := automationHTTPStringValue(parsed, "verificationCode"); got != "429792" {
t.Fatalf("unexpected verificationCode: %q parsed=%s", got, automationHTTPMarshal(t, parsed))
}
if got := parsed["permissionApplied"]; got != true {
t.Fatalf("expected permissionApplied=true, got %#v parsed=%s", got, automationHTTPMarshal(t, parsed))
}
if got := automationHTTPStringValue(parsed, "permissionOrigin"); got != fixtureServer.URL {
t.Fatalf("unexpected permissionOrigin: %q parsed=%s", got, automationHTTPMarshal(t, parsed))
}
signature := automationHTTPStringValue(parsed, "signature")
if !strings.Contains(signature, "Best regards") || !strings.Contains(signature, "ChatGPT") {
t.Fatalf("unexpected signature: %q parsed=%s", signature, automationHTTPMarshal(t, parsed))
}
var hookResp automationHTTPHookEnvelopeResponse
hookErr := automationHTTPRequestJSON(http.MethodPost, baseURL+"/api/automation/hooks/mail/probe-message", map[string]any{
"params": map[string]any{
"inboxUrl": fixtureServer.URL,
},
"timeoutMs": 45000,
}, &hookResp)
if hookErr != nil {
t.Fatalf("run public hook via http failed: %v", hookErr)
}
if !hookResp.OK || hookResp.Status != "success" {
t.Fatalf("unexpected hook response: %+v", hookResp)
}
if got := automationHTTPStringValue(hookResp.Result, "verificationCode"); got != "429792" {
t.Fatalf("unexpected hook verificationCode: %q resp=%s", got, automationHTTPMarshal(t, hookResp))
}
if got := automationHTTPStringValue(hookResp.Result, "senderEmail"); got != "noreply@tm.openai.com" {
t.Fatalf("unexpected hook senderEmail: %q resp=%s", got, automationHTTPMarshal(t, hookResp))
}
t.Logf("automation http result: %s", automationHTTPMarshal(t, map[string]any{
"profileId": createResp.ProfileID,
"launchCode": createResp.LaunchCode,
"runStatus": runResp.Data.Run.Status,
"runSummary": runResp.Data.Run.Summary,
"hookStatus": hookResp.Status,
"hookSummary": hookResp.Summary,
"mailboxName": automationHTTPStringValue(parsed, "mailboxName"),
"senderEmail": automationHTTPStringValue(parsed, "senderEmail"),
"recipientEmail": automationHTTPStringValue(parsed, "recipientEmail"),
"verificationCode": automationHTTPStringValue(parsed, "verificationCode"),
"signature": signature,
"subject": automationHTTPStringValue(parsed, "subject"),
}))
}