mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
259 lines
8.9 KiB
Go
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"),
|
|
}))
|
|
}
|