github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/depsfile/locks_file_test.go (about)

     1  package depsfile
     2  
     3  import (
     4  	"bufio"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  	"github.com/hashicorp/terraform/internal/addrs"
    13  	"github.com/hashicorp/terraform/internal/getproviders"
    14  	"github.com/hashicorp/terraform/internal/tfdiags"
    15  )
    16  
    17  func TestLoadLocksFromFile(t *testing.T) {
    18  	// For ease of test maintenance we treat every file under
    19  	// test-data/locks-files as a test case which is subject
    20  	// at least to testing that it produces an expected set
    21  	// of diagnostics represented via specially-formatted comments
    22  	// in the fixture files (which might be the empty set, if
    23  	// there are no such comments).
    24  	//
    25  	// Some of the files also have additional assertions that
    26  	// are encoded in the test code below. These must pass
    27  	// in addition to the standard diagnostics tests, if present.
    28  	files, err := ioutil.ReadDir("testdata/locks-files")
    29  	if err != nil {
    30  		t.Fatal(err.Error())
    31  	}
    32  
    33  	for _, info := range files {
    34  		testName := filepath.Base(info.Name())
    35  		filename := filepath.Join("testdata/locks-files", testName)
    36  		t.Run(testName, func(t *testing.T) {
    37  			f, err := os.Open(filename)
    38  			if err != nil {
    39  				t.Fatal(err.Error())
    40  			}
    41  			defer f.Close()
    42  			const errorPrefix = "# ERROR: "
    43  			const warningPrefix = "# WARNING: "
    44  			wantErrors := map[int]string{}
    45  			wantWarnings := map[int]string{}
    46  			sc := bufio.NewScanner(f)
    47  			lineNum := 1
    48  			for sc.Scan() {
    49  				l := sc.Text()
    50  				if pos := strings.Index(l, errorPrefix); pos != -1 {
    51  					wantSummary := l[pos+len(errorPrefix):]
    52  					wantErrors[lineNum] = wantSummary
    53  				}
    54  				if pos := strings.Index(l, warningPrefix); pos != -1 {
    55  					wantSummary := l[pos+len(warningPrefix):]
    56  					wantWarnings[lineNum] = wantSummary
    57  				}
    58  				lineNum++
    59  			}
    60  			if err := sc.Err(); err != nil {
    61  				t.Fatal(err.Error())
    62  			}
    63  
    64  			locks, diags := LoadLocksFromFile(filename)
    65  			gotErrors := map[int]string{}
    66  			gotWarnings := map[int]string{}
    67  			for _, diag := range diags {
    68  				summary := diag.Description().Summary
    69  				if diag.Source().Subject == nil {
    70  					// We don't expect any sourceless diagnostics here.
    71  					t.Errorf("unexpected sourceless diagnostic: %s", summary)
    72  					continue
    73  				}
    74  				lineNum := diag.Source().Subject.Start.Line
    75  				switch sev := diag.Severity(); sev {
    76  				case tfdiags.Error:
    77  					gotErrors[lineNum] = summary
    78  				case tfdiags.Warning:
    79  					gotWarnings[lineNum] = summary
    80  				default:
    81  					t.Errorf("unexpected diagnostic severity %s", sev)
    82  				}
    83  			}
    84  
    85  			if diff := cmp.Diff(wantErrors, gotErrors); diff != "" {
    86  				t.Errorf("wrong errors\n%s", diff)
    87  			}
    88  			if diff := cmp.Diff(wantWarnings, gotWarnings); diff != "" {
    89  				t.Errorf("wrong warnings\n%s", diff)
    90  			}
    91  
    92  			switch testName {
    93  			// These are the file-specific test assertions. Not all files
    94  			// need custom test assertions in addition to the standard
    95  			// diagnostics assertions implemented above, so the cases here
    96  			// don't need to be exhaustive for all files.
    97  			//
    98  			// Please keep these in alphabetical order so the list is easy
    99  			// to scan!
   100  
   101  			case "empty.hcl":
   102  				if got, want := len(locks.providers), 0; got != want {
   103  					t.Errorf("wrong number of providers %d; want %d", got, want)
   104  				}
   105  
   106  			case "valid-provider-locks.hcl":
   107  				if got, want := len(locks.providers), 3; got != want {
   108  					t.Errorf("wrong number of providers %d; want %d", got, want)
   109  				}
   110  
   111  				t.Run("version-only", func(t *testing.T) {
   112  					if lock := locks.Provider(addrs.MustParseProviderSourceString("terraform.io/test/version-only")); lock != nil {
   113  						if got, want := lock.Version().String(), "1.0.0"; got != want {
   114  							t.Errorf("wrong version\ngot:  %s\nwant: %s", got, want)
   115  						}
   116  						if got, want := getproviders.VersionConstraintsString(lock.VersionConstraints()), ""; got != want {
   117  							t.Errorf("wrong version constraints\ngot:  %s\nwant: %s", got, want)
   118  						}
   119  						if got, want := len(lock.hashes), 0; got != want {
   120  							t.Errorf("wrong number of hashes %d; want %d", got, want)
   121  						}
   122  					}
   123  				})
   124  
   125  				t.Run("version-and-constraints", func(t *testing.T) {
   126  					if lock := locks.Provider(addrs.MustParseProviderSourceString("terraform.io/test/version-and-constraints")); lock != nil {
   127  						if got, want := lock.Version().String(), "1.2.0"; got != want {
   128  							t.Errorf("wrong version\ngot:  %s\nwant: %s", got, want)
   129  						}
   130  						if got, want := getproviders.VersionConstraintsString(lock.VersionConstraints()), "~> 1.2"; got != want {
   131  							t.Errorf("wrong version constraints\ngot:  %s\nwant: %s", got, want)
   132  						}
   133  						if got, want := len(lock.hashes), 0; got != want {
   134  							t.Errorf("wrong number of hashes %d; want %d", got, want)
   135  						}
   136  					}
   137  				})
   138  
   139  				t.Run("all-the-things", func(t *testing.T) {
   140  					if lock := locks.Provider(addrs.MustParseProviderSourceString("terraform.io/test/all-the-things")); lock != nil {
   141  						if got, want := lock.Version().String(), "3.0.10"; got != want {
   142  							t.Errorf("wrong version\ngot:  %s\nwant: %s", got, want)
   143  						}
   144  						if got, want := getproviders.VersionConstraintsString(lock.VersionConstraints()), ">= 3.0.2"; got != want {
   145  							t.Errorf("wrong version constraints\ngot:  %s\nwant: %s", got, want)
   146  						}
   147  						wantHashes := []getproviders.Hash{
   148  							getproviders.MustParseHash("test:placeholder-hash-1"),
   149  							getproviders.MustParseHash("test:placeholder-hash-2"),
   150  							getproviders.MustParseHash("test:placeholder-hash-3"),
   151  						}
   152  						if diff := cmp.Diff(wantHashes, lock.hashes); diff != "" {
   153  							t.Errorf("wrong hashes\n%s", diff)
   154  						}
   155  					}
   156  				})
   157  			}
   158  		})
   159  	}
   160  }
   161  
   162  func TestLoadLocksFromFileAbsent(t *testing.T) {
   163  	t.Run("lock file is a directory", func(t *testing.T) {
   164  		// This can never happen when Terraform is the one generating the
   165  		// lock file, but might arise if the user makes a directory with the
   166  		// lock file's name for some reason. (There is no actual reason to do
   167  		// so, so that would always be a mistake.)
   168  		locks, diags := LoadLocksFromFile("testdata")
   169  		if len(locks.providers) != 0 {
   170  			t.Errorf("returned locks has providers; expected empty locks")
   171  		}
   172  		if !diags.HasErrors() {
   173  			t.Fatalf("LoadLocksFromFile succeeded; want error")
   174  		}
   175  		// This is a generic error message from HCL itself, so upgrading HCL
   176  		// in future might cause a different error message here.
   177  		want := `Failed to read file: The configuration file "testdata" could not be read.`
   178  		got := diags.Err().Error()
   179  		if got != want {
   180  			t.Errorf("wrong error message\ngot:  %s\nwant: %s", got, want)
   181  		}
   182  	})
   183  	t.Run("lock file doesn't exist", func(t *testing.T) {
   184  		locks, diags := LoadLocksFromFile("testdata/nonexist.hcl")
   185  		if len(locks.providers) != 0 {
   186  			t.Errorf("returned locks has providers; expected empty locks")
   187  		}
   188  		if !diags.HasErrors() {
   189  			t.Fatalf("LoadLocksFromFile succeeded; want error")
   190  		}
   191  		// This is a generic error message from HCL itself, so upgrading HCL
   192  		// in future might cause a different error message here.
   193  		want := `Failed to read file: The configuration file "testdata/nonexist.hcl" could not be read.`
   194  		got := diags.Err().Error()
   195  		if got != want {
   196  			t.Errorf("wrong error message\ngot:  %s\nwant: %s", got, want)
   197  		}
   198  	})
   199  }
   200  
   201  func TestSaveLocksToFile(t *testing.T) {
   202  	locks := NewLocks()
   203  
   204  	fooProvider := addrs.MustParseProviderSourceString("test/foo")
   205  	barProvider := addrs.MustParseProviderSourceString("test/bar")
   206  	bazProvider := addrs.MustParseProviderSourceString("test/baz")
   207  	booProvider := addrs.MustParseProviderSourceString("test/boo")
   208  	oneDotOh := getproviders.MustParseVersion("1.0.0")
   209  	oneDotTwo := getproviders.MustParseVersion("1.2.0")
   210  	atLeastOneDotOh := getproviders.MustParseVersionConstraints(">= 1.0.0")
   211  	pessimisticOneDotOh := getproviders.MustParseVersionConstraints("~> 1")
   212  	abbreviatedOneDotTwo := getproviders.MustParseVersionConstraints("1.2")
   213  	hashes := []getproviders.Hash{
   214  		getproviders.MustParseHash("test:cccccccccccccccccccccccccccccccccccccccccccccccc"),
   215  		getproviders.MustParseHash("test:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
   216  		getproviders.MustParseHash("test:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
   217  	}
   218  	locks.SetProvider(fooProvider, oneDotOh, atLeastOneDotOh, hashes)
   219  	locks.SetProvider(barProvider, oneDotTwo, pessimisticOneDotOh, nil)
   220  	locks.SetProvider(bazProvider, oneDotTwo, nil, nil)
   221  	locks.SetProvider(booProvider, oneDotTwo, abbreviatedOneDotTwo, nil)
   222  
   223  	dir := t.TempDir()
   224  
   225  	filename := filepath.Join(dir, LockFilePath)
   226  	diags := SaveLocksToFile(locks, filename)
   227  	if diags.HasErrors() {
   228  		t.Fatalf("unexpected errors\n%s", diags.Err().Error())
   229  	}
   230  
   231  	fileInfo, err := os.Stat(filename)
   232  	if err != nil {
   233  		t.Fatalf(err.Error())
   234  	}
   235  	if mode := fileInfo.Mode(); mode&0111 != 0 {
   236  		t.Fatalf("Expected lock file to be non-executable: %o", mode)
   237  	}
   238  
   239  	gotContentBytes, err := ioutil.ReadFile(filename)
   240  	if err != nil {
   241  		t.Fatalf(err.Error())
   242  	}
   243  	gotContent := string(gotContentBytes)
   244  	wantContent := `# This file is maintained automatically by "terraform init".
   245  # Manual edits may be lost in future updates.
   246  
   247  provider "registry.terraform.io/test/bar" {
   248    version     = "1.2.0"
   249    constraints = "~> 1.0"
   250  }
   251  
   252  provider "registry.terraform.io/test/baz" {
   253    version = "1.2.0"
   254  }
   255  
   256  provider "registry.terraform.io/test/boo" {
   257    version     = "1.2.0"
   258    constraints = "1.2.0"
   259  }
   260  
   261  provider "registry.terraform.io/test/foo" {
   262    version     = "1.0.0"
   263    constraints = ">= 1.0.0"
   264    hashes = [
   265      "test:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   266      "test:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
   267      "test:cccccccccccccccccccccccccccccccccccccccccccccccc",
   268    ]
   269  }
   270  `
   271  	if diff := cmp.Diff(wantContent, gotContent); diff != "" {
   272  		t.Errorf("wrong result\n%s", diff)
   273  	}
   274  }