fix: skip tui for recommend login

This commit is contained in:
shangguanxuan.sgx committed 2026-06-17 18:47:51 +08:00
1 parent 7720e3ec9b
commit d2fd7e00b5
3 files changed
+151 -55

No files matched your search

+22 -11
View File
@@ -55,16 +55,26 @@
"steps": [
"User runs dws auth login --recommend.",
"CLI completes OAuth loopback or device login, lets the user choose the DingTalk organization through the normal login flow, and saves the token.",
"After the login identity and organization are settled, interactive CLI shows the Feishu-like terminal authorization guide: 选择操作, then 授权范围选择.",
"CLI calls pat.batch_plan with recommend=true by default, or recommend=false when the user selected 全部授权, grantType=permanent, caller=auth_login_recommend, dryRun=true.",
"CLI then shows the product-domain selector with all product lines selected by default; user can toggle, filter, or confirm.",
"CLI re-plans with the selected productCodes before starting the grant flow.",
"CLI does not show the product-domain TUI for --recommend.",
"CLI calls pat.batch_plan with recommend=true, grantType=permanent, caller=auth_login_recommend, dryRun=true.",
"PAT core loads the Diamond-driven recommended set, excludes non-recommended medium-risk scopes from the default plan, and returns selectedScopes plus warnings/skipped scopes.",
"CLI calls pat.batch_grant with scopes=selectedScopes, startFlow=true, noWait=true, caller=auth_login_recommend.",
"PAT core creates a single batch Device Code Flow and returns PAT_BATCH_AUTH_PENDING with flowId/userCode/uri.",
"CLI opens uri, polls flow status, and retries the original grant after approval."
]
},
{
"name": "loginInteractiveTuiWithoutRecommend",
"steps": [
"User runs dws auth login without --recommend in an interactive terminal.",
"CLI completes OAuth loopback or device login, lets the user choose the DingTalk organization through the normal login flow, and saves the token.",
"After the login identity and organization are settled, CLI shows the Feishu-like terminal authorization guide: 选择操作, then 授权范围选择.",
"CLI calls pat.batch_plan with recommend=true when the user selected 推荐授权, or recommend=false when the user selected 全部授权.",
"CLI shows the product-domain selector with all product lines selected by default; user can toggle, filter, or confirm.",
"CLI re-plans with the selected productCodes before starting the grant flow.",
"Without --yes, CLI calls pat.batch_grant with scopes=selectedScopes, startFlow=true, noWait=true, then opens/polls the returned authorization URL."
]
},
{
"name": "batchChmodWithoutYes",
"steps": [
@@ -212,7 +222,7 @@
"branch": "codex/dws-auth-login-recommend",
"commit": "2264743e78f71d3664b21d90e3a6a36f383beed7",
"prepubPackage": "pushed to GitHub branch codex/dws-auth-login-recommend with CLI commit 2264743e78f71d3664b21d90e3a6a36f383beed7; rebuilt local default /usr/local/bin/dws -> /Users/xuan/bin/dws from current source; local go test ./internal/app ./internal/pat ./internal/errors ./internal/auth and go test ./test/unit passed. Still blocked_no_aone_app_for_current_github_repo_lippi_dws_release_uses_gitlab_release_repo for formal prepub package evidence.",
"prepubFunctionalVerification": "local_cli_tui_contract_ok: interactive default dws auth login --recommend now follows the terminal usage chain: 选择操作, 选择授权范围, then default-all-selected product domains before grant. 推荐授权 uses recommend=true; 全部授权 replans selected product domains with recommend=false. explicit --format json and any non-TTY stdin/stdout/stderr keep structured non-interactive mode. local_yes_contract_ok: dws auth login --recommend --yes skips the TUI/browser flow and calls pat.batch_grant without startFlow/noWait, preserving direct recommended direct grant behavior. current_head_e2e_pending_server_redeploy"
"prepubFunctionalVerification": "local_cli_tui_contract_ok: dws auth login --recommend now skips terminal TUI/product-domain selection and directly uses server recommend=true selectedScopes. Interactive default dws auth login without --recommend now follows the terminal usage chain after login/org selection: 选择操作, 选择授权范围, then default-all-selected product domains before grant. 推荐授权 uses recommend=true; 全部授权 replans selected product domains with recommend=false. explicit --format json and any non-TTY stdin/stdout/stderr keep structured non-interactive mode. local_yes_contract_ok: dws auth login --recommend --yes skips the TUI/browser flow and calls pat.batch_grant without startFlow/noWait, preserving direct recommended direct grant behavior. current_head_e2e_pending_server_redeploy"
},
{
"repo": "lippi-open-mcpserver-gw",
@@ -354,6 +364,7 @@
"2026-06-17 CLI --yes follow-up: commit 3ec35e30e828b088a8e9415d76804f56feed6354 fixes dws auth login --recommend --yes to behave like batch auto-confirm: resolveAuthLoginConfig reads inherited --yes, recommend human TUI is skipped, and RunLoginRecommendAuthorizationWithOptions calls pat.batch_grant without startFlow/noWait so no browser authorization page is created. No --yes keeps the previous TUI plus batch authorization page flow. Verification passed: go test ./internal/pat -run TestLoginRecommendAuthorization; go test ./internal/app -run TestAuthLoginRecommendSelectorRespectsExplicitFormat|TestLoginRecommendProductLabelMatchesTUITarget|TestResolveAuthLoginConfigReadsInheritedYes; go test ./internal/app ./internal/pat ./internal/errors; go test ./test/unit; /usr/local/bin/dws -> /Users/xuan/bin/dws rebuilt from this commit.",
"2026-06-17 CLI TUI chain correction: commit 2264743e78f71d3664b21d90e3a6a36f383beed7 changes TUI from a single product selector into the intended terminal usage chain from the MGDone screenshots. Interactive dws auth login --recommend now shows 选择操作 (直接使用CLI / 一键配置智能体应用 / 手动输入应用凭证), then 选择授权范围 (推荐授权 / 全部授权), then the default-all-selected 选择要授权的业务域 product-domain multiselect. 推荐授权 keeps recommend=true; 全部授权 replans with recommend=false for selected product domains. Complete runtime AppKey/AppSecret overrides now skip MCP clientId fetch so manual credentials are real rather than decorative. Verification passed: go test ./internal/pat -run TestLoginRecommendAuthorization; go test ./internal/app -run TestAuthLoginRecommendSelectorRespectsExplicitFormat|TestLoginRecommendProductLabelMatchesTUITarget|TestResolveAuthLoginConfigReadsInheritedYes; go test ./internal/auth -run TestDeviceFlow|TestOAuth|TestCredential|TestReset|TestAppConfig|TestClient; go test ./internal/app ./internal/pat ./internal/errors ./internal/auth; go test ./test/unit; /usr/local/bin/dws -> /Users/xuan/bin/dws rebuilt from this commit.",
"2026-06-17 CLI TUI sequence correction: commit 537719c677a9c79d8c45115c7bbbb29fff8dfe1f moves the terminal authorization guide into runRecommend, so dws auth login --recommend first completes DingTalk login, organization selection, and token persistence, then starts 选择操作 -> 选择授权范围 -> default-all-selected 选择要授权的业务域. A new app-layer regression test asserts the token is already readable before any TUI selector runs. Verification passed: go test ./internal/app -run TestAuthLoginRecommendTUIRunsAfterLoginTokenSaved|TestAuthLoginRecommendSelectorRespectsExplicitFormat|TestLoginRecommendProductLabelMatchesTUITarget|TestResolveAuthLoginConfigReadsInheritedYes; go test ./internal/pat -run TestLoginRecommendAuthorization; go test ./internal/app ./internal/pat ./internal/errors ./internal/auth; go test ./test/unit; go build -o /Users/xuan/bin/dws ./cmd/main.go; /usr/local/bin/dws version --format json reports version dev, edition open.",
"2026-06-17 CLI TUI condition correction: dws auth login --recommend no longer shows 选择操作, 授权范围选择, or product-domain TUI. It directly calls pat.batch_plan recommend=true and grants the server-selected recommended scopes. The post-login TUI now belongs only to interactive dws auth login without --recommend, after login/org selection/token persistence. Verification target: TestAuthLoginRecommendSkipsPostLoginTUI and TestAuthLoginWithoutRecommendTUIRunsAfterLoginTokenSaved.",
"2026-06-17 MGDone MCP receipt: Cursor mastergo-magic-mcp server was started from /Users/xuan/.cursor/mcp.json with token redacted. tools/list exposed mcp__getDesignSections/mcp__getDsl/mcp__getDesignSvgs/mcp__getDesignTexts. mcp__getDesignSections for fileId=187681481694834, layerId=74:36608 returned design-sections API not available; fallback mcp__getDsl with the same page_id returned an empty nodes/components DSL. Therefore current terminal implementation uses the visible MGDone screenshot target for TUI text/label/control behavior and records that a true layer_id/source_layer_id is still needed for pixel-level DSL extraction.",
"2026-06-17 frontend account-row fix: commit f33e349b2c29c7ba139e48fdfbdd70759ce3a4c4 makes the authorization account row prefer userAvatar/userName before orgLogo/orgName to avoid mixed org icon + user name rendering. Verification: npm run build passed with existing bundle-size warnings only.",
"Frontend O2 non-production task 149432767 succeeded for commit f33e349b2c29c7ba139e48fdfbdd70759ce3a4c4, flow 114592657, version 5.219.0, pub_env daily, build id 53073038, assetCount 203. CDN bundle contains userAvatar|| and no longer contains the old orgLogo|| account-row priority; old title 可授权的权限(已选 remains absent.",
@@ -478,12 +489,12 @@
"/var/folders/hz/2sm0ql493cv6jngtjk7xsby00000gp/T/codex-clipboard-16c11a9e-7ae7-4703-aa66-4251f7b59f16.png",
"/var/folders/hz/2sm0ql493cv6jngtjk7xsby00000gp/T/codex-clipboard-5aff04a6-9a45-4ba5-8741-b7abcd4f2303.png"
],
"usageGuide": "Interactive dws auth login --recommend starts the terminal authorization guide only after OAuth/device login, DingTalk organization selection, login summary, and token persistence are complete. The first guide screen is 使用引导 / 选择操作 with options 直接使用CLI, 一键配置智能体应用, 手动输入应用凭证. 直接使用CLI continues directly to authorization setup; manual credentials collect AppKey/AppSecret for subsequent runtime credential use and do not replace the already selected login organization.",
"usageGuide": "Interactive dws auth login without --recommend starts the terminal authorization guide only after OAuth/device login, DingTalk organization selection, login summary, and token persistence are complete. dws auth login --recommend skips this TUI entirely and uses the server recommended set. The first guide screen for the non-recommend TUI is 使用引导 / 选择操作 with options 直接使用CLI, 一键配置智能体应用, 手动输入应用凭证. 直接使用CLI continues directly to authorization setup; manual credentials collect AppKey/AppSecret for subsequent runtime credential use and do not replace the already selected login organization.",
"authorizationScopeSelector": "After the usage guide, the TUI presents 授权范围选择 with 推荐授权 and 全部授权. 推荐授权 is the default safe path and calls pat.batch_plan with recommend=true. 全部授权 replans the selected product domains with recommend=false so PAT core/server risk handling decides pending/grant behavior.",
"loginRecommendSelector": "After the login is already complete and the usage/scope guides have run, interactive dws auth login --recommend presents 选择要授权的业务域 with all product domains selected by default.",
"loginRecommendSelector": "After the login is already complete and the usage/scope guides have run, interactive dws auth login without --recommend presents 选择要授权的业务域 with all product domains selected by default. The --recommend flag does not show this selector.",
"rowFormat": "Product rows follow the MGDone terminal target: selected checkbox, left-aligned product code, Chinese product name, and short operation summary separated by a plain hyphen, such as approval 审批 - 审批实例,审批模板,审批任务管理.",
"controls": "TUI must support space toggle, up/down navigation, slash filter, enter confirm, and ctrl+a select all through the underlying selector component. Description text is 空格选择 回车确认 while the component renders the detailed key hints.",
"nextStep": "Without --yes, after product confirmation the CLI prints/opens the complete encoded authorization URL returned by the server and waits for browser approval. With --yes, the CLI skips the product TUI and web authorization page and directly grants the service-selected safe scopes."
"nextStep": "Without --yes, --recommend directly prints/opens the complete encoded authorization URL returned by the server for the recommended scope set. In the non-recommend TUI path, the CLI prints/opens that URL after product confirmation. With --yes, the CLI skips TUI and web authorization page and directly grants the service-selected safe scopes when a direct-grant path is available."
},
"securityRules": [
"Gateway derives orgId and uid from trusted token/header only.",
@@ -497,8 +508,8 @@
"acceptanceCriteria": [
"Prepub package/build succeeds for every changed repo: dingtalk-workspace-cli, lippi-open-mcpserver-gw, lippi-pat-core-wukong-capability, lippi-open-app-dev, and lippi-open-dev-front.",
"Prepub deployed versions include the current branch commits for CLI, gateway, PAT core, BFF, and frontend.",
"On prepub, dws auth login --recommend opens the batch authorization URL after successful login.",
"On prepub, interactive dws auth login --recommend first completes DingTalk login and organization selection, then displays the terminal authorization chain: 选择操作, 授权范围选择, and default-all-selected product-domain TUI before opening the authorization URL.",
"On prepub, dws auth login --recommend opens the batch authorization URL after successful login without showing terminal product-domain selection.",
"On prepub, interactive dws auth login without --recommend first completes DingTalk login and organization selection, then displays the terminal authorization chain: 选择操作, 授权范围选择, and default-all-selected product-domain TUI before opening the authorization URL.",
"On prepub, dws auth login --recommend --yes does not open the authorization page and grants the service-selected recommended scopes directly.",
"On prepub, dws pat chmod --recommend --product calendar without --yes opens the authorization URL.",
"On prepub, dws pat chmod --recommend --product calendar --yes preserves the existing direct grant behavior.",
@@ -515,5 +526,5 @@
"muskChiefOperator": "Review the product flow for speed, minimum surface area, and user clarity.",
"computerUse": "Verify prepub/front-end page only after frontend repo and build credentials are available."
},
"updatedAt": "2026-06-17T17:52:22+08:00"
"updatedAt": "2026-06-17T18:37:31+08:00"
}
+37 -18
View File
@@ -114,8 +114,8 @@ func newAuthLoginCommand(patCaller edition.ToolCaller) *cobra.Command {
configDir := defaultConfigDir()
var tokenData *authpkg.TokenData
format, _ := cmd.Root().PersistentFlags().GetString("format")
recommendHumanMode := !cfg.Yes && authLoginShouldUseRecommendHumanMode(cmd, format, cfg.Recommend)
recommendScopeMode := pat.LoginRecommendScopeRecommended
postLoginTUIMode := !cfg.Yes && authLoginShouldUsePostLoginTUIMode(cmd, format, cfg.Recommend)
humanAuthMode := !cfg.Yes && authLoginShouldUseHumanAuthorizationMode(cmd, format, cfg.Recommend || postLoginTUIMode)
switch {
case strings.TrimSpace(cfg.Token) != "":
@@ -153,11 +153,12 @@ func newAuthLoginCommand(patCaller edition.ToolCaller) *cobra.Command {
clearCompatCache()
w := cmd.OutOrStdout()
runRecommend := func() error {
if !cfg.Recommend {
runPostLoginAuthorization := func() error {
if !cfg.Recommend && !postLoginTUIMode {
return nil
}
if recommendHumanMode {
recommendScopeMode := pat.LoginRecommendScopeRecommended
if postLoginTUIMode {
action, err := authLoginGuideActionSelector()
if err != nil {
return err
@@ -171,13 +172,13 @@ func newAuthLoginCommand(patCaller edition.ToolCaller) *cobra.Command {
}
}
opts := pat.LoginRecommendOptions{Confirmed: cfg.Yes, ScopeMode: recommendScopeMode}
if recommendHumanMode {
if postLoginTUIMode {
opts.ProductSelector = func(products []pat.LoginRecommendProduct) ([]string, error) {
return loginRecommendProductSelector(products)
}
}
retryFormat := format
if recommendHumanMode {
if humanAuthMode {
retryFormat = "table"
}
run := func(ctx context.Context) error {
@@ -197,8 +198,8 @@ func newAuthLoginCommand(patCaller edition.ToolCaller) *cobra.Command {
}
// Check if JSON output is requested
if strings.EqualFold(strings.TrimSpace(format), "json") && !recommendHumanMode {
if err := runRecommend(); err != nil {
if strings.EqualFold(strings.TrimSpace(format), "json") && !humanAuthMode {
if err := runPostLoginAuthorization(); err != nil {
return err
}
return writeAuthLoginJSON(w, tokenData, cfg.Force)
@@ -226,7 +227,7 @@ func newAuthLoginCommand(patCaller edition.ToolCaller) *cobra.Command {
}
}
fmt.Fprintf(w, "Token 将自动刷新,无需重复登录\n")
return runRecommend()
return runPostLoginAuthorization()
},
}
cmd.Flags().String("token", "", "Access token")
@@ -759,20 +760,38 @@ func selectLoginRecommendProducts(products []pat.LoginRecommendProduct) ([]strin
return selected, nil
}
func authLoginShouldShowRecommendSelector(cmd *cobra.Command, format string) bool {
return authLoginShouldUseRecommendHumanModeForTerminal(cmd, format, true, authLoginInteractiveTerminal())
func authLoginShouldShowPostLoginTUI(cmd *cobra.Command, format string, recommend bool) bool {
return authLoginShouldUsePostLoginTUIModeForTerminal(cmd, format, recommend, authLoginInteractiveTerminal())
}
func authLoginShouldShowRecommendSelectorForTerminal(cmd *cobra.Command, format string, interactive bool) bool {
return authLoginShouldUseRecommendHumanModeForTerminal(cmd, format, true, interactive)
func authLoginShouldShowPostLoginTUIForTerminal(cmd *cobra.Command, format string, recommend bool, interactive bool) bool {
return authLoginShouldUsePostLoginTUIModeForTerminal(cmd, format, recommend, interactive)
}
func authLoginShouldUseRecommendHumanMode(cmd *cobra.Command, format string, recommend bool) bool {
return authLoginShouldUseRecommendHumanModeForTerminal(cmd, format, recommend, authLoginInteractiveTerminal())
func authLoginShouldUsePostLoginTUIMode(cmd *cobra.Command, format string, recommend bool) bool {
return authLoginShouldUsePostLoginTUIModeForTerminal(cmd, format, recommend, authLoginInteractiveTerminal())
}
func authLoginShouldUseRecommendHumanModeForTerminal(cmd *cobra.Command, format string, recommend bool, interactive bool) bool {
if !recommend || !interactive {
func authLoginShouldUsePostLoginTUIModeForTerminal(cmd *cobra.Command, format string, recommend bool, interactive bool) bool {
if recommend || !interactive {
return false
}
return authLoginAllowsInteractiveDefault(cmd, format)
}
func authLoginShouldUseHumanAuthorizationMode(cmd *cobra.Command, format string, hasAuthorizationFlow bool) bool {
return authLoginShouldUseHumanAuthorizationModeForTerminal(cmd, format, hasAuthorizationFlow, authLoginInteractiveTerminal())
}
func authLoginShouldUseHumanAuthorizationModeForTerminal(cmd *cobra.Command, format string, hasAuthorizationFlow bool, interactive bool) bool {
if !hasAuthorizationFlow || !interactive {
return false
}
return authLoginAllowsInteractiveDefault(cmd, format)
}
func authLoginAllowsInteractiveDefault(cmd *cobra.Command, format string) bool {
if cmd == nil || cmd.Root() == nil {
return false
}
if !strings.EqualFold(strings.TrimSpace(format), "json") {
+92 -26
View File
@@ -184,7 +184,7 @@ func TestAuthStatusRefreshFailureLeavesStoredTokenIntact(t *testing.T) {
}
}
func TestAuthLoginRecommendSelectorRespectsExplicitFormat(t *testing.T) {
func TestAuthLoginPostLoginTUIModeRespectsRecommendAndFormat(t *testing.T) {
newRoot := func(t *testing.T) *cobra.Command {
t.Helper()
root := &cobra.Command{Use: "dws"}
@@ -192,13 +192,23 @@ func TestAuthLoginRecommendSelectorRespectsExplicitFormat(t *testing.T) {
return root
}
t.Run("default json still shows selector for interactive login", func(t *testing.T) {
t.Run("recommend skips tui but keeps human auth for interactive login", func(t *testing.T) {
root := newRoot(t)
if !authLoginShouldShowRecommendSelectorForTerminal(root, "json", true) {
t.Fatal("default json format should still show recommend selector in an interactive terminal")
if authLoginShouldShowPostLoginTUIForTerminal(root, "json", true, true) {
t.Fatal("--recommend must not show the post-login product TUI")
}
if !authLoginShouldUseRecommendHumanModeForTerminal(root, "json", true, true) {
t.Fatal("default json format should use human recommend flow in an interactive terminal")
if !authLoginShouldUseHumanAuthorizationModeForTerminal(root, "json", true, true) {
t.Fatal("default interactive --recommend should still use human authorization flow")
}
})
t.Run("without recommend shows tui for interactive default login", func(t *testing.T) {
root := newRoot(t)
if !authLoginShouldShowPostLoginTUIForTerminal(root, "json", false, true) {
t.Fatal("default interactive login without --recommend should show post-login TUI")
}
if !authLoginShouldUseHumanAuthorizationModeForTerminal(root, "json", true, true) {
t.Fatal("default interactive post-login TUI flow should use human authorization flow")
}
})
@@ -207,41 +217,41 @@ func TestAuthLoginRecommendSelectorRespectsExplicitFormat(t *testing.T) {
if err := root.PersistentFlags().Set("format", "json"); err != nil {
t.Fatalf("set format: %v", err)
}
if authLoginShouldShowRecommendSelectorForTerminal(root, "json", true) {
t.Fatal("explicit --format json must not show recommend selector")
if authLoginShouldShowPostLoginTUIForTerminal(root, "json", false, true) {
t.Fatal("explicit --format json must not show post-login TUI")
}
if authLoginShouldUseRecommendHumanModeForTerminal(root, "json", true, true) {
t.Fatal("explicit --format json must keep machine-readable recommend flow")
if authLoginShouldUseHumanAuthorizationModeForTerminal(root, "json", true, true) {
t.Fatal("explicit --format json must keep machine-readable authorization flow")
}
})
t.Run("table shows selector", func(t *testing.T) {
t.Run("table without recommend shows tui", func(t *testing.T) {
root := newRoot(t)
if err := root.PersistentFlags().Set("format", "table"); err != nil {
t.Fatalf("set format: %v", err)
}
if !authLoginShouldShowRecommendSelectorForTerminal(root, "table", true) {
t.Fatal("table format should show recommend selector in an interactive terminal")
if !authLoginShouldShowPostLoginTUIForTerminal(root, "table", false, true) {
t.Fatal("table format should show post-login TUI without --recommend")
}
if !authLoginShouldUseRecommendHumanModeForTerminal(root, "table", true, true) {
t.Fatal("table format should use human recommend flow in an interactive terminal")
if !authLoginShouldUseHumanAuthorizationModeForTerminal(root, "table", true, true) {
t.Fatal("table format should use human authorization flow in an interactive terminal")
}
})
t.Run("non interactive skips selector", func(t *testing.T) {
root := newRoot(t)
if authLoginShouldShowRecommendSelectorForTerminal(root, "json", false) {
t.Fatal("non-interactive login should skip recommend selector")
if authLoginShouldShowPostLoginTUIForTerminal(root, "json", false, false) {
t.Fatal("non-interactive login should skip post-login TUI")
}
if authLoginShouldUseRecommendHumanModeForTerminal(root, "json", true, false) {
t.Fatal("non-interactive login should keep machine-readable recommend flow")
if authLoginShouldUseHumanAuthorizationModeForTerminal(root, "json", true, false) {
t.Fatal("non-interactive login should keep machine-readable authorization flow")
}
})
t.Run("without recommend keeps normal login output contract", func(t *testing.T) {
t.Run("without authorization flow keeps normal login output contract", func(t *testing.T) {
root := newRoot(t)
if authLoginShouldUseRecommendHumanModeForTerminal(root, "json", false, true) {
t.Fatal("login without --recommend should not switch default json to human mode")
if authLoginShouldUseHumanAuthorizationModeForTerminal(root, "json", false, true) {
t.Fatal("login without a post-login authorization flow should not switch default json to human mode")
}
})
}
@@ -287,7 +297,63 @@ func TestResolveAuthLoginConfigReadsInheritedYes(t *testing.T) {
}
}
func TestAuthLoginRecommendTUIRunsAfterLoginTokenSaved(t *testing.T) {
func TestAuthLoginRecommendSkipsPostLoginTUI(t *testing.T) {
t.Setenv(keychain.DisableKeychainEnv, "1")
t.Setenv(keychain.StorageDirEnv, t.TempDir())
t.Setenv("DWS_CONFIG_DIR", t.TempDir())
oldGuideSelector := authLoginGuideActionSelector
oldGuideApplier := authLoginGuideActionApplier
oldScopeSelector := loginRecommendScopeModeSelector
oldProductSelector := loginRecommendProductSelector
oldInteractiveTerminal := authLoginInteractiveTerminal
t.Cleanup(func() {
authLoginGuideActionSelector = oldGuideSelector
authLoginGuideActionApplier = oldGuideApplier
loginRecommendScopeModeSelector = oldScopeSelector
loginRecommendProductSelector = oldProductSelector
authLoginInteractiveTerminal = oldInteractiveTerminal
})
authLoginInteractiveTerminal = func() bool { return true }
authLoginGuideActionSelector = func() (authLoginGuideAction, error) {
t.Fatal("--recommend must not call the post-login guide selector")
return "", nil
}
authLoginGuideActionApplier = func(*cobra.Command, string, authLoginGuideAction) error {
t.Fatal("--recommend must not apply a post-login guide action")
return nil
}
loginRecommendScopeModeSelector = func() (pat.LoginRecommendScopeMode, error) {
t.Fatal("--recommend must not call the scope-mode TUI")
return "", nil
}
loginRecommendProductSelector = func([]pat.LoginRecommendProduct) ([]string, error) {
t.Fatal("--recommend must not call the product-domain TUI")
return nil, nil
}
fake := &authLoginRecommendSequenceCaller{responses: []string{
`{"success":true,"data":{"items":[{"scope":"calendar.event:read","productCode":"calendar","productName":"日历"}],"selectedScopes":["calendar.event:read"]}}`,
`{"success":true,"data":{"grantedScopes":["calendar.event:read"]}}`,
}}
cmd := newAuthLoginCommand(fake)
var out bytes.Buffer
cmd.SetOut(&out)
cmd.SetErr(&out)
cmd.SetArgs([]string{"--token", "login-token", "--recommend"})
if err := cmd.Execute(); err != nil {
t.Fatalf("auth login --recommend error = %v\noutput:\n%s", err, out.String())
}
if len(fake.tools) != 2 {
t.Fatalf("CallTool count = %d, want plan + grant", len(fake.tools))
}
if fake.tools[0] != "pat.batch_plan" || fake.tools[1] != "pat.batch_grant" {
t.Fatalf("tool sequence = %v, want plan, grant", fake.tools)
}
}
func TestAuthLoginWithoutRecommendTUIRunsAfterLoginTokenSaved(t *testing.T) {
t.Setenv(keychain.DisableKeychainEnv, "1")
t.Setenv(keychain.StorageDirEnv, t.TempDir())
configDir := t.TempDir()
@@ -347,13 +413,13 @@ func TestAuthLoginRecommendTUIRunsAfterLoginTokenSaved(t *testing.T) {
var out bytes.Buffer
cmd.SetOut(&out)
cmd.SetErr(&out)
cmd.SetArgs([]string{"--token", "login-token", "--recommend"})
cmd.SetArgs([]string{"--token", "login-token"})
if err := cmd.Execute(); err != nil {
t.Fatalf("auth login --recommend error = %v\noutput:\n%s", err, out.String())
t.Fatalf("auth login error = %v\noutput:\n%s", err, out.String())
}
if !sawTokenBeforeTUI {
t.Fatal("recommend TUI selector was not called")
t.Fatal("post-login TUI selector was not called")
}
if len(fake.tools) != 3 {
t.Fatalf("CallTool count = %d, want initial plan + selected plan + grant", len(fake.tools))