github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/getproviders/multi_source_test.go (about) 1 package getproviders 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/google/go-cmp/cmp" 8 "github.com/eliastor/durgaform/internal/addrs" 9 ) 10 11 func TestMultiSourceAvailableVersions(t *testing.T) { 12 platform1 := Platform{OS: "amigaos", Arch: "m68k"} 13 platform2 := Platform{OS: "aros", Arch: "arm"} 14 15 t.Run("unfiltered merging", func(t *testing.T) { 16 s1 := NewMockSource([]PackageMeta{ 17 FakePackageMeta( 18 addrs.NewDefaultProvider("foo"), 19 MustParseVersion("1.0.0"), 20 VersionList{MustParseVersion("5.0")}, 21 platform1, 22 ), 23 FakePackageMeta( 24 addrs.NewDefaultProvider("foo"), 25 MustParseVersion("1.0.0"), 26 VersionList{MustParseVersion("5.0")}, 27 platform2, 28 ), 29 FakePackageMeta( 30 addrs.NewDefaultProvider("bar"), 31 MustParseVersion("1.0.0"), 32 VersionList{MustParseVersion("5.0")}, 33 platform2, 34 ), 35 }, 36 nil, 37 ) 38 s2 := NewMockSource([]PackageMeta{ 39 FakePackageMeta( 40 addrs.NewDefaultProvider("foo"), 41 MustParseVersion("1.0.0"), 42 VersionList{MustParseVersion("5.0")}, 43 platform1, 44 ), 45 FakePackageMeta( 46 addrs.NewDefaultProvider("foo"), 47 MustParseVersion("1.2.0"), 48 VersionList{MustParseVersion("5.0")}, 49 platform1, 50 ), 51 FakePackageMeta( 52 addrs.NewDefaultProvider("bar"), 53 MustParseVersion("1.0.0"), 54 VersionList{MustParseVersion("5.0")}, 55 platform1, 56 ), 57 }, 58 nil, 59 ) 60 multi := MultiSource{ 61 {Source: s1}, 62 {Source: s2}, 63 } 64 65 // AvailableVersions produces the union of all versions available 66 // across all of the sources. 67 got, _, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("foo")) 68 if err != nil { 69 t.Fatalf("unexpected error: %s", err) 70 } 71 want := VersionList{ 72 MustParseVersion("1.0.0"), 73 MustParseVersion("1.2.0"), 74 } 75 76 if diff := cmp.Diff(want, got); diff != "" { 77 t.Errorf("wrong result\n%s", diff) 78 } 79 80 _, _, err = multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("baz")) 81 if want, ok := err.(ErrRegistryProviderNotKnown); !ok { 82 t.Fatalf("wrong error type:\ngot: %T\nwant: %T", err, want) 83 } 84 }) 85 86 t.Run("merging with filters", func(t *testing.T) { 87 // This is just testing that filters are being honored at all, using a 88 // specific pair of filters. The different filter combinations 89 // themselves are tested in TestMultiSourceSelector. 90 91 s1 := NewMockSource([]PackageMeta{ 92 FakePackageMeta( 93 addrs.NewDefaultProvider("foo"), 94 MustParseVersion("1.0.0"), 95 VersionList{MustParseVersion("5.0")}, 96 platform1, 97 ), 98 FakePackageMeta( 99 addrs.NewDefaultProvider("bar"), 100 MustParseVersion("1.0.0"), 101 VersionList{MustParseVersion("5.0")}, 102 platform1, 103 ), 104 }, 105 nil, 106 ) 107 s2 := NewMockSource([]PackageMeta{ 108 FakePackageMeta( 109 addrs.NewDefaultProvider("foo"), 110 MustParseVersion("1.2.0"), 111 VersionList{MustParseVersion("5.0")}, 112 platform1, 113 ), 114 FakePackageMeta( 115 addrs.NewDefaultProvider("bar"), 116 MustParseVersion("1.2.0"), 117 VersionList{MustParseVersion("5.0")}, 118 platform1, 119 ), 120 }, 121 nil, 122 ) 123 multi := MultiSource{ 124 { 125 Source: s1, 126 Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"), 127 }, 128 { 129 Source: s2, 130 Include: mustParseMultiSourceMatchingPatterns("hashicorp/bar"), 131 }, 132 } 133 134 got, _, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("foo")) 135 if err != nil { 136 t.Fatalf("unexpected error: %s", err) 137 } 138 want := VersionList{ 139 MustParseVersion("1.0.0"), 140 // 1.2.0 isn't present because s3 doesn't include "foo" 141 } 142 if diff := cmp.Diff(want, got); diff != "" { 143 t.Errorf("wrong result\n%s", diff) 144 } 145 146 got, _, err = multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("bar")) 147 if err != nil { 148 t.Fatalf("unexpected error: %s", err) 149 } 150 want = VersionList{ 151 MustParseVersion("1.0.0"), 152 MustParseVersion("1.2.0"), // included because s2 matches "bar" 153 } 154 if diff := cmp.Diff(want, got); diff != "" { 155 t.Errorf("wrong result\n%s", diff) 156 } 157 158 _, _, err = multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("baz")) 159 if want, ok := err.(ErrRegistryProviderNotKnown); !ok { 160 t.Fatalf("wrong error type:\ngot: %T\nwant: %T", err, want) 161 } 162 }) 163 164 t.Run("provider not found", func(t *testing.T) { 165 s1 := NewMockSource(nil, nil) 166 s2 := NewMockSource(nil, nil) 167 multi := MultiSource{ 168 {Source: s1}, 169 {Source: s2}, 170 } 171 172 _, _, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("foo")) 173 if err == nil { 174 t.Fatal("expected error, got success") 175 } 176 177 wantErr := `provider registry registry.durgaform.io does not have a provider named registry.terraform.io/hashicorp/foo` 178 179 if err.Error() != wantErr { 180 t.Fatalf("wrong error.\ngot: %s\nwant: %s\n", err, wantErr) 181 } 182 183 }) 184 185 t.Run("merging with warnings", func(t *testing.T) { 186 platform1 := Platform{OS: "amigaos", Arch: "m68k"} 187 platform2 := Platform{OS: "aros", Arch: "arm"} 188 s1 := NewMockSource([]PackageMeta{ 189 FakePackageMeta( 190 addrs.NewDefaultProvider("bar"), 191 MustParseVersion("1.0.0"), 192 VersionList{MustParseVersion("5.0")}, 193 platform2, 194 ), 195 }, 196 map[addrs.Provider]Warnings{ 197 addrs.NewDefaultProvider("bar"): {"WARNING!"}, 198 }, 199 ) 200 s2 := NewMockSource([]PackageMeta{ 201 FakePackageMeta( 202 addrs.NewDefaultProvider("bar"), 203 MustParseVersion("1.0.0"), 204 VersionList{MustParseVersion("5.0")}, 205 platform1, 206 ), 207 }, 208 nil, 209 ) 210 multi := MultiSource{ 211 {Source: s1}, 212 {Source: s2}, 213 } 214 215 // AvailableVersions produces the union of all versions available 216 // across all of the sources. 217 got, warns, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("bar")) 218 if err != nil { 219 t.Fatalf("unexpected error: %s", err) 220 } 221 want := VersionList{ 222 MustParseVersion("1.0.0"), 223 } 224 if diff := cmp.Diff(want, got); diff != "" { 225 t.Errorf("wrong result\n%s", diff) 226 } 227 228 if len(warns) != 1 { 229 t.Fatalf("wrong number of warnings. Got %d, wanted 1", len(warns)) 230 } 231 if warns[0] != "WARNING!" { 232 t.Fatalf("wrong warnings. Got %s, wanted \"WARNING!\"", warns[0]) 233 } 234 }) 235 } 236 237 func TestMultiSourcePackageMeta(t *testing.T) { 238 platform1 := Platform{OS: "amigaos", Arch: "m68k"} 239 platform2 := Platform{OS: "aros", Arch: "arm"} 240 241 // We'll use the Filename field of the fake PackageMetas we created above 242 // to create a difference between the packages in s1 and the ones in s2, 243 // so we can test where individual packages came from below. 244 fakeFilename := func(fn string, meta PackageMeta) PackageMeta { 245 meta.Filename = fn 246 return meta 247 } 248 249 onlyInS1 := fakeFilename("s1", FakePackageMeta( 250 addrs.NewDefaultProvider("foo"), 251 MustParseVersion("1.0.0"), 252 VersionList{MustParseVersion("5.0")}, 253 platform2, 254 )) 255 onlyInS2 := fakeFilename("s2", FakePackageMeta( 256 addrs.NewDefaultProvider("foo"), 257 MustParseVersion("1.2.0"), 258 VersionList{MustParseVersion("5.0")}, 259 platform1, 260 )) 261 inBothS1 := fakeFilename("s1", FakePackageMeta( 262 addrs.NewDefaultProvider("foo"), 263 MustParseVersion("1.0.0"), 264 VersionList{MustParseVersion("5.0")}, 265 platform1, 266 )) 267 inBothS2 := fakeFilename("s2", inBothS1) 268 s1 := NewMockSource([]PackageMeta{ 269 inBothS1, 270 onlyInS1, 271 fakeFilename("s1", FakePackageMeta( 272 addrs.NewDefaultProvider("bar"), 273 MustParseVersion("1.0.0"), 274 VersionList{MustParseVersion("5.0")}, 275 platform2, 276 )), 277 }, 278 nil, 279 ) 280 s2 := NewMockSource([]PackageMeta{ 281 inBothS2, 282 onlyInS2, 283 fakeFilename("s2", FakePackageMeta( 284 addrs.NewDefaultProvider("bar"), 285 MustParseVersion("1.0.0"), 286 VersionList{MustParseVersion("5.0")}, 287 platform1, 288 )), 289 }, nil) 290 multi := MultiSource{ 291 {Source: s1}, 292 {Source: s2}, 293 } 294 295 t.Run("only in s1", func(t *testing.T) { 296 got, err := multi.PackageMeta( 297 context.Background(), 298 addrs.NewDefaultProvider("foo"), 299 MustParseVersion("1.0.0"), 300 platform2, 301 ) 302 want := onlyInS1 303 if err != nil { 304 t.Fatalf("unexpected error: %s", err) 305 } 306 if diff := cmp.Diff(want, got); diff != "" { 307 t.Errorf("wrong result\n%s", diff) 308 } 309 }) 310 t.Run("only in s2", func(t *testing.T) { 311 got, err := multi.PackageMeta( 312 context.Background(), 313 addrs.NewDefaultProvider("foo"), 314 MustParseVersion("1.2.0"), 315 platform1, 316 ) 317 want := onlyInS2 318 if err != nil { 319 t.Fatalf("unexpected error: %s", err) 320 } 321 if diff := cmp.Diff(want, got); diff != "" { 322 t.Errorf("wrong result\n%s", diff) 323 } 324 }) 325 t.Run("in both", func(t *testing.T) { 326 got, err := multi.PackageMeta( 327 context.Background(), 328 addrs.NewDefaultProvider("foo"), 329 MustParseVersion("1.0.0"), 330 platform1, 331 ) 332 want := inBothS1 // S1 "wins" because it's earlier in the MultiSource 333 if err != nil { 334 t.Fatalf("unexpected error: %s", err) 335 } 336 if diff := cmp.Diff(want, got); diff != "" { 337 t.Errorf("wrong result\n%s", diff) 338 } 339 340 // Make sure inBothS1 and inBothS2 really are different; if not then 341 // that's a test bug which we'd rather catch than have this test 342 // accidentally passing without actually checking anything. 343 if diff := cmp.Diff(inBothS1, inBothS2); diff == "" { 344 t.Fatalf("test bug: inBothS1 and inBothS2 are indistinguishable") 345 } 346 }) 347 t.Run("in neither", func(t *testing.T) { 348 _, err := multi.PackageMeta( 349 context.Background(), 350 addrs.NewDefaultProvider("nonexist"), 351 MustParseVersion("1.0.0"), 352 platform1, 353 ) 354 // This case reports "platform not supported" because it assumes that 355 // a caller would only pass to it package versions that were returned 356 // by a previousc all to AvailableVersions, and therefore a missing 357 // object ought to be valid provider/version but an unsupported 358 // platform. 359 if want, ok := err.(ErrPlatformNotSupported); !ok { 360 t.Fatalf("wrong error type:\ngot: %T\nwant: %T", err, want) 361 } 362 }) 363 } 364 365 func TestMultiSourceSelector(t *testing.T) { 366 emptySource := NewMockSource(nil, nil) 367 368 tests := map[string]struct { 369 Selector MultiSourceSelector 370 Provider addrs.Provider 371 WantMatch bool 372 }{ 373 "default provider with no constraints": { 374 MultiSourceSelector{ 375 Source: emptySource, 376 }, 377 addrs.NewDefaultProvider("foo"), 378 true, 379 }, 380 "built-in provider with no constraints": { 381 MultiSourceSelector{ 382 Source: emptySource, 383 }, 384 addrs.NewBuiltInProvider("bar"), 385 true, 386 }, 387 388 // Include constraints 389 "default provider with include constraint that matches it exactly": { 390 MultiSourceSelector{ 391 Source: emptySource, 392 Include: mustParseMultiSourceMatchingPatterns("hashicorp/foo"), 393 }, 394 addrs.NewDefaultProvider("foo"), 395 true, 396 }, 397 "default provider with include constraint that matches it via type wildcard": { 398 MultiSourceSelector{ 399 Source: emptySource, 400 Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"), 401 }, 402 addrs.NewDefaultProvider("foo"), 403 true, 404 }, 405 "default provider with include constraint that matches it via namespace wildcard": { 406 MultiSourceSelector{ 407 Source: emptySource, 408 Include: mustParseMultiSourceMatchingPatterns("*/*"), 409 }, 410 addrs.NewDefaultProvider("foo"), 411 true, 412 }, 413 "default provider with non-normalized include constraint that matches it via type wildcard": { 414 MultiSourceSelector{ 415 Source: emptySource, 416 Include: mustParseMultiSourceMatchingPatterns("HashiCorp/*"), 417 }, 418 addrs.NewDefaultProvider("foo"), 419 true, 420 }, 421 "built-in provider with exact include constraint that does not match it": { 422 MultiSourceSelector{ 423 Source: emptySource, 424 Include: mustParseMultiSourceMatchingPatterns("hashicorp/foo"), 425 }, 426 addrs.NewBuiltInProvider("bar"), 427 false, 428 }, 429 "built-in provider with type-wild include constraint that does not match it": { 430 MultiSourceSelector{ 431 Source: emptySource, 432 Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"), 433 }, 434 addrs.NewBuiltInProvider("bar"), 435 false, 436 }, 437 "built-in provider with namespace-wild include constraint that does not match it": { 438 MultiSourceSelector{ 439 Source: emptySource, 440 Include: mustParseMultiSourceMatchingPatterns("*/*"), 441 }, 442 // Doesn't match because builtin providers are in "durgaform.io", 443 // but a pattern with no hostname is for registry.durgaform.io. 444 addrs.NewBuiltInProvider("bar"), 445 false, 446 }, 447 "built-in provider with include constraint that matches it via type wildcard": { 448 MultiSourceSelector{ 449 Source: emptySource, 450 Include: mustParseMultiSourceMatchingPatterns("durgaform.io/builtin/*"), 451 }, 452 addrs.NewBuiltInProvider("bar"), 453 true, 454 }, 455 456 // Exclude constraints 457 "default provider with exclude constraint that matches it exactly": { 458 MultiSourceSelector{ 459 Source: emptySource, 460 Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/foo"), 461 }, 462 addrs.NewDefaultProvider("foo"), 463 false, 464 }, 465 "default provider with exclude constraint that matches it via type wildcard": { 466 MultiSourceSelector{ 467 Source: emptySource, 468 Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/*"), 469 }, 470 addrs.NewDefaultProvider("foo"), 471 false, 472 }, 473 "default provider with exact exclude constraint that doesn't match it": { 474 MultiSourceSelector{ 475 Source: emptySource, 476 Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/bar"), 477 }, 478 addrs.NewDefaultProvider("foo"), 479 true, 480 }, 481 "default provider with non-normalized exclude constraint that matches it via type wildcard": { 482 MultiSourceSelector{ 483 Source: emptySource, 484 Exclude: mustParseMultiSourceMatchingPatterns("HashiCorp/*"), 485 }, 486 addrs.NewDefaultProvider("foo"), 487 false, 488 }, 489 490 // Both include and exclude in a single selector 491 "default provider with exclude wildcard overriding include exact": { 492 MultiSourceSelector{ 493 Source: emptySource, 494 Include: mustParseMultiSourceMatchingPatterns("hashicorp/foo"), 495 Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/*"), 496 }, 497 addrs.NewDefaultProvider("foo"), 498 false, 499 }, 500 "default provider with exclude wildcard overriding irrelevant include exact": { 501 MultiSourceSelector{ 502 Source: emptySource, 503 Include: mustParseMultiSourceMatchingPatterns("hashicorp/bar"), 504 Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/*"), 505 }, 506 addrs.NewDefaultProvider("foo"), 507 false, 508 }, 509 "default provider with exclude exact overriding include wildcard": { 510 MultiSourceSelector{ 511 Source: emptySource, 512 Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"), 513 Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/foo"), 514 }, 515 addrs.NewDefaultProvider("foo"), 516 false, 517 }, 518 "default provider with irrelevant exclude exact overriding include wildcard": { 519 MultiSourceSelector{ 520 Source: emptySource, 521 Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"), 522 Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/bar"), 523 }, 524 addrs.NewDefaultProvider("foo"), 525 true, 526 }, 527 } 528 529 for name, test := range tests { 530 t.Run(name, func(t *testing.T) { 531 t.Logf("include: %s", test.Selector.Include) 532 t.Logf("exclude: %s", test.Selector.Exclude) 533 t.Logf("provider: %s", test.Provider) 534 got := test.Selector.CanHandleProvider(test.Provider) 535 want := test.WantMatch 536 if got != want { 537 t.Errorf("wrong result %t; want %t", got, want) 538 } 539 }) 540 } 541 } 542 543 func mustParseMultiSourceMatchingPatterns(strs ...string) MultiSourceMatchingPatterns { 544 ret, err := ParseMultiSourceMatchingPatterns(strs) 545 if err != nil { 546 panic(err) 547 } 548 return ret 549 }