Your ChatGPT verification code
From: ChatGPT <noreply@tm.openai.com>
To: target@example.com
Hello,
Your verification code is 429792.
Please use this code to continue signing in.
Best regards
ChatGPT
package backend import ( "bytes" "encoding/json" "fmt" "io" "net" "net/http" "net/http/httptest" "os" "path/filepath" "regexp" goruntime "runtime" "strings" "testing" "time" "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"), })) } func lookupAutomationHTTPProbeNode(t *testing.T) string { t.Helper() const preferred = `D:\code\plugin\nodejs\node.exe` if _, err := os.Stat(preferred); err == nil { return preferred } return lookupAutomationTestNode(t) } func lookupAutomationHTTPProbeChrome(t *testing.T) string { t.Helper() const preferred = `C:\Program Files\Google\Chrome\Application\chrome.exe` if _, err := os.Stat(preferred); err == nil { return preferred } t.Skip("system chrome is not installed") return "" } func automationHTTPRepoRoot(t *testing.T) string { t.Helper() _, file, _, ok := goruntime.Caller(0) if !ok { t.Fatal("resolve repo root failed") } return filepath.Dir(filepath.Dir(file)) } func automationHTTPFreePort(t *testing.T) int { t.Helper() ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("allocate port failed: %v", err) } defer ln.Close() return ln.Addr().(*net.TCPAddr).Port } func prepareAutomationHTTPRuntime(appRoot string, repoRoot string, runtimeVersion string) error { repoRuntimeDir := filepath.Join(repoRoot, "data", "runtime", "automation", strings.TrimSpace(runtimeVersion)) tempRuntimeDir := filepath.Join(appRoot, "data", "runtime", "automation", strings.TrimSpace(runtimeVersion)) if _, err := os.Stat(repoRuntimeDir); err != nil { return fmt.Errorf("repo runtime not found: %w", err) } if err := os.MkdirAll(filepath.Join(tempRuntimeDir, "node_modules"), 0o755); err != nil { return err } if err := automationHTTPCopyFile( filepath.Join(repoRuntimeDir, "runner.cjs"), filepath.Join(tempRuntimeDir, "runner.cjs"), ); err != nil { return err } return automationHTTPCopyDir( filepath.Join(repoRuntimeDir, "node_modules", "playwright-core"), filepath.Join(tempRuntimeDir, "node_modules", "playwright-core"), ) } func automationHTTPCopyFile(src string, dst string) error { data, err := os.ReadFile(src) if err != nil { return err } if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil { return err } return os.WriteFile(dst, data, 0o644) } func automationHTTPCopyDir(src string, dst string) error { return filepath.Walk(src, func(path string, info os.FileInfo, walkErr error) error { if walkErr != nil { return walkErr } relativePath, err := filepath.Rel(src, path) if err != nil { return err } targetPath := filepath.Join(dst, relativePath) if info.IsDir() { return os.MkdirAll(targetPath, 0o755) } data, err := os.ReadFile(path) if err != nil { return err } if err := os.MkdirAll(filepath.Dir(targetPath), 0o755); err != nil { return err } return os.WriteFile(targetPath, data, info.Mode()) }) } func automationHTTPRequestJSON(method string, url string, payload any, target any) error { var body io.Reader if payload != nil { data, err := json.Marshal(payload) if err != nil { return err } body = bytes.NewReader(data) } req, err := http.NewRequest(method, url, body) if err != nil { return err } if payload != nil { req.Header.Set("Content-Type", "application/json") } resp, err := (&http.Client{Timeout: 120 * time.Second}).Do(req) if err != nil { return err } defer resp.Body.Close() raw, err := io.ReadAll(resp.Body) if err != nil { return err } if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("%s %s returned %d: %s", method, url, resp.StatusCode, strings.TrimSpace(string(raw))) } if target == nil { return nil } if err := json.Unmarshal(raw, target); err != nil { return fmt.Errorf("decode %s %s failed: %w; body=%s", method, url, err, string(raw)) } return nil } func automationHTTPHasScript(items []struct { ID string `json:"id"` }, scriptID string) bool { for _, item := range items { if strings.TrimSpace(item.ID) == scriptID { return true } } return false } func automationHTTPStringValue(payload map[string]any, key string) string { if payload == nil { return "" } value, _ := payload[key].(string) return strings.TrimSpace(value) } func automationHTTPMarshal(t *testing.T, value any) string { t.Helper() data, err := json.Marshal(value) if err != nil { t.Fatalf("marshal debug payload failed: %v", err) } return string(data) } const automationHTTPMailFixtureHTML = `
From: ChatGPT <noreply@tm.openai.com>
To: target@example.com
Hello,
Your verification code is 429792.
Please use this code to continue signing in.
Best regards
ChatGPT