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 }