blob: 7db4763033df9b5390bf9f3db182360c2719c7b7 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package graph
import (
"bytes"
"context"
"fmt"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclparse"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/moduletest"
)
func TestTransformForTest(t *testing.T) {
str := func(providers map[string]string) string {
var buffer bytes.Buffer
for key, config := range providers {
buffer.WriteString(fmt.Sprintf("%s: %s\n", key, config))
}
return buffer.String()
}
convertToProviders := func(t *testing.T, contents map[string]string) map[string]*configs.Provider {
t.Helper()
providers := make(map[string]*configs.Provider)
for key, content := range contents {
parser := hclparse.NewParser()
file, diags := parser.ParseHCL([]byte(content), fmt.Sprintf("%s.hcl", key))
if diags.HasErrors() {
t.Fatal(diags.Error())
}
provider := &configs.Provider{
Config: file.Body,
}
parts := strings.Split(key, ".")
provider.Name = parts[0]
if len(parts) > 1 {
provider.Alias = parts[1]
}
providers[key] = provider
}
return providers
}
tcs := map[string]struct {
configProviders map[string]string
fileProviders map[string]string
runProviders []configs.PassedProviderConfig
expectedProviders map[string]string
expectedErrors []string
}{
"empty": {
configProviders: make(map[string]string),
expectedProviders: make(map[string]string),
},
"only providers in config": {
configProviders: map[string]string{
"foo": "source = \"config\"",
"bar": "source = \"config\"",
},
expectedProviders: map[string]string{
"foo": "source = \"config\"",
"bar": "source = \"config\"",
},
},
"only providers in test file": {
configProviders: make(map[string]string),
fileProviders: map[string]string{
"foo": "source = \"testfile\"",
"bar": "source = \"testfile\"",
},
expectedProviders: map[string]string{
"foo": "source = \"testfile\"",
"bar": "source = \"testfile\"",
},
},
"only providers in run block": {
configProviders: make(map[string]string),
runProviders: []configs.PassedProviderConfig{
{
InChild: &configs.ProviderConfigRef{
Name: "foo",
},
InParent: &configs.ProviderConfigRef{
Name: "bar",
},
},
},
expectedProviders: make(map[string]string),
expectedErrors: []string{
":0,0-0: Missing provider definition for bar; This provider block references a provider definition that does not exist.",
},
},
"subset of providers in test file": {
configProviders: make(map[string]string),
fileProviders: map[string]string{
"bar": "source = \"testfile\"",
},
runProviders: []configs.PassedProviderConfig{
{
InChild: &configs.ProviderConfigRef{
Name: "foo",
},
InParent: &configs.ProviderConfigRef{
Name: "bar",
},
},
},
expectedProviders: map[string]string{
"foo": "source = \"testfile\"",
},
},
"overrides providers in config": {
configProviders: map[string]string{
"foo": "source = \"config\"",
"bar": "source = \"config\"",
},
fileProviders: map[string]string{
"bar": "source = \"testfile\"",
},
expectedProviders: map[string]string{
"foo": "source = \"config\"",
"bar": "source = \"testfile\"",
},
},
"overrides subset of providers in config": {
configProviders: map[string]string{
"foo": "source = \"config\"",
"bar": "source = \"config\"",
},
fileProviders: map[string]string{
"foo": "source = \"testfile\"",
"bar": "source = \"testfile\"",
},
runProviders: []configs.PassedProviderConfig{
{
InChild: &configs.ProviderConfigRef{
Name: "bar",
},
InParent: &configs.ProviderConfigRef{
Name: "bar",
},
},
},
expectedProviders: map[string]string{
"foo": "source = \"config\"",
"bar": "source = \"testfile\"",
},
},
"handles aliases": {
configProviders: map[string]string{
"foo.primary": "source = \"config\"",
"foo.secondary": "source = \"config\"",
},
fileProviders: map[string]string{
"foo": "source = \"testfile\"",
},
runProviders: []configs.PassedProviderConfig{
{
InChild: &configs.ProviderConfigRef{
Name: "foo.secondary",
},
InParent: &configs.ProviderConfigRef{
Name: "foo",
},
},
},
expectedProviders: map[string]string{
"foo.primary": "source = \"config\"",
"foo.secondary": "source = \"testfile\"",
},
},
"ignores unexpected providers in test file": {
configProviders: make(map[string]string),
fileProviders: map[string]string{
"foo": "source = \"testfile\"",
"bar": "source = \"testfile\"",
},
expectedProviders: map[string]string{
"foo": "source = \"testfile\"",
},
},
}
for name, tc := range tcs {
t.Run(name, func(t *testing.T) {
config := &configs.Config{
Module: &configs.Module{
ProviderConfigs: convertToProviders(t, tc.configProviders),
},
}
file := &moduletest.File{
Config: &configs.TestFile{
Providers: convertToProviders(t, tc.fileProviders),
},
}
run := &moduletest.Run{
Config: &configs.TestRun{
Providers: tc.runProviders,
},
ModuleConfig: config,
}
availableProviders := make(map[string]bool, len(tc.expectedProviders))
for provider := range tc.expectedProviders {
availableProviders[provider] = true
}
ctx := NewEvalContext(&EvalContextOpts{
CancelCtx: context.Background(),
StopCtx: context.Background(),
})
ctx.configProviders = map[string]map[string]bool{
run.GetModuleConfigID(): availableProviders,
}
diags := TransformConfigForRun(ctx, run, file)
var actualErrs []string
for _, err := range diags.Errs() {
actualErrs = append(actualErrs, err.Error())
}
if diff := cmp.Diff(actualErrs, tc.expectedErrors, cmpopts.IgnoreUnexported()); len(diff) > 0 {
t.Errorf("unmatched errors\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", strings.Join(tc.expectedErrors, "\n"), strings.Join(actualErrs, "\n"), diff)
}
converted := make(map[string]string)
for key, provider := range config.Module.ProviderConfigs {
content, err := provider.Config.Content(&hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{Name: "source", Required: true},
},
})
if err != nil {
t.Fatal(err)
}
source, diags := content.Attributes["source"].Expr.Value(nil)
if diags.HasErrors() {
t.Fatal(diags.Error())
}
converted[key] = fmt.Sprintf("source = %q", source.AsString())
}
if diff := cmp.Diff(tc.expectedProviders, converted); len(diff) > 0 {
t.Errorf("%s\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", "after transform mismatch", str(tc.expectedProviders), str(converted), diff)
}
})
}
}