2017-08-28 20:44:35 +00:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/hashicorp/vault/api"
|
|
|
|
"github.com/posener/complete"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestPredictVaultPaths(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
client, closer := testVaultServer(t)
|
|
|
|
defer closer()
|
|
|
|
|
|
|
|
data := map[string]interface{}{"a": "b"}
|
|
|
|
if _, err := client.Logical().Write("secret/bar", data); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if _, err := client.Logical().Write("secret/foo", data); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if _, err := client.Logical().Write("secret/zip/zap", data); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if _, err := client.Logical().Write("secret/zip/zonk", data); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if _, err := client.Logical().Write("secret/zip/twoot", data); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
cases := []struct {
|
2017-08-28 21:05:09 +00:00
|
|
|
name string
|
|
|
|
args complete.Args
|
|
|
|
includeFiles bool
|
|
|
|
exp []string
|
2017-08-28 20:44:35 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
"has_args",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret/foo", "a=b"},
|
|
|
|
Last: "a=b",
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"has_args_no_files",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret/foo", "a=b"},
|
|
|
|
Last: "a=b",
|
|
|
|
},
|
|
|
|
false,
|
2017-08-28 20:44:35 +00:00
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"part_mount",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "s"},
|
|
|
|
Last: "s",
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
|
|
|
[]string{"secret/", "sys/"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"part_mount_no_files",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "s"},
|
|
|
|
Last: "s",
|
|
|
|
},
|
|
|
|
false,
|
2017-08-28 20:44:35 +00:00
|
|
|
[]string{"secret/", "sys/"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"only_mount",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "sec"},
|
|
|
|
Last: "sec",
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
2017-08-28 20:44:35 +00:00
|
|
|
[]string{"secret/bar", "secret/foo", "secret/zip/"},
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
{
|
|
|
|
"only_mount_no_files",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "sec"},
|
|
|
|
Last: "sec",
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
[]string{"secret/zip/"},
|
|
|
|
},
|
2017-08-28 20:44:35 +00:00
|
|
|
{
|
|
|
|
"full_mount",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret"},
|
|
|
|
Last: "secret",
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
2017-08-28 20:44:35 +00:00
|
|
|
[]string{"secret/bar", "secret/foo", "secret/zip/"},
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
{
|
|
|
|
"full_mount_no_files",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret"},
|
|
|
|
Last: "secret",
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
[]string{"secret/zip/"},
|
|
|
|
},
|
2017-08-28 20:44:35 +00:00
|
|
|
{
|
|
|
|
"full_mount_slash",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret/"},
|
|
|
|
Last: "secret/",
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
2017-08-28 20:44:35 +00:00
|
|
|
[]string{"secret/bar", "secret/foo", "secret/zip/"},
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
{
|
|
|
|
"full_mount_slash_no_files",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret/"},
|
|
|
|
Last: "secret/",
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
[]string{"secret/zip/"},
|
|
|
|
},
|
2017-08-28 20:44:35 +00:00
|
|
|
{
|
|
|
|
"path_partial",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret/z"},
|
|
|
|
Last: "secret/z",
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
2017-08-28 20:44:35 +00:00
|
|
|
[]string{"secret/zip/twoot", "secret/zip/zap", "secret/zip/zonk"},
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
{
|
|
|
|
"path_partial_no_files",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret/z"},
|
|
|
|
Last: "secret/z",
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
[]string{"secret/zip/"},
|
|
|
|
},
|
2017-08-28 20:44:35 +00:00
|
|
|
{
|
|
|
|
"subpath_partial_z",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret/zip/z"},
|
|
|
|
Last: "secret/zip/z",
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
2017-08-28 20:44:35 +00:00
|
|
|
[]string{"secret/zip/zap", "secret/zip/zonk"},
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
{
|
|
|
|
"subpath_partial_z_no_files",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret/zip/z"},
|
|
|
|
Last: "secret/zip/z",
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
[]string{"secret/zip/z"},
|
|
|
|
},
|
2017-08-28 20:44:35 +00:00
|
|
|
{
|
|
|
|
"subpath_partial_t",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret/zip/t"},
|
|
|
|
Last: "secret/zip/t",
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
2017-08-28 20:44:35 +00:00
|
|
|
[]string{"secret/zip/twoot"},
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
{
|
|
|
|
"subpath_partial_t_no_files",
|
|
|
|
complete.Args{
|
|
|
|
All: []string{"read", "secret/zip/t"},
|
|
|
|
Last: "secret/zip/t",
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
[]string{"secret/zip/t"},
|
|
|
|
},
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("group", func(t *testing.T) {
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
p := NewPredict()
|
|
|
|
p.client = client
|
|
|
|
|
|
|
|
f := p.vaultPaths(tc.includeFiles)
|
2017-08-28 20:44:35 +00:00
|
|
|
act := f(tc.args)
|
|
|
|
if !reflect.DeepEqual(act, tc.exp) {
|
|
|
|
t.Errorf("expected %q to be %q", act, tc.exp)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-09-05 03:55:19 +00:00
|
|
|
func TestPredict_Audits(t *testing.T) {
|
2017-08-28 20:44:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
client, closer := testVaultServer(t)
|
|
|
|
defer closer()
|
|
|
|
|
2017-09-05 03:55:19 +00:00
|
|
|
badClient, badCloser := testVaultServerBad(t)
|
|
|
|
defer badCloser()
|
|
|
|
|
|
|
|
if err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{
|
|
|
|
Type: "file",
|
|
|
|
Options: map[string]string{
|
|
|
|
"file_path": "discard",
|
|
|
|
},
|
|
|
|
}); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
cases := []struct {
|
2017-09-05 03:55:19 +00:00
|
|
|
name string
|
|
|
|
client *api.Client
|
|
|
|
exp []string
|
2017-08-28 20:44:35 +00:00
|
|
|
}{
|
|
|
|
{
|
2017-09-05 03:55:19 +00:00
|
|
|
"not_connected_client",
|
|
|
|
badClient,
|
2017-08-28 20:44:35 +00:00
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
2017-09-05 03:55:19 +00:00
|
|
|
"good_path",
|
|
|
|
client,
|
|
|
|
[]string{"file/"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("group", func(t *testing.T) {
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
p := NewPredict()
|
|
|
|
p.client = tc.client
|
|
|
|
|
|
|
|
act := p.audits()
|
|
|
|
if !reflect.DeepEqual(act, tc.exp) {
|
|
|
|
t.Errorf("expected %q to be %q", act, tc.exp)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPredict_Mounts(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
client, closer := testVaultServer(t)
|
|
|
|
defer closer()
|
|
|
|
|
|
|
|
badClient, badCloser := testVaultServerBad(t)
|
|
|
|
defer badCloser()
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
name string
|
|
|
|
client *api.Client
|
|
|
|
exp []string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"not_connected_client",
|
|
|
|
badClient,
|
|
|
|
defaultPredictVaultMounts,
|
2017-08-28 20:44:35 +00:00
|
|
|
},
|
|
|
|
{
|
2017-09-05 03:55:19 +00:00
|
|
|
"good_path",
|
|
|
|
client,
|
2018-01-10 16:33:53 +00:00
|
|
|
[]string{"cubbyhole/", "identity/", "secret/", "sys/"},
|
2017-08-28 20:44:35 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("group", func(t *testing.T) {
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
p := NewPredict()
|
2017-09-05 03:55:19 +00:00
|
|
|
p.client = tc.client
|
|
|
|
|
|
|
|
act := p.mounts()
|
|
|
|
if !reflect.DeepEqual(act, tc.exp) {
|
|
|
|
t.Errorf("expected %q to be %q", act, tc.exp)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-03-26 17:40:33 +00:00
|
|
|
func TestPredict_Plugins(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
client, closer := testVaultServer(t)
|
|
|
|
defer closer()
|
|
|
|
|
|
|
|
badClient, badCloser := testVaultServerBad(t)
|
|
|
|
defer badCloser()
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
name string
|
|
|
|
client *api.Client
|
|
|
|
exp []string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"not_connected_client",
|
|
|
|
badClient,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"good_path",
|
|
|
|
client,
|
|
|
|
[]string{
|
|
|
|
"cassandra-database-plugin",
|
|
|
|
"hana-database-plugin",
|
|
|
|
"mongodb-database-plugin",
|
|
|
|
"mssql-database-plugin",
|
|
|
|
"mysql-aurora-database-plugin",
|
|
|
|
"mysql-database-plugin",
|
|
|
|
"mysql-legacy-database-plugin",
|
|
|
|
"mysql-rds-database-plugin",
|
|
|
|
"postgresql-database-plugin",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("group", func(t *testing.T) {
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
p := NewPredict()
|
|
|
|
p.client = tc.client
|
|
|
|
|
|
|
|
act := p.plugins()
|
|
|
|
if !reflect.DeepEqual(act, tc.exp) {
|
|
|
|
t.Errorf("expected %q to be %q", act, tc.exp)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-09-05 03:55:19 +00:00
|
|
|
func TestPredict_Policies(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
client, closer := testVaultServer(t)
|
|
|
|
defer closer()
|
|
|
|
|
|
|
|
badClient, badCloser := testVaultServerBad(t)
|
|
|
|
defer badCloser()
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
name string
|
|
|
|
client *api.Client
|
|
|
|
exp []string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"not_connected_client",
|
|
|
|
badClient,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"good_path",
|
|
|
|
client,
|
|
|
|
[]string{"default", "root"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("group", func(t *testing.T) {
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
p := NewPredict()
|
|
|
|
p.client = tc.client
|
2017-08-29 04:24:22 +00:00
|
|
|
|
2017-09-05 03:55:19 +00:00
|
|
|
act := p.policies()
|
2017-08-28 20:44:35 +00:00
|
|
|
if !reflect.DeepEqual(act, tc.exp) {
|
|
|
|
t.Errorf("expected %q to be %q", act, tc.exp)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
func TestPredict_Paths(t *testing.T) {
|
2017-08-28 20:44:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
client, closer := testVaultServer(t)
|
|
|
|
defer closer()
|
|
|
|
|
|
|
|
data := map[string]interface{}{"a": "b"}
|
|
|
|
if _, err := client.Logical().Write("secret/bar", data); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if _, err := client.Logical().Write("secret/foo", data); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if _, err := client.Logical().Write("secret/zip/zap", data); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
cases := []struct {
|
2017-08-28 21:05:09 +00:00
|
|
|
name string
|
|
|
|
path string
|
|
|
|
includeFiles bool
|
|
|
|
exp []string
|
2017-08-28 20:44:35 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
"bad_path",
|
|
|
|
"nope/not/a/real/path/ever",
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
2017-08-28 20:44:35 +00:00
|
|
|
[]string{"nope/not/a/real/path/ever"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"good_path",
|
|
|
|
"secret/",
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
2017-08-28 20:44:35 +00:00
|
|
|
[]string{"secret/bar", "secret/foo", "secret/zip/"},
|
|
|
|
},
|
2017-08-28 21:05:09 +00:00
|
|
|
{
|
|
|
|
"good_path_no_files",
|
|
|
|
"secret/",
|
|
|
|
false,
|
|
|
|
[]string{"secret/zip/"},
|
|
|
|
},
|
2017-08-28 20:44:35 +00:00
|
|
|
{
|
|
|
|
"partial_match",
|
|
|
|
"secret/z",
|
2017-08-28 21:05:09 +00:00
|
|
|
true,
|
|
|
|
[]string{"secret/zip/"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"partial_match_no_files",
|
|
|
|
"secret/z",
|
|
|
|
false,
|
2017-08-28 20:44:35 +00:00
|
|
|
[]string{"secret/zip/"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("group", func(t *testing.T) {
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
p := NewPredict()
|
|
|
|
p.client = client
|
|
|
|
|
|
|
|
act := p.paths(tc.path, tc.includeFiles)
|
2017-08-28 20:44:35 +00:00
|
|
|
if !reflect.DeepEqual(act, tc.exp) {
|
|
|
|
t.Errorf("expected %q to be %q", act, tc.exp)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
func TestPredict_ListPaths(t *testing.T) {
|
2017-08-28 20:44:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
client, closer := testVaultServer(t)
|
|
|
|
defer closer()
|
|
|
|
|
2017-09-05 03:55:19 +00:00
|
|
|
badClient, badCloser := testVaultServerBad(t)
|
|
|
|
defer badCloser()
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
data := map[string]interface{}{"a": "b"}
|
|
|
|
if _, err := client.Logical().Write("secret/bar", data); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if _, err := client.Logical().Write("secret/foo", data); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
name string
|
|
|
|
client *api.Client
|
|
|
|
path string
|
|
|
|
exp []string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"bad_path",
|
|
|
|
client,
|
|
|
|
"nope/not/a/real/path/ever",
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"good_path",
|
|
|
|
client,
|
|
|
|
"secret/",
|
|
|
|
[]string{"bar", "foo"},
|
|
|
|
},
|
2017-09-05 03:55:19 +00:00
|
|
|
{
|
|
|
|
"not_connected_client",
|
|
|
|
badClient,
|
|
|
|
"secret/",
|
|
|
|
nil,
|
|
|
|
},
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("group", func(t *testing.T) {
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
p := NewPredict()
|
2017-09-05 03:55:19 +00:00
|
|
|
p.client = tc.client
|
2017-08-29 04:24:22 +00:00
|
|
|
|
|
|
|
act := p.listPaths(tc.path)
|
2017-08-28 20:44:35 +00:00
|
|
|
if !reflect.DeepEqual(act, tc.exp) {
|
|
|
|
t.Errorf("expected %q to be %q", act, tc.exp)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
func TestPredict_HasPathArg(t *testing.T) {
|
2017-08-28 20:44:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
name string
|
|
|
|
args []string
|
|
|
|
exp bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"nil",
|
|
|
|
nil,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"empty",
|
|
|
|
[]string{},
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"empty_string",
|
|
|
|
[]string{""},
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"single",
|
|
|
|
[]string{"foo"},
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"multiple",
|
|
|
|
[]string{"foo", "bar", "baz"},
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
p := NewPredict()
|
|
|
|
if act := p.hasPathArg(tc.args); act != tc.exp {
|
2017-08-28 20:44:35 +00:00
|
|
|
t.Errorf("expected %t to be %t", act, tc.exp)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|