blob: 0ac759ebfdc576984bee3203a85be8c2c11899a2 [file] [log] [blame]
// Copyright 2019 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 lucicfg
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"go.chromium.org/luci/common/api/luci_config/config/v1"
. "github.com/smartystreets/goconvey/convey"
. "go.chromium.org/luci/common/testing/assertions"
)
func TestConfigSet(t *testing.T) {
t.Parallel()
ctx := context.Background()
Convey("With temp dir", t, func() {
tmp, err := ioutil.TempDir("", "lucicfg")
So(err, ShouldBeNil)
defer os.RemoveAll(tmp)
path := func(p ...string) string {
return filepath.Join(append([]string{tmp}, p...)...)
}
So(os.Mkdir(path("subdir"), 0700), ShouldBeNil)
So(ioutil.WriteFile(path("a.cfg"), []byte("a\n"), 0600), ShouldBeNil)
So(ioutil.WriteFile(path("subdir", "b.cfg"), []byte("b\n"), 0600), ShouldBeNil)
Convey("Reading", func() {
Convey("Success", func() {
cfg, err := ReadConfigSet(tmp, "set name")
So(err, ShouldBeNil)
So(cfg, ShouldResemble, ConfigSet{
Name: "set name",
Data: map[string][]byte{
"a.cfg": []byte("a\n"),
"subdir/b.cfg": []byte("b\n"),
},
})
So(cfg.Files(), ShouldResemble, []string{
"a.cfg",
"subdir/b.cfg",
})
})
Convey("Missing dir", func() {
_, err := ReadConfigSet(path("unknown"), "zzz")
So(err, ShouldNotBeNil)
})
})
})
Convey("Validation", t, func() {
const configSetName = "config set name"
validator := testValidator{
res: []*ValidationMessage{
{Severity: "ERROR", Text: "Boo"},
},
}
cfg := ConfigSet{
Name: configSetName,
Data: map[string][]byte{
"a.cfg": []byte("aaa"),
"b.cfg": {0, 1, 2},
},
}
So(cfg.Validate(ctx, &validator), ShouldResemble, &ValidationResult{
ConfigSet: configSetName,
Messages: validator.res,
})
So(validator.req, ShouldResemble, &ValidationRequest{
ConfigSet: configSetName,
Files: []*config.LuciConfigValidateConfigRequestMessageFile{
{Path: "a.cfg", Content: "YWFh"},
{Path: "b.cfg", Content: "AAEC"},
},
})
})
Convey("RPC error", t, func() {
validator := testValidator{
err: fmt.Errorf("BOOM"),
}
cfg := ConfigSet{
Name: "set",
Data: map[string][]byte{"a.cfg": []byte("aaa")},
}
res := cfg.Validate(ctx, &validator)
So(res, ShouldResemble, &ValidationResult{
ConfigSet: "set",
Failed: true,
RPCError: "BOOM",
})
// This is considered overall failure.
err := res.OverallError(false)
So(err, ShouldErrLike, "BOOM")
So(res.Failed, ShouldBeTrue)
})
Convey("Overall error check", t, func() {
result := func(level ...string) *ValidationResult {
res := &ValidationResult{}
for _, l := range level {
res.Messages = append(res.Messages, &ValidationMessage{
Severity: l,
Text: "boo",
})
}
return res
}
// Fail on warnings = false.
So(result().OverallError(false), ShouldBeNil)
So(result("INFO", "WARNING").OverallError(false), ShouldBeNil)
So(result("INFO", "ERROR").OverallError(false), ShouldErrLike, "some files were invalid")
// Fail on warnings = true.
So(result().OverallError(true), ShouldBeNil)
So(result("INFO").OverallError(true), ShouldBeNil)
So(result("INFO", "WARNING", "ERROR").OverallError(true), ShouldErrLike, "some files were invalid")
So(result("INFO", "WARNING").OverallError(true), ShouldErrLike, "some files had validation warnings")
})
}
type testValidator struct {
req *ValidationRequest // captured request
res []*ValidationMessage // a reply to send
err error // an RPC error
}
func (t *testValidator) Validate(ctx context.Context, req *ValidationRequest) ([]*ValidationMessage, error) {
t.req = req
return t.res, t.err
}