fix: skip tui for recommend login
This commit is contained in:
1 parent
7720e3ec9b
commit
d2fd7e00b5
3 files changed
+151
-55
No files matched your search
+22
-11
@@ -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"
|
||||
}
|
||||
@@ -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") {
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in new issue
Block a user