2017-03-25 13:36:39 +00:00
|
|
|
package options
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
2020-09-12 15:36:44 +00:00
|
|
|
"regexp"
|
2017-03-25 13:36:39 +00:00
|
|
|
"testing"
|
2017-03-25 15:53:30 +00:00
|
|
|
"time"
|
2017-03-25 13:36:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var optsTests = []struct {
|
|
|
|
input []string
|
|
|
|
output Options
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
[]string{"foo=bar", "bar=baz ", "k="},
|
|
|
|
Options{
|
|
|
|
"foo": "bar",
|
|
|
|
"bar": "baz",
|
|
|
|
"k": "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[]string{"Foo=23", "baR", "k=thing with spaces"},
|
|
|
|
Options{
|
|
|
|
"foo": "23",
|
|
|
|
"bar": "",
|
|
|
|
"k": "thing with spaces",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[]string{"k=thing with spaces", "k2=more spaces = not evil"},
|
|
|
|
Options{
|
|
|
|
"k": "thing with spaces",
|
|
|
|
"k2": "more spaces = not evil",
|
|
|
|
},
|
|
|
|
},
|
2017-03-25 14:39:38 +00:00
|
|
|
{
|
|
|
|
[]string{"x=1", "foo=bar", "y=2", "foo=bar"},
|
|
|
|
Options{
|
|
|
|
"x": "1",
|
|
|
|
"y": "2",
|
|
|
|
"foo": "bar",
|
|
|
|
},
|
|
|
|
},
|
2017-03-25 13:36:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseOptions(t *testing.T) {
|
|
|
|
for i, test := range optsTests {
|
|
|
|
t.Run(fmt.Sprintf("test-%v", i), func(t *testing.T) {
|
|
|
|
opts, err := Parse(test.input)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to parse options: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(opts, test.output) {
|
|
|
|
t.Fatalf("wrong result, want:\n %#v\ngot:\n %#v", test.output, opts)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var invalidOptsTests = []struct {
|
|
|
|
input []string
|
|
|
|
err string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
[]string{"=bar", "bar=baz", "k="},
|
2018-01-06 22:11:54 +00:00
|
|
|
"Fatal: empty key is not a valid option",
|
2017-03-25 13:36:39 +00:00
|
|
|
},
|
2017-03-25 14:39:38 +00:00
|
|
|
{
|
|
|
|
[]string{"x=1", "foo=bar", "y=2", "foo=baz"},
|
2018-01-06 22:11:54 +00:00
|
|
|
`Fatal: key "foo" present more than once`,
|
2017-03-25 14:39:38 +00:00
|
|
|
},
|
2017-03-25 13:36:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseInvalidOptions(t *testing.T) {
|
|
|
|
for _, test := range invalidOptsTests {
|
|
|
|
t.Run(test.err, func(t *testing.T) {
|
|
|
|
_, err := Parse(test.input)
|
|
|
|
if err == nil {
|
|
|
|
t.Fatalf("expected error (%v) not found, err is nil", test.err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err.Error() != test.err {
|
|
|
|
t.Fatalf("expected error %q, got %q", test.err, err.Error())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var extractTests = []struct {
|
|
|
|
input Options
|
|
|
|
ns string
|
|
|
|
output Options
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
input: Options{
|
|
|
|
"foo.bar:": "baz",
|
|
|
|
"s3.timeout": "10s",
|
|
|
|
"sftp.timeout": "5s",
|
|
|
|
"global": "foobar",
|
|
|
|
},
|
|
|
|
ns: "s3",
|
|
|
|
output: Options{
|
|
|
|
"timeout": "10s",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestOptionsExtract(t *testing.T) {
|
|
|
|
for _, test := range extractTests {
|
|
|
|
t.Run(test.ns, func(t *testing.T) {
|
|
|
|
opts := test.input.Extract(test.ns)
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(opts, test.output) {
|
|
|
|
t.Fatalf("wrong result, want:\n %#v\ngot:\n %#v", test.output, opts)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2017-03-25 15:53:30 +00:00
|
|
|
|
|
|
|
// Target is used for Apply() tests
|
|
|
|
type Target struct {
|
|
|
|
Name string `option:"name"`
|
|
|
|
ID int `option:"id"`
|
|
|
|
Timeout time.Duration `option:"timeout"`
|
2020-12-23 20:26:04 +00:00
|
|
|
Switch bool `option:"switch"`
|
2017-03-25 15:53:30 +00:00
|
|
|
Other string
|
|
|
|
}
|
|
|
|
|
|
|
|
var setTests = []struct {
|
|
|
|
input Options
|
|
|
|
output Target
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
Options{
|
|
|
|
"name": "foobar",
|
|
|
|
},
|
|
|
|
Target{
|
|
|
|
Name: "foobar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Options{
|
|
|
|
"name": "foobar",
|
|
|
|
"id": "1234",
|
|
|
|
},
|
|
|
|
Target{
|
|
|
|
Name: "foobar",
|
|
|
|
ID: 1234,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Options{
|
|
|
|
"timeout": "10m3s",
|
|
|
|
},
|
|
|
|
Target{
|
2020-12-23 20:26:04 +00:00
|
|
|
Timeout: 10*time.Minute + 3*time.Second,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Options{
|
|
|
|
"switch": "true",
|
|
|
|
},
|
|
|
|
Target{
|
|
|
|
Switch: true,
|
2017-03-25 15:53:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestOptionsApply(t *testing.T) {
|
|
|
|
for i, test := range setTests {
|
|
|
|
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
|
|
|
|
var dst Target
|
2017-03-25 16:20:03 +00:00
|
|
|
err := test.input.Apply("", &dst)
|
2017-03-25 15:53:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if dst != test.output {
|
|
|
|
t.Fatalf("wrong result, want:\n %#v\ngot:\n %#v", test.output, dst)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var invalidSetTests = []struct {
|
2017-03-25 16:20:03 +00:00
|
|
|
input Options
|
|
|
|
namespace string
|
|
|
|
err string
|
2017-03-25 15:53:30 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
Options{
|
|
|
|
"first_name": "foobar",
|
|
|
|
},
|
2017-03-25 16:20:03 +00:00
|
|
|
"ns",
|
2018-01-06 22:11:54 +00:00
|
|
|
"Fatal: option ns.first_name is not known",
|
2017-03-25 15:53:30 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Options{
|
|
|
|
"id": "foobar",
|
|
|
|
},
|
2017-03-25 16:20:03 +00:00
|
|
|
"ns",
|
2017-03-25 15:53:30 +00:00
|
|
|
`strconv.ParseInt: parsing "foobar": invalid syntax`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Options{
|
|
|
|
"timeout": "2134",
|
|
|
|
},
|
2017-03-25 16:20:03 +00:00
|
|
|
"ns",
|
2020-09-12 15:36:44 +00:00
|
|
|
`time: missing unit in duration "?2134"?`,
|
2017-03-25 15:53:30 +00:00
|
|
|
},
|
2020-12-23 20:26:04 +00:00
|
|
|
{
|
|
|
|
Options{
|
|
|
|
"switch": "yes",
|
|
|
|
},
|
|
|
|
"ns",
|
|
|
|
`strconv.ParseBool: parsing "yes": invalid syntax`,
|
|
|
|
},
|
2017-03-25 15:53:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestOptionsApplyInvalid(t *testing.T) {
|
|
|
|
for i, test := range invalidSetTests {
|
|
|
|
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
|
|
|
|
var dst Target
|
2017-03-25 16:20:03 +00:00
|
|
|
err := test.input.Apply(test.namespace, &dst)
|
2017-03-25 15:53:30 +00:00
|
|
|
if err == nil {
|
|
|
|
t.Fatalf("expected error %v not found", test.err)
|
|
|
|
}
|
|
|
|
|
2020-12-23 20:26:04 +00:00
|
|
|
matched, e := regexp.MatchString(test.err, err.Error())
|
|
|
|
if e != nil {
|
|
|
|
t.Fatal(e)
|
2020-09-12 15:36:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !matched {
|
|
|
|
t.Fatalf("expected error to match %q, got %q", test.err, err.Error())
|
2017-03-25 15:53:30 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2017-04-13 21:55:20 +00:00
|
|
|
|
|
|
|
func TestListOptions(t *testing.T) {
|
2020-12-23 20:26:04 +00:00
|
|
|
teststruct := struct {
|
2017-04-13 21:55:20 +00:00
|
|
|
Foo string `option:"foo" help:"bar text help"`
|
|
|
|
}{}
|
|
|
|
|
2020-12-23 20:26:04 +00:00
|
|
|
tests := []struct {
|
2017-04-13 21:55:20 +00:00
|
|
|
cfg interface{}
|
|
|
|
opts []Help
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
Foo string `option:"foo" help:"bar text help"`
|
|
|
|
}{},
|
|
|
|
[]Help{
|
2017-05-16 23:28:39 +00:00
|
|
|
{Name: "foo", Text: "bar text help"},
|
2017-04-13 21:55:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
Foo string `option:"foo" help:"bar text help"`
|
|
|
|
Bar string `option:"bar" help:"bar text help"`
|
|
|
|
}{},
|
|
|
|
[]Help{
|
2017-05-16 23:28:39 +00:00
|
|
|
{Name: "foo", Text: "bar text help"},
|
|
|
|
{Name: "bar", Text: "bar text help"},
|
2017-04-13 21:55:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
Bar string `option:"bar" help:"bar text help"`
|
|
|
|
Foo string `option:"foo" help:"bar text help"`
|
|
|
|
}{},
|
|
|
|
[]Help{
|
2017-05-16 23:28:39 +00:00
|
|
|
{Name: "bar", Text: "bar text help"},
|
|
|
|
{Name: "foo", Text: "bar text help"},
|
2017-04-13 21:55:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
&teststruct,
|
|
|
|
[]Help{
|
2017-05-16 23:28:39 +00:00
|
|
|
{Name: "foo", Text: "bar text help"},
|
2017-04-13 21:55:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run("", func(t *testing.T) {
|
|
|
|
opts := listOptions(test.cfg)
|
|
|
|
if !reflect.DeepEqual(opts, test.opts) {
|
|
|
|
t.Fatalf("wrong opts, want:\n %v\ngot:\n %v", test.opts, opts)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAppendAllOptions(t *testing.T) {
|
2020-12-23 20:26:04 +00:00
|
|
|
tests := []struct {
|
2017-04-13 21:55:20 +00:00
|
|
|
cfgs map[string]interface{}
|
|
|
|
opts []Help
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
map[string]interface{}{
|
|
|
|
"local": struct {
|
|
|
|
Foo string `option:"foo" help:"bar text help"`
|
|
|
|
}{},
|
|
|
|
"sftp": struct {
|
|
|
|
Foo string `option:"foo" help:"bar text help2"`
|
|
|
|
Bar string `option:"bar" help:"bar text help"`
|
|
|
|
}{},
|
|
|
|
},
|
|
|
|
[]Help{
|
2017-05-16 23:28:39 +00:00
|
|
|
{Namespace: "local", Name: "foo", Text: "bar text help"},
|
|
|
|
{Namespace: "sftp", Name: "bar", Text: "bar text help"},
|
|
|
|
{Namespace: "sftp", Name: "foo", Text: "bar text help2"},
|
2017-04-13 21:55:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run("", func(t *testing.T) {
|
|
|
|
var opts []Help
|
|
|
|
for ns, cfg := range test.cfgs {
|
|
|
|
opts = appendAllOptions(opts, ns, cfg)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(opts, test.opts) {
|
|
|
|
t.Fatalf("wrong list, want:\n %v\ngot:\n %v", test.opts, opts)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|