Files
2026-07-13 13:00:08 +08:00

621 lines
23 KiB
Go

package main
import (
"encoding/json"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"
"reasonix/internal/config"
)
func TestNormalizeBotInstallTarget(t *testing.T) {
cases := []struct {
provider string
domain string
wantProvider string
wantDomain string
}{
{provider: "lark", wantProvider: "feishu", wantDomain: "lark"},
{provider: "feishu", domain: "lark", wantProvider: "feishu", wantDomain: "lark"},
{provider: "wechat", wantProvider: "weixin", wantDomain: "weixin"},
{provider: "weixin", domain: "anything", wantProvider: "weixin", wantDomain: "weixin"},
{provider: "unknown", domain: "unknown", wantProvider: "feishu", wantDomain: "feishu"},
}
for _, tc := range cases {
gotProvider, gotDomain := normalizeBotInstallTarget(tc.provider, tc.domain)
if gotProvider != tc.wantProvider || gotDomain != tc.wantDomain {
t.Fatalf("normalizeBotInstallTarget(%q,%q) = %q,%q; want %q,%q", tc.provider, tc.domain, gotProvider, gotDomain, tc.wantProvider, tc.wantDomain)
}
}
}
func TestLarkInstallFollowsSDKDomainSwitchAndStoresSecret(t *testing.T) {
isolateDesktopUserDirs(t)
t.Cleanup(func() { _ = os.Unsetenv("LARK_BOT_APP_SECRET") })
pollCount := 0
var beginHost string
var pollHosts []string
var actions []string
withRewrittenHTTP(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/oauth/v1/app/registration" {
http.NotFound(w, r)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
switch r.Form.Get("action") {
case "begin":
beginHost = r.Header.Get("X-Test-Original-Host")
actions = append(actions, "begin")
if r.Form.Get("archetype") != "PersonalAgent" || r.Form.Get("auth_method") != "client_secret" {
http.Error(w, "wrong begin form", http.StatusBadRequest)
return
}
writeJSON(t, w, map[string]any{
"device_code": "dev-lark",
"verification_uri_complete": "https://open.feishu.cn/page/launcher?user_code=CODE",
"user_code": "CODE",
"interval": 3,
"expire_in": 300,
})
case "poll":
pollHosts = append(pollHosts, r.Header.Get("X-Test-Original-Host"))
actions = append(actions, "poll")
if r.Form.Get("device_code") != "dev-lark" {
http.Error(w, "wrong device code", http.StatusBadRequest)
return
}
pollCount++
if pollCount == 1 {
writeJSON(t, w, map[string]any{"user_info": map[string]any{"tenant_brand": "lark"}})
return
}
writeJSON(t, w, map[string]any{
"client_id": "cli-1",
"client_secret": "secret-1",
"user_info": map[string]any{"tenant_brand": "lark", "open_id": "ou-installer"},
})
default:
http.Error(w, "unknown action", http.StatusBadRequest)
}
}))
app := NewApp()
start, err := app.StartBotConnectionInstall("lark", "")
if err != nil {
t.Fatalf("StartBotConnectionInstall: %v", err)
}
if !start.OK || start.Domain != "lark" || start.InstallID == "" || start.URL == "" || start.DeviceCode != "dev-lark" {
t.Fatalf("start result = %+v, want ok lark-capable QR result", start)
}
qrURL, err := url.Parse(start.URL)
if err != nil {
t.Fatalf("start URL = %q, want valid QR URL: %v", start.URL, err)
}
query := qrURL.Query()
if query.Get("user_code") != "CODE" || query.Get("from") != "sdk" || query.Get("tp") != "sdk" || query.Get("source") != "go-sdk" {
t.Fatalf("start URL query = %v, want SDK registration QR metadata with user_code", query)
}
if qrURL.Host != "open.feishu.cn" {
t.Fatalf("start URL host = %q, want SDK Feishu launcher host", qrURL.Host)
}
pending, err := app.PollBotConnectionInstall(start.InstallID)
if err != nil {
t.Fatalf("PollBotConnectionInstall pending: %v", err)
}
if pending.Done || pending.Status != "pending" {
t.Fatalf("pending poll result = %+v, want pending domain switch", pending)
}
poll, err := app.PollBotConnectionInstall(start.InstallID)
if err != nil {
t.Fatalf("PollBotConnectionInstall: %v", err)
}
if !poll.Done {
t.Fatalf("poll result = %+v, want done", poll)
}
if poll.Connection.Provider != "feishu" || poll.Connection.Domain != "lark" || poll.Connection.ID != "feishu-lark" {
t.Fatalf("connection = %+v, want feishu-lark from tenant_brand", poll.Connection)
}
if beginHost != "accounts.feishu.cn" {
t.Fatalf("begin host = %q, want SDK Feishu accounts host", beginHost)
}
if got := strings.Join(pollHosts, ","); got != "accounts.feishu.cn,accounts.larksuite.com" {
t.Fatalf("poll hosts = %q, want Feishu poll then Lark poll", got)
}
if got := strings.Join(actions, ","); got != "begin,poll,poll" {
t.Fatalf("registration actions = %q, want SDK begin, domain switch, final poll", got)
}
if poll.Connection.WorkspaceRoot != "" {
t.Fatalf("connection workspaceRoot = %q, want empty global default", poll.Connection.WorkspaceRoot)
}
if poll.Connection.Credential.AppID != "cli-1" || poll.Connection.Credential.AppSecretEnv != "LARK_BOT_APP_SECRET" || !poll.Connection.Credential.SecretSet {
t.Fatalf("credential = %+v, want stored Lark secret", poll.Connection.Credential)
}
cfg := config.LoadForEdit(config.UserConfigPath())
if !cfg.Bot.Enabled || !cfg.Bot.Feishu.Enabled || cfg.Bot.Feishu.Domain != "lark" || cfg.Bot.Feishu.Mode != "websocket" || !cfg.Bot.Feishu.RequireMention {
t.Fatalf("saved feishu config = %+v, want enabled websocket lark with mention gating", cfg.Bot.Feishu)
}
if len(cfg.Bot.Allowlist.FeishuUsers) != 1 || cfg.Bot.Allowlist.FeishuUsers[0] != "ou-installer" {
t.Fatalf("feishu allowlist = %+v, want installer open_id", cfg.Bot.Allowlist.FeishuUsers)
}
if err := os.Unsetenv("LARK_BOT_APP_SECRET"); err != nil {
t.Fatalf("unset lark secret env: %v", err)
}
reloaded, err := config.Load()
if err != nil {
t.Fatalf("reload config: %v", err)
}
if got := os.Getenv("LARK_BOT_APP_SECRET"); got != "secret-1" {
t.Fatalf("reloaded LARK_BOT_APP_SECRET = %q, want persisted secret", got)
}
if len(reloaded.Bot.Connections) != 1 || !botConnectionView(reloaded.Bot.Connections[0]).Credential.SecretSet {
t.Fatalf("reloaded connections = %+v, want secret to survive restart", reloaded.Bot.Connections)
}
}
func TestFeishuInstallSwitchesToLarkDomainWhenTenantBrandIsLark(t *testing.T) {
isolateDesktopUserDirs(t)
t.Cleanup(func() { _ = os.Unsetenv("LARK_BOT_APP_SECRET") })
pollCount := 0
var beginHost string
var pollHosts []string
withRewrittenHTTP(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/oauth/v1/app/registration" {
http.NotFound(w, r)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
switch r.Form.Get("action") {
case "begin":
beginHost = r.Header.Get("X-Test-Original-Host")
writeJSON(t, w, map[string]any{
"device_code": "dev-feishu",
"verification_uri_complete": "https://accounts.example/verify?user_code=CODE",
"user_code": "CODE",
"interval": 3,
"expire_in": 300,
})
case "poll":
pollHosts = append(pollHosts, r.Header.Get("X-Test-Original-Host"))
if r.Form.Get("device_code") != "dev-feishu" {
http.Error(w, "wrong device code", http.StatusBadRequest)
return
}
pollCount++
if pollCount == 1 {
writeJSON(t, w, map[string]any{"user_info": map[string]any{"tenant_brand": "lark"}})
return
}
writeJSON(t, w, map[string]any{
"client_id": "cli-lark",
"client_secret": "secret-lark",
"user_info": map[string]any{"tenant_brand": "lark", "open_id": "ou-lark-installer"},
})
default:
http.Error(w, "unknown action", http.StatusBadRequest)
}
}))
app := NewApp()
start, err := app.StartBotConnectionInstall("feishu", "")
if err != nil {
t.Fatalf("StartBotConnectionInstall: %v", err)
}
if !start.OK || start.Domain != "feishu" || start.DeviceCode != "dev-feishu" {
t.Fatalf("start result = %+v, want Feishu QR result", start)
}
pending, err := app.PollBotConnectionInstall(start.InstallID)
if err != nil {
t.Fatalf("PollBotConnectionInstall pending: %v", err)
}
if pending.Done || pending.Status != "pending" {
t.Fatalf("pending poll result = %+v, want pending domain switch", pending)
}
poll, err := app.PollBotConnectionInstall(start.InstallID)
if err != nil {
t.Fatalf("PollBotConnectionInstall: %v", err)
}
if !poll.Done || poll.Connection.Domain != "lark" || poll.Connection.Credential.AppSecretEnv != "LARK_BOT_APP_SECRET" {
t.Fatalf("poll result = %+v, want stored Lark connection after domain switch", poll)
}
if beginHost != "accounts.feishu.cn" {
t.Fatalf("begin host = %q, want Feishu accounts host", beginHost)
}
if got := strings.Join(pollHosts, ","); got != "accounts.feishu.cn,accounts.larksuite.com" {
t.Fatalf("poll hosts = %q, want Feishu poll then Lark poll", got)
}
}
func TestFeishuInstallStoresFeishuSecretAndSurvivesReload(t *testing.T) {
isolateDesktopUserDirs(t)
t.Cleanup(func() { _ = os.Unsetenv("FEISHU_BOT_APP_SECRET") })
var hosts []string
withRewrittenHTTP(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/oauth/v1/app/registration" {
http.NotFound(w, r)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
hosts = append(hosts, r.Form.Get("action")+":"+r.Header.Get("X-Test-Original-Host"))
switch r.Form.Get("action") {
case "begin":
writeJSON(t, w, map[string]any{
"device_code": "dev-feishu",
"verification_uri_complete": "https://accounts.example/verify?user_code=CODE",
"user_code": "CODE",
"interval": 3,
"expire_in": 300,
})
case "poll":
writeJSON(t, w, map[string]any{
"client_id": "cli-feishu",
"client_secret": "secret-feishu",
"user_info": map[string]any{"tenant_brand": "feishu", "open_id": "ou-feishu-installer"},
})
default:
http.Error(w, "unknown action", http.StatusBadRequest)
}
}))
app := NewApp()
start, err := app.StartBotConnectionInstall("feishu", "")
if err != nil {
t.Fatalf("StartBotConnectionInstall: %v", err)
}
if !start.OK || start.Domain != "feishu" || start.InstallID == "" {
t.Fatalf("start result = %+v, want ok Feishu QR result", start)
}
poll, err := app.PollBotConnectionInstall(start.InstallID)
if err != nil {
t.Fatalf("PollBotConnectionInstall: %v", err)
}
if !poll.Done {
t.Fatalf("poll result = %+v, want done", poll)
}
if poll.Connection.Provider != "feishu" || poll.Connection.Domain != "feishu" || poll.Connection.ID != "feishu-feishu" {
t.Fatalf("connection = %+v, want feishu-feishu", poll.Connection)
}
if poll.Connection.Credential.AppID != "cli-feishu" || poll.Connection.Credential.AppSecretEnv != "FEISHU_BOT_APP_SECRET" || !poll.Connection.Credential.SecretSet {
t.Fatalf("credential = %+v, want stored Feishu secret", poll.Connection.Credential)
}
if got := strings.Join(hosts, ","); got != "begin:accounts.feishu.cn,poll:accounts.feishu.cn" {
t.Fatalf("registration hosts = %q, want Feishu begin and poll", got)
}
cfg := config.LoadForEdit(config.UserConfigPath())
if !cfg.Bot.Enabled || !cfg.Bot.Feishu.Enabled || cfg.Bot.Feishu.Domain != "feishu" || cfg.Bot.Feishu.AppID != "cli-feishu" {
t.Fatalf("saved feishu config = %+v, want enabled Feishu websocket config", cfg.Bot.Feishu)
}
if len(cfg.Bot.Allowlist.FeishuUsers) != 1 || cfg.Bot.Allowlist.FeishuUsers[0] != "ou-feishu-installer" {
t.Fatalf("feishu allowlist = %+v, want installer open_id", cfg.Bot.Allowlist.FeishuUsers)
}
if err := os.Unsetenv("FEISHU_BOT_APP_SECRET"); err != nil {
t.Fatalf("unset feishu secret env: %v", err)
}
reloaded, err := config.Load()
if err != nil {
t.Fatalf("reload config: %v", err)
}
if got := os.Getenv("FEISHU_BOT_APP_SECRET"); got != "secret-feishu" {
t.Fatalf("reloaded FEISHU_BOT_APP_SECRET = %q, want persisted secret", got)
}
if len(reloaded.Bot.Connections) != 1 || !botConnectionView(reloaded.Bot.Connections[0]).Credential.SecretSet {
t.Fatalf("reloaded connections = %+v, want secret to survive restart", reloaded.Bot.Connections)
}
}
func TestWeixinInstallStoresSavedAccountAndConnection(t *testing.T) {
isolateDesktopUserDirs(t)
withRewrittenHTTP(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/ilink/bot/get_bot_qrcode":
if r.URL.Query().Get("bot_type") != "3" {
http.Error(w, "missing bot type", http.StatusBadRequest)
return
}
writeJSON(t, w, map[string]any{
"qrcode": "qr-weixin",
"qrcode_img_content": "data:image/png;base64,abc",
})
case "/ilink/bot/get_qrcode_status":
if r.URL.Query().Get("qrcode") != "qr-weixin" {
http.Error(w, "wrong qr", http.StatusBadRequest)
return
}
writeJSON(t, w, map[string]any{
"status": "confirmed",
"ilink_bot_id": "weixin-account",
"bot_token": "token-1",
"ilink_user_id": "user-1",
"baseurl": "https://ilinkai.weixin.qq.com",
})
default:
http.NotFound(w, r)
}
}))
app := NewApp()
start, err := app.StartBotConnectionInstall("weixin", "")
if err != nil {
t.Fatalf("StartBotConnectionInstall: %v", err)
}
if !start.OK || start.Provider != "weixin" || start.Domain != "weixin" || start.URL != "data:image/png;base64,abc" || start.DeviceCode != "qr-weixin" {
t.Fatalf("start result = %+v, want weixin QR result", start)
}
poll, err := app.PollBotConnectionInstall(start.InstallID)
if err != nil {
t.Fatalf("PollBotConnectionInstall: %v", err)
}
if !poll.Done {
t.Fatalf("poll result = %+v, want done", poll)
}
if poll.Connection.Provider != "weixin" || poll.Connection.Domain != "weixin" || poll.Connection.Credential.AccountID != "weixin-account" {
t.Fatalf("connection = %+v, want weixin account connection", poll.Connection)
}
if poll.Connection.WorkspaceRoot != "" {
t.Fatalf("connection workspaceRoot = %q, want empty global default", poll.Connection.WorkspaceRoot)
}
if poll.Connection.Credential.TokenEnv != "WEIXIN_BOT_TOKEN" || !poll.Connection.Credential.SecretSet {
t.Fatalf("credential = %+v, want saved account to count as configured token", poll.Connection.Credential)
}
cfg := config.LoadForEdit(config.UserConfigPath())
if !cfg.Bot.Enabled || !cfg.Bot.Weixin.Enabled || cfg.Bot.Weixin.AccountID != "weixin-account" || cfg.Bot.Weixin.TokenEnv != "WEIXIN_BOT_TOKEN" {
t.Fatalf("saved weixin config = %+v, want enabled saved account", cfg.Bot.Weixin)
}
if len(cfg.Bot.Allowlist.WeixinUsers) != 1 || cfg.Bot.Allowlist.WeixinUsers[0] != "user-1" {
t.Fatalf("weixin allowlist = %+v, want installer user id", cfg.Bot.Allowlist.WeixinUsers)
}
reloaded, err := config.Load()
if err != nil {
t.Fatalf("reload config: %v", err)
}
if len(reloaded.Bot.Connections) != 1 {
t.Fatalf("reloaded connections = %+v, want saved weixin connection", reloaded.Bot.Connections)
}
reloadedConnection := botConnectionView(reloaded.Bot.Connections[0])
if reloadedConnection.Credential.AccountID != "weixin-account" || !reloadedConnection.Credential.SecretSet {
t.Fatalf("reloaded credential = %+v, want saved weixin account to survive restart", reloadedConnection.Credential)
}
}
func TestFeishuRegistrationQRCodeURLAddsSDKMetadata(t *testing.T) {
qrURL, err := feishuRegistrationQRCodeURL("https://open.larksuite.com/page/launcher?user_code=ABCD-1234&source=old")
if err != nil {
t.Fatalf("feishuRegistrationQRCodeURL: %v", err)
}
parsed, err := url.Parse(qrURL)
if err != nil {
t.Fatalf("parse QR URL: %v", err)
}
query := parsed.Query()
if query.Get("user_code") != "ABCD-1234" {
t.Fatalf("user_code = %q, want preserved code", query.Get("user_code"))
}
if query.Get("from") != "sdk" || query.Get("tp") != "sdk" || query.Get("source") != "go-sdk" {
t.Fatalf("query = %v, want SDK registration metadata", query)
}
}
func TestDiagnoseBotConnectionBuildsReportDetailForMissingSecret(t *testing.T) {
isolateDesktopUserDirs(t)
t.Setenv("FEISHU_BOT_APP_SECRET_PRIVATE", "")
app := NewApp()
if _, err := app.upsertBotConnection(config.BotConnectionConfig{
ID: "feishu-lark",
Provider: "feishu",
Domain: "lark",
Label: "Lark",
Enabled: true,
Status: "connected",
WorkspaceRoot: "/Users/alice/work/reasonix",
Credential: config.BotConnectionCredential{
AppID: "cli-private",
AppSecretEnv: "FEISHU_BOT_APP_SECRET_PRIVATE",
},
SessionMappings: []config.BotConnectionSessionMapping{{
RemoteID: "ou-private",
SessionID: "session-private",
Scope: "project",
WorkspaceRoot: "/Users/alice/work/reasonix",
}},
}, nil); err != nil {
t.Fatalf("upsert connection: %v", err)
}
diag, err := app.DiagnoseBotConnection("feishu-lark")
if err != nil {
t.Fatalf("DiagnoseBotConnection: %v", err)
}
if diag.Status != "warning" || diag.Phase != "credential" || diag.Code != "secret_missing" || diag.ReportKind != "bot" || diag.ReportDetail == "" {
t.Fatalf("diagnostic = %+v, want warning credential report", diag)
}
for _, leaked := range []string{"FEISHU_BOT_APP_SECRET_PRIVATE", "/Users/alice", "ou-private", "session-private"} {
if strings.Contains(diag.ReportDetail, leaked) {
t.Fatalf("diagnostic report leaked %q in %s", leaked, diag.ReportDetail)
}
}
var payload frontendCrashPayload
if err := json.Unmarshal([]byte(diag.ReportDetail), &payload); err != nil {
t.Fatalf("report detail is not structured JSON: %v", err)
}
if payload.Kind != "bot" || payload.Source != "bot.runtime" || payload.Label != "bot.feishu.lark.credential" {
t.Fatalf("payload = %+v, want bot runtime credential label", payload)
}
for _, want := range []string{
"app_secret_env_configured: true",
"secret_available: false",
"workspace_scope: project",
"session_mappings: 1",
"summary: required bot credential is not available",
} {
if !strings.Contains(payload.Message, want) {
t.Fatalf("payload message = %q, want it to contain %q", payload.Message, want)
}
}
report, err := crashReportFromDetail(diag.ReportKind, diag.ReportDetail)
if err != nil {
t.Fatalf("crashReportFromDetail: %v", err)
}
if report.Kind != "bot" || report.Source != "bot.runtime" || report.ErrorType != "BotConnectionDiagnostic" {
t.Fatalf("report = %+v, want accepted bot report", report)
}
}
func TestBotConnectionSendFailureReportRedactsEnvNames(t *testing.T) {
conn := config.BotConnectionConfig{
ID: "feishu-lark",
Provider: "feishu",
Domain: "lark",
Label: "Lark",
Enabled: true,
Status: "connected",
Credential: config.BotConnectionCredential{
AppSecretEnv: "FEISHU_BOT_APP_SECRET_PRIVATE",
},
}
diag := botConnectionDiagnostic(&conn, conn.ID, "error", "send", "test_send_failed", "feishu app_id or FEISHU_BOT_APP_SECRET_PRIVATE is not configured", true)
if diag.ReportKind != "bot" || diag.ReportDetail == "" {
t.Fatalf("diagnostic = %+v, want reportable bot diagnostic", diag)
}
if strings.Contains(diag.ReportDetail, "FEISHU_BOT_APP_SECRET_PRIVATE") {
t.Fatalf("diagnostic report leaked env name in %s", diag.ReportDetail)
}
var payload frontendCrashPayload
if err := json.Unmarshal([]byte(diag.ReportDetail), &payload); err != nil {
t.Fatalf("report detail is not structured JSON: %v", err)
}
if !strings.Contains(payload.ErrorMessage, "[redacted-env]") {
t.Fatalf("payload errorMessage = %q, want redacted env marker", payload.ErrorMessage)
}
}
func TestDiagnoseWeixinConnectionDetectsMissingSavedAccountWithoutTokenEnv(t *testing.T) {
isolateDesktopUserDirs(t)
app := NewApp()
if _, err := app.upsertBotConnection(config.BotConnectionConfig{
ID: "weixin-weixin",
Provider: "weixin",
Domain: "weixin",
Label: "微信",
Enabled: true,
Status: "connected",
Credential: config.BotConnectionCredential{
AccountID: "missing-account",
},
}, nil); err != nil {
t.Fatalf("upsert connection: %v", err)
}
diag, err := app.DiagnoseBotConnection("weixin-weixin")
if err != nil {
t.Fatalf("DiagnoseBotConnection: %v", err)
}
if diag.Status != "warning" || diag.Phase != "credential" || diag.Code != "secret_missing" || diag.ReportKind != "bot" || diag.ReportDetail == "" {
t.Fatalf("diagnostic = %+v, want missing local credential warning", diag)
}
if strings.Contains(diag.ReportDetail, "missing-account") {
t.Fatalf("diagnostic report leaked account id in %s", diag.ReportDetail)
}
}
func TestRememberBotConnectionRemoteStoresStableScope(t *testing.T) {
isolateDesktopUserDirs(t)
app := NewApp()
if _, err := app.upsertBotConnection(config.BotConnectionConfig{
ID: "feishu-lark",
Provider: "feishu",
Domain: "lark",
Label: "kun",
Enabled: true,
Status: "connected",
}, nil); err != nil {
t.Fatalf("upsert global connection: %v", err)
}
if err := app.rememberBotConnectionRemote("feishu-lark", "ou_global"); err != nil {
t.Fatalf("remember global remote: %v", err)
}
cfg := config.LoadForEdit(config.UserConfigPath())
if got := cfg.Bot.Connections[0].SessionMappings[0]; got.Scope != "global" || got.WorkspaceRoot != "" || got.RemoteID != "ou_global" {
t.Fatalf("global mapping = %+v, want scope=global without workspace", got)
}
if _, err := app.upsertBotConnection(config.BotConnectionConfig{
ID: "weixin-project",
Provider: "weixin",
Domain: "weixin",
Label: "project",
Enabled: true,
Status: "connected",
WorkspaceRoot: "/tmp/reasonix-project",
}, nil); err != nil {
t.Fatalf("upsert project connection: %v", err)
}
if err := app.rememberBotConnectionRemote("weixin-project", "wxid_project"); err != nil {
t.Fatalf("remember project remote: %v", err)
}
cfg = config.LoadForEdit(config.UserConfigPath())
var projectMapping config.BotConnectionSessionMapping
for _, conn := range cfg.Bot.Connections {
if conn.ID == "weixin-project" && len(conn.SessionMappings) == 1 {
projectMapping = conn.SessionMappings[0]
}
}
if projectMapping.Scope != "project" || projectMapping.WorkspaceRoot != "/tmp/reasonix-project" || projectMapping.RemoteID != "wxid_project" {
t.Fatalf("project mapping = %+v, want project scope and workspace", projectMapping)
}
}
func writeJSON(t *testing.T, w http.ResponseWriter, value any) {
t.Helper()
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(value); err != nil {
t.Fatalf("write json: %v", err)
}
}
func withRewrittenHTTP(t *testing.T, handler http.Handler) {
t.Helper()
server := httptest.NewServer(handler)
target, err := url.Parse(server.URL)
if err != nil {
t.Fatal(err)
}
previous := http.DefaultTransport
http.DefaultTransport = rewriteHTTPTransport{target: target, next: previous}
t.Cleanup(func() {
http.DefaultTransport = previous
server.Close()
})
}
type rewriteHTTPTransport struct {
target *url.URL
next http.RoundTripper
}
func (r rewriteHTTPTransport) RoundTrip(req *http.Request) (*http.Response, error) {
clone := req.Clone(req.Context())
clone.Header.Set("X-Test-Original-Host", req.URL.Host)
clone.URL.Scheme = r.target.Scheme
clone.URL.Host = r.target.Host
clone.Host = r.target.Host
if r.next == nil {
r.next = http.DefaultTransport
}
clone.URL.Path = "/" + strings.TrimLeft(clone.URL.Path, "/")
return r.next.RoundTrip(clone)
}