internal/depsfile: Loading locks from HCL files on disk

This is the initial implementation of the parser/decoder portion of the
new dependency lock file handler. It's currently dead code because the
caller isn't written yet. We'll continue to build out this functionality
here until we have the basic level of both load and save functionality
before introducing this into the provider installer codepath.
This commit is contained in:
Martin Atkins
2020-09-03 18:35:31 -07:00
parent 6993ecb0a6
commit 92723661d0
11 changed files with 784 additions and 1 deletions

View File

@@ -0,0 +1,164 @@
package depsfile
import (
"bufio"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/internal/getproviders"
"github.com/hashicorp/terraform/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 := map[getproviders.Platform][]string{
{OS: "amigaos", Arch: "m68k"}: {
"placeholder-hash-1",
},
{OS: "tos", Arch: "m68k"}: {
"placeholder-hash-2",
"placeholder-hash-3",
},
}
if diff := cmp.Diff(wantHashes, lock.hashes); diff != "" {
t.Errorf("wrong hashes\n%s", diff)
}
}
})
}
})
}
}