mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-04 13:17:43 -06:00
b9a93a0fe7
This is part of a general effort to move all of Terraform's non-library package surface under internal in order to reinforce that these are for internal use within Terraform only. If you were previously importing packages under this prefix into an external codebase, you could pin to an earlier release tag as an interim solution until you've make a plan to achieve the same functionality some other way.
279 lines
9.6 KiB
Go
279 lines
9.6 KiB
Go
package depsfile
|
|
|
|
import (
|
|
"bufio"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/getproviders"
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
)
|
|
|
|
func TestLoadLocksFromFile(t *testing.T) {
|
|
// For ease of test maintenance we treat every file under
|
|
// test-data/locks-files as a test case which is subject
|
|
// at least to testing that it produces an expected set
|
|
// of diagnostics represented via specially-formatted comments
|
|
// in the fixture files (which might be the empty set, if
|
|
// there are no such comments).
|
|
//
|
|
// Some of the files also have additional assertions that
|
|
// are encoded in the test code below. These must pass
|
|
// in addition to the standard diagnostics tests, if present.
|
|
files, err := ioutil.ReadDir("testdata/locks-files")
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
|
|
for _, info := range files {
|
|
testName := filepath.Base(info.Name())
|
|
filename := filepath.Join("testdata/locks-files", testName)
|
|
t.Run(testName, func(t *testing.T) {
|
|
f, err := os.Open(filename)
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
defer f.Close()
|
|
const errorPrefix = "# ERROR: "
|
|
const warningPrefix = "# WARNING: "
|
|
wantErrors := map[int]string{}
|
|
wantWarnings := map[int]string{}
|
|
sc := bufio.NewScanner(f)
|
|
lineNum := 1
|
|
for sc.Scan() {
|
|
l := sc.Text()
|
|
if pos := strings.Index(l, errorPrefix); pos != -1 {
|
|
wantSummary := l[pos+len(errorPrefix):]
|
|
wantErrors[lineNum] = wantSummary
|
|
}
|
|
if pos := strings.Index(l, warningPrefix); pos != -1 {
|
|
wantSummary := l[pos+len(warningPrefix):]
|
|
wantWarnings[lineNum] = wantSummary
|
|
}
|
|
lineNum++
|
|
}
|
|
if err := sc.Err(); err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
|
|
locks, diags := LoadLocksFromFile(filename)
|
|
gotErrors := map[int]string{}
|
|
gotWarnings := map[int]string{}
|
|
for _, diag := range diags {
|
|
summary := diag.Description().Summary
|
|
if diag.Source().Subject == nil {
|
|
// We don't expect any sourceless diagnostics here.
|
|
t.Errorf("unexpected sourceless diagnostic: %s", summary)
|
|
continue
|
|
}
|
|
lineNum := diag.Source().Subject.Start.Line
|
|
switch sev := diag.Severity(); sev {
|
|
case tfdiags.Error:
|
|
gotErrors[lineNum] = summary
|
|
case tfdiags.Warning:
|
|
gotWarnings[lineNum] = summary
|
|
default:
|
|
t.Errorf("unexpected diagnostic severity %s", sev)
|
|
}
|
|
}
|
|
|
|
if diff := cmp.Diff(wantErrors, gotErrors); diff != "" {
|
|
t.Errorf("wrong errors\n%s", diff)
|
|
}
|
|
if diff := cmp.Diff(wantWarnings, gotWarnings); diff != "" {
|
|
t.Errorf("wrong warnings\n%s", diff)
|
|
}
|
|
|
|
switch testName {
|
|
// These are the file-specific test assertions. Not all files
|
|
// need custom test assertions in addition to the standard
|
|
// diagnostics assertions implemented above, so the cases here
|
|
// don't need to be exhaustive for all files.
|
|
//
|
|
// Please keep these in alphabetical order so the list is easy
|
|
// to scan!
|
|
|
|
case "empty.hcl":
|
|
if got, want := len(locks.providers), 0; got != want {
|
|
t.Errorf("wrong number of providers %d; want %d", got, want)
|
|
}
|
|
|
|
case "valid-provider-locks.hcl":
|
|
if got, want := len(locks.providers), 3; got != want {
|
|
t.Errorf("wrong number of providers %d; want %d", got, want)
|
|
}
|
|
|
|
t.Run("version-only", func(t *testing.T) {
|
|
if lock := locks.Provider(addrs.MustParseProviderSourceString("terraform.io/test/version-only")); lock != nil {
|
|
if got, want := lock.Version().String(), "1.0.0"; got != want {
|
|
t.Errorf("wrong version\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
if got, want := getproviders.VersionConstraintsString(lock.VersionConstraints()), ""; got != want {
|
|
t.Errorf("wrong version constraints\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
if got, want := len(lock.hashes), 0; got != want {
|
|
t.Errorf("wrong number of hashes %d; want %d", got, want)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("version-and-constraints", func(t *testing.T) {
|
|
if lock := locks.Provider(addrs.MustParseProviderSourceString("terraform.io/test/version-and-constraints")); lock != nil {
|
|
if got, want := lock.Version().String(), "1.2.0"; got != want {
|
|
t.Errorf("wrong version\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
if got, want := getproviders.VersionConstraintsString(lock.VersionConstraints()), "~> 1.2"; got != want {
|
|
t.Errorf("wrong version constraints\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
if got, want := len(lock.hashes), 0; got != want {
|
|
t.Errorf("wrong number of hashes %d; want %d", got, want)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("all-the-things", func(t *testing.T) {
|
|
if lock := locks.Provider(addrs.MustParseProviderSourceString("terraform.io/test/all-the-things")); lock != nil {
|
|
if got, want := lock.Version().String(), "3.0.10"; got != want {
|
|
t.Errorf("wrong version\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
if got, want := getproviders.VersionConstraintsString(lock.VersionConstraints()), ">= 3.0.2"; got != want {
|
|
t.Errorf("wrong version constraints\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
wantHashes := []getproviders.Hash{
|
|
getproviders.MustParseHash("test:placeholder-hash-1"),
|
|
getproviders.MustParseHash("test:placeholder-hash-2"),
|
|
getproviders.MustParseHash("test:placeholder-hash-3"),
|
|
}
|
|
if diff := cmp.Diff(wantHashes, lock.hashes); diff != "" {
|
|
t.Errorf("wrong hashes\n%s", diff)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadLocksFromFileAbsent(t *testing.T) {
|
|
t.Run("lock file is a directory", func(t *testing.T) {
|
|
// This can never happen when Terraform is the one generating the
|
|
// lock file, but might arise if the user makes a directory with the
|
|
// lock file's name for some reason. (There is no actual reason to do
|
|
// so, so that would always be a mistake.)
|
|
locks, diags := LoadLocksFromFile("testdata")
|
|
if len(locks.providers) != 0 {
|
|
t.Errorf("returned locks has providers; expected empty locks")
|
|
}
|
|
if !diags.HasErrors() {
|
|
t.Fatalf("LoadLocksFromFile succeeded; want error")
|
|
}
|
|
// This is a generic error message from HCL itself, so upgrading HCL
|
|
// in future might cause a different error message here.
|
|
want := `Failed to read file: The configuration file "testdata" could not be read.`
|
|
got := diags.Err().Error()
|
|
if got != want {
|
|
t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
})
|
|
t.Run("lock file doesn't exist", func(t *testing.T) {
|
|
locks, diags := LoadLocksFromFile("testdata/nonexist.hcl")
|
|
if len(locks.providers) != 0 {
|
|
t.Errorf("returned locks has providers; expected empty locks")
|
|
}
|
|
if !diags.HasErrors() {
|
|
t.Fatalf("LoadLocksFromFile succeeded; want error")
|
|
}
|
|
// This is a generic error message from HCL itself, so upgrading HCL
|
|
// in future might cause a different error message here.
|
|
want := `Failed to read file: The configuration file "testdata/nonexist.hcl" could not be read.`
|
|
got := diags.Err().Error()
|
|
if got != want {
|
|
t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestSaveLocksToFile(t *testing.T) {
|
|
locks := NewLocks()
|
|
|
|
fooProvider := addrs.MustParseProviderSourceString("test/foo")
|
|
barProvider := addrs.MustParseProviderSourceString("test/bar")
|
|
bazProvider := addrs.MustParseProviderSourceString("test/baz")
|
|
booProvider := addrs.MustParseProviderSourceString("test/boo")
|
|
oneDotOh := getproviders.MustParseVersion("1.0.0")
|
|
oneDotTwo := getproviders.MustParseVersion("1.2.0")
|
|
atLeastOneDotOh := getproviders.MustParseVersionConstraints(">= 1.0.0")
|
|
pessimisticOneDotOh := getproviders.MustParseVersionConstraints("~> 1")
|
|
abbreviatedOneDotTwo := getproviders.MustParseVersionConstraints("1.2")
|
|
hashes := []getproviders.Hash{
|
|
getproviders.MustParseHash("test:cccccccccccccccccccccccccccccccccccccccccccccccc"),
|
|
getproviders.MustParseHash("test:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
|
|
getproviders.MustParseHash("test:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
|
|
}
|
|
locks.SetProvider(fooProvider, oneDotOh, atLeastOneDotOh, hashes)
|
|
locks.SetProvider(barProvider, oneDotTwo, pessimisticOneDotOh, nil)
|
|
locks.SetProvider(bazProvider, oneDotTwo, nil, nil)
|
|
locks.SetProvider(booProvider, oneDotTwo, abbreviatedOneDotTwo, nil)
|
|
|
|
dir, err := ioutil.TempDir("", "terraform-internal-depsfile-savelockstofile")
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
filename := filepath.Join(dir, LockFilePath)
|
|
diags := SaveLocksToFile(locks, filename)
|
|
if diags.HasErrors() {
|
|
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
|
|
}
|
|
|
|
fileInfo, err := os.Stat(filename)
|
|
if err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
if mode := fileInfo.Mode(); mode&0111 != 0 {
|
|
t.Fatalf("Expected lock file to be non-executable: %o", mode)
|
|
}
|
|
|
|
gotContentBytes, err := ioutil.ReadFile(filename)
|
|
if err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
gotContent := string(gotContentBytes)
|
|
wantContent := `# This file is maintained automatically by "terraform init".
|
|
# Manual edits may be lost in future updates.
|
|
|
|
provider "registry.terraform.io/test/bar" {
|
|
version = "1.2.0"
|
|
constraints = "~> 1.0"
|
|
}
|
|
|
|
provider "registry.terraform.io/test/baz" {
|
|
version = "1.2.0"
|
|
}
|
|
|
|
provider "registry.terraform.io/test/boo" {
|
|
version = "1.2.0"
|
|
constraints = "1.2.0"
|
|
}
|
|
|
|
provider "registry.terraform.io/test/foo" {
|
|
version = "1.0.0"
|
|
constraints = ">= 1.0.0"
|
|
hashes = [
|
|
"test:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
"test:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
"test:cccccccccccccccccccccccccccccccccccccccccccccccc",
|
|
]
|
|
}
|
|
`
|
|
if diff := cmp.Diff(wantContent, gotContent); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
}
|