Files
dingtalk-workspace-cli/internal/app/profile_full_coverage_test.go
audanye-sudo 2d143589f8 feat(cli): add deterministic multi-account profile workflows
Accept corpId:userId and friendly organization/account selectors across global --profile, profile switch/use, event child processes, and multi-profile command execution.

List every local account in storage order with live identity-token status, preserve exact current and previous identities, and require explicit selection when an organization has no deterministic current account.

Extend auth logout to remove one exact account, every account in one organization, or all local accounts while revoking each token with its persisted credentials.

Use in-memory login tokens for identity enrichment before persistence, refresh generated Schema artifacts, and cover the complete CLI flow with isolated beta.3 end-to-end tests.
2026-07-17 00:56:57 +08:00

166 lines
6.4 KiB
Go

package app
import (
"context"
"errors"
"io"
"testing"
authpkg "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/auth"
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
)
func TestCrossPlatformCoverageProfileRemainingCoverage(t *testing.T) {
oldMigrate := profileEnsureProfilesMigration
oldLoad := profileLoadProfiles
oldPrevious := profileUsePrevious
oldCurrent := profileSetCurrent
oldInteractive := profileSwitchInteractiveTerminal
oldTUI := profileSwitchTUIRunner
oldProgram := profileRunTeaProgram
t.Cleanup(func() {
profileEnsureProfilesMigration = oldMigrate
profileLoadProfiles = oldLoad
profileUsePrevious = oldPrevious
profileSetCurrent = oldCurrent
profileSwitchInteractiveTerminal = oldInteractive
profileSwitchTUIRunner = oldTUI
profileRunTeaProgram = oldProgram
})
fail := errors.New("failure")
list := newProfileListCommand()
_, _, _ = authCoverageRoot(list, "table", false)
profileEnsureProfilesMigration = func(string) error { return fail }
if err := list.RunE(list, nil); err == nil {
t.Fatal("profile-list migration should fail")
}
profileEnsureProfilesMigration = func(string) error { return nil }
profileLoadProfiles = func(string) (*authpkg.ProfilesConfig, error) { return nil, fail }
if err := list.RunE(list, nil); err == nil {
t.Fatal("profile-list load should fail")
}
selectorCmd := &cobra.Command{}
addProfileSwitchSelectorFlags(selectorCmd)
_ = selectorCmd.Flags().Set("name", " ")
if _, err := profileSwitchSelectorFromCommand(selectorCmd, nil); err == nil {
t.Fatal("blank profile selector should fail")
}
profileSetCurrent = func(string, string) (*authpkg.Profile, error) {
return &authpkg.Profile{CorpID: "ding", CorpName: "Corp"}, nil
}
profileUsePrevious = func(string) (*authpkg.Profile, error) { return nil, fail }
cmd := &cobra.Command{}
_, _, _ = authCoverageRoot(cmd, "table", false)
if err := switchProfileAndWrite(cmd, "cfg", "-", false); err == nil {
t.Fatal("previous-profile error should fail")
}
profileUsePrevious = func(string) (*authpkg.Profile, error) { return &authpkg.Profile{CorpID: "previous"}, nil }
if err := switchProfileAndWrite(cmd, "cfg", "-", false); err != nil {
t.Fatal(err)
}
jsonCmd := &cobra.Command{}
_, _, _ = authCoverageRoot(jsonCmd, "json", false)
profileLoadProfiles = func(string) (*authpkg.ProfilesConfig, error) { return nil, fail }
if err := switchProfileAndWrite(jsonCmd, "cfg", "ding", false); err == nil {
t.Fatal("JSON profile load should fail")
}
profileLoadProfiles = func(string) (*authpkg.ProfilesConfig, error) {
return &authpkg.ProfilesConfig{CurrentProfile: "ding"}, nil
}
jsonCmd.SetOut(&appFailWriter{err: fail})
if err := switchProfileAndWrite(jsonCmd, "cfg", "ding", false); err == nil {
t.Fatal("JSON profile write should fail")
}
profileSwitchInteractiveTerminal = func() bool { return true }
profileEnsureProfilesMigration = func(string) error { return fail }
if _, err := selectProfileSwitchProfile(cmd, "cfg"); err == nil {
t.Fatal("selector migration should fail")
}
profileEnsureProfilesMigration = func(string) error { return nil }
profileLoadProfiles = func(string) (*authpkg.ProfilesConfig, error) { return nil, fail }
if _, err := selectProfileSwitchProfile(cmd, "cfg"); err == nil {
t.Fatal("selector load should fail")
}
profileLoadProfiles = func(string) (*authpkg.ProfilesConfig, error) { return nil, nil }
if _, err := selectProfileSwitchProfile(cmd, "cfg"); err == nil {
t.Fatal("empty selector profiles should fail")
}
choices := []string{}
profileSwitchTUIRunner = func(_ *cobra.Command, _ *authpkg.ProfilesConfig, choice string) (string, error) {
choices = append(choices, choice)
return choice, nil
}
for _, cfg := range []*authpkg.ProfilesConfig{
{CurrentProfile: "current", PrimaryProfile: "primary", Profiles: []authpkg.Profile{{CorpID: "first"}}},
{PrimaryProfile: "primary", Profiles: []authpkg.Profile{{CorpID: "first"}}},
{Profiles: []authpkg.Profile{{CorpID: "first"}}},
} {
profileLoadProfiles = func(string) (*authpkg.ProfilesConfig, error) { return cfg, nil }
if _, err := selectProfileSwitchProfile(cmd, "cfg"); err != nil {
t.Fatal(err)
}
}
if len(choices) != 3 || choices[0] != "current" || choices[1] != "first" || choices[2] != "first" {
t.Fatalf("profile choices = %#v", choices)
}
tuiCmd := &cobra.Command{}
tuiCmd.SetContext(context.Background())
tuiCmd.SetIn(io.NopCloser(&emptyReader{}))
tuiCmd.SetErr(io.Discard)
cfg := &authpkg.ProfilesConfig{Profiles: []authpkg.Profile{{CorpID: "ding"}}}
profileRunTeaProgram = func(*tea.Program) (tea.Model, error) { return nil, tea.ErrInterrupted }
if _, err := runProfileSwitchTUI(tuiCmd, cfg, "ding"); err == nil {
t.Fatal("interrupted TUI should fail")
}
profileRunTeaProgram = func(*tea.Program) (tea.Model, error) { return nil, fail }
if _, err := runProfileSwitchTUI(tuiCmd, cfg, "ding"); err == nil {
t.Fatal("failed TUI should fail")
}
profileRunTeaProgram = func(*tea.Program) (tea.Model, error) { return structModel{}, nil }
if _, err := runProfileSwitchTUI(tuiCmd, cfg, "ding"); err == nil {
t.Fatal("wrong TUI model should fail")
}
for _, model := range []profileSwitchTUIModel{{aborted: true}, {submitted: false}} {
model := model
profileRunTeaProgram = func(*tea.Program) (tea.Model, error) { return model, nil }
if _, err := runProfileSwitchTUI(tuiCmd, cfg, "ding"); err == nil {
t.Fatal("aborted TUI should fail")
}
}
final := newProfileSwitchTUIModel(cfg, "ding")
final.submitted = true
profileRunTeaProgram = func(*tea.Program) (tea.Model, error) { return final, nil }
if got, err := runProfileSwitchTUI(tuiCmd, cfg, "ding"); err != nil || got != "ding" {
t.Fatalf("submitted TUI = %q, %v", got, err)
}
profiles := make([]authpkg.Profile, 8)
model := profileSwitchTUIModel{profiles: profiles, selected: 7, offset: 99}
model.ensureSelectedVisible()
if model.offset != 3 {
t.Fatalf("clamped offset = %d", model.offset)
}
model.offset = -2
model.selected = 0
model.ensureSelectedVisible()
if model.offset != 0 {
t.Fatalf("negative offset = %d", model.offset)
}
}
type emptyReader struct{}
func (*emptyReader) Read([]byte) (int, error) { return 0, io.EOF }
type structModel struct{}
func (structModel) Init() tea.Cmd { return nil }
func (structModel) Update(tea.Msg) (tea.Model, tea.Cmd) { return structModel{}, nil }
func (structModel) View() string { return "" }