package cli_test import ( "testing" "git.eeqj.de/sneak/secret/internal/cli" "github.com/spf13/cobra" "github.com/stretchr/testify/require" ) // TestRejectedMoveWithinVaultLeavesStateUnchanged is a regression test for // https://git.eeqj.de/sneak/secret/issues/73, where a forced move of a secret // onto itself deleted it, also when "work" was spelled two ways, and a failed // move within "work" left "work" the current vault. "default" is the current // vault in every case, and each case runs on its own copy of the state // directory. // //nolint:paralleltest // newTwoVaultFs uses t.Setenv func TestRejectedMoveWithinVaultLeavesStateUnchanged(t *testing.T) { before := snapshotStateDir(t, newTwoVaultFs(t)) require.Equal(t, "default", before[testStateDir+"/currentvault"]) const ( ontoItself = "secret 'x' cannot be moved onto itself" workX = "work:x" ) tests := []struct { command string source, dest string force bool wantErr string }{ {"mv x x", "x", "x", false, ontoItself}, {"mv --force x x", "x", "x", true, ontoItself}, {"mv --force work:x work:", workX, "work:", true, ontoItself}, // An empty destination name defaults to the source name. {`mv --force work:x ""`, workX, "", true, ontoItself}, // "work" is a vault name, so the destination is work:x. {"mv --force work:x work", workX, "work", true, ontoItself}, { "mv work:nosuch work:y", "work:nosuch", "work:y", false, "secret 'nosuch' not found", }, // Only an existing vault is used, so ".." cannot reach the state // directory itself. { "mv --force ..:x ..:y", "..:x", "..:y", true, "vault '..' does not exist", }, // Each of these spells "work" a second way. The spelling is not an // existing vault name, so the move is not taken for a move between // two vaults, which would delete the destination, here the source. { "mv --force work:x work/:x", workX, "work/:x", true, "vault 'work/' does not exist", }, { "mv --force work/:x work:", "work/:x", "work:", true, "vault 'work/' does not exist", }, { "mv --force work:x ./work:x", workX, "./work:x", true, "vault './work' does not exist", }, } for _, tt := range tests { t.Run(tt.command, func(t *testing.T) { fs := newFsFromSnapshot(t, before) c := cli.NewCLIInstanceWithStateDir(fs, testStateDir) err := c.MoveSecret(&cobra.Command{}, tt.source, tt.dest, tt.force) require.Equal(t, before, snapshotStateDir(t, fs)) require.EqualError(t, err, tt.wantErr) }) } } // TestMoveWithinOtherVaultKeepsCurrentVault checks that `secret mv work:x // work:y`, with "default" the current vault, renames "x" to "y" in "work" and // leaves "default" the current vault. // //nolint:paralleltest // newTwoVaultFs uses t.Setenv func TestMoveWithinOtherVaultKeepsCurrentVault(t *testing.T) { fs := newTwoVaultFs(t) c := cli.NewCLIInstanceWithStateDir(fs, testStateDir) err := c.MoveSecret(&cobra.Command{}, "work:x", "work:y", false) require.NoError(t, err) after := snapshotStateDir(t, fs) workSecrets := testStateDir + "/vaults.d/work/secrets.d/" require.Equal(t, "default", after[testStateDir+"/currentvault"]) require.Contains(t, after, workSecrets+"y/") require.NotContains(t, after, workSecrets+"x/") }