1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-01-10 04:18:14 +02:00
oauth2-proxy/pkg/apis/options/load_test.go
2020-05-21 22:43:42 +01:00

306 lines
9.8 KiB
Go

package options
import (
"fmt"
"io/ioutil"
"os"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
"github.com/spf13/pflag"
)
func TestOptionsSuite(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Options Suite")
}
var _ = Describe("Load", func() {
Context("with a testOptions structure", func() {
type TestOptionSubStruct struct {
StringSliceOption []string `flag:"string-slice-option" cfg:"string_slice_option"`
}
type TestOptions struct {
StringOption string `flag:"string-option" cfg:"string_option"`
Sub TestOptionSubStruct `cfg:",squash"`
// Check exported but internal fields do not break loading
Internal *string `cfg:",internal"`
// Check unexported fields do not break loading
unexported string
}
type MissingSquashTestOptions struct {
StringOption string `flag:"string-option" cfg:"string_option"`
Sub TestOptionSubStruct
}
type MissingCfgTestOptions struct {
StringOption string `flag:"string-option"`
Sub TestOptionSubStruct `cfg:",squash"`
}
type MissingFlagTestOptions struct {
StringOption string `cfg:"string_option"`
Sub TestOptionSubStruct `cfg:",squash"`
}
var testOptionsConfigBytes = []byte(`
string_option="foo"
string_slice_option="a,b,c,d"
`)
var testOptionsFlagSet *pflag.FlagSet
type testOptionsTableInput struct {
env map[string]string
args []string
configFile []byte
flagSet func() *pflag.FlagSet
expectedErr error
input interface{}
expectedOutput interface{}
}
BeforeEach(func() {
testOptionsFlagSet = pflag.NewFlagSet("testFlagSet", pflag.ExitOnError)
testOptionsFlagSet.String("string-option", "default", "")
testOptionsFlagSet.StringSlice("string-slice-option", []string{"a", "b"}, "")
})
DescribeTable("Load",
func(o *testOptionsTableInput) {
var configFileName string
if o.configFile != nil {
By("Creating a config file")
configFile, err := ioutil.TempFile("", "oauth2-proxy-test-legacy-config-file")
Expect(err).ToNot(HaveOccurred())
defer configFile.Close()
_, err = configFile.Write(o.configFile)
Expect(err).ToNot(HaveOccurred())
defer os.Remove(configFile.Name())
configFileName = configFile.Name()
}
if len(o.env) > 0 {
By("Setting environment variables")
for k, v := range o.env {
os.Setenv(k, v)
defer os.Unsetenv(k)
}
}
Expect(o.flagSet).ToNot(BeNil())
flagSet := o.flagSet()
Expect(flagSet).ToNot(BeNil())
if len(o.args) > 0 {
By("Parsing flag arguments")
Expect(flagSet.Parse(o.args)).To(Succeed())
}
var input interface{}
if o.input != nil {
input = o.input
} else {
input = &TestOptions{}
}
err := Load(configFileName, flagSet, input)
if o.expectedErr != nil {
Expect(err).To(MatchError(o.expectedErr.Error()))
} else {
Expect(err).ToNot(HaveOccurred())
}
Expect(input).To(Equal(o.expectedOutput))
},
Entry("with just a config file", &testOptionsTableInput{
configFile: testOptionsConfigBytes,
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedOutput: &TestOptions{
StringOption: "foo",
Sub: TestOptionSubStruct{
StringSliceOption: []string{"a", "b", "c", "d"},
},
},
}),
Entry("when setting env variables", &testOptionsTableInput{
configFile: testOptionsConfigBytes,
env: map[string]string{
"OAUTH2_PROXY_STRING_OPTION": "bar",
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
},
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedOutput: &TestOptions{
StringOption: "bar",
Sub: TestOptionSubStruct{
StringSliceOption: []string{"a", "b", "c"},
},
},
}),
Entry("when setting flags", &testOptionsTableInput{
configFile: testOptionsConfigBytes,
env: map[string]string{
"OAUTH2_PROXY_STRING_OPTION": "bar",
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
},
args: []string{
"--string-option", "baz",
"--string-slice-option", "a,b,c,d,e",
},
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedOutput: &TestOptions{
StringOption: "baz",
Sub: TestOptionSubStruct{
StringSliceOption: []string{"a", "b", "c", "d", "e"},
},
},
}),
Entry("when setting flags multiple times", &testOptionsTableInput{
configFile: testOptionsConfigBytes,
env: map[string]string{
"OAUTH2_PROXY_STRING_OPTION": "bar",
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
},
args: []string{
"--string-option", "baz",
"--string-slice-option", "x",
"--string-slice-option", "y",
"--string-slice-option", "z",
},
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedOutput: &TestOptions{
StringOption: "baz",
Sub: TestOptionSubStruct{
StringSliceOption: []string{"x", "y", "z"},
},
},
}),
Entry("when setting env variables without a config file", &testOptionsTableInput{
env: map[string]string{
"OAUTH2_PROXY_STRING_OPTION": "bar",
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
},
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedOutput: &TestOptions{
StringOption: "bar",
Sub: TestOptionSubStruct{
StringSliceOption: []string{"a", "b", "c"},
},
},
}),
Entry("when setting flags without a config file", &testOptionsTableInput{
env: map[string]string{
"OAUTH2_PROXY_STRING_OPTION": "bar",
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
},
args: []string{
"--string-option", "baz",
"--string-slice-option", "a,b,c,d,e",
},
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedOutput: &TestOptions{
StringOption: "baz",
Sub: TestOptionSubStruct{
StringSliceOption: []string{"a", "b", "c", "d", "e"},
},
},
}),
Entry("when setting flags without a config file", &testOptionsTableInput{
env: map[string]string{
"OAUTH2_PROXY_STRING_OPTION": "bar",
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
},
args: []string{
"--string-option", "baz",
"--string-slice-option", "a,b,c,d,e",
},
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedOutput: &TestOptions{
StringOption: "baz",
Sub: TestOptionSubStruct{
StringSliceOption: []string{"a", "b", "c", "d", "e"},
},
},
}),
Entry("when nothing is set it should use flag defaults", &testOptionsTableInput{
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedOutput: &TestOptions{
StringOption: "default",
Sub: TestOptionSubStruct{
StringSliceOption: []string{"a", "b"},
},
},
}),
Entry("with an invalid config file", &testOptionsTableInput{
configFile: []byte(`slice_option = foo`),
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedErr: fmt.Errorf("unable to load config file: While parsing config: (1, 16): never reached"),
expectedOutput: &TestOptions{},
}),
Entry("with an invalid flagset", &testOptionsTableInput{
flagSet: func() *pflag.FlagSet {
// Missing a flag
f := pflag.NewFlagSet("testFlagSet", pflag.ExitOnError)
f.String("string-option", "default", "")
return f
},
expectedErr: fmt.Errorf("unable to register flags: field \"string-slice-option\" does not have a registered flag"),
expectedOutput: &TestOptions{},
}),
Entry("with an struct is missing the squash tag", &testOptionsTableInput{
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedErr: fmt.Errorf("unable to register flags: field \".Sub\" does not have required cfg tag: `,squash`"),
input: &MissingSquashTestOptions{},
expectedOutput: &MissingSquashTestOptions{},
}),
Entry("with a field is missing the cfg tag", &testOptionsTableInput{
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedErr: fmt.Errorf("unable to register flags: field \".StringOption\" does not have required tags (cfg, flag)"),
input: &MissingCfgTestOptions{},
expectedOutput: &MissingCfgTestOptions{},
}),
Entry("with a field is missing the flag tag", &testOptionsTableInput{
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedErr: fmt.Errorf("unable to register flags: field \".StringOption\" does not have required tags (cfg, flag)"),
input: &MissingFlagTestOptions{},
expectedOutput: &MissingFlagTestOptions{},
}),
Entry("with existing unexported fields", &testOptionsTableInput{
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
input: &TestOptions{
unexported: "unexported",
},
expectedOutput: &TestOptions{
StringOption: "default",
Sub: TestOptionSubStruct{
StringSliceOption: []string{"a", "b"},
},
unexported: "unexported",
},
}),
Entry("with an unknown option in the config file", &testOptionsTableInput{
configFile: []byte(`unknown_option="foo"`),
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
expectedErr: fmt.Errorf("error unmarshalling config: 1 error(s) decoding:\n\n* '' has invalid keys: unknown_option"),
// Viper will unmarshal before returning the error, so this is the default output
expectedOutput: &TestOptions{
StringOption: "default",
Sub: TestOptionSubStruct{
StringSliceOption: []string{"a", "b"},
},
},
}),
Entry("with an empty Options struct, should return default values", &testOptionsTableInput{
flagSet: NewFlagSet,
input: &Options{},
expectedOutput: NewOptions(),
}),
)
})
})