databricks-cli/acceptance/config_test.go

133 lines
3.0 KiB
Go

package acceptance_test
import (
"os"
"path/filepath"
"slices"
"strings"
"testing"
"dario.cat/mergo"
"github.com/BurntSushi/toml"
"github.com/databricks/cli/libs/testdiff"
"github.com/stretchr/testify/require"
)
const configFilename = "test.toml"
type TestConfig struct {
// Place to describe what's wrong with this test. Does not affect how the test is run.
Badness string
// Which OSes the test is enabled on. Each string is compared against runtime.GOOS.
// If absent, default to true.
GOOS map[string]bool
// If true, do not run this test against cloud environment
LocalOnly bool
// List of additional replacements to apply on this test.
// Old is a regexp, New is a replacement expression.
Repls []testdiff.Replacement
// List of server stubs to load. Example configuration:
//
// [[Server]]
// Pattern = "POST /api/2.1/jobs/create"
// Response.Body = '''
// {
// "job_id": 1111
// }
// '''
Server []ServerStub
// Record the requests made to the server and write them as output to
// out.requests.txt
RecordRequests bool
// List of request headers to include when recording requests.
IncludeRequestHeaders []string
}
type ServerStub struct {
// The HTTP method and path to match. Examples:
// 1. /api/2.0/clusters/list (matches all methods)
// 2. GET /api/2.0/clusters/list
Pattern string
// The response body to return.
Response struct {
Body string
StatusCode int
}
}
// FindConfigs finds all the config relevant for this test,
// ordered from the most outermost (at acceptance/) to current test directory (identified by dir).
// Argument dir must be a relative path from the root of acceptance tests (<project_root>/acceptance/).
func FindConfigs(t *testing.T, dir string) []string {
configs := []string{}
for {
path := filepath.Join(dir, configFilename)
_, err := os.Stat(path)
if err == nil {
configs = append(configs, path)
}
if dir == "" || dir == "." {
break
}
dir = filepath.Dir(dir)
if err == nil || os.IsNotExist(err) {
continue
}
t.Fatalf("Error while reading %s: %s", path, err)
}
slices.Reverse(configs)
return configs
}
// LoadConfig loads the config file. Non-leaf configs are cached.
func LoadConfig(t *testing.T, dir string) (TestConfig, string) {
configs := FindConfigs(t, dir)
if len(configs) == 0 {
return TestConfig{}, "(no config)"
}
result := DoLoadConfig(t, configs[0])
for _, cfgName := range configs[1:] {
cfg := DoLoadConfig(t, cfgName)
err := mergo.Merge(&result, cfg, mergo.WithOverride, mergo.WithAppendSlice)
if err != nil {
t.Fatalf("Error during config merge: %s: %s", cfgName, err)
}
}
return result, strings.Join(configs, ", ")
}
func DoLoadConfig(t *testing.T, path string) TestConfig {
bytes, err := os.ReadFile(path)
if err != nil {
t.Fatalf("failed to read config: %s", err)
}
var config TestConfig
meta, err := toml.Decode(string(bytes), &config)
require.NoError(t, err)
keys := meta.Undecoded()
if len(keys) > 0 {
t.Fatalf("Undecoded keys in %s: %#v", path, keys)
}
return config
}