github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/alias/set/set_test.go (about)

     1  package set
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"testing"
     7  
     8  	"github.com/MakeNowJust/heredoc"
     9  	"github.com/ungtb10d/cli/v2/internal/config"
    10  	"github.com/ungtb10d/cli/v2/pkg/cmdutil"
    11  	"github.com/ungtb10d/cli/v2/pkg/extensions"
    12  	"github.com/ungtb10d/cli/v2/pkg/iostreams"
    13  	"github.com/ungtb10d/cli/v2/test"
    14  	"github.com/google/shlex"
    15  	"github.com/spf13/cobra"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func runCommand(cfg config.Config, isTTY bool, cli string, in string) (*test.CmdOut, error) {
    21  	ios, stdin, stdout, stderr := iostreams.Test()
    22  	ios.SetStdoutTTY(isTTY)
    23  	ios.SetStdinTTY(isTTY)
    24  	ios.SetStderrTTY(isTTY)
    25  	stdin.WriteString(in)
    26  
    27  	factory := &cmdutil.Factory{
    28  		IOStreams: ios,
    29  		Config: func() (config.Config, error) {
    30  			return cfg, nil
    31  		},
    32  		ExtensionManager: &extensions.ExtensionManagerMock{
    33  			ListFunc: func() []extensions.Extension {
    34  				return []extensions.Extension{}
    35  			},
    36  		},
    37  	}
    38  
    39  	cmd := NewCmdSet(factory, nil)
    40  
    41  	// fake command nesting structure needed for validCommand
    42  	rootCmd := &cobra.Command{}
    43  	rootCmd.AddCommand(cmd)
    44  	prCmd := &cobra.Command{Use: "pr"}
    45  	prCmd.AddCommand(&cobra.Command{Use: "checkout"})
    46  	prCmd.AddCommand(&cobra.Command{Use: "status"})
    47  	rootCmd.AddCommand(prCmd)
    48  	issueCmd := &cobra.Command{Use: "issue"}
    49  	issueCmd.AddCommand(&cobra.Command{Use: "list"})
    50  	rootCmd.AddCommand(issueCmd)
    51  	apiCmd := &cobra.Command{Use: "api"}
    52  	apiCmd.AddCommand(&cobra.Command{Use: "graphql"})
    53  	rootCmd.AddCommand(apiCmd)
    54  
    55  	argv, err := shlex.Split("set " + cli)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	rootCmd.SetArgs(argv)
    60  
    61  	rootCmd.SetIn(stdin)
    62  	rootCmd.SetOut(io.Discard)
    63  	rootCmd.SetErr(io.Discard)
    64  
    65  	_, err = rootCmd.ExecuteC()
    66  	return &test.CmdOut{
    67  		OutBuf: stdout,
    68  		ErrBuf: stderr,
    69  	}, err
    70  }
    71  
    72  func TestAliasSet_gh_command(t *testing.T) {
    73  	cfg := config.NewFromString(``)
    74  
    75  	_, err := runCommand(cfg, true, "pr 'pr status'", "")
    76  	assert.EqualError(t, err, `could not create alias: "pr" is already a gh command`)
    77  }
    78  
    79  func TestAliasSet_empty_aliases(t *testing.T) {
    80  	readConfigs := config.StubWriteConfig(t)
    81  
    82  	cfg := config.NewFromString(heredoc.Doc(`
    83  		aliases:
    84  		editor: vim
    85  	`))
    86  
    87  	output, err := runCommand(cfg, true, "co 'pr checkout'", "")
    88  
    89  	if err != nil {
    90  		t.Fatalf("unexpected error: %s", err)
    91  	}
    92  
    93  	mainBuf := bytes.Buffer{}
    94  	readConfigs(&mainBuf, io.Discard)
    95  
    96  	//nolint:staticcheck // prefer exact matchers over ExpectLines
    97  	test.ExpectLines(t, output.Stderr(), "Added alias")
    98  	//nolint:staticcheck // prefer exact matchers over ExpectLines
    99  	test.ExpectLines(t, output.String(), "")
   100  
   101  	expected := `aliases:
   102      co: pr checkout
   103  editor: vim
   104  `
   105  	assert.Equal(t, expected, mainBuf.String())
   106  }
   107  
   108  func TestAliasSet_existing_alias(t *testing.T) {
   109  	_ = config.StubWriteConfig(t)
   110  
   111  	cfg := config.NewFromString(heredoc.Doc(`
   112  		aliases:
   113  		  co: pr checkout
   114  	`))
   115  
   116  	output, err := runCommand(cfg, true, "co 'pr checkout -Rcool/repo'", "")
   117  	require.NoError(t, err)
   118  
   119  	//nolint:staticcheck // prefer exact matchers over ExpectLines
   120  	test.ExpectLines(t, output.Stderr(), "Changed alias.*co.*from.*pr checkout.*to.*pr checkout -Rcool/repo")
   121  }
   122  
   123  func TestAliasSet_space_args(t *testing.T) {
   124  	readConfigs := config.StubWriteConfig(t)
   125  
   126  	cfg := config.NewFromString(``)
   127  
   128  	output, err := runCommand(cfg, true, `il 'issue list -l "cool story"'`, "")
   129  	require.NoError(t, err)
   130  
   131  	mainBuf := bytes.Buffer{}
   132  	readConfigs(&mainBuf, io.Discard)
   133  
   134  	//nolint:staticcheck // prefer exact matchers over ExpectLines
   135  	test.ExpectLines(t, output.Stderr(), `Adding alias for.*il.*issue list -l "cool story"`)
   136  
   137  	//nolint:staticcheck // prefer exact matchers over ExpectLines
   138  	test.ExpectLines(t, mainBuf.String(), `il: issue list -l "cool story"`)
   139  }
   140  
   141  func TestAliasSet_arg_processing(t *testing.T) {
   142  	readConfigs := config.StubWriteConfig(t)
   143  
   144  	cases := []struct {
   145  		Cmd                string
   146  		ExpectedOutputLine string
   147  		ExpectedConfigLine string
   148  	}{
   149  		{`il "issue list"`, "- Adding alias for.*il.*issue list", "il: issue list"},
   150  
   151  		{`iz 'issue list'`, "- Adding alias for.*iz.*issue list", "iz: issue list"},
   152  
   153  		{`ii 'issue list --author="$1" --label="$2"'`,
   154  			`- Adding alias for.*ii.*issue list --author="\$1" --label="\$2"`,
   155  			`ii: issue list --author="\$1" --label="\$2"`},
   156  
   157  		{`ix "issue list --author='\$1' --label='\$2'"`,
   158  			`- Adding alias for.*ix.*issue list --author='\$1' --label='\$2'`,
   159  			`ix: issue list --author='\$1' --label='\$2'`},
   160  	}
   161  
   162  	for _, c := range cases {
   163  		t.Run(c.Cmd, func(t *testing.T) {
   164  			cfg := config.NewFromString(``)
   165  
   166  			output, err := runCommand(cfg, true, c.Cmd, "")
   167  			if err != nil {
   168  				t.Fatalf("got unexpected error running %s: %s", c.Cmd, err)
   169  			}
   170  
   171  			mainBuf := bytes.Buffer{}
   172  			readConfigs(&mainBuf, io.Discard)
   173  
   174  			//nolint:staticcheck // prefer exact matchers over ExpectLines
   175  			test.ExpectLines(t, output.Stderr(), c.ExpectedOutputLine)
   176  			//nolint:staticcheck // prefer exact matchers over ExpectLines
   177  			test.ExpectLines(t, mainBuf.String(), c.ExpectedConfigLine)
   178  		})
   179  	}
   180  }
   181  
   182  func TestAliasSet_init_alias_cfg(t *testing.T) {
   183  	readConfigs := config.StubWriteConfig(t)
   184  
   185  	cfg := config.NewFromString(heredoc.Doc(`
   186  		editor: vim
   187  	`))
   188  
   189  	output, err := runCommand(cfg, true, "diff 'pr diff'", "")
   190  	require.NoError(t, err)
   191  
   192  	mainBuf := bytes.Buffer{}
   193  	readConfigs(&mainBuf, io.Discard)
   194  
   195  	expected := `editor: vim
   196  aliases:
   197      diff: pr diff
   198  `
   199  
   200  	//nolint:staticcheck // prefer exact matchers over ExpectLines
   201  	test.ExpectLines(t, output.Stderr(), "Adding alias for.*diff.*pr diff", "Added alias.")
   202  	assert.Equal(t, expected, mainBuf.String())
   203  }
   204  
   205  func TestAliasSet_existing_aliases(t *testing.T) {
   206  	readConfigs := config.StubWriteConfig(t)
   207  
   208  	cfg := config.NewFromString(heredoc.Doc(`
   209  		aliases:
   210  		  foo: bar
   211  	`))
   212  
   213  	output, err := runCommand(cfg, true, "view 'pr view'", "")
   214  	require.NoError(t, err)
   215  
   216  	mainBuf := bytes.Buffer{}
   217  	readConfigs(&mainBuf, io.Discard)
   218  
   219  	expected := `aliases:
   220      foo: bar
   221      view: pr view
   222  `
   223  
   224  	//nolint:staticcheck // prefer exact matchers over ExpectLines
   225  	test.ExpectLines(t, output.Stderr(), "Adding alias for.*view.*pr view", "Added alias.")
   226  	assert.Equal(t, expected, mainBuf.String())
   227  
   228  }
   229  
   230  func TestAliasSet_invalid_command(t *testing.T) {
   231  	cfg := config.NewFromString(``)
   232  
   233  	_, err := runCommand(cfg, true, "co 'pe checkout'", "")
   234  	assert.EqualError(t, err, "could not create alias: pe checkout does not correspond to a gh command")
   235  }
   236  
   237  func TestShellAlias_flag(t *testing.T) {
   238  	readConfigs := config.StubWriteConfig(t)
   239  
   240  	cfg := config.NewFromString(``)
   241  
   242  	output, err := runCommand(cfg, true, "--shell igrep 'gh issue list | grep'", "")
   243  	if err != nil {
   244  		t.Fatalf("unexpected error: %s", err)
   245  	}
   246  
   247  	mainBuf := bytes.Buffer{}
   248  	readConfigs(&mainBuf, io.Discard)
   249  
   250  	//nolint:staticcheck // prefer exact matchers over ExpectLines
   251  	test.ExpectLines(t, output.Stderr(), "Adding alias for.*igrep")
   252  
   253  	expected := `aliases:
   254      igrep: '!gh issue list | grep'
   255  `
   256  	assert.Equal(t, expected, mainBuf.String())
   257  }
   258  
   259  func TestShellAlias_bang(t *testing.T) {
   260  	readConfigs := config.StubWriteConfig(t)
   261  
   262  	cfg := config.NewFromString(``)
   263  
   264  	output, err := runCommand(cfg, true, "igrep '!gh issue list | grep'", "")
   265  	require.NoError(t, err)
   266  
   267  	mainBuf := bytes.Buffer{}
   268  	readConfigs(&mainBuf, io.Discard)
   269  
   270  	//nolint:staticcheck // prefer exact matchers over ExpectLines
   271  	test.ExpectLines(t, output.Stderr(), "Adding alias for.*igrep")
   272  
   273  	expected := `aliases:
   274      igrep: '!gh issue list | grep'
   275  `
   276  	assert.Equal(t, expected, mainBuf.String())
   277  }
   278  
   279  func TestShellAlias_from_stdin(t *testing.T) {
   280  	readConfigs := config.StubWriteConfig(t)
   281  
   282  	cfg := config.NewFromString(``)
   283  
   284  	output, err := runCommand(cfg, true, "users -", `api graphql -F name="$1" -f query='
   285      query ($name: String!) {
   286          user(login: $name) {
   287              name
   288          }
   289      }'`)
   290  
   291  	require.NoError(t, err)
   292  
   293  	mainBuf := bytes.Buffer{}
   294  	readConfigs(&mainBuf, io.Discard)
   295  
   296  	//nolint:staticcheck // prefer exact matchers over ExpectLines
   297  	test.ExpectLines(t, output.Stderr(), "Adding alias for.*users")
   298  
   299  	expected := `aliases:
   300      users: |-
   301          api graphql -F name="$1" -f query='
   302              query ($name: String!) {
   303                  user(login: $name) {
   304                      name
   305                  }
   306              }'
   307  `
   308  
   309  	assert.Equal(t, expected, mainBuf.String())
   310  }
   311  
   312  func TestShellAlias_getExpansion(t *testing.T) {
   313  	tests := []struct {
   314  		name         string
   315  		want         string
   316  		expansionArg string
   317  		stdin        string
   318  	}{
   319  		{
   320  			name:         "co",
   321  			want:         "pr checkout",
   322  			expansionArg: "pr checkout",
   323  		},
   324  		{
   325  			name:         "co",
   326  			want:         "pr checkout",
   327  			expansionArg: "pr checkout",
   328  			stdin:        "api graphql -F name=\"$1\"",
   329  		},
   330  		{
   331  			name:         "stdin",
   332  			expansionArg: "-",
   333  			want:         "api graphql -F name=\"$1\"",
   334  			stdin:        "api graphql -F name=\"$1\"",
   335  		},
   336  	}
   337  
   338  	for _, tt := range tests {
   339  		t.Run(tt.name, func(t *testing.T) {
   340  			ios, stdin, _, _ := iostreams.Test()
   341  			ios.SetStdinTTY(false)
   342  
   343  			_, err := stdin.WriteString(tt.stdin)
   344  			assert.NoError(t, err)
   345  
   346  			expansion, err := getExpansion(&SetOptions{
   347  				Expansion: tt.expansionArg,
   348  				IO:        ios,
   349  			})
   350  			assert.NoError(t, err)
   351  
   352  			assert.Equal(t, expansion, tt.want)
   353  		})
   354  	}
   355  }