blob: 666919b4924ab1631bee5e4e4d5e49b21b252fa4 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// +build !windows
package sideeffects
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/golang/protobuf/jsonpb"
"github.com/google/uuid"
. "github.com/smartystreets/goconvey/convey"
. "go.chromium.org/luci/common/testing/assertions"
"go.chromium.org/chromiumos/infra/proto/go/test_platform/side_effects"
)
func basicConfig() *side_effects.Config {
return &side_effects.Config{
Tko: &side_effects.TKOConfig{
ProxySocket: tempFile(),
MysqlUser: "foo-user",
EncryptedMysqlPassword: "encrypted-password",
},
GoogleStorage: &side_effects.GoogleStorageConfig{
Bucket: "foo-bucket",
CredentialsFile: tempFile(),
},
}
}
func tempFile() string {
f, _ := ioutil.TempFile("", "")
return f.Name()
}
func TestSuccess(t *testing.T) {
Convey("Given a complete config pointing to existing files", t, func() {
cfg := basicConfig()
err := ValidateConfig(cfg)
Convey("no error is returned.", func() {
So(err, ShouldBeNil)
})
})
}
func TestMissingArgs(t *testing.T) {
Convey("Given a side_effects.Config with a missing", t, func() {
cases := []struct {
name string
fieldDropper func(*side_effects.Config)
}{
{
name: "proxy socket",
fieldDropper: func(c *side_effects.Config) {
c.Tko.ProxySocket = ""
},
},
{
name: "MySQL user",
fieldDropper: func(c *side_effects.Config) {
c.Tko.MysqlUser = ""
},
},
{
name: "Encrypted MySQL password",
fieldDropper: func(c *side_effects.Config) {
c.Tko.EncryptedMysqlPassword = ""
},
},
{
name: "Google Storage bucket",
fieldDropper: func(c *side_effects.Config) {
c.GoogleStorage.Bucket = ""
},
},
}
for _, c := range cases {
Convey(c.name, func() {
cfg := basicConfig()
c.fieldDropper(cfg)
err := ValidateConfig(cfg)
Convey("then the correct error is returned.", func() {
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, c.name)
})
})
}
})
}
func TestMissingFiles(t *testing.T) {
Convey("Given a missing", t, func() {
cases := []struct {
name string
fileDropper func(c *side_effects.Config)
}{
{
name: "proxy socket",
fileDropper: func(c *side_effects.Config) {
c.Tko.ProxySocket = uuid.New().String()
},
},
}
for _, c := range cases {
Convey(c.name, func() {
cfg := basicConfig()
c.fileDropper(cfg)
err := ValidateConfig(cfg)
Convey("then the correct error is returned.", func() {
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, c.name)
})
})
}
})
}
func TestWriteConfigToDisk(t *testing.T) {
Convey("Given side_effects.Config object", t, func() {
want := &side_effects.Config{
Tko: &side_effects.TKOConfig{
ProxySocket: "foo-socket",
MysqlUser: "foo-user",
MysqlPasswordFile: "foo-password-file",
},
GoogleStorage: &side_effects.GoogleStorageConfig{
Bucket: "foo-bucket",
CredentialsFile: "foo-creds",
},
}
Convey("when WriteConfigToDisk is called", func() {
dir, _ := ioutil.TempDir("", "")
err := WriteConfigToDisk(dir, want)
So(err, ShouldBeNil)
Convey("then the side_effects_config.json file contains the original object", func() {
f, fileErr := os.Open(filepath.Join(dir, "side_effects_config.json"))
So(fileErr, ShouldBeNil)
got := &side_effects.Config{}
um := jsonpb.Unmarshaler{}
unmarshalErr := um.Unmarshal(f, got)
So(unmarshalErr, ShouldBeNil)
So(got, ShouldResembleProto, want)
})
})
})
}
type fakeCloudKMSClient struct{}
func newFakeCloudKMSClient() *fakeCloudKMSClient {
return &fakeCloudKMSClient{}
}
func (c *fakeCloudKMSClient) Decrypt(_ context.Context, _ string) ([]byte, error) {
return []byte("decrypted-password"), nil
}
func TestPopulateTKOPasswordFile(t *testing.T) {
Convey("Given side_effects.Config with an encrypted password", t, func() {
ctx := context.Background()
cfg := basicConfig()
fc := newFakeCloudKMSClient()
Convey("when PopulateTKOPasswordFile is called", func() {
err := PopulateTKOPasswordFile(ctx, fc, cfg)
So(err, ShouldBeNil)
Convey("then side_effects.Config is populated with the password file path", func() {
So(cfg.GetTko().GetMysqlPasswordFile(), ShouldNotBeBlank)
Convey("which points to a file populated with right contents", func() {
got, err := ioutil.ReadFile(cfg.GetTko().GetMysqlPasswordFile())
So(err, ShouldBeNil)
So(string(got), ShouldEqual, "decrypted-password")
os.Remove(cfg.GetTko().GetMysqlPasswordFile())
})
})
})
})
}
func TestCleanupExistingFiles(t *testing.T) {
Convey("Given side_effects.Config pointing to an existing MySQL password file", t, func() {
f := tempFile()
cfg := &side_effects.Config{
Tko: &side_effects.TKOConfig{
MysqlPasswordFile: f,
},
}
Convey("when CleanupTempFiles is called", func() {
err := CleanupTempFiles(cfg)
So(err, ShouldBeNil)
Convey("then the password file is removed from both disk and config", func() {
_, err := os.Stat(f)
So(err, ShouldNotBeNil)
So(os.IsNotExist(err), ShouldBeTrue)
})
})
})
}
func TestCleanupNonExistingFiles(t *testing.T) {
Convey("Given side_effects.Config pointing to a non existing MySQL password file", t, func() {
cfg := &side_effects.Config{
Tko: &side_effects.TKOConfig{
MysqlPasswordFile: uuid.New().String(),
},
}
Convey("when CleanupTempFiles is called it does not return an error", func() {
err := CleanupTempFiles(cfg)
So(err, ShouldBeNil)
})
})
}