github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/utils/argparser/args_test.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package argparser
    16  
    17  import (
    18  	"reflect"
    19  	"testing"
    20  
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  var forceOpt = &Option{"force", "f", "", OptionalFlag, "force desc", nil, false}
    26  var messageOpt = &Option{"message", "m", "msg", OptionalValue, "msg desc", nil, false}
    27  var fileTypeOpt = &Option{"file-type", "", "", OptionalValue, "file type", nil, false}
    28  var notOpt = &Option{"not", "", "", OptionalValue, "not desc", nil, true}
    29  
    30  func TestParsing(t *testing.T) {
    31  	tests := []struct {
    32  		name         string
    33  		options      []*Option
    34  		args         []string
    35  		expectedOpts map[string]string
    36  		expectedArgs []string
    37  		expectedErr  string
    38  	}{
    39  		{
    40  			name:         "empty",
    41  			options:      []*Option{},
    42  			args:         []string{},
    43  			expectedOpts: map[string]string{},
    44  			expectedArgs: []string{},
    45  		},
    46  		{
    47  			name:         "no options",
    48  			options:      []*Option{},
    49  			args:         []string{"a", "b", "c"},
    50  			expectedOpts: map[string]string{},
    51  			expectedArgs: []string{"a", "b", "c"},
    52  		},
    53  		{
    54  			name:        "-h",
    55  			options:     []*Option{},
    56  			args:        []string{"a", "-h", "c"},
    57  			expectedErr: "Help",
    58  		},
    59  		{
    60  			name:        "--help",
    61  			options:     []*Option{},
    62  			args:        []string{"a", "--help", "c"},
    63  			expectedErr: "Help",
    64  		},
    65  		{
    66  			name:         "force",
    67  			options:      []*Option{forceOpt},
    68  			args:         []string{"--force", "b", "c"},
    69  			expectedOpts: map[string]string{"force": ""},
    70  			expectedArgs: []string{"b", "c"},
    71  		},
    72  		{
    73  			name:         "force abbrev",
    74  			options:      []*Option{forceOpt},
    75  			args:         []string{"b", "-f", "c"},
    76  			expectedOpts: map[string]string{"force": ""},
    77  			expectedArgs: []string{"b", "c"},
    78  		},
    79  		{
    80  			name:         "message",
    81  			options:      []*Option{forceOpt, messageOpt},
    82  			args:         []string{"-m", "b", "c"},
    83  			expectedOpts: map[string]string{"message": "b"},
    84  			expectedArgs: []string{"c"},
    85  		},
    86  		{
    87  			name:         "message equals value",
    88  			options:      []*Option{forceOpt, messageOpt},
    89  			args:         []string{"b", "--message=value", "c"},
    90  			expectedOpts: map[string]string{"message": "value"},
    91  			expectedArgs: []string{"b", "c"},
    92  		},
    93  		{
    94  			name:         "message colon value",
    95  			options:      []*Option{forceOpt, messageOpt},
    96  			args:         []string{"b", "--message:value", "c"},
    97  			expectedOpts: map[string]string{"message": "value"},
    98  			expectedArgs: []string{"b", "c"},
    99  		},
   100  		{
   101  			name:         "empty string",
   102  			options:      []*Option{forceOpt, messageOpt},
   103  			args:         []string{"b", "--message=value", ""},
   104  			expectedOpts: map[string]string{"message": "value"},
   105  			expectedArgs: []string{"b", ""},
   106  		},
   107  		{
   108  			name:         "value attached to flag",
   109  			options:      []*Option{forceOpt},
   110  			args:         []string{"-fvalue"},
   111  			expectedOpts: map[string]string{"force": ""},
   112  			expectedArgs: []string{"value"},
   113  		},
   114  		{
   115  			name:         "-mvalue",
   116  			options:      []*Option{forceOpt, messageOpt},
   117  			args:         []string{"b", "-mvalue", "c"},
   118  			expectedOpts: map[string]string{"message": "value"},
   119  			expectedArgs: []string{"b", "c"},
   120  		},
   121  		{
   122  			name:        "--messagevalue",
   123  			options:     []*Option{forceOpt, messageOpt},
   124  			args:        []string{"b", "--messagevalue", "c"},
   125  			expectedErr: "error: unknown option `messagevalue'",
   126  		},
   127  		{
   128  			name:         "-fm football",
   129  			options:      []*Option{forceOpt, messageOpt},
   130  			args:         []string{"-fm football"},
   131  			expectedOpts: map[string]string{"message": "football", "force": ""},
   132  			expectedArgs: []string{},
   133  		},
   134  		{
   135  			name:         "-ffootball",
   136  			options:      []*Option{forceOpt, messageOpt},
   137  			args:         []string{"-ffootball"},
   138  			expectedOpts: map[string]string{"force": ""},
   139  			expectedArgs: []string{"football"},
   140  		},
   141  		{
   142  			name:         "-m f",
   143  			options:      []*Option{forceOpt, messageOpt},
   144  			args:         []string{"-m f"},
   145  			expectedOpts: map[string]string{"message": "f"},
   146  			expectedArgs: []string{},
   147  		},
   148  		{
   149  			name:        "-fm",
   150  			options:     []*Option{forceOpt, messageOpt},
   151  			args:        []string{"-fm"},
   152  			expectedErr: "error: no value for option `m'",
   153  		},
   154  		{
   155  			name:         "-m f value",
   156  			options:      []*Option{forceOpt, messageOpt},
   157  			args:         []string{"-m f", "value"},
   158  			expectedOpts: map[string]string{"message": "f"},
   159  			expectedArgs: []string{"value"},
   160  		},
   161  		{
   162  			name:         "--not string list value",
   163  			options:      []*Option{forceOpt, messageOpt, notOpt},
   164  			args:         []string{"-m f", "value", "--not", "main", "branch"},
   165  			expectedOpts: map[string]string{"message": "f", "not": "main,branch"},
   166  			expectedArgs: []string{"value"},
   167  		},
   168  		{
   169  			name:         "-fm value",
   170  			options:      []*Option{forceOpt, messageOpt},
   171  			args:         []string{"-fm", "value"},
   172  			expectedOpts: map[string]string{"message": "value", "force": ""},
   173  			expectedArgs: []string{},
   174  		},
   175  		{
   176  			name:         "file-type not force",
   177  			options:      []*Option{forceOpt, messageOpt, fileTypeOpt},
   178  			args:         []string{"--file-type=csv"},
   179  			expectedOpts: map[string]string{"file-type": "csv"},
   180  			expectedArgs: []string{},
   181  		},
   182  		{
   183  			name:        "unsupported arg",
   184  			options:     []*Option{forceOpt, messageOpt},
   185  			args:        []string{"-v"},
   186  			expectedErr: "error: unknown option `v'",
   187  		},
   188  		{
   189  			name:        "duplicate arg",
   190  			options:     []*Option{forceOpt, messageOpt},
   191  			args:        []string{"-f", "-f"},
   192  			expectedErr: "error: multiple values provided for `force'",
   193  		},
   194  	}
   195  
   196  	for _, test := range tests {
   197  		t.Run(test.name, func(t *testing.T) {
   198  			parser := NewArgParserWithVariableArgs("test")
   199  
   200  			for _, opt := range test.options {
   201  				parser.SupportOption(opt)
   202  			}
   203  
   204  			exp := &ArgParseResults{test.expectedOpts, test.expectedArgs, parser, NO_POSITIONAL_ARGS}
   205  
   206  			res, err := parser.Parse(test.args)
   207  			if test.expectedErr != "" {
   208  				require.Error(t, err, test.expectedErr)
   209  			} else {
   210  				require.NoError(t, err)
   211  				assert.Equal(t, exp, res)
   212  			}
   213  		})
   214  	}
   215  }
   216  
   217  func TestValidation(t *testing.T) {
   218  	ap := NewArgParserWithVariableArgs("test")
   219  	ap.SupportsString("string", "s", "string_value", "A string")
   220  	ap.SupportsString("string2", "", "string_value", "Another string")
   221  	ap.SupportsFlag("flag", "f", "A flag")
   222  	ap.SupportsFlag("flag2", "", "Another flag")
   223  	ap.SupportsInt("integer", "n", "num", "A number")
   224  	ap.SupportsInt("integer2", "", "num", "Another number")
   225  	apr, err := ap.Parse([]string{"-s", "string", "--flag", "--n", "1234", "a", "b", "c"})
   226  
   227  	if err != nil {
   228  		t.Fatal(err.Error())
   229  	}
   230  
   231  	if !apr.ContainsAll("string", "flag", "integer") {
   232  		t.Error("Missing expected parameter(s)")
   233  	}
   234  
   235  	if apr.ContainsAny("string2", "flag2", "integer2") {
   236  		t.Error("Contains unexpected parameter(s)")
   237  	}
   238  
   239  	if val := apr.MustGetValue("string"); val != "string" {
   240  		t.Error("Bad val for -string")
   241  	}
   242  
   243  	if val := apr.GetValueOrDefault("string2", "default"); val != "default" {
   244  		t.Error("Bad val for -string2")
   245  	}
   246  
   247  	if _, ok := apr.GetValue("string2"); ok {
   248  		t.Error("Should not be able to get missing parameter string2")
   249  	}
   250  
   251  	if val, ok := apr.GetValue("string"); !ok || val != "string" {
   252  		t.Error("Bad val for --string")
   253  	}
   254  
   255  	if val, ok := apr.GetInt("integer"); !ok || val != 1234 {
   256  		t.Error("Bad val for --integer")
   257  	}
   258  
   259  	if val := apr.GetIntOrDefault("integer2", 5678); val != 5678 {
   260  		t.Error("Bad val for --integer2")
   261  	}
   262  
   263  	trueFlags := apr.AnyFlagsEqualTo(true)
   264  	falseFlags := apr.AnyFlagsEqualTo(false)
   265  
   266  	if trueFlags.Size() != 1 || falseFlags.Size() != 1 {
   267  		t.Error("AnyFlagsEqualTo error")
   268  	}
   269  
   270  	trueSet := apr.FlagsEqualTo([]string{"flag"}, true)
   271  	falseSet := apr.FlagsEqualTo([]string{"flag"}, false)
   272  
   273  	if trueSet.Size() != 1 && falseSet.Size() != 0 {
   274  		t.Error("FlagsEqualTo error")
   275  	}
   276  
   277  	expectedArgs := []string{"a", "b", "c"}
   278  
   279  	if apr.NArg() != 3 || apr.Arg(0) != "a" || !reflect.DeepEqual(apr.Args, expectedArgs) {
   280  		t.Error("Arg list issues")
   281  	}
   282  }
   283  
   284  func TestDropValue(t *testing.T) {
   285  	ap := NewArgParserWithVariableArgs("test")
   286  
   287  	ap.SupportsString("string", "", "string_value", "A string")
   288  	ap.SupportsFlag("flag", "", "A flag")
   289  
   290  	apr, err := ap.Parse([]string{"--string", "str", "--flag", "1234"})
   291  	if err != nil {
   292  		t.Fatal(err.Error())
   293  	}
   294  
   295  	newApr1 := apr.DropValue("string")
   296  	require.NotEqualf(t, apr, newApr1, "Original value and new value are equal")
   297  
   298  	_, hasVal := newApr1.GetValue("string")
   299  	if hasVal {
   300  		t.Error("DropValue failed to drop string")
   301  	}
   302  	_, hasVal = newApr1.GetValue("flag")
   303  	if !hasVal {
   304  		t.Error("DropValue dropped the wrong value")
   305  	}
   306  	if newApr1.NArg() != 1 || newApr1.Arg(0) != "1234" {
   307  		t.Error("DropValue didn't preserve args")
   308  	}
   309  
   310  	newApr2 := apr.DropValue("flag")
   311  	require.NotEqualf(t, apr, newApr2, "DropValue failes to drop flag")
   312  
   313  	_, hasVal = newApr2.GetValue("string")
   314  	if !hasVal {
   315  		t.Error("DropValue dropped the wrong value")
   316  	}
   317  	_, hasVal = newApr2.GetValue("flag")
   318  	if hasVal {
   319  		t.Error("DropValue failed to drop flag")
   320  	}
   321  	if newApr2.NArg() != 1 || newApr2.Arg(0) != "1234" {
   322  		t.Error("DropValue didn't preserve args")
   323  	}
   324  
   325  }