test about half of what matters
This commit is contained in:
parent
6f163b5e22
commit
632af1a2be
19
.milpa/commands/joao/test/unit.sh
Normal file
19
.milpa/commands/joao/test/unit.sh
Normal file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright © 2021 Roberto Hidalgo <joao@un.rob.mx>
|
||||
|
||||
cd "$MILPA_REPO_ROOT" || @milpa.fail "could not cd into $MILPA_REPO_ROOT"
|
||||
@milpa.log info "Running unit tests"
|
||||
args=()
|
||||
after_run=complete
|
||||
if [[ "${MILPA_OPT_COVERAGE}" ]]; then
|
||||
after_run=success
|
||||
args=( -coverprofile=coverage.out --coverpkg=./...)
|
||||
fi
|
||||
gotestsum --format testname -- "$MILPA_ARG_SPEC" "${args[@]}" || exit 2
|
||||
@milpa.log "$after_run" "Unit tests passed"
|
||||
|
||||
[[ ! "${MILPA_OPT_COVERAGE}" ]] && exit
|
||||
@milpa.log info "Building coverage report"
|
||||
go tool cover -html=coverage.out -o coverage.html || @milpa.fail "could not build reports"
|
||||
@milpa.log complete "Coverage report ready at coverage.html"
|
13
.milpa/commands/joao/test/unit.yaml
Normal file
13
.milpa/commands/joao/test/unit.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
summary: Runs unit tests
|
||||
description: |
|
||||
Runs unit tests using gotestsum
|
||||
arguments:
|
||||
- name: spec
|
||||
default: ./...
|
||||
description: the package to test
|
||||
values:
|
||||
dirs: "*"
|
||||
options:
|
||||
coverage:
|
||||
type: bool
|
||||
description: if provided, will output coverage reports
|
5
bad-test.yaml
Normal file
5
bad-test.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
_config: !!joao
|
||||
name: some:test
|
||||
vault: bad-example
|
||||
int: -:a\
|
||||
|
105
cmd/fetch_test.go
Normal file
105
cmd/fetch_test.go
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <joao@un.rob.mx>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package cmd_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "git.rob.mx/nidito/joao/cmd"
|
||||
"git.rob.mx/nidito/joao/internal/op-client/mock"
|
||||
"github.com/1Password/connect-sdk-go/onepassword"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func TestFetch(t *testing.T) {
|
||||
mockOPConnect(t)
|
||||
f := testConfig.Fields
|
||||
s := testConfig.Sections
|
||||
defer func() { testConfig.Fields = f; testConfig.Sections = s }()
|
||||
|
||||
testConfig.Sections = append(testConfig.Sections,
|
||||
&onepassword.ItemSection{ID: "o", Label: "o"},
|
||||
&onepassword.ItemSection{ID: "e-fez-tambem", Label: "e-fez-tambem"},
|
||||
)
|
||||
testConfig.Fields = append(testConfig.Fields,
|
||||
&onepassword.ItemField{
|
||||
ID: "o.ganso.gosto",
|
||||
Section: &onepassword.ItemSection{ID: "o", Label: "o"},
|
||||
Type: "STRING",
|
||||
Label: "ganso.gosto",
|
||||
Value: "da dupla",
|
||||
},
|
||||
&onepassword.ItemField{
|
||||
ID: "e-fez-tambem.0",
|
||||
Section: &onepassword.ItemSection{ID: "e-fez-tambem", Label: "e-fez-tambem"},
|
||||
Type: "STRING",
|
||||
Label: "0",
|
||||
Value: "quém!",
|
||||
},
|
||||
&onepassword.ItemField{
|
||||
ID: "e-fez-tambem.1",
|
||||
Section: &onepassword.ItemSection{ID: "e-fez-tambem", Label: "e-fez-tambem"},
|
||||
Type: "STRING",
|
||||
Label: "1",
|
||||
Value: "quém!",
|
||||
},
|
||||
&onepassword.ItemField{
|
||||
ID: "e-fez-tambem.2",
|
||||
Section: &onepassword.ItemSection{ID: "e-fez-tambem", Label: "e-fez-tambem"},
|
||||
Type: "STRING",
|
||||
Label: "2",
|
||||
Value: "quém!",
|
||||
})
|
||||
mock.Update(testConfig)
|
||||
root := fromProjectRoot()
|
||||
out := bytes.Buffer{}
|
||||
Fetch.SetBindings()
|
||||
cmd := &cobra.Command{}
|
||||
cmd.Flags().Bool("dry-run", true, "")
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
Fetch.Cobra = cmd
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
err := Fetch.Run(cmd, []string{root + "/test.yaml"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("could not get: %s", err)
|
||||
}
|
||||
|
||||
expected := `_config: !!joao
|
||||
name: some:test
|
||||
vault: example
|
||||
# not sorted on purpose
|
||||
int: 1 # line
|
||||
# foot
|
||||
string: pato
|
||||
bool: false
|
||||
secret: !!secret very secret
|
||||
nested:
|
||||
string: quem
|
||||
int: 1
|
||||
secret: !!secret very secret
|
||||
bool: true
|
||||
list:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
list:
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
o:
|
||||
ganso:
|
||||
gosto: da dupla
|
||||
e-fez-tambem:
|
||||
- quém!
|
||||
- quém!
|
||||
- quém!`
|
||||
|
||||
if got := out.String(); strings.TrimSpace(got) != expected {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
15
cmd/get.go
15
cmd/get.go
@ -77,6 +77,7 @@ looks at the filesystem or remotely, using 1password (over the CLI if available,
|
||||
}
|
||||
|
||||
if query == "" || query == "." {
|
||||
var bytes []byte
|
||||
switch format {
|
||||
case "yaml", "raw", "diff-yaml":
|
||||
modes := []config.OutputMode{}
|
||||
@ -86,22 +87,18 @@ looks at the filesystem or remotely, using 1password (over the CLI if available,
|
||||
if format == "diff-yaml" {
|
||||
modes = append(modes, config.OutputModeNoComments, config.OutputModeSorted)
|
||||
}
|
||||
bytes, err := cfg.AsYAML(modes...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = cmd.Cobra.OutOrStdout().Write(bytes)
|
||||
return err
|
||||
bytes, err = cfg.AsYAML(modes...)
|
||||
case "json", "op":
|
||||
bytes, err := cfg.AsJSON(redacted, format == "op")
|
||||
bytes, err = cfg.AsJSON(redacted, format == "op")
|
||||
default:
|
||||
return fmt.Errorf("unknown format %s", format)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = cmd.Cobra.OutOrStdout().Write(bytes)
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("unknown format %s", format)
|
||||
}
|
||||
|
||||
parts := strings.Split(query, ".")
|
||||
|
||||
|
331
cmd/get_test.go
331
cmd/get_test.go
@ -11,9 +11,166 @@ import (
|
||||
"testing"
|
||||
|
||||
. "git.rob.mx/nidito/joao/cmd"
|
||||
opclient "git.rob.mx/nidito/joao/internal/op-client"
|
||||
"git.rob.mx/nidito/joao/internal/op-client/mock"
|
||||
"github.com/1Password/connect-sdk-go/connect"
|
||||
"github.com/1Password/connect-sdk-go/onepassword"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var testConfig = &onepassword.Item{
|
||||
Title: "some:test",
|
||||
Vault: onepassword.ItemVault{ID: "example"},
|
||||
Category: "PASSWORD",
|
||||
Sections: []*onepassword.ItemSection{
|
||||
{ID: "~annotations", Label: "~annotations"},
|
||||
// {ID: "nested", Label: "nested"},
|
||||
{ID: "list", Label: "list"},
|
||||
},
|
||||
Fields: []*onepassword.ItemField{
|
||||
{
|
||||
ID: "password",
|
||||
Type: "CONCEALED",
|
||||
Purpose: "PASSWORD",
|
||||
Label: "password",
|
||||
Value: "56615e9be5f0ce5f97d5b446faaa1d39f95a13a1ea8326ae933c3d29eb29735c",
|
||||
},
|
||||
{
|
||||
ID: "notesPlain",
|
||||
Type: "STRING",
|
||||
Purpose: "NOTES",
|
||||
Label: "notesPlain",
|
||||
Value: "flushed by joao",
|
||||
},
|
||||
{
|
||||
ID: "~annotations.int",
|
||||
Section: &onepassword.ItemSection{ID: "~annotations", Label: "~annotations"},
|
||||
Type: "STRING",
|
||||
Label: "int",
|
||||
Value: "int",
|
||||
},
|
||||
{
|
||||
ID: "int",
|
||||
Type: "STRING",
|
||||
Label: "int",
|
||||
Value: "1",
|
||||
},
|
||||
{
|
||||
ID: "string",
|
||||
Type: "STRING",
|
||||
Label: "string",
|
||||
Value: "pato",
|
||||
},
|
||||
{
|
||||
ID: "~annotations.bool",
|
||||
Section: &onepassword.ItemSection{ID: "~annotations", Label: "~annotations"},
|
||||
Type: "STRING",
|
||||
Label: "bool",
|
||||
Value: "bool",
|
||||
},
|
||||
{
|
||||
ID: "bool",
|
||||
Type: "STRING",
|
||||
Label: "bool",
|
||||
Value: "false",
|
||||
},
|
||||
{
|
||||
ID: "~annotations.secret",
|
||||
Section: &onepassword.ItemSection{ID: "~annotations", Label: "~annotations"},
|
||||
Type: "STRING",
|
||||
Label: "secret",
|
||||
Value: "secret",
|
||||
},
|
||||
{
|
||||
ID: "secret",
|
||||
Type: "CONCEALED",
|
||||
Label: "secret",
|
||||
Value: "very secret",
|
||||
},
|
||||
{
|
||||
ID: "nested.string",
|
||||
Section: &onepassword.ItemSection{ID: "nested", Label: "nested"},
|
||||
Type: "STRING",
|
||||
Label: "string",
|
||||
Value: "quem",
|
||||
},
|
||||
{
|
||||
ID: "~annotations.nested.int",
|
||||
Section: &onepassword.ItemSection{ID: "~annotations", Label: "~annotations"},
|
||||
Type: "STRING",
|
||||
Label: "nested.int",
|
||||
Value: "int",
|
||||
},
|
||||
{
|
||||
ID: "nested.int",
|
||||
Section: &onepassword.ItemSection{ID: "nested", Label: "nested"},
|
||||
Type: "STRING",
|
||||
Label: "int",
|
||||
Value: "1",
|
||||
},
|
||||
{
|
||||
ID: "~annotations.nested.secret",
|
||||
Section: &onepassword.ItemSection{ID: "~annotations", Label: "~annotations"},
|
||||
Type: "STRING",
|
||||
Label: "nested.secret",
|
||||
Value: "secret",
|
||||
},
|
||||
{
|
||||
ID: "nested.secret",
|
||||
Section: &onepassword.ItemSection{ID: "nested", Label: "nested"},
|
||||
Type: "CONCEALED",
|
||||
Label: "secret",
|
||||
Value: "very secret",
|
||||
},
|
||||
{
|
||||
ID: "~annotations.nested.bool",
|
||||
Section: &onepassword.ItemSection{ID: "~annotations", Label: "~annotations"},
|
||||
Type: "STRING",
|
||||
Label: "nested.bool",
|
||||
Value: "bool",
|
||||
},
|
||||
{
|
||||
ID: "nested.bool",
|
||||
Section: &onepassword.ItemSection{ID: "nested", Label: "nested"},
|
||||
Type: "STRING",
|
||||
Label: "bool",
|
||||
Value: "true",
|
||||
},
|
||||
{
|
||||
ID: "list.0",
|
||||
Section: &onepassword.ItemSection{ID: "list", Label: "list"},
|
||||
Type: "STRING",
|
||||
Label: "0",
|
||||
Value: "one",
|
||||
},
|
||||
{
|
||||
ID: "list.1",
|
||||
Section: &onepassword.ItemSection{ID: "list", Label: "list"},
|
||||
Type: "STRING",
|
||||
Label: "1",
|
||||
Value: "two",
|
||||
},
|
||||
{
|
||||
ID: "list.2",
|
||||
Section: &onepassword.ItemSection{ID: "list", Label: "list"},
|
||||
Type: "STRING",
|
||||
Label: "2",
|
||||
Value: "three",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func mockOPConnect(t *testing.T) {
|
||||
t.Helper()
|
||||
opclient.ConnectClientFactory = func(host, token, userAgent string) connect.Client {
|
||||
return &mock.Client{}
|
||||
}
|
||||
client := opclient.NewConnect("", "")
|
||||
opclient.Use(client)
|
||||
mock.Add(testConfig)
|
||||
}
|
||||
|
||||
func fromProjectRoot() string {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
dir := path.Join(path.Dir(filename), "../")
|
||||
@ -24,29 +181,42 @@ func fromProjectRoot() string {
|
||||
return wd
|
||||
}
|
||||
|
||||
func TestGetRedacted(t *testing.T) {
|
||||
func TestGetBadYAML(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
out := bytes.Buffer{}
|
||||
Get.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.Flags().Bool("redacted", true, "")
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
Get.Cobra = cmd
|
||||
err := Get.Run(cmd, []string{root + "/test.yaml", ".", "--redacted"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("could not get: %s", err)
|
||||
err := Get.Run(cmd, []string{root + "/bad-test.yaml", "."})
|
||||
if err == nil {
|
||||
t.Fatalf("Did not throw on bad path: %s", out.String())
|
||||
}
|
||||
|
||||
expected, err := os.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file: %s", err)
|
||||
wantedPrefix := "could not parse file"
|
||||
wantedSuffix := "/bad-test.yaml as yaml: line 4: mapping values are not allowed in this context"
|
||||
if got := err.Error(); !(strings.HasPrefix(got, wantedPrefix) && strings.HasSuffix(got, wantedSuffix)) {
|
||||
t.Fatalf("Failed with bad error, wanted %s /some-path%s, got %s", wantedPrefix, wantedSuffix, got)
|
||||
}
|
||||
}
|
||||
|
||||
got := out.String()
|
||||
if strings.TrimSpace(got) != strings.ReplaceAll(strings.TrimSpace(string(expected)), " very secret", "") {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
func TestGetBadPath(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
Get.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
Get.Cobra = cmd
|
||||
err := Get.Run(cmd, []string{root + "/does-not-exist.yaml", "."})
|
||||
if err == nil {
|
||||
t.Fatalf("Did not throw on bad path: %s", out.String())
|
||||
}
|
||||
wantedPrefix := "could not read file"
|
||||
wantedSuffix := "/does-not-exist.yaml"
|
||||
|
||||
if got := err.Error(); !(strings.HasPrefix(got, wantedPrefix) && strings.HasSuffix(got, wantedSuffix)) {
|
||||
t.Fatalf("Failed with bad error, wanted %s /some-path%s, got %s", wantedPrefix, wantedSuffix, got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,8 +240,32 @@ func TestGetNormal(t *testing.T) {
|
||||
t.Fatalf("could not read file: %s", err)
|
||||
}
|
||||
|
||||
got := out.String()
|
||||
if strings.TrimSpace(got) != strings.TrimSpace(string(expected)) {
|
||||
if got := out.String(); strings.TrimSpace(got) != strings.TrimSpace(string(expected)) {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRedacted(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
out := bytes.Buffer{}
|
||||
Get.SetBindings()
|
||||
cmd := &cobra.Command{}
|
||||
cmd.Flags().Bool("redacted", true, "")
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
Get.Cobra = cmd
|
||||
err := Get.Run(cmd, []string{root + "/test.yaml", ".", "--redacted"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("could not get: %s", err)
|
||||
}
|
||||
|
||||
expected, err := os.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file: %s", err)
|
||||
}
|
||||
|
||||
if got := out.String(); strings.TrimSpace(got) != strings.ReplaceAll(strings.TrimSpace(string(expected)), " very secret", "") {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
@ -82,6 +276,7 @@ func TestGetPath(t *testing.T) {
|
||||
Get.SetBindings()
|
||||
cmd := &cobra.Command{}
|
||||
cmd.Flags().Bool("redacted", false, "")
|
||||
cmd.Flags().StringP("output", "o", "yaml", "")
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
Get.Cobra = cmd
|
||||
@ -92,8 +287,29 @@ func TestGetPath(t *testing.T) {
|
||||
}
|
||||
|
||||
expected := "very secret"
|
||||
got := out.String()
|
||||
if strings.TrimSpace(got) != strings.TrimSpace(expected) {
|
||||
if got := out.String(); strings.TrimSpace(got) != strings.TrimSpace(expected) {
|
||||
t.Fatalf("did not get expected scalar output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
|
||||
out = bytes.Buffer{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
err = Get.Run(cmd, []string{root + "/test.yaml", "nested", "--output", "diff-yaml"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("could not get: %s", err)
|
||||
}
|
||||
|
||||
expected = `bool: true
|
||||
int: 1
|
||||
list:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
secret: very secret
|
||||
string: quem`
|
||||
|
||||
if got := out.String(); strings.TrimSpace(got) != strings.TrimSpace(expected) {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
@ -116,11 +332,14 @@ func TestGetPathCollection(t *testing.T) {
|
||||
|
||||
expected := `bool: true
|
||||
int: 1
|
||||
list:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
secret: very secret
|
||||
string: quem`
|
||||
|
||||
got := out.String()
|
||||
if strings.TrimSpace(got) != expected {
|
||||
if got := out.String(); strings.TrimSpace(got) != expected {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
@ -153,13 +372,16 @@ list:
|
||||
nested:
|
||||
bool: true
|
||||
int: 1
|
||||
list:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
secret: !!secret very secret
|
||||
string: quem
|
||||
secret: !!secret very secret
|
||||
string: "pato"`
|
||||
string: pato`
|
||||
|
||||
got := out.String()
|
||||
if strings.TrimSpace(got) != expected {
|
||||
if got := out.String(); strings.TrimSpace(got) != expected {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
@ -180,10 +402,9 @@ func TestGetJSON(t *testing.T) {
|
||||
t.Fatalf("could not get: %s", err)
|
||||
}
|
||||
|
||||
expected := `{"bool":false,"int":1,"list":["one","two","three"],"nested":{"bool":true,"int":1,"secret":"very secret","string":"quem"},"secret":"very secret","string":"pato"}`
|
||||
expected := `{"bool":false,"int":1,"list":["one","two","three"],"nested":{"bool":true,"int":1,"list":[1,2,3],"secret":"very secret","string":"quem"},"secret":"very secret","string":"pato"}`
|
||||
|
||||
got := out.String()
|
||||
if strings.TrimSpace(got) != expected {
|
||||
if got := out.String(); strings.TrimSpace(got) != expected {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
@ -204,10 +425,9 @@ func TestGetJSONPathScalar(t *testing.T) {
|
||||
t.Fatalf("could not get: %s", err)
|
||||
}
|
||||
|
||||
expected := `very secret`
|
||||
expected := `very secret` // nolint: ifshort
|
||||
|
||||
got := out.String()
|
||||
if strings.TrimSpace(got) != expected {
|
||||
if got := out.String(); strings.TrimSpace(got) != expected {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
@ -228,10 +448,9 @@ func TestGetJSONPathCollection(t *testing.T) {
|
||||
t.Fatalf("could not get: %s", err)
|
||||
}
|
||||
|
||||
expected := `{"bool":true,"int":1,"secret":"very secret","string":"quem"}`
|
||||
expected := `{"bool":true,"int":1,"list":[1,2,3],"secret":"very secret","string":"quem"}`
|
||||
|
||||
got := out.String()
|
||||
if strings.TrimSpace(got) != expected {
|
||||
if got := out.String(); strings.TrimSpace(got) != expected {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
@ -252,10 +471,9 @@ func TestGetJSONRedacted(t *testing.T) {
|
||||
t.Fatalf("could not get: %s", err)
|
||||
}
|
||||
|
||||
expected := `{"bool":false,"int":1,"list":["one","two","three"],"nested":{"bool":true,"int":1,"secret":"","string":"quem"},"secret":"","string":"pato"}`
|
||||
expected := `{"bool":false,"int":1,"list":["one","two","three"],"nested":{"bool":true,"int":1,"list":[1,2,3],"secret":"","string":"quem"},"secret":"","string":"pato"}`
|
||||
|
||||
got := out.String()
|
||||
if strings.TrimSpace(got) != expected {
|
||||
if got := out.String(); strings.TrimSpace(got) != expected {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
@ -275,10 +493,47 @@ func TestGetJSONOP(t *testing.T) {
|
||||
t.Fatalf("could not get: %s", err)
|
||||
}
|
||||
|
||||
expected := `{"id":"","title":"some:test","vault":{"id":"example"},"category":"PASSWORD","sections":[{"id":"~annotations","label":"~annotations"},{"id":"nested","label":"nested"}],"fields":[{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"56615e9be5f0ce5f97d5b446faaa1d39f95a13a1ea8326ae933c3d29eb29735c"},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain","value":"flushed by joao"},{"id":"~annotations.int","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"int","value":"int"},{"id":"int","type":"STRING","label":"int","value":"1"},{"id":"string","type":"STRING","label":"string","value":"pato"},{"id":"~annotations.bool","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"bool","value":"bool"},{"id":"bool","type":"STRING","label":"bool","value":"false"},{"id":"~annotations.secret","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"secret","value":"secret"},{"id":"secret","type":"CONCEALED","label":"secret","value":"very secret"},{"id":"nested.string","section":{"id":"nested"},"type":"STRING","label":"string","value":"quem"},{"id":"~annotations.nested.int","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"nested.int","value":"int"},{"id":"nested.int","section":{"id":"nested"},"type":"STRING","label":"int","value":"1"},{"id":"~annotations.nested.secret","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"nested.secret","value":"secret"},{"id":"nested.secret","section":{"id":"nested"},"type":"CONCEALED","label":"secret","value":"very secret"},{"id":"~annotations.nested.bool","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"nested.bool","value":"bool"},{"id":"nested.bool","section":{"id":"nested"},"type":"STRING","label":"bool","value":"true"},{"id":"list.0","section":{"id":"list"},"type":"STRING","label":"0","value":"one"},{"id":"list.1","section":{"id":"list"},"type":"STRING","label":"1","value":"two"},{"id":"list.2","section":{"id":"list"},"type":"STRING","label":"2","value":"three"}],"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}`
|
||||
expected := `{"id":"","title":"some:test","vault":{"id":"example"},"category":"PASSWORD","sections":[{"id":"~annotations","label":"~annotations"},{"id":"nested","label":"nested"},{"id":"list","label":"list"}],"fields":[{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"cedbdf86fb15cf1237569e9b3188372d623aea9d6a707401aca656645590227c"},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain","value":"flushed by joao"},{"id":"~annotations.int","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"int","value":"int"},{"id":"int","type":"STRING","label":"int","value":"1"},{"id":"string","type":"STRING","label":"string","value":"pato"},{"id":"~annotations.bool","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"bool","value":"bool"},{"id":"bool","type":"STRING","label":"bool","value":"false"},{"id":"~annotations.secret","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"secret","value":"secret"},{"id":"secret","type":"CONCEALED","label":"secret","value":"very secret"},{"id":"nested.string","section":{"id":"nested"},"type":"STRING","label":"string","value":"quem"},{"id":"~annotations.nested.int","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"nested.int","value":"int"},{"id":"nested.int","section":{"id":"nested"},"type":"STRING","label":"int","value":"1"},{"id":"~annotations.nested.secret","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"nested.secret","value":"secret"},{"id":"nested.secret","section":{"id":"nested"},"type":"CONCEALED","label":"secret","value":"very secret"},{"id":"~annotations.nested.bool","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"nested.bool","value":"bool"},{"id":"nested.bool","section":{"id":"nested"},"type":"STRING","label":"bool","value":"true"},{"id":"~annotations.nested.list.0","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"nested.list.0","value":"int"},{"id":"nested.list.0","section":{"id":"nested"},"type":"STRING","label":"list.0","value":"1"},{"id":"~annotations.nested.list.1","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"nested.list.1","value":"int"},{"id":"nested.list.1","section":{"id":"nested"},"type":"STRING","label":"list.1","value":"2"},{"id":"~annotations.nested.list.2","section":{"id":"~annotations","label":"~annotations"},"type":"STRING","label":"nested.list.2","value":"int"},{"id":"nested.list.2","section":{"id":"nested"},"type":"STRING","label":"list.2","value":"3"},{"id":"list.0","section":{"id":"list"},"type":"STRING","label":"0","value":"one"},{"id":"list.1","section":{"id":"list"},"type":"STRING","label":"1","value":"two"},{"id":"list.2","section":{"id":"list"},"type":"STRING","label":"2","value":"three"}],"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}`
|
||||
|
||||
got := out.String()
|
||||
if strings.TrimSpace(got) != expected {
|
||||
if got := out.String(); strings.TrimSpace(got) != expected {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRemote(t *testing.T) {
|
||||
mockOPConnect(t)
|
||||
root := fromProjectRoot()
|
||||
out := bytes.Buffer{}
|
||||
Get.SetBindings()
|
||||
cmd := &cobra.Command{}
|
||||
cmd.Flags().Bool("redacted", false, "")
|
||||
cmd.Flags().Bool("remote", true, "")
|
||||
cmd.Flags().StringP("output", "o", "diff-yaml", "")
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
Get.Cobra = cmd
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
err := Get.Run(cmd, []string{root + "/test.yaml", ".", "--output", "diff-yaml", "--remote"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("could not get: %s", err)
|
||||
}
|
||||
|
||||
expected := `bool: false
|
||||
int: 1
|
||||
list:
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
nested:
|
||||
bool: true
|
||||
int: 1
|
||||
secret: !!secret very secret
|
||||
string: quem
|
||||
secret: !!secret very secret
|
||||
string: pato`
|
||||
|
||||
if got := out.String(); strings.TrimSpace(got) != expected {
|
||||
t.Fatalf("did not get expected output:\nwanted: %s\ngot: %s", expected, got)
|
||||
}
|
||||
}
|
||||
|
10
cmd/set.go
10
cmd/set.go
@ -5,6 +5,7 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@ -89,7 +90,7 @@ Will read values from stdin (or ﹅--from﹅ a file) and store it at the ﹅PATH
|
||||
}
|
||||
|
||||
if delete && input != "/dev/stdin" {
|
||||
logrus.Warn("Ignoring --file while deleting")
|
||||
logrus.Warn("Ignoring --input while deleting")
|
||||
}
|
||||
|
||||
cfg, err = config.Load(path, false)
|
||||
@ -104,7 +105,12 @@ Will read values from stdin (or ﹅--from﹅ a file) and store it at the ﹅PATH
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
valueBytes, err := os.ReadFile(input)
|
||||
var valueBytes []byte
|
||||
if input == "/dev/stdin" {
|
||||
valueBytes, err = ioutil.ReadAll(cmd.Cobra.InOrStdin())
|
||||
} else {
|
||||
valueBytes, err = os.ReadFile(input)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
457
cmd/set_test.go
Normal file
457
cmd/set_test.go
Normal file
@ -0,0 +1,457 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <joao@un.rob.mx>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package cmd_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "git.rob.mx/nidito/joao/cmd"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func tempTestYaml(root, name string, data []byte) (string, func(), error) {
|
||||
path := fmt.Sprintf("%s/test-%s.yaml", root, name)
|
||||
if err := ioutil.WriteFile(path, data, fs.FileMode(0644)); err != nil {
|
||||
return path, nil, fmt.Errorf("could not create test file")
|
||||
}
|
||||
return path, func() { os.Remove(path) }, nil
|
||||
}
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
Set.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
stdin := bytes.Buffer{}
|
||||
stdin.Write([]byte("pato\nganso\nmarreco\n"))
|
||||
cmd.SetIn(&stdin)
|
||||
Set.Cobra = cmd
|
||||
cmd.Flags().Bool("secret", false, "")
|
||||
cmd.Flags().Bool("delete", false, "")
|
||||
cmd.Flags().Bool("json", false, "")
|
||||
cmd.Flags().Bool("flush", false, "")
|
||||
original, err := ioutil.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
path, cleanup, err := tempTestYaml(root, "set-plain", original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
err = Set.Run(cmd, []string{path, "string"})
|
||||
if err != nil {
|
||||
t.Fatalf("Threw on good set: %s", err)
|
||||
}
|
||||
|
||||
changed, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
if string(changed) == string(original) {
|
||||
t.Fatal("Did not change file")
|
||||
}
|
||||
|
||||
if !strings.Contains(string(changed), `
|
||||
string: |-
|
||||
pato
|
||||
ganso
|
||||
marreco`) {
|
||||
t.Fatalf("Did not contain expected new string, got:\n%s", changed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetSecret(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
Set.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
stdin := bytes.Buffer{}
|
||||
stdin.Write([]byte("new secret\n"))
|
||||
cmd.SetIn(&stdin)
|
||||
Set.Cobra = cmd
|
||||
cmd.Flags().Bool("secret", true, "")
|
||||
cmd.Flags().Bool("delete", false, "")
|
||||
cmd.Flags().Bool("json", false, "")
|
||||
cmd.Flags().Bool("flush", false, "")
|
||||
original, err := ioutil.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
path, cleanup, err := tempTestYaml(root, "set-plain", original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
err = Set.Run(cmd, []string{path, "secret", "--secret"})
|
||||
if err != nil {
|
||||
t.Fatalf("Threw on good set: %s", err)
|
||||
}
|
||||
|
||||
changed, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
if string(changed) == string(original) {
|
||||
t.Fatal("Did not change file")
|
||||
}
|
||||
|
||||
if !strings.Contains(string(changed), "\nsecret: !!secret new secret\n") {
|
||||
t.Fatalf("Did not contain expected new string, got:\n%s", changed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetFromFile(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
Set.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
Set.Cobra = cmd
|
||||
cmd.Flags().Bool("secret", false, "")
|
||||
cmd.Flags().Bool("delete", false, "")
|
||||
cmd.Flags().Bool("json", false, "")
|
||||
cmd.Flags().Bool("flush", false, "")
|
||||
original, err := ioutil.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
dataPath, dataCleanup, err := tempTestYaml(root, "set-from-file-data", []byte("ganso"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer dataCleanup()
|
||||
cmd.Flags().StringP("input", "i", dataPath, "")
|
||||
|
||||
path, cleanup, err := tempTestYaml(root, "set-from-file", original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
err = Set.Run(cmd, []string{path, "string", "--input", dataPath})
|
||||
if err != nil {
|
||||
t.Fatalf("Threw on good set: %s", err)
|
||||
}
|
||||
|
||||
changed, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
if string(changed) == string(original) {
|
||||
t.Fatal("Did not change file")
|
||||
}
|
||||
|
||||
if !strings.Contains(string(changed), "\nstring: ganso\n") {
|
||||
t.Fatalf("Did not contain expected new string, got:\n%s", changed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetNew(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
Set.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
stdin := bytes.Buffer{}
|
||||
stdin.Write([]byte("pato\nganso\nmarreco\ncisne\n"))
|
||||
cmd.SetIn(&stdin)
|
||||
Set.Cobra = cmd
|
||||
cmd.Flags().Bool("secret", false, "")
|
||||
cmd.Flags().Bool("delete", false, "")
|
||||
cmd.Flags().Bool("json", false, "")
|
||||
cmd.Flags().Bool("flush", false, "")
|
||||
cmd.Flags().StringP("input", "i", "/dev/stdin", "")
|
||||
original, err := ioutil.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
path, cleanup, err := tempTestYaml(root, "set-new-key", original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
err = Set.Run(cmd, []string{path, "quarteto"})
|
||||
if err != nil {
|
||||
t.Fatalf("Threw on good new set: %s", err)
|
||||
}
|
||||
|
||||
changed, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
if string(changed) == string(original) {
|
||||
t.Fatal("Did not change file")
|
||||
}
|
||||
|
||||
if !strings.Contains(string(changed), `
|
||||
quarteto: |-
|
||||
pato
|
||||
ganso
|
||||
marreco
|
||||
cisne`) {
|
||||
t.Fatalf("Did not contain expected new string, got:\n%s", changed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetNested(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
Set.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
stdin := bytes.Buffer{}
|
||||
stdin.Write([]byte("tico"))
|
||||
cmd.SetIn(&stdin)
|
||||
Set.Cobra = cmd
|
||||
cmd.Flags().Bool("secret", false, "")
|
||||
cmd.Flags().Bool("delete", false, "")
|
||||
cmd.Flags().Bool("json", false, "")
|
||||
cmd.Flags().Bool("flush", false, "")
|
||||
cmd.Flags().StringP("input", "i", "/dev/stdin", "")
|
||||
original, err := ioutil.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
path, cleanup, err := tempTestYaml(root, "set-nested-key", original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
err = Set.Run(cmd, []string{path, "nested.tico"})
|
||||
if err != nil {
|
||||
t.Fatalf("Threw on good nested set: %s", err)
|
||||
}
|
||||
|
||||
changed, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
if string(changed) == string(original) {
|
||||
t.Fatal("Did not change file")
|
||||
}
|
||||
|
||||
if !strings.Contains(string(changed), `
|
||||
tico: tico
|
||||
`) {
|
||||
t.Fatalf("Did not contain expected new string, got:\n%s", changed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetJSON(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
Set.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
stdin := bytes.Buffer{}
|
||||
stdin.Write([]byte(`{"foram": "ensaiar", "para": "começar"}`))
|
||||
cmd.SetIn(&stdin)
|
||||
Set.Cobra = cmd
|
||||
cmd.Flags().Bool("secret", false, "")
|
||||
cmd.Flags().Bool("delete", false, "")
|
||||
cmd.Flags().Bool("json", true, "")
|
||||
cmd.Flags().Bool("flush", false, "")
|
||||
cmd.Flags().StringP("input", "i", "/dev/stdin", "")
|
||||
original, err := ioutil.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
path, cleanup, err := tempTestYaml(root, "set-json", original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
err = Set.Run(cmd, []string{path, "na-beira-da-lagoa"})
|
||||
if err != nil {
|
||||
t.Fatalf("Threw on good nested set: %s", err)
|
||||
}
|
||||
|
||||
changed, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
if string(changed) == string(original) {
|
||||
t.Fatal("Did not change file")
|
||||
}
|
||||
|
||||
if !strings.Contains(string(changed), `
|
||||
na-beira-da-lagoa:
|
||||
foram: ensaiar
|
||||
para: começar
|
||||
`) {
|
||||
t.Fatalf("Did not contain expected new entry tree, got:\n%s", changed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetList(t *testing.T) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
|
||||
root := fromProjectRoot()
|
||||
Set.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
stdin := bytes.Buffer{}
|
||||
stdin.Write([]byte("um"))
|
||||
cmd.SetIn(&stdin)
|
||||
Set.Cobra = cmd
|
||||
cmd.Flags().Bool("secret", false, "")
|
||||
cmd.Flags().Bool("delete", false, "")
|
||||
cmd.Flags().Bool("json", false, "")
|
||||
cmd.Flags().Bool("flush", false, "")
|
||||
cmd.Flags().StringP("input", "i", "/dev/stdin", "")
|
||||
original, err := ioutil.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
path, cleanup, err := tempTestYaml(root, "set-list-key", original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
err = Set.Run(cmd, []string{path, "asdf.0"})
|
||||
if err != nil {
|
||||
t.Fatalf("Threw on good nested set: %s", err)
|
||||
}
|
||||
|
||||
changed, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
if string(changed) == string(original) {
|
||||
t.Fatal("Did not change file")
|
||||
}
|
||||
|
||||
if !strings.Contains(string(changed), `
|
||||
asdf:
|
||||
- um
|
||||
`) {
|
||||
t.Fatalf("Did not contain expected new string, got:\n%s", changed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
Set.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
Set.Cobra = cmd
|
||||
cmd.Flags().Bool("secret", false, "")
|
||||
cmd.Flags().Bool("delete", true, "")
|
||||
cmd.Flags().Bool("json", false, "")
|
||||
cmd.Flags().Bool("flush", false, "")
|
||||
cmd.Flags().StringP("input", "i", "/dev/stdin", "")
|
||||
original, err := ioutil.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
path, cleanup, err := tempTestYaml(root, "set-delete-key", original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
err = Set.Run(cmd, []string{path, "string", "--delete"})
|
||||
if err != nil {
|
||||
t.Fatalf("Threw on good set delete: %s", err)
|
||||
}
|
||||
|
||||
changed, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
if string(changed) == string(original) {
|
||||
t.Fatal("Did not change file")
|
||||
}
|
||||
|
||||
if strings.Contains(string(changed), `
|
||||
string: pato
|
||||
`) {
|
||||
t.Fatalf("Still contains deleted key, got:\n%s", changed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteNested(t *testing.T) {
|
||||
root := fromProjectRoot()
|
||||
Set.SetBindings()
|
||||
out := bytes.Buffer{}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&out)
|
||||
Set.Cobra = cmd
|
||||
cmd.Flags().Bool("secret", false, "")
|
||||
cmd.Flags().Bool("delete", true, "")
|
||||
cmd.Flags().Bool("json", false, "")
|
||||
cmd.Flags().Bool("flush", false, "")
|
||||
cmd.Flags().StringP("input", "i", "/dev/stdin", "")
|
||||
original, err := ioutil.ReadFile(root + "/test.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
path, cleanup, err := tempTestYaml(root, "set-delete-nested-key", original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
err = Set.Run(cmd, []string{path, "nested.string", "--delete"})
|
||||
if err != nil {
|
||||
t.Fatalf("Threw on good set delete nested: %s", err)
|
||||
}
|
||||
|
||||
changed, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file")
|
||||
}
|
||||
|
||||
if string(changed) == string(original) {
|
||||
t.Fatal("Did not change file")
|
||||
}
|
||||
|
||||
if strings.Contains(string(changed), `
|
||||
string: quem
|
||||
`) {
|
||||
t.Fatalf("Still contains deleted nested key, got:\n%s", changed)
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import (
|
||||
var Plugin = &command.Command{
|
||||
Path: []string{"vault", "server"},
|
||||
Summary: "Starts a vault-joao-plugin server",
|
||||
Description: `Runs ﹅joao﹅ as a vault plugin.
|
||||
Description: `Runs ﹅joao﹅ as a vault plugin. See https://developer.hashicorp.com/vault/docs/plugins
|
||||
|
||||
You'll need to install ﹅joao﹅ in the machine running ﹅vault﹅ to ﹅plugin_directory﹅ as specified by vault's config. The installed ﹅joao﹅ executable needs to be executable for the user running vault only.
|
||||
|
||||
|
@ -3,10 +3,14 @@
|
||||
package opclient
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/1Password/connect-sdk-go/connect"
|
||||
op "github.com/1Password/connect-sdk-go/onepassword"
|
||||
)
|
||||
|
||||
var ConnectClientFactory func(host, token, userAgent string) connect.Client = connect.NewClientWithUserAgent
|
||||
|
||||
// UUIDLength defines the required length of UUIDs.
|
||||
const UUIDLength = 26
|
||||
|
||||
@ -33,7 +37,7 @@ type Connect struct {
|
||||
const userAgent = "nidito-joao"
|
||||
|
||||
func NewConnect(host, token string) *Connect {
|
||||
client := connect.NewClientWithUserAgent(host, token, userAgent)
|
||||
client := ConnectClientFactory(host, token, userAgent)
|
||||
return &Connect{client: client}
|
||||
}
|
||||
|
||||
@ -41,12 +45,27 @@ func (b *Connect) Get(vault, name string) (*op.Item, error) {
|
||||
return b.client.GetItem(name, vault)
|
||||
}
|
||||
|
||||
func (b *Connect) Update(item *op.Item) error {
|
||||
func (b *Connect) Update(item *op.Item, remote *op.Item) error {
|
||||
_, err := b.client.UpdateItem(item, item.Vault.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Connect) List(vault, prefix string) ([]string, error) {
|
||||
// TODO: get this done
|
||||
return nil, nil
|
||||
items, err := b.client.GetItems(vault)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := []string{}
|
||||
for _, item := range items {
|
||||
if prefix != "" && !strings.HasPrefix(item.Title, prefix) {
|
||||
continue
|
||||
}
|
||||
res = append(res, item.Title)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (b *Connect) Create(item *op.Item) error {
|
||||
_, err := b.client.CreateItem(item, item.Vault.ID)
|
||||
return err
|
||||
}
|
||||
|
@ -89,6 +89,12 @@ func (cfg *Config) Set(path []string, data []byte, isSecret, parseEntry bool) er
|
||||
if err := yaml.Unmarshal(data, newEntry); err != nil {
|
||||
return err
|
||||
}
|
||||
if newEntry.Kind == yaml.MappingNode || newEntry.Kind == yaml.SequenceNode {
|
||||
newEntry.Style = yaml.FoldedStyle | yaml.LiteralStyle
|
||||
for _, v := range newEntry.Content {
|
||||
v.Style = yaml.FlowStyle
|
||||
}
|
||||
}
|
||||
} else {
|
||||
valueStr = strings.Trim(valueStr, "\n")
|
||||
if isSecret {
|
||||
@ -108,12 +114,14 @@ func (cfg *Config) Set(path []string, data []byte, isSecret, parseEntry bool) er
|
||||
entry := cfg.Tree
|
||||
for idx, key := range path {
|
||||
if len(path)-1 == idx {
|
||||
dst := entry.ChildNamed(key)
|
||||
if dst == nil {
|
||||
if entry.Kind == yaml.MappingNode {
|
||||
if dst := entry.ChildNamed(key); dst == nil {
|
||||
key := NewEntry(key, yaml.ScalarNode)
|
||||
if entry.Kind == yaml.MappingNode {
|
||||
logrus.Infof("setting new map key %v", newEntry.Path)
|
||||
entry.Content = append(entry.Content, key, newEntry)
|
||||
} else {
|
||||
logrus.Infof("setting new list key %v", newEntry.Path)
|
||||
entry.Kind = yaml.SequenceNode
|
||||
entry.Content = append(entry.Content, newEntry)
|
||||
}
|
||||
} else {
|
||||
@ -131,12 +139,15 @@ func (cfg *Config) Set(path []string, data []byte, isSecret, parseEntry bool) er
|
||||
}
|
||||
|
||||
kind := yaml.MappingNode
|
||||
if isNumeric(key) {
|
||||
if idx+1 == len(path)-1 && isNumeric(path[idx+1]) {
|
||||
kind = yaml.SequenceNode
|
||||
}
|
||||
sub := NewEntry(key, kind)
|
||||
sub.Path = append(entry.Path, key) // nolint: gocritic
|
||||
entry.Content = append(entry.Content, sub)
|
||||
|
||||
keyEntry := NewEntry(sub.Name(), yaml.ScalarNode)
|
||||
keyEntry.Value = key
|
||||
entry.Content = append(entry.Content, keyEntry, sub)
|
||||
entry = sub
|
||||
}
|
||||
|
||||
|
@ -206,6 +206,10 @@ func (e *Entry) MarshalYAML() (*yaml.Node, error) {
|
||||
}
|
||||
} else {
|
||||
entries := e.Contents()
|
||||
if len(entries)%2 != 0 {
|
||||
return nil, fmt.Errorf("cannot decode odd numbered contents list: %s", e.Path)
|
||||
}
|
||||
|
||||
for i := 0; i < len(entries); i += 2 {
|
||||
key := entries[i]
|
||||
value := entries[i+1]
|
||||
@ -213,6 +217,11 @@ func (e *Entry) MarshalYAML() (*yaml.Node, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
if key.Type == "" {
|
||||
key.Kind = yaml.ScalarNode
|
||||
key.Type = "!!map"
|
||||
}
|
||||
|
||||
keyNode, err := key.MarshalYAML()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -288,11 +297,11 @@ func (e *Entry) FromOP(fields []*op.ItemField) error {
|
||||
Type: kind,
|
||||
}
|
||||
if isNumeric(key) {
|
||||
// logrus.Debugf("hydrating sequence value at %s", path)
|
||||
logrus.Debugf("hydrating sequence value at %s", path)
|
||||
container.Kind = yaml.SequenceNode
|
||||
container.Content = append(container.Content, newEntry)
|
||||
} else {
|
||||
// logrus.Debugf("hydrating map value at %s", path)
|
||||
logrus.Debugf("hydrating map value at %s", path)
|
||||
keyEntry := NewEntry(key, yaml.ScalarNode)
|
||||
keyEntry.Value = key
|
||||
container.Content = append(container.Content, keyEntry, newEntry)
|
||||
@ -303,27 +312,23 @@ func (e *Entry) FromOP(fields []*op.ItemField) error {
|
||||
subContainer := container.ChildNamed(key)
|
||||
if subContainer != nil {
|
||||
container = subContainer
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
kind := yaml.MappingNode
|
||||
if idx+1 < len(path)-1 && isNumeric(path[idx+1]) {
|
||||
// logrus.Debugf("creating sequence container for key %s at %s", key, path)
|
||||
if idx+1 == len(path)-1 && isNumeric(path[idx+1]) {
|
||||
logrus.Debugf("creating sequence container for key %s at %s", key, path)
|
||||
kind = yaml.SequenceNode
|
||||
}
|
||||
child := NewEntry(key, kind)
|
||||
child.Path = append(container.Path, key) // nolint: gocritic
|
||||
|
||||
if kind == yaml.SequenceNode {
|
||||
container.Content = append(container.Content, child)
|
||||
} else {
|
||||
// logrus.Debugf("creating mapping container for %s at %s", key, container.Path)
|
||||
keyEntry := NewEntry(child.Name(), yaml.ScalarNode)
|
||||
keyEntry.Value = key
|
||||
container.Content = append(container.Content, keyEntry, child)
|
||||
}
|
||||
container = child
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -332,7 +337,7 @@ func (e *Entry) ToOP() []*op.ItemField {
|
||||
ret := []*op.ItemField{}
|
||||
var section *op.ItemSection
|
||||
|
||||
if e.Kind == yaml.ScalarNode {
|
||||
if e.IsScalar() {
|
||||
name := e.Path[len(e.Path)-1]
|
||||
fullPath := strings.Join(e.Path, ".")
|
||||
if len(e.Path) > 1 {
|
||||
@ -466,10 +471,8 @@ func (e *Entry) Merge(other *Entry) error {
|
||||
if err := local.Merge(remote); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
logrus.Debugf("adding new collection value at %s", remote.Path)
|
||||
local.Content = append(local.Content, remote)
|
||||
}
|
||||
local.Content = append(local.Content, remote)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -48,7 +48,11 @@ func Load(ref string, preferRemote bool) (*Config, error) {
|
||||
return nil, fmt.Errorf("could not load %s from local as it's not a path", ref)
|
||||
}
|
||||
|
||||
return FromFile(ref)
|
||||
cfg, err := FromFile(ref)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load file %s: %w", ref, err)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// FromFile reads a path and returns a config.
|
||||
@ -86,7 +90,7 @@ func FromYAML(data []byte) (*Config, error) {
|
||||
|
||||
err := yaml.Unmarshal(data, &cfg.Tree)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not parse %w", err)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
|
@ -15,22 +15,6 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func (cfg *Config) Lookup(query []string) (*Entry, error) {
|
||||
if len(query) == 0 || len(query) == 1 && query[0] == "." {
|
||||
return cfg.Tree, nil
|
||||
}
|
||||
|
||||
entry := cfg.Tree
|
||||
for _, part := range query {
|
||||
entry = entry.ChildNamed(part)
|
||||
if entry == nil {
|
||||
return nil, fmt.Errorf("value not found at %s of %s", part, query)
|
||||
}
|
||||
}
|
||||
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
func findRepoConfig(from string) (*opDetails, error) {
|
||||
parts := strings.Split(from, "/")
|
||||
for i := len(parts); i > 0; i-- {
|
||||
@ -107,7 +91,7 @@ func AutocompleteKeys(cmd *command.Command, currentValue, config string) ([]stri
|
||||
|
||||
keys, err := KeysFromYAML(buf)
|
||||
if err != nil {
|
||||
return nil, flag, err
|
||||
return nil, flag, fmt.Errorf("could not parse file %s as %w", file, err)
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
|
@ -84,7 +84,7 @@ func (cfg *Config) ToOP() *op.Item {
|
||||
continue
|
||||
}
|
||||
|
||||
if value.Kind == yaml.MappingNode {
|
||||
if value.Kind == yaml.MappingNode || value.Kind == yaml.SequenceNode {
|
||||
sections = append(sections, &op.ItemSection{
|
||||
ID: value.Name(),
|
||||
Label: value.Name(),
|
||||
|
Loading…
Reference in New Issue
Block a user