blob: 45c5948e7177946ce7e25decabba9cf329535b35 [file] [log] [blame]
// Copyright 2016 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cfgclient
import (
"context"
"fmt"
"net/url"
"testing"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/config"
"go.chromium.org/luci/config/server/cfgclient/backend"
. "github.com/smartystreets/goconvey/convey"
)
// testingBackend is a Backend implementation that ignores Authority.
type testingBackend struct {
serviceURL *url.URL
err error
items []*config.Config
url url.URL
lastParams backend.Params
}
func (tb *testingBackend) ServiceURL(context.Context) url.URL { return *tb.serviceURL }
func (tb *testingBackend) Get(c context.Context, configSet config.Set, path string, p backend.Params) (*config.Config, error) {
tb.lastParams = p
if err := tb.err; err != nil {
return nil, tb.err
}
if len(tb.items) == 0 {
return nil, config.ErrNoConfig
}
return tb.cloneItems()[0], nil
}
func (tb *testingBackend) GetAll(c context.Context, t backend.GetAllTarget, path string, p backend.Params) (
[]*config.Config, error) {
tb.lastParams = p
if err := tb.err; err != nil {
return nil, tb.err
}
return tb.cloneItems(), nil
}
func (tb *testingBackend) GetConfigInterface(c context.Context, a backend.Authority) config.Interface {
panic("not supported")
}
func (tb *testingBackend) cloneItems() []*config.Config {
clones := make([]*config.Config, len(tb.items))
for i, it := range tb.items {
clone := *it
clones[i] = &clone
}
return clones
}
type errorMultiResolver struct {
getErr func(i int) error
out *[]string
}
func (er *errorMultiResolver) PrepareMulti(size int) {
*er.out = make([]string, size)
}
func (er *errorMultiResolver) ResolveItemAt(i int, it *config.Config) error {
if err := er.getErr(i); err != nil {
return err
}
(*er.out)[i] = it.Content
return nil
}
func TestConfig(t *testing.T) {
t.Parallel()
Convey(`A testing backend`, t, func() {
c := context.Background()
var err error
tb := testingBackend{
items: []*config.Config{
{Meta: config.Meta{"projects/foo", "path", "####", "v1", "config_url"}, Content: "foo"},
{Meta: config.Meta{"projects/bar", "path", "####", "v1", "config_url"}, Content: "bar"},
},
}
transformItems := func(fn func(int, *config.Config)) {
for i, itm := range tb.items {
fn(i, itm)
}
}
tb.serviceURL, err = url.Parse("http://example.com/config")
if err != nil {
panic(err)
}
c = backend.WithBackend(c, &tb)
Convey(`Can resolve its service URL.`, func() {
So(ServiceURL(c), ShouldResemble, *tb.serviceURL)
})
// Caching / type test cases.
Convey(`Test byte resolver`, func() {
transformItems(func(i int, ci *config.Config) {
ci.Content = string([]byte{byte(i)})
})
Convey(`Single`, func() {
var val []byte
So(Get(c, AsService, "", "", Bytes(&val), nil), ShouldBeNil)
So(val, ShouldResemble, []byte{0})
})
Convey(`Multi`, func() {
var (
val [][]byte
meta []*config.Meta
)
So(Projects(c, AsService, "", BytesSlice(&val), &meta), ShouldBeNil)
So(val, ShouldResemble, [][]byte{{0}, {1}})
So(meta, ShouldResemble, []*config.Meta{
{"projects/foo", "path", "####", "v1", "config_url"},
{"projects/bar", "path", "####", "v1", "config_url"},
})
})
})
Convey(`Test string resolver`, func() {
transformItems(func(i int, ci *config.Config) {
ci.Content = fmt.Sprintf("[%d]", i)
})
Convey(`Single`, func() {
var val string
So(Get(c, AsService, "", "", String(&val), nil), ShouldBeNil)
So(val, ShouldEqual, "[0]")
})
Convey(`Multi`, func() {
var (
val []string
meta []*config.Meta
)
So(Projects(c, AsService, "", StringSlice(&val), &meta), ShouldBeNil)
So(val, ShouldResemble, []string{"[0]", "[1]"})
So(meta, ShouldResemble, []*config.Meta{
{"projects/foo", "path", "####", "v1", "config_url"},
{"projects/bar", "path", "####", "v1", "config_url"},
})
})
})
Convey(`Test nil resolver`, func() {
transformItems(func(i int, ci *config.Config) {
ci.Content = fmt.Sprintf("[%d]", i)
})
Convey(`Single`, func() {
var meta config.Meta
So(Get(c, AsService, "", "", nil, &meta), ShouldBeNil)
So(meta, ShouldResemble, config.Meta{"projects/foo", "path", "####", "v1", "config_url"})
So(tb.lastParams.Content, ShouldBeFalse)
})
Convey(`Multi`, func() {
var meta []*config.Meta
So(Projects(c, AsService, "", nil, &meta), ShouldBeNil)
So(meta, ShouldResemble, []*config.Meta{
{"projects/foo", "path", "####", "v1", "config_url"},
{"projects/bar", "path", "####", "v1", "config_url"},
})
So(tb.lastParams.Content, ShouldBeFalse)
})
})
Convey(`Projects with some errors returns a MultiError.`, func() {
testErr := errors.New("test error")
var (
val []string
meta []*config.Meta
)
er := errorMultiResolver{
out: &val,
getErr: func(i int) error {
if i == 1 {
return testErr
}
return nil
},
}
err := Projects(c, AsService, "", &er, &meta)
// We have a MultiError with an error in position 1.
So(err, ShouldResemble, errors.MultiError{nil, testErr})
// One of our configs should have resolved.
So(val, ShouldResemble, []string{"foo", ""})
// Meta still works.
So(meta, ShouldResemble, []*config.Meta{
{"projects/foo", "path", "####", "v1", "config_url"},
{"projects/bar", "path", "####", "v1", "config_url"},
})
})
})
}