github.com/tomwright/dasel@v1.27.3/internal/command/root_put_test.go (about)

     1  package command_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"github.com/tomwright/dasel/internal/command"
     7  	"io"
     8  	"os"
     9  	"strings"
    10  	"testing"
    11  )
    12  
    13  func TestRootCMD_Put(t *testing.T) {
    14  	t.Run("InvalidFile", expectErr(
    15  		[]string{"put", "string", "-f", "bad.json", "-s", "x", "y"},
    16  		"could not open input file",
    17  	))
    18  	t.Run("MissingParser", expectErr(
    19  		[]string{"put", "string", "-s", "x", "y"},
    20  		"parser flag required when reading from stdin",
    21  	))
    22  	t.Run("StdinStdout", expectOutput(
    23  		`{"name": "Tom"}`,
    24  		[]string{"put", "string", "-f", "stdin", "-o", "stdout", "-p", "json", "-s", ".name", "Frank"},
    25  		`{
    26    "name": "Frank"
    27  }
    28  `,
    29  	))
    30  	t.Run("StdinStdoutAlias", expectOutput(
    31  		`{"name": "Tom"}`,
    32  		[]string{"put", "string", "-f", "-", "-o", "-", "-p", "json", "-s", ".name", "Frank"},
    33  		`{
    34    "name": "Frank"
    35  }
    36  `,
    37  	))
    38  
    39  	t.Run("InvalidSingleSelector", expectErrFromInput(
    40  		`{"name": "Tom"}`,
    41  		[]string{"put", "string", "-f", "stdin", "-o", "stdout", "-p", "json", "-s", "[-]", "Frank"},
    42  		"invalid index: -",
    43  	))
    44  	t.Run("InvalidMultiSelector", expectErrFromInput(
    45  		`{"name": "Tom"}`,
    46  		[]string{"put", "string", "-f", "stdin", "-o", "stdout", "-p", "json", "-m", "-s", "[-]", "Frank"},
    47  		"invalid index: -",
    48  	))
    49  
    50  	t.Run("InvalidObjectSingleSelector", expectErrFromInput(
    51  		`{"name": "Tom"}`,
    52  		[]string{"put", "object", "-f", "stdin", "-o", "stdout", "-p", "json", "-t", "string", "-s", "[-]", "Frank"},
    53  		"invalid index: -",
    54  	))
    55  	t.Run("InvalidMultiSelector", expectErrFromInput(
    56  		`{"name": "Tom"}`,
    57  		[]string{"put", "object", "-f", "stdin", "-o", "stdout", "-p", "json", "-m", "-t", "string", "-s", "[-]", "Frank"},
    58  		"invalid index: -",
    59  	))
    60  }
    61  
    62  func putTest(in string, varType string, parser string, selector string, value string, out string, expErr error, additionalArgs ...string) func(t *testing.T) {
    63  	args := []string{
    64  		"put", varType,
    65  	}
    66  	args = append(args, additionalArgs...)
    67  	args = append(args, "-p", parser, selector, value)
    68  
    69  	return baseTest(in, out, expErr, args...)
    70  }
    71  
    72  func baseTest(in string, out string, expErr error, args ...string) func(t *testing.T) {
    73  	return func(t *testing.T) {
    74  		cmd := command.NewRootCMD()
    75  		outputBuffer := bytes.NewBuffer([]byte{})
    76  
    77  		cmd.SetOut(outputBuffer)
    78  		cmd.SetIn(strings.NewReader(in))
    79  		cmd.SetArgs(args)
    80  
    81  		err := cmd.Execute()
    82  
    83  		if expErr == nil && err != nil {
    84  			t.Errorf("expected err %v, got %v", expErr, err)
    85  			return
    86  		}
    87  		if expErr != nil && err == nil {
    88  			t.Errorf("expected err %v, got %v", expErr, err)
    89  			return
    90  		}
    91  		if expErr != nil && err != nil && err.Error() != expErr.Error() {
    92  			t.Errorf("expected err %v, got %v", expErr, err)
    93  			return
    94  		}
    95  
    96  		output, err := io.ReadAll(outputBuffer)
    97  		if err != nil {
    98  			t.Errorf("unexpected error reading output buffer: %s", err)
    99  			return
   100  		}
   101  
   102  		outputStr := string(output)
   103  		if out != outputStr {
   104  			t.Errorf("expected result %v, got %v", out, outputStr)
   105  		}
   106  	}
   107  }
   108  
   109  func putFileTest(in string, varType string, parser string, selector string, value string, out string, outFile string, expErr error, additionalArgs ...string) func(t *testing.T) {
   110  	return func(t *testing.T) {
   111  		defer func() {
   112  			_ = os.Remove(outFile)
   113  		}()
   114  		cmd := command.NewRootCMD()
   115  
   116  		args := []string{
   117  			"put", varType,
   118  		}
   119  		args = append(args, additionalArgs...)
   120  		args = append(args, "-p", parser, "-o", outFile, selector, value)
   121  
   122  		cmd.SetIn(strings.NewReader(in))
   123  		cmd.SetArgs(args)
   124  
   125  		err := cmd.Execute()
   126  
   127  		if expErr == nil && err != nil {
   128  			t.Errorf("expected err %v, got %v", expErr, err)
   129  			return
   130  		}
   131  		if expErr != nil && err == nil {
   132  			t.Errorf("expected err %v, got %v", expErr, err)
   133  			return
   134  		}
   135  		if expErr != nil && err != nil && err.Error() != expErr.Error() {
   136  			t.Errorf("expected err %v, got %v", expErr, err)
   137  			return
   138  		}
   139  
   140  		output, err := os.ReadFile(outFile)
   141  		if err != nil {
   142  			t.Errorf("could not read output file: %s", err)
   143  			return
   144  		}
   145  
   146  		out = strings.TrimSpace(out)
   147  		outputStr := strings.TrimSpace(string(output))
   148  		if out != outputStr {
   149  			t.Errorf("expected result %v, got %v", out, outputStr)
   150  		}
   151  	}
   152  }
   153  
   154  func TestRootCMD_Put_JSON(t *testing.T) {
   155  	t.Run("String", putStringTest(`{
   156    "id": "x"
   157  }`, "json", "id", "y", `{
   158    "id": "y"
   159  }
   160  `, nil))
   161  	t.Run("Int", putIntTest(`{
   162    "id": 123
   163  }`, "json", "id", "456", `{
   164    "id": 456
   165  }
   166  `, nil))
   167  	t.Run("Bool", putBoolTest(`{
   168    "id": true
   169  }`, "json", "id", "false", `{
   170    "id": false
   171  }
   172  `, nil))
   173  	t.Run("OverwriteObject", putObjectTest(`{
   174    "numbers": [
   175      {
   176        "rank": 1,
   177        "number": "one"
   178      },
   179      {
   180        "rank": 2,
   181        "number": "two"
   182      },
   183      {
   184        "rank": 3,
   185        "number": "three"
   186      }
   187    ]
   188  }`, "json", ".numbers.[0]", []string{"number=five", "rank=5"}, []string{"string", "int"}, `{
   189    "numbers": [
   190      {
   191        "number": "five",
   192        "rank": 5
   193      },
   194      {
   195        "number": "two",
   196        "rank": 2
   197      },
   198      {
   199        "number": "three",
   200        "rank": 3
   201      }
   202    ]
   203  }`, nil))
   204  
   205  	t.Run("OverwriteObjectAtRoot", putObjectTest(`{
   206    "rank": 1,
   207    "number": "one"
   208  }`, "json", ".", []string{"number=five", "rank=5"}, []string{"string", "int"}, `{
   209    "number": "five",
   210    "rank": 5
   211  }`, nil))
   212  
   213  	t.Run("OverwriteObjectAtRootCompact", putObjectTest(`{
   214    "rank": 1,
   215    "number": "one"
   216  }`, "json", ".", []string{"number=five", "rank=5"}, []string{"string", "int"}, `{"number":"five","rank":5}
   217  `, nil, "-c"))
   218  
   219  	t.Run("MultipleObject", putObjectTest(`{
   220    "numbers": [
   221      {
   222        "rank": 1,
   223        "number": "one"
   224      },
   225      {
   226        "rank": 2,
   227        "number": "two"
   228      },
   229      {
   230        "rank": 3,
   231        "number": "three"
   232      }
   233    ]
   234  }`, "json", ".numbers.[*]", []string{"number=five", "rank=5"}, []string{"string", "int"}, `{
   235    "numbers": [
   236      {
   237        "number": "five",
   238        "rank": 5
   239      },
   240      {
   241        "number": "five",
   242        "rank": 5
   243      },
   244      {
   245        "number": "five",
   246        "rank": 5
   247      }
   248    ]
   249  }`, nil, "-m"))
   250  
   251  	t.Run("AppendObject", putObjectTest(`{
   252    "numbers": [
   253      {
   254        "rank": 1,
   255        "number": "one"
   256      },
   257      {
   258        "rank": 2,
   259        "number": "two"
   260      },
   261      {
   262        "rank": 3,
   263        "number": "three"
   264      }
   265    ]
   266  }`, "json", ".numbers.[]", []string{"rank=4", "number=four"}, []string{"int", "string"}, `{
   267    "numbers": [
   268      {
   269        "number": "one",
   270        "rank": 1
   271      },
   272      {
   273        "number": "two",
   274        "rank": 2
   275      },
   276      {
   277        "number": "three",
   278        "rank": 3
   279      },
   280      {
   281        "number": "four",
   282        "rank": 4
   283      }
   284    ]
   285  }`, nil))
   286  
   287  	t.Run("EmptyObject", putObjectTest(`{
   288    "numbers": [
   289      {
   290        "rank": 1,
   291        "number": "one"
   292      },
   293      {
   294        "rank": 2,
   295        "number": "two"
   296      },
   297      {
   298        "rank": 3,
   299        "number": "three"
   300      }
   301    ]
   302  }`, "json", ".numbers.[]", []string{}, []string{}, `{
   303    "numbers": [
   304      {
   305        "number": "one",
   306        "rank": 1
   307      },
   308      {
   309        "number": "two",
   310        "rank": 2
   311      },
   312      {
   313        "number": "three",
   314        "rank": 3
   315      },
   316      {}
   317    ]
   318  }`, nil))
   319  
   320  	t.Run("AppendObjectMulti", putObjectTest(`{
   321    "numbers": [
   322      {
   323        "rank": 1,
   324        "number": "one"
   325      },
   326      {
   327        "rank": 2,
   328        "number": "two"
   329      },
   330      {
   331        "rank": 3,
   332        "number": "three"
   333      }
   334    ]
   335  }`, "json", ".numbers.[]", []string{"rank=4", "number=four"}, []string{"int", "string"}, `{
   336    "numbers": [
   337      {
   338        "number": "one",
   339        "rank": 1
   340      },
   341      {
   342        "number": "two",
   343        "rank": 2
   344      },
   345      {
   346        "number": "three",
   347        "rank": 3
   348      },
   349      {
   350        "number": "four",
   351        "rank": 4
   352      }
   353    ]
   354  }`, nil, "-m"))
   355  
   356  	t.Run("MultipleString", putStringTest(`[
   357    {"value": "A"},
   358    {"value": "B"},
   359    {"value": "C"}
   360  ]`, "json", "[*].value", "X", `[
   361    {
   362      "value": "X"
   363    },
   364    {
   365      "value": "X"
   366    },
   367    {
   368      "value": "X"
   369    }
   370  ]
   371  `, nil, "-m"))
   372  
   373  	t.Run("KeySearch", putStringTest(`{
   374    "users": [
   375  	{
   376  	  "primary": true,
   377  	  "name": {
   378  		"first": "Tom",
   379  		"last": "Wright"
   380  	  }
   381  	},
   382  	{
   383  	  "primary": false,
   384        "extra": {
   385          "name": {
   386            "first": "Joe",
   387            "last": "Blogs"
   388          }
   389        },
   390  	  "name": {
   391  		"first": "Jim",
   392  		"last": "Wright"
   393  	  }
   394  	}
   395    ]
   396  }`, "json", ".(?:-=name).first", "Bobby", `{
   397    "users": [
   398      {
   399        "name": {
   400          "first": "Bobby",
   401          "last": "Wright"
   402        },
   403        "primary": true
   404      },
   405      {
   406        "extra": {
   407          "name": {
   408            "first": "Bobby",
   409            "last": "Blogs"
   410          }
   411        },
   412        "name": {
   413          "first": "Bobby",
   414          "last": "Wright"
   415        },
   416        "primary": false
   417      }
   418    ]
   419  }
   420  `, nil, "-m"))
   421  
   422  	t.Run("ValueSearch", putStringTest(`{
   423    "users": [
   424  	{
   425  	  "primary": true,
   426  	  "name": {
   427  		"first": "Tom",
   428  		"last": "Wright"
   429  	  }
   430  	},
   431  	{
   432  	  "primary": false,
   433        "extra": {
   434          "name": {
   435            "first": "Joe",
   436            "last": "Blogs"
   437          }
   438        },
   439  	  "name": {
   440  		"first": "Jim",
   441  		"last": "Wright"
   442  	  }
   443  	}
   444    ]
   445  }`, "json", ".(?:.=Wright)", "Wrighto", `{
   446    "users": [
   447      {
   448        "name": {
   449          "first": "Tom",
   450          "last": "Wrighto"
   451        },
   452        "primary": true
   453      },
   454      {
   455        "extra": {
   456          "name": {
   457            "first": "Joe",
   458            "last": "Blogs"
   459          }
   460        },
   461        "name": {
   462          "first": "Jim",
   463          "last": "Wrighto"
   464        },
   465        "primary": false
   466      }
   467    ]
   468  }
   469  `, nil, "-m"))
   470  
   471  	t.Run("KeyValueSearch", putStringTest(`{
   472    "users": [
   473  	{
   474  	  "primary": true,
   475  	  "name": {
   476  		"first": "Tom",
   477  		"last": "Wright"
   478  	  }
   479  	},
   480  	{
   481  	  "primary": false,
   482        "extra": {
   483          "name": {
   484            "first": "Joe",
   485            "last": "Blogs"
   486          }
   487        },
   488  	  "name": {
   489  		"first": "Jim",
   490  		"last": "Wright"
   491  	  }
   492  	}
   493    ]
   494  }`, "json", ".(?:.last=Wright).first", "Fred", `{
   495    "users": [
   496      {
   497        "name": {
   498          "first": "Fred",
   499          "last": "Wright"
   500        },
   501        "primary": true
   502      },
   503      {
   504        "extra": {
   505          "name": {
   506            "first": "Joe",
   507            "last": "Blogs"
   508          }
   509        },
   510        "name": {
   511          "first": "Fred",
   512          "last": "Wright"
   513        },
   514        "primary": false
   515      }
   516    ]
   517  }
   518  `, nil, "-m"))
   519  
   520  	t.Run("StringMultiObjectDocument", putStringTest(`{
   521    "id": "x"
   522  }
   523  {
   524    "id": "y"
   525  }`, "json", ".[0].id", "z", `{
   526    "id": "z"
   527  }
   528  {
   529    "id": "y"
   530  }
   531  `, nil))
   532  
   533  	t.Run("StringMultiArrayDocument", putStringTest(`[
   534    "a",
   535    "b",
   536    "c"
   537  ]
   538  [
   539    "d",
   540    "e",
   541    "f"
   542  ]`, "json", ".[1].[1]", "z", `[
   543    "a",
   544    "b",
   545    "c"
   546  ]
   547  [
   548    "d",
   549    "z",
   550    "f"
   551  ]
   552  `, nil))
   553  
   554  	t.Run("InsertDocumentAtProperty", putDocumentTest(`{}`, "json", ".person", `{"name":"Tom"}`, `{
   555    "person": {
   556      "name": "Tom"
   557    }
   558  }
   559  `, nil))
   560  
   561  	t.Run("InsertDocumentAtPropertyCompact", putDocumentTest(`{}`, "json", ".person", `{"name":"Tom"}`, `{"person":{"name":"Tom"}}
   562  `, nil, "-c"))
   563  
   564  	t.Run("InvalidDocumentParser", putDocumentTest(`{}`, "json", ".person", `name: Tom`, ``,
   565  		fmt.Errorf("could not get document parser: unknown parser: bad"), "-d", "bad"))
   566  
   567  	t.Run("InsertDocumentAtPropertyWithDifferentParser", putDocumentTest(`{}`, "json", ".person", `name: Tom`, `{
   568    "person": {
   569      "name": "Tom"
   570    }
   571  }
   572  `, nil, "-d", "yaml"))
   573  
   574  	t.Run("AppendDocumentToProperty", putDocumentTest(`{"people": []}`, "json", ".people.[]", `{"name":"Tom"}`, `{
   575    "people": [
   576      {
   577        "name": "Tom"
   578      }
   579    ]
   580  }
   581  `, nil))
   582  
   583  	t.Run("AppendDocumentToPropertyMulti", putDocumentTest(`{"people": []}{"people": []}`, "json", ".[*].people.[]", `{"name":"Tom"}`, `{
   584    "people": [
   585      {
   586        "name": "Tom"
   587      }
   588    ]
   589  }
   590  {
   591    "people": [
   592      {
   593        "name": "Tom"
   594      }
   595    ]
   596  }
   597  `, nil, "-m"))
   598  
   599  	t.Run("InsertDocumentAtRoot", putDocumentTest(`{}`, "json", ".", `{"name":"Tom"}`, `{
   600    "name": "Tom"
   601  }
   602  `, nil))
   603  
   604  	t.Run("AppendDocumentAtRoot", putDocumentTest(`[]`, "json", ".[]", `{"name":"Tom"}`, `[
   605    {
   606      "name": "Tom"
   607    }
   608  ]
   609  `, nil))
   610  
   611  	t.Run("AppendDocumentAtRootMulti", putDocumentTest(`[][]`, "json", ".[*].[]", `{"name":"Tom"}`, `[
   612    {
   613      "name": "Tom"
   614    }
   615  ]
   616  [
   617    {
   618      "name": "Tom"
   619    }
   620  ]
   621  `, nil, "-m"))
   622  
   623  	// https://github.com/TomWright/dasel/issues/66
   624  	t.Run("PutJSONDocumentAtYAMLProperty", putDocumentTest(`foo: true
   625  bar: 5
   626  baz:
   627    qux: false
   628    quux: "yes"
   629    quuz: 7`, "yaml", ".baz", `{"qux": false,"quux": "no","quuz": 8}`, `bar: 5
   630  baz:
   631    quux: "no"
   632    quuz: 8
   633    qux: false
   634  foo: true
   635  `, nil, "-d", "json"))
   636  
   637  	t.Run("MergeInputDocumentsPut", putIntTest(`
   638  {
   639  "number": 1
   640  }
   641  {
   642  "number": 2
   643  }
   644  {
   645  "number": 3
   646  }
   647  `, "json", ".[0].number", `4`, `[
   648    {
   649      "number": 4
   650    },
   651    {
   652      "number": 2
   653    },
   654    {
   655      "number": 3
   656    }
   657  ]
   658  `, nil, "--merge-input-documents"))
   659  
   660  	t.Run("ValueFlag", func(t *testing.T) {
   661  		// Test -v/--value flag
   662  		// Workaround for https://github.com/TomWright/dasel/issues/117
   663  
   664  		t.Run("StringWithDash", baseTest(`{
   665    "id": "x"
   666  }`, `{
   667    "id": "-abc"
   668  }
   669  `, nil, "put", "string", "-p", "json", "-v", "-abc", ".id"))
   670  
   671  		t.Run("NegativeInt", baseTest(`{
   672    "id": 1
   673  }`, `{
   674    "id": -1
   675  }
   676  `, nil, "put", "int", "-p", "json", "-v", "-1", ".id"))
   677  	})
   678  	t.Run("StringWithDashWithSelectorFlag", baseTest(`{
   679    "id": "x"
   680  }`, `{
   681    "id": "-abc"
   682  }
   683  `, nil, "put", "string", "-p", "json", "-v", "-abc", "-s", ".id"))
   684  
   685  	t.Run("NegativeIntWithSelectorFlag", baseTest(`{
   686    "id": 1
   687  }`, `{
   688    "id": -1
   689  }
   690  `, nil, "put", "int", "-p", "json", "-v", "-1", "-s", ".id"))
   691  
   692  	t.Run("PutStringEscapeHTMLOn", putStringTest(`{
   693    "user": "Old"
   694  }
   695  `, "json", `.user`, `Tom <contact@tomwright.me>`, `{
   696    "user": "Tom \u003ccontact@tomwright.me\u003e"
   697  }
   698  `, nil, "--escape-html=true"))
   699  
   700  	t.Run("PutStringEscapeHTMLOff", putStringTest(`{
   701    "user": "Tom <contact@tomwright.me>"
   702  }
   703  `, "json", `.user`, `Tom <contact@tomwright.me>`, `{
   704    "user": "Tom <contact@tomwright.me>"
   705  }
   706  `, nil, "--escape-html=false"))
   707  
   708  	t.Run("PutObjectEscapeHTMLOn", putObjectTest(`{
   709    "user": "Old"
   710  }
   711  `, "json", `.`, []string{`user=Tom <contact@tomwright.me>`}, []string{"string"}, `{
   712    "user": "Tom \u003ccontact@tomwright.me\u003e"
   713  }
   714  `, nil, "--escape-html=true"))
   715  
   716  	t.Run("PutObjectEscapeHTMLOff", putObjectTest(`{
   717    "user": "Old"
   718  }
   719  `, "json", `.`, []string{`user=Tom <contact@tomwright.me>`}, []string{"string"}, `{
   720    "user": "Tom <contact@tomwright.me>"
   721  }
   722  `, nil, "--escape-html=false"))
   723  
   724  	t.Run("PutDocumentEscapeHTMLOn", putDocumentTest(`{
   725    "user": "Old"
   726  }
   727  `, "json", `.`, `{"user": "Tom <contact@tomwright.me>"}`, `{
   728    "user": "Tom \u003ccontact@tomwright.me\u003e"
   729  }
   730  `, nil, "--escape-html=true"))
   731  
   732  	t.Run("PutDocumentEscapeHTMLOff", putDocumentTest(`{
   733    "user": "Tom <contact@tomwright.me>"
   734  }
   735  `, "json", `.`, `{"user": "Tom <contact@tomwright.me>"}`, `{
   736    "user": "Tom <contact@tomwright.me>"
   737  }
   738  `, nil, "--escape-html=false"))
   739  
   740  	t.Run("StringFromFile", putStringTest(`{
   741    "id": "x"
   742  }`, "json", ".value", "", `{
   743    "id": "x",
   744    "value": "This is a string value"
   745  }
   746  `, nil, "--value-file", "../../tests/assets/string-value.txt"))
   747  
   748  	t.Run("IntFromFile", putIntTest(`{
   749    "id": "x"
   750  }`, "json", ".value", "", `{
   751    "id": "x",
   752    "value": 12345
   753  }
   754  `, nil, "--value-file", "../../tests/assets/int-value.txt"))
   755  
   756  	t.Run("DocumentFromFile", putDocumentTest(`{
   757    "id": "x"
   758  }`, "json", ".value", "", `{
   759    "id": "x",
   760    "value": {
   761      "this": "is a value"
   762    }
   763  }
   764  `, nil, "--value-file", "../../tests/assets/json-value.json"))
   765  }
   766  
   767  func TestRootCMD_Put_YAML(t *testing.T) {
   768  	t.Run("String", putStringTest(`
   769  id: "x"
   770  name: "Tom"
   771  `, "yaml", "id", "y", `id: "y"
   772  name: Tom
   773  `, nil))
   774  	t.Run("StringInFile", putFileTest(`
   775  id: "x"
   776  name: "Tom"
   777  `, "string", "yaml", "id", "y", `id: "y"
   778  name: Tom
   779  `, "TestRootCMD_Put_YAML_out.yaml", nil))
   780  	t.Run("Int", putIntTest(`
   781  id: 123
   782  `, "yaml", "id", "456", `id: 456
   783  `, nil))
   784  	t.Run("Bool", putBoolTest(`
   785  id: true
   786  `, "yaml", "id", "false", `id: false
   787  `, nil))
   788  	t.Run("OverwriteObject", putObjectTest(`
   789  numbers:
   790  - number: one
   791    rank: 1
   792  - number: two
   793    rank: 2
   794  - number: three
   795    rank: 3
   796  `, "yaml", ".numbers.[0]", []string{"number=five", "rank=5"}, []string{"string", "int"}, `
   797  numbers:
   798  - number: five
   799    rank: 5
   800  - number: two
   801    rank: 2
   802  - number: three
   803    rank: 3
   804  `, nil))
   805  	t.Run("StringInMultiDocument", putStringTest(`
   806  id: "x"
   807  ---
   808  id: "y"
   809  ---
   810  id: "z"
   811  `, "yaml", "[1].id", "1", `id: x
   812  ---
   813  id: "1"
   814  ---
   815  id: z
   816  `, nil))
   817  
   818  	t.Run("StringWithDotInName", putStringTest(`
   819  id: "asd"
   820  my.name: "Tom"
   821  `, "yaml", `my\.name`, "Jim", `id: asd
   822  my.name: Jim
   823  `, nil))
   824  
   825  	t.Run("NewValInExistingMap", putStringTest(`
   826  metadata:
   827    annotations:
   828      node.longhorn.io/default-disks-config: '[ { "name":"fast",  "path":"/mnt/data-fast1", "allowScheduling":true, "tags":["fast"]}, { "name":"slow",  "path":"/mnt/data-slow1", "allowScheduling":true, "tags":["slow"]} ]'
   829  `, "yaml", `metadata.labels.node\.longhorn\.io\/create-default-disk`, "config", `metadata:
   830    annotations:
   831      node.longhorn.io/default-disks-config: '[ { "name":"fast",  "path":"/mnt/data-fast1",
   832        "allowScheduling":true, "tags":["fast"]}, { "name":"slow",  "path":"/mnt/data-slow1",
   833        "allowScheduling":true, "tags":["slow"]} ]'
   834    labels:
   835      node.longhorn.io/create-default-disk: config
   836  `, nil))
   837  
   838  	// https://github.com/TomWright/dasel/issues/102
   839  	// Worked in v1.13.2
   840  	t.Run("BlankInput", putStringTest(``, "yaml", `[0].job_name`, "logging", `- job_name: logging
   841  `, nil))
   842  }
   843  
   844  func TestRootCMD_Put_TOML(t *testing.T) {
   845  	t.Run("String", putStringTest(`
   846  id = "x"
   847  `, "toml", "id", "y", `id = "y"
   848  `, nil))
   849  	t.Run("Int", putIntTest(`
   850  id = 123
   851  `, "toml", "id", "456", `id = 456
   852  `, nil))
   853  	t.Run("Bool", putBoolTest(`
   854  id = true
   855  `, "toml", "id", "false", `id = false
   856  `, nil))
   857  	t.Run("OverwriteObject", putObjectTest(`
   858  [[numbers]]
   859    number = "one"
   860    rank = 1
   861  
   862  [[numbers]]
   863    number = "two"
   864    rank = 2
   865  
   866  [[numbers]]
   867    number = "three"
   868    rank = 3
   869  `, "toml", ".numbers.[0]", []string{"number=five", "rank=5"}, []string{"string", "int"}, `
   870  [[numbers]]
   871    number = "five"
   872    rank = 5
   873  
   874  [[numbers]]
   875    number = "two"
   876    rank = 2
   877  
   878  [[numbers]]
   879    number = "three"
   880    rank = 3
   881  `, nil))
   882  }
   883  
   884  func TestRootCMD_Put_XML(t *testing.T) {
   885  	t.Run("String", putStringTest(`<data><id>x</id></data>`, "xml", ".data.id", "y", `<data>
   886    <id>y</id>
   887  </data>
   888  `, nil))
   889  	t.Run("Int", putIntTest(`<data><id>1</id></data>`, "xml", ".data.id", "2", `<data>
   890    <id>2</id>
   891  </data>
   892  `, nil))
   893  	t.Run("Bool", putBoolTest(`<data><id>false</id></data>`, "xml", ".data.id", "true", `<data>
   894    <id>true</id>
   895  </data>
   896  `, nil))
   897  	t.Run("OverwriteObject", putObjectTest(`<data><id>x</id></data>`, "xml", ".data", []string{"id=y", "rank=5"}, []string{"string", "int"}, `<data>
   898    <id>y</id>
   899    <rank>5</rank>
   900  </data>
   901  `, nil))
   902  	t.Run("AppendObject", putObjectTest(`<data><item><value>1</value></item><item><value>2</value></item></data>`, "xml", ".data.item.[]", []string{"value=3"}, []string{"int"}, `<data>
   903    <item>
   904      <value>1</value>
   905    </item>
   906    <item>
   907      <value>2</value>
   908    </item>
   909    <item>
   910      <value>3</value>
   911    </item>
   912  </data>
   913  `, nil))
   914  }
   915  
   916  func TestRootCMD_Put_CSV(t *testing.T) {
   917  	t.Run("String", putStringTest(`id,name
   918  1,Tom
   919  2,Jim
   920  `, "csv", ".[0].id", "3", `id,name
   921  3,Tom
   922  2,Jim
   923  `, nil))
   924  	t.Run("NewString", putStringTest(`id,name
   925  1,Tom
   926  2,Jim
   927  `, "csv", ".[0].age", "27", `id,name,age
   928  1,Tom,27
   929  2,Jim,
   930  `, nil))
   931  }
   932  
   933  func putObjectTest(in string, parser string, selector string, values []string, types []string, out string, expErr error, additionalArgs ...string) func(t *testing.T) {
   934  	return func(t *testing.T) {
   935  		cmd := command.NewRootCMD()
   936  		outputBuffer := bytes.NewBuffer([]byte{})
   937  
   938  		args := []string{
   939  			"put", "object", "-p", parser, "-o", "stdout",
   940  		}
   941  		for _, t := range types {
   942  			args = append(args, "-t", t)
   943  		}
   944  		args = append(args, additionalArgs...)
   945  		args = append(args, selector)
   946  		args = append(args, values...)
   947  
   948  		cmd.SetOut(outputBuffer)
   949  		cmd.SetIn(strings.NewReader(in))
   950  		cmd.SetArgs(args)
   951  
   952  		err := cmd.Execute()
   953  
   954  		if expErr == nil && err != nil {
   955  			t.Errorf("expected err %v, got %v", expErr, err)
   956  			return
   957  		}
   958  		if expErr != nil && err == nil {
   959  			t.Errorf("expected err %v, got %v", expErr, err)
   960  			return
   961  		}
   962  		if expErr != nil && err != nil && err.Error() != expErr.Error() {
   963  			t.Errorf("expected err %v, got %v", expErr, err)
   964  			return
   965  		}
   966  
   967  		output, err := io.ReadAll(outputBuffer)
   968  		if err != nil {
   969  			t.Errorf("unexpected error reading output buffer: %s", err)
   970  			return
   971  		}
   972  
   973  		out = strings.TrimSpace(out)
   974  		outputStr := strings.TrimSpace(string(output))
   975  		if out != outputStr {
   976  			t.Errorf("expected result %v, got %v", out, outputStr)
   977  		}
   978  	}
   979  }
   980  
   981  func putDocumentTest(in string, parser string, selector string, document string, out string, expErr error, additionalArgs ...string) func(t *testing.T) {
   982  	return func(t *testing.T) {
   983  		cmd := command.NewRootCMD()
   984  		outputBuffer := bytes.NewBuffer([]byte{})
   985  
   986  		args := []string{
   987  			"put", "document", "-p", parser, "-o", "stdout",
   988  		}
   989  		args = append(args, additionalArgs...)
   990  		args = append(args, selector)
   991  		args = append(args, document)
   992  
   993  		cmd.SetOut(outputBuffer)
   994  		cmd.SetIn(strings.NewReader(in))
   995  		cmd.SetArgs(args)
   996  
   997  		err := cmd.Execute()
   998  
   999  		if expErr == nil && err != nil {
  1000  			t.Errorf("expected err %v, got %v", expErr, err)
  1001  			return
  1002  		}
  1003  		if expErr != nil && err == nil {
  1004  			t.Errorf("expected err %v, got %v", expErr, err)
  1005  			return
  1006  		}
  1007  		if expErr != nil && err != nil && err.Error() != expErr.Error() {
  1008  			t.Errorf("expected err %v, got %v", expErr, err)
  1009  			return
  1010  		}
  1011  		if expErr != nil {
  1012  			return
  1013  		}
  1014  
  1015  		output, err := io.ReadAll(outputBuffer)
  1016  		if err != nil {
  1017  			t.Errorf("unexpected error reading output buffer: %s", err)
  1018  			return
  1019  		}
  1020  
  1021  		out = strings.TrimSpace(out)
  1022  		outputStr := strings.TrimSpace(string(output))
  1023  		if out != outputStr {
  1024  			t.Errorf("expected result %v, got %v", out, outputStr)
  1025  		}
  1026  	}
  1027  }
  1028  
  1029  func putStringTest(in string, parser string, selector string, value string, out string, expErr error, additionalArgs ...string) func(t *testing.T) {
  1030  	return putTest(in, "string", parser, selector, value, out, expErr, additionalArgs...)
  1031  }
  1032  
  1033  func putIntTest(in string, parser string, selector string, value string, out string, expErr error, additionalArgs ...string) func(t *testing.T) {
  1034  	return putTest(in, "int", parser, selector, value, out, expErr, additionalArgs...)
  1035  }
  1036  
  1037  func putBoolTest(in string, parser string, selector string, value string, out string, expErr error, additionalArgs ...string) func(t *testing.T) {
  1038  	return putTest(in, "bool", parser, selector, value, out, expErr, additionalArgs...)
  1039  }